gspec 1.16.0 → 1.17.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.
- package/README.md +33 -6
- package/bin/gspec.js +214 -33
- package/commands/gspec.analyze.md +9 -0
- package/commands/gspec.audit.md +84 -9
- package/commands/gspec.feature.md +10 -0
- package/commands/gspec.implement.md +19 -8
- package/commands/gspec.tasks.md +150 -0
- package/dist/antigravity/gspec-analyze/SKILL.md +9 -0
- package/dist/antigravity/gspec-audit/SKILL.md +85 -10
- package/dist/antigravity/gspec-feature/SKILL.md +10 -0
- package/dist/antigravity/gspec-implement/SKILL.md +19 -8
- package/dist/antigravity/gspec-tasks/SKILL.md +154 -0
- package/dist/claude/gspec-analyze/SKILL.md +9 -0
- package/dist/claude/gspec-audit/SKILL.md +85 -10
- package/dist/claude/gspec-feature/SKILL.md +10 -0
- package/dist/claude/gspec-implement/SKILL.md +19 -8
- package/dist/claude/gspec-tasks/SKILL.md +155 -0
- package/dist/codex/gspec-analyze/SKILL.md +9 -0
- package/dist/codex/gspec-audit/SKILL.md +85 -10
- package/dist/codex/gspec-feature/SKILL.md +10 -0
- package/dist/codex/gspec-implement/SKILL.md +19 -8
- package/dist/codex/gspec-tasks/SKILL.md +154 -0
- package/dist/cursor/gspec-analyze.mdc +9 -0
- package/dist/cursor/gspec-audit.mdc +85 -10
- package/dist/cursor/gspec-feature.mdc +10 -0
- package/dist/cursor/gspec-implement.mdc +19 -8
- package/dist/cursor/gspec-tasks.mdc +153 -0
- package/dist/opencode/gspec-analyze/SKILL.md +9 -0
- package/dist/opencode/gspec-audit/SKILL.md +85 -10
- package/dist/opencode/gspec-feature/SKILL.md +10 -0
- package/dist/opencode/gspec-implement/SKILL.md +19 -8
- package/dist/opencode/gspec-tasks/SKILL.md +154 -0
- package/package.json +1 -1
- package/templates/spec-sync.md +2 -1
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ These documents become the shared context for all subsequent AI interactions. Wh
|
|
|
25
25
|
|
|
26
26
|
The only commands you *need* are the four fundamentals and `/gspec-implement`. Everything else exists to help when your project calls for it.
|
|
27
27
|
|
|
28
|
-
The fundamentals give your AI tool enough context to build well — it knows what the product is, how it should look, what technologies to use, and what engineering standards to follow. From there, `/gspec-implement` can take a plain-language description and start building. The remaining commands — `/gspec-research`, `/gspec-feature`, `/gspec-architect`, `/gspec-analyze`, and `/gspec-audit` — add structure and rigor when the scope or complexity warrants it.
|
|
28
|
+
The fundamentals give your AI tool enough context to build well — it knows what the product is, how it should look, what technologies to use, and what engineering standards to follow. From there, `/gspec-implement` can take a plain-language description and start building. The remaining commands — `/gspec-research`, `/gspec-feature`, `/gspec-architect`, `/gspec-tasks`, `/gspec-analyze`, and `/gspec-audit` — add structure and rigor when the scope or complexity warrants it.
|
|
29
29
|
|
|
30
30
|
```mermaid
|
|
31
31
|
flowchart LR
|
|
@@ -42,11 +42,14 @@ flowchart LR
|
|
|
42
42
|
Architect["4. Architect
|
|
43
43
|
technical blueprint"]
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
Plan["5. Plan
|
|
46
|
+
ordered tasks"]
|
|
47
|
+
|
|
48
|
+
Analyze["6. Analyze & Audit
|
|
46
49
|
reconcile specs
|
|
47
50
|
check specs vs code"]
|
|
48
51
|
|
|
49
|
-
Build["
|
|
52
|
+
Build["7. Build
|
|
50
53
|
implement"]
|
|
51
54
|
|
|
52
55
|
Define --> Research
|
|
@@ -55,9 +58,12 @@ flowchart LR
|
|
|
55
58
|
Research --> Specify
|
|
56
59
|
Research --> Build
|
|
57
60
|
Specify --> Architect
|
|
61
|
+
Specify --> Plan
|
|
58
62
|
Specify --> Build
|
|
63
|
+
Architect --> Plan
|
|
59
64
|
Architect --> Analyze
|
|
60
65
|
Architect --> Build
|
|
66
|
+
Plan --> Build
|
|
61
67
|
Analyze --> Build
|
|
62
68
|
Build --> Define
|
|
63
69
|
|
|
@@ -65,6 +71,7 @@ flowchart LR
|
|
|
65
71
|
style Research fill:#a855f7,color:#fff,stroke:none
|
|
66
72
|
style Specify fill:#f59e0b,color:#fff,stroke:none
|
|
67
73
|
style Architect fill:#f59e0b,color:#fff,stroke:none
|
|
74
|
+
style Plan fill:#f59e0b,color:#fff,stroke:none
|
|
68
75
|
style Analyze fill:#f59e0b,color:#fff,stroke:none
|
|
69
76
|
style Build fill:#22c55e,color:#fff,stroke:none
|
|
70
77
|
```
|
|
@@ -105,7 +112,15 @@ Use `/gspec-feature` when you want detailed PRDs with prioritized capabilities a
|
|
|
105
112
|
|
|
106
113
|
Use `/gspec-architect` when your feature involves significant technical complexity — new data models, service boundaries, auth flows, or integration points that benefit from upfront design. It also **identifies technical gaps and ambiguities** in your specs and proposes solutions, so that `/gspec-implement` can focus on building rather than making architectural decisions. For straightforward features, `/gspec-implement` can make sound architectural decisions on its own using your `stack` and `practices` specs.
|
|
107
114
|
|
|
108
|
-
**5.
|
|
115
|
+
**5. Plan** *(optional)* — Decompose a feature PRD into ordered work.
|
|
116
|
+
|
|
117
|
+
| Command | Role | What it produces |
|
|
118
|
+
|---|---|---|
|
|
119
|
+
| `/gspec-tasks` | Engineering Lead | A sibling `gspec/features/<feature>.tasks.md` file with stable task IDs, explicit `deps:` lines, and `[P]` markers for parallel-safe work |
|
|
120
|
+
|
|
121
|
+
Use `/gspec-tasks` after `/gspec-feature` (and after `/gspec-architect` when it exists) for any feature large enough that build order matters or that has work which could legitimately run in parallel. The output is what `/gspec-implement` consumes — when a tasks file exists for an in-scope feature, implement plans phases from it, respecting deps and surfacing `[P]`-marked tasks for parallel execution. Trivial features can skip this step and go straight to `/gspec-implement`, which falls back to PRD-checkbox-driven planning.
|
|
122
|
+
|
|
123
|
+
**6. Analyze & Audit** *(optional)* — Reconcile discrepancies before building, and keep specs honest as the codebase evolves.
|
|
109
124
|
|
|
110
125
|
| Command | Role | What it does |
|
|
111
126
|
|---|---|---|
|
|
@@ -116,11 +131,11 @@ Use `/gspec-analyze` after `/gspec-architect` (or any time multiple specs exist)
|
|
|
116
131
|
|
|
117
132
|
Use `/gspec-audit` periodically — before a major release, after a long sprint, or any time you suspect docs have drifted from code. Audit reads package manifests, configs, source files, and test output, then asks you per-finding whether to update the spec to match the code, keep the spec and fix the code separately, or defer. Each finding is presented one at a time with the spec quote and the code evidence side by side. Audit never modifies code.
|
|
118
133
|
|
|
119
|
-
**
|
|
134
|
+
**7. Build** — Implement with full context.
|
|
120
135
|
|
|
121
136
|
| Command | Role | What it does |
|
|
122
137
|
|---|---|---|
|
|
123
|
-
| `/gspec-implement` | Senior Engineer | Reads all specs, plans the build order, and implements |
|
|
138
|
+
| `/gspec-implement` | Senior Engineer | Reads all specs (including any `*.tasks.md` files), plans the build order, and implements |
|
|
124
139
|
|
|
125
140
|
**Spec Sync** — gspec includes always-on spec sync that automatically keeps your specification documents in sync as the code evolves. This is installed alongside the skills and requires no manual intervention — when code changes affect spec-documented behavior, the sync rules prompt your AI tool to update the relevant gspec files.
|
|
126
141
|
|
|
@@ -182,6 +197,18 @@ Saved specs are organized by type in `~/.gspec/` (profiles, stacks, styles, prac
|
|
|
182
197
|
gspec restore playbook/my-starter
|
|
183
198
|
```
|
|
184
199
|
|
|
200
|
+
## Extensions
|
|
201
|
+
|
|
202
|
+
Author your own skills and have them auto-installed alongside the built-in `gspec-*` commands in every project. Extensions live in `~/.gspec/extensions/` as Markdown files with `name` and `description` frontmatter and the same shape as anything in `commands/`.
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
gspec extension save ./my-deploy.md # Install a local skill file as a user extension
|
|
206
|
+
gspec extension list # See what's installed
|
|
207
|
+
gspec extension remove my-deploy # Delete from ~/.gspec/extensions/
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
When you next run `npx gspec` in a project, the installer copies the built-in skills first, then emits each valid extension into the same per-platform install directory using the same formatting. Extension names that collide with built-in `gspec-*` skills are rejected with an error; malformed or duplicate extensions are skipped with a warning.
|
|
211
|
+
|
|
185
212
|
## Output Structure
|
|
186
213
|
|
|
187
214
|
All specifications live in a `gspec/` directory at your project root:
|
package/bin/gspec.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { program } from 'commander';
|
|
4
|
-
import { readdir, readFile, writeFile, mkdir, stat } from 'node:fs/promises';
|
|
4
|
+
import { readdir, readFile, writeFile, mkdir, stat, unlink } from 'node:fs/promises';
|
|
5
5
|
import { join, dirname, basename } from 'node:path';
|
|
6
6
|
import { homedir } from 'node:os';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
8
|
import { createInterface } from 'node:readline';
|
|
9
9
|
import chalk from 'chalk';
|
|
10
|
+
import { TARGETS as EMITTER_TARGETS } from '../scripts/emitters.js';
|
|
10
11
|
|
|
11
12
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
13
|
const DIST_DIR = join(__dirname, '..', 'dist');
|
|
@@ -28,38 +29,21 @@ const BANNER = `
|
|
|
28
29
|
${chalk.white('═════════════════════════════baller.software═══')}
|
|
29
30
|
`;
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
installDir: '.agent/skills',
|
|
47
|
-
label: 'Antigravity',
|
|
48
|
-
layout: 'directory',
|
|
49
|
-
},
|
|
50
|
-
codex: {
|
|
51
|
-
sourceDir: join(DIST_DIR, 'codex'),
|
|
52
|
-
installDir: '.agents/skills',
|
|
53
|
-
label: 'Codex',
|
|
54
|
-
layout: 'directory',
|
|
55
|
-
},
|
|
56
|
-
opencode: {
|
|
57
|
-
sourceDir: join(DIST_DIR, 'opencode'),
|
|
58
|
-
installDir: '.opencode/skills',
|
|
59
|
-
label: 'Open Code',
|
|
60
|
-
layout: 'directory',
|
|
61
|
-
},
|
|
62
|
-
};
|
|
32
|
+
// Derive install-side TARGETS from the shared emitter config so we have one source of truth.
|
|
33
|
+
// `sourceDir` is computed from the shared `distSubdir`; `emit` is reused for installing user extensions.
|
|
34
|
+
const TARGETS = Object.fromEntries(
|
|
35
|
+
Object.entries(EMITTER_TARGETS).map(([key, t]) => [key, {
|
|
36
|
+
...t,
|
|
37
|
+
sourceDir: join(DIST_DIR, t.distSubdir),
|
|
38
|
+
}]),
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
// Names emitted by core gspec; user extensions cannot collide with these.
|
|
42
|
+
const BUILTIN_SKILL_NAMES = new Set([
|
|
43
|
+
'gspec-profile', 'gspec-feature', 'gspec-tasks', 'gspec-style',
|
|
44
|
+
'gspec-stack', 'gspec-practices', 'gspec-architect', 'gspec-analyze',
|
|
45
|
+
'gspec-audit', 'gspec-research', 'gspec-implement', 'gspec-migrate',
|
|
46
|
+
]);
|
|
63
47
|
|
|
64
48
|
const TARGET_CHOICES = [
|
|
65
49
|
{ key: '1', name: 'claude', label: 'Claude Code' },
|
|
@@ -1397,6 +1381,8 @@ program
|
|
|
1397
1381
|
|
|
1398
1382
|
await install(targetName, process.cwd());
|
|
1399
1383
|
|
|
1384
|
+
await installExtensions(targetName, process.cwd());
|
|
1385
|
+
|
|
1400
1386
|
await seedFromSavedSpecs(process.cwd());
|
|
1401
1387
|
|
|
1402
1388
|
await installSpecSync(targetName, process.cwd());
|
|
@@ -1424,6 +1410,176 @@ program
|
|
|
1424
1410
|
}
|
|
1425
1411
|
});
|
|
1426
1412
|
|
|
1413
|
+
// --- Extensions ---
|
|
1414
|
+
|
|
1415
|
+
const EXTENSIONS_DIR = join(GSPEC_HOME, 'extensions');
|
|
1416
|
+
const EXTENSION_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
1417
|
+
|
|
1418
|
+
async function loadExtensions() {
|
|
1419
|
+
let entries;
|
|
1420
|
+
try {
|
|
1421
|
+
entries = await readdir(EXTENSIONS_DIR);
|
|
1422
|
+
} catch (e) {
|
|
1423
|
+
if (e.code === 'ENOENT') return [];
|
|
1424
|
+
throw e;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
const files = entries.filter((f) => f.endsWith('.md'));
|
|
1428
|
+
const loaded = [];
|
|
1429
|
+
for (const file of files) {
|
|
1430
|
+
const path = join(EXTENSIONS_DIR, file);
|
|
1431
|
+
const content = await readFile(path, 'utf-8');
|
|
1432
|
+
const { fields, body } = parseFrontmatter(content);
|
|
1433
|
+
loaded.push({ file, path, fields, body, content });
|
|
1434
|
+
}
|
|
1435
|
+
return loaded;
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
function validateExtension(ext) {
|
|
1439
|
+
const errors = [];
|
|
1440
|
+
if (!ext.fields.name) errors.push("missing 'name' frontmatter");
|
|
1441
|
+
if (!ext.fields.description) errors.push("missing 'description' frontmatter");
|
|
1442
|
+
if (ext.fields.name && !EXTENSION_NAME_RE.test(ext.fields.name)) {
|
|
1443
|
+
errors.push(`invalid name "${ext.fields.name}" (must match /^[a-z0-9][a-z0-9-]*$/)`);
|
|
1444
|
+
}
|
|
1445
|
+
if (ext.fields.name && BUILTIN_SKILL_NAMES.has(ext.fields.name)) {
|
|
1446
|
+
errors.push(`name "${ext.fields.name}" collides with a built-in gspec skill`);
|
|
1447
|
+
}
|
|
1448
|
+
return errors;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
async function installExtensions(targetName, cwd) {
|
|
1452
|
+
const extensions = await loadExtensions();
|
|
1453
|
+
if (extensions.length === 0) return;
|
|
1454
|
+
|
|
1455
|
+
const target = TARGETS[targetName];
|
|
1456
|
+
const valid = [];
|
|
1457
|
+
for (const ext of extensions) {
|
|
1458
|
+
const errors = validateExtension(ext);
|
|
1459
|
+
if (errors.length > 0) {
|
|
1460
|
+
console.warn(chalk.yellow(` ! Skipping extension ${ext.file}: ${errors.join('; ')}`));
|
|
1461
|
+
continue;
|
|
1462
|
+
}
|
|
1463
|
+
valid.push(ext);
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
// Resolve duplicates by name (last write wins, with a warning)
|
|
1467
|
+
const byName = new Map();
|
|
1468
|
+
for (const ext of valid) {
|
|
1469
|
+
if (byName.has(ext.fields.name)) {
|
|
1470
|
+
console.warn(chalk.yellow(
|
|
1471
|
+
` ! Extension name "${ext.fields.name}" defined in two files; ${ext.file} overrides ${byName.get(ext.fields.name).file}`
|
|
1472
|
+
));
|
|
1473
|
+
}
|
|
1474
|
+
byName.set(ext.fields.name, ext);
|
|
1475
|
+
}
|
|
1476
|
+
const finalSet = Array.from(byName.values());
|
|
1477
|
+
if (finalSet.length === 0) return;
|
|
1478
|
+
|
|
1479
|
+
console.log(chalk.bold(`\nInstalling ${finalSet.length} user extension${finalSet.length === 1 ? '' : 's'} from ~/.gspec/extensions/...\n`));
|
|
1480
|
+
const installPath = join(cwd, target.installDir);
|
|
1481
|
+
for (const ext of finalSet) {
|
|
1482
|
+
const meta = { name: ext.fields.name, description: ext.fields.description };
|
|
1483
|
+
await target.emit(installPath, ext.body, meta);
|
|
1484
|
+
console.log(` ${chalk.green('+')} ${ext.fields.name} ${chalk.dim('(extension)')}`);
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
async function extensionList() {
|
|
1489
|
+
console.log(BANNER);
|
|
1490
|
+
const extensions = await loadExtensions();
|
|
1491
|
+
if (extensions.length === 0) {
|
|
1492
|
+
console.log(chalk.dim('\n No extensions installed in ~/.gspec/extensions/.\n'));
|
|
1493
|
+
console.log(chalk.dim(' Use "gspec extension save <path>" to install one.\n'));
|
|
1494
|
+
return;
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
console.log(chalk.bold(`\n ${extensions.length} extension${extensions.length === 1 ? '' : 's'} in ~/.gspec/extensions/:\n`));
|
|
1498
|
+
for (const ext of extensions) {
|
|
1499
|
+
const errors = validateExtension(ext);
|
|
1500
|
+
const name = ext.fields.name || chalk.dim('(no name)');
|
|
1501
|
+
const desc = ext.fields.description ? chalk.dim(` — ${ext.fields.description}`) : '';
|
|
1502
|
+
if (errors.length > 0) {
|
|
1503
|
+
console.log(` ${chalk.yellow('!')} ${ext.file} → ${name}${desc}`);
|
|
1504
|
+
console.log(` ${chalk.yellow(errors.join('; '))}`);
|
|
1505
|
+
} else {
|
|
1506
|
+
console.log(` ${chalk.green('•')} ${name}${desc}`);
|
|
1507
|
+
console.log(` ${chalk.dim(ext.file)}`);
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
console.log();
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
async function extensionSave(srcPath) {
|
|
1514
|
+
console.log(BANNER);
|
|
1515
|
+
|
|
1516
|
+
if (!srcPath) {
|
|
1517
|
+
console.error(chalk.red('\n Usage: gspec extension save <path-to-extension.md>\n'));
|
|
1518
|
+
process.exit(1);
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
let content;
|
|
1522
|
+
try {
|
|
1523
|
+
content = await readFile(srcPath, 'utf-8');
|
|
1524
|
+
} catch (e) {
|
|
1525
|
+
if (e.code === 'ENOENT') {
|
|
1526
|
+
console.error(chalk.red(`\n File not found: ${srcPath}\n`));
|
|
1527
|
+
process.exit(1);
|
|
1528
|
+
}
|
|
1529
|
+
throw e;
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
const { fields } = parseFrontmatter(content);
|
|
1533
|
+
const ext = { file: basename(srcPath), fields };
|
|
1534
|
+
const errors = validateExtension(ext);
|
|
1535
|
+
if (errors.length > 0) {
|
|
1536
|
+
console.error(chalk.red(`\n Cannot save extension: ${errors.join('; ')}\n`));
|
|
1537
|
+
process.exit(1);
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
await mkdir(EXTENSIONS_DIR, { recursive: true });
|
|
1541
|
+
const destPath = join(EXTENSIONS_DIR, `${fields.name}.md`);
|
|
1542
|
+
|
|
1543
|
+
try {
|
|
1544
|
+
await stat(destPath);
|
|
1545
|
+
const overwrite = await promptConfirm(chalk.yellow(`\n Extension "${fields.name}" already exists. Overwrite? [y/N]: `));
|
|
1546
|
+
if (!overwrite) {
|
|
1547
|
+
console.log(chalk.dim('\n Cancelled.\n'));
|
|
1548
|
+
return;
|
|
1549
|
+
}
|
|
1550
|
+
} catch (e) {
|
|
1551
|
+
if (e.code !== 'ENOENT') throw e;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
await writeFile(destPath, content, 'utf-8');
|
|
1555
|
+
console.log(chalk.green(`\n ✓ Saved extension to ~/.gspec/extensions/${fields.name}.md\n`));
|
|
1556
|
+
console.log(chalk.dim(` It will be installed alongside core skills the next time you run "gspec" in a project.\n`));
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
async function extensionRemove(name) {
|
|
1560
|
+
console.log(BANNER);
|
|
1561
|
+
|
|
1562
|
+
if (!name) {
|
|
1563
|
+
console.error(chalk.red('\n Usage: gspec extension remove <name>\n'));
|
|
1564
|
+
process.exit(1);
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
const path = join(EXTENSIONS_DIR, `${name}.md`);
|
|
1568
|
+
try {
|
|
1569
|
+
await stat(path);
|
|
1570
|
+
} catch (e) {
|
|
1571
|
+
if (e.code === 'ENOENT') {
|
|
1572
|
+
console.error(chalk.red(`\n Extension not found: ~/.gspec/extensions/${name}.md\n`));
|
|
1573
|
+
process.exit(1);
|
|
1574
|
+
}
|
|
1575
|
+
throw e;
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
await unlink(path);
|
|
1579
|
+
console.log(chalk.green(`\n ✓ Removed ~/.gspec/extensions/${name}.md\n`));
|
|
1580
|
+
console.log(chalk.dim(` Already-installed copies in projects (.claude/skills/, .cursor/commands/, etc.) are left in place — delete them manually if desired.\n`));
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1427
1583
|
program
|
|
1428
1584
|
.command('save')
|
|
1429
1585
|
.description('Save a gspec spec to ~/.gspec for reuse across projects')
|
|
@@ -1446,4 +1602,29 @@ program
|
|
|
1446
1602
|
await createPlaybook();
|
|
1447
1603
|
});
|
|
1448
1604
|
|
|
1605
|
+
const extensionCmd = program
|
|
1606
|
+
.command('extension')
|
|
1607
|
+
.description('Manage user-authored gspec extension skills in ~/.gspec/extensions/');
|
|
1608
|
+
|
|
1609
|
+
extensionCmd
|
|
1610
|
+
.command('list')
|
|
1611
|
+
.description('List installed extensions')
|
|
1612
|
+
.action(async () => {
|
|
1613
|
+
await extensionList();
|
|
1614
|
+
});
|
|
1615
|
+
|
|
1616
|
+
extensionCmd
|
|
1617
|
+
.command('save <path>')
|
|
1618
|
+
.description('Save a local .md skill file as a user extension in ~/.gspec/extensions/')
|
|
1619
|
+
.action(async (path) => {
|
|
1620
|
+
await extensionSave(path);
|
|
1621
|
+
});
|
|
1622
|
+
|
|
1623
|
+
extensionCmd
|
|
1624
|
+
.command('remove <name>')
|
|
1625
|
+
.description('Remove a user extension from ~/.gspec/extensions/ (does not uninstall already-emitted copies)')
|
|
1626
|
+
.action(async (name) => {
|
|
1627
|
+
await extensionRemove(name);
|
|
1628
|
+
});
|
|
1629
|
+
|
|
1449
1630
|
program.parse();
|
|
@@ -31,6 +31,7 @@ Read **every** available gspec document in this order:
|
|
|
31
31
|
6. `gspec/architecture.md` — Technical blueprint: project structure, data model, API design, environment
|
|
32
32
|
7. `gspec/research.md` — Competitive analysis and feature proposals
|
|
33
33
|
8. `gspec/features/*.md` — Individual feature requirements and dependencies
|
|
34
|
+
9. `gspec/features/*.tasks.md` — For any feature that has a tasks file, read it alongside the PRD. Tasks files declare a build order and parallelism strategy that must stay consistent with the PRD's capabilities
|
|
34
35
|
|
|
35
36
|
If fewer than two spec files exist, inform the user that there is nothing to cross-reference and stop.
|
|
36
37
|
|
|
@@ -75,6 +76,14 @@ Systematically compare specs against each other. Look for these categories of di
|
|
|
75
76
|
- Acceptance criteria in a feature PRD contradict architectural decisions
|
|
76
77
|
- Edge cases handled differently across specs
|
|
77
78
|
|
|
79
|
+
#### Tasks ↔ PRD Conflicts
|
|
80
|
+
For any feature that has a `gspec/features/<feature>.tasks.md` file, validate the tasks file against its PRD:
|
|
81
|
+
- A task's `covers:` line quotes capability text that does not exist in the PRD (orphan task)
|
|
82
|
+
- A PRD capability is not `covers:`-referenced by any task in the tasks file (orphan capability — every unchecked capability must be covered by at least one task)
|
|
83
|
+
- A task's checkbox is `- [x]` but its covered capability is still `- [ ]` in the PRD, or vice versa (state inconsistency)
|
|
84
|
+
- A task's `deps:` references a task ID that does not exist in the file
|
|
85
|
+
- The tasks file's `feature:` frontmatter slug does not match its filename's feature slug
|
|
86
|
+
|
|
78
87
|
**Do NOT flag:**
|
|
79
88
|
- Minor wording or style differences that don't change meaning
|
|
80
89
|
- Missing information (gaps are for `gspec-architect` to handle)
|
package/commands/gspec.audit.md
CHANGED
|
@@ -12,11 +12,12 @@ You should:
|
|
|
12
12
|
- Read and deeply internalize all available gspec documents
|
|
13
13
|
- Inspect the actual codebase — package manifests, source files, tests, configs, stylesheets, routes, data models, and git history where relevant
|
|
14
14
|
- Identify concrete drift — not stylistic differences, but substantive mismatches where the spec and the code disagree on a fact, technology, behavior, or requirement
|
|
15
|
+
- Identify **orphan capabilities** — coherent feature-level capabilities the code implements that no feature PRD describes
|
|
15
16
|
- Present each discrepancy to the user one at a time, clearly showing what each side says
|
|
16
17
|
- Offer resolution options with a recommendation
|
|
17
18
|
- Wait for the user's decision before moving to the next discrepancy
|
|
18
|
-
- Update the affected spec files to reflect each resolution
|
|
19
|
-
- Never modify code as part of this command — audit only updates specs
|
|
19
|
+
- Update the affected spec files to reflect each resolution; for orphan capabilities, draft a new feature PRD in `gspec/features/` when the user accepts
|
|
20
|
+
- Never modify code as part of this command — audit only updates specs and adds new feature PRDs
|
|
20
21
|
|
|
21
22
|
---
|
|
22
23
|
|
|
@@ -34,6 +35,7 @@ Read **every** available gspec document in this order:
|
|
|
34
35
|
6. `gspec/architecture.md` — Technical blueprint: project structure, data model, API design, environment
|
|
35
36
|
7. `gspec/research.md` — Competitive analysis and feature proposals (informational only — not audited against code)
|
|
36
37
|
8. `gspec/features/*.md` — Individual feature requirements, priorities, and capability checkboxes
|
|
38
|
+
9. `gspec/features/*.tasks.md` — When a feature has a tasks file, also read it. Tasks files declare a per-task execution checkbox state and `covers:` traceability to PRD capabilities; both are subject to drift checks against the code
|
|
37
39
|
|
|
38
40
|
If the `gspec/` directory is empty, inform the user that there are no specs to audit and stop.
|
|
39
41
|
|
|
@@ -55,6 +57,11 @@ Build a picture of what the code **actually** is. Read the following, as availab
|
|
|
55
57
|
- Component library usage — what the UI actually imports and composes
|
|
56
58
|
- Test files — what framework, what coverage areas
|
|
57
59
|
|
|
60
|
+
**Capability mapping**
|
|
61
|
+
- Build a short mental list of the coherent, user-visible capabilities the code implements — not low-level details, but feature-level units (e.g. "users can export data as CSV", "admin can invite team members", "documents have version history"). A capability typically shows up as a cluster: a route + handler + UI surface + test, or an end-to-end flow.
|
|
62
|
+
- For each capability, note whether it appears in any `gspec/features/*.md` PRD (by feature name, capability checkbox, or acceptance criteria). Capabilities with no PRD coverage are candidates for the **Orphan Capability** category in Phase 3.
|
|
63
|
+
- Be deliberately conservative: a utility helper, an internal admin script, or a piece of plumbing is **not** a capability worth a PRD. Only flag things a user (end user, admin, integrator) would recognize as a feature.
|
|
64
|
+
|
|
58
65
|
**Version control signals** (use sparingly; git log is authoritative only where the spec makes explicit claims about workflow)
|
|
59
66
|
- `git log --oneline -n 20` for recent commit-message style (only if practices.md makes claims about commit conventions)
|
|
60
67
|
- `git config --local --get-regexp '^branch\.'` / branch listing for branching strategy (only if practices.md makes claims about branching)
|
|
@@ -99,6 +106,22 @@ Systematically compare specs against the evidence from Phase 2. Look for these c
|
|
|
99
106
|
- A feature PRD's acceptance criteria describe behavior that the code explicitly handles differently
|
|
100
107
|
- A feature PRD references a data field, endpoint, or UI element whose implementation has diverged (e.g., PRD says "users can filter by tag", code has filter-by-category)
|
|
101
108
|
|
|
109
|
+
#### Tasks Drift (only when a tasks file exists for the feature)
|
|
110
|
+
- A task is marked `- [x]` in the tasks file but the code does not implement what the task describes
|
|
111
|
+
- A task is marked `- [ ]` but the code clearly implements it (the checkbox should be updated)
|
|
112
|
+
- A task's `covers:` references capability text the PRD no longer contains (the PRD was edited but the tasks file wasn't refreshed — recommend regenerating via `/gspec-tasks`)
|
|
113
|
+
- A capability is marked `- [x]` in the PRD but one or more of its covering tasks is still `- [ ]` (or vice versa) — flag the inconsistency and recommend the user reconcile state
|
|
114
|
+
|
|
115
|
+
#### Orphan Capability (code implements a feature that has no PRD)
|
|
116
|
+
- The code ships a coherent, user-visible capability that no `gspec/features/*.md` PRD describes
|
|
117
|
+
- Evidence is typically a cluster — a route + handler + UI surface + test — that adds up to something a user would call a feature
|
|
118
|
+
- An orphan capability is **not** the same as Feature Drift: drift is divergence within a specced feature; an orphan is an entirely unspecced feature
|
|
119
|
+
- Use the **capability mapping** from Phase 2 as your candidate list. Filter out:
|
|
120
|
+
- Internal utilities, admin scripts, dev tooling, or plumbing the user never sees
|
|
121
|
+
- Capabilities that *are* covered by an existing PRD even if checkboxes are stale (those are Feature Drift, not orphans)
|
|
122
|
+
- Capabilities that are partial enough that calling them a "feature" overstates them (note the partial work in the audit summary instead)
|
|
123
|
+
- The recommended resolution is to draft a new feature PRD in `gspec/features/` so the capability is captured, its checkboxes can drive future audits, and `gspec-implement` can extend it correctly
|
|
124
|
+
|
|
102
125
|
#### Profile Drift (rare; treat conservatively)
|
|
103
126
|
- The profile's stated audience, scope, or value proposition conflicts with what the product actually does in code (e.g., profile says "B2B only" but the code has a consumer signup flow)
|
|
104
127
|
- **Profile drift is usually a signal to update the product, not the spec.** Flag profile drift for user discussion rather than recommending an automatic spec update.
|
|
@@ -125,7 +148,7 @@ For each discrepancy, present:
|
|
|
125
148
|
```
|
|
126
149
|
### Drift [N]: [Brief title]
|
|
127
150
|
|
|
128
|
-
**Category:** [Stack / Architecture / Style / Practice / Feature / Profile]
|
|
151
|
+
**Category:** [Stack / Architecture / Style / Practice / Feature / Orphan Capability / Profile]
|
|
129
152
|
|
|
130
153
|
**Spec says:**
|
|
131
154
|
- **[File, section]**: [exact quote or precise summary]
|
|
@@ -145,6 +168,33 @@ For each discrepancy, present:
|
|
|
145
168
|
Which would you like?
|
|
146
169
|
```
|
|
147
170
|
|
|
171
|
+
For an **Orphan Capability** finding, the presentation differs slightly — there is no "spec says" side, and the resolution options are different:
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
### Drift [N]: Orphan Capability — [Capability name]
|
|
175
|
+
|
|
176
|
+
**Category:** Orphan Capability
|
|
177
|
+
|
|
178
|
+
**Spec says:** *(no PRD covers this capability)*
|
|
179
|
+
|
|
180
|
+
**Code shows:**
|
|
181
|
+
- **Capability:** [one-sentence description in user-facing terms]
|
|
182
|
+
- **Evidence:** [route(s), handler file(s), UI file(s), test file(s) — concrete paths]
|
|
183
|
+
- **Scope estimate:** [trivial / focused single feature / large enough to need decomposition]
|
|
184
|
+
|
|
185
|
+
**Why this matters:** Without a PRD, future audits can't track this capability's completeness, `gspec-implement` won't know how to extend it correctly, and the team has no documented intent to compare against.
|
|
186
|
+
|
|
187
|
+
**Recommended action:** Draft a new feature PRD in `gspec/features/` so the capability is captured.
|
|
188
|
+
|
|
189
|
+
**Options:**
|
|
190
|
+
1. **Draft a feature PRD now** — Audit will create `gspec/features/<slug>.md` following the gspec-feature schema, marking implemented capabilities as `- [x]` based on the code evidence. *(See Phase 5 for the inline drafting protocol.)*
|
|
191
|
+
2. **Defer to `/gspec-feature` later** — Audit notes this in the code-follow-up summary so you can run `/gspec-feature` on it as a separate, deeper conversation.
|
|
192
|
+
3. **Not actually a feature** — The code is internal plumbing or out of scope; audit drops the finding and won't re-flag it (note this back to the user as a hint they may want to add a comment in the code so future audits know).
|
|
193
|
+
4. **Defer** — Skip for now.
|
|
194
|
+
|
|
195
|
+
Which would you like?
|
|
196
|
+
```
|
|
197
|
+
|
|
148
198
|
**Wait for the user's response before proceeding.** The user may:
|
|
149
199
|
- Choose an option by number
|
|
150
200
|
- Propose a different resolution (e.g., partially update the spec)
|
|
@@ -164,31 +214,56 @@ When updating specs to match the code:
|
|
|
164
214
|
- **Do not rewrite sections** — if a one-line change resolves the drift, make a one-line change
|
|
165
215
|
- **Do not add changelog annotations** — git history captures what changed
|
|
166
216
|
|
|
217
|
+
#### Drafting a new feature PRD for an Orphan Capability
|
|
218
|
+
|
|
219
|
+
When the user picks option 1 ("Draft a feature PRD now") for an Orphan Capability finding, audit creates a new file in `gspec/features/`. The drafting follows the **same schema and rules as `gspec-feature`** — do not invent a different format. Specifically:
|
|
220
|
+
|
|
221
|
+
- **Filename:** kebab-case slug derived from the capability name, e.g. `csv-export.md`, `team-invitations.md`. Confirm the slug with the user before writing if it's not obvious.
|
|
222
|
+
- **Frontmatter:** the file must start with
|
|
223
|
+
```
|
|
224
|
+
---
|
|
225
|
+
spec-version: <<<SPEC_VERSION>>>
|
|
226
|
+
---
|
|
227
|
+
```
|
|
228
|
+
followed by the main heading.
|
|
229
|
+
- **Required sections** (in this order, no extras): Overview, Users & Use Cases, Scope, Capabilities, Dependencies, Assumptions & Risks, Success Metrics, Implementation Context.
|
|
230
|
+
- **Capabilities section is the load-bearing one for audit:** list each user-visible capability the code already implements as a checkbox, and mark it `- [x]` when the code clearly satisfies it. Include 2–4 brief acceptance criteria per capability based on what the code actually does (read tests and handlers to extract these). If a capability is only partially implemented, leave it `- [ ]` and note the gap.
|
|
231
|
+
- **Priority:** assign `P0`/`P1`/`P2` based on the capability's apparent centrality. Lean toward `P0` for capabilities the code clearly treats as core; `P1`/`P2` for ancillary ones.
|
|
232
|
+
- **Technology agnosticism:** the PRD must not name specific frameworks, libraries, databases, or services even though you derived it from concrete code. Use generic terms ("data store", "API", "authentication service"). Refer to `gspec-feature`'s technology-agnostic vocabulary list if needed.
|
|
233
|
+
- **Portability:** do not reference project-specific personas, design system details, or stack choices. Use generic role descriptions ("end users", "administrators").
|
|
234
|
+
- **Resolve ambiguity inline before writing:** if the code's intent is unclear (e.g., is this admin-only or for all users? is this experimental or shipped?), ask the user 1–2 targeted questions in chat *before* writing the file. Do not embed unresolved questions in the PRD.
|
|
235
|
+
- **Implementation Context block:** include the standard verbatim note at the bottom (see `gspec-feature`'s section 8).
|
|
236
|
+
- **Decomposition:** if the orphan capability is actually a *cluster* of distinct features (audit's "Scope estimate" was "large enough to need decomposition"), pause and propose a breakdown to the user before writing — same protocol as `gspec-feature` for multi-feature output. Confirm the breakdown, then write one file per feature.
|
|
237
|
+
|
|
238
|
+
After writing, briefly tell the user what was created (filename + capability list with checkbox states) and continue to the next finding.
|
|
239
|
+
|
|
167
240
|
### Phase 6: Final Verification
|
|
168
241
|
|
|
169
242
|
After all discrepancies have been resolved (or deferred):
|
|
170
243
|
|
|
171
244
|
1. **Re-read the updated specs** briefly to confirm the edits landed correctly
|
|
172
245
|
2. **Present a summary:**
|
|
173
|
-
- Total discrepancies found, grouped by category
|
|
246
|
+
- Total discrepancies found, grouped by category (including Orphan Capability)
|
|
174
247
|
- Number where spec was updated to match code
|
|
175
248
|
- Number where spec was kept as-is (code flagged for follow-up)
|
|
249
|
+
- Number of new feature PRDs created (with filenames) and capabilities those PRDs cover
|
|
176
250
|
- Number deferred
|
|
177
|
-
- List of files that were updated
|
|
251
|
+
- List of files that were updated or created
|
|
178
252
|
3. **Flag code follow-ups**: if the user chose "Keep the spec and fix code" for any finding, list those at the end as a punch list so they don't get lost. Do not modify code — this is a reference list for the user or a follow-up implement run.
|
|
253
|
+
4. **Flag orphan-capability hand-offs**: if the user picked "Defer to `/gspec-feature` later" for any orphan capability, list the capability and the evidence (file paths) so a follow-up `/gspec-feature` run has everything it needs.
|
|
179
254
|
|
|
180
255
|
---
|
|
181
256
|
|
|
182
257
|
## Rules
|
|
183
258
|
|
|
184
259
|
- **Never modify code.** This command only reads code and updates specs. If a drift suggests the code should change, list it in the code-follow-up summary and let the user decide whether to run `/gspec-implement` or fix it themselves.
|
|
185
|
-
- **Never create new
|
|
186
|
-
- **Never silently update specs.** Every change requires user approval via the drift resolution flow.
|
|
260
|
+
- **Never create new foundation specs.** Audit must not create `profile.md`, `stack.md`, `style.md`/`style.html`, `practices.md`, `architecture.md`, or `research.md`. The only new files audit may create are feature PRDs in `gspec/features/`, and only as the explicit resolution to an Orphan Capability finding.
|
|
261
|
+
- **Never silently update specs.** Every change — including creating a new feature PRD — requires user approval via the drift resolution flow.
|
|
187
262
|
- **One discrepancy at a time.** Do not batch resolutions — the user decides each one individually.
|
|
188
263
|
- **Be precise about the evidence.** Quote the spec, cite the file and line range where the code contradicts it. Vague drift reports ("the architecture is out of date") are not actionable.
|
|
189
|
-
- **Prioritize by impact.** Present drifts that would cause incorrect implementation or confused future work first. Cosmetic drift comes last.
|
|
264
|
+
- **Prioritize by impact.** Present drifts that would cause incorrect implementation or confused future work first. Cosmetic drift comes last. Orphan Capabilities sit alongside Feature Drift in priority — both directly affect what `gspec-implement` will produce next.
|
|
190
265
|
- **Treat the profile conservatively.** Profile drift usually reflects an intentional pivot and deserves a human decision, not an automatic spec update.
|
|
191
|
-
- **Respect the scope hint.** If the user passes a hint like "audit the stack only", stick to it.
|
|
266
|
+
- **Respect the scope hint.** If the user passes a hint like "audit the stack only", stick to it. A scope hint of "audit features" includes Orphan Capability detection; a hint that excludes features (e.g. "audit the stack only") suppresses it.
|
|
192
267
|
|
|
193
268
|
---
|
|
194
269
|
|
|
@@ -186,6 +186,16 @@ When generating multiple features from a large request:
|
|
|
186
186
|
|
|
187
187
|
---
|
|
188
188
|
|
|
189
|
+
## After Writing the PRD
|
|
190
|
+
|
|
191
|
+
After saving each PRD, end your response with a brief next-step pointer:
|
|
192
|
+
|
|
193
|
+
> *For larger features, run `/gspec-tasks <feature-slug>` to produce an ordered task plan with explicit dependencies and parallel-execution markers before running `/gspec-implement`. Trivial features can skip straight to `/gspec-implement`.*
|
|
194
|
+
|
|
195
|
+
This is a one-line nudge, not a prompt — do not generate the tasks file from this skill, and do not block the user on it.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
189
199
|
## Tone & Style
|
|
190
200
|
|
|
191
201
|
- Clear, neutral, product-led
|