intentdna 1.6.4 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/cli/commands/compile.js +2 -47
- package/dist/cli/commands/context.d.ts +8 -0
- package/dist/cli/commands/context.js +63 -0
- package/dist/cli/commands/feedback.d.ts +1 -0
- package/dist/cli/commands/feedback.js +11 -0
- package/dist/cli/commands/generate.js +5 -33
- package/dist/cli/commands/init.js +11 -63
- package/dist/cli/commands/run.js +5 -4
- package/dist/cli/commands/show.js +2 -38
- package/dist/cli/commands/sync.d.ts +9 -6
- package/dist/cli/commands/sync.js +209 -190
- package/dist/cli/commands/templates.d.ts +10 -1
- package/dist/cli/commands/templates.js +50 -1
- package/dist/cli/commands/validate.js +15 -9
- package/dist/cli/commands/verify.js +97 -25
- package/dist/cli/index.js +76 -11
- package/dist/compiler/cascade.d.ts +3 -1
- package/dist/compiler/cascade.js +51 -0
- package/dist/compiler/compile.js +37 -0
- package/dist/compiler/diagnostics.d.ts +17 -0
- package/dist/compiler/diagnostics.js +30 -0
- package/dist/compiler/index.d.ts +3 -0
- package/dist/compiler/index.js +8 -11
- package/dist/compiler/input-resolver.d.ts +25 -0
- package/dist/compiler/input-resolver.js +175 -0
- package/dist/hooks/cli.d.ts +10 -1
- package/dist/hooks/cli.js +39 -26
- package/dist/hooks/event-registry.d.ts +17 -0
- package/dist/hooks/event-registry.js +89 -0
- package/dist/hooks/protocol.d.ts +1 -1
- package/dist/hooks/schema.js +5 -1
- package/dist/hooks/state.d.ts +2 -0
- package/dist/hooks/state.js +23 -2
- package/dist/mcp/index.js +2 -0
- package/dist/mcp/tools-compile.js +18 -49
- package/dist/mcp/tools-context.d.ts +2 -0
- package/dist/mcp/tools-context.js +85 -0
- package/dist/mcp/tools-enforce.d.ts +2 -2
- package/dist/mcp/tools-enforce.js +21 -53
- package/dist/mcp/tools-observability.js +24 -0
- package/dist/report/kernel-signals.js +3 -0
- package/dist/report/report-package.d.ts +56 -0
- package/dist/report/report-package.js +85 -0
- package/dist/runtime/agent-md.d.ts +1 -0
- package/dist/runtime/agent-md.js +21 -3
- package/dist/runtime/context-sources.d.ts +14 -0
- package/dist/runtime/context-sources.js +60 -0
- package/dist/runtime/markdown-target-registry.d.ts +17 -0
- package/dist/runtime/markdown-target-registry.js +37 -0
- package/dist/runtime/markdown.d.ts +2 -1
- package/dist/runtime/markdown.js +9 -12
- package/dist/runtime/output-artifact-registry.d.ts +11 -0
- package/dist/runtime/output-artifact-registry.js +30 -0
- package/dist/runtime/settings-adapter.d.ts +2 -1
- package/dist/runtime/settings-adapter.js +4 -12
- package/dist/runtime/skill-adapter.d.ts +32 -4
- package/dist/runtime/skill-adapter.js +187 -10
- package/dist/runtime/workflow-runner.d.ts +1 -1
- package/dist/runtime/workflow-runner.js +1 -1
- package/dist/schema/controller-registry.d.ts +29 -0
- package/dist/schema/controller-registry.js +35 -0
- package/dist/schema/types.d.ts +33 -0
- package/dist/schema/validate.js +157 -138
- package/dist/schema/validators/controllers.d.ts +3 -0
- package/dist/schema/validators/controllers.js +156 -0
- package/dist/signals/index.d.ts +10 -0
- package/dist/signals/index.js +90 -5
- package/dist/templates/catalog.d.ts +19 -0
- package/dist/templates/catalog.js +57 -0
- package/dist/templates/flutter-rewrite.dna.yaml +2 -2
- package/dist/templates/metadata.d.ts +6 -0
- package/dist/templates/metadata.js +32 -0
- package/package.json +1 -1
- package/spec/foundation-hardening.md +2 -1
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* dna templates
|
|
2
|
+
* dna templates — template catalog and deployment commands.
|
|
3
3
|
*/
|
|
4
4
|
import { readdirSync, copyFileSync, existsSync, readFileSync, mkdirSync } from "node:fs";
|
|
5
5
|
import { resolve, join } from "node:path";
|
|
6
6
|
import { execSync } from "node:child_process";
|
|
7
|
+
import { readFile } from "node:fs/promises";
|
|
8
|
+
import { findTemplateCatalogEntry, listTemplateCatalog } from "../../templates/catalog.js";
|
|
7
9
|
/**
|
|
8
10
|
* Resolve the global intentdna templates directory.
|
|
9
11
|
* Strategy: npm root -g + /intentdna/dist/templates
|
|
@@ -45,6 +47,53 @@ function verifyProjectRoot(cwd) {
|
|
|
45
47
|
}
|
|
46
48
|
return cwd;
|
|
47
49
|
}
|
|
50
|
+
export async function runTemplatesList(opts = {}) {
|
|
51
|
+
const templates = await listTemplateCatalog({ projectDir: opts.projectDir });
|
|
52
|
+
if (templates.length === 0) {
|
|
53
|
+
process.stderr.write("No templates found.\n");
|
|
54
|
+
return 1;
|
|
55
|
+
}
|
|
56
|
+
process.stderr.write(`Available templates (${templates.length}):\n`);
|
|
57
|
+
for (const template of templates) {
|
|
58
|
+
const ns = template.namespace ? ` ns=${template.namespace}` : "";
|
|
59
|
+
const version = template.version ? ` v${template.version}` : "";
|
|
60
|
+
process.stderr.write(` - ${template.name} [${template.source}]${ns}${version}`);
|
|
61
|
+
if (template.displayName)
|
|
62
|
+
process.stderr.write(` — ${template.displayName}`);
|
|
63
|
+
process.stderr.write("\n");
|
|
64
|
+
}
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
67
|
+
export async function runTemplatesShow(opts) {
|
|
68
|
+
if (!opts.name) {
|
|
69
|
+
process.stderr.write("Usage: dna templates show <name>\n");
|
|
70
|
+
return 2;
|
|
71
|
+
}
|
|
72
|
+
const template = await findTemplateCatalogEntry(opts.name, { projectDir: opts.projectDir });
|
|
73
|
+
if (!template) {
|
|
74
|
+
process.stderr.write(`Unknown template: ${opts.name}\n`);
|
|
75
|
+
return 2;
|
|
76
|
+
}
|
|
77
|
+
const content = await readFile(template.path, "utf-8");
|
|
78
|
+
process.stderr.write(`Template: ${template.name}\n`);
|
|
79
|
+
process.stderr.write(`Source: ${template.source}\n`);
|
|
80
|
+
process.stderr.write(`Path: ${template.path}\n`);
|
|
81
|
+
if (template.displayName)
|
|
82
|
+
process.stderr.write(`Name: ${template.displayName}\n`);
|
|
83
|
+
if (template.namespace)
|
|
84
|
+
process.stderr.write(`Namespace: ${template.namespace}\n`);
|
|
85
|
+
if (template.version)
|
|
86
|
+
process.stderr.write(`Version: ${template.version}\n`);
|
|
87
|
+
process.stderr.write(`Content hash: ${template.contentHash}\n`);
|
|
88
|
+
if (template.keywords.length > 0) {
|
|
89
|
+
process.stderr.write(`Match keywords: ${template.keywords.join(", ")}\n`);
|
|
90
|
+
}
|
|
91
|
+
process.stderr.write("\n---\n");
|
|
92
|
+
process.stdout.write(content);
|
|
93
|
+
if (!content.endsWith("\n"))
|
|
94
|
+
process.stdout.write("\n");
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
48
97
|
export async function runTemplatesDeploy(opts = {}) {
|
|
49
98
|
try {
|
|
50
99
|
const cwd = process.cwd();
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Validate DNA files for structural correctness.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { formatDiagnostic, loadDNAWithDiagnostics } from "../../compiler/index.js";
|
|
7
7
|
export async function runValidate(opts) {
|
|
8
8
|
if (opts.files.length === 0) {
|
|
9
9
|
process.stderr.write("Error: no files specified\n");
|
|
@@ -11,17 +11,23 @@ export async function runValidate(opts) {
|
|
|
11
11
|
}
|
|
12
12
|
let allValid = true;
|
|
13
13
|
for (const file of opts.files) {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
const result = await loadDNAWithDiagnostics(file);
|
|
15
|
+
const errors = result.diagnostics.filter((diagnostic) => diagnostic.severity === "error");
|
|
16
|
+
const warnings = result.diagnostics.filter((diagnostic) => diagnostic.severity === "warning");
|
|
17
|
+
if (errors.length > 0 || !result.dna) {
|
|
18
|
+
allValid = false;
|
|
19
|
+
process.stderr.write(`invalid: ${file}\n`);
|
|
16
20
|
if (!opts.quiet) {
|
|
17
|
-
|
|
21
|
+
for (const diagnostic of result.diagnostics) {
|
|
22
|
+
process.stderr.write(`${formatDiagnostic(diagnostic)}\n`);
|
|
23
|
+
}
|
|
18
24
|
}
|
|
25
|
+
continue;
|
|
19
26
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
process.stderr.write(`${err.message}\n`);
|
|
27
|
+
if (!opts.quiet) {
|
|
28
|
+
process.stderr.write(`valid: ${file}\n`);
|
|
29
|
+
for (const warning of warnings) {
|
|
30
|
+
process.stderr.write(`${formatDiagnostic(warning)}\n`);
|
|
25
31
|
}
|
|
26
32
|
}
|
|
27
33
|
}
|
|
@@ -218,6 +218,18 @@ function truncate(s, max) {
|
|
|
218
218
|
return s;
|
|
219
219
|
return s.slice(0, max - 3) + "...";
|
|
220
220
|
}
|
|
221
|
+
async function checkTemplateVersionsQuiet(configs) {
|
|
222
|
+
const stderr = process.stderr;
|
|
223
|
+
const originalWrite = stderr.write;
|
|
224
|
+
stderr.write = (() => true);
|
|
225
|
+
try {
|
|
226
|
+
const { checkTemplateVersions } = await import("./sync.js");
|
|
227
|
+
return await checkTemplateVersions(configs);
|
|
228
|
+
}
|
|
229
|
+
finally {
|
|
230
|
+
stderr.write = originalWrite;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
221
233
|
/**
|
|
222
234
|
* U3: Comprehensive health report for DNA installation.
|
|
223
235
|
* Checks IR, configs, templates, hooks, and trace system.
|
|
@@ -225,7 +237,7 @@ function truncate(s, max) {
|
|
|
225
237
|
export async function runHealth() {
|
|
226
238
|
const cwd = process.cwd();
|
|
227
239
|
const checks = [];
|
|
228
|
-
const { resolve } = await import("node:path");
|
|
240
|
+
const { resolve, basename } = await import("node:path");
|
|
229
241
|
const { stat, readFile: rf, readdir } = await import("node:fs/promises");
|
|
230
242
|
// 1. Check compiled IR
|
|
231
243
|
const irPath = resolve(cwd, ".dna", "compiled", "ir.json");
|
|
@@ -245,15 +257,15 @@ export async function runHealth() {
|
|
|
245
257
|
checks.push({ name: "Compiled IR", status: "warn", detail: "Legacy format — run `dna sync` to upgrade" });
|
|
246
258
|
}
|
|
247
259
|
else {
|
|
248
|
-
checks.push({ name: "Compiled IR", status: "fail", detail: "Unrecognized format" });
|
|
260
|
+
checks.push({ name: "Compiled IR", status: "fail", detail: "Unrecognized format — run `dna sync` to regenerate" });
|
|
249
261
|
}
|
|
250
262
|
}
|
|
251
263
|
catch {
|
|
252
264
|
checks.push({ name: "Compiled IR", status: "fail", detail: "Not found — run `dna sync`" });
|
|
253
265
|
}
|
|
254
266
|
// 2. Check DNA config files
|
|
255
|
-
const {
|
|
256
|
-
const configs = await
|
|
267
|
+
const { detectDNAConfigs } = await import("../../compiler/index.js");
|
|
268
|
+
const configs = await detectDNAConfigs(cwd);
|
|
257
269
|
if (configs.length > 0) {
|
|
258
270
|
checks.push({ name: "DNA configs", status: "pass", detail: `${configs.length} config(s) found` });
|
|
259
271
|
}
|
|
@@ -262,51 +274,72 @@ export async function runHealth() {
|
|
|
262
274
|
}
|
|
263
275
|
// 3. Check config validity
|
|
264
276
|
if (configs.length > 0) {
|
|
265
|
-
const {
|
|
277
|
+
const { loadDNAWithDiagnostics, formatDiagnostic } = await import("../../compiler/index.js");
|
|
266
278
|
let valid = 0;
|
|
267
279
|
let invalid = 0;
|
|
280
|
+
let warningCount = 0;
|
|
268
281
|
const errors = [];
|
|
269
282
|
for (const configPath of configs) {
|
|
270
|
-
|
|
271
|
-
|
|
283
|
+
const result = await loadDNAWithDiagnostics(configPath);
|
|
284
|
+
const errorDiagnostics = result.diagnostics.filter((diagnostic) => diagnostic.severity === "error");
|
|
285
|
+
const warningDiagnostics = result.diagnostics.filter((diagnostic) => diagnostic.severity === "warning");
|
|
286
|
+
warningCount += warningDiagnostics.length;
|
|
287
|
+
if (errorDiagnostics.length === 0 && result.dna) {
|
|
272
288
|
valid++;
|
|
273
289
|
}
|
|
274
|
-
|
|
290
|
+
else {
|
|
275
291
|
invalid++;
|
|
276
|
-
|
|
277
|
-
errors.push(`${basename(configPath)}: ${err instanceof Error ? err.message : String(err)}`);
|
|
292
|
+
errors.push(`${basename(configPath)}: ${errorDiagnostics.map(formatDiagnostic).join(", ")}`);
|
|
278
293
|
}
|
|
279
294
|
}
|
|
280
295
|
if (invalid === 0) {
|
|
281
|
-
|
|
296
|
+
const detail = warningCount > 0 ? `${valid} config(s) valid, ${warningCount} warning(s)` : `${valid} config(s) valid`;
|
|
297
|
+
checks.push({ name: "Config validity", status: warningCount > 0 ? "warn" : "pass", detail });
|
|
282
298
|
}
|
|
283
299
|
else {
|
|
284
|
-
checks.push({ name: "Config validity", status: "fail", detail: errors.join("; ") });
|
|
300
|
+
checks.push({ name: "Config validity", status: "fail", detail: `${errors.join("; ")} — fix listed config errors, then run \`dna validate\`` });
|
|
285
301
|
}
|
|
286
302
|
}
|
|
287
303
|
// 4. Check template versions
|
|
288
304
|
if (configs.length > 0) {
|
|
289
|
-
const { checkTemplateVersions: checkVersions } = await import("./sync.js");
|
|
290
|
-
// Suppress stderr output from checkTemplateVersions — we'll report ourselves
|
|
291
|
-
const origWrite = process.stderr.write;
|
|
292
|
-
process.stderr.write = (() => true);
|
|
293
305
|
try {
|
|
294
|
-
const upgrades = await
|
|
306
|
+
const upgrades = await checkTemplateVersionsQuiet(configs);
|
|
295
307
|
if (upgrades.length === 0) {
|
|
296
308
|
checks.push({ name: "Template versions", status: "pass", detail: "All up to date" });
|
|
297
309
|
}
|
|
298
310
|
else {
|
|
299
|
-
const
|
|
300
|
-
|
|
311
|
+
const versionUpdates = upgrades.filter(u => u.reason === "version-update");
|
|
312
|
+
const missingHash = upgrades.filter(u => u.reason === "missing-hash");
|
|
313
|
+
const contentDrift = upgrades.filter(u => u.reason === "content-drift");
|
|
314
|
+
const parts = [];
|
|
315
|
+
if (versionUpdates.length > 0) {
|
|
316
|
+
const names = versionUpdates.map(u => `${u.templateName} (${u.currentVersion} → ${u.availableVersion})`);
|
|
317
|
+
parts.push(`Upgrade available: ${names.join(", ")}`);
|
|
318
|
+
}
|
|
319
|
+
if (missingHash.length > 0) {
|
|
320
|
+
const names = missingHash.map(u => `${u.templateName} (${u.currentVersion})`);
|
|
321
|
+
parts.push(`Template metadata incomplete: ${names.join(", ")} — run dna init --upgrade <template>`);
|
|
322
|
+
}
|
|
323
|
+
if (contentDrift.length > 0) {
|
|
324
|
+
const names = contentDrift.map(u => `${u.templateName} (${u.currentVersion})`);
|
|
325
|
+
parts.push(`Template content drift: ${names.join(", ")} — run dna init --upgrade <template>`);
|
|
326
|
+
}
|
|
327
|
+
checks.push({ name: "Template versions", status: "warn", detail: parts.join("; ") });
|
|
301
328
|
}
|
|
302
329
|
}
|
|
303
330
|
catch {
|
|
304
|
-
checks.push({ name: "Template versions", status: "warn", detail: "Could not check versions" });
|
|
305
|
-
}
|
|
306
|
-
finally {
|
|
307
|
-
process.stderr.write = origWrite;
|
|
331
|
+
checks.push({ name: "Template versions", status: "warn", detail: "Could not check versions — run `dna templates list` and retry `dna verify --health`" });
|
|
308
332
|
}
|
|
309
333
|
}
|
|
334
|
+
// 4.5 Check sync mode detection
|
|
335
|
+
try {
|
|
336
|
+
const { detectMode } = await import("./sync.js");
|
|
337
|
+
const mode = await detectMode();
|
|
338
|
+
checks.push({ name: "Sync mode", status: "pass", detail: `auto resolves to ${mode}` });
|
|
339
|
+
}
|
|
340
|
+
catch {
|
|
341
|
+
checks.push({ name: "Sync mode", status: "warn", detail: "Could not auto-detect plugin/bin mode — use `dna sync --mode bin` if plugin mode is unavailable" });
|
|
342
|
+
}
|
|
310
343
|
// 5. Check .claude settings or plugin registration
|
|
311
344
|
const settingsPath = resolve(cwd, ".claude", "settings.json");
|
|
312
345
|
try {
|
|
@@ -319,7 +352,7 @@ export async function runHealth() {
|
|
|
319
352
|
checks.push({ name: "Hook registration", status: "pass", detail: `${eventCount} event(s) in settings.json` });
|
|
320
353
|
}
|
|
321
354
|
else {
|
|
322
|
-
checks.push({ name: "Hook registration", status: "warn", detail: "
|
|
355
|
+
checks.push({ name: "Hook registration", status: "warn", detail: "settings.json has no hooks — run `dna sync --mode bin` or use plugin-managed hooks" });
|
|
323
356
|
}
|
|
324
357
|
}
|
|
325
358
|
catch {
|
|
@@ -332,13 +365,52 @@ export async function runHealth() {
|
|
|
332
365
|
checks.push({ name: "MCP server", status: "pass", detail: "dna-mcp registered in .mcp.json" });
|
|
333
366
|
}
|
|
334
367
|
else {
|
|
335
|
-
checks.push({ name: "Hook registration", status: "warn", detail: "No hooks or MCP server found" });
|
|
368
|
+
checks.push({ name: "Hook registration", status: "warn", detail: "No hooks or MCP server found — run `dna sync`" });
|
|
336
369
|
}
|
|
337
370
|
}
|
|
338
371
|
catch {
|
|
339
372
|
checks.push({ name: "Hook registration", status: "warn", detail: "No settings.json or .mcp.json — run `dna setup` or `dna sync`" });
|
|
340
373
|
}
|
|
341
374
|
}
|
|
375
|
+
// 5.5 Check generated skill map/body references
|
|
376
|
+
const skillsDir = resolve(cwd, ".claude", "skills");
|
|
377
|
+
const skillMapPath = resolve(skillsDir, ".intentdna-skill-map.json");
|
|
378
|
+
try {
|
|
379
|
+
const raw = await rf(skillMapPath, "utf-8");
|
|
380
|
+
const map = JSON.parse(raw);
|
|
381
|
+
const skills = Array.isArray(map.skills) ? map.skills : [];
|
|
382
|
+
const missingBodies = [];
|
|
383
|
+
for (const skill of skills) {
|
|
384
|
+
if (!skill.bodyPath)
|
|
385
|
+
continue;
|
|
386
|
+
try {
|
|
387
|
+
await stat(resolve(skillsDir, skill.bodyPath));
|
|
388
|
+
}
|
|
389
|
+
catch {
|
|
390
|
+
missingBodies.push(`${skill.name ?? "unknown"}: ${skill.bodyPath}`);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if (missingBodies.length === 0) {
|
|
394
|
+
checks.push({ name: "Generated skills", status: "pass", detail: `${skills.length} skill map entr${skills.length === 1 ? "y" : "ies"} with valid bodies` });
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
checks.push({ name: "Generated skills", status: "fail", detail: `Missing body files: ${missingBodies.join(", ")} — run \`dna sync --skills .claude/skills\`` });
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
catch {
|
|
401
|
+
try {
|
|
402
|
+
const entries = await readdir(skillsDir);
|
|
403
|
+
const hasSkillDirs = entries.some((entry) => entry.startsWith("dna-"));
|
|
404
|
+
checks.push({
|
|
405
|
+
name: "Generated skills",
|
|
406
|
+
status: hasSkillDirs ? "warn" : "pass",
|
|
407
|
+
detail: hasSkillDirs ? "Skill dirs exist without .intentdna-skill-map.json — run `dna sync`" : "No generated skill dirs found",
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
catch {
|
|
411
|
+
checks.push({ name: "Generated skills", status: "pass", detail: "No skills directory" });
|
|
412
|
+
}
|
|
413
|
+
}
|
|
342
414
|
// 6. Check trace system
|
|
343
415
|
const traceDir = resolve(cwd, ".dna", "state", "trace");
|
|
344
416
|
try {
|
package/dist/cli/index.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* help Show this help message
|
|
13
13
|
*/
|
|
14
14
|
import { parseArgs } from "node:util";
|
|
15
|
-
const HELP = `Intent DNA CLI
|
|
15
|
+
const HELP = `Intent DNA CLI v1.7.0
|
|
16
16
|
|
|
17
17
|
Usage: dna <command> [options]
|
|
18
18
|
|
|
@@ -29,7 +29,8 @@ Commands:
|
|
|
29
29
|
show Show gene expression state
|
|
30
30
|
evolve Preview epigenetic marker changes from persisted outcomes
|
|
31
31
|
epigenetic Record outcomes, view markers and summaries
|
|
32
|
-
feedback Build local kernel report (--days <N>, --json, --evolve preview, --dna-id <id>)
|
|
32
|
+
feedback Build local kernel report (--days <N>, --json, --package, --evolve preview, --dna-id <id>)
|
|
33
|
+
context Inspect planning-only advisory context sources
|
|
33
34
|
templates Template management (deploy to global install)
|
|
34
35
|
|
|
35
36
|
Options:
|
|
@@ -68,15 +69,39 @@ Examples:
|
|
|
68
69
|
dna evolve my_dna_id Preview marker changes without persisting
|
|
69
70
|
dna evolve my_dna_id --apply Unsupported in this kernel; use sync --evolve for explicit mutation
|
|
70
71
|
`;
|
|
72
|
+
const COMMAND_HELP = {
|
|
73
|
+
sync: `Usage: dna sync [files...] [options]\n\nOptions:\n --mode auto|plugin|bin Sync mode (default: auto)\n --dry-run Print planned outputs without writing files\n --post-summary Print registry-backed output summary\n --target <target> Markdown target for injection\n --inject <path> File to inject markdown into\n --agents <dir> Generate agent files\n --skills <dir> Generate skill files\n --workflow <dir> Generate workflow shell scripts\n --settings <path> Write hook settings in bin mode\n --lock <path> Write lock file (default .dna/lock)\n`,
|
|
74
|
+
verify: `Usage: dna verify [--lock <path>] [--stats] [--health]\n\nOptions:\n --health Comprehensive installation health check\n --stats Show hook call statistics\n --lock <path> Lock file path (default .dna/lock)\n`,
|
|
75
|
+
templates: `Usage: dna templates <list|show|deploy>\n\nSubcommands:\n list List built-in and project templates\n show <name> Show template metadata and YAML\n deploy Copy local templates to global install\n`,
|
|
76
|
+
context: `Usage: dna context <index|search|doc> [query|id] [files...]\n\nSubcommands:\n index List explicitly declared planning-only advisory context sources\n search <query> Search source metadata\n doc <id> Read a local file/doc source body\n`,
|
|
77
|
+
run: `Usage: dna run --dna <file> --workflow <name> --task <id> [options]\n\nOptions:\n --dry-run Print execution plan only\n --permission-mode <mode> Claude permission mode (default: default)\n`,
|
|
78
|
+
compile: `Usage: dna compile <files...> [--target claude-sdk] [--context <name>] [--output <path>] [--ir]\n`,
|
|
79
|
+
validate: `Usage: dna validate <files...> [--quiet]\n`,
|
|
80
|
+
show: `Usage: dna show <files...> [--context <name>] [--json]\n`,
|
|
81
|
+
init: `Usage: dna init [--template <name>|--template list|--register <path>|--upgrade <name>]\n`,
|
|
82
|
+
};
|
|
83
|
+
function commandHelp(command) {
|
|
84
|
+
return command ? COMMAND_HELP[command] : undefined;
|
|
85
|
+
}
|
|
71
86
|
async function main() {
|
|
72
87
|
const args = process.argv.slice(2);
|
|
73
88
|
const command = args[0];
|
|
74
|
-
if (!command || command === "
|
|
89
|
+
if (!command || command === "--help" || command === "-h") {
|
|
75
90
|
process.stderr.write(HELP);
|
|
76
91
|
process.exit(0);
|
|
77
92
|
}
|
|
93
|
+
if (command === "help") {
|
|
94
|
+
const help = commandHelp(args[1]);
|
|
95
|
+
process.stderr.write(help ?? HELP);
|
|
96
|
+
process.exit(0);
|
|
97
|
+
}
|
|
78
98
|
// Parse remaining args (after command)
|
|
79
99
|
const rest = args.slice(1);
|
|
100
|
+
if (rest.includes("--help") || rest.includes("-h")) {
|
|
101
|
+
const help = commandHelp(command);
|
|
102
|
+
process.stderr.write(help ?? HELP);
|
|
103
|
+
process.exit(help ? 0 : 2);
|
|
104
|
+
}
|
|
80
105
|
switch (command) {
|
|
81
106
|
case "sync": {
|
|
82
107
|
const { values, positionals } = parseArgs({
|
|
@@ -96,11 +121,19 @@ async function main() {
|
|
|
96
121
|
settings: { type: "string" },
|
|
97
122
|
lock: { type: "string", default: ".dna/lock" },
|
|
98
123
|
remove: { type: "boolean", default: false },
|
|
99
|
-
|
|
124
|
+
mode: { type: "string", default: "auto" },
|
|
125
|
+
plugin: { type: "boolean" },
|
|
126
|
+
"dry-run": { type: "boolean", default: false },
|
|
127
|
+
"post-summary": { type: "boolean", default: false },
|
|
100
128
|
},
|
|
101
129
|
allowPositionals: true,
|
|
102
130
|
strict: false,
|
|
103
131
|
});
|
|
132
|
+
const mode = values.mode;
|
|
133
|
+
if (!["auto", "plugin", "bin"].includes(mode)) {
|
|
134
|
+
process.stderr.write(`Error: --mode must be one of: auto, plugin, bin\n`);
|
|
135
|
+
process.exit(2);
|
|
136
|
+
}
|
|
104
137
|
const { runSync } = await import("./commands/sync.js");
|
|
105
138
|
const code = await runSync({
|
|
106
139
|
files: positionals,
|
|
@@ -118,7 +151,10 @@ async function main() {
|
|
|
118
151
|
settingsPath: values.settings,
|
|
119
152
|
lockFile: values.lock,
|
|
120
153
|
remove: values.remove,
|
|
154
|
+
mode: mode,
|
|
121
155
|
plugin: values.plugin,
|
|
156
|
+
dryRun: values["dry-run"],
|
|
157
|
+
postSummary: values["post-summary"],
|
|
122
158
|
});
|
|
123
159
|
process.exit(code);
|
|
124
160
|
break;
|
|
@@ -413,6 +449,25 @@ async function main() {
|
|
|
413
449
|
process.exit(code);
|
|
414
450
|
break;
|
|
415
451
|
}
|
|
452
|
+
case "context": {
|
|
453
|
+
const subcommand = rest[0] ?? "index";
|
|
454
|
+
const contextRest = rest.slice(1);
|
|
455
|
+
const { positionals } = parseArgs({
|
|
456
|
+
args: contextRest,
|
|
457
|
+
options: {},
|
|
458
|
+
allowPositionals: true,
|
|
459
|
+
strict: false,
|
|
460
|
+
});
|
|
461
|
+
const { runContext } = await import("./commands/context.js");
|
|
462
|
+
const code = await runContext({
|
|
463
|
+
subcommand,
|
|
464
|
+
query: subcommand === "search" ? positionals[0] : undefined,
|
|
465
|
+
id: subcommand === "doc" ? positionals[0] : undefined,
|
|
466
|
+
files: subcommand === "index" ? positionals : positionals.slice(1),
|
|
467
|
+
});
|
|
468
|
+
process.exit(code);
|
|
469
|
+
break;
|
|
470
|
+
}
|
|
416
471
|
case "feedback": {
|
|
417
472
|
const { values: fbValues } = parseArgs({
|
|
418
473
|
args: rest,
|
|
@@ -420,6 +475,7 @@ async function main() {
|
|
|
420
475
|
days: { type: "string", short: "d", default: "7" },
|
|
421
476
|
json: { type: "boolean", default: false },
|
|
422
477
|
evolve: { type: "boolean", default: false },
|
|
478
|
+
package: { type: "boolean", default: false },
|
|
423
479
|
"dna-id": { type: "string" },
|
|
424
480
|
},
|
|
425
481
|
strict: false,
|
|
@@ -429,20 +485,29 @@ async function main() {
|
|
|
429
485
|
days: parseInt(fbValues.days, 10) || 7,
|
|
430
486
|
json: fbValues.json,
|
|
431
487
|
evolve: fbValues.evolve,
|
|
488
|
+
packageReport: fbValues.package,
|
|
432
489
|
dnaId: fbValues["dna-id"],
|
|
433
490
|
});
|
|
434
491
|
process.exit(code);
|
|
435
492
|
break;
|
|
436
493
|
}
|
|
437
494
|
case "templates": {
|
|
438
|
-
const subcommand = rest[0] ?? "";
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
495
|
+
const subcommand = rest[0] ?? "list";
|
|
496
|
+
const { runTemplatesDeploy, runTemplatesList, runTemplatesShow } = await import("./commands/templates.js");
|
|
497
|
+
if (subcommand === "list") {
|
|
498
|
+
const code = await runTemplatesList();
|
|
499
|
+
process.exit(code);
|
|
442
500
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
501
|
+
if (subcommand === "show") {
|
|
502
|
+
const code = await runTemplatesShow({ name: rest[1] ?? "" });
|
|
503
|
+
process.exit(code);
|
|
504
|
+
}
|
|
505
|
+
if (subcommand === "deploy") {
|
|
506
|
+
const code = await runTemplatesDeploy();
|
|
507
|
+
process.exit(code);
|
|
508
|
+
}
|
|
509
|
+
process.stderr.write(COMMAND_HELP.templates);
|
|
510
|
+
process.exit(2);
|
|
446
511
|
break;
|
|
447
512
|
}
|
|
448
513
|
default:
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - attract/repel: same target accumulates, different targets independent
|
|
11
11
|
* - amplify/suppress: factors multiply (1.5x × 2.0x = 3.0x)
|
|
12
12
|
*/
|
|
13
|
-
import type { IntentDNA, Gene, ContextRegion, EpigeneticMarker, RoleDef, WorkflowDef, LegibilityAssetMap, VerifierCommandPolicy, ControllerDef } from "../schema/types.js";
|
|
13
|
+
import type { IntentDNA, Gene, ContextRegion, EpigeneticMarker, RoleDef, WorkflowDef, LegibilityAssetMap, VerifierCommandPolicy, ControllerDef, AdvisoryContextSource, PlanningContext } from "../schema/types.js";
|
|
14
14
|
export interface CascadedDNA {
|
|
15
15
|
genes: Record<string, Gene>;
|
|
16
16
|
contexts: Record<string, ContextRegion>;
|
|
@@ -27,6 +27,8 @@ export interface CascadedDNA {
|
|
|
27
27
|
per_role?: Record<string, string[]>;
|
|
28
28
|
per_workflow?: Record<string, string[]>;
|
|
29
29
|
};
|
|
30
|
+
context_sources?: AdvisoryContextSource[];
|
|
31
|
+
planning_context?: PlanningContext;
|
|
30
32
|
verifier_policy?: VerifierCommandPolicy;
|
|
31
33
|
}
|
|
32
34
|
/**
|
package/dist/compiler/cascade.js
CHANGED
|
@@ -156,6 +156,47 @@ function mergeContextFiles(base, incoming) {
|
|
|
156
156
|
per_workflow: mergePrefixedRecords(base.per_workflow, incoming.per_workflow),
|
|
157
157
|
};
|
|
158
158
|
}
|
|
159
|
+
function prefixContextSources(sources, ns) {
|
|
160
|
+
if (!sources)
|
|
161
|
+
return undefined;
|
|
162
|
+
if (!ns)
|
|
163
|
+
return sources;
|
|
164
|
+
return sources.map((source) => ({ ...source, id: `${ns}_${source.id}` }));
|
|
165
|
+
}
|
|
166
|
+
function prefixPlanningContext(planning, ns) {
|
|
167
|
+
if (!planning)
|
|
168
|
+
return undefined;
|
|
169
|
+
if (!ns)
|
|
170
|
+
return planning;
|
|
171
|
+
return {
|
|
172
|
+
...planning,
|
|
173
|
+
sources: planning.sources.map((sourceId) => `${ns}_${sourceId}`),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function mergeContextSources(base, incoming) {
|
|
177
|
+
if (!base)
|
|
178
|
+
return incoming;
|
|
179
|
+
if (!incoming)
|
|
180
|
+
return base;
|
|
181
|
+
const merged = new Map();
|
|
182
|
+
for (const source of base)
|
|
183
|
+
merged.set(source.id, source);
|
|
184
|
+
for (const source of incoming)
|
|
185
|
+
merged.set(source.id, source);
|
|
186
|
+
return [...merged.values()];
|
|
187
|
+
}
|
|
188
|
+
function mergePlanningContext(base, incoming) {
|
|
189
|
+
if (!base)
|
|
190
|
+
return incoming;
|
|
191
|
+
if (!incoming)
|
|
192
|
+
return base;
|
|
193
|
+
return {
|
|
194
|
+
sources: incoming.sources,
|
|
195
|
+
instructions: incoming.instructions ?? base.instructions,
|
|
196
|
+
require_quotes: incoming.require_quotes ?? base.require_quotes,
|
|
197
|
+
require_attribution: incoming.require_attribution ?? base.require_attribution,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
159
200
|
function mergeVerifierPolicy(base, incoming) {
|
|
160
201
|
if (!base)
|
|
161
202
|
return incoming;
|
|
@@ -257,6 +298,8 @@ export function cascadeDNA(layers) {
|
|
|
257
298
|
// legibility_assets/context_files: highest-priority DNA wins (last in sorted layers)
|
|
258
299
|
let legibilityAssets;
|
|
259
300
|
let contextFiles;
|
|
301
|
+
let contextSources;
|
|
302
|
+
let planningContext;
|
|
260
303
|
let verifierPolicy;
|
|
261
304
|
for (const dna of sorted) {
|
|
262
305
|
if (dna.legibility_assets) {
|
|
@@ -265,6 +308,12 @@ export function cascadeDNA(layers) {
|
|
|
265
308
|
if (dna.context_files) {
|
|
266
309
|
contextFiles = mergeContextFiles(contextFiles, prefixContextFiles(dna.context_files, dna.namespace));
|
|
267
310
|
}
|
|
311
|
+
if (dna.context_sources) {
|
|
312
|
+
contextSources = mergeContextSources(contextSources, prefixContextSources(dna.context_sources, dna.namespace));
|
|
313
|
+
}
|
|
314
|
+
if (dna.planning_context) {
|
|
315
|
+
planningContext = mergePlanningContext(planningContext, prefixPlanningContext(dna.planning_context, dna.namespace));
|
|
316
|
+
}
|
|
268
317
|
if (dna.verifier_policy) {
|
|
269
318
|
verifierPolicy = mergeVerifierPolicy(verifierPolicy, dna.verifier_policy);
|
|
270
319
|
}
|
|
@@ -280,6 +329,8 @@ export function cascadeDNA(layers) {
|
|
|
280
329
|
warnings: warnings.length > 0 ? warnings : undefined,
|
|
281
330
|
legibility_assets: legibilityAssets,
|
|
282
331
|
context_files: contextFiles,
|
|
332
|
+
context_sources: contextSources,
|
|
333
|
+
planning_context: planningContext,
|
|
283
334
|
verifier_policy: verifierPolicy,
|
|
284
335
|
};
|
|
285
336
|
}
|
package/dist/compiler/compile.js
CHANGED
|
@@ -172,6 +172,39 @@ function legibilityAssetsToContextFiles(assets) {
|
|
|
172
172
|
return undefined;
|
|
173
173
|
return { mandatory, per_role, per_workflow };
|
|
174
174
|
}
|
|
175
|
+
function compileAdvisoryContext(cascaded) {
|
|
176
|
+
const declaredSources = cascaded?.context_sources ?? [];
|
|
177
|
+
const planning = cascaded?.planning_context;
|
|
178
|
+
const plannedSourceIds = planning?.sources ?? declaredSources.map((source) => source.id);
|
|
179
|
+
const sourceById = new Map(declaredSources.map((source) => [source.id, source]));
|
|
180
|
+
const sources = plannedSourceIds
|
|
181
|
+
.map((sourceId) => sourceById.get(sourceId))
|
|
182
|
+
.filter((source) => Boolean(source))
|
|
183
|
+
.map((source) => ({
|
|
184
|
+
...source,
|
|
185
|
+
usage: "planning_only",
|
|
186
|
+
non_enforcing: true,
|
|
187
|
+
requires_quotes: planning?.require_quotes ?? true,
|
|
188
|
+
requires_attribution: planning?.require_attribution ?? true,
|
|
189
|
+
}));
|
|
190
|
+
const compiledPlanning = planning ? {
|
|
191
|
+
sources: [...planning.sources],
|
|
192
|
+
instructions: planning.instructions ? [...planning.instructions] : undefined,
|
|
193
|
+
require_quotes: planning.require_quotes ?? true,
|
|
194
|
+
require_attribution: planning.require_attribution ?? true,
|
|
195
|
+
non_enforcing: true,
|
|
196
|
+
} : undefined;
|
|
197
|
+
const injections = sources.length > 0 ? [{
|
|
198
|
+
type: "preference",
|
|
199
|
+
text: `Advisory context sources are available for planning only: ${sources.map((source) => source.id).join(", ")}. Cite sources and quote relevant passages; these sources do not satisfy enforcement or handoff requirements.`,
|
|
200
|
+
relevance_tags: ["advisory_context", "planning_only"],
|
|
201
|
+
}] : [];
|
|
202
|
+
return {
|
|
203
|
+
sources: sources.length > 0 ? sources : undefined,
|
|
204
|
+
planning: compiledPlanning,
|
|
205
|
+
injections,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
175
208
|
function resolvePostCheckCommand(check) {
|
|
176
209
|
const normalized = check.trim();
|
|
177
210
|
const builtins = {
|
|
@@ -362,6 +395,8 @@ export function compileDNA(activated, cascaded) {
|
|
|
362
395
|
directives.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
|
|
363
396
|
const legacyContextFiles = cascaded?.context_files;
|
|
364
397
|
const compiledContextFiles = legacyContextFiles ?? legibilityAssetsToContextFiles(cascaded?.legibility_assets);
|
|
398
|
+
const advisoryContext = compileAdvisoryContext(cascaded);
|
|
399
|
+
injections.push(...advisoryContext.injections);
|
|
365
400
|
return {
|
|
366
401
|
prompt_directives: directives,
|
|
367
402
|
tool_filters: toolFilters,
|
|
@@ -369,6 +404,8 @@ export function compileDNA(activated, cascaded) {
|
|
|
369
404
|
post_execution_validators: validators,
|
|
370
405
|
verifier_specs: verifierSpecs.length > 0 ? verifierSpecs : undefined,
|
|
371
406
|
context_injections: injections,
|
|
407
|
+
context_sources: advisoryContext.sources,
|
|
408
|
+
planning_context: advisoryContext.planning,
|
|
372
409
|
source_dna_ids: activated.source_ids,
|
|
373
410
|
compiled_at: new Date().toISOString(),
|
|
374
411
|
active_context: activated.active_context ?? undefined,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type DiagnosticSeverity = "error" | "warning" | "info";
|
|
2
|
+
export interface Diagnostic {
|
|
3
|
+
severity: DiagnosticSeverity;
|
|
4
|
+
code: string;
|
|
5
|
+
message: string;
|
|
6
|
+
file?: string;
|
|
7
|
+
path?: string;
|
|
8
|
+
hint?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare class DNADiagnosticsError extends Error {
|
|
11
|
+
readonly diagnostics: Diagnostic[];
|
|
12
|
+
constructor(message: string, diagnostics: Diagnostic[]);
|
|
13
|
+
}
|
|
14
|
+
export declare function hasDiagnosticErrors(diagnostics: Diagnostic[]): boolean;
|
|
15
|
+
export declare function formatDiagnostic(diagnostic: Diagnostic): string;
|
|
16
|
+
export declare function formatDiagnostics(diagnostics: Diagnostic[]): string;
|
|
17
|
+
export declare function diagnosticsFromError(error: unknown, file?: string): Diagnostic[];
|