@ekanos/cli 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +81 -2
  2. package/dist/bin.js +14 -1
  3. package/dist/bin.js.map +1 -1
  4. package/dist/commands/dev.d.ts +4 -0
  5. package/dist/commands/dev.js +6 -20
  6. package/dist/commands/dev.js.map +1 -1
  7. package/dist/commands/init.d.ts +61 -0
  8. package/dist/commands/init.js +145 -18
  9. package/dist/commands/init.js.map +1 -1
  10. package/dist/commands/publish.d.ts +13 -0
  11. package/dist/commands/publish.js +38 -15
  12. package/dist/commands/publish.js.map +1 -1
  13. package/dist/commands/status.js +9 -2
  14. package/dist/commands/status.js.map +1 -1
  15. package/dist/commands/upgrade.d.ts +47 -0
  16. package/dist/commands/upgrade.js +546 -0
  17. package/dist/commands/upgrade.js.map +1 -0
  18. package/dist/commands/validate.d.ts +5 -0
  19. package/dist/commands/validate.js +20 -6
  20. package/dist/commands/validate.js.map +1 -1
  21. package/dist/context.d.ts +9 -0
  22. package/dist/context.js +9 -0
  23. package/dist/context.js.map +1 -1
  24. package/dist/copy-tree.d.ts +48 -0
  25. package/dist/copy-tree.js +55 -0
  26. package/dist/copy-tree.js.map +1 -0
  27. package/dist/delegate.d.ts +28 -0
  28. package/dist/delegate.js +136 -0
  29. package/dist/delegate.js.map +1 -0
  30. package/dist/harness-scaffold.d.ts +21 -6
  31. package/dist/harness-scaffold.js +8 -5
  32. package/dist/harness-scaffold.js.map +1 -1
  33. package/dist/index.js +40 -3
  34. package/dist/index.js.map +1 -1
  35. package/dist/package-manager.d.ts +10 -0
  36. package/dist/package-manager.js +32 -0
  37. package/dist/package-manager.js.map +1 -1
  38. package/dist/parse-argv.d.ts +7 -1
  39. package/dist/parse-argv.js +10 -2
  40. package/dist/parse-argv.js.map +1 -1
  41. package/dist/project-checks.d.ts +9 -0
  42. package/dist/project-checks.js +29 -0
  43. package/dist/project-checks.js.map +1 -1
  44. package/dist/project.d.ts +23 -0
  45. package/dist/project.js +66 -1
  46. package/dist/project.js.map +1 -1
  47. package/dist/publish-api.d.ts +1 -1
  48. package/dist/publish-api.js +2 -2
  49. package/dist/publish-api.js.map +1 -1
  50. package/dist/safe-fs.d.ts +1 -1
  51. package/dist/safe-fs.js.map +1 -1
  52. package/dist/schema-skew.d.ts +77 -0
  53. package/dist/schema-skew.js +163 -0
  54. package/dist/schema-skew.js.map +1 -0
  55. package/dist/sources-api.d.ts +1 -1
  56. package/dist/sources-api.js.map +1 -1
  57. package/dist/toolchain-api.d.ts +54 -0
  58. package/dist/toolchain-api.js +58 -0
  59. package/dist/toolchain-api.js.map +1 -0
  60. package/dist/toolchain-resolve.d.ts +26 -0
  61. package/dist/toolchain-resolve.js +100 -0
  62. package/dist/toolchain-resolve.js.map +1 -0
  63. package/dist/toolchain.d.ts +21 -0
  64. package/dist/toolchain.js +16 -0
  65. package/dist/toolchain.js.map +1 -0
  66. package/dist/update-notice.d.ts +32 -0
  67. package/dist/update-notice.js +180 -0
  68. package/dist/update-notice.js.map +1 -0
  69. package/dist/validate-findings.d.ts +12 -0
  70. package/dist/validate-findings.js +14 -1
  71. package/dist/validate-findings.js.map +1 -1
  72. package/package.json +1 -1
  73. package/templates/AGENTS.md.tmpl +81 -0
  74. package/templates/CLAUDE.md.tmpl +2 -1
  75. package/templates/claude-skill.md.tmpl +20 -2
  76. package/templates/docs/api-notes.md.tmpl +21 -0
  77. package/templates/mcp.json.tmpl +8 -0
  78. package/templates/skills/build-integration/SKILL.md +178 -0
  79. package/templates/skills/build-integration/references/api-docs-research.md +93 -0
  80. package/templates/skills/build-integration/references/api-wiring.md +94 -0
  81. package/templates/skills/build-integration/references/widget-design.md +132 -0
  82. package/templates/skills/polish-widgets/SKILL.md +139 -0
  83. package/templates/skills/polish-widgets/references/patterns.md +269 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../../src/commands/upgrade.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Finding } from '@ekanos/integration-schema';\nimport { spawn } from 'node:child_process';\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\n\nimport { resolveAuthEnvironment } from '../auth/session';\nimport { cliVersion } from '../compatibility';\nimport type { CliContext } from '../context';\nimport { networkError, preconditionError, validationError } from '../errors';\nimport { EXIT_CODES } from '../exit-codes';\nimport { readInstalledPackageManifest } from '../harness-scaffold';\nimport {\n type PackageManager,\n detectPackageManager,\n installExactArgs,\n lockfileName,\n} from '../package-manager';\nimport { loadProject } from '../project';\nimport { classifyPath, writeFileNoFollow } from '../safe-fs';\nimport { renderTemplate, toDisplayName } from '../templates';\nimport {\n TOOLCHAIN_PACKAGES,\n type ToolchainKey,\n type ToolchainVersions,\n} from '../toolchain';\nimport { resolveToolchainTarget } from '../toolchain-resolve';\nimport { collectAllFindings } from '../validate-findings';\nimport {\n AGENT_CONTEXT_FILES,\n DOCS_GROUNDING_FILES,\n copyBundledSkillTrees,\n} from './init';\nimport { promptYesNo } from './publish';\n\n/**\n * `ekanos upgrade` — move a project's toolchain to a coherent target version\n * and VERIFY it before calling the job done.\n *\n * The whole point of this verb is that it cannot leave a project worse off\n * than it found it: `package.json` and the lockfile are snapshotted before\n * anything installs, the target set is only ever a coherence-checked whole\n * (never \"latest of each, independently\" — see `toolchain-resolve.ts`), and\n * any failure from install through validate through the project's own test\n * script rolls everything back to the snapshot and says exactly which step\n * failed. A partner (or an agent) running this unattended should never end up\n * with a half-upgraded, uninstallable project.\n */\n\nexport interface UpgradeArgs {\n dir: string;\n host?: string;\n env: Record<string, string | undefined>;\n /** Report current vs. target and exit 0. Never mutates anything. */\n check: boolean;\n /** Skip the interactive confirm; required to mutate outside a TTY. */\n yes: boolean;\n /** Skip the post-install validate + test-script verification. */\n noVerify: boolean;\n stdinIsTTY?: boolean;\n confirm?: (question: string) => Promise<boolean>;\n}\n\n/** Injectable seams so tests can exercise install/verify/rollback without shelling out. */\nexport interface UpgradeDeps {\n resolveTarget?: typeof resolveToolchainTarget;\n runInstall?: (\n argv: string[],\n bin: string,\n cwd: string,\n ) => Promise<{ code: number; output: string }>;\n runValidateStep?: (\n projectDir: string,\n ) => Promise<{ ok: boolean; findings?: Finding[] }>;\n runTestStep?: (\n projectDir: string,\n pm: PackageManager,\n ) => Promise<{ code: number; output: string }>;\n}\n\ninterface DeltaEntry {\n current: string | null;\n target: string;\n changed: boolean;\n}\n\nexport async function runUpgrade(\n ctx: CliContext,\n args: UpgradeArgs,\n deps: UpgradeDeps = {},\n): Promise<number> {\n const loaded = loadProject(args.dir);\n const pm = detectPackageManager(loaded.projectDir);\n\n // Captured BEFORE anything installs: the templates the project's CURRENT\n // (pre-upgrade) local `@ekanos/cli` ships, which is what actually\n // generated the agent-context files on disk — not necessarily this\n // process's own bundled templates, since `upgrade` is a machine-level verb\n // that never delegates and is routinely invoked via a DIFFERENT (global)\n // install. See `regenerateAgentContextFiles`.\n const oldAgentTemplates = captureAgentTemplates(loaded.projectDir);\n\n const current = readCurrentToolchain(loaded.projectDir);\n\n const session = resolveSessionQuietly(ctx, args);\n const resolveTarget = deps.resolveTarget ?? resolveToolchainTarget;\n const resolution = await resolveTarget({\n host: session?.host,\n session: session?.session,\n });\n\n if (resolution.status === 'unavailable') {\n throw networkError(\n 'Could not resolve a toolchain target from the Fusion host or the ' +\n 'npm registry.',\n 'Check connectivity and retry. If this deployment has no ' +\n '\"/api/partner/toolchain\" route yet, the npm registry is the ' +\n 'fallback and a failure there means npm itself is unreachable.',\n );\n }\n\n if (resolution.status === 'incoherent') {\n throw preconditionError(\n `The resolved toolchain versions do not form an installable set: ` +\n resolution.detail,\n 'Retry in a moment — this is almost always a multi-package publish ' +\n 'still in flight, and it self-resolves once every package finishes ' +\n 'publishing.',\n );\n }\n\n const target = resolution.target;\n const deltas = computeDeltas(current, target);\n\n if (args.check) {\n return ctx.succeed(\n {\n source: resolution.source,\n current,\n target,\n deltas,\n ...(resolution.source === 'host'\n ? { minimum: resolution.minimum, notes: resolution.notes }\n : {}),\n },\n checkSummary(resolution.source, deltas),\n );\n }\n\n const changedKeys = (Object.keys(deltas) as ToolchainKey[]).filter(\n (key) => deltas[key]!.changed,\n );\n\n if (changedKeys.length === 0) {\n return ctx.succeed(\n { source: resolution.source, current, target, deltas },\n `upgrade: already at the target toolchain (source: ${resolution.source}).`,\n );\n }\n\n const isHuman = !ctx.jsonMode;\n const stdinIsTTY = args.stdinIsTTY ?? process.stdin.isTTY === true;\n const canPrompt = isHuman && stdinIsTTY;\n\n if (!args.yes) {\n if (!canPrompt) {\n throw validationError(\n 'Refusing to modify the project non-interactively without --yes.',\n 'Pass \"ekanos upgrade --yes\" to proceed, or \"ekanos upgrade --check\" ' +\n 'to preview the target with no changes.',\n );\n }\n\n ctx.log(planSummary(resolution.source, deltas));\n const confirm = args.confirm ?? promptYesNo;\n const confirmed = await confirm('Upgrade now? [y/N]');\n\n if (!confirmed) {\n throw validationError(\n 'Upgrade was not confirmed.',\n 'Re-run \"ekanos upgrade --yes\" to proceed non-interactively, or ' +\n '\"ekanos upgrade --check\" to preview with no changes. Nothing was ' +\n 'modified.',\n );\n }\n }\n\n const packageJsonPath = path.join(loaded.projectDir, 'package.json');\n const manifest = readManifest(packageJsonPath);\n const specs = buildInstallSpecs(manifest, target, changedKeys);\n\n if (specs.length === 0) {\n return ctx.succeed(\n { source: resolution.source, current, target, deltas },\n 'upgrade: none of the changed packages are direct dependencies of ' +\n 'this project — nothing to install directly. Their versions follow ' +\n 'from whichever direct dependency pins them.',\n );\n }\n\n const snapshot = snapshotProject(loaded.projectDir, pm);\n\n try {\n const runInstall = deps.runInstall ?? defaultRunInstall;\n\n for (const group of [\n specs.filter((s) => !s.dev),\n specs.filter((s) => s.dev),\n ]) {\n if (group.length === 0) continue;\n\n const argv = installExactArgs(\n pm,\n group.map((s) => `${s.name}@${s.version}`),\n group[0]!.dev,\n );\n ctx.log(`Running \"${pm.bin} ${argv.join(' ')}\" in ${loaded.projectDir}…`);\n const result = await runInstall(argv, pm.bin, loaded.projectDir);\n\n if (result.code !== 0) {\n return await failAndRollback(\n ctx,\n snapshot,\n pm,\n loaded.projectDir,\n runInstall,\n {\n step: 'install',\n exitCode: EXIT_CODES.PRECONDITION_FAILED,\n message: `\"${pm.bin} ${argv.join(' ')}\" exited with code ${result.code}.`,\n output: result.output,\n },\n );\n }\n }\n\n if (!args.noVerify) {\n const runValidateStep = deps.runValidateStep ?? defaultRunValidateStep;\n const validateResult = await runValidateStep(loaded.projectDir);\n\n if (!validateResult.ok) {\n return await failAndRollback(\n ctx,\n snapshot,\n pm,\n loaded.projectDir,\n runInstall,\n {\n step: 'validate',\n exitCode: EXIT_CODES.VALIDATION,\n message:\n 'The upgraded toolchain fails \"ekanos validate\" against this ' +\n \"project's own integration definition.\",\n output: JSON.stringify(validateResult.findings ?? [], null, 2),\n },\n );\n }\n\n const runTestStep = deps.runTestStep ?? defaultRunTestStep;\n const testResult = await runTestStep(loaded.projectDir, pm);\n\n if (testResult.code !== 0) {\n return await failAndRollback(\n ctx,\n snapshot,\n pm,\n loaded.projectDir,\n runInstall,\n {\n step: 'test',\n exitCode: testResult.code,\n message: `The project's test script exited with code ${testResult.code} on the upgraded toolchain.`,\n output: testResult.output,\n },\n );\n }\n }\n } finally {\n snapshot.dispose();\n }\n\n const newTemplatesDir = path.join(\n loaded.projectDir,\n 'node_modules',\n '@ekanos',\n 'cli',\n 'templates',\n );\n\n const regenerated = regenerateAgentContextFiles({\n projectDir: loaded.projectDir,\n slug: loaded.primary.slug,\n oldTemplates: oldAgentTemplates,\n newTemplatesDir,\n });\n\n // The regenerated AGENTS.md and skills may now REFERENCE artifacts this\n // project predates (docs/api-notes.md, .mcp.json, bundled skill trees) —\n // instructions pointing at files that don't exist are exactly the stale-docs\n // failure the docs-grounding feature closes, so create what's missing.\n // Create-if-missing ONLY: these files are partner/agent-authored after the\n // scaffold and are never regenerated.\n const scaffolded = scaffoldMissingDocsArtifacts({\n projectDir: loaded.projectDir,\n slug: loaded.primary.slug,\n newTemplatesDir,\n });\n\n // `specs` only ever covers DIRECT dependencies (see `buildInstallSpecs`) —\n // a changed key with no spec is transitive and was never actually\n // installed here, so the report (and the human summary) must say so\n // explicitly rather than implying every changed key was upgraded. This\n // matters most for exactly the scenario this PR's own delegation feature\n // targets: a globally-installed `ekanos` with no local `@ekanos/cli`\n // devDependency, where `cli` changes in `deltas` but the running global\n // binary itself is untouched by this command.\n const installedKeys = specs.map((spec) => spec.key);\n const skippedKeys = changedKeys.filter((key) => !installedKeys.includes(key));\n\n return ctx.succeed(\n {\n source: resolution.source,\n current,\n target,\n deltas,\n installed: installedKeys,\n skippedTransitive: skippedKeys,\n regenerated,\n scaffolded,\n },\n successSummary(installedKeys, skippedKeys, deltas, regenerated, scaffolded),\n );\n}\n\n/**\n * Best-effort host/session resolution — NEVER throws. `upgrade` must work for\n * a partner who has never logged in at all (pure npm-registry flow), so the\n * throwing `resolveAuthEnvironment`/`requireSession` machinery is unusable\n * here; this mirrors the tolerant lookup `update-notice.ts` uses.\n */\nfunction resolveSessionQuietly(\n ctx: CliContext,\n args: UpgradeArgs,\n): { host: string; session: { accessToken: string } } | null {\n try {\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.dir,\n });\n const session = env.store.read(env.host);\n return session ? { host: env.host, session } : null;\n } catch {\n return null;\n }\n}\n\nfunction readCurrentToolchain(\n projectDir: string,\n): Record<ToolchainKey, string | null> {\n const result = {} as Record<ToolchainKey, string | null>;\n\n for (const [key, pkg] of Object.entries(TOOLCHAIN_PACKAGES) as [\n ToolchainKey,\n string,\n ][]) {\n const installed = readInstalledPackageManifest(projectDir, pkg);\n result[key] = installed?.version ?? (key === 'cli' ? cliVersion() : null);\n }\n\n return result;\n}\n\nfunction computeDeltas(\n current: Record<ToolchainKey, string | null>,\n target: ToolchainVersions,\n): Record<ToolchainKey, DeltaEntry> {\n const keys = Object.keys(TOOLCHAIN_PACKAGES) as ToolchainKey[];\n const result = {} as Record<ToolchainKey, DeltaEntry>;\n\n for (const key of keys) {\n result[key] = {\n current: current[key],\n target: target[key],\n changed: current[key] !== target[key],\n };\n }\n\n return result;\n}\n\ninterface InstallSpec {\n key: ToolchainKey;\n name: string;\n version: string;\n dev: boolean;\n}\n\n/**\n * Only packages the project depends on DIRECTLY are installed explicitly —\n * `@ekanos/integration-schema` and often `@ekanos/harness` are transitive\n * (pulled in by `@ekanos/sdk`), and forcing an exact transitive version would\n * need a package-manager-specific override mechanism this CLI does not\n * manage. Their versions follow from whichever direct dependency pins them,\n * which is exactly what the coherent target guarantees is already correct.\n */\nfunction buildInstallSpecs(\n manifest: Record<string, unknown>,\n target: ToolchainVersions,\n changedKeys: readonly ToolchainKey[],\n): InstallSpec[] {\n const deps = (manifest.dependencies ?? {}) as Record<string, unknown>;\n const devDeps = (manifest.devDependencies ?? {}) as Record<string, unknown>;\n\n const specs: InstallSpec[] = [];\n\n for (const key of changedKeys) {\n const pkg = TOOLCHAIN_PACKAGES[key];\n if (pkg in deps) {\n specs.push({ key, name: pkg, version: target[key], dev: false });\n } else if (pkg in devDeps) {\n specs.push({ key, name: pkg, version: target[key], dev: true });\n }\n }\n\n return specs;\n}\n\nfunction readManifest(packageJsonPath: string): Record<string, unknown> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));\n } catch (error) {\n throw preconditionError(\n `Could not read ${packageJsonPath}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n 'Ensure the project has a valid package.json, then re-run \"ekanos upgrade\".',\n );\n }\n if (typeof parsed !== 'object' || parsed === null) {\n throw preconditionError(\n `${packageJsonPath} does not contain a JSON object.`,\n 'Fix package.json, then re-run \"ekanos upgrade\".',\n );\n }\n return parsed as Record<string, unknown>;\n}\n\ninterface Snapshot {\n restore: () => void;\n dispose: () => void;\n}\n\n/**\n * Copy `package.json` and the lockfile to a temp directory before anything\n * installs. `restore()` copies them back (and removes a lockfile that did not\n * exist before, if the failed install created one); `dispose()` cleans up the\n * temp copies once the run is over, success or failure.\n */\nfunction snapshotProject(projectDir: string, pm: PackageManager): Snapshot {\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ekanos-upgrade-'));\n const packageJsonPath = path.join(projectDir, 'package.json');\n\n // bun has shipped both a text (`bun.lock`) and a legacy binary\n // (`bun.lockb`) lockfile; snapshot whichever is actually present.\n const lockCandidates =\n pm.name === 'bun' ? ['bun.lock', 'bun.lockb'] : [lockfileName(pm)];\n const lockName = lockCandidates.find((name) =>\n fs.existsSync(path.join(projectDir, name)),\n );\n const lockfilePath = lockName\n ? path.join(projectDir, lockName)\n : path.join(projectDir, lockfileName(pm));\n const lockfileExisted = lockName !== undefined;\n\n fs.copyFileSync(packageJsonPath, path.join(tmpDir, 'package.json'));\n if (lockfileExisted) {\n fs.copyFileSync(lockfilePath, path.join(tmpDir, 'lockfile.snapshot'));\n }\n\n return {\n restore: () => {\n fs.copyFileSync(path.join(tmpDir, 'package.json'), packageJsonPath);\n if (lockfileExisted) {\n fs.copyFileSync(path.join(tmpDir, 'lockfile.snapshot'), lockfilePath);\n } else if (fs.existsSync(lockfilePath)) {\n fs.rmSync(lockfilePath);\n }\n },\n dispose: () => {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n },\n };\n}\n\ninterface StepFailure {\n step: 'install' | 'validate' | 'test';\n exitCode: number;\n message: string;\n output: string;\n}\n\n/**\n * The one rollback path every failure funnels through: restore the snapshot,\n * reinstall to put `node_modules` back in sync with the restored\n * `package.json`/lockfile, and report exactly which step failed and what it\n * said — with an explicit statement that the project was restored, so a\n * partner (or an agent) never has to wonder whether they are looking at a\n * half-upgraded tree.\n */\nasync function failAndRollback(\n ctx: CliContext,\n snapshot: Snapshot,\n pm: PackageManager,\n projectDir: string,\n runInstall: (\n argv: string[],\n bin: string,\n cwd: string,\n ) => Promise<{ code: number; output: string }>,\n failure: StepFailure,\n): Promise<number> {\n ctx.warn(\n `ekanos: the \"${failure.step}\" step failed — rolling back package.json ` +\n 'and the lockfile, then reinstalling to restore node_modules…',\n );\n\n snapshot.restore();\n // The SAME injectable installer seam used for the forward install — a test\n // that mocks it never shells out for the rollback path either.\n const restore = await runInstall(['install'], pm.bin, projectDir);\n\n return ctx.failWith({\n code: `upgrade_${failure.step}_failed`,\n message: failure.message,\n hint:\n `The project HAS BEEN RESTORED to its pre-upgrade state (package.json, ` +\n `lockfile and node_modules all rolled back${\n restore.code === 0\n ? ''\n : ' — though the restore install itself also failed with code ' +\n `${restore.code}; run \"${pm.bin} install\" by hand to finish ` +\n 'repairing node_modules'\n }). Read the output in data.output, fix the underlying issue, and ` +\n 're-run \"ekanos upgrade\".',\n exitCode: failure.exitCode,\n data: { step: failure.step, output: failure.output },\n });\n}\n\nasync function defaultRunInstall(\n argv: string[],\n bin: string,\n cwd: string,\n): Promise<{ code: number; output: string }> {\n return new Promise((resolve, reject) => {\n const child = spawn(bin, argv, { cwd, stdio: ['ignore', 'pipe', 'pipe'] });\n let output = '';\n child.stdout?.on('data', (chunk: Buffer) => {\n output += chunk.toString();\n });\n child.stderr?.on('data', (chunk: Buffer) => {\n output += chunk.toString();\n });\n child.on('error', reject);\n child.on('close', (code) => resolve({ code: code ?? 1, output }));\n });\n}\n\nasync function defaultRunValidateStep(\n projectDir: string,\n): Promise<{ ok: boolean; findings?: Finding[] }> {\n const loaded = loadProject(projectDir);\n const findings = await collectAllFindings(loaded);\n const errorCount = findings.filter((f) => f.severity === 'error').length;\n return { ok: errorCount === 0, findings };\n}\n\nasync function defaultRunTestStep(\n projectDir: string,\n pm: PackageManager,\n): Promise<{ code: number; output: string }> {\n return new Promise((resolve, reject) => {\n const child = spawn(pm.bin, ['run', 'test'], {\n cwd: projectDir,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n let output = '';\n child.stdout?.on('data', (chunk: Buffer) => {\n output += chunk.toString();\n });\n child.stderr?.on('data', (chunk: Buffer) => {\n output += chunk.toString();\n });\n child.on('error', reject);\n child.on('close', (code) => resolve({ code: code ?? 1, output }));\n });\n}\n\ninterface RegenerationReport {\n updated: string[];\n unchanged: string[];\n needsAttention: string[];\n}\n\n/**\n * Read every `AGENT_CONTEXT_FILES` template source out of the project's\n * CURRENT (pre-upgrade) local `@ekanos/cli` install, before anything installs\n * over it. Missing entirely (the package isn't a resolvable local dependency)\n * just means every file below is treated conservatively — see\n * `regenerateAgentContextFiles`.\n */\nfunction captureAgentTemplates(projectDir: string): Map<string, string> {\n const templatesDir = path.join(\n projectDir,\n 'node_modules',\n '@ekanos',\n 'cli',\n 'templates',\n );\n const captured = new Map<string, string>();\n\n for (const { template } of AGENT_CONTEXT_FILES) {\n const templatePath = path.join(templatesDir, template);\n try {\n captured.set(template, fs.readFileSync(templatePath, 'utf8'));\n } catch {\n // Not resolvable — left out of the map, handled as \"can't verify\" below.\n }\n }\n\n return captured;\n}\n\n/**\n * Create the docs-grounding artifacts a pre-0.1.7 project is missing, from\n * the NEWLY installed CLI's bundled templates: `docs/api-notes.md`,\n * `.mcp.json`, and any bundled skill trees. Strictly create-if-missing —\n * an existing file is the partner's (api-notes is agent-authored, .mcp.json\n * may carry local servers) and is never touched, force or not. Writes go\n * through safe-fs like init's own scaffold pass.\n */\nfunction scaffoldMissingDocsArtifacts(params: {\n projectDir: string;\n slug: string;\n newTemplatesDir: string;\n}): { created: string[]; needsAttention: string[] } {\n const vars = { SLUG: params.slug, DISPLAY_NAME: toDisplayName(params.slug) };\n const created: string[] = [];\n const needsAttention: string[] = [];\n\n // This runs AFTER snapshot.dispose(): the toolchain upgrade has already\n // succeeded and there is no rollback left to protect. A refused write here\n // (a symlinked `docs/` or `.claude` ancestor — safe-fs throws rather than\n // follow it) must therefore be REPORTED, never thrown — the same stance\n // `regenerateAgentContextFiles` takes, for the same reason: a hard failure\n // now would misreport a successful upgrade.\n for (const { template, dest } of DOCS_GROUNDING_FILES) {\n const destPath = path.join(params.projectDir, dest);\n const kind = classifyPath(destPath);\n // A real file or directory is the partner's — skipping it quietly IS the\n // contract. A symlink (or fifo, socket, …) is not \"already there\": it is\n // a path we refuse to write through, and silence would hide that — same\n // reporting stance as regenerateAgentContextFiles.\n if (kind === 'file' || kind === 'directory') continue;\n if (kind !== 'missing') {\n needsAttention.push(dest);\n continue;\n }\n\n const templatePath = path.join(params.newTemplatesDir, template);\n if (!fs.existsSync(templatePath)) continue; // older target toolchain\n\n try {\n writeFileNoFollow(\n params.projectDir,\n destPath,\n renderTemplate(fs.readFileSync(templatePath, 'utf8'), vars),\n );\n created.push(dest);\n } catch {\n needsAttention.push(dest);\n }\n }\n\n try {\n // 'collect': a refused file is recorded and the walk continues, so files\n // that DID land — in this skill tree or an earlier one — stay in\n // `created` instead of vanishing when a later write refuses.\n const skills = copyBundledSkillTrees(\n params.projectDir,\n false,\n path.join(params.newTemplatesDir, 'skills'),\n 'collect',\n );\n created.push(...skills.created);\n needsAttention.push(...skills.refused);\n } catch {\n // Last resort (an unreadable templates dir, not a refused write — those\n // are collected above).\n needsAttention.push('.claude/skills');\n }\n\n return { created, needsAttention };\n}\n\n/**\n * Regenerate `AGENTS.md`, `CLAUDE.md` and the Claude Code skill from the\n * NEWLY installed CLI's bundled templates — using the exact byte-comparison\n * discipline `harness-scaffold.ts` applies to the dev harness shell: a file\n * whose current bytes match what the project's PRE-upgrade local CLI would\n * have rendered (captured in `oldTemplates`, before install ran) is\n * untouched by the partner and safe to regenerate; anything else has been\n * edited and is left alone, reported so a human can merge it by hand. A\n * stale AGENTS.md teaches an agent yesterday's contract — that is as harmful\n * as a stale binary, which is why this runs on every successful upgrade\n * rather than only on `init`.\n */\nfunction regenerateAgentContextFiles(params: {\n projectDir: string;\n slug: string;\n oldTemplates: Map<string, string>;\n newTemplatesDir: string;\n}): RegenerationReport {\n const vars = { SLUG: params.slug, DISPLAY_NAME: toDisplayName(params.slug) };\n const updated: string[] = [];\n const unchanged: string[] = [];\n const needsAttention: string[] = [];\n\n for (const { template, dest } of AGENT_CONTEXT_FILES) {\n const destPath = path.join(params.projectDir, dest);\n const kind = classifyPath(destPath);\n if (kind === 'missing') continue; // never scaffolded — nothing to upgrade\n if (kind !== 'file') {\n // A symlink (or anything else odd) is not ours to write through — same\n // stance as init's scaffold pass, but reported instead of thrown: the\n // upgrade itself already succeeded, so surface it for a human.\n needsAttention.push(dest);\n continue;\n }\n\n const current = fs.readFileSync(destPath, 'utf8');\n const oldSource = params.oldTemplates.get(template);\n\n const oldRendered =\n oldSource === undefined ? null : renderTemplate(oldSource, vars);\n\n if (oldRendered === null || current !== oldRendered) {\n needsAttention.push(dest);\n continue;\n }\n\n const newTemplatePath = path.join(params.newTemplatesDir, template);\n if (!fs.existsSync(newTemplatePath)) continue;\n\n const newRendered = renderTemplate(\n fs.readFileSync(newTemplatePath, 'utf8'),\n vars,\n );\n\n if (newRendered === current) {\n unchanged.push(dest);\n continue;\n }\n\n try {\n writeFileNoFollow(params.projectDir, destPath, newRendered);\n updated.push(dest);\n } catch {\n // A symlinked ANCESTOR (.claude centralized across projects) passes the\n // leaf classifyPath check above but is refused by the guarded write —\n // and this runs post-rollback, so a throw here would misreport a\n // successful toolchain upgrade as failed. Report it instead, like every\n // other post-rollback write path in this file.\n needsAttention.push(dest);\n }\n }\n\n return { updated, unchanged, needsAttention };\n}\n\nfunction checkSummary(\n source: 'host' | 'npm',\n deltas: Record<ToolchainKey, DeltaEntry>,\n): string {\n const lines = [`upgrade --check: target resolved from ${source}.`];\n for (const [key, delta] of Object.entries(deltas)) {\n lines.push(\n ` ${key}: ${delta.current ?? '(not installed)'} → ${delta.target}` +\n (delta.changed ? '' : ' (already current)'),\n );\n }\n return lines.join('\\n');\n}\n\nfunction planSummary(\n source: string,\n deltas: Record<ToolchainKey, DeltaEntry>,\n): string {\n const changing = (Object.entries(deltas) as [ToolchainKey, DeltaEntry][])\n .filter(([, d]) => d.changed)\n .map(\n ([key, d]) => `${key} ${d.current ?? '(not installed)'} → ${d.target}`,\n );\n return (\n `Upgrading (target resolved from ${source}):\\n` +\n changing.map((line) => ` ${line}`).join('\\n')\n );\n}\n\nfunction successSummary(\n installedKeys: readonly ToolchainKey[],\n skippedKeys: readonly ToolchainKey[],\n deltas: Record<ToolchainKey, DeltaEntry>,\n regenerated: RegenerationReport,\n scaffolded: { created: string[]; needsAttention: string[] },\n): string {\n const installed = installedKeys\n .map((key) => `${key} → ${deltas[key]!.target}`)\n .join(', ');\n const parts = [\n installed.length > 0\n ? `upgrade: OK — installed ${installed}.`\n : 'upgrade: OK — nothing was a direct dependency of this project, so nothing was installed here.',\n ];\n if (skippedKeys.length > 0) {\n // These changed in the resolved target but are transitive (or, for\n // \"cli\", the invoked binary was global with no local devDependency) —\n // never touched by the install step above.\n const skipped = skippedKeys\n .map((key) => `${key} → ${deltas[key]!.target}`)\n .join(', ');\n parts.push(\n `Not installed directly (transitive or not a local dependency): ${skipped}.`,\n );\n }\n if (regenerated.updated.length > 0) {\n parts.push(`regenerated ${regenerated.updated.join(', ')}.`);\n }\n if (scaffolded.created.length > 0) {\n parts.push(\n `created ${scaffolded.created.join(', ')} (new in this toolchain — see ` +\n `AGENTS.md's \"API documentation\" section).`,\n );\n }\n if (scaffolded.needsAttention.length > 0) {\n parts.push(\n `could not scaffold ${scaffolded.needsAttention.join(', ')} (a path ` +\n 'component is a symlink or unwritable) — the toolchain upgrade itself ' +\n 'succeeded. Fix the path, then run \"ekanos init --slug <slug> --dir .\" ' +\n 'WITHOUT --force: init is per-file idempotent and creates only what ' +\n 'is missing. Never use --force here — it overwrites every scaffolded ' +\n 'file, including src/integration.ts.',\n );\n }\n if (regenerated.needsAttention.length > 0) {\n parts.push(\n `${regenerated.needsAttention.join(', ')} were locally modified — ` +\n 'left untouched, merge the new template by hand.',\n );\n }\n return parts.join(' ');\n}\n"]}
@@ -3,6 +3,7 @@ import type { ExitCode } from '../exit-codes.js';
3
3
  export interface ValidateArgs {
4
4
  /** Project directory (contains ekanos.json). Defaults to cwd. */
5
5
  dir: string;
6
+ env?: Record<string, string | undefined>;
6
7
  }
7
8
  /**
8
9
  * `validate` — parse the declaration with the REAL zod schemas from
@@ -13,5 +14,9 @@ export interface ValidateArgs {
13
14
  *
14
15
  * The findings pass itself lives in `validate-findings.ts`, shared with
15
16
  * `publish` so the publish gate cannot drift from what validate checks.
17
+ *
18
+ * Runs the schema-skew gate FIRST, before anything else — see
19
+ * `schema-skew.ts` for why a newer project schema than the CLI bundles makes
20
+ * every finding below it unreliable.
16
21
  */
17
22
  export declare function runValidate(ctx: CliContext, args: ValidateArgs): Promise<ExitCode>;
@@ -1,5 +1,7 @@
1
1
  import { validationError } from '../errors.js';
2
2
  import { loadProject } from '../project.js';
3
+ import { assertSchemaNotSkewed } from '../schema-skew.js';
4
+ import { announceUpdate, checkForUpdate } from '../update-notice.js';
3
5
  import { collectAllFindings } from '../validate-findings.js';
4
6
  /**
5
7
  * `validate` — parse the declaration with the REAL zod schemas from
@@ -10,22 +12,34 @@ import { collectAllFindings } from '../validate-findings.js';
10
12
  *
11
13
  * The findings pass itself lives in `validate-findings.ts`, shared with
12
14
  * `publish` so the publish gate cannot drift from what validate checks.
15
+ *
16
+ * Runs the schema-skew gate FIRST, before anything else — see
17
+ * `schema-skew.ts` for why a newer project schema than the CLI bundles makes
18
+ * every finding below it unreliable.
13
19
  */
14
20
  export async function runValidate(ctx, args) {
21
+ var _a;
15
22
  const loaded = loadProject(args.dir);
23
+ const skew = assertSchemaNotSkewed(ctx, loaded.projectDir);
24
+ const notice = await checkForUpdate({ env: (_a = args.env) !== null && _a !== void 0 ? _a : process.env });
25
+ announceUpdate(ctx, notice);
16
26
  const slugs = loaded.integrations.map((i) => i.slug).join(', ');
17
27
  ctx.log(`Validating ${loaded.integrations.length} integration` +
18
28
  `${loaded.integrations.length === 1 ? '' : 's'} (${slugs})…`);
19
29
  const findings = await collectAllFindings(loaded);
20
30
  const errorCount = findings.filter((f) => f.severity === 'error').length;
21
- const data = {
22
- slug: loaded.primary.slug,
23
- integrations: loaded.integrations.map((i) => ({
31
+ const data = Object.assign(Object.assign({ slug: loaded.primary.slug, integrations: loaded.integrations.map((i) => ({
24
32
  slug: i.slug,
25
33
  entry: i.entry,
26
- })),
27
- findings,
28
- };
34
+ })), findings }, (skew.status === 'project-older'
35
+ ? {
36
+ schemaSkew: {
37
+ status: skew.status,
38
+ cliSchemaVersion: skew.cliSchemaVersion,
39
+ projectSchemaVersion: skew.projectSchemaVersion,
40
+ },
41
+ }
42
+ : {})), (notice ? { toolchain: notice } : {}));
29
43
  if (errorCount > 0) {
30
44
  return ctx.fail(validationError(`${errorCount} validation finding${errorCount === 1 ? '' : 's'}.`, 'Resolve the error-severity findings in data.findings, then re-run ' +
31
45
  '"ekanos validate".'), data);
@@ -1 +1 @@
1
- {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE5C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAO1D;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,GAAe,EACf,IAAkB;IAElB,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACrC,MAAM,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChE,GAAG,CAAC,GAAG,CACL,cAAc,MAAM,CAAC,YAAY,CAAC,MAAM,cAAc;QACpD,GAAG,MAAM,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,IAAI,CAC/D,CAAC;IAEF,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAElD,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;IACzE,MAAM,IAAI,GAAG;QACX,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI;QACzB,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC5C,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,KAAK,EAAE,CAAC,CAAC,KAAK;SACf,CAAC,CAAC;QACH,QAAQ;KACT,CAAC;IAEF,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;QACnB,OAAO,GAAG,CAAC,IAAI,CACb,eAAe,CACb,GAAG,UAAU,sBAAsB,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,EACjE,oEAAoE;YAClE,oBAAoB,CACvB,EACD,IAAI,CACL,CAAC;IACJ,CAAC;IAED,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,kCAAkC,KAAK,GAAG,CAAC,CAAC;AACvE,CAAC","sourcesContent":["import type { CliContext } from '../context';\nimport { validationError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport { loadProject } from '../project';\nimport { collectAllFindings } from '../validate-findings';\n\nexport interface ValidateArgs {\n /** Project directory (contains ekanos.json). Defaults to cwd. */\n dir: string;\n}\n\n/**\n * `validate` — parse the declaration with the REAL zod schemas from\n * `@ekanos/integration-schema` (never a reimplementation), gather structured\n * findings, and add project-level integrity checks (the vitest inline-SDK\n * check). Exit 0 with `findings: []` when clean; exit 3 when any error-severity\n * finding is present, with the findings carried in the envelope's `data`.\n *\n * The findings pass itself lives in `validate-findings.ts`, shared with\n * `publish` so the publish gate cannot drift from what validate checks.\n */\nexport async function runValidate(\n ctx: CliContext,\n args: ValidateArgs,\n): Promise<ExitCode> {\n const loaded = loadProject(args.dir);\n const slugs = loaded.integrations.map((i) => i.slug).join(', ');\n ctx.log(\n `Validating ${loaded.integrations.length} integration` +\n `${loaded.integrations.length === 1 ? '' : 's'} (${slugs})…`,\n );\n\n const findings = await collectAllFindings(loaded);\n\n const errorCount = findings.filter((f) => f.severity === 'error').length;\n const data = {\n slug: loaded.primary.slug,\n integrations: loaded.integrations.map((i) => ({\n slug: i.slug,\n entry: i.entry,\n })),\n findings,\n };\n\n if (errorCount > 0) {\n return ctx.fail(\n validationError(\n `${errorCount} validation finding${errorCount === 1 ? '' : 's'}.`,\n 'Resolve the error-severity findings in data.findings, then re-run ' +\n '\"ekanos validate\".',\n ),\n data,\n );\n }\n\n return ctx.succeed(data, `validate: OK — no findings for ${slugs}.`);\n}\n"]}
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE5C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAQ1D;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,GAAe,EACf,IAAkB;;IAElB,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAErC,MAAM,IAAI,GAAG,qBAAqB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IAE3D,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,EAAE,GAAG,EAAE,MAAA,IAAI,CAAC,GAAG,mCAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACtE,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAE5B,MAAM,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChE,GAAG,CAAC,GAAG,CACL,cAAc,MAAM,CAAC,YAAY,CAAC,MAAM,cAAc;QACpD,GAAG,MAAM,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,IAAI,CAC/D,CAAC;IAEF,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAElD,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;IACzE,MAAM,IAAI,iCACR,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,EACzB,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC5C,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,KAAK,EAAE,CAAC,CAAC,KAAK;SACf,CAAC,CAAC,EACH,QAAQ,IACL,CAAC,IAAI,CAAC,MAAM,KAAK,eAAe;QACjC,CAAC,CAAC;YACE,UAAU,EAAE;gBACV,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;gBACvC,oBAAoB,EAAE,IAAI,CAAC,oBAAoB;aAChD;SACF;QACH,CAAC,CAAC,EAAE,CAAC,GACJ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CACzC,CAAC;IAEF,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;QACnB,OAAO,GAAG,CAAC,IAAI,CACb,eAAe,CACb,GAAG,UAAU,sBAAsB,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,EACjE,oEAAoE;YAClE,oBAAoB,CACvB,EACD,IAAI,CACL,CAAC;IACJ,CAAC;IAED,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,kCAAkC,KAAK,GAAG,CAAC,CAAC;AACvE,CAAC","sourcesContent":["import type { CliContext } from '../context';\nimport { validationError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport { loadProject } from '../project';\nimport { assertSchemaNotSkewed } from '../schema-skew';\nimport { announceUpdate, checkForUpdate } from '../update-notice';\nimport { collectAllFindings } from '../validate-findings';\n\nexport interface ValidateArgs {\n /** Project directory (contains ekanos.json). Defaults to cwd. */\n dir: string;\n env?: Record<string, string | undefined>;\n}\n\n/**\n * `validate` — parse the declaration with the REAL zod schemas from\n * `@ekanos/integration-schema` (never a reimplementation), gather structured\n * findings, and add project-level integrity checks (the vitest inline-SDK\n * check). Exit 0 with `findings: []` when clean; exit 3 when any error-severity\n * finding is present, with the findings carried in the envelope's `data`.\n *\n * The findings pass itself lives in `validate-findings.ts`, shared with\n * `publish` so the publish gate cannot drift from what validate checks.\n *\n * Runs the schema-skew gate FIRST, before anything else — see\n * `schema-skew.ts` for why a newer project schema than the CLI bundles makes\n * every finding below it unreliable.\n */\nexport async function runValidate(\n ctx: CliContext,\n args: ValidateArgs,\n): Promise<ExitCode> {\n const loaded = loadProject(args.dir);\n\n const skew = assertSchemaNotSkewed(ctx, loaded.projectDir);\n\n const notice = await checkForUpdate({ env: args.env ?? process.env });\n announceUpdate(ctx, notice);\n\n const slugs = loaded.integrations.map((i) => i.slug).join(', ');\n ctx.log(\n `Validating ${loaded.integrations.length} integration` +\n `${loaded.integrations.length === 1 ? '' : 's'} (${slugs})…`,\n );\n\n const findings = await collectAllFindings(loaded);\n\n const errorCount = findings.filter((f) => f.severity === 'error').length;\n const data = {\n slug: loaded.primary.slug,\n integrations: loaded.integrations.map((i) => ({\n slug: i.slug,\n entry: i.entry,\n })),\n findings,\n ...(skew.status === 'project-older'\n ? {\n schemaSkew: {\n status: skew.status,\n cliSchemaVersion: skew.cliSchemaVersion,\n projectSchemaVersion: skew.projectSchemaVersion,\n },\n }\n : {}),\n ...(notice ? { toolchain: notice } : {}),\n };\n\n if (errorCount > 0) {\n return ctx.fail(\n validationError(\n `${errorCount} validation finding${errorCount === 1 ? '' : 's'}.`,\n 'Resolve the error-severity findings in data.findings, then re-run ' +\n '\"ekanos validate\".',\n ),\n data,\n );\n }\n\n return ctx.succeed(data, `validate: OK — no findings for ${slugs}.`);\n}\n"]}
package/dist/context.d.ts CHANGED
@@ -20,6 +20,15 @@ export interface ContextInput {
20
20
  /** Overrides process.env for CLAUDECODE detection (tests). */
21
21
  env?: Record<string, string | undefined>;
22
22
  }
23
+ /**
24
+ * `data` is deliberately `unknown` above — each verb owns its own shape — but
25
+ * one convention spans several of them: `validate`, `dev`, `publish` and
26
+ * `status` all add an OPTIONAL `toolchain` field to `data` (never to the
27
+ * envelope's top level) when the proactive update check in `update-notice.ts`
28
+ * finds this CLI is behind. See that file's `ToolchainNoticeData` for the
29
+ * shape, and `README.md`'s "Keeping the toolchain current" section for the
30
+ * full contract. It is absent, not `null`, when there is nothing to report.
31
+ */
23
32
  /**
24
33
  * The agent-native output envelope, resolved ONCE at startup and threaded
25
34
  * through every command.
package/dist/context.js CHANGED
@@ -1,4 +1,13 @@
1
1
  import { EXIT_CODES } from './exit-codes.js';
2
+ /**
3
+ * `data` is deliberately `unknown` above — each verb owns its own shape — but
4
+ * one convention spans several of them: `validate`, `dev`, `publish` and
5
+ * `status` all add an OPTIONAL `toolchain` field to `data` (never to the
6
+ * envelope's top level) when the proactive update check in `update-notice.ts`
7
+ * finds this CLI is behind. See that file's `ToolchainNoticeData` for the
8
+ * shape, and `README.md`'s "Keeping the toolchain current" section for the
9
+ * full contract. It is absent, not `null`, when there is nothing to report.
10
+ */
2
11
  /**
3
12
  * The agent-native output envelope, resolved ONCE at startup and threaded
4
13
  * through every command.
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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","sourcesContent":["import { CliError } from './errors';\nimport { EXIT_CODES, type ExitCode } from './exit-codes';\n\n/**\n * A single writable sink. Real streams satisfy this; tests pass a capturing\n * buffer so the stdout-purity invariant can be asserted byte-for-byte.\n */\nexport interface WriteStream {\n write(chunk: string): void;\n isTTY?: boolean;\n}\n\nexport interface ContextInput {\n /**\n * The user's EXPLICIT preference, already parsed: `true` for `--json`,\n * `false` for `--no-json`, `undefined` when neither was passed. The\n * undefined case is what allows the mode to be inferred instead.\n */\n jsonFlag?: boolean;\n stdout?: WriteStream;\n stderr?: WriteStream;\n /** Overrides process.env for CLAUDECODE detection (tests). */\n env?: Record<string, string | undefined>;\n}\n\n/** The success envelope. */\ninterface OkEnvelope {\n ok: true;\n data: unknown;\n}\n\n/** The error envelope. `data` is optional — validate carries its findings here. */\ninterface ErrEnvelope {\n ok: false;\n data?: unknown;\n error: { code: string; message: string; hint: string };\n}\n\n/**\n * The agent-native output envelope, resolved ONCE at startup and threaded\n * through every command.\n *\n * The load-bearing invariant: in JSON mode, stdout receives EXACTLY ONE JSON\n * object for the whole process — nothing else, ever. Every progress line, warn,\n * and scrap of human prose goes to stderr. This is the single most common way\n * an agent's tool call breaks (a stray log line on stdout makes the JSON\n * unparseable), so the context enforces it structurally: `succeed`/`fail` are\n * the only writers of stdout, and a `settled` latch makes a second call throw\n * rather than emit a second object.\n */\nexport class CliContext {\n readonly jsonMode: boolean;\n /**\n * The environment the context resolved against — exposed so decoration\n * (the gradient banner) can honour `NO_COLOR` / `EKANOS_NO_BANNER` without\n * every call site threading `process.env` through by hand.\n */\n readonly env: Readonly<Record<string, string | undefined>>;\n private readonly stdout: WriteStream;\n private readonly stderr: WriteStream;\n private settled = false;\n\n constructor(input: ContextInput = {}) {\n const stdout = input.stdout ?? defaultStream(process.stdout);\n const stderr = input.stderr ?? defaultStream(process.stderr);\n const env = input.env ?? process.env;\n\n this.stdout = stdout;\n this.stderr = stderr;\n this.env = env;\n\n // Resolve JSON mode once. An EXPLICIT flag always wins: `--json` forces the\n // envelope on even at a terminal, `--no-json` forces the human form even\n // when piped. Only when the user expressed no preference is the mode\n // inferred — from a non-TTY stdout (piped into another program or an agent)\n // or the CLAUDECODE marker.\n const nonTty = stdout.isTTY !== true;\n this.jsonMode = input.jsonFlag ?? (nonTty || env.CLAUDECODE === '1');\n }\n\n /** Whether stderr is a real terminal — decoration (the banner) gates on this. */\n get stderrIsTTY(): boolean {\n return this.stderr.isTTY === true;\n }\n\n /** Human/progress output — ALWAYS stderr, so stdout stays pure in JSON mode. */\n log(message: string): void {\n this.stderr.write(`${message}\\n`);\n }\n\n /** A warning — stderr, no ANSI. */\n warn(message: string): void {\n this.stderr.write(`${message}\\n`);\n }\n\n /**\n * Emit the `claude-code-hint` protocol line to STDERR. Used on --help and on\n * an unknown command so an agent reading stderr is told what to do next\n * without the line ever touching stdout.\n */\n emitClaudeCodeHint(payload: Record<string, unknown>): void {\n this.stderr.write(`claude-code-hint: ${JSON.stringify(payload)}\\n`);\n }\n\n /**\n * Terminal success. Writes the single stdout object (JSON mode) or a concise\n * human line (human mode). Returns the OK exit code so the caller can end the\n * process with it. Idempotent-by-latch: a second settle throws.\n */\n succeed(data: unknown, humanSummary?: string): ExitCode {\n this.settle();\n if (this.jsonMode) {\n const envelope: OkEnvelope = { ok: true, data };\n this.stdout.write(`${JSON.stringify(envelope)}\\n`);\n } else {\n this.stdout.write(`${humanSummary ?? 'OK'}\\n`);\n }\n return EXIT_CODES.OK;\n }\n\n /**\n * Terminal failure. Writes the single stdout error object (JSON mode) or the\n * message + hint to stderr (human mode). Returns the error's exit code.\n *\n * `error.hint` is guaranteed non-empty by CliError's constructor, so the\n * envelope's \"every error carries a hint\" invariant holds for every path\n * that reaches here.\n */\n fail(error: CliError, data?: unknown): ExitCode {\n this.failWith({\n code: error.code,\n message: error.message,\n hint: error.hint,\n exitCode: error.exitCode,\n data,\n });\n return error.exitCode;\n }\n\n /**\n * The low-level failure emitter. Used directly by `test`, whose exit code is\n * a TRANSPARENT passthrough of the delegated runner's exit code (which is not\n * one of the CLI's own frozen numeric conditions) and whose error `code` is\n * an out-of-taxonomy string. Enforces the same non-empty-hint invariant as\n * CliError so no failure path can emit an empty hint.\n */\n failWith(params: {\n code: string;\n message: string;\n hint: string;\n exitCode: number;\n data?: unknown;\n }): number {\n this.settle();\n if (!params.hint || params.hint.trim().length === 0) {\n throw new Error(\n `failWith for \"${params.message}\" was given an empty hint. Every CLI ` +\n 'error must carry an imperative remediation.',\n );\n }\n if (this.jsonMode) {\n const envelope: ErrEnvelope = {\n ok: false,\n ...(params.data !== undefined ? { data: params.data } : {}),\n error: {\n code: params.code,\n message: params.message,\n hint: params.hint,\n },\n };\n this.stdout.write(`${JSON.stringify(envelope)}\\n`);\n } else {\n this.stderr.write(`Error: ${params.message}\\n`);\n this.stderr.write(`Hint: ${params.hint}\\n`);\n }\n return params.exitCode;\n }\n\n private settle(): void {\n if (this.settled) {\n throw new Error(\n 'CliContext settled twice — a command tried to emit a second ' +\n 'terminal result. In JSON mode this would corrupt stdout with two ' +\n 'objects. This is a bug in the command.',\n );\n }\n this.settled = true;\n }\n}\n\nfunction defaultStream(stream: NodeJS.WriteStream): WriteStream {\n return {\n write: (chunk: string) => {\n stream.write(chunk);\n },\n isTTY: stream.isTTY,\n };\n}\n"]}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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","sourcesContent":["import { CliError } from './errors';\nimport { EXIT_CODES, type ExitCode } from './exit-codes';\n\n/**\n * A single writable sink. Real streams satisfy this; tests pass a capturing\n * buffer so the stdout-purity invariant can be asserted byte-for-byte.\n */\nexport interface WriteStream {\n write(chunk: string): void;\n isTTY?: boolean;\n}\n\nexport interface ContextInput {\n /**\n * The user's EXPLICIT preference, already parsed: `true` for `--json`,\n * `false` for `--no-json`, `undefined` when neither was passed. The\n * undefined case is what allows the mode to be inferred instead.\n */\n jsonFlag?: boolean;\n stdout?: WriteStream;\n stderr?: WriteStream;\n /** Overrides process.env for CLAUDECODE detection (tests). */\n env?: Record<string, string | undefined>;\n}\n\n/** The success envelope. */\ninterface OkEnvelope {\n ok: true;\n data: unknown;\n}\n\n/** The error envelope. `data` is optional — validate carries its findings here. */\ninterface ErrEnvelope {\n ok: false;\n data?: unknown;\n error: { code: string; message: string; hint: string };\n}\n\n/**\n * `data` is deliberately `unknown` above — each verb owns its own shape — but\n * one convention spans several of them: `validate`, `dev`, `publish` and\n * `status` all add an OPTIONAL `toolchain` field to `data` (never to the\n * envelope's top level) when the proactive update check in `update-notice.ts`\n * finds this CLI is behind. See that file's `ToolchainNoticeData` for the\n * shape, and `README.md`'s \"Keeping the toolchain current\" section for the\n * full contract. It is absent, not `null`, when there is nothing to report.\n */\n\n/**\n * The agent-native output envelope, resolved ONCE at startup and threaded\n * through every command.\n *\n * The load-bearing invariant: in JSON mode, stdout receives EXACTLY ONE JSON\n * object for the whole process — nothing else, ever. Every progress line, warn,\n * and scrap of human prose goes to stderr. This is the single most common way\n * an agent's tool call breaks (a stray log line on stdout makes the JSON\n * unparseable), so the context enforces it structurally: `succeed`/`fail` are\n * the only writers of stdout, and a `settled` latch makes a second call throw\n * rather than emit a second object.\n */\nexport class CliContext {\n readonly jsonMode: boolean;\n /**\n * The environment the context resolved against — exposed so decoration\n * (the gradient banner) can honour `NO_COLOR` / `EKANOS_NO_BANNER` without\n * every call site threading `process.env` through by hand.\n */\n readonly env: Readonly<Record<string, string | undefined>>;\n private readonly stdout: WriteStream;\n private readonly stderr: WriteStream;\n private settled = false;\n\n constructor(input: ContextInput = {}) {\n const stdout = input.stdout ?? defaultStream(process.stdout);\n const stderr = input.stderr ?? defaultStream(process.stderr);\n const env = input.env ?? process.env;\n\n this.stdout = stdout;\n this.stderr = stderr;\n this.env = env;\n\n // Resolve JSON mode once. An EXPLICIT flag always wins: `--json` forces the\n // envelope on even at a terminal, `--no-json` forces the human form even\n // when piped. Only when the user expressed no preference is the mode\n // inferred — from a non-TTY stdout (piped into another program or an agent)\n // or the CLAUDECODE marker.\n const nonTty = stdout.isTTY !== true;\n this.jsonMode = input.jsonFlag ?? (nonTty || env.CLAUDECODE === '1');\n }\n\n /** Whether stderr is a real terminal — decoration (the banner) gates on this. */\n get stderrIsTTY(): boolean {\n return this.stderr.isTTY === true;\n }\n\n /** Human/progress output — ALWAYS stderr, so stdout stays pure in JSON mode. */\n log(message: string): void {\n this.stderr.write(`${message}\\n`);\n }\n\n /** A warning — stderr, no ANSI. */\n warn(message: string): void {\n this.stderr.write(`${message}\\n`);\n }\n\n /**\n * Emit the `claude-code-hint` protocol line to STDERR. Used on --help and on\n * an unknown command so an agent reading stderr is told what to do next\n * without the line ever touching stdout.\n */\n emitClaudeCodeHint(payload: Record<string, unknown>): void {\n this.stderr.write(`claude-code-hint: ${JSON.stringify(payload)}\\n`);\n }\n\n /**\n * Terminal success. Writes the single stdout object (JSON mode) or a concise\n * human line (human mode). Returns the OK exit code so the caller can end the\n * process with it. Idempotent-by-latch: a second settle throws.\n */\n succeed(data: unknown, humanSummary?: string): ExitCode {\n this.settle();\n if (this.jsonMode) {\n const envelope: OkEnvelope = { ok: true, data };\n this.stdout.write(`${JSON.stringify(envelope)}\\n`);\n } else {\n this.stdout.write(`${humanSummary ?? 'OK'}\\n`);\n }\n return EXIT_CODES.OK;\n }\n\n /**\n * Terminal failure. Writes the single stdout error object (JSON mode) or the\n * message + hint to stderr (human mode). Returns the error's exit code.\n *\n * `error.hint` is guaranteed non-empty by CliError's constructor, so the\n * envelope's \"every error carries a hint\" invariant holds for every path\n * that reaches here.\n */\n fail(error: CliError, data?: unknown): ExitCode {\n this.failWith({\n code: error.code,\n message: error.message,\n hint: error.hint,\n exitCode: error.exitCode,\n data,\n });\n return error.exitCode;\n }\n\n /**\n * The low-level failure emitter. Used directly by `test`, whose exit code is\n * a TRANSPARENT passthrough of the delegated runner's exit code (which is not\n * one of the CLI's own frozen numeric conditions) and whose error `code` is\n * an out-of-taxonomy string. Enforces the same non-empty-hint invariant as\n * CliError so no failure path can emit an empty hint.\n */\n failWith(params: {\n code: string;\n message: string;\n hint: string;\n exitCode: number;\n data?: unknown;\n }): number {\n this.settle();\n if (!params.hint || params.hint.trim().length === 0) {\n throw new Error(\n `failWith for \"${params.message}\" was given an empty hint. Every CLI ` +\n 'error must carry an imperative remediation.',\n );\n }\n if (this.jsonMode) {\n const envelope: ErrEnvelope = {\n ok: false,\n ...(params.data !== undefined ? { data: params.data } : {}),\n error: {\n code: params.code,\n message: params.message,\n hint: params.hint,\n },\n };\n this.stdout.write(`${JSON.stringify(envelope)}\\n`);\n } else {\n this.stderr.write(`Error: ${params.message}\\n`);\n this.stderr.write(`Hint: ${params.hint}\\n`);\n }\n return params.exitCode;\n }\n\n private settle(): void {\n if (this.settled) {\n throw new Error(\n 'CliContext settled twice — a command tried to emit a second ' +\n 'terminal result. In JSON mode this would corrupt stdout with two ' +\n 'objects. This is a bug in the command.',\n );\n }\n this.settled = true;\n }\n}\n\nfunction defaultStream(stream: NodeJS.WriteStream): WriteStream {\n return {\n write: (chunk: string) => {\n stream.write(chunk);\n },\n isTTY: stream.isTTY,\n };\n}\n"]}
@@ -0,0 +1,48 @@
1
+ export interface CopyTreeResult {
2
+ /** Files written, as paths relative to the destination root. */
3
+ created: string[];
4
+ /** Files left alone because they already exist (and force was off). */
5
+ skipped: string[];
6
+ /**
7
+ * Files a guarded write REFUSED (a symlink at or above the destination),
8
+ * relative to the destination root. Always empty in the default 'throw'
9
+ * mode — populated only under `onRefusal: 'collect'`.
10
+ */
11
+ refused: string[];
12
+ }
13
+ /**
14
+ * Copy a directory tree byte-for-byte — NO `__TOKEN__` rendering, ever.
15
+ *
16
+ * This exists for bundled skill trees (`templates/skills/<name>/`), whose
17
+ * content is prose FOR an agent, not a template: a literal `__SLUG__` in a
18
+ * skill's instructions must survive the copy, and `renderTemplate` would
19
+ * throw on it as an unsubstituted placeholder. So the bytes are copied as
20
+ * bytes, and the walk is generic — a new file added to a bundled tree ships
21
+ * without any per-file registration.
22
+ *
23
+ * Idempotency matches the rest of `init`: an existing file is left untouched
24
+ * and reported as skipped unless `force` is set. Entries are visited in
25
+ * sorted order so the reported lists are deterministic.
26
+ */
27
+ export declare function copyTreeVerbatim(srcRoot: string, destRoot: string, options: {
28
+ force: boolean;
29
+ /**
30
+ * The directory every write must stay inside — the scaffolded project
31
+ * root, not `destRoot`, so a symlink planted at any component of the
32
+ * destination (`.claude`, `.claude/skills`, a file inside the tree) is
33
+ * refused instead of followed. See `safe-fs.ts` for the threat model:
34
+ * `fs.existsSync` calls a dangling symlink absent, and a raw copy would
35
+ * then materialize the bytes OUTSIDE the project.
36
+ */
37
+ root: string;
38
+ /**
39
+ * What a refused write does. 'throw' (the default) is for scaffold time,
40
+ * where a planted symlink should stop everything loudly. 'collect' is for
41
+ * AFTER-THE-FACT passes (upgrade's create-if-missing scaffolding, which
42
+ * runs once the toolchain install has already succeeded): each refusal is
43
+ * recorded per-file in `refused` and the walk continues, so files that
44
+ * did land are reported truthfully instead of vanishing from the result
45
+ * when a later file throws.
46
+ */
47
+ onRefusal?: 'throw' | 'collect';
48
+ }): CopyTreeResult;
@@ -0,0 +1,55 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { classifyPath, writeFileNoFollow } from './safe-fs.js';
4
+ /**
5
+ * Copy a directory tree byte-for-byte — NO `__TOKEN__` rendering, ever.
6
+ *
7
+ * This exists for bundled skill trees (`templates/skills/<name>/`), whose
8
+ * content is prose FOR an agent, not a template: a literal `__SLUG__` in a
9
+ * skill's instructions must survive the copy, and `renderTemplate` would
10
+ * throw on it as an unsubstituted placeholder. So the bytes are copied as
11
+ * bytes, and the walk is generic — a new file added to a bundled tree ships
12
+ * without any per-file registration.
13
+ *
14
+ * Idempotency matches the rest of `init`: an existing file is left untouched
15
+ * and reported as skipped unless `force` is set. Entries are visited in
16
+ * sorted order so the reported lists are deterministic.
17
+ */
18
+ export function copyTreeVerbatim(srcRoot, destRoot, options) {
19
+ const created = [];
20
+ const skipped = [];
21
+ const refused = [];
22
+ const walk = (relativeDir) => {
23
+ const entries = fs
24
+ .readdirSync(path.join(srcRoot, relativeDir), { withFileTypes: true })
25
+ .sort((a, b) => a.name.localeCompare(b.name));
26
+ for (const entry of entries) {
27
+ const relative = path.join(relativeDir, entry.name);
28
+ if (entry.isDirectory()) {
29
+ walk(relative);
30
+ continue;
31
+ }
32
+ const destPath = path.join(destRoot, relative);
33
+ const kind = classifyPath(destPath);
34
+ // A symlink is never "already exists" and never overwritten — it is a
35
+ // planted path and always an error, force or not; writeFileNoFollow
36
+ // refuses it. Everything else that exists keeps init's skip semantics.
37
+ if (kind !== 'missing' && kind !== 'symlink' && !options.force) {
38
+ skipped.push(relative);
39
+ continue;
40
+ }
41
+ try {
42
+ writeFileNoFollow(options.root, destPath, fs.readFileSync(path.join(srcRoot, relative)));
43
+ created.push(relative);
44
+ }
45
+ catch (error) {
46
+ if (options.onRefusal !== 'collect')
47
+ throw error;
48
+ refused.push(relative);
49
+ }
50
+ }
51
+ };
52
+ walk('');
53
+ return { created, skipped, refused };
54
+ }
55
+ //# sourceMappingURL=copy-tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy-tree.js","sourceRoot":"","sources":["../src/copy-tree.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAe5D;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAe,EACf,QAAgB,EAChB,OAqBC;IAED,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,MAAM,IAAI,GAAG,CAAC,WAAmB,EAAE,EAAE;QACnC,MAAM,OAAO,GAAG,EAAE;aACf,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;aACrE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAEhD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACpD,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACf,SAAS;YACX,CAAC;YAED,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC/C,MAAM,IAAI,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YAEpC,sEAAsE;YACtE,oEAAoE;YACpE,uEAAuE;YACvE,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;gBAC/D,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACvB,SAAS;YACX,CAAC;YAED,IAAI,CAAC;gBACH,iBAAiB,CACf,OAAO,CAAC,IAAI,EACZ,QAAQ,EACR,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAC9C,CAAC;gBACF,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACzB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS;oBAAE,MAAM,KAAK,CAAC;gBACjD,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AACvC,CAAC","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { classifyPath, writeFileNoFollow } from './safe-fs';\n\nexport interface CopyTreeResult {\n /** Files written, as paths relative to the destination root. */\n created: string[];\n /** Files left alone because they already exist (and force was off). */\n skipped: string[];\n /**\n * Files a guarded write REFUSED (a symlink at or above the destination),\n * relative to the destination root. Always empty in the default 'throw'\n * mode — populated only under `onRefusal: 'collect'`.\n */\n refused: string[];\n}\n\n/**\n * Copy a directory tree byte-for-byte — NO `__TOKEN__` rendering, ever.\n *\n * This exists for bundled skill trees (`templates/skills/<name>/`), whose\n * content is prose FOR an agent, not a template: a literal `__SLUG__` in a\n * skill's instructions must survive the copy, and `renderTemplate` would\n * throw on it as an unsubstituted placeholder. So the bytes are copied as\n * bytes, and the walk is generic — a new file added to a bundled tree ships\n * without any per-file registration.\n *\n * Idempotency matches the rest of `init`: an existing file is left untouched\n * and reported as skipped unless `force` is set. Entries are visited in\n * sorted order so the reported lists are deterministic.\n */\nexport function copyTreeVerbatim(\n srcRoot: string,\n destRoot: string,\n options: {\n force: boolean;\n /**\n * The directory every write must stay inside — the scaffolded project\n * root, not `destRoot`, so a symlink planted at any component of the\n * destination (`.claude`, `.claude/skills`, a file inside the tree) is\n * refused instead of followed. See `safe-fs.ts` for the threat model:\n * `fs.existsSync` calls a dangling symlink absent, and a raw copy would\n * then materialize the bytes OUTSIDE the project.\n */\n root: string;\n /**\n * What a refused write does. 'throw' (the default) is for scaffold time,\n * where a planted symlink should stop everything loudly. 'collect' is for\n * AFTER-THE-FACT passes (upgrade's create-if-missing scaffolding, which\n * runs once the toolchain install has already succeeded): each refusal is\n * recorded per-file in `refused` and the walk continues, so files that\n * did land are reported truthfully instead of vanishing from the result\n * when a later file throws.\n */\n onRefusal?: 'throw' | 'collect';\n },\n): CopyTreeResult {\n const created: string[] = [];\n const skipped: string[] = [];\n const refused: string[] = [];\n\n const walk = (relativeDir: string) => {\n const entries = fs\n .readdirSync(path.join(srcRoot, relativeDir), { withFileTypes: true })\n .sort((a, b) => a.name.localeCompare(b.name));\n\n for (const entry of entries) {\n const relative = path.join(relativeDir, entry.name);\n if (entry.isDirectory()) {\n walk(relative);\n continue;\n }\n\n const destPath = path.join(destRoot, relative);\n const kind = classifyPath(destPath);\n\n // A symlink is never \"already exists\" and never overwritten — it is a\n // planted path and always an error, force or not; writeFileNoFollow\n // refuses it. Everything else that exists keeps init's skip semantics.\n if (kind !== 'missing' && kind !== 'symlink' && !options.force) {\n skipped.push(relative);\n continue;\n }\n\n try {\n writeFileNoFollow(\n options.root,\n destPath,\n fs.readFileSync(path.join(srcRoot, relative)),\n );\n created.push(relative);\n } catch (error) {\n if (options.onRefusal !== 'collect') throw error;\n refused.push(relative);\n }\n }\n };\n\n walk('');\n return { created, skipped, refused };\n}\n"]}
@@ -0,0 +1,28 @@
1
+ import { spawn } from 'node:child_process';
2
+ export interface DelegationPlan {
3
+ shouldDelegate: boolean;
4
+ ownVersion: string;
5
+ localVersion?: string;
6
+ /** Absolute path to the local CLI's bin entry, when `shouldDelegate`. */
7
+ localBinPath?: string;
8
+ }
9
+ /**
10
+ * Decide whether this invocation should delegate. Pure and side-effect-free
11
+ * (beyond the one file read of the local `@ekanos/cli` manifest, mirroring
12
+ * `readInstalledHarness`/`dev`'s own project-package resolution rather than
13
+ * writing a second one) so it is directly testable without spawning anything.
14
+ */
15
+ export declare function planDelegation(argv: readonly string[], env: Record<string, string | undefined>, cwd: string): DelegationPlan;
16
+ /**
17
+ * The one-line stderr announcement — human mode only, and NEVER on stdout
18
+ * (a delegated `validate --json` must still emit exactly one JSON object on
19
+ * stdout, from the CHILD, with nothing of ours ahead of it).
20
+ */
21
+ export declare function announceDelegation(plan: DelegationPlan, argv: readonly string[], env: Record<string, string | undefined>, stdoutIsTTY: boolean): void;
22
+ /**
23
+ * Spawn the local CLI's own bin entry with the SAME node executable,
24
+ * `stdio: 'inherit'` so the JSON envelope and banner gating are
25
+ * byte-identical to a direct run, and propagate its exit code exactly.
26
+ * `spawnFn` is injectable so tests never actually fork a process.
27
+ */
28
+ export declare function delegate(plan: DelegationPlan, argv: readonly string[], env: NodeJS.ProcessEnv, spawnFn?: typeof spawn): Promise<number>;
@@ -0,0 +1,136 @@
1
+ import { spawn } from 'node:child_process';
2
+ import * as path from 'node:path';
3
+ import { cliVersion } from './compatibility.js';
4
+ import { readInstalledPackageManifest } from './harness-scaffold.js';
5
+ /**
6
+ * Wrapper-style delegation to the project-local `@ekanos/cli` — the
7
+ * Gradle-wrapper / Yarn / `npx` pattern, applied to this CLI.
8
+ *
9
+ * A global install bundles ONE `@ekanos/integration-schema` version but
10
+ * routinely serves N projects on different `@ekanos/sdk` versions — it
11
+ * cannot be correct for all of them simultaneously (this is exactly the
12
+ * failure mode `schema-skew.ts` detects). Delegation dissolves the tension:
13
+ * `ekanos` stays on PATH for convenience, but the four verbs that actually
14
+ * read or generate project files always run through the project's OWN
15
+ * pinned copy when one is installed and differs from the invoked binary.
16
+ *
17
+ * Machine-level verbs (`init`, `login`, `logout`, `whoami`, `sources`,
18
+ * `use`, `status`, `upgrade`) are deliberately NEVER delegated: you never
19
+ * want an old local copy handling credentials, and `upgrade` must always be
20
+ * the newest logic doing the upgrading — delegating it could have an old CLI
21
+ * "upgrade" a project using its own out-of-date resolution.
22
+ */
23
+ const DELEGATABLE_VERBS = new Set(['validate', 'publish', 'dev', 'test']);
24
+ /** The first bare (non-flag) token before a standalone `--` — mirrors `splitCommand` in `index.ts`. */
25
+ function firstCommand(argv) {
26
+ for (const token of argv) {
27
+ if (token === '--')
28
+ break;
29
+ if (!token.startsWith('-'))
30
+ return token;
31
+ }
32
+ return null;
33
+ }
34
+ /** `--dir <path>` / `--dir=<path>`, before a standalone `--`. */
35
+ function scanDirFlag(argv) {
36
+ for (let i = 0; i < argv.length; i++) {
37
+ const token = argv[i];
38
+ if (token === '--')
39
+ break;
40
+ if (token === '--dir')
41
+ return argv[i + 1];
42
+ if (token.startsWith('--dir='))
43
+ return token.slice('--dir='.length);
44
+ }
45
+ return undefined;
46
+ }
47
+ /**
48
+ * Decide whether this invocation should delegate. Pure and side-effect-free
49
+ * (beyond the one file read of the local `@ekanos/cli` manifest, mirroring
50
+ * `readInstalledHarness`/`dev`'s own project-package resolution rather than
51
+ * writing a second one) so it is directly testable without spawning anything.
52
+ */
53
+ export function planDelegation(argv, env, cwd) {
54
+ var _a;
55
+ const ownVersion = cliVersion();
56
+ // The recursion guard, and the explicit escape hatch, both win outright.
57
+ if (env.EKANOS_DELEGATED === '1' || env.EKANOS_NO_DELEGATE === '1') {
58
+ return { shouldDelegate: false, ownVersion };
59
+ }
60
+ const command = firstCommand(argv);
61
+ if (!command || !DELEGATABLE_VERBS.has(command)) {
62
+ return { shouldDelegate: false, ownVersion };
63
+ }
64
+ const projectDir = path.resolve(cwd, (_a = scanDirFlag(argv)) !== null && _a !== void 0 ? _a : '.');
65
+ // Best-effort, like every other resolution this function does: a corrupt
66
+ // local manifest must fall through to "don't delegate", never throw. This
67
+ // runs in `bin.ts` BEFORE `run()`'s try/catch exists, so an uncaught throw
68
+ // here would bypass the JSON-envelope error machinery entirely and print a
69
+ // raw stderr string instead — a real regression in the "exactly one
70
+ // terminal emission" contract the rest of this package guarantees.
71
+ let local = null;
72
+ try {
73
+ local = readInstalledPackageManifest(projectDir, '@ekanos/cli');
74
+ }
75
+ catch (_b) {
76
+ local = null;
77
+ }
78
+ if (!local || local.version === ownVersion) {
79
+ return { shouldDelegate: false, ownVersion, localVersion: local === null || local === void 0 ? void 0 : local.version };
80
+ }
81
+ return {
82
+ shouldDelegate: true,
83
+ ownVersion,
84
+ localVersion: local.version,
85
+ localBinPath: path.join(projectDir, 'node_modules', '@ekanos', 'cli', 'dist', 'bin.js'),
86
+ };
87
+ }
88
+ /**
89
+ * Same JSON-mode inference `CliContext` uses (`context.ts`) — duplicated
90
+ * deliberately rather than shared, because this runs BEFORE a `CliContext`
91
+ * exists (delegation is decided ahead of the whole `run()` machinery). Keep
92
+ * this in sync with `CliContext`'s constructor if that heuristic ever
93
+ * changes: an explicit `--json`/`--no-json` wins, otherwise a non-TTY stdout
94
+ * or `CLAUDECODE=1` means JSON mode.
95
+ */
96
+ function looksLikeJsonMode(argv, env, stdoutIsTTY) {
97
+ for (const token of argv) {
98
+ if (token === '--')
99
+ break;
100
+ if (token === '--json')
101
+ return true;
102
+ if (token === '--no-json')
103
+ return false;
104
+ }
105
+ return !stdoutIsTTY || env.CLAUDECODE === '1';
106
+ }
107
+ /**
108
+ * The one-line stderr announcement — human mode only, and NEVER on stdout
109
+ * (a delegated `validate --json` must still emit exactly one JSON object on
110
+ * stdout, from the CHILD, with nothing of ours ahead of it).
111
+ */
112
+ export function announceDelegation(plan, argv, env, stdoutIsTTY) {
113
+ if (!plan.shouldDelegate)
114
+ return;
115
+ if (looksLikeJsonMode(argv, env, stdoutIsTTY))
116
+ return;
117
+ process.stderr.write(`ekanos: using this project's pinned CLI ${plan.localVersion} (global: ` +
118
+ `${plan.ownVersion}). Set EKANOS_NO_DELEGATE=1 to bypass.\n`);
119
+ }
120
+ /**
121
+ * Spawn the local CLI's own bin entry with the SAME node executable,
122
+ * `stdio: 'inherit'` so the JSON envelope and banner gating are
123
+ * byte-identical to a direct run, and propagate its exit code exactly.
124
+ * `spawnFn` is injectable so tests never actually fork a process.
125
+ */
126
+ export function delegate(plan, argv, env, spawnFn = spawn) {
127
+ return new Promise((resolve) => {
128
+ const child = spawnFn(process.execPath, [plan.localBinPath, ...argv], {
129
+ stdio: 'inherit',
130
+ env: Object.assign(Object.assign({}, env), { EKANOS_DELEGATED: '1' }),
131
+ });
132
+ child.on('error', () => resolve(1));
133
+ child.on('exit', (code) => resolve(code !== null && code !== void 0 ? code : 1));
134
+ });
135
+ }
136
+ //# sourceMappingURL=delegate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delegate.js","sourceRoot":"","sources":["../src/delegate.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from 'node:child_process';\nimport * as path from 'node:path';\n\nimport { cliVersion } from './compatibility';\nimport { readInstalledPackageManifest } from './harness-scaffold';\n\n/**\n * Wrapper-style delegation to the project-local `@ekanos/cli` — the\n * Gradle-wrapper / Yarn / `npx` pattern, applied to this CLI.\n *\n * A global install bundles ONE `@ekanos/integration-schema` version but\n * routinely serves N projects on different `@ekanos/sdk` versions — it\n * cannot be correct for all of them simultaneously (this is exactly the\n * failure mode `schema-skew.ts` detects). Delegation dissolves the tension:\n * `ekanos` stays on PATH for convenience, but the four verbs that actually\n * read or generate project files always run through the project's OWN\n * pinned copy when one is installed and differs from the invoked binary.\n *\n * Machine-level verbs (`init`, `login`, `logout`, `whoami`, `sources`,\n * `use`, `status`, `upgrade`) are deliberately NEVER delegated: you never\n * want an old local copy handling credentials, and `upgrade` must always be\n * the newest logic doing the upgrading — delegating it could have an old CLI\n * \"upgrade\" a project using its own out-of-date resolution.\n */\n\nconst DELEGATABLE_VERBS = new Set(['validate', 'publish', 'dev', 'test']);\n\nexport interface DelegationPlan {\n shouldDelegate: boolean;\n ownVersion: string;\n localVersion?: string;\n /** Absolute path to the local CLI's bin entry, when `shouldDelegate`. */\n localBinPath?: string;\n}\n\n/** The first bare (non-flag) token before a standalone `--` — mirrors `splitCommand` in `index.ts`. */\nfunction firstCommand(argv: readonly string[]): string | null {\n for (const token of argv) {\n if (token === '--') break;\n if (!token.startsWith('-')) return token;\n }\n return null;\n}\n\n/** `--dir <path>` / `--dir=<path>`, before a standalone `--`. */\nfunction scanDirFlag(argv: readonly string[]): string | undefined {\n for (let i = 0; i < argv.length; i++) {\n const token = argv[i]!;\n if (token === '--') break;\n if (token === '--dir') return argv[i + 1];\n if (token.startsWith('--dir=')) return token.slice('--dir='.length);\n }\n return undefined;\n}\n\n/**\n * Decide whether this invocation should delegate. Pure and side-effect-free\n * (beyond the one file read of the local `@ekanos/cli` manifest, mirroring\n * `readInstalledHarness`/`dev`'s own project-package resolution rather than\n * writing a second one) so it is directly testable without spawning anything.\n */\nexport function planDelegation(\n argv: readonly string[],\n env: Record<string, string | undefined>,\n cwd: string,\n): DelegationPlan {\n const ownVersion = cliVersion();\n\n // The recursion guard, and the explicit escape hatch, both win outright.\n if (env.EKANOS_DELEGATED === '1' || env.EKANOS_NO_DELEGATE === '1') {\n return { shouldDelegate: false, ownVersion };\n }\n\n const command = firstCommand(argv);\n if (!command || !DELEGATABLE_VERBS.has(command)) {\n return { shouldDelegate: false, ownVersion };\n }\n\n const projectDir = path.resolve(cwd, scanDirFlag(argv) ?? '.');\n // Best-effort, like every other resolution this function does: a corrupt\n // local manifest must fall through to \"don't delegate\", never throw. This\n // runs in `bin.ts` BEFORE `run()`'s try/catch exists, so an uncaught throw\n // here would bypass the JSON-envelope error machinery entirely and print a\n // raw stderr string instead — a real regression in the \"exactly one\n // terminal emission\" contract the rest of this package guarantees.\n let local: ReturnType<typeof readInstalledPackageManifest> = null;\n try {\n local = readInstalledPackageManifest(projectDir, '@ekanos/cli');\n } catch {\n local = null;\n }\n\n if (!local || local.version === ownVersion) {\n return { shouldDelegate: false, ownVersion, localVersion: local?.version };\n }\n\n return {\n shouldDelegate: true,\n ownVersion,\n localVersion: local.version,\n localBinPath: path.join(\n projectDir,\n 'node_modules',\n '@ekanos',\n 'cli',\n 'dist',\n 'bin.js',\n ),\n };\n}\n\n/**\n * Same JSON-mode inference `CliContext` uses (`context.ts`) — duplicated\n * deliberately rather than shared, because this runs BEFORE a `CliContext`\n * exists (delegation is decided ahead of the whole `run()` machinery). Keep\n * this in sync with `CliContext`'s constructor if that heuristic ever\n * changes: an explicit `--json`/`--no-json` wins, otherwise a non-TTY stdout\n * or `CLAUDECODE=1` means JSON mode.\n */\nfunction looksLikeJsonMode(\n argv: readonly string[],\n env: Record<string, string | undefined>,\n stdoutIsTTY: boolean,\n): boolean {\n for (const token of argv) {\n if (token === '--') break;\n if (token === '--json') return true;\n if (token === '--no-json') return false;\n }\n return !stdoutIsTTY || env.CLAUDECODE === '1';\n}\n\n/**\n * The one-line stderr announcement — human mode only, and NEVER on stdout\n * (a delegated `validate --json` must still emit exactly one JSON object on\n * stdout, from the CHILD, with nothing of ours ahead of it).\n */\nexport function announceDelegation(\n plan: DelegationPlan,\n argv: readonly string[],\n env: Record<string, string | undefined>,\n stdoutIsTTY: boolean,\n): void {\n if (!plan.shouldDelegate) return;\n if (looksLikeJsonMode(argv, env, stdoutIsTTY)) return;\n\n process.stderr.write(\n `ekanos: using this project's pinned CLI ${plan.localVersion} (global: ` +\n `${plan.ownVersion}). Set EKANOS_NO_DELEGATE=1 to bypass.\\n`,\n );\n}\n\n/**\n * Spawn the local CLI's own bin entry with the SAME node executable,\n * `stdio: 'inherit'` so the JSON envelope and banner gating are\n * byte-identical to a direct run, and propagate its exit code exactly.\n * `spawnFn` is injectable so tests never actually fork a process.\n */\nexport function delegate(\n plan: DelegationPlan,\n argv: readonly string[],\n env: NodeJS.ProcessEnv,\n spawnFn: typeof spawn = spawn,\n): Promise<number> {\n return new Promise((resolve) => {\n const child = spawnFn(process.execPath, [plan.localBinPath!, ...argv], {\n stdio: 'inherit',\n env: { ...env, EKANOS_DELEGATED: '1' },\n });\n child.on('error', () => resolve(1));\n child.on('exit', (code) => resolve(code ?? 1));\n });\n}\n"]}