gsd-lite 0.5.3 → 0.5.5

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 (44) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.mcp.json +0 -0
  4. package/README.md +0 -0
  5. package/agents/debugger.md +4 -2
  6. package/agents/executor.md +0 -0
  7. package/agents/researcher.md +0 -0
  8. package/agents/reviewer.md +1 -1
  9. package/commands/doctor.md +0 -0
  10. package/commands/prd.md +0 -0
  11. package/commands/resume.md +0 -0
  12. package/commands/start.md +0 -0
  13. package/commands/status.md +0 -0
  14. package/commands/stop.md +0 -0
  15. package/hooks/context-monitor.js +0 -0
  16. package/hooks/gsd-auto-update.cjs +0 -0
  17. package/hooks/gsd-context-monitor.cjs +0 -0
  18. package/hooks/gsd-session-init.cjs +0 -0
  19. package/hooks/gsd-statusline.cjs +1 -1
  20. package/hooks/hooks.json +0 -0
  21. package/install.js +0 -0
  22. package/launcher.js +0 -0
  23. package/package.json +1 -1
  24. package/references/anti-rationalization-full.md +0 -0
  25. package/references/evidence-spec.md +0 -0
  26. package/references/execution-loop.md +0 -0
  27. package/references/git-worktrees.md +0 -0
  28. package/references/questioning.md +0 -0
  29. package/references/review-classification.md +0 -0
  30. package/references/state-diagram.md +0 -0
  31. package/references/testing-patterns.md +0 -0
  32. package/src/schema.js +41 -3
  33. package/src/server.js +9 -3
  34. package/src/tools/orchestrator.js +7 -0
  35. package/src/tools/state.js +7 -4
  36. package/src/tools/verify.js +0 -0
  37. package/src/utils.js +0 -0
  38. package/uninstall.js +0 -0
  39. package/workflows/debugging.md +0 -0
  40. package/workflows/deviation-rules.md +0 -0
  41. package/workflows/execution-flow.md +0 -0
  42. package/workflows/research.md +0 -0
  43. package/workflows/review-cycle.md +0 -0
  44. package/workflows/tdd-cycle.md +0 -0
@@ -13,7 +13,7 @@
13
13
  "name": "gsd",
14
14
  "source": "./",
15
15
  "description": "AI orchestration tool — GSD management shell + Superpowers quality core. 5 commands, 4 agents, 5 workflows, MCP server, context monitoring.",
16
- "version": "0.5.3",
16
+ "version": "0.5.5",
17
17
  "keywords": [
18
18
  "orchestration",
19
19
  "mcp",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gsd",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "description": "AI orchestration tool for Claude Code — GSD management shell + Superpowers quality core",
5
5
  "author": {
6
6
  "name": "sdsrss",
package/.mcp.json CHANGED
File without changes
package/README.md CHANGED
File without changes
@@ -67,9 +67,11 @@ Phase 4 修复方向建议:
67
67
  "task_id": "2.3",
68
68
  "outcome": "root_cause_found | fix_suggested | failed",
69
69
  "root_cause": "Description of the identified root cause",
70
- "evidence": ["ev:repro:error-xyz", "ev:trace:data-flow"],
70
+ "evidence": [
71
+ { "id": "ev:repro:error-xyz", "scope": "task:2.3", "command": "npm test", "exit_code": 1, "stdout": "...", "stderr": "...", "timestamp": "ISO8601" }
72
+ ],
71
73
  "hypothesis_tested": [
72
- { "hypothesis": "X causes Y", "result": "confirmed | rejected", "evidence": "..." }
74
+ { "hypothesis": "X causes Y", "result": "confirmed | rejected", "evidence": "non-empty string (required)" }
73
75
  ],
74
76
  "fix_direction": "Suggested fix approach for executor",
75
77
  "fix_attempts": 0,
File without changes
File without changes
@@ -93,7 +93,7 @@ Minor = 建议修复 (命名/风格)
93
93
  {
94
94
  "scope": "task | phase",
95
95
  "scope_id": "2.3 (task scope: string ID) | 2 (phase scope: number ID)",
96
- "review_level": "L2 | L1-batch",
96
+ "review_level": "L2 | L1-batch | L1",
97
97
  "spec_passed": true,
98
98
  "quality_passed": false,
99
99
  "critical_issues": [
File without changes
package/commands/prd.md CHANGED
File without changes
File without changes
package/commands/start.md CHANGED
File without changes
File without changes
package/commands/stop.md CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -74,7 +74,7 @@ process.stdin.on('end', () => {
74
74
  let needsWrite = true;
75
75
  try {
76
76
  const existing = JSON.parse(fs.readFileSync(bridgePath, 'utf8'));
77
- if (existing.remaining_percentage === remaining) needsWrite = false;
77
+ if (existing.remaining_percentage === remaining && existing.has_gsd === hasGsd) needsWrite = false;
78
78
  } catch { /* no existing file */ }
79
79
  if (needsWrite) {
80
80
  const tmpBridge = bridgePath + '.tmp';
package/hooks/hooks.json CHANGED
File without changes
package/install.js CHANGED
File without changes
package/launcher.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gsd-lite",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "description": "AI orchestration tool for Claude Code — GSD management shell + Superpowers quality core",
5
5
  "type": "module",
6
6
  "bin": {
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/src/schema.js CHANGED
@@ -17,6 +17,23 @@ export const WORKFLOW_MODES = [
17
17
  'failed',
18
18
  ];
19
19
 
20
+ // Valid workflow_mode transitions — unlisted transitions are rejected by validateStateUpdate.
21
+ // Terminal states (completed, failed) are guarded separately by the FROM-terminal check in state-update.
22
+ export const WORKFLOW_TRANSITIONS = {
23
+ planning: ['executing_task', 'paused_by_user'],
24
+ executing_task: ['planning', 'reviewing_task', 'reviewing_phase', 'awaiting_user', 'awaiting_clear', 'paused_by_user', 'reconcile_workspace', 'replan_required', 'research_refresh_needed', 'failed'],
25
+ reviewing_task: ['executing_task', 'reviewing_phase', 'awaiting_user', 'awaiting_clear', 'paused_by_user', 'failed'],
26
+ reviewing_phase: ['executing_task', 'awaiting_user', 'awaiting_clear', 'paused_by_user', 'completed', 'failed'],
27
+ awaiting_user: ['executing_task', 'reviewing_task', 'reviewing_phase', 'paused_by_user', 'awaiting_clear'],
28
+ awaiting_clear: ['executing_task', 'paused_by_user'],
29
+ paused_by_user: ['executing_task', 'awaiting_user', 'awaiting_clear', 'reconcile_workspace', 'replan_required', 'research_refresh_needed', 'reviewing_task', 'reviewing_phase'],
30
+ reconcile_workspace: ['executing_task', 'paused_by_user'],
31
+ replan_required: ['executing_task', 'paused_by_user'],
32
+ research_refresh_needed: ['executing_task', 'reviewing_task', 'reviewing_phase', 'paused_by_user'],
33
+ completed: [], // terminal — guarded by FROM-terminal check
34
+ failed: [], // terminal — guarded by FROM-terminal check
35
+ };
36
+
20
37
  export const TASK_LIFECYCLE = {
21
38
  pending: ['running', 'blocked'],
22
39
  running: ['checkpointed', 'blocked', 'failed', 'accepted'], // accepted: auto-accept for L0/review_required=false (atomic, skips checkpointed)
@@ -154,11 +171,28 @@ export function validateStateUpdate(state, updates) {
154
171
 
155
172
  for (const key of Object.keys(updates)) {
156
173
  switch (key) {
157
- case 'workflow_mode':
174
+ case 'workflow_mode': {
158
175
  if (!WORKFLOW_MODES.includes(updates.workflow_mode)) {
159
176
  errors.push(`Invalid workflow_mode: ${updates.workflow_mode}`);
177
+ break;
178
+ }
179
+ // Transition whitelist — reject unlisted transitions
180
+ const currentMode = state.workflow_mode;
181
+ if (currentMode && updates.workflow_mode !== currentMode) {
182
+ const allowed = WORKFLOW_TRANSITIONS[currentMode];
183
+ if (allowed && !allowed.includes(updates.workflow_mode)) {
184
+ errors.push(`Invalid workflow_mode transition: '${currentMode}' → '${updates.workflow_mode}' (allowed: ${allowed.join(', ') || 'none (terminal state)'})`);
185
+ }
186
+ }
187
+ // Guard: 'completed' requires all phases accepted
188
+ if (updates.workflow_mode === 'completed' && Array.isArray(state.phases)) {
189
+ const unfinished = state.phases.filter(p => p.lifecycle !== 'accepted');
190
+ if (unfinished.length > 0) {
191
+ errors.push(`Cannot set workflow_mode to 'completed': ${unfinished.length} phase(s) not accepted (${unfinished.map(p => `${p.id}:${p.lifecycle}`).join(', ')})`);
192
+ }
160
193
  }
161
194
  break;
195
+ }
162
196
  case 'current_phase':
163
197
  if (!Number.isFinite(updates.current_phase)) {
164
198
  errors.push('current_phase must be a finite number');
@@ -383,8 +417,12 @@ export function validateState(state) {
383
417
  }
384
418
  }
385
419
  }
386
- // P2-9: workflow_mode consistency — completed project must not have active/running tasks
420
+ // P2-9: workflow_mode consistency — completed project requires all phases accepted
387
421
  if (state.workflow_mode === 'completed' && Array.isArray(state.phases)) {
422
+ const unfinishedPhases = state.phases.filter(p => p.lifecycle !== 'accepted');
423
+ if (unfinishedPhases.length > 0) {
424
+ errors.push(`Completed project has ${unfinishedPhases.length} unfinished phase(s): ${unfinishedPhases.map(p => `${p.id}:${p.lifecycle}`).join(', ')}`);
425
+ }
388
426
  for (const phase of state.phases) {
389
427
  for (const task of (phase.todo || [])) {
390
428
  if (task.lifecycle === 'running') {
@@ -560,7 +598,7 @@ export function validateReviewerResult(r) {
560
598
  if (!(typeof r.scope_id === 'string' || typeof r.scope_id === 'number') || r.scope_id === '') {
561
599
  errors.push('missing scope_id');
562
600
  }
563
- if (!['L2', 'L1-batch'].includes(r.review_level)) errors.push('invalid review_level');
601
+ if (!['L2', 'L1-batch', 'L1'].includes(r.review_level)) errors.push('invalid review_level (expected L2, L1-batch, or L1)');
564
602
  if (typeof r.spec_passed !== 'boolean') errors.push('spec_passed must be boolean');
565
603
  if (typeof r.quality_passed !== 'boolean') errors.push('quality_passed must be boolean');
566
604
  if (!Array.isArray(r.critical_issues)) errors.push('critical_issues must be array');
package/src/server.js CHANGED
@@ -90,7 +90,7 @@ const TOOLS = [
90
90
  properties: {
91
91
  updates: {
92
92
  type: 'object',
93
- description: 'Key-value pairs of canonical fields to update',
93
+ description: 'Key-value pairs of canonical fields: workflow_mode, current_phase, current_task, current_review, git_head, plan_version, schema_version, total_phases, project, decisions, context, evidence, research',
94
94
  },
95
95
  },
96
96
  required: ['updates'],
@@ -105,7 +105,13 @@ const TOOLS = [
105
105
  phase_id: { type: 'number', description: 'Phase number to complete' },
106
106
  verification: {
107
107
  type: 'object',
108
- description: 'Optional precomputed verification: {lint: {exit_code: number}, typecheck: {exit_code: number}, test: {exit_code: number}} — all three keys required, exit_code 0 = passed',
108
+ description: 'Optional precomputed verification — all three keys required, exit_code 0 = passed',
109
+ properties: {
110
+ lint: { type: 'object', properties: { exit_code: { type: 'number' } }, required: ['exit_code'] },
111
+ typecheck: { type: 'object', properties: { exit_code: { type: 'number' } }, required: ['exit_code'] },
112
+ test: { type: 'object', properties: { exit_code: { type: 'number' } }, required: ['exit_code'] },
113
+ },
114
+ required: ['lint', 'typecheck', 'test'],
109
115
  },
110
116
  run_verify: {
111
117
  type: 'boolean',
@@ -135,7 +141,7 @@ const TOOLS = [
135
141
  properties: {
136
142
  result: {
137
143
  type: 'object',
138
- description: 'Executor result: {task_id: string, outcome: "checkpointed"|"blocked"|"failed", summary: string, checkpoint_commit: string|null, files_changed: string[], decisions: string[], blockers: object[], contract_changed: boolean, evidence: object[]}',
144
+ description: 'Executor result: {task_id: string, outcome: "checkpointed"|"blocked"|"failed", summary: string, checkpoint_commit: string|null, files_changed: string[], decisions: [{id, summary, rationale}], blockers: [{description}], contract_changed: boolean, evidence: string[]}',
139
145
  },
140
146
  },
141
147
  required: ['result'],
@@ -450,6 +450,13 @@ async function resumeExecutingTask(state, basePath) {
450
450
 
451
451
  if (selection.task) {
452
452
  const task = selection.task;
453
+ // Two-step transition for needs_revalidation: must go through pending first
454
+ if (task.lifecycle === 'needs_revalidation') {
455
+ const resetError = await persist(basePath, {
456
+ phases: [{ id: phase.id, todo: [{ id: task.id, lifecycle: 'pending' }] }],
457
+ });
458
+ if (resetError) return resetError;
459
+ }
453
460
  const persistError = await persist(basePath, {
454
461
  workflow_mode: 'executing_task',
455
462
  current_task: task.id,
@@ -341,10 +341,13 @@ function verificationPassed(verification) {
341
341
 
342
342
  function verificationSummary(verification) {
343
343
  if (!verification || typeof verification !== 'object') return 'no verification details';
344
- return ['lint', 'typecheck', 'test']
345
- .filter((key) => verification[key])
346
- .map((key) => `${key}:${verification[key].exit_code}`)
347
- .join(', ');
344
+ const parts = ['lint', 'typecheck', 'test'].map((key) => {
345
+ const v = verification[key];
346
+ if (!v) return `${key}:missing`;
347
+ if (typeof v !== 'object' || !('exit_code' in v)) return `${key}:invalid-format (expected {exit_code: number})`;
348
+ return `${key}:${v.exit_code === 0 ? 'pass' : `fail(${v.exit_code})`}`;
349
+ });
350
+ return parts.join(', ');
348
351
  }
349
352
 
350
353
  export async function phaseComplete({
File without changes
package/src/utils.js CHANGED
File without changes
package/uninstall.js CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes