@spekn/cli 1.0.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 (159) hide show
  1. package/dist/__tests__/export-cli.test.d.ts +1 -0
  2. package/dist/__tests__/export-cli.test.js +70 -0
  3. package/dist/__tests__/tui-args-policy.test.d.ts +1 -0
  4. package/dist/__tests__/tui-args-policy.test.js +50 -0
  5. package/dist/acp-S2MHZOAD.mjs +23 -0
  6. package/dist/acp-UCCI44JY.mjs +25 -0
  7. package/dist/auth/credentials-store.d.ts +2 -0
  8. package/dist/auth/credentials-store.js +5 -0
  9. package/dist/auth/device-flow.d.ts +36 -0
  10. package/dist/auth/device-flow.js +189 -0
  11. package/dist/auth/jwt.d.ts +1 -0
  12. package/dist/auth/jwt.js +6 -0
  13. package/dist/auth/session.d.ts +67 -0
  14. package/dist/auth/session.js +86 -0
  15. package/dist/auth-login.d.ts +34 -0
  16. package/dist/auth-login.js +202 -0
  17. package/dist/auth-logout.d.ts +25 -0
  18. package/dist/auth-logout.js +115 -0
  19. package/dist/auth-status.d.ts +24 -0
  20. package/dist/auth-status.js +109 -0
  21. package/dist/backlog-generate.d.ts +11 -0
  22. package/dist/backlog-generate.js +308 -0
  23. package/dist/backlog-health.d.ts +11 -0
  24. package/dist/backlog-health.js +287 -0
  25. package/dist/bridge-login.d.ts +40 -0
  26. package/dist/bridge-login.js +277 -0
  27. package/dist/chunk-3PAYRI4G.mjs +2428 -0
  28. package/dist/chunk-M4CS3A25.mjs +2426 -0
  29. package/dist/commands/auth/login.d.ts +30 -0
  30. package/dist/commands/auth/login.js +164 -0
  31. package/dist/commands/auth/logout.d.ts +25 -0
  32. package/dist/commands/auth/logout.js +115 -0
  33. package/dist/commands/auth/status.d.ts +24 -0
  34. package/dist/commands/auth/status.js +109 -0
  35. package/dist/commands/backlog/generate.d.ts +11 -0
  36. package/dist/commands/backlog/generate.js +308 -0
  37. package/dist/commands/backlog/health.d.ts +11 -0
  38. package/dist/commands/backlog/health.js +287 -0
  39. package/dist/commands/bridge/login.d.ts +36 -0
  40. package/dist/commands/bridge/login.js +258 -0
  41. package/dist/commands/export.d.ts +35 -0
  42. package/dist/commands/export.js +485 -0
  43. package/dist/commands/marketplace-export.d.ts +21 -0
  44. package/dist/commands/marketplace-export.js +214 -0
  45. package/dist/commands/project-clean.d.ts +1 -0
  46. package/dist/commands/project-clean.js +126 -0
  47. package/dist/commands/repo/common.d.ts +105 -0
  48. package/dist/commands/repo/common.js +775 -0
  49. package/dist/commands/repo/detach.d.ts +2 -0
  50. package/dist/commands/repo/detach.js +120 -0
  51. package/dist/commands/repo/register.d.ts +21 -0
  52. package/dist/commands/repo/register.js +175 -0
  53. package/dist/commands/repo/sync.d.ts +22 -0
  54. package/dist/commands/repo/sync.js +873 -0
  55. package/dist/commands/skills-import-local.d.ts +16 -0
  56. package/dist/commands/skills-import-local.js +352 -0
  57. package/dist/commands/spec/drift-check.d.ts +3 -0
  58. package/dist/commands/spec/drift-check.js +186 -0
  59. package/dist/commands/spec/frontmatter.d.ts +11 -0
  60. package/dist/commands/spec/frontmatter.js +219 -0
  61. package/dist/commands/spec/lint.d.ts +11 -0
  62. package/dist/commands/spec/lint.js +499 -0
  63. package/dist/commands/spec/parse.d.ts +11 -0
  64. package/dist/commands/spec/parse.js +162 -0
  65. package/dist/export.d.ts +35 -0
  66. package/dist/export.js +485 -0
  67. package/dist/index.d.ts +11 -0
  68. package/dist/index.js +21 -0
  69. package/dist/main.d.ts +1 -0
  70. package/dist/main.js +115280 -0
  71. package/dist/marketplace-export.d.ts +21 -0
  72. package/dist/marketplace-export.js +214 -0
  73. package/dist/project-clean.d.ts +1 -0
  74. package/dist/project-clean.js +126 -0
  75. package/dist/project-context.d.ts +99 -0
  76. package/dist/project-context.js +376 -0
  77. package/dist/repo-common.d.ts +101 -0
  78. package/dist/repo-common.js +671 -0
  79. package/dist/repo-detach.d.ts +2 -0
  80. package/dist/repo-detach.js +102 -0
  81. package/dist/repo-ingest.d.ts +29 -0
  82. package/dist/repo-ingest.js +305 -0
  83. package/dist/repo-register.d.ts +21 -0
  84. package/dist/repo-register.js +175 -0
  85. package/dist/repo-sync.d.ts +16 -0
  86. package/dist/repo-sync.js +152 -0
  87. package/dist/resources/prompt-loader.d.ts +1 -0
  88. package/dist/resources/prompt-loader.js +62 -0
  89. package/dist/resources/prompts/README.md +21 -0
  90. package/dist/resources/prompts/prompts/repo-analysis.prompt.md +126 -0
  91. package/dist/resources/prompts/repo-analysis.prompt.md +151 -0
  92. package/dist/resources/prompts/repo-sync-analysis.prompt.md +85 -0
  93. package/dist/skills-import-local.d.ts +16 -0
  94. package/dist/skills-import-local.js +352 -0
  95. package/dist/spec-drift-check.d.ts +3 -0
  96. package/dist/spec-drift-check.js +186 -0
  97. package/dist/spec-frontmatter.d.ts +11 -0
  98. package/dist/spec-frontmatter.js +219 -0
  99. package/dist/spec-lint.d.ts +11 -0
  100. package/dist/spec-lint.js +499 -0
  101. package/dist/spec-parse.d.ts +11 -0
  102. package/dist/spec-parse.js +162 -0
  103. package/dist/stubs/dotenv.d.ts +5 -0
  104. package/dist/stubs/dotenv.js +6 -0
  105. package/dist/stubs/typeorm.d.ts +22 -0
  106. package/dist/stubs/typeorm.js +28 -0
  107. package/dist/tui/app.d.ts +7 -0
  108. package/dist/tui/app.js +122 -0
  109. package/dist/tui/args.d.ts +8 -0
  110. package/dist/tui/args.js +57 -0
  111. package/dist/tui/capabilities/policy.d.ts +7 -0
  112. package/dist/tui/capabilities/policy.js +64 -0
  113. package/dist/tui/components/frame.d.ts +8 -0
  114. package/dist/tui/components/frame.js +8 -0
  115. package/dist/tui/components/status-bar.d.ts +8 -0
  116. package/dist/tui/components/status-bar.js +8 -0
  117. package/dist/tui/index.d.ts +2 -0
  118. package/dist/tui/index.js +23 -0
  119. package/dist/tui/index.mjs +7563 -0
  120. package/dist/tui/keymap/use-global-keymap.d.ts +19 -0
  121. package/dist/tui/keymap/use-global-keymap.js +82 -0
  122. package/dist/tui/navigation/nav-items.d.ts +3 -0
  123. package/dist/tui/navigation/nav-items.js +18 -0
  124. package/dist/tui/screens/bridge.d.ts +8 -0
  125. package/dist/tui/screens/bridge.js +19 -0
  126. package/dist/tui/screens/decisions.d.ts +5 -0
  127. package/dist/tui/screens/decisions.js +28 -0
  128. package/dist/tui/screens/export.d.ts +5 -0
  129. package/dist/tui/screens/export.js +16 -0
  130. package/dist/tui/screens/home.d.ts +5 -0
  131. package/dist/tui/screens/home.js +33 -0
  132. package/dist/tui/screens/locked.d.ts +5 -0
  133. package/dist/tui/screens/locked.js +9 -0
  134. package/dist/tui/screens/specs.d.ts +5 -0
  135. package/dist/tui/screens/specs.js +31 -0
  136. package/dist/tui/services/client.d.ts +1 -0
  137. package/dist/tui/services/client.js +18 -0
  138. package/dist/tui/services/context-service.d.ts +19 -0
  139. package/dist/tui/services/context-service.js +246 -0
  140. package/dist/tui/shared-enums.d.ts +16 -0
  141. package/dist/tui/shared-enums.js +19 -0
  142. package/dist/tui/state/use-app-state.d.ts +35 -0
  143. package/dist/tui/state/use-app-state.js +177 -0
  144. package/dist/tui/types.d.ts +77 -0
  145. package/dist/tui/types.js +2 -0
  146. package/dist/tui-bundle.d.ts +1 -0
  147. package/dist/tui-bundle.js +5 -0
  148. package/dist/tui-entry.mjs +1407 -0
  149. package/dist/utils/cli-runtime.d.ts +5 -0
  150. package/dist/utils/cli-runtime.js +22 -0
  151. package/dist/utils/help-error.d.ts +7 -0
  152. package/dist/utils/help-error.js +14 -0
  153. package/dist/utils/interaction.d.ts +19 -0
  154. package/dist/utils/interaction.js +93 -0
  155. package/dist/utils/structured-log.d.ts +7 -0
  156. package/dist/utils/structured-log.js +112 -0
  157. package/dist/utils/trpc-url.d.ts +4 -0
  158. package/dist/utils/trpc-url.js +15 -0
  159. package/package.json +59 -0
@@ -0,0 +1,499 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * spec-lint CLI Tool
5
+ *
6
+ * Validate specification markdown quality and structure.
7
+ *
8
+ * Usage: npm run spec-lint <spec-file-path>
9
+ * Example: npm run spec-lint specs/DECISIONS.md
10
+ */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || (function () {
28
+ var ownKeys = function(o) {
29
+ ownKeys = Object.getOwnPropertyNames || function (o) {
30
+ var ar = [];
31
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
+ return ar;
33
+ };
34
+ return ownKeys(o);
35
+ };
36
+ return function (mod) {
37
+ if (mod && mod.__esModule) return mod;
38
+ var result = {};
39
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
+ __setModuleDefault(result, mod);
41
+ return result;
42
+ };
43
+ })();
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.main = main;
46
+ const fs = __importStar(require("fs"));
47
+ const path = __importStar(require("path"));
48
+ const agents_1 = require("@spekn/agents");
49
+ const shared_1 = require("@spekn/shared");
50
+ const cli_runtime_1 = require("./utils/cli-runtime");
51
+ function parseArgs(args) {
52
+ const options = {};
53
+ let filePath;
54
+ for (let i = 0; i < args.length; i++) {
55
+ const arg = args[i];
56
+ if (arg === "--verbose" || arg === "-v") {
57
+ options.verbose = true;
58
+ }
59
+ else if (arg === "--fix" || arg === "-f") {
60
+ options.fix = true;
61
+ }
62
+ else if (!arg.startsWith("-")) {
63
+ filePath = arg;
64
+ }
65
+ }
66
+ if (!filePath) {
67
+ throw new Error("missing required argument: <spec-file-path>");
68
+ }
69
+ return { filePath, options };
70
+ }
71
+ function printHelp() {
72
+ console.log(`
73
+ spec-lint - Validate specification markdown quality
74
+
75
+ USAGE:
76
+ npm run spec-lint <spec-file-path> [OPTIONS]
77
+
78
+ ARGUMENTS:
79
+ <spec-file-path> Path to the specification markdown file
80
+
81
+ OPTIONS:
82
+ -v, --verbose Show detailed output and suggestions
83
+ -f, --fix Auto-fix deterministic anchor formatting issues
84
+ -h, --help Show this help message
85
+
86
+ LINTING RULES:
87
+ frontmatter-required Specification has valid YAML frontmatter
88
+ anchor-naming Anchors follow naming convention (lowercase, dots)
89
+ anchor-duplicates No duplicate anchors
90
+ anchor-acceptance All anchors have acceptance criteria
91
+ dependency-valid Dependencies reference valid anchors
92
+ markdown-quality Basic markdown quality checks
93
+
94
+ EXAMPLES:
95
+ npm run spec-lint specs/SPECIFICATION.md
96
+ npm run spec-lint specs/DECISIONS.md --verbose
97
+ `);
98
+ }
99
+ function escapeRegExp(value) {
100
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
101
+ }
102
+ function normalizeAnchorForFix(anchor) {
103
+ return anchor
104
+ .trim()
105
+ .toLowerCase()
106
+ .replace(/\s+/g, "-")
107
+ .replace(/[^a-z0-9._-]/g, "-")
108
+ .replace(/\.\./g, ".")
109
+ .replace(/^\./, "")
110
+ .replace(/\.$/, "");
111
+ }
112
+ function applyDeterministicFixes(content) {
113
+ const anchors = (0, agents_1.extractAnchors)(content);
114
+ const uniqueAnchors = Array.from(new Set(anchors.map((a) => a.anchor)));
115
+ let nextContent = content;
116
+ let fixesApplied = 0;
117
+ const replaceWithCount = (input, pattern, replacement) => {
118
+ let count = 0;
119
+ const output = input.replace(pattern, () => {
120
+ count += 1;
121
+ return replacement;
122
+ });
123
+ return { output, count };
124
+ };
125
+ for (const anchor of uniqueAnchors) {
126
+ const normalized = normalizeAnchorForFix(anchor);
127
+ if (!normalized || normalized === anchor) {
128
+ continue;
129
+ }
130
+ const escaped = escapeRegExp(anchor);
131
+ const bracket = replaceWithCount(nextContent, new RegExp(`\\[${escaped}\\]`, "g"), `[${normalized}]`);
132
+ nextContent = bracket.output;
133
+ fixesApplied += bracket.count;
134
+ const prefixed = replaceWithCount(nextContent, new RegExp(`spec:${escaped}\\b`, "gi"), `spec:${normalized}`);
135
+ nextContent = prefixed.output;
136
+ fixesApplied += prefixed.count;
137
+ const hashDeps = replaceWithCount(nextContent, new RegExp(`#${escaped}\\b`, "g"), `#${normalized}`);
138
+ nextContent = hashDeps.output;
139
+ fixesApplied += hashDeps.count;
140
+ }
141
+ return { content: nextContent, fixesApplied };
142
+ }
143
+ function checkAnchorNaming(anchors) {
144
+ const issues = [];
145
+ anchors.forEach((anchor) => {
146
+ // Check lowercase
147
+ if (anchor.anchor !== anchor.anchor.toLowerCase()) {
148
+ issues.push({
149
+ severity: "error",
150
+ rule: "anchor-naming",
151
+ message: "Anchor should be lowercase",
152
+ anchor: anchor.anchor,
153
+ line: anchor.lineNumber,
154
+ suggestion: anchor.anchor.toLowerCase(),
155
+ });
156
+ }
157
+ // Check valid characters (only lowercase letters, numbers, dots, hyphens)
158
+ if (!anchor.anchor.match(/^[a-z0-9._-]+$/)) {
159
+ issues.push({
160
+ severity: "error",
161
+ rule: "anchor-naming",
162
+ message: "Anchor contains invalid characters (only a-z, 0-9, ., -, _ allowed)",
163
+ anchor: anchor.anchor,
164
+ line: anchor.lineNumber,
165
+ });
166
+ }
167
+ // Check for double dots or leading/trailing dots
168
+ if (anchor.anchor.match(/\.\./)) {
169
+ issues.push({
170
+ severity: "warning",
171
+ rule: "anchor-naming",
172
+ message: "Anchor contains consecutive dots",
173
+ anchor: anchor.anchor,
174
+ line: anchor.lineNumber,
175
+ });
176
+ }
177
+ if (anchor.anchor.startsWith(".") || anchor.anchor.endsWith(".")) {
178
+ issues.push({
179
+ severity: "warning",
180
+ rule: "anchor-naming",
181
+ message: "Anchor should not start or end with a dot",
182
+ anchor: anchor.anchor,
183
+ line: anchor.lineNumber,
184
+ });
185
+ }
186
+ // Recommend dot notation for hierarchical anchors
187
+ if (anchor.anchor.includes("_") && !anchor.anchor.includes(".")) {
188
+ issues.push({
189
+ severity: "info",
190
+ rule: "anchor-naming",
191
+ message: "Consider using dot notation for hierarchical structure",
192
+ anchor: anchor.anchor,
193
+ line: anchor.lineNumber,
194
+ suggestion: anchor.anchor.replace(/_/g, "."),
195
+ });
196
+ }
197
+ });
198
+ return issues;
199
+ }
200
+ function checkDuplicateAnchors(anchors) {
201
+ const issues = [];
202
+ const anchorMap = new Map();
203
+ anchors.forEach((anchor) => {
204
+ const normalized = anchor.anchor.toLowerCase();
205
+ if (!anchorMap.has(normalized)) {
206
+ anchorMap.set(normalized, []);
207
+ }
208
+ anchorMap.get(normalized).push(anchor.lineNumber);
209
+ });
210
+ anchorMap.forEach((lines, anchor) => {
211
+ if (lines.length > 1) {
212
+ issues.push({
213
+ severity: "error",
214
+ rule: "anchor-duplicates",
215
+ message: `Duplicate anchor found on lines: ${lines.join(", ")}`,
216
+ anchor,
217
+ });
218
+ }
219
+ });
220
+ return issues;
221
+ }
222
+ function checkAcceptanceCriteria(content, anchors) {
223
+ const issues = [];
224
+ anchors.forEach((anchor) => {
225
+ const anchorContent = (0, agents_1.extractAnchorContent)(anchor.anchor, content);
226
+ if (!anchorContent)
227
+ return;
228
+ // Check for acceptance criteria section
229
+ const hasAcceptanceCriteria = anchorContent.match(/\*\*Acceptance Criteria\*\*:|##\s*Acceptance Criteria|###\s*Acceptance Criteria/i);
230
+ if (!hasAcceptanceCriteria) {
231
+ issues.push({
232
+ severity: "warning",
233
+ rule: "anchor-acceptance",
234
+ message: "Anchor lacks explicit acceptance criteria section",
235
+ anchor: anchor.anchor,
236
+ line: anchor.lineNumber,
237
+ });
238
+ }
239
+ });
240
+ return issues;
241
+ }
242
+ function checkDependencyValidity(content, anchors) {
243
+ const issues = [];
244
+ const validAnchors = (0, agents_1.getValidAnchors)(content);
245
+ anchors.forEach((anchor) => {
246
+ const anchorContent = (0, agents_1.extractAnchorContent)(anchor.anchor, content);
247
+ if (!anchorContent)
248
+ return;
249
+ // Look for dependency references
250
+ const dependencyMatch = anchorContent.match(/\*\*Dependencies\*\*:([^\n]+)/i);
251
+ if (dependencyMatch) {
252
+ const dependencyText = dependencyMatch[1];
253
+ const dependencyAnchors = dependencyText.match(/#[a-z0-9._-]+/gi);
254
+ if (dependencyAnchors) {
255
+ dependencyAnchors.forEach((dep) => {
256
+ const depNormalized = dep.replace(/^#/, "").toLowerCase();
257
+ const isValid = validAnchors.some((valid) => valid.toLowerCase() === depNormalized);
258
+ if (!isValid) {
259
+ issues.push({
260
+ severity: "error",
261
+ rule: "dependency-valid",
262
+ message: `Dependency references non-existent anchor: ${dep}`,
263
+ anchor: anchor.anchor,
264
+ line: anchor.lineNumber,
265
+ });
266
+ }
267
+ });
268
+ }
269
+ }
270
+ });
271
+ return issues;
272
+ }
273
+ function checkMarkdownQuality(content) {
274
+ const issues = [];
275
+ const lines = content.split("\n");
276
+ lines.forEach((line, index) => {
277
+ const lineNumber = index + 1;
278
+ // Check for trailing whitespace
279
+ if (line.endsWith(" ") || line.endsWith("\t")) {
280
+ issues.push({
281
+ severity: "info",
282
+ rule: "markdown-quality",
283
+ message: "Line has trailing whitespace",
284
+ line: lineNumber,
285
+ });
286
+ }
287
+ // Check for inconsistent heading markers
288
+ const headingMatch = line.match(/^(#{1,6})\s/);
289
+ if (headingMatch && line.includes("#", headingMatch[1].length + 1)) {
290
+ const afterHeading = line.substring(headingMatch[0].length);
291
+ if (!afterHeading.startsWith("#")) {
292
+ // Check if there's a # not at the start (could be an anchor)
293
+ if (afterHeading.includes("#") &&
294
+ !afterHeading.match(/^#[a-z0-9._-]+/i)) {
295
+ issues.push({
296
+ severity: "warning",
297
+ rule: "markdown-quality",
298
+ message: "Unexpected # character in heading",
299
+ line: lineNumber,
300
+ });
301
+ }
302
+ }
303
+ }
304
+ });
305
+ return issues;
306
+ }
307
+ function checkFrontmatter(content) {
308
+ const issues = [];
309
+ const result = (0, shared_1.parseSpecificationFrontmatter)(content);
310
+ if (!result.hasFrontmatter) {
311
+ issues.push({
312
+ severity: "error",
313
+ rule: "frontmatter-required",
314
+ message: "Specification is missing required YAML frontmatter (delimited by --- markers)",
315
+ });
316
+ return issues;
317
+ }
318
+ if (result.errors?.length) {
319
+ for (const error of result.errors) {
320
+ issues.push({
321
+ severity: "error",
322
+ rule: "frontmatter-required",
323
+ message: `Frontmatter validation error: ${error}`,
324
+ });
325
+ }
326
+ return issues;
327
+ }
328
+ const fm = result.frontmatter;
329
+ if (!fm)
330
+ return issues;
331
+ // Check hint lengths
332
+ if (fm.hints) {
333
+ for (const [layer, hints] of Object.entries(fm.hints)) {
334
+ if (!hints)
335
+ continue;
336
+ for (let i = 0; i < hints.length; i++) {
337
+ if (hints[i].length > shared_1.HINT_MAX_LENGTH) {
338
+ issues.push({
339
+ severity: "error",
340
+ rule: "frontmatter-required",
341
+ message: `Hint hints.${layer}[${i}] exceeds ${shared_1.HINT_MAX_LENGTH} character limit (${hints[i].length} chars)`,
342
+ });
343
+ }
344
+ }
345
+ }
346
+ }
347
+ // Check type-specific properties match declared type
348
+ const typeSpecific = ["capability", "architectural", "workflow", "operational"];
349
+ const present = typeSpecific.filter((t) => fm[t]);
350
+ if (present.length > 1) {
351
+ issues.push({
352
+ severity: "error",
353
+ rule: "frontmatter-required",
354
+ message: `Multiple type-specific properties found (${present.join(", ")}). Only one is allowed.`,
355
+ });
356
+ }
357
+ if (present.length === 1 && present[0] !== fm.type) {
358
+ issues.push({
359
+ severity: "warning",
360
+ rule: "frontmatter-required",
361
+ message: `Type-specific property "${present[0]}" does not match declared type "${fm.type}"`,
362
+ });
363
+ }
364
+ return issues;
365
+ }
366
+ function main(argv) {
367
+ const args = argv ?? process.argv.slice(2);
368
+ if ((0, cli_runtime_1.hasHelpFlag)(args)) {
369
+ printHelp();
370
+ return 0;
371
+ }
372
+ let filePath;
373
+ let options;
374
+ try {
375
+ ({ filePath, options } = parseArgs(args));
376
+ }
377
+ catch {
378
+ console.error("Error: Missing required argument <spec-file-path>");
379
+ printHelp();
380
+ return 1;
381
+ }
382
+ // Resolve file path
383
+ const resolvedPath = path.resolve(process.cwd(), filePath);
384
+ // Check if file exists
385
+ if (!fs.existsSync(resolvedPath)) {
386
+ console.error(`Error: File not found: ${resolvedPath}`);
387
+ return 1;
388
+ }
389
+ // Read file content
390
+ let content;
391
+ try {
392
+ content = fs.readFileSync(resolvedPath, "utf-8");
393
+ }
394
+ catch (error) {
395
+ console.error(`Error reading file: ${(0, cli_runtime_1.errorMessage)(error)}`);
396
+ return 1;
397
+ }
398
+ let fixSummary = null;
399
+ if (options.fix) {
400
+ const fixed = applyDeterministicFixes(content);
401
+ const fileModified = fixed.content !== content;
402
+ if (fileModified) {
403
+ fs.writeFileSync(resolvedPath, fixed.content, "utf-8");
404
+ content = fixed.content;
405
+ }
406
+ fixSummary = {
407
+ attempted: true,
408
+ fixesApplied: fixed.fixesApplied,
409
+ fileModified,
410
+ };
411
+ }
412
+ // Extract anchors
413
+ const anchors = (0, agents_1.extractAnchors)(content);
414
+ console.log(`\nLinting ${path.basename(filePath)}...`);
415
+ console.log(`Found ${anchors.length} anchor(s)\n`);
416
+ // Run all lint checks
417
+ const issues = [
418
+ ...checkFrontmatter(content),
419
+ ...checkAnchorNaming(anchors),
420
+ ...checkDuplicateAnchors(anchors),
421
+ ...checkAcceptanceCriteria(content, anchors),
422
+ ...checkDependencyValidity(content, anchors),
423
+ ...checkMarkdownQuality(content),
424
+ ];
425
+ // Group issues by severity
426
+ const errors = issues.filter((i) => i.severity === "error");
427
+ const warnings = issues.filter((i) => i.severity === "warning");
428
+ const infos = issues.filter((i) => i.severity === "info");
429
+ // Display results
430
+ if (issues.length === 0) {
431
+ console.log("✓ No issues found. Specification is clean!\n");
432
+ return 0;
433
+ }
434
+ if (errors.length > 0) {
435
+ console.log(`\n❌ Errors (${errors.length}):\n`);
436
+ errors.forEach((issue, index) => {
437
+ console.log(`${index + 1}. [${issue.rule}] ${issue.message}`);
438
+ if (issue.anchor)
439
+ console.log(` Anchor: ${issue.anchor}`);
440
+ if (issue.line)
441
+ console.log(` Line: ${issue.line}`);
442
+ if (issue.suggestion && options.verbose)
443
+ console.log(` Suggestion: ${issue.suggestion}`);
444
+ console.log();
445
+ });
446
+ }
447
+ if (warnings.length > 0) {
448
+ console.log(`\n⚠️ Warnings (${warnings.length}):\n`);
449
+ warnings.forEach((issue, index) => {
450
+ console.log(`${index + 1}. [${issue.rule}] ${issue.message}`);
451
+ if (issue.anchor)
452
+ console.log(` Anchor: ${issue.anchor}`);
453
+ if (issue.line)
454
+ console.log(` Line: ${issue.line}`);
455
+ if (issue.suggestion && options.verbose)
456
+ console.log(` Suggestion: ${issue.suggestion}`);
457
+ console.log();
458
+ });
459
+ }
460
+ if (infos.length > 0 && options.verbose) {
461
+ console.log(`\nℹ️ Info (${infos.length}):\n`);
462
+ infos.forEach((issue, index) => {
463
+ console.log(`${index + 1}. [${issue.rule}] ${issue.message}`);
464
+ if (issue.anchor)
465
+ console.log(` Anchor: ${issue.anchor}`);
466
+ if (issue.line)
467
+ console.log(` Line: ${issue.line}`);
468
+ if (issue.suggestion)
469
+ console.log(` Suggestion: ${issue.suggestion}`);
470
+ console.log();
471
+ });
472
+ }
473
+ // Summary
474
+ console.log("Summary:");
475
+ console.log(` Errors: ${errors.length}`);
476
+ console.log(` Warnings: ${warnings.length}`);
477
+ console.log(` Info: ${infos.length}`);
478
+ if (fixSummary?.attempted) {
479
+ console.log(` Fixes applied: ${fixSummary.fixesApplied}`);
480
+ console.log(` File modified: ${fixSummary.fileModified ? "yes" : "no"}`);
481
+ }
482
+ console.log();
483
+ // Exit with error code if there are errors
484
+ if (errors.length > 0) {
485
+ console.log("❌ Linting failed due to errors.\n");
486
+ return 1;
487
+ }
488
+ else if (warnings.length > 0) {
489
+ console.log("⚠️ Linting passed with warnings.\n");
490
+ return 0;
491
+ }
492
+ else {
493
+ console.log("✓ Linting passed.\n");
494
+ return 0;
495
+ }
496
+ }
497
+ if (require.main === module) {
498
+ void (0, cli_runtime_1.runCliMain)(main, { errorPrefix: "spec-lint failed" });
499
+ }
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * spec-parse CLI Tool
4
+ *
5
+ * Parse specification markdown and extract all valid anchors.
6
+ *
7
+ * Usage: npm run spec-parse <spec-file-path>
8
+ * Example: npm run spec-parse specs/SPECIFICATION.md
9
+ */
10
+ declare function main(argv?: string[]): number;
11
+ export { main };
@@ -0,0 +1,162 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * spec-parse CLI Tool
5
+ *
6
+ * Parse specification markdown and extract all valid anchors.
7
+ *
8
+ * Usage: npm run spec-parse <spec-file-path>
9
+ * Example: npm run spec-parse specs/SPECIFICATION.md
10
+ */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || (function () {
28
+ var ownKeys = function(o) {
29
+ ownKeys = Object.getOwnPropertyNames || function (o) {
30
+ var ar = [];
31
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
+ return ar;
33
+ };
34
+ return ownKeys(o);
35
+ };
36
+ return function (mod) {
37
+ if (mod && mod.__esModule) return mod;
38
+ var result = {};
39
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
+ __setModuleDefault(result, mod);
41
+ return result;
42
+ };
43
+ })();
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.main = main;
46
+ const fs = __importStar(require("fs"));
47
+ const path = __importStar(require("path"));
48
+ const agents_1 = require("@spekn/agents");
49
+ const cli_runtime_1 = require("./utils/cli-runtime");
50
+ function parseArgs(args) {
51
+ const options = {};
52
+ let filePath;
53
+ for (let i = 0; i < args.length; i++) {
54
+ const arg = args[i];
55
+ if (arg === "--verbose" || arg === "-v") {
56
+ options.verbose = true;
57
+ }
58
+ else if (arg === "--json" || arg === "-j") {
59
+ options.json = true;
60
+ }
61
+ else if (!arg.startsWith("-")) {
62
+ filePath = arg;
63
+ }
64
+ }
65
+ if (!filePath) {
66
+ throw new Error("missing required argument: <spec-file-path>");
67
+ }
68
+ return { filePath, options };
69
+ }
70
+ function printHelp() {
71
+ console.log(`
72
+ spec-parse - Parse specification markdown and extract anchors
73
+
74
+ USAGE:
75
+ npm run spec-parse <spec-file-path> [OPTIONS]
76
+
77
+ ARGUMENTS:
78
+ <spec-file-path> Path to the specification markdown file
79
+
80
+ OPTIONS:
81
+ -v, --verbose Show detailed output
82
+ -j, --json Output in JSON format
83
+ -h, --help Show this help message
84
+
85
+ EXAMPLES:
86
+ npm run spec-parse specs/SPECIFICATION.md
87
+ npm run spec-parse specs/WORKFLOW.md --json
88
+ npm run spec-parse specs/DECISIONS.md --verbose
89
+ `);
90
+ }
91
+ function main(argv) {
92
+ const args = argv ?? process.argv.slice(2);
93
+ if ((0, cli_runtime_1.hasHelpFlag)(args)) {
94
+ printHelp();
95
+ return 0;
96
+ }
97
+ let filePath;
98
+ let options;
99
+ try {
100
+ ({ filePath, options } = parseArgs(args));
101
+ }
102
+ catch {
103
+ console.error("Error: Missing required argument <spec-file-path>");
104
+ printHelp();
105
+ return 1;
106
+ }
107
+ // Resolve file path
108
+ const resolvedPath = path.resolve(process.cwd(), filePath);
109
+ // Check if file exists
110
+ if (!fs.existsSync(resolvedPath)) {
111
+ console.error(`Error: File not found: ${resolvedPath}`);
112
+ return 1;
113
+ }
114
+ // Read file content
115
+ let content;
116
+ try {
117
+ content = fs.readFileSync(resolvedPath, "utf-8");
118
+ }
119
+ catch (error) {
120
+ console.error(`Error reading file: ${(0, cli_runtime_1.errorMessage)(error)}`);
121
+ return 1;
122
+ }
123
+ // Extract anchors
124
+ const anchors = (0, agents_1.extractAnchors)(content);
125
+ if (anchors.length === 0) {
126
+ console.log("No anchors found in the specification.");
127
+ return 0;
128
+ }
129
+ // Output results
130
+ if (options.json) {
131
+ console.log(JSON.stringify(anchors, null, 2));
132
+ }
133
+ else {
134
+ console.log(`\nFound ${anchors.length} anchor(s) in ${path.basename(filePath)}:\n`);
135
+ // Create table data
136
+ const tableData = anchors.map((anchor) => ({
137
+ Anchor: anchor.anchor,
138
+ Level: "H" + anchor.level,
139
+ Line: anchor.lineNumber,
140
+ Heading: options.verbose
141
+ ? anchor.headingText
142
+ : anchor.headingText.substring(0, 60) +
143
+ (anchor.headingText.length > 60 ? "..." : ""),
144
+ }));
145
+ // Print table
146
+ console.table(tableData);
147
+ if (options.verbose) {
148
+ console.log("\nAnchor Details:");
149
+ anchors.forEach((anchor, index) => {
150
+ console.log(`\n${index + 1}. ${anchor.anchor}`);
151
+ console.log(` Level: ${anchor.level}`);
152
+ console.log(` Line: ${anchor.lineNumber}`);
153
+ console.log(` Heading: ${anchor.headingText}`);
154
+ });
155
+ }
156
+ console.log(`\nTotal: ${anchors.length} anchor(s)\n`);
157
+ }
158
+ return 0;
159
+ }
160
+ if (require.main === module) {
161
+ void (0, cli_runtime_1.runCliMain)(main, { errorPrefix: "spec-parse failed" });
162
+ }
@@ -0,0 +1,5 @@
1
+ export declare function config(): void;
2
+ declare const _default: {
3
+ config: typeof config;
4
+ };
5
+ export default _default;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.config = config;
4
+ // No-op stub for dotenv — prevents noisy logs from bundled @spekn/shared code.
5
+ function config() { }
6
+ exports.default = { config };