@khoavu882/doflow 2.0.0-beta.3 → 2.0.0-beta.5

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 (236) hide show
  1. package/bin/doflow.js +2 -2
  2. package/core/.antigravity-plugin/plugin.json +0 -1
  3. package/core/.claude-plugin/plugin.json +1 -1
  4. package/core/.codex-plugin/plugin.json +1 -1
  5. package/core/.plugin/plugin.json +1 -1
  6. package/core/harnesses/claude/hooks/mcp-tool-guard.sh +7 -17
  7. package/core/harnesses/claude/hooks/pre-bash-guard.sh +7 -17
  8. package/core/harnesses/claude/hooks/pre-implement-gate.sh +7 -18
  9. package/core/harnesses/claude/hooks/session-start.sh +5 -3
  10. package/core/harnesses/claude/hooks/stop-check.sh +5 -3
  11. package/core/harnesses/claude/hooks/subagent-audit.sh +5 -5
  12. package/core/harnesses/claude/hooks/user-prompt-submit.sh +5 -3
  13. package/core/harnesses/codex/hooks/mcp-tool-guard.sh +7 -15
  14. package/core/harnesses/codex/hooks/pre-bash-guard.sh +7 -15
  15. package/core/harnesses/codex/hooks/pre-implement-gate.sh +7 -16
  16. package/core/harnesses/codex/hooks/session-start.sh +5 -3
  17. package/core/harnesses/codex/hooks/stop-check.sh +6 -5
  18. package/core/harnesses/codex/hooks/subagent-audit.sh +17 -6
  19. package/core/harnesses/codex/hooks/user-prompt-submit.sh +5 -4
  20. package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +7 -18
  21. package/core/harnesses/gemini/hooks/pre-bash-guard.sh +7 -18
  22. package/core/harnesses/gemini/hooks/pre-implement-gate.sh +7 -19
  23. package/core/harnesses/gemini/hooks/session-start.sh +5 -3
  24. package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +20 -5
  25. package/core/harnesses/kiro/hooks/pre-bash-guard.sh +20 -5
  26. package/core/harnesses/kiro/hooks/pre-implement-gate.sh +20 -5
  27. package/core/harnesses/kiro/hooks/session-start.sh +11 -5
  28. package/core/harnesses/kiro/hooks/stop-check.sh +21 -30
  29. package/core/harnesses/pi/extensions/doflow-guard.js +159 -0
  30. package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +27 -11
  31. package/core/harnesses/shared/hooks/adapters/antigravity.js +14 -57
  32. package/core/harnesses/shared/hooks/bridge/bridge.sh +184 -0
  33. package/core/harnesses/shared/hooks/bridge/envelope.jq +60 -0
  34. package/core/harnesses/shared/hooks/bridge/respond.sh +62 -0
  35. package/core/harnesses/shared/hooks/policies/capture-failure.sh +4 -2
  36. package/core/harnesses/shared/hooks/policies/contract.sh +75 -0
  37. package/core/harnesses/shared/hooks/policies/deny-json.sh +39 -4
  38. package/core/harnesses/shared/hooks/policies/lib.sh +4 -2
  39. package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +46 -34
  40. package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +75 -25
  41. package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +67 -73
  42. package/core/harnesses/shared/hooks/policies/session-context.sh +37 -7
  43. package/core/harnesses/shared/hooks/policies/stop-check.sh +42 -15
  44. package/core/harnesses/shared/hooks/policies/subagent-audit.sh +35 -5
  45. package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +40 -7
  46. package/core/harnesses/shared/hooks/stream-hook-runner.js +69 -181
  47. package/core/registry/assets.json +19 -0
  48. package/core/registry/contracts.json +2 -2
  49. package/core/registry/harnesses.json +196 -10
  50. package/core/registry/lifecycle.json +2626 -7
  51. package/core/registry/storage.json +110 -83
  52. package/core/registry/verification.json +1 -1
  53. package/core/shared/scripts/doflow/bash/render-puml.sh +2 -2
  54. package/core/shared/scripts/doflow/bin/doflow-run +10 -6
  55. package/core/shared/skills/do-design/SKILL.md +6 -6
  56. package/core/shared/skills/do-execute-plan/SKILL.md +11 -10
  57. package/core/shared/skills/do-execute-plan/references/readiness_gate.md +2 -0
  58. package/core/shared/skills/do-execute-plan/references/scaffold.md +2 -2
  59. package/core/shared/skills/do-implement/SKILL.md +3 -2
  60. package/core/shared/skills/do-plan/SKILL.md +7 -8
  61. package/package.json +4 -2
  62. package/src/adapters/antigravity/{index.ts → index.js} +141 -105
  63. package/src/adapters/claude/{index.ts → index.js} +63 -49
  64. package/src/adapters/claude/{mcp.ts → mcp.js} +16 -16
  65. package/src/adapters/codex/{agents.ts → agents.js} +24 -24
  66. package/src/adapters/codex/{config.ts → config.js} +49 -49
  67. package/src/adapters/codex/{hooks.ts → hooks.js} +62 -27
  68. package/src/adapters/codex/{index.ts → index.js} +148 -105
  69. package/src/adapters/codex/{mcp.ts → mcp.js} +38 -38
  70. package/src/adapters/codex/{native-projection.ts → native-projection.js} +5 -5
  71. package/src/adapters/copilot/{index.ts → index.js} +42 -42
  72. package/src/adapters/{copy-tree.ts → copy-tree.js} +74 -74
  73. package/src/adapters/gemini/{hooks.ts → hooks.js} +78 -37
  74. package/src/adapters/gemini/{index.ts → index.js} +96 -57
  75. package/src/adapters/{hook-commands.ts → hook-commands.js} +8 -8
  76. package/src/adapters/{index.ts → index.js} +13 -13
  77. package/src/adapters/kiro/{index.ts → index.js} +49 -35
  78. package/src/adapters/{mcp-entries.ts → mcp-entries.js} +55 -55
  79. package/src/adapters/opencode/{index.ts → index.js} +52 -52
  80. package/src/adapters/pi/hooks.js +119 -0
  81. package/src/adapters/pi/{index.ts → index.js} +64 -39
  82. package/src/adapters/pi/{json-members.ts → json-members.js} +23 -23
  83. package/src/adapters/pi/{mcp.ts → mcp.js} +70 -70
  84. package/src/adapters/{resolve-source.ts → resolve-source.js} +1 -1
  85. package/src/cli/commands/{install.ts → install.js} +25 -25
  86. package/src/cli/commands/{list-backups.ts → list-backups.js} +4 -4
  87. package/src/cli/commands/{reconcile.ts → reconcile.js} +19 -19
  88. package/src/cli/commands/{remove.ts → remove.js} +10 -10
  89. package/src/cli/commands/{rollback.ts → rollback.js} +17 -17
  90. package/src/cli/commands/{self-update.ts → self-update.js} +3 -3
  91. package/src/cli/commands/{status.ts → status.js} +24 -24
  92. package/src/cli/commands/{tools.ts → tools.js} +16 -16
  93. package/src/cli/commands/{update.ts → update.js} +20 -20
  94. package/src/cli/{index.ts → index.js} +43 -31
  95. package/src/cli/{runtime-commands.ts → runtime-commands.js} +97 -58
  96. package/src/cli/{shared.ts → shared.js} +40 -40
  97. package/src/helper/{bash-helper.ts → bash-helper.js} +7 -7
  98. package/src/helper/{git.ts → git.js} +2 -2
  99. package/src/helper/{guardrails.ts → guardrails.js} +10 -10
  100. package/src/helper/{harness-paths.ts → harness-paths.js} +24 -24
  101. package/src/helper/{marker-merge.ts → marker-merge.js} +4 -4
  102. package/src/helper/{prompt.ts → prompt.js} +5 -5
  103. package/src/helper/{repo-root.ts → repo-root.js} +2 -2
  104. package/src/helper/{settings-merge.ts → settings-merge.js} +27 -27
  105. package/src/helper/{settings-scope.ts → settings-scope.js} +3 -3
  106. package/src/helper/{toml.ts → toml.js} +48 -48
  107. package/src/install/{backup-set.ts → backup-set.js} +16 -16
  108. package/src/install/{backup.ts → backup.js} +65 -65
  109. package/src/install/{context.ts → context.js} +15 -15
  110. package/src/install/{manifest.ts → manifest.js} +27 -27
  111. package/src/install/{mcp.ts → mcp.js} +30 -30
  112. package/src/install/{paths.ts → paths.js} +15 -15
  113. package/src/install/{targets.ts → targets.js} +10 -10
  114. package/src/install/{tool-lifecycle.ts → tool-lifecycle.js} +20 -20
  115. package/src/lifecycle/{bash-availability.ts → bash-availability.js} +1 -1
  116. package/src/lifecycle/coverage.js +57 -0
  117. package/src/lifecycle/hook-step.js +61 -0
  118. package/src/lifecycle/{index.ts → index.js} +160 -146
  119. package/src/lifecycle/{mcp-index.ts → mcp-index.js} +1 -1
  120. package/src/lifecycle/{policies.ts → policies.js} +11 -11
  121. package/src/lifecycle/{view.ts → view.js} +28 -28
  122. package/src/registry/{index.ts → index.js} +195 -66
  123. package/src/release/build-package.js +91 -0
  124. package/src/release/changelog-section.js +70 -0
  125. package/src/release/{sync-plugin-versions.ts → sync-plugin-versions.js} +4 -4
  126. package/src/runtime/advice/{cli.ts → cli.js} +31 -31
  127. package/src/runtime/advice/{client.ts → client.js} +44 -44
  128. package/src/runtime/advice/{config.ts → config.js} +16 -16
  129. package/src/runtime/advice/{doctor.ts → doctor.js} +32 -32
  130. package/src/runtime/advice/{endpoint-guard.ts → endpoint-guard.js} +20 -20
  131. package/src/runtime/advice/{kinds.ts → kinds.js} +16 -16
  132. package/src/runtime/advice/providers/{ollama-systemone.ts → ollama-systemone.js} +21 -21
  133. package/src/runtime/advice/{record.ts → record.js} +19 -19
  134. package/src/runtime/advice/{registry.ts → registry.js} +16 -16
  135. package/src/runtime/advice/{strength.ts → strength.js} +7 -7
  136. package/src/runtime/{brief-tools.ts → brief-tools.js} +50 -50
  137. package/src/runtime/{c4-project.ts → c4-project.js} +27 -27
  138. package/src/runtime/candidate.js +505 -0
  139. package/src/runtime/{capability-router.ts → capability-router.js} +39 -39
  140. package/src/runtime/{checkouts.ts → checkouts.js} +21 -21
  141. package/src/runtime/{claims.ts → claims.js} +44 -35
  142. package/src/runtime/{cli-result.ts → cli-result.js} +15 -8
  143. package/src/runtime/{cli.ts → cli.js} +59 -292
  144. package/src/runtime/{command-detect.ts → command-detect.js} +47 -47
  145. package/src/runtime/context/budget.js +203 -0
  146. package/src/runtime/context/compose.js +1367 -0
  147. package/src/runtime/context/delivery-store.js +102 -0
  148. package/src/runtime/context/mandatory.js +594 -0
  149. package/src/runtime/context/pointer.js +142 -0
  150. package/src/runtime/context-pack.js +1277 -0
  151. package/src/runtime/{decision-register.ts → decision-register.js} +67 -67
  152. package/src/runtime/evidence-fields.js +39 -0
  153. package/src/runtime/{evidence-ledger.ts → evidence-ledger.js} +32 -25
  154. package/src/runtime/failure/{capture.ts → capture.js} +29 -29
  155. package/src/runtime/failure/{classifier.ts → classifier.js} +4 -4
  156. package/src/runtime/failure/{cli.ts → cli.js} +30 -30
  157. package/src/runtime/failure/{home.ts → home.js} +6 -6
  158. package/src/runtime/failure/{store.ts → store.js} +32 -32
  159. package/src/runtime/{feature-resolve.ts → feature-resolve.js} +6 -6
  160. package/src/runtime/{freshness.ts → freshness.js} +59 -22
  161. package/src/runtime/{health.ts → health.js} +72 -72
  162. package/src/runtime/{history-compactor.ts → history-compactor.js} +25 -25
  163. package/src/runtime/{implementation-gate.ts → implementation-gate.js} +30 -30
  164. package/src/runtime/inventory/{coverage.ts → coverage.js} +10 -10
  165. package/src/runtime/inventory/{identity.ts → identity.js} +2 -2
  166. package/src/runtime/inventory/{index.ts → index.js} +36 -36
  167. package/src/runtime/inventory/{precedence.ts → precedence.js} +2 -2
  168. package/src/runtime/inventory/{read-scopes.ts → read-scopes.js} +9 -9
  169. package/src/runtime/inventory/{siblings.ts → siblings.js} +15 -15
  170. package/src/runtime/knowledge/{chunker.ts → chunker.js} +12 -12
  171. package/src/runtime/knowledge/{hybrid.ts → hybrid.js} +17 -17
  172. package/src/runtime/knowledge/{index-store.ts → index-store.js} +23 -23
  173. package/src/runtime/knowledge/{retrieval.ts → retrieval.js} +27 -27
  174. package/src/runtime/{leak-scan.ts → leak-scan.js} +12 -12
  175. package/src/runtime/lifecycle/{cli.ts → cli.js} +32 -32
  176. package/src/runtime/lifecycle/{event-store.ts → event-store.js} +48 -48
  177. package/src/runtime/lifecycle/{fold.ts → fold.js} +41 -41
  178. package/src/runtime/lifecycle/{followup.ts → followup.js} +54 -54
  179. package/src/runtime/lifecycle/{goal.ts → goal.js} +50 -50
  180. package/src/runtime/lifecycle/{intent-writer.ts → intent-writer.js} +5 -5
  181. package/src/runtime/lifecycle/{overview.ts → overview.js} +47 -47
  182. package/src/runtime/lifecycle/{release.ts → release.js} +29 -29
  183. package/src/runtime/lifecycle/{report-store.ts → report-store.js} +25 -25
  184. package/src/runtime/lifecycle/{retention.ts → retention.js} +37 -37
  185. package/src/runtime/lifecycle/{root.ts → root.js} +9 -9
  186. package/src/runtime/lifecycle/{status.ts → status.js} +64 -64
  187. package/src/runtime/lifecycle/{store-upkeep.ts → store-upkeep.js} +46 -46
  188. package/src/runtime/{locator-resolve.ts → locator-resolve.js} +7 -7
  189. package/src/runtime/{mask.ts → mask.js} +22 -22
  190. package/src/runtime/{mcp-consultations.ts → mcp-consultations.js} +54 -54
  191. package/src/runtime/{mcp-reach.ts → mcp-reach.js} +24 -24
  192. package/src/runtime/{model-router.ts → model-router.js} +20 -20
  193. package/src/runtime/{outcome.ts → outcome.js} +27 -27
  194. package/src/runtime/{reach.ts → reach.js} +21 -21
  195. package/src/runtime/{readiness-record.ts → readiness-record.js} +12 -12
  196. package/src/runtime/{readiness.ts → readiness.js} +113 -43
  197. package/src/runtime/{recovery.ts → recovery.js} +15 -15
  198. package/src/runtime/{research-request.ts → research-request.js} +34 -34
  199. package/src/runtime/{retrieval-plan.ts → retrieval-plan.js} +453 -104
  200. package/src/runtime/retrieval-record.js +223 -0
  201. package/src/runtime/scaffold/{artifacts.ts → artifacts.js} +22 -19
  202. package/src/runtime/scaffold/{fingerprint.ts → fingerprint.js} +3 -3
  203. package/src/runtime/scaffold/{generate.ts → generate.js} +109 -109
  204. package/src/runtime/scaffold/{languages.ts → languages.js} +34 -34
  205. package/src/runtime/source-version.js +289 -0
  206. package/src/runtime/storage/{cli.ts → cli.js} +9 -9
  207. package/src/runtime/storage/{engine.ts → engine.js} +37 -37
  208. package/src/runtime/storage/{registry.ts → registry.js} +19 -19
  209. package/src/runtime/storage/{roots.ts → roots.js} +8 -8
  210. package/src/runtime/storage/{rules.ts → rules.js} +26 -26
  211. package/src/runtime/storage/{upkeep.ts → upkeep.js} +35 -35
  212. package/src/runtime/storage/{work-status.ts → work-status.js} +39 -39
  213. package/src/runtime/{task-attempts.ts → task-attempts.js} +62 -62
  214. package/src/runtime/{task-classifier.ts → task-classifier.js} +34 -34
  215. package/src/runtime/{task-scope.ts → task-scope.js} +40 -15
  216. package/src/runtime/{task-state.ts → task-state.js} +86 -21
  217. package/src/runtime/trace/{indicators.ts → indicators.js} +22 -22
  218. package/src/runtime/trace/{ledger.ts → ledger.js} +42 -42
  219. package/src/runtime/trace/{render.ts → render.js} +7 -7
  220. package/src/runtime/trace/{views.ts → views.js} +40 -40
  221. package/src/runtime/verification/{contract-runner.ts → contract-runner.js} +23 -23
  222. package/src/runtime/verification/{engine.ts → engine.js} +143 -143
  223. package/src/runtime/verification/{registry.ts → registry.js} +6 -6
  224. package/src/runtime/verification/{scope-bound.ts → scope-bound.js} +50 -50
  225. package/src/runtime/verification/{test-selection.ts → test-selection.js} +28 -28
  226. package/src/runtime/{workflow-engine.ts → workflow-engine.js} +50 -50
  227. package/src/runtime/{workflow-orchestrator.ts → workflow-orchestrator.js} +91 -91
  228. package/src/runtime/{worktree.ts → worktree.js} +31 -31
  229. package/src/state/{index.ts → index.js} +26 -26
  230. package/src/state/{lockfile.ts → lockfile.js} +20 -20
  231. package/src/state/{run-lock.ts → run-lock.js} +44 -44
  232. package/core/.antigravity-plugin/hooks.json +0 -39
  233. package/core/harnesses/shared/hooks/adapters/arg-fields.js +0 -19
  234. package/core/harnesses/shared/hooks/adapters/gemini.js +0 -114
  235. package/core/harnesses/shared/hooks/hooks.json +0 -47
  236. package/src/runtime/context-pack.ts +0 -492
@@ -12,12 +12,12 @@ const fs = require('node:fs');
12
12
  const path = require('node:path');
13
13
  const crypto = require('node:crypto');
14
14
 
15
- function sha256(buf: any): string {
15
+ function sha256(buf ) {
16
16
  return crypto.createHash('sha256').update(buf).digest('hex');
17
17
  }
18
18
 
19
- function walkRelFiles(dir: string, fsImpl: any): string[] {
20
- const out: string[] = [];
19
+ function walkRelFiles(dir , fsImpl ) {
20
+ const out = [];
21
21
  for (const entry of fsImpl.readdirSync(dir, { withFileTypes: true })) {
22
22
  const abs = path.join(dir, entry.name);
23
23
  if (entry.isDirectory()) out.push(...walkRelFiles(abs, fsImpl).map((rel) => path.join(entry.name, rel)));
@@ -33,29 +33,29 @@ function walkRelFiles(dir: string, fsImpl: any): string[] {
33
33
  * branched on inside an adapter, so the shape stays visible next to the asset it applies to.
34
34
  * @type {Record<string, (sourceRel: string) => string>}
35
35
  */
36
- const LAYOUTS: Record<string, (sourceRel: string) => string> = {
36
+ const LAYOUTS = {
37
37
  /** `spec-analyst.md` -> `spec-analyst/agent.md` */
38
- 'dir-per-file:agent.md': (sourceRel: string) => {
38
+ 'dir-per-file:agent.md': (sourceRel ) => {
39
39
  const ext = path.extname(sourceRel);
40
40
  return path.join(sourceRel.slice(0, sourceRel.length - ext.length), `agent${ext || '.md'}`);
41
41
  },
42
42
  /** `RULE_01_SAFETY.md` -> `RULE_01_SAFETY.instructions.md` — GitHub's path-specific
43
43
  * instructions are discovered by their `.instructions.md` suffix. */
44
- 'instructions-md': (sourceRel: string) => {
44
+ 'instructions-md': (sourceRel ) => {
45
45
  const ext = path.extname(sourceRel);
46
46
  return `${sourceRel.slice(0, sourceRel.length - ext.length)}.instructions.md`;
47
47
  },
48
48
  /** `MODE_Orchestration.md` -> `doflow-orchestration.md` — Claude's /config picker lists
49
49
  * output styles by file name; the doflow- prefix keeps DoFlow's styles grouped and
50
50
  * non-colliding with anything a user authors by hand. */
51
- 'doflow-output-style': (sourceRel: string) => {
51
+ 'doflow-output-style': (sourceRel ) => {
52
52
  const ext = path.extname(sourceRel);
53
53
  const base = path.basename(sourceRel, ext).replace(/^MODE_/i, '').replace(/_/g, '-').toLowerCase();
54
54
  return `doflow-${base}${ext || '.md'}`;
55
55
  },
56
56
  /** `core-implementer.md` -> `core-implementer.json` — kiro-cli discovers custom agents as
57
57
  * `.kiro/agents/<name>.json` (2.27.1 `kiro-cli agent list` did not list `.md` agents). */
58
- 'kiro-agent-json': (sourceRel: string) => {
58
+ 'kiro-agent-json': (sourceRel ) => {
59
59
  const ext = path.extname(sourceRel);
60
60
  return `${sourceRel.slice(0, sourceRel.length - ext.length)}.json`;
61
61
  },
@@ -63,8 +63,8 @@ const LAYOUTS: Record<string, (sourceRel: string) => string> = {
63
63
 
64
64
  /** Resolve a declared layout name to its mapper. Unknown names fail loudly rather than silently
65
65
  * mirroring, because a typo would otherwise install to the wrong shape and look like it worked. */
66
- function resolveLayout(name: any): (sourceRel: string) => string {
67
- if (!name) return (rel: string) => rel;
66
+ function resolveLayout(name ) {
67
+ if (!name) return (rel ) => rel;
68
68
  const layout = LAYOUTS[name];
69
69
  if (!layout) throw new Error(`Unknown copy-tree layout '${name}' (known: ${Object.keys(LAYOUTS).join(', ')})`);
70
70
  return layout;
@@ -79,39 +79,39 @@ function resolveLayout(name: any): (sourceRel: string) => string {
79
79
  * applyTree can re-derive the written bytes from the untouched source at any later retry.
80
80
  * @type {Record<string, (sourceRel: string, content: Buffer) => Buffer>}
81
81
  */
82
- const TRANSFORMS: Record<string, (sourceRel: string, content: any) => any> = {
82
+ const TRANSFORMS = {
83
83
  /** Shared mode doc -> Claude output style with name/description/keep-coding-instructions */
84
84
  'claude-output-styles': renderClaudeOutputStyle,
85
85
  /** `<rule>.md` -> `<rule>.instructions.md` body under a Copilot applyTo header */
86
- 'copilot-rule-instructions': (sourceRel: string, content: any) => {
86
+ 'copilot-rule-instructions': (sourceRel , content ) => {
87
87
  void sourceRel;
88
88
  return Buffer.from(`---\napplyTo: '**'\n---\n\n${stripFrontmatter(content.toString('utf8'))}`);
89
89
  },
90
90
  /** Shared agent spec frontmatter -> only the keys Gemini CLI's subagent schema accepts */
91
- 'gemini-agents': (sourceRel: string, content: any) => {
91
+ 'gemini-agents': (sourceRel , content ) => {
92
92
  void sourceRel;
93
93
  return Buffer.from(filterGeminiAgentFrontmatter(content.toString('utf8')));
94
94
  },
95
95
  /** Shared agent spec frontmatter -> OpenCode's markdown-agent vocabulary */
96
- 'opencode-agents': (sourceRel: string, content: any) => {
96
+ 'opencode-agents': (sourceRel , content ) => {
97
97
  void sourceRel;
98
98
  return Buffer.from(renderOpencodeAgent(content.toString('utf8')));
99
99
  },
100
100
  /** Shared agent spec -> kiro-cli custom agent JSON (name, description, prompt, tools, hooks) */
101
- 'kiro-agents': (sourceRel: string, content: any) => {
101
+ 'kiro-agents': (sourceRel , content ) => {
102
102
  void sourceRel;
103
103
  return Buffer.from(renderKiroAgent(content.toString('utf8')));
104
104
  },
105
105
  };
106
106
 
107
- function resolveTransform(name: any): ((sourceRel: string, content: any) => any) | null {
107
+ function resolveTransform(name ) {
108
108
  if (!name) return null;
109
109
  const transform = TRANSFORMS[name];
110
110
  if (!transform) throw new Error(`Unknown copy-tree transform '${name}' (known: ${Object.keys(TRANSFORMS).join(', ')})`);
111
111
  return transform;
112
112
  }
113
113
 
114
- function stripFrontmatter(text: string): string {
114
+ function stripFrontmatter(text ) {
115
115
  if (!text.startsWith('---')) return text;
116
116
  const end = text.indexOf('\n---', 3);
117
117
  if (end === -1) return text;
@@ -122,7 +122,7 @@ function stripFrontmatter(text: string): string {
122
122
  * built-in engineering instructions (these styles shape HOW DoFlow works, not WHETHER Claude
123
123
  * codes). The description is lifted from the document's first `**Purpose**` line so the /config
124
124
  * picker explains each style without opening it. */
125
- function renderClaudeOutputStyle(sourceRel: string, content: any): string {
125
+ function renderClaudeOutputStyle(sourceRel , content ) {
126
126
  const text = Buffer.isBuffer(content) ? content.toString('utf8') : String(content);
127
127
  const base = path.basename(sourceRel).replace(/\.[^.]+$/, '').replace(/^MODE_/i, '').replace(/_/g, ' ');
128
128
  const purpose = text.match(/\*\*Purpose\*\*[:*]*\s*(.+)?/);
@@ -137,11 +137,11 @@ const GEMINI_AGENT_KEYS = new Set(['name', 'description', 'kind', 'tools', 'mcpS
137
137
  /** Keep only Gemini's documented top-level frontmatter keys, with their indented continuation
138
138
  * lines, in source order. The body after the closing `---` passes through byte for byte, and a
139
139
  * file without frontmatter is returned unchanged. */
140
- function filterGeminiAgentFrontmatter(text: string): string {
140
+ function filterGeminiAgentFrontmatter(text ) {
141
141
  const match = text.match(/^---\r?\n([\s\S]*?\r?\n)---(?=\r?\n|$)/);
142
142
  if (!match) return text;
143
143
  let keep = false;
144
- const kept = match[1].split(/(?<=\n)/).filter((line: string) => {
144
+ const kept = match[1].split(/(?<=\n)/).filter((line ) => {
145
145
  const key = line.match(/^([A-Za-z][A-Za-z0-9_-]*):/);
146
146
  if (key) keep = GEMINI_AGENT_KEYS.has(key[1]);
147
147
  return keep;
@@ -152,12 +152,12 @@ function filterGeminiAgentFrontmatter(text: string): string {
152
152
  /** The command a Kiro hook runs for one front-door script: the project copy when the git root
153
153
  * holds one, else the user-scope copy under $HOME. One builder, so the static `doflow` agent
154
154
  * (core/harnesses/kiro/agents/doflow.json) and every generated specialist share the exact string. */
155
- function kiroHookCommand(script: string): string {
155
+ function kiroHookCommand(script ) {
156
156
  return `bash "$(root=$(git rev-parse --show-toplevel 2>/dev/null) && [ -f "$root/.kiro/hooks/${script}" ] && printf '%s/.kiro/hooks/${script}' "$root" || printf '%s/.kiro/hooks/${script}' "$HOME")"`;
157
157
  }
158
158
 
159
159
  /** One kiro-cli agent hook entry. */
160
- function kiroHook({ name, trigger, matcher, script, timeout }: { name: string, trigger: string, matcher?: string, script: string, timeout: number }): any {
160
+ function kiroHook({ name, trigger, matcher, script, timeout } ) {
161
161
  return {
162
162
  name, trigger, ...(matcher ? { matcher } : {}),
163
163
  action: { type: 'command', command: kiroHookCommand(script) }, timeout,
@@ -168,7 +168,7 @@ function kiroHook({ name, trigger, matcher, script, timeout }: { name: string, t
168
168
  * "*" matcher: Kiro documents "@<server>" (every tool of one server) and "*" but no all-MCP
169
169
  * matcher, and mcp-tool-guard.sh exits 0 immediately for any non-MCP tool name, so "*" costs one
170
170
  * fast no-op for built-in tools and still covers every MCP server. */
171
- function kiroPreToolUseHooks(): any[] {
171
+ function kiroPreToolUseHooks() {
172
172
  return [
173
173
  kiroHook({ name: 'doflow-pre-implementation-gate', trigger: 'preToolUse', matcher: 'write', script: 'pre-implement-gate.sh', timeout: 10 }),
174
174
  kiroHook({ name: 'doflow-pre-bash-guard', trigger: 'preToolUse', matcher: 'shell', script: 'pre-bash-guard.sh', timeout: 10 }),
@@ -178,8 +178,8 @@ function kiroPreToolUseHooks(): any[] {
178
178
 
179
179
  /** Map one core/shared agent-spec file onto a kiro-cli custom agent. Spec keys other than
180
180
  * name/description (model: inherit, effort) are dropped: Kiro inherits the session model. */
181
- function renderKiroAgent(text: string): string {
182
- const fm: Record<string, string> = {};
181
+ function renderKiroAgent(text ) {
182
+ const fm = {};
183
183
  let body = text;
184
184
  if (text.startsWith('---')) {
185
185
  const end = text.indexOf('\n---', 3);
@@ -206,8 +206,8 @@ const OPENCODE_READONLY_AGENTS = new Set(['spec-analyst', 'system-architect', 'q
206
206
  * (description / mode / permission; model omitted because OpenCode has no 'inherit'). Unknown
207
207
  * spec keys (tools, effort) are dropped rather than passed through, so OpenCode never sees
208
208
  * vocabulary it does not define. Body below the frontmatter passes through untouched. */
209
- function renderOpencodeAgent(text: string): string {
210
- const fm: Record<string, string> = {};
209
+ function renderOpencodeAgent(text ) {
210
+ const fm = {};
211
211
  let body = text;
212
212
  if (text.startsWith('---')) {
213
213
  const end = text.indexOf('\n---', 3);
@@ -233,7 +233,7 @@ function renderOpencodeAgent(text: string): string {
233
233
  * the destination tree beyond checking existence. `relPath` is destination-relative and doubles as
234
234
  * the ledger identity, so a layout change relocates the recorded resource too — planTree's
235
235
  * relocation handling then removes the old path rather than orphaning it. */
236
- function discoverTree({ sourceDir, destDir, fsImpl = fs, layout, transform }: any): { files: any[] } {
236
+ function discoverTree({ sourceDir, destDir, fsImpl = fs, layout, transform } ) {
237
237
  if (!fsImpl.existsSync(sourceDir)) throw new Error(`copy-tree source is missing: ${sourceDir}`);
238
238
  const mapRel = typeof layout === 'function' ? layout : resolveLayout(layout);
239
239
  const mapContent = typeof transform === 'function' ? transform : resolveTransform(transform);
@@ -248,7 +248,7 @@ function discoverTree({ sourceDir, destDir, fsImpl = fs, layout, transform }: an
248
248
  }
249
249
 
250
250
  /** A file's identity on disk (device + inode), or null when the path does not resolve to one. */
251
- function fileId(file: string, fsImpl: any): string | null {
251
+ function fileId(file , fsImpl ) {
252
252
  try {
253
253
  const stat = fsImpl.statSync(file, { bigint: true });
254
254
  // An fs stand-in that reports no inode identifies nothing, rather than every file as one.
@@ -265,9 +265,9 @@ function fileId(file: string, fsImpl: any): string | null {
265
265
  * a hard link answers the same way. Built on first use, one stat per path; a path that does not
266
266
  * exist matches nothing, and `target` never matches itself.
267
267
  */
268
- function sameFileIndex(paths: string[], fsImpl: any = fs): (target: string) => string | null {
269
- let byId: Map<string, string> | undefined;
270
- return (target: string) => {
268
+ function sameFileIndex(paths , fsImpl = fs) {
269
+ let byId ;
270
+ return (target ) => {
271
271
  if (byId === undefined) {
272
272
  byId = new Map();
273
273
  for (const candidate of paths) {
@@ -292,31 +292,31 @@ function sameFileIndex(paths: string[], fsImpl: any = fs): (target: string) => s
292
292
  * `siblingReplaced` lists the harnesses whose recorded bytes this plan replaces: the file matches
293
293
  * neither the source nor this harness's own record, only a sibling's (forced or not).
294
294
  */
295
- function planTree({ sourceDir, destDir, previousResources = [], operation = 'apply', fsImpl = fs, layout, transform, force = false, keepModified = false, siblingFingerprints = new Map() }: any): any {
296
- const prevByPath = new Map<string, any>(previousResources.map((resource: any) => [resource.relPath, resource]));
297
- const changes: any[] = [];
298
- const conflicts: string[] = [];
299
- const kept: any[] = [];
300
- const siblingReplaced = new Set<string>();
295
+ function planTree({ sourceDir, destDir, previousResources = [], operation = 'apply', fsImpl = fs, layout, transform, force = false, keepModified = false, siblingFingerprints = new Map() } ) {
296
+ const prevByPath = new Map (previousResources.map((resource ) => [resource.relPath, resource]));
297
+ const changes = [];
298
+ const conflicts = [];
299
+ const kept = [];
300
+ const siblingReplaced = new Set ();
301
301
 
302
302
  // Fingerprints the CURRENT source would write, keyed by destination. Resolved lazily and only
303
303
  // when a recorded fingerprint has already failed to match, so the common removal path still
304
304
  // never reads the source tree. A missing source directory is not an error here — an asset can be
305
305
  // removed after its source moved — it simply leaves the recorded fingerprint as the only signal.
306
- let sourceByDest: Map<string, string> | undefined;
307
- const sourceFingerprint = (destAbs: string): string | undefined => {
306
+ let sourceByDest ;
307
+ const sourceFingerprint = (destAbs ) => {
308
308
  if (sourceByDest === undefined) {
309
309
  sourceByDest = sourceDir && fsImpl.existsSync(sourceDir)
310
- ? new Map<string, string>(discoverTree({ sourceDir, destDir, fsImpl, layout, transform }).files.map((file): [string, string] => [file.destAbs, file.fingerprint]))
310
+ ? new Map (discoverTree({ sourceDir, destDir, fsImpl, layout, transform }).files.map((file) => [file.destAbs, file.fingerprint]))
311
311
  : new Map();
312
312
  }
313
313
  return sourceByDest.get(destAbs);
314
314
  };
315
315
 
316
316
  // Every destination the current source produces, set once the apply path has discovered them.
317
- let producedSameFile: ((target: string) => string | null) | undefined;
317
+ let producedSameFile ;
318
318
 
319
- const proposeRemoval = (prev: any, { relocated = false }: { relocated?: boolean } = {}) => {
319
+ const proposeRemoval = (prev , { relocated = false } = {}) => {
320
320
  // prev's OWN recorded location, not the current destDir — an asset whose nativeDir changed
321
321
  // since prev was recorded must be removed from where it actually is, not from where it would
322
322
  // land today. Absent (pre-this-fix data, or a caller-constructed previousResources entry with
@@ -414,9 +414,9 @@ function planTree({ sourceDir, destDir, previousResources = [], operation = 'app
414
414
 
415
415
  /** Write every create/update change. Preserves the source file's mode (so a hook script's +x
416
416
  * bit survives the copy without a separate chmod pass) and mtime, matching this codebase's
417
- * general convention for a lifecycle-owned write (see src/adapters/claude/index.ts's
417
+ * general convention for a lifecycle-owned write (see src/adapters/claude/index.js's
418
418
  * applySettingsAsset for the same pattern applied to a transformed-content file). */
419
- function applyTree({ changes = [], fsImpl = fs, transform }: any): { applied: number } {
419
+ function applyTree({ changes = [], fsImpl = fs, transform } ) {
420
420
  const mapContent = typeof transform === 'function' ? transform : resolveTransform(transform);
421
421
  let applied = 0;
422
422
  for (const change of changes) {
@@ -440,15 +440,15 @@ function applyTree({ changes = [], fsImpl = fs, transform }: any): { applied: nu
440
440
  }
441
441
 
442
442
  /** Why a removal the runtime prune planned may no longer run, or null when it still may: the
443
- * prune's plan-time proof (src/lifecycle/index.ts pruneRetiredRuntimeFiles) re-run right before the
443
+ * prune's plan-time proof (src/lifecycle/index.js pruneRetiredRuntimeFiles) re-run right before the
444
444
  * unlink, so a directory swapped for a symlink, or a file linked to a shipped one, after the plan was
445
445
  * made is not deleted through. `guard` is `{ root, source }`: the tree and the source it projects.
446
446
  * One walk of the source and one stat per shipped path per tree per call, cached in `cache`. */
447
- function pruneGuardRefusal(change: any, cache: Map<string, any>, fsImpl: any): string | null {
447
+ function pruneGuardRefusal(change , cache , fsImpl ) {
448
448
  const { root, source } = change.pruneGuard;
449
449
  const key = `${root}\0${source}`;
450
450
  if (!cache.has(key)) {
451
- const produced = fsImpl.existsSync(source) ? discoverTree({ sourceDir: source, destDir: root, fsImpl }).files.map((file: any) => file.destAbs) : [];
451
+ const produced = fsImpl.existsSync(source) ? discoverTree({ sourceDir: source, destDir: root, fsImpl }).files.map((file ) => file.destAbs) : [];
452
452
  cache.set(key, { realRoot: fsImpl.realpathSync(root), sameFile: sameFileIndex(produced, fsImpl) });
453
453
  }
454
454
  const { realRoot, sameFile } = cache.get(key);
@@ -460,9 +460,9 @@ function pruneGuardRefusal(change: any, cache: Map<string, any>, fsImpl: any): s
460
460
  /** Delete every remove change, re-checking each file's fingerprint immediately before deletion —
461
461
  * refuses (throws) rather than silently deleting a file modified since planTree ran. A removal the
462
462
  * runtime prune planned (`pruneGuard`) also re-checks its containment and same-file proof there. */
463
- function removeTree({ changes = [], fsImpl = fs }: any): { removed: number } {
463
+ function removeTree({ changes = [], fsImpl = fs } ) {
464
464
  let removed = 0;
465
- const guards = new Map<string, any>();
465
+ const guards = new Map ();
466
466
  for (const change of changes) {
467
467
  if (change.operation !== 'remove' || change.kept) continue;
468
468
  if (!fsImpl.existsSync(change.target)) continue;
@@ -486,7 +486,7 @@ const ANCHOR_DIRS = new Set(['.agents', '.claude', '.codex', '.copilot', '.doflo
486
486
  * which may hold user content elsewhere. The match is by basename, so a root with another name
487
487
  * (`~/.gemini/config`, `~/.pi/agent`, `~/.config/opencode`) is pruned once it is empty; no file is
488
488
  * ever lost, since rmdir refuses a folder that holds one. */
489
- function pruneEmptyAncestors(startDir: string, { fsImpl = fs }: { fsImpl?: any } = {}): void {
489
+ function pruneEmptyAncestors(startDir , { fsImpl = fs } = {}) {
490
490
  let current = startDir;
491
491
  for (;;) {
492
492
  const basename = path.basename(current);
@@ -500,11 +500,11 @@ function pruneEmptyAncestors(startDir: string, { fsImpl = fs }: { fsImpl?: any }
500
500
  * `plannedWrites` lists the destinations the run being verified wrote. An absent source file is
501
501
  * not reported in general, because a user may delete one, but an absent file this run wrote means
502
502
  * the run lost it (a case-only rename once deleted the file it had just written) and is a conflict. */
503
- function verifyTree({ sourceDir, destDir, fsImpl = fs, layout, transform, plannedWrites = [] }: any): { ok: boolean, resources: any[], conflicts: string[] } {
503
+ function verifyTree({ sourceDir, destDir, fsImpl = fs, layout, transform, plannedWrites = [] } ) {
504
504
  const { files } = discoverTree({ sourceDir, destDir, fsImpl, layout, transform });
505
505
  const written = new Set(plannedWrites);
506
- const resources: any[] = [];
507
- const conflicts: string[] = [];
506
+ const resources = [];
507
+ const conflicts = [];
508
508
  for (const file of files) {
509
509
  if (!file.exists) {
510
510
  if (written.has(file.destAbs)) conflicts.push(`${file.relPath} was written by this run but is missing`);
@@ -520,14 +520,14 @@ function verifyTree({ sourceDir, destDir, fsImpl = fs, layout, transform, planne
520
520
  // ---- per-adapter copy-tree glue, shared so codex/claude/gemini don't each carry their own copy ----
521
521
 
522
522
  /** Filter a harness's asset list down to the ones this engine handles. */
523
- function copyTreeAssets(assets: any): any[] {
524
- return (assets || []).filter((asset: any) => asset?.renderer === 'copy-tree');
523
+ function copyTreeAssets(assets ) {
524
+ return (assets || []).filter((asset ) => asset?.renderer === 'copy-tree');
525
525
  }
526
526
 
527
527
  /** Recursively sort object keys before serializing, so two logically-equal objects with
528
528
  * differently-ordered keys (e.g. after a settings file is merged and re-merged) fingerprint
529
529
  * identically instead of spuriously registering as changed. */
530
- function stableSort(value: any): any {
530
+ function stableSort(value ) {
531
531
  if (Array.isArray(value)) return value.map(stableSort);
532
532
  if (!value || typeof value !== 'object') return value;
533
533
  return Object.fromEntries(Object.keys(value).sort().map((key) => [key, stableSort(value[key])]));
@@ -535,22 +535,22 @@ function stableSort(value: any): any {
535
535
 
536
536
  /** Content fingerprint every adapter uses to track what it owns: sha256 of a string as-is, or of
537
537
  * a stably key-sorted JSON serialization of anything else. */
538
- function fingerprint(value: any): string {
538
+ function fingerprint(value ) {
539
539
  return sha256(typeof value === 'string' ? value : JSON.stringify(stableSort(value)));
540
540
  }
541
541
 
542
542
  /** Read and parse a native JSON file an adapter merges into. Distinguishes "absent" (safe to
543
543
  * create) from "present but unparseable" (a conflict to report, never silently overwritten) —
544
544
  * every adapter that merges into a native JSON settings/config file needs exactly this. */
545
- function readJson(file: string, { fsImpl = fs }: { fsImpl?: any } = {}): { exists: boolean, value: any, error: string | null } {
545
+ function readJson(file , { fsImpl = fs } = {}) {
546
546
  if (!fsImpl.existsSync(file)) return { exists: false, value: {}, error: null };
547
547
  try { return { exists: true, value: JSON.parse(fsImpl.readFileSync(file, 'utf8')), error: null }; }
548
- catch (error: any) { return { exists: true, value: null, error: `Invalid JSON in ${file}: ${error.message}` }; }
548
+ catch (error ) { return { exists: true, value: null, error: `Invalid JSON in ${file}: ${error.message}` }; }
549
549
  }
550
550
 
551
551
  /** Resolve an asset's source directory against the repo root, refusing a path that escapes the
552
552
  * repository or does not exist. `harnessName` only shapes the thrown error message. */
553
- function sourceDirFor(asset: any, context: any = {}, fsImpl: any = fs, harnessName: string = 'Adapter'): string {
553
+ function sourceDirFor(asset , context = {}, fsImpl = fs, harnessName = 'Adapter') {
554
554
  if (!asset || typeof asset.source !== 'string') throw new Error(`${harnessName} asset requires a source path`);
555
555
  const repoRoot = context.repoRoot ? path.resolve(context.repoRoot) : process.cwd();
556
556
  const source = path.resolve(repoRoot, asset.source);
@@ -561,14 +561,14 @@ function sourceDirFor(asset: any, context: any = {}, fsImpl: any = fs, harnessNa
561
561
  }
562
562
 
563
563
  /** Resolve an asset's native destination directory under the harness's already-resolved config dir. */
564
- function copyTreeDestDir(configDir: string, asset: any): string {
564
+ function copyTreeDestDir(configDir , asset ) {
565
565
  return path.join(configDir, asset.nativeDir || '');
566
566
  }
567
567
 
568
568
  /** Destination of a `../.doflow` shared-tree asset at the scope root (`<project>/.doflow`, or
569
569
  * `$HOME/.doflow` globally), for harnesses whose tree root sits more than one level under the scope
570
570
  * root. Returns null for any other nativeDir, so the caller falls back to `copyTreeDestDir`. */
571
- function sharedTreeDestDir(rootDir: string, nativeDir: any): string | null {
571
+ function sharedTreeDestDir(rootDir , nativeDir ) {
572
572
  if (nativeDir !== '../.doflow' && !String(nativeDir || '').startsWith('../.doflow/')) return null;
573
573
  const sharedRoot = path.join(rootDir, '.doflow');
574
574
  const dest = path.join(rootDir, '.doflow', nativeDir.slice('../.doflow'.length));
@@ -578,25 +578,25 @@ function sharedTreeDestDir(rootDir: string, nativeDir: any): string | null {
578
578
  return dest;
579
579
  }
580
580
 
581
- /** The assets that project a tree several harnesses claim at one scope root: the runtime tree, and
582
- * the guidance tree. A harness whose
581
+ /** The assets that project a tree several harnesses claim at one scope root: the runtime, guidance,
582
+ * and shared hooks trees. A harness whose
583
583
  * install predates its own rows for a shared tree (Pi gained guidance rows in v1.15.0) can only
584
584
  * recognise the bytes a sibling recorded. */
585
585
  const SHARED_TREE_ASSETS = new Set([
586
586
  'scripts.doflow', 'runtime.cli', 'runtime.lib', 'runtime.registry',
587
- 'guidance.context-layer', 'kiro.guidance-tree',
587
+ 'guidance.context-layer', 'kiro.guidance-tree', 'hooks.shared',
588
588
  ]);
589
589
 
590
590
  /** For each shared-tree target, the fingerprints other harnesses recorded and which harness
591
- * recorded each, so an update accepts a runtime or guidance tree a sibling wrote (several harnesses
591
+ * recorded each, so an update accepts a runtime, guidance, or shared hooks tree a sibling wrote (several harnesses
592
592
  * claim one `.doflow` file) while bytes no such row recorded are still refused as a hand edit. */
593
- function ledgerSiblingFingerprints(resources: any, harness: string): Map<string, Map<string, string>> {
594
- const byTarget = new Map<string, Map<string, string>>();
593
+ function ledgerSiblingFingerprints(resources , harness ) {
594
+ const byTarget = new Map ();
595
595
  for (const resource of resources || []) {
596
596
  if (resource.kind !== 'copy-tree-file' || resource.harness === harness || !SHARED_TREE_ASSETS.has(resource.assetId)) continue;
597
597
  if (typeof resource.target !== 'string' || typeof resource.fingerprint !== 'string') continue;
598
598
  if (!byTarget.has(resource.target)) byTarget.set(resource.target, new Map());
599
- const byFingerprint = byTarget.get(resource.target)!;
599
+ const byFingerprint = byTarget.get(resource.target) ;
600
600
  if (!byFingerprint.has(resource.fingerprint)) byFingerprint.set(resource.fingerprint, resource.harness);
601
601
  }
602
602
  return byTarget;
@@ -604,17 +604,17 @@ function ledgerSiblingFingerprints(resources: any, harness: string): Map<string,
604
604
 
605
605
  /** The one-line plan notice for a plan whose copy-tree results replaced sibling-written files, or
606
606
  * an empty list when none did. */
607
- function siblingReplacedNotices(results: any[]): string[] {
607
+ function siblingReplacedNotices(results ) {
608
608
  const harnesses = [...new Set(results.flatMap((result) => result.siblingReplaced))].sort();
609
609
  if (!harnesses.length) return [];
610
610
  return [`replaced shared files written by ${harnesses.join(', ')}; reinstall ${harnesses.length > 1 ? 'those harnesses' : 'that harness'} to restore them`];
611
611
  }
612
612
 
613
613
  /** Narrow a harness's flat neutral-resource list to one asset's previously-owned copy-tree files. */
614
- function ledgerFileResources(resources: any, harness: string, assetId: string): any[] {
614
+ function ledgerFileResources(resources , harness , assetId ) {
615
615
  return (resources || [])
616
- .filter((resource: any) => resource.harness === harness && resource.assetId === assetId && resource.kind === 'copy-tree-file')
617
- .map((resource: any) => ({ relPath: resource.identity, fingerprint: resource.fingerprint, target: resource.target }));
616
+ .filter((resource ) => resource.harness === harness && resource.assetId === assetId && resource.kind === 'copy-tree-file')
617
+ .map((resource ) => ({ relPath: resource.identity, fingerprint: resource.fingerprint, target: resource.target }));
618
618
  }
619
619
 
620
620
  module.exports = { kiroHook, kiroHookCommand, kiroPreToolUseHooks, walkRelFiles, discoverTree, sameFileIndex, planTree, applyTree, removeTree, verifyTree, copyTreeAssets, copyTreeDestDir, sharedTreeDestDir, ledgerFileResources, ledgerSiblingFingerprints, siblingReplacedNotices, resolveLayout, LAYOUTS, resolveTransform, TRANSFORMS, fingerprint, pruneEmptyAncestors, readJson, sourceDirFor };