create-claude-stack 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/README.md +126 -0
- package/bin/create.js +353 -0
- package/package.json +36 -0
- package/template/CLAUDE.md +88 -0
- package/template/agents/engineer.md +39 -0
- package/template/agents/obsidian.md +51 -0
- package/template/agents/pm.md +45 -0
- package/template/agents/researcher.md +57 -0
- package/template/hooks/discord-notify.sh +46 -0
- package/template/hooks/gsd-context-monitor.js +156 -0
- package/template/hooks/safety-guard.js +81 -0
- package/template/mcp.json +34 -0
- package/template/rules/agents.md +20 -0
- package/template/rules/code-standards.md +30 -0
- package/template/rules/cognitive.md +37 -0
- package/template/rules/git-workflow.md +22 -0
- package/template/rules/new-project.md +13 -0
- package/template/settings.json +145 -0
package/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Claude Power Stack
|
|
2
|
+
|
|
3
|
+
A power-user `~/.claude` configuration for Claude Code — with GSD workflow, safety hooks, modular rules, and a fleet of specialized agents.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# npm create (recommended — zero global install)
|
|
9
|
+
npm create claude-stack@latest
|
|
10
|
+
|
|
11
|
+
# or npx
|
|
12
|
+
npx create-claude-stack@latest
|
|
13
|
+
|
|
14
|
+
# or curl (macOS / Linux / WSL)
|
|
15
|
+
curl -fsSL https://raw.githubusercontent.com/nattechan/claude-power-stack/main/install.sh | bash
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The installer is interactive: it asks where your projects live, whether you use Obsidian, and whether you want Discord notifications. **Existing files are never silently overwritten** — conflicts are shown with a diff and you choose what to do.
|
|
19
|
+
|
|
20
|
+
## What's included
|
|
21
|
+
|
|
22
|
+
### `CLAUDE.md`
|
|
23
|
+
Global instructions that load in every Claude Code session. Establishes the GSD workflow, tool hierarchy, confidence protocol, and session state pattern. Edit it to describe your stack.
|
|
24
|
+
|
|
25
|
+
### `rules/`
|
|
26
|
+
Modular rule files loaded alongside `CLAUDE.md`:
|
|
27
|
+
|
|
28
|
+
| File | What it governs |
|
|
29
|
+
|------|----------------|
|
|
30
|
+
| `cognitive.md` | Think flags, PDCA loop, confidence protocol |
|
|
31
|
+
| `git-workflow.md` | Branch model (`nc{N}`), commit format, PR flow |
|
|
32
|
+
| `code-standards.md` | Language standards, TDD, file size limits |
|
|
33
|
+
| `agents.md` | Which agent to reach for which task |
|
|
34
|
+
| `new-project.md` | New project checklist, GSD customization |
|
|
35
|
+
|
|
36
|
+
### `hooks/`
|
|
37
|
+
Lifecycle hooks that run automatically:
|
|
38
|
+
|
|
39
|
+
| Hook | When it runs | What it does |
|
|
40
|
+
|------|-------------|--------------|
|
|
41
|
+
| `safety-guard.js` | Before every Bash tool call | Blocks `rm -rf /`, fork bombs, DROP DATABASE, force push to main, etc. |
|
|
42
|
+
| `gsd-context-monitor.js` | After every tool call | Warns the agent at 35% / 25% context remaining so it can wrap up before hitting limits |
|
|
43
|
+
| `discord-notify.sh` | On session stop + notification | Posts a message to a Discord channel (optional — needs `DISCORD_WEBHOOK_URL` in `.env`) |
|
|
44
|
+
|
|
45
|
+
### `agents/`
|
|
46
|
+
Specialized subagents dispatched with the Agent tool:
|
|
47
|
+
|
|
48
|
+
| Agent | Use for |
|
|
49
|
+
|-------|---------|
|
|
50
|
+
| `engineer` | Code implementation, debugging, TDD, PR review |
|
|
51
|
+
| `pm` | GSD phase planning, roadmap, milestone management |
|
|
52
|
+
| `researcher` | Web research, docs, library comparisons |
|
|
53
|
+
| `obsidian` | Save findings to vault, query knowledge, daily logs |
|
|
54
|
+
|
|
55
|
+
### `mcp.json`
|
|
56
|
+
Global MCP server configuration: `filesystem`, `brave-search`, `firecrawl`, `repomix`.
|
|
57
|
+
|
|
58
|
+
### `settings.json`
|
|
59
|
+
Permissions (allow/ask/deny), hooks wiring, enabled plugins, and autoApprove list.
|
|
60
|
+
|
|
61
|
+
## Prerequisites
|
|
62
|
+
|
|
63
|
+
- **Claude Code CLI** — [install guide](https://docs.anthropic.com/en/docs/claude-code)
|
|
64
|
+
- **Node.js 18+** — required for hooks and the installer
|
|
65
|
+
- **GSD plugin** — install inside Claude Code: `/install get-shit-done`
|
|
66
|
+
|
|
67
|
+
## Recommended plugins
|
|
68
|
+
|
|
69
|
+
Install these in Claude Code after setup:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
superpowers — writing plans, executing plans, brainstorming, debugging
|
|
73
|
+
everything-claude-code — 100+ skills: TDD, security review, code review, etc.
|
|
74
|
+
commit-commands — /commit, /push, /pr in one shot
|
|
75
|
+
feature-dev — feature planning and implementation workflow
|
|
76
|
+
frontend-design — UI/UX implementation workflow
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Optional integrations
|
|
80
|
+
|
|
81
|
+
### Discord notifications
|
|
82
|
+
|
|
83
|
+
Add your webhook to `~/.claude/.env`:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/your/webhook
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Session-complete and attention-needed events post to your channel.
|
|
90
|
+
|
|
91
|
+
### Obsidian vault
|
|
92
|
+
|
|
93
|
+
If you chose Obsidian during install, the `obsidian` agent knows your vault path.
|
|
94
|
+
Install the `obsidian` plugin in Claude Code for `/obsidian-daily-note`, `/obsidian-compress`, etc.
|
|
95
|
+
|
|
96
|
+
### Brave Search
|
|
97
|
+
|
|
98
|
+
Free tier at [brave.com/search/api](https://brave.com/search/api) — 2 000 queries/month.
|
|
99
|
+
Add `BRAVE_API_KEY` to `~/.claude/.env`.
|
|
100
|
+
|
|
101
|
+
## Customizing
|
|
102
|
+
|
|
103
|
+
- **CLAUDE.md** — edit to describe your tech stack, preferred tools, and project domains
|
|
104
|
+
- **rules/** — each file is ≤ 100 lines; add or remove rule files as needed
|
|
105
|
+
- **agents/** — copy an existing agent as a template for domain-specific agents
|
|
106
|
+
- **hooks/** — edit `safety-guard.js` to add your own blocked command patterns
|
|
107
|
+
- **mcp.json** — add project-specific MCP servers (n8n, Supabase, Sentry, etc.)
|
|
108
|
+
|
|
109
|
+
## Project layout after install
|
|
110
|
+
|
|
111
|
+
```text
|
|
112
|
+
~/.claude/
|
|
113
|
+
├── CLAUDE.md ← Global instructions
|
|
114
|
+
├── mcp.json ← MCP servers
|
|
115
|
+
├── settings.json ← Permissions + hooks
|
|
116
|
+
├── .env.example ← API key template (copy to .env)
|
|
117
|
+
├── rules/ ← Modular rule files
|
|
118
|
+
├── hooks/ ← Lifecycle hooks
|
|
119
|
+
├── agents/ ← Subagent definitions
|
|
120
|
+
└── tasks/
|
|
121
|
+
└── lessons.md ← Self-learning log
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
MIT
|
package/bin/create.js
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const rl_ = require('readline');
|
|
8
|
+
const { execSync } = require('child_process');
|
|
9
|
+
|
|
10
|
+
const HOME = os.homedir();
|
|
11
|
+
const CLAUDE_DIR = path.join(HOME, '.claude');
|
|
12
|
+
const TMPL_DIR = path.join(__dirname, '..', 'template');
|
|
13
|
+
|
|
14
|
+
// ── ANSI helpers ──────────────────────────────────────────────────────────────
|
|
15
|
+
const C = {
|
|
16
|
+
green: s => `\x1b[32m${s}\x1b[0m`,
|
|
17
|
+
yellow: s => `\x1b[33m${s}\x1b[0m`,
|
|
18
|
+
cyan: s => `\x1b[36m${s}\x1b[0m`,
|
|
19
|
+
red: s => `\x1b[31m${s}\x1b[0m`,
|
|
20
|
+
bold: s => `\x1b[1m${s}\x1b[0m`,
|
|
21
|
+
dim: s => `\x1b[2m${s}\x1b[0m`,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const ok = msg => console.log(` ${C.green('✓')} ${msg}`);
|
|
25
|
+
const skip = msg => console.log(` ${C.dim('–')} ${msg} ${C.dim('(skipped)')}`);
|
|
26
|
+
const warn = msg => console.log(` ${C.yellow('⚠')} ${msg}`);
|
|
27
|
+
const err = msg => console.log(` ${C.red('✗')} ${msg}`);
|
|
28
|
+
const hdr = msg => console.log(`\n${C.bold(msg)}`);
|
|
29
|
+
|
|
30
|
+
const DRY_RUN = process.argv.includes('--dry-run');
|
|
31
|
+
|
|
32
|
+
// ── Readline helpers ──────────────────────────────────────────────────────────
|
|
33
|
+
let rl;
|
|
34
|
+
|
|
35
|
+
function openRL() {
|
|
36
|
+
rl = rl_.createInterface({ input: process.stdin, output: process.stdout });
|
|
37
|
+
}
|
|
38
|
+
function closeRL() {
|
|
39
|
+
rl.close();
|
|
40
|
+
}
|
|
41
|
+
function ask(question, defaultVal) {
|
|
42
|
+
return new Promise(resolve => {
|
|
43
|
+
const hint = defaultVal ? ` ${C.dim(`[${defaultVal}]`)}` : '';
|
|
44
|
+
rl.question(` ${question}${hint} `, ans => {
|
|
45
|
+
resolve(ans.trim() || defaultVal || '');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function askYN(question, defaultYes = true) {
|
|
50
|
+
return new Promise(resolve => {
|
|
51
|
+
const hint = defaultYes ? C.dim('[Y/n]') : C.dim('[y/N]');
|
|
52
|
+
rl.question(` ${question} ${hint} `, ans => {
|
|
53
|
+
const a = ans.trim().toLowerCase();
|
|
54
|
+
resolve(!a ? defaultYes : a === 'y' || a === 'yes');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
async function askChoice(question, choices) {
|
|
59
|
+
// choices: [{key, label}]
|
|
60
|
+
console.log(` ${question}`);
|
|
61
|
+
choices.forEach(({ key, label }) => console.log(` ${C.cyan(key)}) ${label}`));
|
|
62
|
+
return new Promise(resolve => {
|
|
63
|
+
rl.question(' Choice: ', ans => resolve(ans.trim().toLowerCase()));
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ── File helpers ──────────────────────────────────────────────────────────────
|
|
68
|
+
function readTemplate(relPath) {
|
|
69
|
+
return fs.readFileSync(path.join(TMPL_DIR, relPath), 'utf8');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function substitute(content, vars) {
|
|
73
|
+
let result = content;
|
|
74
|
+
for (const [k, v] of Object.entries(vars)) {
|
|
75
|
+
result = result.split(k).join(v);
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function fileExists(relPath) {
|
|
81
|
+
return fs.existsSync(path.join(CLAUDE_DIR, relPath));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function destContent(relPath) {
|
|
85
|
+
const p = path.join(CLAUDE_DIR, relPath);
|
|
86
|
+
return fs.existsSync(p) ? fs.readFileSync(p, 'utf8') : null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ── Conflict resolution ───────────────────────────────────────────────────────
|
|
90
|
+
// Returns true if the file should be written.
|
|
91
|
+
async function resolveConflict(relPath, newContent) {
|
|
92
|
+
const existing = destContent(relPath);
|
|
93
|
+
|
|
94
|
+
// File doesn't exist → always write
|
|
95
|
+
if (existing === null) return true;
|
|
96
|
+
|
|
97
|
+
// File is identical → nothing to do
|
|
98
|
+
if (existing === newContent) { skip(relPath); return false; }
|
|
99
|
+
|
|
100
|
+
// File differs → ask
|
|
101
|
+
console.log(`\n ${C.yellow('conflict')} ${relPath}`);
|
|
102
|
+
const choice = await askChoice(
|
|
103
|
+
'This file already exists and differs from the template. What would you like to do?',
|
|
104
|
+
[
|
|
105
|
+
{ key: 'o', label: 'Overwrite with template version' },
|
|
106
|
+
{ key: 'k', label: 'Keep existing (skip this file)' },
|
|
107
|
+
{ key: 'm', label: 'Merge: keep existing, append template as a comment block at the end' },
|
|
108
|
+
{ key: 'd', label: 'Show diff then decide' },
|
|
109
|
+
]
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
if (choice === 'd') {
|
|
113
|
+
// Show a simple line diff summary
|
|
114
|
+
const existingLines = existing.split('\n');
|
|
115
|
+
const newLines = newContent.split('\n');
|
|
116
|
+
console.log(C.dim(` --- existing (${existingLines.length} lines) +++ template (${newLines.length} lines)`));
|
|
117
|
+
// Print first differing lines for context
|
|
118
|
+
let shown = 0;
|
|
119
|
+
for (let i = 0; i < Math.max(existingLines.length, newLines.length) && shown < 8; i++) {
|
|
120
|
+
const eL = existingLines[i] ?? '';
|
|
121
|
+
const nL = newLines[i] ?? '';
|
|
122
|
+
if (eL !== nL) {
|
|
123
|
+
if (eL) console.log(C.red(` - ${eL.slice(0, 80)}`));
|
|
124
|
+
if (nL) console.log(C.green(` + ${nL.slice(0, 80)}`));
|
|
125
|
+
shown++;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (shown === 8) console.log(C.dim(' ... (truncated)'));
|
|
129
|
+
return resolveConflict(relPath, newContent); // re-ask after showing diff
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (choice === 'o') return true;
|
|
133
|
+
if (choice === 'k') { skip(relPath); return false; }
|
|
134
|
+
if (choice === 'm') {
|
|
135
|
+
const merged = existing + '\n\n# ─── Template version (appended by claude-power-stack installer) ───\n# ' +
|
|
136
|
+
newContent.split('\n').join('\n# ');
|
|
137
|
+
if (!DRY_RUN) {
|
|
138
|
+
fs.mkdirSync(path.dirname(path.join(CLAUDE_DIR, relPath)), { recursive: true });
|
|
139
|
+
fs.writeFileSync(path.join(CLAUDE_DIR, relPath), merged);
|
|
140
|
+
}
|
|
141
|
+
ok(`${relPath} ${C.dim('(merged)')}`);
|
|
142
|
+
return false; // already written
|
|
143
|
+
}
|
|
144
|
+
// unrecognised → keep
|
|
145
|
+
skip(relPath);
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ── Backup ────────────────────────────────────────────────────────────────────
|
|
150
|
+
function backupExisting() {
|
|
151
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
152
|
+
const backup = `${CLAUDE_DIR}.bak-${stamp}`;
|
|
153
|
+
if (!DRY_RUN) fs.cpSync(CLAUDE_DIR, backup, { recursive: true });
|
|
154
|
+
warn(`Full backup written to ${backup}`);
|
|
155
|
+
return backup;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ── Install one file ──────────────────────────────────────────────────────────
|
|
159
|
+
async function installFile(relPath, vars) {
|
|
160
|
+
let content;
|
|
161
|
+
try {
|
|
162
|
+
content = readTemplate(relPath);
|
|
163
|
+
} catch {
|
|
164
|
+
warn(`Template not found: ${relPath}`);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const final = substitute(content, vars);
|
|
169
|
+
const write = await resolveConflict(relPath, final);
|
|
170
|
+
|
|
171
|
+
if (write) {
|
|
172
|
+
if (!DRY_RUN) {
|
|
173
|
+
fs.mkdirSync(path.dirname(path.join(CLAUDE_DIR, relPath)), { recursive: true });
|
|
174
|
+
fs.writeFileSync(path.join(CLAUDE_DIR, relPath), final);
|
|
175
|
+
}
|
|
176
|
+
ok(relPath);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ── Preview what will change ──────────────────────────────────────────────────
|
|
181
|
+
function previewChanges(files, vars) {
|
|
182
|
+
const statuses = files.map(f => {
|
|
183
|
+
const tmplPath = path.join(TMPL_DIR, f);
|
|
184
|
+
if (!fs.existsSync(tmplPath)) return { f, status: 'missing-template' };
|
|
185
|
+
const existing = destContent(f);
|
|
186
|
+
if (existing === null) return { f, status: 'new' };
|
|
187
|
+
const newContent = substitute(fs.readFileSync(tmplPath, 'utf8'), vars);
|
|
188
|
+
if (existing === newContent) return { f, status: 'unchanged' };
|
|
189
|
+
return { f, status: 'conflict' };
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const newFiles = statuses.filter(s => s.status === 'new');
|
|
193
|
+
const conflicts = statuses.filter(s => s.status === 'conflict');
|
|
194
|
+
const unchanged = statuses.filter(s => s.status === 'unchanged');
|
|
195
|
+
|
|
196
|
+
hdr('Preview of changes:');
|
|
197
|
+
if (newFiles.length) {
|
|
198
|
+
console.log(`\n ${C.green('New files')} (${newFiles.length}):`);
|
|
199
|
+
newFiles.forEach(s => console.log(` ${C.green('+')} ${s.f}`));
|
|
200
|
+
}
|
|
201
|
+
if (conflicts.length) {
|
|
202
|
+
console.log(`\n ${C.yellow('Conflicts')} (${conflicts.length}) — you will be asked what to do:`);
|
|
203
|
+
conflicts.forEach(s => console.log(` ${C.yellow('~')} ${s.f}`));
|
|
204
|
+
}
|
|
205
|
+
if (unchanged.length) {
|
|
206
|
+
console.log(`\n ${C.dim('Unchanged')} (${unchanged.length}) — will be skipped automatically`);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return { newFiles, conflicts, unchanged };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
213
|
+
async function main() {
|
|
214
|
+
console.log(`\n${C.bold('━━━ Claude Power Stack Installer ━━━')}`);
|
|
215
|
+
console.log(' Power-user ~/.claude configuration with GSD, safety hooks, and modular rules.\n');
|
|
216
|
+
|
|
217
|
+
if (DRY_RUN) warn('Dry-run mode — no files will be written.\n');
|
|
218
|
+
|
|
219
|
+
openRL();
|
|
220
|
+
|
|
221
|
+
// 1. Gather config
|
|
222
|
+
hdr('Configuration:');
|
|
223
|
+
const srcDir = await ask('Where do you store your projects?', path.join(HOME, 'src'));
|
|
224
|
+
const useObsidian = await askYN('Do you use Obsidian?', false);
|
|
225
|
+
let obsidianPath = path.join(HOME, 'obsidian_vault');
|
|
226
|
+
if (useObsidian) {
|
|
227
|
+
const defaultVault = path.join(HOME, 'Library', 'Mobile Documents',
|
|
228
|
+
'iCloud~md~obsidian', 'Documents', 'obsidian_vault');
|
|
229
|
+
obsidianPath = await ask('Obsidian vault path?', defaultVault);
|
|
230
|
+
}
|
|
231
|
+
const useDiscord = await askYN('Enable Discord notifications? (add webhook to .env later)', false);
|
|
232
|
+
|
|
233
|
+
closeRL();
|
|
234
|
+
|
|
235
|
+
const vars = {
|
|
236
|
+
'__SRC_DIR__': srcDir,
|
|
237
|
+
'__OBSIDIAN_PATH__': obsidianPath,
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const ALL_FILES = [
|
|
241
|
+
'CLAUDE.md',
|
|
242
|
+
'mcp.json',
|
|
243
|
+
'settings.json',
|
|
244
|
+
'rules/cognitive.md',
|
|
245
|
+
'rules/git-workflow.md',
|
|
246
|
+
'rules/code-standards.md',
|
|
247
|
+
'rules/agents.md',
|
|
248
|
+
'rules/new-project.md',
|
|
249
|
+
'hooks/safety-guard.js',
|
|
250
|
+
'hooks/gsd-context-monitor.js',
|
|
251
|
+
'hooks/discord-notify.sh',
|
|
252
|
+
'agents/engineer.md',
|
|
253
|
+
'agents/pm.md',
|
|
254
|
+
'agents/researcher.md',
|
|
255
|
+
'agents/obsidian.md',
|
|
256
|
+
];
|
|
257
|
+
|
|
258
|
+
// 2. Preview
|
|
259
|
+
const existingClaude = fs.existsSync(CLAUDE_DIR);
|
|
260
|
+
const preview = previewChanges(ALL_FILES, vars);
|
|
261
|
+
|
|
262
|
+
// 3. Backup offer
|
|
263
|
+
if (existingClaude) {
|
|
264
|
+
openRL();
|
|
265
|
+
console.log('');
|
|
266
|
+
const doBackup = await askYN(
|
|
267
|
+
`~/.claude already exists. Create a full backup before installing?`,
|
|
268
|
+
true
|
|
269
|
+
);
|
|
270
|
+
if (doBackup) backupExisting();
|
|
271
|
+
closeRL();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// 4. Final confirm
|
|
275
|
+
openRL();
|
|
276
|
+
console.log('');
|
|
277
|
+
console.log(` Will install to: ${C.cyan(CLAUDE_DIR)}`);
|
|
278
|
+
if (preview.conflicts.length) {
|
|
279
|
+
console.log(` ${C.yellow(preview.conflicts.length + ' conflicts')} will be resolved interactively.`);
|
|
280
|
+
}
|
|
281
|
+
const go = await askYN('Proceed with installation?', true);
|
|
282
|
+
closeRL();
|
|
283
|
+
|
|
284
|
+
if (!go) { console.log('\n Aborted.'); process.exit(0); }
|
|
285
|
+
|
|
286
|
+
// 5. Install files
|
|
287
|
+
hdr('Installing:');
|
|
288
|
+
if (!DRY_RUN) fs.mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
289
|
+
|
|
290
|
+
openRL(); // keep open for conflict resolution prompts
|
|
291
|
+
for (const f of ALL_FILES) {
|
|
292
|
+
await installFile(f, vars);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Make discord hook executable
|
|
296
|
+
const discordHookDest = path.join(CLAUDE_DIR, 'hooks', 'discord-notify.sh');
|
|
297
|
+
if (!DRY_RUN && fs.existsSync(discordHookDest)) {
|
|
298
|
+
fs.chmodSync(discordHookDest, 0o755);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// .env.example
|
|
302
|
+
const envLines = [
|
|
303
|
+
'# Claude Power Stack — API Keys',
|
|
304
|
+
'# Copy this file to .env and fill in your keys.',
|
|
305
|
+
'',
|
|
306
|
+
'# Web search (free tier: https://brave.com/search/api/)',
|
|
307
|
+
'BRAVE_API_KEY=',
|
|
308
|
+
'',
|
|
309
|
+
'# Web scraping (https://firecrawl.dev)',
|
|
310
|
+
'FIRECRAWL_API_KEY=',
|
|
311
|
+
];
|
|
312
|
+
if (useDiscord) {
|
|
313
|
+
envLines.push('', '# Discord notifications (create webhook in Server Settings → Integrations)');
|
|
314
|
+
envLines.push('DISCORD_WEBHOOK_URL=');
|
|
315
|
+
}
|
|
316
|
+
const envContent = envLines.join('\n') + '\n';
|
|
317
|
+
const envWrite = await resolveConflict('.env.example', envContent);
|
|
318
|
+
if (envWrite && !DRY_RUN) {
|
|
319
|
+
fs.writeFileSync(path.join(CLAUDE_DIR, '.env.example'), envContent);
|
|
320
|
+
ok('.env.example');
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// tasks/ scaffold (only create if doesn't exist)
|
|
324
|
+
const lessonsPath = path.join(CLAUDE_DIR, 'tasks', 'lessons.md');
|
|
325
|
+
if (!fs.existsSync(lessonsPath)) {
|
|
326
|
+
if (!DRY_RUN) {
|
|
327
|
+
fs.mkdirSync(path.join(CLAUDE_DIR, 'tasks'), { recursive: true });
|
|
328
|
+
fs.writeFileSync(lessonsPath, '# Lessons\n\n| Date | What went wrong | Rule to follow |\n|------|----------------|----------------|\n');
|
|
329
|
+
}
|
|
330
|
+
ok('tasks/lessons.md');
|
|
331
|
+
} else {
|
|
332
|
+
skip('tasks/lessons.md (already exists — your notes are safe)');
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
closeRL();
|
|
336
|
+
|
|
337
|
+
// 6. Next steps
|
|
338
|
+
hdr('Done! Next steps:\n');
|
|
339
|
+
console.log(` 1. ${C.bold('Add API keys')}`);
|
|
340
|
+
console.log(` cp ~/.claude/.env.example ~/.claude/.env`);
|
|
341
|
+
console.log(` # Edit .env — add BRAVE_API_KEY at minimum\n`);
|
|
342
|
+
console.log(` 2. ${C.bold('Install GSD workflow plugin')} (primary workflow engine)`);
|
|
343
|
+
console.log(` Open Claude Code and run: /install get-shit-done\n`);
|
|
344
|
+
console.log(` 3. ${C.bold('Install recommended plugins')} in Claude Code:`);
|
|
345
|
+
console.log(` superpowers · everything-claude-code · commit-commands\n`);
|
|
346
|
+
if (useDiscord) {
|
|
347
|
+
console.log(` 4. ${C.bold('Set up Discord notifications')}`);
|
|
348
|
+
console.log(` Add your webhook URL to ~/.claude/.env as DISCORD_WEBHOOK_URL\n`);
|
|
349
|
+
}
|
|
350
|
+
console.log(` Full docs: https://github.com/nattechan/claude-power-stack\n`);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
main().catch(e => { console.error(C.red('\nFatal: ' + e.message)); process.exit(1); });
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-claude-stack",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Scaffold a power-user Claude Code configuration with GSD workflow, safety hooks, and modular rules",
|
|
5
|
+
"bin": {
|
|
6
|
+
"create-claude-stack": "bin/create.js"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/nattechan/claude-power-stack.git"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/nattechan/claude-power-stack#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/nattechan/claude-power-stack/issues"
|
|
15
|
+
},
|
|
16
|
+
"author": "",
|
|
17
|
+
"files": [
|
|
18
|
+
"bin/",
|
|
19
|
+
"template/"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"test": "node bin/create.js --dry-run"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"claude",
|
|
26
|
+
"claude-code",
|
|
27
|
+
"anthropic",
|
|
28
|
+
"ai",
|
|
29
|
+
"developer-tools",
|
|
30
|
+
"configuration"
|
|
31
|
+
],
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18.0.0"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# CLAUDE.md — Global Protocol
|
|
2
|
+
|
|
3
|
+
## Context Budget
|
|
4
|
+
|
|
5
|
+
`CLAUDE.md` ≤ 150 lines. Each `rules/` file ≤ 100 lines. Prefer a file path reference over inlining content. If adding content would exceed the budget, trim something first.
|
|
6
|
+
|
|
7
|
+
## Role
|
|
8
|
+
|
|
9
|
+
Generalist engineer and technical PM. Delegates to specialized agents.
|
|
10
|
+
GSD is the primary workflow for all project work.
|
|
11
|
+
|
|
12
|
+
## Primary Workflow — GSD
|
|
13
|
+
|
|
14
|
+
```text
|
|
15
|
+
/gsd:discuss-phase → /gsd:plan-phase → /gsd:execute-phase → /gsd:verify-work → /gsd:ship
|
|
16
|
+
...repeat per phase... → /gsd:complete-milestone → /gsd:new-milestone
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Quick access: `/gsd:new-project` | `/gsd:progress` | `/gsd:plan-phase` | `/gsd:execute-phase` | `/gsd:quick` | `/gsd:do`
|
|
20
|
+
|
|
21
|
+
## Available Tools
|
|
22
|
+
|
|
23
|
+
**Hierarchy: CLI > MCP > Plugin.**
|
|
24
|
+
|
|
25
|
+
### CLIs
|
|
26
|
+
|
|
27
|
+
| CLI | Purpose |
|
|
28
|
+
|-----|---------|
|
|
29
|
+
| `gh` | GitHub: PRs, issues, Actions, releases |
|
|
30
|
+
| `git` | version control |
|
|
31
|
+
| `supabase` | local dev, migrations, functions |
|
|
32
|
+
| `vercel` | deploy and manage Vercel projects |
|
|
33
|
+
| `docker` | build images, compose stacks |
|
|
34
|
+
| `npx playwright` / `playwright-cli` | browser automation |
|
|
35
|
+
| `repomix` | pack repo into LLM file (`--style markdown`, `--remote <url>`) |
|
|
36
|
+
| `gemini` | Gemini CLI: research, code review, multi-modal |
|
|
37
|
+
| `firecrawl` | web scraping: scrape, crawl, map, search |
|
|
38
|
+
| `tsc` / `ts-node` | TypeScript compiler and runner |
|
|
39
|
+
| `markdownlint-cli2` | lint/fix markdown |
|
|
40
|
+
|
|
41
|
+
### MCPs
|
|
42
|
+
|
|
43
|
+
- `filesystem` — direct access to `__SRC_DIR__`
|
|
44
|
+
- `context7` — up-to-date library docs
|
|
45
|
+
- `brave-search` — web search (requires `BRAVE_API_KEY` in `~/.claude/.env`)
|
|
46
|
+
- `firecrawl` — web scraping (requires `FIRECRAWL_API_KEY`)
|
|
47
|
+
|
|
48
|
+
### Plugins (invoke via Skill tool)
|
|
49
|
+
|
|
50
|
+
`superpowers` · `GSD` · `commit-commands` · `everything-claude-code` · `feature-dev` · `frontend-design` · `app-security` · `cost-reducer`
|
|
51
|
+
|
|
52
|
+
## Universal Rules
|
|
53
|
+
|
|
54
|
+
1. Check for relevant skill before acting — use Skill tool
|
|
55
|
+
2. GSD for any multi-step project — never plan inline in chat
|
|
56
|
+
3. Tool hierarchy: CLI > MCP > Plugin
|
|
57
|
+
4. Use `gh` CLI for all GitHub operations — no GitHub MCP
|
|
58
|
+
5. Browser automation: `playwright-cli` / `npx playwright` only
|
|
59
|
+
6. Verification: never mark complete without proving it works (run tests, check logs, diff behavior)
|
|
60
|
+
7. Self-learning: after any correction → append to `~/.claude/tasks/lessons.md`: `[date] | what went wrong | rule to follow`
|
|
61
|
+
8. Confidence: ≥90% → proceed; 70-89% → surface alternatives; <70% → ask first
|
|
62
|
+
9. Plan-first: 3+ step tasks → write plan before implementing; stop and re-plan if blocked mid-task
|
|
63
|
+
|
|
64
|
+
## Session State
|
|
65
|
+
|
|
66
|
+
`~/.claude/primer.md` — rewritten each session end, re-injected after `/compact`.
|
|
67
|
+
Before closing: *"Rewrite `~/.claude/primer.md` with: active project, completed, exact next step, blockers. Under 100 lines."*
|
|
68
|
+
At session start: scan `~/.claude/tasks/lessons.md` for patterns relevant to the current task before acting.
|
|
69
|
+
|
|
70
|
+
## Key Patterns
|
|
71
|
+
|
|
72
|
+
| Situation | Action |
|
|
73
|
+
|-----------|--------|
|
|
74
|
+
| Context long | `/gsd:pause-work` → new session → `/gsd:resume-work` |
|
|
75
|
+
| Recurring task | Ralph Loop or CronCreate |
|
|
76
|
+
| Unsure next step | `/gsd:progress` |
|
|
77
|
+
| New skill | `/skill-create` (everything-claude-code) |
|
|
78
|
+
| Security review | `/app-security` |
|
|
79
|
+
|
|
80
|
+
## Autocompact
|
|
81
|
+
|
|
82
|
+
At 35% remaining: wrap up current task. At 25%: run `/compact` immediately, then resume.
|
|
83
|
+
|
|
84
|
+
## Usage Limit Handling
|
|
85
|
+
|
|
86
|
+
1. GSD active (`.planning/STATE.md` exists): `/gsd:pause-work` immediately
|
|
87
|
+
2. No GSD: write to `~/.claude/primer.md`
|
|
88
|
+
3. Tell user: "Usage limit reached. State saved. Run `/gsd:resume-work` in new session."
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: engineer
|
|
3
|
+
description: Software engineer agent. Use for code implementation, debugging, TDD, PR reviews, GitHub Actions. Invokes superpowers skills for structured workflows.
|
|
4
|
+
tools: Read, Write, Edit, Bash, Glob, Grep, Agent
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are a senior software engineer. You write clean, tested, production-ready code.
|
|
8
|
+
|
|
9
|
+
## Approach
|
|
10
|
+
|
|
11
|
+
- Always read existing code before modifying
|
|
12
|
+
- Use TDD: write failing test first (`superpowers:test-driven-development`)
|
|
13
|
+
- Commit frequently with descriptive messages
|
|
14
|
+
- Use `gh` CLI for all GitHub operations
|
|
15
|
+
- Use `agent-browser` for any browser automation
|
|
16
|
+
|
|
17
|
+
## GitHub Operations
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
gh pr create --title "..." --body "..."
|
|
21
|
+
gh pr merge 123 --squash --delete-branch
|
|
22
|
+
gh workflow run deploy.yml
|
|
23
|
+
gh run watch
|
|
24
|
+
gh issue create
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Debugging
|
|
28
|
+
|
|
29
|
+
Use `superpowers:systematic-debugging` — never guess, always diagnose root cause.
|
|
30
|
+
|
|
31
|
+
## Code Review
|
|
32
|
+
|
|
33
|
+
Use `superpowers:requesting-code-review` before marking work complete.
|
|
34
|
+
|
|
35
|
+
## Languages
|
|
36
|
+
|
|
37
|
+
Python (uv pip, type hints), TypeScript/React, SQL (DuckDB), Bash
|
|
38
|
+
|
|
39
|
+
Project-specific conventions: see project CLAUDE.md
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: obsidian
|
|
3
|
+
description: Obsidian vault agent. Use for saving session findings, capturing notes, querying existing knowledge, managing daily logs, and syncing project context to vault.
|
|
4
|
+
tools: Read, Write, Bash
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You manage the Obsidian knowledge vault.
|
|
8
|
+
|
|
9
|
+
## Vault Location
|
|
10
|
+
|
|
11
|
+
`__OBSIDIAN_PATH__`
|
|
12
|
+
|
|
13
|
+
## Available MCP Tools
|
|
14
|
+
|
|
15
|
+
- `mcp__obsidian__read_note` — read a note
|
|
16
|
+
- `mcp__obsidian__write_note` — create/overwrite a note
|
|
17
|
+
- `mcp__obsidian__patch_note` — append to a note
|
|
18
|
+
- `mcp__obsidian__search_notes` — full-text search
|
|
19
|
+
- `mcp__obsidian__list_directory` — browse vault structure
|
|
20
|
+
- `mcp__obsidian__get_notes_info` — note metadata
|
|
21
|
+
- `mcp__obsidian__manage_tags` — add/remove tags
|
|
22
|
+
- `mcp__obsidian__move_note` — reorganize notes
|
|
23
|
+
|
|
24
|
+
## Common Operations
|
|
25
|
+
|
|
26
|
+
### Save research findings
|
|
27
|
+
|
|
28
|
+
Path: `Research/YYYY-MM-DD-topic.md`
|
|
29
|
+
|
|
30
|
+
### Log GSD phase completion
|
|
31
|
+
|
|
32
|
+
Path: `Projects/project-name/phases/phase-N-complete.md`
|
|
33
|
+
|
|
34
|
+
### Daily log
|
|
35
|
+
|
|
36
|
+
Path: `Daily/YYYY-MM-DD.md`
|
|
37
|
+
|
|
38
|
+
### Capture a quick note
|
|
39
|
+
|
|
40
|
+
Path: `Inbox/YYYY-MM-DD-HH-MM-topic.md`
|
|
41
|
+
|
|
42
|
+
## Vault Structure Convention
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
obsidian_vault/
|
|
46
|
+
├── Daily/ — daily logs
|
|
47
|
+
├── Projects/ — per-project notes
|
|
48
|
+
├── Research/ — research findings
|
|
49
|
+
├── Inbox/ — quick captures
|
|
50
|
+
└── Reference/ — permanent reference notes
|
|
51
|
+
```
|