specsmd 0.1.47 → 0.1.51

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.
@@ -124,7 +124,7 @@ You are the **Builder Agent** for FIRE (Fast Intent-Run Engineering).
124
124
 
125
125
  ```yaml
126
126
  run:
127
- id: run-001
127
+ id: run-fabriqa-2026-001
128
128
  scope: batch # single | batch | wide
129
129
  work_items:
130
130
  - id: login-endpoint
@@ -159,7 +159,7 @@ You are the **Builder Agent** for FIRE (Fast Intent-Run Engineering).
159
159
  | Create run.md | (handled by init-run.cjs) | ❌ NO direct write |
160
160
  | Update state.yaml | (handled by scripts) | ❌ NO direct edit |
161
161
 
162
- <check if="about to mkdir .specs-fire/runs/run-XXX">
162
+ <check if="about to mkdir .specs-fire/runs/run-<worktree>-XXX">
163
163
  <action>STOP — use init-run.cjs instead</action>
164
164
  </check>
165
165
  <check if="about to edit state.yaml directly">
@@ -42,7 +42,7 @@ Review code written during a run, auto-fix no-brainer issues, and suggest improv
42
42
  - path: src/routes/index.ts
43
43
  changes: Added login route
44
44
 
45
- run_id: run-001
45
+ run_id: run-fabriqa-2026-001
46
46
  intent_id: user-auth
47
47
  ```
48
48
 
@@ -548,8 +548,8 @@ Supports both single-item and multi-item (batch/wide) runs.
548
548
  ```json
549
549
  {
550
550
  "success": true,
551
- "runId": "run-001",
552
- "runPath": "/project/.specs-fire/runs/run-001",
551
+ "runId": "run-fabriqa-2026-001",
552
+ "runPath": "/project/.specs-fire/runs/run-fabriqa-2026-001",
553
553
  "scope": "batch",
554
554
  "workItems": [...],
555
555
  "currentItem": "wi-1"
@@ -561,10 +561,10 @@ Supports both single-item and multi-item (batch/wide) runs.
561
561
  <script name="complete-run.cjs">
562
562
  ```bash
563
563
  # Complete current item (batch runs - moves to next item)
564
- node scripts/complete-run.cjs /project run-001 --complete-item
564
+ node scripts/complete-run.cjs /project run-fabriqa-2026-001 --complete-item
565
565
 
566
566
  # Complete entire run (single runs or final item in batch)
567
- node scripts/complete-run.cjs /project run-001 --complete-run \
567
+ node scripts/complete-run.cjs /project run-fabriqa-2026-001 --complete-run \
568
568
  --files-created='[{"path":"src/new.ts","purpose":"New feature"}]' \
569
569
  --files-modified='[{"path":"src/old.ts","changes":"Added import"}]' \
570
570
  --tests=5 --coverage=85
@@ -574,7 +574,7 @@ Supports both single-item and multi-item (batch/wide) runs.
574
574
  ```json
575
575
  {
576
576
  "success": true,
577
- "runId": "run-001",
577
+ "runId": "run-fabriqa-2026-001",
578
578
  "completedItem": "wi-1",
579
579
  "nextItem": "wi-2",
580
580
  "remainingItems": 1,
@@ -587,7 +587,7 @@ Supports both single-item and multi-item (batch/wide) runs.
587
587
  ```json
588
588
  {
589
589
  "success": true,
590
- "runId": "run-001",
590
+ "runId": "run-fabriqa-2026-001",
591
591
  "scope": "batch",
592
592
  "workItemsCompleted": 2,
593
593
  "completedAt": "2026-01-20T..."
@@ -619,7 +619,7 @@ Supports both single-item and multi-item (batch/wide) runs.
619
619
  After init-run.cjs creates a run:
620
620
 
621
621
  ```
622
- .specs-fire/runs/run-001/
622
+ .specs-fire/runs/run-fabriqa-2026-001/
623
623
  ├── run.md # Created by init-run.cjs, updated by complete-run.cjs
624
624
  ├── plan.md # Created BEFORE implementation (ALL modes - required)
625
625
  ├── test-report.md # Created AFTER tests pass (required)
@@ -58,7 +58,7 @@ Display current run status and progress.
58
58
  <example_output>
59
59
 
60
60
  ```
61
- ## Run Status: run-003
61
+ ## Run Status: run-fabriqa-2026-003
62
62
 
63
63
  **Work Item**: Add session management
64
64
  **Intent**: User Authentication
@@ -103,7 +103,7 @@ You are the **Orchestrator Agent** for FIRE (Fast Intent-Run Engineering).
103
103
  runs:
104
104
  active: [] # List of active runs (supports multiple parallel runs)
105
105
  completed:
106
- - id: run-001
106
+ - id: run-fabriqa-2026-001
107
107
  work_items:
108
108
  - id: login-endpoint
109
109
  intent: user-auth
@@ -639,7 +639,7 @@ Detect inconsistencies and offer interactive resolution.
639
639
 
640
640
  ## Active Runs
641
641
 
642
- - **Run**: run-002 | **Scope**: single
642
+ - **Run**: run-fabriqa-2026-002 | **Scope**: single
643
643
  - **Current Item**: session-management
644
644
  - **Started**: 2026-01-19T10:30:00Z
645
645
 
@@ -657,7 +657,7 @@ Detect inconsistencies and offer interactive resolution.
657
657
 
658
658
  | # | Type | Location | Issue | Suggested Fix |
659
659
  |---|------|----------|-------|---------------|
660
- | 1 | 🟡 | run-002 | Run started 3 days ago, may be stale | Resume or abandon |
660
+ | 1 | 🟡 | run-fabriqa-2026-002 | Run started 3 days ago, may be stale | Resume or abandon |
661
661
  | 2 | 🔵 | login-endpoint | Frontmatter says 'pending' but state says 'completed' | Sync frontmatter |
662
662
  | 3 | 🟡 | analytics-dashboard.md | Work item on disk but not tracked | Add to state.yaml |
663
663
 
@@ -66,7 +66,7 @@ state:
66
66
  - active: "List of currently active runs (supports multiple parallel runs)"
67
67
  - completed: "List of completed runs with id, work_item, intent, completed timestamp"
68
68
  # Each run (active or completed) has:
69
- # - id: "Run ID (e.g., run-001)"
69
+ # - id: "Run ID (e.g., run-fabriqa-2026-001)"
70
70
  # - scope: "single | batch | wide"
71
71
  # - work_items: "List of work items in this run"
72
72
  # - current_item: "Work item currently being executed (active runs only)"
@@ -93,9 +93,9 @@ naming:
93
93
  note: "Kebab-case, action-oriented name"
94
94
 
95
95
  runs:
96
- format: "run-{NNN}/"
97
- example: "run-001/"
98
- note: "Sequential 3-digit number, folder per run"
96
+ format: "run-{worktree}-{NNN}/"
97
+ example: "run-fabriqa-2026-001/"
98
+ note: "Sequential 3-digit number per worktree, folder per run"
99
99
  contents:
100
100
  - "run.md" # Run log (created by init-run.cjs)
101
101
  - "plan.md" # Implementation plan (ALL modes, created BEFORE implementation)
@@ -2308,6 +2308,20 @@ function rowToFileEntry(row) {
2308
2308
  };
2309
2309
  }
2310
2310
 
2311
+ function rowToWorktreeId(row) {
2312
+ if (!row || typeof row.key !== 'string') {
2313
+ return null;
2314
+ }
2315
+
2316
+ const prefix = 'worktree:item:';
2317
+ if (!row.key.startsWith(prefix)) {
2318
+ return null;
2319
+ }
2320
+
2321
+ const worktreeId = row.key.slice(prefix.length).trim();
2322
+ return worktreeId === '' ? null : worktreeId;
2323
+ }
2324
+
2311
2325
  function moveRowSelection(rows, currentIndex, direction) {
2312
2326
  if (!Array.isArray(rows) || rows.length === 0) {
2313
2327
  return 0;
@@ -2401,9 +2415,6 @@ function buildQuickHelpText(view, options = {}) {
2401
2415
  parts.push('b worktrees', 'u others');
2402
2416
  }
2403
2417
  parts.push('a current', 'f files');
2404
- if (hasWorktrees) {
2405
- parts.push('w worktree');
2406
- }
2407
2418
  }
2408
2419
  parts.push(`tab1 ${activeLabel}`);
2409
2420
 
@@ -2443,7 +2454,6 @@ function buildHelpOverlayLines(options = {}) {
2443
2454
  ...(hasWorktrees ? ['b focus worktrees section', 'u focus other-worktrees section'] : []),
2444
2455
  `a focus active ${itemLabel}`,
2445
2456
  `f focus ${itemLabel} files`,
2446
- ...(hasWorktrees ? ['w open worktree switcher'] : []),
2447
2457
  'up/down or j/k move selection',
2448
2458
  'enter expand/collapse selected folder row',
2449
2459
  'v or space preview selected file',
@@ -2946,9 +2956,7 @@ function createDashboardApp(deps) {
2946
2956
  const previewVisibleRows = Number.isFinite(terminalSize.rows) ? terminalSize.rows : (process.stdout.rows || 40);
2947
2957
  const showErrorPanelForSections = Boolean(error) && previewVisibleRows >= 18;
2948
2958
  const worktreeSectionEnabled = hasMultipleWorktrees(snapshot);
2949
- const otherWorktreesSectionEnabled = worktreeSectionEnabled
2950
- && isSelectedWorktreeMain(snapshot)
2951
- && getEffectiveFlow(activeFlow, snapshot) !== 'simple';
2959
+ const otherWorktreesSectionEnabled = worktreeSectionEnabled;
2952
2960
 
2953
2961
  const getAvailableSections = useCallback((viewId) => {
2954
2962
  const base = getSectionOrderForView(viewId, {
@@ -3093,12 +3101,15 @@ function createDashboardApp(deps) {
3093
3101
  const selectedFocusedRow = getSelectedRow(focusedRows, focusedIndex);
3094
3102
  const selectedFocusedFile = rowToFileEntry(selectedFocusedRow);
3095
3103
 
3096
- const refresh = useCallback(async () => {
3104
+ const refresh = useCallback(async (overrideSelectedWorktreeId = null) => {
3097
3105
  const now = new Date().toISOString();
3106
+ const requestedWorktreeId = typeof overrideSelectedWorktreeId === 'string' && overrideSelectedWorktreeId.trim() !== ''
3107
+ ? overrideSelectedWorktreeId.trim()
3108
+ : selectedWorktreeId;
3098
3109
 
3099
3110
  try {
3100
3111
  const result = await parseSnapshotForActiveFlow(activeFlow, {
3101
- selectedWorktreeId
3112
+ selectedWorktreeId: requestedWorktreeId
3102
3113
  });
3103
3114
 
3104
3115
  if (result?.ok) {
@@ -3149,6 +3160,40 @@ function createDashboardApp(deps) {
3149
3160
  }
3150
3161
  }, [activeFlow, parseSnapshotForActiveFlow, selectedWorktreeId, watchEnabled]);
3151
3162
 
3163
+ const switchToWorktree = useCallback((nextWorktreeId, options = {}) => {
3164
+ const normalizedNextId = typeof nextWorktreeId === 'string' ? nextWorktreeId.trim() : '';
3165
+ if (normalizedNextId === '') {
3166
+ setStatusLine('No worktree selected.');
3167
+ return false;
3168
+ }
3169
+
3170
+ const nextItem = worktreeItems.find((item) => item.id === normalizedNextId);
3171
+ if (!nextItem) {
3172
+ setStatusLine('Selected worktree is no longer available.');
3173
+ return false;
3174
+ }
3175
+
3176
+ if (!nextItem.flowAvailable) {
3177
+ setStatusLine(`Flow is unavailable in worktree: ${getWorktreeDisplayName(nextItem)}`);
3178
+ return false;
3179
+ }
3180
+
3181
+ const changed = normalizedNextId !== selectedWorktreeId;
3182
+ setSelectedWorktreeId(normalizedNextId);
3183
+ setPreviewTarget(null);
3184
+ setPreviewOpen(false);
3185
+ setOverlayPreviewOpen(false);
3186
+ setPreviewScroll(0);
3187
+ setPaneFocus('main');
3188
+
3189
+ if (changed || options.forceRefresh) {
3190
+ setStatusLine(`Switched to worktree: ${getWorktreeDisplayName(nextItem)}`);
3191
+ void refresh(normalizedNextId);
3192
+ }
3193
+
3194
+ return true;
3195
+ }, [refresh, selectedWorktreeId, worktreeItems]);
3196
+
3152
3197
  useInput((input, key) => {
3153
3198
  if ((key.ctrl && input === 'c') || input === 'q') {
3154
3199
  exit();
@@ -3192,20 +3237,8 @@ function createDashboardApp(deps) {
3192
3237
 
3193
3238
  if (key.return || key.enter) {
3194
3239
  const selectedOverlayItem = worktreeItems[clampIndex(worktreeOverlayIndex, worktreeItems.length || 1)];
3195
- if (!selectedOverlayItem) {
3196
- setStatusLine('No worktree selected.');
3197
- setWorktreeOverlayOpen(false);
3198
- return;
3199
- }
3200
- setSelectedWorktreeId(selectedOverlayItem.id);
3240
+ switchToWorktree(selectedOverlayItem?.id || '', { forceRefresh: true });
3201
3241
  setWorktreeOverlayOpen(false);
3202
- setPreviewTarget(null);
3203
- setPreviewOpen(false);
3204
- setOverlayPreviewOpen(false);
3205
- setPreviewScroll(0);
3206
- setPaneFocus('main');
3207
- setStatusLine(`Switched to worktree: ${getWorktreeDisplayName(selectedOverlayItem)}`);
3208
- void refresh();
3209
3242
  return;
3210
3243
  }
3211
3244
 
@@ -3236,13 +3269,6 @@ function createDashboardApp(deps) {
3236
3269
  return;
3237
3270
  }
3238
3271
 
3239
- if (ui.view === 'runs' && input === 'w' && worktreeSectionEnabled) {
3240
- setWorktreeOverlayIndex(clampIndex(worktreeItems.findIndex((item) => item.id === selectedWorktreeId), worktreeItems.length || 1));
3241
- setWorktreeOverlayOpen(true);
3242
- setPaneFocus('main');
3243
- return;
3244
- }
3245
-
3246
3272
  if ((input === ']' || input === 'm') && availableFlowIds.length > 1) {
3247
3273
  snapshotHashRef.current = safeJsonHash(null);
3248
3274
  errorHashRef.current = null;
@@ -3460,6 +3486,15 @@ function createDashboardApp(deps) {
3460
3486
  ...previous,
3461
3487
  [targetSection]: nextIndex
3462
3488
  }));
3489
+
3490
+ if (targetSection === 'worktrees' && worktreeSectionEnabled) {
3491
+ const nextRow = getSelectedRow(targetRows, nextIndex);
3492
+ const nextWorktreeId = rowToWorktreeId(nextRow);
3493
+ if (nextWorktreeId && nextWorktreeId !== selectedWorktreeId) {
3494
+ switchToWorktree(nextWorktreeId);
3495
+ }
3496
+ }
3497
+
3463
3498
  return;
3464
3499
  }
3465
3500
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specsmd",
3
- "version": "0.1.47",
3
+ "version": "0.1.51",
4
4
  "description": "Multi-agent orchestration system for AI-native software development. Delivers AI-DLC, Agile, and custom SDLC flows as markdown-based agent systems.",
5
5
  "main": "lib/installer.js",
6
6
  "bin": {