intentdna 1.6.5 → 1.7.4
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/README.md +26 -15
- 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 +3 -2
- package/dist/cli/commands/feedback.js +16 -5
- 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 +191 -186
- 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.d.ts +32 -0
- package/dist/cli/commands/verify.js +270 -31
- package/dist/cli/index.js +78 -11
- package/dist/compiler/activate.js +11 -6
- package/dist/compiler/cascade.d.ts +5 -1
- package/dist/compiler/cascade.js +74 -1
- package/dist/compiler/compile.js +38 -0
- package/dist/compiler/diagnostics.d.ts +17 -0
- package/dist/compiler/diagnostics.js +30 -0
- package/dist/compiler/index.d.ts +7 -0
- package/dist/compiler/index.js +13 -13
- package/dist/compiler/input-resolver.d.ts +32 -0
- package/dist/compiler/input-resolver.js +281 -0
- package/dist/compiler/provenance.d.ts +8 -0
- package/dist/compiler/provenance.js +127 -0
- package/dist/governance/index.d.ts +4 -3
- package/dist/governance/index.js +3 -4
- package/dist/governance/runtime-decision-event.d.ts +85 -0
- package/dist/governance/runtime-decision-event.js +231 -0
- package/dist/governance/types.d.ts +3 -3
- package/dist/governance/types.js +3 -3
- package/dist/hooks/cli.d.ts +10 -1
- package/dist/hooks/cli.js +199 -35
- package/dist/hooks/state.d.ts +5 -10
- package/dist/hooks/state.js +170 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- 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 +19 -49
- package/dist/mcp/tools-observability.js +26 -2
- package/dist/mcp/tools-state.d.ts +1 -1
- package/dist/mcp/tools-state.js +17 -7
- package/dist/report/kernel-report.d.ts +27 -0
- package/dist/report/kernel-report.js +60 -19
- package/dist/report/kernel-signals.d.ts +5 -2
- package/dist/report/kernel-signals.js +87 -2
- package/dist/report/report-package.d.ts +64 -0
- package/dist/report/report-package.js +90 -0
- package/dist/runtime/agent-md.d.ts +1 -0
- package/dist/runtime/agent-md.js +21 -3
- package/dist/runtime/claude-sdk.d.ts +9 -4
- package/dist/runtime/claude-sdk.js +9 -0
- package/dist/runtime/context-sources.d.ts +14 -0
- package/dist/runtime/context-sources.js +60 -0
- package/dist/runtime/plugin-adapter.d.ts +5 -1
- package/dist/runtime/plugin-adapter.js +2 -0
- package/dist/runtime/skill-adapter.d.ts +32 -4
- package/dist/runtime/skill-adapter.js +184 -9
- package/dist/runtime/workflow-runner.d.ts +1 -1
- package/dist/runtime/workflow-runner.js +1 -1
- package/dist/schema/types.d.ts +84 -0
- package/dist/schema/validate.js +156 -2
- package/dist/schema/validators/controllers.js +16 -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/package.json +1 -1
- package/spec/README.md +1 -1
- package/spec/foundation-hardening.md +2 -1
- package/spec/schema-spec.md +78 -1
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
{
|
|
10
10
|
"name": "intentdna",
|
|
11
11
|
"description": "Declarative policy layer for AI agent behavior with plugin-managed hook runtime for Claude Code.",
|
|
12
|
-
"version": "1.
|
|
12
|
+
"version": "1.7.4",
|
|
13
13
|
"author": {
|
|
14
14
|
"name": "Samuel"
|
|
15
15
|
},
|
|
@@ -25,5 +25,5 @@
|
|
|
25
25
|
]
|
|
26
26
|
}
|
|
27
27
|
],
|
|
28
|
-
"version": "1.
|
|
28
|
+
"version": "1.7.4"
|
|
29
29
|
}
|
package/README.md
CHANGED
|
@@ -1,26 +1,32 @@
|
|
|
1
1
|
# Intent DNA
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Enterprise safety control plane for AI agent behavior.**
|
|
4
4
|
|
|
5
|
-
Intent DNA
|
|
5
|
+
Intent DNA lets an organization declare safety policy once, cascade it through Species → Enterprise → Project → Personal → Role → Context → Task, and compile it into deterministic gates for every agent, project, and harness. It governs how agents operate across existing tools and systems; it does not replace your business systems or act as a domain-specific automation runtime.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
## What It Is
|
|
10
10
|
|
|
11
|
-
Intent DNA is
|
|
11
|
+
Intent DNA is an enforcement-first control plane for organization-wide agent safety:
|
|
12
12
|
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
13
|
+
- **Enterprise / Species policy bundles** define shared red lines across projects and teams
|
|
14
|
+
- **Cascade governance** preserves provenance and prevents lower layers from relaxing organization thresholds
|
|
15
|
+
- **Constraint IR** carries policy provenance into hook, SDK-check, and CI strict gates
|
|
16
|
+
- **RuntimeDecisionEvent evidence** records who/what/why/from which policy for every allow/warn/block/escalate/validate decision
|
|
17
|
+
- **MCP, reports, state, trace, audit, and evolution** query or package evidence; they are not the policy source of truth
|
|
17
18
|
|
|
18
19
|
It governs the agents operating on your systems rather than re-implementing the systems themselves.
|
|
19
20
|
|
|
20
21
|
The core model is:
|
|
21
22
|
|
|
22
23
|
```text
|
|
23
|
-
|
|
24
|
+
Enterprise / Species Policy Bundle
|
|
25
|
+
→ Species→Enterprise→Project→Personal→Role→Context→Task Cascade
|
|
26
|
+
→ Constraint IR + Provenance Manifest
|
|
27
|
+
→ Hook / SDK-check / CI strict gate
|
|
28
|
+
→ RuntimeDecisionEvent
|
|
29
|
+
→ MCP / report / dashboard / evolution evidence layer
|
|
24
30
|
```
|
|
25
31
|
|
|
26
32
|
For the architectural model and long-term design, see [DESIGN.md](DESIGN.md).
|
|
@@ -30,9 +36,10 @@ For the schema contract, see [spec/schema-spec.md](spec/schema-spec.md).
|
|
|
30
36
|
## Design Principles
|
|
31
37
|
|
|
32
38
|
- Govern agents rather than re-implementing business systems.
|
|
33
|
-
- Declare
|
|
34
|
-
-
|
|
35
|
-
-
|
|
39
|
+
- Declare enterprise safety red lines first, then compile them into native control surfaces.
|
|
40
|
+
- Treat Species and Enterprise thresholds as non-relaxable by Project, Personal, Role, Context, or Task layers.
|
|
41
|
+
- Keep enforcement points authoritative: hooks, SDK checks, and CI gates make decisions from compiled IR and provenance.
|
|
42
|
+
- Treat runtime state, validators, traces, reports, MCP queries, and signals as evidence assets, not policy truth.
|
|
36
43
|
- Keep adapters and signal ingestion pluggable so the control plane outlives any single tool.
|
|
37
44
|
|
|
38
45
|
---
|
|
@@ -70,10 +77,10 @@ dna init --upgrade <template-name>
|
|
|
70
77
|
|
|
71
78
|
## How It Works
|
|
72
79
|
|
|
73
|
-
Intent DNA compiles to your tool's native control surfaces:
|
|
80
|
+
Intent DNA compiles organization policy to your tool's native control surfaces:
|
|
74
81
|
|
|
75
82
|
```text
|
|
76
|
-
DNA → Constraint IR →
|
|
83
|
+
DNA → Cascade provenance → Constraint IR → hooks / SDK checks / CI gates → evidence
|
|
77
84
|
```
|
|
78
85
|
|
|
79
86
|
Typical outputs include:
|
|
@@ -82,12 +89,14 @@ Typical outputs include:
|
|
|
82
89
|
|--------|---------|
|
|
83
90
|
| `CLAUDE.md` | Behavioral guidance |
|
|
84
91
|
| `.dna/compiled/ir.json` | Runtime-readable compiled constraints |
|
|
92
|
+
| provenance manifest | Policy bundle, cascade layers, winners, and compiled IR hash |
|
|
93
|
+
| RuntimeDecisionEvent | Enterprise evidence for allow/warn/block/escalate/validate decisions |
|
|
85
94
|
| `.dna/state/sessions/*/handoffs/*.json` | Local artifact manifests for workflow handoff facts |
|
|
86
95
|
| `.claude/agents/*.md` | Role definitions |
|
|
87
96
|
| `.claude/skills/*/SKILL.md` | Workflow entrypoints |
|
|
88
97
|
| `.claude/settings.json` or plugin runtime | Hook wiring |
|
|
89
98
|
|
|
90
|
-
**Key idea:** prompts can drift, but
|
|
99
|
+
**Key idea:** prompts can drift, but non-relaxable enterprise thresholds, compiled gates, and provenance-backed runtime evidence make behavior governable.
|
|
91
100
|
|
|
92
101
|
---
|
|
93
102
|
|
|
@@ -95,11 +104,13 @@ Typical outputs include:
|
|
|
95
104
|
|
|
96
105
|
Intent DNA currently focuses on:
|
|
97
106
|
|
|
107
|
+
- Enterprise Safety Slice: shared policy bundles, cascade provenance, and non-relaxable organization thresholds
|
|
98
108
|
- Claude Code / Claude-style harnesses
|
|
99
109
|
- Role-based agent workflows
|
|
100
110
|
- Template-driven project bootstrap and upgrades
|
|
101
111
|
- Deterministic guardrails and stateful enforcement
|
|
102
112
|
- Resolver-backed artifact handoff between workflow steps
|
|
113
|
+
- Evidence-layer MCP/report/state/trace/audit surfaces
|
|
103
114
|
- Dogfood-driven template iteration
|
|
104
115
|
|
|
105
116
|
Adapter and ecosystem direction are tracked in [ROADMAP.md](ROADMAP.md), not duplicated here.
|
|
@@ -157,7 +168,7 @@ workflows:
|
|
|
157
168
|
description: Review the implementation
|
|
158
169
|
```
|
|
159
170
|
|
|
160
|
-
**Cascade:** DNA files inherit and override by priority. `threshold` codons (safety rules)
|
|
171
|
+
**Cascade:** DNA files inherit and override by priority. `threshold` codons (safety rules) from Species or Enterprise are red lines: Project, Personal, Role, Context, and Task layers may add stricter rules but cannot relax them.
|
|
161
172
|
|
|
162
173
|
**Genes:** Behavioral traits (attract/repel/threshold/weight/sense).
|
|
163
174
|
|
|
@@ -4,53 +4,8 @@
|
|
|
4
4
|
* Compile DNA files to framework-specific configuration.
|
|
5
5
|
*/
|
|
6
6
|
import { writeFile } from "node:fs/promises";
|
|
7
|
-
import {
|
|
8
|
-
import { fileURLToPath } from "node:url";
|
|
9
|
-
import { compileFromFiles } from "../../compiler/index.js";
|
|
7
|
+
import { compileFromFiles, expandDNAInputFiles } from "../../compiler/index.js";
|
|
10
8
|
import { compileForClaude } from "../../runtime/claude-sdk.js";
|
|
11
|
-
/**
|
|
12
|
-
* Resolve the species default DNA path relative to this module's location.
|
|
13
|
-
*/
|
|
14
|
-
function resolveSpeciesPath(ref) {
|
|
15
|
-
if (!ref.startsWith("species:"))
|
|
16
|
-
return null;
|
|
17
|
-
const name = ref.slice("species:".length);
|
|
18
|
-
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
19
|
-
// From dist/cli/commands/ -> dist/species/
|
|
20
|
-
return resolve(thisDir, "..", "..", "species", `${name}.dna.json`);
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Expand species references in file list and add species default if any DNA inherits it.
|
|
24
|
-
*/
|
|
25
|
-
async function expandFiles(files) {
|
|
26
|
-
const { readFile } = await import("node:fs/promises");
|
|
27
|
-
const expanded = [];
|
|
28
|
-
const seen = new Set();
|
|
29
|
-
// First pass: collect all files and check for species references
|
|
30
|
-
for (const file of files) {
|
|
31
|
-
const abs = resolve(file);
|
|
32
|
-
if (seen.has(abs))
|
|
33
|
-
continue;
|
|
34
|
-
seen.add(abs);
|
|
35
|
-
expanded.push(abs);
|
|
36
|
-
// Check if this DNA inherits from species
|
|
37
|
-
try {
|
|
38
|
-
const raw = await readFile(abs, "utf-8");
|
|
39
|
-
const dna = JSON.parse(raw);
|
|
40
|
-
for (const ref of dna.cascade?.inherits ?? []) {
|
|
41
|
-
const speciesPath = resolveSpeciesPath(ref);
|
|
42
|
-
if (speciesPath && !seen.has(speciesPath)) {
|
|
43
|
-
seen.add(speciesPath);
|
|
44
|
-
expanded.unshift(speciesPath); // species goes first (lowest priority)
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
catch {
|
|
49
|
-
// Will fail later during compilation with proper error
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return expanded;
|
|
53
|
-
}
|
|
54
9
|
export async function runCompile(opts) {
|
|
55
10
|
if (opts.files.length === 0) {
|
|
56
11
|
process.stderr.write("Error: no files specified\n");
|
|
@@ -62,7 +17,7 @@ export async function runCompile(opts) {
|
|
|
62
17
|
return 2;
|
|
63
18
|
}
|
|
64
19
|
try {
|
|
65
|
-
const expandedFiles = await
|
|
20
|
+
const expandedFiles = await expandDNAInputFiles(opts.files);
|
|
66
21
|
const ir = await compileFromFiles(expandedFiles, opts.context);
|
|
67
22
|
let output;
|
|
68
23
|
if (opts.ir) {
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { findContextSource, formatContextSource, loadContextSourceIndex, readContextSourceDocument, searchContextSources, } from "../../runtime/context-sources.js";
|
|
3
|
+
function usage() {
|
|
4
|
+
return "Usage: dna context <index|search|doc> [query|id] [files...]\n";
|
|
5
|
+
}
|
|
6
|
+
export async function runContext(opts) {
|
|
7
|
+
const projectDir = opts.projectDir ?? process.cwd();
|
|
8
|
+
const files = opts.files?.map((file) => resolve(projectDir, file));
|
|
9
|
+
const index = await loadContextSourceIndex(projectDir, files);
|
|
10
|
+
switch (opts.subcommand) {
|
|
11
|
+
case "index": {
|
|
12
|
+
if (index.sources.length === 0) {
|
|
13
|
+
process.stdout.write("No advisory context sources declared.\n");
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
process.stdout.write(index.sources.map(formatContextSource).join("\n") + "\n");
|
|
17
|
+
if (index.planning_instructions.length > 0) {
|
|
18
|
+
process.stdout.write("\nPlanning instructions:\n");
|
|
19
|
+
for (const instruction of index.planning_instructions) {
|
|
20
|
+
process.stdout.write(`- ${instruction}\n`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
case "search": {
|
|
26
|
+
const query = opts.query ?? "";
|
|
27
|
+
const matches = searchContextSources(index, query);
|
|
28
|
+
if (matches.length === 0) {
|
|
29
|
+
process.stdout.write(`No advisory context sources matched '${query}'.\n`);
|
|
30
|
+
return 1;
|
|
31
|
+
}
|
|
32
|
+
process.stdout.write(matches.map(formatContextSource).join("\n") + "\n");
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
case "doc": {
|
|
36
|
+
const id = opts.id ?? "";
|
|
37
|
+
if (!id) {
|
|
38
|
+
process.stderr.write("Error: context doc requires a source id.\n");
|
|
39
|
+
process.stderr.write(usage());
|
|
40
|
+
return 2;
|
|
41
|
+
}
|
|
42
|
+
const source = findContextSource(index, id);
|
|
43
|
+
if (!source) {
|
|
44
|
+
process.stderr.write(`Error: unknown advisory context source '${id}'.\n`);
|
|
45
|
+
return 1;
|
|
46
|
+
}
|
|
47
|
+
const doc = await readContextSourceDocument(projectDir, source);
|
|
48
|
+
process.stdout.write(`${formatContextSource(source)}\n`);
|
|
49
|
+
process.stdout.write("Planning-only advisory context; does not satisfy enforcement, handoff, or ArtifactManifest requirements.\n");
|
|
50
|
+
if (doc.content !== undefined) {
|
|
51
|
+
process.stdout.write("\n---\n");
|
|
52
|
+
process.stdout.write(doc.content.endsWith("\n") ? doc.content : `${doc.content}\n`);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
process.stdout.write("No local document body is available for this source type.\n");
|
|
56
|
+
}
|
|
57
|
+
return 0;
|
|
58
|
+
}
|
|
59
|
+
default:
|
|
60
|
+
process.stderr.write(usage());
|
|
61
|
+
return 2;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* dna feedback [--days <N>] [--json] [--evolve]
|
|
3
3
|
*
|
|
4
|
-
* Build a local kernel observability report from
|
|
5
|
-
*
|
|
4
|
+
* Build a local kernel observability report from RuntimeDecisionEvent evidence.
|
|
5
|
+
* Legacy traces and verifier results are included as diagnostic context only.
|
|
6
6
|
*/
|
|
7
7
|
export interface FeedbackOptions {
|
|
8
8
|
days: number;
|
|
9
9
|
json: boolean;
|
|
10
10
|
evolve: boolean;
|
|
11
11
|
dnaId?: string;
|
|
12
|
+
packageReport?: boolean;
|
|
12
13
|
}
|
|
13
14
|
export declare function runFeedback(opts: FeedbackOptions): Promise<number>;
|
|
@@ -1,20 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* dna feedback [--days <N>] [--json] [--evolve]
|
|
3
3
|
*
|
|
4
|
-
* Build a local kernel observability report from
|
|
5
|
-
*
|
|
4
|
+
* Build a local kernel observability report from RuntimeDecisionEvent evidence.
|
|
5
|
+
* Legacy traces and verifier results are included as diagnostic context only.
|
|
6
6
|
*/
|
|
7
7
|
import { buildKernelReport, formatKernelReport } from "../../report/kernel-report.js";
|
|
8
|
+
import { buildReportPackage } from "../../report/report-package.js";
|
|
8
9
|
export async function runFeedback(opts) {
|
|
10
|
+
if (opts.packageReport) {
|
|
11
|
+
const reportPackage = await buildReportPackage({
|
|
12
|
+
projectDir: process.cwd(),
|
|
13
|
+
days: opts.days,
|
|
14
|
+
includeMarkerPreview: opts.evolve,
|
|
15
|
+
dnaId: opts.dnaId,
|
|
16
|
+
});
|
|
17
|
+
process.stdout.write(JSON.stringify(reportPackage, null, 2) + "\n");
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
9
20
|
const report = await buildKernelReport({
|
|
10
21
|
projectDir: process.cwd(),
|
|
11
22
|
days: opts.days,
|
|
12
23
|
includeMarkerPreview: opts.evolve,
|
|
13
24
|
dnaId: opts.dnaId,
|
|
14
25
|
});
|
|
15
|
-
if (report.summary.
|
|
16
|
-
process.stderr.write(`No
|
|
17
|
-
process.stderr.write("
|
|
26
|
+
if (report.summary.runtime_decisions === 0 && report.summary.legacy_diagnostics === 0) {
|
|
27
|
+
process.stderr.write(`No RuntimeDecisionEvent evidence or legacy diagnostics found for the last ${opts.days} day(s).\n`);
|
|
28
|
+
process.stderr.write("RuntimeDecisionEvent evidence is generated by dna-hook during Claude Code sessions.\n");
|
|
18
29
|
return 0;
|
|
19
30
|
}
|
|
20
31
|
if (opts.json) {
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
import { writeFile, readFile, readdir, mkdir, copyFile, access } from "node:fs/promises";
|
|
7
7
|
import { resolve, dirname, basename } from "node:path";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
|
-
import { createHash } from "node:crypto";
|
|
10
9
|
import { parseYAML } from "../../schema/yaml-parser.js";
|
|
10
|
+
import { listTemplateCatalog, calculateTemplateContentHash } from "../../templates/catalog.js";
|
|
11
11
|
import { sanitizeTemplateName } from "../util/version.js";
|
|
12
12
|
import { validateDNA } from "../../schema/validate.js";
|
|
13
13
|
import { askString, askChoice, askNumber, askYesNo, closePrompt } from "../util/prompt.js";
|
|
@@ -23,53 +23,20 @@ async function dirExists(path) {
|
|
|
23
23
|
return false;
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Extract namespace and display name from a template YAML file.
|
|
28
|
-
*/
|
|
29
|
-
async function readTemplateMetadata(filePath) {
|
|
30
|
-
try {
|
|
31
|
-
const content = await readFile(filePath, "utf-8");
|
|
32
|
-
const data = parseYAML(content);
|
|
33
|
-
return {
|
|
34
|
-
namespace: typeof data.namespace === "string" ? data.namespace : undefined,
|
|
35
|
-
displayName: typeof data.name === "string" ? data.name : undefined,
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
return {};
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
26
|
async function listTemplates() {
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
results.push({ name, namespace: meta.namespace, displayName: meta.displayName, source: "builtin" });
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
catch { /* no built-in templates */ }
|
|
54
|
-
// Project-local templates
|
|
55
|
-
if (await dirExists(PROJECT_TEMPLATES_DIR)) {
|
|
56
|
-
try {
|
|
57
|
-
const files = await readdir(PROJECT_TEMPLATES_DIR);
|
|
58
|
-
for (const f of files.filter((f) => f.endsWith(".dna.yaml"))) {
|
|
59
|
-
const name = f.replace(".dna.yaml", "");
|
|
60
|
-
const meta = await readTemplateMetadata(resolve(PROJECT_TEMPLATES_DIR, f));
|
|
61
|
-
results.push({ name, namespace: meta.namespace, displayName: meta.displayName, source: "project" });
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
catch { /* no project templates */ }
|
|
65
|
-
}
|
|
66
|
-
return results;
|
|
27
|
+
const entries = await listTemplateCatalog();
|
|
28
|
+
return entries.map((entry) => ({
|
|
29
|
+
name: entry.name,
|
|
30
|
+
namespace: entry.namespace,
|
|
31
|
+
displayName: entry.displayName,
|
|
32
|
+
source: entry.source,
|
|
33
|
+
}));
|
|
67
34
|
}
|
|
68
35
|
/**
|
|
69
36
|
* Calculate SHA256 hash of template content (truncated to 16 hex chars).
|
|
70
37
|
*/
|
|
71
38
|
export function calculateTemplateHash(content) {
|
|
72
|
-
return
|
|
39
|
+
return calculateTemplateContentHash(content);
|
|
73
40
|
}
|
|
74
41
|
/**
|
|
75
42
|
* Inject _source_template, _template_version, and _template_content_hash fields after the namespace/type line in YAML content.
|
|
@@ -122,27 +89,8 @@ function inferTemplateName(id) {
|
|
|
122
89
|
* Priority: .dna/configs/*.yaml (multi-config) > legacy single-config
|
|
123
90
|
*/
|
|
124
91
|
async function autoDetectConfigsForUpgrade() {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const files = await readdir(MULTI_CONFIG_DIR);
|
|
128
|
-
const yamls = files
|
|
129
|
-
.filter(f => f.endsWith(".yaml") || f.endsWith(".yml"))
|
|
130
|
-
.sort()
|
|
131
|
-
.map(f => resolve(MULTI_CONFIG_DIR, f));
|
|
132
|
-
if (yamls.length > 0)
|
|
133
|
-
return yamls;
|
|
134
|
-
}
|
|
135
|
-
catch { /* dir doesn't exist */ }
|
|
136
|
-
// Fallback: legacy single-config paths
|
|
137
|
-
const candidates = [
|
|
138
|
-
".dna/config.yaml", ".dna/config.yml", ".dna/config.json",
|
|
139
|
-
".dna.yaml", ".dna.yml", ".dna.json",
|
|
140
|
-
];
|
|
141
|
-
for (const name of candidates) {
|
|
142
|
-
if (await dirExists(name))
|
|
143
|
-
return [name];
|
|
144
|
-
}
|
|
145
|
-
return [];
|
|
92
|
+
const { detectDNAConfigs } = await import("../../compiler/index.js");
|
|
93
|
+
return detectDNAConfigs(process.cwd());
|
|
146
94
|
}
|
|
147
95
|
/**
|
|
148
96
|
* Upgrade config from a newer template version while preserving user-filled variables.
|
package/dist/cli/commands/run.js
CHANGED
|
@@ -9,7 +9,7 @@ import { spawn } from "node:child_process";
|
|
|
9
9
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
10
10
|
import { join } from "node:path";
|
|
11
11
|
import { randomUUID } from "node:crypto";
|
|
12
|
-
import { loadDNA, compileFromFiles } from "../../compiler/index.js";
|
|
12
|
+
import { loadDNA, compileFromFiles, expandDNAInputFiles } from "../../compiler/index.js";
|
|
13
13
|
import { cascadeDNA } from "../../compiler/cascade.js";
|
|
14
14
|
import { compileWorkflow } from "../../compiler/workflow.js";
|
|
15
15
|
import { compileAllRolesToAgentMD, writeAgentMDFiles, toKebabCase, } from "../../runtime/agent-md.js";
|
|
@@ -254,14 +254,15 @@ export async function runRun(opts) {
|
|
|
254
254
|
return 2;
|
|
255
255
|
}
|
|
256
256
|
const maxBudget = opts.maxBudget ?? 5;
|
|
257
|
-
const permissionMode = opts.permissionMode ?? "
|
|
257
|
+
const permissionMode = opts.permissionMode ?? "default";
|
|
258
258
|
const agentsDir = opts.agentsDir ?? ".claude/agents";
|
|
259
259
|
try {
|
|
260
260
|
// ── Step 1: Load & compile DNA ───────────────────────
|
|
261
261
|
log("Loading DNA files...");
|
|
262
|
-
const
|
|
262
|
+
const expandedDNAFiles = await expandDNAInputFiles(opts.dnaFiles);
|
|
263
|
+
const dnas = await Promise.all(expandedDNAFiles.map(loadDNA));
|
|
263
264
|
const cascaded = cascadeDNA(dnas);
|
|
264
|
-
const ir = await compileFromFiles(
|
|
265
|
+
const ir = await compileFromFiles(expandedDNAFiles, {
|
|
265
266
|
context: opts.context,
|
|
266
267
|
});
|
|
267
268
|
// ── Step 2: Find & compile workflow ──────────────────
|
|
@@ -3,51 +3,15 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Show DNA gene expression state in human-readable format.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
7
|
-
import { readFile } from "node:fs/promises";
|
|
8
|
-
import { fileURLToPath } from "node:url";
|
|
9
|
-
import { loadDNA, cascadeDNA, activateDNA } from "../../compiler/index.js";
|
|
6
|
+
import { loadDNA, cascadeDNA, activateDNA, expandDNAInputFiles } from "../../compiler/index.js";
|
|
10
7
|
import { formatGene, bold } from "../util/format.js";
|
|
11
|
-
function resolveSpeciesPath(ref) {
|
|
12
|
-
if (!ref.startsWith("species:"))
|
|
13
|
-
return null;
|
|
14
|
-
const name = ref.slice("species:".length);
|
|
15
|
-
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
16
|
-
return resolve(thisDir, "..", "..", "species", `${name}.dna.json`);
|
|
17
|
-
}
|
|
18
|
-
async function expandFiles(files) {
|
|
19
|
-
const expanded = [];
|
|
20
|
-
const seen = new Set();
|
|
21
|
-
for (const file of files) {
|
|
22
|
-
const abs = resolve(file);
|
|
23
|
-
if (seen.has(abs))
|
|
24
|
-
continue;
|
|
25
|
-
seen.add(abs);
|
|
26
|
-
expanded.push(abs);
|
|
27
|
-
try {
|
|
28
|
-
const raw = await readFile(abs, "utf-8");
|
|
29
|
-
const dna = JSON.parse(raw);
|
|
30
|
-
for (const ref of dna.cascade?.inherits ?? []) {
|
|
31
|
-
const speciesPath = resolveSpeciesPath(ref);
|
|
32
|
-
if (speciesPath && !seen.has(speciesPath)) {
|
|
33
|
-
seen.add(speciesPath);
|
|
34
|
-
expanded.unshift(speciesPath);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
// Will fail later with proper error
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return expanded;
|
|
43
|
-
}
|
|
44
8
|
export async function runShow(opts) {
|
|
45
9
|
if (opts.files.length === 0) {
|
|
46
10
|
process.stderr.write("Error: no files specified\n");
|
|
47
11
|
return 2;
|
|
48
12
|
}
|
|
49
13
|
try {
|
|
50
|
-
const expandedFiles = await
|
|
14
|
+
const expandedFiles = await expandDNAInputFiles(opts.files);
|
|
51
15
|
const dnas = await Promise.all(expandedFiles.map(loadDNA));
|
|
52
16
|
const cascaded = cascadeDNA(dnas);
|
|
53
17
|
const activated = activateDNA(cascaded, opts.context ?? null);
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Targets: claude-md, soul-md, cursorrules, system-prompt
|
|
12
12
|
*/
|
|
13
|
+
export type SyncMode = "auto" | "plugin" | "bin";
|
|
13
14
|
export interface SyncOptions {
|
|
14
15
|
files: string[];
|
|
15
16
|
target: string;
|
|
@@ -26,7 +27,10 @@ export interface SyncOptions {
|
|
|
26
27
|
settingsPath?: string;
|
|
27
28
|
lockFile?: string;
|
|
28
29
|
remove: boolean;
|
|
30
|
+
mode?: SyncMode;
|
|
29
31
|
plugin?: boolean;
|
|
32
|
+
dryRun?: boolean;
|
|
33
|
+
postSummary?: boolean;
|
|
30
34
|
}
|
|
31
35
|
/**
|
|
32
36
|
* Detect whether intentdna is running as a Claude Code plugin or standalone binary.
|
|
@@ -47,8 +51,8 @@ export declare function detectMode(): Promise<"plugin" | "bin">;
|
|
|
47
51
|
*/
|
|
48
52
|
export declare function cleanStaleDNAFiles(dir: string, type: "agents" | "skills", activeNamespaces: string[]): Promise<string[]>;
|
|
49
53
|
/**
|
|
50
|
-
* Auto-detect DNA
|
|
51
|
-
*
|
|
54
|
+
* Auto-detect DNA config file in current directory (legacy single-config).
|
|
55
|
+
* Priority: .dna/config.yaml > .dna/config.yml > .dna/config.json > .dna.yaml > .dna.yml > .dna.json
|
|
52
56
|
*/
|
|
53
57
|
export declare function autoDetectConfigs(): Promise<string[]>;
|
|
54
58
|
/**
|
|
@@ -74,12 +78,11 @@ export interface TemplateUpgradeInfo {
|
|
|
74
78
|
templateName: string;
|
|
75
79
|
currentVersion: string;
|
|
76
80
|
availableVersion: string;
|
|
81
|
+
reason: "version-update" | "missing-hash" | "content-drift";
|
|
77
82
|
}
|
|
78
83
|
/**
|
|
79
|
-
* Check template
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* Uses content hash comparison instead of semver — detects any template content change.
|
|
84
|
+
* Check stamped template metadata against the current template catalog.
|
|
85
|
+
* Version mismatch is the primary upgrade signal; content hash drift is a fallback.
|
|
83
86
|
*/
|
|
84
87
|
export declare function checkTemplateVersions(configPaths: string[]): Promise<TemplateUpgradeInfo[]>;
|
|
85
88
|
export declare function runSync(opts: SyncOptions): Promise<number>;
|