bigpowers 2.81.0 → 2.82.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.
- package/.pi/package.json +1 -1
- package/CHANGELOG.md +20 -0
- package/CLAUDE.md +7 -0
- package/CONVENTIONS.md +46 -0
- package/bin/bigpowers.js +8 -12
- package/bin/setup.js +288 -0
- package/constitution.md +217 -0
- package/package.json +7 -1
- package/scripts/lib/__pycache__/link_utils.cpython-312.pyc +0 -0
- package/scripts/lib/install-helpers.js +224 -0
- package/scripts/lib/plan-consistency-check.sh +18 -15
- package/scripts/lib/sync-post.sh +10 -1
package/.pi/package.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
## [2.82.1](https://github.com/danielvm-git/bigpowers/compare/v2.82.0...v2.82.1) (2026-07-24)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **specs:** quote YAML string with colon in e61 epic ([25fddd8](https://github.com/danielvm-git/bigpowers/commit/25fddd8942eef67ab5c26793f3276d32e67305be))
|
|
7
|
+
|
|
8
|
+
# [2.82.0](https://github.com/danielvm-git/bigpowers/compare/v2.81.0...v2.82.0) (2026-07-24)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **e55:** add gemini configuration to extension manifest for AGY CLI discovery ([8c717db](https://github.com/danielvm-git/bigpowers/commit/8c717db2bb85126adcab02354f5ca45da8482b4c))
|
|
14
|
+
* **plan-consistency-check:** stop treating story deliverables as specs ([8d9814a](https://github.com/danielvm-git/bigpowers/commit/8d9814a5ce09e8b8ce563accb785f109dc55b56e))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* **installer:** add interactive setup with BMAD-polished UI ([5ba8da8](https://github.com/danielvm-git/bigpowers/commit/5ba8da851987ac6e856a6efee6e2729d0fab3ec7))
|
|
20
|
+
|
|
1
21
|
# [2.81.0](https://github.com/danielvm-git/bigpowers/compare/v2.80.0...v2.81.0) (2026-07-23)
|
|
2
22
|
|
|
3
23
|
|
package/CLAUDE.md
CHANGED
|
@@ -2,11 +2,18 @@
|
|
|
2
2
|
# story: e51s05
|
|
3
3
|
# story: e45s22
|
|
4
4
|
# story: e45s23
|
|
5
|
+
# story: e55s03
|
|
5
6
|
|
|
6
7
|
# bigpowers — Claude Code
|
|
7
8
|
|
|
8
9
|
Read CONVENTIONS.md before any GitHub or git operation.
|
|
9
10
|
|
|
11
|
+
[`constitution.md`](constitution.md) is a consolidated entry point synthesizing
|
|
12
|
+
this project's doctrine (this file, CONVENTIONS.md, docs/PRINCIPLES.md,
|
|
13
|
+
docs/references/*.md) into bigspec's B0-B10 + Capstone blocks, with citations
|
|
14
|
+
back to the fuller text. It's a starting point for a reader, not a
|
|
15
|
+
replacement — this file remains fully authoritative for its own content today.
|
|
16
|
+
|
|
10
17
|
<!-- BEGIN bigpowers:context-routing -->
|
|
11
18
|
## Context Routing
|
|
12
19
|
|
package/CONVENTIONS.md
CHANGED
|
@@ -10,9 +10,17 @@
|
|
|
10
10
|
# story: e45s36
|
|
11
11
|
# story: e27s01
|
|
12
12
|
# story: e29s03
|
|
13
|
+
# story: e54s03
|
|
14
|
+
# story: e55s03
|
|
13
15
|
|
|
14
16
|
# Conventions
|
|
15
17
|
|
|
18
|
+
[`constitution.md`](constitution.md) is a consolidated entry point synthesizing
|
|
19
|
+
this project's doctrine (this file, CLAUDE.md, docs/PRINCIPLES.md,
|
|
20
|
+
docs/references/*.md) into bigspec's B0-B10 + Capstone blocks, with citations
|
|
21
|
+
back to the fuller text below. It's a starting point for a reader, not a
|
|
22
|
+
replacement — this file remains fully authoritative for its own content today.
|
|
23
|
+
|
|
16
24
|
## Conventional Commits & Semantic Versioning
|
|
17
25
|
|
|
18
26
|
All changes to this repository MUST follow the [Conventional Commits 1.0.0](https://www.conventionalcommits.org/en/v1.0.0/) specification. Versioning MUST strictly adhere to [Semantic Versioning 2.0.0](https://semver.org/).
|
|
@@ -386,6 +394,44 @@ This story (e53s02) builds the mechanism only; it creates no real tombstone stub
|
|
|
386
394
|
itself, so the live skill count doesn't move yet. The first real use is expected in
|
|
387
395
|
e56 (skill reclassification/merges).
|
|
388
396
|
|
|
397
|
+
## Catalog Freeze (e54-e59 migration)
|
|
398
|
+
|
|
399
|
+
The 80-skill catalog is frozen as of `specs/tech-architecture/CATALOG-BASELINE-<date>.yaml`
|
|
400
|
+
(e54s01) so e56's reclassification work (80 skills → ~28 procedures) has a stable
|
|
401
|
+
starting point instead of a moving target. `bash scripts/check-catalog-drift.sh`
|
|
402
|
+
(e54s02) diffs the live catalog against that baseline on every Preflight run and
|
|
403
|
+
prints `WARNING:` lines for drift — it is advisory only (always exits 0) and never
|
|
404
|
+
blocks Preflight or CI.
|
|
405
|
+
|
|
406
|
+
**Frozen (triggers a drift warning):**
|
|
407
|
+
- Adding a new `skills/<name>/SKILL.md` directory
|
|
408
|
+
- Structural changes to an existing skill's `phase`, `effort`, or `model` frontmatter
|
|
409
|
+
fields
|
|
410
|
+
|
|
411
|
+
**Not frozen (no warning; proceed as normal):**
|
|
412
|
+
- Bug fixes within an existing skill's existing shape
|
|
413
|
+
- `description:` edits
|
|
414
|
+
- Any other content fix that doesn't touch `phase`/`effort`/`model`
|
|
415
|
+
|
|
416
|
+
**Exit criterion:** the freeze ends when e56 (reclassify-catalog) merges. e56's
|
|
417
|
+
completion is the unfreeze event itself — there is no calendar date, and no other
|
|
418
|
+
story or action lifts the freeze early.
|
|
419
|
+
|
|
420
|
+
**Exception marker:** a change that is genuinely unrelated to the migration (e.g. an
|
|
421
|
+
urgent fix that happens to touch a frozen field) can suppress its own drift warning
|
|
422
|
+
by adding a comment containing the literal string `freeze-exception` to the affected
|
|
423
|
+
`SKILL.md`, for example:
|
|
424
|
+
|
|
425
|
+
```
|
|
426
|
+
<!-- freeze-exception: urgent security fix, unrelated to the e54-e59 migration -->
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
`scripts/check-catalog-drift.sh` checks for this exact substring — it must remain
|
|
430
|
+
byte-identical between this document and the script, or the exception path silently
|
|
431
|
+
stops working. Use it sparingly: it should be a marker for something a human decided
|
|
432
|
+
was safe to change during the freeze, not a way to permanently silence the gate for a
|
|
433
|
+
skill that should just wait until e56.
|
|
434
|
+
|
|
389
435
|
## File-Size Exceptions
|
|
390
436
|
|
|
391
437
|
The 300-line file cap exists to ensure files fit in a single agent context
|
package/bin/bigpowers.js
CHANGED
|
@@ -32,21 +32,17 @@ function run(cmd, cwd = ROOT) {
|
|
|
32
32
|
const cmd = process.argv[2];
|
|
33
33
|
|
|
34
34
|
if (cmd === 'setup' || cmd === 'install') {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
console.log(' Skills installed in ~/.claude/skills/');
|
|
40
|
-
console.log(' Run again anytime to update: bigpowers setup\n');
|
|
41
|
-
process.exit(0);
|
|
35
|
+
// Delegate to interactive installer
|
|
36
|
+
const setupScript = path.join(ROOT, 'bin', 'setup.js');
|
|
37
|
+
require(setupScript);
|
|
38
|
+
return;
|
|
42
39
|
}
|
|
43
40
|
|
|
44
41
|
if (cmd === 'update') {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
process.exit(0);
|
|
42
|
+
// Delegate to interactive installer (same as setup)
|
|
43
|
+
const setupScript = path.join(ROOT, 'bin', 'setup.js');
|
|
44
|
+
require(setupScript);
|
|
45
|
+
return;
|
|
50
46
|
}
|
|
51
47
|
|
|
52
48
|
if (cmd === 'status') {
|
package/bin/setup.js
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// bigpowers setup — interactive installer
|
|
3
|
+
// Combines GSD simplicity with BMAD visual polish.
|
|
4
|
+
|
|
5
|
+
const { execSync } = require('child_process');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { installGlobal, installLocal, uninstallTool, detectExistingInstall } = require('../scripts/lib/install-helpers');
|
|
9
|
+
|
|
10
|
+
const ROOT = path.resolve(path.dirname(__filename), '..');
|
|
11
|
+
const pkg = require(path.join(ROOT, 'package.json'));
|
|
12
|
+
|
|
13
|
+
// ── ANSI helpers ─────────────────────────────────────────────────────────────
|
|
14
|
+
const c = {
|
|
15
|
+
cyan: (s) => `\x1b[36m${s}\x1b[0m`,
|
|
16
|
+
green: (s) => `\x1b[32m${s}\x1b[0m`,
|
|
17
|
+
red: (s) => `\x1b[31m${s}\x1b[0m`,
|
|
18
|
+
dim: (s) => `\x1b[2m${s}\x1b[0m`,
|
|
19
|
+
bold: (s) => `\x1b[1m${s}\x1b[0m`,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// ── ASCII banner ─────────────────────────────────────────────────────────────
|
|
23
|
+
const BANNER = `
|
|
24
|
+
${c.cyan('╔════════════════════════════════════════════════════════════════════════════╗')}
|
|
25
|
+
${c.cyan('║')} ${c.cyan('║')}
|
|
26
|
+
${c.cyan('║')} ${c.bold(c.cyan('██████╗ ██╗ ██████╗ ██████╗ ██████╗ █ █ ███████╗██████╗ ███████╗'))} ${c.cyan('║')}
|
|
27
|
+
${c.cyan('║')} ${c.bold(c.cyan('██╔══██╗██║██╔════╝ ██╔══██╗██╔═══██╗█ █ ██╔════╝██╔══██╗██╔════╝'))} ${c.cyan('║')}
|
|
28
|
+
${c.cyan('║')} ${c.bold(c.cyan('██████╔╝██║██║ ███╗██████╔╝██║ ██║█ ██ █ █████╗ ██████╔╝███████╗'))} ${c.cyan('║')}
|
|
29
|
+
${c.cyan('║')} ${c.bold(c.cyan('██╔══██╗██║██║ ██║██╔═══╝ ██║ ██║█ ████ █ ██╔══╝ ██╔══██╗╚════██║'))} ${c.cyan('║')}
|
|
30
|
+
${c.cyan('║')} ${c.bold(c.cyan('██████╔╝██║╚██████╔╝██║ ╚██████╔╝███ ███ ███████╗██║ ██║███████║'))} ${c.cyan('║')}
|
|
31
|
+
${c.cyan('║')} ${c.bold(c.cyan('╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝'))} ${c.cyan('║')}
|
|
32
|
+
${c.cyan('║')} ${c.cyan('║')}
|
|
33
|
+
${c.cyan('║')} ${c.dim('Spec-Driven & Test-First Skills for Solo Developers')} ${c.cyan('║')}
|
|
34
|
+
${c.cyan('║')} ${c.dim(`v${pkg.version}`)} ${c.cyan('║')}
|
|
35
|
+
${c.cyan('╚════════════════════════════════════════════════════════════════════════════╝')}
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
// ── Tool definitions ─────────────────────────────────────────────────────────
|
|
39
|
+
const TOOLS = [
|
|
40
|
+
{ id: 'claude', name: 'Claude Code', globalPath: '~/.claude', localPath: '.claude' },
|
|
41
|
+
{ id: 'antigravity', name: 'Antigravity', globalPath: '~/.gemini/antigravity', localPath: '.gemini/antigravity' },
|
|
42
|
+
{ id: 'augment', name: 'Augment', globalPath: '~/.augment', localPath: '.augment' },
|
|
43
|
+
{ id: 'cline', name: 'Cline', globalPath: '.clinerules', localPath: '.clinerules' },
|
|
44
|
+
{ id: 'codebuddy', name: 'CodeBuddy', globalPath: '~/.codebuddy', localPath: '.codebuddy' },
|
|
45
|
+
{ id: 'codex', name: 'Codex', globalPath: '~/.codex', localPath: '.codex' },
|
|
46
|
+
{ id: 'copilot', name: 'Copilot', globalPath: '~/.copilot', localPath: '.copilot' },
|
|
47
|
+
{ id: 'cursor', name: 'Cursor', globalPath: '~/.cursor', localPath: '.cursor' },
|
|
48
|
+
{ id: 'gemini', name: 'Gemini', globalPath: '~/.gemini', localPath: '.gemini' },
|
|
49
|
+
{ id: 'hermes', name: 'Hermes Agent', globalPath: '~/.hermes', localPath: '.hermes' },
|
|
50
|
+
{ id: 'kilo', name: 'Kilo', globalPath: '~/.config/kilo', localPath: '.config/kilo' },
|
|
51
|
+
{ id: 'opencode', name: 'OpenCode', globalPath: '~/.config/opencode', localPath: '.config/opencode' },
|
|
52
|
+
{ id: 'qwen', name: 'Qwen Code', globalPath: '~/.qwen', localPath: '.qwen' },
|
|
53
|
+
{ id: 'trae', name: 'Trae', globalPath: '~/.trae', localPath: '.trae' },
|
|
54
|
+
{ id: 'windsurf', name: 'Windsurf', globalPath: '~/.codeium/windsurf', localPath: '.codeium/windsurf' },
|
|
55
|
+
];
|
|
56
|
+
const ALL_ID = '__all__';
|
|
57
|
+
const SUPPORTED_IDS = new Set(['claude', 'gemini', 'pi', 'cursor', 'codex']);
|
|
58
|
+
const UNSUPPORTED_HINT = '(TODO)';
|
|
59
|
+
|
|
60
|
+
// ── clack helpers (lazy ESM import from CJS) ─────────────────────────────────
|
|
61
|
+
let _clack;
|
|
62
|
+
async function clack() {
|
|
63
|
+
if (!_clack) _clack = await import('@clack/prompts');
|
|
64
|
+
return _clack;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function sleep(ms) {
|
|
68
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function runInherited(cmd) {
|
|
72
|
+
execSync(cmd, { cwd: ROOT, stdio: 'inherit' });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
// ── Uninstall flow ──────────────────────────────────────────────────────────
|
|
78
|
+
async function handleUninstall() {
|
|
79
|
+
const { multiselect: ms, confirm: cf, spinner: sp, outro: ot, isCancel: ic } = await clack();
|
|
80
|
+
|
|
81
|
+
const toolOptions = TOOLS.filter((t) => SUPPORTED_IDS.has(t.id)).map((tool) => {
|
|
82
|
+
return { value: tool.id, label: tool.name, hint: `(${tool.globalPath})` };
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const selectedToolIds = await ms({
|
|
86
|
+
message: 'Uninstall from which tools?',
|
|
87
|
+
options: toolOptions,
|
|
88
|
+
initialValues: ['claude'],
|
|
89
|
+
required: true,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
if (ic(selectedToolIds)) {
|
|
93
|
+
ot('Cancelled.');
|
|
94
|
+
process.exit(0);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const yes = await cf({
|
|
98
|
+
message: `Remove symlinks from ${selectedToolIds.length} tool${selectedToolIds.length > 1 ? 's' : ''}?`,
|
|
99
|
+
initialValue: true,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
if (!yes || ic(yes)) {
|
|
103
|
+
ot('Cancelled.');
|
|
104
|
+
process.exit(0);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const s = await sp();
|
|
108
|
+
s.start('Removing...');
|
|
109
|
+
|
|
110
|
+
const results = [];
|
|
111
|
+
for (const toolId of selectedToolIds) {
|
|
112
|
+
const tool = TOOLS.find((t) => t.id === toolId);
|
|
113
|
+
try {
|
|
114
|
+
uninstallTool(toolId, ROOT);
|
|
115
|
+
results.push({ name: tool.name, ok: true });
|
|
116
|
+
} catch (err) {
|
|
117
|
+
results.push({ name: tool.name, ok: false, error: err.message });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
s.stop('Done.');
|
|
122
|
+
|
|
123
|
+
console.log('');
|
|
124
|
+
for (const r of results) {
|
|
125
|
+
const icon = r.ok ? c.green('✓') : c.red('✗');
|
|
126
|
+
const detail = r.ok ? c.dim('removed') : c.red(r.error);
|
|
127
|
+
console.log(` ${icon} ${r.name} — ${detail}`);
|
|
128
|
+
}
|
|
129
|
+
console.log('');
|
|
130
|
+
|
|
131
|
+
ot('Uninstall complete.');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ── Main ─────────────────────────────────────────────────────────────────────
|
|
135
|
+
async function main() {
|
|
136
|
+
const { intro, outro, select, multiselect, spinner, confirm, isCancel } = await clack();
|
|
137
|
+
|
|
138
|
+
console.log(BANNER);
|
|
139
|
+
intro(c.bold(`bigpowers v${pkg.version}`));
|
|
140
|
+
|
|
141
|
+
// ── Step 0: Detect existing install ────────────────────────────────────
|
|
142
|
+
const hasExisting = detectExistingInstall();
|
|
143
|
+
let mode = 'install';
|
|
144
|
+
|
|
145
|
+
if (hasExisting) {
|
|
146
|
+
mode = await select({
|
|
147
|
+
message: 'Existing installation detected. How would you like to proceed?',
|
|
148
|
+
options: [
|
|
149
|
+
{ value: 'install', label: 'Setup / Update', hint: '(reinstall or add tools)' },
|
|
150
|
+
{ value: 'uninstall', label: 'Uninstall', hint: '(remove symlinks and hooks)' },
|
|
151
|
+
],
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
if (isCancel(mode)) {
|
|
155
|
+
outro('Cancelled.');
|
|
156
|
+
process.exit(0);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (mode === 'uninstall') {
|
|
161
|
+
await handleUninstall(clack);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ── Step 1: Global vs Local ──────────────────────────────────────────────
|
|
166
|
+
const installMode = await select({
|
|
167
|
+
message: 'Where would you like to install?',
|
|
168
|
+
options: [
|
|
169
|
+
{ value: 'global', label: 'Global', hint: '(~/.claude/skills/) — available in all projects' },
|
|
170
|
+
{ value: 'local', label: 'Local', hint: '(./.claude/skills/) — this project only' },
|
|
171
|
+
],
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
if (isCancel(installMode)) {
|
|
175
|
+
outro('Setup cancelled.');
|
|
176
|
+
process.exit(0);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ── Step 2: Tool selection ─────────────────────────────────────────────
|
|
180
|
+
const toolOptions = TOOLS.map((tool) => {
|
|
181
|
+
const p = installMode === 'global' ? tool.globalPath : tool.localPath;
|
|
182
|
+
const supported = SUPPORTED_IDS.has(tool.id);
|
|
183
|
+
return {
|
|
184
|
+
value: tool.id,
|
|
185
|
+
label: supported ? tool.name : c.dim(tool.name),
|
|
186
|
+
hint: supported ? `(${p})` : c.dim(`${UNSUPPORTED_HINT} ${p}`),
|
|
187
|
+
};
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
let selectedToolIds = await multiselect({
|
|
191
|
+
message: 'Install for which tools?',
|
|
192
|
+
options: toolOptions,
|
|
193
|
+
initialValues: ['claude'],
|
|
194
|
+
required: true,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
if (isCancel(selectedToolIds)) {
|
|
198
|
+
outro('Setup cancelled.');
|
|
199
|
+
process.exit(0);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Filter out TODO tools — they can't be installed yet
|
|
203
|
+
const selectedTools = selectedToolIds.filter((id) => SUPPORTED_IDS.has(id));
|
|
204
|
+
const todoSkipped = selectedToolIds.filter((id) => !SUPPORTED_IDS.has(id));
|
|
205
|
+
if (todoSkipped.length > 0) {
|
|
206
|
+
const names = todoSkipped.map((id) => TOOLS.find((t) => t.id === id)?.name || id);
|
|
207
|
+
console.log(`\n ${c.dim('TODO (not yet implemented):')} ${names.join(', ')}`);
|
|
208
|
+
}
|
|
209
|
+
if (selectedTools.length === 0) {
|
|
210
|
+
console.log(`\n ${c.red('No supported tools selected.')}`);
|
|
211
|
+
outro('Setup cancelled.');
|
|
212
|
+
process.exit(0);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ── Step 3: Confirm ───────────────────────────────────────────────────
|
|
216
|
+
const skillsDir = path.join(ROOT, 'skills');
|
|
217
|
+
const skillCount = fs
|
|
218
|
+
.readdirSync(skillsDir)
|
|
219
|
+
.filter((d) => fs.existsSync(path.join(skillsDir, d, 'SKILL.md'))).length;
|
|
220
|
+
|
|
221
|
+
const yes = await confirm({
|
|
222
|
+
message: `Install ${skillCount} skills to ${selectedTools.length} runtime${selectedTools.length > 1 ? 's' : ''}?`,
|
|
223
|
+
initialValue: true,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
if (!yes || isCancel(yes)) {
|
|
227
|
+
outro('Setup cancelled.');
|
|
228
|
+
process.exit(0);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ── Step 4: Install ───────────────────────────────────────────────────
|
|
232
|
+
const s = await spinner();
|
|
233
|
+
s.start('Preparing installation...');
|
|
234
|
+
await sleep(200);
|
|
235
|
+
|
|
236
|
+
s.message('Syncing skills...');
|
|
237
|
+
try {
|
|
238
|
+
runInherited('bash scripts/sync-skills.sh');
|
|
239
|
+
} catch {
|
|
240
|
+
s.error('Sync failed.');
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const results = [];
|
|
245
|
+
for (const toolId of selectedTools) {
|
|
246
|
+
const tool = TOOLS.find((t) => t.id === toolId);
|
|
247
|
+
const p = installMode === 'global' ? tool.globalPath : tool.localPath;
|
|
248
|
+
|
|
249
|
+
s.message(`Installing ${tool.name}...`);
|
|
250
|
+
await sleep(300);
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
if (installMode === 'global') {
|
|
254
|
+
installGlobal(tool, ROOT);
|
|
255
|
+
} else {
|
|
256
|
+
installLocal(tool, ROOT);
|
|
257
|
+
}
|
|
258
|
+
results.push({ name: tool.name, path: p, ok: true });
|
|
259
|
+
} catch (err) {
|
|
260
|
+
results.push({ name: tool.name, path: p, ok: false, error: err.message });
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
s.stop('Installation complete.');
|
|
265
|
+
|
|
266
|
+
// ── Step 5: Results ────────────────────────────────────────────────────
|
|
267
|
+
console.log('');
|
|
268
|
+
for (const r of results) {
|
|
269
|
+
const icon = r.ok ? c.green('✓') : c.red('✗');
|
|
270
|
+
const detail = r.ok ? c.dim(r.path) : c.red(r.error);
|
|
271
|
+
console.log(` ${icon} ${r.name} — ${detail}`);
|
|
272
|
+
}
|
|
273
|
+
console.log('');
|
|
274
|
+
|
|
275
|
+
const allOk = results.every((r) => r.ok);
|
|
276
|
+
if (allOk) {
|
|
277
|
+
outro(`bigpowers installed. ${c.dim('Run bigpowers update anytime.')}`);
|
|
278
|
+
} else {
|
|
279
|
+
outro('Some installations failed. Check errors above.');
|
|
280
|
+
process.exit(1);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// ── Run ──────────────────────────────────────────────────────────────────────
|
|
285
|
+
main().catch((err) => {
|
|
286
|
+
console.error(`${c.red('Error:')} ${err.message}`);
|
|
287
|
+
process.exit(1);
|
|
288
|
+
});
|
package/constitution.md
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# bigpowers Constitution
|
|
2
|
+
|
|
3
|
+
<!-- story: e55s02 -->
|
|
4
|
+
|
|
5
|
+
> **Status:** consolidated entry point for bigpowers' doctrine, built from a
|
|
6
|
+
> full audit of every current rule source
|
|
7
|
+
> (`specs/epics/e55-extract-constitution/e55s01-doctrine-mapping.md`).
|
|
8
|
+
>
|
|
9
|
+
> **Authority:** this file is where a reader starts. It does **not** yet
|
|
10
|
+
> replace `CLAUDE.md` or `CONVENTIONS.md` — both remain fully authoritative in
|
|
11
|
+
> their own right today. Each block below synthesizes what those files (and
|
|
12
|
+
> the supporting `docs/references/*.md` sources) currently say, with citations
|
|
13
|
+
> to the fuller text rather than a full copy of it, so this file doesn't
|
|
14
|
+
> become a second place the same rule can drift out of sync.
|
|
15
|
+
>
|
|
16
|
+
> This file has no `constitution_version` or amendment scheme — that
|
|
17
|
+
> compatibility-tracking mechanism is a later concern (e59, absorb-fresh-
|
|
18
|
+
> references), not something this story invents ahead of schedule.
|
|
19
|
+
>
|
|
20
|
+
> Structure follows bigspec's 11 blocks (B0–B10) plus a Capstone category, used
|
|
21
|
+
> here purely as an organizing skeleton (`specs/reborn-constitution.md` Part I),
|
|
22
|
+
> not as a source of content — every claim below is about what *this* repo's
|
|
23
|
+
> doctrine actually says today.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## B0 · Token/Context Substrate
|
|
28
|
+
|
|
29
|
+
Content is written to be consumed by an agent under a real token budget, not
|
|
30
|
+
skimmed by a human. Files stay under size caps so they fit a single context
|
|
31
|
+
window; skills load progressively (frontmatter → summary → detail) instead of
|
|
32
|
+
being read in full up front; `verify:` commands are one-line and runnable.
|
|
33
|
+
|
|
34
|
+
- 300-line file cap, `SKILL.md` size caps — `CONVENTIONS.md` §Code Style, `docs/PRINCIPLES.md` §5/§8
|
|
35
|
+
- Context Routing (load by glob, not the full doc tree) — `CLAUDE.md`
|
|
36
|
+
- Token Economy, high-density skill descriptions — `docs/PRINCIPLES.md` §5, `docs/references/akita.md`
|
|
37
|
+
- Full progressive-disclosure framework, `bts_map`/`bts_compress`/`sqz`/`rtk` tooling — `docs/references/context-engineering.md`
|
|
38
|
+
- `terse-mode` past ~20 turns — `CONVENTIONS.md` §Risk Tiers (`terse-when-heavy`)
|
|
39
|
+
|
|
40
|
+
## B1 · Agent-Grade Craftsmanship
|
|
41
|
+
|
|
42
|
+
The code floor: functions 4–20 lines, files under 300 lines, one responsibility
|
|
43
|
+
per module, names specific enough that `grep` returns under 5 hits, no
|
|
44
|
+
duplication, exceptions over error codes, dead code deleted not commented out,
|
|
45
|
+
Boy Scout Rule, Law of Demeter, error messages that carry a remediation hint.
|
|
46
|
+
|
|
47
|
+
- Full Code Style, Comments, Dependencies, Structure, Formatting, Logging sections — `CONVENTIONS.md`
|
|
48
|
+
- SRP, Boy Scout Rule, intention-revealing names — `docs/PRINCIPLES.md` §1
|
|
49
|
+
- Deep modules, information hiding — `docs/PRINCIPLES.md` §2, `docs/references/ousterhout.md`, `docs/references/pocock.md`
|
|
50
|
+
- Grep-ability, structured observability, remediation hints — `docs/PRINCIPLES.md` §5, `docs/references/akita.md`
|
|
51
|
+
- SOLID, DRY, refactoring catalog, seams for testability — `docs/references/sandi-metz.md`, `docs/references/fowler.md`, `docs/references/uncle-bob.md`, `docs/references/feathers.md`, `docs/references/pragmatic-programmer.md`, `docs/references/rich-hickey.md`
|
|
52
|
+
- Conventional Commits message mechanics — `docs/references/git-integration.md`
|
|
53
|
+
|
|
54
|
+
## B2 · Think-First Behavior
|
|
55
|
+
|
|
56
|
+
Understand before acting; act minimally. Assumptions get surfaced before code
|
|
57
|
+
is written, ambiguous requests get multiple readings named explicitly, and
|
|
58
|
+
edits stay surgical — touching only what the task requires.
|
|
59
|
+
|
|
60
|
+
- Session Start read order, "minimum code", "one clarifying question beats a wrong assumption" — `CLAUDE.md`
|
|
61
|
+
- Think First, Zoom-Out Strategy, Surgical Edits — `docs/PRINCIPLES.md` §3
|
|
62
|
+
- Zoom-out mandate, `define-success` hard gate, "we'll decide during code" red flag — `docs/references/orchestration.md`, `docs/references/workflow-steps.md`
|
|
63
|
+
- Tidy First?, tracer bullets, good-enough software — `docs/references/kent-beck.md`, `docs/references/pragmatic-programmer.md`
|
|
64
|
+
- System 1 vs System 2 thinking, model-escalation cost-benefit — `docs/references/thinking-models.md`
|
|
65
|
+
- Domain probes (`grill-me`), model-domain invariant grilling — `docs/references/domain-probes.md`, `docs/references/ddd.md`
|
|
66
|
+
|
|
67
|
+
## B3 · The Skill Primitive
|
|
68
|
+
|
|
69
|
+
A skill is a verb-noun procedure, not a class of code. Every critical-path
|
|
70
|
+
skill writes `handoff.next_skill` as its last action so work can resume
|
|
71
|
+
anywhere it stopped.
|
|
72
|
+
|
|
73
|
+
- Workflow Mandate: route work through bigpowers skills, never write code directly — `CLAUDE.md`, `CONVENTIONS.md` §Agent Workflow Mandates
|
|
74
|
+
- Skill Naming, Tombstone Aliases, next_skill signaling mandate — `CONVENTIONS.md`
|
|
75
|
+
- Skill-Based Architecture, zoom-out mandate — `docs/PRINCIPLES.md` §3
|
|
76
|
+
- `seed-conventions` bootstrap, `survey-context` phase mapping — `docs/references/agent-config-files-and-okf.md`, `docs/references/workflow-steps.md`
|
|
77
|
+
|
|
78
|
+
## B4 · Spec-as-Contract
|
|
79
|
+
|
|
80
|
+
The spec is the source of truth, not the chat history. `plan-work` produces
|
|
81
|
+
`verify:`-bearing tasks before any feature code is written; a story only
|
|
82
|
+
counts as done once `verify-work` confirms it against those criteria.
|
|
83
|
+
|
|
84
|
+
- `plan-work` before feature code, UAT sign-off mandate, delta-tag requirement — `CONVENTIONS.md` §Agent Workflow Mandates
|
|
85
|
+
- Intent/delivery/execution file ownership table — `CONVENTIONS.md` §specs/
|
|
86
|
+
- SDD, BDD/Gherkin as the link — `docs/PRINCIPLES.md` §4, `docs/references/wasowski.md`
|
|
87
|
+
- Ubiquitous Language, information-first design — `docs/references/pocock.md`, `docs/references/rich-hickey.md`
|
|
88
|
+
- Story maturity/INVEST gates, NFR Gate pattern, structured-spec pattern — `docs/references/bcp.md`, `docs/references/bcp-plus.md`, `docs/references/spec-kit.md`
|
|
89
|
+
- Bounded context, context mapping, `define-language` — `docs/references/ddd.md`
|
|
90
|
+
|
|
91
|
+
## B5 · Risk-Tiered Verification
|
|
92
|
+
|
|
93
|
+
Proof is proportionate to risk. Every story is tagged P0–P3 at plan time; that
|
|
94
|
+
tier picks how much verification it gets — full multi-phase UAT for P0, down
|
|
95
|
+
to typecheck-and-lint for P3.
|
|
96
|
+
|
|
97
|
+
- Full Risk Tiers (P0–P3) table, Tests (F.I.R.S.T) — `CONVENTIONS.md` §Risk Tiers, §Tests
|
|
98
|
+
- F.I.R.S.T Testing — `docs/PRINCIPLES.md` §1, `docs/references/kent-beck.md`, `docs/references/uncle-bob.md`, `docs/references/tdd.md`, `docs/references/wasowski.md` *(stated in at least 5 sources — a live duplication instance, not evidence of 5 distinct rules)*
|
|
99
|
+
- P0–P3 risk-based test-depth tables — `docs/references/bmad.md`, `docs/references/tea.md`
|
|
100
|
+
- Boundary conditions, public-interface-only testing (T4/T5/T8), ≥95% coverage, NFR Evidence Audit — `docs/references/tdd.md`, `docs/references/bmad.md`, `docs/references/tea.md`
|
|
101
|
+
- OKF validation gates on structure/provenance, never a value — `docs/references/okf.md`
|
|
102
|
+
- Slopcheck [OK]/[SUS]/[SLOP], supply-chain and security-audit checklists — `docs/references/security-threats.md`
|
|
103
|
+
|
|
104
|
+
## B6 · Gates & Hard-Stops
|
|
105
|
+
|
|
106
|
+
Forward progress blocks until conditions are met — Preflight and CI must be
|
|
107
|
+
green before any forward work, and any reproducible gate failure is a
|
|
108
|
+
discovered defect, not something to note and move past.
|
|
109
|
+
|
|
110
|
+
- Always Green / Shift Left, Discovered Defects fix-or-log ladder, banned dismissive phrases — `CONVENTIONS.md` §Always Green, §Discovered Defects
|
|
111
|
+
- Hard Gates — `docs/PRINCIPLES.md` §6
|
|
112
|
+
- Broken Windows, quality-score gate, review workflow gate — `docs/references/pragmatic-programmer.md`, `docs/references/code-review.md`
|
|
113
|
+
- Checkpoint state machine, safety checkpoints, never-auto-merge — `docs/references/checkpoints.md`, `docs/references/git-integration.md`
|
|
114
|
+
- 6-phase Confirm-gates, deviation-from-plan red flag, mode-scaled strictness — `docs/references/orchestration.md`, `docs/references/orchestration-modes.md`
|
|
115
|
+
- Full Traceability Gate pipeline (coverage matrix, blind-spot categories, PASS/CONCERNS/FAIL/WAIVED verdicts) — `docs/references/traceability-gate.md`
|
|
116
|
+
- Four gate types (Confirm/Quality/Safety/Transition) — `docs/references/gates.md`
|
|
117
|
+
|
|
118
|
+
**Today's proof mechanism, described honestly:** the actual gate that runs on
|
|
119
|
+
every `npm run compliance` invocation is a Gherkin-based self-compliance suite
|
|
120
|
+
with a 94% pass threshold (`docs/PRINCIPLES.md` §6/§7, `CLAUDE.md` §Commands,
|
|
121
|
+
`docs/references/gates.md`). This is what genuinely exists and gates merges
|
|
122
|
+
today. `specs/reborn-constitution.md`'s Capstone block describes a *target*
|
|
123
|
+
state where outcome evals (with-skill vs without-skill delta) replace this
|
|
124
|
+
threshold — that replacement is **e58's job (evals-over-compliance), not yet
|
|
125
|
+
built**. The 94% Gherkin gate is not deprecated; it is the live mechanism
|
|
126
|
+
until e58 ships something to replace it with.
|
|
127
|
+
|
|
128
|
+
## B7 · Context Isolation
|
|
129
|
+
|
|
130
|
+
Context rot is the primary failure mode of long agentic sessions. The fix is
|
|
131
|
+
fresh-context subagents with file-based handoff, not a bigger prompt —
|
|
132
|
+
`state.yaml` tracks precise session state so any interruption can resume
|
|
133
|
+
exactly where it stopped.
|
|
134
|
+
|
|
135
|
+
- Token Management (Write/Select/Compress/Isolate, effort classification) — `CLAUDE.md`
|
|
136
|
+
- Session Governance via `state.yaml`/`release-plan.yaml` — `docs/PRINCIPLES.md` §6
|
|
137
|
+
- Model-escalation to fresh-context subagents — `docs/references/thinking-models.md`
|
|
138
|
+
- Handoff-contract principle, `dispatch-agents`/`delegate-task` scoped file access, `kickoff-branch` worktree isolation, `agent-locks.yaml` concurrency — `docs/references/context-engineering.md`
|
|
139
|
+
- `state.yaml` tracking, resume-via-survey-context, stale-lock detection — `docs/references/gsd.md`, `docs/references/semantic-context-bridge.md`
|
|
140
|
+
|
|
141
|
+
## B8 · Self-Describing Artifacts
|
|
142
|
+
|
|
143
|
+
Every planning artifact has exactly one owning file — `specs/execution-status.yaml`
|
|
144
|
+
is the sole source of truth for story status, `specs/release-plan.yaml` for
|
|
145
|
+
epic ordering, and so on. Generated artifacts (`.cursor/`, `.gemini/`,
|
|
146
|
+
`.pi/`, `website/`) are never hand-edited; their `SKILL.md` sources are.
|
|
147
|
+
|
|
148
|
+
- Full specs/ ownership model, Documentation Responsibilities table, Generated artifact targets, docs/references SSOT Sync — `CONVENTIONS.md` §specs/
|
|
149
|
+
- Never-edit-generated-artifacts, single-source-of-truth `SKILL.md` — `CLAUDE.md`
|
|
150
|
+
- OKF bundle frontmatter (`okf_kind`/`okf_version`/provenance), kind-aware validation, "gate on pipeline not value" — `docs/references/okf.md`
|
|
151
|
+
- CLAUDE.md/AGENTS.md dual-pattern — `docs/references/agent-config-files-and-okf.md`
|
|
152
|
+
- Skill graph, OKF wiki bundle, targets-registry as the single Integration Registry — `docs/references/semantic-context-bridge.md`, `docs/references/targets-registry.md`
|
|
153
|
+
|
|
154
|
+
*Note: `docs/references/okf.md` describes OKF-bundle conformance as achievable
|
|
155
|
+
"if all `SKILL.md` files add frontmatter fields" — that's a conditional, not a
|
|
156
|
+
claim that every generated file already carries this envelope today. Making
|
|
157
|
+
that universal across the whole `specs/` tree is e57's job (adopt-okf-envelope),
|
|
158
|
+
not yet built.*
|
|
159
|
+
|
|
160
|
+
## B9 · Effort Accounting (BCP)
|
|
161
|
+
|
|
162
|
+
BCP (Business Complexity Points) sizes a story before it's built, from a
|
|
163
|
+
6-step method summing per-element Fibonacci scores. It lives at the story
|
|
164
|
+
level only — there is no per-task `[BCP N]` annotation in the current method.
|
|
165
|
+
|
|
166
|
+
- Full BCP accounting mandate, Timestamp mandate, story-level-only rule — `CONVENTIONS.md` §BCP accounting mandate, §Timestamp mandate
|
|
167
|
+
- `effort:` frontmatter (light/standard/heavy) — `docs/references/context-engineering.md`, `docs/references/model-profiles.md`
|
|
168
|
+
- 6-step sizing method, size bands, velocity tracking — `docs/references/bcp.md`
|
|
169
|
+
- BCP-Plus 13-dimension framework — `docs/references/bcp-plus.md`
|
|
170
|
+
|
|
171
|
+
*Note: some older docs (`docs/references/workflow-artifacts.md`,
|
|
172
|
+
`docs/references/orchestration.md`) still reference a per-task `[BCP N]` tag
|
|
173
|
+
convention that `CONVENTIONS.md` has since superseded — a live drift instance
|
|
174
|
+
this consolidation surfaces rather than resolves. Resolving it is a
|
|
175
|
+
`docs/references` sync task, not this story's job.*
|
|
176
|
+
|
|
177
|
+
## B10 · The Synthesis
|
|
178
|
+
|
|
179
|
+
The 6-phase core loop — Discover → Elaborate → Plan → Build → Verify →
|
|
180
|
+
Release — runs at project, epic, and story scope alike. *Sustain* is a
|
|
181
|
+
session-flow state, not a seventh lifecycle phase.
|
|
182
|
+
|
|
183
|
+
- 6-Phase Lifecycle, Hard Gates, 94% threshold — `docs/PRINCIPLES.md` §6
|
|
184
|
+
- Same 6-phase loop, `orchestrate-project` phase sequencing — `docs/references/orchestration.md`, `docs/references/workflow-steps.md`
|
|
185
|
+
- BMAD's Discover→Elaborate→Plan→Build→Sustain arc, TEA's workflows, SDD lifecycle integration — `docs/references/bmad.md`, `docs/references/tea.md`, `docs/references/wasowski.md`
|
|
186
|
+
|
|
187
|
+
## ★ Capstone · Outcome Evals
|
|
188
|
+
|
|
189
|
+
The foundation should prove it produces better software, not just that it
|
|
190
|
+
follows its own rules. Bigpowers doesn't have this yet in the target form —
|
|
191
|
+
what exists today is described honestly in B6 above: a 94% Gherkin
|
|
192
|
+
self-compliance gate. `reborn-constitution.md`'s Capstone block names the
|
|
193
|
+
target (with-skill vs without-skill delta, pass@k + token cost) as what
|
|
194
|
+
**e58 (evals-over-compliance)** is expected to build. Until then:
|
|
195
|
+
|
|
196
|
+
- Request-review checklist / quality-score gate as today's closest analog — `docs/references/code-review.md`
|
|
197
|
+
- Human-in-the-loop UAT mandate after every story — `docs/references/spec-kit.md`
|
|
198
|
+
- `run-evals` + regression UAT phase, `specs/EVALS-<feature>.md` — `docs/references/workflow-steps.md`, `docs/references/workflow-artifacts.md`
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## What this file deliberately doesn't do (yet)
|
|
203
|
+
|
|
204
|
+
- **No content migration.** Full rule text stays in `CONVENTIONS.md`,
|
|
205
|
+
`CLAUDE.md`, `docs/PRINCIPLES.md`, and `docs/references/*.md`. Moving
|
|
206
|
+
rule-classified content in is e56's job (reclassify-catalog) — this file
|
|
207
|
+
only builds the destination structure.
|
|
208
|
+
- **No cross-references back from `CLAUDE.md`/`CONVENTIONS.md` yet.** Those
|
|
209
|
+
files don't point here yet; that's e55s03.
|
|
210
|
+
- **No amendment/versioning scheme.** `constitution_version`, SemVer bumps,
|
|
211
|
+
and ADR-gated amendments are e59's job (absorb-fresh-references).
|
|
212
|
+
- **No universal OKF envelope claim.** Not every generated file in this repo
|
|
213
|
+
carries `okf_kind`/`okf_version` frontmatter yet — that's e57's job
|
|
214
|
+
(adopt-okf-envelope).
|
|
215
|
+
- **No `count-bcp` skill.** BCP sizing today is the 6-step method in
|
|
216
|
+
`docs/references/bcp.md`, applied by hand during `plan-work`/`plan-release` —
|
|
217
|
+
not a dedicated skill or kernel module.
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bigpowers",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.82.1",
|
|
4
4
|
"description": "73 agent skills synthesizing 17 years of software engineering discipline into a prescriptive methodology for solo developers",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
+
"setup": "node bin/setup.js",
|
|
7
8
|
"compliance": "bash scripts/audit-compliance.sh specs/verifications/features && bash scripts/validate-doctrine.sh && python3 scripts/check-skill-links.py",
|
|
8
9
|
"doctrine": "bash scripts/validate-doctrine.sh",
|
|
9
10
|
"sync": "bash scripts/sync-skills.sh",
|
|
@@ -47,11 +48,16 @@
|
|
|
47
48
|
},
|
|
48
49
|
"bin": {
|
|
49
50
|
"bigpowers": "./bin/bigpowers.js",
|
|
51
|
+
"bigpowers-setup": "./bin/setup.js",
|
|
50
52
|
"bigpowers-dashboard": "./dashboard/bin/dashboard.js"
|
|
51
53
|
},
|
|
52
54
|
"engines": {
|
|
53
55
|
"node": ">=14.0.0"
|
|
54
56
|
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"@clack/prompts": "^0.10.0",
|
|
59
|
+
"picocolors": "^1.1.1"
|
|
60
|
+
},
|
|
55
61
|
"devDependencies": {
|
|
56
62
|
"@semantic-release/changelog": "^6.0.3",
|
|
57
63
|
"@semantic-release/exec": "^7.1.0",
|
|
Binary file
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// install-helpers.js — symlink helpers for bigpowers setup
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
function linkSkills(skillsDir, targetDir) {
|
|
8
|
+
for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
|
|
9
|
+
if (!entry.isDirectory()) continue;
|
|
10
|
+
if (!fs.existsSync(path.join(skillsDir, entry.name, 'SKILL.md'))) continue;
|
|
11
|
+
|
|
12
|
+
const src = path.join(skillsDir, entry.name);
|
|
13
|
+
const dst = path.join(targetDir, entry.name);
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
const stat = fs.lstatSync(dst);
|
|
17
|
+
if (stat.isSymbolicLink()) fs.unlinkSync(dst);
|
|
18
|
+
} catch {}
|
|
19
|
+
|
|
20
|
+
fs.symlinkSync(src, dst);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function linkDir(src, dst) {
|
|
25
|
+
try {
|
|
26
|
+
const stat = fs.lstatSync(dst);
|
|
27
|
+
if (stat.isSymbolicLink()) fs.unlinkSync(dst);
|
|
28
|
+
} catch {}
|
|
29
|
+
fs.symlinkSync(src, dst);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function linkFile(src, dst) {
|
|
33
|
+
try {
|
|
34
|
+
const stat = fs.lstatSync(dst);
|
|
35
|
+
if (stat.isSymbolicLink()) fs.unlinkSync(dst);
|
|
36
|
+
} catch {}
|
|
37
|
+
fs.symlinkSync(src, dst);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function linkHook(src, dst) {
|
|
41
|
+
if (!fs.existsSync(src)) return;
|
|
42
|
+
try {
|
|
43
|
+
const stat = fs.lstatSync(dst);
|
|
44
|
+
if (stat.isSymbolicLink()) fs.unlinkSync(dst);
|
|
45
|
+
} catch {}
|
|
46
|
+
fs.symlinkSync(src, dst);
|
|
47
|
+
try { fs.chmodSync(src, 0o755); } catch {}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ── Install per tool ─────────────────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
function installGlobal(tool, repoRoot) {
|
|
53
|
+
const homeDir = require('os').homedir();
|
|
54
|
+
const skillsDir = path.join(repoRoot, 'skills');
|
|
55
|
+
|
|
56
|
+
switch (tool.id) {
|
|
57
|
+
case 'claude': {
|
|
58
|
+
const targetDir = path.join(homeDir, '.claude', 'skills');
|
|
59
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
60
|
+
linkSkills(skillsDir, targetDir);
|
|
61
|
+
|
|
62
|
+
const hooksDir = path.join(homeDir, '.claude', 'hooks');
|
|
63
|
+
fs.mkdirSync(hooksDir, { recursive: true });
|
|
64
|
+
linkHook(
|
|
65
|
+
path.join(repoRoot, 'guard-git', 'scripts', 'block-dangerous-git.sh'),
|
|
66
|
+
path.join(hooksDir, 'block-dangerous-git.sh')
|
|
67
|
+
);
|
|
68
|
+
linkHook(
|
|
69
|
+
path.join(repoRoot, 'scripts', 'hooks', 'rtk-rewrite.sh'),
|
|
70
|
+
path.join(hooksDir, 'rtk-rewrite.sh')
|
|
71
|
+
);
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
case 'gemini': {
|
|
75
|
+
const extSrc = path.join(repoRoot, '.gemini', 'extensions', 'bigpowers');
|
|
76
|
+
const extDst = path.join(homeDir, '.gemini', 'config', 'plugins', 'bigpowers');
|
|
77
|
+
fs.mkdirSync(path.dirname(extDst), { recursive: true });
|
|
78
|
+
linkDir(extSrc, extDst);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
case 'pi': {
|
|
82
|
+
const targetDir = path.join(homeDir, '.pi', 'agent', 'skills');
|
|
83
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
84
|
+
linkSkills(skillsDir, targetDir);
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
case 'cursor': {
|
|
88
|
+
const rulesSrc = path.join(repoRoot, '.cursor', 'rules');
|
|
89
|
+
const rulesDst = path.join(homeDir, '.cursor', 'rules');
|
|
90
|
+
fs.mkdirSync(path.dirname(rulesDst), { recursive: true });
|
|
91
|
+
linkDir(rulesSrc, rulesDst);
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case 'codex': {
|
|
95
|
+
const templatePath = path.join(repoRoot, 'templates', 'codex', 'AGENTS.md');
|
|
96
|
+
const targetPath = path.join(homeDir, '.codex', 'AGENTS.md');
|
|
97
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
98
|
+
linkFile(templatePath, targetPath);
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function installLocal(tool, repoRoot) {
|
|
105
|
+
const skillsDir = path.join(repoRoot, 'skills');
|
|
106
|
+
const cwd = process.cwd();
|
|
107
|
+
|
|
108
|
+
switch (tool.id) {
|
|
109
|
+
case 'claude': {
|
|
110
|
+
const targetDir = path.join(cwd, '.claude', 'skills');
|
|
111
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
112
|
+
linkSkills(skillsDir, targetDir);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
case 'gemini': {
|
|
116
|
+
const extSrc = path.join(repoRoot, '.gemini', 'extensions', 'bigpowers');
|
|
117
|
+
const extDst = path.join(cwd, '.gemini', 'extensions', 'bigpowers');
|
|
118
|
+
fs.mkdirSync(path.dirname(extDst), { recursive: true });
|
|
119
|
+
linkDir(extSrc, extDst);
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
case 'pi': {
|
|
123
|
+
const targetDir = path.join(cwd, '.pi', 'agent', 'skills');
|
|
124
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
125
|
+
linkSkills(skillsDir, targetDir);
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
case 'cursor': {
|
|
129
|
+
const rulesSrc = path.join(repoRoot, '.cursor', 'rules');
|
|
130
|
+
const rulesDst = path.join(cwd, '.cursor', 'rules');
|
|
131
|
+
fs.mkdirSync(path.dirname(rulesDst), { recursive: true });
|
|
132
|
+
linkDir(rulesSrc, rulesDst);
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
case 'codex': {
|
|
136
|
+
const templatePath = path.join(repoRoot, 'templates', 'codex', 'AGENTS.md');
|
|
137
|
+
const targetPath = path.join(cwd, '.codex', 'AGENTS.md');
|
|
138
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
139
|
+
linkFile(templatePath, targetPath);
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ── Uninstall per tool ───────────────────────────────────────────────────────
|
|
146
|
+
|
|
147
|
+
function uninstallTool(toolId, repoRoot) {
|
|
148
|
+
const homeDir = require('os').homedir();
|
|
149
|
+
|
|
150
|
+
switch (toolId) {
|
|
151
|
+
case 'claude': {
|
|
152
|
+
const skillsDir = path.join(homeDir, '.claude', 'skills');
|
|
153
|
+
if (fs.existsSync(skillsDir)) {
|
|
154
|
+
for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
|
|
155
|
+
if (!entry.isSymbolicLink()) continue;
|
|
156
|
+
const target = fs.readlinkSync(path.join(skillsDir, entry.name));
|
|
157
|
+
if (target.includes('bigpowers')) {
|
|
158
|
+
removeSymlink(path.join(skillsDir, entry.name));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const hooksDir = path.join(homeDir, '.claude', 'hooks');
|
|
163
|
+
removeSymlink(path.join(hooksDir, 'block-dangerous-git.sh'));
|
|
164
|
+
removeSymlink(path.join(hooksDir, 'rtk-rewrite.sh'));
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
case 'gemini':
|
|
168
|
+
removeSymlink(path.join(homeDir, '.gemini', 'config', 'plugins', 'bigpowers'));
|
|
169
|
+
break;
|
|
170
|
+
case 'pi': {
|
|
171
|
+
const skillsDir = path.join(homeDir, '.pi', 'agent', 'skills');
|
|
172
|
+
if (fs.existsSync(skillsDir)) {
|
|
173
|
+
for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
|
|
174
|
+
if (!entry.isSymbolicLink()) continue;
|
|
175
|
+
const target = fs.readlinkSync(path.join(skillsDir, entry.name));
|
|
176
|
+
if (target.includes('bigpowers')) {
|
|
177
|
+
removeSymlink(path.join(skillsDir, entry.name));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
case 'cursor':
|
|
184
|
+
removeSymlink(path.join(homeDir, '.cursor', 'rules'));
|
|
185
|
+
break;
|
|
186
|
+
case 'codex':
|
|
187
|
+
removeSymlink(path.join(homeDir, '.codex', 'AGENTS.md'));
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function removeSymlink(p) {
|
|
193
|
+
try {
|
|
194
|
+
const stat = fs.lstatSync(p);
|
|
195
|
+
if (stat.isSymbolicLink()) {
|
|
196
|
+
fs.unlinkSync(p);
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
} catch {}
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function detectExistingInstall() {
|
|
204
|
+
const homeDir = require('os').homedir();
|
|
205
|
+
const marker = path.join(homeDir, '.claude', 'skills', 'survey-context');
|
|
206
|
+
try {
|
|
207
|
+
const stat = fs.lstatSync(marker);
|
|
208
|
+
return stat.isSymbolicLink();
|
|
209
|
+
} catch {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
module.exports = {
|
|
215
|
+
linkSkills,
|
|
216
|
+
linkDir,
|
|
217
|
+
linkFile,
|
|
218
|
+
linkHook,
|
|
219
|
+
installGlobal,
|
|
220
|
+
installLocal,
|
|
221
|
+
uninstallTool,
|
|
222
|
+
removeSymlink,
|
|
223
|
+
detectExistingInstall,
|
|
224
|
+
};
|
|
@@ -51,26 +51,29 @@ shopt -u nullglob
|
|
|
51
51
|
(( ${#SPECS[@]} > 0 )) || report CRITICAL "No story spec .md files in capsule (run slice-tasks first)"
|
|
52
52
|
(( ${#TASKS[@]} > 0 )) || report CRITICAL "No *-tasks.yaml files in capsule (run plan-work first)"
|
|
53
53
|
|
|
54
|
+
# A story's real spec is whatever its own tasks.yaml declares via `spec:` —
|
|
55
|
+
# not every e*s*.md file, which also matches non-spec deliverables a story
|
|
56
|
+
# produces (e.g. an audit story's own eNNsYY-<slug>.md output document).
|
|
54
57
|
SPEC_IDS=()
|
|
55
|
-
for spec in "${SPECS[@]+"${SPECS[@]}"}"; do
|
|
56
|
-
base="$(basename "$spec" .md)"
|
|
57
|
-
sid="$(echo "$base" | grep -oE 'e[0-9]+s[0-9]+' | head -1)"
|
|
58
|
-
[[ -n "$sid" ]] || continue
|
|
59
|
-
SPEC_IDS+=("$sid")
|
|
60
|
-
in_array "$sid" "${EPIC_STORIES[@]+"${EPIC_STORIES[@]}"}" \
|
|
61
|
-
|| report HIGH "Story $sid in spec file but missing from epic.yaml manifest"
|
|
62
|
-
grep -qE '^#{2,3} (17\.|Acceptance|Verification Script)' "$spec" \
|
|
63
|
-
|| report HIGH "Story $sid spec missing §17 acceptance criteria or Verification Script"
|
|
64
|
-
grep -qiE 'ambiguous|TBD|TODO|FIXME' "$spec" \
|
|
65
|
-
&& report MED "Story $sid spec contains ambiguous/TBD markers"
|
|
66
|
-
done
|
|
67
|
-
|
|
68
58
|
for tasks in "${TASKS[@]+"${TASKS[@]}"}"; do
|
|
69
59
|
base="$(basename "$tasks" -tasks.yaml)"
|
|
70
60
|
sid="$(echo "$base" | grep -oE 'e[0-9]+s[0-9]+' | head -1)"
|
|
71
61
|
[[ -n "$sid" ]] || continue
|
|
72
|
-
|
|
73
|
-
|
|
62
|
+
|
|
63
|
+
spec_name="$(grep -E '^spec:' "$tasks" | head -1 | sed -E 's/^spec:[[:space:]]*//; s/^"(.*)"$/\1/')"
|
|
64
|
+
spec="$CAPSULE/$spec_name"
|
|
65
|
+
if [[ -z "$spec_name" || ! -f "$spec" ]]; then
|
|
66
|
+
report CRITICAL "Story $sid tasks.yaml declares no valid spec: file"
|
|
67
|
+
else
|
|
68
|
+
SPEC_IDS+=("$sid")
|
|
69
|
+
in_array "$sid" "${EPIC_STORIES[@]+"${EPIC_STORIES[@]}"}" \
|
|
70
|
+
|| report HIGH "Story $sid in spec file but missing from epic.yaml manifest"
|
|
71
|
+
grep -qE '^#{2,3} (17\.|Acceptance|Verification Script)' "$spec" \
|
|
72
|
+
|| report HIGH "Story $sid spec missing §17 acceptance criteria or Verification Script"
|
|
73
|
+
grep -qiE 'ambiguous|TBD|TODO|FIXME' "$spec" \
|
|
74
|
+
&& report MED "Story $sid spec contains ambiguous/TBD markers"
|
|
75
|
+
fi
|
|
76
|
+
|
|
74
77
|
grep -qE '^[[:space:]]*verify:' "$tasks" \
|
|
75
78
|
|| report CRITICAL "Story $sid tasks.yaml missing runnable verify: commands"
|
|
76
79
|
grep -qE '^status:[[:space:]]*(failing|todo|passing)' "$tasks" \
|
package/scripts/lib/sync-post.sh
CHANGED
|
@@ -24,7 +24,16 @@ sync_post_run() {
|
|
|
24
24
|
jq -n --arg name "bigpowers" \
|
|
25
25
|
--arg version "$pkg_version" \
|
|
26
26
|
--arg desc "${skill_count} skills — ${pkg_desc}" \
|
|
27
|
-
'{
|
|
27
|
+
'{
|
|
28
|
+
name: $name,
|
|
29
|
+
version: $version,
|
|
30
|
+
description: $desc,
|
|
31
|
+
keywords: ["gemini-extension"],
|
|
32
|
+
gemini: {
|
|
33
|
+
skills: ["./skills"],
|
|
34
|
+
commands: ["./commands"]
|
|
35
|
+
}
|
|
36
|
+
}' > "$GEMINI_MANIFEST"
|
|
28
37
|
|
|
29
38
|
jq -n --arg version "$pkg_version" \
|
|
30
39
|
--arg desc "${skill_count} skills — ${pkg_desc}" \
|