gswd 1.0.0 → 1.1.0

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 (86) hide show
  1. package/bin/gswd-tools.cjs +228 -0
  2. package/bin/install.js +8 -0
  3. package/commands/gswd/imagine.md +7 -1
  4. package/commands/gswd/start.md +507 -32
  5. package/dist/lib/audit.d.ts +205 -0
  6. package/dist/lib/audit.js +805 -0
  7. package/dist/lib/bootstrap.d.ts +103 -0
  8. package/dist/lib/bootstrap.js +563 -0
  9. package/dist/lib/compile.d.ts +239 -0
  10. package/dist/lib/compile.js +1152 -0
  11. package/dist/lib/config.d.ts +49 -0
  12. package/dist/lib/config.js +150 -0
  13. package/dist/lib/imagine-agents.d.ts +54 -0
  14. package/dist/lib/imagine-agents.js +185 -0
  15. package/dist/lib/imagine-gate.d.ts +47 -0
  16. package/dist/lib/imagine-gate.js +131 -0
  17. package/dist/lib/imagine-input.d.ts +46 -0
  18. package/dist/lib/imagine-input.js +233 -0
  19. package/dist/lib/imagine-synthesis.d.ts +90 -0
  20. package/dist/lib/imagine-synthesis.js +453 -0
  21. package/dist/lib/imagine.d.ts +56 -0
  22. package/dist/lib/imagine.js +413 -0
  23. package/dist/lib/intake.d.ts +27 -0
  24. package/dist/lib/intake.js +82 -0
  25. package/dist/lib/parse.d.ts +59 -0
  26. package/dist/lib/parse.js +171 -0
  27. package/dist/lib/render.d.ts +309 -0
  28. package/dist/lib/render.js +624 -0
  29. package/dist/lib/specify-agents.d.ts +120 -0
  30. package/dist/lib/specify-agents.js +269 -0
  31. package/dist/lib/specify-journeys.d.ts +124 -0
  32. package/dist/lib/specify-journeys.js +279 -0
  33. package/dist/lib/specify-nfr.d.ts +45 -0
  34. package/dist/lib/specify-nfr.js +159 -0
  35. package/dist/lib/specify-roles.d.ts +46 -0
  36. package/dist/lib/specify-roles.js +88 -0
  37. package/dist/lib/specify.d.ts +70 -0
  38. package/dist/lib/specify.js +676 -0
  39. package/dist/lib/state.d.ts +140 -0
  40. package/dist/lib/state.js +340 -0
  41. package/dist/tests/audit.test.d.ts +4 -0
  42. package/dist/tests/audit.test.js +1579 -0
  43. package/dist/tests/bootstrap.test.d.ts +5 -0
  44. package/dist/tests/bootstrap.test.js +611 -0
  45. package/dist/tests/compile.test.d.ts +4 -0
  46. package/dist/tests/compile.test.js +862 -0
  47. package/dist/tests/config.test.d.ts +4 -0
  48. package/dist/tests/config.test.js +191 -0
  49. package/dist/tests/imagine-agents.test.d.ts +6 -0
  50. package/dist/tests/imagine-agents.test.js +179 -0
  51. package/dist/tests/imagine-gate.test.d.ts +6 -0
  52. package/dist/tests/imagine-gate.test.js +264 -0
  53. package/dist/tests/imagine-input.test.d.ts +6 -0
  54. package/dist/tests/imagine-input.test.js +283 -0
  55. package/dist/tests/imagine-synthesis.test.d.ts +7 -0
  56. package/dist/tests/imagine-synthesis.test.js +380 -0
  57. package/dist/tests/imagine.test.d.ts +8 -0
  58. package/dist/tests/imagine.test.js +406 -0
  59. package/dist/tests/parse.test.d.ts +4 -0
  60. package/dist/tests/parse.test.js +285 -0
  61. package/dist/tests/render.test.d.ts +4 -0
  62. package/dist/tests/render.test.js +236 -0
  63. package/dist/tests/specify-agents.test.d.ts +4 -0
  64. package/dist/tests/specify-agents.test.js +352 -0
  65. package/dist/tests/specify-journeys.test.d.ts +5 -0
  66. package/dist/tests/specify-journeys.test.js +440 -0
  67. package/dist/tests/specify-nfr.test.d.ts +4 -0
  68. package/dist/tests/specify-nfr.test.js +205 -0
  69. package/dist/tests/specify-roles.test.d.ts +4 -0
  70. package/dist/tests/specify-roles.test.js +136 -0
  71. package/dist/tests/specify.test.d.ts +9 -0
  72. package/dist/tests/specify.test.js +544 -0
  73. package/dist/tests/state.test.d.ts +4 -0
  74. package/dist/tests/state.test.js +316 -0
  75. package/lib/bootstrap.ts +37 -11
  76. package/lib/compile.ts +426 -4
  77. package/lib/imagine-agents.ts +53 -7
  78. package/lib/imagine-synthesis.ts +170 -6
  79. package/lib/imagine.ts +59 -5
  80. package/lib/intake.ts +60 -0
  81. package/lib/parse.ts +2 -1
  82. package/lib/render.ts +566 -5
  83. package/lib/specify-agents.ts +25 -3
  84. package/lib/state.ts +115 -0
  85. package/package.json +4 -2
  86. package/templates/gswd/DECISIONS.template.md +3 -0
@@ -11,6 +11,9 @@
11
11
  * state read Read STATE.json
12
12
  * state update-stage <stage> <status> Update stage status
13
13
  * state checkpoint <workflow> <id> Write checkpoint
14
+ * state cascade-invalidate <stage> Cascade-invalidate downstream stages
15
+ * state set-manual-mode <true|false> Enable/disable manual mode
16
+ * state update-manual-checkpoint <stage> <type> Update manual checkpoint
14
17
  * config read Read config.json gswd section
15
18
  * config merge [--overrides json] Merge gswd config
16
19
  * render banner <name> Render stage banner
@@ -27,6 +30,11 @@
27
30
  * audit [--auto-fix] Run Audit workflow
28
31
  * compile Run Compile workflow
29
32
  * bootstrap [@idea.md] [--auto] [--resume] [--skip-research] [--policy <name>] Run full pipeline
33
+ * render logo Render GSWD ASCII logo
34
+ * render first-run-guide Render first-run command guide
35
+ * first-run-complete Mark first run as complete in STATE.json
36
+ * intake write --description "..." --source interactive Write INTAKE.json
37
+ * intake read Read INTAKE.json
30
38
  */
31
39
 
32
40
  'use strict';
@@ -95,6 +103,25 @@ function outputText(text) {
95
103
  process.stdout.write(text + '\n');
96
104
  }
97
105
 
106
+ // ─── Standalone Pipeline Guard ───────────────────────────────────────────────
107
+
108
+ /**
109
+ * Check if running inside Claude Code. Pipeline commands require Claude Code's
110
+ * Task() API for agent spawning. When called standalone, print human-readable error.
111
+ */
112
+ function guardPipelineCommand(commandName) {
113
+ // In Claude Code, spawnFn is provided at call time — this guard catches
114
+ // direct CLI invocation where pipeline commands produce no useful output.
115
+ // Check: if stdout is a TTY (real terminal, not piped), the user is likely
116
+ // running this manually outside Claude Code.
117
+ if (process.stdout.isTTY) {
118
+ console.log(`\nGSWD pipeline commands require Claude Code.`);
119
+ console.log(`Run /gswd:start inside a Claude Code session to use the full pipeline.\n`);
120
+ process.exit(2);
121
+ }
122
+ // Non-TTY (piped/CI): fall through to existing JSON error behavior
123
+ }
124
+
98
125
  // ─── Argument Parsing ────────────────────────────────────────────────────────
99
126
 
100
127
  const args = process.argv.slice(2);
@@ -124,6 +151,9 @@ Commands:
124
151
  state read Read STATE.json
125
152
  state update-stage <stage> <status> Update stage status
126
153
  state checkpoint <workflow> <id> Write checkpoint
154
+ state cascade-invalidate <stage> Cascade-invalidate downstream stages
155
+ state set-manual-mode <true|false> Enable/disable manual mode
156
+ state update-manual-checkpoint <stage> <type> Update manual checkpoint
127
157
  config read Read config.json gswd section
128
158
  config merge [--overrides json] Merge gswd config
129
159
  render banner <name> Render stage banner
@@ -189,6 +219,12 @@ try {
189
219
  case 'bootstrap':
190
220
  handleBootstrap();
191
221
  break;
222
+ case 'first-run-complete':
223
+ handleFirstRunComplete();
224
+ break;
225
+ case 'intake':
226
+ handleIntake();
227
+ break;
192
228
  case '--help':
193
229
  case '-h':
194
230
  case 'help':
@@ -305,6 +341,38 @@ function handleState() {
305
341
  outputJson({ checkpoint_written: true, workflow, checkpoint_id: checkpointId });
306
342
  break;
307
343
  }
344
+ case 'cascade-invalidate': {
345
+ const stage = args[2];
346
+ if (!stage || !['imagine', 'specify', 'audit', 'compile'].includes(stage)) {
347
+ outputError('Usage: state cascade-invalidate <stage> (imagine|specify|audit|compile)');
348
+ process.exit(1);
349
+ }
350
+ stateModule.cascadeInvalidate(STATE_PATH, stage);
351
+ outputJson({ success: true, stage, action: 'cascade-invalidate' });
352
+ break;
353
+ }
354
+ case 'set-manual-mode': {
355
+ const enabledArg = args[2];
356
+ if (enabledArg !== 'true' && enabledArg !== 'false') {
357
+ outputError('Usage: state set-manual-mode <true|false>');
358
+ process.exit(1);
359
+ }
360
+ const enabled = enabledArg === 'true';
361
+ stateModule.setManualMode(STATE_PATH, enabled);
362
+ outputJson({ success: true, manual_mode: enabled });
363
+ break;
364
+ }
365
+ case 'update-manual-checkpoint': {
366
+ const stage = args[2] || null;
367
+ const interruptType = args[3] || null;
368
+ if (interruptType && interruptType !== 'mid_stage' && interruptType !== 'between_stages') {
369
+ outputError('Usage: state update-manual-checkpoint <stage|null> <mid_stage|between_stages|null>');
370
+ process.exit(1);
371
+ }
372
+ stateModule.updateManualCheckpoint(STATE_PATH, stage, interruptType);
373
+ outputJson({ success: true, stage, interrupt_type: interruptType });
374
+ break;
375
+ }
308
376
  default:
309
377
  outputError(`Unknown state subcommand: ${subcommand}`);
310
378
  process.exit(1);
@@ -393,6 +461,46 @@ function handleRender() {
393
461
  }
394
462
  break;
395
463
  }
464
+ case 'logo': {
465
+ const pkg = require('../package.json');
466
+ outputText(renderModule.renderLogo(pkg.version));
467
+ break;
468
+ }
469
+ case 'first-run-guide': {
470
+ outputText(renderModule.renderFirstRunGuide());
471
+ break;
472
+ }
473
+ case 'file-inventory': {
474
+ const defaultFiles = [
475
+ { path: '.planning/IMAGINE.md', description: 'Product vision, target user, and direction' },
476
+ { path: '.planning/DECISIONS.md', description: 'Frozen decisions, success metrics, risks' },
477
+ { path: '.planning/SPEC.md', description: 'Functional requirements with acceptance criteria' },
478
+ { path: '.planning/NFR.md', description: 'Non-functional requirements with thresholds' },
479
+ { path: '.planning/JOURNEYS.md', description: 'User journeys with FR and integration linkages' },
480
+ { path: '.planning/INTEGRATIONS.md', description: 'External integrations with status and fallbacks' },
481
+ { path: '.planning/research/ARCHITECTURE.md', description: 'Architecture patterns and project structure' },
482
+ { path: '.planning/research/FEATURES.md', description: 'Feature landscape with prioritization' },
483
+ { path: '.planning/research/PITFALLS.md', description: 'Common pitfalls and prevention strategies' },
484
+ { path: '.planning/research/STACK.md', description: 'Technology stack recommendations' },
485
+ { path: '.planning/research/SUMMARY.md', description: 'Research executive summary' },
486
+ { path: '.planning/PROJECT.md', description: 'GSD project context (compiled)' },
487
+ { path: '.planning/REQUIREMENTS.md', description: 'GSD requirements with traceability (compiled)' },
488
+ { path: '.planning/ROADMAP.md', description: 'GSD roadmap with phased journeys (compiled)' },
489
+ { path: '.planning/STATE.md', description: 'GSD project state and decisions (compiled)' },
490
+ { path: '.planning/research/gswd/SUMMARY.md', description: 'GSD-format research bridge (compiled)' },
491
+ ];
492
+
493
+ // Filter to only files that exist
494
+ const existingFiles = defaultFiles.filter(f => {
495
+ try {
496
+ fs.accessSync(path.join(CWD, f.path));
497
+ return true;
498
+ } catch { return false; }
499
+ });
500
+
501
+ process.stdout.write(renderModule.renderFileInventory(existingFiles) + '\n');
502
+ break;
503
+ }
396
504
  default:
397
505
  outputError(`Unknown render subcommand: ${subcommand}`);
398
506
  process.exit(1);
@@ -517,11 +625,77 @@ function handleCommit() {
517
625
  }
518
626
 
519
627
  function handleImagine() {
628
+ guardPipelineCommand('imagine');
629
+
630
+ // Handle update-vision subcommand (IMAGINE-05/06)
631
+ if (args[1] === 'update-vision') {
632
+ const newVision = args.slice(2).join(' ');
633
+ if (!newVision) {
634
+ outputError('Usage: imagine update-vision "<vision text>"');
635
+ process.exit(1);
636
+ }
637
+ const stateModule = loadModule('state');
638
+
639
+ const filesToUpdate = [
640
+ path.join(PLANNING_DIR, 'DECISIONS.md'),
641
+ path.join(PLANNING_DIR, 'IMAGINE.md'),
642
+ ];
643
+
644
+ const updated = [];
645
+ for (const filePath of filesToUpdate) {
646
+ try {
647
+ const content = fs.readFileSync(filePath, 'utf-8');
648
+ // Replace content between ## Vision and next ## heading (or end of file)
649
+ const updatedContent = content.replace(
650
+ /(^## Vision\s*\n)([\s\S]*?)(?=\n## |\n# |$)/m,
651
+ `$1\n${newVision}\n`
652
+ );
653
+ stateModule.safeWriteFile(filePath, updatedContent);
654
+ updated.push(filePath);
655
+ } catch (err) {
656
+ outputError(`Failed to update vision in ${filePath}: ${err.message}`);
657
+ process.exit(1);
658
+ }
659
+ }
660
+ outputJson({ updated: true, files: updated });
661
+ return; // Exit handleImagine — do not proceed to main flow
662
+ }
663
+
520
664
  const imagineModule = loadModule('imagine');
521
665
 
522
666
  const autoMode = hasFlag('--auto');
523
667
  const skipResearch = hasFlag('--skip-research');
524
668
 
669
+ // Parse --rerun-context flag for re-run augmentation (IMAGINE-03)
670
+ const rerunContextPath = getFlag('--rerun-context');
671
+ let userFeedback;
672
+ let previousAgentOutputs;
673
+
674
+ if (rerunContextPath) {
675
+ const resolvedPath = path.resolve(CWD, rerunContextPath);
676
+ try {
677
+ userFeedback = fs.readFileSync(resolvedPath, 'utf-8');
678
+ } catch {
679
+ // Non-fatal: file not found, proceed without re-run augmentation
680
+ }
681
+ // Read previous agent outputs from persisted artifact files
682
+ previousAgentOutputs = {};
683
+ const agentFileMap = {
684
+ 'market-researcher': path.join(PLANNING_DIR, 'COMPETITION.md'),
685
+ 'icp-persona': path.join(PLANNING_DIR, 'ICP.md'),
686
+ 'positioning': path.join(PLANNING_DIR, 'GTM.md'),
687
+ 'brainstorm-alternatives': path.join(PLANNING_DIR, 'DIRECTIONS.md'),
688
+ 'devils-advocate': path.join(PLANNING_DIR, 'RISKS.md'),
689
+ };
690
+ for (const [agentName, filePath] of Object.entries(agentFileMap)) {
691
+ try {
692
+ previousAgentOutputs[agentName] = fs.readFileSync(filePath, 'utf-8');
693
+ } catch {
694
+ // Agent artifact not found — no previous output for this agent
695
+ }
696
+ }
697
+ }
698
+
525
699
  // Find idea file: look for arg starting with @ or ending with .md
526
700
  let ideaFilePath = null;
527
701
  for (let i = 1; i < args.length; i++) {
@@ -553,6 +727,8 @@ function handleImagine() {
553
727
  skipResearch,
554
728
  planningDir: PLANNING_DIR,
555
729
  configPath: CONFIG_PATH,
730
+ previousAgentOutputs,
731
+ userFeedback,
556
732
  };
557
733
 
558
734
  imagineModule.runImagine(intakeOptions).then(function(result) {
@@ -576,6 +752,8 @@ function handleImagine() {
576
752
  skipResearch,
577
753
  planningDir: PLANNING_DIR,
578
754
  configPath: CONFIG_PATH,
755
+ previousAgentOutputs,
756
+ userFeedback,
579
757
  };
580
758
 
581
759
  // runImagine is async, handle with promise
@@ -591,6 +769,7 @@ function handleImagine() {
591
769
  }
592
770
 
593
771
  function handleSpecify() {
772
+ guardPipelineCommand('specify');
594
773
  const specifyModule = loadModule('specify');
595
774
 
596
775
  const autoMode = hasFlag('--auto');
@@ -620,6 +799,7 @@ function handleSpecify() {
620
799
  }
621
800
 
622
801
  function handleBootstrap() {
802
+ guardPipelineCommand('bootstrap');
623
803
  const bootstrapModule = loadModule('bootstrap');
624
804
 
625
805
  const autoMode = hasFlag('--auto');
@@ -664,6 +844,7 @@ function handleBootstrap() {
664
844
  }
665
845
 
666
846
  function handleAudit() {
847
+ guardPipelineCommand('audit');
667
848
  const auditModule = loadModule('audit');
668
849
 
669
850
  const autoFix = hasFlag('--auto-fix');
@@ -692,6 +873,7 @@ function handleAudit() {
692
873
  }
693
874
 
694
875
  function handleCompile() {
876
+ guardPipelineCommand('compile');
695
877
  const compileModule = loadModule('compile');
696
878
 
697
879
  try {
@@ -714,3 +896,49 @@ function handleCompile() {
714
896
  process.exit(1);
715
897
  }
716
898
  }
899
+
900
+ function handleFirstRunComplete() {
901
+ const stateModule = loadModule('state');
902
+ const state = stateModule.readState(STATE_PATH);
903
+ if (!state) {
904
+ outputJson({ first_run_complete: false, error: 'STATE.json not found' });
905
+ return;
906
+ }
907
+ state.first_run_complete = true;
908
+ stateModule.writeState(STATE_PATH, state);
909
+ outputJson({ first_run_complete: true });
910
+ }
911
+
912
+ function handleIntake() {
913
+ const subCmd = args[1];
914
+ const intakeModule = loadModule('intake');
915
+
916
+ switch (subCmd) {
917
+ case 'write': {
918
+ const description = getFlag('--description');
919
+ const source = getFlag('--source') || 'interactive';
920
+ const ideaFile = getFlag('--idea-file');
921
+
922
+ if (!description) {
923
+ outputError('--description is required');
924
+ process.exit(1);
925
+ }
926
+
927
+ const result = intakeModule.writeIntake(GSWD_DIR, {
928
+ product_description: description,
929
+ source: source,
930
+ ...(ideaFile ? { idea_file: ideaFile } : {}),
931
+ });
932
+ outputJson(result);
933
+ break;
934
+ }
935
+ case 'read': {
936
+ const data = intakeModule.readIntake(GSWD_DIR);
937
+ outputJson(data || { error: 'No INTAKE.json found' });
938
+ break;
939
+ }
940
+ default:
941
+ outputError(`Unknown intake subcommand: ${subCmd}. Use 'write' or 'read'.`);
942
+ process.exit(1);
943
+ }
944
+ }
package/bin/install.js CHANGED
@@ -73,6 +73,14 @@ if (!fs.existsSync(srcDir)) {
73
73
  // Create destination directory
74
74
  fs.mkdirSync(destDir, { recursive: true });
75
75
 
76
+ // Remove any stale .md files from a previous install before copying
77
+ if (fs.existsSync(destDir)) {
78
+ const existing = fs.readdirSync(destDir).filter(f => f.endsWith('.md'));
79
+ for (const file of existing) {
80
+ fs.unlinkSync(path.join(destDir, file));
81
+ }
82
+ }
83
+
76
84
  // Copy all .md files
77
85
  const files = fs.readdirSync(srcDir).filter(f => f.endsWith('.md'));
78
86
  let copied = 0;
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: gswd:imagine
3
3
  description: Validate product direction and freeze key decisions
4
- argument-hint: "[--auto] [--resume] [--skip-research] [@idea.md]"
4
+ argument-hint: "[--auto] [--resume] [--skip-research] [--rerun-context <path>] [@idea.md]"
5
5
  allowed-tools:
6
6
  - Bash
7
7
  - Read
@@ -30,6 +30,7 @@ Turn a fuzzy product idea into a validated direction with frozen decisions.
30
30
  - `--auto` — apply auto policy for decision-making
31
31
  - `--resume` — continue from last checkpoint
32
32
  - `--skip-research` — disable research agents (faster, offline-friendly)
33
+ - `--rerun-context <path>` — provide previous agent outputs and user feedback for augmented re-run
33
34
 
34
35
  **After this command:** Run `/gswd:specify` to build the execution-grade spec.
35
36
  </objective>
@@ -42,6 +43,11 @@ Turn a fuzzy product idea into a validated direction with frozen decisions.
42
43
  node ./bin/gswd-tools.cjs imagine $ARGUMENTS
43
44
  ```
44
45
 
46
+ 2.5. If `--rerun-context` flag is present, read the revision-notes file at the provided path and pass its content as additional context to the imagine workflow. The workflow will:
47
+ - Pass previous agent outputs to each agent's prompt as `<rerun_context>`
48
+ - Include user feedback in agent prompts so agents can build on or pivot from prior findings
49
+ - This enables the "Not satisfied — tell me more" re-run flow from `/gswd:start`
50
+
45
51
  3. The CLI runs the imagine workflow which may spawn research agents (unless `--skip-research`).
46
52
 
47
53
  4. Interactive prompts will ask the founder to confirm or override key decisions.