diff --git a/.agents/skills/impeccable/scripts/context.mjs b/.agents/skills/impeccable/scripts/context.mjs index 3afb81b99..05accaf8f 100644 --- a/.agents/skills/impeccable/scripts/context.mjs +++ b/.agents/skills/impeccable/scripts/context.mjs @@ -285,10 +285,31 @@ function resolveEnvContextDir(cwd) { return path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); } +function resolveTargetPath(cwd, targetPath) { + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + if (fs.existsSync(abs)) return abs; + return findUniqueBareTarget(cwd, targetPath) || abs; +} + +function findUniqueBareTarget(cwd, targetPath) { + const absCwd = path.resolve(cwd); + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(absCwd, targetPath); + const rel = path.relative(absCwd, abs); + if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return null; + const segments = rel.split(path.sep).filter(Boolean); + if (segments.length !== 1) return null; + const name = segments[0]; + const repoRoot = findMonorepoRoot(absCwd); + if (!repoRoot) return null; + const matches = discoverTargetCandidates(repoRoot).filter((candidate) => candidate.name === name); + if (matches.length !== 1) return null; + return path.resolve(repoRoot, matches[0].path); +} + function resolveTargetDir(cwd, options = {}) { const targetPath = options && typeof options === 'object' ? options.targetPath : null; if (!targetPath || !String(targetPath).trim()) return cwd; - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + const abs = resolveTargetPath(cwd, targetPath); try { const stat = fs.statSync(abs); return stat.isDirectory() ? abs : path.dirname(abs); @@ -1188,13 +1209,19 @@ async function cli() { throw err; } const targetProvided = hasTargetOption(cliOptions); - const targetExists = targetProvided ? pathExistsForTarget(process.cwd(), cliOptions.targetPath) : null; + const resolvedTargetPath = targetProvided + ? resolveTargetPath(process.cwd(), cliOptions.targetPath) + : null; + const targetExists = targetProvided ? fs.existsSync(resolvedTargetPath) : null; const selection = resolveTargetSelection(process.cwd(), cliOptions); if (selection) { process.stdout.write(buildTargetSelectionDirective(selection) + '\n'); process.exit(0); } - const ctx = loadContext(process.cwd(), cliOptions); + const ctx = loadContext( + process.cwd(), + resolvedTargetPath ? { targetPath: resolvedTargetPath } : cliOptions, + ); const updateDirective = await computeUpdateDirective(); if (!ctx.hasProduct) { @@ -1308,11 +1335,6 @@ function hasTargetOption(options) { return !!(options && typeof options.targetPath === 'string' && options.targetPath.trim()); } -function pathExistsForTarget(cwd, targetPath) { - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); - return fs.existsSync(abs); -} - const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], codex: ['.codex/hooks.json'], diff --git a/.claude/skills/impeccable/scripts/context.mjs b/.claude/skills/impeccable/scripts/context.mjs index 3afb81b99..05accaf8f 100644 --- a/.claude/skills/impeccable/scripts/context.mjs +++ b/.claude/skills/impeccable/scripts/context.mjs @@ -285,10 +285,31 @@ function resolveEnvContextDir(cwd) { return path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); } +function resolveTargetPath(cwd, targetPath) { + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + if (fs.existsSync(abs)) return abs; + return findUniqueBareTarget(cwd, targetPath) || abs; +} + +function findUniqueBareTarget(cwd, targetPath) { + const absCwd = path.resolve(cwd); + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(absCwd, targetPath); + const rel = path.relative(absCwd, abs); + if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return null; + const segments = rel.split(path.sep).filter(Boolean); + if (segments.length !== 1) return null; + const name = segments[0]; + const repoRoot = findMonorepoRoot(absCwd); + if (!repoRoot) return null; + const matches = discoverTargetCandidates(repoRoot).filter((candidate) => candidate.name === name); + if (matches.length !== 1) return null; + return path.resolve(repoRoot, matches[0].path); +} + function resolveTargetDir(cwd, options = {}) { const targetPath = options && typeof options === 'object' ? options.targetPath : null; if (!targetPath || !String(targetPath).trim()) return cwd; - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + const abs = resolveTargetPath(cwd, targetPath); try { const stat = fs.statSync(abs); return stat.isDirectory() ? abs : path.dirname(abs); @@ -1188,13 +1209,19 @@ async function cli() { throw err; } const targetProvided = hasTargetOption(cliOptions); - const targetExists = targetProvided ? pathExistsForTarget(process.cwd(), cliOptions.targetPath) : null; + const resolvedTargetPath = targetProvided + ? resolveTargetPath(process.cwd(), cliOptions.targetPath) + : null; + const targetExists = targetProvided ? fs.existsSync(resolvedTargetPath) : null; const selection = resolveTargetSelection(process.cwd(), cliOptions); if (selection) { process.stdout.write(buildTargetSelectionDirective(selection) + '\n'); process.exit(0); } - const ctx = loadContext(process.cwd(), cliOptions); + const ctx = loadContext( + process.cwd(), + resolvedTargetPath ? { targetPath: resolvedTargetPath } : cliOptions, + ); const updateDirective = await computeUpdateDirective(); if (!ctx.hasProduct) { @@ -1308,11 +1335,6 @@ function hasTargetOption(options) { return !!(options && typeof options.targetPath === 'string' && options.targetPath.trim()); } -function pathExistsForTarget(cwd, targetPath) { - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); - return fs.existsSync(abs); -} - const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], codex: ['.codex/hooks.json'], diff --git a/.cursor/skills/impeccable/scripts/context.mjs b/.cursor/skills/impeccable/scripts/context.mjs index 3afb81b99..05accaf8f 100644 --- a/.cursor/skills/impeccable/scripts/context.mjs +++ b/.cursor/skills/impeccable/scripts/context.mjs @@ -285,10 +285,31 @@ function resolveEnvContextDir(cwd) { return path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); } +function resolveTargetPath(cwd, targetPath) { + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + if (fs.existsSync(abs)) return abs; + return findUniqueBareTarget(cwd, targetPath) || abs; +} + +function findUniqueBareTarget(cwd, targetPath) { + const absCwd = path.resolve(cwd); + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(absCwd, targetPath); + const rel = path.relative(absCwd, abs); + if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return null; + const segments = rel.split(path.sep).filter(Boolean); + if (segments.length !== 1) return null; + const name = segments[0]; + const repoRoot = findMonorepoRoot(absCwd); + if (!repoRoot) return null; + const matches = discoverTargetCandidates(repoRoot).filter((candidate) => candidate.name === name); + if (matches.length !== 1) return null; + return path.resolve(repoRoot, matches[0].path); +} + function resolveTargetDir(cwd, options = {}) { const targetPath = options && typeof options === 'object' ? options.targetPath : null; if (!targetPath || !String(targetPath).trim()) return cwd; - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + const abs = resolveTargetPath(cwd, targetPath); try { const stat = fs.statSync(abs); return stat.isDirectory() ? abs : path.dirname(abs); @@ -1188,13 +1209,19 @@ async function cli() { throw err; } const targetProvided = hasTargetOption(cliOptions); - const targetExists = targetProvided ? pathExistsForTarget(process.cwd(), cliOptions.targetPath) : null; + const resolvedTargetPath = targetProvided + ? resolveTargetPath(process.cwd(), cliOptions.targetPath) + : null; + const targetExists = targetProvided ? fs.existsSync(resolvedTargetPath) : null; const selection = resolveTargetSelection(process.cwd(), cliOptions); if (selection) { process.stdout.write(buildTargetSelectionDirective(selection) + '\n'); process.exit(0); } - const ctx = loadContext(process.cwd(), cliOptions); + const ctx = loadContext( + process.cwd(), + resolvedTargetPath ? { targetPath: resolvedTargetPath } : cliOptions, + ); const updateDirective = await computeUpdateDirective(); if (!ctx.hasProduct) { @@ -1308,11 +1335,6 @@ function hasTargetOption(options) { return !!(options && typeof options.targetPath === 'string' && options.targetPath.trim()); } -function pathExistsForTarget(cwd, targetPath) { - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); - return fs.existsSync(abs); -} - const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], codex: ['.codex/hooks.json'], diff --git a/.gemini/skills/impeccable/scripts/context.mjs b/.gemini/skills/impeccable/scripts/context.mjs index 3afb81b99..05accaf8f 100644 --- a/.gemini/skills/impeccable/scripts/context.mjs +++ b/.gemini/skills/impeccable/scripts/context.mjs @@ -285,10 +285,31 @@ function resolveEnvContextDir(cwd) { return path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); } +function resolveTargetPath(cwd, targetPath) { + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + if (fs.existsSync(abs)) return abs; + return findUniqueBareTarget(cwd, targetPath) || abs; +} + +function findUniqueBareTarget(cwd, targetPath) { + const absCwd = path.resolve(cwd); + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(absCwd, targetPath); + const rel = path.relative(absCwd, abs); + if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return null; + const segments = rel.split(path.sep).filter(Boolean); + if (segments.length !== 1) return null; + const name = segments[0]; + const repoRoot = findMonorepoRoot(absCwd); + if (!repoRoot) return null; + const matches = discoverTargetCandidates(repoRoot).filter((candidate) => candidate.name === name); + if (matches.length !== 1) return null; + return path.resolve(repoRoot, matches[0].path); +} + function resolveTargetDir(cwd, options = {}) { const targetPath = options && typeof options === 'object' ? options.targetPath : null; if (!targetPath || !String(targetPath).trim()) return cwd; - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + const abs = resolveTargetPath(cwd, targetPath); try { const stat = fs.statSync(abs); return stat.isDirectory() ? abs : path.dirname(abs); @@ -1188,13 +1209,19 @@ async function cli() { throw err; } const targetProvided = hasTargetOption(cliOptions); - const targetExists = targetProvided ? pathExistsForTarget(process.cwd(), cliOptions.targetPath) : null; + const resolvedTargetPath = targetProvided + ? resolveTargetPath(process.cwd(), cliOptions.targetPath) + : null; + const targetExists = targetProvided ? fs.existsSync(resolvedTargetPath) : null; const selection = resolveTargetSelection(process.cwd(), cliOptions); if (selection) { process.stdout.write(buildTargetSelectionDirective(selection) + '\n'); process.exit(0); } - const ctx = loadContext(process.cwd(), cliOptions); + const ctx = loadContext( + process.cwd(), + resolvedTargetPath ? { targetPath: resolvedTargetPath } : cliOptions, + ); const updateDirective = await computeUpdateDirective(); if (!ctx.hasProduct) { @@ -1308,11 +1335,6 @@ function hasTargetOption(options) { return !!(options && typeof options.targetPath === 'string' && options.targetPath.trim()); } -function pathExistsForTarget(cwd, targetPath) { - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); - return fs.existsSync(abs); -} - const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], codex: ['.codex/hooks.json'], diff --git a/.github/skills/impeccable/scripts/context.mjs b/.github/skills/impeccable/scripts/context.mjs index 3afb81b99..05accaf8f 100644 --- a/.github/skills/impeccable/scripts/context.mjs +++ b/.github/skills/impeccable/scripts/context.mjs @@ -285,10 +285,31 @@ function resolveEnvContextDir(cwd) { return path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); } +function resolveTargetPath(cwd, targetPath) { + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + if (fs.existsSync(abs)) return abs; + return findUniqueBareTarget(cwd, targetPath) || abs; +} + +function findUniqueBareTarget(cwd, targetPath) { + const absCwd = path.resolve(cwd); + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(absCwd, targetPath); + const rel = path.relative(absCwd, abs); + if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return null; + const segments = rel.split(path.sep).filter(Boolean); + if (segments.length !== 1) return null; + const name = segments[0]; + const repoRoot = findMonorepoRoot(absCwd); + if (!repoRoot) return null; + const matches = discoverTargetCandidates(repoRoot).filter((candidate) => candidate.name === name); + if (matches.length !== 1) return null; + return path.resolve(repoRoot, matches[0].path); +} + function resolveTargetDir(cwd, options = {}) { const targetPath = options && typeof options === 'object' ? options.targetPath : null; if (!targetPath || !String(targetPath).trim()) return cwd; - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + const abs = resolveTargetPath(cwd, targetPath); try { const stat = fs.statSync(abs); return stat.isDirectory() ? abs : path.dirname(abs); @@ -1188,13 +1209,19 @@ async function cli() { throw err; } const targetProvided = hasTargetOption(cliOptions); - const targetExists = targetProvided ? pathExistsForTarget(process.cwd(), cliOptions.targetPath) : null; + const resolvedTargetPath = targetProvided + ? resolveTargetPath(process.cwd(), cliOptions.targetPath) + : null; + const targetExists = targetProvided ? fs.existsSync(resolvedTargetPath) : null; const selection = resolveTargetSelection(process.cwd(), cliOptions); if (selection) { process.stdout.write(buildTargetSelectionDirective(selection) + '\n'); process.exit(0); } - const ctx = loadContext(process.cwd(), cliOptions); + const ctx = loadContext( + process.cwd(), + resolvedTargetPath ? { targetPath: resolvedTargetPath } : cliOptions, + ); const updateDirective = await computeUpdateDirective(); if (!ctx.hasProduct) { @@ -1308,11 +1335,6 @@ function hasTargetOption(options) { return !!(options && typeof options.targetPath === 'string' && options.targetPath.trim()); } -function pathExistsForTarget(cwd, targetPath) { - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); - return fs.existsSync(abs); -} - const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], codex: ['.codex/hooks.json'], diff --git a/.grok/skills/impeccable/scripts/context.mjs b/.grok/skills/impeccable/scripts/context.mjs index 3afb81b99..05accaf8f 100644 --- a/.grok/skills/impeccable/scripts/context.mjs +++ b/.grok/skills/impeccable/scripts/context.mjs @@ -285,10 +285,31 @@ function resolveEnvContextDir(cwd) { return path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); } +function resolveTargetPath(cwd, targetPath) { + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + if (fs.existsSync(abs)) return abs; + return findUniqueBareTarget(cwd, targetPath) || abs; +} + +function findUniqueBareTarget(cwd, targetPath) { + const absCwd = path.resolve(cwd); + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(absCwd, targetPath); + const rel = path.relative(absCwd, abs); + if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return null; + const segments = rel.split(path.sep).filter(Boolean); + if (segments.length !== 1) return null; + const name = segments[0]; + const repoRoot = findMonorepoRoot(absCwd); + if (!repoRoot) return null; + const matches = discoverTargetCandidates(repoRoot).filter((candidate) => candidate.name === name); + if (matches.length !== 1) return null; + return path.resolve(repoRoot, matches[0].path); +} + function resolveTargetDir(cwd, options = {}) { const targetPath = options && typeof options === 'object' ? options.targetPath : null; if (!targetPath || !String(targetPath).trim()) return cwd; - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + const abs = resolveTargetPath(cwd, targetPath); try { const stat = fs.statSync(abs); return stat.isDirectory() ? abs : path.dirname(abs); @@ -1188,13 +1209,19 @@ async function cli() { throw err; } const targetProvided = hasTargetOption(cliOptions); - const targetExists = targetProvided ? pathExistsForTarget(process.cwd(), cliOptions.targetPath) : null; + const resolvedTargetPath = targetProvided + ? resolveTargetPath(process.cwd(), cliOptions.targetPath) + : null; + const targetExists = targetProvided ? fs.existsSync(resolvedTargetPath) : null; const selection = resolveTargetSelection(process.cwd(), cliOptions); if (selection) { process.stdout.write(buildTargetSelectionDirective(selection) + '\n'); process.exit(0); } - const ctx = loadContext(process.cwd(), cliOptions); + const ctx = loadContext( + process.cwd(), + resolvedTargetPath ? { targetPath: resolvedTargetPath } : cliOptions, + ); const updateDirective = await computeUpdateDirective(); if (!ctx.hasProduct) { @@ -1308,11 +1335,6 @@ function hasTargetOption(options) { return !!(options && typeof options.targetPath === 'string' && options.targetPath.trim()); } -function pathExistsForTarget(cwd, targetPath) { - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); - return fs.existsSync(abs); -} - const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], codex: ['.codex/hooks.json'], diff --git a/.hermes/skills/impeccable/scripts/context.mjs b/.hermes/skills/impeccable/scripts/context.mjs index 3afb81b99..05accaf8f 100644 --- a/.hermes/skills/impeccable/scripts/context.mjs +++ b/.hermes/skills/impeccable/scripts/context.mjs @@ -285,10 +285,31 @@ function resolveEnvContextDir(cwd) { return path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); } +function resolveTargetPath(cwd, targetPath) { + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + if (fs.existsSync(abs)) return abs; + return findUniqueBareTarget(cwd, targetPath) || abs; +} + +function findUniqueBareTarget(cwd, targetPath) { + const absCwd = path.resolve(cwd); + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(absCwd, targetPath); + const rel = path.relative(absCwd, abs); + if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return null; + const segments = rel.split(path.sep).filter(Boolean); + if (segments.length !== 1) return null; + const name = segments[0]; + const repoRoot = findMonorepoRoot(absCwd); + if (!repoRoot) return null; + const matches = discoverTargetCandidates(repoRoot).filter((candidate) => candidate.name === name); + if (matches.length !== 1) return null; + return path.resolve(repoRoot, matches[0].path); +} + function resolveTargetDir(cwd, options = {}) { const targetPath = options && typeof options === 'object' ? options.targetPath : null; if (!targetPath || !String(targetPath).trim()) return cwd; - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + const abs = resolveTargetPath(cwd, targetPath); try { const stat = fs.statSync(abs); return stat.isDirectory() ? abs : path.dirname(abs); @@ -1188,13 +1209,19 @@ async function cli() { throw err; } const targetProvided = hasTargetOption(cliOptions); - const targetExists = targetProvided ? pathExistsForTarget(process.cwd(), cliOptions.targetPath) : null; + const resolvedTargetPath = targetProvided + ? resolveTargetPath(process.cwd(), cliOptions.targetPath) + : null; + const targetExists = targetProvided ? fs.existsSync(resolvedTargetPath) : null; const selection = resolveTargetSelection(process.cwd(), cliOptions); if (selection) { process.stdout.write(buildTargetSelectionDirective(selection) + '\n'); process.exit(0); } - const ctx = loadContext(process.cwd(), cliOptions); + const ctx = loadContext( + process.cwd(), + resolvedTargetPath ? { targetPath: resolvedTargetPath } : cliOptions, + ); const updateDirective = await computeUpdateDirective(); if (!ctx.hasProduct) { @@ -1308,11 +1335,6 @@ function hasTargetOption(options) { return !!(options && typeof options.targetPath === 'string' && options.targetPath.trim()); } -function pathExistsForTarget(cwd, targetPath) { - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); - return fs.existsSync(abs); -} - const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], codex: ['.codex/hooks.json'], diff --git a/.kiro/skills/impeccable/scripts/context.mjs b/.kiro/skills/impeccable/scripts/context.mjs index 3afb81b99..05accaf8f 100644 --- a/.kiro/skills/impeccable/scripts/context.mjs +++ b/.kiro/skills/impeccable/scripts/context.mjs @@ -285,10 +285,31 @@ function resolveEnvContextDir(cwd) { return path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); } +function resolveTargetPath(cwd, targetPath) { + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + if (fs.existsSync(abs)) return abs; + return findUniqueBareTarget(cwd, targetPath) || abs; +} + +function findUniqueBareTarget(cwd, targetPath) { + const absCwd = path.resolve(cwd); + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(absCwd, targetPath); + const rel = path.relative(absCwd, abs); + if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return null; + const segments = rel.split(path.sep).filter(Boolean); + if (segments.length !== 1) return null; + const name = segments[0]; + const repoRoot = findMonorepoRoot(absCwd); + if (!repoRoot) return null; + const matches = discoverTargetCandidates(repoRoot).filter((candidate) => candidate.name === name); + if (matches.length !== 1) return null; + return path.resolve(repoRoot, matches[0].path); +} + function resolveTargetDir(cwd, options = {}) { const targetPath = options && typeof options === 'object' ? options.targetPath : null; if (!targetPath || !String(targetPath).trim()) return cwd; - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + const abs = resolveTargetPath(cwd, targetPath); try { const stat = fs.statSync(abs); return stat.isDirectory() ? abs : path.dirname(abs); @@ -1188,13 +1209,19 @@ async function cli() { throw err; } const targetProvided = hasTargetOption(cliOptions); - const targetExists = targetProvided ? pathExistsForTarget(process.cwd(), cliOptions.targetPath) : null; + const resolvedTargetPath = targetProvided + ? resolveTargetPath(process.cwd(), cliOptions.targetPath) + : null; + const targetExists = targetProvided ? fs.existsSync(resolvedTargetPath) : null; const selection = resolveTargetSelection(process.cwd(), cliOptions); if (selection) { process.stdout.write(buildTargetSelectionDirective(selection) + '\n'); process.exit(0); } - const ctx = loadContext(process.cwd(), cliOptions); + const ctx = loadContext( + process.cwd(), + resolvedTargetPath ? { targetPath: resolvedTargetPath } : cliOptions, + ); const updateDirective = await computeUpdateDirective(); if (!ctx.hasProduct) { @@ -1308,11 +1335,6 @@ function hasTargetOption(options) { return !!(options && typeof options.targetPath === 'string' && options.targetPath.trim()); } -function pathExistsForTarget(cwd, targetPath) { - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); - return fs.existsSync(abs); -} - const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], codex: ['.codex/hooks.json'], diff --git a/.opencode/skills/impeccable/scripts/context.mjs b/.opencode/skills/impeccable/scripts/context.mjs index 3afb81b99..05accaf8f 100644 --- a/.opencode/skills/impeccable/scripts/context.mjs +++ b/.opencode/skills/impeccable/scripts/context.mjs @@ -285,10 +285,31 @@ function resolveEnvContextDir(cwd) { return path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); } +function resolveTargetPath(cwd, targetPath) { + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + if (fs.existsSync(abs)) return abs; + return findUniqueBareTarget(cwd, targetPath) || abs; +} + +function findUniqueBareTarget(cwd, targetPath) { + const absCwd = path.resolve(cwd); + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(absCwd, targetPath); + const rel = path.relative(absCwd, abs); + if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return null; + const segments = rel.split(path.sep).filter(Boolean); + if (segments.length !== 1) return null; + const name = segments[0]; + const repoRoot = findMonorepoRoot(absCwd); + if (!repoRoot) return null; + const matches = discoverTargetCandidates(repoRoot).filter((candidate) => candidate.name === name); + if (matches.length !== 1) return null; + return path.resolve(repoRoot, matches[0].path); +} + function resolveTargetDir(cwd, options = {}) { const targetPath = options && typeof options === 'object' ? options.targetPath : null; if (!targetPath || !String(targetPath).trim()) return cwd; - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + const abs = resolveTargetPath(cwd, targetPath); try { const stat = fs.statSync(abs); return stat.isDirectory() ? abs : path.dirname(abs); @@ -1188,13 +1209,19 @@ async function cli() { throw err; } const targetProvided = hasTargetOption(cliOptions); - const targetExists = targetProvided ? pathExistsForTarget(process.cwd(), cliOptions.targetPath) : null; + const resolvedTargetPath = targetProvided + ? resolveTargetPath(process.cwd(), cliOptions.targetPath) + : null; + const targetExists = targetProvided ? fs.existsSync(resolvedTargetPath) : null; const selection = resolveTargetSelection(process.cwd(), cliOptions); if (selection) { process.stdout.write(buildTargetSelectionDirective(selection) + '\n'); process.exit(0); } - const ctx = loadContext(process.cwd(), cliOptions); + const ctx = loadContext( + process.cwd(), + resolvedTargetPath ? { targetPath: resolvedTargetPath } : cliOptions, + ); const updateDirective = await computeUpdateDirective(); if (!ctx.hasProduct) { @@ -1308,11 +1335,6 @@ function hasTargetOption(options) { return !!(options && typeof options.targetPath === 'string' && options.targetPath.trim()); } -function pathExistsForTarget(cwd, targetPath) { - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); - return fs.existsSync(abs); -} - const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], codex: ['.codex/hooks.json'], diff --git a/.pi/skills/impeccable/scripts/context.mjs b/.pi/skills/impeccable/scripts/context.mjs index 3afb81b99..05accaf8f 100644 --- a/.pi/skills/impeccable/scripts/context.mjs +++ b/.pi/skills/impeccable/scripts/context.mjs @@ -285,10 +285,31 @@ function resolveEnvContextDir(cwd) { return path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); } +function resolveTargetPath(cwd, targetPath) { + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + if (fs.existsSync(abs)) return abs; + return findUniqueBareTarget(cwd, targetPath) || abs; +} + +function findUniqueBareTarget(cwd, targetPath) { + const absCwd = path.resolve(cwd); + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(absCwd, targetPath); + const rel = path.relative(absCwd, abs); + if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return null; + const segments = rel.split(path.sep).filter(Boolean); + if (segments.length !== 1) return null; + const name = segments[0]; + const repoRoot = findMonorepoRoot(absCwd); + if (!repoRoot) return null; + const matches = discoverTargetCandidates(repoRoot).filter((candidate) => candidate.name === name); + if (matches.length !== 1) return null; + return path.resolve(repoRoot, matches[0].path); +} + function resolveTargetDir(cwd, options = {}) { const targetPath = options && typeof options === 'object' ? options.targetPath : null; if (!targetPath || !String(targetPath).trim()) return cwd; - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + const abs = resolveTargetPath(cwd, targetPath); try { const stat = fs.statSync(abs); return stat.isDirectory() ? abs : path.dirname(abs); @@ -1188,13 +1209,19 @@ async function cli() { throw err; } const targetProvided = hasTargetOption(cliOptions); - const targetExists = targetProvided ? pathExistsForTarget(process.cwd(), cliOptions.targetPath) : null; + const resolvedTargetPath = targetProvided + ? resolveTargetPath(process.cwd(), cliOptions.targetPath) + : null; + const targetExists = targetProvided ? fs.existsSync(resolvedTargetPath) : null; const selection = resolveTargetSelection(process.cwd(), cliOptions); if (selection) { process.stdout.write(buildTargetSelectionDirective(selection) + '\n'); process.exit(0); } - const ctx = loadContext(process.cwd(), cliOptions); + const ctx = loadContext( + process.cwd(), + resolvedTargetPath ? { targetPath: resolvedTargetPath } : cliOptions, + ); const updateDirective = await computeUpdateDirective(); if (!ctx.hasProduct) { @@ -1308,11 +1335,6 @@ function hasTargetOption(options) { return !!(options && typeof options.targetPath === 'string' && options.targetPath.trim()); } -function pathExistsForTarget(cwd, targetPath) { - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); - return fs.existsSync(abs); -} - const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], codex: ['.codex/hooks.json'], diff --git a/.qoder/skills/impeccable/scripts/context.mjs b/.qoder/skills/impeccable/scripts/context.mjs index 3afb81b99..05accaf8f 100644 --- a/.qoder/skills/impeccable/scripts/context.mjs +++ b/.qoder/skills/impeccable/scripts/context.mjs @@ -285,10 +285,31 @@ function resolveEnvContextDir(cwd) { return path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); } +function resolveTargetPath(cwd, targetPath) { + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + if (fs.existsSync(abs)) return abs; + return findUniqueBareTarget(cwd, targetPath) || abs; +} + +function findUniqueBareTarget(cwd, targetPath) { + const absCwd = path.resolve(cwd); + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(absCwd, targetPath); + const rel = path.relative(absCwd, abs); + if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return null; + const segments = rel.split(path.sep).filter(Boolean); + if (segments.length !== 1) return null; + const name = segments[0]; + const repoRoot = findMonorepoRoot(absCwd); + if (!repoRoot) return null; + const matches = discoverTargetCandidates(repoRoot).filter((candidate) => candidate.name === name); + if (matches.length !== 1) return null; + return path.resolve(repoRoot, matches[0].path); +} + function resolveTargetDir(cwd, options = {}) { const targetPath = options && typeof options === 'object' ? options.targetPath : null; if (!targetPath || !String(targetPath).trim()) return cwd; - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + const abs = resolveTargetPath(cwd, targetPath); try { const stat = fs.statSync(abs); return stat.isDirectory() ? abs : path.dirname(abs); @@ -1188,13 +1209,19 @@ async function cli() { throw err; } const targetProvided = hasTargetOption(cliOptions); - const targetExists = targetProvided ? pathExistsForTarget(process.cwd(), cliOptions.targetPath) : null; + const resolvedTargetPath = targetProvided + ? resolveTargetPath(process.cwd(), cliOptions.targetPath) + : null; + const targetExists = targetProvided ? fs.existsSync(resolvedTargetPath) : null; const selection = resolveTargetSelection(process.cwd(), cliOptions); if (selection) { process.stdout.write(buildTargetSelectionDirective(selection) + '\n'); process.exit(0); } - const ctx = loadContext(process.cwd(), cliOptions); + const ctx = loadContext( + process.cwd(), + resolvedTargetPath ? { targetPath: resolvedTargetPath } : cliOptions, + ); const updateDirective = await computeUpdateDirective(); if (!ctx.hasProduct) { @@ -1308,11 +1335,6 @@ function hasTargetOption(options) { return !!(options && typeof options.targetPath === 'string' && options.targetPath.trim()); } -function pathExistsForTarget(cwd, targetPath) { - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); - return fs.existsSync(abs); -} - const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], codex: ['.codex/hooks.json'], diff --git a/.rovodev/skills/impeccable/scripts/context.mjs b/.rovodev/skills/impeccable/scripts/context.mjs index 3afb81b99..05accaf8f 100644 --- a/.rovodev/skills/impeccable/scripts/context.mjs +++ b/.rovodev/skills/impeccable/scripts/context.mjs @@ -285,10 +285,31 @@ function resolveEnvContextDir(cwd) { return path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); } +function resolveTargetPath(cwd, targetPath) { + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + if (fs.existsSync(abs)) return abs; + return findUniqueBareTarget(cwd, targetPath) || abs; +} + +function findUniqueBareTarget(cwd, targetPath) { + const absCwd = path.resolve(cwd); + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(absCwd, targetPath); + const rel = path.relative(absCwd, abs); + if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return null; + const segments = rel.split(path.sep).filter(Boolean); + if (segments.length !== 1) return null; + const name = segments[0]; + const repoRoot = findMonorepoRoot(absCwd); + if (!repoRoot) return null; + const matches = discoverTargetCandidates(repoRoot).filter((candidate) => candidate.name === name); + if (matches.length !== 1) return null; + return path.resolve(repoRoot, matches[0].path); +} + function resolveTargetDir(cwd, options = {}) { const targetPath = options && typeof options === 'object' ? options.targetPath : null; if (!targetPath || !String(targetPath).trim()) return cwd; - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + const abs = resolveTargetPath(cwd, targetPath); try { const stat = fs.statSync(abs); return stat.isDirectory() ? abs : path.dirname(abs); @@ -1188,13 +1209,19 @@ async function cli() { throw err; } const targetProvided = hasTargetOption(cliOptions); - const targetExists = targetProvided ? pathExistsForTarget(process.cwd(), cliOptions.targetPath) : null; + const resolvedTargetPath = targetProvided + ? resolveTargetPath(process.cwd(), cliOptions.targetPath) + : null; + const targetExists = targetProvided ? fs.existsSync(resolvedTargetPath) : null; const selection = resolveTargetSelection(process.cwd(), cliOptions); if (selection) { process.stdout.write(buildTargetSelectionDirective(selection) + '\n'); process.exit(0); } - const ctx = loadContext(process.cwd(), cliOptions); + const ctx = loadContext( + process.cwd(), + resolvedTargetPath ? { targetPath: resolvedTargetPath } : cliOptions, + ); const updateDirective = await computeUpdateDirective(); if (!ctx.hasProduct) { @@ -1308,11 +1335,6 @@ function hasTargetOption(options) { return !!(options && typeof options.targetPath === 'string' && options.targetPath.trim()); } -function pathExistsForTarget(cwd, targetPath) { - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); - return fs.existsSync(abs); -} - const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], codex: ['.codex/hooks.json'], diff --git a/.trae-cn/skills/impeccable/scripts/context.mjs b/.trae-cn/skills/impeccable/scripts/context.mjs index 3afb81b99..05accaf8f 100644 --- a/.trae-cn/skills/impeccable/scripts/context.mjs +++ b/.trae-cn/skills/impeccable/scripts/context.mjs @@ -285,10 +285,31 @@ function resolveEnvContextDir(cwd) { return path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); } +function resolveTargetPath(cwd, targetPath) { + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + if (fs.existsSync(abs)) return abs; + return findUniqueBareTarget(cwd, targetPath) || abs; +} + +function findUniqueBareTarget(cwd, targetPath) { + const absCwd = path.resolve(cwd); + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(absCwd, targetPath); + const rel = path.relative(absCwd, abs); + if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return null; + const segments = rel.split(path.sep).filter(Boolean); + if (segments.length !== 1) return null; + const name = segments[0]; + const repoRoot = findMonorepoRoot(absCwd); + if (!repoRoot) return null; + const matches = discoverTargetCandidates(repoRoot).filter((candidate) => candidate.name === name); + if (matches.length !== 1) return null; + return path.resolve(repoRoot, matches[0].path); +} + function resolveTargetDir(cwd, options = {}) { const targetPath = options && typeof options === 'object' ? options.targetPath : null; if (!targetPath || !String(targetPath).trim()) return cwd; - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + const abs = resolveTargetPath(cwd, targetPath); try { const stat = fs.statSync(abs); return stat.isDirectory() ? abs : path.dirname(abs); @@ -1188,13 +1209,19 @@ async function cli() { throw err; } const targetProvided = hasTargetOption(cliOptions); - const targetExists = targetProvided ? pathExistsForTarget(process.cwd(), cliOptions.targetPath) : null; + const resolvedTargetPath = targetProvided + ? resolveTargetPath(process.cwd(), cliOptions.targetPath) + : null; + const targetExists = targetProvided ? fs.existsSync(resolvedTargetPath) : null; const selection = resolveTargetSelection(process.cwd(), cliOptions); if (selection) { process.stdout.write(buildTargetSelectionDirective(selection) + '\n'); process.exit(0); } - const ctx = loadContext(process.cwd(), cliOptions); + const ctx = loadContext( + process.cwd(), + resolvedTargetPath ? { targetPath: resolvedTargetPath } : cliOptions, + ); const updateDirective = await computeUpdateDirective(); if (!ctx.hasProduct) { @@ -1308,11 +1335,6 @@ function hasTargetOption(options) { return !!(options && typeof options.targetPath === 'string' && options.targetPath.trim()); } -function pathExistsForTarget(cwd, targetPath) { - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); - return fs.existsSync(abs); -} - const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], codex: ['.codex/hooks.json'], diff --git a/.trae/skills/impeccable/scripts/context.mjs b/.trae/skills/impeccable/scripts/context.mjs index 3afb81b99..05accaf8f 100644 --- a/.trae/skills/impeccable/scripts/context.mjs +++ b/.trae/skills/impeccable/scripts/context.mjs @@ -285,10 +285,31 @@ function resolveEnvContextDir(cwd) { return path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); } +function resolveTargetPath(cwd, targetPath) { + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + if (fs.existsSync(abs)) return abs; + return findUniqueBareTarget(cwd, targetPath) || abs; +} + +function findUniqueBareTarget(cwd, targetPath) { + const absCwd = path.resolve(cwd); + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(absCwd, targetPath); + const rel = path.relative(absCwd, abs); + if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return null; + const segments = rel.split(path.sep).filter(Boolean); + if (segments.length !== 1) return null; + const name = segments[0]; + const repoRoot = findMonorepoRoot(absCwd); + if (!repoRoot) return null; + const matches = discoverTargetCandidates(repoRoot).filter((candidate) => candidate.name === name); + if (matches.length !== 1) return null; + return path.resolve(repoRoot, matches[0].path); +} + function resolveTargetDir(cwd, options = {}) { const targetPath = options && typeof options === 'object' ? options.targetPath : null; if (!targetPath || !String(targetPath).trim()) return cwd; - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + const abs = resolveTargetPath(cwd, targetPath); try { const stat = fs.statSync(abs); return stat.isDirectory() ? abs : path.dirname(abs); @@ -1188,13 +1209,19 @@ async function cli() { throw err; } const targetProvided = hasTargetOption(cliOptions); - const targetExists = targetProvided ? pathExistsForTarget(process.cwd(), cliOptions.targetPath) : null; + const resolvedTargetPath = targetProvided + ? resolveTargetPath(process.cwd(), cliOptions.targetPath) + : null; + const targetExists = targetProvided ? fs.existsSync(resolvedTargetPath) : null; const selection = resolveTargetSelection(process.cwd(), cliOptions); if (selection) { process.stdout.write(buildTargetSelectionDirective(selection) + '\n'); process.exit(0); } - const ctx = loadContext(process.cwd(), cliOptions); + const ctx = loadContext( + process.cwd(), + resolvedTargetPath ? { targetPath: resolvedTargetPath } : cliOptions, + ); const updateDirective = await computeUpdateDirective(); if (!ctx.hasProduct) { @@ -1308,11 +1335,6 @@ function hasTargetOption(options) { return !!(options && typeof options.targetPath === 'string' && options.targetPath.trim()); } -function pathExistsForTarget(cwd, targetPath) { - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); - return fs.existsSync(abs); -} - const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], codex: ['.codex/hooks.json'], diff --git a/.vibe/skills/impeccable/scripts/context.mjs b/.vibe/skills/impeccable/scripts/context.mjs index 3afb81b99..05accaf8f 100644 --- a/.vibe/skills/impeccable/scripts/context.mjs +++ b/.vibe/skills/impeccable/scripts/context.mjs @@ -285,10 +285,31 @@ function resolveEnvContextDir(cwd) { return path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); } +function resolveTargetPath(cwd, targetPath) { + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + if (fs.existsSync(abs)) return abs; + return findUniqueBareTarget(cwd, targetPath) || abs; +} + +function findUniqueBareTarget(cwd, targetPath) { + const absCwd = path.resolve(cwd); + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(absCwd, targetPath); + const rel = path.relative(absCwd, abs); + if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return null; + const segments = rel.split(path.sep).filter(Boolean); + if (segments.length !== 1) return null; + const name = segments[0]; + const repoRoot = findMonorepoRoot(absCwd); + if (!repoRoot) return null; + const matches = discoverTargetCandidates(repoRoot).filter((candidate) => candidate.name === name); + if (matches.length !== 1) return null; + return path.resolve(repoRoot, matches[0].path); +} + function resolveTargetDir(cwd, options = {}) { const targetPath = options && typeof options === 'object' ? options.targetPath : null; if (!targetPath || !String(targetPath).trim()) return cwd; - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + const abs = resolveTargetPath(cwd, targetPath); try { const stat = fs.statSync(abs); return stat.isDirectory() ? abs : path.dirname(abs); @@ -1188,13 +1209,19 @@ async function cli() { throw err; } const targetProvided = hasTargetOption(cliOptions); - const targetExists = targetProvided ? pathExistsForTarget(process.cwd(), cliOptions.targetPath) : null; + const resolvedTargetPath = targetProvided + ? resolveTargetPath(process.cwd(), cliOptions.targetPath) + : null; + const targetExists = targetProvided ? fs.existsSync(resolvedTargetPath) : null; const selection = resolveTargetSelection(process.cwd(), cliOptions); if (selection) { process.stdout.write(buildTargetSelectionDirective(selection) + '\n'); process.exit(0); } - const ctx = loadContext(process.cwd(), cliOptions); + const ctx = loadContext( + process.cwd(), + resolvedTargetPath ? { targetPath: resolvedTargetPath } : cliOptions, + ); const updateDirective = await computeUpdateDirective(); if (!ctx.hasProduct) { @@ -1308,11 +1335,6 @@ function hasTargetOption(options) { return !!(options && typeof options.targetPath === 'string' && options.targetPath.trim()); } -function pathExistsForTarget(cwd, targetPath) { - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); - return fs.existsSync(abs); -} - const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], codex: ['.codex/hooks.json'], diff --git a/plugin/skills/impeccable/scripts/context.mjs b/plugin/skills/impeccable/scripts/context.mjs index 3afb81b99..05accaf8f 100644 --- a/plugin/skills/impeccable/scripts/context.mjs +++ b/plugin/skills/impeccable/scripts/context.mjs @@ -285,10 +285,31 @@ function resolveEnvContextDir(cwd) { return path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed); } +function resolveTargetPath(cwd, targetPath) { + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + if (fs.existsSync(abs)) return abs; + return findUniqueBareTarget(cwd, targetPath) || abs; +} + +function findUniqueBareTarget(cwd, targetPath) { + const absCwd = path.resolve(cwd); + const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(absCwd, targetPath); + const rel = path.relative(absCwd, abs); + if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return null; + const segments = rel.split(path.sep).filter(Boolean); + if (segments.length !== 1) return null; + const name = segments[0]; + const repoRoot = findMonorepoRoot(absCwd); + if (!repoRoot) return null; + const matches = discoverTargetCandidates(repoRoot).filter((candidate) => candidate.name === name); + if (matches.length !== 1) return null; + return path.resolve(repoRoot, matches[0].path); +} + function resolveTargetDir(cwd, options = {}) { const targetPath = options && typeof options === 'object' ? options.targetPath : null; if (!targetPath || !String(targetPath).trim()) return cwd; - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); + const abs = resolveTargetPath(cwd, targetPath); try { const stat = fs.statSync(abs); return stat.isDirectory() ? abs : path.dirname(abs); @@ -1188,13 +1209,19 @@ async function cli() { throw err; } const targetProvided = hasTargetOption(cliOptions); - const targetExists = targetProvided ? pathExistsForTarget(process.cwd(), cliOptions.targetPath) : null; + const resolvedTargetPath = targetProvided + ? resolveTargetPath(process.cwd(), cliOptions.targetPath) + : null; + const targetExists = targetProvided ? fs.existsSync(resolvedTargetPath) : null; const selection = resolveTargetSelection(process.cwd(), cliOptions); if (selection) { process.stdout.write(buildTargetSelectionDirective(selection) + '\n'); process.exit(0); } - const ctx = loadContext(process.cwd(), cliOptions); + const ctx = loadContext( + process.cwd(), + resolvedTargetPath ? { targetPath: resolvedTargetPath } : cliOptions, + ); const updateDirective = await computeUpdateDirective(); if (!ctx.hasProduct) { @@ -1308,11 +1335,6 @@ function hasTargetOption(options) { return !!(options && typeof options.targetPath === 'string' && options.targetPath.trim()); } -function pathExistsForTarget(cwd, targetPath) { - const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath); - return fs.existsSync(abs); -} - const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({ 'claude-code': ['.claude/settings.local.json', '.claude/settings.json'], codex: ['.codex/hooks.json'],