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
@@ -1,4 +1,35 @@
1
1
  import { extractGeneFromReason } from "../evolution/trace-bridge.js";
2
+ const SAFE_EXTERNAL_GENE = /^[A-Za-z0-9:_-]+$/;
3
+ function hasSafeExternalGene(gene) {
4
+ return typeof gene === "string" && SAFE_EXTERNAL_GENE.test(gene);
5
+ }
6
+ function hasNonBlankText(value) {
7
+ return typeof value === "string" && value.trim().length > 0;
8
+ }
9
+ function isSignalStatus(value) {
10
+ return value === "positive" || value === "negative" || value === "neutral";
11
+ }
12
+ function isExternalSignalClassification(value) {
13
+ return value === undefined || value === "protective" || value === "friction" || value === "unknown";
14
+ }
15
+ function hasFiniteConfidence(value) {
16
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 1;
17
+ }
18
+ function hasExternalSignalShape(signal) {
19
+ return isSignalStatus(signal.status) &&
20
+ hasNonBlankText(signal.source) &&
21
+ isExternalSignalClassification(signal.classification) &&
22
+ (signal.confidence === undefined || hasFiniteConfidence(signal.confidence));
23
+ }
24
+ function isRecord(value) {
25
+ return typeof value === "object" && value !== null && !Array.isArray(value);
26
+ }
27
+ function asString(value) {
28
+ return typeof value === "string" ? value : undefined;
29
+ }
30
+ function asNumber(value) {
31
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
32
+ }
2
33
  function toSignalEvidence(result) {
3
34
  const payload = {
4
35
  target: result.target,
@@ -6,23 +37,75 @@ function toSignalEvidence(result) {
6
37
  exit_code: result.exit_code,
7
38
  artifact: result.artifact,
8
39
  message: result.message,
40
+ evidence_ref: "evidence_ref" in result ? result.evidence_ref : undefined,
9
41
  };
10
42
  return Object.values(payload).some((value) => value !== undefined) ? payload : undefined;
11
43
  }
44
+ function hasExternalSignalInputContract(signal) {
45
+ return (hasExternalSignalShape(signal) &&
46
+ hasFiniteConfidence(signal.confidence) &&
47
+ hasNonBlankText(signal.mapping_policy) &&
48
+ hasNonBlankText(signal.evidence_ref) &&
49
+ hasSafeExternalGene(signal.gene));
50
+ }
51
+ function hasExternalSignalEnvelopeContract(signal) {
52
+ return (isSignalStatus(signal.status) &&
53
+ hasNonBlankText(signal.source) &&
54
+ isExternalSignalClassification(signal.classification) &&
55
+ hasFiniteConfidence(signal.confidence) &&
56
+ hasNonBlankText(signal.mapping_policy) &&
57
+ hasNonBlankText(signal.evidence_payload?.evidence_ref) &&
58
+ hasSafeExternalGene(signal.gene));
59
+ }
12
60
  function normalizeExternalSignals(adapterName, payload) {
13
61
  const items = Array.isArray(payload) ? payload : [payload];
14
- return items.map((item, index) => {
15
- const timestamp = item.timestamp ?? new Date().toISOString();
62
+ return items.map((raw, index) => {
63
+ const timestamp = isRecord(raw) ? asString(raw.timestamp) ?? new Date().toISOString() : new Date().toISOString();
64
+ if (!isRecord(raw)) {
65
+ return {
66
+ id: `${adapterName}:invalid:${timestamp}:${index}`,
67
+ kind: "external",
68
+ status: "neutral",
69
+ timestamp,
70
+ source: `${adapterName}.invalid`,
71
+ adapter: adapterName,
72
+ classification: "unknown",
73
+ evolution_eligible: false,
74
+ };
75
+ }
76
+ const item = {
77
+ id: asString(raw.id),
78
+ status: raw.status,
79
+ timestamp,
80
+ gene: asString(raw.gene),
81
+ source: asString(raw.source) ?? "",
82
+ detail: asString(raw.detail),
83
+ target: asString(raw.target),
84
+ evidence: asString(raw.evidence),
85
+ exit_code: asNumber(raw.exit_code),
86
+ artifact: asString(raw.artifact),
87
+ message: asString(raw.message),
88
+ evidence_ref: asString(raw.evidence_ref),
89
+ classification: raw.classification,
90
+ confidence: typeof raw.confidence === "number" ? raw.confidence : undefined,
91
+ mapping_policy: asString(raw.mapping_policy),
92
+ };
93
+ const shapeValid = hasExternalSignalShape(item);
94
+ const complete = hasExternalSignalInputContract(item);
16
95
  return {
17
- id: item.id ?? `${adapterName}:${item.source}:${timestamp}:${index}`,
96
+ id: item.id ?? `${adapterName}:${shapeValid ? item.source : "invalid"}:${timestamp}:${index}`,
18
97
  kind: "external",
19
- status: item.status,
98
+ status: shapeValid ? item.status : "neutral",
20
99
  timestamp,
21
100
  gene: item.gene,
22
- source: item.source,
101
+ source: hasNonBlankText(item.source) ? item.source : `${adapterName}.invalid`,
23
102
  detail: item.detail ?? item.message ?? item.evidence,
24
103
  adapter: adapterName,
25
104
  evidence_payload: toSignalEvidence(item),
105
+ classification: isExternalSignalClassification(item.classification) ? (item.classification ?? "unknown") : "unknown",
106
+ confidence: hasFiniteConfidence(item.confidence) ? item.confidence : undefined,
107
+ mapping_policy: item.mapping_policy,
108
+ evolution_eligible: complete,
26
109
  };
27
110
  });
28
111
  }
@@ -85,6 +168,8 @@ export function signalsToOutcomes(signals, dnaId = "default") {
85
168
  for (const signal of signals) {
86
169
  if (!signal.gene)
87
170
  continue;
171
+ if (signal.kind === "external" && !hasExternalSignalEnvelopeContract(signal))
172
+ continue;
88
173
  const effect = signal.status === "positive"
89
174
  ? "helped"
90
175
  : signal.status === "negative"
@@ -0,0 +1,19 @@
1
+ export declare const BUILTIN_TEMPLATES_DIR: string;
2
+ export declare const PROJECT_TEMPLATES_DIR = ".dna/templates";
3
+ export interface TemplateCatalogEntry {
4
+ name: string;
5
+ path: string;
6
+ namespace?: string;
7
+ displayName?: string;
8
+ version?: string;
9
+ contentHash: string;
10
+ source: "builtin" | "project";
11
+ keywords: readonly string[];
12
+ }
13
+ export declare function calculateTemplateContentHash(content: string): string;
14
+ export declare function listTemplateCatalog(options?: {
15
+ projectDir?: string;
16
+ }): Promise<TemplateCatalogEntry[]>;
17
+ export declare function findTemplateCatalogEntry(name: string, options?: {
18
+ projectDir?: string;
19
+ }): Promise<TemplateCatalogEntry | undefined>;
@@ -0,0 +1,57 @@
1
+ import { readFile, readdir } from "node:fs/promises";
2
+ import { createHash } from "node:crypto";
3
+ import { dirname, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { parseYAML } from "../schema/yaml-parser.js";
6
+ import { getTemplateMatchMetadata } from "./metadata.js";
7
+ export const BUILTIN_TEMPLATES_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "..", "templates");
8
+ export const PROJECT_TEMPLATES_DIR = ".dna/templates";
9
+ function calculateContentHash(content) {
10
+ return createHash("sha256").update(content, "utf-8").digest("hex").slice(0, 16);
11
+ }
12
+ async function readTemplateEntry(path, source) {
13
+ const content = await readFile(path, "utf-8");
14
+ const data = parseYAML(content);
15
+ const fileName = path.split("/").pop() ?? path;
16
+ const name = fileName.replace(/\.dna\.ya?ml$/, "");
17
+ const matchMetadata = getTemplateMatchMetadata(name);
18
+ return {
19
+ name,
20
+ path,
21
+ namespace: typeof data.namespace === "string" ? data.namespace : undefined,
22
+ displayName: typeof data.name === "string" ? data.name : undefined,
23
+ version: typeof data.version === "string" ? data.version : undefined,
24
+ contentHash: calculateContentHash(content),
25
+ source,
26
+ keywords: matchMetadata?.keywords ?? [],
27
+ };
28
+ }
29
+ async function collectTemplates(dir, source) {
30
+ try {
31
+ const files = await readdir(dir);
32
+ const templateFiles = files
33
+ .filter((file) => file.endsWith(".dna.yaml") || file.endsWith(".dna.yml"))
34
+ .sort();
35
+ const entries = [];
36
+ for (const file of templateFiles) {
37
+ entries.push(await readTemplateEntry(resolve(dir, file), source));
38
+ }
39
+ return entries;
40
+ }
41
+ catch {
42
+ return [];
43
+ }
44
+ }
45
+ export function calculateTemplateContentHash(content) {
46
+ return calculateContentHash(content);
47
+ }
48
+ export async function listTemplateCatalog(options) {
49
+ const projectDir = options?.projectDir ?? process.cwd();
50
+ const builtin = await collectTemplates(BUILTIN_TEMPLATES_DIR, "builtin");
51
+ const project = await collectTemplates(resolve(projectDir, PROJECT_TEMPLATES_DIR), "project");
52
+ return [...builtin, ...project];
53
+ }
54
+ export async function findTemplateCatalogEntry(name, options) {
55
+ const entries = await listTemplateCatalog(options);
56
+ return entries.find((entry) => entry.name === name || entry.displayName === name || entry.namespace === name);
57
+ }
@@ -335,8 +335,8 @@ controllers:
335
335
  - "- MAX_ROUNDS: stop, report max rounds reached and next focus"
336
336
  stop_report:
337
337
  - "On REQUEST_CHANGES, BLOCKED, or MAX_ROUNDS, output exactly these Chinese sections:"
338
- - "结论:state the direct stop trigger, current round, and current commit if one exists."
339
- - "为什么停止:explain the workflow principle in plain language; if this is a current-round new red, explain that continuing would build on a bad baseline."
338
+ - "结论:state the direct stop trigger, current round, and current commit if one exists; cite verifier result_id, trace_id, or ArtifactManifest artifact id."
339
+ - "为什么停止:explain the workflow principle in plain language; if this is a current-round new red, explain that continuing would build on a bad baseline; prose-only claims are not sufficient."
340
340
  - "是否回滚:say whether rollback/revert is recommended. If the commit direction looks wrong or the blast radius is unclear, recommend revert; if the failure is narrow and fixable, recommend fix-forward."
341
341
  - "下一步:give one concrete next action. If recommending fix-forward, include a copy-paste single-fix prompt with current commit, exact failure, allowed scope, forbidden actions, verification command, commit expectation, and stop conditions."
342
342
  constraints:
@@ -0,0 +1,6 @@
1
+ export interface TemplateMatchMetadata {
2
+ name: string;
3
+ keywords: readonly string[];
4
+ }
5
+ export declare const TEMPLATE_MATCH_METADATA: readonly TemplateMatchMetadata[];
6
+ export declare function getTemplateMatchMetadata(name: string): TemplateMatchMetadata | undefined;
@@ -0,0 +1,32 @@
1
+ export const TEMPLATE_MATCH_METADATA = [
2
+ { name: "code-review-pipeline", keywords: ["review", "code review", "pr review", "pull request", "审查"] },
3
+ { name: "safe-refactoring", keywords: ["refactor", "refactoring", "restructure", "rewrite safely", "重构"] },
4
+ { name: "tdd-strict", keywords: ["tdd", "test driven", "test first", "red green", "测试驱动"] },
5
+ { name: "full-pipeline", keywords: ["pipeline", "plan implement test", "full workflow", "完整流水线"] },
6
+ { name: "systematic-debugging", keywords: ["debug", "debugging", "bug fix", "troubleshoot", "调试"] },
7
+ { name: "verification-loop", keywords: ["verify", "verification", "validate", "check", "验证"] },
8
+ { name: "secure-dev", keywords: ["security", "secure", "owasp", "vulnerability", "安全"] },
9
+ { name: "enterprise-baseline", keywords: ["enterprise", "organization", "company", "corporate", "企业"] },
10
+ { name: "frontend-quality", keywords: ["frontend", "react", "vue", "css", "ui", "前端"] },
11
+ { name: "api-backend", keywords: ["api", "backend", "rest", "graphql", "server", "后端"] },
12
+ { name: "mobile-dev", keywords: ["mobile", "ios", "android", "react native", "移动"] },
13
+ { name: "documentation-writer", keywords: ["doc", "documentation", "readme", "wiki", "文档"] },
14
+ { name: "devops-cicd", keywords: ["devops", "ci", "cd", "deploy", "pipeline", "运维"] },
15
+ { name: "monorepo-governance", keywords: ["monorepo", "workspace", "packages", "governance"] },
16
+ { name: "flutter-behavior-lock", keywords: ["flutter", "behavior lock", "behavior spec", "行为锁定"] },
17
+ { name: "flutter-rewrite", keywords: ["flutter", "rewrite", "migration", "flutter rewrite"] },
18
+ { name: "flutter-refactoring-rescue", keywords: ["flutter", "rescue", "refactoring rescue"] },
19
+ { name: "multi-perspective-review", keywords: ["multi perspective", "multiple reviewer", "多角度"] },
20
+ { name: "subagent-parallel", keywords: ["parallel", "subagent", "concurrent", "并行"] },
21
+ { name: "yolo-with-guardrails", keywords: ["yolo", "fast", "guardrails", "move fast"] },
22
+ { name: "brainstorming-first", keywords: ["brainstorm", "explore", "ideation", "探索"] },
23
+ { name: "branch-finishing", keywords: ["branch", "finish", "cleanup", "pr prep"] },
24
+ { name: "pr-submitter", keywords: ["pr", "pull request", "submit"] },
25
+ { name: "migration-safety", keywords: ["migration", "migrate", "upgrade", "database migration", "dependency upgrade", "迁移"] },
26
+ { name: "incident-response", keywords: ["incident", "production issue", "hotfix", "outage", "postmortem", "事故"] },
27
+ { name: "performance-audit", keywords: ["performance", "profiling", "benchmark", "optimize", "latency", "性能"] },
28
+ ];
29
+ const TEMPLATE_MATCH_METADATA_BY_NAME = new Map(TEMPLATE_MATCH_METADATA.map((entry) => [entry.name, entry]));
30
+ export function getTemplateMatchMetadata(name) {
31
+ return TEMPLATE_MATCH_METADATA_BY_NAME.get(name);
32
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "intentdna",
3
- "version": "1.6.4",
3
+ "version": "1.7.0",
4
4
  "description": "Intent DNA — Declarative policy layer for AI agent behavior",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -173,5 +173,6 @@ hook 报错时输出人类可读的提示而非 JSON。`dna verify` 输出友好
173
173
 
174
174
  ### 第三阶段
175
175
  - [x] dna sync 自动检测模板版本 + 提示升级
176
- - [x] MCP server 常驻进程解决冷启动
176
+ - [x] MCP server 常驻进程复用完整 hook runtime + IR cache
177
+ - [ ] Claude settings hook 零进程调度(后续性能优化)
177
178
  - [x] 错误信息人类可读