claudeforge-cli 1.0.2 → 1.0.4
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 +13 -0
- package/package.json +1 -1
- package/src/commands/upgrade.js +144 -56
- package/templates/claude/commands/analyze-project.md.tpl +10 -10
package/README.md
CHANGED
|
@@ -114,6 +114,19 @@ uv tool install claudeforge
|
|
|
114
114
|
claudeforge --version
|
|
115
115
|
```
|
|
116
116
|
|
|
117
|
+
### Update to latest version
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
# npm
|
|
121
|
+
npm update -g claudeforge-cli
|
|
122
|
+
|
|
123
|
+
# pip
|
|
124
|
+
pip install --upgrade claudeforge
|
|
125
|
+
|
|
126
|
+
# uv
|
|
127
|
+
uv tool upgrade claudeforge
|
|
128
|
+
```
|
|
129
|
+
|
|
117
130
|
---
|
|
118
131
|
|
|
119
132
|
## Quick Start
|
package/package.json
CHANGED
package/src/commands/upgrade.js
CHANGED
|
@@ -6,45 +6,98 @@ const scaffolder = require('../scaffolder');
|
|
|
6
6
|
const logger = require('../logger');
|
|
7
7
|
const fs = require('fs-extra');
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
9
|
+
// ── Infrastructure files — always safe to update ──────────────────────────────
|
|
10
|
+
// These are claudeforge-owned. User edits here are not expected.
|
|
11
|
+
const INFRASTRUCTURE = new Set([
|
|
12
|
+
'.claude/README.md',
|
|
13
|
+
'.claude/settings.local.json',
|
|
14
|
+
'.claude/hooks/pre-tool-use.sh',
|
|
15
|
+
'.claude/hooks/post-tool-use.sh',
|
|
16
|
+
'.claude/rules/no-sensitive-files.md',
|
|
17
|
+
'.claude/skills/project-conventions/SKILL.md',
|
|
18
|
+
'memory/MEMORY.md',
|
|
19
|
+
// Built-in slash commands — all claudeforge-managed
|
|
20
|
+
'.claude/commands/setup-project.md',
|
|
21
|
+
'.claude/commands/analyze-project.md',
|
|
22
|
+
'.claude/commands/memory-sync.md',
|
|
23
|
+
'.claude/commands/project-health.md',
|
|
24
|
+
'.claude/commands/standup.md',
|
|
25
|
+
'.claude/commands/explain-codebase.md',
|
|
26
|
+
'.claude/commands/fix-issue.md',
|
|
27
|
+
'.claude/commands/scaffold-structure.md',
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
// ── User-owned files — skip unless --all ─────────────────────────────────────
|
|
31
|
+
// These are edited by the user after init. Only touch with explicit --all.
|
|
32
|
+
const USER_OWNED = new Set([
|
|
33
|
+
'CLAUDE.md',
|
|
34
|
+
'CLAUDE.local.md',
|
|
35
|
+
'.env.example',
|
|
36
|
+
'.mcp.json',
|
|
37
|
+
'.gitignore',
|
|
38
|
+
'.claude/settings.json',
|
|
39
|
+
'.claude/agents/code-reviewer.md',
|
|
40
|
+
'.claude/commands/commit.md',
|
|
41
|
+
'.claude/commands/review-pr.md',
|
|
42
|
+
'memory/feedback_communication.md',
|
|
43
|
+
'memory/project_ai_workflow.md',
|
|
44
|
+
'memory/user_profile.md',
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
// ── Full manifest (must stay in sync with init.js) ────────────────────────────
|
|
48
|
+
const MANIFEST = [
|
|
49
|
+
// Directories
|
|
50
|
+
{ type: 'dir', dest: '.claude' },
|
|
51
|
+
{ type: 'dir', dest: '.claude/agents' },
|
|
52
|
+
{ type: 'dir', dest: '.claude/commands' },
|
|
53
|
+
{ type: 'dir', dest: '.claude/hooks' },
|
|
54
|
+
{ type: 'dir', dest: '.claude/rules' },
|
|
55
|
+
{ type: 'dir', dest: '.claude/skills' },
|
|
56
|
+
{ type: 'dir', dest: '.claude/skills/project-conventions' },
|
|
57
|
+
{ type: 'dir', dest: 'memory' },
|
|
58
|
+
|
|
59
|
+
// .claude/ root
|
|
60
|
+
{ type: 'file', src: 'claude/README.md.tpl', dest: '.claude/README.md' },
|
|
61
|
+
{ type: 'file', src: 'claude/settings.json.tpl', dest: '.claude/settings.json' },
|
|
62
|
+
{ type: 'file', src: 'claude/settings.local.json.tpl', dest: '.claude/settings.local.json' },
|
|
63
|
+
|
|
64
|
+
// Agents
|
|
65
|
+
{ type: 'file', src: 'claude/agents/code-reviewer.md.tpl', dest: '.claude/agents/code-reviewer.md' },
|
|
66
|
+
|
|
67
|
+
// Built-in commands
|
|
68
|
+
{ type: 'file', src: 'claude/commands/commit.md.tpl', dest: '.claude/commands/commit.md' },
|
|
69
|
+
{ type: 'file', src: 'claude/commands/review-pr.md.tpl', dest: '.claude/commands/review-pr.md' },
|
|
70
|
+
{ type: 'file', src: 'claude/commands/setup-project.md.tpl', dest: '.claude/commands/setup-project.md' },
|
|
71
|
+
{ type: 'file', src: 'claude/commands/analyze-project.md.tpl', dest: '.claude/commands/analyze-project.md' },
|
|
72
|
+
{ type: 'file', src: 'claude/commands/memory-sync.md.tpl', dest: '.claude/commands/memory-sync.md' },
|
|
73
|
+
{ type: 'file', src: 'claude/commands/project-health.md.tpl', dest: '.claude/commands/project-health.md' },
|
|
74
|
+
{ type: 'file', src: 'claude/commands/standup.md.tpl', dest: '.claude/commands/standup.md' },
|
|
75
|
+
{ type: 'file', src: 'claude/commands/explain-codebase.md.tpl', dest: '.claude/commands/explain-codebase.md' },
|
|
76
|
+
{ type: 'file', src: 'claude/commands/fix-issue.md.tpl', dest: '.claude/commands/fix-issue.md' },
|
|
77
|
+
{ type: 'file', src: 'claude/commands/scaffold-structure.md.tpl',dest: '.claude/commands/scaffold-structure.md' },
|
|
78
|
+
|
|
79
|
+
// Hooks
|
|
80
|
+
{ type: 'file', src: 'claude/hooks/pre-tool-use.sh.tpl', dest: '.claude/hooks/pre-tool-use.sh' },
|
|
81
|
+
{ type: 'file', src: 'claude/hooks/post-tool-use.sh.tpl', dest: '.claude/hooks/post-tool-use.sh' },
|
|
31
82
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
{ src: '
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
{ src: '
|
|
40
|
-
{ src: '.
|
|
41
|
-
{ src: '
|
|
42
|
-
{ src: '
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
{ src: '
|
|
46
|
-
{ src: '
|
|
47
|
-
{ src: '
|
|
83
|
+
// Rules
|
|
84
|
+
{ type: 'file', src: 'claude/rules/no-sensitive-files.md.tpl', dest: '.claude/rules/no-sensitive-files.md' },
|
|
85
|
+
|
|
86
|
+
// Skills
|
|
87
|
+
{ type: 'file', src: 'claude/skills/project-conventions/SKILL.md.tpl', dest: '.claude/skills/project-conventions/SKILL.md' },
|
|
88
|
+
|
|
89
|
+
// Memory
|
|
90
|
+
{ type: 'file', src: 'memory/MEMORY.md.tpl', dest: 'memory/MEMORY.md' },
|
|
91
|
+
{ type: 'file', src: 'memory/user_profile.md.tpl', dest: 'memory/user_profile.md' },
|
|
92
|
+
{ type: 'file', src: 'memory/feedback_communication.md.tpl', dest: 'memory/feedback_communication.md' },
|
|
93
|
+
{ type: 'file', src: 'memory/project_ai_workflow.md.tpl', dest: 'memory/project_ai_workflow.md' },
|
|
94
|
+
|
|
95
|
+
// Project root
|
|
96
|
+
{ type: 'file', src: 'CLAUDE.md.tpl', dest: 'CLAUDE.md' },
|
|
97
|
+
{ type: 'file', src: 'CLAUDE.local.md.tpl', dest: 'CLAUDE.local.md' },
|
|
98
|
+
{ type: 'file', src: '.env.example.tpl', dest: '.env.example' },
|
|
99
|
+
{ type: 'file', src: 'mcp.json.tpl', dest: '.mcp.json' },
|
|
100
|
+
{ type: 'file', src: '.gitignore.tpl', dest: '.gitignore' },
|
|
48
101
|
];
|
|
49
102
|
|
|
50
103
|
async function upgrade(options) {
|
|
@@ -65,22 +118,57 @@ async function upgrade(options) {
|
|
|
65
118
|
console.log('');
|
|
66
119
|
|
|
67
120
|
const templatesDir = path.join(__dirname, '../../templates');
|
|
68
|
-
const
|
|
121
|
+
const stats = { updated: 0, created: 0, skipped: 0 };
|
|
69
122
|
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
for (const entry of manifest) {
|
|
73
|
-
const srcAbs = path.join(templatesDir, entry.src);
|
|
123
|
+
for (const entry of MANIFEST) {
|
|
74
124
|
const destAbs = path.join(targetDir, entry.dest);
|
|
75
125
|
|
|
76
|
-
|
|
126
|
+
// Always ensure directories exist
|
|
127
|
+
if (entry.type === 'dir') {
|
|
128
|
+
await scaffolder.ensureDir(destAbs, dryRun);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const isInfrastructure = INFRASTRUCTURE.has(entry.dest);
|
|
133
|
+
const isUserOwned = USER_OWNED.has(entry.dest);
|
|
134
|
+
const alreadyExists = await fs.pathExists(destAbs);
|
|
135
|
+
|
|
136
|
+
// Decision logic:
|
|
137
|
+
// 1. Doesn't exist yet → always create (new file added in this version)
|
|
138
|
+
// 2. Infrastructure → always update
|
|
139
|
+
// 3. User-owned exists → skip unless --all
|
|
140
|
+
let action;
|
|
141
|
+
if (!alreadyExists) {
|
|
142
|
+
action = 'create';
|
|
143
|
+
} else if (isInfrastructure) {
|
|
144
|
+
action = 'update';
|
|
145
|
+
} else if (includeAll) {
|
|
146
|
+
action = 'update';
|
|
147
|
+
} else {
|
|
148
|
+
action = 'skip';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (action === 'skip') {
|
|
152
|
+
console.log(` ${chalk.dim('–')} ${chalk.dim(entry.dest)} ${chalk.dim('skipped (user-owned)')}`);
|
|
153
|
+
stats.skipped++;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const srcAbs = path.join(templatesDir, entry.src);
|
|
77
158
|
|
|
78
159
|
if (dryRun) {
|
|
79
|
-
|
|
160
|
+
const label = action === 'create' ? chalk.green('would create') : chalk.cyan('would update');
|
|
161
|
+
console.log(` ${chalk.cyan('~')} ${chalk.dim(entry.dest)} ${label}`);
|
|
80
162
|
} else {
|
|
81
|
-
|
|
163
|
+
await scaffolder.writeFile(srcAbs, destAbs, { force: true, dryRun: false });
|
|
164
|
+
if (action === 'create') {
|
|
165
|
+
console.log(` ${chalk.green('+')} ${chalk.dim(entry.dest)} ${chalk.green('created (new in this version)')}`);
|
|
166
|
+
stats.created++;
|
|
167
|
+
} else {
|
|
168
|
+
console.log(` ${chalk.yellow('↺')} ${chalk.dim(entry.dest)}`);
|
|
169
|
+
stats.updated++;
|
|
170
|
+
}
|
|
82
171
|
}
|
|
83
|
-
stats.updated++;
|
|
84
172
|
}
|
|
85
173
|
|
|
86
174
|
// Re-chmod hooks
|
|
@@ -93,22 +181,22 @@ async function upgrade(options) {
|
|
|
93
181
|
console.log(chalk.dim(' ─────────────────────────────────────────'));
|
|
94
182
|
|
|
95
183
|
if (dryRun) {
|
|
96
|
-
console.log(chalk.cyan(` Dry run
|
|
97
|
-
console.log('');
|
|
184
|
+
console.log(chalk.cyan(` Dry run complete. Run without --dry-run to apply changes.`));
|
|
98
185
|
} else {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
186
|
+
if (stats.created > 0) {
|
|
187
|
+
console.log(chalk.green(` ✓ ${stats.created} new file(s) created (added in this version)`));
|
|
188
|
+
}
|
|
189
|
+
console.log(chalk.green(` ✓ ${stats.updated} infrastructure file(s) updated to latest templates`));
|
|
190
|
+
if (stats.skipped > 0 && !includeAll) {
|
|
191
|
+
console.log(chalk.dim(` – ${stats.skipped} user-owned file(s) skipped (use --all to update them too)`));
|
|
104
192
|
}
|
|
105
193
|
console.log('');
|
|
106
194
|
console.log(chalk.bold(' What to do next:'));
|
|
107
195
|
console.log('');
|
|
108
196
|
console.log(` ${chalk.dim('›')} ${chalk.cyan('/project-health')} ${chalk.dim('audit your setup to see if anything else needs attention')}`);
|
|
109
|
-
console.log(` ${chalk.dim('›')} ${chalk.cyan('claudeforge status')}
|
|
110
|
-
console.log('');
|
|
197
|
+
console.log(` ${chalk.dim('›')} ${chalk.cyan('claudeforge status')} ${chalk.dim('verify the updated files look correct')}`);
|
|
111
198
|
}
|
|
199
|
+
console.log('');
|
|
112
200
|
}
|
|
113
201
|
|
|
114
202
|
module.exports = upgrade;
|
|
@@ -15,17 +15,17 @@ Do not ask the user for a description. Read the codebase directly to infer every
|
|
|
15
15
|
|
|
16
16
|
### 1b. Full dependency map
|
|
17
17
|
|
|
18
|
-
!`cat package.json 2>/dev/null`
|
|
19
|
-
!`cat requirements.txt 2>/dev/null || cat pyproject.toml 2>/dev/null`
|
|
20
|
-
!`cat go.mod 2>/dev/null`
|
|
21
|
-
!`cat Cargo.toml 2>/dev/null`
|
|
22
|
-
!`cat pom.xml 2>/dev/null | head -80`
|
|
23
|
-
!`cat Gemfile 2>/dev/null`
|
|
18
|
+
!`cat package.json 2>/dev/null || echo "(no package.json)"`
|
|
19
|
+
!`cat requirements.txt 2>/dev/null || cat pyproject.toml 2>/dev/null || echo "(no python deps)"`
|
|
20
|
+
!`cat go.mod 2>/dev/null || echo "(no go.mod)"`
|
|
21
|
+
!`cat Cargo.toml 2>/dev/null || echo "(no Cargo.toml)"`
|
|
22
|
+
!`cat pom.xml 2>/dev/null | head -80 || echo "(no pom.xml)"`
|
|
23
|
+
!`cat Gemfile 2>/dev/null || echo "(no Gemfile)"`
|
|
24
24
|
|
|
25
25
|
### 1c. Project structure
|
|
26
26
|
|
|
27
|
-
!`find . -type f \( -name "*.js" -o -name "*.ts" -o -name "*.py" -o -name "*.go" -o -name "*.rs" -o -name "*.java" -o -name "*.rb" \) -not -path "*/node_modules/*" -not -path "*/.git/*" -not -path "*/dist/*" -not -path "*/__pycache__/*" -not -path "*/target/*" | head -80`
|
|
28
|
-
!`find . -type d -not -path "*/node_modules/*" -not -path "*/.git/*" -not -path "*/__pycache__/*" -not -path "*/target/*" -not -path "*/dist/*" | head -40`
|
|
27
|
+
!`find . -type f \( -name "*.js" -o -name "*.ts" -o -name "*.py" -o -name "*.go" -o -name "*.rs" -o -name "*.java" -o -name "*.rb" \) -not -path "*/node_modules/*" -not -path "*/.git/*" -not -path "*/dist/*" -not -path "*/__pycache__/*" -not -path "*/target/*" 2>/dev/null | head -80 || echo "(no source files found)"`
|
|
28
|
+
!`find . -type d -not -path "*/node_modules/*" -not -path "*/.git/*" -not -path "*/__pycache__/*" -not -path "*/target/*" -not -path "*/dist/*" 2>/dev/null | head -40 || echo "(no directories found)"`
|
|
29
29
|
|
|
30
30
|
### 1d. Read actual source code — infer real patterns
|
|
31
31
|
|
|
@@ -49,13 +49,13 @@ Read enough code to answer:
|
|
|
49
49
|
### 1e. Git history — understand how the project evolved
|
|
50
50
|
|
|
51
51
|
!`git log --oneline -20 2>/dev/null || echo "(no git history)"`
|
|
52
|
-
!`git log --pretty=format:"%s" -50 2>/dev/null | sort | uniq -c | sort -rn | head -20`
|
|
52
|
+
!`git log --pretty=format:"%s" -50 2>/dev/null | sort | uniq -c | sort -rn | head -20 || echo "(no commits)"`
|
|
53
53
|
|
|
54
54
|
What do the commit messages reveal about the team's workflow and focus areas?
|
|
55
55
|
|
|
56
56
|
### 1f. Existing CI/CD and infrastructure
|
|
57
57
|
|
|
58
|
-
!`
|
|
58
|
+
!`find .github/workflows -name "*.yml" 2>/dev/null | head -5 | xargs cat 2>/dev/null | head -100 || echo "(no GitHub Actions)"`
|
|
59
59
|
!`cat Dockerfile 2>/dev/null | head -40 || echo "(no Dockerfile)"`
|
|
60
60
|
!`cat docker-compose.yml 2>/dev/null || echo "(no docker-compose)"`
|
|
61
61
|
!`cat .env.example 2>/dev/null || cat .env.sample 2>/dev/null || echo "(no .env.example)"`
|