maestro-flow 0.3.8 → 0.3.9

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 (211) hide show
  1. package/.claude/commands/learn-decompose.md +3 -3
  2. package/.claude/commands/learn-follow.md +5 -5
  3. package/.claude/commands/learn-investigate.md +3 -3
  4. package/.claude/commands/learn-retro.md +6 -6
  5. package/.claude/commands/learn-second-opinion.md +3 -3
  6. package/.claude/commands/maestro-analyze.md +123 -99
  7. package/.claude/commands/maestro-brainstorm.md +2 -2
  8. package/.claude/commands/maestro-execute.md +137 -97
  9. package/.claude/commands/maestro-fork.md +111 -0
  10. package/.claude/commands/maestro-init.md +6 -6
  11. package/.claude/commands/maestro-merge.md +77 -0
  12. package/.claude/commands/maestro-milestone-audit.md +72 -60
  13. package/.claude/commands/maestro-milestone-complete.md +67 -59
  14. package/.claude/commands/maestro-milestone-release.md +6 -6
  15. package/.claude/commands/maestro-plan.md +151 -130
  16. package/.claude/commands/maestro-quick.md +4 -4
  17. package/.claude/commands/maestro-roadmap.md +5 -5
  18. package/.claude/commands/maestro-spec-generate.md +5 -5
  19. package/.claude/commands/maestro-ui-design.md +3 -3
  20. package/.claude/commands/maestro-verify.md +106 -87
  21. package/.claude/commands/maestro.md +10 -4
  22. package/.claude/commands/manage-codebase-rebuild.md +4 -4
  23. package/.claude/commands/manage-codebase-refresh.md +1 -1
  24. package/.claude/commands/manage-harvest.md +5 -5
  25. package/.claude/commands/manage-issue-discover.md +1 -1
  26. package/.claude/commands/manage-issue-execute.md +6 -6
  27. package/.claude/commands/manage-issue.md +6 -6
  28. package/.claude/commands/manage-learn.md +2 -2
  29. package/.claude/commands/manage-memory-capture.md +4 -4
  30. package/.claude/commands/manage-memory.md +2 -2
  31. package/.claude/commands/manage-status.md +24 -24
  32. package/.claude/commands/quality-business-test.md +5 -5
  33. package/.claude/commands/quality-debug.md +4 -4
  34. package/.claude/commands/quality-integration-test.md +4 -4
  35. package/.claude/commands/quality-refactor.md +3 -3
  36. package/.claude/commands/quality-retrospective.md +2 -2
  37. package/.claude/commands/quality-review.md +4 -4
  38. package/.claude/commands/quality-sync.md +3 -3
  39. package/.claude/commands/quality-test-gen.md +4 -4
  40. package/.claude/commands/quality-test.md +9 -9
  41. package/.claude/commands/spec-add.md +2 -2
  42. package/.claude/commands/spec-load.md +1 -1
  43. package/.claude/commands/spec-setup.md +5 -5
  44. package/.claude/commands/wiki-connect.md +3 -3
  45. package/.claude/commands/wiki-digest.md +4 -4
  46. package/.codex/skills/maestro-analyze/SKILL.md +52 -14
  47. package/.codex/skills/maestro-execute/SKILL.md +27 -26
  48. package/.codex/skills/maestro-milestone-audit/SKILL.md +103 -209
  49. package/.codex/skills/maestro-milestone-complete/SKILL.md +149 -158
  50. package/.codex/skills/maestro-plan/SKILL.md +47 -17
  51. package/.codex/skills/maestro-roadmap/SKILL.md +3 -2
  52. package/.codex/skills/team-coordinate/roles/coordinator/commands/monitor.md +2 -2
  53. package/.codex/skills/team-executor/roles/executor/commands/monitor.md +1 -1
  54. package/.codex/skills/team-lifecycle-v4/roles/coordinator/commands/monitor.md +2 -2
  55. package/.codex/skills/team-lifecycle-v4/specs/knowledge-transfer.md +2 -2
  56. package/.codex/skills/team-quality-assurance/roles/coordinator/commands/monitor.md +1 -1
  57. package/.codex/skills/team-review/roles/coordinator/commands/monitor.md +1 -1
  58. package/.codex/skills/team-tech-debt/roles/coordinator/commands/monitor.md +1 -1
  59. package/.codex/skills/team-testing/roles/coordinator/commands/monitor.md +1 -1
  60. package/README.md +19 -14
  61. package/README.zh-CN.md +16 -12
  62. package/bin/maestro-mcp.js +1 -1
  63. package/chains/_intent-map.json +21 -9
  64. package/chains/_router.json +30 -77
  65. package/chains/brainstorm-driven.json +17 -6
  66. package/chains/full-lifecycle.json +22 -23
  67. package/chains/milestone-close.json +20 -7
  68. package/chains/milestone-fork-merge.json +50 -0
  69. package/chains/roadmap-driven.json +17 -6
  70. package/chains/spec-driven.json +17 -6
  71. package/dashboard/dist/assets/{ArtifactsPage-BmPOu8sO.js → ArtifactsPage-DZNCi6tn.js} +12 -7
  72. package/dashboard/dist/assets/ChatInput-Bvr-FeEq.js +49 -0
  73. package/dashboard/dist/assets/ChatPage-D9zTkJZo.js +22 -0
  74. package/dashboard/dist/assets/CollabPage-B4NAHXS2.js +1 -0
  75. package/dashboard/dist/assets/ExecutionPanel-CFt4LJyq.js +1 -0
  76. package/dashboard/dist/assets/KanbanPage-C8USth6H.js +21 -0
  77. package/dashboard/dist/assets/{MarkdownRenderer-BjZ43aSa.js → MarkdownRenderer-X4af_WNb.js} +1 -1
  78. package/dashboard/dist/assets/McpPage-BKfCVIyU.js +21 -0
  79. package/dashboard/dist/assets/OutputPanel-BlBQFJSW.js +1 -0
  80. package/dashboard/dist/assets/ProblemsPanel-De3DLvoI.js +1 -0
  81. package/dashboard/dist/assets/{RequirementBoardPage-B7yRL0s_.js → RequirementBoardPage-Bf1trzqs.js} +2 -2
  82. package/dashboard/dist/assets/{RequirementPage-D8J_-b6O.js → RequirementPage-Bllxe2XI.js} +10 -5
  83. package/dashboard/dist/assets/{SpecsPage-6lO8v8_C.js → SpecsPage-9lwxKT27.js} +2 -2
  84. package/dashboard/dist/assets/{SupervisorPage-Ds5N378a.js → SupervisorPage-SusdfHFq.js} +1 -1
  85. package/dashboard/dist/assets/{TeamsPage-DrkKr17T.js → TeamsPage-DsuM6OwC.js} +2 -2
  86. package/dashboard/dist/assets/TreeBrowser-Q12qobZs.js +6 -0
  87. package/dashboard/dist/assets/WorkflowPage-D_Fzdy3_.js +6 -0
  88. package/dashboard/dist/assets/{arrow-left-CadP5YgU.js → arrow-left-Bqtb2hle.js} +1 -1
  89. package/dashboard/dist/assets/{check-5xufDzS8.js → check-u6fGOwQO.js} +1 -1
  90. package/dashboard/dist/assets/{chevron-right-CYbpR4ev.js → chevron-right-Csu22t58.js} +1 -1
  91. package/dashboard/dist/assets/{circle-Bm-5Q-Yh.js → circle-CMrkbRNg.js} +1 -1
  92. package/dashboard/dist/assets/{circle-alert-BqcYuT7x.js → circle-alert-c3tH1P4z.js} +1 -1
  93. package/dashboard/dist/assets/{circle-check-big-yyzAFysU.js → circle-check-big-TDSeWstm.js} +1 -1
  94. package/dashboard/dist/assets/{circle-check-DEVzW_lm.js → circle-check-gYxxSYuH.js} +1 -1
  95. package/dashboard/dist/assets/{code-BBdC8Wmw.js → code-CFN2uX9V.js} +1 -1
  96. package/dashboard/dist/assets/{columns-3-CQ9Trztr.js → columns-3-38xIDlzy.js} +1 -1
  97. package/dashboard/dist/assets/{download-DayuF-sn.js → download-DC7KkKyP.js} +1 -1
  98. package/dashboard/dist/assets/{folder-CqXeSKeC.js → folder-CWq_lAnf.js} +1 -1
  99. package/dashboard/dist/assets/index-DWG-WrzT.js +231 -0
  100. package/dashboard/dist/assets/{index-Dru5HYy0.js → index-Do71weNR.js} +1 -1
  101. package/dashboard/dist/assets/index-GUNJodSR.css +1 -0
  102. package/dashboard/dist/assets/{list-DBOD6IUt.js → list-CgIP_2A-.js} +1 -1
  103. package/dashboard/dist/assets/{minus-fQI1Syn2.js → minus-DYoN5UGk.js} +1 -1
  104. package/dashboard/dist/assets/{pen-line-Bkbbngl5.js → pen-line-Bh_WKYHm.js} +1 -1
  105. package/dashboard/dist/assets/{proxy-teW12DdZ.js → proxy-BKxDAKTj.js} +1 -1
  106. package/dashboard/dist/assets/{search-Bq3ygFUW.js → search-SieXnOgr.js} +1 -1
  107. package/dashboard/dist/assets/{shallow-22ZN8sFt.js → shallow-Bme1JY57.js} +1 -1
  108. package/dashboard/dist/assets/{table-BEYtdWc4.js → table-llyEtj-7.js} +1 -1
  109. package/dashboard/dist/assets/terminal-BB3Xfuv5.js +6 -0
  110. package/dashboard/dist/assets/{trash-2-DMqGBgcF.js → trash-2-C8f4vFFM.js} +1 -1
  111. package/dashboard/dist/assets/{zap-9DVkGVtt.js → zap-4uwlzVm0.js} +1 -1
  112. package/dashboard/dist/index.html +2 -2
  113. package/dashboard/dist-server/dashboard/src/server/agents/claude-code-adapter.js +8 -4
  114. package/dashboard/dist-server/dashboard/src/server/agents/claude-code-adapter.js.map +1 -1
  115. package/dashboard/dist-server/dashboard/src/server/agents/entry-normalizer.d.ts +1 -0
  116. package/dashboard/dist-server/dashboard/src/server/agents/entry-normalizer.js +2 -1
  117. package/dashboard/dist-server/dashboard/src/server/agents/entry-normalizer.js.map +1 -1
  118. package/dashboard/dist-server/dashboard/src/server/agents/stream-json-adapter.js +20 -10
  119. package/dashboard/dist-server/dashboard/src/server/agents/stream-json-adapter.js.map +1 -1
  120. package/dashboard/dist-server/dashboard/src/server/routes/git.d.ts +2 -0
  121. package/dashboard/dist-server/dashboard/src/server/routes/git.js +79 -0
  122. package/dashboard/dist-server/dashboard/src/server/routes/git.js.map +1 -0
  123. package/dashboard/dist-server/dashboard/src/server/routes/index.js +3 -0
  124. package/dashboard/dist-server/dashboard/src/server/routes/index.js.map +1 -1
  125. package/dashboard/dist-server/dashboard/src/server/routes/workspace.js +43 -0
  126. package/dashboard/dist-server/dashboard/src/server/routes/workspace.js.map +1 -1
  127. package/dashboard/dist-server/dashboard/src/server/state/state-manager.js +43 -3
  128. package/dashboard/dist-server/dashboard/src/server/state/state-manager.js.map +1 -1
  129. package/dashboard/package.json +59 -59
  130. package/dist/src/cli.js +3 -1
  131. package/dist/src/cli.js.map +1 -1
  132. package/dist/src/commands/{team.d.ts → collab.d.ts} +2 -2
  133. package/dist/src/commands/collab.d.ts.map +1 -0
  134. package/dist/src/commands/{team.js → collab.js} +391 -24
  135. package/dist/src/commands/collab.js.map +1 -0
  136. package/dist/src/commands/msg.d.ts.map +1 -1
  137. package/dist/src/commands/msg.js +4 -3
  138. package/dist/src/commands/msg.js.map +1 -1
  139. package/dist/src/hooks/team-monitor.d.ts.map +1 -1
  140. package/dist/src/hooks/team-monitor.js +16 -0
  141. package/dist/src/hooks/team-monitor.js.map +1 -1
  142. package/dist/src/tools/collab-adapter.d.ts +85 -0
  143. package/dist/src/tools/collab-adapter.d.ts.map +1 -0
  144. package/dist/src/tools/collab-adapter.js +320 -0
  145. package/dist/src/tools/collab-adapter.js.map +1 -0
  146. package/dist/src/tools/namespace-guard.d.ts +2 -0
  147. package/dist/src/tools/namespace-guard.d.ts.map +1 -1
  148. package/dist/src/tools/namespace-guard.js +12 -0
  149. package/dist/src/tools/namespace-guard.js.map +1 -1
  150. package/dist/src/tools/phase-gate-evaluator.d.ts +45 -0
  151. package/dist/src/tools/phase-gate-evaluator.d.ts.map +1 -0
  152. package/dist/src/tools/phase-gate-evaluator.js +42 -0
  153. package/dist/src/tools/phase-gate-evaluator.js.map +1 -0
  154. package/dist/src/tools/team-members.d.ts +18 -0
  155. package/dist/src/tools/team-members.d.ts.map +1 -1
  156. package/dist/src/tools/team-members.js +50 -0
  157. package/dist/src/tools/team-members.js.map +1 -1
  158. package/dist/src/tools/team-tasks.d.ts +120 -0
  159. package/dist/src/tools/team-tasks.d.ts.map +1 -0
  160. package/dist/src/tools/team-tasks.js +365 -0
  161. package/dist/src/tools/team-tasks.js.map +1 -0
  162. package/dist/src/tools/transition-recorder.d.ts +3 -0
  163. package/dist/src/tools/transition-recorder.d.ts.map +1 -1
  164. package/dist/src/tools/transition-recorder.js +52 -1
  165. package/dist/src/tools/transition-recorder.js.map +1 -1
  166. package/dist/src/utils/get-version.d.ts.map +1 -1
  167. package/dist/src/utils/get-version.js +15 -4
  168. package/dist/src/utils/get-version.js.map +1 -1
  169. package/package.json +1 -1
  170. package/templates/config.json +7 -0
  171. package/templates/worktree-scope.json +10 -0
  172. package/templates/worktrees.json +27 -0
  173. package/workflows/analyze.md +86 -36
  174. package/workflows/brainstorm.md +17 -37
  175. package/workflows/execute.md +94 -28
  176. package/workflows/fork.md +309 -0
  177. package/workflows/init.md +10 -1
  178. package/workflows/issue.md +66 -7
  179. package/workflows/maestro-coordinate.md +23 -16
  180. package/workflows/maestro.md +52 -35
  181. package/workflows/merge.md +285 -0
  182. package/workflows/milestone-audit.md +89 -70
  183. package/workflows/milestone-complete.md +89 -156
  184. package/workflows/plan.md +122 -17
  185. package/workflows/retrospective.md +3 -3
  186. package/workflows/roadmap.md +11 -3
  187. package/workflows/spec-generate.md +9 -0
  188. package/workflows/status.md +76 -27
  189. package/workflows/ui-design.md +14 -12
  190. package/workflows/verify.md +44 -8
  191. package/.claude/commands/maestro-phase-add.md +0 -63
  192. package/.claude/commands/maestro-phase-transition.md +0 -75
  193. package/.codex/skills/maestro-phase-add/SKILL.md +0 -154
  194. package/.codex/skills/maestro-phase-transition/SKILL.md +0 -173
  195. package/chains/singles/phase-add.json +0 -31
  196. package/chains/singles/phase-transition.json +0 -23
  197. package/dashboard/dist/assets/ChatInput-CL8YDfOU.js +0 -67
  198. package/dashboard/dist/assets/ChatPage-CT-ozBK2.js +0 -8
  199. package/dashboard/dist/assets/CollabPage-C0rWMden.js +0 -1
  200. package/dashboard/dist/assets/KanbanPage-C6WbAlwI.js +0 -16
  201. package/dashboard/dist/assets/McpPage-BPIXADQi.js +0 -16
  202. package/dashboard/dist/assets/TreeBrowser-g_QUKemL.js +0 -11
  203. package/dashboard/dist/assets/WorkflowPage-X8aNkDEr.js +0 -6
  204. package/dashboard/dist/assets/git-branch-SqFf4Ru5.js +0 -6
  205. package/dashboard/dist/assets/index-D2Mtyw7I.css +0 -1
  206. package/dashboard/dist/assets/index-nufWop4p.js +0 -231
  207. package/dashboard/dist/assets/wrench-B84-zdLI.js +0 -11
  208. package/dist/src/commands/team.d.ts.map +0 -1
  209. package/dist/src/commands/team.js.map +0 -1
  210. package/workflows/phase-add.md +0 -252
  211. package/workflows/phase-transition.md +0 -399
@@ -0,0 +1,309 @@
1
+ # Workflow: fork
2
+
3
+ Create a git worktree for an entire milestone, enabling inter-milestone parallel development. Copies `.workflow/` context into the worktree since `.workflow/` is gitignored.
4
+
5
+ Worktrees operate at the **milestone level** — all phases within a milestone are owned by one worktree and executed sequentially inside it. Per-phase parallelism within a milestone is not supported.
6
+
7
+ ---
8
+
9
+ ## Step 1: Parse Arguments and Flags
10
+
11
+ ```javascript
12
+ const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString()
13
+
14
+ // Parse flags
15
+ const syncMode = $ARGUMENTS.includes('--sync')
16
+ const baseMatch = $ARGUMENTS.match(/--base\s+(\S+)/)
17
+ const baseBranch = baseMatch ? baseMatch[1] : 'HEAD'
18
+
19
+ // Parse milestone number: -m <N> or bare <N>
20
+ const mFlagMatch = $ARGUMENTS.match(/-m\s+(\d+)/)
21
+ const cleaned = $ARGUMENTS
22
+ .replace(/--sync|--base\s+\S+|-m\s+\d+/g, '')
23
+ .trim()
24
+ const bareNumMatch = cleaned.match(/^(\d+)$/)
25
+ const milestoneNum = mFlagMatch
26
+ ? parseInt(mFlagMatch[1])
27
+ : bareNumMatch
28
+ ? parseInt(bareNumMatch[1])
29
+ : null
30
+ ```
31
+
32
+ ---
33
+
34
+ ## Step 2: Validate Prerequisites
35
+
36
+ ```
37
+ IF NOT file_exists(".workflow/state.json"):
38
+ ERROR E001: "Project not initialized. Run maestro-init first."
39
+ EXIT
40
+
41
+ IF NOT file_exists(".workflow/roadmap.md"):
42
+ ERROR E002: "No roadmap found. Run maestro-roadmap first."
43
+ EXIT
44
+
45
+ IF file_exists(".workflow/worktree-scope.json"):
46
+ ERROR E003: "Cannot fork from inside a worktree. Run from the main worktree."
47
+ EXIT
48
+
49
+ IF milestoneNum === null:
50
+ ERROR E004: "Milestone number required. Usage: maestro-fork -m <number>"
51
+ EXIT
52
+
53
+ Read .workflow/state.json → projectState
54
+ Read .workflow/config.json → config (if exists, else use defaults)
55
+
56
+ worktreeRoot = config.worktree?.root ?? ".worktrees"
57
+ branchPrefix = config.worktree?.branch_prefix ?? "milestone/"
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Step 3: Resolve Milestone
63
+
64
+ ```
65
+ // Lookup milestone by number from state.json.milestones[]
66
+ IF NOT projectState.milestones || NOT Array.isArray(projectState.milestones):
67
+ ERROR E005: "No milestones defined in state.json."
68
+ EXIT
69
+
70
+ // milestones[] is 0-indexed, milestoneNum is 1-based
71
+ milestoneEntry = projectState.milestones[milestoneNum - 1]
72
+
73
+ IF NOT milestoneEntry:
74
+ availableList = projectState.milestones
75
+ .map((m, i) => " M" + (i + 1) + ": " + m.name + " (" + m.title + ")")
76
+ .join("\n")
77
+ ERROR E006: "Milestone {milestoneNum} not found.\nAvailable:\n{availableList}"
78
+ EXIT
79
+
80
+ milestoneName = milestoneEntry.name // e.g. "Production"
81
+ milestoneTitle = milestoneEntry.title // e.g. "生产就绪"
82
+ milestonePhases = milestoneEntry.phases // e.g. [3, 4]
83
+ milestoneSlug = milestoneName.toLowerCase().replace(/[^a-z0-9]+/g, '-').substring(0, 40)
84
+ ```
85
+
86
+ ---
87
+
88
+ ## Step 4: Sync Mode (--sync)
89
+
90
+ If `syncMode` is true, this is a sync operation on an existing worktree, not a fork.
91
+
92
+ ```
93
+ IF syncMode:
94
+ Read .workflow/worktrees.json → registry
95
+ entry = registry.worktrees.find(w =>
96
+ w.milestone_num === milestoneNum && w.status === "active"
97
+ )
98
+
99
+ IF NOT entry:
100
+ ERROR E007: "No active worktree for milestone {milestoneNum} ({milestoneName})"
101
+ EXIT
102
+
103
+ // Step 4a: Pull source code
104
+ Bash("cd {entry.path} && git merge main")
105
+ IF conflict:
106
+ WARN "Merge conflict in worktree. Resolve in {entry.path} before continuing."
107
+ EXIT
108
+
109
+ // Step 4b: Re-copy shared context
110
+ Copy .workflow/project.md → {entry.path}/.workflow/project.md
111
+ Copy .workflow/roadmap.md → {entry.path}/.workflow/roadmap.md
112
+ Copy .workflow/config.json → {entry.path}/.workflow/config.json (if exists)
113
+ Copy .workflow/specs/ → {entry.path}/.workflow/specs/ (if exists)
114
+
115
+ Display "Worktree for M{milestoneNum} ({milestoneName}) synced with main."
116
+ EXIT (sync complete)
117
+ ```
118
+
119
+ ---
120
+
121
+ ## Step 5: Validate & Confirm
122
+
123
+ ```
124
+ // Load phase index files for all milestone phases
125
+ phaseList = []
126
+ for (phaseNum of milestonePhases):
127
+ Glob: .workflow/phases/{NN}-*/index.json where NN matches phaseNum
128
+ Read index.json → phaseIndex
129
+ phaseList.push(phaseIndex)
130
+
131
+ // Validate: milestone should have at least one non-completed phase
132
+ nonCompleted = phaseList.filter(p => p.status !== "completed")
133
+ IF nonCompleted.length === 0:
134
+ Display "All phases in M{milestoneNum} ({milestoneName}) are already completed. Nothing to fork."
135
+ EXIT
136
+
137
+ // Check for already-forked milestone
138
+ IF file_exists(".workflow/worktrees.json"):
139
+ Read .workflow/worktrees.json → existingRegistry
140
+ alreadyForked = existingRegistry.worktrees.find(w =>
141
+ w.milestone_num === milestoneNum && w.status === "active"
142
+ )
143
+ IF alreadyForked:
144
+ ERROR E008: "M{milestoneNum} already has an active worktree at {alreadyForked.path}. Merge or cleanup first."
145
+ EXIT
146
+
147
+ Display "Fork Milestone {milestoneNum}: {milestoneName} ({milestoneTitle})"
148
+ Display "Phases ({phaseList.length}):"
149
+ for (p of phaseList):
150
+ Display " Phase {p.phase}: {p.title} [{p.status}]"
151
+
152
+ AskUserQuestion: "Create worktree for this milestone? (y/n)"
153
+ IF response !== 'y': EXIT
154
+ ```
155
+
156
+ ---
157
+
158
+ ## Step 6: Create Worktree
159
+
160
+ ```
161
+ const forkSessionId = "fork-" + getUtc8ISOString().substring(0, 19).replace(/[-:T]/g, '')
162
+ const baseCommit = Bash("git rev-parse HEAD").trim()
163
+ const branch = branchPrefix + milestoneSlug
164
+ const wtPath = worktreeRoot + "/m" + milestoneNum + "-" + milestoneSlug
165
+
166
+ // 6a: Clean up stale worktree/branch if exists
167
+ IF directory_exists(wtPath):
168
+ Bash("git worktree remove --force {wtPath}") // ignore errors
169
+ Bash("git branch -D {branch}") // ignore errors (may not exist)
170
+
171
+ // 6b: Create worktree
172
+ Bash("git worktree add -b {branch} {wtPath} {baseBranch}")
173
+
174
+ // 6c: Create .workflow/ structure in worktree
175
+ Bash("mkdir -p {wtPath}/.workflow/phases")
176
+
177
+ // 6d: Copy shared context (read-only)
178
+ Copy .workflow/project.md → {wtPath}/.workflow/project.md
179
+ Copy .workflow/roadmap.md → {wtPath}/.workflow/roadmap.md
180
+ IF file_exists(".workflow/config.json"):
181
+ Copy .workflow/config.json → {wtPath}/.workflow/config.json
182
+ IF directory_exists(".workflow/specs"):
183
+ Copy .workflow/specs/ → {wtPath}/.workflow/specs/
184
+
185
+ // 6e: Copy ALL phase directories for this milestone
186
+ ownedPhaseNumbers = []
187
+ for (p of phaseList):
188
+ NN = String(p.phase).padStart(2, '0')
189
+ Copy .workflow/phases/{NN}-{p.slug}/ → {wtPath}/.workflow/phases/{NN}-{p.slug}/
190
+ ownedPhaseNumbers.push(p.phase)
191
+
192
+ // 6f: Copy completed dependency phase dirs outside this milestone (read-only reference)
193
+ allDeps = new Set()
194
+ for (p of phaseList):
195
+ for (dep of p.depends_on):
196
+ IF NOT ownedPhaseNumbers.includes(dep):
197
+ allDeps.add(dep)
198
+
199
+ for (dep of allDeps):
200
+ depNN = String(dep).padStart(2, '0')
201
+ Glob: .workflow/phases/{depNN}-*/index.json
202
+ Read → depIndex
203
+ Copy .workflow/phases/{depNN}-{depIndex.slug}/ → {wtPath}/.workflow/phases/{depNN}-{depIndex.slug}/
204
+
205
+ // 6g: Write worktree-scope.json
206
+ Write {wtPath}/.workflow/worktree-scope.json:
207
+ {
208
+ "worktree": true,
209
+ "milestone_num": milestoneNum,
210
+ "milestone": milestoneName,
211
+ "owned_phases": ownedPhaseNumbers,
212
+ "main_worktree": resolve(cwd),
213
+ "branch": branch,
214
+ "base_commit": baseCommit,
215
+ "created_at": getUtc8ISOString()
216
+ }
217
+
218
+ // 6h: Write scoped state.json
219
+ Read .workflow/state.json → mainState
220
+ firstPending = phaseList.find(p => p.status !== "completed")
221
+ scopedState = {
222
+ ...mainState,
223
+ current_phase: firstPending?.phase ?? phaseList[0].phase,
224
+ current_milestone: milestoneName
225
+ }
226
+ Write {wtPath}/.workflow/state.json: scopedState
227
+ ```
228
+
229
+ ---
230
+
231
+ ## Step 7: Update Main Registry
232
+
233
+ ```
234
+ IF file_exists(".workflow/worktrees.json"):
235
+ Read .workflow/worktrees.json → registry
236
+ ELSE:
237
+ registry = { version: "1.0", worktrees: [], fork_sessions: [] }
238
+
239
+ registry.worktrees.push({
240
+ milestone_num: milestoneNum,
241
+ milestone: milestoneName,
242
+ slug: milestoneSlug,
243
+ branch: branch,
244
+ path: wtPath,
245
+ base_commit: baseCommit,
246
+ status: "active",
247
+ created_at: getUtc8ISOString(),
248
+ owned_phases: ownedPhaseNumbers,
249
+ fork_session: forkSessionId
250
+ })
251
+
252
+ registry.fork_sessions.push({
253
+ session_id: forkSessionId,
254
+ created_at: getUtc8ISOString(),
255
+ milestone_num: milestoneNum,
256
+ milestone: milestoneName,
257
+ base_branch: baseBranch,
258
+ base_commit: baseCommit
259
+ })
260
+
261
+ Write .workflow/worktrees.json: registry
262
+
263
+ // Mark milestone phases as "forked" in main
264
+ for (p of phaseList):
265
+ IF p.status !== "completed":
266
+ NN = String(p.phase).padStart(2, '0')
267
+ Read .workflow/phases/{NN}-{p.slug}/index.json → idx
268
+ idx.status = "forked"
269
+ idx.updated_at = getUtc8ISOString()
270
+ Write .workflow/phases/{NN}-{p.slug}/index.json: idx
271
+
272
+ mainState.last_updated = getUtc8ISOString()
273
+ Write .workflow/state.json: mainState
274
+ ```
275
+
276
+ ---
277
+
278
+ ## Step 8: Display Summary
279
+
280
+ ```
281
+ Display:
282
+ === FORK COMPLETE ===
283
+ Session: {forkSessionId}
284
+ Base: {baseBranch} ({baseCommit.substring(0, 7)})
285
+ Milestone: M{milestoneNum} — {milestoneName} ({milestoneTitle})
286
+ Branch: {branch}
287
+ Path: {wtPath}
288
+ Phases: {ownedPhaseNumbers.join(', ')}
289
+
290
+ Next steps (run in the worktree):
291
+ cd {wtPath}
292
+
293
+ # Sequential lifecycle for each phase:
294
+ /maestro-analyze {firstPending.phase}
295
+ /maestro-plan {firstPending.phase}
296
+ /maestro-execute {firstPending.phase}
297
+ /maestro-verify {firstPending.phase}
298
+ /maestro-phase-transition {firstPending.phase}
299
+ # ... repeat for next phases in milestone
300
+
301
+ Or delegate (automated):
302
+ maestro delegate "run full lifecycle for milestone" --cd {wtPath} --mode write
303
+
304
+ Sync worktree with main (if needed later):
305
+ /maestro-fork -m {milestoneNum} --sync
306
+
307
+ When all phases in milestone complete:
308
+ /maestro-merge -m {milestoneNum}
309
+ ```
package/workflows/init.md CHANGED
@@ -4,6 +4,15 @@ Project initialization with automatic state detection. Creates project infrastru
4
4
 
5
5
  ---
6
6
 
7
+ ## Worktree Guard
8
+
9
+ ```
10
+ # Block in worktree
11
+ IF file_exists(".workflow/worktree-scope.json"):
12
+ ERROR "Cannot run maestro-init inside a worktree. Run from the main worktree."
13
+ EXIT
14
+ ```
15
+
7
16
  ## Step 1: State Detection
8
17
 
9
18
  Detect current project state to determine initialization path.
@@ -41,7 +50,7 @@ CHECK .workflow/state.json exists?
41
50
 
42
51
  If `--auto` flag: skip interactive questioning, extract from @ referenced document.
43
52
  If `--from-brainstorm SESSION-ID`:
44
- - Locate brainstorm session directory (`.workflow/scratch/brainstorm-*/` or `.workflow/phases/*/`)
53
+ - Locate brainstorm session directory (`.workflow/scratch/brainstorm-*/`)
45
54
  - Read `guidance-specification.md`:
46
55
  - Problem statement → project vision + core value
47
56
  - Features → project goals (Active requirements)
@@ -65,14 +65,21 @@ Parse options from ARGS:
65
65
  Options:
66
66
  --title TEXT Issue title (required)
67
67
  --severity VALUE critical|high|medium|low (default: medium)
68
- --source VALUE verification|antipattern|discuss|discovery|manual (default: manual)
68
+ --source VALUE planned|supplement|bug|review|verification|discovery|manual (default: manual)
69
69
  --phase VALUE Phase reference, e.g. "01-auth" (optional)
70
+ --milestone VALUE Milestone reference, e.g. "MVP" (optional, auto-derived from state.json if omitted)
70
71
  --description TEXT Detailed description (optional, prompted if missing)
71
72
  --priority NUMBER 1-5, lower is higher priority (default: 3)
72
73
  --tags TAG1,TAG2 Comma-separated tags (optional)
73
74
 
74
75
  If --title is missing:
75
76
  AskUserQuestion({ question: "What is the issue title?" })
77
+
78
+ Derive milestone_ref if not provided:
79
+ IF --milestone not provided AND file_exists(".workflow/state.json"):
80
+ milestone_ref = state.json.current_milestone
81
+ ELSE:
82
+ milestone_ref = --milestone value or null
76
83
  ```
77
84
 
78
85
  Generate issue ID:
@@ -96,6 +103,7 @@ Build issue record from template:
96
103
  "priority": {PRIORITY},
97
104
  "severity": "{SEVERITY}",
98
105
  "source": "{SOURCE}",
106
+ "milestone_ref": "{MILESTONE_REF or null}",
99
107
  "phase_ref": "{PHASE_REF or null}",
100
108
  "gap_ref": null,
101
109
  "description": "{DESCRIPTION}",
@@ -135,8 +143,56 @@ Write to storage:
135
143
  Title: {TITLE}
136
144
  Status: open
137
145
  Severity: {SEVERITY}
146
+ ```
147
+
148
+ Ask for supplementary information:
149
+
150
+ ```
151
+ 4. Ask user for supplementary context:
152
+ AskUserQuestion({
153
+ question: "Do you have any supplementary information for this issue?\n1. Additional context or background?\n2. Reproduction steps or affected files?\n3. Related issues or tasks?\n4. Other notes?\n\n(Press Enter to skip)"
154
+ })
155
+
156
+ 5. Process user response:
157
+ - If response is non-empty:
158
+ a. Build supplement entry:
159
+ {
160
+ "content": "{RESPONSE_TEXT}",
161
+ "stage": "post_creation",
162
+ "author": "user",
163
+ "created_at": "{NOW_ISO}"
164
+ }
165
+ b. Read issues.jsonl
166
+ c. Find the just-created issue by {ID}
167
+ d. Initialize supplements as [] if not present
168
+ e. Append the supplement entry
169
+ f. Set updated_at = NOW_ISO
170
+ g. Rewrite issues.jsonl with updated record
171
+ h. Display: "Added supplement to {ID}"
172
+ - If response is empty: skip supplement step
173
+ ```
138
174
 
139
- 4. Suggest next steps:
175
+ Cross-milestone conflict check (for supplement issues):
176
+
177
+ ```
178
+ 6. IF source == "supplement" AND milestone_ref is not null:
179
+ a. Read .workflow/roadmap.md
180
+ b. Identify phases belonging to OTHER milestones (not milestone_ref)
181
+ c. For each other-milestone phase, check if plan.json exists:
182
+ Read .workflow/phases/{NN}-{slug}/plan.json (if exists)
183
+ Collect files_to_create[] as planned_files
184
+ d. IF affected_components in the new issue overlap with planned_files:
185
+ WARNING: "Conflict detected: this supplement issue affects components planned in milestone {other_milestone}"
186
+ Display:
187
+ " Issue affects: {overlapping_components}"
188
+ " Planned in: Phase {NN} ({milestone})"
189
+ Display: "Consider: minimal fix now (supplement), or defer to {other_milestone} for full rework"
190
+ ```
191
+
192
+ Suggest next steps:
193
+
194
+ ```
195
+ 7. Suggest next steps:
140
196
  - Skill({ skill: "manage-issue", args: "status {ID}" }) -- View full details
141
197
  - Skill({ skill: "manage-issue", args: "link {ID} --task TASK-NNN" }) -- Link to a task
142
198
  - Skill({ skill: "manage-issue", args: "list" }) -- List all issues
@@ -150,11 +206,12 @@ Parse filter options from ARGS:
150
206
 
151
207
  ```
152
208
  Options:
153
- --status VALUE Filter by status (open|in_progress|completed|failed|deferred)
154
- --phase VALUE Filter by phase_ref
155
- --severity VALUE Filter by severity (critical|high|medium|low)
156
- --source VALUE Filter by source
157
- --all Include closed issues from issue-history.jsonl
209
+ --status VALUE Filter by status (open|in_progress|completed|failed|deferred)
210
+ --phase VALUE Filter by phase_ref
211
+ --milestone VALUE Filter by milestone_ref
212
+ --severity VALUE Filter by severity (critical|high|medium|low)
213
+ --source VALUE Filter by source
214
+ --all Include closed issues from issue-history.jsonl
158
215
  ```
159
216
 
160
217
  Read and filter:
@@ -167,6 +224,7 @@ Read and filter:
167
224
  - If --status: match record.status == VALUE
168
225
  - If --phase: match record.phase_ref contains VALUE
169
226
  - If --severity: match record.severity == VALUE
227
+ - If --milestone: match record.milestone_ref == VALUE
170
228
  - If --source: match record.source == VALUE
171
229
  5. Sort by priority (ascending), then severity order (critical > high > medium > low)
172
230
  ```
@@ -284,6 +342,7 @@ Options:
284
342
  --tags TAG1,TAG2 Replace tags
285
343
  --add-tag TAG Add a tag
286
344
  --phase VALUE Set phase_ref
345
+ --milestone VALUE Set milestone_ref
287
346
  --fix-direction TEXT Set fix_direction
288
347
  --description TEXT Update description
289
348
  --note TEXT Add feedback entry (type=clarification)
@@ -37,9 +37,9 @@ test -f .workflow/state.json && echo "exists" || echo "missing"
37
37
  ```javascript
38
38
  const projectState = {
39
39
  initialized: true,
40
- current_phase: /* from state.json */,
41
- phase_slug: '...',
42
- phase_status: '...', // pending|exploring|planning|executing|verifying|testing|completed|blocked
40
+ current_milestone: /* from state.json */,
41
+ latest_artifact: /* last artifact in artifacts[] */,
42
+ milestone_progress: '...', // derived from artifact registry
43
43
  phase_artifacts: { brainstorm: false, analysis: false, context: false, plan: false, verification: false, uat: false },
44
44
  execution: { tasks_completed: 0, tasks_total: 0 },
45
45
  verification_status: 'pending',
@@ -75,6 +75,10 @@ if (exactMatch[normalized]) {
75
75
 
76
76
  #### 3a-2: Structured intent extraction (LLM-native)
77
77
 
78
+ > **Implementation note**: The runtime (`src/coordinator/intent-router.ts`) currently
79
+ > uses pattern-based regex matching via `chains/_intent-map.json`. The structured
80
+ > extraction below is a design target, not the current implementation.
81
+
78
82
  Instead of regex, extract a structured intent tuple using LLM semantic understanding:
79
83
 
80
84
  ```json
@@ -105,7 +109,7 @@ function routeIntent(intent, projectState) {
105
109
  // Action × Object matrix
106
110
  const matrix = {
107
111
  'fix': { 'bug': 'debug', 'issue': 'issue', 'code': 'debug', 'performance': 'debug', 'security': 'debug', '_default': 'debug' },
108
- 'create': { 'feature': 'quick', 'issue': 'issue', 'test': 'test_gen', 'spec': 'spec_generate', 'ui': 'ui_design', 'config': 'init', 'phase': 'phase_add', '_default': 'quick' },
112
+ 'create': { 'feature': 'quick', 'issue': 'issue', 'test': 'test_gen', 'spec': 'spec_generate', 'ui': 'ui_design', 'config': 'init', 'phase': 'roadmap', '_default': 'quick' },
109
113
  'analyze': { 'bug': 'analyze', 'issue': 'issue_analyze', 'code': 'analyze', 'codebase': 'spec_map', '_default': 'analyze' },
110
114
  'explore': { 'issue': 'issue_discover', 'feature': 'brainstorm', 'ui': 'ui_design', '_default': 'brainstorm' },
111
115
  'plan': { 'issue': 'issue_plan', 'spec': 'spec_generate', '_default': 'plan' },
@@ -115,8 +119,8 @@ function routeIntent(intent, projectState) {
115
119
  'test': { '_default': 'test' },
116
120
  'debug': { '_default': 'debug' },
117
121
  'refactor': { '_default': 'refactor' },
118
- 'manage': { 'issue': 'issue', 'milestone': 'milestone_audit', 'phase': 'phase_transition', 'memory': 'memory', 'doc': 'sync', 'codebase': 'codebase_refresh', 'team': 'team_coordinate', '_default': 'status' },
119
- 'transition':{ 'phase': 'phase_transition', 'milestone': 'milestone_complete', '_default': 'phase_transition' },
122
+ 'manage': { 'issue': 'issue', 'milestone': 'milestone_audit', 'phase': 'milestone_close', 'memory': 'memory', 'doc': 'sync', 'codebase': 'codebase_refresh', 'team': 'team_coordinate', '_default': 'status' },
123
+ 'transition':{ 'phase': 'milestone_close', 'milestone': 'milestone_complete', '_default': 'milestone_close' },
120
124
  'continue': { '_default': 'state_continue' },
121
125
  'sync': { '_default': 'sync' },
122
126
  'learn': { '_default': 'learn' },
@@ -166,18 +170,18 @@ function detectNextAction(s) {
166
170
  if (s.verification_status === 'passed') {
167
171
  if (!s.review_verdict) return { chain: 'review', steps: [{ cmd: 'quality-review', args: '{phase}' }] };
168
172
  if (s.uat_status === 'pending') return { chain: 'test', steps: [{ cmd: 'quality-test', args: '{phase}' }] };
169
- if (s.uat_status === 'passed') return { chain: 'phase-transition', steps: [{ cmd: 'maestro-phase-transition' }] };
173
+ if (s.uat_status === 'passed') return { chain: 'milestone-close', steps: [{ cmd: 'maestro-milestone-audit' }, { cmd: 'maestro-milestone-complete' }] };
170
174
  return { chain: 'debug', steps: [{ cmd: 'quality-debug', args: '--from-uat {phase}' }] };
171
175
  }
172
176
  return { chain: 'quality-loop-partial', steps: [{ cmd: 'maestro-plan', args: '{phase} --gaps' }, { cmd: 'maestro-execute', args: '{phase}' }, { cmd: 'maestro-verify', args: '{phase}' }] };
173
177
  }
174
178
  if (ps === 'testing') {
175
- if (s.uat_status === 'passed') return { chain: 'phase-transition', steps: [{ cmd: 'maestro-phase-transition' }] };
179
+ if (s.uat_status === 'passed') return { chain: 'milestone-close', steps: [{ cmd: 'maestro-milestone-audit' }, { cmd: 'maestro-milestone-complete' }] };
176
180
  return { chain: 'debug', steps: [{ cmd: 'quality-debug', args: '--from-uat {phase}' }] };
177
181
  }
178
182
  if (ps === 'completed') {
179
183
  if (s.phases_completed >= s.phases_total) return { chain: 'milestone-close', steps: [{ cmd: 'maestro-milestone-audit' }, { cmd: 'maestro-milestone-complete' }] };
180
- return { chain: 'phase-transition', steps: [{ cmd: 'maestro-phase-transition' }] };
184
+ return { chain: 'milestone-close', steps: [{ cmd: 'maestro-milestone-audit' }, { cmd: 'maestro-milestone-complete' }] };
181
185
  }
182
186
  if (ps === 'blocked') return { chain: 'debug', steps: [{ cmd: 'quality-debug' }] };
183
187
  return { chain: 'status', steps: [{ cmd: 'manage-status' }] };
@@ -205,8 +209,8 @@ const chainMap = {
205
209
  'retrospective': [{ cmd: 'quality-retrospective', args: '{phase}' }],
206
210
  'learn': [{ cmd: 'manage-learn', args: '"{description}"' }],
207
211
  'sync': [{ cmd: 'quality-sync', args: '{phase}' }],
208
- 'phase_transition': [{ cmd: 'maestro-phase-transition' }],
209
- 'phase_add': [{ cmd: 'maestro-phase-add', args: '"{description}"' }],
212
+ 'milestone_close': [{ cmd: 'maestro-milestone-audit' }, { cmd: 'maestro-milestone-complete' }],
213
+ 'roadmap': [{ cmd: 'maestro-roadmap', args: '"{description}"' }],
210
214
  'milestone_audit': [{ cmd: 'maestro-milestone-audit' }],
211
215
  'milestone_complete': [{ cmd: 'maestro-milestone-complete' }],
212
216
  'codebase_rebuild': [{ cmd: 'manage-codebase-rebuild' }],
@@ -223,6 +227,8 @@ const chainMap = {
223
227
  'issue_plan': [{ cmd: 'manage-issue-plan', args: '"{description}"' }],
224
228
  'issue_execute': [{ cmd: 'manage-issue-execute', args: '"{description}"' }],
225
229
  'quick': [{ cmd: 'maestro-quick', args: '"{description}"' }],
230
+ 'fork': [{ cmd: 'maestro-fork', args: '-m {milestone_num}' }],
231
+ 'merge': [{ cmd: 'maestro-merge', args: '-m {milestone_num}' }],
226
232
  'team_lifecycle': [{ cmd: 'team-lifecycle-v4', args: '"{description}"' }],
227
233
  'team_coordinate': [{ cmd: 'team-coordinate', args: '"{description}"' }],
228
234
  'team_qa': [{ cmd: 'team-quality-assurance', args: '"{description}"' }],
@@ -234,7 +240,7 @@ const chainMap = {
234
240
  'spec-driven': [{ cmd: 'maestro-init' }, { cmd: 'maestro-spec-generate', args: '"{description}"' }, { cmd: 'maestro-plan', args: '{phase}' }, { cmd: 'maestro-execute', args: '{phase}' }, { cmd: 'maestro-verify', args: '{phase}' }],
235
241
  'brainstorm-driven': [{ cmd: 'maestro-brainstorm', args: '"{description}"' }, { cmd: 'maestro-plan', args: '{phase}' }, { cmd: 'maestro-execute', args: '{phase}' }, { cmd: 'maestro-verify', args: '{phase}' }],
236
242
  'ui-design-driven': [{ cmd: 'maestro-ui-design', args: '{phase}' }, { cmd: 'maestro-plan', args: '{phase}' }, { cmd: 'maestro-execute', args: '{phase}' }, { cmd: 'maestro-verify', args: '{phase}' }],
237
- 'full-lifecycle': [{ cmd: 'maestro-plan', args: '{phase}' }, { cmd: 'maestro-execute', args: '{phase}' }, { cmd: 'maestro-verify', args: '{phase}' }, { cmd: 'quality-review', args: '{phase}' }, { cmd: 'quality-test', args: '{phase}' }, { cmd: 'maestro-phase-transition' }],
243
+ 'full-lifecycle': [{ cmd: 'maestro-plan', args: '{phase}' }, { cmd: 'maestro-execute', args: '{phase}' }, { cmd: 'maestro-verify', args: '{phase}' }, { cmd: 'quality-review', args: '{phase}' }, { cmd: 'quality-test', args: '{phase}' }, { cmd: 'maestro-milestone-audit' }, { cmd: 'maestro-milestone-complete' }],
238
244
  'execute-verify': [{ cmd: 'maestro-execute', args: '{phase}' }, { cmd: 'maestro-verify', args: '{phase}' }],
239
245
  'quality-loop': [{ cmd: 'maestro-verify', args: '{phase}' }, { cmd: 'quality-review', args: '{phase}' }, { cmd: 'quality-test', args: '{phase}' }, { cmd: 'quality-debug', args: '--from-uat {phase}' }, { cmd: 'maestro-plan', args: '{phase} --gaps' }, { cmd: 'maestro-execute', args: '{phase}' }],
240
246
  'milestone-close': [{ cmd: 'maestro-milestone-audit' }, { cmd: 'maestro-milestone-complete' }],
@@ -269,7 +275,8 @@ function resolvePhase() {
269
275
  // Fallback regex
270
276
  const m = intent.match(/phase\s*(\d+)|^(\d+)$/);
271
277
  if (m) return m[1] || m[2];
272
- if (projectState.initialized) return projectState.current_phase;
278
+ // With scratch-based architecture, commands default to milestone-wide when no phase specified
279
+ // Return null to let commands use their default scope routing
273
280
  return null;
274
281
  }
275
282
 
@@ -320,7 +327,7 @@ const state = {
320
327
  };
321
328
  Write(`${sessionDir}/state.json`, JSON.stringify(state, null, 2));
322
329
 
323
- const context = { current_phase: resolvedPhase, user_intent: intent, issue_id: resolvedIssueId, spec_session_id: null };
330
+ const context = { resolved_phase: resolvedPhase, user_intent: intent, issue_id: resolvedIssueId, spec_session_id: null };
324
331
  ```
325
332
 
326
333
  ---
@@ -338,7 +345,7 @@ const AUTO_FLAG_MAP = {
338
345
 
339
346
  function assembleArgs(step) {
340
347
  let a = (step.args || '')
341
- .replace(/\{phase\}/g, context.current_phase || '')
348
+ .replace(/\{phase\}/g, context.resolved_phase || '') // empty = milestone-wide default
342
349
  .replace(/\{description\}/g, context.user_intent || '')
343
350
  .replace(/\{issue_id\}/g, context.issue_id || '')
344
351
  .replace(/\{spec_session_id\}/g, context.spec_session_id || '')
@@ -418,7 +425,7 @@ step.completed_at = new Date().toISOString();
418
425
 
419
426
  // Context propagation
420
427
  const phaseMatch = output.match(/PHASE:\s*(\d+)/m);
421
- if (phaseMatch) context.current_phase = phaseMatch[1];
428
+ if (phaseMatch) context.resolved_phase = phaseMatch[1];
422
429
  const specMatch = output.match(/SPEC-[\w-]+/);
423
430
  if (specMatch) context.spec_session_id = specMatch[0];
424
431
  const scratchMatch = output.match(/scratch_dir:\s*(.+)/m);