@oscharko-dev/keiko-cli 0.2.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.
Files changed (77) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/context.d.ts +3 -0
  3. package/dist/context.d.ts.map +1 -0
  4. package/dist/context.js +103 -0
  5. package/dist/doctor.d.ts +24 -0
  6. package/dist/doctor.d.ts.map +1 -0
  7. package/dist/doctor.js +108 -0
  8. package/dist/evaluate.d.ts +8 -0
  9. package/dist/evaluate.d.ts.map +1 -0
  10. package/dist/evaluate.js +270 -0
  11. package/dist/evidence.d.ts +9 -0
  12. package/dist/evidence.d.ts.map +1 -0
  13. package/dist/evidence.js +129 -0
  14. package/dist/gateway-config.d.ts +12 -0
  15. package/dist/gateway-config.d.ts.map +1 -0
  16. package/dist/gateway-config.js +19 -0
  17. package/dist/gen-tests.d.ts +8 -0
  18. package/dist/gen-tests.d.ts.map +1 -0
  19. package/dist/gen-tests.js +216 -0
  20. package/dist/index.d.ts +18 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +26 -0
  23. package/dist/init.d.ts +9 -0
  24. package/dist/init.d.ts.map +1 -0
  25. package/dist/init.js +122 -0
  26. package/dist/install-layout.d.ts +19 -0
  27. package/dist/install-layout.d.ts.map +1 -0
  28. package/dist/install-layout.js +76 -0
  29. package/dist/investigate.d.ts +9 -0
  30. package/dist/investigate.d.ts.map +1 -0
  31. package/dist/investigate.js +249 -0
  32. package/dist/launcher-paths.d.ts +4 -0
  33. package/dist/launcher-paths.d.ts.map +1 -0
  34. package/dist/launcher-paths.js +69 -0
  35. package/dist/launcher-platforms.d.ts +25 -0
  36. package/dist/launcher-platforms.d.ts.map +1 -0
  37. package/dist/launcher-platforms.js +131 -0
  38. package/dist/launcher-state.d.ts +25 -0
  39. package/dist/launcher-state.d.ts.map +1 -0
  40. package/dist/launcher-state.js +228 -0
  41. package/dist/launcher.d.ts +21 -0
  42. package/dist/launcher.d.ts.map +1 -0
  43. package/dist/launcher.js +439 -0
  44. package/dist/lifecycle.d.ts +22 -0
  45. package/dist/lifecycle.d.ts.map +1 -0
  46. package/dist/lifecycle.js +425 -0
  47. package/dist/memory.d.ts +14 -0
  48. package/dist/memory.d.ts.map +1 -0
  49. package/dist/memory.js +290 -0
  50. package/dist/models.d.ts +4 -0
  51. package/dist/models.d.ts.map +1 -0
  52. package/dist/models.js +62 -0
  53. package/dist/prompt-enhancer.d.ts +13 -0
  54. package/dist/prompt-enhancer.d.ts.map +1 -0
  55. package/dist/prompt-enhancer.js +261 -0
  56. package/dist/repair.d.ts +10 -0
  57. package/dist/repair.d.ts.map +1 -0
  58. package/dist/repair.js +402 -0
  59. package/dist/run.d.ts +10 -0
  60. package/dist/run.d.ts.map +1 -0
  61. package/dist/run.js +269 -0
  62. package/dist/runner.d.ts +7 -0
  63. package/dist/runner.d.ts.map +1 -0
  64. package/dist/runner.js +108 -0
  65. package/dist/state-paths.d.ts +43 -0
  66. package/dist/state-paths.d.ts.map +1 -0
  67. package/dist/state-paths.js +396 -0
  68. package/dist/ui.d.ts +39 -0
  69. package/dist/ui.d.ts.map +1 -0
  70. package/dist/ui.js +450 -0
  71. package/dist/uninstall.d.ts +10 -0
  72. package/dist/uninstall.d.ts.map +1 -0
  73. package/dist/uninstall.js +345 -0
  74. package/dist/verify.d.ts +3 -0
  75. package/dist/verify.d.ts.map +1 -0
  76. package/dist/verify.js +108 -0
  77. package/package.json +42 -0
@@ -0,0 +1,216 @@
1
+ // `keiko gen-tests` — generates a reviewable unit-test patch for a target file/dir/changed-set
2
+ // (ADR-0008 D9). Dry-run by default; --apply writes the tests and runs verification. The text path
3
+ // prints the reviewable proposed diff AND the #6 validation summary so a terminal reviewer sees the
4
+ // actual generated test code (AC #4/#6); --json emits the full UnitTestWorkflowReport. The gateway
5
+ // ModelPort is built from config (loadGatewayConfigFromFile); tests inject deps.model directly so no live
6
+ // gateway is needed. Exit 0 on completed/dry-run, 1 on rejected/cancelled/failed/runtime, 2 on
7
+ // usage. Mirrors runVerifyCli's flag-parse / typed-error-catch structure.
8
+ import { Gateway, ConfigInvalidError, GatewayError, assertConfiguredModel, selectConfiguredModel, redact, } from "@oscharko-dev/keiko-model-gateway";
9
+ import { GatewayModelPort } from "@oscharko-dev/keiko-harness";
10
+ import { WorkspaceError } from "@oscharko-dev/keiko-workspace";
11
+ import { generateUnitTests, renderMarkdownReport } from "@oscharko-dev/keiko-workflows";
12
+ import { loadGatewayConfigFromFile } from "./gateway-config.js";
13
+ const USAGE = `Usage:
14
+ keiko gen-tests (--file PATH | --dir PATH) [--function NAME] [--changed FILE[,FILE]]
15
+ [--apply] [--model MODEL_ID] [--config PATH] [--json] [--dir-root PATH]
16
+
17
+ Generates a reviewable unit-test patch for a target TypeScript file, function, or
18
+ module. Dry-run by default (prints the proposed diff, writes nothing); pass --apply
19
+ to write the tests and run verification through the safe tool + verification layers.
20
+ `;
21
+ // Returns the value of a `--flag value` pair, undefined if absent, or null if present without a
22
+ // value (a usage error) — identical contract to runVerifyCli's flagValue.
23
+ function flagValue(args, name) {
24
+ const i = args.indexOf(name);
25
+ if (i === -1) {
26
+ return undefined;
27
+ }
28
+ const value = args[i + 1];
29
+ return value === undefined || value.startsWith("--") ? null : value;
30
+ }
31
+ const VALUE_FLAGS = [
32
+ "--file",
33
+ "--dir",
34
+ "--function",
35
+ "--changed",
36
+ "--model",
37
+ "--config",
38
+ "--dir-root",
39
+ ];
40
+ // Reads every value flag once; returns null if any is present without a value (a usage error).
41
+ function readValueFlags(args) {
42
+ const values = {};
43
+ for (const flag of VALUE_FLAGS) {
44
+ const value = flagValue(args, flag);
45
+ if (value === null) {
46
+ return null;
47
+ }
48
+ values[flag] = value;
49
+ }
50
+ return values;
51
+ }
52
+ function parseArgs(args) {
53
+ const values = readValueFlags(args);
54
+ if (values === null) {
55
+ return null;
56
+ }
57
+ const file = values["--file"];
58
+ const dir = values["--dir"];
59
+ // Exactly one of --file / --dir is required.
60
+ if ((file === undefined) === (dir === undefined)) {
61
+ return null;
62
+ }
63
+ const changedRaw = values["--changed"];
64
+ const changedPaths = changedRaw === undefined
65
+ ? undefined
66
+ : changedRaw
67
+ .split(",")
68
+ .map((p) => p.trim())
69
+ .filter((p) => p.length > 0);
70
+ return {
71
+ file,
72
+ dir,
73
+ fn: values["--function"],
74
+ changed: changedPaths === undefined || changedPaths.length === 0 ? undefined : changedPaths,
75
+ apply: args.includes("--apply"),
76
+ model: values["--model"],
77
+ config: values["--config"],
78
+ json: args.includes("--json"),
79
+ dirRoot: values["--dir-root"] ?? ".",
80
+ };
81
+ }
82
+ // --changed composes with both --file and --dir: when present it is the authoritative target set.
83
+ function resolveTarget(parsed) {
84
+ if (parsed.changed !== undefined) {
85
+ return { kind: "changedFiles", filePaths: parsed.changed };
86
+ }
87
+ if (parsed.dir !== undefined) {
88
+ return { kind: "module", moduleDir: parsed.dir };
89
+ }
90
+ return {
91
+ kind: "file",
92
+ filePath: parsed.file ?? "",
93
+ ...(parsed.fn === undefined ? {} : { targetFunction: parsed.fn }),
94
+ };
95
+ }
96
+ // Builds a ModelPort from the gateway config, or returns a usage/runtime error code via io. The
97
+ // default selector is workflow-safe: generated test patches need tool use and structured output.
98
+ // Explicit --model remains operator-controlled after config membership checks.
99
+ function buildModel(parsed, io, env) {
100
+ try {
101
+ const path = parsed.config ?? env.KEIKO_CONFIG_FILE;
102
+ if (path === undefined) {
103
+ throw new ConfigInvalidError("no config source; pass --config PATH or set KEIKO_CONFIG_FILE");
104
+ }
105
+ const config = loadGatewayConfigFromFile(path, env);
106
+ if (parsed.model !== undefined) {
107
+ assertConfiguredModel(config, parsed.model);
108
+ }
109
+ const modelId = parsed.model ??
110
+ selectConfiguredModel(config, {
111
+ kind: "chat",
112
+ toolCalling: true,
113
+ structuredOutput: true,
114
+ });
115
+ if (modelId === undefined) {
116
+ io.err("Error: no configured workflow-capable chat model is available.\n");
117
+ return 1;
118
+ }
119
+ return { port: new GatewayModelPort(new Gateway(config)), modelId };
120
+ }
121
+ catch (error) {
122
+ if (error instanceof GatewayError) {
123
+ io.err(`Error: model gateway configuration problem — ${redact(error.message)}\n` +
124
+ `Provide a gateway config with --config PATH or KEIKO_CONFIG_FILE.\n`);
125
+ return 1;
126
+ }
127
+ throw error;
128
+ }
129
+ }
130
+ function resolveConfiguredModelId(parsed, env) {
131
+ const path = parsed.config ?? env.KEIKO_CONFIG_FILE;
132
+ if (path === undefined) {
133
+ return parsed.model ?? "default";
134
+ }
135
+ const config = loadGatewayConfigFromFile(path, env);
136
+ if (parsed.model !== undefined) {
137
+ assertConfiguredModel(config, parsed.model);
138
+ return parsed.model;
139
+ }
140
+ return selectConfiguredModel(config, {
141
+ kind: "chat",
142
+ toolCalling: true,
143
+ structuredOutput: true,
144
+ });
145
+ }
146
+ function resolveModel(parsed, io, env, deps) {
147
+ if (deps.model !== undefined) {
148
+ try {
149
+ const modelId = resolveConfiguredModelId(parsed, env);
150
+ if (modelId === undefined) {
151
+ io.err("Error: no configured workflow-capable chat model is available.\n");
152
+ return 1;
153
+ }
154
+ return { port: deps.model, modelId };
155
+ }
156
+ catch (error) {
157
+ if (error instanceof GatewayError) {
158
+ io.err(`Error: model gateway configuration problem — ${redact(error.message)}\n`);
159
+ return 1;
160
+ }
161
+ throw error;
162
+ }
163
+ }
164
+ return buildModel(parsed, io, env);
165
+ }
166
+ function printText(report, io) {
167
+ io.out(`${renderMarkdownReport(report)}\n`);
168
+ if (report.dryRunPreview !== undefined) {
169
+ io.out(`\n${report.dryRunPreview}\n`);
170
+ }
171
+ if (report.proposedDiff !== undefined) {
172
+ io.out(`\n--- proposed test patch ---\n${report.proposedDiff}\n`);
173
+ }
174
+ }
175
+ function exitCodeFor(status) {
176
+ return status === "completed" || status === "dry-run" ? 0 : 1;
177
+ }
178
+ export async function runGenTestsCli(args, io, env = {}, deps = {}) {
179
+ // Issue #640: handle --help / -h before workflow-arg validation so help discovery exits 0
180
+ // with usage on stdout, not 2 with a validation error on stderr.
181
+ if (args.includes("--help") || args.includes("-h")) {
182
+ io.out(USAGE);
183
+ return 0;
184
+ }
185
+ const parsed = parseArgs(args);
186
+ if (parsed === null) {
187
+ io.err(USAGE);
188
+ return 2;
189
+ }
190
+ const model = resolveModel(parsed, io, env, deps);
191
+ if (typeof model === "number") {
192
+ return model;
193
+ }
194
+ try {
195
+ const report = await generateUnitTests({
196
+ workspaceRoot: parsed.dirRoot,
197
+ target: resolveTarget(parsed),
198
+ apply: parsed.apply,
199
+ modelId: model.modelId,
200
+ }, { model: model.port });
201
+ if (parsed.json) {
202
+ io.out(`${JSON.stringify(report, null, 2)}\n`);
203
+ }
204
+ else {
205
+ printText(report, io);
206
+ }
207
+ return exitCodeFor(report.status);
208
+ }
209
+ catch (error) {
210
+ if (error instanceof WorkspaceError) {
211
+ io.err(`Error [${error.code}]: ${error.message}\n`);
212
+ return 1;
213
+ }
214
+ throw error;
215
+ }
216
+ }
@@ -0,0 +1,18 @@
1
+ export { runCli, type CliIo } from "./runner.js";
2
+ export { runAgentCli } from "./run.js";
3
+ export { runGenTestsCli } from "./gen-tests.js";
4
+ export { runInvestigateCli } from "./investigate.js";
5
+ export { runVerifyCli } from "./verify.js";
6
+ export { runEvaluateCli, type EvaluateDeps } from "./evaluate.js";
7
+ export { runPromptEnhancerCli, type PromptEnhancerCliDeps } from "./prompt-enhancer.js";
8
+ export { runEvidenceCli } from "./evidence.js";
9
+ export { runContextCli } from "./context.js";
10
+ export { runModelsCli } from "./models.js";
11
+ export { runInitCli, KEIKO_START_SCRIPT, KEIKO_STOP_SCRIPT, type InitCliDeps } from "./init.js";
12
+ export { runDoctorCli, type DoctorCliDeps } from "./doctor.js";
13
+ export { runRepairCli, type RepairCliDeps } from "./repair.js";
14
+ export { runUninstallCli, type UninstallCliDeps } from "./uninstall.js";
15
+ export { runLifecycleCli, type LifecycleCliDeps } from "./lifecycle.js";
16
+ export { runUiCli, parseUiArgs, waitForShutdown, type UiCliDeps } from "./ui.js";
17
+ export { runLauncherCli, type LauncherCliDeps } from "./launcher.js";
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,MAAM,EAAE,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAChG,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,eAAe,EAAE,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,26 @@
1
+ // Public barrel for the keiko-cli package (ADR-0019 §"Target Package Topology"
2
+ // row keiko-cli). The CLI command surface — runCli dispatcher, the per-command
3
+ // handlers, and the small set of CLI-layer types each handler exposes — is
4
+ // re-exported here so the root product package's bin shim
5
+ // (`src/cli/index.ts` → `dist/cli/index.js`) and the surface-parity evaluator
6
+ // (`src/evaluations/surface-parity.ts`) consume the public surface only.
7
+ // Explicit-named re-exports (no `export *`) keep the surface auditable.
8
+ export { runCli } from "./runner.js";
9
+ export { runAgentCli } from "./run.js";
10
+ export { runGenTestsCli } from "./gen-tests.js";
11
+ export { runInvestigateCli } from "./investigate.js";
12
+ export { runVerifyCli } from "./verify.js";
13
+ export { runEvaluateCli } from "./evaluate.js";
14
+ export { runPromptEnhancerCli } from "./prompt-enhancer.js";
15
+ export { runEvidenceCli } from "./evidence.js";
16
+ export { runContextCli } from "./context.js";
17
+ export { runModelsCli } from "./models.js";
18
+ export { runInitCli, KEIKO_START_SCRIPT, KEIKO_STOP_SCRIPT } from "./init.js";
19
+ export { runDoctorCli } from "./doctor.js";
20
+ export { runRepairCli } from "./repair.js";
21
+ export { runUninstallCli } from "./uninstall.js";
22
+ export { runLifecycleCli } from "./lifecycle.js";
23
+ export { runUiCli, parseUiArgs, waitForShutdown } from "./ui.js";
24
+ export { runLauncherCli } from "./launcher.js";
25
+ // gateway-config.ts is a helper module (resolveConfigPathFromArgs) used internally by
26
+ // models.ts; it does not expose a CLI command, so nothing is re-exported here.
package/dist/init.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ import type { EnvSource } from "@oscharko-dev/keiko-model-gateway";
2
+ import type { CliIo } from "./runner.js";
3
+ export declare const KEIKO_START_SCRIPT = "node ./node_modules/@oscharko-dev/keiko/dist/cli/index.js start";
4
+ export declare const KEIKO_STOP_SCRIPT = "node ./node_modules/@oscharko-dev/keiko/dist/cli/index.js stop";
5
+ export interface InitCliDeps {
6
+ readonly cwd?: string | undefined;
7
+ }
8
+ export declare function runInitCli(args: readonly string[], io: CliIo, _env: EnvSource, deps?: InitCliDeps): number;
9
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AACnE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,eAAO,MAAM,kBAAkB,oEAAoE,CAAC;AACpG,eAAO,MAAM,iBAAiB,mEAAmE,CAAC;AAkBlG,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC;AA0GD,wBAAgB,UAAU,CACxB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,EAAE,KAAK,EACT,IAAI,EAAE,SAAS,EACf,IAAI,GAAE,WAAgB,GACrB,MAAM,CAiCR"}
package/dist/init.js ADDED
@@ -0,0 +1,122 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ export const KEIKO_START_SCRIPT = "node ./node_modules/@oscharko-dev/keiko/dist/cli/index.js start";
4
+ export const KEIKO_STOP_SCRIPT = "node ./node_modules/@oscharko-dev/keiko/dist/cli/index.js stop";
5
+ const USAGE = `Usage:
6
+ keiko init [--package PATH] [--force] [--dry-run]
7
+
8
+ Adds local package.json scripts for running Keiko:
9
+ keiko:start -> node ./node_modules/@oscharko-dev/keiko/dist/cli/index.js start
10
+ keiko:stop -> node ./node_modules/@oscharko-dev/keiko/dist/cli/index.js stop
11
+
12
+ Run this from the project where @oscharko-dev/keiko is installed.
13
+ `;
14
+ const EXPECTED_SCRIPTS = {
15
+ "keiko:start": KEIKO_START_SCRIPT,
16
+ "keiko:stop": KEIKO_STOP_SCRIPT,
17
+ };
18
+ function readFlagValue(args, index) {
19
+ const value = args[index + 1];
20
+ return value === undefined || value.startsWith("--") ? null : value;
21
+ }
22
+ function parseInitArgs(args, cwd) {
23
+ let packagePath = resolve(cwd, "package.json");
24
+ let force = false;
25
+ let dryRun = false;
26
+ for (let i = 0; i < args.length; i += 1) {
27
+ const arg = args[i];
28
+ if (arg === "--help" || arg === "-h") {
29
+ return "help";
30
+ }
31
+ if (arg === "--force") {
32
+ force = true;
33
+ continue;
34
+ }
35
+ if (arg === "--dry-run") {
36
+ dryRun = true;
37
+ continue;
38
+ }
39
+ if (arg === "--package") {
40
+ const value = readFlagValue(args, i);
41
+ if (value === null)
42
+ return null;
43
+ packagePath = resolve(cwd, value);
44
+ i += 1;
45
+ continue;
46
+ }
47
+ return null;
48
+ }
49
+ return { packagePath, force, dryRun };
50
+ }
51
+ function isRecord(value) {
52
+ return typeof value === "object" && value !== null && !Array.isArray(value);
53
+ }
54
+ function stringifyPackageJson(data) {
55
+ return `${JSON.stringify(data, null, 2)}\n`;
56
+ }
57
+ function loadPackageJson(packagePath) {
58
+ if (!existsSync(packagePath)) {
59
+ return { ok: false, message: `keiko init: package.json not found at ${packagePath}.\n` };
60
+ }
61
+ let packageJson;
62
+ try {
63
+ packageJson = JSON.parse(readFileSync(packagePath, "utf8"));
64
+ }
65
+ catch {
66
+ return {
67
+ ok: false,
68
+ message: `keiko init: package.json at ${packagePath} is not valid JSON.\n`,
69
+ };
70
+ }
71
+ if (!isRecord(packageJson)) {
72
+ return { ok: false, message: "keiko init: package.json must contain a JSON object.\n" };
73
+ }
74
+ return { ok: true, packageJson };
75
+ }
76
+ function initializedPackageJson(packageJson, force) {
77
+ const existingScripts = packageJson.scripts;
78
+ if (existingScripts !== undefined && !isRecord(existingScripts)) {
79
+ return { ok: false, message: "keiko init: package.json scripts must be a JSON object.\n" };
80
+ }
81
+ const scripts = existingScripts ?? {};
82
+ const conflicts = Object.entries(EXPECTED_SCRIPTS)
83
+ .filter(([name, value]) => scripts[name] !== undefined && scripts[name] !== value)
84
+ .map(([name]) => name);
85
+ if (conflicts.length > 0 && !force) {
86
+ return {
87
+ ok: false,
88
+ message: `keiko init: package.json already defines conflicting script(s): ${conflicts.join(", ")}.\n` +
89
+ "Run `npx keiko init --force` to overwrite them.\n",
90
+ };
91
+ }
92
+ return { ok: true, value: { ...packageJson, scripts: { ...scripts, ...EXPECTED_SCRIPTS } } };
93
+ }
94
+ export function runInitCli(args, io, _env, deps = {}) {
95
+ const cwd = deps.cwd ?? process.cwd();
96
+ const parsed = parseInitArgs(args, cwd);
97
+ if (parsed === "help") {
98
+ io.out(USAGE);
99
+ return 0;
100
+ }
101
+ if (parsed === null) {
102
+ io.err(USAGE);
103
+ return 2;
104
+ }
105
+ const loaded = loadPackageJson(parsed.packagePath);
106
+ if (!loaded.ok) {
107
+ io.err(loaded.message);
108
+ return 1;
109
+ }
110
+ const initialized = initializedPackageJson(loaded.packageJson, parsed.force);
111
+ if (!initialized.ok) {
112
+ io.err(initialized.message);
113
+ return 1;
114
+ }
115
+ if (parsed.dryRun) {
116
+ io.out(stringifyPackageJson(initialized.value));
117
+ return 0;
118
+ }
119
+ writeFileSync(parsed.packagePath, stringifyPackageJson(initialized.value), "utf8");
120
+ io.out("Keiko scripts added to package.json:\n" + " npm run keiko:start\n" + " npm run keiko:stop\n");
121
+ return 0;
122
+ }
@@ -0,0 +1,19 @@
1
+ import type { EnvSource } from "@oscharko-dev/keiko-model-gateway";
2
+ export interface PreferredInstallLayout {
3
+ readonly binPath: string;
4
+ readonly staticRoot: string;
5
+ }
6
+ export interface LocalPackageInstallLayout extends PreferredInstallLayout {
7
+ readonly packageRoot: string;
8
+ }
9
+ export type KeikoBinarySource = "local-build" | "local-package" | "env-override" | "argv" | "path";
10
+ export interface KeikoBinaryResolution {
11
+ readonly binPath: string;
12
+ readonly source: KeikoBinarySource;
13
+ }
14
+ export declare function hasBuiltKeikoLayout(root: string): boolean;
15
+ export declare function localPackageRoot(cwd: string): string;
16
+ export declare function builtCheckoutLayout(cwd: string): PreferredInstallLayout | undefined;
17
+ export declare function resolvePreferredInstallLayout(cwd: string): PreferredInstallLayout | undefined;
18
+ export declare function resolveKeikoBinary(cwd: string, env?: EnvSource, argv?: readonly string[]): KeikoBinaryResolution | undefined;
19
+ //# sourceMappingURL=install-layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-layout.d.ts","sourceRoot":"","sources":["../src/install-layout.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAInE,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,yBAA0B,SAAQ,sBAAsB;IACvE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,MAAM,iBAAiB,GACzB,aAAa,GACb,eAAe,GACf,cAAc,GACd,MAAM,GACN,MAAM,CAAC;AAEX,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;CACpC;AAiBD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAKzD;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEpD;AAUD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,sBAAsB,GAAG,SAAS,CAGnF;AASD,wBAAgB,6BAA6B,CAC3C,GAAG,EAAE,MAAM,GACV,sBAAsB,GAAG,SAAS,CAEpC;AAED,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,EACX,GAAG,GAAE,SAAuB,EAC5B,IAAI,GAAE,SAAS,MAAM,EAAiB,GACrC,qBAAqB,GAAG,SAAS,CAsBnC"}
@@ -0,0 +1,76 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { isAbsolute, join, resolve } from "node:path";
3
+ const ROOT_PACKAGE_NAME = "@oscharko-dev/keiko";
4
+ function readRootPackageName(cwd) {
5
+ const packageJsonPath = join(cwd, "package.json");
6
+ if (!existsSync(packageJsonPath))
7
+ return undefined;
8
+ try {
9
+ const parsed = JSON.parse(readFileSync(packageJsonPath, "utf8"));
10
+ return typeof parsed.name === "string" ? parsed.name : undefined;
11
+ }
12
+ catch {
13
+ return undefined;
14
+ }
15
+ }
16
+ export function hasBuiltKeikoLayout(root) {
17
+ return (existsSync(resolve(root, "dist", "cli", "index.js")) &&
18
+ existsSync(resolve(root, "dist", "ui", "static", "index.html")));
19
+ }
20
+ export function localPackageRoot(cwd) {
21
+ return resolve(cwd, "node_modules", "@oscharko-dev", "keiko");
22
+ }
23
+ function builtLayoutAt(root) {
24
+ if (!hasBuiltKeikoLayout(root))
25
+ return undefined;
26
+ return {
27
+ binPath: resolve(root, "dist", "cli", "index.js"),
28
+ staticRoot: resolve(root, "dist", "ui", "static"),
29
+ };
30
+ }
31
+ export function builtCheckoutLayout(cwd) {
32
+ if (readRootPackageName(cwd) !== ROOT_PACKAGE_NAME)
33
+ return undefined;
34
+ return builtLayoutAt(cwd);
35
+ }
36
+ function localPackageLayout(cwd) {
37
+ const packageRoot = localPackageRoot(cwd);
38
+ const preferred = builtLayoutAt(packageRoot);
39
+ if (preferred === undefined)
40
+ return undefined;
41
+ return { ...preferred, packageRoot };
42
+ }
43
+ export function resolvePreferredInstallLayout(cwd) {
44
+ return builtCheckoutLayout(cwd) ?? localPackageLayout(cwd);
45
+ }
46
+ export function resolveKeikoBinary(cwd, env = process.env, argv = process.argv) {
47
+ const checks = [
48
+ { source: "env-override", binPath: absoluteExistingPath(env.KEIKO_CLI_BIN_PATH ?? process.env.KEIKO_CLI_BIN_PATH) },
49
+ { source: "argv", binPath: absoluteExistingPath(argv[1]) },
50
+ { source: "local-build", binPath: builtCheckoutLayout(cwd)?.binPath },
51
+ { source: "local-package", binPath: localPackageLayout(cwd)?.binPath },
52
+ ];
53
+ for (const candidate of checks) {
54
+ if (candidate.binPath !== undefined) {
55
+ return { source: candidate.source, binPath: candidate.binPath };
56
+ }
57
+ }
58
+ const pathHit = resolveKeikoBinaryFromPath(process.platform, env.PATH ?? process.env.PATH);
59
+ if (pathHit !== undefined) {
60
+ return { source: "path", binPath: pathHit };
61
+ }
62
+ return undefined;
63
+ }
64
+ function absoluteExistingPath(value) {
65
+ return typeof value === "string" && isAbsolute(value) && existsSync(value) ? value : undefined;
66
+ }
67
+ function resolveKeikoBinaryFromPath(platform, pathValue) {
68
+ if (typeof pathValue !== "string" || pathValue.length === 0)
69
+ return undefined;
70
+ const delimiter = platform === "win32" ? ";" : ":";
71
+ const names = platform === "win32" ? ["keiko.cmd", "keiko.exe", "keiko.bat", "keiko"] : ["keiko"];
72
+ return pathValue
73
+ .split(delimiter)
74
+ .flatMap((directory) => names.map((name) => join(directory, name)))
75
+ .find((candidate) => existsSync(candidate));
76
+ }
@@ -0,0 +1,9 @@
1
+ import { type EnvSource } from "@oscharko-dev/keiko-model-gateway";
2
+ import { type ModelPort } from "@oscharko-dev/keiko-harness";
3
+ import type { CliIo } from "./runner.js";
4
+ export interface InvestigateDeps {
5
+ readonly model?: ModelPort | undefined;
6
+ readonly readFile?: ((path: string) => string) | undefined;
7
+ }
8
+ export declare function runInvestigateCli(args: readonly string[], io: CliIo, env?: EnvSource, deps?: InvestigateDeps): Promise<number>;
9
+ //# sourceMappingURL=investigate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"investigate.d.ts","sourceRoot":"","sources":["../src/investigate.ts"],"names":[],"mappings":"AAUA,OAAO,EAEL,KAAK,SAAS,EAMf,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAU/E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAczC,MAAM,WAAW,eAAe;IAE9B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAEvC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,SAAS,CAAC;CAC5D;AA4OD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,EAAE,KAAK,EACT,GAAG,GAAE,SAAc,EACnB,IAAI,GAAE,eAAoB,GACzB,OAAO,CAAC,MAAM,CAAC,CAgCjB"}