maxsimcli 3.12.0 → 4.0.1

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 (179) hide show
  1. package/README.md +43 -17
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/adapters/index.d.ts +0 -11
  4. package/dist/adapters/index.d.ts.map +1 -1
  5. package/dist/adapters/index.js +4 -40
  6. package/dist/adapters/index.js.map +1 -1
  7. package/dist/assets/CHANGELOG.md +70 -0
  8. package/dist/assets/dashboard/client/assets/{index-wtQDvXzr.js → index-C_eAetZJ.js} +60 -60
  9. package/dist/assets/dashboard/client/assets/index-CmiJKqOU.css +32 -0
  10. package/dist/assets/dashboard/client/index.html +2 -2
  11. package/dist/assets/dashboard/server.js +467 -271
  12. package/dist/assets/templates/agents/AGENTS.md +13 -1
  13. package/dist/assets/templates/agents/maxsim-debugger.md +2 -2
  14. package/dist/assets/templates/agents/maxsim-executor.md +5 -5
  15. package/dist/assets/templates/agents/maxsim-phase-researcher.md +2 -2
  16. package/dist/assets/templates/agents/maxsim-plan-checker.md +2 -2
  17. package/dist/assets/templates/agents/maxsim-planner.md +3 -3
  18. package/dist/assets/templates/commands/maxsim/add-todo.md +15 -5
  19. package/dist/assets/templates/commands/maxsim/discuss-phase.md +1 -0
  20. package/dist/assets/templates/commands/maxsim/init-existing.md +4 -0
  21. package/dist/assets/templates/commands/maxsim/new-project.md +4 -0
  22. package/dist/assets/templates/references/thinking-partner.md +41 -0
  23. package/dist/assets/templates/skills/batch-worktree/SKILL.md +137 -0
  24. package/dist/assets/templates/skills/brainstorming/SKILL.md +159 -0
  25. package/dist/assets/templates/skills/roadmap-writing/SKILL.md +198 -0
  26. package/dist/assets/templates/skills/sdd/SKILL.md +175 -0
  27. package/dist/assets/templates/skills/simplify/SKILL.md +48 -0
  28. package/dist/assets/templates/skills/using-maxsim/SKILL.md +6 -1
  29. package/dist/assets/templates/templates/acceptance-criteria.md +10 -0
  30. package/dist/assets/templates/templates/decisions.md +10 -0
  31. package/dist/assets/templates/templates/no-gos.md +9 -0
  32. package/dist/assets/templates/workflows/add-todo.md +89 -0
  33. package/dist/assets/templates/workflows/discuss-phase.md +85 -1
  34. package/dist/assets/templates/workflows/execute-phase.md +22 -2
  35. package/dist/assets/templates/workflows/execute-plan.md +166 -0
  36. package/dist/assets/templates/workflows/init-existing.md +116 -0
  37. package/dist/assets/templates/workflows/new-project.md +105 -1
  38. package/dist/assets/templates/workflows/plan-phase.md +3 -3
  39. package/dist/assets/templates/workflows/quick.md +2 -2
  40. package/dist/cli.cjs +1264 -882
  41. package/dist/cli.cjs.map +1 -1
  42. package/dist/cli.js +97 -74
  43. package/dist/cli.js.map +1 -1
  44. package/dist/core/artefakte.d.ts +12 -0
  45. package/dist/core/artefakte.d.ts.map +1 -0
  46. package/dist/core/artefakte.js +136 -0
  47. package/dist/core/artefakte.js.map +1 -0
  48. package/dist/core/commands.d.ts +13 -13
  49. package/dist/core/commands.d.ts.map +1 -1
  50. package/dist/core/commands.js +44 -57
  51. package/dist/core/commands.js.map +1 -1
  52. package/dist/core/config.d.ts +4 -3
  53. package/dist/core/config.d.ts.map +1 -1
  54. package/dist/core/config.js +14 -18
  55. package/dist/core/config.js.map +1 -1
  56. package/dist/core/context-loader.d.ts +20 -0
  57. package/dist/core/context-loader.d.ts.map +1 -0
  58. package/dist/core/context-loader.js +154 -0
  59. package/dist/core/context-loader.js.map +1 -0
  60. package/dist/core/core.d.ts +8 -2
  61. package/dist/core/core.d.ts.map +1 -1
  62. package/dist/core/core.js +47 -11
  63. package/dist/core/core.js.map +1 -1
  64. package/dist/core/dashboard-launcher.d.ts +1 -1
  65. package/dist/core/dashboard-launcher.d.ts.map +1 -1
  66. package/dist/core/dashboard-launcher.js +18 -15
  67. package/dist/core/dashboard-launcher.js.map +1 -1
  68. package/dist/core/frontmatter.d.ts +5 -5
  69. package/dist/core/frontmatter.d.ts.map +1 -1
  70. package/dist/core/frontmatter.js +21 -26
  71. package/dist/core/frontmatter.js.map +1 -1
  72. package/dist/core/index.d.ts +8 -3
  73. package/dist/core/index.d.ts.map +1 -1
  74. package/dist/core/index.js +23 -3
  75. package/dist/core/index.js.map +1 -1
  76. package/dist/core/init.d.ts +14 -14
  77. package/dist/core/init.d.ts.map +1 -1
  78. package/dist/core/init.js +93 -154
  79. package/dist/core/init.js.map +1 -1
  80. package/dist/core/milestone.d.ts +3 -3
  81. package/dist/core/milestone.d.ts.map +1 -1
  82. package/dist/core/milestone.js +9 -9
  83. package/dist/core/milestone.js.map +1 -1
  84. package/dist/core/phase.d.ts +9 -9
  85. package/dist/core/phase.d.ts.map +1 -1
  86. package/dist/core/phase.js +64 -68
  87. package/dist/core/phase.js.map +1 -1
  88. package/dist/core/roadmap.d.ts +4 -3
  89. package/dist/core/roadmap.d.ts.map +1 -1
  90. package/dist/core/roadmap.js +46 -109
  91. package/dist/core/roadmap.js.map +1 -1
  92. package/dist/core/skills.d.ts +19 -0
  93. package/dist/core/skills.d.ts.map +1 -0
  94. package/dist/core/skills.js +145 -0
  95. package/dist/core/skills.js.map +1 -0
  96. package/dist/core/start.d.ts +15 -0
  97. package/dist/core/start.d.ts.map +1 -0
  98. package/dist/core/start.js +80 -0
  99. package/dist/core/start.js.map +1 -0
  100. package/dist/core/state.d.ts +13 -13
  101. package/dist/core/state.d.ts.map +1 -1
  102. package/dist/core/state.js +119 -126
  103. package/dist/core/state.js.map +1 -1
  104. package/dist/core/template.d.ts +3 -3
  105. package/dist/core/template.d.ts.map +1 -1
  106. package/dist/core/template.js +12 -14
  107. package/dist/core/template.js.map +1 -1
  108. package/dist/core/types.d.ts +14 -2
  109. package/dist/core/types.d.ts.map +1 -1
  110. package/dist/core/types.js +8 -0
  111. package/dist/core/types.js.map +1 -1
  112. package/dist/core/verify.d.ts +10 -9
  113. package/dist/core/verify.d.ts.map +1 -1
  114. package/dist/core/verify.js +38 -48
  115. package/dist/core/verify.js.map +1 -1
  116. package/dist/core-TFSlUjV1.cjs +4312 -0
  117. package/dist/core-TFSlUjV1.cjs.map +1 -0
  118. package/dist/install/adapters.d.ts +2 -11
  119. package/dist/install/adapters.d.ts.map +1 -1
  120. package/dist/install/adapters.js +16 -154
  121. package/dist/install/adapters.js.map +1 -1
  122. package/dist/install/copy.d.ts +1 -10
  123. package/dist/install/copy.d.ts.map +1 -1
  124. package/dist/install/copy.js +5 -125
  125. package/dist/install/copy.js.map +1 -1
  126. package/dist/install/hooks.d.ts +4 -5
  127. package/dist/install/hooks.d.ts.map +1 -1
  128. package/dist/install/hooks.js +46 -71
  129. package/dist/install/hooks.js.map +1 -1
  130. package/dist/install/index.js +163 -226
  131. package/dist/install/index.js.map +1 -1
  132. package/dist/install/manifest.d.ts +5 -2
  133. package/dist/install/manifest.d.ts.map +1 -1
  134. package/dist/install/manifest.js +20 -26
  135. package/dist/install/manifest.js.map +1 -1
  136. package/dist/install/patches.d.ts +1 -2
  137. package/dist/install/patches.d.ts.map +1 -1
  138. package/dist/install/patches.js +4 -16
  139. package/dist/install/patches.js.map +1 -1
  140. package/dist/install/shared.d.ts +24 -18
  141. package/dist/install/shared.d.ts.map +1 -1
  142. package/dist/install/shared.js +65 -35
  143. package/dist/install/shared.js.map +1 -1
  144. package/dist/install/uninstall.d.ts +2 -3
  145. package/dist/install/uninstall.d.ts.map +1 -1
  146. package/dist/install/uninstall.js +24 -82
  147. package/dist/install/uninstall.js.map +1 -1
  148. package/dist/install.cjs +321 -1230
  149. package/dist/install.cjs.map +1 -1
  150. package/dist/mcp-server.cjs +22527 -46
  151. package/dist/mcp-server.cjs.map +1 -1
  152. package/dist/skills-BOSxYUzf.cjs +6812 -0
  153. package/dist/skills-BOSxYUzf.cjs.map +1 -0
  154. package/package.json +1 -1
  155. package/dist/adapters/codex.d.ts +0 -19
  156. package/dist/adapters/codex.d.ts.map +0 -1
  157. package/dist/adapters/codex.js +0 -94
  158. package/dist/adapters/codex.js.map +0 -1
  159. package/dist/adapters/gemini.d.ts +0 -19
  160. package/dist/adapters/gemini.d.ts.map +0 -1
  161. package/dist/adapters/gemini.js +0 -96
  162. package/dist/adapters/gemini.js.map +0 -1
  163. package/dist/adapters/opencode.d.ts +0 -17
  164. package/dist/adapters/opencode.d.ts.map +0 -1
  165. package/dist/adapters/opencode.js +0 -111
  166. package/dist/adapters/opencode.js.map +0 -1
  167. package/dist/adapters/transforms/content.d.ts +0 -39
  168. package/dist/adapters/transforms/content.d.ts.map +0 -1
  169. package/dist/adapters/transforms/content.js +0 -125
  170. package/dist/adapters/transforms/content.js.map +0 -1
  171. package/dist/adapters/transforms/frontmatter.d.ts +0 -42
  172. package/dist/adapters/transforms/frontmatter.d.ts.map +0 -1
  173. package/dist/adapters/transforms/frontmatter.js +0 -204
  174. package/dist/adapters/transforms/frontmatter.js.map +0 -1
  175. package/dist/adapters/transforms/tool-maps.d.ts +0 -20
  176. package/dist/adapters/transforms/tool-maps.d.ts.map +0 -1
  177. package/dist/adapters/transforms/tool-maps.js +0 -64
  178. package/dist/adapters/transforms/tool-maps.js.map +0 -1
  179. package/dist/assets/dashboard/client/assets/index-CxFKStBk.css +0 -32
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Ported from maxsim/bin/lib/state.cjs
5
5
  */
6
- import type { StateMetricOptions, StateDecisionOptions, StateBlockerOptions, StateSessionOptions } from './types.js';
6
+ import type { StateMetricOptions, StateDecisionOptions, StateBlockerOptions, StateSessionOptions, CmdResult } from './types.js';
7
7
  export declare function stateExtractField(content: string, fieldName: string): string | null;
8
8
  export declare function stateReplaceField(content: string, fieldName: string, newValue: string): string | null;
9
9
  /**
@@ -11,16 +11,16 @@ export declare function stateReplaceField(content: string, fieldName: string, ne
11
11
  * Returns updated content or null if section not found.
12
12
  */
13
13
  export declare function appendToStateSection(content: string, sectionPattern: RegExp, entry: string, placeholderPatterns?: RegExp[]): string | null;
14
- export declare function cmdStateLoad(cwd: string, raw: boolean): void;
15
- export declare function cmdStateGet(cwd: string, section: string | null, raw: boolean): void;
16
- export declare function cmdStatePatch(cwd: string, patches: Record<string, string>, raw: boolean): void;
17
- export declare function cmdStateUpdate(cwd: string, field: string | undefined, value: string | undefined): void;
18
- export declare function cmdStateAdvancePlan(cwd: string, raw: boolean): void;
19
- export declare function cmdStateRecordMetric(cwd: string, options: StateMetricOptions, raw: boolean): void;
20
- export declare function cmdStateUpdateProgress(cwd: string, raw: boolean): void;
21
- export declare function cmdStateAddDecision(cwd: string, options: StateDecisionOptions, raw: boolean): void;
22
- export declare function cmdStateAddBlocker(cwd: string, text: string | StateBlockerOptions, raw: boolean): void;
23
- export declare function cmdStateResolveBlocker(cwd: string, text: string | null, raw: boolean): void;
24
- export declare function cmdStateRecordSession(cwd: string, options: StateSessionOptions, raw: boolean): void;
25
- export declare function cmdStateSnapshot(cwd: string, raw: boolean): void;
14
+ export declare function cmdStateLoad(cwd: string, raw: boolean): Promise<CmdResult>;
15
+ export declare function cmdStateGet(cwd: string, section: string | null, raw: boolean): CmdResult;
16
+ export declare function cmdStatePatch(cwd: string, patches: Record<string, string>, raw: boolean): CmdResult;
17
+ export declare function cmdStateUpdate(cwd: string, field: string | undefined, value: string | undefined): CmdResult;
18
+ export declare function cmdStateAdvancePlan(cwd: string, raw: boolean): CmdResult;
19
+ export declare function cmdStateRecordMetric(cwd: string, options: StateMetricOptions, raw: boolean): CmdResult;
20
+ export declare function cmdStateUpdateProgress(cwd: string, raw: boolean): CmdResult;
21
+ export declare function cmdStateAddDecision(cwd: string, options: StateDecisionOptions, raw: boolean): CmdResult;
22
+ export declare function cmdStateAddBlocker(cwd: string, text: string | StateBlockerOptions, raw: boolean): CmdResult;
23
+ export declare function cmdStateResolveBlocker(cwd: string, text: string | null, raw: boolean): CmdResult;
24
+ export declare function cmdStateRecordSession(cwd: string, options: StateSessionOptions, raw: boolean): CmdResult;
25
+ export declare function cmdStateSnapshot(cwd: string, raw: boolean): CmdResult;
26
26
  //# sourceMappingURL=state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/core/state.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,OAAO,KAAK,EAGV,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EAGpB,MAAM,YAAY,CAAC;AAIpB,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAInF;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOrG;AAYD;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,MAAM,EACb,mBAAmB,CAAC,EAAE,MAAM,EAAE,GAC7B,MAAM,GAAG,IAAI,CAYf;AAID,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CA0C5D;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CAiCnF;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CA2B9F;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAqBtG;AAID,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CA2BnE;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CA+BjG;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CAkCtE;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CA+BlG;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,mBAAmB,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CA4BtG;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CA6B3F;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CA8BnG;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CA6FhE"}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/core/state.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,OAAO,KAAK,EAGV,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EAGnB,SAAS,EACV,MAAM,YAAY,CAAC;AAgBpB,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUnF;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUrG;AAYD;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,MAAM,EACb,mBAAmB,CAAC,EAAE,MAAM,EAAE,GAC7B,MAAM,GAAG,IAAI,CAYf;AAID,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAwChF;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,OAAO,GAAG,SAAS,CA4BxF;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,OAAO,GAAG,SAAS,CAyBnG;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAmB3G;AAID,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,SAAS,CA0BxE;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,EAAE,GAAG,EAAE,OAAO,GAAG,SAAS,CA+BtG;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,SAAS,CAiC3E;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,EAAE,GAAG,EAAE,OAAO,GAAG,SAAS,CA8BvG;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,mBAAmB,EAAE,GAAG,EAAE,OAAO,GAAG,SAAS,CA2B3G;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,OAAO,GAAG,SAAS,CA8BhG;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,EAAE,GAAG,EAAE,OAAO,GAAG,SAAS,CA8BxG;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,SAAS,CAyFrE"}
@@ -27,19 +27,41 @@ const node_fs_1 = __importDefault(require("node:fs"));
27
27
  const node_path_1 = __importDefault(require("node:path"));
28
28
  const escape_string_regexp_1 = __importDefault(require("escape-string-regexp"));
29
29
  const core_js_1 = require("./core.js");
30
+ const types_js_1 = require("./types.js");
30
31
  // ─── Internal helpers ────────────────────────────────────────────────────────
32
+ /**
33
+ * Parse a markdown table row into cells, handling escaped pipes (`\|`) within cell content.
34
+ * Strips leading/trailing pipe characters and trims each cell.
35
+ */
36
+ function parseTableRow(row) {
37
+ // Replace escaped pipes with a placeholder, split, then restore
38
+ const placeholder = '\x00PIPE\x00';
39
+ const safe = row.replace(/\\\|/g, placeholder);
40
+ return safe.split('|').map(c => c.replaceAll(placeholder, '|').trim()).filter(Boolean);
41
+ }
31
42
  function stateExtractField(content, fieldName) {
32
- const pattern = new RegExp(`\\*\\*${fieldName}:\\*\\*\\s*(.+)`, 'i');
33
- const match = content.match(pattern);
34
- return match ? match[1].trim() : null;
43
+ const escaped = (0, escape_string_regexp_1.default)(fieldName);
44
+ // Match **fieldName:** with optional extra whitespace around the name and colon
45
+ const boldPattern = new RegExp(`\\*\\*\\s*${escaped}\\s*:\\s*\\*\\*\\s*(.+)`, 'i');
46
+ const boldMatch = content.match(boldPattern);
47
+ if (boldMatch)
48
+ return boldMatch[1].trim();
49
+ // Fallback: match plain "fieldName: value" (no bold markers)
50
+ const plainPattern = new RegExp(`^\\s*${escaped}\\s*:\\s*(.+)`, 'im');
51
+ const plainMatch = content.match(plainPattern);
52
+ return plainMatch ? plainMatch[1].trim() : null;
35
53
  }
36
54
  function stateReplaceField(content, fieldName, newValue) {
37
55
  const escaped = (0, escape_string_regexp_1.default)(fieldName);
38
- const pattern = new RegExp(`(\\*\\*${escaped}:\\*\\*\\s*)(.*)`, 'i');
39
- if (pattern.test(content)) {
40
- return content.replace(pattern, (_match, prefix) => `${prefix}${newValue}`);
41
- }
42
- return null;
56
+ // Match **fieldName:** with optional extra whitespace
57
+ const boldPattern = new RegExp(`(\\*\\*\\s*${escaped}\\s*:\\s*\\*\\*\\s*)(.*)`, 'i');
58
+ let replaced = content.replace(boldPattern, (_match, prefix) => `${prefix}${newValue}`);
59
+ if (replaced !== content)
60
+ return replaced;
61
+ // Fallback: plain "fieldName: value"
62
+ const plainPattern = new RegExp(`(^[ \\t]*${escaped}\\s*:\\s*)(.*)`, 'im');
63
+ replaced = content.replace(plainPattern, (_match, prefix) => `${prefix}${newValue}`);
64
+ return replaced !== content ? replaced : null;
43
65
  }
44
66
  function readTextArgOrFile(cwd, value, filePath, label) {
45
67
  if (!filePath)
@@ -69,18 +91,14 @@ function appendToStateSection(content, sectionPattern, entry, placeholderPattern
69
91
  return content.replace(sectionPattern, (_m, header) => `${header}${sectionBody}`);
70
92
  }
71
93
  // ─── State commands ──────────────────────────────────────────────────────────
72
- function cmdStateLoad(cwd, raw) {
94
+ async function cmdStateLoad(cwd, raw) {
73
95
  const config = (0, core_js_1.loadConfig)(cwd);
74
- let stateRaw = '';
75
- try {
76
- stateRaw = node_fs_1.default.readFileSync((0, core_js_1.statePath)(cwd), 'utf-8');
77
- }
78
- catch (e) {
79
- /* optional op, ignore */
80
- (0, core_js_1.debugLog)(e);
81
- }
82
- const configExists = node_fs_1.default.existsSync((0, core_js_1.configPath)(cwd));
83
- const roadmapExists = node_fs_1.default.existsSync((0, core_js_1.roadmapPath)(cwd));
96
+ const [stateContent, configExists, roadmapExists] = await Promise.all([
97
+ (0, core_js_1.safeReadFileAsync)((0, core_js_1.statePath)(cwd)),
98
+ node_fs_1.default.promises.access((0, core_js_1.configPath)(cwd)).then(() => true, () => false),
99
+ node_fs_1.default.promises.access((0, core_js_1.roadmapPath)(cwd)).then(() => true, () => false),
100
+ ]);
101
+ const stateRaw = stateContent ?? '';
84
102
  const stateExists = stateRaw.length > 0;
85
103
  const result = {
86
104
  config,
@@ -105,38 +123,34 @@ function cmdStateLoad(cwd, raw) {
105
123
  `roadmap_exists=${roadmapExists}`,
106
124
  `state_exists=${stateExists}`,
107
125
  ];
108
- (0, core_js_1.output)(result, true, lines.join('\n'));
126
+ return (0, types_js_1.cmdOk)(result, lines.join('\n'));
109
127
  }
110
- (0, core_js_1.output)(result);
128
+ return (0, types_js_1.cmdOk)(result);
111
129
  }
112
130
  function cmdStateGet(cwd, section, raw) {
113
131
  const statePath = (0, core_js_1.statePath)(cwd);
114
132
  try {
115
133
  const content = node_fs_1.default.readFileSync(statePath, 'utf-8');
116
134
  if (!section) {
117
- (0, core_js_1.output)({ content }, raw, content);
118
- return;
135
+ return (0, types_js_1.cmdOk)({ content }, raw ? content : undefined);
119
136
  }
120
- const fieldEscaped = (0, escape_string_regexp_1.default)(section);
121
- // Check for **field:** value
122
- const fieldPattern = new RegExp(`\\*\\*${fieldEscaped}:\\*\\*\\s*(.*)`, 'i');
123
- const fieldMatch = content.match(fieldPattern);
124
- if (fieldMatch) {
125
- (0, core_js_1.output)({ [section]: fieldMatch[1].trim() }, raw, fieldMatch[1].trim());
126
- return;
137
+ // Check for **field:** value (reuse stateExtractField for format tolerance)
138
+ const fieldValue = stateExtractField(content, section);
139
+ if (fieldValue !== null) {
140
+ return (0, types_js_1.cmdOk)({ [section]: fieldValue }, raw ? fieldValue : undefined);
127
141
  }
128
- // Check for ## Section
129
- const sectionPattern = new RegExp(`##\\s*${fieldEscaped}\\s*\n([\\s\\S]*?)(?=\\n##|$)`, 'i');
142
+ // Check for ## or ### Section, tolerating extra blank lines after header
143
+ const fieldEscaped = (0, escape_string_regexp_1.default)(section);
144
+ const sectionPattern = new RegExp(`#{2,3}\\s*${fieldEscaped}\\s*\\n\\s*\\n?([\\s\\S]*?)(?=\\n#{2,3}\\s|$)`, 'i');
130
145
  const sectionMatch = content.match(sectionPattern);
131
146
  if (sectionMatch) {
132
- (0, core_js_1.output)({ [section]: sectionMatch[1].trim() }, raw, sectionMatch[1].trim());
133
- return;
147
+ return (0, types_js_1.cmdOk)({ [section]: sectionMatch[1].trim() }, raw ? sectionMatch[1].trim() : undefined);
134
148
  }
135
- (0, core_js_1.output)({ error: `Section or field "${section}" not found` }, raw, '');
149
+ return (0, types_js_1.cmdOk)({ error: `Section or field "${section}" not found` }, raw ? '' : undefined);
136
150
  }
137
151
  catch (e) {
138
152
  (0, core_js_1.rethrowCliSignals)(e);
139
- (0, core_js_1.error)('STATE.md not found');
153
+ return (0, types_js_1.cmdErr)('STATE.md not found');
140
154
  }
141
155
  }
142
156
  function cmdStatePatch(cwd, patches, raw) {
@@ -145,10 +159,9 @@ function cmdStatePatch(cwd, patches, raw) {
145
159
  let content = node_fs_1.default.readFileSync(statePath, 'utf-8');
146
160
  const results = { updated: [], failed: [] };
147
161
  for (const [field, value] of Object.entries(patches)) {
148
- const fieldEscaped = (0, escape_string_regexp_1.default)(field);
149
- const pattern = new RegExp(`(\\*\\*${fieldEscaped}:\\*\\*\\s*)(.*)`, 'i');
150
- if (pattern.test(content)) {
151
- content = content.replace(pattern, (_match, prefix) => `${prefix}${value}`);
162
+ const result = stateReplaceField(content, field, value);
163
+ if (result) {
164
+ content = result;
152
165
  results.updated.push(field);
153
166
  }
154
167
  else {
@@ -158,56 +171,52 @@ function cmdStatePatch(cwd, patches, raw) {
158
171
  if (results.updated.length > 0) {
159
172
  node_fs_1.default.writeFileSync(statePath, content, 'utf-8');
160
173
  }
161
- (0, core_js_1.output)(results, raw, results.updated.length > 0 ? 'true' : 'false');
174
+ return (0, types_js_1.cmdOk)(results, raw ? (results.updated.length > 0 ? 'true' : 'false') : undefined);
162
175
  }
163
176
  catch (e) {
164
177
  (0, core_js_1.rethrowCliSignals)(e);
165
- (0, core_js_1.error)('STATE.md not found');
178
+ return (0, types_js_1.cmdErr)('STATE.md not found');
166
179
  }
167
180
  }
168
181
  function cmdStateUpdate(cwd, field, value) {
169
182
  if (!field || value === undefined) {
170
- (0, core_js_1.error)('field and value required for state update');
183
+ return (0, types_js_1.cmdErr)('field and value required for state update');
171
184
  }
172
185
  const statePath = (0, core_js_1.statePath)(cwd);
173
186
  try {
174
- let content = node_fs_1.default.readFileSync(statePath, 'utf-8');
175
- const fieldEscaped = (0, escape_string_regexp_1.default)(field);
176
- const pattern = new RegExp(`(\\*\\*${fieldEscaped}:\\*\\*\\s*)(.*)`, 'i');
177
- if (pattern.test(content)) {
178
- content = content.replace(pattern, (_match, prefix) => `${prefix}${value}`);
179
- node_fs_1.default.writeFileSync(statePath, content, 'utf-8');
180
- (0, core_js_1.output)({ updated: true });
187
+ const content = node_fs_1.default.readFileSync(statePath, 'utf-8');
188
+ const result = stateReplaceField(content, field, value);
189
+ if (result) {
190
+ node_fs_1.default.writeFileSync(statePath, result, 'utf-8');
191
+ return (0, types_js_1.cmdOk)({ updated: true });
181
192
  }
182
193
  else {
183
- (0, core_js_1.output)({ updated: false, reason: `Field "${field}" not found in STATE.md` });
194
+ return (0, types_js_1.cmdOk)({ updated: false, reason: `Field "${field}" not found in STATE.md` });
184
195
  }
185
196
  }
186
197
  catch (e) {
187
198
  (0, core_js_1.rethrowCliSignals)(e);
188
- (0, core_js_1.output)({ updated: false, reason: 'STATE.md not found' });
199
+ return (0, types_js_1.cmdOk)({ updated: false, reason: 'STATE.md not found' });
189
200
  }
190
201
  }
191
202
  // ─── State Progression Engine ────────────────────────────────────────────────
192
203
  function cmdStateAdvancePlan(cwd, raw) {
193
204
  const statePath = (0, core_js_1.statePath)(cwd);
194
205
  if (!node_fs_1.default.existsSync(statePath)) {
195
- (0, core_js_1.output)({ error: 'STATE.md not found' }, raw);
196
- return;
206
+ return (0, types_js_1.cmdOk)({ error: 'STATE.md not found' });
197
207
  }
198
208
  let content = node_fs_1.default.readFileSync(statePath, 'utf-8');
199
209
  const currentPlan = parseInt(stateExtractField(content, 'Current Plan') ?? '', 10);
200
210
  const totalPlans = parseInt(stateExtractField(content, 'Total Plans in Phase') ?? '', 10);
201
211
  const today = (0, core_js_1.todayISO)();
202
212
  if (isNaN(currentPlan) || isNaN(totalPlans)) {
203
- (0, core_js_1.output)({ error: 'Cannot parse Current Plan or Total Plans in Phase from STATE.md' }, raw);
204
- return;
213
+ return (0, types_js_1.cmdOk)({ error: 'Cannot parse Current Plan or Total Plans in Phase from STATE.md' });
205
214
  }
206
215
  if (currentPlan >= totalPlans) {
207
216
  content = stateReplaceField(content, 'Status', 'Phase complete — ready for verification') || content;
208
217
  content = stateReplaceField(content, 'Last Activity', today) || content;
209
218
  node_fs_1.default.writeFileSync(statePath, content, 'utf-8');
210
- (0, core_js_1.output)({ advanced: false, reason: 'last_plan', current_plan: currentPlan, total_plans: totalPlans, status: 'ready_for_verification' }, raw, 'false');
219
+ return (0, types_js_1.cmdOk)({ advanced: false, reason: 'last_plan', current_plan: currentPlan, total_plans: totalPlans, status: 'ready_for_verification' }, raw ? 'false' : undefined);
211
220
  }
212
221
  else {
213
222
  const newPlan = currentPlan + 1;
@@ -215,22 +224,21 @@ function cmdStateAdvancePlan(cwd, raw) {
215
224
  content = stateReplaceField(content, 'Status', 'Ready to execute') || content;
216
225
  content = stateReplaceField(content, 'Last Activity', today) || content;
217
226
  node_fs_1.default.writeFileSync(statePath, content, 'utf-8');
218
- (0, core_js_1.output)({ advanced: true, previous_plan: currentPlan, current_plan: newPlan, total_plans: totalPlans }, raw, 'true');
227
+ return (0, types_js_1.cmdOk)({ advanced: true, previous_plan: currentPlan, current_plan: newPlan, total_plans: totalPlans }, raw ? 'true' : undefined);
219
228
  }
220
229
  }
221
230
  function cmdStateRecordMetric(cwd, options, raw) {
222
231
  const statePath = (0, core_js_1.statePath)(cwd);
223
232
  if (!node_fs_1.default.existsSync(statePath)) {
224
- (0, core_js_1.output)({ error: 'STATE.md not found' }, raw);
225
- return;
233
+ return (0, types_js_1.cmdOk)({ error: 'STATE.md not found' });
226
234
  }
227
235
  let content = node_fs_1.default.readFileSync(statePath, 'utf-8');
228
236
  const { phase, plan, duration, tasks, files } = options;
229
237
  if (!phase || !plan || !duration) {
230
- (0, core_js_1.output)({ error: 'phase, plan, and duration required' }, raw);
231
- return;
238
+ return (0, types_js_1.cmdOk)({ error: 'phase, plan, and duration required' });
232
239
  }
233
- const metricsPattern = /(##\s*Performance Metrics[\s\S]*?\n\|[^\n]+\n\|[-|\s]+\n)([\s\S]*?)(?=\n##|\n$|$)/i;
240
+ // Flexible: tolerate varying heading levels (##/###), flexible separator lines (|---|, | --- |, |:---|)
241
+ const metricsPattern = /(#{2,3}\s*Performance Metrics[\s\S]*?\n\|[^\n]+\n\|[\s:|\-]+\n)([\s\S]*?)(?=\n#{2,3}\s|\n$|$)/i;
234
242
  const metricsMatch = content.match(metricsPattern);
235
243
  if (metricsMatch) {
236
244
  let tableBody = metricsMatch[2].trimEnd();
@@ -243,17 +251,16 @@ function cmdStateRecordMetric(cwd, options, raw) {
243
251
  }
244
252
  content = content.replace(metricsPattern, (_match, header) => `${header}${tableBody}\n`);
245
253
  node_fs_1.default.writeFileSync(statePath, content, 'utf-8');
246
- (0, core_js_1.output)({ recorded: true, phase, plan, duration }, raw, 'true');
254
+ return (0, types_js_1.cmdOk)({ recorded: true, phase, plan, duration }, raw ? 'true' : undefined);
247
255
  }
248
256
  else {
249
- (0, core_js_1.output)({ recorded: false, reason: 'Performance Metrics section not found in STATE.md' }, raw, 'false');
257
+ return (0, types_js_1.cmdOk)({ recorded: false, reason: 'Performance Metrics section not found in STATE.md' }, raw ? 'false' : undefined);
250
258
  }
251
259
  }
252
260
  function cmdStateUpdateProgress(cwd, raw) {
253
261
  const statePath = (0, core_js_1.statePath)(cwd);
254
262
  if (!node_fs_1.default.existsSync(statePath)) {
255
- (0, core_js_1.output)({ error: 'STATE.md not found' }, raw);
256
- return;
263
+ return (0, types_js_1.cmdOk)({ error: 'STATE.md not found' });
257
264
  }
258
265
  let content = node_fs_1.default.readFileSync(statePath, 'utf-8');
259
266
  const phasesDir = (0, core_js_1.phasesPath)(cwd);
@@ -273,21 +280,19 @@ function cmdStateUpdateProgress(cwd, raw) {
273
280
  const filled = Math.round(percent / 100 * barWidth);
274
281
  const bar = '\u2588'.repeat(filled) + '\u2591'.repeat(barWidth - filled);
275
282
  const progressStr = `[${bar}] ${percent}%`;
276
- const progressPattern = /(\*\*Progress:\*\*\s*).*/i;
277
- if (progressPattern.test(content)) {
278
- content = content.replace(progressPattern, (_match, prefix) => `${prefix}${progressStr}`);
279
- node_fs_1.default.writeFileSync(statePath, content, 'utf-8');
280
- (0, core_js_1.output)({ updated: true, percent, completed: totalSummaries, total: totalPlans, bar: progressStr }, raw, progressStr);
283
+ const result = stateReplaceField(content, 'Progress', progressStr);
284
+ if (result) {
285
+ node_fs_1.default.writeFileSync(statePath, result, 'utf-8');
286
+ return (0, types_js_1.cmdOk)({ updated: true, percent, completed: totalSummaries, total: totalPlans, bar: progressStr }, raw ? progressStr : undefined);
281
287
  }
282
288
  else {
283
- (0, core_js_1.output)({ updated: false, reason: 'Progress field not found in STATE.md' }, raw, 'false');
289
+ return (0, types_js_1.cmdOk)({ updated: false, reason: 'Progress field not found in STATE.md' }, raw ? 'false' : undefined);
284
290
  }
285
291
  }
286
292
  function cmdStateAddDecision(cwd, options, raw) {
287
293
  const statePath = (0, core_js_1.statePath)(cwd);
288
294
  if (!node_fs_1.default.existsSync(statePath)) {
289
- (0, core_js_1.output)({ error: 'STATE.md not found' }, raw);
290
- return;
295
+ return (0, types_js_1.cmdOk)({ error: 'STATE.md not found' });
291
296
  }
292
297
  const { phase, summary, summary_file, rationale, rationale_file } = options;
293
298
  let summaryText;
@@ -298,12 +303,10 @@ function cmdStateAddDecision(cwd, options, raw) {
298
303
  }
299
304
  catch (thrown) {
300
305
  const e = thrown;
301
- (0, core_js_1.output)({ added: false, reason: e.message }, raw, 'false');
302
- return;
306
+ return (0, types_js_1.cmdOk)({ added: false, reason: e.message }, raw ? 'false' : undefined);
303
307
  }
304
308
  if (!summaryText) {
305
- (0, core_js_1.output)({ error: 'summary required' }, raw);
306
- return;
309
+ return (0, types_js_1.cmdOk)({ error: 'summary required' });
307
310
  }
308
311
  const content = node_fs_1.default.readFileSync(statePath, 'utf-8');
309
312
  const entry = `- [Phase ${phase || '?'}]: ${summaryText}${rationaleText ? ` — ${rationaleText}` : ''}`;
@@ -311,17 +314,16 @@ function cmdStateAddDecision(cwd, options, raw) {
311
314
  const updated = appendToStateSection(content, sectionPattern, entry, [/None yet\.?\s*\n?/gi, /No decisions yet\.?\s*\n?/gi]);
312
315
  if (updated) {
313
316
  node_fs_1.default.writeFileSync(statePath, updated, 'utf-8');
314
- (0, core_js_1.output)({ added: true, decision: entry }, raw, 'true');
317
+ return (0, types_js_1.cmdOk)({ added: true, decision: entry }, raw ? 'true' : undefined);
315
318
  }
316
319
  else {
317
- (0, core_js_1.output)({ added: false, reason: 'Decisions section not found in STATE.md' }, raw, 'false');
320
+ return (0, types_js_1.cmdOk)({ added: false, reason: 'Decisions section not found in STATE.md' }, raw ? 'false' : undefined);
318
321
  }
319
322
  }
320
323
  function cmdStateAddBlocker(cwd, text, raw) {
321
324
  const statePath = (0, core_js_1.statePath)(cwd);
322
325
  if (!node_fs_1.default.existsSync(statePath)) {
323
- (0, core_js_1.output)({ error: 'STATE.md not found' }, raw);
324
- return;
326
+ return (0, types_js_1.cmdOk)({ error: 'STATE.md not found' });
325
327
  }
326
328
  const blockerOptions = typeof text === 'object' && text !== null ? text : { text: text };
327
329
  let blockerText;
@@ -330,12 +332,10 @@ function cmdStateAddBlocker(cwd, text, raw) {
330
332
  }
331
333
  catch (thrown) {
332
334
  const e = thrown;
333
- (0, core_js_1.output)({ added: false, reason: e.message }, raw, 'false');
334
- return;
335
+ return (0, types_js_1.cmdOk)({ added: false, reason: e.message }, raw ? 'false' : undefined);
335
336
  }
336
337
  if (!blockerText) {
337
- (0, core_js_1.output)({ error: 'text required' }, raw);
338
- return;
338
+ return (0, types_js_1.cmdOk)({ error: 'text required' });
339
339
  }
340
340
  const content = node_fs_1.default.readFileSync(statePath, 'utf-8');
341
341
  const entry = `- ${blockerText}`;
@@ -343,50 +343,48 @@ function cmdStateAddBlocker(cwd, text, raw) {
343
343
  const updated = appendToStateSection(content, sectionPattern, entry, [/None\.?\s*\n?/gi, /None yet\.?\s*\n?/gi]);
344
344
  if (updated) {
345
345
  node_fs_1.default.writeFileSync(statePath, updated, 'utf-8');
346
- (0, core_js_1.output)({ added: true, blocker: blockerText }, raw, 'true');
346
+ return (0, types_js_1.cmdOk)({ added: true, blocker: blockerText }, raw ? 'true' : undefined);
347
347
  }
348
348
  else {
349
- (0, core_js_1.output)({ added: false, reason: 'Blockers section not found in STATE.md' }, raw, 'false');
349
+ return (0, types_js_1.cmdOk)({ added: false, reason: 'Blockers section not found in STATE.md' }, raw ? 'false' : undefined);
350
350
  }
351
351
  }
352
352
  function cmdStateResolveBlocker(cwd, text, raw) {
353
353
  const statePath = (0, core_js_1.statePath)(cwd);
354
354
  if (!node_fs_1.default.existsSync(statePath)) {
355
- (0, core_js_1.output)({ error: 'STATE.md not found' }, raw);
356
- return;
355
+ return (0, types_js_1.cmdOk)({ error: 'STATE.md not found' });
357
356
  }
358
357
  if (!text) {
359
- (0, core_js_1.output)({ error: 'text required' }, raw);
360
- return;
358
+ return (0, types_js_1.cmdOk)({ error: 'text required' });
361
359
  }
362
360
  let content = node_fs_1.default.readFileSync(statePath, 'utf-8');
363
- const sectionPattern = /(###?\s*(?:Blockers|Blockers\/Concerns|Concerns)\s*\n)([\s\S]*?)(?=\n###?|\n##[^#]|$)/i;
361
+ const sectionPattern = /(#{2,3}\s*(?:Blockers|Blockers\/Concerns|Concerns)\s*\n\s*\n?)([\s\S]*?)(?=\n#{2,3}\s|$)/i;
364
362
  const match = content.match(sectionPattern);
365
363
  if (match) {
366
364
  const sectionBody = match[2];
367
365
  const lines = sectionBody.split('\n');
368
366
  const filtered = lines.filter(line => {
369
- if (!line.startsWith('- '))
367
+ // Match - or * bullets, optionally indented
368
+ if (!/^\s*[-*]\s+/.test(line))
370
369
  return true;
371
370
  return !line.toLowerCase().includes(text.toLowerCase());
372
371
  });
373
372
  let newBody = filtered.join('\n');
374
- if (!newBody.trim() || !newBody.includes('- ')) {
373
+ if (!newBody.trim() || !/^\s*[-*]\s+/m.test(newBody)) {
375
374
  newBody = 'None\n';
376
375
  }
377
376
  content = content.replace(sectionPattern, (_match, header) => `${header}${newBody}`);
378
377
  node_fs_1.default.writeFileSync(statePath, content, 'utf-8');
379
- (0, core_js_1.output)({ resolved: true, blocker: text }, raw, 'true');
378
+ return (0, types_js_1.cmdOk)({ resolved: true, blocker: text }, raw ? 'true' : undefined);
380
379
  }
381
380
  else {
382
- (0, core_js_1.output)({ resolved: false, reason: 'Blockers section not found in STATE.md' }, raw, 'false');
381
+ return (0, types_js_1.cmdOk)({ resolved: false, reason: 'Blockers section not found in STATE.md' }, raw ? 'false' : undefined);
383
382
  }
384
383
  }
385
384
  function cmdStateRecordSession(cwd, options, raw) {
386
385
  const statePath = (0, core_js_1.statePath)(cwd);
387
386
  if (!node_fs_1.default.existsSync(statePath)) {
388
- (0, core_js_1.output)({ error: 'STATE.md not found' }, raw);
389
- return;
387
+ return (0, types_js_1.cmdOk)({ error: 'STATE.md not found' });
390
388
  }
391
389
  let content = node_fs_1.default.readFileSync(statePath, 'utf-8');
392
390
  const now = new Date().toISOString();
@@ -420,24 +418,19 @@ function cmdStateRecordSession(cwd, options, raw) {
420
418
  }
421
419
  if (updated.length > 0) {
422
420
  node_fs_1.default.writeFileSync(statePath, content, 'utf-8');
423
- (0, core_js_1.output)({ recorded: true, updated }, raw, 'true');
421
+ return (0, types_js_1.cmdOk)({ recorded: true, updated }, raw ? 'true' : undefined);
424
422
  }
425
423
  else {
426
- (0, core_js_1.output)({ recorded: false, reason: 'No session fields found in STATE.md' }, raw, 'false');
424
+ return (0, types_js_1.cmdOk)({ recorded: false, reason: 'No session fields found in STATE.md' }, raw ? 'false' : undefined);
427
425
  }
428
426
  }
429
427
  function cmdStateSnapshot(cwd, raw) {
430
428
  const statePath = (0, core_js_1.statePath)(cwd);
431
429
  if (!node_fs_1.default.existsSync(statePath)) {
432
- (0, core_js_1.output)({ error: 'STATE.md not found' }, raw);
433
- return;
430
+ return (0, types_js_1.cmdOk)({ error: 'STATE.md not found' });
434
431
  }
435
432
  const content = node_fs_1.default.readFileSync(statePath, 'utf-8');
436
- const extractField = (fieldName) => {
437
- const pattern = new RegExp(`\\*\\*${fieldName}:\\*\\*\\s*(.+)`, 'i');
438
- const match = content.match(pattern);
439
- return match ? match[1].trim() : null;
440
- };
433
+ const extractField = (fieldName) => stateExtractField(content, fieldName);
441
434
  const currentPhase = extractField('Current Phase');
442
435
  const currentPhaseName = extractField('Current Phase Name');
443
436
  const totalPhasesRaw = extractField('Total Phases');
@@ -452,12 +445,16 @@ function cmdStateSnapshot(cwd, raw) {
452
445
  const totalPlansInPhase = totalPlansRaw ? parseInt(totalPlansRaw, 10) : null;
453
446
  const progressPercent = progressRaw ? parseInt(progressRaw.replace('%', ''), 10) : null;
454
447
  const decisions = [];
455
- const decisionsMatch = content.match(/##\s*Decisions Made[\s\S]*?\n\|[^\n]+\n\|[-|\s]+\n([\s\S]*?)(?=\n##|\n$|$)/i);
448
+ // Tolerate ##/### heading levels and flexible separator lines (|---|, | --- |, |:---|)
449
+ const decisionsMatch = content.match(/#{2,3}\s*Decisions Made[\s\S]*?\n\|[^\n]+\n\|[\s:|\-]+\n([\s\S]*?)(?=\n#{2,3}\s|\n$|$)/i);
456
450
  if (decisionsMatch) {
457
451
  const tableBody = decisionsMatch[1];
458
- const rows = tableBody.trim().split('\n').filter(r => r.includes('|'));
452
+ const rows = tableBody.trim().split('\n').filter(r => r.includes('|') && !r.match(/^\s*$/));
459
453
  for (const row of rows) {
460
- const cells = row.split('|').map(c => c.trim()).filter(Boolean);
454
+ // Skip separator lines that snuck through
455
+ if (/^\s*\|[\s:\-|]+\|\s*$/.test(row))
456
+ continue;
457
+ const cells = parseTableRow(row);
461
458
  if (cells.length >= 3) {
462
459
  decisions.push({
463
460
  phase: cells[0],
@@ -468,12 +465,14 @@ function cmdStateSnapshot(cwd, raw) {
468
465
  }
469
466
  }
470
467
  const blockers = [];
471
- const blockersMatch = content.match(/##\s*Blockers\s*\n([\s\S]*?)(?=\n##|$)/i);
468
+ // Tolerate ##/### heading levels
469
+ const blockersMatch = content.match(/#{2,3}\s*Blockers\s*\n([\s\S]*?)(?=\n#{2,3}\s|$)/i);
472
470
  if (blockersMatch) {
473
471
  const blockersSection = blockersMatch[1];
474
- const items = blockersSection.match(/^-\s+(.+)$/gm) || [];
472
+ // Match - or * bullets, optionally indented
473
+ const items = blockersSection.match(/^\s*[-*]\s+(.+)$/gm) || [];
475
474
  for (const item of items) {
476
- blockers.push(item.replace(/^-\s+/, '').trim());
475
+ blockers.push(item.replace(/^\s*[-*]\s+/, '').trim());
477
476
  }
478
477
  }
479
478
  const session = {
@@ -481,18 +480,12 @@ function cmdStateSnapshot(cwd, raw) {
481
480
  stopped_at: null,
482
481
  resume_file: null,
483
482
  };
484
- const sessionMatch = content.match(/##\s*Session\s*\n([\s\S]*?)(?=\n##|$)/i);
483
+ const sessionMatch = content.match(/#{2,3}\s*Session\s*\n\s*\n?([\s\S]*?)(?=\n#{2,3}\s|$)/i);
485
484
  if (sessionMatch) {
486
485
  const sessionSection = sessionMatch[1];
487
- const lastDateMatch = sessionSection.match(/\*\*Last Date:\*\*\s*(.+)/i);
488
- const stoppedAtMatch = sessionSection.match(/\*\*Stopped At:\*\*\s*(.+)/i);
489
- const resumeFileMatch = sessionSection.match(/\*\*Resume File:\*\*\s*(.+)/i);
490
- if (lastDateMatch)
491
- session.last_date = lastDateMatch[1].trim();
492
- if (stoppedAtMatch)
493
- session.stopped_at = stoppedAtMatch[1].trim();
494
- if (resumeFileMatch)
495
- session.resume_file = resumeFileMatch[1].trim();
486
+ session.last_date = stateExtractField(sessionSection, 'Last Date');
487
+ session.stopped_at = stateExtractField(sessionSection, 'Stopped At') || stateExtractField(sessionSection, 'Stopped at');
488
+ session.resume_file = stateExtractField(sessionSection, 'Resume File') || stateExtractField(sessionSection, 'Resume file');
496
489
  }
497
490
  const snapshot = {
498
491
  current_phase: currentPhase,
@@ -509,6 +502,6 @@ function cmdStateSnapshot(cwd, raw) {
509
502
  paused_at: pausedAt,
510
503
  session,
511
504
  };
512
- (0, core_js_1.output)(snapshot, raw);
505
+ return (0, types_js_1.cmdOk)(snapshot);
513
506
  }
514
507
  //# sourceMappingURL=state.js.map