claudeforge-cli 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/LICENSE +21 -0
- package/README.md +431 -0
- package/bin/cli.js +155 -0
- package/package.json +43 -0
- package/src/commands/add.js +205 -0
- package/src/commands/create.js +218 -0
- package/src/commands/github.js +479 -0
- package/src/commands/init.js +107 -0
- package/src/commands/project.js +123 -0
- package/src/commands/status.js +183 -0
- package/src/commands/upgrade.js +114 -0
- package/src/index.js +6 -0
- package/src/logger.js +90 -0
- package/src/scaffolder.js +45 -0
- package/src/stack-detector.js +62 -0
- package/templates/.env.example.tpl +21 -0
- package/templates/.gitignore.tpl +40 -0
- package/templates/CLAUDE.local.md.tpl +32 -0
- package/templates/CLAUDE.md.tpl +112 -0
- package/templates/claude/README.md.tpl +94 -0
- package/templates/claude/agents/code-reviewer.md.tpl +142 -0
- package/templates/claude/commands/commit.md.tpl +34 -0
- package/templates/claude/commands/explain-codebase.md.tpl +37 -0
- package/templates/claude/commands/fix-issue.md.tpl +43 -0
- package/templates/claude/commands/memory-sync.md.tpl +49 -0
- package/templates/claude/commands/project-health.md.tpl +70 -0
- package/templates/claude/commands/review-pr.md.tpl +43 -0
- package/templates/claude/commands/scaffold-structure.md.tpl +308 -0
- package/templates/claude/commands/setup-project.md.tpl +253 -0
- package/templates/claude/commands/standup.md.tpl +34 -0
- package/templates/claude/hooks/post-tool-use.sh.tpl +44 -0
- package/templates/claude/hooks/pre-tool-use.sh.tpl +64 -0
- package/templates/claude/rules/no-sensitive-files.md.tpl +29 -0
- package/templates/claude/settings.json.tpl +50 -0
- package/templates/claude/settings.local.json.tpl +4 -0
- package/templates/claude/skills/project-conventions/SKILL.md.tpl +39 -0
- package/templates/mcp.json.tpl +9 -0
- package/templates/memory/MEMORY.md.tpl +37 -0
- package/templates/memory/feedback_communication.md.tpl +29 -0
- package/templates/memory/project_ai_workflow.md.tpl +43 -0
- package/templates/memory/user_profile.md.tpl +30 -0
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
const { detect } = require('../stack-detector');
|
|
7
|
+
const logger = require('../logger');
|
|
8
|
+
|
|
9
|
+
async function github(options) {
|
|
10
|
+
const targetDir = path.resolve(options.dir || process.cwd());
|
|
11
|
+
const dryRun = options.dryRun || false;
|
|
12
|
+
const withDevcontainer = options.devcontainer !== false;
|
|
13
|
+
const withPrTemplate = options.prTemplate !== false;
|
|
14
|
+
const withIssueTemplates = options.issueTemplates !== false;
|
|
15
|
+
|
|
16
|
+
// Auto-detect stack unless overridden
|
|
17
|
+
let stack = options.stack;
|
|
18
|
+
if (!stack) {
|
|
19
|
+
const { labels } = await detect(targetDir);
|
|
20
|
+
stack = detectPrimaryStack(labels);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log('');
|
|
24
|
+
console.log(chalk.bold.cyan(' claudeforge') + chalk.dim(' — GitHub Setup'));
|
|
25
|
+
console.log(chalk.dim(' ─────────────────────────────────────────'));
|
|
26
|
+
if (dryRun) console.log(chalk.bold.yellow(' [DRY RUN] No files will be written'));
|
|
27
|
+
console.log('');
|
|
28
|
+
console.log(` ${chalk.dim('Detected stack:')} ${chalk.white(stack)}`);
|
|
29
|
+
console.log('');
|
|
30
|
+
|
|
31
|
+
const writes = [];
|
|
32
|
+
|
|
33
|
+
// ── GitHub Actions CI ─────────────────────────────────────────────────────
|
|
34
|
+
writes.push({
|
|
35
|
+
dest: '.github/workflows/ci.yml',
|
|
36
|
+
content: buildCI(stack),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// ── PR Template ───────────────────────────────────────────────────────────
|
|
40
|
+
if (withPrTemplate) {
|
|
41
|
+
writes.push({
|
|
42
|
+
dest: '.github/pull_request_template.md',
|
|
43
|
+
content: PR_TEMPLATE,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ── Issue Templates ───────────────────────────────────────────────────────
|
|
48
|
+
if (withIssueTemplates) {
|
|
49
|
+
writes.push({
|
|
50
|
+
dest: '.github/ISSUE_TEMPLATE/bug_report.md',
|
|
51
|
+
content: BUG_TEMPLATE,
|
|
52
|
+
});
|
|
53
|
+
writes.push({
|
|
54
|
+
dest: '.github/ISSUE_TEMPLATE/feature_request.md',
|
|
55
|
+
content: FEATURE_TEMPLATE,
|
|
56
|
+
});
|
|
57
|
+
writes.push({
|
|
58
|
+
dest: '.github/ISSUE_TEMPLATE/config.yml',
|
|
59
|
+
content: ISSUE_CONFIG,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ── CODEOWNERS ────────────────────────────────────────────────────────────
|
|
64
|
+
writes.push({
|
|
65
|
+
dest: '.github/CODEOWNERS',
|
|
66
|
+
content: CODEOWNERS,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// ── Devcontainer ──────────────────────────────────────────────────────────
|
|
70
|
+
if (withDevcontainer) {
|
|
71
|
+
writes.push({
|
|
72
|
+
dest: '.devcontainer/devcontainer.json',
|
|
73
|
+
content: buildDevcontainer(stack),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ── Write files ───────────────────────────────────────────────────────────
|
|
78
|
+
let created = 0;
|
|
79
|
+
let skipped = 0;
|
|
80
|
+
|
|
81
|
+
for (const w of writes) {
|
|
82
|
+
const destAbs = path.join(targetDir, w.dest);
|
|
83
|
+
|
|
84
|
+
if (dryRun) {
|
|
85
|
+
console.log(` ${chalk.cyan('~')} ${chalk.dim(w.dest)} ${chalk.cyan('would create')}`);
|
|
86
|
+
created++;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const exists = await fs.pathExists(destAbs);
|
|
91
|
+
if (exists) {
|
|
92
|
+
console.log(` ${chalk.gray('○')} ${chalk.dim(w.dest)} ${chalk.gray('skipped (already exists)')}`);
|
|
93
|
+
skipped++;
|
|
94
|
+
} else {
|
|
95
|
+
await fs.ensureDir(path.dirname(destAbs));
|
|
96
|
+
await fs.writeFile(destAbs, w.content, 'utf8');
|
|
97
|
+
console.log(` ${chalk.green('✓')} ${chalk.dim(w.dest)}`);
|
|
98
|
+
created++;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
console.log('');
|
|
103
|
+
console.log(chalk.dim(' ─────────────────────────────────────────'));
|
|
104
|
+
|
|
105
|
+
if (dryRun) {
|
|
106
|
+
console.log(chalk.cyan(` Dry run: ${created} file(s) would be created.`));
|
|
107
|
+
console.log('');
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
console.log(chalk.green(` ✓ ${created} file(s) created, ${skipped} skipped.`));
|
|
112
|
+
console.log('');
|
|
113
|
+
|
|
114
|
+
logger.hints('What to do next:', [
|
|
115
|
+
{ cmd: 'git add .github/ .devcontainer/', note: 'stage the new files' },
|
|
116
|
+
{ cmd: '/setup-project "your description"', note: 'run in Claude Code chat — Claude can refine the CI workflow for your stack' },
|
|
117
|
+
{ cmd: 'claudeforge status', note: 'confirm full project setup' },
|
|
118
|
+
]);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ── Stack detection ───────────────────────────────────────────────────────────
|
|
122
|
+
|
|
123
|
+
function detectPrimaryStack(labels) {
|
|
124
|
+
const l = labels.join(' ').toLowerCase();
|
|
125
|
+
if (l.includes('rust')) return 'rust';
|
|
126
|
+
if (l.includes('go')) return 'go';
|
|
127
|
+
if (l.includes('python') || l.includes('fastapi') || l.includes('django')) return 'python';
|
|
128
|
+
if (l.includes('node') || l.includes('typescript') || l.includes('react') || l.includes('next.js')) return 'node';
|
|
129
|
+
return 'generic';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ── CI workflow builders ──────────────────────────────────────────────────────
|
|
133
|
+
|
|
134
|
+
function buildCI(stack) {
|
|
135
|
+
if (stack === 'node') return NODE_CI;
|
|
136
|
+
if (stack === 'python') return PYTHON_CI;
|
|
137
|
+
if (stack === 'go') return GO_CI;
|
|
138
|
+
if (stack === 'rust') return RUST_CI;
|
|
139
|
+
return GENERIC_CI;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const NODE_CI = `name: CI
|
|
143
|
+
|
|
144
|
+
on:
|
|
145
|
+
push:
|
|
146
|
+
branches: [main, master]
|
|
147
|
+
pull_request:
|
|
148
|
+
branches: [main, master]
|
|
149
|
+
|
|
150
|
+
jobs:
|
|
151
|
+
test:
|
|
152
|
+
name: Lint & Test
|
|
153
|
+
runs-on: ubuntu-latest
|
|
154
|
+
|
|
155
|
+
strategy:
|
|
156
|
+
matrix:
|
|
157
|
+
node-version: [18, 20, 22]
|
|
158
|
+
|
|
159
|
+
steps:
|
|
160
|
+
- uses: actions/checkout@v4
|
|
161
|
+
|
|
162
|
+
- name: Set up Node.js \${{ matrix.node-version }}
|
|
163
|
+
uses: actions/setup-node@v4
|
|
164
|
+
with:
|
|
165
|
+
node-version: \${{ matrix.node-version }}
|
|
166
|
+
cache: npm
|
|
167
|
+
|
|
168
|
+
- name: Install dependencies
|
|
169
|
+
run: npm ci
|
|
170
|
+
|
|
171
|
+
- name: Lint
|
|
172
|
+
run: npm run lint --if-present
|
|
173
|
+
|
|
174
|
+
- name: Type check
|
|
175
|
+
run: npm run typecheck --if-present
|
|
176
|
+
|
|
177
|
+
- name: Test
|
|
178
|
+
run: npm test
|
|
179
|
+
|
|
180
|
+
- name: Build
|
|
181
|
+
run: npm run build --if-present
|
|
182
|
+
`;
|
|
183
|
+
|
|
184
|
+
const PYTHON_CI = `name: CI
|
|
185
|
+
|
|
186
|
+
on:
|
|
187
|
+
push:
|
|
188
|
+
branches: [main, master]
|
|
189
|
+
pull_request:
|
|
190
|
+
branches: [main, master]
|
|
191
|
+
|
|
192
|
+
jobs:
|
|
193
|
+
test:
|
|
194
|
+
name: Lint & Test
|
|
195
|
+
runs-on: ubuntu-latest
|
|
196
|
+
|
|
197
|
+
strategy:
|
|
198
|
+
matrix:
|
|
199
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
200
|
+
|
|
201
|
+
steps:
|
|
202
|
+
- uses: actions/checkout@v4
|
|
203
|
+
|
|
204
|
+
- name: Set up Python \${{ matrix.python-version }}
|
|
205
|
+
uses: actions/setup-python@v5
|
|
206
|
+
with:
|
|
207
|
+
python-version: \${{ matrix.python-version }}
|
|
208
|
+
cache: pip
|
|
209
|
+
|
|
210
|
+
- name: Install dependencies
|
|
211
|
+
run: |
|
|
212
|
+
pip install --upgrade pip
|
|
213
|
+
pip install -r requirements.txt
|
|
214
|
+
pip install -r requirements-dev.txt 2>/dev/null || true
|
|
215
|
+
|
|
216
|
+
- name: Lint (ruff)
|
|
217
|
+
run: ruff check . --if-present 2>/dev/null || true
|
|
218
|
+
|
|
219
|
+
- name: Type check (mypy)
|
|
220
|
+
run: mypy . --if-present 2>/dev/null || true
|
|
221
|
+
|
|
222
|
+
- name: Test
|
|
223
|
+
run: pytest -v
|
|
224
|
+
`;
|
|
225
|
+
|
|
226
|
+
const GO_CI = `name: CI
|
|
227
|
+
|
|
228
|
+
on:
|
|
229
|
+
push:
|
|
230
|
+
branches: [main, master]
|
|
231
|
+
pull_request:
|
|
232
|
+
branches: [main, master]
|
|
233
|
+
|
|
234
|
+
jobs:
|
|
235
|
+
test:
|
|
236
|
+
name: Lint, Vet & Test
|
|
237
|
+
runs-on: ubuntu-latest
|
|
238
|
+
|
|
239
|
+
steps:
|
|
240
|
+
- uses: actions/checkout@v4
|
|
241
|
+
|
|
242
|
+
- name: Set up Go
|
|
243
|
+
uses: actions/setup-go@v5
|
|
244
|
+
with:
|
|
245
|
+
go-version: stable
|
|
246
|
+
|
|
247
|
+
- name: Download dependencies
|
|
248
|
+
run: go mod download
|
|
249
|
+
|
|
250
|
+
- name: Vet
|
|
251
|
+
run: go vet ./...
|
|
252
|
+
|
|
253
|
+
- name: golangci-lint
|
|
254
|
+
uses: golangci/golangci-lint-action@v6
|
|
255
|
+
with:
|
|
256
|
+
version: latest
|
|
257
|
+
|
|
258
|
+
- name: Test
|
|
259
|
+
run: go test -race -coverprofile=coverage.txt ./...
|
|
260
|
+
|
|
261
|
+
- name: Build
|
|
262
|
+
run: go build ./...
|
|
263
|
+
`;
|
|
264
|
+
|
|
265
|
+
const RUST_CI = `name: CI
|
|
266
|
+
|
|
267
|
+
on:
|
|
268
|
+
push:
|
|
269
|
+
branches: [main, master]
|
|
270
|
+
pull_request:
|
|
271
|
+
branches: [main, master]
|
|
272
|
+
|
|
273
|
+
jobs:
|
|
274
|
+
test:
|
|
275
|
+
name: Lint, Clippy & Test
|
|
276
|
+
runs-on: ubuntu-latest
|
|
277
|
+
|
|
278
|
+
steps:
|
|
279
|
+
- uses: actions/checkout@v4
|
|
280
|
+
|
|
281
|
+
- name: Install Rust toolchain
|
|
282
|
+
uses: dtolnay/rust-toolchain@stable
|
|
283
|
+
with:
|
|
284
|
+
components: rustfmt, clippy
|
|
285
|
+
|
|
286
|
+
- name: Cache cargo
|
|
287
|
+
uses: Swatinem/rust-cache@v2
|
|
288
|
+
|
|
289
|
+
- name: Format check
|
|
290
|
+
run: cargo fmt --check
|
|
291
|
+
|
|
292
|
+
- name: Clippy
|
|
293
|
+
run: cargo clippy --all-targets --all-features -- -D warnings
|
|
294
|
+
|
|
295
|
+
- name: Test
|
|
296
|
+
run: cargo test --all-features
|
|
297
|
+
|
|
298
|
+
- name: Build (release)
|
|
299
|
+
run: cargo build --release
|
|
300
|
+
`;
|
|
301
|
+
|
|
302
|
+
const GENERIC_CI = `name: CI
|
|
303
|
+
|
|
304
|
+
on:
|
|
305
|
+
push:
|
|
306
|
+
branches: [main, master]
|
|
307
|
+
pull_request:
|
|
308
|
+
branches: [main, master]
|
|
309
|
+
|
|
310
|
+
jobs:
|
|
311
|
+
build:
|
|
312
|
+
name: Build & Test
|
|
313
|
+
runs-on: ubuntu-latest
|
|
314
|
+
|
|
315
|
+
steps:
|
|
316
|
+
- uses: actions/checkout@v4
|
|
317
|
+
|
|
318
|
+
- name: Run tests
|
|
319
|
+
run: |
|
|
320
|
+
echo "Add your test command here"
|
|
321
|
+
# e.g. make test, ./scripts/test.sh, etc.
|
|
322
|
+
`;
|
|
323
|
+
|
|
324
|
+
// ── Static templates ──────────────────────────────────────────────────────────
|
|
325
|
+
|
|
326
|
+
const PR_TEMPLATE = `## Summary
|
|
327
|
+
|
|
328
|
+
<!-- What does this PR do? 1-3 bullet points. -->
|
|
329
|
+
|
|
330
|
+
-
|
|
331
|
+
-
|
|
332
|
+
|
|
333
|
+
## Type of change
|
|
334
|
+
|
|
335
|
+
- [ ] Bug fix
|
|
336
|
+
- [ ] New feature
|
|
337
|
+
- [ ] Refactor / cleanup
|
|
338
|
+
- [ ] Documentation
|
|
339
|
+
- [ ] CI / infrastructure
|
|
340
|
+
|
|
341
|
+
## Test plan
|
|
342
|
+
|
|
343
|
+
<!-- How was this tested? -->
|
|
344
|
+
|
|
345
|
+
- [ ] Unit tests pass (\`npm test\` / \`pytest\` / \`go test\` / \`cargo test\`)
|
|
346
|
+
- [ ] Manually tested locally
|
|
347
|
+
- [ ] Added new tests for new behaviour
|
|
348
|
+
|
|
349
|
+
## Checklist
|
|
350
|
+
|
|
351
|
+
- [ ] Code follows the style guide in CLAUDE.md
|
|
352
|
+
- [ ] No hardcoded secrets or credentials
|
|
353
|
+
- [ ] CLAUDE.md / docs updated if needed
|
|
354
|
+
- [ ] PR title follows conventional commits (\`feat:\`, \`fix:\`, \`chore:\`, etc.)
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
> Reviewed by [code-reviewer agent](.claude/agents/code-reviewer.md) before merging.
|
|
358
|
+
`;
|
|
359
|
+
|
|
360
|
+
const BUG_TEMPLATE = `---
|
|
361
|
+
name: Bug report
|
|
362
|
+
about: Something is broken
|
|
363
|
+
title: '[Bug] '
|
|
364
|
+
labels: bug
|
|
365
|
+
assignees: ''
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Describe the bug
|
|
369
|
+
|
|
370
|
+
<!-- Clear description of what went wrong. -->
|
|
371
|
+
|
|
372
|
+
## To reproduce
|
|
373
|
+
|
|
374
|
+
1.
|
|
375
|
+
2.
|
|
376
|
+
3.
|
|
377
|
+
|
|
378
|
+
## Expected behaviour
|
|
379
|
+
|
|
380
|
+
## Actual behaviour
|
|
381
|
+
|
|
382
|
+
## Environment
|
|
383
|
+
|
|
384
|
+
- OS:
|
|
385
|
+
- Node / Python / Go / Rust version:
|
|
386
|
+
- Package version:
|
|
387
|
+
|
|
388
|
+
## Additional context
|
|
389
|
+
|
|
390
|
+
<!-- Logs, screenshots, etc. -->
|
|
391
|
+
`;
|
|
392
|
+
|
|
393
|
+
const FEATURE_TEMPLATE = `---
|
|
394
|
+
name: Feature request
|
|
395
|
+
about: Suggest an improvement or new capability
|
|
396
|
+
title: '[Feature] '
|
|
397
|
+
labels: enhancement
|
|
398
|
+
assignees: ''
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## Problem
|
|
402
|
+
|
|
403
|
+
<!-- What problem would this solve? -->
|
|
404
|
+
|
|
405
|
+
## Proposed solution
|
|
406
|
+
|
|
407
|
+
## Alternatives considered
|
|
408
|
+
|
|
409
|
+
## Additional context
|
|
410
|
+
`;
|
|
411
|
+
|
|
412
|
+
const ISSUE_CONFIG = `blank_issues_enabled: false
|
|
413
|
+
contact_links:
|
|
414
|
+
- name: Documentation
|
|
415
|
+
url: https://github.com/your-org/your-repo#readme
|
|
416
|
+
about: Read the docs first
|
|
417
|
+
`;
|
|
418
|
+
|
|
419
|
+
const CODEOWNERS = `# CODEOWNERS
|
|
420
|
+
# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
|
|
421
|
+
|
|
422
|
+
# Default owners for everything
|
|
423
|
+
* @your-org/your-team
|
|
424
|
+
|
|
425
|
+
# Infrastructure
|
|
426
|
+
.github/ @your-org/platform
|
|
427
|
+
.devcontainer/ @your-org/platform
|
|
428
|
+
`;
|
|
429
|
+
|
|
430
|
+
// ── Devcontainer builder ──────────────────────────────────────────────────────
|
|
431
|
+
|
|
432
|
+
function buildDevcontainer(stack) {
|
|
433
|
+
const image = {
|
|
434
|
+
node: 'mcr.microsoft.com/devcontainers/javascript-node:22',
|
|
435
|
+
python: 'mcr.microsoft.com/devcontainers/python:3.12',
|
|
436
|
+
go: 'mcr.microsoft.com/devcontainers/go:1.22',
|
|
437
|
+
rust: 'mcr.microsoft.com/devcontainers/rust:latest',
|
|
438
|
+
generic: 'mcr.microsoft.com/devcontainers/base:ubuntu',
|
|
439
|
+
}[stack] || 'mcr.microsoft.com/devcontainers/base:ubuntu';
|
|
440
|
+
|
|
441
|
+
const features = [];
|
|
442
|
+
if (stack !== 'node') features.push('"ghcr.io/devcontainers/features/node:1": {}');
|
|
443
|
+
if (stack === 'python') features.push('"ghcr.io/devcontainers/features/python:1": {}');
|
|
444
|
+
|
|
445
|
+
const postCreate = {
|
|
446
|
+
node: 'npm install',
|
|
447
|
+
python: 'pip install -r requirements.txt 2>/dev/null || true',
|
|
448
|
+
go: 'go mod download',
|
|
449
|
+
rust: 'rustup component add rustfmt clippy',
|
|
450
|
+
generic: 'echo "Add your setup commands here"',
|
|
451
|
+
}[stack] || 'echo done';
|
|
452
|
+
|
|
453
|
+
return JSON.stringify({
|
|
454
|
+
name: 'claudeforge Dev Container',
|
|
455
|
+
image,
|
|
456
|
+
features: features.length > 0 ? Object.fromEntries(features.map(f => {
|
|
457
|
+
const [key, val] = f.split(': ');
|
|
458
|
+
return [key.replace(/"/g, ''), JSON.parse(val)];
|
|
459
|
+
})) : undefined,
|
|
460
|
+
customizations: {
|
|
461
|
+
vscode: {
|
|
462
|
+
extensions: [
|
|
463
|
+
'anthropics.claude-code',
|
|
464
|
+
'dbaeumer.vscode-eslint',
|
|
465
|
+
'esbenp.prettier-vscode',
|
|
466
|
+
'eamodio.gitlens',
|
|
467
|
+
],
|
|
468
|
+
settings: {
|
|
469
|
+
'editor.formatOnSave': true,
|
|
470
|
+
'editor.defaultFormatter': 'esbenp.prettier-vscode',
|
|
471
|
+
},
|
|
472
|
+
},
|
|
473
|
+
},
|
|
474
|
+
postCreateCommand: postCreate,
|
|
475
|
+
remoteUser: 'vscode',
|
|
476
|
+
}, null, 2) + '\n';
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
module.exports = github;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const scaffolder = require('../scaffolder');
|
|
5
|
+
const logger = require('../logger');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Static manifest — defines every file and directory to scaffold.
|
|
9
|
+
* Ordered so parent directories appear before their children.
|
|
10
|
+
*
|
|
11
|
+
* type: 'dir' — create directory (no template src needed)
|
|
12
|
+
* type: 'file' — copy template src → dest
|
|
13
|
+
*/
|
|
14
|
+
const MANIFEST = [
|
|
15
|
+
// ── Directories ──────────────────────────────────────────────────────────
|
|
16
|
+
{ type: 'dir', dest: '.claude' },
|
|
17
|
+
{ type: 'dir', dest: '.claude/agents' },
|
|
18
|
+
{ type: 'dir', dest: '.claude/commands' },
|
|
19
|
+
{ type: 'dir', dest: '.claude/hooks' },
|
|
20
|
+
{ type: 'dir', dest: '.claude/rules' },
|
|
21
|
+
{ type: 'dir', dest: '.claude/skills' },
|
|
22
|
+
{ type: 'dir', dest: '.claude/skills/project-conventions' },
|
|
23
|
+
{ type: 'dir', dest: 'memory' },
|
|
24
|
+
|
|
25
|
+
// ── .claude/ root files ───────────────────────────────────────────────────
|
|
26
|
+
{ type: 'file', src: 'claude/README.md.tpl', dest: '.claude/README.md' },
|
|
27
|
+
{ type: 'file', src: 'claude/settings.json.tpl', dest: '.claude/settings.json' },
|
|
28
|
+
{ type: 'file', src: 'claude/settings.local.json.tpl', dest: '.claude/settings.local.json' },
|
|
29
|
+
|
|
30
|
+
// ── .claude/agents/ ───────────────────────────────────────────────────────
|
|
31
|
+
{ type: 'file', src: 'claude/agents/code-reviewer.md.tpl', dest: '.claude/agents/code-reviewer.md' },
|
|
32
|
+
|
|
33
|
+
// ── .claude/commands/ ─────────────────────────────────────────────────────
|
|
34
|
+
// Core workflow commands
|
|
35
|
+
{ type: 'file', src: 'claude/commands/commit.md.tpl', dest: '.claude/commands/commit.md' },
|
|
36
|
+
{ type: 'file', src: 'claude/commands/review-pr.md.tpl', dest: '.claude/commands/review-pr.md' },
|
|
37
|
+
// AI setup & maintenance commands
|
|
38
|
+
{ type: 'file', src: 'claude/commands/setup-project.md.tpl', dest: '.claude/commands/setup-project.md' },
|
|
39
|
+
{ type: 'file', src: 'claude/commands/memory-sync.md.tpl', dest: '.claude/commands/memory-sync.md' },
|
|
40
|
+
{ type: 'file', src: 'claude/commands/project-health.md.tpl', dest: '.claude/commands/project-health.md' },
|
|
41
|
+
// Developer productivity commands
|
|
42
|
+
{ type: 'file', src: 'claude/commands/standup.md.tpl', dest: '.claude/commands/standup.md' },
|
|
43
|
+
{ type: 'file', src: 'claude/commands/explain-codebase.md.tpl',dest: '.claude/commands/explain-codebase.md' },
|
|
44
|
+
{ type: 'file', src: 'claude/commands/fix-issue.md.tpl', dest: '.claude/commands/fix-issue.md' },
|
|
45
|
+
{ type: 'file', src: 'claude/commands/scaffold-structure.md.tpl', dest: '.claude/commands/scaffold-structure.md' },
|
|
46
|
+
|
|
47
|
+
// ── .claude/hooks/ ────────────────────────────────────────────────────────
|
|
48
|
+
{ type: 'file', src: 'claude/hooks/pre-tool-use.sh.tpl', dest: '.claude/hooks/pre-tool-use.sh' },
|
|
49
|
+
{ type: 'file', src: 'claude/hooks/post-tool-use.sh.tpl', dest: '.claude/hooks/post-tool-use.sh' },
|
|
50
|
+
|
|
51
|
+
// ── .claude/rules/ ────────────────────────────────────────────────────────
|
|
52
|
+
{ type: 'file', src: 'claude/rules/no-sensitive-files.md.tpl', dest: '.claude/rules/no-sensitive-files.md' },
|
|
53
|
+
|
|
54
|
+
// ── .claude/skills/ ───────────────────────────────────────────────────────
|
|
55
|
+
{ type: 'file', src: 'claude/skills/project-conventions/SKILL.md.tpl', dest: '.claude/skills/project-conventions/SKILL.md' },
|
|
56
|
+
|
|
57
|
+
// ── memory/ ───────────────────────────────────────────────────────────────
|
|
58
|
+
{ type: 'file', src: 'memory/MEMORY.md.tpl', dest: 'memory/MEMORY.md' },
|
|
59
|
+
{ type: 'file', src: 'memory/user_profile.md.tpl', dest: 'memory/user_profile.md' },
|
|
60
|
+
{ type: 'file', src: 'memory/feedback_communication.md.tpl', dest: 'memory/feedback_communication.md' },
|
|
61
|
+
{ type: 'file', src: 'memory/project_ai_workflow.md.tpl', dest: 'memory/project_ai_workflow.md' },
|
|
62
|
+
|
|
63
|
+
// ── Project root files ────────────────────────────────────────────────────
|
|
64
|
+
{ type: 'file', src: 'CLAUDE.md.tpl', dest: 'CLAUDE.md' },
|
|
65
|
+
{ type: 'file', src: 'CLAUDE.local.md.tpl', dest: 'CLAUDE.local.md' },
|
|
66
|
+
{ type: 'file', src: '.env.example.tpl', dest: '.env.example' },
|
|
67
|
+
{ type: 'file', src: 'mcp.json.tpl', dest: '.mcp.json' },
|
|
68
|
+
{ type: 'file', src: '.gitignore.tpl', dest: '.gitignore' },
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
async function init(options) {
|
|
72
|
+
const targetDir = path.resolve(options.dir || process.cwd());
|
|
73
|
+
const { force, dryRun } = options;
|
|
74
|
+
|
|
75
|
+
logger.banner(dryRun);
|
|
76
|
+
logger.info(`Target: ${targetDir}`);
|
|
77
|
+
console.log('');
|
|
78
|
+
|
|
79
|
+
const stats = { created: 0, skipped: 0, overwritten: 0 };
|
|
80
|
+
const templatesDir = path.join(__dirname, '../../templates');
|
|
81
|
+
|
|
82
|
+
for (const entry of MANIFEST) {
|
|
83
|
+
const destAbs = path.join(targetDir, entry.dest);
|
|
84
|
+
|
|
85
|
+
if (entry.type === 'dir') {
|
|
86
|
+
await scaffolder.ensureDir(destAbs, dryRun);
|
|
87
|
+
logger.dirResult(entry.dest, dryRun);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// type === 'file'
|
|
92
|
+
const srcAbs = path.join(templatesDir, entry.src);
|
|
93
|
+
const result = await scaffolder.writeFile(srcAbs, destAbs, { force, dryRun });
|
|
94
|
+
stats[result]++;
|
|
95
|
+
logger.fileResult(result, entry.dest, dryRun);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Make hook scripts executable on Unix systems
|
|
99
|
+
if (!dryRun) {
|
|
100
|
+
await scaffolder.chmod(path.join(targetDir, '.claude/hooks/pre-tool-use.sh'), 0o755);
|
|
101
|
+
await scaffolder.chmod(path.join(targetDir, '.claude/hooks/post-tool-use.sh'), 0o755);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
logger.summary(stats, dryRun);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports = init;
|