bossbuild 0.97.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/LICENSE +21 -0
- package/PRINCIPLES.md +70 -0
- package/README.md +213 -0
- package/VERSION +1 -0
- package/bin/boss +3 -0
- package/library/README.md +19 -0
- package/library/agents/.gitkeep +0 -0
- package/library/agents/mentor-venture.md +57 -0
- package/library/hooks/.gitkeep +0 -0
- package/library/hooks/auto-log.js +133 -0
- package/library/hooks/memory-cue.js +82 -0
- package/library/hooks/secrets-guard.js +87 -0
- package/library/memory-seed/README.md +29 -0
- package/library/memory-seed/durable-facts-example.md +16 -0
- package/library/practices/.gitkeep +0 -0
- package/library/practices/agent-security.md +111 -0
- package/library/practices/ai-adoption-culture.md +104 -0
- package/library/practices/ai-ux-patterns.md +246 -0
- package/library/practices/celebration-of-done.md +100 -0
- package/library/practices/conscience-voicing.md +121 -0
- package/library/practices/context-discipline.md +116 -0
- package/library/practices/design-system.md +152 -0
- package/library/practices/git-workflow.md +119 -0
- package/library/practices/harm-taxonomy.md +45 -0
- package/library/practices/quality-ratchet.md +48 -0
- package/library/practices/revalidation.md +57 -0
- package/library/practices/scalable-architecture.md +111 -0
- package/library/practices/ship-it-live.md +149 -0
- package/library/practices/skill-authoring.md +70 -0
- package/library/skills/.gitkeep +0 -0
- package/library/skills/boss-learn/SKILL.md +63 -0
- package/library/skills/boss-sync/SKILL.md +48 -0
- package/package.json +49 -0
- package/registry/CHANGELOG.md +2737 -0
- package/src/board.js +655 -0
- package/src/brain.js +288 -0
- package/src/cli.js +542 -0
- package/src/conscience.js +426 -0
- package/src/insights.js +147 -0
- package/src/learn.js +92 -0
- package/src/map.js +103 -0
- package/src/modes.js +82 -0
- package/src/paths.js +36 -0
- package/src/registry.js +34 -0
- package/src/scaffold.js +138 -0
- package/src/sync.js +292 -0
- package/src/team.js +103 -0
- package/stages/L0-quickstart/manifest.json +12 -0
- package/stages/L0-quickstart/template/.claude/agents/coder-generalist.md +31 -0
- package/stages/L0-quickstart/template/.claude/agents/mentor-venture.md +57 -0
- package/stages/L0-quickstart/template/.claude/agents/pm.md +28 -0
- package/stages/L0-quickstart/template/.claude/hooks/conscience.js +89 -0
- package/stages/L0-quickstart/template/.claude/hooks/lib/loop-runtime.js +507 -0
- package/stages/L0-quickstart/template/.claude/hooks/lib/yaml.js +163 -0
- package/stages/L0-quickstart/template/.claude/hooks/memory-cue.js +82 -0
- package/stages/L0-quickstart/template/.claude/hooks/secrets-guard.js +87 -0
- package/stages/L0-quickstart/template/.claude/rules/your-app-code.md +17 -0
- package/stages/L0-quickstart/template/.claude/settings.json +36 -0
- package/stages/L0-quickstart/template/.claude/skills/boss/SKILL.md +161 -0
- package/stages/L0-quickstart/template/.claude/skills/boss-learn/SKILL.md +63 -0
- package/stages/L0-quickstart/template/.claude/skills/boss-sync/SKILL.md +55 -0
- package/stages/L0-quickstart/template/.claude/skills/canvas/SKILL.md +112 -0
- package/stages/L0-quickstart/template/.claude/skills/comprehend/SKILL.md +72 -0
- package/stages/L0-quickstart/template/.claude/skills/decide/SKILL.md +122 -0
- package/stages/L0-quickstart/template/.claude/skills/feedback/SKILL.md +68 -0
- package/stages/L0-quickstart/template/.claude/skills/import/SKILL.md +73 -0
- package/stages/L0-quickstart/template/.claude/skills/persona/SKILL.md +92 -0
- package/stages/L0-quickstart/template/.claude/skills/prototype/SKILL.md +114 -0
- package/stages/L0-quickstart/template/.claude/skills/triage/SKILL.md +104 -0
- package/stages/L0-quickstart/template/.claude/skills/welcome/SKILL.md +262 -0
- package/stages/L0-quickstart/template/AGENTS.md +31 -0
- package/stages/L0-quickstart/template/CLAUDE.md +57 -0
- package/stages/L0-quickstart/template/docs/IDS.md +42 -0
- package/stages/L0-quickstart/template/docs/ideas/INDEX.md +24 -0
- package/stages/L0-quickstart/template/docs/loops/canvas-loop.md +90 -0
- package/stages/L0-quickstart/template/docs/loops/capture-loop.md +64 -0
- package/stages/L1-mvp/manifest.json +12 -0
- package/stages/L1-mvp/template/.claude/agents/mentor-architect.md +124 -0
- package/stages/L1-mvp/template/.claude/agents/mentor-cofounder.md +85 -0
- package/stages/L1-mvp/template/.claude/agents/mentor-gtm.md +49 -0
- package/stages/L1-mvp/template/.claude/agents/program-manager.md +46 -0
- package/stages/L1-mvp/template/.claude/agents/tester.md +42 -0
- package/stages/L1-mvp/template/.claude/hooks/auto-log.js +133 -0
- package/stages/L1-mvp/template/.claude/rules/feature-context.md +18 -0
- package/stages/L1-mvp/template/.claude/skills/ai-cost/SKILL.md +249 -0
- package/stages/L1-mvp/template/.claude/skills/ai-failure-states/SKILL.md +226 -0
- package/stages/L1-mvp/template/.claude/skills/ai-first-init/SKILL.md +227 -0
- package/stages/L1-mvp/template/.claude/skills/close/SKILL.md +170 -0
- package/stages/L1-mvp/template/.claude/skills/consult/SKILL.md +72 -0
- package/stages/L1-mvp/template/.claude/skills/cost-review/SKILL.md +204 -0
- package/stages/L1-mvp/template/.claude/skills/design-tokens-init/SKILL.md +192 -0
- package/stages/L1-mvp/template/.claude/skills/drift-deep/SKILL.md +170 -0
- package/stages/L1-mvp/template/.claude/skills/evals/SKILL.md +154 -0
- package/stages/L1-mvp/template/.claude/skills/extract/SKILL.md +209 -0
- package/stages/L1-mvp/template/.claude/skills/judge-traces/SKILL.md +68 -0
- package/stages/L1-mvp/template/.claude/skills/log/SKILL.md +64 -0
- package/stages/L1-mvp/template/.claude/skills/practice/SKILL.md +92 -0
- package/stages/L1-mvp/template/.claude/skills/pretotype/SKILL.md +95 -0
- package/stages/L1-mvp/template/.claude/skills/red-team/SKILL.md +137 -0
- package/stages/L1-mvp/template/.claude/skills/revalidate/SKILL.md +51 -0
- package/stages/L1-mvp/template/.claude/skills/ship/SKILL.md +105 -0
- package/stages/L1-mvp/template/.claude/skills/smoke/SKILL.md +43 -0
- package/stages/L1-mvp/template/.claude/skills/spec/SKILL.md +145 -0
- package/stages/L1-mvp/template/claude-append.md +122 -0
- package/stages/L1-mvp/template/docs/loops/ai-failure-state-loop.md +107 -0
- package/stages/L1-mvp/template/docs/loops/coordination-loop.md +116 -0
- package/stages/L1-mvp/template/docs/loops/cost-budget-loop.md +117 -0
- package/stages/L1-mvp/template/docs/loops/cost-review-loop.md +113 -0
- package/stages/L1-mvp/template/docs/loops/design-tokens-loop.md +98 -0
- package/stages/L1-mvp/template/docs/loops/drift-loop.md +149 -0
- package/stages/L1-mvp/template/docs/loops/extraction-loop.md +128 -0
- package/stages/L1-mvp/template/docs/loops/focus-loop.md +106 -0
- package/stages/L1-mvp/template/docs/loops/pretotype-loop.md +88 -0
- package/stages/L1-mvp/template/docs/loops/spec-loop.md +83 -0
- package/stages/L2-v1/manifest.json +12 -0
- package/stages/L2-v1/template/.claude/agents/db-architect.md +91 -0
- package/stages/L2-v1/template/.claude/agents/mentor-business.md +124 -0
- package/stages/L2-v1/template/.claude/agents/mentor-fundraising.md +72 -0
- package/stages/L2-v1/template/.claude/agents/mentor-pitch.md +84 -0
- package/stages/L2-v1/template/.claude/agents/mentor-talent.md +84 -0
- package/stages/L2-v1/template/.claude/agents/ui-designer.md +81 -0
- package/stages/L2-v1/template/.claude/agents/ux-designer.md +87 -0
- package/stages/L2-v1/template/.claude/skills/board/SKILL.md +98 -0
- package/stages/L2-v1/template/.claude/skills/design-review/SKILL.md +77 -0
- package/stages/L2-v1/template/.claude/skills/ux-check/SKILL.md +93 -0
- package/stages/L2-v1/template/claude-append.md +59 -0
- package/stages/L2-v1/template/docs/loops/design-drift-loop.md +108 -0
- package/stages/L3-scale/README.md +13 -0
package/src/cli.js
ADDED
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
import { mkdirSync, existsSync, writeFileSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join, resolve, basename } from 'node:path';
|
|
3
|
+
import { execSync, spawn } from 'node:child_process';
|
|
4
|
+
import { bossVersion, STAGE_ORDER, resolveStageId } from './paths.js';
|
|
5
|
+
import { applyStage, applyStageSafe, appendClaudeBlock, appendMarkedBlock, readStageManifest } from './scaffold.js';
|
|
6
|
+
import { registerProject, listProjects, findByPath } from './registry.js';
|
|
7
|
+
import { planSync, applySync, computeSettingsMerge } from './sync.js';
|
|
8
|
+
import { learn, LIBRARY_CATEGORIES } from './learn.js';
|
|
9
|
+
import { statusConscience, consciencePause, conscienceResume, conscienceMute, conscienceUnmute, conscienceActivity } from './conscience.js';
|
|
10
|
+
import { board, boardHtml } from './board.js';
|
|
11
|
+
import { map } from './map.js';
|
|
12
|
+
import { brain } from './brain.js';
|
|
13
|
+
import { insights } from './insights.js';
|
|
14
|
+
import { renderTeam, addCollaborator, removeCollaborator, isTeam, resolveIdentity } from './team.js';
|
|
15
|
+
|
|
16
|
+
const STAMP = '.boss/manifest.json';
|
|
17
|
+
|
|
18
|
+
function stageVars(name, stageId, mode) {
|
|
19
|
+
return {
|
|
20
|
+
PROJECT_NAME: name,
|
|
21
|
+
DATE: new Date().toISOString().slice(0, 10),
|
|
22
|
+
BOSS_VERSION: bossVersion(),
|
|
23
|
+
STAGE: stageId,
|
|
24
|
+
MODE: mode || stageId,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function writeStamp(targetDir, stamp) {
|
|
29
|
+
mkdirSync(join(targetDir, '.boss'), { recursive: true });
|
|
30
|
+
writeFileSync(join(targetDir, STAMP), JSON.stringify(stamp, null, 2) + '\n');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function readStamp(dir) {
|
|
34
|
+
const file = join(dir, STAMP);
|
|
35
|
+
if (!existsSync(file)) return null;
|
|
36
|
+
return JSON.parse(readFileSync(file, 'utf8'));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function cmdNew(args) {
|
|
40
|
+
const name = args.find((a) => !a.startsWith('--'));
|
|
41
|
+
const aiNative = args.includes('--ai'); // IDEA-022 Track 3 — additive, opt-in
|
|
42
|
+
if (!name) return fail('usage: boss new <project-name> [--ai]');
|
|
43
|
+
const targetDir = resolve(process.cwd(), name);
|
|
44
|
+
if (existsSync(targetDir)) return fail(`'${name}' already exists here.`);
|
|
45
|
+
|
|
46
|
+
const stageId = STAGE_ORDER[0]; // L0-quickstart
|
|
47
|
+
const manifest = readStageManifest(stageId);
|
|
48
|
+
mkdirSync(targetDir, { recursive: true });
|
|
49
|
+
applyStage(stageId, targetDir, stageVars(name, stageId, manifest.name));
|
|
50
|
+
|
|
51
|
+
const stamp = {
|
|
52
|
+
name,
|
|
53
|
+
bossVersion: bossVersion(),
|
|
54
|
+
stage: stageId,
|
|
55
|
+
mode: manifest.name,
|
|
56
|
+
installedLayers: [stageId],
|
|
57
|
+
agents: manifest.agents || [],
|
|
58
|
+
skills: manifest.skills || [],
|
|
59
|
+
hooks: manifest.hooks || [],
|
|
60
|
+
loops: manifest.loops || [],
|
|
61
|
+
createdAt: new Date().toISOString(),
|
|
62
|
+
};
|
|
63
|
+
writeStamp(targetDir, stamp);
|
|
64
|
+
|
|
65
|
+
// User-tunable defaults the /boss spin-up skill reads. Separate from manifest.json
|
|
66
|
+
// (the install record) so users can edit prefs without touching the layer ledger.
|
|
67
|
+
writeFileSync(
|
|
68
|
+
join(targetDir, '.boss', 'config.json'),
|
|
69
|
+
JSON.stringify({
|
|
70
|
+
github: 'ask', // ask | always | never — create a remote when an idea lands
|
|
71
|
+
visibility: 'private', // private | public
|
|
72
|
+
license: 'proprietary', // proprietary | MIT | Apache-2.0 | AGPL-3.0
|
|
73
|
+
// Optional founder-cohort declaration (v0.20.0+). When set, the conscience
|
|
74
|
+
// hook includes the cohort in its additionalContext so Claude composes the
|
|
75
|
+
// voice appropriately for the cohort — first-product gets teaching;
|
|
76
|
+
// returning-founder gets a harder question; vibe-virtuoso gets sharper
|
|
77
|
+
// architecture. Options: vibe-coder-newbie | eng-builder | non-tech-founder
|
|
78
|
+
// | first-product | vibe-virtuoso | indie-hacker | returning-founder |
|
|
79
|
+
// domain-expert | null. /boss skill asks during spin-up; user can edit later.
|
|
80
|
+
cohort: null,
|
|
81
|
+
// Opt-in share-up (IDEA-021/024). Default OFF — BOSS never sends usage anywhere on its own.
|
|
82
|
+
// `boss insights` reads your trace locally; `/feedback` sends only what you explicitly approve.
|
|
83
|
+
// If a future version offers to share anonymized loop-closure signals UP to improve BOSS, it
|
|
84
|
+
// is gated on this flag being true AND a per-send confirmation. Telemetry is never a default.
|
|
85
|
+
shareUp: false,
|
|
86
|
+
// AI-native augmentation (IDEA-022 Track 3, opt-in via `--ai`). When true, `/comprehend` tailors
|
|
87
|
+
// the scaffold to what BOSS understands (seeds the venture brain, fills the overview). The
|
|
88
|
+
// deterministic template scaffold above is ALWAYS the reversible base — this only augments it.
|
|
89
|
+
aiNative,
|
|
90
|
+
}, null, 2) + '\n',
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
execSync('git init -q', { cwd: targetDir });
|
|
95
|
+
} catch { /* git optional */ }
|
|
96
|
+
|
|
97
|
+
registerProject({
|
|
98
|
+
name,
|
|
99
|
+
path: targetDir,
|
|
100
|
+
stage: stageId,
|
|
101
|
+
mode: manifest.name,
|
|
102
|
+
bossVersion: bossVersion(),
|
|
103
|
+
createdAt: stamp.createdAt,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
console.log(`\n ✦ Created ${name} — ${manifest.name} mode (${stageId}, BOSS ${bossVersion()})`);
|
|
107
|
+
console.log(` agents: ${stamp.agents.join(', ') || '—'}`);
|
|
108
|
+
console.log(` skills: ${stamp.skills.join(', ') || '—'}`);
|
|
109
|
+
console.log(`\n Next:`);
|
|
110
|
+
console.log(` cd ${name}`);
|
|
111
|
+
console.log(` code ${name} # or open the folder in your editor (Cursor, etc.)`);
|
|
112
|
+
console.log(` claude # open Claude Code (works in the terminal or the editor panel)`);
|
|
113
|
+
console.log(` > /boss <your idea> # spin up — a sentence, a doc, a deck, or a link`);
|
|
114
|
+
console.log(` # (first time? /welcome · already written it down? /import <file|url>)`);
|
|
115
|
+
if (aiNative) {
|
|
116
|
+
console.log(` > /comprehend # AI-native: tailor the scaffold to what BOSS understands (augments, never replaces)`);
|
|
117
|
+
}
|
|
118
|
+
console.log('');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// boss adopt — bring BOSS into an ALREADY-STARTED repo, non-destructively.
|
|
122
|
+
// "Lite BOSS" is the design, not a fallback (Principle 2): adopt at the lightest
|
|
123
|
+
// register that matches where the app already is, then `boss unlock` upward on
|
|
124
|
+
// evidence. ≈ a safe scaffold (copy-if-absent) + settings merge + stamp + register.
|
|
125
|
+
function cmdAdopt(args) {
|
|
126
|
+
const flags = parseArgs(args);
|
|
127
|
+
const targetDir = process.cwd();
|
|
128
|
+
if (existsSync(join(targetDir, STAMP))) {
|
|
129
|
+
return fail('already a BOSS project (.boss/manifest.json here). Use `boss sync` to update or `boss unlock <mode>` to add a mode.');
|
|
130
|
+
}
|
|
131
|
+
// Default to the lightest register (Quickstart); `--mode mvp` etc. adopts higher
|
|
132
|
+
// when the app has already earned it (real users, a real build).
|
|
133
|
+
const stageId = flags.mode ? resolveStageId(flags.mode) : STAGE_ORDER[0];
|
|
134
|
+
if (!stageId) return fail(`unknown mode '${flags.mode}'.`);
|
|
135
|
+
let manifest;
|
|
136
|
+
try { manifest = readStageManifest(stageId); }
|
|
137
|
+
catch { return fail(`mode '${flags.mode}' isn't authored yet.`); }
|
|
138
|
+
|
|
139
|
+
const name = basename(targetDir);
|
|
140
|
+
|
|
141
|
+
// 1. Non-destructive scaffold of the FULL chain up to the target mode — adopting
|
|
142
|
+
// at MVP must also lay down Quickstart's foundation (welcome/boss/triage/...),
|
|
143
|
+
// exactly as `boss new` + `boss unlock mvp` would. Copy-if-absent throughout.
|
|
144
|
+
const chain = STAGE_ORDER
|
|
145
|
+
.slice(0, STAGE_ORDER.indexOf(stageId) + 1)
|
|
146
|
+
.filter((s) => { try { readStageManifest(s); return true; } catch { return false; } });
|
|
147
|
+
const claudePreexisted = existsSync(join(targetDir, 'CLAUDE.md'));
|
|
148
|
+
const agentsPreexisted = existsSync(join(targetDir, 'AGENTS.md'));
|
|
149
|
+
const copied = [];
|
|
150
|
+
const skipped = [];
|
|
151
|
+
for (const s of chain) {
|
|
152
|
+
const m = readStageManifest(s);
|
|
153
|
+
const r = applyStageSafe(s, targetDir, stageVars(name, s, m.name));
|
|
154
|
+
copied.push(...r.copied);
|
|
155
|
+
skipped.push(...r.skipped);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// 2a. If the repo already had an AGENTS.md, we skipped the template's — leave
|
|
159
|
+
// the founder's host-neutral rules intact and append BOSS's as a marked block
|
|
160
|
+
// (so BOSS's working discipline lands alongside theirs).
|
|
161
|
+
if (agentsPreexisted) {
|
|
162
|
+
appendMarkedBlock(join(targetDir, 'AGENTS.md'), 'adopt',
|
|
163
|
+
`## BOSS working rules — adopted ${stageVars(name, stageId, manifest.name).DATE}\n\n` +
|
|
164
|
+
`1. **Capture before you build** (every idea → \`IDEA-NNN\`; see \`docs/ideas/INDEX.md\`).\n` +
|
|
165
|
+
`2. **Stack-neutral until decided.** 3. **Docs are source of truth, not chat.**\n` +
|
|
166
|
+
`4. **Small, reversible steps.** 5. **Ask before irreversible actions.** 6. **Don't over-build.**\n` +
|
|
167
|
+
`7. **Grow through modes** (Quickstart → MVP → V1 → Scale): \`boss unlock <mode>\`.`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// 2b. If the repo already had a CLAUDE.md, we skipped the template's — leave the
|
|
171
|
+
// founder's rules intact, import the (now-present) AGENTS.md so the rules
|
|
172
|
+
// reach Claude, and append a small marked BOSS orientation block.
|
|
173
|
+
if (claudePreexisted) {
|
|
174
|
+
appendClaudeBlock('adopt', targetDir,
|
|
175
|
+
`@AGENTS.md\n\n` +
|
|
176
|
+
`## BOSS — adopted ${stageVars(name, stageId, manifest.name).DATE}\n\n` +
|
|
177
|
+
`This repo was adopted into BOSS at **${manifest.name}** mode (non-destructively — your files were untouched).\n` +
|
|
178
|
+
`Host-neutral working rules are imported from \`@AGENTS.md\` above. New: \`.claude/skills/\` + \`.claude/agents/\` for this mode, a conscience hook, and \`docs/\` capture surfaces.\n` +
|
|
179
|
+
`Run **\`/welcome\`** to orient, **\`/boss\`** to spin up an idea, or **\`boss map\`** to see what's available.\n` +
|
|
180
|
+
`Grow ceremony as the project earns it: \`boss unlock <mode>\`.`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 3. Stamp .boss/ (mode + not-self-hosted) so it's a real BOSS project. Agents /
|
|
184
|
+
// skills / hooks / loops are the UNION across the installed chain.
|
|
185
|
+
const u = { agents: new Set(), skills: new Set(), hooks: new Set(), loops: new Set() };
|
|
186
|
+
for (const s of chain) {
|
|
187
|
+
const m = readStageManifest(s);
|
|
188
|
+
(m.agents || []).forEach((x) => u.agents.add(x));
|
|
189
|
+
(m.skills || []).forEach((x) => u.skills.add(x));
|
|
190
|
+
(m.hooks || []).forEach((x) => u.hooks.add(x));
|
|
191
|
+
(m.loops || []).forEach((x) => u.loops.add(x));
|
|
192
|
+
}
|
|
193
|
+
const stamp = {
|
|
194
|
+
name, bossVersion: bossVersion(), stage: stageId, mode: manifest.name,
|
|
195
|
+
installedLayers: chain, agents: [...u.agents], skills: [...u.skills],
|
|
196
|
+
hooks: [...u.hooks], loops: [...u.loops],
|
|
197
|
+
createdAt: new Date().toISOString(), adopted: true,
|
|
198
|
+
};
|
|
199
|
+
writeStamp(targetDir, stamp);
|
|
200
|
+
// config.json only if absent — never clobber a founder's prefs.
|
|
201
|
+
const cfgPath = join(targetDir, '.boss', 'config.json');
|
|
202
|
+
if (!existsSync(cfgPath)) {
|
|
203
|
+
writeFileSync(cfgPath, JSON.stringify({
|
|
204
|
+
github: 'ask', visibility: 'private', license: 'proprietary', cohort: null, shareUp: false,
|
|
205
|
+
aiNative: !!flags.ai, // IDEA-022 Track 3 — `/comprehend` reads the adopted repo to tailor + seed the brain
|
|
206
|
+
}, null, 2) + '\n');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// 4. Merge the conscience hook registration into settings.json (additive —
|
|
210
|
+
// preserves the founder's permissions + any hooks they already wired).
|
|
211
|
+
const settings = computeSettingsMerge(targetDir, chain);
|
|
212
|
+
if (settings && settings.changed) {
|
|
213
|
+
const dest = join(targetDir, settings.rel);
|
|
214
|
+
mkdirSync(join(targetDir, '.claude'), { recursive: true });
|
|
215
|
+
writeFileSync(dest, JSON.stringify(settings.merged, null, 2) + '\n');
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// 5. Register as a normal (not self-hosted) project — rides the usual sync loop.
|
|
219
|
+
registerProject({
|
|
220
|
+
name, path: targetDir, stage: stageId, mode: manifest.name,
|
|
221
|
+
bossVersion: bossVersion(), createdAt: stamp.createdAt,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
console.log(`\n ✦ Adopted ${name} into BOSS — ${manifest.name} mode (${stageId}, BOSS ${bossVersion()})`);
|
|
225
|
+
console.log(` ${copied.length} file(s) added · ${skipped.length} of yours left untouched${claudePreexisted ? ' · CLAUDE.md preserved (BOSS block appended)' : ''}`);
|
|
226
|
+
console.log(` skills: ${stamp.skills.join(', ') || '—'}`);
|
|
227
|
+
console.log(`\n Next:`);
|
|
228
|
+
console.log(` claude # open Claude Code here`);
|
|
229
|
+
console.log(` > /welcome # what BOSS added + how the conscience works`);
|
|
230
|
+
if (flags.ai) {
|
|
231
|
+
console.log(` > /comprehend # AI-native: read this repo, tailor the scaffold + seed the venture brain`);
|
|
232
|
+
}
|
|
233
|
+
console.log(` boss map # what's available · boss unlock <mode> to grow`);
|
|
234
|
+
console.log('');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function cmdUnlock(args) {
|
|
238
|
+
const layer = args[0];
|
|
239
|
+
const stamp = readStamp(process.cwd());
|
|
240
|
+
if (!stamp) return fail('not a BOSS project (no .boss/manifest.json here).');
|
|
241
|
+
if (!layer) return fail(`usage: boss unlock <mode> (current: ${stamp.mode || stamp.stage})`);
|
|
242
|
+
|
|
243
|
+
const target = resolveStageId(layer);
|
|
244
|
+
if (!target) return fail(`unknown mode '${layer}'. options: ${STAGE_ORDER.join(', ')}`);
|
|
245
|
+
if (stamp.installedLayers.includes(target)) return fail(`${target} already installed.`);
|
|
246
|
+
|
|
247
|
+
let m, applied;
|
|
248
|
+
try {
|
|
249
|
+
m = readStageManifest(target);
|
|
250
|
+
applied = applyStage(target, process.cwd(), stageVars(stamp.name, target, m.name));
|
|
251
|
+
} catch (e) {
|
|
252
|
+
return fail(`${target} not authored yet — ${e.message}`);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
stamp.stage = target;
|
|
256
|
+
stamp.mode = m.name;
|
|
257
|
+
stamp.installedLayers.push(target);
|
|
258
|
+
stamp.agents = [...new Set([...(stamp.agents || []), ...(m.agents || [])])];
|
|
259
|
+
stamp.skills = [...new Set([...(stamp.skills || []), ...(m.skills || [])])];
|
|
260
|
+
stamp.hooks = [...new Set([...(stamp.hooks || []), ...(m.hooks || [])])];
|
|
261
|
+
stamp.loops = [...new Set([...(stamp.loops || []), ...(m.loops || [])])];
|
|
262
|
+
writeStamp(process.cwd(), stamp);
|
|
263
|
+
registerProject({ name: stamp.name, path: process.cwd(), stage: target, mode: m.name, bossVersion: bossVersion() });
|
|
264
|
+
console.log(`\n ✦ Unlocked ${m.name} mode (${target}).`);
|
|
265
|
+
if (applied.appendedClaude) console.log(` + appended ${m.name} working rules to CLAUDE.md`);
|
|
266
|
+
console.log('');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function cmdStatus(args) {
|
|
270
|
+
const stamp = readStamp(process.cwd());
|
|
271
|
+
if (!stamp) return fail('not a BOSS project (no .boss/manifest.json here).');
|
|
272
|
+
const f = parseArgs(args || []);
|
|
273
|
+
// `boss status --conscience` — drill into the conscience-state surface
|
|
274
|
+
// (asked-for by eng-builder / indie-hacker / vibe-virtuoso personas in
|
|
275
|
+
// v0.19 reactions: "I want to see what fired and why").
|
|
276
|
+
if (f.conscience) {
|
|
277
|
+
console.log(`\n ${stamp.name}`);
|
|
278
|
+
return statusConscience(process.cwd());
|
|
279
|
+
}
|
|
280
|
+
const current = bossVersion();
|
|
281
|
+
console.log(`\n ${stamp.name}`);
|
|
282
|
+
console.log(` mode: ${stamp.mode || stamp.stage} (${stamp.stage})`);
|
|
283
|
+
console.log(` layers: ${stamp.installedLayers.join(' → ')}`);
|
|
284
|
+
console.log(` BOSS pinned: ${stamp.bossVersion}`);
|
|
285
|
+
console.log(` BOSS current: ${current}`);
|
|
286
|
+
if (stamp.bossVersion !== current) {
|
|
287
|
+
console.log(` ⟳ newer practices available — run /boss-sync to review the diff`);
|
|
288
|
+
}
|
|
289
|
+
console.log('');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function cmdBoard(args = []) {
|
|
293
|
+
const stamp = readStamp(process.cwd());
|
|
294
|
+
if (!stamp) return fail('not a BOSS project (no .boss/manifest.json here).');
|
|
295
|
+
if (args.includes('--html')) {
|
|
296
|
+
const out = boardHtml(process.cwd(), stamp.name);
|
|
297
|
+
console.log(`\n ✦ Visual board → ${out}`);
|
|
298
|
+
console.log(' A read of your files. Re-run `boss board --html` to refresh.\n');
|
|
299
|
+
// Best-effort open in the default browser; printing the path is the contract.
|
|
300
|
+
const opener = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
301
|
+
try { spawn(opener, [out], { stdio: 'ignore', detached: true, shell: process.platform === 'win32' }).unref(); } catch { /* path already printed */ }
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
// Owner lens (founder layer slice 2b): show `@owner` on cards only when this is a
|
|
305
|
+
// team (dormant-solo); `--mine` narrows to the cards I own.
|
|
306
|
+
const me = args.includes('--mine') ? resolveIdentity().handle : null;
|
|
307
|
+
board(process.cwd(), stamp.name, {
|
|
308
|
+
next: args.includes('--next'),
|
|
309
|
+
blocked: args.includes('--blocked'),
|
|
310
|
+
json: args.includes('--json'),
|
|
311
|
+
all: args.includes('--all'),
|
|
312
|
+
owners: isTeam(process.cwd()),
|
|
313
|
+
mine: me ? '@' + me : null,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function cmdMap() {
|
|
318
|
+
const stamp = readStamp(process.cwd());
|
|
319
|
+
if (!stamp) return fail('not a BOSS project (no .boss/manifest.json here).');
|
|
320
|
+
map(process.cwd(), stamp);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function cmdBrain(args) {
|
|
324
|
+
const stamp = readStamp(process.cwd());
|
|
325
|
+
if (!stamp) return fail('not a BOSS project (no .boss/manifest.json here).');
|
|
326
|
+
try {
|
|
327
|
+
brain(process.cwd(), stamp, args);
|
|
328
|
+
} catch (e) {
|
|
329
|
+
return fail(e.message);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function cmdInsights() {
|
|
334
|
+
// Read-your-own-trace lens (IDEA-021): works across all registered projects on this machine,
|
|
335
|
+
// so it doesn't require being inside a BOSS project. Local-only; nothing is sent.
|
|
336
|
+
insights(process.cwd());
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function cmdTeam(args) {
|
|
340
|
+
// The venture's people (founder layer slice 2, IDEA-037/FEAT-021). Dormant-solo:
|
|
341
|
+
// an empty roster reads as a solo venture and changes nothing else.
|
|
342
|
+
const stamp = readStamp(process.cwd());
|
|
343
|
+
if (!stamp) return fail('not a BOSS project (no .boss/manifest.json here).');
|
|
344
|
+
const [sub, ...rest] = args;
|
|
345
|
+
const handle = rest.find((a) => !a.startsWith('--'));
|
|
346
|
+
const name = rest.filter((a) => a !== handle && !a.startsWith('--')).join(' ').trim() || null;
|
|
347
|
+
try {
|
|
348
|
+
if (sub === 'add') {
|
|
349
|
+
if (!handle) return fail('usage: boss team add <@github-username> ["Name"]');
|
|
350
|
+
const firstCofounder = !isTeam(process.cwd()); // solo → team transition
|
|
351
|
+
const r = addCollaborator(process.cwd(), handle, name);
|
|
352
|
+
const msg = r.added ? `\n ✦ Added ${r.handle} to the venture.`
|
|
353
|
+
: r.self ? `\n ${r.handle} is you — you're already on the venture.`
|
|
354
|
+
: `\n ${r.handle} is already on the venture.`;
|
|
355
|
+
console.log(msg);
|
|
356
|
+
// One-time, on the solo→team transition: point the new partnership at the
|
|
357
|
+
// consent conversation (founder layer slice 5 / ai-adoption-culture practice).
|
|
358
|
+
if (r.added && firstCofounder) {
|
|
359
|
+
console.log('\n You\'re a team now. Before you divide the work, have the AI consent + norms');
|
|
360
|
+
console.log(' conversation — ask `mentor-cofounder` to walk you through it (who automates what,');
|
|
361
|
+
console.log(' what stays human, "would I be proud to hand this to my cofounder?").');
|
|
362
|
+
}
|
|
363
|
+
} else if (sub === 'remove') {
|
|
364
|
+
if (!handle) return fail('usage: boss team remove <@github-username>');
|
|
365
|
+
const r = removeCollaborator(process.cwd(), handle);
|
|
366
|
+
console.log(r.removed ? '\n ✦ Removed from the venture.' : '\n Not on the roster.');
|
|
367
|
+
} else if (sub && sub !== 'list') {
|
|
368
|
+
return fail(`unknown subcommand 'team ${sub}'. options: (none) | add | remove`);
|
|
369
|
+
}
|
|
370
|
+
console.log(renderTeam(process.cwd()));
|
|
371
|
+
} catch (e) {
|
|
372
|
+
return fail(e.message);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function cmdList() {
|
|
377
|
+
const projects = listProjects();
|
|
378
|
+
if (!projects.length) {
|
|
379
|
+
console.log('\n No projects registered yet. Run `boss new <name>`.\n');
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
console.log(`\n ${projects.length} connected project(s):\n`);
|
|
383
|
+
for (const p of projects) {
|
|
384
|
+
console.log(` ${p.name.padEnd(20)} ${(p.mode || p.stage || '?').padEnd(12)} BOSS@${p.bossVersion || '?'}`);
|
|
385
|
+
console.log(` ${''.padEnd(20)} ${p.path}`);
|
|
386
|
+
}
|
|
387
|
+
console.log('');
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Minimal flag parser: returns { _: [positionals], flag: value|true }.
|
|
391
|
+
function parseArgs(args) {
|
|
392
|
+
const out = { _: [] };
|
|
393
|
+
for (let i = 0; i < args.length; i++) {
|
|
394
|
+
const a = args[i];
|
|
395
|
+
if (a.startsWith('--')) {
|
|
396
|
+
const key = a.slice(2);
|
|
397
|
+
const next = args[i + 1];
|
|
398
|
+
if (next !== undefined && !next.startsWith('--')) { out[key] = next; i++; }
|
|
399
|
+
else out[key] = true;
|
|
400
|
+
} else {
|
|
401
|
+
out._.push(a);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return out;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function cmdSync(args) {
|
|
408
|
+
const { _: pos, apply } = parseArgs(args);
|
|
409
|
+
void pos;
|
|
410
|
+
const stamp = readStamp(process.cwd());
|
|
411
|
+
if (!stamp) return fail('not a BOSS project (no .boss/manifest.json here).');
|
|
412
|
+
|
|
413
|
+
const plan = planSync(process.cwd(), stamp);
|
|
414
|
+
const changed = plan.entries.filter((e) => e.status !== 'ok');
|
|
415
|
+
const settingsChanged = !!(plan.settings && plan.settings.changed);
|
|
416
|
+
|
|
417
|
+
console.log(`\n ${stamp.name} — sync`);
|
|
418
|
+
console.log(` pin: ${plan.pin}${plan.drift ? ` → current ${plan.current}` : ' (current)'}`);
|
|
419
|
+
console.log(` layers: ${plan.layers.join(' → ')}\n`);
|
|
420
|
+
|
|
421
|
+
if (!changed.length && !settingsChanged) {
|
|
422
|
+
console.log(' ✓ BOSS-managed skills/agents/hooks are up to date.');
|
|
423
|
+
if (plan.drift && !apply) console.log(' (run `boss sync --apply` to bump the pin to current.)');
|
|
424
|
+
} else {
|
|
425
|
+
for (const e of changed) {
|
|
426
|
+
const mark = e.status === 'new' ? '+ new ' : `~ changed (${e.delta} lines)`;
|
|
427
|
+
console.log(` ${mark} ${e.kind}/${e.name} → ${e.rel}`);
|
|
428
|
+
}
|
|
429
|
+
if (settingsChanged) {
|
|
430
|
+
console.log(` ~ merge settings/hooks → ${plan.settings.rel} (additive — keeps your permissions)`);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (!apply) {
|
|
435
|
+
console.log('\n Preview only. Run `boss sync --apply` to write these and bump the pin,');
|
|
436
|
+
console.log(' or use `/boss-sync` in Claude for a reviewed, narrated update.\n');
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const { written, stamp: next } = applySync(process.cwd(), plan, stamp);
|
|
441
|
+
writeStamp(process.cwd(), next);
|
|
442
|
+
registerProject({
|
|
443
|
+
name: next.name, path: process.cwd(), stage: next.stage, mode: next.mode, bossVersion: next.bossVersion,
|
|
444
|
+
});
|
|
445
|
+
console.log(`\n ✦ Synced ${written.length} file(s); pin now ${next.bossVersion}.`);
|
|
446
|
+
if (written.length) console.log(' Review the changes with `git diff` before committing.\n');
|
|
447
|
+
else console.log('');
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function cmdLearn(args) {
|
|
451
|
+
const f = parseArgs(args);
|
|
452
|
+
const versionKind = f.major ? 'major' : f.patch ? 'patch' : 'minor';
|
|
453
|
+
let res;
|
|
454
|
+
try {
|
|
455
|
+
res = learn({
|
|
456
|
+
srcPath: f._[0],
|
|
457
|
+
category: f.as,
|
|
458
|
+
note: typeof f.note === 'string' ? f.note : undefined,
|
|
459
|
+
versionKind,
|
|
460
|
+
explicitVersion: typeof f.version === 'string' ? f.version : undefined,
|
|
461
|
+
});
|
|
462
|
+
} catch (e) {
|
|
463
|
+
return fail(e.message);
|
|
464
|
+
}
|
|
465
|
+
console.log(`\n ✦ Learned ${res.name} UP into ${res.dest}`);
|
|
466
|
+
console.log(` BOSS ${res.prev} → ${res.next} (VERSION + package.json + CHANGELOG updated)`);
|
|
467
|
+
console.log(` in ${res.root}`);
|
|
468
|
+
console.log(' Review, then commit. Connected projects pull it via `boss sync` / `/boss-sync`.\n');
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function cmdConscience(args) {
|
|
472
|
+
const [sub, ...rest] = args;
|
|
473
|
+
const flags = parseArgs(rest);
|
|
474
|
+
try {
|
|
475
|
+
if (sub === 'pause') return consciencePause(flags);
|
|
476
|
+
if (sub === 'resume') return conscienceResume();
|
|
477
|
+
if (sub === 'mute') return conscienceMute(flags);
|
|
478
|
+
if (sub === 'unmute') return conscienceUnmute(flags);
|
|
479
|
+
if (sub === 'activity') return conscienceActivity(process.cwd());
|
|
480
|
+
if (sub === 'cost') return conscienceActivity(process.cwd(), { asCost: true });
|
|
481
|
+
if (sub === 'status' || !sub) {
|
|
482
|
+
const stamp = readStamp(process.cwd());
|
|
483
|
+
if (!stamp) return fail('not a BOSS project (no .boss/manifest.json here).');
|
|
484
|
+
console.log(`\n ${stamp.name}`);
|
|
485
|
+
return statusConscience(process.cwd());
|
|
486
|
+
}
|
|
487
|
+
return fail(`unknown subcommand 'conscience ${sub}'. options: pause | resume | mute | unmute | status | activity | cost`);
|
|
488
|
+
} catch (e) {
|
|
489
|
+
return fail(e.message);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function fail(msg) {
|
|
494
|
+
console.error(` boss: ${msg}`);
|
|
495
|
+
process.exitCode = 1;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
export function run(argv) {
|
|
499
|
+
const [cmd, ...args] = argv;
|
|
500
|
+
switch (cmd) {
|
|
501
|
+
case 'new': return cmdNew(args);
|
|
502
|
+
case 'adopt': return cmdAdopt(args);
|
|
503
|
+
case 'unlock': return cmdUnlock(args);
|
|
504
|
+
case 'status': return cmdStatus(args);
|
|
505
|
+
case 'board': return cmdBoard(args);
|
|
506
|
+
case 'map': return cmdMap();
|
|
507
|
+
case 'brain': return cmdBrain(args);
|
|
508
|
+
case 'insights': return cmdInsights();
|
|
509
|
+
case 'team': return cmdTeam(args);
|
|
510
|
+
case 'list': return cmdList();
|
|
511
|
+
case 'sync': return cmdSync(args);
|
|
512
|
+
case 'learn': return cmdLearn(args);
|
|
513
|
+
case 'conscience': return cmdConscience(args);
|
|
514
|
+
case 'version': case '--version': case '-v':
|
|
515
|
+
return console.log(bossVersion());
|
|
516
|
+
default:
|
|
517
|
+
console.log(`BOSS ${bossVersion()}\n`);
|
|
518
|
+
console.log(' boss new <name> [--ai] scaffold a new project in Quickstart mode + register it (--ai: tailor via /comprehend)');
|
|
519
|
+
console.log(' boss adopt [--mode <m>] [--ai] bring BOSS into an already-started repo, non-destructively (--ai: comprehend it)');
|
|
520
|
+
console.log(' boss unlock <mode> level up: quickstart → mvp → v1 → scale');
|
|
521
|
+
console.log(' boss status this project: mode, pinned version, drift');
|
|
522
|
+
console.log(' boss status --conscience this project: loop states + cohort + recent overrides');
|
|
523
|
+
console.log(' boss map live cheatsheet: where you are + what\'s one unlock away');
|
|
524
|
+
console.log(' boss board [--html] a live read of what\'s in flight (captured → shipped); --html opens a visual kanban');
|
|
525
|
+
console.log(' boss board --next|--blocked|--json what to pick up next · what\'s not moving · the projection as JSON (agent-readable)');
|
|
526
|
+
console.log(' boss board --mine (team) narrow the board to the cards you own (owner: @you)');
|
|
527
|
+
console.log(' boss brain [--diff|--relationship] the conscience\'s read (POV); --diff = how it evolved; --relationship = what it said & what you did');
|
|
528
|
+
console.log(' boss brain forget --before <date> evict old reads (living memory; founder-invoked)');
|
|
529
|
+
console.log(' boss insights read your own projects\' trace: where each loop stands (local · nothing sent)');
|
|
530
|
+
console.log(' boss team [add @user|remove @user] who\'s on the venture (solo by default; add a cofounder to light up the team layer)');
|
|
531
|
+
console.log(' boss list all connected projects');
|
|
532
|
+
console.log(' boss sync [--apply] pull current BOSS skills/agents/hooks into this project (DOWN)');
|
|
533
|
+
console.log(` boss learn <p> --as <c> promote a pattern UP into the library (${LIBRARY_CATEGORIES.join('|')})`);
|
|
534
|
+
console.log(' boss conscience pause silence the conscience for a bounded session [--for 8h|--until-resume]');
|
|
535
|
+
console.log(' boss conscience resume re-enable the conscience');
|
|
536
|
+
console.log(' boss conscience mute silence ONE moment (drift|caution|…) [--for 7d|--until-resume]');
|
|
537
|
+
console.log(' boss conscience unmute un-silence a moment (or --all)');
|
|
538
|
+
console.log(' boss conscience activity how often the conscience fires (over-fire check; alias: cost)');
|
|
539
|
+
console.log(' boss version BOSS version\n');
|
|
540
|
+
console.log(' modes: Quickstart (capture an idea) · MVP (build it) · V1 (ship it) · Scale (grow it)\n');
|
|
541
|
+
}
|
|
542
|
+
}
|