@interf/compiler 0.1.10 → 0.1.12

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 (111) hide show
  1. package/README.md +23 -7
  2. package/dist/commands/benchmark.d.ts.map +1 -1
  3. package/dist/commands/benchmark.js +1 -8
  4. package/dist/commands/benchmark.js.map +1 -1
  5. package/dist/commands/compile.d.ts.map +1 -1
  6. package/dist/commands/compile.js.map +1 -1
  7. package/dist/commands/create-workflow-wizard.d.ts +65 -0
  8. package/dist/commands/create-workflow-wizard.d.ts.map +1 -0
  9. package/dist/commands/create-workflow-wizard.js +299 -0
  10. package/dist/commands/create-workflow-wizard.js.map +1 -0
  11. package/dist/commands/create.d.ts +1 -37
  12. package/dist/commands/create.d.ts.map +1 -1
  13. package/dist/commands/create.js +11 -313
  14. package/dist/commands/create.js.map +1 -1
  15. package/dist/commands/init.d.ts.map +1 -1
  16. package/dist/commands/init.js +2 -1
  17. package/dist/commands/init.js.map +1 -1
  18. package/dist/commands/reset.js +3 -3
  19. package/dist/commands/reset.js.map +1 -1
  20. package/dist/commands/verify.d.ts.map +1 -1
  21. package/dist/commands/verify.js +9 -0
  22. package/dist/commands/verify.js.map +1 -1
  23. package/dist/index.d.ts +2 -2
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +1 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/lib/agent-skills.d.ts +21 -0
  28. package/dist/lib/agent-skills.d.ts.map +1 -0
  29. package/dist/lib/agent-skills.js +215 -0
  30. package/dist/lib/agent-skills.js.map +1 -0
  31. package/dist/lib/agents.d.ts +11 -13
  32. package/dist/lib/agents.d.ts.map +1 -1
  33. package/dist/lib/agents.js +12 -212
  34. package/dist/lib/agents.js.map +1 -1
  35. package/dist/lib/filesystem.d.ts +0 -1
  36. package/dist/lib/filesystem.d.ts.map +1 -1
  37. package/dist/lib/filesystem.js +0 -5
  38. package/dist/lib/filesystem.js.map +1 -1
  39. package/dist/lib/interf-bootstrap.d.ts +13 -0
  40. package/dist/lib/interf-bootstrap.d.ts.map +1 -0
  41. package/dist/lib/interf-bootstrap.js +215 -0
  42. package/dist/lib/interf-bootstrap.js.map +1 -0
  43. package/dist/lib/interf-detect.d.ts +31 -0
  44. package/dist/lib/interf-detect.d.ts.map +1 -0
  45. package/dist/lib/interf-detect.js +146 -0
  46. package/dist/lib/interf-detect.js.map +1 -0
  47. package/dist/lib/interf-scaffold.d.ts +15 -0
  48. package/dist/lib/interf-scaffold.d.ts.map +1 -0
  49. package/dist/lib/interf-scaffold.js +592 -0
  50. package/dist/lib/interf-scaffold.js.map +1 -0
  51. package/dist/lib/interf.d.ts +4 -36
  52. package/dist/lib/interf.d.ts.map +1 -1
  53. package/dist/lib/interf.js +5 -1103
  54. package/dist/lib/interf.js.map +1 -1
  55. package/dist/lib/local-workflows.d.ts +7 -0
  56. package/dist/lib/local-workflows.d.ts.map +1 -1
  57. package/dist/lib/local-workflows.js +83 -10
  58. package/dist/lib/local-workflows.js.map +1 -1
  59. package/dist/lib/obsidian.d.ts +6 -0
  60. package/dist/lib/obsidian.d.ts.map +1 -0
  61. package/dist/lib/obsidian.js +166 -0
  62. package/dist/lib/obsidian.js.map +1 -0
  63. package/dist/lib/runtime.d.ts.map +1 -1
  64. package/dist/lib/runtime.js +1 -1
  65. package/dist/lib/runtime.js.map +1 -1
  66. package/dist/lib/schema.d.ts +1 -0
  67. package/dist/lib/schema.d.ts.map +1 -1
  68. package/dist/lib/schema.js +1 -1
  69. package/dist/lib/schema.js.map +1 -1
  70. package/dist/lib/state.d.ts +2 -4
  71. package/dist/lib/state.d.ts.map +1 -1
  72. package/dist/lib/state.js +1 -1
  73. package/dist/lib/state.js.map +1 -1
  74. package/dist/lib/util.d.ts +4 -0
  75. package/dist/lib/util.d.ts.map +1 -0
  76. package/dist/lib/util.js +14 -0
  77. package/dist/lib/util.js.map +1 -0
  78. package/dist/lib/validate-interface.d.ts +77 -0
  79. package/dist/lib/validate-interface.d.ts.map +1 -0
  80. package/dist/lib/validate-interface.js +485 -0
  81. package/dist/lib/validate-interface.js.map +1 -0
  82. package/dist/lib/validate-kb.d.ts +73 -0
  83. package/dist/lib/validate-kb.d.ts.map +1 -0
  84. package/dist/lib/validate-kb.js +223 -0
  85. package/dist/lib/validate-kb.js.map +1 -0
  86. package/dist/lib/validate.d.ts +17 -146
  87. package/dist/lib/validate.d.ts.map +1 -1
  88. package/dist/lib/validate.js +31 -713
  89. package/dist/lib/validate.js.map +1 -1
  90. package/dist/lib/workflow-definitions.d.ts.map +1 -1
  91. package/dist/lib/workflow-definitions.js +8 -0
  92. package/dist/lib/workflow-definitions.js.map +1 -1
  93. package/dist/lib/workflow-helpers.d.ts +41 -0
  94. package/dist/lib/workflow-helpers.d.ts.map +1 -0
  95. package/dist/lib/workflow-helpers.js +189 -0
  96. package/dist/lib/workflow-helpers.js.map +1 -0
  97. package/dist/lib/workflows-interface.d.ts +79 -0
  98. package/dist/lib/workflows-interface.d.ts.map +1 -0
  99. package/dist/lib/workflows-interface.js +625 -0
  100. package/dist/lib/workflows-interface.js.map +1 -0
  101. package/dist/lib/workflows-kb.d.ts +50 -0
  102. package/dist/lib/workflows-kb.d.ts.map +1 -0
  103. package/dist/lib/workflows-kb.js +282 -0
  104. package/dist/lib/workflows-kb.js.map +1 -0
  105. package/dist/lib/workflows.d.ts +11 -115
  106. package/dist/lib/workflows.d.ts.map +1 -1
  107. package/dist/lib/workflows.js +14 -1072
  108. package/dist/lib/workflows.js.map +1 -1
  109. package/package.json +8 -3
  110. package/skills/knowledge-base/compile/references/stage-entities.md +2 -2
  111. package/templates/knowledge-base/README.md +1 -1
@@ -1,1104 +1,6 @@
1
- import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync, copyFileSync, } from "node:fs";
2
- import { createHash } from "node:crypto";
3
- import { homedir } from "node:os";
4
- import { basename, join, resolve, relative } from "node:path";
5
- import { KNOWLEDGE_BASE_INTERFIGNORE } from "./config.js";
6
- import { getInterfaceWorkflow, getKnowledgeBaseWorkflow, listInterfaceWorkflowChoices, listKnowledgeBaseInterfaceWorkflowChoices, } from "./workflow-definitions.js";
7
- import { refreshKnowledgeBaseArtifacts, saveState, initKnowledgeBaseState, initInterfaceState, refreshInterfaceArtifacts, } from "./state.js";
8
- import { loadUserConfig } from "./user-config.js";
9
- import { safeReadJsonFile } from "./parse.js";
10
- import { renderJsonFrontmatter } from "./parse.js";
11
- import { InterfConfigSchema, } from "./schema.js";
12
- const INTERF_CONTAINER_NAME = "interf";
13
- const WORKFLOW_CONTAINER_NAME = "workflows";
14
- const BENCHMARK_CONTAINER_NAME = "benchmarks";
15
- const INTERF_CONFIG_FILE = "interf.json";
16
- const WORKFLOW_PACKAGE_DIR = "workflow";
17
- const BASE_GRAPH_SETTINGS = {
18
- "collapse-filter": true,
19
- search: 'path:"summaries" OR path:"knowledge" OR path:"home"',
20
- showTags: false,
21
- showAttachments: false,
22
- hideUnresolved: true,
23
- showOrphans: false,
24
- };
25
- const INTERFACE_GRAPH_SETTINGS = {
26
- "collapse-filter": true,
27
- search: 'path:"knowledge" OR path:"briefs" OR path:"summaries" OR path:"home"',
28
- showTags: false,
29
- showAttachments: false,
30
- hideUnresolved: true,
31
- showOrphans: false,
32
- };
33
- function buildInterfaceStagePolicySection(notes) {
34
- if (!notes || notes.length === 0)
35
- return [];
36
- return [
37
- "### Workflow bias",
38
- ...notes.map((note) => `- ${note}`),
39
- "",
40
- ];
41
- }
42
- function formatWorkflowStageFolders(stages) {
43
- return stages.map((stage) => `\`workflow/compile/stages/${stage.skillDir}/\``).join(", ");
44
- }
45
- function formatWorkflowStageSequence(stages) {
46
- return stages.map((stage) => stage.label).join(" -> ");
47
- }
48
- function buildWorkspaceRuntimeControlLines(workspaceLabel) {
49
- return [
50
- `- Source folder = CLI control plane and real files. This ${workspaceLabel} = generated workspace.`,
51
- `- The source folder normally has no generated \`AGENTS.md\` or \`CLAUDE.md\`. For agents, this ${workspaceLabel} workspace is the bootstrap entrypoint.`,
52
- `- \`.interf/run.json\` — current/last deterministic stage run, including executor and timestamps.`,
53
- `- \`.interf/stage-contract.json\` — machine-readable contract for the active stage.`,
54
- `- Stage instructions are flexible. Interf cares about contract compliance: reads, writes, verifiers, and state transitions.`,
55
- `- Local workflow docs customize stage implementation. They do not replace the workflow's stage order, proof rules, or state flow.`,
56
- ];
57
- }
58
- function buildWorkspaceAgentLayerLines(stageFolders, workspaceLabel) {
59
- return [
60
- `Treat \`AGENTS.md\` as the directory-level router for navigation, path rules, command usage, and which local workflow docs to read next.`,
61
- `Treat local \`workflow/.../SKILL.md\` files as workspace instruction docs. Do not assume they are globally installed slash-command skills; load them because \`AGENTS.md\` or the stage contract points to them.`,
62
- `If \`workflow/use/query/\` exists, read it before answering manual questions from this ${workspaceLabel}. For interfaces, that local query doc may send you to the parent knowledge-base \`../../workflow/use/query/SKILL.md\` loop.`,
63
- `On the first substantive manual question, run the raw-source gate immediately. For interfaces, verify parent knowledge-base reachability first, then check raw reachability. If the environment asks for permission, request it immediately instead of guessing. After that check, start the answer with exactly one line: \`Raw access: confirmed\` or \`Raw access: unavailable\`. \`confirmed\` means a suggested raw path was actually readable; \`unavailable\` means the raw check failed or permission was denied.`,
64
- `If local workflow docs exist under \`workflow/compile/stages/\` (${stageFolders}), read them before running a stage. A doc can declare \`mode: extend\` or \`mode: override\` in JSON frontmatter. Override replaces the bundled workflow instructions for that stage, but Interf still enforces the same proof, schema, verifier, and state-machine rules.`,
65
- ];
66
- }
67
- function topKnowledgeBaseManualPreflightBlock() {
68
- return [
69
- "## First question: required preflight",
70
- "",
71
- "Before describing this workspace or answering from compiled notes:",
72
- "",
73
- "1. Read `workflow/use/query/SKILL.md` now.",
74
- "2. Confirm `home.md`, `knowledge/`, and `summaries/` are reachable.",
75
- "3. Inspect `.interf/source-access.json` and try one `suggested_checks` path immediately.",
76
- "4. If the environment asks for permission, request it right away instead of guessing.",
77
- "5. Start the answer with exactly `Raw access: confirmed` or `Raw access: unavailable`.",
78
- "",
79
- ];
80
- }
81
- function topInterfaceManualPreflightBlock() {
82
- return [
83
- "## First question: required preflight",
84
- "",
85
- "Before describing this workspace or answering from compiled notes:",
86
- "",
87
- "1. Read local `workflow/use/query/SKILL.md` now.",
88
- "2. Confirm local interface artifacts are reachable: `home.md`, `knowledge/`, `briefs/`, and local `summaries/`.",
89
- "3. Confirm parent KB reachability via `../../AGENTS.md`, `../../home.md`, or `../../summaries/`.",
90
- "4. Inspect `../../.interf/source-access.json` and try one `suggested_checks` path immediately.",
91
- "5. If the environment asks for permission, request it right away instead of guessing.",
92
- "6. Start the answer with exactly `Raw access: confirmed` or `Raw access: unavailable`.",
93
- "",
94
- ];
95
- }
96
- function renderInterfaceCompilePlanStageSection(stage, index, useCase, notes) {
97
- const heading = `## Stage ${index + 1} — ${stage.label}`;
98
- switch (stage.contractType) {
99
- case "interface-retrieval":
100
- return [
101
- heading,
102
- "### Goal",
103
- `Retrieve the knowledge-base summaries needed to answer this interface's job: ${useCase}.`,
104
- "### Filter criteria",
105
- `- prioritize summaries that directly help answer: ${useCase}`,
106
- "### Key entities:",
107
- "- people, companies, products, systems, decisions, claims, and evidence objects tied to the use case",
108
- "### Priority evidence tiers:",
109
- "- direct source-backed summaries",
110
- "- recent or canonical summaries",
111
- "- linked supporting summaries that close coverage gaps",
112
- "### Truth modes to prioritize or downweight:",
113
- "- prioritize current, observed, and final material",
114
- "- downweight speculative, stale, or weakly supported material unless needed for coverage",
115
- "### Expected relevant file count:",
116
- "- start narrow, then expand until coverage proof is complete",
117
- ...buildInterfaceStagePolicySection(notes),
118
- "### Method",
119
- "- Scan frontmatter for all knowledge-base summaries in `../../summaries/`.",
120
- "- Read candidate abstracts before narrowing.",
121
- "- Expand through linked summaries when needed to prove coverage.",
122
- "- Write `.interf/relevant.json` and `.interf/coverage.json`.",
123
- "",
124
- ];
125
- case "interface-analysis":
126
- return [
127
- heading,
128
- "### Goal",
129
- `Extract the interface-specific structure and findings needed for: ${useCase}.`,
130
- "### Batching:",
131
- "- analyze relevant summaries in bounded batches, then merge local findings",
132
- "### Entities:",
133
- "- derive the entities, themes, and evidence objects this interface needs",
134
- "### Claims:",
135
- "- record evidence-backed claims, contradictions, and open questions for the use case",
136
- ...buildInterfaceStagePolicySection(notes),
137
- "### Method",
138
- "- Use `.interf/relevant.json` as the analysis ledger.",
139
- "- Refine the local ontology from evidence, not just the initial wording.",
140
- "- Write `.interf/analysis.json`.",
141
- "",
142
- ];
143
- case "interface-output":
144
- return [
145
- heading,
146
- "### Goal",
147
- "Compile local interface outputs an agent can use immediately.",
148
- "### Output files",
149
- "- `home.md`",
150
- "- `knowledge/`",
151
- "- `briefs/`",
152
- "- `summaries/` when interface-local summaries are useful",
153
- ...buildInterfaceStagePolicySection(notes),
154
- "### Method",
155
- `- Write local notes and briefs for: ${useCase}.`,
156
- "- Keep citations pointing at `../../summaries/...` when using parent knowledge-base evidence.",
157
- "- Write `home.md`, `knowledge/`, and `briefs/`, then update `.interf/state.json`.",
158
- "",
159
- ];
160
- default:
161
- return [
162
- heading,
163
- "### Goal",
164
- `Complete the ${stage.label} stage for this interface.`,
165
- "### Method",
166
- "- Follow the active stage contract and write the required artifacts.",
167
- ...buildInterfaceStagePolicySection(notes),
168
- "",
169
- ];
170
- }
171
- }
172
- function renderInterfaceCompilePlanTemplate(options) {
173
- const useCase = options.useCase?.trim() || options.workflowPlaceholder || options.workflowHint;
174
- const stageSections = options.stages.flatMap((stage, index) => renderInterfaceCompilePlanStageSection(stage, index, useCase, options.stagePolicyNotes?.[stage.id]));
175
- return [
176
- "# Compile Plan",
177
- "",
178
- "## Interface Goal",
179
- `- workflow: \`${options.workflowId}\` (${options.workflowLabel})`,
180
- `- use case: ${useCase}`,
181
- `- method: ${options.workflowHint}`,
182
- "",
183
- ...stageSections,
184
- ].join("\n");
185
- }
186
- export function writeInterfaceCompilePlanTemplate(dirPath, options) {
187
- const workflow = getInterfaceWorkflow(options.workflowId, {
188
- sourcePath: options.sourcePath,
189
- workspacePath: dirPath,
190
- });
191
- const config = readInterfConfig(dirPath);
192
- const interfaceName = options.interfaceName ?? config?.name ?? basename(dirPath);
193
- const content = renderInterfaceCompilePlanTemplate({
194
- interfaceName,
195
- workflowId: workflow.id,
196
- workflowLabel: workflow.label,
197
- workflowHint: workflow.hint,
198
- workflowPlaceholder: workflow.placeholder,
199
- useCase: options.useCase,
200
- stages: workflow.stages,
201
- stagePolicyNotes: workflow.stagePolicyNotes,
202
- });
203
- writeFileSync(join(dirPath, "compile-plan.md"), content);
204
- }
205
- export function readInterfConfig(dirPath) {
206
- const configPath = join(dirPath, INTERF_CONFIG_FILE);
207
- if (!existsSync(configPath))
208
- return null;
209
- const raw = safeReadJsonFile(configPath, "Interf config");
210
- if (!raw)
211
- return null;
212
- const parsed = InterfConfigSchema.safeParse(raw);
213
- return parsed.success ? parsed.data : null;
214
- }
215
- export function detectInterf(cwd) {
216
- // Check if cwd itself is a knowledge-base (has interf.json)
217
- const config = readInterfConfig(cwd);
218
- if (config)
219
- return { path: cwd, config };
220
- const directChildren = listKnowledgeBaseChildren(join(cwd, INTERF_CONTAINER_NAME));
221
- if (directChildren.length === 1) {
222
- return directChildren[0];
223
- }
224
- if (basename(cwd) === INTERF_CONTAINER_NAME) {
225
- const nestedChildren = listKnowledgeBaseChildren(cwd);
226
- if (nestedChildren.length === 1) {
227
- return nestedChildren[0];
228
- }
229
- }
230
- return null;
231
- }
232
- export function resolveKnowledgeBase(cwd) {
233
- const detected = detectInterf(cwd);
234
- if (!detected)
235
- return null;
236
- if (detected.config.type === "knowledge-base") {
237
- const sourcePath = resolveKnowledgeBaseSourcePath(detected.path, detected.config);
238
- return {
239
- sourcePath,
240
- knowledgeBasePath: detected.path,
241
- config: detected.config,
242
- };
243
- }
244
- return null;
245
- }
246
- export function resolveInterfaceKnowledgeBasePath(interfacePath) {
247
- const config = readInterfConfig(interfacePath);
248
- if (!config || config.type !== "interface")
249
- return null;
250
- const knowledgeBasePath = config.knowledge_base?.path;
251
- if (!knowledgeBasePath)
252
- return null;
253
- return resolve(interfacePath, knowledgeBasePath);
254
- }
255
- export function resolveKnowledgeBaseSourcePath(knowledgeBasePath, config = readInterfConfig(knowledgeBasePath)) {
256
- return config?.type === "knowledge-base" && config.source?.path
257
- ? resolve(knowledgeBasePath, config.source.path)
258
- : resolve(knowledgeBasePath, "..", "..");
259
- }
260
- function listKnowledgeBaseChildren(containerPath) {
261
- if (!existsSync(containerPath))
262
- return [];
263
- try {
264
- if (!statSync(containerPath).isDirectory())
265
- return [];
266
- }
267
- catch {
268
- return [];
269
- }
270
- return readdirSync(containerPath)
271
- .map((entry) => join(containerPath, entry))
272
- .filter((entryPath) => {
273
- try {
274
- return statSync(entryPath).isDirectory();
275
- }
276
- catch {
277
- return false;
278
- }
279
- })
280
- .map((entryPath) => {
281
- const config = readInterfConfig(entryPath);
282
- return config?.type === "knowledge-base" ? { path: entryPath, config } : null;
283
- })
284
- .filter((value) => value !== null);
285
- }
286
- export function listKnowledgeBasesForSourceFolder(sourcePath) {
287
- return listKnowledgeBaseChildren(join(sourcePath, INTERF_CONTAINER_NAME));
288
- }
289
- function assertKnowledgeBaseContainer(sourcePath) {
290
- const containerPath = join(sourcePath, INTERF_CONTAINER_NAME);
291
- if (!existsSync(containerPath)) {
292
- return containerPath;
293
- }
294
- const stat = statSync(containerPath);
295
- if (!stat.isDirectory()) {
296
- throw new Error(`Interf container exists and is not a directory: ${containerPath}`);
297
- }
298
- const unexpectedEntries = readdirSync(containerPath).filter((entry) => {
299
- if (entry.startsWith("."))
300
- return false;
301
- const entryPath = join(containerPath, entry);
302
- if (entry === WORKFLOW_CONTAINER_NAME || entry === BENCHMARK_CONTAINER_NAME) {
303
- try {
304
- return !statSync(entryPath).isDirectory();
305
- }
306
- catch {
307
- return true;
308
- }
309
- }
310
- try {
311
- if (!statSync(entryPath).isDirectory())
312
- return true;
313
- }
314
- catch {
315
- return true;
316
- }
317
- const config = readInterfConfig(entryPath);
318
- return config?.type !== "knowledge-base";
319
- });
320
- if (unexpectedEntries.length > 0) {
321
- throw new Error(`Interf container contains non-knowledge-base entries: ${containerPath}`);
322
- }
323
- return containerPath;
324
- }
325
- function isInterfaceVaultPath(vaultPath) {
326
- const segments = resolve(vaultPath).split(/[\\/]+/).filter(Boolean);
327
- const interfIndex = segments.lastIndexOf(INTERF_CONTAINER_NAME);
328
- const interfacesIndex = segments.lastIndexOf("interfaces");
329
- return interfIndex >= 0 && interfacesIndex > interfIndex && interfacesIndex < segments.length - 1;
330
- }
331
- export function cleanupLegacyObsidianInterfaceVaultRegistrations() {
332
- const registryPath = getObsidianRegistryPath();
333
- if (!registryPath || !existsSync(registryPath))
334
- return 0;
335
- try {
336
- const current = JSON.parse(readFileSync(registryPath, "utf8"));
337
- const rawVaults = current && typeof current === "object" && current.vaults && typeof current.vaults === "object"
338
- ? { ...current.vaults }
339
- : null;
340
- if (!rawVaults)
341
- return 0;
342
- let removed = 0;
343
- for (const [key, value] of Object.entries(rawVaults)) {
344
- const vaultPath = typeof value?.path === "string" ? resolve(value.path) : null;
345
- if (!vaultPath)
346
- continue;
347
- if (!isInterfaceVaultPath(vaultPath))
348
- continue;
349
- const config = readInterfConfig(vaultPath);
350
- const isLegacyInterfaceVault = !config || config.type === "interface";
351
- if (!isLegacyInterfaceVault)
352
- continue;
353
- delete rawVaults[key];
354
- removed += 1;
355
- }
356
- if (removed === 0)
357
- return 0;
358
- const next = {
359
- ...(current && typeof current === "object" ? current : {}),
360
- vaults: rawVaults,
361
- };
362
- writeFileSync(registryPath, JSON.stringify(next) + "\n");
363
- return removed;
364
- }
365
- catch {
366
- return 0;
367
- }
368
- }
369
- function assertWritableTargetDir(dirPath, label) {
370
- if (!existsSync(dirPath))
371
- return;
372
- const stat = statSync(dirPath);
373
- if (!stat.isDirectory()) {
374
- throw new Error(`${label} target exists and is not a directory: ${dirPath}`);
375
- }
376
- if (readdirSync(dirPath).length > 0) {
377
- throw new Error(`${label} target already exists and is not empty: ${dirPath}`);
378
- }
379
- }
380
- function assertWorkspaceName(name, label) {
381
- if (!/^[a-z0-9][a-z0-9-]*$/.test(name)) {
382
- throw new Error(`${label} name must be a safe slug with lowercase letters, numbers, and dashes only: ${name}`);
383
- }
384
- }
385
- function renderClaudeBootstrap(content) {
386
- return [
387
- "<!-- Generated from AGENTS.md. Edit AGENTS.md; Interf will rewrite this file. -->",
388
- "",
389
- content.trimEnd(),
390
- "",
391
- ].join("\n");
392
- }
393
- function writeInterfConfigFile(dirPath, config) {
394
- writeFileSync(join(dirPath, INTERF_CONFIG_FILE), `${JSON.stringify(config, null, 2)}\n`);
395
- }
396
- function writeAgentBootstrapFiles(dirPath, content) {
397
- writeFileSync(join(dirPath, "AGENTS.md"), content);
398
- writeFileSync(join(dirPath, "CLAUDE.md"), renderClaudeBootstrap(content));
399
- }
400
- function insertLineAfterFirstMatch(content, predicate, insertedLine) {
401
- if (content.includes(insertedLine))
402
- return content;
403
- const lines = content.split("\n");
404
- const index = lines.findIndex(predicate);
405
- if (index === -1)
406
- return content;
407
- lines.splice(index + 1, 0, insertedLine);
408
- return lines.join("\n");
409
- }
410
- function insertBlockBeforeFirstMatch(content, predicate, blockLines) {
411
- const marker = blockLines.find((line) => line.trim().length > 0);
412
- if (marker && content.includes(marker))
413
- return content;
414
- const lines = content.split("\n");
415
- const index = lines.findIndex(predicate);
416
- if (index === -1)
417
- return content;
418
- lines.splice(index, 0, ...blockLines);
419
- return lines.join("\n");
420
- }
421
- function refreshLegacyAgentsBootstrap(content) {
422
- const legacyLine = "On the first substantive reply in a manual session, make the raw-access status explicit: confirmed, unverified, or unavailable. Do not trigger a permission prompt just to greet the user; verify raw access only when the user asks about raw files or the answer depends on raw verification.";
423
- const previousLine = "On the first substantive reply in a manual session, start with exactly one line: `Raw access: unverified`, `Raw access: confirmed`, or `Raw access: unavailable`. Use `unverified` until you actually run the raw-source gate, `confirmed` only after a suggested raw path is readable, and `unavailable` only after a raw check fails or permission is denied. Do not trigger a permission prompt just to greet the user; verify raw access only when the user asks about raw files or the answer depends on raw verification.";
424
- const currentLine = "On the first substantive manual question, run the raw-source gate immediately. For interfaces, verify parent knowledge-base reachability first, then check raw reachability. If the environment asks for permission, request it immediately instead of guessing. After that check, start the answer with exactly one line: `Raw access: confirmed` or `Raw access: unavailable`. `confirmed` means a suggested raw path was actually readable; `unavailable` means the raw check failed or permission was denied.";
425
- let replaced = content;
426
- if (replaced.includes(legacyLine))
427
- replaced = replaced.replace(legacyLine, currentLine);
428
- if (replaced.includes(previousLine))
429
- replaced = replaced.replace(previousLine, currentLine);
430
- let refreshed = insertLineAfterFirstMatch(replaced, (line) => line.startsWith("If `workflow/use/query/` exists, read it before answering manual questions"), currentLine);
431
- const block = refreshed.includes("../../.interf/source-access.json")
432
- ? topInterfaceManualPreflightBlock()
433
- : topKnowledgeBaseManualPreflightBlock();
434
- refreshed = insertBlockBeforeFirstMatch(refreshed, (line) => line.startsWith("## How this "), block);
435
- return refreshed;
436
- }
437
- function refreshLegacyKnowledgeBaseQuerySkill(content) {
438
- const legacyBlock = [
439
- "First-reply rule:",
440
- "- on your first substantive reply, say whether raw access is confirmed, unverified, or unavailable",
441
- "- do not trigger a permission prompt just to introduce the workspace",
442
- "- if the user asks whether raw files are reachable, perform the raw-source gate immediately and report the result clearly",
443
- "",
444
- ].join("\n");
445
- const currentBlock = [
446
- "First-reply rule:",
447
- "- on the first substantive manual question, inspect `.interf/source-access.json` immediately and try one `suggested_checks` path",
448
- "- if the environment asks for permission, request it immediately instead of answering as though raw were missing",
449
- "- the first line of the answer must be exactly `Raw access: confirmed` or `Raw access: unavailable`",
450
- "- use `Raw access: confirmed` only after a suggested raw path is readable from this session",
451
- "- use `Raw access: unavailable` only after a raw check fails or permission is denied",
452
- "- after the preflight, still prefer compiled knowledge unless the user asks for direct quotes or raw verification",
453
- "",
454
- ].join("\n");
455
- const topBlock = [
456
- "## First question: required preflight",
457
- "",
458
- "Before browsing notes on your own or answering from compiled knowledge:",
459
- "",
460
- "- inspect `.interf/source-access.json` immediately and try one `suggested_checks` path",
461
- "- if the environment asks for permission, request it immediately instead of answering as though raw were missing",
462
- "- the first line of the answer must be exactly `Raw access: confirmed` or `Raw access: unavailable`",
463
- "",
464
- ];
465
- let normalized = content.includes(legacyBlock) ? content.replace(legacyBlock, currentBlock) : content;
466
- normalized = insertBlockBeforeFirstMatch(normalized, (line) => line.startsWith("Default loop:"), topBlock);
467
- return insertBlockBeforeFirstMatch(normalized, (line) => line.startsWith("You can edit this file to bias manual question-answering behavior"), [
468
- "First-reply rule:",
469
- "- on the first substantive manual question, inspect `.interf/source-access.json` immediately and try one `suggested_checks` path",
470
- "- if the environment asks for permission, request it immediately instead of answering as though raw were missing",
471
- "- the first line of the answer must be exactly `Raw access: confirmed` or `Raw access: unavailable`",
472
- "- use `Raw access: confirmed` only after a suggested raw path is readable from this session",
473
- "- use `Raw access: unavailable` only after a raw check fails or permission is denied",
474
- "- after the preflight, still prefer compiled knowledge unless the user asks for direct quotes or raw verification",
475
- "",
476
- ]);
477
- }
478
- function refreshLegacyInterfaceQuerySkill(content) {
479
- const legacyBlock = [
480
- "First-reply rule:",
481
- "- on your first substantive reply, say whether raw access is confirmed, unverified, or unavailable",
482
- "- do not trigger a permission prompt just to introduce the workspace",
483
- "- if the user asks whether raw files are reachable, perform the raw-source gate immediately and report the result clearly",
484
- "",
485
- ].join("\n");
486
- const currentBlock = [
487
- "First-reply rule:",
488
- "- on the first substantive manual question, verify parent KB reachability first, then inspect `../../.interf/source-access.json` immediately and try one `suggested_checks` path",
489
- "- if the environment asks for permission, request it immediately instead of answering as though raw were missing",
490
- "- the first line of the answer must be exactly `Raw access: confirmed` or `Raw access: unavailable`",
491
- "- use `Raw access: confirmed` only after a suggested raw path is readable from this session",
492
- "- use `Raw access: unavailable` only after a raw check fails or permission is denied",
493
- "- after the preflight, use local interface artifacts first, then the parent KB loop, and raw files mainly for quotes or verification",
494
- "",
495
- ].join("\n");
496
- const topBlock = [
497
- "## First question: required preflight",
498
- "",
499
- "Before browsing notes on your own or answering from compiled interface outputs:",
500
- "",
501
- "- verify parent KB reachability first, then inspect `../../.interf/source-access.json` immediately and try one `suggested_checks` path",
502
- "- if the environment asks for permission, request it immediately instead of answering as though raw were missing",
503
- "- the first line of the answer must be exactly `Raw access: confirmed` or `Raw access: unavailable`",
504
- "",
505
- ];
506
- let normalized = content.includes(legacyBlock) ? content.replace(legacyBlock, currentBlock) : content;
507
- normalized = insertBlockBeforeFirstMatch(normalized, (line) => line.startsWith("Default loop:"), topBlock);
508
- return insertBlockBeforeFirstMatch(normalized, (line) => line.startsWith("Keep the interface-specific behavior here."), [
509
- "First-reply rule:",
510
- "- on the first substantive manual question, verify parent KB reachability first, then inspect `../../.interf/source-access.json` immediately and try one `suggested_checks` path",
511
- "- if the environment asks for permission, request it immediately instead of answering as though raw were missing",
512
- "- the first line of the answer must be exactly `Raw access: confirmed` or `Raw access: unavailable`",
513
- "- use `Raw access: confirmed` only after a suggested raw path is readable from this session",
514
- "- use `Raw access: unavailable` only after a raw check fails or permission is denied",
515
- "- after the preflight, use local interface artifacts first, then the parent KB loop, and raw files mainly for quotes or verification",
516
- "",
517
- ]);
518
- }
519
- function writeWorkflowStarterDocs(dirPath, docs) {
520
- for (const doc of docs ?? []) {
521
- const targetPath = join(dirPath, doc.relativePath);
522
- mkdirSync(resolve(targetPath, ".."), { recursive: true });
523
- writeFileSync(targetPath, doc.content);
524
- }
525
- }
526
- function workflowPackagePath(dirPath) {
527
- return join(dirPath, WORKFLOW_PACKAGE_DIR);
528
- }
529
- function workflowInterfacesPath(knowledgeBasePath) {
530
- return join(workflowPackagePath(knowledgeBasePath), "interfaces");
531
- }
532
- function writeWorkflowPackageJson(rootPath, targetType, workflow) {
533
- writeFileSync(join(rootPath, "workflow.json"), `${JSON.stringify({
534
- id: workflow.id,
535
- type: targetType,
536
- label: workflow.label,
537
- hint: workflow.hint,
538
- ...(workflow.placeholder ? { placeholder: workflow.placeholder } : {}),
539
- stages: workflow.stages.map((stage) => ({
540
- id: stage.id,
541
- label: stage.label,
542
- description: stage.description,
543
- contract_type: stage.contractType,
544
- skill_dir: stage.skillDir,
545
- ...(stage.acceptance ? { acceptance: stage.acceptance } : {}),
546
- })),
547
- ...(workflow.stagePolicyNotes ? { stage_policy_notes: workflow.stagePolicyNotes } : {}),
548
- }, null, 2)}\n`);
549
- }
550
- function defaultWorkflowReadme(targetType, workflow) {
551
- const stageLines = workflow.stages.map((stage) => `- \`${stage.id}\` — ${stage.description} (${stage.contractType})`);
552
- return [
553
- `# ${workflow.label}`,
554
- "",
555
- `This is the local ${targetType} workflow package. Interf uses it when you run \`interf compile\`.`,
556
- "",
557
- "- `workflow.json` defines the stage graph and contract kinds for this workspace.",
558
- "- `create/` explains what the scaffold/setup step produces.",
559
- "- `compile/stages/` contains one stage folder per compile stage.",
560
- "- `use/query/` explains how to use the compiled knowledge base or interface in manual agent sessions.",
561
- "",
562
- `Workflow id: \`${workflow.id}\``,
563
- `Method: ${workflow.hint}`,
564
- "",
565
- "Compile stages:",
566
- ...stageLines,
567
- "",
568
- "Interf CLI remains the referee for contracts, proofs, validators, and runtime state under `.interf/`.",
569
- "",
570
- ].join("\n");
571
- }
572
- function defaultWorkflowCreateSkill(targetType, workflow) {
573
- return [
574
- "# Create",
575
- "",
576
- `This doc explains what Interf scaffolded for the selected ${targetType} workflow.`,
577
- "",
578
- `- selected workflow: \`${workflow.id}\``,
579
- `- method: ${workflow.hint}`,
580
- "- the CLI owns create-time validation, folder creation, config writing, and bootstrap docs",
581
- "- edit this workflow package after scaffold if you want to refine how later compile/query behavior works",
582
- "",
583
- ].join("\n");
584
- }
585
- function defaultKnowledgeBaseStageSkill(workflow, stage) {
586
- const notes = workflow.stagePolicyNotes?.[stage.id] ?? [];
587
- return [
588
- renderJsonFrontmatter({ mode: "extend" }),
589
- `# ${stage.label}`,
590
- "",
591
- `This workflow stage customizes the built-in ${stage.contractType} contract for the \`${workflow.id}\` knowledge-base workflow.`,
592
- "",
593
- `- purpose: ${stage.description}`,
594
- ...notes.map((note) => `- ${note}`),
595
- "",
596
- "Interf still enforces the same contract reads, writes, proof rules, validators, and state transitions for this stage.",
597
- "",
598
- ].join("\n");
599
- }
600
- function defaultInterfaceStageSkill(workflow, stage) {
601
- const notes = workflow.stagePolicyNotes?.[stage.id] ?? [];
602
- return [
603
- renderJsonFrontmatter({ mode: "extend" }),
604
- `# ${stage.label}`,
605
- "",
606
- `This workflow stage customizes the built-in ${stage.contractType} contract for the \`${workflow.id}\` interface workflow.`,
607
- "",
608
- `- purpose: ${stage.description}`,
609
- ...notes.map((note) => `- ${note}`),
610
- "",
611
- "Interf still enforces the same contract reads, writes, coverage proof rules, validators, and state transitions for this stage.",
612
- "",
613
- ].join("\n");
614
- }
615
- function defaultKnowledgeBaseQuerySkill() {
616
- return [
617
- "# Knowledge Base Query",
618
- "",
619
- "## First question: required preflight",
620
- "",
621
- "Before browsing notes on your own or answering from compiled knowledge:",
622
- "",
623
- "- inspect `.interf/source-access.json` immediately and try one `suggested_checks` path",
624
- "- if the environment asks for permission, request it immediately instead of answering as though raw were missing",
625
- "- the first line of the answer must be exactly `Raw access: confirmed` or `Raw access: unavailable`",
626
- "",
627
- "Use this skill when answering questions from inside this knowledge base in a manual agent session.",
628
- "AGENTS.md should route manual questions here before an agent starts browsing notes on its own.",
629
- "This is the canonical parent-knowledge-base query loop that attached interfaces can inherit before raw fallback.",
630
- "This is a workspace-local query doc, not a required globally installed slash-command skill.",
631
- "",
632
- "Default loop:",
633
- "- start with `AGENTS.md`, then `home.md`",
634
- "- browse `knowledge/` indexes, entities, and claims first",
635
- "- follow wikilinks and backlinks outward before treating one note as complete",
636
- "- use `summaries/` for deeper evidence",
637
- "- use raw source files only when needed for verification, direct quotes, ambiguity, or missing context",
638
- "",
639
- "Raw-source gate:",
640
- "- inspect `.interf/source-access.json` before depending on raw files",
641
- "- verify one `suggested_checks` path is actually readable from this session",
642
- "- if raw files are not reachable, say the answer is based on compiled knowledge and summaries only",
643
- "",
644
- "First-reply rule:",
645
- "- on the first substantive manual question, inspect `.interf/source-access.json` immediately and try one `suggested_checks` path",
646
- "- if the environment asks for permission, request it immediately instead of answering as though raw were missing",
647
- "- the first line of the answer must be exactly `Raw access: confirmed` or `Raw access: unavailable`",
648
- "- use `Raw access: confirmed` only after a suggested raw path is readable from this session",
649
- "- use `Raw access: unavailable` only after a raw check fails or permission is denied",
650
- "- after the preflight, still prefer compiled knowledge unless the user asks for direct quotes or raw verification",
651
- "",
652
- "You can edit this file to bias manual question-answering behavior without changing compile-stage logic.",
653
- "",
654
- ].join("\n");
655
- }
656
- function defaultInterfaceQuerySkill() {
657
- return [
658
- "# Interface Query",
659
- "",
660
- "## First question: required preflight",
661
- "",
662
- "Before browsing notes on your own or answering from compiled interface outputs:",
663
- "",
664
- "- verify parent KB reachability first, then inspect `../../.interf/source-access.json` immediately and try one `suggested_checks` path",
665
- "- if the environment asks for permission, request it immediately instead of answering as though raw were missing",
666
- "- the first line of the answer must be exactly `Raw access: confirmed` or `Raw access: unavailable`",
667
- "",
668
- "Use this skill when answering questions from inside this interface in a manual agent session.",
669
- "AGENTS.md should route manual questions here before an agent starts browsing notes on its own.",
670
- "Treat this as the interface-local query layer, not a replacement for the parent knowledge-base query loop.",
671
- "This is a workspace-local query doc, not a required globally installed slash-command skill.",
672
- "",
673
- "Default loop:",
674
- "- start with `AGENTS.md`, then `home.md`",
675
- "- browse local `knowledge/`, `briefs/`, and local `summaries/` first",
676
- "- follow wikilinks and backlinks outward before treating one note as complete",
677
- "- if local interface artifacts are insufficient, load and follow the parent knowledge-base query guidance at `../../workflow/use/query/SKILL.md` before going raw",
678
- "- use parent knowledge-base artifacts in this order: `../../home.md`, then `../../knowledge/`, then `../../summaries/`",
679
- "- use raw source files only when needed for verification, direct quotes, ambiguity, or missing context",
680
- "",
681
- "Raw-source gate:",
682
- "- inspect `../../.interf/source-access.json` before depending on raw files",
683
- "- verify one `suggested_checks` path is actually readable from this session",
684
- "- if raw files are not reachable, say the answer is based on interface and knowledge-base artifacts only",
685
- "",
686
- "First-reply rule:",
687
- "- on the first substantive manual question, verify parent KB reachability first, then inspect `../../.interf/source-access.json` immediately and try one `suggested_checks` path",
688
- "- if the environment asks for permission, request it immediately instead of answering as though raw were missing",
689
- "- the first line of the answer must be exactly `Raw access: confirmed` or `Raw access: unavailable`",
690
- "- use `Raw access: confirmed` only after a suggested raw path is readable from this session",
691
- "- use `Raw access: unavailable` only after a raw check fails or permission is denied",
692
- "- after the preflight, use local interface artifacts first, then the parent KB loop, and raw files mainly for quotes or verification",
693
- "",
694
- "Keep the interface-specific behavior here. Keep the parent knowledge-base-wide retrieval and raw-fallback policy in `../../workflow/use/query/SKILL.md`.",
695
- "",
696
- "You can edit this file to bias manual question-answering behavior without changing workflow stage logic.",
697
- "",
698
- ].join("\n");
699
- }
700
- function writeKnowledgeBaseWorkflowPackage(knowledgeBasePath, workflow, interfaceTemplates) {
701
- const workflowDir = workflowPackagePath(knowledgeBasePath);
702
- mkdirSync(join(workflowDir, "create"), { recursive: true });
703
- mkdirSync(join(workflowDir, "use", "query"), { recursive: true });
704
- mkdirSync(join(workflowDir, "compile", "stages"), { recursive: true });
705
- for (const stage of workflow.stages) {
706
- mkdirSync(join(workflowDir, "compile", "stages", stage.skillDir), { recursive: true });
707
- }
708
- writeWorkflowPackageJson(workflowDir, "knowledge-base", workflow);
709
- writeFileSync(join(workflowDir, "README.md"), defaultWorkflowReadme("knowledge-base", workflow));
710
- writeFileSync(join(workflowDir, "create", "SKILL.md"), defaultWorkflowCreateSkill("knowledge-base", workflow));
711
- writeFileSync(join(workflowDir, "use", "query", "SKILL.md"), defaultKnowledgeBaseQuerySkill());
712
- for (const stage of workflow.stages) {
713
- writeFileSync(join(workflowDir, "compile", "stages", stage.skillDir, "SKILL.md"), defaultKnowledgeBaseStageSkill(workflow, stage));
714
- }
715
- writeWorkflowStarterDocs(workflowDir, workflow.starterDocs);
716
- const interfacesDir = workflowInterfacesPath(knowledgeBasePath);
717
- mkdirSync(interfacesDir, { recursive: true });
718
- for (const template of interfaceTemplates) {
719
- writeInterfaceWorkflowTemplate(join(interfacesDir, template.id), template);
720
- }
721
- }
722
- function writeInterfaceWorkflowTemplate(targetDir, workflow) {
723
- mkdirSync(join(targetDir, "create"), { recursive: true });
724
- mkdirSync(join(targetDir, "use", "query"), { recursive: true });
725
- mkdirSync(join(targetDir, "compile", "stages"), { recursive: true });
726
- for (const stage of workflow.stages) {
727
- mkdirSync(join(targetDir, "compile", "stages", stage.skillDir), { recursive: true });
728
- }
729
- writeWorkflowPackageJson(targetDir, "interface", workflow);
730
- writeFileSync(join(targetDir, "README.md"), defaultWorkflowReadme("interface", workflow));
731
- writeFileSync(join(targetDir, "create", "SKILL.md"), defaultWorkflowCreateSkill("interface", workflow));
732
- writeFileSync(join(targetDir, "use", "query", "SKILL.md"), defaultInterfaceQuerySkill());
733
- for (const stage of workflow.stages) {
734
- writeFileSync(join(targetDir, "compile", "stages", stage.skillDir, "SKILL.md"), defaultInterfaceStageSkill(workflow, stage));
735
- }
736
- writeWorkflowStarterDocs(targetDir, workflow.starterDocs);
737
- }
738
- function writeInterfaceWorkflowPackage(interfacePath, workflow) {
739
- writeInterfaceWorkflowTemplate(workflowPackagePath(interfacePath), workflow);
740
- }
741
- export function syncClaudeBootstrap(dirPath) {
742
- const agentsPath = join(dirPath, "AGENTS.md");
743
- if (!existsSync(agentsPath))
744
- return;
745
- writeFileSync(join(dirPath, "CLAUDE.md"), renderClaudeBootstrap(readFileSync(agentsPath, "utf8")));
746
- }
747
- export function refreshWorkspaceBootstrapGuidance(dirPath) {
748
- const config = readInterfConfig(dirPath);
749
- if (!config)
750
- return false;
751
- let changed = false;
752
- const agentsPath = join(dirPath, "AGENTS.md");
753
- if (existsSync(agentsPath)) {
754
- const current = readFileSync(agentsPath, "utf8");
755
- const refreshed = refreshLegacyAgentsBootstrap(current);
756
- if (refreshed !== current) {
757
- writeFileSync(agentsPath, refreshed);
758
- changed = true;
759
- }
760
- }
761
- const querySkillPath = join(dirPath, "workflow", "use", "query", "SKILL.md");
762
- if (existsSync(querySkillPath)) {
763
- const current = readFileSync(querySkillPath, "utf8");
764
- const refreshed = config.type === "interface"
765
- ? refreshLegacyInterfaceQuerySkill(current)
766
- : refreshLegacyKnowledgeBaseQuerySkill(current);
767
- if (refreshed !== current) {
768
- writeFileSync(querySkillPath, refreshed);
769
- changed = true;
770
- }
771
- }
772
- if (changed || existsSync(agentsPath)) {
773
- syncClaudeBootstrap(dirPath);
774
- }
775
- return changed;
776
- }
777
- export function createKnowledgeBase(name, sourcePath, workflowId = "interf") {
778
- return createKnowledgeBaseWithWorkflow(name, sourcePath, workflowId);
779
- }
780
- export function createKnowledgeBaseWithWorkflow(name, sourcePath, workflowId = "interf") {
781
- assertWorkspaceName(name, "Knowledge base");
782
- const containerPath = assertKnowledgeBaseContainer(sourcePath);
783
- mkdirSync(containerPath, { recursive: true });
784
- const knowledgeBasePath = join(containerPath, name);
785
- assertWritableTargetDir(knowledgeBasePath, "Knowledge base");
786
- mkdirSync(knowledgeBasePath, { recursive: true });
787
- const selectedWorkflow = getKnowledgeBaseWorkflow(workflowId, { sourcePath });
788
- const interfaceTemplates = selectedWorkflow.interfaceTemplates ?? listInterfaceWorkflowChoices(sourcePath);
789
- const knowledgeBaseStageFolders = formatWorkflowStageFolders(selectedWorkflow.stages);
790
- const knowledgeBaseStageSequence = formatWorkflowStageSequence(selectedWorkflow.stages);
791
- writeInterfConfigFile(knowledgeBasePath, {
792
- type: "knowledge-base",
793
- name,
794
- workflow: selectedWorkflow.id,
795
- source: {
796
- path: "../..",
797
- },
798
- });
799
- // .interfignore
800
- if (existsSync(KNOWLEDGE_BASE_INTERFIGNORE)) {
801
- copyFileSync(KNOWLEDGE_BASE_INTERFIGNORE, join(knowledgeBasePath, ".interfignore"));
802
- }
803
- writeKnowledgeBaseWorkflowPackage(knowledgeBasePath, selectedWorkflow, interfaceTemplates);
804
- // Agent bootstrap docs
805
- writeAgentBootstrapFiles(knowledgeBasePath, [
806
- `# ${name}`,
807
- ``,
808
- ...topKnowledgeBaseManualPreflightBlock(),
809
- `This is the main Interf knowledge base — the primary local knowledge layer over the source files in the attached folder above this named folder.`,
810
- ``,
811
- `## How this knowledge base works`,
812
- ``,
813
- `- \`interf.json\` — config. Points to the source folder via \`source.path\`.`,
814
- `- Workflow: \`${selectedWorkflow.id}\` (${selectedWorkflow.label}). This is the methodology you chose for this knowledge base.`,
815
- `- Interf generates a stage contract from that workflow each time you run \`interf compile\`.`,
816
- `- \`workflow/\` — the local editable method package for this knowledge base.`,
817
- `- \`workflow/workflow.json\` — stage graph and contract kinds for the selected workflow.`,
818
- `- \`workflow/create/\` — create-time setup notes for this workflow.`,
819
- `- \`workflow/compile/stages/\` — one stage folder per compile stage: ${knowledgeBaseStageFolders}.`,
820
- `- \`workflow/use/query/\` — manual-use guidance when an agent is answering questions from this knowledge base.`,
821
- `- \`workflow/interfaces/\` — interface workflow templates available on top of this knowledge base.`,
822
- `- \`summaries/\` — per-file evidence objects. One per source file.`,
823
- `- \`knowledge/\` — the durable cross-file knowledge layer built by compile.`,
824
- `- \`home.md\` — start here. Overview of what's been built.`,
825
- ...buildWorkspaceRuntimeControlLines("knowledge-base"),
826
- `- \`interfaces/\` — focused workspaces for specific jobs or perspectives built on top of this knowledge base.`,
827
- `- \`.interf/source-access.json\` — quick raw-file access check for manual agents.`,
828
- `- Source files live in the grandparent folder (\`../../\`). They are never modified by interf.`,
829
- `- Default agent entrypoint is this knowledge-base workspace.`,
830
- `- Automated compile workers run from this workspace and read local \`AGENTS.md\`, \`.interf/stage-contract.json\`, and workflow docs here.`,
831
- `- If your coding agent is sandboxed to the current directory tree and raw source access is blocked, relaunch from the source folder or grant the source folder alongside this workspace.`,
832
- `- Before relying on raw files directly, inspect \`.interf/source-access.json\` and verify one path from \`suggested_checks\` is readable. These are canonical absolute source-file paths so they still work from nested interfaces. If raw paths are not reachable, fall back to \`summaries/\` and say raw fallback is unavailable.`,
833
- ``,
834
- `## Manual access checklist`,
835
- ``,
836
- `Use this checklist on the first substantive manual question so you do not get stuck later in the session:`,
837
- ``,
838
- `1. Read this \`AGENTS.md\` and then \`workflow/use/query/SKILL.md\`.`,
839
- `2. Confirm the compiled knowledge-base surface is reachable: \`home.md\`, \`knowledge/\`, and \`summaries/\`.`,
840
- `3. Run the raw-source preflight via \`.interf/source-access.json\` immediately. If the environment asks for permission, request it right away.`,
841
- `4. After that preflight, start the answer with exactly \`Raw access: confirmed\` or \`Raw access: unavailable\`.`,
842
- ``,
843
- `## Preferred operation`,
844
- ``,
845
- `Use the Interf CLI for normal operations:`,
846
- ``,
847
- `\`\`\``,
848
- `interf compile compile this knowledge base`,
849
- `interf verify summarize verify summary coverage and quality`,
850
- `interf verify compile verify graph/inventory consistency`,
851
- `interf create interface create an interface`,
852
- `interf status show compile health`,
853
- `\`\`\``,
854
- ``,
855
- `## For agents without the CLI`,
856
- ``,
857
- `Start with \`workflow/use/query/SKILL.md\`, then \`home.md\`. Browse \`knowledge/\` for entities and claims. Use \`summaries/\` for deeper evidence. Access source files at \`../../\` only when the raw-source gate says they are actually reachable.`,
858
- ...buildWorkspaceAgentLayerLines(knowledgeBaseStageFolders, "knowledge base"),
859
- `Navigation ladder: \`AGENTS.md\` -> local \`workflow/use/query/SKILL.md\` -> \`home.md\` -> local \`knowledge/\` indexes and notes -> linked \`summaries/\` evidence -> raw source files only when needed and actually reachable.`,
860
- `Backlinks and outward wikilinks are part of the interface. If a note links to a summary or another knowledge note, follow that link before guessing from one file in isolation.`,
861
- `If a question seems answerable from the current note but confidence is low, expand outward through linked summaries and related notes first. Use raw files mainly for verification, direct quotes, ambiguity, or missing context.`,
862
- `Raw-source gate: before giving an answer that depends on original source files, inspect \`.interf/source-access.json\` and verify one path from \`suggested_checks\` is actually readable from this session.`,
863
- `If that raw-file check fails or the agent asks for permission and the user has not granted it, do not claim raw-file verification. Say explicitly that raw source access is unavailable in this session and limit the answer to compiled summaries and knowledge artifacts.`,
864
- `When answering knowledge-base-wide questions, do not rely on a few hand-picked files. First verify summarize/compile integrity via \`.interf/inventory.json\`, \`.interf/health.json\`, or \`interf verify summarize --json\` and \`interf verify compile --json\`, then drill into the relevant summaries.`,
865
- `Treat summary titles as source-grounded descriptions, not absolute truth. Broader semantic claims should come from \`knowledge/\`, not from over-reading a single summary filename.`,
866
- ``,
867
- `## Obsidian (optional)`,
868
- ``,
869
- `If Obsidian viewer defaults are enabled, this knowledge base gets minimal graph defaults and Interf attempts to register this named folder with Obsidian.`,
870
- `If the running app does not pick it up immediately, reopen Obsidian or add the folder once via Manage vaults.`,
871
- ``,
872
- `## Rules`,
873
- ``,
874
- `- Do not modify source files in the grandparent folder.`,
875
- `- Follow prose-as-title: summary filenames are claims, not slugs.`,
876
- `- Follow wiki-link-as-prose: links read as sentences.`,
877
- ``,
878
- ].join("\n"));
879
- // home.md
880
- writeFileSync(join(knowledgeBasePath, "home.md"), `# ${name} — Home\n\nNot yet compiled. Run \`interf compile\` to execute the workflow-defined compile stages for this knowledge base (${knowledgeBaseStageSequence}).\n`);
881
- // .gitignore
882
- writeFileSync(join(knowledgeBasePath, ".gitignore"), "summaries/\nknowledge/\n.interf/\n.obsidian/\n");
883
- // Init state
884
- saveState(knowledgeBasePath, initKnowledgeBaseState());
885
- refreshKnowledgeBaseArtifacts(knowledgeBasePath, { ensureViewSpec: true });
886
- writeObsidianDefaults(knowledgeBasePath, "knowledge-base");
887
- return knowledgeBasePath;
888
- }
889
- export function createInterface(name, knowledgeBasePath, knowledgeBaseName, workflowId = "briefing") {
890
- return createInterfaceWithWorkflow(name, knowledgeBasePath, knowledgeBaseName, workflowId);
891
- }
892
- export function createInterfaceWithWorkflow(name, knowledgeBasePath, knowledgeBaseName, workflowId = "briefing") {
893
- assertWorkspaceName(name, "Interface");
894
- const interfacesDir = join(knowledgeBasePath, "interfaces");
895
- mkdirSync(interfacesDir, { recursive: true });
896
- const dirPath = resolve(interfacesDir, name);
897
- assertWritableTargetDir(dirPath, `Interface "${name}"`);
898
- mkdirSync(dirPath, { recursive: true });
899
- const knowledgeBaseRelativePath = relative(dirPath, knowledgeBasePath) || ".";
900
- const selectedWorkflow = listKnowledgeBaseInterfaceWorkflowChoices(knowledgeBasePath).find((candidate) => candidate.id === workflowId) ?? getInterfaceWorkflow(workflowId, { sourcePath: resolveKnowledgeBaseSourcePath(knowledgeBasePath) });
901
- const interfaceStageFolders = formatWorkflowStageFolders(selectedWorkflow.stages);
902
- const interfaceStageSequence = formatWorkflowStageSequence(selectedWorkflow.stages);
903
- writeInterfConfigFile(dirPath, {
904
- type: "interface",
905
- name,
906
- workflow: selectedWorkflow.id,
907
- knowledge_base: {
908
- path: knowledgeBaseRelativePath,
909
- },
910
- });
911
- // .gitignore
912
- writeFileSync(join(dirPath, ".gitignore"), "knowledge/\nbriefs/\nsummaries/\n.interf/\n.obsidian/\n");
913
- writeInterfaceWorkflowPackage(dirPath, selectedWorkflow);
914
- // Agent bootstrap docs
915
- writeAgentBootstrapFiles(dirPath, [
916
- `# ${name}`,
917
- ``,
918
- ...topInterfaceManualPreflightBlock(),
919
- `This is an Interf interface — a focused workspace built on top of the \`${knowledgeBaseName}\` knowledge base for one job, perspective, or workflow.`,
920
- ``,
921
- `## How this interface works`,
922
- ``,
923
- `- \`interf.json\` — config. Points to the main knowledge base via \`knowledge_base.path\`.`,
924
- `- Workflow: \`${selectedWorkflow.id}\` (${selectedWorkflow.label}). This is the methodology you chose for this interface.`,
925
- `- Interf generates a stage contract from that workflow each time you run \`interf compile\`.`,
926
- `- \`workflow/\` — the local editable method package for this interface.`,
927
- `- \`workflow/workflow.json\` — stage graph and contract kinds for the selected interface workflow.`,
928
- `- \`workflow/create/\` — create-time setup notes for this interface workflow.`,
929
- `- \`workflow/compile/stages/\` — one stage folder per compile stage: ${interfaceStageFolders}.`,
930
- `- \`workflow/use/query/\` — interface-local manual-use guidance.`,
931
- `- \`../../workflow/use/query/\` — parent knowledge-base manual-use guidance. Use it when local interface artifacts are insufficient before any raw-source fallback.`,
932
- `- \`compile-plan.md\` — interface execution plan for the workflow-defined compile stages (${interfaceStageSequence}). It is the initial runtime hypothesis, not the final word.`,
933
- `- \`knowledge/\` — this interface's local entities, claims, and indexes.`,
934
- `- \`briefs/\` — hero documents for this use case.`,
935
- `- \`summaries/\` — interface-local summaries or periodic summaries when this interface needs them. These are not the main knowledge-base summaries.`,
936
- ...buildWorkspaceRuntimeControlLines("interface"),
937
- `- \`home.md\` — start here.`,
938
- `- Knowledge-base raw-file quick check is at \`../../.interf/source-access.json\`.`,
939
- `- Main knowledge base is at \`../../\`. Knowledge-base summaries are at \`../../summaries/\`.`,
940
- `- Agent access hierarchy for this interface is: local interface artifacts -> parent knowledge-base artifacts -> raw source files only when needed.`,
941
- `- Default agent entrypoint is this interface workspace.`,
942
- `- Automated compile workers run from this workspace and start with local \`AGENTS.md\`, then parent knowledge-base entrypoints and workflow docs.`,
943
- `- If your coding agent is sandboxed to the current directory tree and raw source access is blocked, relaunch from the source folder or grant access to both the source root and this interface.`,
944
- `- Before raw-file fallback, verify that the parent knowledge base is reachable by opening \`../../AGENTS.md\`, \`../../home.md\`, or a summary under \`../../summaries/\`.`,
945
- `- Before relying on raw files directly, inspect \`../../.interf/source-access.json\` and verify one path from \`suggested_checks\` is readable. These are canonical absolute source-file paths shared by the parent knowledge base. If raw paths are not reachable, continue from knowledge-base summaries and say raw fallback is unavailable.`,
946
- `- In Obsidian, browse this interface from the parent knowledge-base vault so links to knowledge-base summaries and source files remain navigable.`,
947
- ``,
948
- `## Manual access checklist`,
949
- ``,
950
- `Use this checklist on the first substantive manual question so you do not get stuck later in the session:`,
951
- ``,
952
- `1. Read this \`AGENTS.md\` and then local \`workflow/use/query/SKILL.md\`.`,
953
- `2. Confirm the local interface surface is reachable: \`home.md\`, \`knowledge/\`, \`briefs/\`, and any local \`summaries/\`.`,
954
- `3. Confirm the parent knowledge base is reachable: \`../../AGENTS.md\`, \`../../home.md\`, and parent summaries.`,
955
- `4. Run the raw-source preflight through \`../../.interf/source-access.json\` immediately. If the environment asks for permission, request it right away.`,
956
- `5. After that preflight, start the answer with exactly \`Raw access: confirmed\` or \`Raw access: unavailable\`.`,
957
- ``,
958
- `## Preferred operation`,
959
- ``,
960
- `Use the Interf CLI:`,
961
- ``,
962
- `\`\`\``,
963
- `interf verify interface-plan verify the scaffold and compile plan`,
964
- `interf compile compile this interface`,
965
- `interf verify retrieve verify retrieval proof before analyze/compile`,
966
- `interf status show compile health`,
967
- `\`\`\``,
968
- ``,
969
- `## For agents without the CLI`,
970
- ``,
971
- `Read \`compile-plan.md\` for the execution plan. Start with \`workflow/use/query/SKILL.md\`, then \`home.md\`. Browse \`knowledge/\` for local entities, claims, and indexes. Use \`briefs/\` and local \`summaries/\` as high-level entry points. If that is insufficient, load \`../../workflow/use/query/SKILL.md\` and use the parent knowledge-base query loop before any raw fallback.`,
972
- ...buildWorkspaceAgentLayerLines(interfaceStageFolders, "interface"),
973
- `Navigation ladder: \`AGENTS.md\` -> local \`workflow/use/query/SKILL.md\` -> \`home.md\` -> local \`knowledge/\`, \`briefs/\`, and local \`summaries/\` -> parent \`../../workflow/use/query/SKILL.md\` -> parent \`../../home.md\`, \`../../knowledge/\`, and \`../../summaries/\` -> raw source files only when needed and actually reachable.`,
974
- `Knowledge-base gate: before raw fallback, verify that \`../../AGENTS.md\`, \`../../home.md\`, or a parent summary in \`../../summaries/\` is reachable from this session.`,
975
- `Backlinks and outward wikilinks are part of the interface. Follow linked claims, entities, briefs, and parent summaries before treating the current note as a complete answer.`,
976
- `If a question seems answerable from the current note but confidence is low, expand outward through the parent knowledge-base query loop first. Use raw files mainly for verification, direct quotes, ambiguity, or missing context.`,
977
- `Raw-source gate: before giving an answer that depends on original source files, inspect \`../../.interf/source-access.json\` and verify one path from \`suggested_checks\` is actually readable from this session.`,
978
- `If that raw-file check fails or the agent asks for permission and the user has not granted it, do not claim raw-file verification. Say explicitly that raw source access is unavailable in this session and limit the answer to compiled knowledge-base and interface artifacts.`,
979
- `If you are using Obsidian, open the parent knowledge-base vault rather than this interface folder alone. Standalone interface vaults cannot resolve links into the parent knowledge base.`,
980
- `Do not synthesize from the main knowledge base until the interface plan and retrieve coverage are both proved. Check \`compile-plan.md\`, \`.interf/coverage.json\`, \`.interf/relevant.json\`, and \`.interf/health.json\` first.`,
981
- `If a stage is active, \`.interf/stage-contract.json\` and \`.interf/run.json\` are the durable source of truth for what the harness expects and which executor is running.`,
982
- `Run \`interf verify interface-plan --json\` after creation and \`interf verify retrieve --json\` before analyze/compile if you need deterministic pass/fail checks.`,
983
- `For interface questions, the expected loop is: scan summary frontmatter -> review candidate abstracts -> expand through links if needed -> refine the runtime from evidence -> answer with evidence.`,
984
- ``,
985
- `## Rules`,
986
- ``,
987
- `- All outputs are local to this interface.`,
988
- `- Do not modify the main knowledge base or source files.`,
989
- `- Prefer evidence-linked notes over summaries without sources.`,
990
- ``,
991
- ].join("\n"));
992
- // home.md
993
- writeFileSync(join(dirPath, "home.md"), `# ${name} — Home\n\nNot yet compiled. Run \`interf compile\` to start interface compilation.\n\nThis interface reads from the parent knowledge base at \`../../\`. In Obsidian, open the parent knowledge-base vault so links to knowledge-base summaries and source files resolve correctly.\n`);
994
- writeInterfaceCompilePlanTemplate(dirPath, {
995
- workflowId: selectedWorkflow.id,
996
- sourcePath: resolveKnowledgeBaseSourcePath(knowledgeBasePath),
997
- interfaceName: name,
998
- });
999
- // Init state
1000
- saveState(dirPath, initInterfaceState());
1001
- refreshInterfaceArtifacts(dirPath, { ensureViewSpec: true });
1002
- writeObsidianDefaults(dirPath, "interface", { registerVault: false });
1003
- return dirPath;
1004
- }
1005
- function writeObsidianDefaults(dirPath, type, options = {}) {
1006
- const viewer = loadUserConfig()?.viewer ?? "plain";
1007
- if (viewer !== "obsidian")
1008
- return;
1009
- const obsidianDir = join(dirPath, ".obsidian");
1010
- mkdirSync(obsidianDir, { recursive: true });
1011
- const graphSettings = type === "knowledge-base" ? BASE_GRAPH_SETTINGS : INTERFACE_GRAPH_SETTINGS;
1012
- writeFileSync(join(obsidianDir, "graph.json"), JSON.stringify(graphSettings, null, 2) + "\n");
1013
- if (process.env.INTERF_DISABLE_VIEWER_OPEN === "1")
1014
- return;
1015
- if (options.registerVault === false) {
1016
- removeObsidianVaultRegistration(resolve(dirPath));
1017
- return;
1018
- }
1019
- ensureObsidianVaultRegistered(resolve(dirPath));
1020
- }
1021
- function ensureObsidianVaultRegistered(dirPath) {
1022
- const registryPath = getObsidianRegistryPath();
1023
- if (!registryPath)
1024
- return false;
1025
- try {
1026
- mkdirSync(resolve(registryPath, ".."), { recursive: true });
1027
- const current = existsSync(registryPath)
1028
- ? JSON.parse(readFileSync(registryPath, "utf8"))
1029
- : {};
1030
- const vaults = current && typeof current === "object" && current.vaults && typeof current.vaults === "object"
1031
- ? current.vaults
1032
- : {};
1033
- for (const [key, value] of Object.entries(vaults)) {
1034
- if (typeof value?.path === "string" && resolve(value.path) === dirPath && key !== getObsidianVaultId(dirPath)) {
1035
- delete vaults[key];
1036
- }
1037
- }
1038
- const vaultId = getObsidianVaultId(dirPath);
1039
- const existing = vaults[vaultId];
1040
- vaults[vaultId] = {
1041
- ...(existing ?? {}),
1042
- path: dirPath,
1043
- ts: Date.now(),
1044
- };
1045
- const next = {
1046
- ...(current && typeof current === "object" ? current : {}),
1047
- vaults,
1048
- };
1049
- writeFileSync(registryPath, JSON.stringify(next) + "\n");
1050
- return true;
1051
- }
1052
- catch {
1053
- return false;
1054
- }
1055
- }
1056
- function removeObsidianVaultRegistration(dirPath) {
1057
- const registryPath = getObsidianRegistryPath();
1058
- if (!registryPath || !existsSync(registryPath))
1059
- return false;
1060
- try {
1061
- const current = JSON.parse(readFileSync(registryPath, "utf8"));
1062
- const vaults = current && typeof current === "object" && current.vaults && typeof current.vaults === "object"
1063
- ? { ...current.vaults }
1064
- : null;
1065
- if (!vaults)
1066
- return false;
1067
- let changed = false;
1068
- for (const [key, value] of Object.entries(vaults)) {
1069
- if (typeof value?.path === "string" && resolve(value.path) === dirPath) {
1070
- delete vaults[key];
1071
- changed = true;
1072
- }
1073
- }
1074
- if (!changed)
1075
- return false;
1076
- const next = {
1077
- ...(current && typeof current === "object" ? current : {}),
1078
- vaults,
1079
- };
1080
- writeFileSync(registryPath, JSON.stringify(next) + "\n");
1081
- return true;
1082
- }
1083
- catch {
1084
- return false;
1085
- }
1086
- }
1087
- function getObsidianRegistryPath() {
1088
- if (process.env.INTERF_OBSIDIAN_CONFIG_DIR) {
1089
- return join(process.env.INTERF_OBSIDIAN_CONFIG_DIR, "obsidian.json");
1090
- }
1091
- if (process.platform === "darwin") {
1092
- return join(homedir(), "Library", "Application Support", "obsidian", "obsidian.json");
1093
- }
1094
- if (process.platform === "win32") {
1095
- const appData = process.env.APPDATA ?? join(homedir(), "AppData", "Roaming");
1096
- return join(appData, "obsidian", "obsidian.json");
1097
- }
1098
- const xdgConfig = process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config");
1099
- return join(xdgConfig, "obsidian", "obsidian.json");
1100
- }
1101
- function getObsidianVaultId(dirPath) {
1102
- return createHash("md5").update(dirPath).digest("hex").slice(0, 16);
1103
- }
1
+ // Barrel re-exports from the three focused sub-modules.
2
+ // Every symbol previously exported from this file is still importable here.
3
+ export { INTERF_CONTAINER_NAME, WORKFLOW_CONTAINER_NAME, BENCHMARK_CONTAINER_NAME, INTERF_CONFIG_FILE, WORKFLOW_PACKAGE_DIR, readInterfConfig, detectInterf, resolveKnowledgeBase, resolveInterfaceKnowledgeBasePath, resolveKnowledgeBaseSourcePath, listKnowledgeBasesForSourceFolder, assertKnowledgeBaseContainer, assertWritableTargetDir, assertWorkspaceName, } from "./interf-detect.js";
4
+ export { syncClaudeBootstrap, refreshWorkspaceBootstrapGuidance, refreshLegacyAgentsBootstrap, refreshLegacyKnowledgeBaseQuerySkill, refreshLegacyInterfaceQuerySkill, insertLineAfterFirstMatch, insertBlockBeforeFirstMatch, renderClaudeBootstrap, topKnowledgeBaseManualPreflightBlock, topInterfaceManualPreflightBlock, buildWorkspaceRuntimeControlLines, buildWorkspaceAgentLayerLines, } from "./interf-bootstrap.js";
5
+ export { buildInterfaceStagePolicySection, formatWorkflowStageFolders, formatWorkflowStageSequence, writeInterfaceCompilePlanTemplate, createKnowledgeBase, createKnowledgeBaseWithWorkflow, createInterface, createInterfaceWithWorkflow, } from "./interf-scaffold.js";
1104
6
  //# sourceMappingURL=interf.js.map