@interf/compiler 0.5.1 → 0.6.1

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 (93) hide show
  1. package/README.md +126 -187
  2. package/builtin-workflows/interf/README.md +22 -10
  3. package/builtin-workflows/interf/compile/stages/shape/SKILL.md +6 -3
  4. package/builtin-workflows/interf/compile/stages/structure/SKILL.md +3 -0
  5. package/builtin-workflows/interf/compile/stages/summarize/SKILL.md +18 -2
  6. package/builtin-workflows/interf/improve/SKILL.md +2 -2
  7. package/builtin-workflows/interf/workflow.json +18 -4
  8. package/builtin-workflows/interf/{compiled.schema.json → workflow.schema.json} +9 -2
  9. package/dist/commands/check-draft.js +3 -3
  10. package/dist/commands/compile-controller.js +6 -13
  11. package/dist/commands/compile.d.ts +19 -1
  12. package/dist/commands/compile.js +98 -28
  13. package/dist/commands/create-workflow-wizard.d.ts +20 -2
  14. package/dist/commands/create-workflow-wizard.js +163 -27
  15. package/dist/commands/create.d.ts +1 -1
  16. package/dist/commands/create.js +67 -60
  17. package/dist/commands/dataset-selection.d.ts +6 -0
  18. package/dist/commands/dataset-selection.js +11 -0
  19. package/dist/commands/default.js +3 -3
  20. package/dist/commands/doctor.js +8 -8
  21. package/dist/commands/executor-flow.d.ts +1 -1
  22. package/dist/commands/executor-flow.js +5 -2
  23. package/dist/commands/init.d.ts +5 -0
  24. package/dist/commands/init.js +56 -48
  25. package/dist/commands/list.js +6 -3
  26. package/dist/commands/reset.js +1 -1
  27. package/dist/commands/source-config-wizard.d.ts +2 -2
  28. package/dist/commands/source-config-wizard.js +50 -17
  29. package/dist/commands/test.d.ts +0 -6
  30. package/dist/commands/test.js +9 -17
  31. package/dist/index.d.ts +1 -1
  32. package/dist/index.js +1 -1
  33. package/dist/lib/agent-args.d.ts +1 -0
  34. package/dist/lib/agent-args.js +10 -0
  35. package/dist/lib/agent-execution.js +2 -1
  36. package/dist/lib/agent-preflight.js +2 -1
  37. package/dist/lib/agent-shells.d.ts +26 -1
  38. package/dist/lib/agent-shells.js +213 -39
  39. package/dist/lib/agents.d.ts +1 -1
  40. package/dist/lib/agents.js +1 -1
  41. package/dist/lib/builtin-compiled-workflow.d.ts +6 -97
  42. package/dist/lib/builtin-compiled-workflow.js +66 -125
  43. package/dist/lib/compiled-compile.d.ts +0 -4
  44. package/dist/lib/compiled-compile.js +9 -28
  45. package/dist/lib/compiled-paths.d.ts +1 -0
  46. package/dist/lib/compiled-paths.js +3 -0
  47. package/dist/lib/compiled-reset.d.ts +1 -0
  48. package/dist/lib/compiled-reset.js +42 -14
  49. package/dist/lib/compiled-schema.d.ts +9 -5
  50. package/dist/lib/compiled-schema.js +45 -14
  51. package/dist/lib/discovery.d.ts +1 -1
  52. package/dist/lib/discovery.js +2 -2
  53. package/dist/lib/executors.d.ts +1 -1
  54. package/dist/lib/executors.js +2 -2
  55. package/dist/lib/interf-scaffold.js +4 -11
  56. package/dist/lib/interf-workflow-package.d.ts +8 -3
  57. package/dist/lib/interf-workflow-package.js +128 -62
  58. package/dist/lib/local-workflows.d.ts +4 -3
  59. package/dist/lib/local-workflows.js +126 -103
  60. package/dist/lib/runtime-acceptance.js +15 -3
  61. package/dist/lib/runtime-contracts.js +3 -2
  62. package/dist/lib/runtime-paths.d.ts +1 -0
  63. package/dist/lib/runtime-paths.js +4 -1
  64. package/dist/lib/runtime-prompt.js +3 -1
  65. package/dist/lib/runtime-reconcile.js +88 -51
  66. package/dist/lib/runtime-runs.js +27 -15
  67. package/dist/lib/runtime.d.ts +1 -1
  68. package/dist/lib/runtime.js +1 -1
  69. package/dist/lib/schema.d.ts +71 -14
  70. package/dist/lib/schema.js +15 -12
  71. package/dist/lib/state-view.js +6 -6
  72. package/dist/lib/state.d.ts +1 -0
  73. package/dist/lib/state.js +7 -0
  74. package/dist/lib/test-execution.js +2 -2
  75. package/dist/lib/validate-compiled.js +9 -6
  76. package/dist/lib/validate.d.ts +3 -1
  77. package/dist/lib/validate.js +4 -11
  78. package/dist/lib/workflow-authoring.d.ts +26 -0
  79. package/dist/lib/workflow-authoring.js +119 -0
  80. package/dist/lib/workflow-definitions.d.ts +11 -1
  81. package/dist/lib/workflow-definitions.js +12 -15
  82. package/dist/lib/workflow-edit-session.d.ts +16 -0
  83. package/dist/lib/workflow-edit-session.js +57 -0
  84. package/dist/lib/workflow-edit-utils.d.ts +10 -0
  85. package/dist/lib/workflow-edit-utils.js +39 -0
  86. package/dist/lib/workflow-improvement.js +30 -217
  87. package/dist/lib/workflow-stage-policy.d.ts +5 -0
  88. package/dist/lib/workflow-stage-policy.js +31 -0
  89. package/package.json +4 -5
  90. package/dist/lib/obsidian.d.ts +0 -1
  91. package/dist/lib/obsidian.js +0 -15
  92. package/dist/lib/summarize-plan.d.ts +0 -17
  93. package/dist/lib/summarize-plan.js +0 -120
@@ -1,4 +1,4 @@
1
- import { appendFileSync, cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync, } from "node:fs";
1
+ import { appendFileSync, existsSync, mkdirSync, writeFileSync, } from "node:fs";
2
2
  import { join, relative } from "node:path";
3
3
  import { createWorkflowImprovementShell, freezeWorkflowImprovementShell, } from "./agent-shells.js";
4
4
  import { readInterfConfig } from "./interf.js";
@@ -7,44 +7,9 @@ import { readJsonFileWithSchema } from "./parse.js";
7
7
  import { saveCompiledInterfConfig } from "./source-config.js";
8
8
  import { WorkflowImprovementRunLedgerSchema, } from "./schema.js";
9
9
  import { resolveWorkflowImprovementReviewPaths } from "./workflow-review-paths.js";
10
+ import { runWorkflowEditSession } from "./workflow-edit-session.js";
10
11
  import { targetTestRunsRootForCompiled, targetTestSandboxesRootForCompiled, workflowImprovementRunRoot, workflowPackagePathForCompiled, } from "./compiled-paths.js";
11
- function copyDirectory(sourcePath, targetPath) {
12
- rmSync(targetPath, { recursive: true, force: true });
13
- mkdirSync(targetPath, { recursive: true });
14
- cpSync(sourcePath, targetPath, { recursive: true });
15
- }
16
- function listFileSnapshot(dirPath) {
17
- const snapshot = new Map();
18
- if (!existsSync(dirPath))
19
- return snapshot;
20
- const collect = (currentPath) => {
21
- for (const entry of readdirSync(currentPath)) {
22
- const fullPath = join(currentPath, entry);
23
- const stat = statSync(fullPath);
24
- if (stat.isDirectory()) {
25
- collect(fullPath);
26
- continue;
27
- }
28
- if (!stat.isFile())
29
- continue;
30
- snapshot.set(relative(dirPath, fullPath), readFileSync(fullPath, "utf8"));
31
- }
32
- };
33
- collect(dirPath);
34
- return snapshot;
35
- }
36
- function directoriesMatch(leftPath, rightPath) {
37
- const left = listFileSnapshot(leftPath);
38
- const right = listFileSnapshot(rightPath);
39
- if (left.size !== right.size)
40
- return false;
41
- for (const [relativePath, content] of left.entries()) {
42
- if (right.get(relativePath) !== content) {
43
- return false;
44
- }
45
- }
46
- return true;
47
- }
12
+ import { WORKFLOW_SCHEMA_FILE } from "./compiled-schema.js";
48
13
  function toShellArtifactPath(absolutePath, sourceRoot, shellRoot) {
49
14
  if (!absolutePath)
50
15
  return null;
@@ -90,16 +55,18 @@ function buildLoopContext(options) {
90
55
  }
91
56
  function buildWorkflowImprovementPrompt() {
92
57
  return [
93
- "This is an automated Interf Compiler workflow-improvement run, not an open-ended chat session.",
58
+ "This is an automated Interf workflow-improvement run, not an open-ended chat session.",
94
59
  "The user already invoked this through `interf compile` with self-improving loops enabled. Execute it now.",
95
60
  "Read `runtime/loop-context.json` first.",
96
- "Then read `workflow/README.md`, `workflow/workflow.json`, `workflow/compiled.schema.json`, and `workflow/improve/SKILL.md` if it exists.",
61
+ `Then read \`workflow/README.md\`, \`workflow/workflow.json\`, \`workflow/${WORKFLOW_SCHEMA_FILE}\`, and \`workflow/improve/SKILL.md\` if it exists.`,
97
62
  "Review preserved evidence from earlier failures under `artifacts/` before you edit the workflow.",
98
63
  "Treat `workflow/improve/SKILL.md` as guidance for how to improve the workflow, not as the default file to edit.",
99
64
  "Prefer editing the stage docs, workflow contract, or schema ownership that actually change compiled outputs.",
100
65
  "Edit only files under `workflow/`.",
101
66
  "Do not edit truth checks, test specs, raw dataset files, or generated compiled outputs.",
102
- "Keep the workflow valid for the current compiler API and compiled schema.",
67
+ "Keep the workflow valid for the current compiler API and workflow schema.",
68
+ "Respect stage boundaries: a stage may only introduce links that resolve within that stage's declared writes or already-existing read zones.",
69
+ "Do not make one stage point at files or notes that are only created later by another stage.",
103
70
  "Prefer small, defensible changes to workflow docs, stage docs, stage policies, or schema ownership over random churn.",
104
71
  "Do not narrate plans or ask follow-up questions.",
105
72
  "Only emit user-visible updates that begin with STATUS:, DONE:, BLOCKED:, or ERROR:.",
@@ -193,170 +160,14 @@ export async function runWorkflowImprovementLoop(options) {
193
160
  context,
194
161
  });
195
162
  const workflowRoot = workflowPackagePathForCompiled(options.compiledPath);
196
- copyDirectory(workflowRoot, shell.workflowBeforePath);
197
- const prompt = buildWorkflowImprovementPrompt();
198
- writeFileSync(shell.promptLogPath, `${prompt}\n`);
199
- let code = 1;
200
- let executeError = null;
201
- try {
202
- code = await options.executor.execute(shell.rootPath, prompt, {
203
- eventLogPath: shell.eventLogPath,
204
- statusLogPath: shell.statusLogPath,
205
- });
206
- }
207
- catch (error) {
208
- executeError = error instanceof Error ? error.message : String(error);
209
- }
210
- copyDirectory(workflowRoot, shell.workflowAfterPath);
163
+ const session = await runWorkflowEditSession({
164
+ executor: options.executor,
165
+ workflowPath: workflowRoot,
166
+ shell,
167
+ prompt: buildWorkflowImprovementPrompt(),
168
+ validate: validateWorkflowPackage,
169
+ });
211
170
  const preservedShellManifestPath = freezeWorkflowImprovementShell(shell.rootPath);
212
- const changed = !directoriesMatch(shell.workflowBeforePath, shell.workflowAfterPath);
213
- if (executeError) {
214
- copyDirectory(shell.workflowBeforePath, workflowRoot);
215
- const summary = `Workflow improver failed before completing: ${executeError}`;
216
- writeWorkflowImprovementRunLedger(options.compiledPath, options.runId, buildWorkflowImprovementLoopRecord({
217
- targetName: compiledName,
218
- workflowId: options.workflowId,
219
- runId: options.runId,
220
- loopIndex: options.loopIndex,
221
- shellPath: shell.rootPath,
222
- loopRootPath: shell.loopRootPath,
223
- workflowBeforePath: shell.workflowBeforePath,
224
- workflowAfterPath: shell.workflowAfterPath,
225
- promptLogPath: shell.promptLogPath,
226
- eventLogPath: shell.eventLogPath,
227
- statusLogPath: shell.statusLogPath,
228
- preservedShellManifestPath,
229
- changed,
230
- validation: null,
231
- result: "executor-failed",
232
- summary,
233
- }), {
234
- targetName: compiledName,
235
- workflowId: options.workflowId,
236
- maxLoops: options.maxLoops,
237
- maxAttempts: options.maxAttempts,
238
- });
239
- return {
240
- status: "executor-failed",
241
- changed,
242
- validation: null,
243
- summary,
244
- shellPath: shell.rootPath,
245
- loopRootPath: shell.loopRootPath,
246
- };
247
- }
248
- if (code !== 0) {
249
- copyDirectory(shell.workflowBeforePath, workflowRoot);
250
- const summary = `Workflow improver exited with code ${code}.`;
251
- writeWorkflowImprovementRunLedger(options.compiledPath, options.runId, buildWorkflowImprovementLoopRecord({
252
- targetName: compiledName,
253
- workflowId: options.workflowId,
254
- runId: options.runId,
255
- loopIndex: options.loopIndex,
256
- shellPath: shell.rootPath,
257
- loopRootPath: shell.loopRootPath,
258
- workflowBeforePath: shell.workflowBeforePath,
259
- workflowAfterPath: shell.workflowAfterPath,
260
- promptLogPath: shell.promptLogPath,
261
- eventLogPath: shell.eventLogPath,
262
- statusLogPath: shell.statusLogPath,
263
- preservedShellManifestPath,
264
- changed,
265
- validation: null,
266
- result: "executor-failed",
267
- summary,
268
- }), {
269
- targetName: compiledName,
270
- workflowId: options.workflowId,
271
- maxLoops: options.maxLoops,
272
- maxAttempts: options.maxAttempts,
273
- });
274
- return {
275
- status: "executor-failed",
276
- changed,
277
- validation: null,
278
- summary,
279
- shellPath: shell.rootPath,
280
- loopRootPath: shell.loopRootPath,
281
- };
282
- }
283
- if (!changed) {
284
- const validation = validateWorkflowPackage(workflowRoot);
285
- const result = validation.ok ? "no-change" : "invalid";
286
- if (!validation.ok) {
287
- copyDirectory(shell.workflowBeforePath, workflowRoot);
288
- }
289
- const summary = validation.ok
290
- ? "Workflow improver made no workflow edits."
291
- : `Workflow variation is invalid without any workflow edits: ${validation.summary}`;
292
- writeWorkflowImprovementRunLedger(options.compiledPath, options.runId, buildWorkflowImprovementLoopRecord({
293
- targetName: compiledName,
294
- workflowId: options.workflowId,
295
- runId: options.runId,
296
- loopIndex: options.loopIndex,
297
- shellPath: shell.rootPath,
298
- loopRootPath: shell.loopRootPath,
299
- workflowBeforePath: shell.workflowBeforePath,
300
- workflowAfterPath: shell.workflowAfterPath,
301
- promptLogPath: shell.promptLogPath,
302
- eventLogPath: shell.eventLogPath,
303
- statusLogPath: shell.statusLogPath,
304
- preservedShellManifestPath,
305
- changed: false,
306
- validation,
307
- result,
308
- summary,
309
- }), {
310
- targetName: compiledName,
311
- workflowId: options.workflowId,
312
- maxLoops: options.maxLoops,
313
- maxAttempts: options.maxAttempts,
314
- });
315
- return {
316
- status: result,
317
- changed: false,
318
- validation,
319
- summary,
320
- shellPath: shell.rootPath,
321
- loopRootPath: shell.loopRootPath,
322
- };
323
- }
324
- const validation = validateWorkflowPackage(workflowRoot);
325
- if (!validation.ok) {
326
- copyDirectory(shell.workflowBeforePath, workflowRoot);
327
- const summary = `Workflow variation failed validation: ${validation.summary}`;
328
- writeWorkflowImprovementRunLedger(options.compiledPath, options.runId, buildWorkflowImprovementLoopRecord({
329
- targetName: compiledName,
330
- workflowId: options.workflowId,
331
- runId: options.runId,
332
- loopIndex: options.loopIndex,
333
- shellPath: shell.rootPath,
334
- loopRootPath: shell.loopRootPath,
335
- workflowBeforePath: shell.workflowBeforePath,
336
- workflowAfterPath: shell.workflowAfterPath,
337
- promptLogPath: shell.promptLogPath,
338
- eventLogPath: shell.eventLogPath,
339
- statusLogPath: shell.statusLogPath,
340
- preservedShellManifestPath,
341
- changed: true,
342
- validation,
343
- result: "invalid",
344
- summary,
345
- }), {
346
- targetName: compiledName,
347
- workflowId: options.workflowId,
348
- maxLoops: options.maxLoops,
349
- maxAttempts: options.maxAttempts,
350
- });
351
- return {
352
- status: "invalid",
353
- changed: true,
354
- validation,
355
- summary,
356
- shellPath: shell.rootPath,
357
- loopRootPath: shell.loopRootPath,
358
- };
359
- }
360
171
  writeWorkflowImprovementRunLedger(options.compiledPath, options.runId, buildWorkflowImprovementLoopRecord({
361
172
  targetName: compiledName,
362
173
  workflowId: options.workflowId,
@@ -370,26 +181,28 @@ export async function runWorkflowImprovementLoop(options) {
370
181
  eventLogPath: shell.eventLogPath,
371
182
  statusLogPath: shell.statusLogPath,
372
183
  preservedShellManifestPath,
373
- changed: true,
374
- validation,
375
- result: "updated",
376
- summary: "Workflow variation updated and validated.",
184
+ changed: session.changed,
185
+ validation: session.validation,
186
+ result: session.status,
187
+ summary: session.summary,
377
188
  }), {
378
189
  targetName: compiledName,
379
190
  workflowId: options.workflowId,
380
191
  maxLoops: options.maxLoops,
381
192
  maxAttempts: options.maxAttempts,
382
193
  });
383
- updateCompiledWorkflowOrigin({
384
- compiledPath: options.compiledPath,
385
- selectedWorkflowId: options.workflowId,
386
- localDraft: true,
387
- });
194
+ if (session.status === "updated") {
195
+ updateCompiledWorkflowOrigin({
196
+ compiledPath: options.compiledPath,
197
+ selectedWorkflowId: options.workflowId,
198
+ localDraft: true,
199
+ });
200
+ }
388
201
  return {
389
- status: "updated",
390
- changed: true,
391
- validation,
392
- summary: "Workflow variation updated and validated.",
202
+ status: session.status,
203
+ changed: session.changed,
204
+ validation: session.validation,
205
+ summary: session.summary,
393
206
  shellPath: shell.rootPath,
394
207
  loopRootPath: shell.loopRootPath,
395
208
  };
@@ -0,0 +1,5 @@
1
+ export interface StagePolicyStageLike {
2
+ id: string;
3
+ }
4
+ export declare function normalizeStagePolicyNotes(value: unknown): Record<string, string[]> | undefined;
5
+ export declare function mergeStagePolicyNotesForStages(stages: readonly StagePolicyStageLike[], baseNotes: unknown, overrideNotes: Record<string, string[]> | undefined): Record<string, string[]> | undefined;
@@ -0,0 +1,31 @@
1
+ export function normalizeStagePolicyNotes(value) {
2
+ if (!value || typeof value !== "object" || Array.isArray(value))
3
+ return undefined;
4
+ const normalized = {};
5
+ for (const [stageId, notes] of Object.entries(value)) {
6
+ if (!Array.isArray(notes))
7
+ continue;
8
+ const cleaned = Array.from(new Set(notes
9
+ .filter((note) => typeof note === "string")
10
+ .map((note) => note.trim())
11
+ .filter((note) => note.length > 0)));
12
+ if (cleaned.length > 0) {
13
+ normalized[stageId] = cleaned;
14
+ }
15
+ }
16
+ return Object.keys(normalized).length > 0 ? normalized : undefined;
17
+ }
18
+ export function mergeStagePolicyNotesForStages(stages, baseNotes, overrideNotes) {
19
+ const normalizedBase = normalizeStagePolicyNotes(baseNotes);
20
+ const merged = {};
21
+ for (const stage of stages) {
22
+ const combined = Array.from(new Set([
23
+ ...(normalizedBase?.[stage.id] ?? []),
24
+ ...((overrideNotes?.[stage.id] ?? []).map((note) => note.trim()).filter((note) => note.length > 0)),
25
+ ]));
26
+ if (combined.length > 0) {
27
+ merged[stage.id] = combined;
28
+ }
29
+ }
30
+ return Object.keys(merged).length > 0 ? merged : undefined;
31
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@interf/compiler",
3
- "version": "0.5.1",
4
- "description": "Interf Compiler is a local runtime for data-processing workflows that prepare datasets for accurate local agent use.",
3
+ "version": "0.6.1",
4
+ "description": "Interf helps make data ready for agent work. Measure raw files first, then prepare a compiled dataset when you need it.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "interf": "dist/bin.js"
@@ -37,13 +37,14 @@
37
37
  "test:internal:cbre:max:keep": "npm run test:matrix -- --matrix internal/test-matrices/cbre-chart-tier1-max.json --keep-project-clones",
38
38
  "test:full": "npm test && npm run test:e2e:quick:matrix",
39
39
  "test:release:matrix": "npm run test:internal:cbre:max",
40
- "test:release": "npm test && npm run test:acceptance-live -- --agent claude-code --with-test && npm run test:acceptance-live -- --agent codex --with-test",
40
+ "test:release": "npm test && npm run test:acceptance-quick:workflow-authoring -- --agent claude-code && npm run test:acceptance-live -- --agent claude-code --with-test && npm run test:acceptance-quick:workflow-authoring -- --agent codex && npm run test:acceptance-live -- --agent codex --with-test",
41
41
  "test:acceptance-live": "npm run build && node scripts/acceptance-live.mjs",
42
42
  "test:acceptance-cache:refresh": "npm run test:acceptance-cache:refresh:claude && npm run test:acceptance-cache:refresh:codex",
43
43
  "test:acceptance-cache:refresh:claude": "node scripts/acceptance-cache-refresh.mjs --agent claude-code",
44
44
  "test:acceptance-cache:refresh:codex": "node scripts/acceptance-cache-refresh.mjs --agent codex",
45
45
  "test:acceptance-quick:create-compiled": "npm run build && node scripts/acceptance-targeted.mjs create-compiled",
46
46
  "test:acceptance-quick:compiled-query": "npm run build && node scripts/acceptance-targeted.mjs compiled-query",
47
+ "test:acceptance-quick:workflow-authoring": "npm run build && node scripts/acceptance-targeted.mjs workflow-authoring",
47
48
  "test:acceptance-quick:self-improving": "npm run build && node scripts/acceptance-targeted.mjs self-improving-compile",
48
49
  "prepublishOnly": "node scripts/prepublish-gate.mjs"
49
50
  },
@@ -59,8 +60,6 @@
59
60
  "interf",
60
61
  "ai",
61
62
  "agent",
62
- "knowledge",
63
- "obsidian",
64
63
  "coding-agent",
65
64
  "claude",
66
65
  "codex"
@@ -1 +0,0 @@
1
- export declare function writeObsidianDefaults(dirPath: string, type: "compiled"): void;
@@ -1,15 +0,0 @@
1
- import { mkdirSync, writeFileSync, } from "node:fs";
2
- import { join } from "node:path";
3
- export function writeObsidianDefaults(dirPath, type) {
4
- const obsidianDir = join(dirPath, ".obsidian");
5
- mkdirSync(obsidianDir, { recursive: true });
6
- const graphSettings = {
7
- "collapse-filter": true,
8
- search: 'path:"summaries" OR path:"knowledge" OR path:"home"',
9
- showTags: false,
10
- showAttachments: false,
11
- hideUnresolved: true,
12
- showOrphans: false,
13
- };
14
- writeFileSync(join(obsidianDir, "graph.json"), JSON.stringify(graphSettings, null, 2) + "\n");
15
- }
@@ -1,17 +0,0 @@
1
- export interface SummarizeTarget {
2
- source: string;
3
- summary: string | null;
4
- output: string;
5
- reason: "pending" | "missing_synth" | "changed" | "full_pass";
6
- }
7
- export interface SummarizePlan {
8
- generatedAt: string;
9
- sourceCount: number;
10
- summaryCount: number;
11
- pendingCount: number;
12
- targetCount: number;
13
- targets: SummarizeTarget[];
14
- }
15
- export declare const SUMMARIZE_PLAN_RELATIVE_PATH: string;
16
- export declare function buildSummarizePlan(sourcePath: string, compiledPath: string, mode?: "pending" | "all"): SummarizePlan;
17
- export declare function writeSummarizePlan(compiledPath: string, plan: SummarizePlan): string;
@@ -1,120 +0,0 @@
1
- import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
2
- import { join, relative } from "node:path";
3
- import { listFilesRecursive } from "./filesystem.js";
4
- import { builtinCompiledZoneRelativePath, compiledSummariesPath, BUILTIN_COMPILED_ZONE_IDS, } from "./compiled-schema.js";
5
- import { discoverSourceFiles } from "./discovery.js";
6
- import { loadState } from "./state.js";
7
- import { parseJsonFrontmatter } from "./parse.js";
8
- import { compiledRuntimeRoot } from "./compiled-paths.js";
9
- export const SUMMARIZE_PLAN_RELATIVE_PATH = join(".interf", "runtime", "summarize-targets.json");
10
- export function buildSummarizePlan(sourcePath, compiledPath, mode = "pending") {
11
- const discovery = discoverSourceFiles(sourcePath, compiledPath);
12
- const sourceFiles = discovery.sourceFiles;
13
- const summariesDir = compiledSummariesPath(compiledPath);
14
- const summaryFiles = listSummaryFiles(summariesDir, compiledPath);
15
- const state = loadState(compiledPath);
16
- const pending = new Set((state?.pending ?? []).filter((value) => typeof value === "string" && sourceFiles.includes(value)));
17
- const summaryIndex = new Map();
18
- for (const summary of summaryFiles) {
19
- const source = readSourceReference(join(compiledPath, summary));
20
- if (source)
21
- summaryIndex.set(source, summary);
22
- }
23
- const targets = new Map();
24
- if (mode === "all") {
25
- for (const src of sourceFiles) {
26
- targets.set(src, {
27
- source: src,
28
- summary: summaryIndex.get(src) ?? null,
29
- output: defaultSummaryPath(src),
30
- reason: "full_pass",
31
- });
32
- }
33
- return {
34
- generatedAt: new Date().toISOString(),
35
- sourceCount: sourceFiles.length,
36
- summaryCount: summaryFiles.length,
37
- pendingCount: pending.size,
38
- targetCount: targets.size,
39
- targets: Array.from(targets.values()).sort((a, b) => a.source.localeCompare(b.source)),
40
- };
41
- }
42
- for (const src of pending) {
43
- targets.set(src, {
44
- source: src,
45
- summary: summaryIndex.get(src) ?? null,
46
- output: defaultSummaryPath(src),
47
- reason: "pending",
48
- });
49
- }
50
- for (const src of sourceFiles) {
51
- if (summaryIndex.has(src))
52
- continue;
53
- if (targets.has(src))
54
- continue;
55
- targets.set(src, {
56
- source: src,
57
- summary: null,
58
- output: defaultSummaryPath(src),
59
- reason: "missing_synth",
60
- });
61
- }
62
- // Detect changed source files (source newer than its summary)
63
- for (const [src, summary] of summaryIndex) {
64
- if (targets.has(src))
65
- continue;
66
- const sourceFile = join(sourcePath, src);
67
- const synthFile = join(compiledPath, summary);
68
- if (!existsSync(sourceFile) || !existsSync(synthFile))
69
- continue;
70
- try {
71
- const sourceMtime = statSync(sourceFile).mtimeMs;
72
- const synthMtime = statSync(synthFile).mtimeMs;
73
- if (sourceMtime > synthMtime) {
74
- targets.set(src, {
75
- source: src,
76
- summary,
77
- output: defaultSummaryPath(src),
78
- reason: "changed",
79
- });
80
- }
81
- }
82
- catch {
83
- continue;
84
- }
85
- }
86
- return {
87
- generatedAt: new Date().toISOString(),
88
- sourceCount: sourceFiles.length,
89
- summaryCount: summaryFiles.length,
90
- pendingCount: pending.size,
91
- targetCount: targets.size,
92
- targets: Array.from(targets.values()).sort((a, b) => a.source.localeCompare(b.source)),
93
- };
94
- }
95
- export function writeSummarizePlan(compiledPath, plan) {
96
- const planPath = join(compiledPath, SUMMARIZE_PLAN_RELATIVE_PATH);
97
- mkdirSync(compiledRuntimeRoot(compiledPath), { recursive: true });
98
- writeFileSync(planPath, JSON.stringify(plan, null, 2) + "\n");
99
- return planPath;
100
- }
101
- function listSummaryFiles(summariesDir, compiledPath) {
102
- return listFilesRecursive(summariesDir, (filePath) => filePath.endsWith(".md")).map((filePath) => relative(compiledPath, filePath));
103
- }
104
- function readSourceReference(synthPath) {
105
- if (!existsSync(synthPath))
106
- return null;
107
- const content = readFileSync(synthPath, "utf-8");
108
- const parsed = parseJsonFrontmatter(content);
109
- const source = typeof parsed?.frontmatter.source === "string"
110
- ? parsed.frontmatter.source
111
- : null;
112
- return source?.trim() ?? null;
113
- }
114
- function defaultSummaryPath(sourcePath) {
115
- const summariesRoot = builtinCompiledZoneRelativePath(BUILTIN_COMPILED_ZONE_IDS.SUMMARIES);
116
- if (sourcePath.endsWith(".md"))
117
- return `${summariesRoot}/${sourcePath}`;
118
- const replaced = sourcePath.replace(/\.[^.]+$/, ".md");
119
- return `${summariesRoot}/${replaced === sourcePath ? `${sourcePath}.md` : replaced}`;
120
- }