kenaz 0.3.3 → 0.3.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 (413) hide show
  1. package/core/.agents/skills/typesafe-ai/LICENSE +21 -21
  2. package/core/.agents/skills/typesafe-ai/SKILL.md +149 -149
  3. package/core/.claude/commands/Ansuz.md +14 -2
  4. package/core/.claude/hooks/__tests__/task-2395-project-registration.test.js +84 -0
  5. package/core/.claude/hooks/handoff-staleness.js +2 -2
  6. package/core/.claude/hooks/session-init.ps1 +49 -49
  7. package/core/.claude/hooks/turn-context.js +50 -2
  8. package/core/.claude/skills/ui-ux-pro-max/data/charts.csv +26 -26
  9. package/core/.claude/skills/ui-ux-pro-max/data/colors.csv +97 -97
  10. package/core/.claude/skills/ui-ux-pro-max/data/icons.csv +101 -101
  11. package/core/.claude/skills/ui-ux-pro-max/data/landing.csv +31 -31
  12. package/core/.claude/skills/ui-ux-pro-max/data/products.csv +96 -96
  13. package/core/.claude/skills/ui-ux-pro-max/data/react-performance.csv +45 -45
  14. package/core/.claude/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -54
  15. package/core/.claude/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -53
  16. package/core/.claude/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -56
  17. package/core/.claude/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -53
  18. package/core/.claude/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -53
  19. package/core/.claude/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -51
  20. package/core/.claude/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -59
  21. package/core/.claude/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -52
  22. package/core/.claude/skills/ui-ux-pro-max/data/stacks/react.csv +54 -54
  23. package/core/.claude/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -61
  24. package/core/.claude/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -54
  25. package/core/.claude/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -51
  26. package/core/.claude/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -50
  27. package/core/.claude/skills/ui-ux-pro-max/data/styles.csv +68 -68
  28. package/core/.claude/skills/ui-ux-pro-max/data/typography.csv +57 -57
  29. package/core/.claude/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -101
  30. package/core/.claude/skills/ui-ux-pro-max/data/ux-guidelines.csv +99 -99
  31. package/core/.claude/skills/ui-ux-pro-max/data/web-interface.csv +31 -31
  32. package/core/.claude/skills/ui-ux-pro-max/scripts/core.py +253 -253
  33. package/core/.claude/skills/ui-ux-pro-max/scripts/design_system.py +1067 -1067
  34. package/core/.claude/skills/ui-ux-pro-max/scripts/search.py +106 -106
  35. package/core/.collaboration/core/task-api.js +1 -4
  36. package/core/.formation/README.md +21 -146
  37. package/core/.formation/bifrost/codex-doctor.js +37 -2
  38. package/core/.formation/bifrost/codex-doctor.test.js +18 -0
  39. package/core/.formation/bifrost/launch.js +1 -1
  40. package/core/.formation/bifrost/openai/codex-hook-shim.js +6 -0
  41. package/core/.formation/bifrost/openai/codex-hook-shim.test.js +11 -1
  42. package/core/.formation/bifrost/setup-codex.js +3 -0
  43. package/core/.formation/spec/task_operations.parity.test.js +0 -1
  44. package/core/.formation/tests/hello.txt +2 -2
  45. package/core/.formation/tools/dashboard-models-chain.test.js +0 -6
  46. package/core/.formation/tools/dispatch-detail/cross-project-worktree.md +1 -9
  47. package/core/.formation/tools/dispatch-detail/worktree-subagent-mode.md +2 -2
  48. package/core/.formation/tools/dispatch.js +28 -549
  49. package/core/.formation/tools/mcp-allowlist.test.js +0 -1
  50. package/core/.formation/tools/post-complete/task2345.test.js +0 -5
  51. package/core/.formation/tools/pre-dispatch-snapshot.test.js +0 -100
  52. package/core/.formation/tools/prompt.js +29 -1623
  53. package/core/.formation/tools/prompt.test.js +26 -594
  54. package/core/.kenaz/knowledge/ladybug_2c51da1e_shadow_file_frame_group.patch +163 -163
  55. package/core/.kenaz/knowledge/ladybug_30cf85690e_shadow_page_read_during_checkpoint.patch +393 -393
  56. package/core/.kenaz/knowledge/ladybug_7a17858d3a_node_group_delete_lock.patch +102 -102
  57. package/core/.kenaz/knowledge/ladybug_926a0ba1_column_checkpoint.patch +412 -412
  58. package/core/.kenaz/knowledge/ladybug_a346c4c4b6_pk_index_header_pages.patch +144 -144
  59. package/core/.kenaz/knowledge/ladybug_ba5f38815b_csr_node_group.patch +86 -86
  60. package/core/.kenaz/muninn_episode_write.ps1 +17 -17
  61. package/core/.kenaz/specialist/field_report_watermark.json +1 -1
  62. package/core/.kenaz/specialist/memory_guard_fired.json +1 -1
  63. package/core/.kenaz/specialist/memory_guard_trigger_cache.json +1 -1
  64. package/core/.kenaz/specialist/t1777_probe.txt +1 -1
  65. package/core/.matrix/index.matrix +5 -5
  66. package/core/.specialist/tools/__tests__/freya-cli-contract.test.js +0 -1
  67. package/core/KENAZ_CORE_VERSION +1 -1
  68. package/core/dev/ANSUZ_STARTUP_INJECTION_REVIEW.md +355 -355
  69. package/core/dev/JEV_INTEGRATION_PLAN.md +189 -189
  70. package/core/dev/design/matrix_format_EXAMPLE_memory.matrix +13 -13
  71. package/core/dev/design/matrix_format_EXAMPLE_project_status.matrix +9 -9
  72. package/core/dev/design/matrix_format_EXAMPLE_tasks.matrix +14 -14
  73. package/core/dev/design/matrix_v2_EXAMPLE.matrix +67 -67
  74. package/core/dev/jev-eval/ACTIVE_CORRECTION.md +52 -52
  75. package/core/dev/jev-eval/CONTEXT_REVIEW.md +31 -31
  76. package/core/dev/jev-eval/EVALUATION_STATUS.md +39 -39
  77. package/core/dev/jev-eval/HISTORICAL_CORPUS.md +33 -33
  78. package/core/dev/jev-eval/README.md +99 -99
  79. package/core/dev/jev-eval/THALAMUS_RANKING.md +58 -58
  80. package/core/dev/jev-eval/reports/annotation-pilot-2026-09-20.json +32 -32
  81. package/core/dev/jev-eval/reports/context-review-2026-09-20.json +48 -48
  82. package/core/dev/jev-eval/reports/history-corpus-2026-09-20.json +52 -52
  83. package/core/dev/pipeline/SELF_ENHANCEMENT_STATUS.json.bak +47 -47
  84. package/core/dev/pipeline/tasks/.gitkeep +1 -1
  85. package/core/dev/pipeline/test_results/TASK_TEST_PARALLEL_002_result.txt +27 -27
  86. package/core/dev/scripts/init_git.sh +0 -0
  87. package/core/dev/scripts/init_project.sh +0 -0
  88. package/core/dev/scripts/legacy/dev_agent_scheduler.ps1.bak +536 -536
  89. package/core/dev/scripts/legacy/multi_agent_scheduler.ps1.bak +1978 -1978
  90. package/core/dev/scripts/matrix/src/cli/commands/list-tags.ts.wip +140 -140
  91. package/core/dev/scripts/matrix/src/cli/commands/manage-tags.ts.wip +173 -173
  92. package/core/dev/scripts/matrix/src/cli/commands/stats.ts.wip +168 -168
  93. package/core/dev/scripts/matrix/src/cli/commands/tokens.ts.wip +126 -126
  94. package/core/dev/scripts/multi_agent_scheduler.sh +0 -0
  95. package/core/dev/scripts/run_once.sh +0 -0
  96. package/core/dev/scripts/setup_mcp.sh +0 -0
  97. package/core/dev/scripts/setup_rag.sh +0 -0
  98. package/core/dev/scripts/start_dev_agent.sh +0 -0
  99. package/core/dev/scripts/start_multi_agent.sh +0 -0
  100. package/core/dev/scripts/sync_task_queue.sh +0 -0
  101. package/core/plugins/kenaz/commands/Amelia.md +1 -1
  102. package/core/plugins/kenaz/commands/Ansuz.md +15 -3
  103. package/core/plugins/kenaz/scripts/kenaz-mode.js +11 -1
  104. package/core/plugins/kenaz/scripts/sync.js +6 -2
  105. package/core/plugins/kenaz/scripts/ygg.js +13 -1
  106. package/core/plugins/kenaz/skills/product_manager/SKILL.md +12 -12
  107. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/charts.csv +26 -26
  108. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/colors.csv +97 -97
  109. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/icons.csv +101 -101
  110. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/landing.csv +31 -31
  111. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/products.csv +96 -96
  112. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/react-performance.csv +45 -45
  113. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -54
  114. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -53
  115. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -56
  116. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -53
  117. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -53
  118. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -51
  119. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -59
  120. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -52
  121. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/react.csv +54 -54
  122. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -61
  123. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -54
  124. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -51
  125. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -50
  126. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/styles.csv +68 -68
  127. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/typography.csv +57 -57
  128. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -101
  129. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/ux-guidelines.csv +99 -99
  130. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/web-interface.csv +31 -31
  131. package/core/plugins/kenaz/skills/ui-ux-pro-max/scripts/core.py +253 -253
  132. package/core/plugins/kenaz/skills/ui-ux-pro-max/scripts/design_system.py +1067 -1067
  133. package/core/plugins/kenaz/skills/ui-ux-pro-max/scripts/search.py +106 -106
  134. package/lib/cli.js +11 -4
  135. package/package.json +11 -7
  136. package/vendor/install-kenaz-cli.js +18 -7
  137. package/core/.formation/tools/broadcast.js +0 -103
  138. package/core/.formation/tools/dispatch-dryrun.test.js +0 -54
  139. package/core/.formation/tools/dispatch-notifier.js +0 -84
  140. package/core/.formation/tools/formations.js +0 -1203
  141. package/core/.formation/tools/headless-branch-recovery.test.js +0 -64
  142. package/core/.formation/tools/headless-dryrun.test.js +0 -79
  143. package/core/.formation/tools/headless-v2.js +0 -659
  144. package/core/.formation/tools/headless.js +0 -1071
  145. package/core/.formation/tools/legacy-scheduler.js +0 -1994
  146. package/core/.formation/tools/logger.js +0 -273
  147. package/core/.formation/tools/memory-loader.js +0 -422
  148. package/core/.formation/tools/process-state.js +0 -33
  149. package/core/.formation/tools/remote-conflict.js +0 -98
  150. package/core/.formation/tools/runner.js +0 -1332
  151. package/core/.formation/tools/task-utils.js +0 -344
  152. package/core/.formation/tools/task2028-headless-worktree.test.js +0 -112
  153. package/core/.formation/tools/tests/headless-mcp-args-plumbing.test.js +0 -123
  154. package/core/.formation/tools/worktree-leak.test.js +0 -162
  155. package/core/k-cli/dist/alaya/run.js +0 -91
  156. package/core/k-cli/dist/core/output.js +0 -24
  157. package/core/k-cli/dist/core/process.js +0 -38
  158. package/core/k-cli/dist/core/project.js +0 -80
  159. package/core/k-cli/dist/core/spawn.js +0 -268
  160. package/core/k-cli/dist/core/types.js +0 -2
  161. package/core/k-cli/dist/dashboard/update.js +0 -53
  162. package/core/k-cli/dist/dashboard/version.js +0 -20
  163. package/core/k-cli/dist/dispatch/run.js +0 -241
  164. package/core/k-cli/dist/dispatch/status.js +0 -103
  165. package/core/k-cli/dist/index.js +0 -283
  166. package/core/k-cli/dist/project/initialize.js +0 -301
  167. package/core/k-cli/dist/project/install.js +0 -89
  168. package/core/k-cli/dist/project/status.js +0 -138
  169. package/core/k-cli/dist/specialist/list.js +0 -100
  170. package/core/k-cli/dist/specialist/run.js +0 -93
  171. package/core/k-cli/dist/task/create.js +0 -194
  172. package/core/k-cli/dist/task/delete.js +0 -98
  173. package/core/k-cli/dist/task/get.js +0 -55
  174. package/core/k-cli/dist/task/list.js +0 -58
  175. package/core/k-cli/dist/task/submit.js +0 -156
  176. package/core/k-cli/dist/ygg/run.js +0 -51
  177. package/core/k-cli/node_modules/.bin/yaml +0 -16
  178. package/core/k-cli/node_modules/.bin/yaml.cmd +0 -17
  179. package/core/k-cli/node_modules/.bin/yaml.ps1 +0 -28
  180. package/core/k-cli/node_modules/.package-lock.json +0 -23
  181. package/core/k-cli/node_modules/yaml/LICENSE +0 -13
  182. package/core/k-cli/node_modules/yaml/README.md +0 -172
  183. package/core/k-cli/node_modules/yaml/bin.mjs +0 -11
  184. package/core/k-cli/node_modules/yaml/browser/dist/compose/compose-collection.js +0 -88
  185. package/core/k-cli/node_modules/yaml/browser/dist/compose/compose-doc.js +0 -43
  186. package/core/k-cli/node_modules/yaml/browser/dist/compose/compose-node.js +0 -109
  187. package/core/k-cli/node_modules/yaml/browser/dist/compose/compose-scalar.js +0 -86
  188. package/core/k-cli/node_modules/yaml/browser/dist/compose/composer.js +0 -217
  189. package/core/k-cli/node_modules/yaml/browser/dist/compose/resolve-block-map.js +0 -115
  190. package/core/k-cli/node_modules/yaml/browser/dist/compose/resolve-block-scalar.js +0 -198
  191. package/core/k-cli/node_modules/yaml/browser/dist/compose/resolve-block-seq.js +0 -49
  192. package/core/k-cli/node_modules/yaml/browser/dist/compose/resolve-end.js +0 -37
  193. package/core/k-cli/node_modules/yaml/browser/dist/compose/resolve-flow-collection.js +0 -207
  194. package/core/k-cli/node_modules/yaml/browser/dist/compose/resolve-flow-scalar.js +0 -223
  195. package/core/k-cli/node_modules/yaml/browser/dist/compose/resolve-props.js +0 -146
  196. package/core/k-cli/node_modules/yaml/browser/dist/compose/util-contains-newline.js +0 -34
  197. package/core/k-cli/node_modules/yaml/browser/dist/compose/util-empty-scalar-position.js +0 -26
  198. package/core/k-cli/node_modules/yaml/browser/dist/compose/util-flow-indent-check.js +0 -15
  199. package/core/k-cli/node_modules/yaml/browser/dist/compose/util-map-includes.js +0 -13
  200. package/core/k-cli/node_modules/yaml/browser/dist/doc/Document.js +0 -335
  201. package/core/k-cli/node_modules/yaml/browser/dist/doc/anchors.js +0 -71
  202. package/core/k-cli/node_modules/yaml/browser/dist/doc/applyReviver.js +0 -55
  203. package/core/k-cli/node_modules/yaml/browser/dist/doc/createNode.js +0 -88
  204. package/core/k-cli/node_modules/yaml/browser/dist/doc/directives.js +0 -176
  205. package/core/k-cli/node_modules/yaml/browser/dist/errors.js +0 -57
  206. package/core/k-cli/node_modules/yaml/browser/dist/index.js +0 -17
  207. package/core/k-cli/node_modules/yaml/browser/dist/log.js +0 -11
  208. package/core/k-cli/node_modules/yaml/browser/dist/nodes/Alias.js +0 -114
  209. package/core/k-cli/node_modules/yaml/browser/dist/nodes/Collection.js +0 -147
  210. package/core/k-cli/node_modules/yaml/browser/dist/nodes/Node.js +0 -38
  211. package/core/k-cli/node_modules/yaml/browser/dist/nodes/Pair.js +0 -36
  212. package/core/k-cli/node_modules/yaml/browser/dist/nodes/Scalar.js +0 -24
  213. package/core/k-cli/node_modules/yaml/browser/dist/nodes/YAMLMap.js +0 -144
  214. package/core/k-cli/node_modules/yaml/browser/dist/nodes/YAMLSeq.js +0 -113
  215. package/core/k-cli/node_modules/yaml/browser/dist/nodes/addPairToJSMap.js +0 -63
  216. package/core/k-cli/node_modules/yaml/browser/dist/nodes/identity.js +0 -36
  217. package/core/k-cli/node_modules/yaml/browser/dist/nodes/toJS.js +0 -37
  218. package/core/k-cli/node_modules/yaml/browser/dist/parse/cst-scalar.js +0 -214
  219. package/core/k-cli/node_modules/yaml/browser/dist/parse/cst-stringify.js +0 -61
  220. package/core/k-cli/node_modules/yaml/browser/dist/parse/cst-visit.js +0 -97
  221. package/core/k-cli/node_modules/yaml/browser/dist/parse/cst.js +0 -98
  222. package/core/k-cli/node_modules/yaml/browser/dist/parse/lexer.js +0 -717
  223. package/core/k-cli/node_modules/yaml/browser/dist/parse/line-counter.js +0 -39
  224. package/core/k-cli/node_modules/yaml/browser/dist/parse/parser.js +0 -967
  225. package/core/k-cli/node_modules/yaml/browser/dist/public-api.js +0 -102
  226. package/core/k-cli/node_modules/yaml/browser/dist/schema/Schema.js +0 -37
  227. package/core/k-cli/node_modules/yaml/browser/dist/schema/common/map.js +0 -17
  228. package/core/k-cli/node_modules/yaml/browser/dist/schema/common/null.js +0 -15
  229. package/core/k-cli/node_modules/yaml/browser/dist/schema/common/seq.js +0 -17
  230. package/core/k-cli/node_modules/yaml/browser/dist/schema/common/string.js +0 -14
  231. package/core/k-cli/node_modules/yaml/browser/dist/schema/core/bool.js +0 -19
  232. package/core/k-cli/node_modules/yaml/browser/dist/schema/core/float.js +0 -43
  233. package/core/k-cli/node_modules/yaml/browser/dist/schema/core/int.js +0 -38
  234. package/core/k-cli/node_modules/yaml/browser/dist/schema/core/schema.js +0 -23
  235. package/core/k-cli/node_modules/yaml/browser/dist/schema/json/schema.js +0 -62
  236. package/core/k-cli/node_modules/yaml/browser/dist/schema/tags.js +0 -96
  237. package/core/k-cli/node_modules/yaml/browser/dist/schema/yaml-1.1/binary.js +0 -58
  238. package/core/k-cli/node_modules/yaml/browser/dist/schema/yaml-1.1/bool.js +0 -26
  239. package/core/k-cli/node_modules/yaml/browser/dist/schema/yaml-1.1/float.js +0 -46
  240. package/core/k-cli/node_modules/yaml/browser/dist/schema/yaml-1.1/int.js +0 -71
  241. package/core/k-cli/node_modules/yaml/browser/dist/schema/yaml-1.1/merge.js +0 -64
  242. package/core/k-cli/node_modules/yaml/browser/dist/schema/yaml-1.1/omap.js +0 -74
  243. package/core/k-cli/node_modules/yaml/browser/dist/schema/yaml-1.1/pairs.js +0 -78
  244. package/core/k-cli/node_modules/yaml/browser/dist/schema/yaml-1.1/schema.js +0 -39
  245. package/core/k-cli/node_modules/yaml/browser/dist/schema/yaml-1.1/set.js +0 -93
  246. package/core/k-cli/node_modules/yaml/browser/dist/schema/yaml-1.1/timestamp.js +0 -101
  247. package/core/k-cli/node_modules/yaml/browser/dist/stringify/foldFlowLines.js +0 -146
  248. package/core/k-cli/node_modules/yaml/browser/dist/stringify/stringify.js +0 -129
  249. package/core/k-cli/node_modules/yaml/browser/dist/stringify/stringifyCollection.js +0 -153
  250. package/core/k-cli/node_modules/yaml/browser/dist/stringify/stringifyComment.js +0 -20
  251. package/core/k-cli/node_modules/yaml/browser/dist/stringify/stringifyDocument.js +0 -85
  252. package/core/k-cli/node_modules/yaml/browser/dist/stringify/stringifyNumber.js +0 -24
  253. package/core/k-cli/node_modules/yaml/browser/dist/stringify/stringifyPair.js +0 -150
  254. package/core/k-cli/node_modules/yaml/browser/dist/stringify/stringifyString.js +0 -336
  255. package/core/k-cli/node_modules/yaml/browser/dist/util.js +0 -11
  256. package/core/k-cli/node_modules/yaml/browser/dist/visit.js +0 -233
  257. package/core/k-cli/node_modules/yaml/browser/index.js +0 -5
  258. package/core/k-cli/node_modules/yaml/browser/package.json +0 -3
  259. package/core/k-cli/node_modules/yaml/dist/cli.d.ts +0 -8
  260. package/core/k-cli/node_modules/yaml/dist/cli.mjs +0 -201
  261. package/core/k-cli/node_modules/yaml/dist/compose/compose-collection.d.ts +0 -11
  262. package/core/k-cli/node_modules/yaml/dist/compose/compose-collection.js +0 -90
  263. package/core/k-cli/node_modules/yaml/dist/compose/compose-doc.d.ts +0 -7
  264. package/core/k-cli/node_modules/yaml/dist/compose/compose-doc.js +0 -45
  265. package/core/k-cli/node_modules/yaml/dist/compose/compose-node.d.ts +0 -29
  266. package/core/k-cli/node_modules/yaml/dist/compose/compose-node.js +0 -112
  267. package/core/k-cli/node_modules/yaml/dist/compose/compose-scalar.d.ts +0 -5
  268. package/core/k-cli/node_modules/yaml/dist/compose/compose-scalar.js +0 -88
  269. package/core/k-cli/node_modules/yaml/dist/compose/composer.d.ts +0 -63
  270. package/core/k-cli/node_modules/yaml/dist/compose/composer.js +0 -222
  271. package/core/k-cli/node_modules/yaml/dist/compose/resolve-block-map.d.ts +0 -6
  272. package/core/k-cli/node_modules/yaml/dist/compose/resolve-block-map.js +0 -117
  273. package/core/k-cli/node_modules/yaml/dist/compose/resolve-block-scalar.d.ts +0 -11
  274. package/core/k-cli/node_modules/yaml/dist/compose/resolve-block-scalar.js +0 -200
  275. package/core/k-cli/node_modules/yaml/dist/compose/resolve-block-seq.d.ts +0 -6
  276. package/core/k-cli/node_modules/yaml/dist/compose/resolve-block-seq.js +0 -51
  277. package/core/k-cli/node_modules/yaml/dist/compose/resolve-end.d.ts +0 -6
  278. package/core/k-cli/node_modules/yaml/dist/compose/resolve-end.js +0 -39
  279. package/core/k-cli/node_modules/yaml/dist/compose/resolve-flow-collection.d.ts +0 -7
  280. package/core/k-cli/node_modules/yaml/dist/compose/resolve-flow-collection.js +0 -209
  281. package/core/k-cli/node_modules/yaml/dist/compose/resolve-flow-scalar.d.ts +0 -10
  282. package/core/k-cli/node_modules/yaml/dist/compose/resolve-flow-scalar.js +0 -225
  283. package/core/k-cli/node_modules/yaml/dist/compose/resolve-props.d.ts +0 -23
  284. package/core/k-cli/node_modules/yaml/dist/compose/resolve-props.js +0 -148
  285. package/core/k-cli/node_modules/yaml/dist/compose/util-contains-newline.d.ts +0 -2
  286. package/core/k-cli/node_modules/yaml/dist/compose/util-contains-newline.js +0 -36
  287. package/core/k-cli/node_modules/yaml/dist/compose/util-empty-scalar-position.d.ts +0 -2
  288. package/core/k-cli/node_modules/yaml/dist/compose/util-empty-scalar-position.js +0 -28
  289. package/core/k-cli/node_modules/yaml/dist/compose/util-flow-indent-check.d.ts +0 -3
  290. package/core/k-cli/node_modules/yaml/dist/compose/util-flow-indent-check.js +0 -17
  291. package/core/k-cli/node_modules/yaml/dist/compose/util-map-includes.d.ts +0 -4
  292. package/core/k-cli/node_modules/yaml/dist/compose/util-map-includes.js +0 -15
  293. package/core/k-cli/node_modules/yaml/dist/doc/Document.d.ts +0 -141
  294. package/core/k-cli/node_modules/yaml/dist/doc/Document.js +0 -337
  295. package/core/k-cli/node_modules/yaml/dist/doc/anchors.d.ts +0 -24
  296. package/core/k-cli/node_modules/yaml/dist/doc/anchors.js +0 -76
  297. package/core/k-cli/node_modules/yaml/dist/doc/applyReviver.d.ts +0 -9
  298. package/core/k-cli/node_modules/yaml/dist/doc/applyReviver.js +0 -57
  299. package/core/k-cli/node_modules/yaml/dist/doc/createNode.d.ts +0 -17
  300. package/core/k-cli/node_modules/yaml/dist/doc/createNode.js +0 -90
  301. package/core/k-cli/node_modules/yaml/dist/doc/directives.d.ts +0 -49
  302. package/core/k-cli/node_modules/yaml/dist/doc/directives.js +0 -178
  303. package/core/k-cli/node_modules/yaml/dist/errors.d.ts +0 -21
  304. package/core/k-cli/node_modules/yaml/dist/errors.js +0 -62
  305. package/core/k-cli/node_modules/yaml/dist/index.d.ts +0 -25
  306. package/core/k-cli/node_modules/yaml/dist/index.js +0 -50
  307. package/core/k-cli/node_modules/yaml/dist/log.d.ts +0 -3
  308. package/core/k-cli/node_modules/yaml/dist/log.js +0 -19
  309. package/core/k-cli/node_modules/yaml/dist/nodes/Alias.d.ts +0 -29
  310. package/core/k-cli/node_modules/yaml/dist/nodes/Alias.js +0 -116
  311. package/core/k-cli/node_modules/yaml/dist/nodes/Collection.d.ts +0 -73
  312. package/core/k-cli/node_modules/yaml/dist/nodes/Collection.js +0 -151
  313. package/core/k-cli/node_modules/yaml/dist/nodes/Node.d.ts +0 -53
  314. package/core/k-cli/node_modules/yaml/dist/nodes/Node.js +0 -40
  315. package/core/k-cli/node_modules/yaml/dist/nodes/Pair.d.ts +0 -22
  316. package/core/k-cli/node_modules/yaml/dist/nodes/Pair.js +0 -39
  317. package/core/k-cli/node_modules/yaml/dist/nodes/Scalar.d.ts +0 -43
  318. package/core/k-cli/node_modules/yaml/dist/nodes/Scalar.js +0 -27
  319. package/core/k-cli/node_modules/yaml/dist/nodes/YAMLMap.d.ts +0 -53
  320. package/core/k-cli/node_modules/yaml/dist/nodes/YAMLMap.js +0 -147
  321. package/core/k-cli/node_modules/yaml/dist/nodes/YAMLSeq.d.ts +0 -60
  322. package/core/k-cli/node_modules/yaml/dist/nodes/YAMLSeq.js +0 -115
  323. package/core/k-cli/node_modules/yaml/dist/nodes/addPairToJSMap.d.ts +0 -4
  324. package/core/k-cli/node_modules/yaml/dist/nodes/addPairToJSMap.js +0 -65
  325. package/core/k-cli/node_modules/yaml/dist/nodes/identity.d.ts +0 -23
  326. package/core/k-cli/node_modules/yaml/dist/nodes/identity.js +0 -53
  327. package/core/k-cli/node_modules/yaml/dist/nodes/toJS.d.ts +0 -29
  328. package/core/k-cli/node_modules/yaml/dist/nodes/toJS.js +0 -39
  329. package/core/k-cli/node_modules/yaml/dist/options.d.ts +0 -350
  330. package/core/k-cli/node_modules/yaml/dist/parse/cst-scalar.d.ts +0 -64
  331. package/core/k-cli/node_modules/yaml/dist/parse/cst-scalar.js +0 -218
  332. package/core/k-cli/node_modules/yaml/dist/parse/cst-stringify.d.ts +0 -8
  333. package/core/k-cli/node_modules/yaml/dist/parse/cst-stringify.js +0 -63
  334. package/core/k-cli/node_modules/yaml/dist/parse/cst-visit.d.ts +0 -39
  335. package/core/k-cli/node_modules/yaml/dist/parse/cst-visit.js +0 -99
  336. package/core/k-cli/node_modules/yaml/dist/parse/cst.d.ts +0 -109
  337. package/core/k-cli/node_modules/yaml/dist/parse/cst.js +0 -112
  338. package/core/k-cli/node_modules/yaml/dist/parse/lexer.d.ts +0 -87
  339. package/core/k-cli/node_modules/yaml/dist/parse/lexer.js +0 -719
  340. package/core/k-cli/node_modules/yaml/dist/parse/line-counter.d.ts +0 -22
  341. package/core/k-cli/node_modules/yaml/dist/parse/line-counter.js +0 -41
  342. package/core/k-cli/node_modules/yaml/dist/parse/parser.d.ts +0 -84
  343. package/core/k-cli/node_modules/yaml/dist/parse/parser.js +0 -972
  344. package/core/k-cli/node_modules/yaml/dist/public-api.d.ts +0 -44
  345. package/core/k-cli/node_modules/yaml/dist/public-api.js +0 -107
  346. package/core/k-cli/node_modules/yaml/dist/schema/Schema.d.ts +0 -17
  347. package/core/k-cli/node_modules/yaml/dist/schema/Schema.js +0 -39
  348. package/core/k-cli/node_modules/yaml/dist/schema/common/map.d.ts +0 -2
  349. package/core/k-cli/node_modules/yaml/dist/schema/common/map.js +0 -19
  350. package/core/k-cli/node_modules/yaml/dist/schema/common/null.d.ts +0 -4
  351. package/core/k-cli/node_modules/yaml/dist/schema/common/null.js +0 -17
  352. package/core/k-cli/node_modules/yaml/dist/schema/common/seq.d.ts +0 -2
  353. package/core/k-cli/node_modules/yaml/dist/schema/common/seq.js +0 -19
  354. package/core/k-cli/node_modules/yaml/dist/schema/common/string.d.ts +0 -2
  355. package/core/k-cli/node_modules/yaml/dist/schema/common/string.js +0 -16
  356. package/core/k-cli/node_modules/yaml/dist/schema/core/bool.d.ts +0 -4
  357. package/core/k-cli/node_modules/yaml/dist/schema/core/bool.js +0 -21
  358. package/core/k-cli/node_modules/yaml/dist/schema/core/float.d.ts +0 -4
  359. package/core/k-cli/node_modules/yaml/dist/schema/core/float.js +0 -47
  360. package/core/k-cli/node_modules/yaml/dist/schema/core/int.d.ts +0 -4
  361. package/core/k-cli/node_modules/yaml/dist/schema/core/int.js +0 -42
  362. package/core/k-cli/node_modules/yaml/dist/schema/core/schema.d.ts +0 -1
  363. package/core/k-cli/node_modules/yaml/dist/schema/core/schema.js +0 -25
  364. package/core/k-cli/node_modules/yaml/dist/schema/json/schema.d.ts +0 -2
  365. package/core/k-cli/node_modules/yaml/dist/schema/json/schema.js +0 -64
  366. package/core/k-cli/node_modules/yaml/dist/schema/json-schema.d.ts +0 -69
  367. package/core/k-cli/node_modules/yaml/dist/schema/tags.d.ts +0 -48
  368. package/core/k-cli/node_modules/yaml/dist/schema/tags.js +0 -99
  369. package/core/k-cli/node_modules/yaml/dist/schema/types.d.ts +0 -92
  370. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/binary.d.ts +0 -2
  371. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/binary.js +0 -70
  372. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/bool.d.ts +0 -7
  373. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/bool.js +0 -29
  374. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/float.d.ts +0 -4
  375. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/float.js +0 -50
  376. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/int.d.ts +0 -5
  377. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/int.js +0 -76
  378. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/merge.d.ts +0 -9
  379. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/merge.js +0 -68
  380. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/omap.d.ts +0 -22
  381. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/omap.js +0 -77
  382. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/pairs.d.ts +0 -10
  383. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/pairs.js +0 -82
  384. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/schema.d.ts +0 -1
  385. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/schema.js +0 -41
  386. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/set.d.ts +0 -28
  387. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/set.js +0 -96
  388. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/timestamp.d.ts +0 -6
  389. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js +0 -105
  390. package/core/k-cli/node_modules/yaml/dist/stringify/foldFlowLines.d.ts +0 -34
  391. package/core/k-cli/node_modules/yaml/dist/stringify/foldFlowLines.js +0 -151
  392. package/core/k-cli/node_modules/yaml/dist/stringify/stringify.d.ts +0 -21
  393. package/core/k-cli/node_modules/yaml/dist/stringify/stringify.js +0 -132
  394. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyCollection.d.ts +0 -17
  395. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyCollection.js +0 -155
  396. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyComment.d.ts +0 -10
  397. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyComment.js +0 -24
  398. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyDocument.d.ts +0 -4
  399. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyDocument.js +0 -87
  400. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyNumber.d.ts +0 -2
  401. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyNumber.js +0 -26
  402. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyPair.d.ts +0 -3
  403. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyPair.js +0 -152
  404. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyString.d.ts +0 -9
  405. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyString.js +0 -338
  406. package/core/k-cli/node_modules/yaml/dist/test-events.d.ts +0 -4
  407. package/core/k-cli/node_modules/yaml/dist/test-events.js +0 -134
  408. package/core/k-cli/node_modules/yaml/dist/util.d.ts +0 -16
  409. package/core/k-cli/node_modules/yaml/dist/util.js +0 -28
  410. package/core/k-cli/node_modules/yaml/dist/visit.d.ts +0 -102
  411. package/core/k-cli/node_modules/yaml/dist/visit.js +0 -236
  412. package/core/k-cli/node_modules/yaml/package.json +0 -97
  413. package/core/k-cli/node_modules/yaml/util.js +0 -2
@@ -1,42 +1,14 @@
1
1
  /**
2
- * Formation Prompt Builder
3
- * 根據陣型和技能定義組裝 prompt
4
- *
5
- * 每個陣型有不同的 prompt 結構:
6
- * - unit: 角色 + 行為 + 任務 + 工作流程 + 輸出
7
- * - solo: 角色 + 視角列表 + 行為 + 任務 + 工作流程 + 輸出
8
- * - squad: 為每個成員分別組裝 prompt(含分工 scope)
9
- * - legion: 為 team lead + 每個成員分別組裝 prompt(含溝通規則)
2
+ * Subagent Prompt Builder
3
+ * Assembles the prompt for the one live dispatch path:
4
+ * node .formation/tools/dispatch.js --skill=do --task=X --dispatch-mode=subagent
5
+ * Task card + commit command + verify/report, behind a transport prefix
6
+ * (SUBAGENT_PREFIX / SUBAGENT_PREFIX_DIRECT / buildSubagentWorktreePrefix).
10
7
  */
11
8
 
12
- // ─────────────────────────────────────────
13
- // Memory Loader(記憶系統整合)
14
- // ─────────────────────────────────────────
15
- let memoryLoader = null;
16
- try {
17
- memoryLoader = require('./memory-loader');
18
- } catch { /* memory-loader not available — graceful degradation */ }
19
-
20
- // ─────────────────────────────────────────
21
- // Shared Principles Loader(共享原則注入)
22
- // ─────────────────────────────────────────
23
9
  const fs = require('fs');
24
10
  const path = require('path');
25
11
  const { buildWorktreeGitAddCommand } = require('./utils');
26
- // TASK_2104: Alaya relevance gate (cosine floor + stack match) extracted to
27
- // a dependency-free module so task-api.js's echoAlayaLessons can share it
28
- // without pulling in Formation-only deps. Re-exported below unchanged so
29
- // prompt.test.js's imports keep working.
30
- const {
31
- buildTaskContextQuery,
32
- deriveTaskStack,
33
- deriveBijaStack,
34
- passesAlayaRelevanceGate,
35
- dedupBijas,
36
- ALAYA_MIN_COSINE_DEFAULT,
37
- ALAYA_STACK_OVERRIDE_COSINE,
38
- ALAYA_CANDIDATE_POOL,
39
- } = require('./alaya-gate');
40
12
 
41
13
  // Shared core-path resolver (validates existsSync+isDirectory, falls back to KENAZ_SOURCE_DIR)
42
14
  let _resolveCoreDir = null;
@@ -44,53 +16,6 @@ try {
44
16
  _resolveCoreDir = require(path.join(__dirname, '..', '..', '.claude', 'hooks', 'core-resolve')).resolveCoreDir;
45
17
  } catch { /* graceful degradation — older installs without the export */ }
46
18
 
47
- let _sharedPrinciplesCache = null;
48
-
49
- // ─────────────────────────────────────────
50
- // Shared Module Tag Rules(模組自動注入規則)
51
- // ─────────────────────────────────────────
52
-
53
- // TASK_2371: opt-in only — a task must carry the explicit review tag. No keyword
54
- // match on generic tags (auth/test/...) or chart warnings; a 100-line OWASP
55
- // block on an unrelated auth card was pure noise.
56
- const MODULE_TAG_RULES = [
57
- { tags: ['security-review'], module: 'security.yaml' },
58
- { tags: ['testing-review'], module: 'testing.yaml' },
59
- { tags: ['code-review'], module: 'code_review.yaml' },
60
- ];
61
-
62
- /**
63
- * Resolve which shared modules to inject based on task tags and optional chart context.
64
- * @param {string|object} taskData - task JSON string or parsed object
65
- * @param {string|null} [chartPath] - optional path to chart.json for richer context
66
- * @param {string|null} [corePath] - KenazAI root path (defaults to two levels up from __dirname)
67
- * @returns {string[]} list of absolute module file paths to inject
68
- */
69
- function resolveModules(taskData, chartPath, corePath) {
70
- const modules = new Set();
71
- let taskTags = [];
72
-
73
- try {
74
- const parsed = typeof taskData === 'string' ? JSON.parse(taskData) : taskData;
75
- if (parsed) {
76
- taskTags = (parsed.tags || []).map(t => String(t).toLowerCase());
77
- }
78
- } catch { /* not JSON */ }
79
-
80
- // Tag-based matching
81
- for (const rule of MODULE_TAG_RULES) {
82
- if (rule.tags.some(t => taskTags.includes(t))) {
83
- modules.add(rule.module);
84
- }
85
- }
86
-
87
- if (modules.size === 0) return [];
88
-
89
- const root = corePath || path.resolve(__dirname, '..', '..');
90
- const modulesDir = path.join(root, '.formation', 'roles', '_shared', 'modules');
91
- return [...modules].map(m => path.join(modulesDir, m));
92
- }
93
-
94
19
  /**
95
20
  * Resolve the KenazAI core path from a sub-project's project.yaml.
96
21
  * Returns null when projectPath is absent or IS the core project.
@@ -122,35 +47,6 @@ function resolveCorePath(projectPath) {
122
47
  } catch { return null; }
123
48
  }
124
49
 
125
- function loadSharedPrinciples(corePath) {
126
- if (_sharedPrinciplesCache) return _sharedPrinciplesCache;
127
-
128
- const principlesPath = path.join(
129
- corePath || path.resolve(__dirname, '..', '..'),
130
- '.specialist', 'shared', 'principles.yaml'
131
- );
132
-
133
- try {
134
- if (!fs.existsSync(principlesPath)) return null;
135
- const content = fs.readFileSync(principlesPath, 'utf-8');
136
-
137
- // 提取 differential_over_intuition 區塊
138
- const ddxMatch = content.match(
139
- /differential_over_intuition:[\s\S]*?(?=\n# ═|$)/
140
- );
141
- if (!ddxMatch) return null;
142
-
143
- _sharedPrinciplesCache = ddxMatch[0].trim();
144
- return _sharedPrinciplesCache;
145
- } catch {
146
- return null;
147
- }
148
- }
149
-
150
- // ─────────────────────────────────────────
151
- // TASK_1998 R2 -> two-axis prompt policy (2026-09-22 user decision)
152
- // ─────────────────────────────────────────
153
-
154
50
  /**
155
51
  * Extract the card's `rigor` field from taskData (JSON string or object).
156
52
  * Returns null when taskData is absent, unparseable, or has no rigor field
@@ -183,78 +79,17 @@ function extractComplexity(taskData) {
183
79
  }
184
80
 
185
81
  /**
186
- * TASK_1998 two-axis prompt policy (2026-09-22 user decision, replaces the
187
- * single rigor-gated R2 diet switch):
188
- *
189
- * CONTEXT volume — driven by `complexity` (how much the task touches):
190
- * simple -> no Related Files Preview, no Known Failures, no Project Memory.
191
- * medium -> Known Failures on; no Preview; no Project Memory.
192
- * complex -> Related Files Preview on; Known Failures + Project Memory on.
193
- * (TASK_2371: Cross-Project Context (Cortex) and Episodic Memory are gone
194
- * for every complexity — stale / unverified outcomes.)
195
- *
196
- * PROCESS strictness — driven by `rigor`:
197
- * low -> the 8-line "## Working contract" only; rune inscription optional.
198
- * medium/high -> Working contract + "## Progress Note (brief)" + the
199
- * rune-inscribe line (TASK_2371: high no longer has a
200
- * mandatory recall/inscribe/Cognitive Checkpoint ritual).
201
- *
202
- * This is the ONLY place either axis branches into its behaviour; every call
203
- * site below reads flags off the returned profile instead of re-testing
204
- * rigor/complexity itself.
205
- *
206
- * Backwards compatible: a bare string (or null/undefined) is treated as the
207
- * legacy single-axis call — `rigor` — and `complexity` defaults to 'medium'.
208
- * @param {string|{rigor?: string|null, complexity?: string|null}|null|undefined} rigorOrOptions
82
+ * Context volume for the dispatch prompt, driven by the card's `complexity`:
83
+ * only a complex card gets the Related Files Preview (dispatch.js reads
84
+ * skipRelatedFilesPreview). Accepts {complexity} or a bare complexity string.
85
+ * @param {string|{complexity?: string|null}|null|undefined} complexityOrOptions
209
86
  */
210
- function promptProfile(rigorOrOptions) {
211
- let rawRigor;
212
- let rawComplexity;
213
- if (rigorOrOptions && typeof rigorOrOptions === 'object') {
214
- rawRigor = rigorOrOptions.rigor;
215
- rawComplexity = rigorOrOptions.complexity;
216
- } else {
217
- // Legacy string-rigor call — complexity defaults to 'medium'.
218
- rawRigor = rigorOrOptions;
219
- rawComplexity = 'medium';
220
- }
221
-
222
- const rigor = (rawRigor === 'low' || rawRigor === 'high') ? rawRigor : 'medium';
223
- const complexity = (rawComplexity === 'simple' || rawComplexity === 'complex') ? rawComplexity : 'medium';
224
-
225
- const CONTEXT_BY_COMPLEXITY = {
226
- simple: {
227
- skipRelatedFilesPreview: true,
228
- skipKnownFailures: true,
229
- skipProjectMemory: true,
230
- },
231
- medium: {
232
- skipRelatedFilesPreview: true,
233
- skipKnownFailures: false,
234
- skipProjectMemory: true,
235
- },
236
- complex: {
237
- skipRelatedFilesPreview: false,
238
- skipKnownFailures: false,
239
- skipProjectMemory: false,
240
- },
241
- };
242
-
243
- // ritualTier drives appendCognitiveCheckpoint:
244
- // 'low' -> Working contract only; 'medium' -> Working contract + short
245
- // progress note + mandatory inscribe line; 'high' -> full ritual (compactRitual=false).
246
- const PROCESS_BY_RIGOR = {
247
- low: { compactRitual: true, ritualTier: 'low' },
248
- medium: { compactRitual: true, ritualTier: 'medium' },
249
- high: { compactRitual: true, ritualTier: 'medium' }, // TASK_2371: high == medium ritual
250
- };
251
-
252
- return {
253
- rigor,
254
- complexity,
255
- ...CONTEXT_BY_COMPLEXITY[complexity],
256
- ...PROCESS_BY_RIGOR[rigor],
257
- };
87
+ function promptProfile(complexityOrOptions) {
88
+ const raw = complexityOrOptions && typeof complexityOrOptions === 'object'
89
+ ? complexityOrOptions.complexity
90
+ : complexityOrOptions;
91
+ const complexity = (raw === 'simple' || raw === 'complex') ? raw : 'medium';
92
+ return { complexity, skipRelatedFilesPreview: complexity !== 'complex' };
258
93
  }
259
94
 
260
95
  /**
@@ -287,17 +122,6 @@ function splitPromptSections(promptText) {
287
122
  return sections;
288
123
  }
289
124
 
290
- // ─────────────────────────────────────────
291
- // 主入口
292
- // ─────────────────────────────────────────
293
-
294
- /**
295
- * 根據陣型分發到對應的 prompt builder
296
- * @param {object} skillDef - parsed YAML
297
- * @param {string} taskInput - 任務描述或 ID
298
- * @param {string|null} taskData - 從 .collaboration 載入的任務 YAML
299
- * @returns {string|string[]} 單一 prompt(unit/solo)或 prompt 陣列(squad)
300
- */
301
125
  // TASK_2374: append-only usage counter (measure-first; cut decisions after 14 days of data).
302
126
  // Env KENAZ_USAGE_COUNTERS_FILE overrides the target (tests). Never throws.
303
127
  function recordSkillUsage(skillDef, options = {}) {
@@ -314,62 +138,13 @@ function recordSkillUsage(skillDef, options = {}) {
314
138
 
315
139
  function buildPrompt(skillDef, taskInput, taskData, options = {}) {
316
140
  const formation = skillDef.formation || 'unit';
317
- recordSkillUsage(skillDef, options);
318
-
319
- const builders = {
320
- unit: buildUnitPrompt,
321
- solo: buildSoloPrompt,
322
- squad: buildSquadPrompts,
323
- legion: buildLegionPrompts,
324
- legacy: buildLegacyPrompt,
325
- };
326
-
327
- // TASK_2391: worktree/direct subagent dispatch gets the short template, not the
328
- // full Formation prompt. squad/legion/legacy and non-subagent paths are unchanged.
329
- if (options.isSubagent && (formation === 'unit' || formation === 'solo')) {
330
- return buildSubagentPrompt(skillDef, taskInput, taskData, options);
331
- }
332
-
333
- const builder = builders[formation];
334
- if (!builder) {
335
- throw new Error(`Unknown formation: ${formation}. Expected: ${Object.keys(builders).join(', ')}`);
141
+ if (formation === 'squad' || formation === 'legion') {
142
+ throw new Error(`Formation '${formation}' (skill '${skillDef.name || 'unknown'}') is retired: only unit/solo skills can be dispatched (--dispatch-mode=subagent).`);
336
143
  }
337
-
338
- return builder(skillDef, taskInput, taskData, options);
339
- }
340
-
341
- // ─────────────────────────────────────────
342
- // Unit:單一角色,直接執行
343
- // ─────────────────────────────────────────
344
-
345
- function buildUnitPrompt(skillDef, taskInput, taskData, options = {}) {
346
- const lines = [];
347
-
348
- lines.push(`## Role`);
349
- lines.push(skillDef.role.identity);
350
- lines.push(`Priority: ${skillDef.role.priority}`);
351
- lines.push('');
352
-
353
- appendProjectContext(lines, options);
354
- appendFormationPreamble(lines, taskData, options);
355
- appendYggdrasilContext(lines, options.yggdrasilContext);
356
- appendMemoryContext(lines, taskData, options);
357
- appendAlayaLessons(lines, taskData);
358
- appendSharedModules(lines, taskData, options);
359
- appendBehavior(lines, skillDef);
360
- appendTask(lines, taskInput, taskData, options);
361
- appendCognitiveRecall(lines, taskData, options);
362
- appendWorkflow(lines, skillDef, options, taskData);
363
- appendOutput(lines, skillDef);
364
- appendCognitiveCheckpoint(lines, taskData, options);
365
-
366
- return lines.join('\n');
144
+ recordSkillUsage(skillDef, options);
145
+ return buildSubagentPrompt(skillDef, taskInput, taskData, options);
367
146
  }
368
147
 
369
- // ─────────────────────────────────────────
370
- // Subagent:短範本(TASK_2391)
371
- // ─────────────────────────────────────────
372
-
373
148
  /**
374
149
  * Short prompt for Agent-tool subagent dispatch (both harnesses): task card,
375
150
  * commit command, verification expectation, report format. The worktree hard
@@ -393,6 +168,8 @@ function buildSubagentPrompt(skillDef, taskInput, taskData, options = {}) {
393
168
  appendRustCheckReminder(lines, taskData, options, isIsolated);
394
169
 
395
170
  lines.push('## Verify and report');
171
+ // Ponytail A/B 2026-10-10: the arm without this line broke an existing fixture test it never ran.
172
+ lines.push('Before editing, grep the callers, tests and fixtures your change reaches; fix what it breaks. Least code that completes the card: reuse existing helpers, no unrequested abstraction, option or duplicate implementation.');
396
173
  lines.push('Run the tests the card names (narrow, not the full suite); say what you did not verify.');
397
174
  const onBlocked = skillDef.collaboration && skillDef.collaboration.on_blocked;
398
175
  if (onBlocked && onBlocked.run) {
@@ -411,516 +188,20 @@ function extractTaskId(taskData) {
411
188
  } catch { return null; }
412
189
  }
413
190
 
414
- // ─────────────────────────────────────────
415
- // Solo:一個 agent 裝備多視角,自主切換
416
- // ─────────────────────────────────────────
417
-
418
- function buildSoloPrompt(skillDef, taskInput, taskData, options = {}) {
419
- const lines = [];
420
-
421
- lines.push(`## Role`);
422
- lines.push(skillDef.role.identity);
423
- lines.push(`Priority: ${skillDef.role.priority}`);
424
- lines.push('');
425
-
426
- appendProjectContext(lines, options);
427
- appendFormationPreamble(lines, taskData, options);
428
- appendYggdrasilContext(lines, options.yggdrasilContext);
429
- appendMemoryContext(lines, taskData, options);
430
- appendAlayaLessons(lines, taskData);
431
-
432
- // 分析原則(Solo strategy 專用)
433
- if (skillDef.analysis_principles && skillDef.analysis_principles.length > 0) {
434
- lines.push(`## Analysis Principles`);
435
- lines.push('');
436
- for (const p of skillDef.analysis_principles) {
437
- lines.push(`### ${p.name}`);
438
- lines.push(p.description.trim());
439
- lines.push('');
440
- }
441
- }
442
-
443
- // 可用視角
444
- if (skillDef.perspectives && skillDef.perspectives.length > 0) {
445
- lines.push(`## Available Perspectives`);
446
- lines.push('');
447
- lines.push('你隨時可以切換到以下任一視角。根據分析過程中的需要自主決定。');
448
- lines.push('');
449
-
450
- for (const p of skillDef.perspectives) {
451
- lines.push(`### ${p.name}`);
452
- lines.push(`- 關注:${p.lens}`);
453
- lines.push(`- 何時切換:${p.trigger}`);
454
- lines.push('');
455
- }
456
- }
457
-
458
- appendBehavior(lines, skillDef);
459
- appendSharedPrinciples(lines, options);
460
- appendTask(lines, taskInput, taskData, options);
461
- appendCognitiveRecall(lines, taskData, options);
462
- appendWorkflow(lines, skillDef, options, taskData);
463
- appendOutput(lines, skillDef);
464
- appendCognitiveCheckpoint(lines, taskData, options);
465
-
466
- return lines.join('\n');
467
- }
468
-
469
- // ─────────────────────────────────────────
470
- // Squad:為每個成員分別組裝 prompt
471
- // ─────────────────────────────────────────
472
-
473
- /**
474
- * Squad 回傳 prompt 陣列,每個成員一份
475
- * @returns {{ memberId: string, prompt: string, skill: string }[]}
476
- */
477
- function buildSquadPrompts(skillDef, taskInput, taskData, options = {}) {
478
- const members = skillDef.members || [];
479
- if (members.length === 0) {
480
- throw new Error('Squad YAML must define `members` array');
481
- }
482
-
483
- // 組裝共享上下文(注入到每個成員 prompt)
484
- const sharedBlock = buildSharedContextBlock(skillDef);
485
-
486
- return members.map(member => {
487
- const lines = [];
488
-
489
- // 成員角色
490
- lines.push(`## Role`);
491
- lines.push(typeof member.role === 'string' ? member.role.trim() : `你是 Squad 的一員:${member.name}`);
492
- lines.push('');
493
-
494
- // 分析模式(mode_behavior 注入)
495
- const analysisMode = options.analysisMode;
496
- if (analysisMode && member.mode_behavior && member.mode_behavior[analysisMode]) {
497
- const modeConfig = skillDef.analysis_mode?.modes?.[analysisMode];
498
- lines.push(`## Analysis Mode: ${analysisMode}`);
499
- if (modeConfig?.description) lines.push(modeConfig.description.trim());
500
- lines.push('');
501
- lines.push('**你在此模式下的立場調整**:');
502
- lines.push(member.mode_behavior[analysisMode].trim());
503
- lines.push('');
504
- }
505
-
506
- // 核心原則(principles 注入 — 所有成員共用的硬規則)
507
- if (skillDef.principles) {
508
- lines.push(`## Principles (絕對遵守)`);
509
- if (skillDef.principles.focus) {
510
- lines.push(skillDef.principles.focus.trim());
511
- lines.push('');
512
- }
513
- if (skillDef.principles.forbidden && skillDef.principles.forbidden.length > 0) {
514
- lines.push('**禁止 (違反即失敗)**:');
515
- for (const f of skillDef.principles.forbidden) lines.push(`- ❌ ${f}`);
516
- lines.push('');
517
- }
518
- if (skillDef.principles.required && skillDef.principles.required.length > 0) {
519
- lines.push('**必須遵守**:');
520
- for (const r of skillDef.principles.required) lines.push(`- ✅ ${r}`);
521
- lines.push('');
522
- }
523
- }
524
-
525
- // 成員身份
526
- lines.push(`## Identity`);
527
- lines.push(`你是 Squad 的成員 **${member.name}** (id: ${member.id || member.name})。`);
528
- lines.push(`你與其他成員並行工作,各自獨立,不能互相通訊。`);
529
- lines.push(`完成你負責的部分後回報結果,Lead 會負責整合。`);
530
- lines.push('');
531
-
532
- // 成員的 scope(負責範圍)— 支援 include/exclude 格式
533
- if (member.scope) {
534
- lines.push(`## Scope (你只能碰這些檔案)`);
535
- if (member.scope.include || member.scope.exclude) {
536
- if (member.scope.include) {
537
- lines.push(`Include:`);
538
- const includes = Array.isArray(member.scope.include) ? member.scope.include : [member.scope.include];
539
- for (const p of includes) lines.push(` - ${p}`);
540
- }
541
- if (member.scope.exclude) {
542
- lines.push(`Exclude:`);
543
- const excludes = Array.isArray(member.scope.exclude) ? member.scope.exclude : [member.scope.exclude];
544
- for (const p of excludes) lines.push(` - ${p}`);
545
- }
546
- } else if (Array.isArray(member.scope)) {
547
- for (const s of member.scope) lines.push(`- ${s}`);
548
- } else {
549
- lines.push(member.scope);
550
- }
551
- lines.push('');
552
- }
553
-
554
- // Yggdrasil network context
555
- appendYggdrasilContext(lines, options.yggdrasilContext);
556
- appendMemoryContext(lines, taskData, options);
557
- appendAlayaLessons(lines, taskData);
558
- appendFormationPreamble(lines, taskData, options);
559
-
560
- // 共享上下文
561
- if (sharedBlock) {
562
- lines.push(sharedBlock);
563
- lines.push('');
564
- }
565
-
566
- // 行為準則(squad 全局 + 成員級別)
567
- const behaviors = [
568
- ...(skillDef.behavior || []),
569
- ...(member.behavior || [])
570
- ];
571
- if (behaviors.length > 0) {
572
- lines.push(`## Behavior`);
573
- for (const b of behaviors) lines.push(`- ${b}`);
574
- lines.push('');
575
- }
576
-
577
- // 任務
578
- appendTask(lines, taskInput, taskData, options);
579
- appendCognitiveRecall(lines, taskData, options);
580
-
581
- // 成員專屬工作流程或全局工作流程
582
- if (member.workflow) {
583
- lines.push(`## Workflow`);
584
- for (const step of member.workflow) lines.push(`- ${step}`);
585
- lines.push('');
586
- appendDispatchOverrides(lines, taskData); // TASK_1820: same post-envelope channel as appendWorkflow
587
- } else {
588
- appendWorkflow(lines, skillDef, options, taskData);
589
- }
590
-
591
- // 輸出
592
- if (member.output) {
593
- lines.push(`## Expected Output`);
594
- lines.push(typeof member.output === 'string' ? member.output.trim() : JSON.stringify(member.output));
595
- } else {
596
- appendOutput(lines, skillDef);
597
- }
598
-
599
- appendCognitiveCheckpoint(lines, taskData, options);
600
-
601
- return {
602
- memberId: member.id || member.name,
603
- prompt: lines.join('\n'),
604
- skill: member.skill || 'do'
605
- };
606
- });
607
- }
608
-
609
- /**
610
- * 組裝 shared_context 區塊(Squad 成員共用的約定)
611
- */
612
- function buildSharedContextBlock(skillDef) {
613
- if (!skillDef.shared_context) return null;
614
-
615
- const lines = [];
616
- lines.push(`## Shared Context (所有成員共享的約定)`);
617
-
618
- if (skillDef.shared_context.description) {
619
- lines.push(skillDef.shared_context.description.trim());
620
- }
621
-
622
- if (skillDef.shared_context.includes) {
623
- lines.push('');
624
- const includes = skillDef.shared_context.includes;
625
- if (typeof includes === 'object' && !Array.isArray(includes)) {
626
- for (const [key, val] of Object.entries(includes)) {
627
- lines.push(`### ${key}`);
628
- lines.push(typeof val === 'string' ? val.trim() : JSON.stringify(val));
629
- lines.push('');
630
- }
631
- } else if (Array.isArray(includes)) {
632
- for (const item of includes) {
633
- if (typeof item === 'string') {
634
- lines.push(`- ${item}`);
635
- } else if (typeof item === 'object') {
636
- for (const [key, val] of Object.entries(item)) {
637
- lines.push(`### ${key}`);
638
- lines.push(typeof val === 'string' ? val.trim() : JSON.stringify(val));
639
- lines.push('');
640
- }
641
- }
642
- }
643
- }
644
- }
645
-
646
- return lines.join('\n');
647
- }
648
-
649
- // ─────────────────────────────────────────
650
- // Legion:為 team lead 和成員組裝 prompt
651
- // ─────────────────────────────────────────
652
-
653
- /**
654
- * Legion 回傳 team config + 每個成員的 prompt
655
- * @returns {{ teamName: string, teamDesc: string, lead: { prompt: string }, members: { name: string, prompt: string }[] }}
656
- */
657
- function buildLegionPrompts(skillDef, taskInput, taskData, options = {}) {
658
- const team = skillDef.team || {};
659
- // 支援 members: [...] 和 members: { roles: [...] } 兩種格式
660
- const rawMembers = skillDef.members || [];
661
- const members = Array.isArray(rawMembers) ? rawMembers : (rawMembers.roles || []);
662
-
663
- // 團隊名稱:優先用 team.name,fallback 到 skillDef.name
664
- const teamName = team.name || skillDef.name || 'Legion';
665
-
666
- // Team lead prompt
667
- const leadLines = [];
668
- leadLines.push(`## Role`);
669
- leadLines.push(`你是 ${teamName} 的 Team Lead。`);
670
- leadLines.push(skillDef.role?.identity || '');
671
- leadLines.push('');
672
- leadLines.push(`## Team Members`);
673
- for (const m of members) {
674
- leadLines.push(`- **${m.name}**: ${m.role || m.description || ''}`);
675
- }
676
- leadLines.push('');
677
-
678
- if (skillDef.communication) {
679
- leadLines.push(`## Communication Rules`);
680
- const comm = skillDef.communication;
681
- if (Array.isArray(comm)) {
682
- for (const rule of comm) leadLines.push(`- ${rule}`);
683
- } else if (typeof comm === 'object') {
684
- if (comm.principles) {
685
- leadLines.push(`### Principles`);
686
- for (const p of comm.principles) leadLines.push(`- ${p}`);
687
- leadLines.push('');
688
- }
689
- if (comm.patterns) {
690
- leadLines.push(`### Patterns`);
691
- for (const p of comm.patterns) {
692
- leadLines.push(`- **${p.name}**: ${p.who} — ${p.content || ''}`);
693
- }
694
- leadLines.push('');
695
- }
696
- if (comm.broadcast_policy) {
697
- leadLines.push(`### Broadcast Policy`);
698
- leadLines.push(comm.broadcast_policy.trim());
699
- leadLines.push('');
700
- }
701
- }
702
- leadLines.push('');
703
- }
704
-
705
- appendYggdrasilContext(leadLines, options.yggdrasilContext);
706
- appendMemoryContext(leadLines, taskData, options);
707
- appendAlayaLessons(leadLines, taskData);
708
- appendFormationPreamble(leadLines, taskData, options);
709
- appendTask(leadLines, taskInput, taskData, options);
710
- appendCognitiveRecall(leadLines, taskData);
711
-
712
- if (skillDef.lifecycle) {
713
- leadLines.push(`## Lifecycle`);
714
- // lifecycle 可能是 { phases: [...] } 或直接是陣列
715
- const phases = Array.isArray(skillDef.lifecycle)
716
- ? skillDef.lifecycle
717
- : (skillDef.lifecycle.phases || []);
718
- for (const step of phases) {
719
- if (typeof step === 'string') {
720
- leadLines.push(`- ${step}`);
721
- } else {
722
- leadLines.push(`### ${step.phase || step.step}`);
723
- if (step.description) leadLines.push(step.description.trim());
724
- if (step.lead_actions) {
725
- leadLines.push('Lead actions:');
726
- for (const a of step.lead_actions) leadLines.push(`- ${a}`);
727
- }
728
- if (step.exit_criteria) {
729
- leadLines.push('Exit criteria:');
730
- for (const c of step.exit_criteria) leadLines.push(`- ${c}`);
731
- }
732
- leadLines.push('');
733
- }
734
- }
735
- leadLines.push('');
736
- }
737
-
738
- appendOutput(leadLines, skillDef);
739
- appendCognitiveCheckpoint(leadLines, taskData, options);
740
-
741
- // Member prompts
742
- // 優先使用 YAML 的 member_prompts 模板(如 forge 的 designer/implementer/reviewer)
743
- const promptTemplates = skillDef.member_prompts || {};
744
-
745
- const memberPrompts = members.map(member => {
746
- const lines = [];
747
-
748
- // 如果有預寫好的 prompt 模板,直接用
749
- const template = promptTemplates[member.id];
750
- if (template) {
751
- lines.push(template.trim());
752
- lines.push('');
753
- } else {
754
- // 自動生成
755
- lines.push(`## Role`);
756
- lines.push(member.role || member.description?.trim() || `你是 ${teamName} 團隊的 ${member.name}。`);
757
- lines.push('');
758
-
759
- if (member.responsibilities) {
760
- lines.push(`## Responsibilities`);
761
- for (const r of member.responsibilities) lines.push(`- ${r}`);
762
- lines.push('');
763
- }
764
-
765
- if (member.skills) {
766
- lines.push(`## Skills`);
767
- for (const s of member.skills) lines.push(`- ${s}`);
768
- lines.push('');
769
- }
770
-
771
- if (member.scope) {
772
- lines.push(`## Scope`);
773
- if (Array.isArray(member.scope)) {
774
- for (const s of member.scope) lines.push(`- ${s}`);
775
- } else {
776
- lines.push(member.scope);
777
- }
778
- lines.push('');
779
- }
780
- }
781
-
782
- lines.push(`## Team Context`);
783
- lines.push(`你是 ${teamName} 團隊的成員。可以透過 SendMessage 和其他成員溝通。`);
784
- lines.push(`Team Lead 會分配任務給你,完成後回報。如果被阻塞,主動溝通。`);
785
- lines.push('');
786
-
787
- appendYggdrasilContext(lines, options.yggdrasilContext);
788
- appendFormationPreamble(lines, taskData, options);
789
- appendTask(lines, taskInput, taskData, options);
790
- appendCognitiveRecall(lines, taskData, options);
791
- appendCognitiveCheckpoint(lines, taskData, options);
792
-
793
- return {
794
- name: member.name,
795
- agentType: member.agent_type || 'general-purpose',
796
- prompt: lines.join('\n')
797
- };
798
- });
799
-
800
- return {
801
- teamName: teamName.toLowerCase().replace(/\s+/g, '-'),
802
- teamDesc: team.description || skillDef.description || '',
803
- lead: { prompt: leadLines.join('\n') },
804
- members: memberPrompts
805
- };
806
- }
807
-
808
- // ─────────────────────────────────────────
809
- // Legacy:排程器模式(不建 prompt,直接走 scheduler loop)
810
- // ─────────────────────────────────────────
811
-
812
- /**
813
- * Legacy 不需要組裝 subagent prompt。
814
- * 回傳 scheduler 配置摘要(供 dry-run 顯示用)。
815
- */
816
- function buildLegacyPrompt(skillDef, taskInput, taskData, options = {}) {
817
- const lines = [];
818
- lines.push(`## Role`);
819
- lines.push(skillDef.role?.identity || 'You are a developer assigned to complete the task below.');
820
- lines.push('');
821
-
822
- appendBehavior(lines, skillDef);
823
- appendProjectContext(lines, options);
824
- appendYggdrasilContext(lines, options.yggdrasilContext);
825
- appendMemoryContext(lines, taskData, options);
826
- appendAlayaLessons(lines, taskData);
827
- appendTask(lines, taskInput, taskData, options);
828
- appendCognitiveRecall(lines, taskData, options);
829
-
830
- if (skillDef.collaboration?.workflow) {
831
- appendWorkflow(lines, skillDef, options, taskData);
832
- }
833
-
834
- appendCognitiveCheckpoint(lines, taskData, options);
835
-
836
- return lines.join('\n');
837
- }
838
-
839
- // ─────────────────────────────────────────
840
- // 共用片段
841
- // ─────────────────────────────────────────
842
-
843
- /**
844
- * Append context-aware shared modules to prompt.
845
- * Reads .formation/roles/_shared/modules/*.yaml based on task tags / chart context.
846
- * Inserted before appendBehavior().
847
- */
848
- function appendSharedModules(lines, taskData, options = {}) {
849
- const corePath = options.corePath || resolveCorePath(options.projectPath) || undefined;
850
- const chartPath = options.chartPath || null;
851
- const modulePaths = resolveModules(taskData, chartPath, corePath);
852
- if (modulePaths.length === 0) return;
853
-
854
- for (const modPath of modulePaths) {
855
- try {
856
- if (!fs.existsSync(modPath)) continue;
857
- const raw = fs.readFileSync(modPath, 'utf-8');
858
-
859
- // Extract name and description for the section header
860
- const nameMatch = raw.match(/^name:\s*(.+)$/m);
861
- const descMatch = raw.match(/^description:\s*(.+)$/m);
862
- const moduleName = nameMatch ? nameMatch[1].trim() : path.basename(modPath, '.yaml');
863
- const moduleDesc = descMatch ? descMatch[1].trim() : '';
864
-
865
- lines.push(`## Shared Module: ${moduleName}`);
866
- if (moduleDesc) {
867
- lines.push(moduleDesc);
868
- lines.push('');
869
- }
870
-
871
- // Inject content section (raw YAML-formatted text, stripped of top-level key)
872
- const contentMatch = raw.match(/^content:\n([\s\S]*)$/m);
873
- if (contentMatch) {
874
- lines.push(contentMatch[1].trimEnd());
875
- } else {
876
- lines.push(raw.trimEnd());
877
- }
878
- lines.push('');
879
- } catch { /* graceful degradation — skip unreadable module */ }
880
- }
881
- }
882
-
883
- function appendBehavior(lines, skillDef) {
884
- if (skillDef.behavior && skillDef.behavior.length > 0) {
885
- lines.push(`## Behavior`);
886
- for (const b of skillDef.behavior) {
887
- lines.push(`- ${b}`);
888
- }
889
- lines.push('');
890
- }
891
- }
892
-
893
- /**
894
- * Append shared principles (DDx protocol) to prompt.
895
- * Loaded from .specialist/shared/principles.yaml.
896
- */
897
- function appendSharedPrinciples(lines, options = {}) {
898
- const corePath = options.corePath || undefined;
899
- const ddx = loadSharedPrinciples(corePath);
900
- if (!ddx) return;
901
-
902
- lines.push(`## Shared Principles — 鑑別診斷 (DDx)`);
903
- lines.push(`遇到 Bug 診斷、架構決策、結果驗證時,必須套用以下 protocol:`);
904
- lines.push('');
905
- lines.push(ddx);
906
- lines.push('');
907
- }
908
-
909
191
  /**
910
192
  * Append project context to prompt (multi-project support).
911
193
  * When projectPath is provided, injects context telling the agent
912
194
  * which project to operate on and where system files are.
913
195
  */
914
196
  function appendProjectContext(lines, options = {}) {
915
- const { projectPath, worktreePath } = options;
197
+ const { projectPath } = options;
916
198
 
917
199
  // Sub-project context — only when projectPath is not the core project
918
200
  if (projectPath) {
919
- const pathMod = require('path');
920
- const coreRoot = pathMod.resolve(__dirname, '..', '..');
201
+ const coreRoot = path.resolve(__dirname, '..', '..');
921
202
  const norm = p => p.replace(/[\\/]+$/, '').replace(/\\/g, '/').toLowerCase();
922
203
 
923
- if (norm(pathMod.resolve(projectPath)) !== norm(coreRoot)) {
204
+ if (norm(path.resolve(projectPath)) !== norm(coreRoot)) {
924
205
  lines.push(`## Project Context [internal — do NOT mention sub-project status to user]`);
925
206
  lines.push(`Working directory: ${projectPath}`);
926
207
  lines.push(`- Task files: ${projectPath}/.kenaz/tasks/`);
@@ -935,121 +216,13 @@ function appendProjectContext(lines, options = {}) {
935
216
  lines.push('');
936
217
  lines.push(`### KENAZ_CORE_PATH (resolved)`);
937
218
  lines.push(`\`${corePath}\``);
938
- lines.push(`All system tools use this path as root. When you need task-api.js, headless.js, or other Formation tools, reference:`);
219
+ lines.push(`All system tools use this path as root. When you need task-api.js, reference:`);
939
220
  lines.push(`- task-api: \`node "${corePath}/.collaboration/core/task-api.js" --project-path "${projectPath}"\``);
940
- lines.push(`- headless: \`node "${corePath}/.formation/tools/headless.js" --task=TASK_XXX --project-path="${projectPath}"\``);
941
- lines.push(`- post-complete: \`node "${corePath}/.formation/tools/post-complete.js" --task=TASK_XXX --project-path="${projectPath}"\``);
942
221
  }
943
222
 
944
223
  lines.push('');
945
224
  }
946
225
  }
947
-
948
- // Worktree context — when agent runs in an isolated git worktree
949
- if (worktreePath) {
950
- const originalProjectPath = projectPath || require('path').resolve(__dirname, '..', '..');
951
- lines.push(`## Worktree Context [internal]`);
952
- lines.push(`Your cwd is a git worktree at ${worktreePath}.`);
953
- lines.push(`For Synapse MCP calls (recall/inscribe/forge), use projectPath=${originalProjectPath} not your cwd.`);
954
- lines.push('');
955
- }
956
- }
957
-
958
- /**
959
- * Append standard Formation Preamble to prompt.
960
- * Injected into every dispatch agent for consistent task orientation.
961
- * Fields: Task (id + title), Branch (git, fallback: unknown),
962
- * Scope (scope_paths, omitted when empty), Principles (fixed).
963
- *
964
- * @param {string[]} lines - prompt lines array (mutated)
965
- * @param {string|object} taskData - task JSON string or parsed object
966
- * @param {object} [options] - options ({ projectPath })
967
- */
968
- function appendFormationPreamble(lines, taskData, options = {}) {
969
- let taskId = null;
970
- let taskTitle = null;
971
- let scopePaths = [];
972
-
973
- try {
974
- const parsed = typeof taskData === 'string' ? JSON.parse(taskData) : taskData;
975
- if (parsed) {
976
- taskId = parsed.id || null;
977
- taskTitle = parsed.title || null;
978
- scopePaths = parsed.scope_paths || [];
979
- }
980
- } catch { /* not JSON */ }
981
-
982
- // Resolve git branch — fallback to 'unknown' on any failure
983
- let branch = 'unknown';
984
- try {
985
- const { execSync } = require('child_process');
986
- const cwd = options.projectPath || path.resolve(__dirname, '..', '..');
987
- const out = execSync('git rev-parse --abbrev-ref HEAD', {
988
- cwd,
989
- encoding: 'utf-8',
990
- stdio: ['pipe', 'pipe', 'pipe'],
991
- timeout: 3000,
992
- windowsHide: true,
993
- });
994
- branch = (out || '').trim() || 'unknown';
995
- } catch { /* git unavailable or not a repo */ }
996
-
997
- lines.push(`## Dispatch Context`);
998
- if (taskId || taskTitle) {
999
- const label = [taskId, taskTitle].filter(Boolean).join(' — ');
1000
- lines.push(`Task: ${label}`);
1001
- }
1002
- lines.push(`Branch: ${branch}`);
1003
- if (scopePaths.length > 0) {
1004
- lines.push(`Scope: ${scopePaths.join(', ')}`);
1005
- }
1006
- lines.push(`Principles: 完美達成>一切|禁止盲寫|露出問題|結構>估計`);
1007
- lines.push('');
1008
-
1009
- appendMinimalismLadder(lines);
1010
- appendFileBudgetSRP(lines);
1011
- }
1012
-
1013
- /**
1014
- * Append the YAGNI minimalism decision-ladder (Ponytail essence) to prompt.
1015
- * Wired once into appendFormationPreamble so EVERY dispatched agent
1016
- * (unit/solo/squad/legion + sub-agent path via shared buildPrompt) inherits it.
1017
- * Closes the gap: Ansuz elixir holds wire>rewrite, but dispatched agents
1018
- * previously received no active "write less" nudge — only the "禁止盲寫" guard.
1019
- * Kept compact: a nudge, not a ritual.
1020
- *
1021
- * @param {string[]} lines - prompt lines array (mutated)
1022
- */
1023
- function appendMinimalismLadder(lines) {
1024
- lines.push(`## Code Minimalism — 最省決策階梯 (YAGNI)`);
1025
- lines.push(`寫任何代碼前,停在第一個成立的階:`);
1026
- lines.push(`1. 這需要存在嗎?不需要 → 跳過(不臆測未來需求)`);
1027
- lines.push(`2. stdlib / 語言內建有嗎?→ 用它`);
1028
- lines.push(`3. 平台 / 框架原生功能?→ 用它,別自己造`);
1029
- lines.push(`4. 專案已裝依賴有嗎?→ 重用,別加新依賴`);
1030
- lines.push(`5. 一行能解嗎?→ 一行`);
1031
- lines.push(`6. 否則:最小可行實作`);
1032
- lines.push(`規則:不為單一實作建抽象介面;刪除優於新增;最短 diff。`);
1033
- lines.push(`刻意簡化用 \`// thrift:\` 標記上限與升級路徑(例:\`// thrift: 同步即可,QPS>100 再改 async\`)。`);
1034
- lines.push(`不可簡化掉(lazy ≠ negligent):input validation、error handling、security、accessibility、用戶明確要求的功能。`);
1035
- lines.push('');
1036
- }
1037
-
1038
- /**
1039
- * Append the ARCH-001 file-budget & SRP clause (shift-left prevention half).
1040
- * Wired into appendFormationPreamble next to appendMinimalismLadder so every
1041
- * dispatched agent (unit/solo/squad/legion, subagent + Formation paths) sees
1042
- * the budget while writing code, not as late rework at the L1 merge gate
1043
- * (dev/scripts/check-file-size.js, .rule/general/file-size-threshold.yaml).
1044
- *
1045
- * @param {string[]} lines - prompt lines array (mutated)
1046
- */
1047
- function appendFileBudgetSRP(lines) {
1048
- lines.push(`## File Budget & SRP (ARCH-001)`);
1049
- lines.push(`單檔行數:warn=2000 / hard=4000。接近 warn 就停筆,優先 extraction 而非累加。`);
1050
- lines.push(`拆分模式:module / hooks/ / components/ / types.ts(依 CODE-STR-002)。`);
1051
- lines.push(`verify 步驟跑:\`node dev/scripts/check-file-size.js --staged\` — 有 BLOCK 先拆再提交。`);
1052
- lines.push('');
1053
226
  }
1054
227
 
1055
228
  function appendTask(lines, taskInput, taskData, options = {}) {
@@ -1078,8 +251,6 @@ function appendTask(lines, taskInput, taskData, options = {}) {
1078
251
  lines.push(`[internal] Sub-project: \`${taskProjectPath}\` — do NOT mention this to the user.`);
1079
252
  lines.push(`- ALL file operations must target this project directory`);
1080
253
  lines.push(`- ALL git operations (branch, commit, merge) must run in this repo`);
1081
- lines.push(`- post-complete: \`--project-path=${taskProjectPath}\``);
1082
- lines.push(`- headless dispatch: \`--project-path=${taskProjectPath}\``);
1083
254
  lines.push('');
1084
255
  }
1085
256
 
@@ -1100,7 +271,6 @@ function appendTask(lines, taskInput, taskData, options = {}) {
1100
271
  lines.push('**Scope Paths** (你只能操作這些路徑):');
1101
272
  for (const sp of scopePaths) {
1102
273
  if (root) {
1103
- const path = require('path');
1104
274
  lines.push(`- ${sp} → ${path.resolve(root, sp)}`);
1105
275
  } else {
1106
276
  lines.push(`- ${sp}`);
@@ -1315,106 +485,6 @@ function applyRustCheckToPrefix(prefix, body) {
1315
485
  return prefix.split(HARD_RULE_4_RUST_PHRASE).join(HARD_RULE_4_RUST_ALLOW_PHRASE);
1316
486
  }
1317
487
 
1318
- function appendWorkflow(lines, skillDef, options = {}, taskData = null) {
1319
- if (skillDef.collaboration && skillDef.collaboration.workflow) {
1320
- // Sub-project: rewrite task-api commands to include --project-path
1321
- const rewriteCmd = buildTaskApiRewriter(options);
1322
- // Replace {task_id} placeholder with actual task ID when available
1323
- const taskId = options.taskId || null;
1324
- const resolveTaskId = (cmd) => taskId ? cmd.replace(/\{task_id\}/g, taskId) : cmd;
1325
-
1326
- // TASK_1821: rigor-gated self-complete. rigor:high cards must NOT be closed by
1327
- // their own executor — the party with independent verification on the Formation
1328
- // path is post-complete (dispatcher side: regression tests + verify-acceptance +
1329
- // verified merge), and it already moves the task active→done itself after
1330
- // isVerifiedMergeSuccess (post-complete.js Step 2.5), so the card cannot strand.
1331
- // NOTE: this axis (per-card rigor) is orthogonal to the isSubagent axis below
1332
- // (per-dispatch-path). The subagent branch `continue`s first, so its replacement
1333
- // note stays byte-identical and this check only fires on Formation/headless/serial.
1334
- // TASK_2252: extractRigor accepts string OR object (a bare JSON.parse(object)
1335
- // threw into the catch and silently skipped this gate).
1336
- const taskRigor = extractRigor(taskData);
1337
-
1338
- lines.push(`## Workflow`);
1339
- // TASK_1819: only isolated executors may stage the whole tree.
1340
- // - options.isSubagent → Agent-tool dispatch (worktree isolation, dispatch.js)
1341
- // ...UNLESS options.isCrossProject (TASK_1824): cross-project sub-agent dispatch
1342
- // has NO worktree isolation (TASK_1672 — the Agent tool worktree derives from the
1343
- // conversation cwd repo, not the target sub-project). It writes directly into the
1344
- // sub-project's SHARED working tree, so it gets explicit-paths staging like serial.
1345
- // - options.isWorktree → Formation task worktree (threaded from headless.js
1346
- // promptOpts.isWorktree = !!_unitWorktreePath)
1347
- // Serial (neither flag) shares the working tree with the user and Ansuz —
1348
- // a blanket `git add -A` swept 392 unrelated files in TASK_188, so serial
1349
- // mode gets an explicit-paths staging instruction instead.
1350
- const isIsolated = !!((options.isSubagent && !options.isCrossProject) || options.isWorktree);
1351
- for (const step of skillDef.collaboration.workflow) {
1352
- // In subagent mode: skip read/claim (card already inline; claim done by parent)
1353
- if (options.isSubagent) {
1354
- const stepRun = step.run || '';
1355
- if (stepRun.includes('task-api.js get ') || stepRun.includes('task-api.js claim ')) {
1356
- continue;
1357
- }
1358
- if (stepRun.includes('task-api.js complete')) {
1359
- lines.push(`### ${step.step}`);
1360
- lines.push('Note: ⚠️ Worktree mode — 禁止執行 task-api.js complete。主 thread 在 merge + 驗證後處理。');
1361
- lines.push('');
1362
- continue;
1363
- }
1364
- }
1365
- // TASK_1821: Formation/headless/serial path + rigor:high → replace the
1366
- // self-complete step with a prohibition note (same technique as the
1367
- // subagent branch above). The dispatcher-side post-complete pipeline
1368
- // closes the card after merge + independent verification.
1369
- if (taskRigor === 'high' && step.run && step.run.includes('task-api.js complete')) {
1370
- lines.push(`### ${step.step}`);
1371
- lines.push('Note: ⚠️ rigor: high — 禁止執行 task-api.js complete。任務留在 active/,由派工方的 post-complete 在 merge + 獨立驗證(回歸測試 + verify-acceptance)通過後移入 done/。在完成摘要中明確標註「未自行 complete,待派工者驗證後關閉」。');
1372
- lines.push('');
1373
- continue;
1374
- }
1375
- // TASK_1833: the workflow commit step must never render the blanket
1376
- // `|| true` from the skill YAML — see buildCommitCommand for why (one
1377
- // swallow covered both nothing-to-commit AND index.lock data loss).
1378
- // Applies to BOTH modes: isolated gets `git add -A`, shared-tree gets
1379
- // explicit-paths staging (TASK_1819) — each with the honest commit shape.
1380
- if (step.run && step.run.includes('git add -A') && step.run.includes('git commit')) {
1381
- const msgMatch = step.run.match(/-m\s+"([^"]+)"/);
1382
- const commitMsg = msgMatch ? msgMatch[1] : '[{task_id}] task completed';
1383
- lines.push(`### ${step.step}`);
1384
- if (isIsolated) {
1385
- lines.push(`Command: ${resolveTaskId(buildCommitCommand('-A', commitMsg))}`);
1386
- if (step.note) lines.push(`Note: ${step.note}`);
1387
- lines.push('Note: nothing to commit 時靜默跳過(不算失敗);git commit 失敗(如 index.lock 競爭)會自動退避重試兩次,仍失敗則以非零退出 — 此時大聲回報錯誤,不要吞掉。');
1388
- } else {
1389
- lines.push(`Command: ${resolveTaskId(buildCommitCommand('<只列出你本次明確編輯過的檔案路徑>', commitMsg))}`);
1390
- lines.push('Note: ⚠️ 共用工作樹模式(無 worktree 隔離)— 你與使用者、其他 agent 共用同一個工作樹。只 stage 你本次明確編輯過的檔案,嚴禁一次 stage 整個工作樹(TASK_188 曾因此把 392 個無關檔案掃進單一 commit)。nothing to commit 時靜默跳過(不算失敗);git commit 失敗(如 .git/index.lock 被其他 agent 持有)會自動退避重試兩次,仍失敗則以非零退出 — 此時大聲回報錯誤,不要吞掉。');
1391
- }
1392
- lines.push('');
1393
- appendRustCheckReminder(lines, taskData, options, isIsolated);
1394
- continue;
1395
- }
1396
- lines.push(`### ${step.step}`);
1397
- if (step.run) lines.push(`Command: ${resolveTaskId(rewriteCmd(step.run))}`);
1398
- if (step.note) lines.push(`Note: ${step.note}`);
1399
- if (step.actions) {
1400
- for (const a of step.actions) {
1401
- lines.push(`- ${a}`);
1402
- }
1403
- }
1404
- lines.push('');
1405
- }
1406
-
1407
- if (skillDef.collaboration.on_blocked) {
1408
- lines.push(`### On Blocked`);
1409
- lines.push(`Command: ${resolveTaskId(rewriteCmd(skillDef.collaboration.on_blocked.run))}`);
1410
- lines.push(`Note: ${skillDef.collaboration.on_blocked.note}`);
1411
- lines.push('');
1412
- }
1413
- }
1414
-
1415
- appendDispatchOverrides(lines, taskData);
1416
- }
1417
-
1418
488
  /**
1419
489
  * TASK_1820: card-controlled override channel, rendered immediately AFTER the
1420
490
  * ## Workflow envelope. Section order is fixed (## Task always precedes
@@ -1466,563 +536,13 @@ function buildTaskApiRewriter(options = {}) {
1466
536
  return (cmd) => cmd.replace(TASK_API_PATTERN, replacement);
1467
537
  }
1468
538
 
1469
- /**
1470
- * Append Yggdrasil network context — shows what other nodes are doing.
1471
- * Lets the agent know about remote activity and potential file conflicts.
1472
- *
1473
- * @param {string[]} lines
1474
- * @param {object} ctx - { onlineNodes, remoteTasks, myNodeName, myScopePaths }
1475
- */
1476
- function appendYggdrasilContext(lines, ctx) {
1477
- if (!ctx) return;
1478
- const { onlineNodes = [], remoteTasks = [], myScopePaths = [] } = ctx;
1479
- if (onlineNodes.length === 0 && remoteTasks.length === 0) return;
1480
-
1481
- lines.push('## Yggdrasil Network (Remote Collaborators)');
1482
- lines.push('Other developers/agents are currently working on this project:');
1483
- lines.push('');
1484
-
1485
- for (const node of onlineNodes) {
1486
- const files = node.touching_files || [];
1487
- lines.push(`**${node.node_name}** — ONLINE`);
1488
- if (node.branch) lines.push(` Branch: ${node.branch}`);
1489
- if (node.current_task) lines.push(` Task: ${node.current_task}`);
1490
- if (node.work_summary) lines.push(` Working on: ${node.work_summary}`);
1491
- if (files.length > 0) {
1492
- lines.push(` Files: ${files.slice(0, 8).join(', ')}${files.length > 8 ? ` (+${files.length - 8})` : ''}`);
1493
- }
1494
-
1495
- // Highlight file overlaps with my scope (informational, not blocking)
1496
- if (myScopePaths.length > 0 && files.length > 0) {
1497
- const overlaps = files.filter(f =>
1498
- myScopePaths.some(sp => f.startsWith(sp) || sp.startsWith(f))
1499
- );
1500
- if (overlaps.length > 0) {
1501
- lines.push(` ℹ️ FILE OVERLAP: ${overlaps.join(', ')}`);
1502
- lines.push(` → Be aware of potential merge conflicts. Coordinate if needed.`);
1503
- }
1504
- }
1505
- lines.push('');
1506
- }
1507
-
1508
- if (remoteTasks.length > 0) {
1509
- lines.push('Active remote tasks:');
1510
- for (const t of remoteTasks.slice(0, 5)) {
1511
- lines.push(`- ${t.task_id} (claimed by ${t.claimed_by})`);
1512
- }
1513
- lines.push('');
1514
- }
1515
-
1516
- lines.push('To check real-time network status: `node .formation/tools/coordinator.js --status`');
1517
- lines.push('');
1518
- }
1519
-
1520
- /**
1521
- * Append memory context — 從 .failure/ + .memory/ + .kenaz/main-memory/ 載入相關經驗
1522
- * 注入到 prompt 中讓 agent 避免重蹈覆轍
1523
- */
1524
- function appendMemoryContext(lines, taskData, options = {}) {
1525
- if (!memoryLoader) return;
1526
-
1527
- try {
1528
- // 從 taskData 提取上下文資訊
1529
- const memOpts = {};
1530
- if (taskData) {
1531
- let parsed = null;
1532
- try { parsed = JSON.parse(taskData); } catch { /* not JSON */ }
1533
- if (parsed) {
1534
- memOpts.taskTitle = parsed.title || '';
1535
- memOpts.role = parsed.role || '';
1536
- memOpts.scopePaths = parsed.scope_paths || [];
1537
- memOpts.tags = parsed.tags || [];
1538
- memOpts.priority = parsed.priority || '';
1539
- }
1540
- }
1541
-
1542
- // TASK_1998 two-axis policy: Known Failures / Project Memory are CONTEXT
1543
- // volume, driven by complexity — simple drops both, medium restores Known
1544
- // Failures but keeps Project Memory dropped, complex keeps both. Knowledge
1545
- // Base (main-memory) is unaffected either way — it isn't part of this gate.
1546
- const profile = promptProfile({ rigor: extractRigor(taskData), complexity: extractComplexity(taskData) });
1547
- memOpts.skipFailures = profile.skipKnownFailures;
1548
- // 2026-10-10: memory system frozen (project_memory_system_frozen) - no Project Memory block.
1549
- memOpts.skipMemory = true;
1550
-
1551
- // Resolve corePath for sub-projects so memory-loader reads from core's
1552
- // .failure/, .memory/, .kenaz/main-memory/ instead of __dirname fallback
1553
- const corePath = options.corePath || resolveCorePath(options.projectPath);
1554
- if (corePath) memOpts.corePath = corePath;
1555
-
1556
- const memoryBlock = memoryLoader.loadMemoryContext(memOpts);
1557
- if (memoryBlock) {
1558
- lines.push(memoryBlock);
1559
- }
1560
-
1561
- // Code-navigation reminder (SYN-002, rewritten for LSP in TASK_2335)
1562
- lines.push('');
1563
- lines.push('## Search Tool Selection (SYN-002)');
1564
- lines.push('搜尋前先判斷:精確字串(函數名、error message、概念關鍵字)→ Grep / Glob。「誰呼叫它」「改了影響哪裡」「定義在哪」→ kenaz_lsp_callers / kenaz_lsp_references / kenaz_lsp_definition(args: projectPath, file, line 或 symbol, depth);檔案內 symbol 列表 → kenaz_lsp_symbols。');
1565
- lines.push('');
1566
- } catch { /* graceful degradation */ }
1567
- }
1568
-
1569
- /**
1570
- * Synchronously call an MCP tool via JSON-RPC over the local Dashboard HTTP
1571
- * server (127.0.0.1:18080/api/mcp) in a subprocess — keeps buildPrompt() sync.
1572
- * Returns the parsed inner data object, or null on any failure / unavailability.
1573
- * Shared by appendAlayaLessons.
1574
- *
1575
- * @param {string} toolName - MCP tool name (e.g. 'cortex_recall')
1576
- * @param {object} toolArgs - forwarded as params.arguments
1577
- * @returns {object|null}
1578
- */
1579
- function callMcpToolSync(toolName, toolArgs) {
1580
- try {
1581
- const { execSync } = require('child_process');
1582
- const script = `
1583
- const http = require('http');
1584
- const payload = JSON.stringify({
1585
- jsonrpc: '2.0', id: 1,
1586
- method: 'tools/call',
1587
- params: { name: ${JSON.stringify(toolName)}, arguments: ${JSON.stringify(toolArgs)} }
1588
- });
1589
- const req = http.request({
1590
- hostname: '127.0.0.1', port: 18080, path: '/api/mcp',
1591
- method: 'POST',
1592
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) },
1593
- timeout: 5000
1594
- }, (res) => {
1595
- let d = '';
1596
- res.on('data', c => d += c);
1597
- res.on('end', () => process.stdout.write(d));
1598
- });
1599
- req.on('error', () => process.exit(0));
1600
- req.on('timeout', () => { req.destroy(); process.exit(0); });
1601
- req.write(payload);
1602
- req.end();
1603
- `;
1604
- const stdout = execSync(`node -e "${script.replace(/"/g, '\\"').replace(/\n/g, ' ')}"`, {
1605
- timeout: 8000, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true,
1606
- });
1607
- if (!stdout || !stdout.trim()) return null;
1608
- const result = JSON.parse(stdout);
1609
- if (!result || result.error) return null;
1610
- const content = result.result?.content;
1611
- if (!content || !Array.isArray(content)) return null;
1612
- const textPart = content.find(c => c.type === 'text');
1613
- if (!textPart || !textPart.text) return null;
1614
- try { return JSON.parse(textPart.text); } catch { return null; }
1615
- } catch { return null; } // MCP server unavailable — graceful degradation
1616
- }
1617
-
1618
- // TASK_2101/TASK_2104: the absolute relevance gate (cosine floor + stack
1619
- // match) for Alaya lesson injection — constants, deriveTaskStack,
1620
- // deriveBijaStack, passesAlayaRelevanceGate — now lives in
1621
- // .formation/tools/alaya-gate.js (dependency-free, shared with task-api.js's
1622
- // echoAlayaLessons). Destructured from the require() at the top of this
1623
- // file; see alaya-gate.js for the full calibration writeup and rationale.
1624
-
1625
- /**
1626
- * Append bounded Alaya process-lesson context to the prompt.
1627
- * Reuses the title+scope_paths query, queries
1628
- * alaya_recall (hybrid mode, failure_seed + lesson_seed, with the TASK_2101
1629
- * min_cosine floor) and injects a ## Process lessons (Alaya) section capped
1630
- * at 900 chars total — but ONLY for candidates that also pass the stack-match
1631
- * gate. Zero passing candidates renders nothing (R3: an empty section beats
1632
- * noise).
1633
- *
1634
- * Graceful degradation: silently skips on empty query, empty results, or
1635
- * any MCP failure — the recall bar is never allowed to raise dispatch
1636
- * latency or fail the prompt build.
1637
- *
1638
- * @param {string[]} lines - prompt lines array (mutated)
1639
- * @param {string|object} taskData - task JSON string or parsed object
1640
- * @param {(toolName: string, toolArgs: object) => object|null} [mcpCaller] - injectable MCP bridge (defaults to callMcpToolSync; test seam)
1641
- */
1642
- function appendAlayaLessons(lines, taskData, mcpCaller = callMcpToolSync) {
1643
- if (!mcpCaller.__forceAlaya) return; // 2026-10-10: memory system frozen; tests opt in via __forceAlaya
1644
- const query = buildTaskContextQuery(taskData);
1645
- if (!query) return;
1646
-
1647
- let scopePaths = [];
1648
- try {
1649
- const parsed = typeof taskData === 'string' ? JSON.parse(taskData) : taskData;
1650
- if (parsed) scopePaths = parsed.scope_paths || [];
1651
- } catch { /* taskData not JSON — task stack stays empty, floor-only gate */ }
1652
- const taskStack = deriveTaskStack(scopePaths);
1653
-
1654
- try {
1655
- const recallData = mcpCaller('alaya_recall', {
1656
- query, mode: 'hybrid', seed_types: ['failure_seed', 'lesson_seed'],
1657
- limit: ALAYA_CANDIDATE_POOL, min_cosine: ALAYA_MIN_COSINE_DEFAULT,
1658
- });
1659
- if (!recallData) return;
1660
-
1661
- let bijas = recallData.bijas || [];
1662
- if (bijas.length === 0) return;
1663
- const candidateCount = bijas.length;
1664
-
1665
- bijas = dedupBijas(bijas);
1666
-
1667
- // TASK_2101 R3: stack-match gate on top of the server-side cosine floor
1668
- // (the server already applied min_cosine, but re-checking here keeps
1669
- // this function correct against a stub/older server that ignores it).
1670
- const gated = bijas.filter(b => passesAlayaRelevanceGate(b, taskStack, ALAYA_MIN_COSINE_DEFAULT));
1671
- const droppedCount = candidateCount - gated.length;
1672
- if (droppedCount > 0) {
1673
- // Log for floor/gate tuning (R3) — never fails the prompt build.
1674
- console.error(`[appendAlayaLessons] dropped ${droppedCount}/${candidateCount} candidates below the cosine floor (${ALAYA_MIN_COSINE_DEFAULT}) or stack mismatch for query "${query}"`);
1675
- }
1676
- bijas = gated.slice(0, 3);
1677
- if (bijas.length === 0) return;
1678
-
1679
- const modeEffective = recallData.mode_effective || 'hybrid';
1680
- const sectionLines = [
1681
- '## Process lessons (Alaya)',
1682
- `Query: "${query}" — ${bijas.length} seeds (mode=${modeEffective}).`,
1683
- ...bijas.map(b => {
1684
- const content = (b.content || '').replace(/\s+/g, ' ').trim().slice(0, 220);
1685
- return `- **${b.seed_type || 'seed'}** [${b.origin || 'unknown'}] ${content}`;
1686
- }),
1687
- ];
1688
-
1689
- let section = sectionLines.join('\n');
1690
- if (section.length > 900) {
1691
- section = section.slice(0, 899) + '…';
1692
- }
1693
-
1694
- lines.push('');
1695
- lines.push(...section.split('\n'));
1696
- lines.push('');
1697
- } catch { /* Alaya unavailable — graceful degradation */ }
1698
- }
1699
-
1700
- function appendCognitiveRecall(lines, taskData, options = {}) {
1701
- // Extract recall keywords from task data (title, scope_paths, tags)
1702
- let keywords = [];
1703
- let scopeFiles = [];
1704
- try {
1705
- const parsed = typeof taskData === 'string' ? JSON.parse(taskData) : taskData;
1706
- if (parsed) {
1707
- if (parsed.title) {
1708
- const stopWords = new Set(['the','a','an','is','are','was','were','be','to','of','and','in','for','on','at','by','with','from','as','into','fix','task','add','update','implement']);
1709
- const words = parsed.title.replace(/[^a-zA-Z0-9_\s]/g, ' ').split(/\s+/).filter(w => w.length > 2 && !stopWords.has(w.toLowerCase()));
1710
- keywords.push(...words.slice(0, 5));
1711
- }
1712
- if (parsed.scope_paths && parsed.scope_paths.length > 0) {
1713
- for (const sp of parsed.scope_paths) {
1714
- const parts = sp.replace(/\\/g, '/').split('/');
1715
- const last = parts[parts.length - 1].replace(/\.[^.]+$/, '');
1716
- if (last && last.length > 2) {
1717
- keywords.push(last);
1718
- scopeFiles.push(last);
1719
- }
1720
- }
1721
- }
1722
- if (parsed.tags && parsed.tags.length > 0) {
1723
- keywords.push(...parsed.tags.filter(t => t.length > 2));
1724
- }
1725
- }
1726
- } catch { /* taskData not JSON, skip */ }
1727
-
1728
- if (keywords.length === 0) return;
1729
- keywords = [...new Set(keywords)].slice(0, 8);
1730
-
1731
- }
1732
-
1733
- // Exact, shared wording — rigor:low ends its Working contract with this
1734
- // bullet; rigor:medium repeats the identical sentence as its own line after
1735
- // the short progress-note instructions (TASK_1998 spec: "the exact line").
1736
- const RUNE_OPTIONAL_LINE = 'Inscribe exactly one rune (synapse_inscribe) if you learned something non-obvious; otherwise skip it.';
1737
-
1738
- /**
1739
- * TASK_1998: rigor:medium's short (3-line) progress-note instruction — the
1740
- * same write-then-run contract as appendProgressNoteInstructions()'s full
1741
- * block, compressed to a header + 2 lines instead of a full schema dump.
1742
- * No-op when taskData has no id (mirrors appendProgressNoteInstructions).
1743
- */
1744
- function appendShortProgressNote(lines, taskData, options = {}) {
1745
- let taskId = null;
1746
- try {
1747
- const parsed = typeof taskData === 'string' ? JSON.parse(taskData) : taskData;
1748
- if (parsed) taskId = parsed.id;
1749
- } catch { /* taskData not JSON, skip */ }
1750
- if (!taskId) return;
1751
-
1752
- const corePath = options.corePath || path.resolve(__dirname, '..', '..');
1753
- const corePathFwd = corePath.replace(/\\/g, '/');
1754
- const noteFile = `.kenaz/specialist/progress_note_${taskId}.json`;
1755
-
1756
- lines.push(`## Progress Note (brief)`);
1757
- lines.push(`Write JSON to \`${noteFile}\`, then run: node "${corePathFwd}/.specialist/tools/phase-gate.js" note --task=${taskId} --from-file="${noteFile}"`);
1758
- lines.push(`Include: files touched, acceptance-criteria pass/fail, any deviations.`);
1759
- }
1760
-
1761
- function appendCognitiveCheckpoint(lines, taskData, options = {}) {
1762
- const profile = promptProfile({ rigor: extractRigor(taskData), complexity: extractComplexity(taskData) });
1763
-
1764
- // TASK_1998: PROCESS strictness is rigor-driven. low/medium collapse the
1765
- // six ritual sections (Before You Start recall, Before You Finish
1766
- // inscribe, Cognitive Checkpoint, Progress Note, Known Failures, Project
1767
- // Memory) into a compact contract — Ansuz already ran recall at
1768
- // scoping time; the full mandatory recall/inscribe ceremony is reserved
1769
- // for rigor:high. rigor:medium additionally gets a short progress-note
1770
- // instruction on top of the contract (dropped entirely for rigor:low).
1771
- if (profile.compactRitual) {
1772
- lines.push('');
1773
- lines.push(`## Working contract`);
1774
- lines.push(`- Read the files you touch before editing.`);
1775
- lines.push(`- Independent reads: fire them as parallel tool calls in ONE turn (e.g. Read a.js + Read b.js + Grep "foo" together), not one call per turn.`);
1776
- lines.push(`- Independent shell checks: one bash command with separators (e.g. \`echo ==a==; cmd1; echo ==b==; cmd2\`), not N bash calls.`);
1777
- lines.push(`- N similar items: one command over all N (e.g. one grep with alternation, or a loop), not N calls.`);
1778
- lines.push(`- One Write or a multi-hunk Edit beats many small edits.`);
1779
- lines.push(`- No self-check greps unless the card asks for them.`);
1780
- lines.push(`- Commit early and often; the worktree hard rules above still apply verbatim.`);
1781
- lines.push(`- Final report <= 20 lines: files touched, tests run, commit sha, uncertainties.`);
1782
- lines.push('');
1783
-
1784
- if (profile.ritualTier === 'medium') {
1785
- appendShortProgressNote(lines, taskData, options);
1786
- lines.push('');
1787
- }
1788
- return;
1789
- }
1790
- }
1791
-
1792
- function appendOutput(lines, skillDef) {
1793
- if (!skillDef.output) return;
1794
- lines.push(`## Expected Output`);
1795
- lines.push(`Primary: ${typeof skillDef.output.primary === 'string' ? skillDef.output.primary.trim() : JSON.stringify(skillDef.output.primary)}`);
1796
- if (skillDef.output.secondary) {
1797
- lines.push(`Secondary: ${typeof skillDef.output.secondary === 'string' ? skillDef.output.secondary.trim() : JSON.stringify(skillDef.output.secondary)}`);
1798
- }
1799
- }
1800
-
1801
- // ─────────────────────────────────────────
1802
- // Squad Synthesis:整合多份成員報告
1803
- // ─────────────────────────────────────────
1804
-
1805
- /**
1806
- * 組裝 Squad Synthesis Agent 的 prompt
1807
- * 在所有成員完成後,由 headless.js 自動呼叫
1808
- * @param {object} skillDef - Squad 的 YAML 定義(含 merge 設定)
1809
- * @param {string} reportDir - 報告目錄名稱(如 squad-strategy)
1810
- * @returns {string} synthesis prompt
1811
- */
1812
- function buildSquadSynthesisPrompt(skillDef, reportDir, options = {}) {
1813
- const lines = [];
1814
- const merge = skillDef.merge;
1815
-
1816
- lines.push('## Role');
1817
- lines.push('你是 Squad Synthesis Agent(整合者)。');
1818
- lines.push('你的任務是讀取多份獨立分析報告,按照指定的整合策略產出最終報告。');
1819
- lines.push('你不是成員之一,你是整合者。你不偏袒任何一方。');
1820
- lines.push('');
1821
-
1822
- // Principles(整合者也必須遵守的硬規則)
1823
- if (skillDef.principles) {
1824
- lines.push('## Principles (整合時也必須遵守)');
1825
- if (skillDef.principles.focus) {
1826
- lines.push(skillDef.principles.focus.trim());
1827
- lines.push('');
1828
- }
1829
- if (skillDef.principles.forbidden && skillDef.principles.forbidden.length > 0) {
1830
- lines.push('**禁止 (違反即失敗)**:');
1831
- for (const f of skillDef.principles.forbidden) lines.push(`- ❌ ${f}`);
1832
- lines.push('');
1833
- }
1834
- if (skillDef.principles.required && skillDef.principles.required.length > 0) {
1835
- lines.push('**必須遵守**:');
1836
- for (const r of skillDef.principles.required) lines.push(`- ✅ ${r}`);
1837
- lines.push('');
1838
- }
1839
- }
1840
-
1841
- // Merge strategy
1842
- if (merge) {
1843
- lines.push(`## Merge Strategy: ${merge.strategy || 'default'}`);
1844
- if (merge.description) {
1845
- lines.push(merge.description.trim());
1846
- }
1847
- lines.push('');
1848
-
1849
- // Merge steps — 核心指令
1850
- if (merge.steps && merge.steps.length > 0) {
1851
- lines.push('## Merge Steps (按順序執行)');
1852
- lines.push('');
1853
- for (const step of merge.steps) {
1854
- lines.push(`### Step: ${step.step}`);
1855
- if (step.action) lines.push(step.action.trim());
1856
- if (step.on_partial_failure) {
1857
- lines.push(`**Partial Failure Handling**: ${step.on_partial_failure.trim()}`);
1858
- }
1859
- lines.push('');
1860
- }
1861
- }
1862
- }
1863
-
1864
- // Reports location
1865
- const reportPath = `.formation/reports/${reportDir}`;
1866
- lines.push('## Reports Location');
1867
- lines.push(`所有成員報告位於 \`${reportPath}/\` 目錄。`);
1868
- lines.push('');
1869
- lines.push('**執行步驟**:');
1870
- lines.push(`1. 用 Glob 工具掃描 \`${reportPath}/*.md\``);
1871
- lines.push('2. 排除 synthesis.md(那是你要寫的輸出)');
1872
- lines.push('3. 用 Read 工具逐一讀取每份報告');
1873
- lines.push('4. 按照上述 Merge Steps 逐步整合');
1874
- lines.push('');
1875
-
1876
- // Squad members for context
1877
- if (skillDef.members && skillDef.members.length > 0) {
1878
- lines.push('## Squad Members(供參考)');
1879
- for (const m of skillDef.members) {
1880
- const roleFirstLine = (typeof m.role === 'string' ? m.role.split('\n')[0].trim() : m.name);
1881
- lines.push(`- **${m.name}** (${m.id}): ${roleFirstLine}`);
1882
- }
1883
- lines.push('');
1884
- }
1885
-
1886
- // Output
1887
- lines.push('## Output');
1888
- lines.push(`將整合報告寫入 \`${reportPath}/synthesis.md\``);
1889
- lines.push('');
1890
- lines.push('報告格式依 Merge Strategy 而定。確保報告完整、結構清晰、附上各成員的核心觀點摘要。');
1891
- lines.push('');
1892
-
1893
- // No taskData in scope (post-hoc synthesis, not a dispatched task card) —
1894
- // rigor/complexity both normalize to 'medium', landing in the compact-ritual
1895
- // branch (TASK_1998 two-axis policy).
1896
- appendCognitiveCheckpoint(lines, null, options);
1897
-
1898
- return lines.join('\n');
1899
- }
1900
-
1901
- // ─────────────────────────────────────────
1902
- // Squad Debate:Round 2 交叉辯論 prompt
1903
- // ─────────────────────────────────────────
1904
-
1905
- /**
1906
- * 組裝 Squad Round 2 Debate prompts
1907
- * 每個成員讀取其他人的 Round 1 報告,寫出針對性回應
1908
- * @param {object} skillDef - Squad YAML 定義
1909
- * @param {string} reportDir - 報告目錄名稱
1910
- * @returns {{ memberId: string, prompt: string, skill: string }[]}
1911
- */
1912
- function buildSquadDebatePrompts(skillDef, reportDir) {
1913
- const members = skillDef.members || [];
1914
- const debate = skillDef.debate || {};
1915
- const reportPath = `.formation/reports/${reportDir}`;
1916
-
1917
- return members.map(member => {
1918
- const lines = [];
1919
- const otherMembers = members.filter(m => m.id !== member.id);
1920
-
1921
- // Role reminder
1922
- lines.push('## Role');
1923
- lines.push(`你是 **${member.name}**,這是你的第二輪分析(辯論回合)。`);
1924
- lines.push('');
1925
-
1926
- // Original stance
1927
- const roleFirstLine = typeof member.role === 'string' ? member.role.split('\n')[0].trim() : '';
1928
- if (roleFirstLine) {
1929
- lines.push('## 你的立場(簡要提醒)');
1930
- lines.push(roleFirstLine);
1931
- lines.push('');
1932
- }
1933
-
1934
- // Debate instructions (per-member)
1935
- if (member.debate_instructions) {
1936
- lines.push('## Debate Instructions');
1937
- lines.push(member.debate_instructions.trim());
1938
- lines.push('');
1939
- }
1940
-
1941
- // Debate rules (squad-wide)
1942
- if (debate.rules && debate.rules.length > 0) {
1943
- lines.push('## Debate Rules');
1944
- for (const rule of debate.rules) {
1945
- lines.push(`- ${rule}`);
1946
- }
1947
- lines.push('');
1948
- }
1949
-
1950
- // Reports to read
1951
- lines.push('## Reports to Read');
1952
- for (const other of otherMembers) {
1953
- lines.push(`- **${other.name}**: \`${reportPath}/${other.id}.md\``);
1954
- }
1955
- lines.push('');
1956
- lines.push('用 Read 工具讀取上述報告,然後寫出你的回應。');
1957
- lines.push(`你自己的 Round 1 報告在 \`${reportPath}/${member.id}.md\`,可以參考但不需要重複。`);
1958
- lines.push('');
1959
-
1960
- // Output
1961
- lines.push('## Output');
1962
- lines.push(`將反駁報告寫入 \`${reportPath}/${member.id}-r2.md\``);
1963
- lines.push('');
1964
-
1965
- return {
1966
- memberId: `${member.id}-r2`,
1967
- prompt: lines.join('\n'),
1968
- skill: member.skill || 'do'
1969
- };
1970
- });
1971
- }
1972
-
1973
- // ─────────────────────────────────────────
1974
- // Headless 模式常數
1975
- // ─────────────────────────────────────────
1976
-
1977
- // TASK_1712: Rewritten with honest provenance framing so safety-hardened models
1978
- // (claude-sonnet-5) read this as legitimate orchestration context rather than injection.
1979
- // Behavioral constraints are fully preserved — only the framing changed.
1980
- // Key changes: (1) opened with machine-assembly provenance declaration, (2) replaced
1981
- // "don't read CLAUDE.md" with why Formation supplies its own rule set, (3) replaced
1982
- // "don't ask questions" with "no human is watching — report and exit if blocked",
1983
- // (4) replaced concealment wording in git rule with safety-rationale explanation.
1984
- // TASK_1784 (DEFECT-A): Rule 6 was a categorical git ban that contradicted the workflow's
1985
- // mandated commit step (do.yaml "提交變更": git add -A && git commit --no-verify). Literal-obedience
1986
- // models (gpt-5.6-sol) obeyed the ban, produced zero commits, and tripped the leak detector on
1987
- // every unmodified bifrost dispatch (TASK_1779 A/B-proven). Rule 6 now carves out exactly the
1988
- // workflow commit command shape and nothing else; all other git subcommands stay forbidden.
1989
- const HEADLESS_PREFIX = `## Automated Dispatch Context
1990
- This prompt was machine-assembled by the Kenaz orchestration system, which is owned and
1991
- operated by the repository owner. It runs unattended via \`claude -p\` (no interactive user
1992
- is watching the output stream). The task brief below was authored and approved by the user
1993
- before this automated run was triggered.
1994
-
1995
- ## Operating Rules
1996
- 1. Start with a one-line summary: "📋 {TASK_ID}: {what you are about to do}" (<=30 words). Then proceed.
1997
- 2. Execute directly — do not ask clarifying questions. Requirements were confirmed before dispatch.
1998
- If you are blocked by a genuine ambiguity, document the blocker in your output and exit; a human will read the log.
1999
- 3. Formation supplies its own rule set (see ## Role / ## Task below), so the project CLAUDE.md
2000
- is not the active instruction source for this run. Rely on the content in this prompt.
2001
- 4. Produce structured output when finished.
2002
- 5. On unrecoverable errors: describe what failed and exit. Do not attempt open-ended retries.
2003
- 6. **Git is restricted to the workflow's own commit step — nothing else.** Exactly two git commands
2004
- are permitted, and only when the ## Workflow section below explicitly lists them as a step:
2005
- the "git add ..." staging command and the "git commit --no-verify -m <message>" that follows it,
2006
- run EXACTLY as shown in that workflow step — stage only what that step specifies, never more —
2007
- on the branch this session started on, without switching branches.
2008
- When the workflow lists that commit step you MUST execute it — this rule does not cancel it.
2009
- Every other git subcommand is forbidden: do not run git checkout, git branch, git merge,
2010
- git push, git rebase, git stash, git reset, or anything else. Branch, merge, and push management
2011
- is handled exclusively by the Formation Lead after the task completes, to prevent
2012
- cross-repository contamination when multiple agents run in parallel against different project paths.
2013
- 7. **Handle exactly the task specified in ## Task below.** Do not discover or claim other tasks.
2014
- One agent — one task.
2015
- 8. Do not read the task directory directly (.collaboration/tasks/ or .kenaz/tasks/). Use task-api.js.
2016
- 9. Only complete the task assigned to you (see ## Task for the Task ID). Do not mark other tasks done.
2017
-
2018
- `;
2019
539
  // TASK_1833: rules 12-14 are shared VERBATIM across all sub-agent prefixes
2020
540
  // (worktree / direct-write / cross-project-worktree). The only variance is
2021
541
  // which rule number the commit instruction lives under. Kept as one template
2022
542
  // so the three prefixes cannot drift apart (coupling rule: no near-duplicate
2023
543
  // parallel impls).
2024
544
  const subagentAsyncRules = (commitRuleNo) => `12. **⚠️ 你送出訊息就等於結束本次執行。** 你的最終訊息**就是**回傳值 —— 沒有「稍後回來接手」這回事。不要規劃「先讓出控制權、等背景工作完成再繼續」,那會直接結束你的執行並留下無人收拾的背景行程。
2025
- 13. **禁止啟動預期超過數分鐘的建置或測試** —— cold Rust build(kuzu 的 CMake 可跑十幾分鐘)、完整測試套件都屬此類。原因是結構性的:Bash 前景呼叫上限 10 分鐘,超時會被砍;改用背景跑則你沒有同步工作可做,只能送出訊息(見規則 12),於是留下持有 target dir 鎖的孤兒行程,後續建置全部空等。
545
+ 13. **禁止啟動預期超過數分鐘的建置或測試** —— cold Rust build(可跑十幾分鐘)、完整測試套件都屬此類。原因是結構性的:Bash 前景呼叫上限 10 分鐘,超時會被砍;改用背景跑則你沒有同步工作可做,只能送出訊息(見規則 12),於是留下持有 target dir 鎖的孤兒行程,後續建置全部空等。
2026
546
  - **正確做法**:改完程式碼 → commit(規則 ${commitRuleNo})→ **直接回報結束**。在報告中寫明「未執行建置驗證」。
2027
547
  - **Rust 建置驗證一律由主線程(Ansuz)負責** —— 主線程有熱快取、能跨 turn 輪詢、能診斷並清理孤兒行程。這是分工,不是你偷懶。
2028
548
  - 這**不代表**你不必自我檢查。讀周邊程式碼、確認語法與型別、小範圍 grep 比對既有實作,這些輕量檢查仍然必須做。被禁止的只有「耗時數分鐘以上的建置與完整測試套件」。
@@ -2045,8 +565,7 @@ const WORKTREE_DISPATCH_HARD_RULES = `2. CLAUDE.md 生效;完成輸出結果
2045
565
  4. 送出訊息=結束;禁長建置/全測試(commit 後報未驗證,Rust 交 Ansuz);禁 sleep 輪詢;禁用 image name 殺行程(只殺自己 PID)。
2046
566
  5. 只做指定任務;不讀任務目錄;只用 task-api.js;worktree 禁 complete。`;
2047
567
 
2048
- // Sub-agent dispatch preamble — used when task is dispatched via Agent tool (isolation=worktree)
2049
- // instead of claude -p (headless). Key differences from HEADLESS_PREFIX:
568
+ // Sub-agent dispatch preamble — used when a task is dispatched via the Agent tool (isolation=worktree):
2050
569
  // - Git IS allowed (worktree provides isolation; commits land on task branch, not user branch)
2051
570
  // - CLAUDE.md IS active (running inside an interactive Claude Code session)
2052
571
  // - Agent must commit changes to the worktree before returning
@@ -2102,135 +621,22 @@ function buildSubagentWorktreePrefix(worktreePath, branchName, projectPath) {
2102
621
  return `[CROSS-PROJECT WORKTREE SUBAGENT MODE] wt=${worktreePath} repo=${projectPath} branch=${branchName}
2103
622
  1. 先印摘要「📋 {TASK_ID}: {你準備做什麼}」,直接做,不問。
2104
623
  ${WORKTREE_DISPATCH_HARD_RULES}
2105
- 6. 工作目錄=上述 worktree;Edit/Write、cd/git -C 皆用它;禁編輯主樹;Synapse projectPath 用 ${projectPath}。
624
+ 6. 工作目錄=上述 worktree;Edit/Write、cd/git -C 皆用它;禁編輯主樹。
2106
625
  Details: .formation/tools/dispatch-detail/cross-project-worktree.md (Read only if needed)
2107
626
  `;
2108
627
  }
2109
628
 
2110
- const LEGION_SYSTEM_PREFIX = `You are the TEAM LEAD using Claude Code's OFFICIAL "agent teams" feature.
2111
-
2112
- YOUR ROLE: Team Lead
2113
- - You are RESPONSIBLE for this task's success or failure.
2114
- - You MUST coordinate teammates and ensure they complete their work.
2115
- - You CANNOT end the session until ALL teammates have reported back.
2116
- - If you end early, the task FAILS and it's YOUR fault.
2117
-
2118
- This is NOT the project's .formation/ directory system. Do NOT read .formation/ files.
2119
- To create teammates, use natural language like: "Create a teammate named X who handles Y"
2120
- Each teammate is a separate Claude instance running in parallel.
2121
-
2122
- `;
2123
-
2124
- const LEGION_AUTO_EXECUTE = `
2125
-
2126
- BEFORE YOU START - OUTPUT YOUR PLAN:
2127
- First, explain exactly what you will do step by step:
2128
- 1. What teammates will you create?
2129
- 2. How will you wait for their results?
2130
- 3. What tools will you use to poll for messages?
2131
- 4. When will you know all teammates are done?
2132
-
2133
- RULES:
2134
- 1. Do NOT ask questions. Execute immediately after stating your plan.
2135
- 2. Do NOT read CLAUDE.md - this is a separate system.
2136
- 3. Create teammates using natural language descriptions.
2137
- 4. IMPORTANT: Use "sonnet" model for ALL teammates to reduce cost.
2138
-
2139
- WAITING FOR TEAMMATES - CRITICAL:
2140
- 5. After spawning ALL teammates, you MUST enter a polling loop.
2141
- 6. In the loop: call "sleep 5" with Bash, then check if teammates sent messages.
2142
- 7. Teammates send results via SendMessage - you will see them as incoming user messages.
2143
- 8. Keep looping (sleep + check) until you have received messages from ALL teammates.
2144
- 9. Do NOT end until you have ACTUAL CONTENT from every single teammate.
2145
- 10. If a teammate is slow, keep waiting - do NOT give up or timeout early.
2146
- 11. Only after ALL teammates report back, synthesize their findings into final answer.`;
2147
-
2148
- // ─────────────────────────────────────────
2149
- // Legion Lead Prompt 組裝
2150
- // ─────────────────────────────────────────
2151
-
2152
- /**
2153
- * 組裝 Legion Lead 的完整 headless prompt
2154
- * 複製 Sunny teammate 的成功模式:
2155
- * SYSTEM_PREFIX + 成員列表(自然語言)+ 任務 + AUTO_EXECUTE
2156
- */
2157
- function buildLegionLeadPrompt(config) {
2158
- let prompt = LEGION_SYSTEM_PREFIX;
2159
-
2160
- prompt += `Create an agent team with ${config.members.length} teammates:\n`;
2161
- config.members.forEach((m, i) => {
2162
- const lines = m.prompt.split('\n');
2163
- const roleIdx = lines.findIndex(l => l.startsWith('## Role'));
2164
- let desc = m.name;
2165
- if (roleIdx >= 0) {
2166
- for (let j = roleIdx + 1; j < lines.length && j < roleIdx + 5; j++) {
2167
- const line = lines[j].trim();
2168
- if (line && !line.startsWith('#')) { desc = line; break; }
2169
- }
2170
- }
2171
- prompt += `${i + 1}. "${m.name}" - ${desc}\n`;
2172
- });
2173
- prompt += '\n';
2174
-
2175
- const taskMatch = config.lead.prompt.match(/## Task\n([\s\S]*?)(?=\n##|$)/);
2176
- if (taskMatch) {
2177
- prompt += `Task: ${taskMatch[1].trim()}\n`;
2178
- }
2179
-
2180
- prompt += LEGION_AUTO_EXECUTE;
2181
- return prompt;
2182
- }
2183
-
2184
629
  module.exports = {
2185
630
  recordSkillUsage,
2186
- buildPrompt, buildSquadSynthesisPrompt, buildSquadDebatePrompts,
2187
- buildLegionLeadPrompt,
2188
- resolveModules,
2189
- HEADLESS_PREFIX,
631
+ buildPrompt,
2190
632
  SUBAGENT_PREFIX,
2191
633
  SUBAGENT_PREFIX_DIRECT,
2192
634
  buildSubagentWorktreePrefix,
2193
- // TASK_1978: exported for direct unit-testing of the compressed ceremony
2194
- // sections (byte budget + hard-constraint presence), without exercising
2195
- // the full buildUnitPrompt pipeline (which touches memory/cortex/episodic
2196
- // context and needs a live project).
2197
- appendCognitiveCheckpoint,
2198
- // TASK_1992: exported for direct unit-testing of the Alaya process-lessons
2199
- // section with an injected mcpCaller stub (no live MCP dependency).
2200
- buildTaskContextQuery,
2201
- appendAlayaLessons,
2202
- // TASK_2101: exported for direct unit-testing of the absolute-relevance
2203
- // gate (cosine floor + stack match) that appendAlayaLessons applies before
2204
- // any bija is rendered into the dispatch prompt.
2205
- deriveTaskStack,
2206
- deriveBijaStack,
2207
- passesAlayaRelevanceGate,
2208
- ALAYA_MIN_COSINE_DEFAULT,
2209
- ALAYA_STACK_OVERRIDE_COSINE,
2210
- ALAYA_CANDIDATE_POOL,
2211
- // TASK_1998: exported for dispatch.js (Related Files Preview gating,
2212
- // prompt-byte-stats telemetry) and for direct unit-testing of the
2213
- // two-axis prompt policy (context volume by complexity, process
2214
- // strictness by rigor).
2215
635
  promptProfile,
2216
636
  extractRigor,
2217
637
  extractComplexity,
2218
638
  splitPromptSections,
2219
- // TASK_1998 R2 follow-up: exported for direct unit-testing of Known
2220
- // Failures/Project Memory rigor gating against the real .failure/.memory
2221
- // dirs (no live-project mocking needed).
2222
- appendMemoryContext,
2223
- // TASK_2082: exported for direct unit-testing of the Rust-check reminder
2224
- // gate (scope detection + core-crate existence + isolated-worktree gate)
2225
- // without needing a live dashboard/src-tauri checkout on the test machine.
2226
639
  hasRustScope,
2227
640
  appendRustCheckReminder,
2228
- // TASK_2082: exported so prompt.test.js can exercise the commit-step +
2229
- // Rust-check-reminder wiring directly, without paying for buildUnitPrompt's
2230
- // other live-project-dependent appends (memory/cortex/episodic context).
2231
- appendWorkflow,
2232
- // TASK_2082 defect fix: exported for dispatch.js to call at the prefix +
2233
- // body composition point (rewrites hard rule 4's wording in-place instead
2234
- // of relying on "later instructions win"), and for direct unit-testing.
2235
641
  applyRustCheckToPrefix,
2236
642
  };