odd-studio 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +19 -0
- package/README.md +229 -0
- package/bin/odd-studio.js +212 -0
- package/hooks/odd-destructive-guard.sh +98 -0
- package/hooks/odd-git-safety.sh +138 -0
- package/hooks/odd-outcome-quality.sh +84 -0
- package/hooks/odd-pre-build.sh +57 -0
- package/hooks/odd-session-save.sh +38 -0
- package/hooks/odd-ui-check.sh +69 -0
- package/package.json +43 -0
- package/scripts/install-skill.js +30 -0
- package/scripts/postinstall.js +28 -0
- package/scripts/scaffold-project.js +61 -0
- package/scripts/setup-hooks.js +105 -0
- package/skill/SKILL.md +464 -0
- package/skill/docs/build/build-protocol.md +532 -0
- package/skill/docs/kb/odd-kb.md +462 -0
- package/skill/docs/planning/build-planner.md +315 -0
- package/skill/docs/planning/outcome-writer.md +328 -0
- package/skill/docs/planning/persona-architect.md +258 -0
- package/skill/docs/planning/systems-mapper.md +270 -0
- package/skill/docs/ui/accessibility.md +415 -0
- package/skill/docs/ui/component-guide.md +356 -0
- package/skill/docs/ui/design-system.md +403 -0
- package/templates/.odd/state.json +16 -0
- package/templates/CLAUDE.md +93 -0
- package/templates/docs/contract-map.md +60 -0
- package/templates/docs/outcomes/.gitkeep +0 -0
- package/templates/docs/outcomes/example-outcome.md +104 -0
- package/templates/docs/personas/.gitkeep +0 -0
- package/templates/docs/personas/example-persona.md +108 -0
- package/templates/docs/plan.md +73 -0
- package/templates/docs/ui/.gitkeep +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "odd-studio",
|
|
3
|
+
"description": "Outcome-Driven Development — a planning and build harness for domain experts building serious software with AI. Installs the /odd skill, safety hooks, and project scaffolding into Claude Code.",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "ODD Studio"
|
|
7
|
+
},
|
|
8
|
+
"skills": [
|
|
9
|
+
"odd"
|
|
10
|
+
],
|
|
11
|
+
"hooks": [
|
|
12
|
+
"odd-git-safety",
|
|
13
|
+
"odd-destructive-guard",
|
|
14
|
+
"odd-outcome-quality",
|
|
15
|
+
"odd-ui-check",
|
|
16
|
+
"odd-pre-build",
|
|
17
|
+
"odd-session-save"
|
|
18
|
+
]
|
|
19
|
+
}
|
package/README.md
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# ODD Studio
|
|
2
|
+
|
|
3
|
+
**Outcome-Driven Development for Claude Code.**
|
|
4
|
+
|
|
5
|
+
A planning and build harness for domain experts who are building serious software with AI — and want to understand exactly what they're doing and why.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## What this is
|
|
10
|
+
|
|
11
|
+
ODD Studio turns Claude Code into a guided coach that takes you through every stage of building a real software system: from understanding your users, to specifying what the system must do, to directing an AI to build it correctly, to verifying that what was built matches what you intended.
|
|
12
|
+
|
|
13
|
+
You don't need to write code. You need to understand your domain. That's the skill that matters now.
|
|
14
|
+
|
|
15
|
+
> *"The AI is the most capable junior engineer who ever lived. It can build anything you describe. It will build exactly what you describe. It will not tell you that what you described is the wrong thing to build. That judgement remains entirely yours."*
|
|
16
|
+
|
|
17
|
+
ODD Studio is the companion tool to **[Book Title]** — the book that teaches Outcome-Driven Development from first principles. Every step in the tool references the relevant chapter. You'll understand the method as you use it, not just follow instructions.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx odd-studio init my-project
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
That's it. This single command:
|
|
28
|
+
|
|
29
|
+
- Scaffolds your project structure (`docs/`, `.odd/`, `CLAUDE.md`)
|
|
30
|
+
- Installs the `/odd` skill into Claude Code
|
|
31
|
+
- Installs six safety hooks into your Claude Code settings
|
|
32
|
+
- Initialises git with an initial commit
|
|
33
|
+
- Prints your three next steps
|
|
34
|
+
|
|
35
|
+
**Then:**
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
cd my-project
|
|
39
|
+
claude .
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Inside Claude Code, type:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
/odd
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## What happens when you type `/odd`
|
|
51
|
+
|
|
52
|
+
Claude Code loads the ODD orchestrator. It checks whether you have an existing project in progress (via your local `.odd/state.json` and ruflo memory) and either:
|
|
53
|
+
|
|
54
|
+
- **New project:** Welcomes you, explains what you're about to build together, and starts with the first question: *Who uses this system, and what do they actually need?*
|
|
55
|
+
- **Returning project:** Shows you exactly where you left off and resumes from there. Nothing is lost between sessions.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## The five stages
|
|
60
|
+
|
|
61
|
+
ODD Studio guides you through five stages, in order. You cannot skip ahead — each stage is the foundation for the next.
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
Stage 1 — Personas
|
|
65
|
+
Who uses your system? Under what constraints?
|
|
66
|
+
Built by: Diana (Persona Architect)
|
|
67
|
+
Output: docs/personas/[name].md for each user type
|
|
68
|
+
|
|
69
|
+
Stage 2 — Outcomes
|
|
70
|
+
What must the system make possible, for whom, when, and how?
|
|
71
|
+
Built by: Marcus (Outcome Writer)
|
|
72
|
+
Output: docs/outcomes/[name].md for each workflow
|
|
73
|
+
|
|
74
|
+
Stage 3 — Contracts
|
|
75
|
+
What does each outcome produce that others depend on?
|
|
76
|
+
Built by: Theo (Systems Mapper)
|
|
77
|
+
Output: docs/contract-map.md
|
|
78
|
+
|
|
79
|
+
Stage 4 — Master Implementation Plan
|
|
80
|
+
What gets built in what order, and why?
|
|
81
|
+
Built by: Rachel (Build Planner)
|
|
82
|
+
Output: docs/plan.md
|
|
83
|
+
|
|
84
|
+
Stage 5 — Build
|
|
85
|
+
Direct Claude Code to build outcome by outcome, verify each one,
|
|
86
|
+
and integrate them into a working system.
|
|
87
|
+
Powered by: ruflo swarm (parallel specialist agents)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
At every step, the tool explains why the step matters — not just what to do.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## The safety layer
|
|
95
|
+
|
|
96
|
+
ODD Studio installs six hooks into Claude Code that run automatically throughout your build:
|
|
97
|
+
|
|
98
|
+
| Hook | When | What it does |
|
|
99
|
+
|------|------|-------------|
|
|
100
|
+
| `odd-git-safety` | Before any bash command | Blocks force-push, hard-reset with uncommitted changes, `git checkout -- .`, `--no-verify` |
|
|
101
|
+
| `odd-destructive-guard` | Before any bash command | Blocks `rm -rf` on project docs, `.env` commits, database drops without warning |
|
|
102
|
+
| `odd-outcome-quality` | After writing to `docs/outcomes/` | Checks all 6 outcome fields are present; flags banned technical vocabulary |
|
|
103
|
+
| `odd-ui-check` | After editing frontend files | Surfaces accessibility reminders; prompts mobile verification |
|
|
104
|
+
| `odd-pre-build` | Before `npm run build` or deploy | Warns on uncommitted changes before deploy; flags unreviewed outcomes |
|
|
105
|
+
| `odd-session-save` | After `git commit` | Saves project state to `.odd/state.json` for session continuity |
|
|
106
|
+
|
|
107
|
+
These hooks inform and protect — they don't block legitimate work. The git hooks block genuinely destructive actions (force-push onto main, committing secrets). Everything else warns and coaches.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## The build layer (ruflo)
|
|
112
|
+
|
|
113
|
+
When you're ready to build, ODD Studio initialises a ruflo swarm — a team of parallel specialist agents that build your outcomes concurrently:
|
|
114
|
+
|
|
115
|
+
- **Coordinator** — reads your contracts, publishes shared technical contracts before parallel building begins (solves the "two architects, one door" problem)
|
|
116
|
+
- **Backend agent** — implements data layer and business logic per your outcome specifications
|
|
117
|
+
- **UI agent** — implements the frontend using shadcn/ui, Tailwind CSS v4, and Framer Motion, against WCAG 2.1 AA accessibility standards
|
|
118
|
+
- **QA agent** — runs your verification steps and reports failures in your language, not technical error messages
|
|
119
|
+
|
|
120
|
+
Ruflo memory ensures continuity across Claude Code sessions — every agent knows the full project state.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Default tech stack
|
|
125
|
+
|
|
126
|
+
ODD Studio configures a considered default stack that handles 90% of projects well:
|
|
127
|
+
|
|
128
|
+
| Layer | Technology | Why |
|
|
129
|
+
|-------|-----------|-----|
|
|
130
|
+
| Framework | Next.js (App Router) + TypeScript | Server-rendered, fast, well-supported |
|
|
131
|
+
| Styling | Tailwind CSS v4 | Consistent design without custom CSS |
|
|
132
|
+
| Components | shadcn/ui | Beautiful, accessible, you own the code |
|
|
133
|
+
| Primitives | Radix UI | Keyboard navigation and ARIA built in |
|
|
134
|
+
| Animation | Framer Motion | Micro-interactions without complexity |
|
|
135
|
+
| Database | PostgreSQL via Prisma | Reliable, type-safe, good migrations |
|
|
136
|
+
| Auth | NextAuth.js | Handles the complexity you don't want to |
|
|
137
|
+
| Payments | Stripe | The right choice for most use cases |
|
|
138
|
+
| Email | Resend | Modern, reliable, developer-friendly |
|
|
139
|
+
| Deploy | Vercel | Zero-configuration deployment |
|
|
140
|
+
|
|
141
|
+
The AI proposes the stack based on your outcomes. You approve it based on consequences, not technical details. If your outcomes require something different (offline-capable, specific hosting requirements, existing systems to integrate with), tell the AI in domain terms and it will adapt.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Project structure
|
|
146
|
+
|
|
147
|
+
After `odd-studio init`, your project looks like this:
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
my-project/
|
|
151
|
+
├── CLAUDE.md ← ODD build rules for Claude Code
|
|
152
|
+
├── .odd/
|
|
153
|
+
│ └── state.json ← Project state (updated automatically)
|
|
154
|
+
└── docs/
|
|
155
|
+
├── plan.md ← Master Implementation Plan
|
|
156
|
+
├── contract-map.md ← Contracts and dependency graph
|
|
157
|
+
├── personas/ ← One file per user type
|
|
158
|
+
│ └── example-persona.md ← Example to learn from (then delete)
|
|
159
|
+
├── outcomes/ ← One file per workflow
|
|
160
|
+
│ └── example-outcome.md ← Example to learn from (then delete)
|
|
161
|
+
└── ui/ ← UI specifications per outcome
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Commands inside `/odd`
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
*plan Start or resume planning (routes to the right stage)
|
|
170
|
+
*build Start or resume building with ruflo swarm
|
|
171
|
+
*status Show full project state
|
|
172
|
+
*persona Jump to persona creation
|
|
173
|
+
*outcome Jump to outcome writing
|
|
174
|
+
*contracts Jump to contract mapping
|
|
175
|
+
*phase-plan Jump to implementation planning
|
|
176
|
+
*ui Load UI excellence layer briefing
|
|
177
|
+
*swarm Initialise ruflo swarm for parallel build
|
|
178
|
+
*export Generate IDE Session Brief → docs/session-brief.md
|
|
179
|
+
*chapter [n] Load coaching from relevant book chapter
|
|
180
|
+
*why Explain why the current step matters
|
|
181
|
+
*kb Load full ODD knowledge base
|
|
182
|
+
*help Show all commands
|
|
183
|
+
*reset Clear state and start over (asks for confirmation)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Other CLI commands
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
odd-studio status # Show current planning state for this project
|
|
192
|
+
odd-studio upgrade # Update the /odd skill and hooks to latest version
|
|
193
|
+
odd-studio export # Instructions for exporting the Session Brief
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## What you'll learn
|
|
199
|
+
|
|
200
|
+
By the time you've built your first system with ODD Studio, you'll understand:
|
|
201
|
+
|
|
202
|
+
- Why your users' constraints — not their preferences — determine good software design
|
|
203
|
+
- Why the connections between features matter more than the features themselves
|
|
204
|
+
- How to describe what you want precisely enough that an AI builds it correctly
|
|
205
|
+
- How to verify that what was built matches what you intended
|
|
206
|
+
- How to direct a build across multiple sessions without losing context
|
|
207
|
+
- How to catch problems before they become expensive to fix
|
|
208
|
+
|
|
209
|
+
These are the skills that make you effective at building with AI — permanently, not just for this project.
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## The book
|
|
214
|
+
|
|
215
|
+
ODD Studio implements the methodology from **[Book Title]**. Each step in the tool references the chapter that explains the underlying principle. The book and the tool are the same learning experience — you can start with either.
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Requirements
|
|
220
|
+
|
|
221
|
+
- Node.js 18+
|
|
222
|
+
- Claude Code installed (`npm install -g @anthropic-ai/claude-code`)
|
|
223
|
+
- ruflo MCP configured in Claude Code (for swarm build features)
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## License
|
|
228
|
+
|
|
229
|
+
MIT
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
import { program } from 'commander';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import ora from 'ora';
|
|
7
|
+
import { createRequire } from 'module';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import fs from 'fs-extra';
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = path.dirname(__filename);
|
|
14
|
+
const require = createRequire(import.meta.url);
|
|
15
|
+
const pkg = require('../package.json');
|
|
16
|
+
|
|
17
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
18
|
+
|
|
19
|
+
// ─── Visual helpers ──────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
const print = {
|
|
22
|
+
logo: () => {
|
|
23
|
+
console.log(chalk.bold.cyan('\n 🎯 ODD Studio'));
|
|
24
|
+
console.log(chalk.dim(' Outcome-Driven Development for Claude Code\n'));
|
|
25
|
+
},
|
|
26
|
+
step: (n, total, msg) => console.log(chalk.dim(` [${n}/${total}]`) + ' ' + msg),
|
|
27
|
+
ok: (msg) => console.log(chalk.green(' ✓ ') + msg),
|
|
28
|
+
warn: (msg) => console.log(chalk.yellow(' ⚠ ') + msg),
|
|
29
|
+
err: (msg) => console.log(chalk.red(' ✗ ') + msg),
|
|
30
|
+
info: (msg) => console.log(chalk.dim(' → ') + msg),
|
|
31
|
+
blank: () => console.log(),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// ─── CLI definition ───────────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
program
|
|
37
|
+
.name('odd-studio')
|
|
38
|
+
.description('Outcome-Driven Development harness for Claude Code')
|
|
39
|
+
.version(pkg.version);
|
|
40
|
+
|
|
41
|
+
// ── init ──────────────────────────────────────────────────────────────────────
|
|
42
|
+
program
|
|
43
|
+
.command('init [project-name]')
|
|
44
|
+
.description('Scaffold a new ODD project and install the /odd skill into Claude Code')
|
|
45
|
+
.option('--skip-skill', 'Skip installing the /odd skill globally (advanced)')
|
|
46
|
+
.option('--skip-hooks', 'Skip installing safety hooks (not recommended)')
|
|
47
|
+
.option('--yes', 'Accept all defaults without prompting')
|
|
48
|
+
.action(async (projectName, options) => {
|
|
49
|
+
print.logo();
|
|
50
|
+
|
|
51
|
+
const { default: installSkill } = await import('../scripts/install-skill.js');
|
|
52
|
+
const { default: setupHooks } = await import('../scripts/setup-hooks.js');
|
|
53
|
+
const { default: scaffoldProject } = await import('../scripts/scaffold-project.js');
|
|
54
|
+
|
|
55
|
+
// Resolve project directory
|
|
56
|
+
const targetDir = projectName
|
|
57
|
+
? path.resolve(process.cwd(), projectName)
|
|
58
|
+
: process.cwd();
|
|
59
|
+
|
|
60
|
+
const resolvedName = projectName || path.basename(process.cwd());
|
|
61
|
+
|
|
62
|
+
console.log(chalk.bold(` Setting up: ${chalk.cyan(resolvedName)}\n`));
|
|
63
|
+
|
|
64
|
+
// 1. Scaffold project structure
|
|
65
|
+
print.step(1, 3, 'Creating project structure...');
|
|
66
|
+
const spinner1 = ora({ text: '', indent: 4 }).start();
|
|
67
|
+
try {
|
|
68
|
+
await scaffoldProject(targetDir, resolvedName);
|
|
69
|
+
spinner1.stop();
|
|
70
|
+
print.ok('Project scaffolded at ' + chalk.cyan(targetDir));
|
|
71
|
+
} catch (e) {
|
|
72
|
+
spinner1.stop();
|
|
73
|
+
print.err('Failed to scaffold project: ' + e.message);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 2. Install /odd skill
|
|
78
|
+
if (!options.skipSkill) {
|
|
79
|
+
print.step(2, 3, 'Installing /odd skill into Claude Code...');
|
|
80
|
+
const spinner2 = ora({ text: '', indent: 4 }).start();
|
|
81
|
+
try {
|
|
82
|
+
const result = await installSkill(PACKAGE_ROOT);
|
|
83
|
+
spinner2.stop();
|
|
84
|
+
print.ok('Skill installed → ' + chalk.cyan(result.destination));
|
|
85
|
+
} catch (e) {
|
|
86
|
+
spinner2.stop();
|
|
87
|
+
print.warn('Could not install skill automatically: ' + e.message);
|
|
88
|
+
print.info('Manual install: copy ' + chalk.dim('skill/') + ' to ' + chalk.dim('~/.claude/skills/odd/'));
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
print.step(2, 3, 'Skipping skill install (--skip-skill)');
|
|
92
|
+
print.warn('Remember to install the skill manually for /odd to work in Claude Code.');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 3. Setup hooks
|
|
96
|
+
if (!options.skipHooks) {
|
|
97
|
+
print.step(3, 3, 'Installing safety hooks into Claude Code settings...');
|
|
98
|
+
const spinner3 = ora({ text: '', indent: 4 }).start();
|
|
99
|
+
try {
|
|
100
|
+
const result = await setupHooks(PACKAGE_ROOT);
|
|
101
|
+
spinner3.stop();
|
|
102
|
+
print.ok('Hooks installed → ' + result.hookCount + ' hooks active');
|
|
103
|
+
} catch (e) {
|
|
104
|
+
spinner3.stop();
|
|
105
|
+
print.warn('Could not install hooks automatically: ' + e.message);
|
|
106
|
+
print.info('Manual install: add entries from ' + chalk.dim('hooks/') + ' to ~/.claude/settings.json');
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
print.step(3, 3, 'Skipping hooks (--skip-hooks)');
|
|
110
|
+
print.warn('Safety hooks not installed. Git guardrails and quality gates will not run.');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ── Done ──
|
|
114
|
+
print.blank();
|
|
115
|
+
console.log(chalk.bold.green(' ✓ ODD Studio is ready.\n'));
|
|
116
|
+
|
|
117
|
+
console.log(chalk.bold(' Next steps:'));
|
|
118
|
+
console.log(chalk.dim(' ─────────────────────────────────────────'));
|
|
119
|
+
if (projectName) {
|
|
120
|
+
console.log(' 1. ' + chalk.cyan(`cd ${projectName}`));
|
|
121
|
+
}
|
|
122
|
+
console.log(' ' + (projectName ? '2' : '1') + '. Open Claude Code: ' + chalk.cyan('claude .'));
|
|
123
|
+
console.log(' ' + (projectName ? '3' : '2') + '. Start your ODD session: ' + chalk.cyan('/odd'));
|
|
124
|
+
print.blank();
|
|
125
|
+
|
|
126
|
+
console.log(chalk.dim(' ODD Studio implements Outcome-Driven Development.'));
|
|
127
|
+
console.log(chalk.dim(' At every step, you\'ll understand why — not just what to do.'));
|
|
128
|
+
print.blank();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// ── status ────────────────────────────────────────────────────────────────────
|
|
132
|
+
program
|
|
133
|
+
.command('status')
|
|
134
|
+
.description('Show the ODD planning status for the current project')
|
|
135
|
+
.action(async () => {
|
|
136
|
+
print.logo();
|
|
137
|
+
const stateFile = path.resolve(process.cwd(), '.odd', 'state.json');
|
|
138
|
+
if (!fs.existsSync(stateFile)) {
|
|
139
|
+
print.warn('No ODD state found in this directory.');
|
|
140
|
+
print.info('Run ' + chalk.cyan('odd-studio init') + ' to initialise a project, or ' + chalk.cyan('cd') + ' into an existing ODD project.');
|
|
141
|
+
process.exit(0);
|
|
142
|
+
}
|
|
143
|
+
const state = await fs.readJson(stateFile);
|
|
144
|
+
console.log(chalk.bold(' Project: ') + chalk.cyan(state.projectName || 'unnamed'));
|
|
145
|
+
console.log(chalk.bold(' Phase: ') + (state.currentPhase || 'Not started'));
|
|
146
|
+
console.log(chalk.bold(' Step: ') + (state.currentStep || 'Not started'));
|
|
147
|
+
print.blank();
|
|
148
|
+
if (state.personas?.length) {
|
|
149
|
+
console.log(chalk.bold(' Personas (' + state.personas.length + '):'));
|
|
150
|
+
state.personas.forEach(p => console.log(' ' + chalk.green('✓ ') + p));
|
|
151
|
+
}
|
|
152
|
+
if (state.outcomes?.length) {
|
|
153
|
+
console.log(chalk.bold(' Outcomes (' + state.outcomes.length + '):'));
|
|
154
|
+
state.outcomes.forEach(o => {
|
|
155
|
+
const icon = o.status === 'verified' ? chalk.green('✓') : o.status === 'reviewed' ? chalk.yellow('◑') : chalk.dim('○');
|
|
156
|
+
console.log(' ' + icon + ' ' + o.name + chalk.dim(' [' + (o.status || 'draft') + ']'));
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
print.blank();
|
|
160
|
+
console.log(chalk.dim(' Open Claude Code and type /odd to resume.'));
|
|
161
|
+
print.blank();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// ── upgrade ───────────────────────────────────────────────────────────────────
|
|
165
|
+
program
|
|
166
|
+
.command('upgrade')
|
|
167
|
+
.description('Upgrade the /odd skill and hooks to the latest version')
|
|
168
|
+
.action(async () => {
|
|
169
|
+
print.logo();
|
|
170
|
+
const { default: installSkill } = await import('../scripts/install-skill.js');
|
|
171
|
+
const { default: setupHooks } = await import('../scripts/setup-hooks.js');
|
|
172
|
+
|
|
173
|
+
console.log(chalk.bold(' Upgrading ODD Studio...\n'));
|
|
174
|
+
|
|
175
|
+
const s1 = ora({ text: 'Updating /odd skill...', indent: 4 }).start();
|
|
176
|
+
try {
|
|
177
|
+
await installSkill(PACKAGE_ROOT, { force: true });
|
|
178
|
+
s1.succeed('Skill updated');
|
|
179
|
+
} catch (e) {
|
|
180
|
+
s1.fail('Skill update failed: ' + e.message);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const s2 = ora({ text: 'Updating hooks...', indent: 4 }).start();
|
|
184
|
+
try {
|
|
185
|
+
await setupHooks(PACKAGE_ROOT, { force: true });
|
|
186
|
+
s2.succeed('Hooks updated');
|
|
187
|
+
} catch (e) {
|
|
188
|
+
s2.fail('Hooks update failed: ' + e.message);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
print.blank();
|
|
192
|
+
print.ok('Upgrade complete.');
|
|
193
|
+
print.blank();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// ── export ────────────────────────────────────────────────────────────────────
|
|
197
|
+
program
|
|
198
|
+
.command('export')
|
|
199
|
+
.description('Export the IDE Session Brief from current project state')
|
|
200
|
+
.action(async () => {
|
|
201
|
+
print.logo();
|
|
202
|
+
const stateFile = path.resolve(process.cwd(), '.odd', 'state.json');
|
|
203
|
+
if (!fs.existsSync(stateFile)) {
|
|
204
|
+
print.err('No ODD state found. Run odd-studio init first.');
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
print.info('Open Claude Code and run: ' + chalk.cyan('/odd') + ' then ' + chalk.cyan('*export'));
|
|
208
|
+
print.info('The Session Brief will be saved to ' + chalk.cyan('docs/session-brief.md'));
|
|
209
|
+
print.blank();
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
program.parse();
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# odd-destructive-guard.sh
|
|
3
|
+
# ODD Studio — Destructive Command Guard (PreToolUse: Bash)
|
|
4
|
+
#
|
|
5
|
+
# Blocks or warns on shell commands that could cause irreversible damage:
|
|
6
|
+
# rm -rf, overwriting critical files, dropping databases, etc.
|
|
7
|
+
|
|
8
|
+
COMMAND="${CLAUDE_TOOL_INPUT:-}"
|
|
9
|
+
|
|
10
|
+
block() {
|
|
11
|
+
echo "🛡️ ODD DESTRUCTIVE GUARD: $1" >&2
|
|
12
|
+
echo "" >&2
|
|
13
|
+
echo "$2" >&2
|
|
14
|
+
exit 2
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
warn() {
|
|
18
|
+
echo "⚠️ ODD DESTRUCTIVE GUARD WARNING: $1" >&2
|
|
19
|
+
echo "$2" >&2
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
# ── rm -rf ────────────────────────────────────────────────────────────────────
|
|
23
|
+
if echo "$COMMAND" | grep -qE 'rm\s+(-[a-zA-Z]*r[a-zA-Z]*f|-[a-zA-Z]*f[a-zA-Z]*r)'; then
|
|
24
|
+
TARGET=$(echo "$COMMAND" | grep -oE 'rm\s+-[a-zA-Z]+\s+\S+' | awk '{print $3}')
|
|
25
|
+
|
|
26
|
+
# Always block rm -rf on common dangerous targets
|
|
27
|
+
if echo "$TARGET" | grep -qE '^(/|~|\$HOME|\.\.)'; then
|
|
28
|
+
block \
|
|
29
|
+
"rm -rf on root-level or parent directory: $TARGET" \
|
|
30
|
+
"This would recursively delete critical system or project files.
|
|
31
|
+
ODD Studio always blocks rm -rf on potentially dangerous paths."
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# Block rm -rf on the project docs/ directory
|
|
35
|
+
if echo "$TARGET" | grep -qE '(docs|personas|outcomes|plan\.md|contract-map)'; then
|
|
36
|
+
block \
|
|
37
|
+
"rm -rf on ODD project documentation: $TARGET" \
|
|
38
|
+
"This would destroy your persona, outcome, and plan documents.
|
|
39
|
+
These are your build specifications — deleting them means losing your planning work.
|
|
40
|
+
If you genuinely need to remove a file, delete it specifically by name."
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# Block rm -rf on .claude/
|
|
44
|
+
if echo "$TARGET" | grep -qE '\.claude'; then
|
|
45
|
+
block \
|
|
46
|
+
"rm -rf on .claude/ directory." \
|
|
47
|
+
"This would remove your Claude Code skills, hooks, and settings.
|
|
48
|
+
Run 'odd-studio upgrade' to update, or edit settings manually."
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Warn (don't block) on other rm -rf
|
|
52
|
+
warn \
|
|
53
|
+
"rm -rf detected: $COMMAND" \
|
|
54
|
+
"Ensure this is intentional — recursive deletion cannot be undone."
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# ── Overwriting .env files ────────────────────────────────────────────────────
|
|
58
|
+
if echo "$COMMAND" | grep -qE '>\s*\.env(\s|$)'; then
|
|
59
|
+
block \
|
|
60
|
+
"Overwriting .env file detected." \
|
|
61
|
+
"ODD Studio never overwrites .env files — they contain credentials.
|
|
62
|
+
Edit .env manually or use a specific key: echo 'KEY=value' >> .env"
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# ── Committing secrets ────────────────────────────────────────────────────────
|
|
66
|
+
if echo "$COMMAND" | grep -qE 'git\s+(add|commit).*\.env'; then
|
|
67
|
+
block \
|
|
68
|
+
"Attempting to commit .env file." \
|
|
69
|
+
"ODD Studio never commits .env files — they contain credentials.
|
|
70
|
+
Add .env to .gitignore if it isn't already:
|
|
71
|
+
echo '.env' >> .gitignore"
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# ── Dropping databases ────────────────────────────────────────────────────────
|
|
75
|
+
if echo "$COMMAND" | grep -qiE '(drop\s+database|dropdb|DROP\s+TABLE.*CASCADE)'; then
|
|
76
|
+
warn \
|
|
77
|
+
"Database destructive operation detected: $COMMAND" \
|
|
78
|
+
"Ensure you are targeting the development database, not production.
|
|
79
|
+
ODD Studio recommends confirming the DATABASE_URL before destructive DB operations."
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
# ── Killing all node processes ────────────────────────────────────────────────
|
|
83
|
+
if echo "$COMMAND" | grep -qE 'kill\s+-9\s+(all|-1|0)'; then
|
|
84
|
+
warn \
|
|
85
|
+
"kill -9 all processes detected." \
|
|
86
|
+
"This terminates all processes for your user, including any development servers.
|
|
87
|
+
Use kill -9 <PID> to target a specific process."
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
# ── curl | bash (pipe to shell) ───────────────────────────────────────────────
|
|
91
|
+
if echo "$COMMAND" | grep -qE '(curl|wget).*\|\s*(bash|sh|zsh)'; then
|
|
92
|
+
warn \
|
|
93
|
+
"Pipe-to-shell pattern detected: $COMMAND" \
|
|
94
|
+
"Executing remote scripts directly is a security risk.
|
|
95
|
+
Consider downloading and reviewing the script before executing."
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
exit 0
|