@ekanos/cli 0.3.6 → 0.4.0
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.
- package/README.md +1 -0
- package/dist/commands/dev.js +5 -2
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/init.js +8 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/upgrade.js +58 -7
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/compatibility.d.ts +4 -1
- package/dist/compatibility.js +4 -1
- package/dist/compatibility.js.map +1 -1
- package/dist/design-findings.d.ts +22 -0
- package/dist/design-findings.js +47 -0
- package/dist/design-findings.js.map +1 -0
- package/dist/harness-scaffold.d.ts +4 -0
- package/dist/harness-scaffold.js +18 -1
- package/dist/harness-scaffold.js.map +1 -1
- package/dist/pnpm-builds.d.ts +41 -6
- package/dist/pnpm-builds.js +169 -0
- package/dist/pnpm-builds.js.map +1 -1
- package/dist/validate-findings.js +2 -0
- package/dist/validate-findings.js.map +1 -1
- package/package.json +2 -2
- package/templates/AGENTS.md.tmpl +44 -9
- package/templates/eslint.config.mjs.tmpl +62 -0
- package/templates/harness/app/[slug]/page.tsx.tmpl +1 -0
- package/templates/harness.config.entry.ts.tmpl +8 -0
- package/templates/package.json.tmpl +7 -4
- package/templates/skills/build-integration/references/widget-design.md +128 -32
- package/templates/skills/polish-widgets/SKILL.md +29 -13
- package/templates/skills/polish-widgets/references/patterns.md +90 -58
- package/templates/src/integration.ts.tmpl +50 -6
package/README.md
CHANGED
|
@@ -135,6 +135,7 @@ imply an isolation that does not exist.
|
|
|
135
135
|
app/harness-shell.tsx 'use client' — injects your registry
|
|
136
136
|
app/page.tsx server component — the index
|
|
137
137
|
app/[slug]/layout.tsx ─┐
|
|
138
|
+
app/[slug]/page.tsx │ (redirects /<slug> to /<slug>/widgets)
|
|
138
139
|
app/[slug]/widgets/page.tsx │
|
|
139
140
|
app/[slug]/widgets/[widgetId]/page.tsx
|
|
140
141
|
app/[slug]/tile/page.tsx ├─ one-line re-exports of @ekanos/harness/routes
|
package/dist/commands/dev.js
CHANGED
|
@@ -2,7 +2,7 @@ import { spawn } from 'node:child_process';
|
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import { assertHarnessCompatible } from '../compatibility.js';
|
|
4
4
|
import { preconditionError, usageError } from '../errors.js';
|
|
5
|
-
import { HARNESS_DIR, HARNESS_PACKAGE, assertShellDirIsOurs, bootstrapHarnessConfig, ensureGitignoreEntry, missingDependencies, readInstalledHarness, scaffoldShell, } from '../harness-scaffold.js';
|
|
5
|
+
import { BUILD_CACHE_DIR, HARNESS_DIR, HARNESS_PACKAGE, assertShellDirIsOurs, bootstrapHarnessConfig, ensureGitignoreEntry, missingDependencies, readInstalledHarness, scaffoldShell, } from '../harness-scaffold.js';
|
|
6
6
|
import { detectPackageManager, execArgs, formatCommand, installCommand, } from '../package-manager.js';
|
|
7
7
|
import { createOutputTail, findIgnoredBuilds, ignoredBuildsHint, } from '../pnpm-builds.js';
|
|
8
8
|
import { loadProject } from '../project.js';
|
|
@@ -99,6 +99,9 @@ export async function runDev(ctx, args) {
|
|
|
99
99
|
ctx.log(`Regenerating ${HARNESS_DIR} — ${shell.staleReasons.join('; ')}. ` +
|
|
100
100
|
`Nothing you own changed; ${config.configPath} is untouched.`);
|
|
101
101
|
}
|
|
102
|
+
if (shell.buildCacheCleared) {
|
|
103
|
+
ctx.log(` removed ${HARNESS_DIR}/${BUILD_CACHE_DIR} (Next's cache from the previous shell)`);
|
|
104
|
+
}
|
|
102
105
|
if (config.bootstrapped) {
|
|
103
106
|
ctx.log(` created harness.config.ts (yours — edit it, we never will)`);
|
|
104
107
|
}
|
|
@@ -119,7 +122,7 @@ export async function runDev(ctx, args) {
|
|
|
119
122
|
const command = (exposed ? `${EXPOSED_ENV}=1 ` : '') +
|
|
120
123
|
formatCommand(pm.bin, execArgs(pm, 'next', serverArgs));
|
|
121
124
|
const url = `http://${displayHost(host)}:${port}`;
|
|
122
|
-
const scaffold = Object.assign({ slug: loaded.primary.slug, integrations: loaded.integrations.map((i) => i.slug), projectDir: loaded.projectDir, harnessDir: shell.harnessDir, harnessVersion, previousVersion: shell.previousVersion, previousMarker: shell.previousMarker, stale: shell.stale, staleReasons: shell.staleReasons, sourceGlobs: (_b = loaded.project.sourceGlobs) !== null && _b !== void 0 ? _b : [], created: shell.created, updated: shell.updated, unchanged: shell.unchanged, configBootstrapped: config.bootstrapped, configPath: config.configPath, gitignoreUpdated }, (notice ? { toolchain: notice } : {}));
|
|
125
|
+
const scaffold = Object.assign({ slug: loaded.primary.slug, integrations: loaded.integrations.map((i) => i.slug), projectDir: loaded.projectDir, harnessDir: shell.harnessDir, harnessVersion, previousVersion: shell.previousVersion, previousMarker: shell.previousMarker, stale: shell.stale, staleReasons: shell.staleReasons, buildCacheCleared: shell.buildCacheCleared, sourceGlobs: (_b = loaded.project.sourceGlobs) !== null && _b !== void 0 ? _b : [], created: shell.created, updated: shell.updated, unchanged: shell.unchanged, configBootstrapped: config.bootstrapped, configPath: config.configPath, gitignoreUpdated }, (notice ? { toolchain: notice } : {}));
|
|
123
126
|
if (exposed) {
|
|
124
127
|
ctx.warn(`WARNING: --host ${host} exposes the harness beyond this machine. It ` +
|
|
125
128
|
'has no authentication, and it renders your fixtures and — in live ' +
|
package/dist/commands/dev.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from 'node:child_process';\nimport * as path from 'node:path';\n\nimport { assertHarnessCompatible } from '../compatibility';\nimport type { CliContext } from '../context';\nimport { preconditionError, usageError } from '../errors';\nimport {\n HARNESS_DIR,\n HARNESS_PACKAGE,\n assertShellDirIsOurs,\n bootstrapHarnessConfig,\n ensureGitignoreEntry,\n missingDependencies,\n readInstalledHarness,\n scaffoldShell,\n} from '../harness-scaffold';\nimport {\n detectPackageManager,\n execArgs,\n formatCommand,\n installCommand,\n} from '../package-manager';\nimport {\n createOutputTail,\n findIgnoredBuilds,\n ignoredBuildsHint,\n} from '../pnpm-builds';\nimport { loadProject } from '../project';\nimport { displayPath } from '../ui';\nimport { announceUpdate, checkForUpdate } from '../update-notice';\n\nexport interface DevArgs {\n /** Project directory (contains ekanos.json). Defaults to cwd. */\n dir: string;\n /** Port for the dev server. */\n port?: string;\n /**\n * Interface to bind. Defaults to loopback — see `resolveHost`. Anything\n * else is an explicit, warned-about decision to expose the harness.\n *\n * NOTE: unrelated to the Ekanos HOST the update notice checks against —\n * this is the local bind address for `next dev`, never an Ekanos origin.\n */\n host?: string;\n /** Rewrite every generated file even when its bytes already match. */\n force: boolean;\n /**\n * Explicit `--start` / `--no-start`. Undefined means \"decide from the\n * output mode\": a JSON caller is an agent or a pipe and must not be handed\n * a process that never exits.\n */\n start?: boolean;\n env?: Record<string, string | undefined>;\n}\n\nconst DEFAULT_PORT = 3100;\n\n/**\n * Loopback, NOT Next's default.\n *\n * `next dev` binds 0.0.0.0 unless told otherwise, so the harness would be\n * reachable by anyone on the same network — a coffee-shop Wi-Fi, a shared\n * office VLAN — while we printed \"http://localhost:3100\" and implied\n * otherwise. It renders a partner's integration with their fixtures and, in\n * live mode, whatever their vendor API returns; there is no auth in front of\n * any of it. Binding loopback by default costs a partner nothing and makes\n * exposure a thing they ask for.\n */\nconst DEFAULT_HOST = '127.0.0.1';\n\n/** Addresses that are only reachable from this machine. */\nconst LOOPBACK_HOSTS = new Set(['127.0.0.1', '::1', 'localhost']);\n\n/**\n * Set for the dev server when `--host` binds beyond loopback. The shell's\n * API routes (`@ekanos/harness/api`) refuse non-loopback `Host` headers\n * unless this is `1`: they hold the partner's local OAuth credentials and\n * run handlers on request, so exposing them has to be a decision — this one,\n * made after the warning below — rather than a side effect of the bind.\n */\nconst EXPOSED_ENV = 'EKANOS_HARNESS_EXPOSED';\n\n/** Where the harness generates, and reads, local dev credentials. */\nconst LOCAL_SECRETS_PATH = path.join('.ekanos', 'local-secrets.json');\n\n/**\n * `dev` — scaffold the local harness shell into the partner's project, then\n * run it with their own Next.\n *\n * The shape of this verb follows from one rule: we must be able to ship a\n * harness bugfix as an `@ekanos/harness` version bump that a partner picks up\n * with `pnpm up`, with zero edits to any file they own. So everything under\n * `.ekanos/` is generated, gitignored and rewritten freely, `harness.config.ts`\n * at the project root is written once and never touched again, and all actual\n * harness behaviour lives in the installed package.\n *\n * Two idempotency properties matter and are both tested: a second run produces\n * byte-identical files and rewrites nothing (so Next's watcher stays quiet),\n * and `.gitignore` never grows a duplicate line.\n */\nexport async function runDev(ctx: CliContext, args: DevArgs): Promise<number> {\n const loaded = loadProject(args.dir);\n const port = parsePort(args.port);\n const host = parseHost(args.host);\n const exposed = !LOOPBACK_HOSTS.has(host);\n const pm = detectPackageManager(loaded.projectDir);\n\n const notice = await checkForUpdate({ env: args.env ?? process.env });\n announceUpdate(ctx, notice);\n\n // Fail before writing anything: the shell declares no dependencies of its\n // own, so a missing one would otherwise surface as an opaque Next\n // resolution error minutes later.\n const missing = missingDependencies(loaded.projectDir);\n if (missing.length > 0) {\n throw preconditionError(\n `${missing.length} package${missing.length === 1 ? '' : 's'} the ` +\n `harness needs ${missing.length === 1 ? 'is' : 'are'} not installed ` +\n `in ${loaded.projectDir}: ${missing.join(', ')}.`,\n `Run \"${installCommand(pm, missing)}\" in ${loaded.projectDir}, then ` +\n 're-run \"ekanos dev\".',\n );\n }\n\n const harness = readInstalledHarness(loaded.projectDir);\n const harnessVersion = harness?.version;\n if (!harnessVersion) {\n throw preconditionError(\n `${HARNESS_PACKAGE} is installed but declares no version.`,\n `Reinstall it with \"${installCommand(pm, [HARNESS_PACKAGE])}\" in ` +\n `${loaded.projectDir}, then re-run \"ekanos dev\".`,\n );\n }\n\n // The templates ship with the CLI and the code they import ships with the\n // harness, so a partner can upgrade one without the other. Name that\n // mismatch here rather than letting it surface later as a module-resolution\n // error inside a shell we cheerfully certified as current.\n assertHarnessCompatible({\n harnessVersion,\n harnessManifest: harness?.manifest,\n installHint: (spec) => installCommand(pm, [spec]),\n });\n\n // Validate the shell target BEFORE writing anything at all. Bootstrapping\n // harness.config.ts first and only then discovering that .ekanos/harness is\n // a file or someone else's tree would leave the project half-scaffolded.\n assertShellDirIsOurs(path.join(loaded.projectDir, HARNESS_DIR), args.force);\n\n const config = bootstrapHarnessConfig(loaded.projectDir, loaded.integrations);\n const shell = scaffoldShell({\n projectDir: loaded.projectDir,\n slug: loaded.primary.slug,\n harnessVersion,\n force: args.force,\n sourceGlobs: loaded.project.sourceGlobs,\n });\n const gitignoreUpdated = ensureGitignoreEntry(loaded.projectDir);\n\n if (shell.stale) {\n ctx.log(\n `Regenerating ${HARNESS_DIR} — ${shell.staleReasons.join('; ')}. ` +\n `Nothing you own changed; ${config.configPath} is untouched.`,\n );\n }\n\n if (config.bootstrapped) {\n ctx.log(` created harness.config.ts (yours — edit it, we never will)`);\n }\n for (const file of shell.created) ctx.log(` created ${HARNESS_DIR}/${file}`);\n for (const file of shell.updated) ctx.log(` updated ${HARNESS_DIR}/${file}`);\n if (gitignoreUpdated) ctx.log(' updated .gitignore (.ekanos/)');\n ctx.log(\n ` live-mode OAuth: the harness generates ${LOCAL_SECRETS_PATH} on first ` +\n 'use — fill it in and follow \"Local OAuth\" on the Activation surface.',\n );\n\n // `--hostname` is passed ALWAYS, never left to Next's default, so the URL\n // we print is the URL that is actually served.\n const serverArgs = ['dev', '--port', String(port), '--hostname', host];\n const serverEnv: Record<string, string> = exposed\n ? { [EXPOSED_ENV]: '1' }\n : {};\n const command =\n (exposed ? `${EXPOSED_ENV}=1 ` : '') +\n formatCommand(pm.bin, execArgs(pm, 'next', serverArgs));\n const url = `http://${displayHost(host)}:${port}`;\n\n const scaffold = {\n slug: loaded.primary.slug,\n integrations: loaded.integrations.map((i) => i.slug),\n projectDir: loaded.projectDir,\n harnessDir: shell.harnessDir,\n harnessVersion,\n previousVersion: shell.previousVersion,\n previousMarker: shell.previousMarker,\n stale: shell.stale,\n staleReasons: shell.staleReasons,\n sourceGlobs: loaded.project.sourceGlobs ?? [],\n created: shell.created,\n updated: shell.updated,\n unchanged: shell.unchanged,\n configBootstrapped: config.bootstrapped,\n configPath: config.configPath,\n gitignoreUpdated,\n ...(notice ? { toolchain: notice } : {}),\n };\n\n if (exposed) {\n ctx.warn(\n `WARNING: --host ${host} exposes the harness beyond this machine. It ` +\n 'has no authentication, and it renders your fixtures and — in live ' +\n 'mode — real responses from your vendor API. Its API routes hold the ' +\n `client secret from ${LOCAL_SECRETS_PATH}, the tokens onTokens stored, ` +\n 'and run your handlers on request; they refuse non-loopback callers ' +\n `unless ${EXPOSED_ENV}=1, which this sets. Anyone who can reach ` +\n `${url} can read and run all of it. Omit --host to bind ${DEFAULT_HOST}.`,\n );\n }\n\n // A JSON caller is an agent or a pipe. Handing it a dev server that never\n // exits would hang its tool call forever and it would never see the\n // envelope, so JSON mode scaffolds and reports the command instead of\n // running it. `--start` overrides; `--no-start` opts out in a terminal.\n const shouldStart = args.start ?? !ctx.jsonMode;\n if (!shouldStart) {\n return ctx.succeed(\n {\n ...scaffold,\n server: { started: false, command, url, port, host, exposed },\n },\n ctx.ui.result({\n title: 'Harness ready',\n rows: [\n ['Directory', displayPath(shell.harnessDir, process.cwd())],\n ['URL', url],\n ['Start', command],\n ],\n }),\n );\n }\n\n ctx.log(ctx.ui.dim(`Starting the harness on ${url} (${command})…`));\n const tail = createOutputTail();\n const exitCode = await spawnServer({\n ctx,\n tail,\n bin: pm.bin,\n argv: execArgs(pm, 'next', serverArgs),\n cwd: shell.harnessDir,\n env: serverEnv,\n });\n\n const data = {\n ...scaffold,\n server: { started: true, command, url, port, host, exposed, exitCode },\n };\n\n if (exitCode === 0) {\n return ctx.succeed(\n data,\n ctx.ui.result({ title: 'Harness stopped', rows: [['URL', url]] }),\n );\n }\n\n const ignoredBuilds = findIgnoredBuilds(tail.text());\n\n return ctx.failWith({\n code: 'dev_server_failed',\n message: `\"${command}\" exited with code ${exitCode}.`,\n hint:\n ignoredBuilds.length > 0\n ? ignoredBuildsHint(ignoredBuilds)\n : 'Read the Next output on stderr. If it cannot resolve @ekanos/harness ' +\n `or next, reinstall dependencies in ${loaded.projectDir}; if a route ` +\n `fails to compile, delete ${shell.harnessDir} and re-run \"ekanos dev\".`,\n // Transparent passthrough of Next's own exit code.\n exitCode,\n data,\n });\n}\n\nfunction spawnServer(params: {\n ctx: CliContext;\n tail: ReturnType<typeof createOutputTail>;\n bin: string;\n argv: string[];\n cwd: string;\n env: Record<string, string>;\n}): Promise<number> {\n return new Promise<number>((resolve, reject) => {\n const child = spawn(params.bin, params.argv, {\n cwd: params.cwd,\n env: { ...process.env, ...params.env },\n // stdin inherited so Next's own key handling works; stdout/stderr piped\n // so we can route them and keep OUR stdout reserved for the envelope.\n stdio: ['inherit', 'pipe', 'pipe'],\n });\n\n child.stdout?.on('data', (chunk: Buffer) => {\n params.tail.push(chunk);\n (params.ctx.jsonMode ? process.stderr : process.stdout).write(chunk);\n });\n child.stderr?.on('data', (chunk: Buffer) => {\n params.tail.push(chunk);\n process.stderr.write(chunk);\n });\n\n child.on('error', (error) => reject(error));\n child.on('close', (code) => resolve(code ?? 1));\n });\n}\n\n/**\n * The hostname to show a human. `127.0.0.1` is spelled `localhost` because\n * that is what people type; a wildcard bind is spelled `localhost` too, but\n * only after `exposed` has already been warned about — printing `0.0.0.0` as\n * a URL is not useful.\n */\nfunction displayHost(host: string): string {\n if (host === '127.0.0.1' || host === '0.0.0.0' || host === '::') {\n return 'localhost';\n }\n if (host === '::1') return '[::1]';\n return host;\n}\n\nfunction parseHost(raw: string | undefined): string {\n if (raw === undefined) return DEFAULT_HOST;\n\n const host = raw.trim();\n if (host.length === 0) {\n throw usageError(\n 'Invalid --host \"\".',\n `Pass an interface to bind, e.g. \"--host 0.0.0.0\" to expose the ` +\n `harness on your network, or omit it to bind ${DEFAULT_HOST}.`,\n );\n }\n return host;\n}\n\nfunction parsePort(raw: string | undefined): number {\n if (raw === undefined) return DEFAULT_PORT;\n\n const port = Number(raw);\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n throw usageError(\n `Invalid --port \"${raw}\".`,\n 'Pass an integer between 1 and 65535, e.g. \"--port 3100\".',\n );\n }\n return port;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from 'node:child_process';\nimport * as path from 'node:path';\n\nimport { assertHarnessCompatible } from '../compatibility';\nimport type { CliContext } from '../context';\nimport { preconditionError, usageError } from '../errors';\nimport {\n BUILD_CACHE_DIR,\n HARNESS_DIR,\n HARNESS_PACKAGE,\n assertShellDirIsOurs,\n bootstrapHarnessConfig,\n ensureGitignoreEntry,\n missingDependencies,\n readInstalledHarness,\n scaffoldShell,\n} from '../harness-scaffold';\nimport {\n detectPackageManager,\n execArgs,\n formatCommand,\n installCommand,\n} from '../package-manager';\nimport {\n createOutputTail,\n findIgnoredBuilds,\n ignoredBuildsHint,\n} from '../pnpm-builds';\nimport { loadProject } from '../project';\nimport { displayPath } from '../ui';\nimport { announceUpdate, checkForUpdate } from '../update-notice';\n\nexport interface DevArgs {\n /** Project directory (contains ekanos.json). Defaults to cwd. */\n dir: string;\n /** Port for the dev server. */\n port?: string;\n /**\n * Interface to bind. Defaults to loopback — see `resolveHost`. Anything\n * else is an explicit, warned-about decision to expose the harness.\n *\n * NOTE: unrelated to the Ekanos HOST the update notice checks against —\n * this is the local bind address for `next dev`, never an Ekanos origin.\n */\n host?: string;\n /** Rewrite every generated file even when its bytes already match. */\n force: boolean;\n /**\n * Explicit `--start` / `--no-start`. Undefined means \"decide from the\n * output mode\": a JSON caller is an agent or a pipe and must not be handed\n * a process that never exits.\n */\n start?: boolean;\n env?: Record<string, string | undefined>;\n}\n\nconst DEFAULT_PORT = 3100;\n\n/**\n * Loopback, NOT Next's default.\n *\n * `next dev` binds 0.0.0.0 unless told otherwise, so the harness would be\n * reachable by anyone on the same network — a coffee-shop Wi-Fi, a shared\n * office VLAN — while we printed \"http://localhost:3100\" and implied\n * otherwise. It renders a partner's integration with their fixtures and, in\n * live mode, whatever their vendor API returns; there is no auth in front of\n * any of it. Binding loopback by default costs a partner nothing and makes\n * exposure a thing they ask for.\n */\nconst DEFAULT_HOST = '127.0.0.1';\n\n/** Addresses that are only reachable from this machine. */\nconst LOOPBACK_HOSTS = new Set(['127.0.0.1', '::1', 'localhost']);\n\n/**\n * Set for the dev server when `--host` binds beyond loopback. The shell's\n * API routes (`@ekanos/harness/api`) refuse non-loopback `Host` headers\n * unless this is `1`: they hold the partner's local OAuth credentials and\n * run handlers on request, so exposing them has to be a decision — this one,\n * made after the warning below — rather than a side effect of the bind.\n */\nconst EXPOSED_ENV = 'EKANOS_HARNESS_EXPOSED';\n\n/** Where the harness generates, and reads, local dev credentials. */\nconst LOCAL_SECRETS_PATH = path.join('.ekanos', 'local-secrets.json');\n\n/**\n * `dev` — scaffold the local harness shell into the partner's project, then\n * run it with their own Next.\n *\n * The shape of this verb follows from one rule: we must be able to ship a\n * harness bugfix as an `@ekanos/harness` version bump that a partner picks up\n * with `pnpm up`, with zero edits to any file they own. So everything under\n * `.ekanos/` is generated, gitignored and rewritten freely, `harness.config.ts`\n * at the project root is written once and never touched again, and all actual\n * harness behaviour lives in the installed package.\n *\n * Two idempotency properties matter and are both tested: a second run produces\n * byte-identical files and rewrites nothing (so Next's watcher stays quiet),\n * and `.gitignore` never grows a duplicate line.\n */\nexport async function runDev(ctx: CliContext, args: DevArgs): Promise<number> {\n const loaded = loadProject(args.dir);\n const port = parsePort(args.port);\n const host = parseHost(args.host);\n const exposed = !LOOPBACK_HOSTS.has(host);\n const pm = detectPackageManager(loaded.projectDir);\n\n const notice = await checkForUpdate({ env: args.env ?? process.env });\n announceUpdate(ctx, notice);\n\n // Fail before writing anything: the shell declares no dependencies of its\n // own, so a missing one would otherwise surface as an opaque Next\n // resolution error minutes later.\n const missing = missingDependencies(loaded.projectDir);\n if (missing.length > 0) {\n throw preconditionError(\n `${missing.length} package${missing.length === 1 ? '' : 's'} the ` +\n `harness needs ${missing.length === 1 ? 'is' : 'are'} not installed ` +\n `in ${loaded.projectDir}: ${missing.join(', ')}.`,\n `Run \"${installCommand(pm, missing)}\" in ${loaded.projectDir}, then ` +\n 're-run \"ekanos dev\".',\n );\n }\n\n const harness = readInstalledHarness(loaded.projectDir);\n const harnessVersion = harness?.version;\n if (!harnessVersion) {\n throw preconditionError(\n `${HARNESS_PACKAGE} is installed but declares no version.`,\n `Reinstall it with \"${installCommand(pm, [HARNESS_PACKAGE])}\" in ` +\n `${loaded.projectDir}, then re-run \"ekanos dev\".`,\n );\n }\n\n // The templates ship with the CLI and the code they import ships with the\n // harness, so a partner can upgrade one without the other. Name that\n // mismatch here rather than letting it surface later as a module-resolution\n // error inside a shell we cheerfully certified as current.\n assertHarnessCompatible({\n harnessVersion,\n harnessManifest: harness?.manifest,\n installHint: (spec) => installCommand(pm, [spec]),\n });\n\n // Validate the shell target BEFORE writing anything at all. Bootstrapping\n // harness.config.ts first and only then discovering that .ekanos/harness is\n // a file or someone else's tree would leave the project half-scaffolded.\n assertShellDirIsOurs(path.join(loaded.projectDir, HARNESS_DIR), args.force);\n\n const config = bootstrapHarnessConfig(loaded.projectDir, loaded.integrations);\n const shell = scaffoldShell({\n projectDir: loaded.projectDir,\n slug: loaded.primary.slug,\n harnessVersion,\n force: args.force,\n sourceGlobs: loaded.project.sourceGlobs,\n });\n const gitignoreUpdated = ensureGitignoreEntry(loaded.projectDir);\n\n if (shell.stale) {\n ctx.log(\n `Regenerating ${HARNESS_DIR} — ${shell.staleReasons.join('; ')}. ` +\n `Nothing you own changed; ${config.configPath} is untouched.`,\n );\n }\n if (shell.buildCacheCleared) {\n ctx.log(\n ` removed ${HARNESS_DIR}/${BUILD_CACHE_DIR} (Next's cache from the previous shell)`,\n );\n }\n\n if (config.bootstrapped) {\n ctx.log(` created harness.config.ts (yours — edit it, we never will)`);\n }\n for (const file of shell.created) ctx.log(` created ${HARNESS_DIR}/${file}`);\n for (const file of shell.updated) ctx.log(` updated ${HARNESS_DIR}/${file}`);\n if (gitignoreUpdated) ctx.log(' updated .gitignore (.ekanos/)');\n ctx.log(\n ` live-mode OAuth: the harness generates ${LOCAL_SECRETS_PATH} on first ` +\n 'use — fill it in and follow \"Local OAuth\" on the Activation surface.',\n );\n\n // `--hostname` is passed ALWAYS, never left to Next's default, so the URL\n // we print is the URL that is actually served.\n const serverArgs = ['dev', '--port', String(port), '--hostname', host];\n const serverEnv: Record<string, string> = exposed\n ? { [EXPOSED_ENV]: '1' }\n : {};\n const command =\n (exposed ? `${EXPOSED_ENV}=1 ` : '') +\n formatCommand(pm.bin, execArgs(pm, 'next', serverArgs));\n const url = `http://${displayHost(host)}:${port}`;\n\n const scaffold = {\n slug: loaded.primary.slug,\n integrations: loaded.integrations.map((i) => i.slug),\n projectDir: loaded.projectDir,\n harnessDir: shell.harnessDir,\n harnessVersion,\n previousVersion: shell.previousVersion,\n previousMarker: shell.previousMarker,\n stale: shell.stale,\n staleReasons: shell.staleReasons,\n buildCacheCleared: shell.buildCacheCleared,\n sourceGlobs: loaded.project.sourceGlobs ?? [],\n created: shell.created,\n updated: shell.updated,\n unchanged: shell.unchanged,\n configBootstrapped: config.bootstrapped,\n configPath: config.configPath,\n gitignoreUpdated,\n ...(notice ? { toolchain: notice } : {}),\n };\n\n if (exposed) {\n ctx.warn(\n `WARNING: --host ${host} exposes the harness beyond this machine. It ` +\n 'has no authentication, and it renders your fixtures and — in live ' +\n 'mode — real responses from your vendor API. Its API routes hold the ' +\n `client secret from ${LOCAL_SECRETS_PATH}, the tokens onTokens stored, ` +\n 'and run your handlers on request; they refuse non-loopback callers ' +\n `unless ${EXPOSED_ENV}=1, which this sets. Anyone who can reach ` +\n `${url} can read and run all of it. Omit --host to bind ${DEFAULT_HOST}.`,\n );\n }\n\n // A JSON caller is an agent or a pipe. Handing it a dev server that never\n // exits would hang its tool call forever and it would never see the\n // envelope, so JSON mode scaffolds and reports the command instead of\n // running it. `--start` overrides; `--no-start` opts out in a terminal.\n const shouldStart = args.start ?? !ctx.jsonMode;\n if (!shouldStart) {\n return ctx.succeed(\n {\n ...scaffold,\n server: { started: false, command, url, port, host, exposed },\n },\n ctx.ui.result({\n title: 'Harness ready',\n rows: [\n ['Directory', displayPath(shell.harnessDir, process.cwd())],\n ['URL', url],\n ['Start', command],\n ],\n }),\n );\n }\n\n ctx.log(ctx.ui.dim(`Starting the harness on ${url} (${command})…`));\n const tail = createOutputTail();\n const exitCode = await spawnServer({\n ctx,\n tail,\n bin: pm.bin,\n argv: execArgs(pm, 'next', serverArgs),\n cwd: shell.harnessDir,\n env: serverEnv,\n });\n\n const data = {\n ...scaffold,\n server: { started: true, command, url, port, host, exposed, exitCode },\n };\n\n if (exitCode === 0) {\n return ctx.succeed(\n data,\n ctx.ui.result({ title: 'Harness stopped', rows: [['URL', url]] }),\n );\n }\n\n const ignoredBuilds = findIgnoredBuilds(tail.text());\n\n return ctx.failWith({\n code: 'dev_server_failed',\n message: `\"${command}\" exited with code ${exitCode}.`,\n hint:\n ignoredBuilds.length > 0\n ? ignoredBuildsHint(ignoredBuilds)\n : 'Read the Next output on stderr. If it cannot resolve @ekanos/harness ' +\n `or next, reinstall dependencies in ${loaded.projectDir}; if a route ` +\n `fails to compile, delete ${shell.harnessDir} and re-run \"ekanos dev\".`,\n // Transparent passthrough of Next's own exit code.\n exitCode,\n data,\n });\n}\n\nfunction spawnServer(params: {\n ctx: CliContext;\n tail: ReturnType<typeof createOutputTail>;\n bin: string;\n argv: string[];\n cwd: string;\n env: Record<string, string>;\n}): Promise<number> {\n return new Promise<number>((resolve, reject) => {\n const child = spawn(params.bin, params.argv, {\n cwd: params.cwd,\n env: { ...process.env, ...params.env },\n // stdin inherited so Next's own key handling works; stdout/stderr piped\n // so we can route them and keep OUR stdout reserved for the envelope.\n stdio: ['inherit', 'pipe', 'pipe'],\n });\n\n child.stdout?.on('data', (chunk: Buffer) => {\n params.tail.push(chunk);\n (params.ctx.jsonMode ? process.stderr : process.stdout).write(chunk);\n });\n child.stderr?.on('data', (chunk: Buffer) => {\n params.tail.push(chunk);\n process.stderr.write(chunk);\n });\n\n child.on('error', (error) => reject(error));\n child.on('close', (code) => resolve(code ?? 1));\n });\n}\n\n/**\n * The hostname to show a human. `127.0.0.1` is spelled `localhost` because\n * that is what people type; a wildcard bind is spelled `localhost` too, but\n * only after `exposed` has already been warned about — printing `0.0.0.0` as\n * a URL is not useful.\n */\nfunction displayHost(host: string): string {\n if (host === '127.0.0.1' || host === '0.0.0.0' || host === '::') {\n return 'localhost';\n }\n if (host === '::1') return '[::1]';\n return host;\n}\n\nfunction parseHost(raw: string | undefined): string {\n if (raw === undefined) return DEFAULT_HOST;\n\n const host = raw.trim();\n if (host.length === 0) {\n throw usageError(\n 'Invalid --host \"\".',\n `Pass an interface to bind, e.g. \"--host 0.0.0.0\" to expose the ` +\n `harness on your network, or omit it to bind ${DEFAULT_HOST}.`,\n );\n }\n return host;\n}\n\nfunction parsePort(raw: string | undefined): number {\n if (raw === undefined) return DEFAULT_PORT;\n\n const port = Number(raw);\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n throw usageError(\n `Invalid --port \"${raw}\".`,\n 'Pass an integer between 1 and 65535, e.g. \"--port 3100\".',\n );\n }\n return port;\n}\n"]}
|
package/dist/commands/init.js
CHANGED
|
@@ -65,6 +65,14 @@ const FILES = [
|
|
|
65
65
|
// and, worse, no type error in the scaffold ever surfaces.
|
|
66
66
|
{ template: 'tsconfig.json.tmpl', dest: 'tsconfig.json' },
|
|
67
67
|
{ template: 'vitest.config.ts.tmpl', dest: 'vitest.config.ts' },
|
|
68
|
+
// The lint config, wiring up BOTH SDK presets: the capability control at
|
|
69
|
+
// `error` and the design rules at `warn`. INIT-ONLY, deliberately —
|
|
70
|
+
// `upgrade` cannot deliver it, because the `eslint` and
|
|
71
|
+
// `@typescript-eslint/parser` devDependencies and the `lint` script it
|
|
72
|
+
// needs come from the package.json merge, which only `init` runs. An
|
|
73
|
+
// existing project gets the preset by hand: add the two devDependencies,
|
|
74
|
+
// a `"lint": "eslint ."` script, and copy this template's contents.
|
|
75
|
+
{ template: 'eslint.config.mjs.tmpl', dest: 'eslint.config.mjs' },
|
|
68
76
|
// Wired via package.json's "prepare" script — see setup-hooks.mjs.tmpl's
|
|
69
77
|
// own header for what it does and why it tolerates a non-git directory.
|
|
70
78
|
{ template: 'scripts/setup-hooks.mjs.tmpl', dest: 'scripts/setup-hooks.mjs' },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { printBanner } from '../banner';\nimport type { CliContext } from '../context';\nimport { copyTreeVerbatim } from '../copy-tree';\nimport { invalidStateError, usageError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport {\n type Manifest,\n type ManifestNote,\n mergeManifest,\n serializeManifest,\n} from '../merge-manifest';\nimport {\n type EkanosProject,\n docEntryProblem,\n serializeProject,\n} from '../project';\nimport { classifyPath, readFileNoFollow, writeFileNoFollow } from '../safe-fs';\nimport { readTemplate, templatesDir, toDisplayName } from '../templates';\nimport { type Ui, displayPath, pluralize } from '../ui';\nimport { canWizard, wizard } from '../wizard';\n\n/**\n * A file init did not write, and what the partner should do about it.\n *\n * Every ERROR path in this CLI carries an imperative hint; a skip did not, and\n * that asymmetry was the bug — `skipped package.json (already exists)` hid the\n * loss of eleven dependencies and the project's module type.\n */\nexport interface SkippedFile {\n file: string;\n reason: string;\n hint: string;\n}\n\nexport interface InitArgs {\n slug?: string;\n /**\n * Explicit target directory. When omitted, init scaffolds into `./<slug>`\n * under `cwd` — never into `cwd` itself. A real partner ran `init` in a\n * populated directory and got six files sprayed into it; `--dir .` is the\n * explicit opt-in for that.\n */\n dir?: string;\n cwd: string;\n force: boolean;\n /** Accepted for non-interactive ergonomics; init never prompts regardless. */\n yes: boolean;\n /**\n * Repeatable `--docs` entries — http(s) URLs or project-relative paths to\n * the target API's documentation, written into the scaffolded ekanos.json\n * as `docs`. Absent flag means the field is simply omitted; init never\n * prompts for it.\n */\n docs?: string[];\n /**\n * Test-only override for the clack wizard gate (`canWizard` in\n * `wizard.ts`) — never set from real CLI wiring. See `RunOptions` in\n * `index.ts`.\n */\n stdinIsTTY?: boolean;\n}\n\nconst SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\n/**\n * The files init writes: template basename → destination path.\n *\n * `package.json` is handled separately (see `writeManifest`) because it is the\n * one file a partner plausibly already has — `npm init -y` before `ekanos\n * init` is the ordinary way to start — and skipping it silently costs them\n * every dependency and script.\n */\n/**\n * The agent-context files. The SDK's day-1 users are coding agents, so the\n * scaffold ships the authoring contract alongside the code: AGENTS.md is the\n * contract, CLAUDE.md points at it, and the Claude Code skill teaches the\n * iterate-validate-publish loop. Version-matched to the CLI because they live\n * in its templates.\n *\n * Exported so `ekanos upgrade` can regenerate exactly this set from the\n * NEWLY installed CLI's templates after a version bump — a stale AGENTS.md\n * teaches an agent yesterday's contract, which is as harmful as a stale\n * binary. One list, so `init` and `upgrade` can never disagree on what \"the\n * agent-context files\" even are.\n */\nexport const AGENT_CONTEXT_FILES: { template: string; dest: string }[] = [\n { template: 'AGENTS.md.tmpl', dest: 'AGENTS.md' },\n { template: 'CLAUDE.md.tmpl', dest: 'CLAUDE.md' },\n { template: 'claude-skill.md.tmpl', dest: '.claude/skills/ekanos/SKILL.md' },\n];\n\n/**\n * The docs-grounding artifacts, exported for the same reason as\n * `AGENT_CONTEXT_FILES`: `upgrade` must create these when they are missing\n * (an older project upgrading into a toolchain whose AGENTS.md and skills\n * now reference them), and one list keeps `init` and `upgrade` from ever\n * disagreeing on what they are. Unlike the agent-context files these are\n * NEVER regenerated — `docs/api-notes.md` is agent/partner-authored after\n * the scaffold and `.mcp.json` may carry local additions — only created\n * when absent.\n */\nexport const DOCS_GROUNDING_FILES: { template: string; dest: string }[] = [\n // The agent-authored snapshot of the target API — see the template itself\n // and AGENTS.md's \"API documentation\" section for the convention.\n { template: 'docs/api-notes.md.tmpl', dest: 'docs/api-notes.md' },\n // Per-project MCP config so Claude Code picks up Context7 (the docs\n // fallback in the resolution ladder) without any global setup.\n { template: 'mcp.json.tmpl', dest: '.mcp.json' },\n];\n\nconst FILES: { template: string; dest: string }[] = [\n { template: 'ekanos.json.tmpl', dest: 'ekanos.json' },\n { template: 'gitignore.tmpl', dest: '.gitignore' },\n // pnpm's build-script approval for esbuild (vitest's dependency). Without\n // it a fresh scaffold's `ekanos test` fails on the first run under pnpm —\n // see the template's own header.\n { template: 'pnpm-workspace.yaml.tmpl', dest: 'pnpm-workspace.yaml' },\n // Without this, the `typecheck` script below prints tsc's help and exits 1 —\n // and, worse, no type error in the scaffold ever surfaces.\n { template: 'tsconfig.json.tmpl', dest: 'tsconfig.json' },\n { template: 'vitest.config.ts.tmpl', dest: 'vitest.config.ts' },\n // Wired via package.json's \"prepare\" script — see setup-hooks.mjs.tmpl's\n // own header for what it does and why it tolerates a non-git directory.\n { template: 'scripts/setup-hooks.mjs.tmpl', dest: 'scripts/setup-hooks.mjs' },\n { template: 'src/integration.ts.tmpl', dest: 'src/integration.ts' },\n {\n template: 'src/integration.test.ts.tmpl',\n dest: 'src/integration.test.ts',\n },\n ...DOCS_GROUNDING_FILES,\n ...AGENT_CONTEXT_FILES,\n];\n\n/**\n * `init` — instantiate a project from the bundled template: ekanos.json, a\n * working minimal defineIntegration, a test, a vitest config (with the\n * required SDK inline block), a .gitignore, and a package.json with real\n * published deps. Idempotent: an existing file is left untouched (reported as\n * skipped) unless --force is passed. Never prompts — a missing --slug fails\n * with a usage error naming the flag.\n *\n * In human mode at a real TTY (`canWizard`), the scaffold runs behind a\n * clack `intro`/spinner/`outro` flow instead of the flat `ctx.log` lines\n * below — see `wizard.ts`. This is a restyle, not a new interaction: the\n * data written to disk and the JSON/human-non-TTY envelope are identical\n * either way, and nothing here ever waits on a prompt.\n */\nexport async function runInit(\n ctx: CliContext,\n args: InitArgs,\n): Promise<ExitCode> {\n const slug = args.slug;\n if (!slug) {\n throw usageError(\n 'init requires a slug.',\n 'Pass it explicitly: \"ekanos init --slug <your-integration> --yes\". ' +\n 'The CLI never prompts, so the slug must be on the command line.',\n );\n }\n if (!SLUG_PATTERN.test(slug)) {\n throw usageError(\n `Invalid slug \"${slug}\".`,\n 'Use kebab-case: lowercase [a-z0-9] segments joined by single hyphens, ' +\n 'e.g. \"acme-crm\".',\n );\n }\n\n // Same rule the ekanos.json schema enforces, applied at the flag so a bad\n // entry fails HERE (usage, exit 2) instead of scaffolding a project whose\n // very first `ekanos validate` cannot even load its config.\n const docs = args.docs ?? [];\n for (const entry of docs) {\n const problem = docEntryProblem(entry);\n if (problem) {\n throw usageError(\n `Invalid --docs value: ${problem}`,\n 'Pass --docs an http(s) URL or a project-relative path, e.g. ' +\n '\"--docs https://developer.example.com/openapi.json\" or ' +\n '\"--docs docs/api.md\". Repeat the flag for multiple entries.',\n );\n }\n }\n\n const useWizard = canWizard(ctx, { stdinIsTTY: args.stdinIsTTY });\n if (useWizard) wizard.intro('ekanos init');\n\n // Default to `./<slug>`, not the cwd: scaffolding six files into whatever\n // directory the partner happened to be sitting in is how a real project got\n // polluted. `--dir .` remains the explicit way to scaffold in place.\n const targetDir =\n args.dir !== undefined\n ? path.resolve(args.cwd, args.dir)\n : path.resolve(args.cwd, slug);\n const vars = { SLUG: slug, DISPLAY_NAME: toDisplayName(slug) };\n\n const created: string[] = [];\n const skipped: SkippedFile[] = [];\n\n // The project root is the guard for every write below: creating it raw is\n // fine (it is the boundary, not inside it), but everything under it goes\n // through safe-fs so a planted symlink refuses loudly instead of teleporting\n // a write outside the project. See safe-fs.ts for the threat model. The one\n // case the guard cannot see is the boundary ITSELF being a symlink — the\n // containment math is lexical against `targetDir`, so a planted link there\n // would relocate every \"contained\" write wholesale. Refuse it. (A symlinked\n // ANCESTOR — macOS's /tmp, a linked home — stays fine: only the leaf is\n // checked.)\n let targetStats: fs.Stats | null = null;\n try {\n targetStats = fs.lstatSync(targetDir);\n } catch {\n targetStats = null; // does not exist yet — ours to create\n }\n if (targetStats?.isSymbolicLink()) {\n throw invalidStateError(\n `The target directory ${targetDir} is a symlink, so the scaffold's ` +\n 'write-containment guarantees cannot hold there.',\n 'Scaffold into the real directory (pass its resolved path to --dir), ' +\n 'or remove the symlink first.',\n );\n }\n fs.mkdirSync(targetDir, { recursive: true });\n\n const spinner = useWizard ? wizard.spinner() : null;\n spinner?.start(`Scaffolding \"${slug}\"…`);\n\n for (const { template, dest } of FILES) {\n const destPath = path.join(targetDir, dest);\n const kind = classifyPath(destPath);\n // A symlink is never \"already exists\": skipping would keep the plant, and\n // writing through it is the attack — writeFileNoFollow refuses it.\n if (kind !== 'missing' && kind !== 'symlink' && !args.force) {\n skipped.push({\n file: dest,\n reason: 'it already exists',\n hint:\n `Compare it against the scaffold and merge anything you are ` +\n `missing, or re-run with --force to overwrite ${dest}.`,\n });\n continue;\n }\n\n writeFileNoFollow(\n targetDir,\n destPath,\n dest === 'ekanos.json'\n ? renderEkanosConfig(vars, docs)\n : readTemplate(template, vars),\n );\n created.push(dest);\n }\n\n const skills = copyBundledSkillTrees(targetDir, args.force);\n created.push(...skills.created);\n skipped.push(...skills.skipped);\n\n const manifest = writeManifest(targetDir, vars, args.force);\n if (manifest.kind === 'created') created.push('package.json');\n\n spinner?.stop(`Scaffolded \"${slug}\"`);\n\n if (useWizard) {\n // Per-file \"created\" detail stays collapsed into the count above — the\n // note surfaces only what needs a partner's attention: skips (they hid\n // real data loss before, see `SkippedFile`'s doc comment) and manifest\n // notes.\n wizard.note(wizardSummaryDetail({ created, skipped, manifest }), 'Summary');\n } else {\n for (const file of created) ctx.log(` created ${file}`);\n if (manifest.kind === 'merged') {\n ctx.log(\n ` merged package.json (added ${manifest.added.length} field${\n manifest.added.length === 1 ? '' : 's'\n })`,\n );\n }\n if (manifest.kind === 'unchanged') {\n ctx.log(' package.json already had everything the scaffold adds');\n }\n for (const skip of skipped) {\n ctx.log(` skipped ${skip.file} — ${skip.reason}. ${skip.hint}`);\n }\n for (const note of manifest.notes) {\n ctx.warn(` ${note.message} ${note.hint}`);\n }\n }\n\n const data = {\n slug,\n dir: targetDir,\n created,\n skipped,\n manifest: {\n action: manifest.kind,\n added: manifest.added,\n kept: manifest.kept,\n notes: manifest.notes,\n },\n };\n\n printBanner(ctx);\n\n if (useWizard) {\n const relDir = path.relative(args.cwd, targetDir) || '.';\n wizard.outro(`Next steps:\\n\\n cd ${relDir}\\n npm install\\n ekanos dev`);\n }\n\n // The wizard's note + outro already said everything; a trailing line would\n // only repeat them.\n return ctx.succeed(\n data,\n useWizard\n ? ''\n : humanResult(ctx.ui, {\n slug,\n dir: displayPath(targetDir, args.cwd),\n created: created.length,\n skipped: skipped.length,\n manifestAction: manifest.kind,\n manifestAdded: manifest.added.length,\n noteCount: manifest.notes.length,\n }),\n );\n}\n\n/**\n * The clack `note()` body for a successful wizard-mode scaffold — collapses\n * \"created\" down to a count (the flat per-file lines this replaces are the\n * `else` branch above) and surfaces only what needs a partner's attention:\n * skips and manifest notes, each with its full hint.\n */\nfunction wizardSummaryDetail(params: {\n created: string[];\n skipped: SkippedFile[];\n manifest: ManifestOutcome;\n}): string {\n const { created, skipped, manifest } = params;\n const lines: string[] = [\n `${created.length} file${created.length === 1 ? '' : 's'} written`,\n ];\n\n if (manifest.kind === 'merged') {\n lines.push(\n `package.json merged (+${manifest.added.length} field${\n manifest.added.length === 1 ? '' : 's'\n })`,\n );\n } else if (manifest.kind === 'unchanged') {\n lines.push('package.json already had everything the scaffold adds');\n }\n\n if (skipped.length > 0) {\n lines.push('', 'Skipped (already existed):');\n\n // Identical advice that only differs by file name is said once, not\n // once per file — twenty near-identical sentences hide the files.\n const groups = new Map<string, string[]>();\n for (const skip of skipped) {\n const key = skip.hint.split(skip.file).join('{file}');\n groups.set(key, [...(groups.get(key) ?? []), skip.file]);\n }\n\n for (const [hint, files] of groups) {\n if (files.length === 1) {\n lines.push(` ${files[0]} — ${hint.replace('{file}', files[0]!)}`);\n continue;\n }\n lines.push(...files.map((file) => ` ${file}`));\n lines.push(` ${hint.split('{file}').join('each file')}`);\n }\n }\n\n for (const note of manifest.notes) {\n lines.push('', `⚠ ${note.message} ${note.hint}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * The scaffolded ekanos.json: the template, plus the collected `--docs`\n * entries when there are any. Parse-then-serialize (rather than string\n * surgery) so the written file is exactly what `loadProject` will read back.\n */\nfunction renderEkanosConfig(\n vars: Readonly<Record<string, string>>,\n docs: string[],\n): string {\n const rendered = readTemplate('ekanos.json.tmpl', vars);\n if (docs.length === 0) return rendered;\n const project = JSON.parse(rendered) as EkanosProject;\n return serializeProject({ ...project, docs });\n}\n\n/**\n * Copy every bundled skill tree (`templates/skills/<name>/`) into the\n * scaffolded project at `.claude/skills/<name>/`, verbatim — see\n * `copyTreeVerbatim` for why these are bytes, never rendered templates. The\n * walk is generic: a skill directory added to the published templates ships\n * with no code change here. A CLI build with no `templates/skills/` at all\n * (today's) scaffolds nothing and reports nothing.\n *\n * `skillsRoot` is injectable for tests; real callers take the default.\n */\nexport function copyBundledSkillTrees(\n targetDir: string,\n force: boolean,\n skillsRoot = path.join(templatesDir(), 'skills'),\n onRefusal: 'throw' | 'collect' = 'throw',\n): { created: string[]; skipped: SkippedFile[]; refused: string[] } {\n if (!fs.existsSync(skillsRoot))\n return { created: [], skipped: [], refused: [] };\n\n const created: string[] = [];\n const skipped: SkippedFile[] = [];\n const refused: string[] = [];\n\n const skillDirs = fs\n .readdirSync(skillsRoot, { withFileTypes: true })\n .filter((entry) => entry.isDirectory())\n .map((entry) => entry.name)\n .sort();\n\n for (const name of skillDirs) {\n const destBase = path.join('.claude', 'skills', name);\n const result = copyTreeVerbatim(\n path.join(skillsRoot, name),\n path.join(targetDir, destBase),\n { force, root: targetDir, onRefusal },\n );\n created.push(...result.created.map((rel) => path.join(destBase, rel)));\n refused.push(...result.refused.map((rel) => path.join(destBase, rel)));\n skipped.push(\n ...result.skipped.map((rel) => {\n const file = path.join(destBase, rel);\n return {\n file,\n reason: 'it already exists',\n hint:\n `Compare it against the bundled skill and merge anything you ` +\n `are missing, or re-run with --force to overwrite ${file}.`,\n };\n }),\n );\n }\n\n return { created, skipped, refused };\n}\n\ntype ManifestOutcome = {\n kind: 'created' | 'merged' | 'unchanged';\n added: string[];\n kept: string[];\n notes: ManifestNote[];\n};\n\n/**\n * Write or MERGE package.json.\n *\n * A partner who ran `npm init -y` first had a manifest with none of the\n * scaffold's dependencies and `\"type\": \"commonjs\"` under ESM sources, and the\n * old behaviour reported that as one word: \"skipped\". Merging is additive — a\n * version or script they already set is theirs and is never touched — so the\n * ordinary `npm init -y` path now lands somewhere that installs and runs.\n */\nfunction writeManifest(\n targetDir: string,\n vars: Readonly<Record<string, string>>,\n force: boolean,\n): ManifestOutcome {\n const destPath = path.join(targetDir, 'package.json');\n const rendered = readTemplate('package.json.tmpl', vars);\n\n // readFileNoFollow refuses a symlinked package.json outright, so the write\n // below can never follow one either; null means genuinely absent.\n const existingRaw = readFileNoFollow(targetDir, destPath);\n\n if (existingRaw === null || force) {\n writeFileNoFollow(targetDir, destPath, rendered);\n return { kind: 'created', added: [], kept: [], notes: [] };\n }\n\n const template = JSON.parse(rendered) as Manifest;\n\n let existing: Manifest;\n try {\n existing = JSON.parse(existingRaw) as Manifest;\n } catch (error) {\n // Unparseable: do not guess, and do not clobber. Name the fix.\n return {\n kind: 'unchanged',\n added: [],\n kept: [],\n notes: [\n {\n field: '(root)',\n message: `package.json is not valid JSON: ${\n error instanceof Error ? error.message : String(error)\n }`,\n hint:\n 'Fix the JSON syntax, then re-run \"ekanos init\" to have the ' +\n \"scaffold's dependencies and scripts merged in.\",\n },\n ],\n };\n }\n\n const result = mergeManifest(existing, template);\n if (!result.changed) {\n return {\n kind: 'unchanged',\n added: [],\n kept: result.kept,\n notes: result.notes,\n };\n }\n\n writeFileNoFollow(targetDir, destPath, serializeManifest(result.manifest));\n return {\n kind: 'merged',\n added: result.added,\n kept: result.kept,\n notes: result.notes,\n };\n}\n\nfunction humanResult(\n ui: Ui,\n params: {\n slug: string;\n dir: string;\n created: number;\n skipped: number;\n manifestAction: ManifestOutcome['kind'];\n manifestAdded: number;\n noteCount: number;\n },\n): string {\n const files = [\n ...(params.created > 0 ? [`${params.created} written`] : []),\n ...(params.manifestAction === 'merged'\n ? [`${pluralize(params.manifestAdded, 'package.json field')} merged`]\n : []),\n ...(params.skipped > 0 ? [`${params.skipped} skipped`] : []),\n ];\n const attention = params.skipped > 0 || params.noteCount > 0;\n\n if (files.length === 0 && params.noteCount === 0) {\n return ui.result({\n title: `${params.slug} is already scaffolded`,\n rows: [['Directory', params.dir]],\n });\n }\n\n return ui.result({\n title: `Scaffolded ${params.slug}`,\n tone: attention ? 'warn' : 'ok',\n rows: [\n ['Directory', params.dir],\n ...(files.length > 0 ? [['Files', files.join(' · ')] as const] : []),\n ...(params.noteCount > 0\n ? [\n [\n 'Attention',\n `${pluralize(params.noteCount, 'note')} above`,\n ] as const,\n ]\n : []),\n [\n 'Next',\n [\n ...(params.dir === '.' ? [] : [`cd ${params.dir}`]),\n 'npm install',\n 'ekanos dev',\n ],\n ],\n ],\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { printBanner } from '../banner';\nimport type { CliContext } from '../context';\nimport { copyTreeVerbatim } from '../copy-tree';\nimport { invalidStateError, usageError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport {\n type Manifest,\n type ManifestNote,\n mergeManifest,\n serializeManifest,\n} from '../merge-manifest';\nimport {\n type EkanosProject,\n docEntryProblem,\n serializeProject,\n} from '../project';\nimport { classifyPath, readFileNoFollow, writeFileNoFollow } from '../safe-fs';\nimport { readTemplate, templatesDir, toDisplayName } from '../templates';\nimport { type Ui, displayPath, pluralize } from '../ui';\nimport { canWizard, wizard } from '../wizard';\n\n/**\n * A file init did not write, and what the partner should do about it.\n *\n * Every ERROR path in this CLI carries an imperative hint; a skip did not, and\n * that asymmetry was the bug — `skipped package.json (already exists)` hid the\n * loss of eleven dependencies and the project's module type.\n */\nexport interface SkippedFile {\n file: string;\n reason: string;\n hint: string;\n}\n\nexport interface InitArgs {\n slug?: string;\n /**\n * Explicit target directory. When omitted, init scaffolds into `./<slug>`\n * under `cwd` — never into `cwd` itself. A real partner ran `init` in a\n * populated directory and got six files sprayed into it; `--dir .` is the\n * explicit opt-in for that.\n */\n dir?: string;\n cwd: string;\n force: boolean;\n /** Accepted for non-interactive ergonomics; init never prompts regardless. */\n yes: boolean;\n /**\n * Repeatable `--docs` entries — http(s) URLs or project-relative paths to\n * the target API's documentation, written into the scaffolded ekanos.json\n * as `docs`. Absent flag means the field is simply omitted; init never\n * prompts for it.\n */\n docs?: string[];\n /**\n * Test-only override for the clack wizard gate (`canWizard` in\n * `wizard.ts`) — never set from real CLI wiring. See `RunOptions` in\n * `index.ts`.\n */\n stdinIsTTY?: boolean;\n}\n\nconst SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\n/**\n * The files init writes: template basename → destination path.\n *\n * `package.json` is handled separately (see `writeManifest`) because it is the\n * one file a partner plausibly already has — `npm init -y` before `ekanos\n * init` is the ordinary way to start — and skipping it silently costs them\n * every dependency and script.\n */\n/**\n * The agent-context files. The SDK's day-1 users are coding agents, so the\n * scaffold ships the authoring contract alongside the code: AGENTS.md is the\n * contract, CLAUDE.md points at it, and the Claude Code skill teaches the\n * iterate-validate-publish loop. Version-matched to the CLI because they live\n * in its templates.\n *\n * Exported so `ekanos upgrade` can regenerate exactly this set from the\n * NEWLY installed CLI's templates after a version bump — a stale AGENTS.md\n * teaches an agent yesterday's contract, which is as harmful as a stale\n * binary. One list, so `init` and `upgrade` can never disagree on what \"the\n * agent-context files\" even are.\n */\nexport const AGENT_CONTEXT_FILES: { template: string; dest: string }[] = [\n { template: 'AGENTS.md.tmpl', dest: 'AGENTS.md' },\n { template: 'CLAUDE.md.tmpl', dest: 'CLAUDE.md' },\n { template: 'claude-skill.md.tmpl', dest: '.claude/skills/ekanos/SKILL.md' },\n];\n\n/**\n * The docs-grounding artifacts, exported for the same reason as\n * `AGENT_CONTEXT_FILES`: `upgrade` must create these when they are missing\n * (an older project upgrading into a toolchain whose AGENTS.md and skills\n * now reference them), and one list keeps `init` and `upgrade` from ever\n * disagreeing on what they are. Unlike the agent-context files these are\n * NEVER regenerated — `docs/api-notes.md` is agent/partner-authored after\n * the scaffold and `.mcp.json` may carry local additions — only created\n * when absent.\n */\nexport const DOCS_GROUNDING_FILES: { template: string; dest: string }[] = [\n // The agent-authored snapshot of the target API — see the template itself\n // and AGENTS.md's \"API documentation\" section for the convention.\n { template: 'docs/api-notes.md.tmpl', dest: 'docs/api-notes.md' },\n // Per-project MCP config so Claude Code picks up Context7 (the docs\n // fallback in the resolution ladder) without any global setup.\n { template: 'mcp.json.tmpl', dest: '.mcp.json' },\n];\n\nconst FILES: { template: string; dest: string }[] = [\n { template: 'ekanos.json.tmpl', dest: 'ekanos.json' },\n { template: 'gitignore.tmpl', dest: '.gitignore' },\n // pnpm's build-script approval for esbuild (vitest's dependency). Without\n // it a fresh scaffold's `ekanos test` fails on the first run under pnpm —\n // see the template's own header.\n { template: 'pnpm-workspace.yaml.tmpl', dest: 'pnpm-workspace.yaml' },\n // Without this, the `typecheck` script below prints tsc's help and exits 1 —\n // and, worse, no type error in the scaffold ever surfaces.\n { template: 'tsconfig.json.tmpl', dest: 'tsconfig.json' },\n { template: 'vitest.config.ts.tmpl', dest: 'vitest.config.ts' },\n // The lint config, wiring up BOTH SDK presets: the capability control at\n // `error` and the design rules at `warn`. INIT-ONLY, deliberately —\n // `upgrade` cannot deliver it, because the `eslint` and\n // `@typescript-eslint/parser` devDependencies and the `lint` script it\n // needs come from the package.json merge, which only `init` runs. An\n // existing project gets the preset by hand: add the two devDependencies,\n // a `\"lint\": \"eslint .\"` script, and copy this template's contents.\n { template: 'eslint.config.mjs.tmpl', dest: 'eslint.config.mjs' },\n // Wired via package.json's \"prepare\" script — see setup-hooks.mjs.tmpl's\n // own header for what it does and why it tolerates a non-git directory.\n { template: 'scripts/setup-hooks.mjs.tmpl', dest: 'scripts/setup-hooks.mjs' },\n { template: 'src/integration.ts.tmpl', dest: 'src/integration.ts' },\n {\n template: 'src/integration.test.ts.tmpl',\n dest: 'src/integration.test.ts',\n },\n ...DOCS_GROUNDING_FILES,\n ...AGENT_CONTEXT_FILES,\n];\n\n/**\n * `init` — instantiate a project from the bundled template: ekanos.json, a\n * working minimal defineIntegration, a test, a vitest config (with the\n * required SDK inline block), a .gitignore, and a package.json with real\n * published deps. Idempotent: an existing file is left untouched (reported as\n * skipped) unless --force is passed. Never prompts — a missing --slug fails\n * with a usage error naming the flag.\n *\n * In human mode at a real TTY (`canWizard`), the scaffold runs behind a\n * clack `intro`/spinner/`outro` flow instead of the flat `ctx.log` lines\n * below — see `wizard.ts`. This is a restyle, not a new interaction: the\n * data written to disk and the JSON/human-non-TTY envelope are identical\n * either way, and nothing here ever waits on a prompt.\n */\nexport async function runInit(\n ctx: CliContext,\n args: InitArgs,\n): Promise<ExitCode> {\n const slug = args.slug;\n if (!slug) {\n throw usageError(\n 'init requires a slug.',\n 'Pass it explicitly: \"ekanos init --slug <your-integration> --yes\". ' +\n 'The CLI never prompts, so the slug must be on the command line.',\n );\n }\n if (!SLUG_PATTERN.test(slug)) {\n throw usageError(\n `Invalid slug \"${slug}\".`,\n 'Use kebab-case: lowercase [a-z0-9] segments joined by single hyphens, ' +\n 'e.g. \"acme-crm\".',\n );\n }\n\n // Same rule the ekanos.json schema enforces, applied at the flag so a bad\n // entry fails HERE (usage, exit 2) instead of scaffolding a project whose\n // very first `ekanos validate` cannot even load its config.\n const docs = args.docs ?? [];\n for (const entry of docs) {\n const problem = docEntryProblem(entry);\n if (problem) {\n throw usageError(\n `Invalid --docs value: ${problem}`,\n 'Pass --docs an http(s) URL or a project-relative path, e.g. ' +\n '\"--docs https://developer.example.com/openapi.json\" or ' +\n '\"--docs docs/api.md\". Repeat the flag for multiple entries.',\n );\n }\n }\n\n const useWizard = canWizard(ctx, { stdinIsTTY: args.stdinIsTTY });\n if (useWizard) wizard.intro('ekanos init');\n\n // Default to `./<slug>`, not the cwd: scaffolding six files into whatever\n // directory the partner happened to be sitting in is how a real project got\n // polluted. `--dir .` remains the explicit way to scaffold in place.\n const targetDir =\n args.dir !== undefined\n ? path.resolve(args.cwd, args.dir)\n : path.resolve(args.cwd, slug);\n const vars = { SLUG: slug, DISPLAY_NAME: toDisplayName(slug) };\n\n const created: string[] = [];\n const skipped: SkippedFile[] = [];\n\n // The project root is the guard for every write below: creating it raw is\n // fine (it is the boundary, not inside it), but everything under it goes\n // through safe-fs so a planted symlink refuses loudly instead of teleporting\n // a write outside the project. See safe-fs.ts for the threat model. The one\n // case the guard cannot see is the boundary ITSELF being a symlink — the\n // containment math is lexical against `targetDir`, so a planted link there\n // would relocate every \"contained\" write wholesale. Refuse it. (A symlinked\n // ANCESTOR — macOS's /tmp, a linked home — stays fine: only the leaf is\n // checked.)\n let targetStats: fs.Stats | null = null;\n try {\n targetStats = fs.lstatSync(targetDir);\n } catch {\n targetStats = null; // does not exist yet — ours to create\n }\n if (targetStats?.isSymbolicLink()) {\n throw invalidStateError(\n `The target directory ${targetDir} is a symlink, so the scaffold's ` +\n 'write-containment guarantees cannot hold there.',\n 'Scaffold into the real directory (pass its resolved path to --dir), ' +\n 'or remove the symlink first.',\n );\n }\n fs.mkdirSync(targetDir, { recursive: true });\n\n const spinner = useWizard ? wizard.spinner() : null;\n spinner?.start(`Scaffolding \"${slug}\"…`);\n\n for (const { template, dest } of FILES) {\n const destPath = path.join(targetDir, dest);\n const kind = classifyPath(destPath);\n // A symlink is never \"already exists\": skipping would keep the plant, and\n // writing through it is the attack — writeFileNoFollow refuses it.\n if (kind !== 'missing' && kind !== 'symlink' && !args.force) {\n skipped.push({\n file: dest,\n reason: 'it already exists',\n hint:\n `Compare it against the scaffold and merge anything you are ` +\n `missing, or re-run with --force to overwrite ${dest}.`,\n });\n continue;\n }\n\n writeFileNoFollow(\n targetDir,\n destPath,\n dest === 'ekanos.json'\n ? renderEkanosConfig(vars, docs)\n : readTemplate(template, vars),\n );\n created.push(dest);\n }\n\n const skills = copyBundledSkillTrees(targetDir, args.force);\n created.push(...skills.created);\n skipped.push(...skills.skipped);\n\n const manifest = writeManifest(targetDir, vars, args.force);\n if (manifest.kind === 'created') created.push('package.json');\n\n spinner?.stop(`Scaffolded \"${slug}\"`);\n\n if (useWizard) {\n // Per-file \"created\" detail stays collapsed into the count above — the\n // note surfaces only what needs a partner's attention: skips (they hid\n // real data loss before, see `SkippedFile`'s doc comment) and manifest\n // notes.\n wizard.note(wizardSummaryDetail({ created, skipped, manifest }), 'Summary');\n } else {\n for (const file of created) ctx.log(` created ${file}`);\n if (manifest.kind === 'merged') {\n ctx.log(\n ` merged package.json (added ${manifest.added.length} field${\n manifest.added.length === 1 ? '' : 's'\n })`,\n );\n }\n if (manifest.kind === 'unchanged') {\n ctx.log(' package.json already had everything the scaffold adds');\n }\n for (const skip of skipped) {\n ctx.log(` skipped ${skip.file} — ${skip.reason}. ${skip.hint}`);\n }\n for (const note of manifest.notes) {\n ctx.warn(` ${note.message} ${note.hint}`);\n }\n }\n\n const data = {\n slug,\n dir: targetDir,\n created,\n skipped,\n manifest: {\n action: manifest.kind,\n added: manifest.added,\n kept: manifest.kept,\n notes: manifest.notes,\n },\n };\n\n printBanner(ctx);\n\n if (useWizard) {\n const relDir = path.relative(args.cwd, targetDir) || '.';\n wizard.outro(`Next steps:\\n\\n cd ${relDir}\\n npm install\\n ekanos dev`);\n }\n\n // The wizard's note + outro already said everything; a trailing line would\n // only repeat them.\n return ctx.succeed(\n data,\n useWizard\n ? ''\n : humanResult(ctx.ui, {\n slug,\n dir: displayPath(targetDir, args.cwd),\n created: created.length,\n skipped: skipped.length,\n manifestAction: manifest.kind,\n manifestAdded: manifest.added.length,\n noteCount: manifest.notes.length,\n }),\n );\n}\n\n/**\n * The clack `note()` body for a successful wizard-mode scaffold — collapses\n * \"created\" down to a count (the flat per-file lines this replaces are the\n * `else` branch above) and surfaces only what needs a partner's attention:\n * skips and manifest notes, each with its full hint.\n */\nfunction wizardSummaryDetail(params: {\n created: string[];\n skipped: SkippedFile[];\n manifest: ManifestOutcome;\n}): string {\n const { created, skipped, manifest } = params;\n const lines: string[] = [\n `${created.length} file${created.length === 1 ? '' : 's'} written`,\n ];\n\n if (manifest.kind === 'merged') {\n lines.push(\n `package.json merged (+${manifest.added.length} field${\n manifest.added.length === 1 ? '' : 's'\n })`,\n );\n } else if (manifest.kind === 'unchanged') {\n lines.push('package.json already had everything the scaffold adds');\n }\n\n if (skipped.length > 0) {\n lines.push('', 'Skipped (already existed):');\n\n // Identical advice that only differs by file name is said once, not\n // once per file — twenty near-identical sentences hide the files.\n const groups = new Map<string, string[]>();\n for (const skip of skipped) {\n const key = skip.hint.split(skip.file).join('{file}');\n groups.set(key, [...(groups.get(key) ?? []), skip.file]);\n }\n\n for (const [hint, files] of groups) {\n if (files.length === 1) {\n lines.push(` ${files[0]} — ${hint.replace('{file}', files[0]!)}`);\n continue;\n }\n lines.push(...files.map((file) => ` ${file}`));\n lines.push(` ${hint.split('{file}').join('each file')}`);\n }\n }\n\n for (const note of manifest.notes) {\n lines.push('', `⚠ ${note.message} ${note.hint}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * The scaffolded ekanos.json: the template, plus the collected `--docs`\n * entries when there are any. Parse-then-serialize (rather than string\n * surgery) so the written file is exactly what `loadProject` will read back.\n */\nfunction renderEkanosConfig(\n vars: Readonly<Record<string, string>>,\n docs: string[],\n): string {\n const rendered = readTemplate('ekanos.json.tmpl', vars);\n if (docs.length === 0) return rendered;\n const project = JSON.parse(rendered) as EkanosProject;\n return serializeProject({ ...project, docs });\n}\n\n/**\n * Copy every bundled skill tree (`templates/skills/<name>/`) into the\n * scaffolded project at `.claude/skills/<name>/`, verbatim — see\n * `copyTreeVerbatim` for why these are bytes, never rendered templates. The\n * walk is generic: a skill directory added to the published templates ships\n * with no code change here. A CLI build with no `templates/skills/` at all\n * (today's) scaffolds nothing and reports nothing.\n *\n * `skillsRoot` is injectable for tests; real callers take the default.\n */\nexport function copyBundledSkillTrees(\n targetDir: string,\n force: boolean,\n skillsRoot = path.join(templatesDir(), 'skills'),\n onRefusal: 'throw' | 'collect' = 'throw',\n): { created: string[]; skipped: SkippedFile[]; refused: string[] } {\n if (!fs.existsSync(skillsRoot))\n return { created: [], skipped: [], refused: [] };\n\n const created: string[] = [];\n const skipped: SkippedFile[] = [];\n const refused: string[] = [];\n\n const skillDirs = fs\n .readdirSync(skillsRoot, { withFileTypes: true })\n .filter((entry) => entry.isDirectory())\n .map((entry) => entry.name)\n .sort();\n\n for (const name of skillDirs) {\n const destBase = path.join('.claude', 'skills', name);\n const result = copyTreeVerbatim(\n path.join(skillsRoot, name),\n path.join(targetDir, destBase),\n { force, root: targetDir, onRefusal },\n );\n created.push(...result.created.map((rel) => path.join(destBase, rel)));\n refused.push(...result.refused.map((rel) => path.join(destBase, rel)));\n skipped.push(\n ...result.skipped.map((rel) => {\n const file = path.join(destBase, rel);\n return {\n file,\n reason: 'it already exists',\n hint:\n `Compare it against the bundled skill and merge anything you ` +\n `are missing, or re-run with --force to overwrite ${file}.`,\n };\n }),\n );\n }\n\n return { created, skipped, refused };\n}\n\ntype ManifestOutcome = {\n kind: 'created' | 'merged' | 'unchanged';\n added: string[];\n kept: string[];\n notes: ManifestNote[];\n};\n\n/**\n * Write or MERGE package.json.\n *\n * A partner who ran `npm init -y` first had a manifest with none of the\n * scaffold's dependencies and `\"type\": \"commonjs\"` under ESM sources, and the\n * old behaviour reported that as one word: \"skipped\". Merging is additive — a\n * version or script they already set is theirs and is never touched — so the\n * ordinary `npm init -y` path now lands somewhere that installs and runs.\n */\nfunction writeManifest(\n targetDir: string,\n vars: Readonly<Record<string, string>>,\n force: boolean,\n): ManifestOutcome {\n const destPath = path.join(targetDir, 'package.json');\n const rendered = readTemplate('package.json.tmpl', vars);\n\n // readFileNoFollow refuses a symlinked package.json outright, so the write\n // below can never follow one either; null means genuinely absent.\n const existingRaw = readFileNoFollow(targetDir, destPath);\n\n if (existingRaw === null || force) {\n writeFileNoFollow(targetDir, destPath, rendered);\n return { kind: 'created', added: [], kept: [], notes: [] };\n }\n\n const template = JSON.parse(rendered) as Manifest;\n\n let existing: Manifest;\n try {\n existing = JSON.parse(existingRaw) as Manifest;\n } catch (error) {\n // Unparseable: do not guess, and do not clobber. Name the fix.\n return {\n kind: 'unchanged',\n added: [],\n kept: [],\n notes: [\n {\n field: '(root)',\n message: `package.json is not valid JSON: ${\n error instanceof Error ? error.message : String(error)\n }`,\n hint:\n 'Fix the JSON syntax, then re-run \"ekanos init\" to have the ' +\n \"scaffold's dependencies and scripts merged in.\",\n },\n ],\n };\n }\n\n const result = mergeManifest(existing, template);\n if (!result.changed) {\n return {\n kind: 'unchanged',\n added: [],\n kept: result.kept,\n notes: result.notes,\n };\n }\n\n writeFileNoFollow(targetDir, destPath, serializeManifest(result.manifest));\n return {\n kind: 'merged',\n added: result.added,\n kept: result.kept,\n notes: result.notes,\n };\n}\n\nfunction humanResult(\n ui: Ui,\n params: {\n slug: string;\n dir: string;\n created: number;\n skipped: number;\n manifestAction: ManifestOutcome['kind'];\n manifestAdded: number;\n noteCount: number;\n },\n): string {\n const files = [\n ...(params.created > 0 ? [`${params.created} written`] : []),\n ...(params.manifestAction === 'merged'\n ? [`${pluralize(params.manifestAdded, 'package.json field')} merged`]\n : []),\n ...(params.skipped > 0 ? [`${params.skipped} skipped`] : []),\n ];\n const attention = params.skipped > 0 || params.noteCount > 0;\n\n if (files.length === 0 && params.noteCount === 0) {\n return ui.result({\n title: `${params.slug} is already scaffolded`,\n rows: [['Directory', params.dir]],\n });\n }\n\n return ui.result({\n title: `Scaffolded ${params.slug}`,\n tone: attention ? 'warn' : 'ok',\n rows: [\n ['Directory', params.dir],\n ...(files.length > 0 ? [['Files', files.join(' · ')] as const] : []),\n ...(params.noteCount > 0\n ? [\n [\n 'Attention',\n `${pluralize(params.noteCount, 'note')} above`,\n ] as const,\n ]\n : []),\n [\n 'Next',\n [\n ...(params.dir === '.' ? [] : [`cd ${params.dir}`]),\n 'npm install',\n 'ekanos dev',\n ],\n ],\n ],\n });\n}\n"]}
|
package/dist/commands/upgrade.js
CHANGED
|
@@ -8,6 +8,7 @@ import { networkError, preconditionError, validationError } from '../errors.js';
|
|
|
8
8
|
import { EXIT_CODES } from '../exit-codes.js';
|
|
9
9
|
import { readInstalledPackageManifest } from '../harness-scaffold.js';
|
|
10
10
|
import { detectPackageManager, installExactArgs, lockfileName, } from '../package-manager.js';
|
|
11
|
+
import { ensureBuildScriptsAnswered, skippedBuildScriptsHint, findIgnoredBuilds, ignoredBuildsHint, isIgnoredBuildsFailure, WORKSPACE_FILE, } from '../pnpm-builds.js';
|
|
11
12
|
import { loadProject } from '../project.js';
|
|
12
13
|
import { classifyPath, writeFileNoFollow } from '../safe-fs.js';
|
|
13
14
|
import { renderTemplate, toDisplayName } from '../templates.js';
|
|
@@ -94,8 +95,26 @@ export async function runUpgrade(ctx, args, deps = {}) {
|
|
|
94
95
|
}));
|
|
95
96
|
}
|
|
96
97
|
const snapshot = snapshotProject(loaded.projectDir, pm);
|
|
98
|
+
let answeredBuildScripts = [];
|
|
99
|
+
let buildScriptsSkipped;
|
|
97
100
|
try {
|
|
98
101
|
const runInstall = (_d = deps.runInstall) !== null && _d !== void 0 ? _d : defaultRunInstall;
|
|
102
|
+
// pnpm 10+ refuses dependency build scripts nobody has answered, and a
|
|
103
|
+
// new toolchain release can grow that list (0.4.0 added @swc/core and
|
|
104
|
+
// @parcel/watcher via next-intl). A project scaffolded under an older
|
|
105
|
+
// CLI has no line for them, so answer the closure BEFORE `pnpm add`, or
|
|
106
|
+
// the install writes placeholders and exits ERR_PNPM_IGNORED_BUILDS.
|
|
107
|
+
const buildScripts = pm.name === 'pnpm'
|
|
108
|
+
? ensureBuildScriptsAnswered(loaded.projectDir)
|
|
109
|
+
: { changed: [] };
|
|
110
|
+
answeredBuildScripts = buildScripts.changed;
|
|
111
|
+
if (buildScripts.skipped) {
|
|
112
|
+
buildScriptsSkipped = buildScripts.skipped;
|
|
113
|
+
ctx.log(ctx.ui.dim(skippedBuildScriptsHint(buildScripts.skipped)));
|
|
114
|
+
}
|
|
115
|
+
if (answeredBuildScripts.length > 0) {
|
|
116
|
+
ctx.log(ctx.ui.dim(`Answered dependency build scripts in pnpm-workspace.yaml: ${answeredBuildScripts.join(', ')}.`));
|
|
117
|
+
}
|
|
99
118
|
for (const group of [
|
|
100
119
|
specs.filter((s) => !s.dev),
|
|
101
120
|
specs.filter((s) => s.dev),
|
|
@@ -109,7 +128,9 @@ export async function runUpgrade(ctx, args, deps = {}) {
|
|
|
109
128
|
return await failAndRollback(ctx, snapshot, pm, loaded.projectDir, runInstall, {
|
|
110
129
|
step: 'install',
|
|
111
130
|
exitCode: EXIT_CODES.PRECONDITION_FAILED,
|
|
112
|
-
message:
|
|
131
|
+
message: isIgnoredBuildsFailure(result.output)
|
|
132
|
+
? `"${pm.bin} ${argv.join(' ')}" exited with code ${result.code}: ${ignoredBuildsHint(findIgnoredBuilds(result.output))}`
|
|
133
|
+
: `"${pm.bin} ${argv.join(' ')}" exited with code ${result.code}.`,
|
|
113
134
|
output: result.output,
|
|
114
135
|
});
|
|
115
136
|
}
|
|
@@ -176,6 +197,7 @@ export async function runUpgrade(ctx, args, deps = {}) {
|
|
|
176
197
|
deltas,
|
|
177
198
|
installed: installedKeys,
|
|
178
199
|
skippedTransitive: skippedKeys,
|
|
200
|
+
buildScripts: Object.assign({ answered: answeredBuildScripts }, (buildScriptsSkipped ? { skipped: buildScriptsSkipped } : {})),
|
|
179
201
|
regenerated,
|
|
180
202
|
scaffolded,
|
|
181
203
|
}, successSummary(ctx.ui, installedKeys, skippedKeys, deltas, regenerated, scaffolded));
|
|
@@ -257,7 +279,7 @@ function readManifest(packageJsonPath) {
|
|
|
257
279
|
return parsed;
|
|
258
280
|
}
|
|
259
281
|
/**
|
|
260
|
-
* Copy `package.json` and the lockfile to a temp directory before anything
|
|
282
|
+
* Copy `package.json`, `pnpm-workspace.yaml` and the lockfile to a temp directory before anything
|
|
261
283
|
* installs. `restore()` copies them back (and removes a lockfile that did not
|
|
262
284
|
* exist before, if the failed install created one); `dispose()` cleans up the
|
|
263
285
|
* temp copies once the run is over, success or failure.
|
|
@@ -273,13 +295,24 @@ function snapshotProject(projectDir, pm) {
|
|
|
273
295
|
? path.join(projectDir, lockName)
|
|
274
296
|
: path.join(projectDir, lockfileName(pm));
|
|
275
297
|
const lockfileExisted = lockName !== undefined;
|
|
298
|
+
const workspacePath = path.join(projectDir, WORKSPACE_FILE);
|
|
299
|
+
const workspaceExisted = fs.existsSync(workspacePath);
|
|
276
300
|
fs.copyFileSync(packageJsonPath, path.join(tmpDir, 'package.json'));
|
|
301
|
+
if (workspaceExisted) {
|
|
302
|
+
fs.copyFileSync(workspacePath, path.join(tmpDir, 'workspace.snapshot'));
|
|
303
|
+
}
|
|
277
304
|
if (lockfileExisted) {
|
|
278
305
|
fs.copyFileSync(lockfilePath, path.join(tmpDir, 'lockfile.snapshot'));
|
|
279
306
|
}
|
|
280
307
|
return {
|
|
281
308
|
restore: () => {
|
|
282
309
|
fs.copyFileSync(path.join(tmpDir, 'package.json'), packageJsonPath);
|
|
310
|
+
if (workspaceExisted) {
|
|
311
|
+
fs.copyFileSync(path.join(tmpDir, 'workspace.snapshot'), workspacePath);
|
|
312
|
+
}
|
|
313
|
+
else if (fs.existsSync(workspacePath)) {
|
|
314
|
+
fs.rmSync(workspacePath);
|
|
315
|
+
}
|
|
283
316
|
if (lockfileExisted) {
|
|
284
317
|
fs.copyFileSync(path.join(tmpDir, 'lockfile.snapshot'), lockfilePath);
|
|
285
318
|
}
|
|
@@ -455,6 +488,11 @@ function regenerateAgentContextFiles(params) {
|
|
|
455
488
|
const updated = [];
|
|
456
489
|
const unchanged = [];
|
|
457
490
|
const needsAttention = [];
|
|
491
|
+
const skipped = [];
|
|
492
|
+
const skip = (file, reason) => {
|
|
493
|
+
needsAttention.push(file);
|
|
494
|
+
skipped.push({ file, reason });
|
|
495
|
+
};
|
|
458
496
|
for (const { template, dest } of AGENT_CONTEXT_FILES) {
|
|
459
497
|
const destPath = path.join(params.projectDir, dest);
|
|
460
498
|
const kind = classifyPath(destPath);
|
|
@@ -464,14 +502,16 @@ function regenerateAgentContextFiles(params) {
|
|
|
464
502
|
// A symlink (or anything else odd) is not ours to write through — same
|
|
465
503
|
// stance as init's scaffold pass, but reported instead of thrown: the
|
|
466
504
|
// upgrade itself already succeeded, so surface it for a human.
|
|
467
|
-
|
|
505
|
+
skip(dest, 'not-a-file');
|
|
468
506
|
continue;
|
|
469
507
|
}
|
|
470
508
|
const current = fs.readFileSync(destPath, 'utf8');
|
|
471
509
|
const oldSource = params.oldTemplates.get(template);
|
|
472
510
|
const oldRendered = oldSource === undefined ? null : renderTemplate(oldSource, vars);
|
|
473
511
|
if (oldRendered === null || current !== oldRendered) {
|
|
474
|
-
|
|
512
|
+
// Differs from what the pre-upgrade CLI rendered: edited by the partner
|
|
513
|
+
// (or the project was renamed after scaffold — same thing to us).
|
|
514
|
+
skip(dest, 'edited-locally');
|
|
475
515
|
continue;
|
|
476
516
|
}
|
|
477
517
|
const newTemplatePath = path.join(params.newTemplatesDir, template);
|
|
@@ -492,10 +532,10 @@ function regenerateAgentContextFiles(params) {
|
|
|
492
532
|
// and this runs post-rollback, so a throw here would misreport a
|
|
493
533
|
// successful toolchain upgrade as failed. Report it instead, like every
|
|
494
534
|
// other post-rollback write path in this file.
|
|
495
|
-
|
|
535
|
+
skip(dest, 'write-refused');
|
|
496
536
|
}
|
|
497
537
|
}
|
|
498
|
-
return { updated, unchanged, needsAttention };
|
|
538
|
+
return { updated, unchanged, needsAttention, skipped };
|
|
499
539
|
}
|
|
500
540
|
function describeDelta(delta) {
|
|
501
541
|
var _a;
|
|
@@ -542,7 +582,18 @@ function successSummary(ui, installedKeys, skippedKeys, deltas, regenerated, sca
|
|
|
542
582
|
'init is per-file idempotent and creates only what is missing.');
|
|
543
583
|
}
|
|
544
584
|
if (regenerated.needsAttention.length > 0) {
|
|
545
|
-
|
|
585
|
+
const edited = regenerated.skipped
|
|
586
|
+
.filter((s) => s.reason === 'edited-locally')
|
|
587
|
+
.map((s) => s.file);
|
|
588
|
+
const other = regenerated.skipped.filter((s) => s.reason !== 'edited-locally');
|
|
589
|
+
if (edited.length > 0) {
|
|
590
|
+
notes.push(`${edited.join(', ')} differ${edited.length === 1 ? 's' : ''} from what the previous CLI scaffolded (edited, or the project was renamed), so the new template was not applied — merge it by hand, or delete the file and run "ekanos init --slug <slug> --dir ." to re-create it.`);
|
|
591
|
+
}
|
|
592
|
+
for (const { file, reason } of other) {
|
|
593
|
+
notes.push(reason === 'not-a-file'
|
|
594
|
+
? `${file} is a symlink or not a regular file, so it was left alone.`
|
|
595
|
+
: `${file} could not be written (a symlinked ancestor?), so it was left alone.`);
|
|
596
|
+
}
|
|
546
597
|
}
|
|
547
598
|
return ui.result({
|
|
548
599
|
title: installedKeys.length > 0 ? 'Toolchain upgraded' : 'Nothing to install',
|