mstro-app 0.4.2 → 0.4.4
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.
- package/bin/mstro.js +119 -40
- package/dist/server/cli/headless/claude-invoker-process.d.ts +11 -0
- package/dist/server/cli/headless/claude-invoker-process.d.ts.map +1 -0
- package/dist/server/cli/headless/claude-invoker-process.js +140 -0
- package/dist/server/cli/headless/claude-invoker-process.js.map +1 -0
- package/dist/server/cli/headless/claude-invoker-stall.d.ts +40 -0
- package/dist/server/cli/headless/claude-invoker-stall.d.ts.map +1 -0
- package/dist/server/cli/headless/claude-invoker-stall.js +98 -0
- package/dist/server/cli/headless/claude-invoker-stall.js.map +1 -0
- package/dist/server/cli/headless/claude-invoker-stream.d.ts +44 -0
- package/dist/server/cli/headless/claude-invoker-stream.d.ts.map +1 -0
- package/dist/server/cli/headless/claude-invoker-stream.js +276 -0
- package/dist/server/cli/headless/claude-invoker-stream.js.map +1 -0
- package/dist/server/cli/headless/claude-invoker-tools.d.ts +21 -0
- package/dist/server/cli/headless/claude-invoker-tools.d.ts.map +1 -0
- package/dist/server/cli/headless/claude-invoker-tools.js +137 -0
- package/dist/server/cli/headless/claude-invoker-tools.js.map +1 -0
- package/dist/server/cli/headless/claude-invoker.d.ts +6 -4
- package/dist/server/cli/headless/claude-invoker.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker.js +10 -804
- package/dist/server/cli/headless/claude-invoker.js.map +1 -1
- package/dist/server/cli/headless/haiku-assessments.d.ts +62 -0
- package/dist/server/cli/headless/haiku-assessments.d.ts.map +1 -0
- package/dist/server/cli/headless/haiku-assessments.js +281 -0
- package/dist/server/cli/headless/haiku-assessments.js.map +1 -0
- package/dist/server/cli/headless/headless-logger.d.ts +3 -2
- package/dist/server/cli/headless/headless-logger.d.ts.map +1 -1
- package/dist/server/cli/headless/headless-logger.js +28 -5
- package/dist/server/cli/headless/headless-logger.js.map +1 -1
- package/dist/server/cli/headless/native-timeout-detector.d.ts +44 -0
- package/dist/server/cli/headless/native-timeout-detector.d.ts.map +1 -0
- package/dist/server/cli/headless/native-timeout-detector.js +99 -0
- package/dist/server/cli/headless/native-timeout-detector.js.map +1 -0
- package/dist/server/cli/headless/stall-assessor.d.ts +2 -110
- package/dist/server/cli/headless/stall-assessor.d.ts.map +1 -1
- package/dist/server/cli/headless/stall-assessor.js +65 -457
- package/dist/server/cli/headless/stall-assessor.js.map +1 -1
- package/dist/server/cli/headless/types.d.ts +4 -1
- package/dist/server/cli/headless/types.d.ts.map +1 -1
- package/dist/server/cli/improvisation-attachments.d.ts +21 -0
- package/dist/server/cli/improvisation-attachments.d.ts.map +1 -0
- package/dist/server/cli/improvisation-attachments.js +116 -0
- package/dist/server/cli/improvisation-attachments.js.map +1 -0
- package/dist/server/cli/improvisation-retry.d.ts +52 -0
- package/dist/server/cli/improvisation-retry.d.ts.map +1 -0
- package/dist/server/cli/improvisation-retry.js +434 -0
- package/dist/server/cli/improvisation-retry.js.map +1 -0
- package/dist/server/cli/improvisation-session-manager.d.ts +10 -266
- package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -1
- package/dist/server/cli/improvisation-session-manager.js +117 -1079
- package/dist/server/cli/improvisation-session-manager.js.map +1 -1
- package/dist/server/cli/improvisation-types.d.ts +86 -0
- package/dist/server/cli/improvisation-types.d.ts.map +1 -0
- package/dist/server/cli/improvisation-types.js +10 -0
- package/dist/server/cli/improvisation-types.js.map +1 -0
- package/dist/server/cli/prompt-builders.d.ts +68 -0
- package/dist/server/cli/prompt-builders.d.ts.map +1 -0
- package/dist/server/cli/prompt-builders.js +312 -0
- package/dist/server/cli/prompt-builders.js.map +1 -0
- package/dist/server/index.js +33 -212
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/bouncer-haiku.d.ts +10 -0
- package/dist/server/mcp/bouncer-haiku.d.ts.map +1 -0
- package/dist/server/mcp/bouncer-haiku.js +152 -0
- package/dist/server/mcp/bouncer-haiku.js.map +1 -0
- package/dist/server/mcp/bouncer-integration.d.ts +3 -4
- package/dist/server/mcp/bouncer-integration.d.ts.map +1 -1
- package/dist/server/mcp/bouncer-integration.js +50 -196
- package/dist/server/mcp/bouncer-integration.js.map +1 -1
- package/dist/server/mcp/security-analysis.d.ts +38 -0
- package/dist/server/mcp/security-analysis.d.ts.map +1 -0
- package/dist/server/mcp/security-analysis.js +183 -0
- package/dist/server/mcp/security-analysis.js.map +1 -0
- package/dist/server/mcp/security-audit.d.ts +1 -1
- package/dist/server/mcp/security-audit.d.ts.map +1 -1
- package/dist/server/mcp/security-patterns.d.ts +1 -25
- package/dist/server/mcp/security-patterns.d.ts.map +1 -1
- package/dist/server/mcp/security-patterns.js +55 -260
- package/dist/server/mcp/security-patterns.js.map +1 -1
- package/dist/server/server-setup.d.ts +22 -0
- package/dist/server/server-setup.d.ts.map +1 -0
- package/dist/server/server-setup.js +101 -0
- package/dist/server/server-setup.js.map +1 -0
- package/dist/server/services/file-explorer-ops.d.ts +24 -0
- package/dist/server/services/file-explorer-ops.d.ts.map +1 -0
- package/dist/server/services/file-explorer-ops.js +211 -0
- package/dist/server/services/file-explorer-ops.js.map +1 -0
- package/dist/server/services/files.d.ts +2 -85
- package/dist/server/services/files.d.ts.map +1 -1
- package/dist/server/services/files.js +7 -427
- package/dist/server/services/files.js.map +1 -1
- package/dist/server/services/plan/composer.d.ts +1 -1
- package/dist/server/services/plan/composer.d.ts.map +1 -1
- package/dist/server/services/plan/composer.js +118 -32
- package/dist/server/services/plan/composer.js.map +1 -1
- package/dist/server/services/plan/config-installer.d.ts +25 -0
- package/dist/server/services/plan/config-installer.d.ts.map +1 -0
- package/dist/server/services/plan/config-installer.js +182 -0
- package/dist/server/services/plan/config-installer.js.map +1 -0
- package/dist/server/services/plan/dependency-resolver.d.ts +1 -1
- package/dist/server/services/plan/dependency-resolver.d.ts.map +1 -1
- package/dist/server/services/plan/dependency-resolver.js +4 -1
- package/dist/server/services/plan/dependency-resolver.js.map +1 -1
- package/dist/server/services/plan/executor.d.ts +38 -74
- package/dist/server/services/plan/executor.d.ts.map +1 -1
- package/dist/server/services/plan/executor.js +274 -460
- package/dist/server/services/plan/executor.js.map +1 -1
- package/dist/server/services/plan/front-matter.d.ts +18 -0
- package/dist/server/services/plan/front-matter.d.ts.map +1 -0
- package/dist/server/services/plan/front-matter.js +44 -0
- package/dist/server/services/plan/front-matter.js.map +1 -0
- package/dist/server/services/plan/output-manager.d.ts +22 -0
- package/dist/server/services/plan/output-manager.d.ts.map +1 -0
- package/dist/server/services/plan/output-manager.js +97 -0
- package/dist/server/services/plan/output-manager.js.map +1 -0
- package/dist/server/services/plan/parser-core.d.ts +20 -0
- package/dist/server/services/plan/parser-core.d.ts.map +1 -0
- package/dist/server/services/plan/parser-core.js +350 -0
- package/dist/server/services/plan/parser-core.js.map +1 -0
- package/dist/server/services/plan/parser-migration.d.ts +5 -0
- package/dist/server/services/plan/parser-migration.d.ts.map +1 -0
- package/dist/server/services/plan/parser-migration.js +124 -0
- package/dist/server/services/plan/parser-migration.js.map +1 -0
- package/dist/server/services/plan/parser.d.ts +11 -3
- package/dist/server/services/plan/parser.d.ts.map +1 -1
- package/dist/server/services/plan/parser.js +184 -369
- package/dist/server/services/plan/parser.js.map +1 -1
- package/dist/server/services/plan/prompt-builder.d.ts +17 -0
- package/dist/server/services/plan/prompt-builder.d.ts.map +1 -0
- package/dist/server/services/plan/prompt-builder.js +137 -0
- package/dist/server/services/plan/prompt-builder.js.map +1 -0
- package/dist/server/services/plan/review-gate.d.ts +28 -0
- package/dist/server/services/plan/review-gate.d.ts.map +1 -0
- package/dist/server/services/plan/review-gate.js +191 -0
- package/dist/server/services/plan/review-gate.js.map +1 -0
- package/dist/server/services/plan/state-reconciler.d.ts +1 -1
- package/dist/server/services/plan/state-reconciler.d.ts.map +1 -1
- package/dist/server/services/plan/state-reconciler.js +59 -7
- package/dist/server/services/plan/state-reconciler.js.map +1 -1
- package/dist/server/services/plan/types.d.ts +68 -0
- package/dist/server/services/plan/types.d.ts.map +1 -1
- package/dist/server/services/platform-credentials.d.ts +24 -0
- package/dist/server/services/platform-credentials.d.ts.map +1 -0
- package/dist/server/services/platform-credentials.js +68 -0
- package/dist/server/services/platform-credentials.js.map +1 -0
- package/dist/server/services/platform.d.ts +1 -31
- package/dist/server/services/platform.d.ts.map +1 -1
- package/dist/server/services/platform.js +11 -109
- package/dist/server/services/platform.js.map +1 -1
- package/dist/server/services/terminal/pty-manager.d.ts +7 -97
- package/dist/server/services/terminal/pty-manager.d.ts.map +1 -1
- package/dist/server/services/terminal/pty-manager.js +53 -266
- package/dist/server/services/terminal/pty-manager.js.map +1 -1
- package/dist/server/services/terminal/pty-utils.d.ts +57 -0
- package/dist/server/services/terminal/pty-utils.d.ts.map +1 -0
- package/dist/server/services/terminal/pty-utils.js +141 -0
- package/dist/server/services/terminal/pty-utils.js.map +1 -0
- package/dist/server/services/websocket/file-definition-handlers.d.ts +4 -0
- package/dist/server/services/websocket/file-definition-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/file-definition-handlers.js +153 -0
- package/dist/server/services/websocket/file-definition-handlers.js.map +1 -0
- package/dist/server/services/websocket/file-explorer-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/file-explorer-handlers.js +52 -391
- package/dist/server/services/websocket/file-explorer-handlers.js.map +1 -1
- package/dist/server/services/websocket/file-search-handlers.d.ts +5 -0
- package/dist/server/services/websocket/file-search-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/file-search-handlers.js +238 -0
- package/dist/server/services/websocket/file-search-handlers.js.map +1 -0
- package/dist/server/services/websocket/file-utils.js +3 -3
- package/dist/server/services/websocket/file-utils.js.map +1 -1
- package/dist/server/services/websocket/git-branch-handlers.d.ts +7 -0
- package/dist/server/services/websocket/git-branch-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/git-branch-handlers.js +110 -0
- package/dist/server/services/websocket/git-branch-handlers.js.map +1 -0
- package/dist/server/services/websocket/git-diff-handlers.d.ts +6 -0
- package/dist/server/services/websocket/git-diff-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/git-diff-handlers.js +123 -0
- package/dist/server/services/websocket/git-diff-handlers.js.map +1 -0
- package/dist/server/services/websocket/git-handlers.d.ts +2 -31
- package/dist/server/services/websocket/git-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-handlers.js +35 -541
- package/dist/server/services/websocket/git-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-log-handlers.d.ts +6 -0
- package/dist/server/services/websocket/git-log-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/git-log-handlers.js +128 -0
- package/dist/server/services/websocket/git-log-handlers.js.map +1 -0
- package/dist/server/services/websocket/git-pr-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-pr-handlers.js +13 -53
- package/dist/server/services/websocket/git-pr-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-tag-handlers.d.ts +6 -0
- package/dist/server/services/websocket/git-tag-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/git-tag-handlers.js +76 -0
- package/dist/server/services/websocket/git-tag-handlers.js.map +1 -0
- package/dist/server/services/websocket/git-utils.d.ts +43 -0
- package/dist/server/services/websocket/git-utils.d.ts.map +1 -0
- package/dist/server/services/websocket/git-utils.js +201 -0
- package/dist/server/services/websocket/git-utils.js.map +1 -0
- package/dist/server/services/websocket/handler.d.ts +2 -0
- package/dist/server/services/websocket/handler.d.ts.map +1 -1
- package/dist/server/services/websocket/handler.js +37 -112
- package/dist/server/services/websocket/handler.js.map +1 -1
- package/dist/server/services/websocket/plan-board-handlers.d.ts +11 -0
- package/dist/server/services/websocket/plan-board-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/plan-board-handlers.js +218 -0
- package/dist/server/services/websocket/plan-board-handlers.js.map +1 -0
- package/dist/server/services/websocket/plan-execution-handlers.d.ts +9 -0
- package/dist/server/services/websocket/plan-execution-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/plan-execution-handlers.js +142 -0
- package/dist/server/services/websocket/plan-execution-handlers.js.map +1 -0
- package/dist/server/services/websocket/plan-handlers.d.ts +7 -2
- package/dist/server/services/websocket/plan-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-handlers.js +21 -462
- package/dist/server/services/websocket/plan-handlers.js.map +1 -1
- package/dist/server/services/websocket/plan-helpers.d.ts +19 -0
- package/dist/server/services/websocket/plan-helpers.d.ts.map +1 -0
- package/dist/server/services/websocket/plan-helpers.js +199 -0
- package/dist/server/services/websocket/plan-helpers.js.map +1 -0
- package/dist/server/services/websocket/plan-issue-handlers.d.ts +12 -0
- package/dist/server/services/websocket/plan-issue-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/plan-issue-handlers.js +162 -0
- package/dist/server/services/websocket/plan-issue-handlers.js.map +1 -0
- package/dist/server/services/websocket/plan-sprint-handlers.d.ts +7 -0
- package/dist/server/services/websocket/plan-sprint-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/plan-sprint-handlers.js +206 -0
- package/dist/server/services/websocket/plan-sprint-handlers.js.map +1 -0
- package/dist/server/services/websocket/quality-complexity.d.ts +14 -0
- package/dist/server/services/websocket/quality-complexity.d.ts.map +1 -0
- package/dist/server/services/websocket/quality-complexity.js +262 -0
- package/dist/server/services/websocket/quality-complexity.js.map +1 -0
- package/dist/server/services/websocket/quality-fix-agent.d.ts +16 -0
- package/dist/server/services/websocket/quality-fix-agent.d.ts.map +1 -0
- package/dist/server/services/websocket/quality-fix-agent.js +140 -0
- package/dist/server/services/websocket/quality-fix-agent.js.map +1 -0
- package/dist/server/services/websocket/quality-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-handlers.js +34 -346
- package/dist/server/services/websocket/quality-handlers.js.map +1 -1
- package/dist/server/services/websocket/quality-linting.d.ts +9 -0
- package/dist/server/services/websocket/quality-linting.d.ts.map +1 -0
- package/dist/server/services/websocket/quality-linting.js +178 -0
- package/dist/server/services/websocket/quality-linting.js.map +1 -0
- package/dist/server/services/websocket/quality-review-agent.d.ts +19 -0
- package/dist/server/services/websocket/quality-review-agent.d.ts.map +1 -0
- package/dist/server/services/websocket/quality-review-agent.js +206 -0
- package/dist/server/services/websocket/quality-review-agent.js.map +1 -0
- package/dist/server/services/websocket/quality-service.d.ts +3 -51
- package/dist/server/services/websocket/quality-service.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-service.js +9 -651
- package/dist/server/services/websocket/quality-service.js.map +1 -1
- package/dist/server/services/websocket/quality-tools.d.ts +23 -0
- package/dist/server/services/websocket/quality-tools.d.ts.map +1 -0
- package/dist/server/services/websocket/quality-tools.js +208 -0
- package/dist/server/services/websocket/quality-tools.js.map +1 -0
- package/dist/server/services/websocket/quality-types.d.ts +59 -0
- package/dist/server/services/websocket/quality-types.d.ts.map +1 -0
- package/dist/server/services/websocket/quality-types.js +101 -0
- package/dist/server/services/websocket/quality-types.js.map +1 -0
- package/dist/server/services/websocket/session-handlers.d.ts +3 -4
- package/dist/server/services/websocket/session-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/session-handlers.js +3 -378
- package/dist/server/services/websocket/session-handlers.js.map +1 -1
- package/dist/server/services/websocket/session-history.d.ts +4 -0
- package/dist/server/services/websocket/session-history.d.ts.map +1 -0
- package/dist/server/services/websocket/session-history.js +208 -0
- package/dist/server/services/websocket/session-history.js.map +1 -0
- package/dist/server/services/websocket/session-initialization.d.ts +5 -0
- package/dist/server/services/websocket/session-initialization.d.ts.map +1 -0
- package/dist/server/services/websocket/session-initialization.js +163 -0
- package/dist/server/services/websocket/session-initialization.js.map +1 -0
- package/dist/server/services/websocket/types.d.ts +12 -2
- package/dist/server/services/websocket/types.d.ts.map +1 -1
- package/package.json +1 -2
- package/server/cli/headless/claude-invoker-process.ts +204 -0
- package/server/cli/headless/claude-invoker-stall.ts +164 -0
- package/server/cli/headless/claude-invoker-stream.ts +353 -0
- package/server/cli/headless/claude-invoker-tools.ts +187 -0
- package/server/cli/headless/claude-invoker.ts +15 -1092
- package/server/cli/headless/haiku-assessments.ts +365 -0
- package/server/cli/headless/headless-logger.ts +26 -5
- package/server/cli/headless/native-timeout-detector.ts +117 -0
- package/server/cli/headless/stall-assessor.ts +65 -618
- package/server/cli/headless/types.ts +4 -1
- package/server/cli/improvisation-attachments.ts +148 -0
- package/server/cli/improvisation-retry.ts +602 -0
- package/server/cli/improvisation-session-manager.ts +140 -1349
- package/server/cli/improvisation-types.ts +98 -0
- package/server/cli/prompt-builders.ts +370 -0
- package/server/index.ts +35 -246
- package/server/mcp/bouncer-haiku.ts +182 -0
- package/server/mcp/bouncer-integration.ts +87 -248
- package/server/mcp/security-analysis.ts +217 -0
- package/server/mcp/security-audit.ts +1 -1
- package/server/mcp/security-patterns.ts +60 -283
- package/server/server-setup.ts +114 -0
- package/server/services/file-explorer-ops.ts +293 -0
- package/server/services/files.ts +20 -532
- package/server/services/plan/composer.ts +140 -35
- package/server/services/plan/config-installer.ts +187 -0
- package/server/services/plan/dependency-resolver.ts +4 -1
- package/server/services/plan/executor.ts +281 -488
- package/server/services/plan/front-matter.ts +48 -0
- package/server/services/plan/output-manager.ts +113 -0
- package/server/services/plan/parser-core.ts +406 -0
- package/server/services/plan/parser-migration.ts +128 -0
- package/server/services/plan/parser.ts +188 -394
- package/server/services/plan/prompt-builder.ts +161 -0
- package/server/services/plan/review-gate.ts +212 -0
- package/server/services/plan/state-reconciler.ts +68 -7
- package/server/services/plan/types.ts +101 -1
- package/server/services/platform-credentials.ts +83 -0
- package/server/services/platform.ts +16 -131
- package/server/services/terminal/pty-manager.ts +66 -313
- package/server/services/terminal/pty-utils.ts +176 -0
- package/server/services/websocket/file-definition-handlers.ts +165 -0
- package/server/services/websocket/file-explorer-handlers.ts +37 -452
- package/server/services/websocket/file-search-handlers.ts +291 -0
- package/server/services/websocket/file-utils.ts +3 -3
- package/server/services/websocket/git-branch-handlers.ts +130 -0
- package/server/services/websocket/git-diff-handlers.ts +140 -0
- package/server/services/websocket/git-handlers.ts +40 -625
- package/server/services/websocket/git-log-handlers.ts +149 -0
- package/server/services/websocket/git-pr-handlers.ts +17 -62
- package/server/services/websocket/git-tag-handlers.ts +91 -0
- package/server/services/websocket/git-utils.ts +230 -0
- package/server/services/websocket/handler.ts +39 -112
- package/server/services/websocket/plan-board-handlers.ts +277 -0
- package/server/services/websocket/plan-execution-handlers.ts +184 -0
- package/server/services/websocket/plan-handlers.ts +23 -544
- package/server/services/websocket/plan-helpers.ts +215 -0
- package/server/services/websocket/plan-issue-handlers.ts +204 -0
- package/server/services/websocket/plan-sprint-handlers.ts +252 -0
- package/server/services/websocket/quality-complexity.ts +294 -0
- package/server/services/websocket/quality-fix-agent.ts +181 -0
- package/server/services/websocket/quality-handlers.ts +36 -404
- package/server/services/websocket/quality-linting.ts +187 -0
- package/server/services/websocket/quality-review-agent.ts +246 -0
- package/server/services/websocket/quality-service.ts +11 -762
- package/server/services/websocket/quality-tools.ts +209 -0
- package/server/services/websocket/quality-types.ts +169 -0
- package/server/services/websocket/session-handlers.ts +5 -437
- package/server/services/websocket/session-history.ts +222 -0
- package/server/services/websocket/session-initialization.ts +209 -0
- package/server/services/websocket/types.ts +46 -2
|
@@ -1,360 +1,41 @@
|
|
|
1
1
|
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
2
|
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
3
|
/**
|
|
4
|
-
* PPS Parser —
|
|
4
|
+
* PPS Parser — Public API for reading .pm/ (or legacy .plan/) directories.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* Entity parsing lives in parser-core.ts; migration in parser-migration.ts.
|
|
7
7
|
*/
|
|
8
8
|
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
9
9
|
import { join } from 'node:path';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return v.slice(1, -1);
|
|
13
|
-
}
|
|
14
|
-
return v;
|
|
15
|
-
}
|
|
16
|
-
function parseYamlValue(v) {
|
|
17
|
-
if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) {
|
|
18
|
-
return v.slice(1, -1);
|
|
19
|
-
}
|
|
20
|
-
if (v.startsWith('[') && v.endsWith(']')) {
|
|
21
|
-
return v.slice(1, -1).split(',').map(s => stripQuotes(s.trim())).filter(Boolean);
|
|
22
|
-
}
|
|
23
|
-
if (v === 'true')
|
|
24
|
-
return true;
|
|
25
|
-
if (v === 'false')
|
|
26
|
-
return false;
|
|
27
|
-
if (v === 'null' || v === '~' || v === '')
|
|
28
|
-
return null;
|
|
29
|
-
if (/^-?\d+(\.\d+)?$/.test(v))
|
|
30
|
-
return Number(v);
|
|
31
|
-
return v;
|
|
32
|
-
}
|
|
33
|
-
/** Consume indented YAML list items starting after the current index. Returns [items, newIndex]. */
|
|
34
|
-
function consumeIndentedList(lines, startIdx) {
|
|
35
|
-
const items = [];
|
|
36
|
-
let i = startIdx;
|
|
37
|
-
while (i + 1 < lines.length && /^\s+-\s/.test(lines[i + 1])) {
|
|
38
|
-
i++;
|
|
39
|
-
const item = lines[i].trim().replace(/^-\s+/, '');
|
|
40
|
-
items.push(stripQuotes(item));
|
|
41
|
-
}
|
|
42
|
-
return [items, i];
|
|
43
|
-
}
|
|
44
|
-
function parseFrontMatter(content) {
|
|
45
|
-
const match = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
46
|
-
if (!match) {
|
|
47
|
-
return { frontMatter: {}, body: content };
|
|
48
|
-
}
|
|
49
|
-
const frontMatter = {};
|
|
50
|
-
const lines = match[1].split('\n');
|
|
51
|
-
for (let i = 0; i < lines.length; i++) {
|
|
52
|
-
const trimmed = lines[i].trim();
|
|
53
|
-
if (!trimmed || trimmed.startsWith('#'))
|
|
54
|
-
continue;
|
|
55
|
-
const colonIdx = trimmed.indexOf(':');
|
|
56
|
-
if (colonIdx === -1)
|
|
57
|
-
continue;
|
|
58
|
-
const key = trimmed.slice(0, colonIdx).trim();
|
|
59
|
-
const rawValue = trimmed.slice(colonIdx + 1).trim();
|
|
60
|
-
if (!rawValue) {
|
|
61
|
-
const [items, newIdx] = consumeIndentedList(lines, i);
|
|
62
|
-
i = newIdx;
|
|
63
|
-
frontMatter[key] = items.length > 0 ? items : null;
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
frontMatter[key] = parseYamlValue(rawValue);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return { frontMatter, body: match[2] };
|
|
70
|
-
}
|
|
71
|
-
// ============================================================================
|
|
72
|
-
// Section Extraction
|
|
73
|
-
// ============================================================================
|
|
74
|
-
function extractSections(body) {
|
|
75
|
-
const sections = new Map();
|
|
76
|
-
const lines = body.split('\n');
|
|
77
|
-
let currentSection = '';
|
|
78
|
-
let currentContent = [];
|
|
79
|
-
for (const line of lines) {
|
|
80
|
-
if (line.startsWith('## ')) {
|
|
81
|
-
if (currentSection) {
|
|
82
|
-
sections.set(currentSection, currentContent.join('\n').trim());
|
|
83
|
-
}
|
|
84
|
-
currentSection = line.slice(3).trim();
|
|
85
|
-
currentContent = [];
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
currentContent.push(line);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
if (currentSection) {
|
|
92
|
-
sections.set(currentSection, currentContent.join('\n').trim());
|
|
93
|
-
}
|
|
94
|
-
return sections;
|
|
95
|
-
}
|
|
96
|
-
function parseCheckboxes(content) {
|
|
97
|
-
const items = [];
|
|
98
|
-
for (const line of content.split('\n')) {
|
|
99
|
-
const match = line.match(/^[-*]\s+\[([ xX])\]\s+(.+)$/);
|
|
100
|
-
if (match) {
|
|
101
|
-
items.push({ text: match[2].trim(), checked: match[1] !== ' ' });
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return items;
|
|
105
|
-
}
|
|
106
|
-
function parseListItems(content) {
|
|
107
|
-
const items = [];
|
|
108
|
-
for (const line of content.split('\n')) {
|
|
109
|
-
const match = line.match(/^[-*]\s+(.+)$/);
|
|
110
|
-
if (match)
|
|
111
|
-
items.push(match[1].trim());
|
|
112
|
-
}
|
|
113
|
-
return items;
|
|
114
|
-
}
|
|
115
|
-
function parseIssueSummaries(content) {
|
|
116
|
-
const summaries = [];
|
|
117
|
-
for (const line of content.split('\n')) {
|
|
118
|
-
// Match: 1. [IS-003](backlog/IS-003.md) — Title (P1)
|
|
119
|
-
const match = line.match(/\d+\.\s+\[([^\]]+)\]\(([^)]+)\)\s*[—–-]\s*(.+?)(?:\s*\((\w+)\))?\s*$/);
|
|
120
|
-
if (match) {
|
|
121
|
-
summaries.push({
|
|
122
|
-
id: match[1],
|
|
123
|
-
path: match[2],
|
|
124
|
-
title: match[3].trim(),
|
|
125
|
-
priority: match[4] || '',
|
|
126
|
-
});
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
// Match: - [IS-001](backlog/IS-001.md) — Title
|
|
130
|
-
const match2 = line.match(/^[-*]\s+\[([^\]]+)\]\(([^)]+)\)\s*[—–-]\s*(.+?)(?:\s*[→→]\s*blocked by\s+\[([^\]]+)\])?\s*$/i);
|
|
131
|
-
if (match2) {
|
|
132
|
-
summaries.push({
|
|
133
|
-
id: match2[1],
|
|
134
|
-
path: match2[2],
|
|
135
|
-
title: match2[3].trim(),
|
|
136
|
-
priority: '',
|
|
137
|
-
blockedBy: match2[4] || undefined,
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
return summaries;
|
|
142
|
-
}
|
|
143
|
-
function parseCompletedSummaries(content) {
|
|
144
|
-
const summaries = [];
|
|
145
|
-
for (const line of content.split('\n')) {
|
|
146
|
-
const match = line.match(/^[-*]\s+\[([^\]]+)\]\(([^)]+)\)\s*[—–-]\s*(.+?)(?:\s*✓)?\s*$/);
|
|
147
|
-
if (match) {
|
|
148
|
-
summaries.push({
|
|
149
|
-
id: match[1],
|
|
150
|
-
path: match[2],
|
|
151
|
-
title: match[3].trim(),
|
|
152
|
-
priority: '',
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
return summaries;
|
|
157
|
-
}
|
|
10
|
+
import { parseBoard, parseIssue, parseMilestone, parseProjectConfig, parseProjectState, parseSprint, parseWorkspace } from './parser-core.js';
|
|
11
|
+
import { isLegacyFormat, migrateToBoards } from './parser-migration.js';
|
|
158
12
|
// ============================================================================
|
|
159
|
-
//
|
|
13
|
+
// Directory Resolution
|
|
160
14
|
// ============================================================================
|
|
161
|
-
function
|
|
162
|
-
|
|
163
|
-
return [];
|
|
164
|
-
const workflows = [];
|
|
165
|
-
for (const line of section.split('\n')) {
|
|
166
|
-
const match = line.match(/\|\s*(\w+)\s*\|\s*(\w+)\s*\|\s*(.+?)\s*\|/);
|
|
167
|
-
if (match && match[1] !== 'Status') {
|
|
168
|
-
workflows.push({
|
|
169
|
-
status: match[1],
|
|
170
|
-
category: match[2],
|
|
171
|
-
description: match[3].trim(),
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
return workflows;
|
|
176
|
-
}
|
|
177
|
-
function parseTeams(section) {
|
|
178
|
-
if (!section)
|
|
179
|
-
return [];
|
|
180
|
-
const teams = [];
|
|
181
|
-
for (const line of section.split('\n')) {
|
|
182
|
-
const match = line.match(/^[-*]\s+(\w+)(?:\s*[—–-]\s*(.+))?$/);
|
|
183
|
-
if (match)
|
|
184
|
-
teams.push({ name: match[1], description: match[2]?.trim() });
|
|
185
|
-
}
|
|
186
|
-
return teams;
|
|
187
|
-
}
|
|
188
|
-
function parseProjectConfig(content) {
|
|
189
|
-
const { frontMatter, body } = parseFrontMatter(content);
|
|
190
|
-
const sections = extractSections(body);
|
|
191
|
-
const idPrefixes = {};
|
|
192
|
-
const rawPrefixes = frontMatter.id_prefixes;
|
|
193
|
-
if (rawPrefixes && typeof rawPrefixes === 'object') {
|
|
194
|
-
Object.assign(idPrefixes, rawPrefixes);
|
|
195
|
-
}
|
|
196
|
-
return {
|
|
197
|
-
name: String(frontMatter.name || ''),
|
|
198
|
-
id: String(frontMatter.id || ''),
|
|
199
|
-
created: String(frontMatter.created || ''),
|
|
200
|
-
status: frontMatter.status || 'active',
|
|
201
|
-
estimation: frontMatter.estimation || 'none',
|
|
202
|
-
idPrefixes,
|
|
203
|
-
workflows: parseWorkflows(sections.get('Workflows')),
|
|
204
|
-
labels: (Array.isArray(frontMatter.labels) ? frontMatter.labels : []),
|
|
205
|
-
teams: parseTeams(sections.get('Teams')),
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
function parseProjectState(content) {
|
|
209
|
-
const { frontMatter, body } = parseFrontMatter(content);
|
|
210
|
-
const sections = extractSections(body);
|
|
211
|
-
return {
|
|
212
|
-
project: String(frontMatter.project || ''),
|
|
213
|
-
currentSprint: frontMatter.current_sprint || null,
|
|
214
|
-
activeMilestone: frontMatter.active_milestone || null,
|
|
215
|
-
paused: frontMatter.paused === true,
|
|
216
|
-
lastSession: frontMatter.last_session || null,
|
|
217
|
-
readyToWork: parseIssueSummaries(sections.get('Ready to Work') || ''),
|
|
218
|
-
inProgress: parseIssueSummaries(sections.get('In Progress') || ''),
|
|
219
|
-
blocked: parseIssueSummaries(sections.get('Blocked') || ''),
|
|
220
|
-
recentlyCompleted: parseCompletedSummaries(sections.get('Recently Completed') || ''),
|
|
221
|
-
warnings: parseListItems(sections.get('Warnings') || ''),
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
function toStringArray(val) {
|
|
225
|
-
return Array.isArray(val) ? val.map(String) : [];
|
|
226
|
-
}
|
|
227
|
-
function optionalString(val) {
|
|
228
|
-
if (val == null)
|
|
229
|
-
return null;
|
|
230
|
-
const s = String(val);
|
|
231
|
-
return s === '' ? null : s;
|
|
15
|
+
export function isBoardCentricFormat(pmDir) {
|
|
16
|
+
return existsSync(join(pmDir, 'boards'));
|
|
232
17
|
}
|
|
233
|
-
function parseIssue(content, filePath) {
|
|
234
|
-
const { frontMatter: fm, body } = parseFrontMatter(content);
|
|
235
|
-
const sections = extractSections(body);
|
|
236
|
-
return {
|
|
237
|
-
id: String(fm.id || ''),
|
|
238
|
-
title: String(fm.title || ''),
|
|
239
|
-
type: fm.type || 'issue',
|
|
240
|
-
status: String(fm.status || 'backlog'),
|
|
241
|
-
priority: String(fm.priority || 'P2'),
|
|
242
|
-
estimate: fm.estimate != null ? fm.estimate : null,
|
|
243
|
-
labels: toStringArray(fm.labels),
|
|
244
|
-
epic: optionalString(fm.epic),
|
|
245
|
-
sprint: optionalString(fm.sprint),
|
|
246
|
-
milestone: optionalString(fm.milestone),
|
|
247
|
-
assigned: optionalString(fm.assigned),
|
|
248
|
-
created: String(fm.created || ''),
|
|
249
|
-
updated: optionalString(fm.updated),
|
|
250
|
-
due: optionalString(fm.due),
|
|
251
|
-
blockedBy: toStringArray(fm.blocked_by),
|
|
252
|
-
blocks: toStringArray(fm.blocks),
|
|
253
|
-
relatesTo: toStringArray(fm.relates_to),
|
|
254
|
-
children: toStringArray(fm.children),
|
|
255
|
-
progress: optionalString(fm.progress),
|
|
256
|
-
description: sections.get('Description') || '',
|
|
257
|
-
acceptanceCriteria: parseCheckboxes(sections.get('Acceptance Criteria') || ''),
|
|
258
|
-
technicalNotes: sections.get('Technical Notes') || null,
|
|
259
|
-
filesToModify: parseListItems(sections.get('Files to Modify') || ''),
|
|
260
|
-
activity: parseListItems(sections.get('Activity') || ''),
|
|
261
|
-
outputFile: optionalString(fm.output_file),
|
|
262
|
-
body,
|
|
263
|
-
path: filePath,
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
function parseSprintIssues(section) {
|
|
267
|
-
if (!section)
|
|
268
|
-
return [];
|
|
269
|
-
const issues = [];
|
|
270
|
-
for (const line of section.split('\n')) {
|
|
271
|
-
const match = line.match(/\|\s*\[([^\]]+)\]\(([^)]+)\)\s*\|\s*(.+?)\s*\|\s*(\S+)\s*\|\s*(\S+)\s*\|/);
|
|
272
|
-
if (match) {
|
|
273
|
-
issues.push({
|
|
274
|
-
id: match[1],
|
|
275
|
-
path: match[2],
|
|
276
|
-
title: match[3].trim(),
|
|
277
|
-
points: /^\d+$/.test(match[4]) ? Number(match[4]) : match[4],
|
|
278
|
-
status: match[5],
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return issues;
|
|
283
|
-
}
|
|
284
|
-
function optionalNumber(val) {
|
|
285
|
-
return val != null ? Number(val) : null;
|
|
286
|
-
}
|
|
287
|
-
function parseSprint(content, filePath) {
|
|
288
|
-
const { frontMatter: fm, body } = parseFrontMatter(content);
|
|
289
|
-
const sections = extractSections(body);
|
|
290
|
-
// Table-based parsing (markdown links in table rows)
|
|
291
|
-
let issues = parseSprintIssues(sections.get('Issues'));
|
|
292
|
-
// Fallback: front matter issues array (e.g., ["backlog/IS-001.md", ...])
|
|
293
|
-
if (issues.length === 0 && Array.isArray(fm.issues)) {
|
|
294
|
-
issues = fm.issues.map(path => {
|
|
295
|
-
const id = path.replace(/^backlog\//, '').replace(/\.md$/, '');
|
|
296
|
-
return { id, path, title: '', points: null, status: '' };
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
return {
|
|
300
|
-
id: String(fm.id || ''),
|
|
301
|
-
title: String(fm.title || ''),
|
|
302
|
-
status: fm.status || 'planned',
|
|
303
|
-
start: String(fm.start || fm.start_date || ''),
|
|
304
|
-
end: String(fm.end || fm.end_date || ''),
|
|
305
|
-
goal: String(fm.goal || sections.get('Goal') || sections.get('Sprint Goal') || ''),
|
|
306
|
-
capacity: optionalNumber(fm.capacity),
|
|
307
|
-
committed: optionalNumber(fm.committed),
|
|
308
|
-
completed: optionalNumber(fm.completed),
|
|
309
|
-
issues,
|
|
310
|
-
path: filePath,
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
function parseMilestone(content, filePath) {
|
|
314
|
-
const { frontMatter, body } = parseFrontMatter(content);
|
|
315
|
-
const sections = extractSections(body);
|
|
316
|
-
const epics = [];
|
|
317
|
-
const epicSection = sections.get('Epics');
|
|
318
|
-
if (epicSection) {
|
|
319
|
-
for (const line of epicSection.split('\n')) {
|
|
320
|
-
const match = line.match(/\|\s*\[([^\]]+)\]\(([^)]+)\)\s*\|\s*(.+?)\s*\|\s*(\S+)\s*\|/);
|
|
321
|
-
if (match) {
|
|
322
|
-
epics.push({
|
|
323
|
-
id: match[1],
|
|
324
|
-
path: match[2],
|
|
325
|
-
title: match[3].trim(),
|
|
326
|
-
progress: match[4],
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
return {
|
|
332
|
-
id: String(frontMatter.id || ''),
|
|
333
|
-
title: String(frontMatter.title || ''),
|
|
334
|
-
status: frontMatter.status || 'planned',
|
|
335
|
-
targetDate: frontMatter.target_date || null,
|
|
336
|
-
progress: frontMatter.progress || null,
|
|
337
|
-
definition: sections.get('Definition of Done') || '',
|
|
338
|
-
epics,
|
|
339
|
-
path: filePath,
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
// ============================================================================
|
|
343
|
-
// Directory Parser
|
|
344
|
-
// ============================================================================
|
|
345
|
-
/** Resolve the PM directory — prefers .pm/, falls back to legacy .plan/ */
|
|
346
18
|
export function resolvePmDir(workingDir) {
|
|
347
|
-
const
|
|
348
|
-
if (existsSync(
|
|
349
|
-
return
|
|
350
|
-
const
|
|
351
|
-
if (existsSync(
|
|
352
|
-
return
|
|
19
|
+
const mstroPmDir = join(workingDir, '.mstro', 'pm');
|
|
20
|
+
if (existsSync(mstroPmDir))
|
|
21
|
+
return mstroPmDir;
|
|
22
|
+
const legacyPmDir = join(workingDir, '.pm');
|
|
23
|
+
if (existsSync(legacyPmDir))
|
|
24
|
+
return legacyPmDir;
|
|
25
|
+
const legacyPlanDir = join(workingDir, '.plan');
|
|
26
|
+
if (existsSync(legacyPlanDir))
|
|
27
|
+
return legacyPlanDir;
|
|
353
28
|
return null;
|
|
354
29
|
}
|
|
30
|
+
export function defaultPmDir(workingDir) {
|
|
31
|
+
return join(workingDir, '.mstro', 'pm');
|
|
32
|
+
}
|
|
355
33
|
export function planDirExists(workingDir) {
|
|
356
34
|
return resolvePmDir(workingDir) !== null;
|
|
357
35
|
}
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// File Utilities
|
|
38
|
+
// ============================================================================
|
|
358
39
|
function readFileIfExists(path) {
|
|
359
40
|
try {
|
|
360
41
|
if (existsSync(path))
|
|
@@ -369,46 +50,117 @@ function readMdFilesInDir(dirPath) {
|
|
|
369
50
|
try {
|
|
370
51
|
return readdirSync(dirPath)
|
|
371
52
|
.filter(f => f.endsWith('.md'))
|
|
372
|
-
.map(name => {
|
|
373
|
-
const content = readFileSync(join(dirPath, name), 'utf-8');
|
|
374
|
-
return { name, content };
|
|
375
|
-
});
|
|
53
|
+
.map(name => ({ name, content: readFileSync(join(dirPath, name), 'utf-8') }));
|
|
376
54
|
}
|
|
377
55
|
catch {
|
|
378
56
|
return [];
|
|
379
57
|
}
|
|
380
58
|
}
|
|
59
|
+
function listDirFiles(dirPath, ext) {
|
|
60
|
+
if (!existsSync(dirPath))
|
|
61
|
+
return [];
|
|
62
|
+
try {
|
|
63
|
+
return readdirSync(dirPath).filter(f => f.endsWith(ext));
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function readReviewResults(reviewsDir) {
|
|
70
|
+
const results = [];
|
|
71
|
+
for (const f of listDirFiles(reviewsDir, '.json')) {
|
|
72
|
+
const content = readFileIfExists(join(reviewsDir, f));
|
|
73
|
+
if (content)
|
|
74
|
+
results.push(JSON.parse(content));
|
|
75
|
+
}
|
|
76
|
+
return results;
|
|
77
|
+
}
|
|
78
|
+
// ============================================================================
|
|
79
|
+
// Defaults
|
|
80
|
+
// ============================================================================
|
|
81
|
+
const defaultProject = {
|
|
82
|
+
name: '', id: '', created: '', status: 'active', estimation: 'none',
|
|
83
|
+
idPrefixes: {}, workflows: [], labels: [], teams: [],
|
|
84
|
+
};
|
|
85
|
+
const defaultState = {
|
|
86
|
+
project: '', currentSprint: null, activeMilestone: null, paused: false,
|
|
87
|
+
lastSession: null, readyToWork: [], inProgress: [], blocked: [],
|
|
88
|
+
recentlyCompleted: [], warnings: [],
|
|
89
|
+
};
|
|
90
|
+
// ============================================================================
|
|
91
|
+
// Board & Plan Directory Parsing
|
|
92
|
+
// ============================================================================
|
|
93
|
+
export function parseBoardDirectory(pmDir, boardId) {
|
|
94
|
+
const boardDir = join(pmDir, 'boards', boardId);
|
|
95
|
+
if (!existsSync(boardDir))
|
|
96
|
+
return null;
|
|
97
|
+
const boardContent = readFileIfExists(join(boardDir, 'board.md'));
|
|
98
|
+
if (!boardContent)
|
|
99
|
+
return null;
|
|
100
|
+
const board = parseBoard(boardContent, `boards/${boardId}/board.md`);
|
|
101
|
+
const stateContent = readFileIfExists(join(boardDir, 'STATE.md'));
|
|
102
|
+
const state = stateContent ? parseProjectState(stateContent) : { ...defaultState };
|
|
103
|
+
const issueFiles = readMdFilesInDir(join(boardDir, 'backlog'));
|
|
104
|
+
const boardPrefix = `boards/${boardId}/`;
|
|
105
|
+
const issues = issueFiles.map(f => {
|
|
106
|
+
const issue = parseIssue(f.content, `${boardPrefix}backlog/${f.name}`);
|
|
107
|
+
issue.blockedBy = issue.blockedBy.map(bp => bp.startsWith('boards/') ? bp : `${boardPrefix}${bp}`);
|
|
108
|
+
issue.blocks = issue.blocks.map(bp => bp.startsWith('boards/') ? bp : `${boardPrefix}${bp}`);
|
|
109
|
+
if (issue.epic && !issue.epic.startsWith('boards/'))
|
|
110
|
+
issue.epic = `${boardPrefix}${issue.epic}`;
|
|
111
|
+
return issue;
|
|
112
|
+
});
|
|
113
|
+
return { board, state, issues };
|
|
114
|
+
}
|
|
115
|
+
function parseBoardCentricState(planDir) {
|
|
116
|
+
const workspaceContent = readFileIfExists(join(planDir, 'workspace.json'));
|
|
117
|
+
const workspace = workspaceContent ? parseWorkspace(workspaceContent) : { activeBoardId: null, boardOrder: [] };
|
|
118
|
+
const boards = [];
|
|
119
|
+
const boardsDir = join(planDir, 'boards');
|
|
120
|
+
if (existsSync(boardsDir)) {
|
|
121
|
+
for (const entry of readdirSync(boardsDir)) {
|
|
122
|
+
const boardMdPath = join(boardsDir, entry, 'board.md');
|
|
123
|
+
if (!existsSync(boardMdPath))
|
|
124
|
+
continue;
|
|
125
|
+
boards.push(parseBoard(readFileSync(boardMdPath, 'utf-8'), `boards/${entry}/board.md`));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const orderMap = new Map(workspace.boardOrder.map((id, i) => [id, i]));
|
|
129
|
+
boards.sort((a, b) => (orderMap.get(a.id) ?? 999) - (orderMap.get(b.id) ?? 999));
|
|
130
|
+
const activeBoard = workspace.activeBoardId ? parseBoardDirectory(planDir, workspace.activeBoardId) : null;
|
|
131
|
+
return { boards, workspace, activeBoard };
|
|
132
|
+
}
|
|
381
133
|
export function parsePlanDirectory(workingDir) {
|
|
382
134
|
const planDir = resolvePmDir(workingDir);
|
|
383
135
|
if (!planDir)
|
|
384
136
|
return null;
|
|
385
|
-
|
|
137
|
+
if (isLegacyFormat(planDir))
|
|
138
|
+
migrateToBoards(planDir);
|
|
386
139
|
const projectContent = readFileIfExists(join(planDir, 'project.md'));
|
|
387
|
-
const project = projectContent
|
|
388
|
-
? parseProjectConfig(projectContent)
|
|
389
|
-
: { name: '', id: '', created: '', status: 'active', estimation: 'none', idPrefixes: {}, workflows: [], labels: [], teams: [] };
|
|
390
|
-
// Parse STATE.md
|
|
391
|
-
const stateContent = readFileIfExists(join(planDir, 'STATE.md'));
|
|
392
|
-
const state = stateContent
|
|
393
|
-
? parseProjectState(stateContent)
|
|
394
|
-
: { project: '', currentSprint: null, activeMilestone: null, paused: false, lastSession: null, readyToWork: [], inProgress: [], blocked: [], recentlyCompleted: [], warnings: [] };
|
|
395
|
-
// Parse backlog issues
|
|
396
|
-
const issueFiles = readMdFilesInDir(join(planDir, 'backlog'));
|
|
397
|
-
const issues = issueFiles.map(f => parseIssue(f.content, `backlog/${f.name}`));
|
|
398
|
-
// Parse sprints
|
|
399
|
-
const sprintFiles = readMdFilesInDir(join(planDir, 'sprints'));
|
|
400
|
-
const sprints = sprintFiles.map(f => parseSprint(f.content, `sprints/${f.name}`));
|
|
401
|
-
// Parse milestones
|
|
140
|
+
const project = projectContent ? parseProjectConfig(projectContent) : { ...defaultProject };
|
|
402
141
|
const milestoneFiles = readMdFilesInDir(join(planDir, 'milestones'));
|
|
403
142
|
const milestones = milestoneFiles.map(f => parseMilestone(f.content, `milestones/${f.name}`));
|
|
404
|
-
|
|
143
|
+
if (!isBoardCentricFormat(planDir)) {
|
|
144
|
+
return {
|
|
145
|
+
project, state: { ...defaultState }, boards: [], workspace: { activeBoardId: null, boardOrder: [] },
|
|
146
|
+
activeBoard: null, issues: [], sprints: [], milestones,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const { boards, workspace, activeBoard } = parseBoardCentricState(planDir);
|
|
150
|
+
return {
|
|
151
|
+
project, state: activeBoard?.state ?? { ...defaultState },
|
|
152
|
+
boards, workspace, activeBoard, issues: activeBoard?.issues ?? [],
|
|
153
|
+
sprints: [], milestones,
|
|
154
|
+
};
|
|
405
155
|
}
|
|
156
|
+
// ============================================================================
|
|
157
|
+
// Single Entity Parsers
|
|
158
|
+
// ============================================================================
|
|
406
159
|
export function parseSingleIssue(workingDir, issuePath) {
|
|
407
160
|
const pmDir = resolvePmDir(workingDir);
|
|
408
161
|
if (!pmDir)
|
|
409
162
|
return null;
|
|
410
|
-
const
|
|
411
|
-
const content = readFileIfExists(fullPath);
|
|
163
|
+
const content = readFileIfExists(join(pmDir, issuePath));
|
|
412
164
|
if (!content)
|
|
413
165
|
return null;
|
|
414
166
|
return parseIssue(content, issuePath);
|
|
@@ -417,8 +169,7 @@ export function parseSingleSprint(workingDir, sprintPath) {
|
|
|
417
169
|
const pmDir = resolvePmDir(workingDir);
|
|
418
170
|
if (!pmDir)
|
|
419
171
|
return null;
|
|
420
|
-
const
|
|
421
|
-
const content = readFileIfExists(fullPath);
|
|
172
|
+
const content = readFileIfExists(join(pmDir, sprintPath));
|
|
422
173
|
if (!content)
|
|
423
174
|
return null;
|
|
424
175
|
return parseSprint(content, sprintPath);
|
|
@@ -427,13 +178,14 @@ export function parseSingleMilestone(workingDir, milestonePath) {
|
|
|
427
178
|
const pmDir = resolvePmDir(workingDir);
|
|
428
179
|
if (!pmDir)
|
|
429
180
|
return null;
|
|
430
|
-
const
|
|
431
|
-
const content = readFileIfExists(fullPath);
|
|
181
|
+
const content = readFileIfExists(join(pmDir, milestonePath));
|
|
432
182
|
if (!content)
|
|
433
183
|
return null;
|
|
434
184
|
return parseMilestone(content, milestonePath);
|
|
435
185
|
}
|
|
436
|
-
|
|
186
|
+
// ============================================================================
|
|
187
|
+
// ID Generation
|
|
188
|
+
// ============================================================================
|
|
437
189
|
export function getNextId(issues, prefix) {
|
|
438
190
|
const escaped = prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
439
191
|
const pattern = new RegExp(`^${escaped}-(\\d+)$`);
|
|
@@ -448,4 +200,67 @@ export function getNextId(issues, prefix) {
|
|
|
448
200
|
}
|
|
449
201
|
return `${prefix}-${String(max + 1).padStart(3, '0')}`;
|
|
450
202
|
}
|
|
203
|
+
export function getNextBoardId(boards) {
|
|
204
|
+
let max = 0;
|
|
205
|
+
for (const board of boards) {
|
|
206
|
+
const match = board.id.match(/^BOARD-(\d+)$/);
|
|
207
|
+
if (match) {
|
|
208
|
+
const num = Number.parseInt(match[1], 10);
|
|
209
|
+
if (num > max)
|
|
210
|
+
max = num;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return `BOARD-${String(max + 1).padStart(3, '0')}`;
|
|
214
|
+
}
|
|
215
|
+
export function getNextBoardNumber(boards) {
|
|
216
|
+
let max = 0;
|
|
217
|
+
for (const board of boards) {
|
|
218
|
+
const match = board.title.match(/^Board (\d+)$/);
|
|
219
|
+
if (match) {
|
|
220
|
+
const num = Number.parseInt(match[1], 10);
|
|
221
|
+
if (num > max)
|
|
222
|
+
max = num;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return max + 1;
|
|
226
|
+
}
|
|
227
|
+
export function parseBoardArtifacts(workingDir, boardId) {
|
|
228
|
+
const pmDir = resolvePmDir(workingDir);
|
|
229
|
+
if (!pmDir)
|
|
230
|
+
return null;
|
|
231
|
+
const boardDir = join(pmDir, 'boards', boardId);
|
|
232
|
+
if (!existsSync(boardDir))
|
|
233
|
+
return null;
|
|
234
|
+
const progressLog = readFileIfExists(join(boardDir, 'progress.md')) ?? '';
|
|
235
|
+
const outputFiles = listDirFiles(join(boardDir, 'out'), '.md');
|
|
236
|
+
const reviewResults = readReviewResults(join(boardDir, 'reviews'));
|
|
237
|
+
const executionLogs = listDirFiles(join(boardDir, 'logs'), '.log').sort();
|
|
238
|
+
return { boardId, progressLog, outputFiles, reviewResults, executionLogs };
|
|
239
|
+
}
|
|
240
|
+
/** @deprecated Use getNextBoardId — kept for migration compatibility */
|
|
241
|
+
export function getNextSprintId(sprints) {
|
|
242
|
+
let max = 0;
|
|
243
|
+
for (const sprint of sprints) {
|
|
244
|
+
const match = sprint.id.match(/^SPRINT-(\d+)$/);
|
|
245
|
+
if (match) {
|
|
246
|
+
const num = Number.parseInt(match[1], 10);
|
|
247
|
+
if (num > max)
|
|
248
|
+
max = num;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return `SPRINT-${String(max + 1).padStart(3, '0')}`;
|
|
252
|
+
}
|
|
253
|
+
/** @deprecated Use parseBoardArtifacts — kept for migration compatibility */
|
|
254
|
+
export function parseSprintArtifacts(workingDir, sprintId) {
|
|
255
|
+
const pmDir = resolvePmDir(workingDir);
|
|
256
|
+
if (!pmDir)
|
|
257
|
+
return null;
|
|
258
|
+
const sandboxDir = join(pmDir, 'sprints', sprintId);
|
|
259
|
+
if (!existsSync(sandboxDir))
|
|
260
|
+
return null;
|
|
261
|
+
const progressLog = readFileIfExists(join(sandboxDir, 'progress.md')) ?? '';
|
|
262
|
+
const outputFiles = listDirFiles(join(sandboxDir, 'out'), '.md');
|
|
263
|
+
const reviewResults = readReviewResults(join(sandboxDir, 'reviews'));
|
|
264
|
+
return { sprintId, progressLog, outputFiles, reviewResults };
|
|
265
|
+
}
|
|
451
266
|
//# sourceMappingURL=parser.js.map
|