gsdd-cli 0.18.5 → 0.19.1

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 (101) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +610 -608
  3. package/agents/DISTILLATION.md +421 -421
  4. package/agents/README.md +62 -62
  5. package/agents/approach-explorer.md +370 -361
  6. package/agents/debugger.md +82 -82
  7. package/agents/executor.md +473 -394
  8. package/agents/integration-checker.md +318 -318
  9. package/agents/mapper.md +103 -103
  10. package/agents/planner.md +342 -313
  11. package/agents/researcher.md +84 -84
  12. package/agents/roadmapper.md +296 -296
  13. package/agents/synthesizer.md +236 -236
  14. package/agents/verifier.md +337 -337
  15. package/bin/adapters/agents.mjs +34 -34
  16. package/bin/adapters/claude.mjs +193 -191
  17. package/bin/adapters/codex.mjs +85 -85
  18. package/bin/adapters/index.mjs +20 -20
  19. package/bin/adapters/opencode.mjs +280 -278
  20. package/bin/gsdd.mjs +123 -116
  21. package/bin/lib/cli-utils.mjs +28 -28
  22. package/bin/lib/evidence-contract.mjs +325 -112
  23. package/bin/lib/file-ops.mjs +186 -144
  24. package/bin/lib/health-truth.mjs +196 -178
  25. package/bin/lib/health.mjs +246 -226
  26. package/bin/lib/init-flow.mjs +247 -231
  27. package/bin/lib/init-prompts.mjs +248 -247
  28. package/bin/lib/init-runtime.mjs +193 -190
  29. package/bin/lib/init.mjs +17 -17
  30. package/bin/lib/lifecycle-preflight.mjs +760 -326
  31. package/bin/lib/lifecycle-state.mjs +356 -267
  32. package/bin/lib/manifest.mjs +116 -114
  33. package/bin/lib/models.mjs +411 -411
  34. package/bin/lib/phase.mjs +365 -358
  35. package/bin/lib/plan-constants.mjs +35 -30
  36. package/bin/lib/provenance.mjs +109 -106
  37. package/bin/lib/rendering.mjs +119 -83
  38. package/bin/lib/runtime-freshness.mjs +214 -214
  39. package/bin/lib/session-fingerprint.mjs +91 -14
  40. package/bin/lib/templates.mjs +225 -224
  41. package/bin/lib/workspace-root.mjs +2 -1
  42. package/distilled/DESIGN.md +2461 -2323
  43. package/distilled/EVIDENCE-INDEX.md +418 -392
  44. package/distilled/README.md +196 -193
  45. package/distilled/SKILL.md +86 -85
  46. package/distilled/templates/agents.block.md +21 -21
  47. package/distilled/templates/agents.md +6 -6
  48. package/distilled/templates/approach.md +272 -232
  49. package/distilled/templates/auth-matrix.md +78 -78
  50. package/distilled/templates/brownfield-change/CHANGE.md +99 -0
  51. package/distilled/templates/brownfield-change/HANDOFF.md +38 -0
  52. package/distilled/templates/brownfield-change/VERIFICATION.md +56 -0
  53. package/distilled/templates/codebase/architecture.md +110 -110
  54. package/distilled/templates/codebase/concerns.md +95 -95
  55. package/distilled/templates/codebase/conventions.md +193 -193
  56. package/distilled/templates/codebase/stack.md +96 -96
  57. package/distilled/templates/delegates/approach-explorer.md +28 -25
  58. package/distilled/templates/delegates/mapper-arch.md +26 -26
  59. package/distilled/templates/delegates/mapper-concerns.md +27 -27
  60. package/distilled/templates/delegates/mapper-quality.md +28 -28
  61. package/distilled/templates/delegates/mapper-tech.md +25 -25
  62. package/distilled/templates/delegates/plan-checker.md +78 -68
  63. package/distilled/templates/delegates/researcher-architecture.md +30 -30
  64. package/distilled/templates/delegates/researcher-features.md +30 -30
  65. package/distilled/templates/delegates/researcher-pitfalls.md +30 -30
  66. package/distilled/templates/delegates/researcher-stack.md +30 -30
  67. package/distilled/templates/delegates/researcher-synthesizer.md +31 -31
  68. package/distilled/templates/research/architecture.md +57 -57
  69. package/distilled/templates/research/features.md +23 -23
  70. package/distilled/templates/research/pitfalls.md +46 -46
  71. package/distilled/templates/research/stack.md +45 -45
  72. package/distilled/templates/research/summary.md +67 -67
  73. package/distilled/templates/roadmap.md +74 -62
  74. package/distilled/templates/spec.md +110 -110
  75. package/distilled/workflows/audit-milestone.md +303 -271
  76. package/distilled/workflows/complete-milestone.md +349 -332
  77. package/distilled/workflows/execute.md +457 -450
  78. package/distilled/workflows/map-codebase.md +253 -253
  79. package/distilled/workflows/new-milestone.md +242 -238
  80. package/distilled/workflows/new-project.md +398 -398
  81. package/distilled/workflows/pause.md +160 -156
  82. package/distilled/workflows/plan-milestone-gaps.md +183 -183
  83. package/distilled/workflows/plan.md +454 -448
  84. package/distilled/workflows/progress.md +227 -223
  85. package/distilled/workflows/quick.md +351 -347
  86. package/distilled/workflows/resume.md +220 -212
  87. package/distilled/workflows/verify-work.md +260 -260
  88. package/distilled/workflows/verify.md +431 -429
  89. package/docs/BROWNFIELD-PROOF.md +95 -95
  90. package/docs/RUNTIME-SUPPORT.md +80 -69
  91. package/docs/USER-GUIDE.md +394 -386
  92. package/docs/VERIFICATION-DISCIPLINE.md +59 -59
  93. package/docs/claude/context-monitor.md +98 -98
  94. package/docs/proof/consumer-node-cli/README.md +37 -37
  95. package/docs/proof/consumer-node-cli/ROADMAP.md +14 -14
  96. package/docs/proof/consumer-node-cli/SPEC.md +17 -17
  97. package/docs/proof/consumer-node-cli/brief.md +9 -9
  98. package/docs/proof/consumer-node-cli/phases/01-foundation/01-01-PLAN.md +34 -34
  99. package/docs/proof/consumer-node-cli/phases/01-foundation/01-01-SUMMARY.md +10 -10
  100. package/docs/proof/consumer-node-cli/phases/01-foundation/01-VERIFICATION.md +30 -30
  101. package/package.json +62 -61
@@ -1,139 +1,181 @@
1
- import { cpSync, existsSync, mkdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from 'fs';
1
+ import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, statSync, unlinkSync, writeFileSync } from 'fs';
2
2
  import { dirname, isAbsolute, relative, resolve } from 'path';
3
3
  import { output, parseFlagValue } from './cli-utils.mjs';
4
4
  import { resolveWorkspaceContext } from './workspace-root.mjs';
5
-
6
- class FileOpError extends Error {}
7
-
8
- function fail(message) {
9
- console.error(message);
10
- throw new FileOpError(message);
11
- }
12
-
13
- function resolveWorkspacePath(cwd, target) {
14
- const workspaceRoot = resolve(cwd);
15
- const resolved = resolve(workspaceRoot, target);
16
- const rel = relative(workspaceRoot, resolved);
17
-
18
- if (rel === '' || (!rel.startsWith('..') && !isAbsolute(rel))) {
19
- return resolved;
20
- }
21
-
22
- fail(`Path must stay inside the workspace: ${target}`);
23
- }
24
-
25
- function getMissingBehavior(args) {
26
- const parsed = parseFlagValue(args, '--missing');
27
- if (!parsed.present) return 'error';
28
- if (parsed.invalid) fail('Usage: --missing <error|ok>');
29
- if (!['error', 'ok'].includes(parsed.value)) fail('Usage: --missing <error|ok>');
30
- return parsed.value;
31
- }
32
-
33
- function cmdCopy(cwd, args) {
34
- const [sourceArg, destinationArg, ...flags] = args;
35
- if (!sourceArg || !destinationArg) {
36
- fail('Usage: gsdd file-op copy <source> <destination> [--missing <error|ok>]');
37
- }
38
-
39
- const missingBehavior = getMissingBehavior(flags);
40
- const source = resolveWorkspacePath(cwd, sourceArg);
41
- const destination = resolveWorkspacePath(cwd, destinationArg);
42
-
43
- if (!existsSync(source)) {
44
- if (missingBehavior === 'ok') {
45
- output({ operation: 'copy', source: sourceArg, destination: destinationArg, changed: false, reason: 'missing_source' });
46
- return;
47
- }
48
- fail(`Source file does not exist: ${sourceArg}`);
49
- }
50
-
51
- if (statSync(source).isDirectory()) {
52
- fail(`Copy only supports files in this phase: ${sourceArg}`);
53
- }
54
-
55
- mkdirSync(dirname(destination), { recursive: true });
56
- cpSync(source, destination, { force: true });
57
- output({ operation: 'copy', source: sourceArg, destination: destinationArg, changed: true });
58
- }
59
-
60
- function cmdDelete(cwd, args) {
61
- const [targetArg, ...flags] = args;
62
- if (!targetArg) {
63
- fail('Usage: gsdd file-op delete <target> [--missing <error|ok>]');
64
- }
65
-
66
- const missingBehavior = getMissingBehavior(flags);
67
- const target = resolveWorkspacePath(cwd, targetArg);
68
-
69
- if (!existsSync(target)) {
70
- if (missingBehavior === 'ok') {
71
- output({ operation: 'delete', target: targetArg, changed: false, reason: 'missing_target' });
72
- return;
73
- }
74
- fail(`Target file does not exist: ${targetArg}`);
75
- }
76
-
77
- if (statSync(target).isDirectory()) {
78
- fail(`Delete only supports files in this phase: ${targetArg}`);
79
- }
80
-
81
- unlinkSync(target);
82
- output({ operation: 'delete', target: targetArg, changed: true });
83
- }
84
-
85
- function cmdRegexSub(cwd, args) {
86
- const [targetArg, pattern, replacement, ...flags] = args;
87
- if (!targetArg || pattern === undefined || replacement === undefined) {
88
- fail('Usage: gsdd file-op regex-sub <target> <pattern> <replacement> [--flags <flags>] [--missing <error|ok>]');
89
- }
90
-
91
- const regexFlags = parseFlagValue(flags, '--flags');
92
- if (regexFlags.present && regexFlags.invalid) {
93
- fail('Usage: --flags <regex-flags>');
94
- }
95
-
96
- const missingBehavior = getMissingBehavior(flags);
97
- const target = resolveWorkspacePath(cwd, targetArg);
98
-
99
- if (!existsSync(target)) {
100
- if (missingBehavior === 'ok') {
101
- output({ operation: 'regex-sub', target: targetArg, changed: false, reason: 'missing_target' });
102
- return;
103
- }
104
- fail(`Target file does not exist: ${targetArg}`);
105
- }
106
-
107
- if (statSync(target).isDirectory()) {
108
- fail(`regex-sub only supports files in this phase: ${targetArg}`);
109
- }
110
-
111
- let regex;
112
- try {
113
- regex = new RegExp(pattern, regexFlags.value || 'g');
114
- } catch (error) {
115
- fail(`Invalid regex pattern: ${error.message}`);
116
- }
117
-
118
- const source = readFileSync(target, 'utf-8');
119
- let replacementCount = 0;
120
- if (regex.global) {
121
- const matches = source.match(regex);
122
- replacementCount = matches ? matches.length : 0;
123
- } else {
124
- replacementCount = regex.test(source) ? 1 : 0;
125
- }
126
-
127
- if (replacementCount === 0) {
128
- fail(`Pattern did not match any text in ${targetArg}`);
129
- }
130
-
131
- const updated = source.replace(regex, replacement);
132
- const changed = updated !== source;
133
- writeFileSync(target, updated);
134
- output({ operation: 'regex-sub', target: targetArg, changed, replacements: replacementCount });
135
- }
136
-
5
+
6
+ class FileOpError extends Error {}
7
+
8
+ function fail(message) {
9
+ console.error(message);
10
+ throw new FileOpError(message);
11
+ }
12
+
13
+ function resolveWorkspacePath(cwd, target) {
14
+ const workspaceRoot = resolve(cwd);
15
+ const resolved = resolve(workspaceRoot, target);
16
+ const rel = relative(workspaceRoot, resolved);
17
+
18
+ if (rel === '' || (!rel.startsWith('..') && !isAbsolute(rel))) {
19
+ return resolved;
20
+ }
21
+
22
+ fail(`Path must stay inside the workspace: ${target}`);
23
+ }
24
+
25
+ function ensureRealPathInsideWorkspace(workspaceRoot, candidate, label) {
26
+ const realWorkspaceRoot = realpathSync(workspaceRoot);
27
+ const realCandidate = realpathSync(candidate);
28
+ const rel = relative(realWorkspaceRoot, realCandidate);
29
+ if (rel === '' || (!rel.startsWith('..') && !isAbsolute(rel))) {
30
+ return realCandidate;
31
+ }
32
+ fail(`${label} must stay inside the workspace: ${candidate}`);
33
+ }
34
+
35
+ function ensureExistingFilePathInsideWorkspace(workspaceRoot, candidate, label) {
36
+ const stats = lstatSync(candidate);
37
+ if (stats.isSymbolicLink()) {
38
+ fail(`${label} cannot be a symlink: ${candidate}`);
39
+ }
40
+ return ensureRealPathInsideWorkspace(workspaceRoot, candidate, label);
41
+ }
42
+
43
+ function ensureParentPathInsideWorkspace(workspaceRoot, candidate, label) {
44
+ let current = dirname(candidate);
45
+ while (!existsSync(current)) {
46
+ const parent = dirname(current);
47
+ if (parent === current) {
48
+ fail(`${label} must stay inside the workspace: ${candidate}`);
49
+ }
50
+ current = parent;
51
+ }
52
+ ensureExistingFilePathInsideWorkspace(workspaceRoot, current, label);
53
+ }
54
+
55
+ function getMissingBehavior(args) {
56
+ const parsed = parseFlagValue(args, '--missing');
57
+ if (!parsed.present) return 'error';
58
+ if (parsed.invalid) fail('Usage: --missing <error|ok>');
59
+ if (!['error', 'ok'].includes(parsed.value)) fail('Usage: --missing <error|ok>');
60
+ return parsed.value;
61
+ }
62
+
63
+ function cmdCopy(cwd, args) {
64
+ const [sourceArg, destinationArg, ...flags] = args;
65
+ if (!sourceArg || !destinationArg) {
66
+ fail('Usage: gsdd file-op copy <source> <destination> [--missing <error|ok>]');
67
+ }
68
+
69
+ const missingBehavior = getMissingBehavior(flags);
70
+ const workspaceRoot = resolve(cwd);
71
+ const source = resolveWorkspacePath(cwd, sourceArg);
72
+ const destination = resolveWorkspacePath(cwd, destinationArg);
73
+
74
+ if (!existsSync(source)) {
75
+ if (missingBehavior === 'ok') {
76
+ output({ operation: 'copy', source: sourceArg, destination: destinationArg, changed: false, reason: 'missing_source' });
77
+ return;
78
+ }
79
+ fail(`Source file does not exist: ${sourceArg}`);
80
+ }
81
+
82
+ if (statSync(source).isDirectory()) {
83
+ fail(`Copy only supports files in this phase: ${sourceArg}`);
84
+ }
85
+
86
+ ensureExistingFilePathInsideWorkspace(workspaceRoot, source, 'Source path');
87
+ if (existsSync(destination)) {
88
+ ensureExistingFilePathInsideWorkspace(workspaceRoot, destination, 'Destination path');
89
+ }
90
+ ensureParentPathInsideWorkspace(workspaceRoot, destination, 'Destination path');
91
+
92
+ mkdirSync(dirname(destination), { recursive: true });
93
+ cpSync(source, destination, { force: true });
94
+ output({ operation: 'copy', source: sourceArg, destination: destinationArg, changed: true });
95
+ }
96
+
97
+ function cmdDelete(cwd, args) {
98
+ const [targetArg, ...flags] = args;
99
+ if (!targetArg) {
100
+ fail('Usage: gsdd file-op delete <target> [--missing <error|ok>]');
101
+ }
102
+
103
+ const missingBehavior = getMissingBehavior(flags);
104
+ const workspaceRoot = resolve(cwd);
105
+ const target = resolveWorkspacePath(cwd, targetArg);
106
+
107
+ if (!existsSync(target)) {
108
+ if (missingBehavior === 'ok') {
109
+ output({ operation: 'delete', target: targetArg, changed: false, reason: 'missing_target' });
110
+ return;
111
+ }
112
+ fail(`Target file does not exist: ${targetArg}`);
113
+ }
114
+
115
+ if (statSync(target).isDirectory()) {
116
+ fail(`Delete only supports files in this phase: ${targetArg}`);
117
+ }
118
+
119
+ ensureExistingFilePathInsideWorkspace(workspaceRoot, target, 'Target path');
120
+ unlinkSync(target);
121
+ output({ operation: 'delete', target: targetArg, changed: true });
122
+ }
123
+
124
+ function cmdRegexSub(cwd, args) {
125
+ const [targetArg, pattern, replacement, ...flags] = args;
126
+ if (!targetArg || pattern === undefined || replacement === undefined) {
127
+ fail('Usage: gsdd file-op regex-sub <target> <pattern> <replacement> [--flags <flags>] [--missing <error|ok>]');
128
+ }
129
+
130
+ const regexFlags = parseFlagValue(flags, '--flags');
131
+ if (regexFlags.present && regexFlags.invalid) {
132
+ fail('Usage: --flags <regex-flags>');
133
+ }
134
+
135
+ const missingBehavior = getMissingBehavior(flags);
136
+ const workspaceRoot = resolve(cwd);
137
+ const target = resolveWorkspacePath(cwd, targetArg);
138
+
139
+ if (!existsSync(target)) {
140
+ if (missingBehavior === 'ok') {
141
+ output({ operation: 'regex-sub', target: targetArg, changed: false, reason: 'missing_target' });
142
+ return;
143
+ }
144
+ fail(`Target file does not exist: ${targetArg}`);
145
+ }
146
+
147
+ if (statSync(target).isDirectory()) {
148
+ fail(`regex-sub only supports files in this phase: ${targetArg}`);
149
+ }
150
+
151
+ ensureExistingFilePathInsideWorkspace(workspaceRoot, target, 'Target path');
152
+
153
+ let regex;
154
+ try {
155
+ regex = new RegExp(pattern, regexFlags.value || 'g');
156
+ } catch (error) {
157
+ fail(`Invalid regex pattern: ${error.message}`);
158
+ }
159
+
160
+ const source = readFileSync(target, 'utf-8');
161
+ let replacementCount = 0;
162
+ if (regex.global) {
163
+ const matches = source.match(regex);
164
+ replacementCount = matches ? matches.length : 0;
165
+ } else {
166
+ replacementCount = regex.test(source) ? 1 : 0;
167
+ }
168
+
169
+ if (replacementCount === 0) {
170
+ fail(`Pattern did not match any text in ${targetArg}`);
171
+ }
172
+
173
+ const updated = source.replace(regex, replacement);
174
+ const changed = updated !== source;
175
+ writeFileSync(target, updated);
176
+ output({ operation: 'regex-sub', target: targetArg, changed, replacements: replacementCount });
177
+ }
178
+
137
179
  export function cmdFileOp(...args) {
138
180
  const { args: normalizedArgs, workspaceRoot, invalid, error } = resolveWorkspaceContext(args);
139
181
  if (invalid) {
@@ -154,14 +196,14 @@ export function cmdFileOp(...args) {
154
196
  case 'regex-sub':
155
197
  cmdRegexSub(workspaceRoot, rest);
156
198
  return;
157
- default:
158
- fail('Usage: gsdd file-op <copy|delete|regex-sub> ...');
159
- }
160
- } catch (error) {
161
- if (error instanceof FileOpError) {
162
- process.exitCode = 1;
163
- return;
164
- }
165
- throw error;
166
- }
167
- }
199
+ default:
200
+ fail('Usage: gsdd file-op <copy|delete|regex-sub> ...');
201
+ }
202
+ } catch (error) {
203
+ if (error instanceof FileOpError) {
204
+ process.exitCode = 1;
205
+ return;
206
+ }
207
+ throw error;
208
+ }
209
+ }