gsd-lite 0.5.3 → 0.5.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.
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 +0 -0
  6. package/agents/executor.md +0 -0
  7. package/agents/researcher.md +0 -0
  8. package/agents/reviewer.md +0 -0
  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 +40 -2
  33. package/src/server.js +9 -3
  34. package/src/tools/orchestrator.js +0 -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.4",
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.4",
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
File without changes
File without changes
File without changes
File without changes
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.4",
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') {
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'],
File without changes
@@ -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