intentdna 1.8.7 → 1.9.0-rc.2

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 (69) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/dist/cli/commands/candidate-promotion.d.ts +15 -0
  4. package/dist/cli/commands/candidate-promotion.js +197 -0
  5. package/dist/cli/commands/run-lifecycle.js +1 -0
  6. package/dist/cli/commands/run.js +76 -42
  7. package/dist/cli/commands/setup.d.ts +10 -0
  8. package/dist/cli/commands/setup.js +129 -36
  9. package/dist/cli/commands/source-assets.d.ts +32 -0
  10. package/dist/cli/commands/source-assets.js +154 -0
  11. package/dist/cli/commands/sync.js +52 -12
  12. package/dist/cli/commands/templates.js +59 -37
  13. package/dist/cli/index.js +161 -5
  14. package/dist/compiler/cascade.js +1 -1
  15. package/dist/hooks/cli.d.ts +6 -0
  16. package/dist/hooks/cli.js +118 -10
  17. package/dist/hooks/enforcement-boundary.d.ts +1 -1
  18. package/dist/hooks/protocol.d.ts +1 -1
  19. package/dist/hooks/state.d.ts +5 -0
  20. package/dist/hooks/state.js +21 -6
  21. package/dist/mcp/tools-state.js +251 -15
  22. package/dist/runtime/claude-sync-target.d.ts +2 -0
  23. package/dist/runtime/claude-sync-target.js +1 -1
  24. package/dist/runtime/evaluator.d.ts +13 -0
  25. package/dist/runtime/evaluator.js +46 -0
  26. package/dist/runtime/index.d.ts +8 -4
  27. package/dist/runtime/index.js +4 -2
  28. package/dist/runtime/plugin-adapter.d.ts +14 -1
  29. package/dist/runtime/plugin-adapter.js +23 -9
  30. package/dist/runtime/run-contracts.d.ts +5 -3
  31. package/dist/runtime/run-contracts.js +11 -1
  32. package/dist/runtime/run-controller.d.ts +15 -1
  33. package/dist/runtime/run-controller.js +235 -94
  34. package/dist/runtime/skill-adapter.js +24 -53
  35. package/dist/runtime/verifier.js +101 -2
  36. package/dist/runtime/workflow-plan-adapter.d.ts +2 -1
  37. package/dist/runtime/workflow-plan-adapter.js +78 -11
  38. package/dist/runtime/workflow-runtime-manifest.d.ts +48 -0
  39. package/dist/runtime/workflow-runtime-manifest.js +196 -0
  40. package/dist/runtime/workflow-variable-scopes.d.ts +12 -0
  41. package/dist/runtime/workflow-variable-scopes.js +55 -0
  42. package/dist/schema/types.d.ts +9 -0
  43. package/dist/schema/validate.js +29 -1
  44. package/dist/sources/authoring-packet.d.ts +8 -0
  45. package/dist/sources/authoring-packet.js +85 -0
  46. package/dist/sources/discovery.d.ts +7 -0
  47. package/dist/sources/discovery.js +150 -0
  48. package/dist/sources/license.d.ts +5 -0
  49. package/dist/sources/license.js +94 -0
  50. package/dist/sources/manifest.d.ts +11 -0
  51. package/dist/sources/manifest.js +84 -0
  52. package/dist/sources/skill-markdown.d.ts +6 -0
  53. package/dist/sources/skill-markdown.js +174 -0
  54. package/dist/sources/types.d.ts +125 -0
  55. package/dist/sources/types.js +1 -0
  56. package/dist/templates/asset-refinery.dna.yaml +192 -0
  57. package/dist/templates/flutter-behavior-lock.dna.yaml +17 -0
  58. package/dist/templates/flutter-refactoring-rescue.dna.yaml +11 -0
  59. package/dist/templates/flutter-rewrite.dna.yaml +154 -28
  60. package/dist/templates/metadata.js +2 -0
  61. package/dist/templates/multi-agent-handoff-coordination.dna.yaml +102 -37
  62. package/dist/templates/persistent-executor.dna.yaml +61 -3
  63. package/dist/templates/research-improvement.dna.yaml +295 -0
  64. package/dist/templates/subagent-parallel.dna.yaml +90 -17
  65. package/dist/templates/workflow-evidence-review.dna.yaml +8 -0
  66. package/dist/workflow/candidate-projection.d.ts +33 -0
  67. package/dist/workflow/candidate-projection.js +70 -0
  68. package/package.json +3 -1
  69. package/scripts/sync-release-plugin.cjs +87 -0
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * dna templates — template catalog and deployment commands.
3
3
  */
4
- import { readdirSync, copyFileSync, existsSync, readFileSync, mkdirSync } from "node:fs";
4
+ import { readdirSync, copyFileSync, existsSync, readFileSync, mkdirSync, lstatSync, } from "node:fs";
5
5
  import { mkdir, readdir, writeFile } from "node:fs/promises";
6
6
  import { homedir } from "node:os";
7
7
  import { resolve, join, basename, dirname } from "node:path";
@@ -15,6 +15,7 @@ import { resolveAuthToken } from "./auth.js";
15
15
  import { missingCliApiTokenReason } from "./cloud-auth-guidance.js";
16
16
  import { createWorkflowDraft, workflowDraftToTemplateYaml, } from "../../workflow/index.js";
17
17
  import { WORKFLOW_ASSET_IDS } from "../../templates/metadata.js";
18
+ import { buildSourceAuthoringPacket } from "../../sources/authoring-packet.js";
18
19
  /**
19
20
  * Resolve the global intentdna templates directory.
20
21
  * Strategy: npm root -g + /intentdna/dist/templates
@@ -1774,6 +1775,9 @@ function candidateTemplateName(sourceId) {
1774
1775
  function normalizeTemplateSaveName(name) {
1775
1776
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "source-candidate";
1776
1777
  }
1778
+ function isSafeCandidateName(name) {
1779
+ return /^[a-z][a-z0-9-]{1,62}$/.test(name);
1780
+ }
1777
1781
  function workflowFromSourceCandidate(candidate) {
1778
1782
  const closestTemplate = candidate.coverage.matchedTemplates[0]?.name;
1779
1783
  const comparePrompt = closestTemplate
@@ -1922,37 +1926,50 @@ function buildSourceCandidatePreview(candidate, requestedTemplateName) {
1922
1926
  boundary: "Read-only candidate preview. It prints a reviewable template YAML draft but does not write files, configure projects, sync runtimes, deploy, or mutate evolution markers.",
1923
1927
  };
1924
1928
  }
1925
- function writeSourceCandidatePreview(preview) {
1926
- process.stderr.write("Template reference candidate preview\n");
1927
- process.stderr.write(`Reference: ${preview.candidate.id}\n`);
1928
- process.stderr.write(`Template: ${preview.templateName}\n`);
1929
- process.stderr.write(`Decision: ${preview.decision.status}\n`);
1930
- process.stderr.write(`Workflow: ${preview.draft.workflow.name}\n`);
1931
- process.stderr.write(`Steps: ${preview.draft.workflow.steps.length}\n`);
1932
- process.stderr.write("\nReview commands\n");
1933
- for (const command of preview.reviewCommands)
1934
- process.stderr.write(` - ${command}\n`);
1935
- process.stderr.write("\nBlocked until review\n");
1936
- for (const blocker of preview.blockedUntilReview)
1937
- process.stderr.write(` - ${blocker}\n`);
1938
- process.stderr.write(`Boundary: ${preview.boundary}\n`);
1939
- process.stderr.write("\n--- YAML preview is written to stdout ---\n");
1940
- process.stdout.write(preview.templateYaml);
1941
- if (!preview.templateYaml.endsWith("\n"))
1942
- process.stdout.write("\n");
1943
- }
1944
1929
  async function saveSourceCandidatePreview(params) {
1945
- const projectDir = params.projectDir ?? process.cwd();
1946
- const templateDir = join(projectDir, ".dna", "templates");
1947
- const path = join(templateDir, `${params.preview.templateName}.dna.yaml`);
1930
+ const projectDir = resolve(params.projectDir ?? process.cwd());
1931
+ const dnaDir = join(projectDir, ".dna");
1932
+ const authoringDir = join(dnaDir, "authoring");
1933
+ const path = join(authoringDir, `${params.preview.templateName}.candidate.dna.yaml`);
1934
+ for (const stagingParent of [dnaDir, authoringDir]) {
1935
+ if (existsSync(stagingParent)
1936
+ && lstatSync(stagingParent).isSymbolicLink()) {
1937
+ return {
1938
+ error: `Candidate staging path must not contain a symlink: ${stagingParent}`,
1939
+ path,
1940
+ };
1941
+ }
1942
+ }
1948
1943
  if (existsSync(path)) {
1949
1944
  return {
1950
- error: `Workflow asset draft already exists: ${params.preview.templateName}`,
1945
+ error: `Candidate already exists: ${params.preview.templateName}`,
1951
1946
  path,
1952
1947
  };
1953
1948
  }
1954
- await mkdir(templateDir, { recursive: true });
1955
- await writeFile(path, params.preview.templateYaml, "utf-8");
1949
+ await mkdir(authoringDir, { recursive: true });
1950
+ for (const stagingParent of [dnaDir, authoringDir]) {
1951
+ if (lstatSync(stagingParent).isSymbolicLink()) {
1952
+ return {
1953
+ error: `Candidate staging path must not contain a symlink: ${stagingParent}`,
1954
+ path,
1955
+ };
1956
+ }
1957
+ }
1958
+ try {
1959
+ await writeFile(path, params.preview.templateYaml, {
1960
+ encoding: "utf-8",
1961
+ flag: "wx",
1962
+ });
1963
+ }
1964
+ catch (error) {
1965
+ if (error.code === "EEXIST") {
1966
+ return {
1967
+ error: `Candidate already exists: ${params.preview.templateName}`,
1968
+ path,
1969
+ };
1970
+ }
1971
+ throw error;
1972
+ }
1956
1973
  const closestTemplate = params.preview.candidate.coverage.matchedTemplates[0]?.name ?? "<current-template>";
1957
1974
  return {
1958
1975
  mode: "saved_template_source_candidate",
@@ -1963,19 +1980,18 @@ async function saveSourceCandidatePreview(params) {
1963
1980
  decision: params.preview.decision,
1964
1981
  reviewCommands: params.preview.reviewCommands,
1965
1982
  nextCommands: [
1966
- `dna assets inspect ${params.preview.templateName} --evidence`,
1983
+ `dna validate ${path}`,
1984
+ `dna workflow project-candidate ${path} --target claude --dry-run`,
1985
+ `dna workflow project-candidate ${path} --target codex --dry-run`,
1967
1986
  `dna assets diff ${closestTemplate} ${params.preview.templateName}`,
1968
1987
  `dna assets adoption ${params.preview.templateName}`,
1969
- `dna org bind --asset ${params.preview.templateName} --status planned --sync-target codex`,
1970
- "dna org rollout",
1971
- "dna sync --codex --dry-run",
1972
1988
  ],
1973
1989
  blockedUntilReview: [
1974
1990
  "Runtime sync remains blocked until asset diff/adoption and organization rollout review pass.",
1975
1991
  "Vercel deployment is unrelated unless a web/public surface changes.",
1976
1992
  "Evolution marker mutation remains blocked until evidence-backed suggestions are reviewed.",
1977
1993
  ],
1978
- boundary: "Saved reference candidate as a project-local draft only. It is not adopted, organization-bound, synced, deployed, or evolved.",
1994
+ boundary: "Saved an unreviewed candidate under .dna/authoring only. It is not promoted, adopted, organization-bound, synced, deployed, or evolved.",
1979
1995
  };
1980
1996
  }
1981
1997
  function writeSavedSourceCandidate(result) {
@@ -2029,8 +2045,12 @@ export async function runTemplatesSources(opts = {}) {
2029
2045
  return 2;
2030
2046
  }
2031
2047
  if (opts.candidate) {
2032
- const preview = buildSourceCandidatePreview(candidate, opts.saveTemplate);
2033
2048
  if (opts.saveTemplate) {
2049
+ if (!isSafeCandidateName(opts.saveTemplate)) {
2050
+ process.stderr.write("Error: invalid candidate name; use 2-63 lowercase letters, digits, or hyphens, starting with a letter.\n");
2051
+ return 2;
2052
+ }
2053
+ const preview = buildSourceCandidatePreview(candidate, opts.saveTemplate);
2034
2054
  const saved = await saveSourceCandidatePreview({ preview, projectDir: opts.projectDir });
2035
2055
  if ("error" in saved) {
2036
2056
  process.stderr.write(`${saved.error}\n`);
@@ -2045,11 +2065,13 @@ export async function runTemplatesSources(opts = {}) {
2045
2065
  writeSavedSourceCandidate(saved);
2046
2066
  return 0;
2047
2067
  }
2048
- if (opts.json) {
2049
- process.stdout.write(`${JSON.stringify(preview, null, 2)}\n`);
2050
- return 0;
2051
- }
2052
- writeSourceCandidatePreview(preview);
2068
+ const packet = await buildSourceAuthoringPacket({
2069
+ skillDir: dirname(candidate.path),
2070
+ projectDir: opts.projectDir,
2071
+ sourceId: candidate.id,
2072
+ });
2073
+ process.stderr.write("Warning: deprecated --candidate alias; use --packet for source authoring packets.\n");
2074
+ process.stdout.write(`${JSON.stringify(packet, null, 2)}\n`);
2053
2075
  return 0;
2054
2076
  }
2055
2077
  const packet = await buildTemplateSourceReviewPacket(candidate);
package/dist/cli/index.js CHANGED
@@ -12,9 +12,57 @@
12
12
  * help Show this help message
13
13
  */
14
14
  import { parseArgs } from "node:util";
15
+ import { basename, isAbsolute, relative, resolve } from "node:path";
15
16
  import { getPackageVersion } from "./util/version.js";
16
17
  import { WORKFLOW_ASSET_IDS } from "../templates/metadata.js";
17
18
  const fallbackVersion = "unknown";
19
+ const REFINERY_CANDIDATE_FILE_RE = /^([a-z][a-z0-9-]{1,62})\.candidate\.dna\.yaml$/;
20
+ function normalizeAssetRefineryVariables(projectDir, variables) {
21
+ const authoringRoot = resolve(projectDir, ".dna", "authoring");
22
+ const normalizeAuthoringPath = (value, label) => {
23
+ const absolute = resolve(projectDir, value);
24
+ const fromAuthoring = relative(authoringRoot, absolute);
25
+ if (fromAuthoring === ""
26
+ || fromAuthoring === ".."
27
+ || fromAuthoring.startsWith("../")
28
+ || fromAuthoring.startsWith("..\\")
29
+ || isAbsolute(fromAuthoring)) {
30
+ return {
31
+ error: `${label} must resolve inside the selected project's .dna/authoring directory`,
32
+ };
33
+ }
34
+ return {
35
+ path: `.dna/authoring/${fromAuthoring.replaceAll("\\", "/")}`,
36
+ absolute,
37
+ };
38
+ };
39
+ const sourcePacket = normalizeAuthoringPath(variables.source_packet ?? ".dna/authoring/source-packet.json", "source_packet");
40
+ if (sourcePacket.error)
41
+ return sourcePacket.error;
42
+ const requestedCandidateName = variables.candidate_name;
43
+ const candidateValue = variables.candidate_path
44
+ ?? `.dna/authoring/${requestedCandidateName ?? "source"}.candidate.dna.yaml`;
45
+ const candidate = normalizeAuthoringPath(candidateValue, "candidate_path");
46
+ if (candidate.error)
47
+ return candidate.error;
48
+ const candidateFilename = basename(candidate.absolute);
49
+ const candidateMatch = REFINERY_CANDIDATE_FILE_RE.exec(candidateFilename);
50
+ if (!candidateMatch) {
51
+ return "candidate_path must end with <safe-name>.candidate.dna.yaml, using 2-63 lowercase letters, digits, or hyphens";
52
+ }
53
+ if (candidate.path.slice(".dna/authoring/".length) !== candidateFilename) {
54
+ return "candidate_path must be a direct child of .dna/authoring";
55
+ }
56
+ const candidateName = candidateMatch[1];
57
+ if (requestedCandidateName !== undefined
58
+ && requestedCandidateName !== candidateName) {
59
+ return "candidate_name must match the basename of candidate_path";
60
+ }
61
+ variables.source_packet = sourcePacket.path;
62
+ variables.candidate_name = candidateName;
63
+ variables.candidate_path = candidate.path;
64
+ return null;
65
+ }
18
66
  function helpText(version) {
19
67
  return `Intent DNA CLI v${version}
20
68
 
@@ -223,9 +271,9 @@ Boundaries:
223
271
  `,
224
272
  sync: `Usage: dna sync [status|doctor|remove] [files...] [options]\n\nSubcommands:\n status Show registry-tracked sync artifacts and drift posture\n doctor Check whether registry-tracked artifacts still match last sync\n remove Remove safe hash-matching IntentDNA-owned/diagnostic artifacts from the registry\n\nOptions:\n --mode auto|plugin|bin Sync mode (default: auto)\n --dry-run Print planned outputs without writing files; with remove, preview removals\n --post-summary Print registry-backed output summary after sync\n --json Print status/doctor JSON to stdout\n --project-dir <path> Read/write local sync configs and artifacts in another project directory\n --target <target> Markdown target for injection\n --inject <path> File to inject markdown into\n --agents <dir> Generate agent files\n --skills <dir> Generate skill files\n --workflow <dir> Generate workflow shell scripts\n --settings <path> Write hook settings in bin mode\n --codex Generate Codex CLI instruction, plugin, fallback hook, and evidence artifacts\n --codex-install-mode <mode> plugin_scoped|project_fallback|managed|none (default project_fallback)\n --codex-dogfood Probe codex exec --json and attach diagnostic evidence\n --lock <path> Write lock file (default .dna/lock)\n --remove Legacy explicit-target removal; requires --inject/--agents/--skills/--workflow/--codex\n`,
225
273
  verify: `Usage: dna verify [--lock <path>] [--stats] [--health] [--strict]\n\nOptions:\n --health Comprehensive installation health check\n --stats Show hook call statistics\n --strict Require valid RuntimeDecisionEvent enterprise evidence\n --lock <path> Lock file path (default .dna/lock)\n`,
226
- templates: `Usage: dna templates <list|marketplace|manage|sources|show|publish|publications|revoke|deploy> [name] [--json] [--project-dir <path>]\n\nSubcommands:\n list List built-in and project templates with marketplace visibility/harness metadata\n marketplace Export a shared intentdna.template_marketplace_catalog.v1 catalog\n manage [name] Show template management view: tags/details/marketplace/step index plus full YAML\n sources [reference] Review external workflow/skill references that may be translated into DNA templates\n show <name> Show template metadata, marketplace metadata, and YAML\n publish Publish an org-private template through Supabase using a local receipt evidence file\n publications List org-private template publications through Supabase as read-only projection\n revoke Mark one org-private template publication unpublished through Supabase metadata\n deploy Copy local templates to global install\n\nOptions:\n --json Print manage/sources/marketplace/publish/publications/revoke JSON to stdout\n --project-dir <path> Include project-local .dna/templates from another project directory\n --output <path> For marketplace: write catalog JSON for online/static import\n --source-dir <path> Scan a reference workflow/skill directory (repeatable)\n --focus all|refresh|gaps For sources list: all references, existing-template refreshes, or uncovered gaps\n --candidate For sources <reference>: print a read-only candidate template YAML preview\n --save-template <name> With --candidate, save the preview as project-local .dna/templates/<name>.dna.yaml\n --org <org> For publish/publications/revoke: organization id/slug/name\n --project <project> For publish/publications/revoke: project id/slug/name\n --template <name> For publish/publications: template name filter\n --publication-id <id> For revoke: org-private template publication id from dna templates publications\n --reason <text> For revoke: optional human reason stored in request metadata\n --receipt <path> For publish: intentdna.org_review_receipt.v1 or intentdna.report_ingest_receipt.v1 evidence file\n --endpoint <url> For publish/publications/revoke: Supabase Edge Function endpoint override\n --auth-config-dir <dir> Read CLI API token config from a specific directory for isolated template publication checks\n --dry-run For publish/publications/revoke: validate request without network upload\n`,
274
+ templates: `Usage: dna templates <list|marketplace|manage|sources|show|publish|publications|revoke|deploy> [name] [--json] [--project-dir <path>]\n\nSubcommands:\n list List built-in and project templates with marketplace visibility/harness metadata\n marketplace Export a shared intentdna.template_marketplace_catalog.v1 catalog\n manage [name] Show template management view: tags/details/marketplace/step index plus full YAML\n sources [reference] Review, snapshot, or packetize external workflow/skill references\n show <name> Show template metadata, marketplace metadata, and YAML\n publish Publish an org-private template through Supabase using a local receipt evidence file\n publications List org-private template publications through Supabase as read-only projection\n revoke Mark one org-private template publication unpublished through Supabase metadata\n deploy Copy local templates to global install\n\nOptions:\n --json Print manage/sources/marketplace/publish/publications/revoke JSON to stdout\n --project-dir <path> Include project-local .dna/templates from another project directory\n --output <path> For marketplace: write catalog JSON for online/static import\n --source-dir <path> Scan a reference workflow/skill directory (repeatable)\n --focus all|refresh|gaps For sources list: all references, existing-template refreshes, or uncovered gaps\n --snapshot Emit deterministic source manifests and hashes\n --packet Emit intentdna.source_authoring_packet.v1 for the selected source\n --write <path> Write a snapshot or packet under .dna/authoring without overwrite\n --candidate Deprecated read-only packet alias retained for one release\n --save-template <name> With --candidate, stage a compatibility candidate under .dna/authoring; never promote it\n --org <org> For publish/publications/revoke: organization id/slug/name\n --project <project> For publish/publications/revoke: project id/slug/name\n --template <name> For publish/publications: template name filter\n --publication-id <id> For revoke: org-private template publication id from dna templates publications\n --reason <text> For revoke: optional human reason stored in request metadata\n --receipt <path> For publish: intentdna.org_review_receipt.v1 or intentdna.report_ingest_receipt.v1 evidence file\n --endpoint <url> For publish/publications/revoke: Supabase Edge Function endpoint override\n --auth-config-dir <dir> Read CLI API token config from a specific directory for isolated template publication checks\n --dry-run For publish/publications/revoke: validate request without network upload\n`,
227
275
  assets: `Usage: dna assets <list|library|candidates|lineage|show|inspect|status|review|governance|lifecycle|adoption|suggestions|diff|evidence> [name] [--evidence] [--json]\n\nSubcommands:\n list List template-backed workflow assets\n library Browse workflow assets by readiness with evidence/governance next commands\n candidates [name] Find candidate workflow assets to extract or promote from dogfood evidence\n lineage <name> Explain origin, cascade, evidence, and human-reviewed evolution route\n lifecycle <name> Show cascade, evidence, suggestion, draft, review, adoption, bind, and sync lifecycle\n show <name> Show workflow asset summary\n inspect <name> Show workflows, controllers, roles, steps, and optional evidence links\n status [name] Show read-only management, evidence, and project configuration posture\n review Show manager-facing asset health summary and next actions\n governance <name> Explain cascade inheritance and preview-first evolution for an asset\n adoption <name> Show adoption readiness checks and the safe adoption path\n suggestions <name> Suggest human-reviewed workflow improvements from evidence\n diff <from> <to> Compare two workflow assets for manager review before adoption\n evidence <name> Summarize registered dogfood progress JSON for a workflow asset\n\nOptions:\n --evidence Include registered evidence/reference links\n --project-dir <path> Read project-local workflow assets and configs from another project directory\n --json Print JSON to stdout\n`,
228
- workflow: `Usage: dna workflow <examples|authoring-packet|intake|route|suggestions|mine|draft|edit-plan|review-suggestion|improve-from-suggestions|from-candidate> [description] [--example <id>] [--strictness standard|high] [--json|--yaml]\n\nSubcommands:\n examples List draft examples\n authoring-packet [goal] Emit the C3 packet for external-agent workflow authoring; no intent modeling or mutation\n intake <description> Review a workflow request and recommend reuse or authoring path\n route <description> Show the personal no-canvas route: describe, author, validate, run; use --org for organization rollout gates\n suggestions <asset> Suggest human-reviewed workflow improvements from evidence without mutation\n mine [asset] Find candidate workflow assets or follow-up drafts from real evidence\n draft <description> Clone a built-in example as a scaffold fallback; structure is not derived from the goal\n edit-plan <instruction> Preview a workflow edit without writing DNA or runtime files\n review-suggestion Persist accept/reject/defer for an evidence-backed suggestion\n improve-from-suggestions Build a reviewable edit plan from an accepted asset suggestion\n from-candidate <id> Act on a dogfood-backed candidate without copying a long command\n\nOptions:\n --name <org> Organization name for --org route planning\n --project <project> Project name for --org route planning\n --sync-target <target> Runtime surface for --org route planning, such as codex or claude\n --org Include organization binding, configure, rollout, sync, deploy, improve, and evolution gates\n --from-template <name> Source workflow asset for route/edit/candidate commands\n --workflow <id> Select a workflow inside a multi-workflow asset template such as fix or behavior-lock\n --suggestion <id> Suggestion id such as template-model-verifier or template-blocked-triage\n --decision <decision> For review-suggestion: accept|reject|defer\n --save-template <name> Save a draft/scaffold as .dna/templates/<name>.dna.yaml for human review and asset inspection\n --project-dir <path> Read/write workflow assets in another project directory\n --force Overwrite an existing saved workflow asset draft\n --operation <op> Optional override; otherwise auto-chosen from the instruction\n --target-step <id> Optional override; otherwise auto-chosen from the edit\n --role <role> Optional override; otherwise auto-chosen/defaulted from the edit\n\nExamples:\n dna workflow examples\n dna workflow authoring-packet "Create a code review workflow" --json\n dna workflow intake "Rewrite module with logic equivalence"\n dna workflow route "Rewrite module with logic equivalence"\n dna workflow route "Rewrite module with logic equivalence" --org --name "Project Admin" --project "Mobile Rewrite" --sync-target codex\n dna workflow suggestions ${WORKFLOW_ASSET_IDS.default} --json\n dna workflow mine ${WORKFLOW_ASSET_IDS.default} --json\n dna workflow draft "Rewrite module with logic equivalence" --example rewrite_governance --strictness high # scaffold fallback only\n dna workflow draft "Rewrite module with logic equivalence" --save-template team-rewrite-flow # scaffold fallback only\n dna workflow edit-plan "Add equivalence verifier after rewrite" --from-template team-rewrite-flow --save-template team-rewrite-flow-v2\n dna workflow review-suggestion --from-template ${WORKFLOW_ASSET_IDS.default} --workflow fix --suggestion template-model-verifier --decision accept\n dna workflow improve-from-suggestions --from-template ${WORKFLOW_ASSET_IDS.default} --workflow fix --suggestion template-model-verifier --save-template ${WORKFLOW_ASSET_IDS.default}-v2\n dna workflow from-candidate extract-diagnosis-review-${WORKFLOW_ASSET_IDS.default} --from-template ${WORKFLOW_ASSET_IDS.default} --save-template ${WORKFLOW_ASSET_IDS.rewriteDiagnosisReview}\n`,
276
+ workflow: `Usage: dna workflow <examples|authoring-packet|intake|route|suggestions|mine|draft|edit-plan|review-suggestion|improve-from-suggestions|from-candidate>\n dna workflow promote-candidate <candidate-path> --save-template <name>\n dna workflow project-candidate <candidate-path> --target claude|codex --dry-run\n\nSubcommands:\n examples List draft examples\n authoring-packet [goal] Emit the C3 packet for external-agent workflow authoring; no intent modeling or mutation\n intake <description> Review a workflow request and recommend reuse or authoring path\n route <description> Show the personal no-canvas route: describe, author, validate, run; use --org for organization rollout gates\n suggestions <asset> Suggest human-reviewed workflow improvements from evidence without mutation\n mine [asset] Find candidate workflow assets or follow-up drafts from real evidence\n draft <description> Clone a built-in example as a scaffold fallback; structure is not derived from the goal\n edit-plan <instruction> Preview a workflow edit without writing DNA or runtime files\n review-suggestion Persist accept/reject/defer for an evidence-backed suggestion\n improve-from-suggestions Build a reviewable edit plan from an accepted asset suggestion\n from-candidate <id> Act on a dogfood-backed candidate without copying a long command\n promote-candidate <path> Revalidate and explicitly copy exact candidate bytes into .dna/templates\n project-candidate <path> Dry-run an unpromoted candidate through Claude or Codex projection\n\nOptions:\n --name <org> Organization name for --org route planning\n --project <project> Project name for --org route planning\n --sync-target <target> Runtime surface for --org route planning, such as codex or claude\n --target claude|codex Projection target for project-candidate\n --dry-run Required for project-candidate; never writes project artifacts\n --org Include organization binding, configure, rollout, sync, deploy, improve, and evolution gates\n --from-template <name> Source workflow asset for route/edit/candidate commands\n --workflow <id> Select a workflow inside a multi-workflow asset template such as fix or behavior-lock\n --suggestion <id> Suggestion id such as template-model-verifier or template-blocked-triage\n --decision <decision> For review-suggestion: accept|reject|defer\n --save-template <name> Promotion target name, or reviewed draft/scaffold name for legacy commands\n --project-dir <path> Read/write workflow assets in another project directory\n --force Overwrite an existing saved workflow asset draft\n --operation <op> Optional override; otherwise auto-chosen from the instruction\n --target-step <id> Optional override; otherwise auto-chosen from the edit\n --role <role> Optional override; otherwise auto-chosen/defaulted from the edit\n\nExamples:\n dna workflow examples\n dna workflow authoring-packet "Create a code review workflow" --json\n dna workflow intake "Rewrite module with logic equivalence"\n dna workflow route "Rewrite module with logic equivalence"\n dna workflow route "Rewrite module with logic equivalence" --org --name "Project Admin" --project "Mobile Rewrite" --sync-target codex\n dna workflow project-candidate .dna/authoring/source.candidate.dna.yaml --target claude --dry-run\n dna workflow promote-candidate .dna/authoring/source.candidate.dna.yaml --save-template source-reviewed\n dna workflow suggestions ${WORKFLOW_ASSET_IDS.default} --json\n dna workflow mine ${WORKFLOW_ASSET_IDS.default} --json\n dna workflow draft "Rewrite module with logic equivalence" --example rewrite_governance --strictness high # scaffold fallback only\n dna workflow draft "Rewrite module with logic equivalence" --save-template team-rewrite-flow # scaffold fallback only\n dna workflow edit-plan "Add equivalence verifier after rewrite" --from-template team-rewrite-flow --save-template team-rewrite-flow-v2\n dna workflow review-suggestion --from-template ${WORKFLOW_ASSET_IDS.default} --workflow fix --suggestion template-model-verifier --decision accept\n dna workflow improve-from-suggestions --from-template ${WORKFLOW_ASSET_IDS.default} --workflow fix --suggestion template-model-verifier --save-template ${WORKFLOW_ASSET_IDS.default}-v2\n dna workflow from-candidate extract-diagnosis-review-${WORKFLOW_ASSET_IDS.default} --from-template ${WORKFLOW_ASSET_IDS.default} --save-template ${WORKFLOW_ASSET_IDS.rewriteDiagnosisReview}\n`,
229
277
  org: `Usage: dna org <intake|configure-plan|route|rollout|init|bind|status|review|receipt> [description] [options]\n\nSubcommands:\n intake [description] Plan organization/project setup and asset binding without writing files\n configure-plan List explicit dna init commands for declared assets missing project config\n route Show the organization operating route and rollout readiness without syncing\n rollout Review sync/downstream rollout readiness without syncing (alias-compatible with route)\n init Create local .dna/org.yaml with organization/project bindings\n bind Add or update a workflow asset binding for a project\n status Show local organization, projects, assets, sync targets, and evidence totals\n review Show manager-facing gaps and next actions\n receipt Emit intentdna.org_review_receipt.v1 from local org review and rollout data\n\nOptions:\n --name <organization> Organization name for intake or init\n --project <project> Project name/id for intake, init, or bind\n --asset <asset> Bind a workflow asset such as ${WORKFLOW_ASSET_IDS.default}\n --status <status> planned|active|paused for org bind (default planned)\n --sync-target <target> Declare a target such as codex or claude\n --project-dir <path> Read/write organization state in a different project directory\n --apply For configure-plan only: create missing .dna/configs from declared assets\n --json Print JSON to stdout\n\nBoundaries:\n - org receipt is local projection evidence only; cloud_authority=false\n - internal_ready candidates are adoption candidates, not org-private SaaS visibility\n - No online org auth, permissions, backend persistence, or cloud storage is implied\n\nExamples:\n dna org intake "Mobile rewrite team" --asset ${WORKFLOW_ASSET_IDS.default} --sync-target codex\n dna org configure-plan\n dna org configure-plan --apply\n dna org route\n dna org rollout\n dna org receipt --json\n dna org init --name "My Org" --project "Mobile Rewrite" --asset ${WORKFLOW_ASSET_IDS.default} --sync-target codex\n dna org bind --asset team-rewrite-flow-v2 --status active --sync-target codex\n dna org review\n`,
230
278
  release: `Usage: dna release <deploy-plan|evidence> [files...] [--json]\n\nSubcommands:\n deploy-plan Read changed files and decide whether Vercel and/or Supabase release verification is required\n evidence Record deploy-plan, deployed URL, checked routes, and cloud checks as intentdna.release_evidence.v1\n\nOptions:\n --json Print JSON to stdout\n --output <path> For evidence: write release evidence JSON (default .dna/release/latest-release-evidence.json)\n --deployment-url <url> For evidence: deployed URL captured from Vercel CLI stdout when web routes changed\n --checked-route <route> For evidence: route verified on the deployed URL (repeatable or comma-separated)\n --checked-cloud-check <id> For evidence: cloud check completed, such as cloud-contract-check or cloud-edge-check (repeatable or comma-separated)\n --dry-run For evidence: do not write the evidence file\n\nBoundaries:\n - CLI/core/runtime/docs/wiki changes do not require Vercel deployment by themselves\n - Web/public surfaces require Vercel CLI deploy only when reviewers or users need a deployed URL\n - Supabase SaaS surfaces require cloud contract, Edge Function, deploy-plan, remote secret-name presence, Vercel public env presence, function health, and live ingest-smoke evidence before production release claims\n - Deployment is separate from org rollout and runtime sync\n - A web deploy is not complete until the deployed URL is opened or fetched and verified\n - A cloud deploy is not complete until Supabase migrations/functions/secrets, Vercel public env presence, and ingestion smoke evidence are recorded\n - Release evidence records supplied checks; it does not run build, deploy, URL verification, Supabase commands, sync, or policy enforcement\n\nExamples:\n dna release deploy-plan\n dna release deploy-plan apps/online/src/main.ts\n dna release deploy-plan src/cli/index.ts --json\n dna release deploy-plan supabase/functions/ingest-report/index.ts --json\n dna release evidence apps/online/src/main.ts --deployment-url https://intentdna.vercel.app --checked-route / --checked-route '/?mode=public' --checked-route '/?mode=public&embed=1' --checked-route /runtime --checked-route /templates --checked-route /readiness --json\n dna release evidence supabase/functions/ingest-report/index.ts --checked-cloud-check cloud-contract-check --checked-cloud-check cloud-edge-check --checked-cloud-check cloud-deploy-plan --checked-cloud-check cloud-function-health --checked-cloud-check cloud-secrets-check --checked-cloud-check cloud-vercel-env-check --checked-cloud-check cloud-ingest-smoke --json\n`,
231
279
  auth: `Usage: dna auth status [--auth-config-dir <dir>] [--json]\n dna auth token set <token> [--auth-config-dir <dir>] [--json]\n dna auth token clear [--auth-config-dir <dir>] [--json]\n dna auth user-jwt status [--auth-config-dir <dir>] [--json]\n dna auth user-jwt set <jwt> [--auth-config-dir <dir>] [--json]\n dna auth user-jwt clear [--auth-config-dir <dir>] [--json]\n dna auth bootstrap-account [--org <name>] [--project <name>] [options]\n dna auth onboard [--project-id <id>] [options]\n dna auth issue-token --project-id <id> [options]\n dna auth projects [options]\n dna auth tokens list [options]\n dna auth tokens revoke <token-id> [--yes] [options]\n\nSubcommands:\n status Show local CLI API token status without network validation\n token set <token> Store a CLI API token in user config outside the project repository\n token clear Remove the stored CLI API token; idempotent if already absent\n user-jwt status Show local Supabase user JWT account bridge status without network validation\n user-jwt set <jwt> Store a Supabase user JWT for account onboarding commands outside the project repository\n user-jwt clear Remove the stored Supabase user JWT; idempotent if already absent\n bootstrap-account Create/reuse Supabase account org/project with a user JWT; token issuance remains dna auth onboard\n onboard Discover account projects, select one safely, issue a CLI API token, and store it locally\n issue-token Call Supabase issue-api-token with a user JWT and store the issued CLI token\n projects Call Supabase list-projects with a user JWT for account onboarding projection\n tokens list Call Supabase manage-api-tokens to list API token metadata only\n tokens revoke <token-id> Revoke an API token after explicit --yes confirmation\n\nOptions:\n --org <name> Organization name for account bootstrap\n --project <name> Project name for account bootstrap\n --org-slug <slug> Optional organization slug for account bootstrap\n --project-slug <slug> Optional project slug for account bootstrap\n --project-hint <path> Optional local project hint stored as account bootstrap metadata\n --project-id <id> Supabase projects.id for issue-token or token filtering\n --organization-id <id> Optional organization id to cross-check or filter\n --token-id <id> API token id for revoke or filtering\n --include-revoked Include revoked API token metadata in tokens list\n --limit <n> Maximum token summaries to request, capped at 100\n --user-jwt <jwt> Supabase user access token override; env or dna auth user-jwt set can also supply it\n --endpoint <url> Edge Function URL; defaults from INTENTDNA_CLOUD_URL\n --auth-config-dir <dir> Read/write CLI API token and user JWT config in a specific directory for isolated local checks\n --token-name <name> Display name for the issued CLI token\n --scope <scope> Requested scope; repeatable or comma-separated, defaults to report:ingest\n --expires-at <iso> Optional token expiry timestamp\n --dry-run Build the request without making a network call or storing a token\n --yes Required before live dna auth tokens revoke mutates account auth state\n --json Print JSON to stdout\n\nBoundaries:\n - CLI ingestion auth uses IntentDNA API tokens, not MCP authorization\n - dna auth bootstrap-account is an onboarding mutation only; it does not issue CLI tokens, upload reports, publish templates, or run workflows\n - dna auth onboard is a CLI onboarding convenience over projects plus issue-token; it does not guess between multiple projects\n - Supabase user JWTs are account onboarding credentials for projects, issue-token, and token management; they are not CLI ingestion API tokens\n - User JWTs are stored only by explicit dna auth user-jwt set and output uses masked previews only\n - Token status is local until cloud token verification exists\n - Raw tokens are not printed after set or issue-token; token list/revoke never prints raw tokens or token hashes\n - Service role keys are server-only and rejected as CLI/user tokens when detected\n - Auth state and account projections do not grant runtime allow/warn/block/validate authority\n\nExamples:\n dna auth status --json\n dna auth token set idna_example_token --json\n dna auth token set idna_example_token --auth-config-dir /tmp/intentdna-auth --json\n dna auth status --auth-config-dir /tmp/intentdna-auth --json\n dna auth token clear --json\n dna auth user-jwt status --json\n dna auth user-jwt set eyJhbGciOi... --json\n dna auth user-jwt clear --json\n dna auth bootstrap-account --org "My Org" --project "IntentDNA" --dry-run --json\n dna auth bootstrap-account --org "My Org" --project "IntentDNA" --json\n dna auth onboard --dry-run --json\n dna auth onboard --project-id 00000000-0000-4000-8000-000000000000 --json\n dna auth issue-token --project-id 00000000-0000-4000-8000-000000000000 --dry-run --json\n dna auth issue-token --project-id 00000000-0000-4000-8000-000000000000 --token-name "Laptop CLI" --json\n dna auth projects --dry-run --json\n dna auth tokens list --project-id 00000000-0000-4000-8000-000000000000 --dry-run --json\n dna auth tokens revoke token_123 --yes --json\n`,
@@ -235,6 +283,7 @@ Boundaries:
235
283
  context: `Usage: dna context <index|search|doc> [query|id] [files...]\n\nSubcommands:\n index List explicitly declared planning-only advisory context sources\n search <query> Search source metadata\n doc <id> Read a local file/doc source body\n`,
236
284
  run: `Usage:
237
285
  dna run [start] --dna <file> --workflow <name> --task <id> [options]
286
+ dna run asset-refinery --var source_packet=<path> --var candidate_path=<path> [--dry-run]
238
287
  dna run status <run-id> [--json] [--project-dir <path>]
239
288
  dna run inspect <run-id> [--json] [--project-dir <path>]
240
289
  dna run resume <run-id> [--json] [--project-dir <path>]
@@ -249,6 +298,7 @@ Actions:
249
298
 
250
299
  Options:
251
300
  --run-id <id> Explicit run id; lifecycle actions also accept a positional id
301
+ --var <key=value> Set a workflow variable; repeat for multiple values
252
302
  --dry-run Print the compiled Controller plan without starting a run
253
303
  --provider <name> claude|codex (default: claude)
254
304
  --provider-executable <path> Override the selected provider executable
@@ -483,13 +533,49 @@ async function main() {
483
533
  vars[v.slice(0, eqIdx)] = v.slice(eqIdx + 1);
484
534
  }
485
535
  }
536
+ let dnaFiles = runValues.dna ?? runPositionals;
537
+ let workflowName = runValues.workflow;
538
+ let taskId = runValues.task;
539
+ if (runAction === "start"
540
+ && !runValues.dna
541
+ && runPositionals.length === 1) {
542
+ const templateName = runPositionals[0];
543
+ const projectDir = runValues["project-dir"];
544
+ const { findTemplateCatalogEntry } = await import("../templates/catalog.js");
545
+ const template = await findTemplateCatalogEntry(templateName, {
546
+ projectDir,
547
+ });
548
+ if (template) {
549
+ if (template.name === "asset-refinery") {
550
+ const variableError = normalizeAssetRefineryVariables(resolve(projectDir ?? process.cwd()), vars);
551
+ if (variableError) {
552
+ process.stderr.write(`Error: ${variableError}\n`);
553
+ return exitWith(2);
554
+ }
555
+ }
556
+ dnaFiles = [template.path];
557
+ taskId ??= template.name;
558
+ if (!workflowName) {
559
+ const { loadDNA } = await import("../compiler/index.js");
560
+ const dna = await loadDNA(template.path);
561
+ const workflowNames = Object.keys(dna.workflows ?? {});
562
+ if (workflowNames.length !== 1) {
563
+ process.stderr.write(`Error: template '${template.name}' has ${workflowNames.length} workflows; select one with --workflow.\n`);
564
+ return exitWith(2);
565
+ }
566
+ workflowName = dna.namespace
567
+ ? `${dna.namespace}_${workflowNames[0]}`
568
+ : workflowNames[0];
569
+ }
570
+ }
571
+ }
486
572
  const { runRun } = await import("./commands/run.js");
487
573
  const code = await runRun({
488
574
  action: runAction,
489
575
  runId: optionRunId ?? positionalRunId,
490
- dnaFiles: runValues.dna ?? runPositionals,
491
- workflowName: runValues.workflow,
492
- taskId: runValues.task,
576
+ dnaFiles,
577
+ workflowName,
578
+ taskId,
493
579
  context: runValues.context,
494
580
  vars: Object.keys(vars).length > 0 ? vars : undefined,
495
581
  dryRun: runValues["dry-run"],
@@ -869,6 +955,9 @@ async function main() {
869
955
  "source-dir": { type: "string", multiple: true },
870
956
  output: { type: "string", short: "o" },
871
957
  focus: { type: "string", default: "all" },
958
+ snapshot: { type: "boolean", default: false },
959
+ packet: { type: "boolean", default: false },
960
+ write: { type: "string" },
872
961
  candidate: { type: "boolean", default: false },
873
962
  "save-template": { type: "string" },
874
963
  org: { type: "string" },
@@ -907,6 +996,21 @@ async function main() {
907
996
  return exitWith(code);
908
997
  }
909
998
  if (subcommand === "sources") {
999
+ if (templateValues.snapshot
1000
+ || templateValues.packet
1001
+ || templateValues.write) {
1002
+ const { runSourceAssets } = await import("./commands/source-assets.js");
1003
+ const code = await runSourceAssets({
1004
+ projectDir: templateValues["project-dir"],
1005
+ sourceDirs: templateValues["source-dir"],
1006
+ sourceId: templatePositionals[0],
1007
+ snapshot: templateValues.snapshot,
1008
+ packet: templateValues.packet,
1009
+ write: templateValues.write,
1010
+ json: templateValues.json,
1011
+ });
1012
+ return exitWith(code);
1013
+ }
910
1014
  const focus = templateValues.focus;
911
1015
  if (!["all", "refresh", "gaps"].includes(focus)) {
912
1016
  process.stderr.write("Error: --focus must be one of: all, refresh, gaps\n");
@@ -1024,7 +1128,9 @@ async function main() {
1024
1128
  name: { type: "string" },
1025
1129
  project: { type: "string" },
1026
1130
  "sync-target": { type: "string" },
1131
+ target: { type: "string" },
1027
1132
  "project-dir": { type: "string" },
1133
+ "dry-run": { type: "boolean", default: false },
1028
1134
  force: { type: "boolean", default: false },
1029
1135
  org: { type: "boolean", default: false },
1030
1136
  json: { type: "boolean", default: false },
@@ -1033,6 +1139,56 @@ async function main() {
1033
1139
  allowPositionals: true,
1034
1140
  strict: false,
1035
1141
  });
1142
+ const projectDir = workflowValues["project-dir"] ?? process.cwd();
1143
+ if (subcommand === "promote-candidate") {
1144
+ const candidatePath = workflowPositionals[0];
1145
+ const templateName = workflowValues["save-template"];
1146
+ if (!candidatePath || !templateName) {
1147
+ process.stderr.write("Error: promote-candidate requires <candidate-path> and --save-template <name>.\n");
1148
+ return exitWith(2);
1149
+ }
1150
+ try {
1151
+ const { promoteCandidate } = await import("./commands/candidate-promotion.js");
1152
+ const result = await promoteCandidate({
1153
+ projectDir,
1154
+ candidatePath,
1155
+ templateName,
1156
+ });
1157
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
1158
+ return exitWith(0);
1159
+ }
1160
+ catch (error) {
1161
+ process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\n`);
1162
+ return exitWith(1);
1163
+ }
1164
+ }
1165
+ if (subcommand === "project-candidate") {
1166
+ const candidatePath = workflowPositionals[0];
1167
+ const target = workflowValues.target;
1168
+ if (!candidatePath || !["claude", "codex"].includes(target ?? "")) {
1169
+ process.stderr.write("Error: project-candidate requires <candidate-path> and --target claude|codex.\n");
1170
+ return exitWith(2);
1171
+ }
1172
+ if (!workflowValues["dry-run"]) {
1173
+ process.stderr.write("Error: project-candidate requires --dry-run.\n");
1174
+ return exitWith(2);
1175
+ }
1176
+ try {
1177
+ const { projectCandidateDryRun } = await import("../workflow/candidate-projection.js");
1178
+ const result = await projectCandidateDryRun({
1179
+ candidatePath,
1180
+ projectDir,
1181
+ target: target,
1182
+ packageVersion: version,
1183
+ });
1184
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
1185
+ return exitWith(0);
1186
+ }
1187
+ catch (error) {
1188
+ process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\n`);
1189
+ return exitWith(1);
1190
+ }
1191
+ }
1036
1192
  const { runWorkflow } = await import("./commands/workflow.js");
1037
1193
  const code = await runWorkflow({
1038
1194
  subcommand,
@@ -251,7 +251,7 @@ export function cascadeDNA(layers) {
251
251
  const ns = dna.namespace;
252
252
  // Merge genes (cross-namespace: additive merge + warning)
253
253
  const origin = originForDNA(dna);
254
- for (const [name, rawGene] of Object.entries(dna.genes)) {
254
+ for (const [name, rawGene] of Object.entries(dna.genes ?? {})) {
255
255
  const gene = withCodonOrigins(rawGene, origin);
256
256
  if (origin.protected_threshold && gene.codons.some((codon) => codon.type === "threshold")) {
257
257
  protectedThresholdGenes.add(name);
@@ -19,6 +19,11 @@ import type { HookEvent, HookOutput } from "./protocol.js";
19
19
  import { blockOutput } from "./protocol.js";
20
20
  import { readWorkflowState } from "./state.js";
21
21
  import type { VerifierResultEntry } from "./state.js";
22
+ interface HookRuntimePaths {
23
+ projectDir: string;
24
+ irPath: string;
25
+ }
26
+ declare function resolveHookRuntimePaths(cwd: string, irPath?: string, explicitIRPath?: boolean): Promise<HookRuntimePaths>;
22
27
  export interface SessionSummary {
23
28
  total: number;
24
29
  blocks: number;
@@ -91,6 +96,7 @@ export declare const recordProducedArtifactsForTest: typeof recordProducedArtifa
91
96
  export declare const resolveWorkflowArtifactFactsForTest: typeof resolveWorkflowArtifactFacts;
92
97
  export declare const readWorkflowStateForHookTest: typeof readWorkflowStateForHook;
93
98
  export declare const runCodexHookWithLoadedIrForTest: typeof runCodexHookWithLoadedIr;
99
+ export declare const resolveHookRuntimePathsForTest: typeof resolveHookRuntimePaths;
94
100
  export declare const inferWorkflowAssetFromIrForTest: typeof inferWorkflowAssetFromIr;
95
101
  export declare const resolveWorkflowArtifactFactsForHookTest: typeof resolveWorkflowArtifactFactsForHook;
96
102
  export declare const finalizeAndResolveArtifactsForHookTest: typeof finalizeAndResolveArtifactsForHook;