@ekanos/cli 0.3.3 → 0.3.5

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 (42) hide show
  1. package/dist/auth/session.js +13 -4
  2. package/dist/auth/session.js.map +1 -1
  3. package/dist/banner.d.ts +1 -1
  4. package/dist/banner.js +4 -13
  5. package/dist/banner.js.map +1 -1
  6. package/dist/commands/dev.js +22 -6
  7. package/dist/commands/dev.js.map +1 -1
  8. package/dist/commands/init.js +68 -30
  9. package/dist/commands/init.js.map +1 -1
  10. package/dist/commands/login.js +25 -14
  11. package/dist/commands/login.js.map +1 -1
  12. package/dist/commands/logout.js +30 -4
  13. package/dist/commands/logout.js.map +1 -1
  14. package/dist/commands/publish.js +25 -12
  15. package/dist/commands/publish.js.map +1 -1
  16. package/dist/commands/sources.js +26 -16
  17. package/dist/commands/sources.js.map +1 -1
  18. package/dist/commands/status.d.ts +1 -1
  19. package/dist/commands/status.js +52 -39
  20. package/dist/commands/status.js.map +1 -1
  21. package/dist/commands/test.js +24 -7
  22. package/dist/commands/test.js.map +1 -1
  23. package/dist/commands/upgrade.js +85 -53
  24. package/dist/commands/upgrade.js.map +1 -1
  25. package/dist/commands/use.js +10 -2
  26. package/dist/commands/use.js.map +1 -1
  27. package/dist/commands/validate.js +21 -3
  28. package/dist/commands/validate.js.map +1 -1
  29. package/dist/commands/whoami.js +16 -6
  30. package/dist/commands/whoami.js.map +1 -1
  31. package/dist/context.js +10 -3
  32. package/dist/context.js.map +1 -1
  33. package/dist/pnpm-builds.d.ts +15 -0
  34. package/dist/pnpm-builds.js +48 -0
  35. package/dist/pnpm-builds.js.map +1 -0
  36. package/dist/ui.d.ts +36 -0
  37. package/dist/ui.js +77 -2
  38. package/dist/ui.js.map +1 -1
  39. package/package.json +2 -2
  40. package/templates/AGENTS.md.tmpl +63 -0
  41. package/templates/package.json.tmpl +4 -4
  42. package/templates/pnpm-workspace.yaml.tmpl +12 -5
@@ -1 +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 Ekanos 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"]}
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 { type Ui, displayPath } from '../ui';\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 Ekanos 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(ctx.ui, 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 ctx.ui.result({\n title: 'Already up to date',\n rows: [['Target', `resolved from ${resolution.source}`]],\n }),\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(ctx.ui, 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 ctx.ui.result({\n title: 'Nothing to install',\n tone: 'warn',\n notes: [\n 'None of the changed packages are direct dependencies of this project.',\n 'Their versions follow from whichever direct dependency pins them.',\n ],\n }),\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(\n ctx.ui.dim(\n `Running \"${pm.bin} ${argv.join(' ')}\" in ${displayPath(loaded.projectDir, process.cwd())}…`,\n ),\n );\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(\n ctx.ui,\n installedKeys,\n skippedKeys,\n deltas,\n regenerated,\n scaffolded,\n ),\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 describeDelta(delta: DeltaEntry): string {\n return `${delta.current ?? 'not installed'} → ${delta.target}`;\n}\n\nfunction checkSummary(\n ui: Ui,\n source: 'host' | 'npm',\n deltas: Record<ToolchainKey, DeltaEntry>,\n): string {\n const entries = Object.entries(deltas) as [ToolchainKey, DeltaEntry][];\n const pending = entries.filter(([, delta]) => delta.changed).length;\n\n return ui.result({\n title:\n pending === 0 ? 'Toolchain is current' : 'Toolchain update available',\n tone: pending === 0 ? 'ok' : 'warn',\n rows: entries.map(([key, delta]): [string, string] => [\n key,\n delta.changed ? describeDelta(delta) : `${delta.target} · current`,\n ]),\n notes: [\n `Target resolved from ${source}.` +\n (pending > 0 ? ' Run \"ekanos upgrade\" to apply it.' : ''),\n ],\n });\n}\n\nfunction planSummary(\n ui: Ui,\n source: string,\n deltas: Record<ToolchainKey, DeltaEntry>,\n): string {\n return ui.result({\n title: 'Upgrading',\n rows: (Object.entries(deltas) as [ToolchainKey, DeltaEntry][])\n .filter(([, delta]) => delta.changed)\n .map(([key, delta]): [string, string] => [key, describeDelta(delta)]),\n notes: [`Target resolved from ${source}.`],\n });\n}\n\nfunction successSummary(\n ui: Ui,\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 arrow = (key: ToolchainKey) => `${key} → ${deltas[key]!.target}`;\n const attention =\n regenerated.needsAttention.length > 0 ||\n scaffolded.needsAttention.length > 0;\n\n const notes: string[] = [];\n if (scaffolded.created.length > 0) {\n notes.push(\n 'New in this toolchain: see AGENTS.md\\'s \"API documentation\" section.',\n );\n }\n if (scaffolded.needsAttention.length > 0) {\n // Never suggest --force here — it overwrites every scaffolded file,\n // including src/integration.ts.\n notes.push(\n 'A path component is a symlink or unwritable, so those files were not created. ' +\n 'Fix the path, then run \"ekanos init --slug <slug> --dir .\" WITHOUT --force: ' +\n 'init is per-file idempotent and creates only what is missing.',\n );\n }\n if (regenerated.needsAttention.length > 0) {\n notes.push(\n 'Locally modified files were left untouched — merge the new template by hand.',\n );\n }\n\n return ui.result({\n title:\n installedKeys.length > 0 ? 'Toolchain upgraded' : 'Nothing to install',\n tone: attention || installedKeys.length === 0 ? 'warn' : 'ok',\n rows: [\n ...(installedKeys.length > 0\n ? [['Installed', installedKeys.map(arrow)] as const]\n : []),\n ...(skippedKeys.length > 0\n ? [['Not installed', skippedKeys.map(arrow)] as const]\n : []),\n ...(regenerated.updated.length > 0\n ? [['Regenerated', regenerated.updated] as const]\n : []),\n ...(scaffolded.created.length > 0\n ? [['Created', scaffolded.created] as const]\n : []),\n ...(regenerated.needsAttention.length > 0\n ? [['Modified', regenerated.needsAttention] as const]\n : []),\n ...(scaffolded.needsAttention.length > 0\n ? [['Could not create', scaffolded.needsAttention] as const]\n : []),\n ],\n notes:\n skippedKeys.length > 0\n ? [\n 'Not installed = transitive, or not a local dependency (e.g. a global CLI).',\n ...notes,\n ]\n : notes,\n });\n}\n"]}
@@ -3,6 +3,7 @@ import { forbiddenError, usageError } from '../errors.js';
3
3
  import { loadProject } from '../project.js';
4
4
  import { fetchSeatsWithOneRefresh } from '../seats.js';
5
5
  import { stillUnauthorizedSourceSeats } from '../sources-api.js';
6
+ import { shortHost } from '../ui.js';
6
7
  import { persistProjectFields } from './publish.js';
7
8
  /**
8
9
  * `use <source-slug>` — set this project's publish target, with the same
@@ -15,7 +16,6 @@ import { persistProjectFields } from './publish.js';
15
16
  * and a later `publish` needs neither flag.
16
17
  */
17
18
  export async function runUse(ctx, args) {
18
- var _a;
19
19
  if (!args.slug || args.slug.trim().length === 0) {
20
20
  throw usageError('No source slug given.', 'Run "ekanos use <source-slug>" — see "ekanos sources" for the ' +
21
21
  'slugs you hold a seat on.');
@@ -47,7 +47,15 @@ export async function runUse(ctx, args) {
47
47
  },
48
48
  sourcePersisted: persisted.source,
49
49
  hostPersisted: persisted.host,
50
- }, `use: ekanos.json now publishes to ${(_a = seat.name) !== null && _a !== void 0 ? _a : seat.slug} (${seat.slug}) on ${env.host}.`);
50
+ }, ctx.ui.result({
51
+ title: 'Publish target set',
52
+ rows: [
53
+ ['Source', seat.name ? `${seat.name} (${seat.slug})` : seat.slug],
54
+ ['Role', seat.roles.join(', ')],
55
+ ['Host', shortHost(env.host)],
56
+ ['Saved to', 'ekanos.json'],
57
+ ],
58
+ }));
51
59
  }
52
60
  function seatsHint(sources, host) {
53
61
  if (sources.length === 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"use.js","sourceRoot":"","sources":["../../src/commands/use.ts"],"names":[],"mappings":"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","sourcesContent":["import { requireSession, resolveAuthEnvironment } from '../auth/session';\nimport type { CliContext } from '../context';\nimport { forbiddenError, usageError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport { loadProject } from '../project';\nimport { fetchSeatsWithOneRefresh } from '../seats';\nimport { type SourceSeat, stillUnauthorizedSourceSeats } from '../sources-api';\nimport { persistProjectFields } from './publish';\n\nexport interface UseArgs {\n host?: string;\n /** Project directory (contains ekanos.json). Defaults to cwd. */\n dir: string;\n /** The source slug to switch this project's publish target to. */\n slug?: string;\n env: Record<string, string | undefined>;\n}\n\n/**\n * `use <source-slug>` — set this project's publish target, with the same\n * seat verification `publish` runs: the slug must name a source the caller\n * actually holds a seat on, so a project can never be pointed at a source by\n * a typo alone.\n *\n * Persisted via the exact same mechanics `publish` uses after a successful\n * submission (`persistProjectFields`), so `host` also sticks on first write\n * and a later `publish` needs neither flag.\n */\nexport async function runUse(\n ctx: CliContext,\n args: UseArgs,\n): Promise<ExitCode> {\n if (!args.slug || args.slug.trim().length === 0) {\n throw usageError(\n 'No source slug given.',\n 'Run \"ekanos use <source-slug>\" — see \"ekanos sources\" for the ' +\n 'slugs you hold a seat on.',\n );\n }\n\n const slug = args.slug.trim();\n\n // Requires a project up front: `use` sets ekanos.json's \"source\" field, so\n // running it outside one is a precondition failure, not a network call.\n const loaded = loadProject(args.dir);\n\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.dir,\n });\n const session = requireSession(env.store, env.host);\n\n const result = await fetchSeatsWithOneRefresh(env, session);\n\n if (result.status === 'unauthorized') {\n stillUnauthorizedSourceSeats(env.host);\n }\n\n const seat = result.sources.find((s) => s.slug === slug);\n\n if (!seat) {\n throw forbiddenError(\n `This account does not hold a seat on source \"${slug}\" on ${env.host}.`,\n seatsHint(result.sources, env.host),\n );\n }\n\n const persisted = persistProjectFields(loaded, seat.slug, env.host);\n\n return ctx.succeed(\n {\n host: env.host,\n source: {\n id: seat.id,\n slug: seat.slug,\n name: seat.name,\n roles: seat.roles,\n },\n sourcePersisted: persisted.source,\n hostPersisted: persisted.host,\n },\n `use: ekanos.json now publishes to ${seat.name ?? seat.slug} (${seat.slug}) on ${env.host}.`,\n );\n}\n\nfunction seatsHint(sources: SourceSeat[], host: string): string {\n if (sources.length === 0) {\n return (\n `You hold no seats on any source on ${host}. Ask an Ekanos super-admin ` +\n `to grant your account a seat on the intended source.`\n );\n }\n\n const list = sources\n .map((s) => `${s.slug} (${s.roles.join(', ')})`)\n .join(', ');\n\n return `You hold a seat on: ${list}. Run \"ekanos use <slug>\" with one of these.`;\n}\n"]}
1
+ {"version":3,"file":"use.js","sourceRoot":"","sources":["../../src/commands/use.ts"],"names":[],"mappings":"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","sourcesContent":["import { requireSession, resolveAuthEnvironment } from '../auth/session';\nimport type { CliContext } from '../context';\nimport { forbiddenError, usageError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport { loadProject } from '../project';\nimport { fetchSeatsWithOneRefresh } from '../seats';\nimport { type SourceSeat, stillUnauthorizedSourceSeats } from '../sources-api';\nimport { shortHost } from '../ui';\nimport { persistProjectFields } from './publish';\n\nexport interface UseArgs {\n host?: string;\n /** Project directory (contains ekanos.json). Defaults to cwd. */\n dir: string;\n /** The source slug to switch this project's publish target to. */\n slug?: string;\n env: Record<string, string | undefined>;\n}\n\n/**\n * `use <source-slug>` — set this project's publish target, with the same\n * seat verification `publish` runs: the slug must name a source the caller\n * actually holds a seat on, so a project can never be pointed at a source by\n * a typo alone.\n *\n * Persisted via the exact same mechanics `publish` uses after a successful\n * submission (`persistProjectFields`), so `host` also sticks on first write\n * and a later `publish` needs neither flag.\n */\nexport async function runUse(\n ctx: CliContext,\n args: UseArgs,\n): Promise<ExitCode> {\n if (!args.slug || args.slug.trim().length === 0) {\n throw usageError(\n 'No source slug given.',\n 'Run \"ekanos use <source-slug>\" — see \"ekanos sources\" for the ' +\n 'slugs you hold a seat on.',\n );\n }\n\n const slug = args.slug.trim();\n\n // Requires a project up front: `use` sets ekanos.json's \"source\" field, so\n // running it outside one is a precondition failure, not a network call.\n const loaded = loadProject(args.dir);\n\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.dir,\n });\n const session = requireSession(env.store, env.host);\n\n const result = await fetchSeatsWithOneRefresh(env, session);\n\n if (result.status === 'unauthorized') {\n stillUnauthorizedSourceSeats(env.host);\n }\n\n const seat = result.sources.find((s) => s.slug === slug);\n\n if (!seat) {\n throw forbiddenError(\n `This account does not hold a seat on source \"${slug}\" on ${env.host}.`,\n seatsHint(result.sources, env.host),\n );\n }\n\n const persisted = persistProjectFields(loaded, seat.slug, env.host);\n\n return ctx.succeed(\n {\n host: env.host,\n source: {\n id: seat.id,\n slug: seat.slug,\n name: seat.name,\n roles: seat.roles,\n },\n sourcePersisted: persisted.source,\n hostPersisted: persisted.host,\n },\n ctx.ui.result({\n title: 'Publish target set',\n rows: [\n ['Source', seat.name ? `${seat.name} (${seat.slug})` : seat.slug],\n ['Role', seat.roles.join(', ')],\n ['Host', shortHost(env.host)],\n ['Saved to', 'ekanos.json'],\n ],\n }),\n );\n}\n\nfunction seatsHint(sources: SourceSeat[], host: string): string {\n if (sources.length === 0) {\n return (\n `You hold no seats on any source on ${host}. Ask an Ekanos super-admin ` +\n `to grant your account a seat on the intended source.`\n );\n }\n\n const list = sources\n .map((s) => `${s.slug} (${s.roles.join(', ')})`)\n .join(', ');\n\n return `You hold a seat on: ${list}. Run \"ekanos use <slug>\" with one of these.`;\n}\n"]}
@@ -6,6 +6,7 @@ import { renderFindings } from '../findings-renderer.js';
6
6
  import { loadProject } from '../project.js';
7
7
  import { assertSchemaNotSkewed } from '../schema-skew.js';
8
8
  import { ENCLOSING_NOTE, extractPathFromMessage, isRootPath, locateDotPath, } from '../source-locator.js';
9
+ import { pluralize } from '../ui.js';
9
10
  import { announceUpdate, checkForUpdate } from '../update-notice.js';
10
11
  import { collectAllFindings } from '../validate-findings.js';
11
12
  /**
@@ -29,8 +30,8 @@ export async function runValidate(ctx, args) {
29
30
  const notice = await checkForUpdate({ env: (_a = args.env) !== null && _a !== void 0 ? _a : process.env });
30
31
  announceUpdate(ctx, notice);
31
32
  const slugs = loaded.integrations.map((i) => i.slug).join(', ');
32
- ctx.log(`Validating ${loaded.integrations.length} integration` +
33
- `${loaded.integrations.length === 1 ? '' : 's'} (${slugs})…`);
33
+ ctx.log(ctx.ui.dim(`Validating ${loaded.integrations.length} integration` +
34
+ `${loaded.integrations.length === 1 ? '' : 's'} (${slugs})…`));
34
35
  const findings = await collectAllFindings(loaded);
35
36
  enrichSchemaFindings(findings, loaded.projectDir);
36
37
  const errorCount = findings.filter((f) => f.severity === 'error').length;
@@ -67,7 +68,24 @@ export async function runValidate(ctx, args) {
67
68
  }
68
69
  const successLine = ctx.jsonMode
69
70
  ? `validate: OK — no findings for ${slugs}.`
70
- : ctx.ui.ok(`${ctx.ui.glyphs.ok} validate: no findings for ${slugs}.`);
71
+ : ctx.ui.result({
72
+ title: findings.length === 0 ? 'No findings' : 'Valid',
73
+ tone: findings.length === 0 ? 'ok' : 'warn',
74
+ rows: [
75
+ ['Checked', loaded.integrations.map((i) => i.slug)],
76
+ ...(findings.length > 0
77
+ ? [
78
+ [
79
+ 'Findings',
80
+ pluralize(findings.length, 'warning or note'),
81
+ ],
82
+ ]
83
+ : []),
84
+ ],
85
+ notes: findings.length > 0
86
+ ? ['None are errors. Run with --json to read them.']
87
+ : [],
88
+ });
71
89
  return ctx.succeed(data, successLine);
72
90
  }
73
91
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAGlC,OAAO,EACL,cAAc,EACd,uBAAuB,EACvB,UAAU,GACX,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE5C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,UAAU,EACV,aAAa,GACd,MAAM,mBAAmB,CAAC;AAC3B,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,QAAE,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;IAClD,oBAAoB,CAAC,QAAQ,EAAE,MAAM,CAAC,UAAU,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,iEAAiE;QACjE,sEAAsE;QACtE,wEAAwE;QACxE,6DAA6D;QAC7D,wEAAwE;QACxE,8DAA8D;QAC9D,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;YAClB,cAAc,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;QAC5D,CAAC;QAED,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,CACvB,eAAe,CACb,GAAG,UAAU,sBAAsB,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,EACjE,oEAAoE;YAClE,oBAAoB,CACvB,EACD,IAAI,EACJ,CAAC,GAAG,CAAC,QAAQ;YACX,CAAC,CAAC,EAAE,eAAe,EAAE,EAAE,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,EAAE;YACjE,CAAC,CAAC,SAAS,CACd,CAAC;QAEF,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;YAClB,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,iBAAiB,EAAE,wBAAwB,CAAC,CAAC,CAAC;QAC1E,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,WAAW,GAAG,GAAG,CAAC,QAAQ;QAC9B,CAAC,CAAC,kCAAkC,KAAK,GAAG;QAC5C,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,8BAA8B,KAAK,GAAG,CAAC,CAAC;IAEzE,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;AACxC,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,SAAS,oBAAoB,CAAC,QAAmB,EAAE,UAAkB;IACnE,MAAM,WAAW,GAAG,IAAI,GAAG,EAAyB,CAAC;IAErD,MAAM,UAAU,GAAG,CAAC,IAAY,EAAiB,EAAE;QACjD,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,MAAM,CAAC;QAExC,IAAI,IAAmB,CAAC;QACxB,IAAI,CAAC;YACH,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;QACjE,CAAC;mBAAO,CAAC;YACP,sEAAsE;YACtE,gEAAgE;YAChE,WAAW;YACX,IAAI,GAAG,IAAI,CAAC;QACd,CAAC;QACD,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IAEF,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,OAAO,CAAC,KAAK,KAAK,mBAAmB;YAAE,SAAS;QACpD,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS;YAAE,SAAS;QAEvE,MAAM,MAAM,GAAG,sBAAsB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACvD,IAAI,CAAC,MAAM;YAAE,SAAS;QAEtB,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,MAAM,KAAK,IAAI;YAAE,SAAS;QAE9B,MAAM,gBAAgB,GAAG,uBAAuB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC9D,IAAI,gBAAgB,EAAE,CAAC;YACrB,8BAA8B,CAC5B,OAAO,EACP,MAAM,EACN,MAAM,CAAC,IAAI,EACX,gBAAgB,CACjB,CAAC;YACF,SAAS;QACX,CAAC;QAED,aAAa,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC;AAED,gKAAgK;AAChK,SAAS,aAAa,CACpB,OAAgB,EAChB,MAAwC;IAExC,IAAI,MAAM,CAAC,UAAU,KAAK,MAAM;QAAE,OAAO;IAEzC,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;IAC3B,IAAI,MAAM,CAAC,UAAU,KAAK,WAAW,EAAE,CAAC;QACtC,OAAO,CAAC,OAAO,GAAG,GAAG,OAAO,CAAC,OAAO,IAAI,cAAc,EAAE,CAAC;IAC3D,CAAC;AACH,CAAC;AAED,SAAS,8BAA8B,CACrC,OAAgB,EAChB,MAAc,EACd,UAAkB,EAClB,OAAiB;IAEjB,MAAM,UAAU,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;IAC9C,MAAM,WAAW,GAAG,OAAO;SACxB,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QACX,MAAM,UAAU,GAAG,UAAU,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QAC/C,OAAO,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACjD,CAAC,CAAC;SACD,MAAM,CACL,CAAC,KAAK,EAAgD,EAAE,CAAC,KAAK,KAAK,IAAI,CACxE,CAAC;IAEJ,MAAM,CAAC,cAAc,CAAC,GAAG,WAAW,CAAC;IACrC,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,cAAc,EAAE,CAAC;QACvE,OAAO,CAAC,IAAI,GAAG,iBAAiB,cAAc,CAAC,UAAU,IAAI,CAAC;IAChE,CAAC;SAAM,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,OAAO,CAAC,IAAI;YACV,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC;IAC9E,CAAC;IAED,MAAM,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC;IAC9B,IAAI,WAAW,KAAK,SAAS;QAAE,OAAO;IAEtC,MAAM,QAAQ,GAAG,UAAU,CAAC,UAAU,CAAC;QACrC,CAAC,CAAC,WAAW;QACb,CAAC,CAAC,GAAG,UAAU,IAAI,WAAW,EAAE,CAAC;IACnC,aAAa,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;AAC1D,CAAC","sourcesContent":["import type { Finding } from '@ekanos/integration-schema';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport type { CliContext } from '../context';\nimport {\n acceptedKeysAt,\n extractUnrecognizedKeys,\n suggestKey,\n} from '../did-you-mean';\nimport { validationError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport { renderFindings } from '../findings-renderer';\nimport { loadProject } from '../project';\nimport { assertSchemaNotSkewed } from '../schema-skew';\nimport {\n ENCLOSING_NOTE,\n extractPathFromMessage,\n isRootPath,\n locateDotPath,\n} from '../source-locator';\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 enrichSchemaFindings(findings, loaded.projectDir);\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 // Render the shared findings block to stderr BEFORE the envelope\n // settles, human mode only — JSON mode already carries the exact same\n // findings in `data.findings` and must stay byte-identical. The closing\n // order is findings block -> short summary (via `ctx.fail`'s\n // `findingsSummary` option) -> next action: `log` doesn't care that the\n // context already settled, so it's safe to call after `fail`.\n if (!ctx.jsonMode) {\n renderFindings(ctx, findings, { cwd: loaded.projectDir });\n }\n\n const exitCode = 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 !ctx.jsonMode\n ? { findingsSummary: { command: 'ekanos validate', errorCount } }\n : undefined,\n );\n\n if (!ctx.jsonMode) {\n ctx.log(ctx.ui.nextAction('ekanos validate', 'after fixing, re-check'));\n }\n\n return exitCode;\n }\n\n const successLine = ctx.jsonMode\n ? `validate: OK — no findings for ${slugs}.`\n : ctx.ui.ok(`${ctx.ui.glyphs.ok} validate: no findings for ${slugs}.`);\n\n return ctx.succeed(data, successLine);\n}\n\n/**\n * Best-effort dot-path → `file:line` + \"did you mean?\" enrichment for\n * `definition.schema` findings — the ones `collectDefinitionFindings` builds\n * directly from a zod issue (see its doc comment in\n * `@ekanos/integration-schema`). Mutates `line` (already part of `Finding`'s\n * shape, just usually absent) and, for an unrecognized key with a close\n * match, `hint`; for a resolved-but-missing key it appends a plain-text (no\n * ANSI — `findings-renderer.ts` styles it at render time) note to `message`.\n * These are additive changes to `data.findings` in `--json` mode too — the\n * envelope's existing fields never change shape or disappear across a minor\n * release, so a partner's own JSON parsing keeps working; only new optional\n * fields are ever added.\n *\n * Every other finding, and every locator miss, passes through unchanged:\n * `source-locator.ts`'s whole design is to never guess, so a miss here is\n * silent, not an error.\n */\nfunction enrichSchemaFindings(findings: Finding[], projectDir: string): void {\n const sourceCache = new Map<string, string | null>();\n\n const readSource = (file: string): string | null => {\n const cached = sourceCache.get(file);\n if (cached !== undefined) return cached;\n\n let text: string | null;\n try {\n text = fs.readFileSync(path.resolve(projectDir, file), 'utf8');\n } catch {\n // Best-effort — an unreadable file (deleted, permissions, a race with\n // a concurrent edit) just means no enrichment, never a validate\n // failure.\n text = null;\n }\n sourceCache.set(file, text);\n return text;\n };\n\n for (const finding of findings) {\n if (finding.check !== 'definition.schema') continue;\n if (finding.line !== undefined || finding.file === undefined) continue;\n\n const parsed = extractPathFromMessage(finding.message);\n if (!parsed) continue;\n\n const source = readSource(finding.file);\n if (source === null) continue;\n\n const unrecognizedKeys = extractUnrecognizedKeys(parsed.rest);\n if (unrecognizedKeys) {\n applyUnrecognizedKeyEnrichment(\n finding,\n source,\n parsed.path,\n unrecognizedKeys,\n );\n continue;\n }\n\n applyLocation(finding, locateDotPath(source, parsed.path));\n }\n}\n\n/** Sets `line`/`message` from a locate result — shared by the plain schema-path case and the unrecognized-key case below. A `'none'` result changes nothing. */\nfunction applyLocation(\n finding: Finding,\n result: ReturnType<typeof locateDotPath>,\n): void {\n if (result.confidence === 'none') return;\n\n finding.line = result.line;\n if (result.confidence === 'enclosing') {\n finding.message = `${finding.message} ${ENCLOSING_NOTE}`;\n }\n}\n\nfunction applyUnrecognizedKeyEnrichment(\n finding: Finding,\n source: string,\n parentPath: string,\n badKeys: string[],\n): void {\n const candidates = acceptedKeysAt(parentPath);\n const suggestions = badKeys\n .map((bad) => {\n const suggestion = suggestKey(bad, candidates);\n return suggestion ? { bad, suggestion } : null;\n })\n .filter(\n (entry): entry is { bad: string; suggestion: string } => entry !== null,\n );\n\n const [onlySuggestion] = suggestions;\n if (suggestions.length === 1 && badKeys.length === 1 && onlySuggestion) {\n finding.hint = `did you mean \"${onlySuggestion.suggestion}\"?`;\n } else if (suggestions.length > 0) {\n finding.hint =\n suggestions.map((s) => `\"${s.bad}\" → \"${s.suggestion}\"`).join(', ') + '?';\n }\n\n const [firstBadKey] = badKeys;\n if (firstBadKey === undefined) return;\n\n const fullPath = isRootPath(parentPath)\n ? firstBadKey\n : `${parentPath}.${firstBadKey}`;\n applyLocation(finding, locateDotPath(source, fullPath));\n}\n"]}
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Finding } from '@ekanos/integration-schema';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport type { CliContext } from '../context';\nimport {\n acceptedKeysAt,\n extractUnrecognizedKeys,\n suggestKey,\n} from '../did-you-mean';\nimport { validationError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport { renderFindings } from '../findings-renderer';\nimport { loadProject } from '../project';\nimport { assertSchemaNotSkewed } from '../schema-skew';\nimport {\n ENCLOSING_NOTE,\n extractPathFromMessage,\n isRootPath,\n locateDotPath,\n} from '../source-locator';\nimport { pluralize } from '../ui';\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 ctx.ui.dim(\n `Validating ${loaded.integrations.length} integration` +\n `${loaded.integrations.length === 1 ? '' : 's'} (${slugs})…`,\n ),\n );\n\n const findings = await collectAllFindings(loaded);\n enrichSchemaFindings(findings, loaded.projectDir);\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 // Render the shared findings block to stderr BEFORE the envelope\n // settles, human mode only — JSON mode already carries the exact same\n // findings in `data.findings` and must stay byte-identical. The closing\n // order is findings block -> short summary (via `ctx.fail`'s\n // `findingsSummary` option) -> next action: `log` doesn't care that the\n // context already settled, so it's safe to call after `fail`.\n if (!ctx.jsonMode) {\n renderFindings(ctx, findings, { cwd: loaded.projectDir });\n }\n\n const exitCode = 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 !ctx.jsonMode\n ? { findingsSummary: { command: 'ekanos validate', errorCount } }\n : undefined,\n );\n\n if (!ctx.jsonMode) {\n ctx.log(ctx.ui.nextAction('ekanos validate', 'after fixing, re-check'));\n }\n\n return exitCode;\n }\n\n const successLine = ctx.jsonMode\n ? `validate: OK — no findings for ${slugs}.`\n : ctx.ui.result({\n title: findings.length === 0 ? 'No findings' : 'Valid',\n tone: findings.length === 0 ? 'ok' : 'warn',\n rows: [\n ['Checked', loaded.integrations.map((i) => i.slug)],\n ...(findings.length > 0\n ? [\n [\n 'Findings',\n pluralize(findings.length, 'warning or note'),\n ] as const,\n ]\n : []),\n ],\n notes:\n findings.length > 0\n ? ['None are errors. Run with --json to read them.']\n : [],\n });\n\n return ctx.succeed(data, successLine);\n}\n\n/**\n * Best-effort dot-path → `file:line` + \"did you mean?\" enrichment for\n * `definition.schema` findings — the ones `collectDefinitionFindings` builds\n * directly from a zod issue (see its doc comment in\n * `@ekanos/integration-schema`). Mutates `line` (already part of `Finding`'s\n * shape, just usually absent) and, for an unrecognized key with a close\n * match, `hint`; for a resolved-but-missing key it appends a plain-text (no\n * ANSI — `findings-renderer.ts` styles it at render time) note to `message`.\n * These are additive changes to `data.findings` in `--json` mode too — the\n * envelope's existing fields never change shape or disappear across a minor\n * release, so a partner's own JSON parsing keeps working; only new optional\n * fields are ever added.\n *\n * Every other finding, and every locator miss, passes through unchanged:\n * `source-locator.ts`'s whole design is to never guess, so a miss here is\n * silent, not an error.\n */\nfunction enrichSchemaFindings(findings: Finding[], projectDir: string): void {\n const sourceCache = new Map<string, string | null>();\n\n const readSource = (file: string): string | null => {\n const cached = sourceCache.get(file);\n if (cached !== undefined) return cached;\n\n let text: string | null;\n try {\n text = fs.readFileSync(path.resolve(projectDir, file), 'utf8');\n } catch {\n // Best-effort — an unreadable file (deleted, permissions, a race with\n // a concurrent edit) just means no enrichment, never a validate\n // failure.\n text = null;\n }\n sourceCache.set(file, text);\n return text;\n };\n\n for (const finding of findings) {\n if (finding.check !== 'definition.schema') continue;\n if (finding.line !== undefined || finding.file === undefined) continue;\n\n const parsed = extractPathFromMessage(finding.message);\n if (!parsed) continue;\n\n const source = readSource(finding.file);\n if (source === null) continue;\n\n const unrecognizedKeys = extractUnrecognizedKeys(parsed.rest);\n if (unrecognizedKeys) {\n applyUnrecognizedKeyEnrichment(\n finding,\n source,\n parsed.path,\n unrecognizedKeys,\n );\n continue;\n }\n\n applyLocation(finding, locateDotPath(source, parsed.path));\n }\n}\n\n/** Sets `line`/`message` from a locate result — shared by the plain schema-path case and the unrecognized-key case below. A `'none'` result changes nothing. */\nfunction applyLocation(\n finding: Finding,\n result: ReturnType<typeof locateDotPath>,\n): void {\n if (result.confidence === 'none') return;\n\n finding.line = result.line;\n if (result.confidence === 'enclosing') {\n finding.message = `${finding.message} ${ENCLOSING_NOTE}`;\n }\n}\n\nfunction applyUnrecognizedKeyEnrichment(\n finding: Finding,\n source: string,\n parentPath: string,\n badKeys: string[],\n): void {\n const candidates = acceptedKeysAt(parentPath);\n const suggestions = badKeys\n .map((bad) => {\n const suggestion = suggestKey(bad, candidates);\n return suggestion ? { bad, suggestion } : null;\n })\n .filter(\n (entry): entry is { bad: string; suggestion: string } => entry !== null,\n );\n\n const [onlySuggestion] = suggestions;\n if (suggestions.length === 1 && badKeys.length === 1 && onlySuggestion) {\n finding.hint = `did you mean \"${onlySuggestion.suggestion}\"?`;\n } else if (suggestions.length > 0) {\n finding.hint =\n suggestions.map((s) => `\"${s.bad}\" → \"${s.suggestion}\"`).join(', ') + '?';\n }\n\n const [firstBadKey] = badKeys;\n if (firstBadKey === undefined) return;\n\n const fullPath = isRootPath(parentPath)\n ? firstBadKey\n : `${parentPath}.${firstBadKey}`;\n applyLocation(finding, locateDotPath(source, fullPath));\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import { requireSession, resolveAuthEnvironment, resolveIdentity, } from '../auth/session.js';
2
2
  import { fetchSeatsWithOneRefresh } from '../seats.js';
3
+ import { shortHost } from '../ui.js';
3
4
  /**
4
5
  * `whoami` — prove the stored session still authenticates against Ekanos.
5
6
  *
@@ -35,12 +36,21 @@ export async function runWhoami(ctx, args) {
35
36
  expiresAt: session.expiresAt,
36
37
  credentialsPath: env.store.filePath,
37
38
  seats,
38
- }, `whoami: ${(_a = identity.email) !== null && _a !== void 0 ? _a : identity.id} on ${env.host}.` +
39
- (seats
40
- ? seats.length > 0
41
- ? ` Seats: ${seats.map((s) => `${s.slug} [${s.roles.join(', ')}]`).join(', ')}.`
42
- : ' No source seats.'
43
- : ' (source seat lookup unavailable)'));
39
+ }, ctx.ui.result({
40
+ title: 'Signed in',
41
+ rows: [
42
+ ['Account', (_a = identity.email) !== null && _a !== void 0 ? _a : identity.id],
43
+ ['Host', shortHost(env.host)],
44
+ [
45
+ 'Seats',
46
+ seats
47
+ ? seats.length > 0
48
+ ? seats.map((s) => `${s.slug} · ${s.roles.join(', ')}`)
49
+ : 'none'
50
+ : 'unavailable right now',
51
+ ],
52
+ ],
53
+ }));
44
54
  }
45
55
  async function fetchSeatsQuietly(env, session) {
46
56
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"whoami.js","sourceRoot":"","sources":["../../src/commands/whoami.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AASpD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,GAAe,EACf,IAAgB;;IAEhB,MAAM,GAAG,GAAG,sBAAsB,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE;QAC3D,UAAU,EAAE,IAAI,CAAC,GAAG;KACrB,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IAEnD,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAEjE,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAEpD,OAAO,GAAG,CAAC,OAAO,CAChB;QACE,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,IAAI,EAAE;YACJ,EAAE,EAAE,QAAQ,CAAC,EAAE;YACf,KAAK,EAAE,QAAQ,CAAC,KAAK;YACrB,IAAI,EAAE,QAAQ,CAAC,IAAI;SACpB;QACD,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,eAAe,EAAE,GAAG,CAAC,KAAK,CAAC,QAAQ;QACnC,KAAK;KACN,EACD,WAAW,MAAA,QAAQ,CAAC,KAAK,mCAAI,QAAQ,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,GAAG;QACxD,CAAC,KAAK;YACJ,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;gBAChB,CAAC,CAAC,WAAW,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;gBAChF,CAAC,CAAC,mBAAmB;YACvB,CAAC,CAAC,mCAAmC,CAAC,CAC3C,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,iBAAiB,CAC9B,GAAmD,EACnD,OAAuD;IAEvD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,wBAAwB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAE5D,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAExC,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACvE,CAAC;eAAO,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC","sourcesContent":["import {\n requireSession,\n resolveAuthEnvironment,\n resolveIdentity,\n} from '../auth/session';\nimport type { CliContext } from '../context';\nimport type { ExitCode } from '../exit-codes';\nimport { fetchSeatsWithOneRefresh } from '../seats';\n\nexport interface WhoamiArgs {\n host?: string;\n /** Where a project's ekanos.json may sit, for its optional `host` field. */\n cwd: string;\n env: Record<string, string | undefined>;\n}\n\n/**\n * `whoami` — prove the stored session still authenticates against Ekanos.\n *\n * The identity is resolved server-side by `/api/auth/cli-whoami`, which reads\n * the account through RLS as the caller. Printing the locally cached email\n * would be a cheaper lie: it would keep saying \"logged in\" after the account\n * was deleted or the token revoked.\n *\n * Exit 4 (auth_required) when there is no session or it cannot be renewed —\n * the code a script should branch on to decide whether to run `ekanos login`.\n *\n * `seats` is a best-effort add-on from the same source-visibility endpoint\n * `ekanos sources` uses, compacted to slug+roles. Its failure — network, MFA,\n * a deployment that predates the route — must never turn a working `whoami`\n * into a failing one, so any error here degrades to `seats: null` rather than\n * propagating.\n */\nexport async function runWhoami(\n ctx: CliContext,\n args: WhoamiArgs,\n): Promise<ExitCode> {\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.cwd,\n });\n const stored = requireSession(env.store, env.host);\n\n const { identity, session } = await resolveIdentity(env, stored);\n\n const seats = await fetchSeatsQuietly(env, session);\n\n return ctx.succeed(\n {\n host: env.host,\n user: {\n id: identity.id,\n email: identity.email,\n name: identity.name,\n },\n expiresAt: session.expiresAt,\n credentialsPath: env.store.filePath,\n seats,\n },\n `whoami: ${identity.email ?? identity.id} on ${env.host}.` +\n (seats\n ? seats.length > 0\n ? ` Seats: ${seats.map((s) => `${s.slug} [${s.roles.join(', ')}]`).join(', ')}.`\n : ' No source seats.'\n : ' (source seat lookup unavailable)'),\n );\n}\n\nasync function fetchSeatsQuietly(\n env: Parameters<typeof fetchSeatsWithOneRefresh>[0],\n session: Parameters<typeof fetchSeatsWithOneRefresh>[1],\n): Promise<{ slug: string; roles: string[] }[] | null> {\n try {\n const result = await fetchSeatsWithOneRefresh(env, session);\n\n if (result.status !== 'ok') return null;\n\n return result.sources.map((s) => ({ slug: s.slug, roles: s.roles }));\n } catch {\n return null;\n }\n}\n"]}
1
+ {"version":3,"file":"whoami.js","sourceRoot":"","sources":["../../src/commands/whoami.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AASlC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,GAAe,EACf,IAAgB;;IAEhB,MAAM,GAAG,GAAG,sBAAsB,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE;QAC3D,UAAU,EAAE,IAAI,CAAC,GAAG;KACrB,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IAEnD,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAEjE,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAEpD,OAAO,GAAG,CAAC,OAAO,CAChB;QACE,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,IAAI,EAAE;YACJ,EAAE,EAAE,QAAQ,CAAC,EAAE;YACf,KAAK,EAAE,QAAQ,CAAC,KAAK;YACrB,IAAI,EAAE,QAAQ,CAAC,IAAI;SACpB;QACD,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,eAAe,EAAE,GAAG,CAAC,KAAK,CAAC,QAAQ;QACnC,KAAK;KACN,EACD,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC;QACZ,KAAK,EAAE,WAAW;QAClB,IAAI,EAAE;YACJ,CAAC,SAAS,QAAE,QAAQ,CAAC,KAAK,mCAAI,QAAQ,CAAC,EAAE,CAAC;YAC1C,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC7B;gBACE,OAAO;gBACP,KAAK;oBACH,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;wBAChB,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;wBACvD,CAAC,CAAC,MAAM;oBACV,CAAC,CAAC,uBAAuB;aAC5B;SACF;KACF,CAAC,CACH,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,iBAAiB,CAC9B,GAAmD,EACnD,OAAuD;IAEvD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,wBAAwB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAE5D,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAExC,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACvE,CAAC;eAAO,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC","sourcesContent":["import {\n requireSession,\n resolveAuthEnvironment,\n resolveIdentity,\n} from '../auth/session';\nimport type { CliContext } from '../context';\nimport type { ExitCode } from '../exit-codes';\nimport { fetchSeatsWithOneRefresh } from '../seats';\nimport { shortHost } from '../ui';\n\nexport interface WhoamiArgs {\n host?: string;\n /** Where a project's ekanos.json may sit, for its optional `host` field. */\n cwd: string;\n env: Record<string, string | undefined>;\n}\n\n/**\n * `whoami` — prove the stored session still authenticates against Ekanos.\n *\n * The identity is resolved server-side by `/api/auth/cli-whoami`, which reads\n * the account through RLS as the caller. Printing the locally cached email\n * would be a cheaper lie: it would keep saying \"logged in\" after the account\n * was deleted or the token revoked.\n *\n * Exit 4 (auth_required) when there is no session or it cannot be renewed —\n * the code a script should branch on to decide whether to run `ekanos login`.\n *\n * `seats` is a best-effort add-on from the same source-visibility endpoint\n * `ekanos sources` uses, compacted to slug+roles. Its failure — network, MFA,\n * a deployment that predates the route — must never turn a working `whoami`\n * into a failing one, so any error here degrades to `seats: null` rather than\n * propagating.\n */\nexport async function runWhoami(\n ctx: CliContext,\n args: WhoamiArgs,\n): Promise<ExitCode> {\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.cwd,\n });\n const stored = requireSession(env.store, env.host);\n\n const { identity, session } = await resolveIdentity(env, stored);\n\n const seats = await fetchSeatsQuietly(env, session);\n\n return ctx.succeed(\n {\n host: env.host,\n user: {\n id: identity.id,\n email: identity.email,\n name: identity.name,\n },\n expiresAt: session.expiresAt,\n credentialsPath: env.store.filePath,\n seats,\n },\n ctx.ui.result({\n title: 'Signed in',\n rows: [\n ['Account', identity.email ?? identity.id],\n ['Host', shortHost(env.host)],\n [\n 'Seats',\n seats\n ? seats.length > 0\n ? seats.map((s) => `${s.slug} · ${s.roles.join(', ')}`)\n : 'none'\n : 'unavailable right now',\n ],\n ],\n }),\n );\n}\n\nasync function fetchSeatsQuietly(\n env: Parameters<typeof fetchSeatsWithOneRefresh>[0],\n session: Parameters<typeof fetchSeatsWithOneRefresh>[1],\n): Promise<{ slug: string; roles: string[] }[] | null> {\n try {\n const result = await fetchSeatsWithOneRefresh(env, session);\n\n if (result.status !== 'ok') return null;\n\n return result.sources.map((s) => ({ slug: s.slug, roles: s.roles }));\n } catch {\n return null;\n }\n}\n"]}
package/dist/context.js CHANGED
@@ -67,6 +67,11 @@ export class CliContext {
67
67
  * without the line ever touching stdout.
68
68
  */
69
69
  emitClaudeCodeHint(payload) {
70
+ // The protocol line is machine-readable JSON for an agent reading
71
+ // stderr. A person at a terminal (human mode on a real stderr TTY) gets
72
+ // the readable error or help text instead of a wall of JSON above it.
73
+ if (!this.jsonMode && this.stderrIsTTY)
74
+ return;
70
75
  this.stderr.write(`claude-code-hint: ${JSON.stringify(payload)}\n`);
71
76
  }
72
77
  /**
@@ -80,7 +85,9 @@ export class CliContext {
80
85
  const envelope = { ok: true, data };
81
86
  this.stdout.write(`${JSON.stringify(envelope)}\n`);
82
87
  }
83
- else {
88
+ else if (humanSummary !== '') {
89
+ // An explicit empty summary means "already said it" — a wizard flow's
90
+ // outro is the result, and a trailing line would just repeat it.
84
91
  this.stdout.write(`${humanSummary !== null && humanSummary !== void 0 ? humanSummary : 'OK'}\n`);
85
92
  }
86
93
  return EXIT_CODES.OK;
@@ -140,8 +147,8 @@ export class CliContext {
140
147
  this.stderr.write(`${this.ui.err(`${this.ui.glyphs.err} ${command} failed: ${errorCount} error${errorCount === 1 ? '' : 's'}`)}\n`);
141
148
  }
142
149
  else {
143
- this.stderr.write(`Error: ${params.message}\n`);
144
- this.stderr.write(`Hint: ${params.hint}\n`);
150
+ this.stderr.write(`${this.ui.err('Error:')} ${params.message}\n`);
151
+ this.stderr.write(`${this.ui.dim('Hint:')} ${params.hint}\n`);
145
152
  }
146
153
  return params.exitCode;
147
154
  }
@@ -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';\nimport { type Ui, createUi } from './ui';\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/** See `fail`'s doc comment — the human-mode epilogue seam for a command that already rendered a findings block. */\ninterface FindingsSummary {\n /** The command name to name in the summary line, e.g. \"ekanos validate\". */\n command: string;\n errorCount: number;\n}\n\nexport interface FailOptions {\n findingsSummary?: FindingsSummary;\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 /**\n * The style layer (`ui.ts`), resolved once against stderr — the stream\n * every human-mode render target uses — so every command shares one color\n * decision instead of each re-deriving it. See `ui.ts` for the tokens and\n * components this carries.\n */\n readonly ui: Ui;\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 this.ui = createUi({ isTTY: stderr.isTTY === true, 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 /**\n * Whether stdout is a real terminal — `wizard.ts`'s `canWizard` gates on\n * this (clack renders to stdout, unlike the rest of this CLI's human\n * output). `ctx.jsonMode` being false does not by itself guarantee this:\n * `--no-json` forces human mode even against a piped stdout.\n */\n get stdoutIsTTY(): boolean {\n return this.stdout.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 * `options.findingsSummary` is the seam for a command that has ALREADY\n * rendered the shared findings block to stderr (human mode) before calling\n * this — `validate`'s and `publish`'s gate-refusal paths. Passing it swaps\n * the default two-line `Error: … / Hint: …` epilogue (whose hint always\n * points at the JSON-only `data.findings`) for one short summary line,\n * `✖ <command> failed: <n> error(s)`, so a partner never reads the same\n * finding jargon twice. JSON mode is completely unaffected either way —\n * `error.hint` always carries the original, JSON-only-appropriate text in\n * the envelope.\n */\n fail(error: CliError, data?: unknown, options?: FailOptions): ExitCode {\n this.failWith({\n code: error.code,\n message: error.message,\n hint: error.hint,\n exitCode: error.exitCode,\n data,\n findingsSummary: options?.findingsSummary,\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 findingsSummary?: FindingsSummary;\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 if (params.findingsSummary) {\n const { command, errorCount } = params.findingsSummary;\n this.stderr.write(\n `${this.ui.err(\n `${this.ui.glyphs.err} ${command} failed: ${errorCount} error${\n errorCount === 1 ? '' : 's'\n }`,\n )}\\n`,\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';\nimport { type Ui, createUi } from './ui';\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/** See `fail`'s doc comment — the human-mode epilogue seam for a command that already rendered a findings block. */\ninterface FindingsSummary {\n /** The command name to name in the summary line, e.g. \"ekanos validate\". */\n command: string;\n errorCount: number;\n}\n\nexport interface FailOptions {\n findingsSummary?: FindingsSummary;\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 /**\n * The style layer (`ui.ts`), resolved once against stderr — the stream\n * every human-mode render target uses — so every command shares one color\n * decision instead of each re-deriving it. See `ui.ts` for the tokens and\n * components this carries.\n */\n readonly ui: Ui;\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 this.ui = createUi({ isTTY: stderr.isTTY === true, 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 /**\n * Whether stdout is a real terminal — `wizard.ts`'s `canWizard` gates on\n * this (clack renders to stdout, unlike the rest of this CLI's human\n * output). `ctx.jsonMode` being false does not by itself guarantee this:\n * `--no-json` forces human mode even against a piped stdout.\n */\n get stdoutIsTTY(): boolean {\n return this.stdout.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 // The protocol line is machine-readable JSON for an agent reading\n // stderr. A person at a terminal (human mode on a real stderr TTY) gets\n // the readable error or help text instead of a wall of JSON above it.\n if (!this.jsonMode && this.stderrIsTTY) return;\n\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 if (humanSummary !== '') {\n // An explicit empty summary means \"already said it\" — a wizard flow's\n // outro is the result, and a trailing line would just repeat it.\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 * `options.findingsSummary` is the seam for a command that has ALREADY\n * rendered the shared findings block to stderr (human mode) before calling\n * this — `validate`'s and `publish`'s gate-refusal paths. Passing it swaps\n * the default two-line `Error: … / Hint: …` epilogue (whose hint always\n * points at the JSON-only `data.findings`) for one short summary line,\n * `✖ <command> failed: <n> error(s)`, so a partner never reads the same\n * finding jargon twice. JSON mode is completely unaffected either way —\n * `error.hint` always carries the original, JSON-only-appropriate text in\n * the envelope.\n */\n fail(error: CliError, data?: unknown, options?: FailOptions): ExitCode {\n this.failWith({\n code: error.code,\n message: error.message,\n hint: error.hint,\n exitCode: error.exitCode,\n data,\n findingsSummary: options?.findingsSummary,\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 findingsSummary?: FindingsSummary;\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 if (params.findingsSummary) {\n const { command, errorCount } = params.findingsSummary;\n this.stderr.write(\n `${this.ui.err(\n `${this.ui.glyphs.err} ${command} failed: ${errorCount} error${\n errorCount === 1 ? '' : 's'\n }`,\n )}\\n`,\n );\n } else {\n this.stderr.write(`${this.ui.err('Error:')} ${params.message}\\n`);\n this.stderr.write(`${this.ui.dim('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"]}