code-as-plan 2.0.0 → 2.0.2
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/bin/install.js +54 -47
- package/cap/workflows/update.md +24 -24
- package/commands/cap/update.md +48 -0
- package/hooks/dist/gsd-check-update.js +6 -6
- package/package.json +1 -1
package/bin/install.js
CHANGED
|
@@ -2686,10 +2686,10 @@ function convertClaudeToGeminiToml(content) {
|
|
|
2686
2686
|
|
|
2687
2687
|
/**
|
|
2688
2688
|
* Copy commands to a flat structure for OpenCode
|
|
2689
|
-
* OpenCode expects: command/
|
|
2690
|
-
* Source structure: commands/
|
|
2691
|
-
*
|
|
2692
|
-
* @param {string} srcDir - Source directory (e.g., commands/
|
|
2689
|
+
* OpenCode expects: command/cap-help.md (invoked as /cap-help)
|
|
2690
|
+
* Source structure: commands/cap/help.md
|
|
2691
|
+
*
|
|
2692
|
+
* @param {string} srcDir - Source directory (e.g., commands/cap/)
|
|
2693
2693
|
* @param {string} destDir - Destination directory (e.g., command/)
|
|
2694
2694
|
* @param {string} prefix - Prefix for filenames (e.g., 'gsd')
|
|
2695
2695
|
* @param {string} pathPrefix - Path prefix for file references
|
|
@@ -2718,7 +2718,7 @@ function copyFlattenedCommands(srcDir, destDir, prefix, pathPrefix, runtime) {
|
|
|
2718
2718
|
|
|
2719
2719
|
if (entry.isDirectory()) {
|
|
2720
2720
|
// Recurse into subdirectories, adding to prefix
|
|
2721
|
-
// e.g., commands/
|
|
2721
|
+
// e.g., commands/cap/debug/start.md -> command/cap-debug-start.md
|
|
2722
2722
|
copyFlattenedCommands(srcPath, destDir, `${prefix}-${entry.name}`, pathPrefix, runtime);
|
|
2723
2723
|
} else if (entry.name.endsWith('.md')) {
|
|
2724
2724
|
// Flatten: help.md -> gsd-help.md
|
|
@@ -3336,7 +3336,7 @@ function uninstall(isGlobal, runtime = 'claude') {
|
|
|
3336
3336
|
if (fs.existsSync(commandDir)) {
|
|
3337
3337
|
const files = fs.readdirSync(commandDir);
|
|
3338
3338
|
for (const file of files) {
|
|
3339
|
-
if (file.startsWith('
|
|
3339
|
+
if (file.startsWith('cap-') && file.endsWith('.md')) {
|
|
3340
3340
|
fs.unlinkSync(path.join(commandDir, file));
|
|
3341
3341
|
removedCount++;
|
|
3342
3342
|
}
|
|
@@ -3481,11 +3481,18 @@ function uninstall(isGlobal, runtime = 'claude') {
|
|
|
3481
3481
|
}
|
|
3482
3482
|
}
|
|
3483
3483
|
} else {
|
|
3484
|
-
const capCommandsDir = path.join(targetDir, 'commands', '
|
|
3484
|
+
const capCommandsDir = path.join(targetDir, 'commands', 'cap');
|
|
3485
3485
|
if (fs.existsSync(capCommandsDir)) {
|
|
3486
3486
|
fs.rmSync(capCommandsDir, { recursive: true });
|
|
3487
3487
|
removedCount++;
|
|
3488
|
-
console.log(` ${green}✓${reset} Removed commands/
|
|
3488
|
+
console.log(` ${green}✓${reset} Removed commands/cap/`);
|
|
3489
|
+
}
|
|
3490
|
+
// Also remove legacy commands/gsd/ if it exists from previous installs
|
|
3491
|
+
const legacyCommandsDir = path.join(targetDir, 'commands', 'gsd');
|
|
3492
|
+
if (fs.existsSync(legacyCommandsDir)) {
|
|
3493
|
+
fs.rmSync(legacyCommandsDir, { recursive: true });
|
|
3494
|
+
removedCount++;
|
|
3495
|
+
console.log(` ${green}✓${reset} Removed legacy commands/gsd/`);
|
|
3489
3496
|
}
|
|
3490
3497
|
}
|
|
3491
3498
|
|
|
@@ -3503,7 +3510,7 @@ function uninstall(isGlobal, runtime = 'claude') {
|
|
|
3503
3510
|
const files = fs.readdirSync(agentsDir);
|
|
3504
3511
|
let agentCount = 0;
|
|
3505
3512
|
for (const file of files) {
|
|
3506
|
-
if (file.startsWith('
|
|
3513
|
+
if (file.startsWith('cap-') && file.endsWith('.md')) {
|
|
3507
3514
|
fs.unlinkSync(path.join(agentsDir, file));
|
|
3508
3515
|
agentCount++;
|
|
3509
3516
|
}
|
|
@@ -3914,7 +3921,7 @@ function writeManifest(configDir, runtime = 'claude') {
|
|
|
3914
3921
|
const isCursor = runtime === 'cursor';
|
|
3915
3922
|
const isWindsurf = runtime === 'windsurf';
|
|
3916
3923
|
const capDir = path.join(configDir, 'cap');
|
|
3917
|
-
const commandsDir = path.join(configDir, 'commands', '
|
|
3924
|
+
const commandsDir = path.join(configDir, 'commands', 'cap');
|
|
3918
3925
|
const opencodeCommandDir = path.join(configDir, 'command');
|
|
3919
3926
|
const codexSkillsDir = path.join(configDir, 'skills');
|
|
3920
3927
|
const agentsDir = path.join(configDir, 'agents');
|
|
@@ -3927,12 +3934,12 @@ function writeManifest(configDir, runtime = 'claude') {
|
|
|
3927
3934
|
if (!isOpencode && !isCodex && !isCopilot && !isAntigravity && !isCursor && !isWindsurf && fs.existsSync(commandsDir)) {
|
|
3928
3935
|
const cmdHashes = generateManifest(commandsDir);
|
|
3929
3936
|
for (const [rel, hash] of Object.entries(cmdHashes)) {
|
|
3930
|
-
manifest.files['commands/
|
|
3937
|
+
manifest.files['commands/cap/' + rel] = hash;
|
|
3931
3938
|
}
|
|
3932
3939
|
}
|
|
3933
3940
|
if (isOpencode && fs.existsSync(opencodeCommandDir)) {
|
|
3934
3941
|
for (const file of fs.readdirSync(opencodeCommandDir)) {
|
|
3935
|
-
if (file.startsWith('
|
|
3942
|
+
if (file.startsWith('cap-') && file.endsWith('.md')) {
|
|
3936
3943
|
manifest.files['command/' + file] = fileHash(path.join(opencodeCommandDir, file));
|
|
3937
3944
|
}
|
|
3938
3945
|
}
|
|
@@ -3948,7 +3955,7 @@ function writeManifest(configDir, runtime = 'claude') {
|
|
|
3948
3955
|
}
|
|
3949
3956
|
if (fs.existsSync(agentsDir)) {
|
|
3950
3957
|
for (const file of fs.readdirSync(agentsDir)) {
|
|
3951
|
-
if (file.startsWith('
|
|
3958
|
+
if (file.startsWith('cap-') && file.endsWith('.md')) {
|
|
3952
3959
|
manifest.files['agents/' + file] = fileHash(path.join(agentsDir, file));
|
|
3953
3960
|
}
|
|
3954
3961
|
}
|
|
@@ -4095,93 +4102,93 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
4095
4102
|
// Clean up orphaned files from previous versions
|
|
4096
4103
|
cleanupOrphanedFiles(targetDir);
|
|
4097
4104
|
|
|
4098
|
-
// OpenCode uses command/ (flat), Codex uses skills/, Claude/Gemini use commands/
|
|
4105
|
+
// OpenCode uses command/ (flat), Codex uses skills/, Claude/Gemini use commands/cap/
|
|
4099
4106
|
if (isOpencode) {
|
|
4100
4107
|
// OpenCode: flat structure in command/ directory
|
|
4101
4108
|
const commandDir = path.join(targetDir, 'command');
|
|
4102
4109
|
fs.mkdirSync(commandDir, { recursive: true });
|
|
4103
|
-
|
|
4104
|
-
// Copy commands/
|
|
4105
|
-
const capSrc = path.join(src, 'commands', '
|
|
4106
|
-
copyFlattenedCommands(capSrc, commandDir, '
|
|
4107
|
-
if (verifyInstalled(commandDir, 'command/
|
|
4108
|
-
const count = fs.readdirSync(commandDir).filter(f => f.startsWith('
|
|
4110
|
+
|
|
4111
|
+
// Copy commands/cap/*.md as command/cap-*.md (flatten structure)
|
|
4112
|
+
const capSrc = path.join(src, 'commands', 'cap');
|
|
4113
|
+
copyFlattenedCommands(capSrc, commandDir, 'cap', pathPrefix, runtime);
|
|
4114
|
+
if (verifyInstalled(commandDir, 'command/cap-*')) {
|
|
4115
|
+
const count = fs.readdirSync(commandDir).filter(f => f.startsWith('cap-')).length;
|
|
4109
4116
|
console.log(` ${green}✓${reset} Installed ${count} commands to command/`);
|
|
4110
4117
|
} else {
|
|
4111
|
-
failures.push('command/
|
|
4118
|
+
failures.push('command/cap-*');
|
|
4112
4119
|
}
|
|
4113
4120
|
} else if (isCodex) {
|
|
4114
4121
|
const skillsDir = path.join(targetDir, 'skills');
|
|
4115
|
-
const capSrc = path.join(src, 'commands', '
|
|
4116
|
-
copyCommandsAsCodexSkills(capSrc, skillsDir, '
|
|
4122
|
+
const capSrc = path.join(src, 'commands', 'cap');
|
|
4123
|
+
copyCommandsAsCodexSkills(capSrc, skillsDir, 'cap', pathPrefix, runtime);
|
|
4117
4124
|
const installedSkillNames = listCodexSkillNames(skillsDir);
|
|
4118
4125
|
if (installedSkillNames.length > 0) {
|
|
4119
4126
|
console.log(` ${green}✓${reset} Installed ${installedSkillNames.length} skills to skills/`);
|
|
4120
4127
|
} else {
|
|
4121
|
-
failures.push('skills/
|
|
4128
|
+
failures.push('skills/cap-*');
|
|
4122
4129
|
}
|
|
4123
4130
|
} else if (isCopilot) {
|
|
4124
4131
|
const skillsDir = path.join(targetDir, 'skills');
|
|
4125
|
-
const capSrc = path.join(src, 'commands', '
|
|
4126
|
-
copyCommandsAsCopilotSkills(capSrc, skillsDir, '
|
|
4132
|
+
const capSrc = path.join(src, 'commands', 'cap');
|
|
4133
|
+
copyCommandsAsCopilotSkills(capSrc, skillsDir, 'cap', isGlobal);
|
|
4127
4134
|
if (fs.existsSync(skillsDir)) {
|
|
4128
4135
|
const count = fs.readdirSync(skillsDir, { withFileTypes: true })
|
|
4129
|
-
.filter(e => e.isDirectory() && e.name.startsWith('
|
|
4136
|
+
.filter(e => e.isDirectory() && e.name.startsWith('cap-')).length;
|
|
4130
4137
|
if (count > 0) {
|
|
4131
4138
|
console.log(` ${green}✓${reset} Installed ${count} skills to skills/`);
|
|
4132
4139
|
} else {
|
|
4133
|
-
failures.push('skills/
|
|
4140
|
+
failures.push('skills/cap-*');
|
|
4134
4141
|
}
|
|
4135
4142
|
} else {
|
|
4136
|
-
failures.push('skills/
|
|
4143
|
+
failures.push('skills/cap-*');
|
|
4137
4144
|
}
|
|
4138
4145
|
} else if (isAntigravity) {
|
|
4139
4146
|
const skillsDir = path.join(targetDir, 'skills');
|
|
4140
|
-
const capSrc = path.join(src, 'commands', '
|
|
4141
|
-
copyCommandsAsAntigravitySkills(capSrc, skillsDir, '
|
|
4147
|
+
const capSrc = path.join(src, 'commands', 'cap');
|
|
4148
|
+
copyCommandsAsAntigravitySkills(capSrc, skillsDir, 'cap', isGlobal);
|
|
4142
4149
|
if (fs.existsSync(skillsDir)) {
|
|
4143
4150
|
const count = fs.readdirSync(skillsDir, { withFileTypes: true })
|
|
4144
|
-
.filter(e => e.isDirectory() && e.name.startsWith('
|
|
4151
|
+
.filter(e => e.isDirectory() && e.name.startsWith('cap-')).length;
|
|
4145
4152
|
if (count > 0) {
|
|
4146
4153
|
console.log(` ${green}✓${reset} Installed ${count} skills to skills/`);
|
|
4147
4154
|
} else {
|
|
4148
|
-
failures.push('skills/
|
|
4155
|
+
failures.push('skills/cap-*');
|
|
4149
4156
|
}
|
|
4150
4157
|
} else {
|
|
4151
|
-
failures.push('skills/
|
|
4158
|
+
failures.push('skills/cap-*');
|
|
4152
4159
|
}
|
|
4153
4160
|
} else if (isCursor) {
|
|
4154
4161
|
const skillsDir = path.join(targetDir, 'skills');
|
|
4155
|
-
const capSrc = path.join(src, 'commands', '
|
|
4156
|
-
copyCommandsAsCursorSkills(capSrc, skillsDir, '
|
|
4162
|
+
const capSrc = path.join(src, 'commands', 'cap');
|
|
4163
|
+
copyCommandsAsCursorSkills(capSrc, skillsDir, 'cap', pathPrefix, runtime);
|
|
4157
4164
|
const installedSkillNames = listCodexSkillNames(skillsDir); // reuse — same dir structure
|
|
4158
4165
|
if (installedSkillNames.length > 0) {
|
|
4159
4166
|
console.log(` ${green}✓${reset} Installed ${installedSkillNames.length} skills to skills/`);
|
|
4160
4167
|
} else {
|
|
4161
|
-
failures.push('skills/
|
|
4168
|
+
failures.push('skills/cap-*');
|
|
4162
4169
|
}
|
|
4163
4170
|
} else if (isWindsurf) {
|
|
4164
4171
|
const skillsDir = path.join(targetDir, 'skills');
|
|
4165
|
-
const capSrc = path.join(src, 'commands', '
|
|
4166
|
-
copyCommandsAsWindsurfSkills(capSrc, skillsDir, '
|
|
4172
|
+
const capSrc = path.join(src, 'commands', 'cap');
|
|
4173
|
+
copyCommandsAsWindsurfSkills(capSrc, skillsDir, 'cap', pathPrefix, runtime);
|
|
4167
4174
|
const installedSkillNames = listCodexSkillNames(skillsDir); // reuse — same dir structure
|
|
4168
4175
|
if (installedSkillNames.length > 0) {
|
|
4169
4176
|
console.log(` ${green}✓${reset} Installed ${installedSkillNames.length} skills to skills/`);
|
|
4170
4177
|
} else {
|
|
4171
|
-
failures.push('skills/
|
|
4178
|
+
failures.push('skills/cap-*');
|
|
4172
4179
|
}
|
|
4173
4180
|
} else {
|
|
4174
4181
|
// Claude Code & Gemini: nested structure in commands/ directory
|
|
4175
4182
|
const commandsDir = path.join(targetDir, 'commands');
|
|
4176
4183
|
fs.mkdirSync(commandsDir, { recursive: true });
|
|
4177
|
-
|
|
4178
|
-
const capSrc = path.join(src, 'commands', '
|
|
4179
|
-
const capDest = path.join(commandsDir, '
|
|
4184
|
+
|
|
4185
|
+
const capSrc = path.join(src, 'commands', 'cap');
|
|
4186
|
+
const capDest = path.join(commandsDir, 'cap');
|
|
4180
4187
|
copyWithPathReplacement(capSrc, capDest, pathPrefix, runtime, true, isGlobal);
|
|
4181
|
-
if (verifyInstalled(capDest, 'commands/
|
|
4182
|
-
console.log(` ${green}✓${reset} Installed commands/
|
|
4188
|
+
if (verifyInstalled(capDest, 'commands/cap')) {
|
|
4189
|
+
console.log(` ${green}✓${reset} Installed commands/cap`);
|
|
4183
4190
|
} else {
|
|
4184
|
-
failures.push('commands/
|
|
4191
|
+
failures.push('commands/cap');
|
|
4185
4192
|
}
|
|
4186
4193
|
}
|
|
4187
4194
|
|
|
@@ -4204,7 +4211,7 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
4204
4211
|
// Remove old CAP agents (gsd-*.md) before copying new ones
|
|
4205
4212
|
if (fs.existsSync(agentsDest)) {
|
|
4206
4213
|
for (const file of fs.readdirSync(agentsDest)) {
|
|
4207
|
-
if (file.startsWith('
|
|
4214
|
+
if (file.startsWith('cap-') && file.endsWith('.md')) {
|
|
4208
4215
|
fs.unlinkSync(path.join(agentsDest, file));
|
|
4209
4216
|
}
|
|
4210
4217
|
}
|
package/cap/workflows/update.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<purpose>
|
|
2
|
-
Check for
|
|
2
|
+
Check for CAP updates via npm, display changelog for versions between installed and latest, obtain user confirmation, and execute clean installation with cache clearing.
|
|
3
3
|
</purpose>
|
|
4
4
|
|
|
5
5
|
<required_reading>
|
|
@@ -9,7 +9,7 @@ Read all files referenced by the invoking prompt's execution_context before star
|
|
|
9
9
|
<process>
|
|
10
10
|
|
|
11
11
|
<step name="get_installed_version">
|
|
12
|
-
Detect whether
|
|
12
|
+
Detect whether CAP is installed locally or globally by checking both locations and validating install integrity.
|
|
13
13
|
|
|
14
14
|
First, derive `PREFERRED_RUNTIME` from the invoking prompt's `execution_context` path:
|
|
15
15
|
- Path contains `/.codex/` -> `codex`
|
|
@@ -17,7 +17,7 @@ First, derive `PREFERRED_RUNTIME` from the invoking prompt's `execution_context`
|
|
|
17
17
|
- Path contains `/.config/opencode/` or `/.opencode/` -> `opencode`
|
|
18
18
|
- Otherwise -> `claude`
|
|
19
19
|
|
|
20
|
-
Use `PREFERRED_RUNTIME` as the first runtime checked so `/
|
|
20
|
+
Use `PREFERRED_RUNTIME` as the first runtime checked so `/cap:update` targets the runtime that invoked it.
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
23
|
# Runtime candidates: "<runtime>:<config-dir>" stored as an array.
|
|
@@ -130,7 +130,7 @@ If multiple runtime installs are detected and the invoking runtime cannot be det
|
|
|
130
130
|
|
|
131
131
|
**If VERSION file missing:**
|
|
132
132
|
```
|
|
133
|
-
##
|
|
133
|
+
## CAP Update
|
|
134
134
|
|
|
135
135
|
**Installed version:** Unknown
|
|
136
136
|
|
|
@@ -146,14 +146,14 @@ Proceed to install step (treat as version 0.0.0 for comparison).
|
|
|
146
146
|
Check npm for latest version:
|
|
147
147
|
|
|
148
148
|
```bash
|
|
149
|
-
npm view
|
|
149
|
+
npm view code-as-plan version 2>/dev/null
|
|
150
150
|
```
|
|
151
151
|
|
|
152
152
|
**If npm check fails:**
|
|
153
153
|
```
|
|
154
154
|
Couldn't check for updates (offline or npm unavailable).
|
|
155
155
|
|
|
156
|
-
To update manually: `npx
|
|
156
|
+
To update manually: `npx code-as-plan@latest --global`
|
|
157
157
|
```
|
|
158
158
|
|
|
159
159
|
Exit.
|
|
@@ -164,7 +164,7 @@ Compare installed vs latest:
|
|
|
164
164
|
|
|
165
165
|
**If installed == latest:**
|
|
166
166
|
```
|
|
167
|
-
##
|
|
167
|
+
## CAP Update
|
|
168
168
|
|
|
169
169
|
**Installed:** X.Y.Z
|
|
170
170
|
**Latest:** X.Y.Z
|
|
@@ -176,7 +176,7 @@ Exit.
|
|
|
176
176
|
|
|
177
177
|
**If installed > latest:**
|
|
178
178
|
```
|
|
179
|
-
##
|
|
179
|
+
## CAP Update
|
|
180
180
|
|
|
181
181
|
**Installed:** X.Y.Z
|
|
182
182
|
**Latest:** A.B.C
|
|
@@ -195,7 +195,7 @@ Exit.
|
|
|
195
195
|
3. Display preview and ask for confirmation:
|
|
196
196
|
|
|
197
197
|
```
|
|
198
|
-
##
|
|
198
|
+
## CAP Update Available
|
|
199
199
|
|
|
200
200
|
**Installed:** 1.5.10
|
|
201
201
|
**Latest:** 1.5.15
|
|
@@ -215,22 +215,22 @@ Exit.
|
|
|
215
215
|
|
|
216
216
|
────────────────────────────────────────────────────────────
|
|
217
217
|
|
|
218
|
-
⚠️ **Note:** The installer performs a clean install of
|
|
219
|
-
- `commands/
|
|
218
|
+
⚠️ **Note:** The installer performs a clean install of CAP folders:
|
|
219
|
+
- `commands/cap/` will be wiped and replaced
|
|
220
220
|
- `cap/` will be wiped and replaced
|
|
221
|
-
- `agents/
|
|
221
|
+
- `agents/cap-*` files will be replaced
|
|
222
222
|
|
|
223
223
|
(Paths are relative to detected runtime install location:
|
|
224
224
|
global: `~/.claude/`, `~/.config/opencode/`, `~/.opencode/`, `~/.gemini/`, or `~/.codex/`
|
|
225
225
|
local: `./.claude/`, `./.config/opencode/`, `./.opencode/`, `./.gemini/`, or `./.codex/`)
|
|
226
226
|
|
|
227
227
|
Your custom files in other locations are preserved:
|
|
228
|
-
- Custom commands not in `commands/
|
|
229
|
-
- Custom agents not prefixed with `
|
|
228
|
+
- Custom commands not in `commands/cap/` ✓
|
|
229
|
+
- Custom agents not prefixed with `cap-` ✓
|
|
230
230
|
- Custom hooks ✓
|
|
231
231
|
- Your CLAUDE.md files ✓
|
|
232
232
|
|
|
233
|
-
If you've modified any
|
|
233
|
+
If you've modified any CAP files directly, they'll be automatically backed up to `cap-local-patches/` and can be reapplied with `/cap:reapply-patches` after the update.
|
|
234
234
|
```
|
|
235
235
|
|
|
236
236
|
Use AskUserQuestion:
|
|
@@ -252,17 +252,17 @@ RUNTIME_FLAG="--$TARGET_RUNTIME"
|
|
|
252
252
|
|
|
253
253
|
**If LOCAL install:**
|
|
254
254
|
```bash
|
|
255
|
-
npx -y
|
|
255
|
+
npx -y code-as-plan@latest "$RUNTIME_FLAG" --local
|
|
256
256
|
```
|
|
257
257
|
|
|
258
258
|
**If GLOBAL install:**
|
|
259
259
|
```bash
|
|
260
|
-
npx -y
|
|
260
|
+
npx -y code-as-plan@latest "$RUNTIME_FLAG" --global
|
|
261
261
|
```
|
|
262
262
|
|
|
263
263
|
**If UNKNOWN install:**
|
|
264
264
|
```bash
|
|
265
|
-
npx -y
|
|
265
|
+
npx -y code-as-plan@latest --claude --global
|
|
266
266
|
```
|
|
267
267
|
|
|
268
268
|
Capture output. If install fails, show error and exit.
|
|
@@ -272,8 +272,8 @@ Clear the update cache so statusline indicator disappears:
|
|
|
272
272
|
```bash
|
|
273
273
|
# Clear update cache across all runtime directories
|
|
274
274
|
for dir in .claude .config/opencode .opencode .gemini .codex; do
|
|
275
|
-
rm -f "./$dir/cache/
|
|
276
|
-
rm -f "$HOME/$dir/cache/
|
|
275
|
+
rm -f "./$dir/cache/cap-update-check.json"
|
|
276
|
+
rm -f "$HOME/$dir/cache/cap-update-check.json"
|
|
277
277
|
done
|
|
278
278
|
```
|
|
279
279
|
|
|
@@ -285,12 +285,12 @@ Format completion message (changelog was already shown in confirmation step):
|
|
|
285
285
|
|
|
286
286
|
```
|
|
287
287
|
╔═══════════════════════════════════════════════════════════╗
|
|
288
|
-
║
|
|
288
|
+
║ CAP Updated: v1.5.10 → v1.5.15 ║
|
|
289
289
|
╚═══════════════════════════════════════════════════════════╝
|
|
290
290
|
|
|
291
291
|
⚠️ Restart your runtime to pick up the new commands.
|
|
292
292
|
|
|
293
|
-
[View full changelog](https://github.com/
|
|
293
|
+
[View full changelog](https://github.com/dwall-sys/code-as-plan/blob/main/CHANGELOG.md)
|
|
294
294
|
```
|
|
295
295
|
</step>
|
|
296
296
|
|
|
@@ -298,13 +298,13 @@ Format completion message (changelog was already shown in confirmation step):
|
|
|
298
298
|
<step name="check_local_patches">
|
|
299
299
|
After update completes, check if the installer detected and backed up any locally modified files:
|
|
300
300
|
|
|
301
|
-
Check for
|
|
301
|
+
Check for cap-local-patches/backup-meta.json in the config directory.
|
|
302
302
|
|
|
303
303
|
**If patches found:**
|
|
304
304
|
|
|
305
305
|
```
|
|
306
306
|
Local patches were backed up before the update.
|
|
307
|
-
Run /
|
|
307
|
+
Run /cap:reapply-patches to merge your modifications into the new version.
|
|
308
308
|
```
|
|
309
309
|
|
|
310
310
|
**If no patches:** Continue normally.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cap:update
|
|
3
|
+
description: "Update CAP to the latest version -- checks npm, shows changelog, performs clean install with cache clearing."
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- Read
|
|
6
|
+
- Bash
|
|
7
|
+
- Glob
|
|
8
|
+
- Grep
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<objective>
|
|
12
|
+
Check for CAP updates via npm, display the changelog for versions between installed and latest, obtain user confirmation, and execute a clean installation with cache clearing.
|
|
13
|
+
|
|
14
|
+
This command orchestrates the update workflow defined in `cap/workflows/update.md`.
|
|
15
|
+
</objective>
|
|
16
|
+
|
|
17
|
+
<context>
|
|
18
|
+
$ARGUMENTS
|
|
19
|
+
|
|
20
|
+
execution_context: commands/cap/update.md
|
|
21
|
+
</context>
|
|
22
|
+
|
|
23
|
+
<process>
|
|
24
|
+
|
|
25
|
+
## Step 1: Load and execute the update workflow
|
|
26
|
+
|
|
27
|
+
Read and follow the full workflow defined in:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
cap/workflows/update.md
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
That workflow handles:
|
|
34
|
+
1. Detecting installed version and install scope (local/global)
|
|
35
|
+
2. Checking npm for the latest `code-as-plan` version
|
|
36
|
+
3. Comparing versions and showing changelog
|
|
37
|
+
4. Obtaining user confirmation before updating
|
|
38
|
+
5. Running `npx code-as-plan@latest` with correct flags
|
|
39
|
+
6. Clearing `cap-update-check.json` cache files
|
|
40
|
+
7. Checking for local patches that need reapplication
|
|
41
|
+
|
|
42
|
+
Follow every step in that workflow exactly as written.
|
|
43
|
+
|
|
44
|
+
</process>
|
|
45
|
+
|
|
46
|
+
<output>
|
|
47
|
+
The workflow produces its own formatted output at each step. Do not add extra framing -- let the workflow messages speak for themselves.
|
|
48
|
+
</output>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
3
|
-
// Check for
|
|
2
|
+
// cap-hook-version: {{GSD_VERSION}}
|
|
3
|
+
// Check for CAP updates in background, write result to cache
|
|
4
4
|
// Called by SessionStart hook - runs once per session
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
@@ -30,7 +30,7 @@ function detectConfigDir(baseDir) {
|
|
|
30
30
|
const globalConfigDir = detectConfigDir(homeDir);
|
|
31
31
|
const projectConfigDir = detectConfigDir(cwd);
|
|
32
32
|
const cacheDir = path.join(globalConfigDir, 'cache');
|
|
33
|
-
const cacheFile = path.join(cacheDir, '
|
|
33
|
+
const cacheFile = path.join(cacheDir, 'cap-update-check.json');
|
|
34
34
|
|
|
35
35
|
// VERSION file locations (check project first, then global)
|
|
36
36
|
const projectVersionFile = path.join(projectConfigDir, 'cap', 'VERSION');
|
|
@@ -71,11 +71,11 @@ const child = spawn(process.execPath, ['-e', `
|
|
|
71
71
|
const hooksDir = path.join(configDir, 'cap', 'hooks');
|
|
72
72
|
try {
|
|
73
73
|
if (fs.existsSync(hooksDir)) {
|
|
74
|
-
const hookFiles = fs.readdirSync(hooksDir).filter(f => f.startsWith('gsd-') && f.endsWith('.js'));
|
|
74
|
+
const hookFiles = fs.readdirSync(hooksDir).filter(f => (f.startsWith('gsd-') || f.startsWith('cap-')) && f.endsWith('.js'));
|
|
75
75
|
for (const hookFile of hookFiles) {
|
|
76
76
|
try {
|
|
77
77
|
const content = fs.readFileSync(path.join(hooksDir, hookFile), 'utf8');
|
|
78
|
-
const versionMatch = content.match(/\\/\\/ gsd-hook-version:\\s*(.+)/);
|
|
78
|
+
const versionMatch = content.match(/\\/\\/ (?:gsd|cap)-hook-version:\\s*(.+)/);
|
|
79
79
|
if (versionMatch) {
|
|
80
80
|
const hookVersion = versionMatch[1].trim();
|
|
81
81
|
if (hookVersion !== installed && !hookVersion.includes('{{')) {
|
|
@@ -93,7 +93,7 @@ const child = spawn(process.execPath, ['-e', `
|
|
|
93
93
|
|
|
94
94
|
let latest = null;
|
|
95
95
|
try {
|
|
96
|
-
latest = execSync('npm view
|
|
96
|
+
latest = execSync('npm view code-as-plan version', { encoding: 'utf8', timeout: 10000, windowsHide: true }).trim();
|
|
97
97
|
} catch (e) {}
|
|
98
98
|
|
|
99
99
|
const result = {
|