maskweaver 0.8.1 → 0.8.2

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 (35) hide show
  1. package/README.ko.md +9 -12
  2. package/README.md +9 -12
  3. package/assets/commands/weave-craft.md +3 -3
  4. package/assets/commands/weave-design.md +5 -2
  5. package/assets/commands/weave-flow.md +3 -13
  6. package/assets/commands/weave-help.md +7 -11
  7. package/assets/commands/weave-prepare.md +2 -2
  8. package/assets/commands/weave-research.md +4 -2
  9. package/dist/plugin/index.d.ts.map +1 -1
  10. package/dist/plugin/index.js +2 -1
  11. package/dist/plugin/index.js.map +1 -1
  12. package/dist/plugin/tools/slashcommand.d.ts.map +1 -1
  13. package/dist/plugin/tools/slashcommand.js +12 -11
  14. package/dist/plugin/tools/slashcommand.js.map +1 -1
  15. package/dist/plugin/tools/weave.d.ts +7 -4
  16. package/dist/plugin/tools/weave.d.ts.map +1 -1
  17. package/dist/plugin/tools/weave.js +226 -66
  18. package/dist/plugin/tools/weave.js.map +1 -1
  19. package/dist/weave/phase-manager.d.ts +5 -0
  20. package/dist/weave/phase-manager.d.ts.map +1 -1
  21. package/dist/weave/phase-manager.js +25 -0
  22. package/dist/weave/phase-manager.js.map +1 -1
  23. package/dist/weave/stages/execute.js +2 -2
  24. package/dist/weave/stages/execute.js.map +1 -1
  25. package/dist/weave/stages/plan.d.ts +8 -0
  26. package/dist/weave/stages/plan.d.ts.map +1 -1
  27. package/dist/weave/stages/plan.js +161 -2
  28. package/dist/weave/stages/plan.js.map +1 -1
  29. package/dist/weave/stages/research.d.ts.map +1 -1
  30. package/dist/weave/stages/research.js +431 -4
  31. package/dist/weave/stages/research.js.map +1 -1
  32. package/dist/weave/types.d.ts +6 -0
  33. package/dist/weave/types.d.ts.map +1 -1
  34. package/package.json +1 -1
  35. package/assets/commands/weave-approve.md +0 -70
@@ -32,18 +32,17 @@ export function createWeaveTool() {
32
32
  description: `Weave: Phase-driven development workflow with expert mask auto-selection and cross-project knowledge sharing.
33
33
 
34
34
  Commands:
35
- - research [docsPath]: Deep-read docs and write persistent research.md
35
+ - research [docsPath]: Deep-read docs + workspace context and write persistent research.md
36
36
  - spec [docsPath]: Generate baseline spec (requirements + AC)
37
- - design [docsPath]: Analyze requirements and create phase-based plan
38
- - prepare [docsPath]: Create research + spec + plan with defaults (vNext happy path)
37
+ - design [docsPath]: Analyze requirements and create phase-based plan (auto-splits oversized plans)
38
+ - prepare [docsPath]: Create research + spec + plan with defaults (auto-splits oversized plans)
39
39
  - refine-plan: Apply annotation notes to active plan
40
40
  - approve-plan: Mark plan reviewed/approved before implementation
41
- - flow [docsPath]: One-command path (prepare -> approve-plan gate -> craft auto-loop)
42
- - craft [phaseId]: Execute next phase automatically if omitted (includes auto task loop)
41
+ - flow [docsPath]: One-command path (prepare -> approve-plan gate -> craft auto-loop + auto finalize)
42
+ - craft [phaseId]: Execute next phase automatically if omitted (includes auto task loop + goal check + auto finalize)
43
43
  - status: View overall progress
44
44
  - worktree: Manage git worktrees for parallel work
45
45
  - verify: Run build/test verification for current worktree
46
- - approve [phaseId]: Mark phase complete (auto phase if omitted)
47
46
  - troubleshoot [error]: Search global knowledge for solutions
48
47
  - record [solution]: Record a troubleshooting solution
49
48
  - repair: Scan and auto-repair corrupted plan YAML files
@@ -58,16 +57,22 @@ Examples:
58
57
  - weave repair
59
58
  - weave troubleshoot "Cannot find module 'xyz'"`,
60
59
  args: {
61
- command: z.enum(['research', 'spec', 'design', 'prepare', 'refine-plan', 'approve-plan', 'flow', 'craft', 'status', 'worktree', 'verify', 'troubleshoot', 'record', 'approve', 'help', 'repair'])
60
+ command: z.enum(['research', 'spec', 'design', 'prepare', 'refine-plan', 'approve-plan', 'flow', 'craft', 'status', 'worktree', 'verify', 'troubleshoot', 'record', 'help', 'repair'])
62
61
  .describe('Weave command to execute'),
63
62
  docsPath: z.string().optional()
64
63
  .describe('Path to requirements documents (for design command)'),
65
64
  phaseId: z.string().optional()
66
- .describe('Phase ID (optional for craft/approve)'),
65
+ .describe('Phase ID (optional for craft)'),
67
66
  projectName: z.string().optional()
68
67
  .describe('Project name (for design command)'),
69
68
  planName: z.string().optional()
70
69
  .describe('Plan name (kebab-case) used for plan filename (optional)'),
70
+ splitPlans: z.boolean().optional()
71
+ .describe('Auto-split oversized plans into multiple shard plan files (default: true)'),
72
+ splitMaxPhases: z.number().int().min(2).max(8).optional()
73
+ .describe('Max phases per shard when splitPlans is enabled (default: 3)'),
74
+ splitMaxHours: z.number().int().min(4).max(40).optional()
75
+ .describe('Max estimated hours per shard when splitPlans is enabled (default: 10)'),
71
76
  planReview: z.string().optional()
72
77
  .describe('Plan review summary text (for approve-plan command)'),
73
78
  notesPath: z.string().optional()
@@ -85,13 +90,11 @@ Examples:
85
90
  bootstrapWeave: z.boolean().optional()
86
91
  .describe('Bootstrap .opencode/weave into new worktree (default: true)'),
87
92
  skipVerify: z.boolean().optional()
88
- .describe('Skip verification before approve (default: false)'),
93
+ .describe('Skip final verification before auto-finalize (default: false)'),
89
94
  verifyMode: z.enum(['quick', 'full']).optional()
90
95
  .describe('Verification mode: quick (typecheck+tests) or full (all available)'),
91
- autoApprove: z.boolean().optional()
92
- .describe('Automatically run approve after flow finishes all tasks (default: false)'),
93
96
  commit: z.boolean().optional()
94
- .describe('Create a git commit on approve (default: false)'),
97
+ .describe('Create git commits during craft loop verification passes (default: false)'),
95
98
  stageAll: z.boolean().optional()
96
99
  .describe('Stage all changes before commit (default: false)'),
97
100
  commitMessage: z.string().optional()
@@ -140,8 +143,6 @@ Examples:
140
143
  return await handleTroubleshoot(args);
141
144
  case 'record':
142
145
  return await handleRecord(args);
143
- case 'approve':
144
- return await handleApprove(args, basePath);
145
146
  case 'repair':
146
147
  return await handleRepair(basePath);
147
148
  case 'help':
@@ -474,6 +475,9 @@ async function handleDesign(args, basePath) {
474
475
  projectName: projectName || 'My Project',
475
476
  planName: normalizePlanName(args.planName, projectName, resolvedDocsPath),
476
477
  basePath,
478
+ splitPlans: args.splitPlans,
479
+ splitMaxPhases: args.splitMaxPhases,
480
+ splitMaxHours: args.splitMaxHours,
477
481
  });
478
482
  await updateActivePlanReviewMetadata(basePath, {
479
483
  researchPath: researchResult.reportPath,
@@ -540,6 +544,9 @@ async function handlePrepare(args, basePath) {
540
544
  projectName: projectName || 'My Project',
541
545
  planName: normalizedPlanName,
542
546
  basePath,
547
+ splitPlans: args.splitPlans,
548
+ splitMaxPhases: args.splitMaxPhases,
549
+ splitMaxHours: args.splitMaxHours,
543
550
  });
544
551
  await updateActivePlanReviewMetadata(basePath, {
545
552
  researchPath: researchResult.reportPath,
@@ -574,6 +581,9 @@ async function handleFlow(args, basePath) {
574
581
  docsPath: args.docsPath,
575
582
  projectName: args.projectName,
576
583
  planName: args.planName,
584
+ splitPlans: args.splitPlans,
585
+ splitMaxPhases: args.splitMaxPhases,
586
+ splitMaxHours: args.splitMaxHours,
577
587
  }, basePath);
578
588
  if (prepareResult.startsWith('Error:')) {
579
589
  return prepareResult;
@@ -669,6 +679,7 @@ async function handleFlow(args, basePath) {
669
679
  projectType: args.projectType,
670
680
  verify: true,
671
681
  verifyMode: args.verifyMode || 'quick',
682
+ skipVerify: args.skipVerify,
672
683
  }, basePath);
673
684
  lines.push('');
674
685
  lines.push('### 2) Craft');
@@ -679,25 +690,7 @@ async function handleFlow(args, basePath) {
679
690
  `Plan gate: PASS (${planGate.nonTrivial ? 'non-trivial' : 'simple'} scope).`,
680
691
  ];
681
692
  if (craftResult.includes('All tasks done')) {
682
- lines.push('');
683
- if (args.autoApprove) {
684
- const approveResult = await handleApprove({
685
- phaseId: resolvedPhaseId,
686
- projectType: args.projectType,
687
- verifyMode: 'full',
688
- }, basePath);
689
- lines.push('### 3) Approve');
690
- lines.push('');
691
- lines.push(approveResult);
692
- reviewLines.push(approveResult.includes('❌')
693
- ? `Auto-approve failed for ${resolvedPhaseId}.`
694
- : `Auto-approve completed for ${resolvedPhaseId}.`);
695
- }
696
- else {
697
- lines.push(`Next: \`weave command=approve phaseId="${resolvedPhaseId}"\``);
698
- lines.push('Tip: set `autoApprove=true` in flow to auto-run full verification + approve.');
699
- reviewLines.push(`All tasks done for ${resolvedPhaseId}; waiting for manual approve.`);
700
- }
693
+ reviewLines.push(`All tasks done for ${resolvedPhaseId}; phase finalization handled automatically in craft.`);
701
694
  }
702
695
  else {
703
696
  reviewLines.push(`Craft auto-loop paused for ${resolvedPhaseId}; rerun craft after implementation updates.`);
@@ -811,7 +804,7 @@ async function handleCraft(args, basePath) {
811
804
  lines.push(`Continue: \`weave command=craft phaseId="${resolvedPhaseId}" taskId="${next.id}"\``);
812
805
  }
813
806
  else {
814
- lines.push(`All tasks done for ${resolvedPhaseId}. Run: weave command=approve phaseId="${resolvedPhaseId}"`);
807
+ lines.push(`All tasks done for ${resolvedPhaseId}. Final goal check + auto finalize will run in this craft execution.`);
815
808
  }
816
809
  }
817
810
  const autoResult = await handleTask({
@@ -829,6 +822,68 @@ async function handleCraft(args, basePath) {
829
822
  lines.push('### Auto Loop');
830
823
  lines.push('');
831
824
  lines.push(autoResult);
825
+ const autoCompleted = autoResult.includes(`All tasks done for ${resolvedPhaseId}`);
826
+ if (autoCompleted) {
827
+ await manager.loadPlan();
828
+ const finalizedPhase = manager.getPhase(resolvedPhaseId);
829
+ if (finalizedPhase) {
830
+ const goalCheck = evaluatePhaseGoal(finalizedPhase);
831
+ lines.push('');
832
+ lines.push('### Final Goal Check');
833
+ lines.push('');
834
+ lines.push(`Phase goal (done_when): ${finalizedPhase.doneWhen || '(missing)'}`);
835
+ for (const check of goalCheck.checks) {
836
+ lines.push(`- ${check.passed ? 'PASS' : 'FAIL'}: ${check.label}`);
837
+ }
838
+ if (!goalCheck.passed) {
839
+ const metadataFailures = goalCheck.failedLabels.filter(label => label.includes('done_when') || label.includes('checklist'));
840
+ if (metadataFailures.length > 0) {
841
+ lines.push('');
842
+ lines.push('🛑 Final goal check failed due to phase metadata gaps.');
843
+ lines.push('Update the phase definition, then re-approve the plan before continuing.');
844
+ lines.push('Suggested path:');
845
+ lines.push('- `weave command=refine-plan`');
846
+ lines.push('- `weave command=approve-plan`');
847
+ lines.push(`- \`weave command=craft phaseId="${resolvedPhaseId}"\``);
848
+ return lines.join('\n');
849
+ }
850
+ const followupTask = await ensureGoalCheckFollowupTask(manager, resolvedPhaseId, goalCheck.failedLabels);
851
+ lines.push('');
852
+ lines.push('⚠️ Final goal check failed. Craft loop is re-entered with a follow-up task.');
853
+ if (followupTask) {
854
+ lines.push(`Created follow-up task: \`${followupTask.id}\` — ${followupTask.name}`);
855
+ }
856
+ const reentryResult = await handleTask({
857
+ phaseId: resolvedPhaseId,
858
+ taskAction: 'auto',
859
+ taskId: followupTask?.id,
860
+ verify: args.verify,
861
+ verifyMode: args.verifyMode,
862
+ commit: args.commit,
863
+ stageAll: args.stageAll,
864
+ commitMessage: args.commitMessage,
865
+ projectType,
866
+ }, basePath);
867
+ lines.push('');
868
+ lines.push('### Auto Loop (Re-entry)');
869
+ lines.push('');
870
+ lines.push(reentryResult);
871
+ }
872
+ else {
873
+ const finalizeResult = await handleApprove({
874
+ phaseId: resolvedPhaseId,
875
+ projectType,
876
+ skipVerify: args.skipVerify,
877
+ verifyMode: 'full',
878
+ source: 'craft',
879
+ }, basePath);
880
+ lines.push('');
881
+ lines.push('### Auto Finalize');
882
+ lines.push('');
883
+ lines.push(finalizeResult);
884
+ }
885
+ }
886
+ }
832
887
  // Return the plan + current auto-loop execution result.
833
888
  return lines.join('\n');
834
889
  }
@@ -1252,7 +1307,7 @@ async function handleTask(args, basePath) {
1252
1307
  case 'next': {
1253
1308
  const next = nextActionable();
1254
1309
  if (!next) {
1255
- return `All tasks are done for ${phaseId}. If ready, run: weave command=approve phaseId="${phaseId}"`;
1310
+ return `All tasks are done for ${phaseId}. Rerun \`weave craft\` to run final goal check + auto finalize.`;
1256
1311
  }
1257
1312
  return [
1258
1313
  `## Next Task for ${phaseId}`,
@@ -1368,7 +1423,7 @@ async function handleTask(args, basePath) {
1368
1423
  return finalize([
1369
1424
  result.body,
1370
1425
  next ? '' : '',
1371
- next ? `Next: \`${next.id}\` (${next.status}) — ${next.name}` : `All tasks done for ${phaseId}. Run: weave command=approve phaseId="${phaseId}"`,
1426
+ next ? `Next: \`${next.id}\` (${next.status}) — ${next.name}` : `All tasks done for ${phaseId}. Rerun \`weave craft\` to run final goal check + auto finalize.`,
1372
1427
  ].filter(Boolean).join('\n'), [
1373
1428
  `Task passed: ${task.id}`,
1374
1429
  next ? `Next actionable task: ${next.id}.` : `All tasks passed for ${phaseId}.`,
@@ -1392,7 +1447,7 @@ async function handleTask(args, basePath) {
1392
1447
  const next = (preferred && preferred.status !== 'passed') ? preferred : nextActionable();
1393
1448
  if (!next) {
1394
1449
  lines.push(`✅ All tasks done for ${phaseId}.`);
1395
- lines.push(`Next: \`weave command=approve phaseId="${phaseId}"\``);
1450
+ lines.push('Proceeding to final goal check in craft.');
1396
1451
  return finalize(lines.join('\n'), [
1397
1452
  `Auto loop completed: all tasks done for ${phaseId}.`,
1398
1453
  ]);
@@ -1555,8 +1610,35 @@ async function handleRepair(basePath) {
1555
1610
  const { results, summary } = await manager.repairPlans();
1556
1611
  return summary;
1557
1612
  }
1613
+ async function maybeAdvanceToNextShard(phaseManager, basePath) {
1614
+ const activePlan = await phaseManager.loadPlan();
1615
+ if (!activePlan)
1616
+ return null;
1617
+ if (activePlan.planRole !== 'shard' || !activePlan.nextPlanName)
1618
+ return null;
1619
+ const allPhasesCompleted = activePlan.phases.length > 0
1620
+ && activePlan.phases.every(phase => phase.status === 'completed');
1621
+ if (!allPhasesCompleted)
1622
+ return null;
1623
+ const allPlans = await phaseManager.loadAllPlans();
1624
+ const nextPlan = allPlans.find(plan => plan.planName === activePlan.nextPlanName);
1625
+ if (!nextPlan) {
1626
+ return `Warning: next shard plan \`${activePlan.nextPlanName}\` not found.`;
1627
+ }
1628
+ await phaseManager.savePlan(nextPlan);
1629
+ const shardLabel = (typeof nextPlan.shardIndex === 'number' && typeof nextPlan.shardTotal === 'number')
1630
+ ? `${nextPlan.shardIndex}/${nextPlan.shardTotal}`
1631
+ : 'next shard';
1632
+ return [
1633
+ `Auto-switched to shard plan: \`${nextPlan.planName}\` (${shardLabel}).`,
1634
+ 'Review/approve this shard before implementation:',
1635
+ '- `weave command=approve-plan`',
1636
+ '- `weave command=craft`',
1637
+ ].join('\n');
1638
+ }
1558
1639
  async function handleApprove(args, basePath) {
1559
- const { phaseId: requestedPhaseId, projectType, skipVerify, verifyMode, commit, stageAll, commitMessage } = args;
1640
+ const { phaseId: requestedPhaseId, projectType, skipVerify, verifyMode, commit, stageAll, commitMessage, source = 'command' } = args;
1641
+ const invokedByCraft = source === 'craft';
1560
1642
  const phaseManager = getPhaseManager(basePath);
1561
1643
  const loadedPlan = await phaseManager.loadPlan();
1562
1644
  const resolvedPhaseId = requestedPhaseId
@@ -1564,7 +1646,7 @@ async function handleApprove(args, basePath) {
1564
1646
  || loadedPlan?.phases.find(p => p.status === 'in_progress')?.id
1565
1647
  || loadedPlan?.phases.find(p => p.status !== 'completed')?.id;
1566
1648
  if (!resolvedPhaseId) {
1567
- return 'Error: No phase found to approve. Run `weave craft` first.';
1649
+ return 'Error: No phase found to finalize. Run `weave craft` first.';
1568
1650
  }
1569
1651
  const finalizeApprove = async (message, reviewLines) => {
1570
1652
  await syncWorkflowArtifacts(basePath, phaseManager, {
@@ -1588,27 +1670,32 @@ async function handleApprove(args, basePath) {
1588
1670
  '',
1589
1671
  `❌ Verification failed at: ${verification.failedAt || 'unknown'}`,
1590
1672
  '',
1591
- 'Fix the failures and re-run approve.',
1673
+ invokedByCraft
1674
+ ? 'Fix the failures and rerun `weave craft`.'
1675
+ : 'Fix the failures and re-run `weave craft`.',
1592
1676
  'You can also run: `weave command=verify`',
1593
1677
  ].join('\n'), [
1594
- `Approve blocked: verification failed for ${resolvedPhaseId}.`,
1678
+ `Finalization blocked: verification failed for ${resolvedPhaseId}.`,
1595
1679
  ]);
1596
1680
  }
1597
- // If no commands detected, allow approve but make it explicit.
1681
+ // If no commands detected, allow finalization but make it explicit.
1598
1682
  if (verification.results.length === 0) {
1599
1683
  await phaseManager.markAllTasksPassed(resolvedPhaseId);
1600
1684
  const result = await handleUserResponse(resolvedPhaseId, 'approve', undefined, basePath);
1685
+ const shardSwitch = await maybeAdvanceToNextShard(phaseManager, basePath);
1601
1686
  return finalizeApprove([
1602
1687
  report,
1603
1688
  '',
1604
1689
  '> No verification commands detected; approved without automated checks.',
1605
1690
  '',
1606
1691
  result.message,
1607
- ].join('\n'), [
1692
+ shardSwitch || '',
1693
+ ].filter(Boolean).join('\n'), [
1608
1694
  `Approved ${resolvedPhaseId} without automated verification commands.`,
1609
- ]);
1695
+ shardSwitch ? 'Advanced to next shard.' : '',
1696
+ ].filter(Boolean));
1610
1697
  }
1611
- // Optional: commit on approve
1698
+ // Optional: commit during finalization
1612
1699
  if (commit) {
1613
1700
  try {
1614
1701
  await ensureGitRepo(basePath);
@@ -1622,13 +1709,13 @@ async function handleApprove(args, basePath) {
1622
1709
  report,
1623
1710
  '',
1624
1711
  '❌ No staged changes to commit.',
1625
- 'Stage files first, or run approve with `stageAll=true`.',
1712
+ 'Stage files first, or rerun with `stageAll=true`.',
1626
1713
  'Example:',
1627
1714
  '```txt',
1628
- `weave command=approve phaseId="${resolvedPhaseId}" commit=true stageAll=true`,
1715
+ `weave command=craft phaseId="${resolvedPhaseId}" commit=true stageAll=true`,
1629
1716
  '```',
1630
1717
  ].join('\n'), [
1631
- `Approve commit blocked: no staged changes for ${resolvedPhaseId}.`,
1718
+ `Finalization commit blocked: no staged changes for ${resolvedPhaseId}.`,
1632
1719
  ]);
1633
1720
  }
1634
1721
  }
@@ -1641,7 +1728,7 @@ async function handleApprove(args, basePath) {
1641
1728
  '',
1642
1729
  formatSecretScanReport(findings),
1643
1730
  ].join('\n'), [
1644
- `Approve commit blocked by secret scan in ${resolvedPhaseId}.`,
1731
+ `Finalization commit blocked by secret scan in ${resolvedPhaseId}.`,
1645
1732
  ]);
1646
1733
  }
1647
1734
  const secretWarning = findings.length > 0
@@ -1657,6 +1744,7 @@ async function handleApprove(args, basePath) {
1657
1744
  const commitOutput = [commitRes.stdout, commitRes.stderr].filter(Boolean).join('\n').trim();
1658
1745
  await phaseManager.markAllTasksPassed(resolvedPhaseId);
1659
1746
  const result = await handleUserResponse(resolvedPhaseId, 'approve', undefined, basePath);
1747
+ const shardSwitch = await maybeAdvanceToNextShard(phaseManager, basePath);
1660
1748
  return finalizeApprove([
1661
1749
  report,
1662
1750
  '',
@@ -1665,9 +1753,11 @@ async function handleApprove(args, basePath) {
1665
1753
  commitOutput ? ['```', commitOutput, '```'].join('\n') : '',
1666
1754
  '',
1667
1755
  result.message,
1756
+ shardSwitch || '',
1668
1757
  ].filter(Boolean).join('\n'), [
1669
- `Approved ${resolvedPhaseId} with commit.`,
1670
- ]);
1758
+ `Auto-finalized ${resolvedPhaseId} with commit.`,
1759
+ shardSwitch ? 'Advanced to next shard.' : '',
1760
+ ].filter(Boolean));
1671
1761
  }
1672
1762
  catch (e) {
1673
1763
  const msg = e instanceof Error ? e.message : String(e);
@@ -1676,7 +1766,7 @@ async function handleApprove(args, basePath) {
1676
1766
  '',
1677
1767
  `❌ Commit failed: ${msg}`,
1678
1768
  ].join('\n'), [
1679
- `Approve commit failed for ${resolvedPhaseId}: ${sanitizeLessonText(msg)}`,
1769
+ `Finalization commit failed for ${resolvedPhaseId}: ${sanitizeLessonText(msg)}`,
1680
1770
  ]);
1681
1771
  }
1682
1772
  }
@@ -1685,18 +1775,26 @@ async function handleApprove(args, basePath) {
1685
1775
  await phaseManager.markAllTasksPassed(resolvedPhaseId);
1686
1776
  }
1687
1777
  const result = await handleUserResponse(resolvedPhaseId, 'approve', undefined, basePath);
1778
+ const shardSwitch = await maybeAdvanceToNextShard(phaseManager, basePath);
1688
1779
  return finalizeApprove([
1689
1780
  report,
1690
1781
  '',
1691
1782
  result.message,
1692
- ].join('\n'), [
1693
- `Approved ${resolvedPhaseId} after verification pass.`,
1694
- ]);
1783
+ shardSwitch || '',
1784
+ ].filter(Boolean).join('\n'), [
1785
+ `Auto-finalized ${resolvedPhaseId} after verification pass.`,
1786
+ shardSwitch ? 'Advanced to next shard.' : '',
1787
+ ].filter(Boolean));
1695
1788
  }
1696
1789
  const result = await handleUserResponse(resolvedPhaseId, 'approve', undefined, basePath);
1697
- return finalizeApprove(result.message, [
1698
- `Approved ${resolvedPhaseId} with skipVerify=true.`,
1699
- ]);
1790
+ const shardSwitch = await maybeAdvanceToNextShard(phaseManager, basePath);
1791
+ return finalizeApprove([
1792
+ result.message,
1793
+ shardSwitch || '',
1794
+ ].filter(Boolean).join('\n'), [
1795
+ `Auto-finalized ${resolvedPhaseId} with skipVerify=true.`,
1796
+ shardSwitch ? 'Advanced to next shard.' : '',
1797
+ ].filter(Boolean));
1700
1798
  }
1701
1799
  function getHelpMessage() {
1702
1800
  return `## Weave Workflow Help (Maskweaver v${VERSION})
@@ -1717,18 +1815,17 @@ To check installed version:
1717
1815
 
1718
1816
  | Command | Description |
1719
1817
  |---------|-------------|
1720
- | \`weave research [docs]\` | Deep-read docs and write persistent research.md |
1818
+ | \`weave research [docs]\` | Deep-read docs + workspace context and write persistent research.md |
1721
1819
  | \`weave spec [docs]\` | Generate baseline spec (requirements + AC) |
1722
- | \`weave prepare [docs]\` | Create spec + phase plan (vNext happy path) |
1820
+ | \`weave prepare [docs]\` | Create spec + phase plan (auto-splits oversized plans) |
1723
1821
  | \`weave refine-plan\` | Apply structured plan-note directives to active plan |
1724
1822
  | \`weave approve-plan\` | Mark plan approved before implementation |
1725
- | \`weave flow [docs]\` | One-command path (prepare -> approve-plan gate -> craft auto-loop) |
1726
- | \`weave design [docs]\` | Analyze requirements and create phase plan |
1727
- | \`weave craft [id]\` | Execute a phase with automatic task loop (auto-select next if omitted) |
1823
+ | \`weave flow [docs]\` | One-command path (prepare -> approve-plan gate -> craft auto-loop + auto finalize) |
1824
+ | \`weave design [docs]\` | Analyze requirements and create phase plan (auto-splits oversized plans) |
1825
+ | \`weave craft [id]\` | Execute a phase with auto task loop + goal check + auto finalize |
1728
1826
  | \`weave status\` | View progress |
1729
1827
  | \`weave worktree ...\` | Manage git worktrees for parallel work |
1730
1828
  | \`weave verify\` | Run build/test verification for current worktree |
1731
- | \`weave approve [id]\` | Mark phase complete (auto phase + verification) |
1732
1829
  | \`weave repair\` | Scan and auto-repair corrupted plan YAML files |
1733
1830
  | \`weave troubleshoot [error]\` | Search global knowledge for solutions |
1734
1831
  | \`weave record [solution]\` | Record a new solution |
@@ -1748,8 +1845,7 @@ weave prepare docs/ # Research + spec + pla
1748
1845
  weave refine-plan # Apply plan-notes directives (optional)
1749
1846
  weave approve-plan # Explicit approval gate
1750
1847
  weave flow # Continue active plan with craft auto-loop
1751
- weave flow autoApprove=true # Auto-run full verify + approve when tasks finish
1752
- weave approve # Auto-select current phase + full verify
1848
+ weave craft # Resume from current phase/task and auto-finalize on completion
1753
1849
  \`\`\`
1754
1850
  `;
1755
1851
  }
@@ -1788,6 +1884,70 @@ function evaluatePlanGate(phase) {
1788
1884
  failedLabels,
1789
1885
  };
1790
1886
  }
1887
+ function evaluatePhaseGoal(phase) {
1888
+ const tasks = phase.tasks || [];
1889
+ const doneWhen = (phase.doneWhen || '').trim();
1890
+ const checklist = phase.checklist || [];
1891
+ const taskText = tasks
1892
+ .map(task => `${task.name} ${task.testCase || ''}`.toLowerCase())
1893
+ .join('\n');
1894
+ const doneWhenTokens = (doneWhen.toLowerCase().match(/[a-z0-9가-힣]{3,}/g) || [])
1895
+ .slice(0, 8);
1896
+ const doneWhenTrace = doneWhenTokens.length === 0
1897
+ ? doneWhen.length > 0
1898
+ : doneWhenTokens.some(token => taskText.includes(token));
1899
+ const checks = [
1900
+ {
1901
+ label: 'Phase done_when is defined',
1902
+ passed: doneWhen.length > 0,
1903
+ },
1904
+ {
1905
+ label: 'All phase tasks are passed',
1906
+ passed: tasks.length > 0 && tasks.every(task => task.status === 'passed'),
1907
+ },
1908
+ {
1909
+ label: 'Phase checklist is defined',
1910
+ passed: checklist.length > 0,
1911
+ },
1912
+ {
1913
+ label: 'Task descriptions are traceable to done_when',
1914
+ passed: doneWhenTrace,
1915
+ },
1916
+ ];
1917
+ const failedLabels = checks.filter(check => !check.passed).map(check => check.label);
1918
+ return {
1919
+ passed: failedLabels.length === 0,
1920
+ checks,
1921
+ failedLabels,
1922
+ };
1923
+ }
1924
+ async function ensureGoalCheckFollowupTask(manager, phaseId, failedLabels) {
1925
+ const plan = await manager.loadPlan();
1926
+ if (!plan)
1927
+ return null;
1928
+ const phase = plan.phases.find(p => p.id === phaseId);
1929
+ if (!phase)
1930
+ return null;
1931
+ const existing = phase.tasks.find(task => task.name.includes('[goal-check]') &&
1932
+ (task.status === 'pending' || task.status === 'in_progress'));
1933
+ if (existing) {
1934
+ return { id: existing.id, name: existing.name };
1935
+ }
1936
+ const taskId = `${phaseId}-T${getNextTaskNumber(phase.tasks)}`;
1937
+ const taskName = `[goal-check] ${phase.name} 목표 정합성 보강`;
1938
+ const testCase = failedLabels.length > 0
1939
+ ? `final goal checks pass: ${failedLabels.join('; ')}`
1940
+ : 'phase done_when and checklist are satisfied';
1941
+ await manager.addTasks(phaseId, [
1942
+ {
1943
+ id: taskId,
1944
+ name: taskName,
1945
+ testCase,
1946
+ maxRetries: 2,
1947
+ },
1948
+ ]);
1949
+ return { id: taskId, name: taskName };
1950
+ }
1791
1951
  async function autoReplanFailedTask(manager, phaseId, taskId, options) {
1792
1952
  const plan = await manager.loadPlan();
1793
1953
  if (!plan)