@aid-framework/cli 0.0.0 → 0.1.48

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 (76) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +225 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/args.d.ts +37 -0
  5. package/dist/args.d.ts.map +1 -0
  6. package/dist/args.js +74 -0
  7. package/dist/args.js.map +1 -0
  8. package/dist/cli.d.ts +17 -0
  9. package/dist/cli.d.ts.map +1 -0
  10. package/dist/cli.js +26 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/commands/eval.d.ts +44 -0
  13. package/dist/commands/eval.d.ts.map +1 -0
  14. package/dist/commands/eval.js +257 -0
  15. package/dist/commands/eval.js.map +1 -0
  16. package/dist/commands/gen.d.ts +42 -0
  17. package/dist/commands/gen.d.ts.map +1 -0
  18. package/dist/commands/gen.js +138 -0
  19. package/dist/commands/gen.js.map +1 -0
  20. package/dist/commands/ingest.d.ts +44 -0
  21. package/dist/commands/ingest.d.ts.map +1 -0
  22. package/dist/commands/ingest.js +238 -0
  23. package/dist/commands/ingest.js.map +1 -0
  24. package/dist/commands/init.d.ts +38 -0
  25. package/dist/commands/init.d.ts.map +1 -0
  26. package/dist/commands/init.js +203 -0
  27. package/dist/commands/init.js.map +1 -0
  28. package/dist/commands/validate.d.ts +22 -0
  29. package/dist/commands/validate.d.ts.map +1 -0
  30. package/dist/commands/validate.js +53 -0
  31. package/dist/commands/validate.js.map +1 -0
  32. package/dist/context.d.ts +37 -0
  33. package/dist/context.d.ts.map +1 -0
  34. package/dist/context.js +36 -0
  35. package/dist/context.js.map +1 -0
  36. package/dist/diagnostics.d.ts +42 -0
  37. package/dist/diagnostics.d.ts.map +1 -0
  38. package/dist/diagnostics.js +47 -0
  39. package/dist/diagnostics.js.map +1 -0
  40. package/dist/exit.d.ts +16 -0
  41. package/dist/exit.d.ts.map +1 -0
  42. package/dist/exit.js +15 -0
  43. package/dist/exit.js.map +1 -0
  44. package/dist/index.d.ts +42 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +29 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/main.d.ts +25 -0
  49. package/dist/main.d.ts.map +1 -0
  50. package/dist/main.js +155 -0
  51. package/dist/main.js.map +1 -0
  52. package/dist/output.d.ts +48 -0
  53. package/dist/output.d.ts.map +1 -0
  54. package/dist/output.js +50 -0
  55. package/dist/output.js.map +1 -0
  56. package/dist/paths.d.ts +21 -0
  57. package/dist/paths.d.ts.map +1 -0
  58. package/dist/paths.js +31 -0
  59. package/dist/paths.js.map +1 -0
  60. package/dist/pipeline.d.ts +74 -0
  61. package/dist/pipeline.d.ts.map +1 -0
  62. package/dist/pipeline.js +113 -0
  63. package/dist/pipeline.js.map +1 -0
  64. package/dist/python.d.ts +30 -0
  65. package/dist/python.d.ts.map +1 -0
  66. package/dist/python.js +57 -0
  67. package/dist/python.js.map +1 -0
  68. package/dist/targets.d.ts +75 -0
  69. package/dist/targets.d.ts.map +1 -0
  70. package/dist/targets.js +136 -0
  71. package/dist/targets.js.map +1 -0
  72. package/dist/usage.d.ts +20 -0
  73. package/dist/usage.d.ts.map +1 -0
  74. package/dist/usage.js +49 -0
  75. package/dist/usage.js.map +1 -0
  76. package/package.json +49 -14
@@ -0,0 +1,257 @@
1
+ /**
2
+ * `aid eval` — run the eval gate, or record the baseline it measures against.
3
+ *
4
+ * **What this shells out to, and why.**
5
+ *
6
+ * The assignment sketched `uv run python -m aid_runtime.evals ...`, but that module is not
7
+ * runnable: there is no `__main__.py`, no console script, and no `argparse` anywhere under
8
+ * `runtimes/python/src/aid_runtime/evals/`. The command it names does not exist.
9
+ *
10
+ * What does exist, one per eval, is the module the generator emits:
11
+ * `app/generated/evals/<id>.py`, with a `main()` that prints `result.summary()` and exits
12
+ * non-zero when the gate fails. That module is the intended surface -- it is generated code
13
+ * the spec already declares, and it is what the committed `app/evals/*.baseline.json` was
14
+ * produced by. So `aid eval` runs `python -m app.generated.evals.<id>` from the project
15
+ * root, which is also what a person would type by hand.
16
+ *
17
+ * The alternative was adding an entry point to `runtimes/`, and that was rejected: the
18
+ * runtime is a landed layer whose published surface other layers depend on, and the
19
+ * assignment says not to modify it. A missing runtime entry point is this layer's problem
20
+ * to route around, not a reason to change a shipped one.
21
+ *
22
+ * **`--baseline` delegates the sampling.** The gate is statistical (`maxRegression` against
23
+ * a stored distribution), so a baseline from a single run is noise shaped like a fact. The
24
+ * generated module's `run()` already samples the dataset `SAMPLES` times -- the N the spec
25
+ * declares -- so recording a baseline calls that, then the runtime's own documented recipe
26
+ * (`EvalBaseline.from_run(await run(), model=...).to_json()`, which is literally the string
27
+ * in the generated `load_baseline`'s error message). Re-sampling here would invent a second
28
+ * N and let the CLI and the module disagree about how many runs a baseline means.
29
+ *
30
+ * That recipe is the one place in the CLI that writes Python. It is a script piped to the
31
+ * interpreter over stdin rather than an API call because the runtime has no such entry
32
+ * point, and it is deliberately short: anything longer would be this layer reimplementing
33
+ * the runtime's measurement.
34
+ */
35
+ import { existsSync } from 'node:fs';
36
+ import { sortDiagnostics } from '@aid-framework/spec';
37
+ import { CLI_DIAGNOSTIC_CODES, cliError, cliWarning } from '../diagnostics.js';
38
+ import { EXIT_FAILURE, EXIT_OK, EXIT_USAGE } from '../exit.js';
39
+ import { outcome } from '../output.js';
40
+ import { absolutePath } from '../paths.js';
41
+ import { lowerSpec, readSpec } from '../pipeline.js';
42
+ import { discoverInterpreter, INTERPRETERS, interpreterCommand, invoke } from '../python.js';
43
+ import { resolveTarget, unknownTargetDiagnostic } from '../targets.js';
44
+ /** Eval ids the spec declares, or the diagnostics that stopped it from declaring any. */
45
+ function declaredSuites(text, specPath) {
46
+ const lowered = lowerSpec(text, specPath);
47
+ if (!lowered.ok)
48
+ return { ok: false, diagnostics: lowered.diagnostics };
49
+ const ir = lowered.ir;
50
+ return {
51
+ ok: true,
52
+ ids: ir.evals.map((entry) => entry.id),
53
+ warnings: lowered.diagnostics,
54
+ };
55
+ }
56
+ /**
57
+ * The baseline recorder, run as `python -`. `python` puts the current directory on
58
+ * `sys.path`, so `import app.generated.evals.<id>` resolves against the project root and
59
+ * each module's own `BASELINE_PATH` decides where its baseline goes -- the same path
60
+ * `load_baseline()` reads back, because it is the same constant.
61
+ */
62
+ function baselineScript(modules) {
63
+ return [
64
+ 'import asyncio',
65
+ 'import importlib',
66
+ 'import pathlib',
67
+ '',
68
+ 'from aid_runtime.evals import EvalBaseline',
69
+ '',
70
+ `MODULES = ${JSON.stringify(modules)}`,
71
+ '',
72
+ 'for name in MODULES:',
73
+ ' module = importlib.import_module(name)',
74
+ ' # run() already samples the dataset SAMPLES times, the N the spec declares.',
75
+ ' baseline = EvalBaseline.from_run(',
76
+ ' asyncio.run(module.run()),',
77
+ ' model=module.MODEL,',
78
+ " note='recorded by `aid eval --baseline`',",
79
+ ' )',
80
+ ' path = pathlib.Path(module.BASELINE_PATH)',
81
+ ' path.parent.mkdir(parents=True, exist_ok=True)',
82
+ ' path.write_text(baseline.to_json(), encoding="utf-8")',
83
+ ' print(f"wrote {path}")',
84
+ '',
85
+ ].join('\n');
86
+ }
87
+ export function evaluate(context, options) {
88
+ const command = 'eval';
89
+ const target = resolveTarget(options.target);
90
+ if (target === undefined) {
91
+ return outcome({
92
+ command,
93
+ exitCode: EXIT_USAGE,
94
+ diagnostics: [unknownTargetDiagnostic(options.target ?? '')],
95
+ });
96
+ }
97
+ const specPath = options.spec ?? target.specPath;
98
+ const fields = { target: target.name, spec: specPath, suite: options.suite ?? null };
99
+ const read = readSpec(context, specPath);
100
+ if (!read.ok) {
101
+ return outcome({ command, exitCode: EXIT_FAILURE, fields, diagnostics: read.diagnostics });
102
+ }
103
+ const declared = declaredSuites(read.text, specPath);
104
+ if (!declared.ok) {
105
+ return outcome({ command, exitCode: EXIT_FAILURE, fields, diagnostics: declared.diagnostics });
106
+ }
107
+ // `--suite` is checked against the ids the spec declares, not against filenames on
108
+ // disk: the set of evals is a property of the spec, so a typo is a usage error and
109
+ // should say which ids exist.
110
+ if (options.suite !== undefined && !declared.ids.includes(options.suite)) {
111
+ return outcome({
112
+ command,
113
+ exitCode: EXIT_USAGE,
114
+ fields,
115
+ diagnostics: [
116
+ cliError({
117
+ code: CLI_DIAGNOSTIC_CODES.suiteNotFound,
118
+ message: `No eval named "${options.suite}" in ${specPath}.`,
119
+ path: [specPath],
120
+ hint: declared.ids.length > 0
121
+ ? `Declared evals: ${declared.ids.join(', ')}.`
122
+ : 'The spec declares no evals.',
123
+ }),
124
+ ],
125
+ });
126
+ }
127
+ const suites = options.suite === undefined ? declared.ids : [options.suite];
128
+ const missing = suites.filter((id) => !existsSync(absolutePath(context.cwd, target.evalModuleFile(id))));
129
+ if (missing.length > 0) {
130
+ return outcome({
131
+ command,
132
+ exitCode: EXIT_FAILURE,
133
+ fields: { ...fields, suites },
134
+ diagnostics: [
135
+ cliError({
136
+ code: CLI_DIAGNOSTIC_CODES.evalModulesMissing,
137
+ message: `No generated eval module for ${missing.map((id) => `"${id}"`).join(', ')}.`,
138
+ path: missing.map((id) => target.evalModuleFile(id)),
139
+ hint: 'Run `aid gen`: the evals that run are the modules the generator emits.',
140
+ }),
141
+ ],
142
+ });
143
+ }
144
+ if (target.runner !== 'python') {
145
+ return outcome({
146
+ command,
147
+ // Exit 2, not 1: this command has no runner for this target, so no eval ran and
148
+ // nothing about the project was judged. `1` would read as "the evals failed".
149
+ exitCode: EXIT_USAGE,
150
+ fields: { ...fields, suites },
151
+ diagnostics: [
152
+ cliError({
153
+ code: CLI_DIAGNOSTIC_CODES.runnerUnsupported,
154
+ message: `\`aid eval\` runs generated Python modules, and ${target.name} emits TypeScript.`,
155
+ hint: 'The emitted eval module exports `sample()` and `evaluate(run, baseline)`; run it with Node.',
156
+ }),
157
+ ],
158
+ });
159
+ }
160
+ const interpreter = discoverInterpreter(context);
161
+ if (interpreter === undefined) {
162
+ return outcome({
163
+ command,
164
+ // Exit 2, not 1: nothing about the project was judged. The gate has no verdict when
165
+ // the thing that would run it cannot start, and `1` would read as "the evals failed".
166
+ exitCode: EXIT_USAGE,
167
+ fields: { ...fields, suites },
168
+ diagnostics: [
169
+ cliError({
170
+ code: CLI_DIAGNOSTIC_CODES.pythonMissing,
171
+ message: `No interpreter could import aid_runtime (tried ${INTERPRETERS.join(', ')}).`,
172
+ hint: 'Install the runtime into the interpreter you run with, or run the generated eval modules yourself.',
173
+ }),
174
+ ],
175
+ });
176
+ }
177
+ const modules = suites.map((id) => target.evalModule(id));
178
+ const base = interpreterCommand(interpreter);
179
+ const common = { ...fields, suites, interpreter };
180
+ if (options.baseline) {
181
+ const recorded = invoke(context, [...base, '-'], baselineScript(modules));
182
+ if (recorded === undefined || recorded.status !== 0) {
183
+ return outcome({
184
+ command,
185
+ exitCode: EXIT_FAILURE,
186
+ fields: common,
187
+ diagnostics: [
188
+ cliError({
189
+ code: CLI_DIAGNOSTIC_CODES.runnerFailed,
190
+ message: `Could not record a baseline with ${interpreter}.`,
191
+ hint: 'Recording a baseline runs the evals, so it needs whatever they need; see the output for why they did not run.',
192
+ }),
193
+ ],
194
+ text: recorded?.stdout.trimEnd() ?? '',
195
+ stderr: recorded?.stderr ?? '',
196
+ });
197
+ }
198
+ return outcome({
199
+ command,
200
+ exitCode: EXIT_OK,
201
+ fields: common,
202
+ text: recorded.stdout.trimEnd(),
203
+ stderr: recorded.stderr,
204
+ });
205
+ }
206
+ const results = [];
207
+ const output = [];
208
+ const spoken = [];
209
+ const diagnostics = [...declared.warnings];
210
+ for (const [index, module] of modules.entries()) {
211
+ const run = invoke(context, [...base, '-m', module]);
212
+ if (run === undefined) {
213
+ return outcome({
214
+ command,
215
+ exitCode: EXIT_USAGE,
216
+ fields: common,
217
+ diagnostics: [
218
+ cliError({
219
+ code: CLI_DIAGNOSTIC_CODES.runnerFailed,
220
+ message: `Could not start ${interpreter} to run "${module}".`,
221
+ }),
222
+ ],
223
+ });
224
+ }
225
+ const suite = suites[index] ?? module;
226
+ results.push({ suite, module, status: run.status ?? EXIT_FAILURE });
227
+ if (run.stdout.trim() !== '')
228
+ output.push(run.stdout.trimEnd());
229
+ // The generated `main()` prints its summary to stdout and exits non-zero on a
230
+ // regression; anything on stderr is the eval reporting something that did not fit in
231
+ // a metric. It is surfaced rather than swallowed, because a gate that hides what it
232
+ // could not measure is a gate that passes for the wrong reason -- and it is surfaced
233
+ // on stderr, including under `--json`, because that is where the process that wrote it
234
+ // wrote it, and `--json` replaces stdout with a document rather than with nothing.
235
+ if (run.stderr.trim() !== '') {
236
+ spoken.push(run.stderr.trimEnd());
237
+ diagnostics.push(cliWarning({
238
+ code: CLI_DIAGNOSTIC_CODES.runnerWarned,
239
+ message: `"${suite}" wrote to stderr.`,
240
+ }));
241
+ }
242
+ }
243
+ // A non-zero exit from a generated module *is* the gate's verdict, so it propagates
244
+ // rather than being reinterpreted here. The CLI reports the gate; it has no opinion
245
+ // about the numbers, and forming one would be the second implementation of the gate
246
+ // that this layer exists to avoid.
247
+ const failed = results.some((result) => result.status !== 0);
248
+ return outcome({
249
+ command,
250
+ exitCode: failed ? EXIT_FAILURE : EXIT_OK,
251
+ fields: { ...common, results },
252
+ diagnostics: sortDiagnostics(diagnostics),
253
+ text: output.join('\n'),
254
+ stderr: spoken.join('\n'),
255
+ });
256
+ }
257
+ //# sourceMappingURL=eval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eval.js","sourceRoot":"","sources":["../../src/commands/eval.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * `aid gen` — compile the spec and reconcile the tree with it.
3
+ *
4
+ * Every decision about *what* changes belongs to `@aid-framework/generator-sdk`: `planRegeneration`
5
+ * decides the actions, `regenerate` applies them, and the drift rule that refuses to
6
+ * overwrite an edited generated file is the SDK's. The CLI decides four things and
7
+ * nothing else: which target, which spec, whether to write, and what the run's exit code
8
+ * means. In particular it does not re-derive ownership, re-hash a file, or re-implement
9
+ * drift -- two implementations of that rule would eventually disagree, and the one that
10
+ * disagreed would be the one holding a developer's edits.
11
+ *
12
+ * One deliberate divergence from `generate-example.mjs`, noted because it is a choice: the
13
+ * manifest is written even when the run reports drift. `regenerate` records the *previous*
14
+ * hash for a file it refused to overwrite, so a run that refuses an edit and a run that
15
+ * discards one cannot disagree about what is on the tree; skipping the manifest write
16
+ * instead leaves files that this run did create unrecorded, and the next run then reports
17
+ * those as drift even though the generator was the one that wrote them.
18
+ */
19
+ import type { RegenerationReport } from '@aid-framework/generator-sdk';
20
+ import type { CliContext } from '../context.js';
21
+ import type { CliDiagnostic } from '../diagnostics.js';
22
+ import type { CommandOutcome } from '../output.js';
23
+ import type { Target } from '../targets.js';
24
+ export interface GenerateOptions {
25
+ readonly spec: string | undefined;
26
+ readonly target: string | undefined;
27
+ readonly dryRun: boolean;
28
+ readonly force: boolean;
29
+ }
30
+ /** A spec that compiled and a plan that was reconciled, or the reason it was not. */
31
+ export interface GenerateSuccess {
32
+ readonly target: Target;
33
+ readonly specPath: string;
34
+ readonly report: RegenerationReport;
35
+ readonly warnings: readonly CliDiagnostic[];
36
+ readonly dryRun: boolean;
37
+ readonly force: boolean;
38
+ readonly manifestWritten: boolean;
39
+ }
40
+ /** The public entry point, and the one `aid init --gen` folds into its own report. */
41
+ export declare function generate(context: CliContext, options: GenerateOptions): CommandOutcome;
42
+ //# sourceMappingURL=gen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gen.d.ts","sourceRoot":"","sources":["../../src/commands/gen.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAYvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAGnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAG5C,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED,qFAAqF;AACrF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;IAC5C,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CACnC;AAwED,sFAAsF;AACtF,wBAAgB,QAAQ,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,eAAe,GAAG,cAAc,CAEtF"}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * `aid gen` — compile the spec and reconcile the tree with it.
3
+ *
4
+ * Every decision about *what* changes belongs to `@aid-framework/generator-sdk`: `planRegeneration`
5
+ * decides the actions, `regenerate` applies them, and the drift rule that refuses to
6
+ * overwrite an edited generated file is the SDK's. The CLI decides four things and
7
+ * nothing else: which target, which spec, whether to write, and what the run's exit code
8
+ * means. In particular it does not re-derive ownership, re-hash a file, or re-implement
9
+ * drift -- two implementations of that rule would eventually disagree, and the one that
10
+ * disagreed would be the one holding a developer's edits.
11
+ *
12
+ * One deliberate divergence from `generate-example.mjs`, noted because it is a choice: the
13
+ * manifest is written even when the run reports drift. `regenerate` records the *previous*
14
+ * hash for a file it refused to overwrite, so a run that refuses an edit and a run that
15
+ * discards one cannot disagree about what is on the tree; skipping the manifest write
16
+ * instead leaves files that this run did create unrecorded, and the next run then reports
17
+ * those as drift even though the generator was the one that wrote them.
18
+ */
19
+ import { createNodeSink, formatRegenerationSummary, hasDrift, planRegeneration, regenerate, rejectionPath, serializeAidManifest, } from '@aid-framework/generator-sdk';
20
+ import { hasErrors, sortDiagnostics } from '@aid-framework/spec';
21
+ import { EXIT_FAILURE, EXIT_OK, EXIT_USAGE } from '../exit.js';
22
+ import { outcome } from '../output.js';
23
+ import { compile, provenanceFor, readManifest, readSpec } from '../pipeline.js';
24
+ import { resolveTarget, unknownTargetDiagnostic } from '../targets.js';
25
+ function lines(label, paths) {
26
+ if (paths.length === 0)
27
+ return [];
28
+ return [label, ...paths.map((path) => ` ${path}`)];
29
+ }
30
+ function render(context, options) {
31
+ const command = 'gen';
32
+ const target = resolveTarget(options.target);
33
+ if (target === undefined) {
34
+ return outcome({
35
+ command,
36
+ exitCode: EXIT_USAGE,
37
+ diagnostics: [unknownTargetDiagnostic(options.target ?? '')],
38
+ });
39
+ }
40
+ const specPath = options.spec ?? target.specPath;
41
+ const fields = { target: target.name, spec: specPath };
42
+ const read = readSpec(context, specPath);
43
+ if (!read.ok) {
44
+ return outcome({ command, exitCode: EXIT_FAILURE, fields, diagnostics: read.diagnostics });
45
+ }
46
+ const compiled = compile(read.text, specPath, target);
47
+ if (!compiled.ok) {
48
+ return outcome({ command, exitCode: EXIT_FAILURE, fields, diagnostics: compiled.diagnostics });
49
+ }
50
+ const sink = createNodeSink(context.cwd);
51
+ const manifest = readManifest(sink, target);
52
+ if (!manifest.ok) {
53
+ return outcome({ command, exitCode: EXIT_FAILURE, fields, diagnostics: manifest.diagnostics });
54
+ }
55
+ const reconciliation = { sink, manifest: manifest.manifest, force: options.force };
56
+ if (options.dryRun) {
57
+ // A dry run takes the branch in which no write call exists at all, rather than the
58
+ // branch that writes and is then trusted not to. `planRegeneration` is the SDK's
59
+ // planner without its writer, so "writes nothing" is a property of the code path
60
+ // and not a promise in a comment.
61
+ return summarize({
62
+ target,
63
+ specPath,
64
+ report: planRegeneration(compiled.plan, reconciliation),
65
+ warnings: compiled.diagnostics,
66
+ dryRun: true,
67
+ force: options.force,
68
+ manifestWritten: false,
69
+ });
70
+ }
71
+ const applied = regenerate(compiled.plan, {
72
+ ...reconciliation,
73
+ provenance: provenanceFor(target, compiled.ir),
74
+ });
75
+ sink.write(target.manifestPath, serializeAidManifest(applied.manifest));
76
+ return summarize({
77
+ target,
78
+ specPath,
79
+ report: applied.report,
80
+ warnings: compiled.diagnostics,
81
+ dryRun: false,
82
+ force: options.force,
83
+ manifestWritten: true,
84
+ });
85
+ }
86
+ /** The public entry point, and the one `aid init --gen` folds into its own report. */
87
+ export function generate(context, options) {
88
+ return render(context, options);
89
+ }
90
+ function summarize(success) {
91
+ const { report, target, specPath, warnings, dryRun, force, manifestWritten } = success;
92
+ const diagnostics = sortDiagnostics([...warnings, ...report.diagnostics]);
93
+ // Two ways to fail, and neither is redundant. `hasErrors` is the SDK's verdict on the
94
+ // run; `hasDrift` is checked alongside it rather than instead of it because drift is
95
+ // reported as an error today, and a gate that would stop failing if that changed is not
96
+ // a gate -- and drift is the one failure that silently destroys a developer's edits.
97
+ // `--force` is the caller answering it, so a run that discarded the edits did what it
98
+ // was told and exits 0; only an unforced run leaves drift outstanding.
99
+ const failed = hasErrors(diagnostics) || (!force && hasDrift(report));
100
+ const text = [
101
+ `${target.name}: ${dryRun ? 'dry run, ' : ''}${formatRegenerationSummary(report)}`,
102
+ ];
103
+ text.push(...lines('created:', report.created));
104
+ text.push(...lines('updated:', report.updated));
105
+ if (report.drifted.length > 0) {
106
+ text.push(dryRun ? 'would refuse to overwrite:' : 'refused to overwrite, the new content is beside it:');
107
+ for (const drift of report.drifted) {
108
+ text.push(` ${drift.path} -> ${rejectionPath(drift.path)}`);
109
+ }
110
+ }
111
+ text.push(...lines('no longer generated:', report.stale));
112
+ return outcome({
113
+ command: 'gen',
114
+ exitCode: failed ? EXIT_FAILURE : EXIT_OK,
115
+ diagnostics,
116
+ text: text.join('\n'),
117
+ fields: {
118
+ target: target.name,
119
+ spec: specPath,
120
+ dryRun,
121
+ manifest: { path: target.manifestPath, written: manifestWritten },
122
+ changes: {
123
+ created: report.created,
124
+ updated: report.updated,
125
+ unchanged: report.unchanged,
126
+ kept: report.kept,
127
+ // Paths, like every sibling here: the report answers "which files", and the
128
+ // rejected content is on disk at the deterministic `rejectionPath` the human
129
+ // summary prints. Mixing record objects into one entry and strings into the rest
130
+ // makes the one shape a consumer has to special-case the one about conflicts.
131
+ conflicts: report.drifted.map((file) => file.path),
132
+ stale: report.stale,
133
+ pruned: report.pruned,
134
+ },
135
+ },
136
+ });
137
+ }
138
+ //# sourceMappingURL=gen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gen.js","sourceRoot":"","sources":["../../src/commands/gen.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * `aid ingest` -- run the generated ingest entry point for a retriever's corpus.
3
+ *
4
+ * **What this shells out to, and why.** Indexing is a batch job, not a request, so the
5
+ * generator emits one runnable module per declared retriever at
6
+ * `app/generated/retrieval/ingest/<id>.py`, with a `main()` that chunks, embeds and writes
7
+ * the corpus. That module is the interface: it already knows the chunking and the store the
8
+ * spec declared, and re-deriving either here would be a second implementation of the
9
+ * runtime's rules. So the command runs `python -m app.generated.retrieval.ingest.<id>` from
10
+ * the project root, which is also what a person would type by hand.
11
+ *
12
+ * **The flag shape is a usage error, not a project verdict.** `--retriever` and `--all` are
13
+ * the two ways to say what to ingest; passing both, or neither, is a mistake in the
14
+ * invocation and exits 2 before the spec is even read. An id the spec does not declare is
15
+ * the same kind of mistake, and its message names the ids that do exist -- a typo should
16
+ * not require opening the spec.
17
+ *
18
+ * **A missing module is a failure, not a usage error.** `--retriever handbook` in a project
19
+ * that has never been generated is a well-formed command that cannot do its job; it exits 1
20
+ * and points at `aid gen`. Exit 2 would tell a script that it passed the wrong arguments,
21
+ * which is false and hides the one action that fixes it.
22
+ *
23
+ * **`--dry-run` is forwarded, not implemented here.** The generated module already grows
24
+ * its own `--dry-run`, which applies the chunker and reports the counts a real run would
25
+ * write without opening a store or calling an embedder. Answering the same question in
26
+ * the CLI would mean re-chunking the corpus, and a second dry run that drifted from the
27
+ * one the module performs is worse than no dry run at all.
28
+ *
29
+ * **Idempotence is the runtime's, not a rescan this command performs.** A chunk id is a
30
+ * content hash, and the store upserts on it, so a second run over unchanged documents
31
+ * replaces rather than appends. The CLI adds no diffing of its own: it would have to
32
+ * reimplement the chunker to know what changed.
33
+ */
34
+ import type { CliContext } from '../context.js';
35
+ import type { CommandOutcome } from '../output.js';
36
+ export interface IngestOptions {
37
+ readonly spec: string | undefined;
38
+ readonly target: string | undefined;
39
+ readonly retriever: string | undefined;
40
+ readonly all: boolean;
41
+ readonly dryRun: boolean;
42
+ }
43
+ export declare function ingest(context: CliContext, options: IngestOptions): CommandOutcome;
44
+ //# sourceMappingURL=ingest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingest.d.ts","sourceRoot":"","sources":["../../src/commands/ingest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAMH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAIhD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAOnD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AA+CD,wBAAgB,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,GAAG,cAAc,CAiLlF"}