agile-context-engineering 0.2.1 → 0.2.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/README.md +4 -0
- package/agile-context-engineering/workflows/update.xml +207 -0
- package/bin/install.js +118 -7
- package/commands/ace/update.md +54 -0
- package/hooks/ace-check-update.js +62 -0
- package/hooks/ace-statusline.js +89 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -124,6 +124,10 @@ npx agile-context-engineering --opencode --global # Crush (formerly OpenCode), g
|
|
|
124
124
|
npx agile-context-engineering --all --global # All runtimes, global install
|
|
125
125
|
```
|
|
126
126
|
|
|
127
|
+
### Updating
|
|
128
|
+
|
|
129
|
+
When a new version is available, your status bar will show a yellow `/ace:update` indicator. Run the `/ace:update` command inside Claude Code to update — it detects your install type (global/local, Claude/Crush) automatically and runs the correct installer.
|
|
130
|
+
|
|
127
131
|
### Prerequisites
|
|
128
132
|
|
|
129
133
|
| Requirement | Purpose |
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
<workflow>
|
|
2
|
+
|
|
3
|
+
<purpose>
|
|
4
|
+
Check for ACE updates via npm, display version comparison, obtain user confirmation,
|
|
5
|
+
and execute clean installation with cache clearing.
|
|
6
|
+
</purpose>
|
|
7
|
+
|
|
8
|
+
<mandatory-context>
|
|
9
|
+
Read all files referenced by the invoking prompt's execution-context before starting.
|
|
10
|
+
</mandatory-context>
|
|
11
|
+
|
|
12
|
+
<process>
|
|
13
|
+
|
|
14
|
+
<!-- ══════════════════════════════════════════════════════════════════ -->
|
|
15
|
+
<!-- STEP 1: DETECT INSTALLATION -->
|
|
16
|
+
<!-- ══════════════════════════════════════════════════════════════════ -->
|
|
17
|
+
|
|
18
|
+
<step name="detect-installation" order="1">
|
|
19
|
+
Detect whether ACE is installed locally or globally, and for which runtime.
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
if [ -f "./.claude/agile-context-engineering/VERSION" ]; then
|
|
23
|
+
echo "SCOPE=local"
|
|
24
|
+
echo "RUNTIME=claude"
|
|
25
|
+
echo "VERSION=$(cat ./.claude/agile-context-engineering/VERSION)"
|
|
26
|
+
elif [ -f "$HOME/.claude/agile-context-engineering/VERSION" ]; then
|
|
27
|
+
echo "SCOPE=global"
|
|
28
|
+
echo "RUNTIME=claude"
|
|
29
|
+
echo "VERSION=$(cat $HOME/.claude/agile-context-engineering/VERSION)"
|
|
30
|
+
elif [ -f "./.opencode/agile-context-engineering/VERSION" ]; then
|
|
31
|
+
echo "SCOPE=local"
|
|
32
|
+
echo "RUNTIME=opencode"
|
|
33
|
+
echo "VERSION=$(cat ./.opencode/agile-context-engineering/VERSION)"
|
|
34
|
+
elif [ -f "$HOME/.opencode/agile-context-engineering/VERSION" ]; then
|
|
35
|
+
echo "SCOPE=global"
|
|
36
|
+
echo "RUNTIME=opencode"
|
|
37
|
+
echo "VERSION=$(cat $HOME/.opencode/agile-context-engineering/VERSION)"
|
|
38
|
+
else
|
|
39
|
+
echo "SCOPE=unknown"
|
|
40
|
+
echo "RUNTIME=claude"
|
|
41
|
+
echo "VERSION=0.0.0"
|
|
42
|
+
fi
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Parse SCOPE, RUNTIME, and VERSION from the output.
|
|
46
|
+
|
|
47
|
+
**If SCOPE is "unknown":**
|
|
48
|
+
```
|
|
49
|
+
## ACE Update
|
|
50
|
+
|
|
51
|
+
**Installed version:** Unknown
|
|
52
|
+
|
|
53
|
+
Your installation doesn't include version tracking.
|
|
54
|
+
Running fresh install...
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Proceed with VERSION=0.0.0.
|
|
58
|
+
</step>
|
|
59
|
+
|
|
60
|
+
<!-- ══════════════════════════════════════════════════════════════════ -->
|
|
61
|
+
<!-- STEP 2: CHECK LATEST VERSION -->
|
|
62
|
+
<!-- ══════════════════════════════════════════════════════════════════ -->
|
|
63
|
+
|
|
64
|
+
<step name="check-latest-version" order="2">
|
|
65
|
+
Check npm for the latest published version:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm view agile-context-engineering version 2>/dev/null
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**If npm check fails:**
|
|
72
|
+
```
|
|
73
|
+
Couldn't check for updates (offline or npm unavailable).
|
|
74
|
+
|
|
75
|
+
To update manually: `npx agile-context-engineering --claude --global`
|
|
76
|
+
```
|
|
77
|
+
Exit.
|
|
78
|
+
</step>
|
|
79
|
+
|
|
80
|
+
<!-- ══════════════════════════════════════════════════════════════════ -->
|
|
81
|
+
<!-- STEP 3: COMPARE VERSIONS -->
|
|
82
|
+
<!-- ══════════════════════════════════════════════════════════════════ -->
|
|
83
|
+
|
|
84
|
+
<step name="compare-versions" order="3">
|
|
85
|
+
Compare installed vs latest.
|
|
86
|
+
|
|
87
|
+
**If installed == latest:**
|
|
88
|
+
```
|
|
89
|
+
## ACE Update
|
|
90
|
+
|
|
91
|
+
**Installed:** X.Y.Z
|
|
92
|
+
**Latest:** X.Y.Z
|
|
93
|
+
|
|
94
|
+
You're already on the latest version.
|
|
95
|
+
```
|
|
96
|
+
Exit.
|
|
97
|
+
|
|
98
|
+
**If installed > latest:**
|
|
99
|
+
```
|
|
100
|
+
## ACE Update
|
|
101
|
+
|
|
102
|
+
**Installed:** X.Y.Z
|
|
103
|
+
**Latest:** A.B.C
|
|
104
|
+
|
|
105
|
+
You're ahead of the latest release (development version?).
|
|
106
|
+
```
|
|
107
|
+
Exit.
|
|
108
|
+
</step>
|
|
109
|
+
|
|
110
|
+
<!-- ══════════════════════════════════════════════════════════════════ -->
|
|
111
|
+
<!-- STEP 4: CONFIRM UPDATE -->
|
|
112
|
+
<!-- ══════════════════════════════════════════════════════════════════ -->
|
|
113
|
+
|
|
114
|
+
<step name="confirm-update" order="4">
|
|
115
|
+
If update is available, show what will happen and ask for confirmation:
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
## ACE Update Available
|
|
119
|
+
|
|
120
|
+
**Installed:** {installed_version}
|
|
121
|
+
**Latest:** {latest_version}
|
|
122
|
+
|
|
123
|
+
The installer performs a clean install of ACE folders:
|
|
124
|
+
- `commands/ace/` will be wiped and replaced
|
|
125
|
+
- `agile-context-engineering/` will be wiped and replaced
|
|
126
|
+
- `agents/ace-*` files will be replaced
|
|
127
|
+
|
|
128
|
+
Your custom files are preserved:
|
|
129
|
+
- Custom commands not in `commands/ace/`
|
|
130
|
+
- Custom agents not prefixed with `ace-`
|
|
131
|
+
- Your CLAUDE.md files
|
|
132
|
+
- Your .ace/ project artifacts
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Use AskUserQuestion:
|
|
136
|
+
- Question: "Proceed with update?"
|
|
137
|
+
- Options:
|
|
138
|
+
- "Yes, update now"
|
|
139
|
+
- "No, cancel"
|
|
140
|
+
|
|
141
|
+
**If user cancels:** Exit.
|
|
142
|
+
</step>
|
|
143
|
+
|
|
144
|
+
<!-- ══════════════════════════════════════════════════════════════════ -->
|
|
145
|
+
<!-- STEP 5: RUN UPDATE -->
|
|
146
|
+
<!-- ══════════════════════════════════════════════════════════════════ -->
|
|
147
|
+
|
|
148
|
+
<step name="run-update" order="5">
|
|
149
|
+
Run the update using detected SCOPE and RUNTIME from step 1.
|
|
150
|
+
|
|
151
|
+
Build the command: `npx agile-context-engineering --{RUNTIME} --{SCOPE}`
|
|
152
|
+
|
|
153
|
+
Examples:
|
|
154
|
+
- Global Claude: `npx agile-context-engineering --claude --global`
|
|
155
|
+
- Local Claude: `npx agile-context-engineering --claude --local`
|
|
156
|
+
- Global Crush: `npx agile-context-engineering --opencode --global`
|
|
157
|
+
- Local Crush: `npx agile-context-engineering --opencode --local`
|
|
158
|
+
|
|
159
|
+
If SCOPE was "unknown", default to `--claude --global`.
|
|
160
|
+
|
|
161
|
+
Capture output. If install fails, show error and exit.
|
|
162
|
+
|
|
163
|
+
Clear the update cache so statusline indicator disappears:
|
|
164
|
+
|
|
165
|
+
**If SCOPE is "local":**
|
|
166
|
+
```bash
|
|
167
|
+
rm -f ./.claude/cache/ace-update-check.json
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**If SCOPE is "global":**
|
|
171
|
+
```bash
|
|
172
|
+
rm -f "$HOME/.claude/cache/ace-update-check.json"
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
For Crush runtime, substitute `.opencode` for `.claude` in the cache path.
|
|
176
|
+
</step>
|
|
177
|
+
|
|
178
|
+
<!-- ══════════════════════════════════════════════════════════════════ -->
|
|
179
|
+
<!-- STEP 6: DISPLAY RESULT -->
|
|
180
|
+
<!-- ══════════════════════════════════════════════════════════════════ -->
|
|
181
|
+
|
|
182
|
+
<step name="display-result" order="6">
|
|
183
|
+
Format completion message:
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
============================================================
|
|
187
|
+
ACE Updated: v{old} -> v{new}
|
|
188
|
+
============================================================
|
|
189
|
+
|
|
190
|
+
Restart Claude Code to pick up the new commands.
|
|
191
|
+
```
|
|
192
|
+
</step>
|
|
193
|
+
|
|
194
|
+
</process>
|
|
195
|
+
|
|
196
|
+
<success_criteria>
|
|
197
|
+
<check>Installed version detected correctly (local/global, claude/crush)</check>
|
|
198
|
+
<check>Latest version checked via npm</check>
|
|
199
|
+
<check>Update skipped if already current</check>
|
|
200
|
+
<check>Clean install warning shown</check>
|
|
201
|
+
<check>User confirmation obtained before update</check>
|
|
202
|
+
<check>Update executed with correct --runtime and --scope flags</check>
|
|
203
|
+
<check>Update cache cleared</check>
|
|
204
|
+
<check>Restart reminder shown</check>
|
|
205
|
+
</success_criteria>
|
|
206
|
+
|
|
207
|
+
</workflow>
|
package/bin/install.js
CHANGED
|
@@ -5,7 +5,7 @@ const path = require('path');
|
|
|
5
5
|
const readline = require('readline');
|
|
6
6
|
const os = require('os');
|
|
7
7
|
|
|
8
|
-
const VERSION = '
|
|
8
|
+
const VERSION = require('../package.json').version;
|
|
9
9
|
|
|
10
10
|
// ANSI color codes
|
|
11
11
|
const colors = {
|
|
@@ -67,12 +67,31 @@ function parseArgs() {
|
|
|
67
67
|
all: args.includes('--all'),
|
|
68
68
|
global: args.includes('--global'),
|
|
69
69
|
local: args.includes('--local'),
|
|
70
|
+
forceStatusline: args.includes('--force-statusline'),
|
|
70
71
|
help: args.includes('--help') || args.includes('-h'),
|
|
71
72
|
version: args.includes('--version') || args.includes('-v'),
|
|
72
73
|
};
|
|
73
74
|
return flags;
|
|
74
75
|
}
|
|
75
76
|
|
|
77
|
+
// Read or create settings.json
|
|
78
|
+
function readSettings(settingsPath) {
|
|
79
|
+
if (fs.existsSync(settingsPath)) {
|
|
80
|
+
try {
|
|
81
|
+
return JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
82
|
+
} catch (e) {
|
|
83
|
+
return {};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return {};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Build hook command with proper quoting for the target directory
|
|
90
|
+
function buildHookCommand(targetDir, hookFile) {
|
|
91
|
+
const hookPath = path.join(targetDir, 'hooks', hookFile);
|
|
92
|
+
return `node "${hookPath.replace(/\\/g, '/')}"`;
|
|
93
|
+
}
|
|
94
|
+
|
|
76
95
|
function showHelp() {
|
|
77
96
|
log(`
|
|
78
97
|
Usage: npx agile-context-engineering [options]
|
|
@@ -82,9 +101,10 @@ Options:
|
|
|
82
101
|
--opencode Install for Crush (formerly OpenCode)
|
|
83
102
|
--all Install for all supported runtimes
|
|
84
103
|
--global Install globally (~/.claude, ~/.opencode)
|
|
85
|
-
--local
|
|
86
|
-
-
|
|
87
|
-
-
|
|
104
|
+
--local Install locally (.claude, .opencode)
|
|
105
|
+
--force-statusline Replace existing statusline configuration
|
|
106
|
+
-h, --help Show this help message
|
|
107
|
+
-v, --version Show version number
|
|
88
108
|
|
|
89
109
|
Examples:
|
|
90
110
|
npx agile-context-engineering # Interactive installation
|
|
@@ -278,6 +298,26 @@ function installForRuntime(runtime, scope, packageDir) {
|
|
|
278
298
|
log(` ✓ Tools installed`, colors.green);
|
|
279
299
|
}
|
|
280
300
|
|
|
301
|
+
// Copy hooks
|
|
302
|
+
const srcHooks = path.join(packageDir, 'hooks');
|
|
303
|
+
const hooksPath = path.join(basePath, 'hooks');
|
|
304
|
+
if (fs.existsSync(srcHooks)) {
|
|
305
|
+
// Only copy ace-* hook files, preserve non-ACE hooks (e.g. GSD)
|
|
306
|
+
if (!fs.existsSync(hooksPath)) {
|
|
307
|
+
fs.mkdirSync(hooksPath, { recursive: true });
|
|
308
|
+
}
|
|
309
|
+
for (const f of fs.readdirSync(srcHooks)) {
|
|
310
|
+
if (f.startsWith('ace-')) {
|
|
311
|
+
fs.copyFileSync(path.join(srcHooks, f), path.join(hooksPath, f));
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
log(` ✓ Hooks installed`, colors.green);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Write VERSION file for update checking
|
|
318
|
+
const versionFile = path.join(acePath, 'VERSION');
|
|
319
|
+
fs.writeFileSync(versionFile, VERSION, 'utf-8');
|
|
320
|
+
|
|
281
321
|
return basePath;
|
|
282
322
|
}
|
|
283
323
|
|
|
@@ -355,21 +395,92 @@ async function main() {
|
|
|
355
395
|
installedPaths.push({ runtime, name: RUNTIMES[runtime].name, path: installedPath });
|
|
356
396
|
}
|
|
357
397
|
|
|
398
|
+
// Configure hooks and statusline in settings.json (Claude Code only, not Crush)
|
|
399
|
+
for (const { runtime, path: basePath } of installedPaths) {
|
|
400
|
+
if (runtime !== 'claude') continue;
|
|
401
|
+
|
|
402
|
+
const settingsPath = path.join(basePath, 'settings.json');
|
|
403
|
+
const settings = readSettings(settingsPath);
|
|
404
|
+
|
|
405
|
+
const statuslineCommand = buildHookCommand(basePath, 'ace-statusline.js');
|
|
406
|
+
const updateCheckCommand = buildHookCommand(basePath, 'ace-check-update.js');
|
|
407
|
+
|
|
408
|
+
// Register SessionStart hook for background update checking
|
|
409
|
+
if (!settings.hooks) settings.hooks = {};
|
|
410
|
+
if (!settings.hooks.SessionStart) settings.hooks.SessionStart = [];
|
|
411
|
+
|
|
412
|
+
const hasAceUpdateHook = settings.hooks.SessionStart.some(entry =>
|
|
413
|
+
entry.hooks && entry.hooks.some(h => h.command && h.command.includes('ace-check-update'))
|
|
414
|
+
);
|
|
415
|
+
if (!hasAceUpdateHook) {
|
|
416
|
+
settings.hooks.SessionStart.push({
|
|
417
|
+
hooks: [{ type: 'command', command: updateCheckCommand }]
|
|
418
|
+
});
|
|
419
|
+
log(` ✓ Configured update check hook`, colors.green);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Handle statusline configuration
|
|
423
|
+
const hasExisting = settings.statusLine != null;
|
|
424
|
+
const isAceStatusline = hasExisting && settings.statusLine.command &&
|
|
425
|
+
settings.statusLine.command.includes('ace-statusline');
|
|
426
|
+
|
|
427
|
+
if (!hasExisting || flags.forceStatusline) {
|
|
428
|
+
// No existing statusline or force flag — install ACE statusline
|
|
429
|
+
settings.statusLine = { type: 'command', command: statuslineCommand };
|
|
430
|
+
log(` ✓ Configured statusline`, colors.green);
|
|
431
|
+
} else if (isAceStatusline) {
|
|
432
|
+
// Already ACE statusline — update path
|
|
433
|
+
settings.statusLine = { type: 'command', command: statuslineCommand };
|
|
434
|
+
log(` ✓ Updated statusline`, colors.green);
|
|
435
|
+
} else if (isInteractive) {
|
|
436
|
+
// Existing non-ACE statusline in interactive mode — ask user
|
|
437
|
+
const existingCmd = settings.statusLine.command || settings.statusLine.url || '(custom)';
|
|
438
|
+
log(`\n ⚠ Existing statusline detected`, colors.yellow);
|
|
439
|
+
log(` Current: ${existingCmd}`, colors.dim);
|
|
440
|
+
log(`\n ACE statusline shows:`, colors.cyan);
|
|
441
|
+
log(` • Model name`, colors.dim);
|
|
442
|
+
log(` • Current task (from todo list)`, colors.dim);
|
|
443
|
+
log(` • Context window usage (color-coded)`, colors.dim);
|
|
444
|
+
log(` • Update notifications`, colors.dim);
|
|
445
|
+
|
|
446
|
+
const rl = createPrompt();
|
|
447
|
+
const choice = await ask(rl, '\n What would you like to do?', [
|
|
448
|
+
{ label: 'Keep existing statusline', value: 'keep' },
|
|
449
|
+
{ label: 'Replace with ACE statusline', value: 'replace' },
|
|
450
|
+
]);
|
|
451
|
+
rl.close();
|
|
452
|
+
|
|
453
|
+
if (choice === 'replace') {
|
|
454
|
+
settings.statusLine = { type: 'command', command: statuslineCommand };
|
|
455
|
+
log(` ✓ Configured statusline`, colors.green);
|
|
456
|
+
} else {
|
|
457
|
+
log(` ⚠ Skipping statusline (kept existing)`, colors.yellow);
|
|
458
|
+
}
|
|
459
|
+
} else {
|
|
460
|
+
// Non-interactive with existing statusline — skip
|
|
461
|
+
log(` ⚠ Skipping statusline (already configured, use --force-statusline to replace)`, colors.yellow);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Write settings
|
|
465
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
|
|
466
|
+
}
|
|
467
|
+
|
|
358
468
|
// Show success message
|
|
359
469
|
log(`\n${'═'.repeat(50)}`, colors.green);
|
|
360
470
|
log(` ACE installed successfully!`, colors.green + colors.bright);
|
|
361
471
|
log(`${'═'.repeat(50)}`, colors.green);
|
|
362
472
|
|
|
363
473
|
log(`\nInstalled locations:`, colors.cyan);
|
|
364
|
-
for (const { name, path: p } of installedPaths) {
|
|
365
|
-
log(` ${
|
|
474
|
+
for (const { name: runtimeName, path: p } of installedPaths) {
|
|
475
|
+
log(` ${runtimeName}: ${p}`, colors.dim);
|
|
366
476
|
}
|
|
367
477
|
|
|
368
478
|
log(`\nInstalled structure:`, colors.cyan);
|
|
369
|
-
for (const {
|
|
479
|
+
for (const { path: p } of installedPaths) {
|
|
370
480
|
log(` ${p}/`, colors.dim);
|
|
371
481
|
log(` commands/ace/ Slash commands`, colors.dim);
|
|
372
482
|
log(` agents/ Agent definitions`, colors.dim);
|
|
483
|
+
log(` hooks/ Statusline & update hooks`, colors.dim);
|
|
373
484
|
log(` ${ACE_DIR_NAME}/`, colors.dim);
|
|
374
485
|
log(` templates/ Project & artifact templates`, colors.dim);
|
|
375
486
|
log(` utils/ Formatting & utility guides`, colors.dim);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ace:update
|
|
3
|
+
description: Update ACE to latest version
|
|
4
|
+
argument-hint: ""
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Bash
|
|
7
|
+
- AskUserQuestion
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
```xml
|
|
11
|
+
<command>
|
|
12
|
+
|
|
13
|
+
<execution-time>
|
|
14
|
+
<runs-after>
|
|
15
|
+
<trigger>When statusline shows the update indicator</trigger>
|
|
16
|
+
<trigger>When user wants to check for or install ACE updates</trigger>
|
|
17
|
+
</runs-after>
|
|
18
|
+
</execution-time>
|
|
19
|
+
|
|
20
|
+
<input>
|
|
21
|
+
<parameters>
|
|
22
|
+
<required></required>
|
|
23
|
+
<optional></optional>
|
|
24
|
+
</parameters>
|
|
25
|
+
</input>
|
|
26
|
+
|
|
27
|
+
<execution-context>
|
|
28
|
+
<update-workflow>@~/.claude/agile-context-engineering/workflows/update.xml</update-workflow>
|
|
29
|
+
<ui-formatting>@~/.claude/agile-context-engineering/utils/ui-formatting.md</ui-formatting>
|
|
30
|
+
</execution-context>
|
|
31
|
+
|
|
32
|
+
<output>
|
|
33
|
+
<objective>
|
|
34
|
+
Check for ACE updates, install if available.
|
|
35
|
+
Automatically detects local vs global installation and Claude vs Crush runtime.
|
|
36
|
+
</objective>
|
|
37
|
+
</output>
|
|
38
|
+
|
|
39
|
+
<process>
|
|
40
|
+
Execute the update workflow from
|
|
41
|
+
`@~/.claude/agile-context-engineering/workflows/update.xml` end-to-end.
|
|
42
|
+
|
|
43
|
+
The workflow handles all logic including:
|
|
44
|
+
1. Installation detection (local/global, Claude/Crush)
|
|
45
|
+
2. Latest version checking via npm
|
|
46
|
+
3. Version comparison
|
|
47
|
+
4. Clean install warning display
|
|
48
|
+
5. User confirmation
|
|
49
|
+
6. Update execution
|
|
50
|
+
7. Cache clearing and restart reminder
|
|
51
|
+
</process>
|
|
52
|
+
|
|
53
|
+
</command>
|
|
54
|
+
```
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Check for ACE updates in background, write result to cache
|
|
3
|
+
// Called by SessionStart hook - runs once per session
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const os = require('os');
|
|
8
|
+
const { spawn } = require('child_process');
|
|
9
|
+
|
|
10
|
+
const homeDir = os.homedir();
|
|
11
|
+
const cwd = process.cwd();
|
|
12
|
+
const cacheDir = path.join(homeDir, '.claude', 'cache');
|
|
13
|
+
const cacheFile = path.join(cacheDir, 'ace-update-check.json');
|
|
14
|
+
|
|
15
|
+
// VERSION file locations (check project first, then global)
|
|
16
|
+
const projectVersionFile = path.join(cwd, '.claude', 'agile-context-engineering', 'VERSION');
|
|
17
|
+
const globalVersionFile = path.join(homeDir, '.claude', 'agile-context-engineering', 'VERSION');
|
|
18
|
+
|
|
19
|
+
// Ensure cache directory exists
|
|
20
|
+
if (!fs.existsSync(cacheDir)) {
|
|
21
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Run check in background (spawn detached process, windowsHide prevents console flash)
|
|
25
|
+
const child = spawn(process.execPath, ['-e', `
|
|
26
|
+
const fs = require('fs');
|
|
27
|
+
const { execSync } = require('child_process');
|
|
28
|
+
|
|
29
|
+
const cacheFile = ${JSON.stringify(cacheFile)};
|
|
30
|
+
const projectVersionFile = ${JSON.stringify(projectVersionFile)};
|
|
31
|
+
const globalVersionFile = ${JSON.stringify(globalVersionFile)};
|
|
32
|
+
|
|
33
|
+
// Check project directory first (local install), then global
|
|
34
|
+
let installed = '0.0.0';
|
|
35
|
+
try {
|
|
36
|
+
if (fs.existsSync(projectVersionFile)) {
|
|
37
|
+
installed = fs.readFileSync(projectVersionFile, 'utf8').trim();
|
|
38
|
+
} else if (fs.existsSync(globalVersionFile)) {
|
|
39
|
+
installed = fs.readFileSync(globalVersionFile, 'utf8').trim();
|
|
40
|
+
}
|
|
41
|
+
} catch (e) {}
|
|
42
|
+
|
|
43
|
+
let latest = null;
|
|
44
|
+
try {
|
|
45
|
+
latest = execSync('npm view agile-context-engineering version', { encoding: 'utf8', timeout: 10000, windowsHide: true }).trim();
|
|
46
|
+
} catch (e) {}
|
|
47
|
+
|
|
48
|
+
const result = {
|
|
49
|
+
update_available: latest && installed !== latest,
|
|
50
|
+
installed,
|
|
51
|
+
latest: latest || 'unknown',
|
|
52
|
+
checked: Math.floor(Date.now() / 1000)
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
fs.writeFileSync(cacheFile, JSON.stringify(result));
|
|
56
|
+
`], {
|
|
57
|
+
stdio: 'ignore',
|
|
58
|
+
windowsHide: true,
|
|
59
|
+
detached: true
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
child.unref();
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Claude Code Statusline - ACE Edition
|
|
3
|
+
// Shows: update indicator | model | current task | directory | context usage
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const os = require('os');
|
|
8
|
+
|
|
9
|
+
// Read JSON from stdin
|
|
10
|
+
let input = '';
|
|
11
|
+
process.stdin.setEncoding('utf8');
|
|
12
|
+
process.stdin.on('data', chunk => input += chunk);
|
|
13
|
+
process.stdin.on('end', () => {
|
|
14
|
+
try {
|
|
15
|
+
const data = JSON.parse(input);
|
|
16
|
+
const model = data.model?.display_name || 'Claude';
|
|
17
|
+
const dir = data.workspace?.current_dir || process.cwd();
|
|
18
|
+
const session = data.session_id || '';
|
|
19
|
+
const remaining = data.context_window?.remaining_percentage;
|
|
20
|
+
|
|
21
|
+
// Context window display (shows USED percentage scaled to 80% limit)
|
|
22
|
+
// Claude Code enforces an 80% context limit, so we scale to show 100% at that point
|
|
23
|
+
let ctx = '';
|
|
24
|
+
if (remaining != null) {
|
|
25
|
+
const rem = Math.round(remaining);
|
|
26
|
+
const rawUsed = Math.max(0, Math.min(100, 100 - rem));
|
|
27
|
+
// Scale: 80% real usage = 100% displayed
|
|
28
|
+
const used = Math.min(100, Math.round((rawUsed / 80) * 100));
|
|
29
|
+
|
|
30
|
+
// Build progress bar (10 segments)
|
|
31
|
+
const filled = Math.floor(used / 10);
|
|
32
|
+
const bar = '\u2588'.repeat(filled) + '\u2591'.repeat(10 - filled);
|
|
33
|
+
|
|
34
|
+
// Color based on scaled usage
|
|
35
|
+
if (used < 63) {
|
|
36
|
+
ctx = ` \x1b[32m${bar} ${used}%\x1b[0m`;
|
|
37
|
+
} else if (used < 81) {
|
|
38
|
+
ctx = ` \x1b[33m${bar} ${used}%\x1b[0m`;
|
|
39
|
+
} else if (used < 95) {
|
|
40
|
+
ctx = ` \x1b[38;5;208m${bar} ${used}%\x1b[0m`;
|
|
41
|
+
} else {
|
|
42
|
+
ctx = ` \x1b[5;31m\u{1F480} ${bar} ${used}%\x1b[0m`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Current task from todos
|
|
47
|
+
let task = '';
|
|
48
|
+
const homeDir = os.homedir();
|
|
49
|
+
const todosDir = path.join(homeDir, '.claude', 'todos');
|
|
50
|
+
if (session && fs.existsSync(todosDir)) {
|
|
51
|
+
try {
|
|
52
|
+
const files = fs.readdirSync(todosDir)
|
|
53
|
+
.filter(f => f.startsWith(session) && f.includes('-agent-') && f.endsWith('.json'))
|
|
54
|
+
.map(f => ({ name: f, mtime: fs.statSync(path.join(todosDir, f)).mtime }))
|
|
55
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
56
|
+
|
|
57
|
+
if (files.length > 0) {
|
|
58
|
+
try {
|
|
59
|
+
const todos = JSON.parse(fs.readFileSync(path.join(todosDir, files[0].name), 'utf8'));
|
|
60
|
+
const inProgress = todos.find(t => t.status === 'in_progress');
|
|
61
|
+
if (inProgress) task = inProgress.activeForm || '';
|
|
62
|
+
} catch (e) {}
|
|
63
|
+
}
|
|
64
|
+
} catch (e) {}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ACE update available?
|
|
68
|
+
let aceUpdate = '';
|
|
69
|
+
const cacheFile = path.join(homeDir, '.claude', 'cache', 'ace-update-check.json');
|
|
70
|
+
if (fs.existsSync(cacheFile)) {
|
|
71
|
+
try {
|
|
72
|
+
const cache = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
|
|
73
|
+
if (cache.update_available) {
|
|
74
|
+
aceUpdate = '\x1b[33m\u2B06 /ace:update\x1b[0m \u2502 ';
|
|
75
|
+
}
|
|
76
|
+
} catch (e) {}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Output
|
|
80
|
+
const dirname = path.basename(dir);
|
|
81
|
+
if (task) {
|
|
82
|
+
process.stdout.write(`${aceUpdate}\x1b[2m${model}\x1b[0m \u2502 \x1b[1m${task}\x1b[0m \u2502 \x1b[2m${dirname}\x1b[0m${ctx}`);
|
|
83
|
+
} else {
|
|
84
|
+
process.stdout.write(`${aceUpdate}\x1b[2m${model}\x1b[0m \u2502 \x1b[2m${dirname}\x1b[0m${ctx}`);
|
|
85
|
+
}
|
|
86
|
+
} catch (e) {
|
|
87
|
+
// Silent fail - don't break statusline on parse errors
|
|
88
|
+
}
|
|
89
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agile-context-engineering",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "ACE - Agile Context Engineering: A spec-driven development system for Claude Code and Crush (formerly OpenCode) with Agile workflows",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"bin",
|
|
12
12
|
"commands",
|
|
13
13
|
"agents",
|
|
14
|
+
"hooks",
|
|
14
15
|
"agile-context-engineering"
|
|
15
16
|
],
|
|
16
17
|
"scripts": {
|