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.
- package/README.ko.md +9 -12
- package/README.md +9 -12
- package/assets/commands/weave-craft.md +3 -3
- package/assets/commands/weave-design.md +5 -2
- package/assets/commands/weave-flow.md +3 -13
- package/assets/commands/weave-help.md +7 -11
- package/assets/commands/weave-prepare.md +2 -2
- package/assets/commands/weave-research.md +4 -2
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +2 -1
- package/dist/plugin/index.js.map +1 -1
- package/dist/plugin/tools/slashcommand.d.ts.map +1 -1
- package/dist/plugin/tools/slashcommand.js +12 -11
- package/dist/plugin/tools/slashcommand.js.map +1 -1
- package/dist/plugin/tools/weave.d.ts +7 -4
- package/dist/plugin/tools/weave.d.ts.map +1 -1
- package/dist/plugin/tools/weave.js +226 -66
- package/dist/plugin/tools/weave.js.map +1 -1
- package/dist/weave/phase-manager.d.ts +5 -0
- package/dist/weave/phase-manager.d.ts.map +1 -1
- package/dist/weave/phase-manager.js +25 -0
- package/dist/weave/phase-manager.js.map +1 -1
- package/dist/weave/stages/execute.js +2 -2
- package/dist/weave/stages/execute.js.map +1 -1
- package/dist/weave/stages/plan.d.ts +8 -0
- package/dist/weave/stages/plan.d.ts.map +1 -1
- package/dist/weave/stages/plan.js +161 -2
- package/dist/weave/stages/plan.js.map +1 -1
- package/dist/weave/stages/research.d.ts.map +1 -1
- package/dist/weave/stages/research.js +431 -4
- package/dist/weave/stages/research.js.map +1 -1
- package/dist/weave/types.d.ts +6 -0
- package/dist/weave/types.d.ts.map +1 -1
- package/package.json +1 -1
- 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 (
|
|
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', '
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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}.
|
|
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}.
|
|
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}.
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
-
`
|
|
1678
|
+
`Finalization blocked: verification failed for ${resolvedPhaseId}.`,
|
|
1595
1679
|
]);
|
|
1596
1680
|
}
|
|
1597
|
-
// If no commands detected, allow
|
|
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
|
-
|
|
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
|
|
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
|
|
1712
|
+
'Stage files first, or rerun with `stageAll=true`.',
|
|
1626
1713
|
'Example:',
|
|
1627
1714
|
'```txt',
|
|
1628
|
-
`weave command=
|
|
1715
|
+
`weave command=craft phaseId="${resolvedPhaseId}" commit=true stageAll=true`,
|
|
1629
1716
|
'```',
|
|
1630
1717
|
].join('\n'), [
|
|
1631
|
-
`
|
|
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
|
-
`
|
|
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
|
-
`
|
|
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
|
-
`
|
|
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
|
-
|
|
1693
|
-
|
|
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
|
-
|
|
1698
|
-
|
|
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 (
|
|
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
|
|
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
|
|
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)
|