From 8967edc988ee146823bca3c51fcf51296e9dec18 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 19:13:44 +0000 Subject: [PATCH] Sync generated provider output --- .agents/skills/impeccable/scripts/context.mjs | 40 +++++++++++++++++-- .claude/skills/impeccable/scripts/context.mjs | 40 +++++++++++++++++-- .cursor/skills/impeccable/scripts/context.mjs | 40 +++++++++++++++++-- .gemini/skills/impeccable/scripts/context.mjs | 40 +++++++++++++++++-- .github/skills/impeccable/scripts/context.mjs | 40 +++++++++++++++++-- .kiro/skills/impeccable/scripts/context.mjs | 40 +++++++++++++++++-- .../skills/impeccable/scripts/context.mjs | 40 +++++++++++++++++-- .pi/skills/impeccable/scripts/context.mjs | 40 +++++++++++++++++-- .qoder/skills/impeccable/scripts/context.mjs | 40 +++++++++++++++++-- .../skills/impeccable/scripts/context.mjs | 40 +++++++++++++++++-- .../skills/impeccable/scripts/context.mjs | 40 +++++++++++++++++-- .trae/skills/impeccable/scripts/context.mjs | 40 +++++++++++++++++-- plugin/skills/impeccable/scripts/context.mjs | 40 +++++++++++++++++-- 13 files changed, 468 insertions(+), 52 deletions(-) diff --git a/.agents/skills/impeccable/scripts/context.mjs b/.agents/skills/impeccable/scripts/context.mjs index 11f2aabe0..9715525db 100644 --- a/.agents/skills/impeccable/scripts/context.mjs +++ b/.agents/skills/impeccable/scripts/context.mjs @@ -7,9 +7,13 @@ * using the existing code as context. * * Path resolution (first match wins): - * 1. Active project root, if PRODUCT.md or DESIGN.md is there + * 1. Active project root, if PRODUCT.md or DESIGN.md is there. An explicit + * --target selects the active project: the workspace child in a + * monorepo, or the nearest directory around the target carrying + * canonical context files in an ordinary repo (issue #376). * 2. Active project .agents/context/ then docs/ - * 3. Monorepo root context, using the same order, as a per-file fallback + * 3. Repo root context, using the same order, as a per-file fallback + * whenever the active project is nested below it * 4. $IMPECCABLE_CONTEXT_DIR (absolute or cwd-relative) — power-user * escape hatch, only consulted when defaults are empty * 5. Active project root as a "nothing found" default @@ -88,7 +92,10 @@ function resolveContext(cwd = process.cwd(), options = {}) { const absCwd = path.resolve(cwd); const project = resolveProject(absCwd, options); const projectContextDir = resolveLocalContextDir(project.projectRoot); - const rootContextDir = project.isMonorepo && project.repoRoot !== project.projectRoot + // Per-file inheritance from the repo root whenever the active project is + // nested below it: monorepo workspace children and explicit-target nested + // products in ordinary repos behave the same way. + const rootContextDir = project.repoRoot !== project.projectRoot ? resolveLocalContextDir(project.repoRoot) : null; @@ -165,7 +172,7 @@ function resolveProject(cwd = process.cwd(), options = {}) { if (!repoRoot) { return { targetDir, - projectRoot: absCwd, + projectRoot: nearestTargetContextRoot(absCwd, targetDir) || absCwd, repoRoot: absCwd, isMonorepo: false, }; @@ -471,6 +478,31 @@ function isExcludedByWorkspacePattern(relSegments, patterns) { }); } +// An explicit --target in an ordinary (non-monorepo) repository must still +// select a nested product's own context (issue #376). Walk from the target up +// to — but not including — the invocation root and return the nearest +// directory carrying context files, in the canonical spot or a fallback dir +// (resolveLocalContextDir covers both). Context files only, not package.json: +// without the monorepo root-context fallback, a package.json marker would +// strand targets inside plain subpackages away from the root PRODUCT.md. The +// cwd's own fallback context dirs (.agents/context, docs) hold the root +// project's context, not a nested product, so they never count. +// Returns null when nothing nested is found, keeping the cwd default. +function nearestTargetContextRoot(absCwd, targetDir) { + if (!isPathInside(targetDir, absCwd)) return null; + const rootFallbackDirs = FALLBACK_DIRS.map((rel) => path.resolve(absCwd, rel)); + let dir = path.resolve(targetDir); + while (dir && dir !== absCwd) { + if (!rootFallbackDirs.includes(dir) && resolveLocalContextDir(dir)) { + return dir; + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + return null; +} + function nearestProjectLikeRoot(repoRoot, targetDir) { let dir = path.resolve(targetDir); const stop = path.resolve(repoRoot); diff --git a/.claude/skills/impeccable/scripts/context.mjs b/.claude/skills/impeccable/scripts/context.mjs index 11f2aabe0..9715525db 100644 --- a/.claude/skills/impeccable/scripts/context.mjs +++ b/.claude/skills/impeccable/scripts/context.mjs @@ -7,9 +7,13 @@ * using the existing code as context. * * Path resolution (first match wins): - * 1. Active project root, if PRODUCT.md or DESIGN.md is there + * 1. Active project root, if PRODUCT.md or DESIGN.md is there. An explicit + * --target selects the active project: the workspace child in a + * monorepo, or the nearest directory around the target carrying + * canonical context files in an ordinary repo (issue #376). * 2. Active project .agents/context/ then docs/ - * 3. Monorepo root context, using the same order, as a per-file fallback + * 3. Repo root context, using the same order, as a per-file fallback + * whenever the active project is nested below it * 4. $IMPECCABLE_CONTEXT_DIR (absolute or cwd-relative) — power-user * escape hatch, only consulted when defaults are empty * 5. Active project root as a "nothing found" default @@ -88,7 +92,10 @@ function resolveContext(cwd = process.cwd(), options = {}) { const absCwd = path.resolve(cwd); const project = resolveProject(absCwd, options); const projectContextDir = resolveLocalContextDir(project.projectRoot); - const rootContextDir = project.isMonorepo && project.repoRoot !== project.projectRoot + // Per-file inheritance from the repo root whenever the active project is + // nested below it: monorepo workspace children and explicit-target nested + // products in ordinary repos behave the same way. + const rootContextDir = project.repoRoot !== project.projectRoot ? resolveLocalContextDir(project.repoRoot) : null; @@ -165,7 +172,7 @@ function resolveProject(cwd = process.cwd(), options = {}) { if (!repoRoot) { return { targetDir, - projectRoot: absCwd, + projectRoot: nearestTargetContextRoot(absCwd, targetDir) || absCwd, repoRoot: absCwd, isMonorepo: false, }; @@ -471,6 +478,31 @@ function isExcludedByWorkspacePattern(relSegments, patterns) { }); } +// An explicit --target in an ordinary (non-monorepo) repository must still +// select a nested product's own context (issue #376). Walk from the target up +// to — but not including — the invocation root and return the nearest +// directory carrying context files, in the canonical spot or a fallback dir +// (resolveLocalContextDir covers both). Context files only, not package.json: +// without the monorepo root-context fallback, a package.json marker would +// strand targets inside plain subpackages away from the root PRODUCT.md. The +// cwd's own fallback context dirs (.agents/context, docs) hold the root +// project's context, not a nested product, so they never count. +// Returns null when nothing nested is found, keeping the cwd default. +function nearestTargetContextRoot(absCwd, targetDir) { + if (!isPathInside(targetDir, absCwd)) return null; + const rootFallbackDirs = FALLBACK_DIRS.map((rel) => path.resolve(absCwd, rel)); + let dir = path.resolve(targetDir); + while (dir && dir !== absCwd) { + if (!rootFallbackDirs.includes(dir) && resolveLocalContextDir(dir)) { + return dir; + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + return null; +} + function nearestProjectLikeRoot(repoRoot, targetDir) { let dir = path.resolve(targetDir); const stop = path.resolve(repoRoot); diff --git a/.cursor/skills/impeccable/scripts/context.mjs b/.cursor/skills/impeccable/scripts/context.mjs index 11f2aabe0..9715525db 100644 --- a/.cursor/skills/impeccable/scripts/context.mjs +++ b/.cursor/skills/impeccable/scripts/context.mjs @@ -7,9 +7,13 @@ * using the existing code as context. * * Path resolution (first match wins): - * 1. Active project root, if PRODUCT.md or DESIGN.md is there + * 1. Active project root, if PRODUCT.md or DESIGN.md is there. An explicit + * --target selects the active project: the workspace child in a + * monorepo, or the nearest directory around the target carrying + * canonical context files in an ordinary repo (issue #376). * 2. Active project .agents/context/ then docs/ - * 3. Monorepo root context, using the same order, as a per-file fallback + * 3. Repo root context, using the same order, as a per-file fallback + * whenever the active project is nested below it * 4. $IMPECCABLE_CONTEXT_DIR (absolute or cwd-relative) — power-user * escape hatch, only consulted when defaults are empty * 5. Active project root as a "nothing found" default @@ -88,7 +92,10 @@ function resolveContext(cwd = process.cwd(), options = {}) { const absCwd = path.resolve(cwd); const project = resolveProject(absCwd, options); const projectContextDir = resolveLocalContextDir(project.projectRoot); - const rootContextDir = project.isMonorepo && project.repoRoot !== project.projectRoot + // Per-file inheritance from the repo root whenever the active project is + // nested below it: monorepo workspace children and explicit-target nested + // products in ordinary repos behave the same way. + const rootContextDir = project.repoRoot !== project.projectRoot ? resolveLocalContextDir(project.repoRoot) : null; @@ -165,7 +172,7 @@ function resolveProject(cwd = process.cwd(), options = {}) { if (!repoRoot) { return { targetDir, - projectRoot: absCwd, + projectRoot: nearestTargetContextRoot(absCwd, targetDir) || absCwd, repoRoot: absCwd, isMonorepo: false, }; @@ -471,6 +478,31 @@ function isExcludedByWorkspacePattern(relSegments, patterns) { }); } +// An explicit --target in an ordinary (non-monorepo) repository must still +// select a nested product's own context (issue #376). Walk from the target up +// to — but not including — the invocation root and return the nearest +// directory carrying context files, in the canonical spot or a fallback dir +// (resolveLocalContextDir covers both). Context files only, not package.json: +// without the monorepo root-context fallback, a package.json marker would +// strand targets inside plain subpackages away from the root PRODUCT.md. The +// cwd's own fallback context dirs (.agents/context, docs) hold the root +// project's context, not a nested product, so they never count. +// Returns null when nothing nested is found, keeping the cwd default. +function nearestTargetContextRoot(absCwd, targetDir) { + if (!isPathInside(targetDir, absCwd)) return null; + const rootFallbackDirs = FALLBACK_DIRS.map((rel) => path.resolve(absCwd, rel)); + let dir = path.resolve(targetDir); + while (dir && dir !== absCwd) { + if (!rootFallbackDirs.includes(dir) && resolveLocalContextDir(dir)) { + return dir; + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + return null; +} + function nearestProjectLikeRoot(repoRoot, targetDir) { let dir = path.resolve(targetDir); const stop = path.resolve(repoRoot); diff --git a/.gemini/skills/impeccable/scripts/context.mjs b/.gemini/skills/impeccable/scripts/context.mjs index 11f2aabe0..9715525db 100644 --- a/.gemini/skills/impeccable/scripts/context.mjs +++ b/.gemini/skills/impeccable/scripts/context.mjs @@ -7,9 +7,13 @@ * using the existing code as context. * * Path resolution (first match wins): - * 1. Active project root, if PRODUCT.md or DESIGN.md is there + * 1. Active project root, if PRODUCT.md or DESIGN.md is there. An explicit + * --target selects the active project: the workspace child in a + * monorepo, or the nearest directory around the target carrying + * canonical context files in an ordinary repo (issue #376). * 2. Active project .agents/context/ then docs/ - * 3. Monorepo root context, using the same order, as a per-file fallback + * 3. Repo root context, using the same order, as a per-file fallback + * whenever the active project is nested below it * 4. $IMPECCABLE_CONTEXT_DIR (absolute or cwd-relative) — power-user * escape hatch, only consulted when defaults are empty * 5. Active project root as a "nothing found" default @@ -88,7 +92,10 @@ function resolveContext(cwd = process.cwd(), options = {}) { const absCwd = path.resolve(cwd); const project = resolveProject(absCwd, options); const projectContextDir = resolveLocalContextDir(project.projectRoot); - const rootContextDir = project.isMonorepo && project.repoRoot !== project.projectRoot + // Per-file inheritance from the repo root whenever the active project is + // nested below it: monorepo workspace children and explicit-target nested + // products in ordinary repos behave the same way. + const rootContextDir = project.repoRoot !== project.projectRoot ? resolveLocalContextDir(project.repoRoot) : null; @@ -165,7 +172,7 @@ function resolveProject(cwd = process.cwd(), options = {}) { if (!repoRoot) { return { targetDir, - projectRoot: absCwd, + projectRoot: nearestTargetContextRoot(absCwd, targetDir) || absCwd, repoRoot: absCwd, isMonorepo: false, }; @@ -471,6 +478,31 @@ function isExcludedByWorkspacePattern(relSegments, patterns) { }); } +// An explicit --target in an ordinary (non-monorepo) repository must still +// select a nested product's own context (issue #376). Walk from the target up +// to — but not including — the invocation root and return the nearest +// directory carrying context files, in the canonical spot or a fallback dir +// (resolveLocalContextDir covers both). Context files only, not package.json: +// without the monorepo root-context fallback, a package.json marker would +// strand targets inside plain subpackages away from the root PRODUCT.md. The +// cwd's own fallback context dirs (.agents/context, docs) hold the root +// project's context, not a nested product, so they never count. +// Returns null when nothing nested is found, keeping the cwd default. +function nearestTargetContextRoot(absCwd, targetDir) { + if (!isPathInside(targetDir, absCwd)) return null; + const rootFallbackDirs = FALLBACK_DIRS.map((rel) => path.resolve(absCwd, rel)); + let dir = path.resolve(targetDir); + while (dir && dir !== absCwd) { + if (!rootFallbackDirs.includes(dir) && resolveLocalContextDir(dir)) { + return dir; + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + return null; +} + function nearestProjectLikeRoot(repoRoot, targetDir) { let dir = path.resolve(targetDir); const stop = path.resolve(repoRoot); diff --git a/.github/skills/impeccable/scripts/context.mjs b/.github/skills/impeccable/scripts/context.mjs index 11f2aabe0..9715525db 100644 --- a/.github/skills/impeccable/scripts/context.mjs +++ b/.github/skills/impeccable/scripts/context.mjs @@ -7,9 +7,13 @@ * using the existing code as context. * * Path resolution (first match wins): - * 1. Active project root, if PRODUCT.md or DESIGN.md is there + * 1. Active project root, if PRODUCT.md or DESIGN.md is there. An explicit + * --target selects the active project: the workspace child in a + * monorepo, or the nearest directory around the target carrying + * canonical context files in an ordinary repo (issue #376). * 2. Active project .agents/context/ then docs/ - * 3. Monorepo root context, using the same order, as a per-file fallback + * 3. Repo root context, using the same order, as a per-file fallback + * whenever the active project is nested below it * 4. $IMPECCABLE_CONTEXT_DIR (absolute or cwd-relative) — power-user * escape hatch, only consulted when defaults are empty * 5. Active project root as a "nothing found" default @@ -88,7 +92,10 @@ function resolveContext(cwd = process.cwd(), options = {}) { const absCwd = path.resolve(cwd); const project = resolveProject(absCwd, options); const projectContextDir = resolveLocalContextDir(project.projectRoot); - const rootContextDir = project.isMonorepo && project.repoRoot !== project.projectRoot + // Per-file inheritance from the repo root whenever the active project is + // nested below it: monorepo workspace children and explicit-target nested + // products in ordinary repos behave the same way. + const rootContextDir = project.repoRoot !== project.projectRoot ? resolveLocalContextDir(project.repoRoot) : null; @@ -165,7 +172,7 @@ function resolveProject(cwd = process.cwd(), options = {}) { if (!repoRoot) { return { targetDir, - projectRoot: absCwd, + projectRoot: nearestTargetContextRoot(absCwd, targetDir) || absCwd, repoRoot: absCwd, isMonorepo: false, }; @@ -471,6 +478,31 @@ function isExcludedByWorkspacePattern(relSegments, patterns) { }); } +// An explicit --target in an ordinary (non-monorepo) repository must still +// select a nested product's own context (issue #376). Walk from the target up +// to — but not including — the invocation root and return the nearest +// directory carrying context files, in the canonical spot or a fallback dir +// (resolveLocalContextDir covers both). Context files only, not package.json: +// without the monorepo root-context fallback, a package.json marker would +// strand targets inside plain subpackages away from the root PRODUCT.md. The +// cwd's own fallback context dirs (.agents/context, docs) hold the root +// project's context, not a nested product, so they never count. +// Returns null when nothing nested is found, keeping the cwd default. +function nearestTargetContextRoot(absCwd, targetDir) { + if (!isPathInside(targetDir, absCwd)) return null; + const rootFallbackDirs = FALLBACK_DIRS.map((rel) => path.resolve(absCwd, rel)); + let dir = path.resolve(targetDir); + while (dir && dir !== absCwd) { + if (!rootFallbackDirs.includes(dir) && resolveLocalContextDir(dir)) { + return dir; + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + return null; +} + function nearestProjectLikeRoot(repoRoot, targetDir) { let dir = path.resolve(targetDir); const stop = path.resolve(repoRoot); diff --git a/.kiro/skills/impeccable/scripts/context.mjs b/.kiro/skills/impeccable/scripts/context.mjs index 11f2aabe0..9715525db 100644 --- a/.kiro/skills/impeccable/scripts/context.mjs +++ b/.kiro/skills/impeccable/scripts/context.mjs @@ -7,9 +7,13 @@ * using the existing code as context. * * Path resolution (first match wins): - * 1. Active project root, if PRODUCT.md or DESIGN.md is there + * 1. Active project root, if PRODUCT.md or DESIGN.md is there. An explicit + * --target selects the active project: the workspace child in a + * monorepo, or the nearest directory around the target carrying + * canonical context files in an ordinary repo (issue #376). * 2. Active project .agents/context/ then docs/ - * 3. Monorepo root context, using the same order, as a per-file fallback + * 3. Repo root context, using the same order, as a per-file fallback + * whenever the active project is nested below it * 4. $IMPECCABLE_CONTEXT_DIR (absolute or cwd-relative) — power-user * escape hatch, only consulted when defaults are empty * 5. Active project root as a "nothing found" default @@ -88,7 +92,10 @@ function resolveContext(cwd = process.cwd(), options = {}) { const absCwd = path.resolve(cwd); const project = resolveProject(absCwd, options); const projectContextDir = resolveLocalContextDir(project.projectRoot); - const rootContextDir = project.isMonorepo && project.repoRoot !== project.projectRoot + // Per-file inheritance from the repo root whenever the active project is + // nested below it: monorepo workspace children and explicit-target nested + // products in ordinary repos behave the same way. + const rootContextDir = project.repoRoot !== project.projectRoot ? resolveLocalContextDir(project.repoRoot) : null; @@ -165,7 +172,7 @@ function resolveProject(cwd = process.cwd(), options = {}) { if (!repoRoot) { return { targetDir, - projectRoot: absCwd, + projectRoot: nearestTargetContextRoot(absCwd, targetDir) || absCwd, repoRoot: absCwd, isMonorepo: false, }; @@ -471,6 +478,31 @@ function isExcludedByWorkspacePattern(relSegments, patterns) { }); } +// An explicit --target in an ordinary (non-monorepo) repository must still +// select a nested product's own context (issue #376). Walk from the target up +// to — but not including — the invocation root and return the nearest +// directory carrying context files, in the canonical spot or a fallback dir +// (resolveLocalContextDir covers both). Context files only, not package.json: +// without the monorepo root-context fallback, a package.json marker would +// strand targets inside plain subpackages away from the root PRODUCT.md. The +// cwd's own fallback context dirs (.agents/context, docs) hold the root +// project's context, not a nested product, so they never count. +// Returns null when nothing nested is found, keeping the cwd default. +function nearestTargetContextRoot(absCwd, targetDir) { + if (!isPathInside(targetDir, absCwd)) return null; + const rootFallbackDirs = FALLBACK_DIRS.map((rel) => path.resolve(absCwd, rel)); + let dir = path.resolve(targetDir); + while (dir && dir !== absCwd) { + if (!rootFallbackDirs.includes(dir) && resolveLocalContextDir(dir)) { + return dir; + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + return null; +} + function nearestProjectLikeRoot(repoRoot, targetDir) { let dir = path.resolve(targetDir); const stop = path.resolve(repoRoot); diff --git a/.opencode/skills/impeccable/scripts/context.mjs b/.opencode/skills/impeccable/scripts/context.mjs index 11f2aabe0..9715525db 100644 --- a/.opencode/skills/impeccable/scripts/context.mjs +++ b/.opencode/skills/impeccable/scripts/context.mjs @@ -7,9 +7,13 @@ * using the existing code as context. * * Path resolution (first match wins): - * 1. Active project root, if PRODUCT.md or DESIGN.md is there + * 1. Active project root, if PRODUCT.md or DESIGN.md is there. An explicit + * --target selects the active project: the workspace child in a + * monorepo, or the nearest directory around the target carrying + * canonical context files in an ordinary repo (issue #376). * 2. Active project .agents/context/ then docs/ - * 3. Monorepo root context, using the same order, as a per-file fallback + * 3. Repo root context, using the same order, as a per-file fallback + * whenever the active project is nested below it * 4. $IMPECCABLE_CONTEXT_DIR (absolute or cwd-relative) — power-user * escape hatch, only consulted when defaults are empty * 5. Active project root as a "nothing found" default @@ -88,7 +92,10 @@ function resolveContext(cwd = process.cwd(), options = {}) { const absCwd = path.resolve(cwd); const project = resolveProject(absCwd, options); const projectContextDir = resolveLocalContextDir(project.projectRoot); - const rootContextDir = project.isMonorepo && project.repoRoot !== project.projectRoot + // Per-file inheritance from the repo root whenever the active project is + // nested below it: monorepo workspace children and explicit-target nested + // products in ordinary repos behave the same way. + const rootContextDir = project.repoRoot !== project.projectRoot ? resolveLocalContextDir(project.repoRoot) : null; @@ -165,7 +172,7 @@ function resolveProject(cwd = process.cwd(), options = {}) { if (!repoRoot) { return { targetDir, - projectRoot: absCwd, + projectRoot: nearestTargetContextRoot(absCwd, targetDir) || absCwd, repoRoot: absCwd, isMonorepo: false, }; @@ -471,6 +478,31 @@ function isExcludedByWorkspacePattern(relSegments, patterns) { }); } +// An explicit --target in an ordinary (non-monorepo) repository must still +// select a nested product's own context (issue #376). Walk from the target up +// to — but not including — the invocation root and return the nearest +// directory carrying context files, in the canonical spot or a fallback dir +// (resolveLocalContextDir covers both). Context files only, not package.json: +// without the monorepo root-context fallback, a package.json marker would +// strand targets inside plain subpackages away from the root PRODUCT.md. The +// cwd's own fallback context dirs (.agents/context, docs) hold the root +// project's context, not a nested product, so they never count. +// Returns null when nothing nested is found, keeping the cwd default. +function nearestTargetContextRoot(absCwd, targetDir) { + if (!isPathInside(targetDir, absCwd)) return null; + const rootFallbackDirs = FALLBACK_DIRS.map((rel) => path.resolve(absCwd, rel)); + let dir = path.resolve(targetDir); + while (dir && dir !== absCwd) { + if (!rootFallbackDirs.includes(dir) && resolveLocalContextDir(dir)) { + return dir; + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + return null; +} + function nearestProjectLikeRoot(repoRoot, targetDir) { let dir = path.resolve(targetDir); const stop = path.resolve(repoRoot); diff --git a/.pi/skills/impeccable/scripts/context.mjs b/.pi/skills/impeccable/scripts/context.mjs index 11f2aabe0..9715525db 100644 --- a/.pi/skills/impeccable/scripts/context.mjs +++ b/.pi/skills/impeccable/scripts/context.mjs @@ -7,9 +7,13 @@ * using the existing code as context. * * Path resolution (first match wins): - * 1. Active project root, if PRODUCT.md or DESIGN.md is there + * 1. Active project root, if PRODUCT.md or DESIGN.md is there. An explicit + * --target selects the active project: the workspace child in a + * monorepo, or the nearest directory around the target carrying + * canonical context files in an ordinary repo (issue #376). * 2. Active project .agents/context/ then docs/ - * 3. Monorepo root context, using the same order, as a per-file fallback + * 3. Repo root context, using the same order, as a per-file fallback + * whenever the active project is nested below it * 4. $IMPECCABLE_CONTEXT_DIR (absolute or cwd-relative) — power-user * escape hatch, only consulted when defaults are empty * 5. Active project root as a "nothing found" default @@ -88,7 +92,10 @@ function resolveContext(cwd = process.cwd(), options = {}) { const absCwd = path.resolve(cwd); const project = resolveProject(absCwd, options); const projectContextDir = resolveLocalContextDir(project.projectRoot); - const rootContextDir = project.isMonorepo && project.repoRoot !== project.projectRoot + // Per-file inheritance from the repo root whenever the active project is + // nested below it: monorepo workspace children and explicit-target nested + // products in ordinary repos behave the same way. + const rootContextDir = project.repoRoot !== project.projectRoot ? resolveLocalContextDir(project.repoRoot) : null; @@ -165,7 +172,7 @@ function resolveProject(cwd = process.cwd(), options = {}) { if (!repoRoot) { return { targetDir, - projectRoot: absCwd, + projectRoot: nearestTargetContextRoot(absCwd, targetDir) || absCwd, repoRoot: absCwd, isMonorepo: false, }; @@ -471,6 +478,31 @@ function isExcludedByWorkspacePattern(relSegments, patterns) { }); } +// An explicit --target in an ordinary (non-monorepo) repository must still +// select a nested product's own context (issue #376). Walk from the target up +// to — but not including — the invocation root and return the nearest +// directory carrying context files, in the canonical spot or a fallback dir +// (resolveLocalContextDir covers both). Context files only, not package.json: +// without the monorepo root-context fallback, a package.json marker would +// strand targets inside plain subpackages away from the root PRODUCT.md. The +// cwd's own fallback context dirs (.agents/context, docs) hold the root +// project's context, not a nested product, so they never count. +// Returns null when nothing nested is found, keeping the cwd default. +function nearestTargetContextRoot(absCwd, targetDir) { + if (!isPathInside(targetDir, absCwd)) return null; + const rootFallbackDirs = FALLBACK_DIRS.map((rel) => path.resolve(absCwd, rel)); + let dir = path.resolve(targetDir); + while (dir && dir !== absCwd) { + if (!rootFallbackDirs.includes(dir) && resolveLocalContextDir(dir)) { + return dir; + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + return null; +} + function nearestProjectLikeRoot(repoRoot, targetDir) { let dir = path.resolve(targetDir); const stop = path.resolve(repoRoot); diff --git a/.qoder/skills/impeccable/scripts/context.mjs b/.qoder/skills/impeccable/scripts/context.mjs index 11f2aabe0..9715525db 100644 --- a/.qoder/skills/impeccable/scripts/context.mjs +++ b/.qoder/skills/impeccable/scripts/context.mjs @@ -7,9 +7,13 @@ * using the existing code as context. * * Path resolution (first match wins): - * 1. Active project root, if PRODUCT.md or DESIGN.md is there + * 1. Active project root, if PRODUCT.md or DESIGN.md is there. An explicit + * --target selects the active project: the workspace child in a + * monorepo, or the nearest directory around the target carrying + * canonical context files in an ordinary repo (issue #376). * 2. Active project .agents/context/ then docs/ - * 3. Monorepo root context, using the same order, as a per-file fallback + * 3. Repo root context, using the same order, as a per-file fallback + * whenever the active project is nested below it * 4. $IMPECCABLE_CONTEXT_DIR (absolute or cwd-relative) — power-user * escape hatch, only consulted when defaults are empty * 5. Active project root as a "nothing found" default @@ -88,7 +92,10 @@ function resolveContext(cwd = process.cwd(), options = {}) { const absCwd = path.resolve(cwd); const project = resolveProject(absCwd, options); const projectContextDir = resolveLocalContextDir(project.projectRoot); - const rootContextDir = project.isMonorepo && project.repoRoot !== project.projectRoot + // Per-file inheritance from the repo root whenever the active project is + // nested below it: monorepo workspace children and explicit-target nested + // products in ordinary repos behave the same way. + const rootContextDir = project.repoRoot !== project.projectRoot ? resolveLocalContextDir(project.repoRoot) : null; @@ -165,7 +172,7 @@ function resolveProject(cwd = process.cwd(), options = {}) { if (!repoRoot) { return { targetDir, - projectRoot: absCwd, + projectRoot: nearestTargetContextRoot(absCwd, targetDir) || absCwd, repoRoot: absCwd, isMonorepo: false, }; @@ -471,6 +478,31 @@ function isExcludedByWorkspacePattern(relSegments, patterns) { }); } +// An explicit --target in an ordinary (non-monorepo) repository must still +// select a nested product's own context (issue #376). Walk from the target up +// to — but not including — the invocation root and return the nearest +// directory carrying context files, in the canonical spot or a fallback dir +// (resolveLocalContextDir covers both). Context files only, not package.json: +// without the monorepo root-context fallback, a package.json marker would +// strand targets inside plain subpackages away from the root PRODUCT.md. The +// cwd's own fallback context dirs (.agents/context, docs) hold the root +// project's context, not a nested product, so they never count. +// Returns null when nothing nested is found, keeping the cwd default. +function nearestTargetContextRoot(absCwd, targetDir) { + if (!isPathInside(targetDir, absCwd)) return null; + const rootFallbackDirs = FALLBACK_DIRS.map((rel) => path.resolve(absCwd, rel)); + let dir = path.resolve(targetDir); + while (dir && dir !== absCwd) { + if (!rootFallbackDirs.includes(dir) && resolveLocalContextDir(dir)) { + return dir; + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + return null; +} + function nearestProjectLikeRoot(repoRoot, targetDir) { let dir = path.resolve(targetDir); const stop = path.resolve(repoRoot); diff --git a/.rovodev/skills/impeccable/scripts/context.mjs b/.rovodev/skills/impeccable/scripts/context.mjs index 11f2aabe0..9715525db 100644 --- a/.rovodev/skills/impeccable/scripts/context.mjs +++ b/.rovodev/skills/impeccable/scripts/context.mjs @@ -7,9 +7,13 @@ * using the existing code as context. * * Path resolution (first match wins): - * 1. Active project root, if PRODUCT.md or DESIGN.md is there + * 1. Active project root, if PRODUCT.md or DESIGN.md is there. An explicit + * --target selects the active project: the workspace child in a + * monorepo, or the nearest directory around the target carrying + * canonical context files in an ordinary repo (issue #376). * 2. Active project .agents/context/ then docs/ - * 3. Monorepo root context, using the same order, as a per-file fallback + * 3. Repo root context, using the same order, as a per-file fallback + * whenever the active project is nested below it * 4. $IMPECCABLE_CONTEXT_DIR (absolute or cwd-relative) — power-user * escape hatch, only consulted when defaults are empty * 5. Active project root as a "nothing found" default @@ -88,7 +92,10 @@ function resolveContext(cwd = process.cwd(), options = {}) { const absCwd = path.resolve(cwd); const project = resolveProject(absCwd, options); const projectContextDir = resolveLocalContextDir(project.projectRoot); - const rootContextDir = project.isMonorepo && project.repoRoot !== project.projectRoot + // Per-file inheritance from the repo root whenever the active project is + // nested below it: monorepo workspace children and explicit-target nested + // products in ordinary repos behave the same way. + const rootContextDir = project.repoRoot !== project.projectRoot ? resolveLocalContextDir(project.repoRoot) : null; @@ -165,7 +172,7 @@ function resolveProject(cwd = process.cwd(), options = {}) { if (!repoRoot) { return { targetDir, - projectRoot: absCwd, + projectRoot: nearestTargetContextRoot(absCwd, targetDir) || absCwd, repoRoot: absCwd, isMonorepo: false, }; @@ -471,6 +478,31 @@ function isExcludedByWorkspacePattern(relSegments, patterns) { }); } +// An explicit --target in an ordinary (non-monorepo) repository must still +// select a nested product's own context (issue #376). Walk from the target up +// to — but not including — the invocation root and return the nearest +// directory carrying context files, in the canonical spot or a fallback dir +// (resolveLocalContextDir covers both). Context files only, not package.json: +// without the monorepo root-context fallback, a package.json marker would +// strand targets inside plain subpackages away from the root PRODUCT.md. The +// cwd's own fallback context dirs (.agents/context, docs) hold the root +// project's context, not a nested product, so they never count. +// Returns null when nothing nested is found, keeping the cwd default. +function nearestTargetContextRoot(absCwd, targetDir) { + if (!isPathInside(targetDir, absCwd)) return null; + const rootFallbackDirs = FALLBACK_DIRS.map((rel) => path.resolve(absCwd, rel)); + let dir = path.resolve(targetDir); + while (dir && dir !== absCwd) { + if (!rootFallbackDirs.includes(dir) && resolveLocalContextDir(dir)) { + return dir; + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + return null; +} + function nearestProjectLikeRoot(repoRoot, targetDir) { let dir = path.resolve(targetDir); const stop = path.resolve(repoRoot); diff --git a/.trae-cn/skills/impeccable/scripts/context.mjs b/.trae-cn/skills/impeccable/scripts/context.mjs index 11f2aabe0..9715525db 100644 --- a/.trae-cn/skills/impeccable/scripts/context.mjs +++ b/.trae-cn/skills/impeccable/scripts/context.mjs @@ -7,9 +7,13 @@ * using the existing code as context. * * Path resolution (first match wins): - * 1. Active project root, if PRODUCT.md or DESIGN.md is there + * 1. Active project root, if PRODUCT.md or DESIGN.md is there. An explicit + * --target selects the active project: the workspace child in a + * monorepo, or the nearest directory around the target carrying + * canonical context files in an ordinary repo (issue #376). * 2. Active project .agents/context/ then docs/ - * 3. Monorepo root context, using the same order, as a per-file fallback + * 3. Repo root context, using the same order, as a per-file fallback + * whenever the active project is nested below it * 4. $IMPECCABLE_CONTEXT_DIR (absolute or cwd-relative) — power-user * escape hatch, only consulted when defaults are empty * 5. Active project root as a "nothing found" default @@ -88,7 +92,10 @@ function resolveContext(cwd = process.cwd(), options = {}) { const absCwd = path.resolve(cwd); const project = resolveProject(absCwd, options); const projectContextDir = resolveLocalContextDir(project.projectRoot); - const rootContextDir = project.isMonorepo && project.repoRoot !== project.projectRoot + // Per-file inheritance from the repo root whenever the active project is + // nested below it: monorepo workspace children and explicit-target nested + // products in ordinary repos behave the same way. + const rootContextDir = project.repoRoot !== project.projectRoot ? resolveLocalContextDir(project.repoRoot) : null; @@ -165,7 +172,7 @@ function resolveProject(cwd = process.cwd(), options = {}) { if (!repoRoot) { return { targetDir, - projectRoot: absCwd, + projectRoot: nearestTargetContextRoot(absCwd, targetDir) || absCwd, repoRoot: absCwd, isMonorepo: false, }; @@ -471,6 +478,31 @@ function isExcludedByWorkspacePattern(relSegments, patterns) { }); } +// An explicit --target in an ordinary (non-monorepo) repository must still +// select a nested product's own context (issue #376). Walk from the target up +// to — but not including — the invocation root and return the nearest +// directory carrying context files, in the canonical spot or a fallback dir +// (resolveLocalContextDir covers both). Context files only, not package.json: +// without the monorepo root-context fallback, a package.json marker would +// strand targets inside plain subpackages away from the root PRODUCT.md. The +// cwd's own fallback context dirs (.agents/context, docs) hold the root +// project's context, not a nested product, so they never count. +// Returns null when nothing nested is found, keeping the cwd default. +function nearestTargetContextRoot(absCwd, targetDir) { + if (!isPathInside(targetDir, absCwd)) return null; + const rootFallbackDirs = FALLBACK_DIRS.map((rel) => path.resolve(absCwd, rel)); + let dir = path.resolve(targetDir); + while (dir && dir !== absCwd) { + if (!rootFallbackDirs.includes(dir) && resolveLocalContextDir(dir)) { + return dir; + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + return null; +} + function nearestProjectLikeRoot(repoRoot, targetDir) { let dir = path.resolve(targetDir); const stop = path.resolve(repoRoot); diff --git a/.trae/skills/impeccable/scripts/context.mjs b/.trae/skills/impeccable/scripts/context.mjs index 11f2aabe0..9715525db 100644 --- a/.trae/skills/impeccable/scripts/context.mjs +++ b/.trae/skills/impeccable/scripts/context.mjs @@ -7,9 +7,13 @@ * using the existing code as context. * * Path resolution (first match wins): - * 1. Active project root, if PRODUCT.md or DESIGN.md is there + * 1. Active project root, if PRODUCT.md or DESIGN.md is there. An explicit + * --target selects the active project: the workspace child in a + * monorepo, or the nearest directory around the target carrying + * canonical context files in an ordinary repo (issue #376). * 2. Active project .agents/context/ then docs/ - * 3. Monorepo root context, using the same order, as a per-file fallback + * 3. Repo root context, using the same order, as a per-file fallback + * whenever the active project is nested below it * 4. $IMPECCABLE_CONTEXT_DIR (absolute or cwd-relative) — power-user * escape hatch, only consulted when defaults are empty * 5. Active project root as a "nothing found" default @@ -88,7 +92,10 @@ function resolveContext(cwd = process.cwd(), options = {}) { const absCwd = path.resolve(cwd); const project = resolveProject(absCwd, options); const projectContextDir = resolveLocalContextDir(project.projectRoot); - const rootContextDir = project.isMonorepo && project.repoRoot !== project.projectRoot + // Per-file inheritance from the repo root whenever the active project is + // nested below it: monorepo workspace children and explicit-target nested + // products in ordinary repos behave the same way. + const rootContextDir = project.repoRoot !== project.projectRoot ? resolveLocalContextDir(project.repoRoot) : null; @@ -165,7 +172,7 @@ function resolveProject(cwd = process.cwd(), options = {}) { if (!repoRoot) { return { targetDir, - projectRoot: absCwd, + projectRoot: nearestTargetContextRoot(absCwd, targetDir) || absCwd, repoRoot: absCwd, isMonorepo: false, }; @@ -471,6 +478,31 @@ function isExcludedByWorkspacePattern(relSegments, patterns) { }); } +// An explicit --target in an ordinary (non-monorepo) repository must still +// select a nested product's own context (issue #376). Walk from the target up +// to — but not including — the invocation root and return the nearest +// directory carrying context files, in the canonical spot or a fallback dir +// (resolveLocalContextDir covers both). Context files only, not package.json: +// without the monorepo root-context fallback, a package.json marker would +// strand targets inside plain subpackages away from the root PRODUCT.md. The +// cwd's own fallback context dirs (.agents/context, docs) hold the root +// project's context, not a nested product, so they never count. +// Returns null when nothing nested is found, keeping the cwd default. +function nearestTargetContextRoot(absCwd, targetDir) { + if (!isPathInside(targetDir, absCwd)) return null; + const rootFallbackDirs = FALLBACK_DIRS.map((rel) => path.resolve(absCwd, rel)); + let dir = path.resolve(targetDir); + while (dir && dir !== absCwd) { + if (!rootFallbackDirs.includes(dir) && resolveLocalContextDir(dir)) { + return dir; + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + return null; +} + function nearestProjectLikeRoot(repoRoot, targetDir) { let dir = path.resolve(targetDir); const stop = path.resolve(repoRoot); diff --git a/plugin/skills/impeccable/scripts/context.mjs b/plugin/skills/impeccable/scripts/context.mjs index 11f2aabe0..9715525db 100644 --- a/plugin/skills/impeccable/scripts/context.mjs +++ b/plugin/skills/impeccable/scripts/context.mjs @@ -7,9 +7,13 @@ * using the existing code as context. * * Path resolution (first match wins): - * 1. Active project root, if PRODUCT.md or DESIGN.md is there + * 1. Active project root, if PRODUCT.md or DESIGN.md is there. An explicit + * --target selects the active project: the workspace child in a + * monorepo, or the nearest directory around the target carrying + * canonical context files in an ordinary repo (issue #376). * 2. Active project .agents/context/ then docs/ - * 3. Monorepo root context, using the same order, as a per-file fallback + * 3. Repo root context, using the same order, as a per-file fallback + * whenever the active project is nested below it * 4. $IMPECCABLE_CONTEXT_DIR (absolute or cwd-relative) — power-user * escape hatch, only consulted when defaults are empty * 5. Active project root as a "nothing found" default @@ -88,7 +92,10 @@ function resolveContext(cwd = process.cwd(), options = {}) { const absCwd = path.resolve(cwd); const project = resolveProject(absCwd, options); const projectContextDir = resolveLocalContextDir(project.projectRoot); - const rootContextDir = project.isMonorepo && project.repoRoot !== project.projectRoot + // Per-file inheritance from the repo root whenever the active project is + // nested below it: monorepo workspace children and explicit-target nested + // products in ordinary repos behave the same way. + const rootContextDir = project.repoRoot !== project.projectRoot ? resolveLocalContextDir(project.repoRoot) : null; @@ -165,7 +172,7 @@ function resolveProject(cwd = process.cwd(), options = {}) { if (!repoRoot) { return { targetDir, - projectRoot: absCwd, + projectRoot: nearestTargetContextRoot(absCwd, targetDir) || absCwd, repoRoot: absCwd, isMonorepo: false, }; @@ -471,6 +478,31 @@ function isExcludedByWorkspacePattern(relSegments, patterns) { }); } +// An explicit --target in an ordinary (non-monorepo) repository must still +// select a nested product's own context (issue #376). Walk from the target up +// to — but not including — the invocation root and return the nearest +// directory carrying context files, in the canonical spot or a fallback dir +// (resolveLocalContextDir covers both). Context files only, not package.json: +// without the monorepo root-context fallback, a package.json marker would +// strand targets inside plain subpackages away from the root PRODUCT.md. The +// cwd's own fallback context dirs (.agents/context, docs) hold the root +// project's context, not a nested product, so they never count. +// Returns null when nothing nested is found, keeping the cwd default. +function nearestTargetContextRoot(absCwd, targetDir) { + if (!isPathInside(targetDir, absCwd)) return null; + const rootFallbackDirs = FALLBACK_DIRS.map((rel) => path.resolve(absCwd, rel)); + let dir = path.resolve(targetDir); + while (dir && dir !== absCwd) { + if (!rootFallbackDirs.includes(dir) && resolveLocalContextDir(dir)) { + return dir; + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + return null; +} + function nearestProjectLikeRoot(repoRoot, targetDir) { let dir = path.resolve(targetDir); const stop = path.resolve(repoRoot);