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
|
@@ -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.0",
|
|
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.0"
|
|
29
29
|
}
|
|
@@ -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
|
+
}
|
|
@@ -5,7 +5,18 @@
|
|
|
5
5
|
* With --evolve: include a preview-only marker diff derived from local signals.
|
|
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,
|
|
@@ -11,51 +11,23 @@
|
|
|
11
11
|
import { readFile, writeFile } from "node:fs/promises";
|
|
12
12
|
import { resolve, dirname } from "node:path";
|
|
13
13
|
import { fileURLToPath } from "node:url";
|
|
14
|
+
import { TEMPLATE_MATCH_METADATA } from "../../templates/metadata.js";
|
|
14
15
|
const SPEC_PATH = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "..", "spec", "schema-spec.md");
|
|
15
16
|
const TEMPLATES_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "templates");
|
|
16
|
-
/** Keyword → template name mapping for scoring */
|
|
17
|
-
const TEMPLATE_KEYWORDS = {
|
|
18
|
-
"code-review-pipeline": ["review", "code review", "pr review", "pull request", "审查"],
|
|
19
|
-
"safe-refactoring": ["refactor", "refactoring", "restructure", "rewrite safely", "重构"],
|
|
20
|
-
"tdd-strict": ["tdd", "test driven", "test first", "red green", "测试驱动"],
|
|
21
|
-
"full-pipeline": ["pipeline", "plan implement test", "full workflow", "完整流水线"],
|
|
22
|
-
"systematic-debugging": ["debug", "debugging", "bug fix", "troubleshoot", "调试"],
|
|
23
|
-
"verification-loop": ["verify", "verification", "validate", "check", "验证"],
|
|
24
|
-
"secure-dev": ["security", "secure", "owasp", "vulnerability", "安全"],
|
|
25
|
-
"enterprise-baseline": ["enterprise", "organization", "company", "corporate", "企业"],
|
|
26
|
-
"frontend-quality": ["frontend", "react", "vue", "css", "ui", "前端"],
|
|
27
|
-
"api-backend": ["api", "backend", "rest", "graphql", "server", "后端"],
|
|
28
|
-
"mobile-dev": ["mobile", "ios", "android", "react native", "移动"],
|
|
29
|
-
"documentation-writer": ["doc", "documentation", "readme", "wiki", "文档"],
|
|
30
|
-
"devops-cicd": ["devops", "ci", "cd", "deploy", "pipeline", "运维"],
|
|
31
|
-
"monorepo-governance": ["monorepo", "workspace", "packages", "governance"],
|
|
32
|
-
"flutter-behavior-lock": ["flutter", "behavior lock", "behavior spec", "行为锁定"],
|
|
33
|
-
"flutter-rewrite": ["flutter", "rewrite", "migration", "flutter rewrite"],
|
|
34
|
-
"flutter-refactoring-rescue": ["flutter", "rescue", "refactoring rescue"],
|
|
35
|
-
"multi-perspective-review": ["multi perspective", "multiple reviewer", "多角度"],
|
|
36
|
-
"subagent-parallel": ["parallel", "subagent", "concurrent", "并行"],
|
|
37
|
-
"yolo-with-guardrails": ["yolo", "fast", "guardrails", "move fast"],
|
|
38
|
-
"brainstorming-first": ["brainstorm", "explore", "ideation", "探索"],
|
|
39
|
-
"branch-finishing": ["branch", "finish", "cleanup", "pr prep"],
|
|
40
|
-
"pr-submitter": ["pr", "pull request", "submit"],
|
|
41
|
-
"migration-safety": ["migration", "migrate", "upgrade", "database migration", "dependency upgrade", "迁移"],
|
|
42
|
-
"incident-response": ["incident", "production issue", "hotfix", "outage", "postmortem", "事故"],
|
|
43
|
-
"performance-audit": ["performance", "profiling", "benchmark", "optimize", "latency", "性能"],
|
|
44
|
-
};
|
|
45
17
|
function scoreTemplates(description) {
|
|
46
18
|
const lower = description.toLowerCase();
|
|
47
19
|
const scores = [];
|
|
48
|
-
for (const
|
|
20
|
+
for (const template of TEMPLATE_MATCH_METADATA) {
|
|
49
21
|
const matched = [];
|
|
50
|
-
for (const kw of keywords) {
|
|
22
|
+
for (const kw of template.keywords) {
|
|
51
23
|
if (lower.includes(kw)) {
|
|
52
24
|
matched.push(kw);
|
|
53
25
|
}
|
|
54
26
|
}
|
|
55
27
|
if (matched.length > 0) {
|
|
56
28
|
scores.push({
|
|
57
|
-
name: template,
|
|
58
|
-
displayName: template.replace(/-/g, " "),
|
|
29
|
+
name: template.name,
|
|
30
|
+
displayName: template.name.replace(/-/g, " "),
|
|
59
31
|
score: matched.length,
|
|
60
32
|
matchedKeywords: matched,
|
|
61
33
|
});
|
|
@@ -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>;
|