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.
Files changed (76) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/dist/cli/commands/compile.js +2 -47
  4. package/dist/cli/commands/context.d.ts +8 -0
  5. package/dist/cli/commands/context.js +63 -0
  6. package/dist/cli/commands/feedback.d.ts +1 -0
  7. package/dist/cli/commands/feedback.js +11 -0
  8. package/dist/cli/commands/generate.js +5 -33
  9. package/dist/cli/commands/init.js +11 -63
  10. package/dist/cli/commands/run.js +5 -4
  11. package/dist/cli/commands/show.js +2 -38
  12. package/dist/cli/commands/sync.d.ts +9 -6
  13. package/dist/cli/commands/sync.js +209 -190
  14. package/dist/cli/commands/templates.d.ts +10 -1
  15. package/dist/cli/commands/templates.js +50 -1
  16. package/dist/cli/commands/validate.js +15 -9
  17. package/dist/cli/commands/verify.js +97 -25
  18. package/dist/cli/index.js +76 -11
  19. package/dist/compiler/cascade.d.ts +3 -1
  20. package/dist/compiler/cascade.js +51 -0
  21. package/dist/compiler/compile.js +37 -0
  22. package/dist/compiler/diagnostics.d.ts +17 -0
  23. package/dist/compiler/diagnostics.js +30 -0
  24. package/dist/compiler/index.d.ts +3 -0
  25. package/dist/compiler/index.js +8 -11
  26. package/dist/compiler/input-resolver.d.ts +25 -0
  27. package/dist/compiler/input-resolver.js +175 -0
  28. package/dist/hooks/cli.d.ts +10 -1
  29. package/dist/hooks/cli.js +39 -26
  30. package/dist/hooks/event-registry.d.ts +17 -0
  31. package/dist/hooks/event-registry.js +89 -0
  32. package/dist/hooks/protocol.d.ts +1 -1
  33. package/dist/hooks/schema.js +5 -1
  34. package/dist/hooks/state.d.ts +2 -0
  35. package/dist/hooks/state.js +23 -2
  36. package/dist/mcp/index.js +2 -0
  37. package/dist/mcp/tools-compile.js +18 -49
  38. package/dist/mcp/tools-context.d.ts +2 -0
  39. package/dist/mcp/tools-context.js +85 -0
  40. package/dist/mcp/tools-enforce.d.ts +2 -2
  41. package/dist/mcp/tools-enforce.js +21 -53
  42. package/dist/mcp/tools-observability.js +24 -0
  43. package/dist/report/kernel-signals.js +3 -0
  44. package/dist/report/report-package.d.ts +56 -0
  45. package/dist/report/report-package.js +85 -0
  46. package/dist/runtime/agent-md.d.ts +1 -0
  47. package/dist/runtime/agent-md.js +21 -3
  48. package/dist/runtime/context-sources.d.ts +14 -0
  49. package/dist/runtime/context-sources.js +60 -0
  50. package/dist/runtime/markdown-target-registry.d.ts +17 -0
  51. package/dist/runtime/markdown-target-registry.js +37 -0
  52. package/dist/runtime/markdown.d.ts +2 -1
  53. package/dist/runtime/markdown.js +9 -12
  54. package/dist/runtime/output-artifact-registry.d.ts +11 -0
  55. package/dist/runtime/output-artifact-registry.js +30 -0
  56. package/dist/runtime/settings-adapter.d.ts +2 -1
  57. package/dist/runtime/settings-adapter.js +4 -12
  58. package/dist/runtime/skill-adapter.d.ts +32 -4
  59. package/dist/runtime/skill-adapter.js +187 -10
  60. package/dist/runtime/workflow-runner.d.ts +1 -1
  61. package/dist/runtime/workflow-runner.js +1 -1
  62. package/dist/schema/controller-registry.d.ts +29 -0
  63. package/dist/schema/controller-registry.js +35 -0
  64. package/dist/schema/types.d.ts +33 -0
  65. package/dist/schema/validate.js +157 -138
  66. package/dist/schema/validators/controllers.d.ts +3 -0
  67. package/dist/schema/validators/controllers.js +156 -0
  68. package/dist/signals/index.d.ts +10 -0
  69. package/dist/signals/index.js +90 -5
  70. package/dist/templates/catalog.d.ts +19 -0
  71. package/dist/templates/catalog.js +57 -0
  72. package/dist/templates/flutter-rewrite.dna.yaml +2 -2
  73. package/dist/templates/metadata.d.ts +6 -0
  74. package/dist/templates/metadata.js +32 -0
  75. package/package.json +1 -1
  76. 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.6.4",
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.6.4"
28
+ "version": "1.7.0"
29
29
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "intentdna",
3
- "version": "1.6.4",
3
+ "version": "1.7.0",
4
4
  "description": "Declarative policy layer for AI agent behavior",
5
5
  "author": {
6
6
  "name": "Samuel"
@@ -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 { resolve, dirname } from "node:path";
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 expandFiles(opts.files);
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,8 @@
1
+ export interface ContextOptions {
2
+ subcommand: string;
3
+ query?: string;
4
+ id?: string;
5
+ files?: string[];
6
+ projectDir?: string;
7
+ }
8
+ export declare function runContext(opts: ContextOptions): Promise<number>;
@@ -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
+ }
@@ -9,5 +9,6 @@ export interface FeedbackOptions {
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>;
@@ -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 [template, keywords] of Object.entries(TEMPLATE_KEYWORDS)) {
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 results = [];
44
- // Built-in templates
45
- try {
46
- const files = await readdir(TEMPLATES_DIR);
47
- for (const f of files.filter((f) => f.endsWith(".dna.yaml"))) {
48
- const name = f.replace(".dna.yaml", "");
49
- const meta = await readTemplateMetadata(resolve(TEMPLATES_DIR, f));
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 createHash("sha256").update(content, "utf-8").digest("hex").slice(0, 16);
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
- // Priority 1: .dna/configs/*.yaml (multi-config)
126
- try {
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.
@@ -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 ?? "bypassPermissions";
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 dnas = await Promise.all(opts.dnaFiles.map(loadDNA));
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(opts.dnaFiles, {
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 { resolve, dirname } from "node:path";
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 expandFiles(opts.files);
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 configs: multi-config (.dna/configs/*.yaml) or legacy single-config.
51
- * Returns array of absolute paths.
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 versions against current template content hashes.
80
- * Returns upgrade info for changed configs and prints human-readable suggestions.
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>;