agileflow 2.77.0 → 2.78.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 +3 -3
- package/package.json +6 -1
- package/scripts/agileflow-configure.js +174 -2
- package/scripts/agileflow-statusline.sh +171 -78
- package/scripts/agileflow-welcome.js +79 -2
- package/scripts/damage-control-bash.js +232 -0
- package/scripts/damage-control-edit.js +243 -0
- package/scripts/damage-control-write.js +243 -0
- package/src/core/agents/accessibility.md +124 -53
- package/src/core/agents/adr-writer.md +192 -52
- package/src/core/agents/analytics.md +139 -60
- package/src/core/agents/api.md +173 -63
- package/src/core/agents/ci.md +139 -57
- package/src/core/agents/compliance.md +159 -68
- package/src/core/agents/configuration/damage-control.md +356 -0
- package/src/core/agents/database.md +162 -61
- package/src/core/agents/datamigration.md +179 -66
- package/src/core/agents/design.md +179 -57
- package/src/core/agents/devops.md +160 -3
- package/src/core/agents/documentation.md +204 -60
- package/src/core/agents/epic-planner.md +147 -55
- package/src/core/agents/integrations.md +197 -69
- package/src/core/agents/mentor.md +158 -57
- package/src/core/agents/mobile.md +159 -67
- package/src/core/agents/monitoring.md +154 -65
- package/src/core/agents/multi-expert.md +115 -43
- package/src/core/agents/orchestrator.md +77 -24
- package/src/core/agents/performance.md +130 -75
- package/src/core/agents/product.md +151 -55
- package/src/core/agents/qa.md +162 -74
- package/src/core/agents/readme-updater.md +178 -76
- package/src/core/agents/refactor.md +148 -95
- package/src/core/agents/research.md +143 -72
- package/src/core/agents/security.md +154 -65
- package/src/core/agents/testing.md +176 -97
- package/src/core/agents/ui.md +170 -79
- package/src/core/commands/adr/list.md +171 -0
- package/src/core/commands/adr/update.md +235 -0
- package/src/core/commands/adr/view.md +252 -0
- package/src/core/commands/adr.md +207 -50
- package/src/core/commands/agent.md +16 -0
- package/src/core/commands/assign.md +148 -44
- package/src/core/commands/auto.md +18 -1
- package/src/core/commands/babysit.md +361 -36
- package/src/core/commands/baseline.md +14 -0
- package/src/core/commands/blockers.md +170 -51
- package/src/core/commands/board.md +144 -66
- package/src/core/commands/changelog.md +15 -0
- package/src/core/commands/ci.md +179 -69
- package/src/core/commands/compress.md +18 -0
- package/src/core/commands/configure.md +16 -0
- package/src/core/commands/context/export.md +193 -4
- package/src/core/commands/context/full.md +191 -18
- package/src/core/commands/context/note.md +248 -4
- package/src/core/commands/debt.md +17 -0
- package/src/core/commands/deploy.md +208 -65
- package/src/core/commands/deps.md +15 -0
- package/src/core/commands/diagnose.md +16 -0
- package/src/core/commands/docs.md +196 -64
- package/src/core/commands/epic/list.md +170 -0
- package/src/core/commands/epic/view.md +242 -0
- package/src/core/commands/epic.md +192 -69
- package/src/core/commands/feedback.md +191 -71
- package/src/core/commands/handoff.md +162 -48
- package/src/core/commands/help.md +9 -0
- package/src/core/commands/ideate.md +446 -0
- package/src/core/commands/impact.md +16 -0
- package/src/core/commands/metrics.md +141 -37
- package/src/core/commands/multi-expert.md +77 -0
- package/src/core/commands/packages.md +16 -0
- package/src/core/commands/pr.md +161 -67
- package/src/core/commands/readme-sync.md +16 -0
- package/src/core/commands/research/analyze.md +568 -0
- package/src/core/commands/research/ask.md +345 -20
- package/src/core/commands/research/import.md +562 -19
- package/src/core/commands/research/list.md +173 -5
- package/src/core/commands/research/view.md +181 -8
- package/src/core/commands/retro.md +135 -48
- package/src/core/commands/review.md +219 -47
- package/src/core/commands/session/end.md +209 -0
- package/src/core/commands/session/history.md +210 -0
- package/src/core/commands/session/init.md +116 -0
- package/src/core/commands/session/new.md +296 -0
- package/src/core/commands/session/resume.md +166 -0
- package/src/core/commands/session/status.md +166 -0
- package/src/core/commands/skill/create.md +115 -17
- package/src/core/commands/skill/delete.md +117 -0
- package/src/core/commands/skill/edit.md +104 -0
- package/src/core/commands/skill/list.md +128 -0
- package/src/core/commands/skill/test.md +135 -0
- package/src/core/commands/skill/upgrade.md +542 -0
- package/src/core/commands/sprint.md +17 -1
- package/src/core/commands/status.md +133 -21
- package/src/core/commands/story/list.md +176 -0
- package/src/core/commands/story/view.md +265 -0
- package/src/core/commands/story-validate.md +101 -1
- package/src/core/commands/story.md +204 -51
- package/src/core/commands/template.md +16 -1
- package/src/core/commands/tests.md +226 -64
- package/src/core/commands/update.md +17 -1
- package/src/core/commands/validate-expertise.md +16 -0
- package/src/core/commands/velocity.md +140 -36
- package/src/core/commands/verify.md +14 -0
- package/src/core/commands/whats-new.md +30 -0
- package/src/core/skills/_learnings/README.md +91 -0
- package/src/core/skills/_learnings/_template.yaml +106 -0
- package/src/core/skills/_learnings/commit.yaml +69 -0
- package/src/core/templates/damage-control-patterns.yaml +234 -0
- package/src/core/templates/skill-template.md +53 -11
- package/tools/cli/commands/start.js +180 -0
- package/tools/cli/tui/Dashboard.js +66 -0
- package/tools/cli/tui/StoryList.js +69 -0
- package/tools/cli/tui/index.js +16 -0
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/agileflow)
|
|
6
|
-
[](docs/04-architecture/commands.md)
|
|
7
7
|
[](docs/04-architecture/subagents.md)
|
|
8
8
|
[](docs/04-architecture/skills.md)
|
|
9
9
|
|
|
@@ -65,7 +65,7 @@ AgileFlow combines three proven methodologies:
|
|
|
65
65
|
|
|
66
66
|
| Component | Count | Description |
|
|
67
67
|
|-----------|-------|-------------|
|
|
68
|
-
| [Commands](docs/04-architecture/commands.md) |
|
|
68
|
+
| [Commands](docs/04-architecture/commands.md) | 68 | Slash commands for agile workflows |
|
|
69
69
|
| [Agents/Experts](docs/04-architecture/subagents.md) | 27 | Specialized agents with self-improving knowledge bases |
|
|
70
70
|
| [Skills](docs/04-architecture/skills.md) | Dynamic | Generated on-demand with `/agileflow:skill:create` |
|
|
71
71
|
|
|
@@ -76,7 +76,7 @@ AgileFlow combines three proven methodologies:
|
|
|
76
76
|
Full documentation lives in [`docs/04-architecture/`](docs/04-architecture/):
|
|
77
77
|
|
|
78
78
|
### Reference
|
|
79
|
-
- [Commands](docs/04-architecture/commands.md) - All
|
|
79
|
+
- [Commands](docs/04-architecture/commands.md) - All 68 slash commands
|
|
80
80
|
- [Agents/Experts](docs/04-architecture/subagents.md) - 27 specialized agents with self-improving knowledge
|
|
81
81
|
- [Skills](docs/04-architecture/skills.md) - Dynamic skill generator with MCP integration
|
|
82
82
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agileflow",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.78.0",
|
|
4
4
|
"description": "AI-driven agile development system for Claude Code, Cursor, Windsurf, and more",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agile",
|
|
@@ -51,6 +51,8 @@
|
|
|
51
51
|
"test:coverage": "jest --coverage"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
+
"blessed": "^0.1.81",
|
|
55
|
+
"blessed-contrib": "^4.10.1",
|
|
54
56
|
"chalk": "^4.1.2",
|
|
55
57
|
"commander": "^12.1.0",
|
|
56
58
|
"fs-extra": "^11.2.0",
|
|
@@ -59,6 +61,9 @@
|
|
|
59
61
|
"ora": "^5.4.1",
|
|
60
62
|
"semver": "^7.6.3"
|
|
61
63
|
},
|
|
64
|
+
"optionalDependencies": {
|
|
65
|
+
"node-pty": "^1.0.0"
|
|
66
|
+
},
|
|
62
67
|
"engines": {
|
|
63
68
|
"node": ">=18.0.0"
|
|
64
69
|
},
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* --detect Show current status
|
|
24
24
|
* --help Show help
|
|
25
25
|
*
|
|
26
|
-
* Features: sessionstart, precompact, ralphloop, selfimprove, archival, statusline, autoupdate
|
|
26
|
+
* Features: sessionstart, precompact, ralphloop, selfimprove, archival, statusline, autoupdate, damagecontrol
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
const fs = require('fs');
|
|
@@ -76,6 +76,11 @@ const FEATURES = {
|
|
|
76
76
|
archival: { script: 'archive-completed-stories.sh', requiresHook: 'sessionstart' },
|
|
77
77
|
statusline: { script: 'agileflow-statusline.sh' },
|
|
78
78
|
autoupdate: { metadataOnly: true }, // Stored in metadata.updates.autoUpdate
|
|
79
|
+
damagecontrol: {
|
|
80
|
+
preToolUseHooks: true,
|
|
81
|
+
scripts: ['damage-control-bash.js', 'damage-control-edit.js', 'damage-control-write.js'],
|
|
82
|
+
patternsFile: 'damage-control-patterns.yaml',
|
|
83
|
+
},
|
|
79
84
|
};
|
|
80
85
|
|
|
81
86
|
// Complete registry of all scripts that may need repair
|
|
@@ -87,6 +92,9 @@ const ALL_SCRIPTS = {
|
|
|
87
92
|
'auto-self-improve.js': { feature: 'selfimprove', required: true },
|
|
88
93
|
'archive-completed-stories.sh': { feature: 'archival', required: true },
|
|
89
94
|
'agileflow-statusline.sh': { feature: 'statusline', required: true },
|
|
95
|
+
'damage-control-bash.js': { feature: 'damagecontrol', required: true },
|
|
96
|
+
'damage-control-edit.js': { feature: 'damagecontrol', required: true },
|
|
97
|
+
'damage-control-write.js': { feature: 'damagecontrol', required: true },
|
|
90
98
|
|
|
91
99
|
// Support scripts (used by commands/agents)
|
|
92
100
|
'obtain-context.js': { usedBy: ['/babysit', '/mentor', '/sprint'] },
|
|
@@ -215,6 +223,7 @@ function detectConfig() {
|
|
|
215
223
|
selfimprove: { enabled: false, valid: true, issues: [], version: null, outdated: false },
|
|
216
224
|
archival: { enabled: false, threshold: null, version: null, outdated: false },
|
|
217
225
|
statusline: { enabled: false, valid: true, issues: [], version: null, outdated: false },
|
|
226
|
+
damagecontrol: { enabled: false, valid: true, issues: [], version: null, outdated: false, level: null, patternCount: 0 },
|
|
218
227
|
},
|
|
219
228
|
metadata: { exists: false, version: null },
|
|
220
229
|
currentVersion: VERSION,
|
|
@@ -298,6 +307,32 @@ function detectConfig() {
|
|
|
298
307
|
}
|
|
299
308
|
}
|
|
300
309
|
}
|
|
310
|
+
|
|
311
|
+
// PreToolUse hooks (damage control)
|
|
312
|
+
if (settings.hooks.PreToolUse) {
|
|
313
|
+
if (Array.isArray(settings.hooks.PreToolUse) && settings.hooks.PreToolUse.length > 0) {
|
|
314
|
+
// Check for damage-control hooks by looking for damage-control scripts
|
|
315
|
+
const hasBashHook = settings.hooks.PreToolUse.some(
|
|
316
|
+
h => h.matcher === 'Bash' && h.hooks?.some(hk => hk.command?.includes('damage-control'))
|
|
317
|
+
);
|
|
318
|
+
const hasEditHook = settings.hooks.PreToolUse.some(
|
|
319
|
+
h => h.matcher === 'Edit' && h.hooks?.some(hk => hk.command?.includes('damage-control'))
|
|
320
|
+
);
|
|
321
|
+
const hasWriteHook = settings.hooks.PreToolUse.some(
|
|
322
|
+
h => h.matcher === 'Write' && h.hooks?.some(hk => hk.command?.includes('damage-control'))
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
if (hasBashHook || hasEditHook || hasWriteHook) {
|
|
326
|
+
status.features.damagecontrol.enabled = true;
|
|
327
|
+
// Count how many of the 3 hooks are present
|
|
328
|
+
const hookCount = [hasBashHook, hasEditHook, hasWriteHook].filter(Boolean).length;
|
|
329
|
+
if (hookCount < 3) {
|
|
330
|
+
status.features.damagecontrol.valid = false;
|
|
331
|
+
status.features.damagecontrol.issues.push(`Only ${hookCount}/3 hooks configured`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
301
336
|
}
|
|
302
337
|
|
|
303
338
|
// StatusLine
|
|
@@ -326,6 +361,11 @@ function detectConfig() {
|
|
|
326
361
|
status.features.archival.threshold = meta.archival.threshold_days;
|
|
327
362
|
}
|
|
328
363
|
|
|
364
|
+
// Damage control metadata
|
|
365
|
+
if (meta.features?.damagecontrol?.enabled) {
|
|
366
|
+
status.features.damagecontrol.level = meta.features.damagecontrol.protectionLevel || 'standard';
|
|
367
|
+
}
|
|
368
|
+
|
|
329
369
|
// Read feature versions from metadata and check if outdated
|
|
330
370
|
if (meta.features) {
|
|
331
371
|
Object.entries(meta.features).forEach(([feature, data]) => {
|
|
@@ -402,6 +442,22 @@ function printStatus(status) {
|
|
|
402
442
|
|
|
403
443
|
printFeature('statusline', 'Status Line');
|
|
404
444
|
|
|
445
|
+
// Damage Control (special display with level info)
|
|
446
|
+
const dc = status.features.damagecontrol;
|
|
447
|
+
if (dc.enabled) {
|
|
448
|
+
let dcStatusText = 'enabled';
|
|
449
|
+
if (dc.level) dcStatusText += ` (${dc.level})`;
|
|
450
|
+
if (!dc.valid) dcStatusText = 'INCOMPLETE';
|
|
451
|
+
const dcIcon = dc.enabled && dc.valid ? '🛡️' : '⚠️';
|
|
452
|
+
const dcColor = dc.enabled && dc.valid ? c.green : c.yellow;
|
|
453
|
+
log(` ${dcIcon} Damage Control: ${dcStatusText}`, dcColor);
|
|
454
|
+
if (dc.issues?.length > 0) {
|
|
455
|
+
dc.issues.forEach(issue => log(` └─ ${issue}`, c.yellow));
|
|
456
|
+
}
|
|
457
|
+
} else {
|
|
458
|
+
log(` ❌ Damage Control: disabled`, c.dim);
|
|
459
|
+
}
|
|
460
|
+
|
|
405
461
|
// Metadata
|
|
406
462
|
if (status.metadata.exists) {
|
|
407
463
|
log(`\nMetadata: v${status.metadata.version}`, c.dim);
|
|
@@ -685,6 +741,79 @@ function enableFeature(feature, options = {}) {
|
|
|
685
741
|
return true; // Skip settings.json write for this feature
|
|
686
742
|
}
|
|
687
743
|
|
|
744
|
+
// Handle damage control (PreToolUse hooks)
|
|
745
|
+
if (feature === 'damagecontrol') {
|
|
746
|
+
const level = options.protectionLevel || 'standard';
|
|
747
|
+
|
|
748
|
+
// Verify all required scripts exist
|
|
749
|
+
const requiredScripts = ['damage-control-bash.js', 'damage-control-edit.js', 'damage-control-write.js'];
|
|
750
|
+
for (const script of requiredScripts) {
|
|
751
|
+
if (!scriptExists(script)) {
|
|
752
|
+
error(`Script not found: ${getScriptPath(script)}`);
|
|
753
|
+
info('Run "npx agileflow update" to reinstall scripts');
|
|
754
|
+
return false;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// Deploy patterns file if not exists
|
|
759
|
+
const patternsDir = path.join(process.cwd(), '.agileflow', 'config');
|
|
760
|
+
const patternsDest = path.join(patternsDir, 'damage-control-patterns.yaml');
|
|
761
|
+
if (!fs.existsSync(patternsDest)) {
|
|
762
|
+
ensureDir(patternsDir);
|
|
763
|
+
// Try to copy from templates
|
|
764
|
+
const templatePath = path.join(process.cwd(), '.agileflow', 'templates', 'damage-control-patterns.yaml');
|
|
765
|
+
if (fs.existsSync(templatePath)) {
|
|
766
|
+
fs.copyFileSync(templatePath, patternsDest);
|
|
767
|
+
success('Deployed damage control patterns');
|
|
768
|
+
} else {
|
|
769
|
+
warn('No patterns template found - hooks will use defaults');
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// Initialize PreToolUse array if not exists
|
|
774
|
+
if (!settings.hooks.PreToolUse) {
|
|
775
|
+
settings.hooks.PreToolUse = [];
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// Helper to add or update a PreToolUse hook
|
|
779
|
+
const addPreToolUseHook = (matcher, scriptName) => {
|
|
780
|
+
const scriptPath = path.join(process.cwd(), '.agileflow', 'scripts', scriptName);
|
|
781
|
+
|
|
782
|
+
// Remove existing hook for this matcher if present
|
|
783
|
+
settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter(h => h.matcher !== matcher);
|
|
784
|
+
|
|
785
|
+
// Add new hook
|
|
786
|
+
settings.hooks.PreToolUse.push({
|
|
787
|
+
matcher,
|
|
788
|
+
hooks: [{ type: 'command', command: `node ${scriptPath}`, timeout: 5 }],
|
|
789
|
+
});
|
|
790
|
+
};
|
|
791
|
+
|
|
792
|
+
// Add hooks for Bash, Edit, Write tools
|
|
793
|
+
addPreToolUseHook('Bash', 'damage-control-bash.js');
|
|
794
|
+
addPreToolUseHook('Edit', 'damage-control-edit.js');
|
|
795
|
+
addPreToolUseHook('Write', 'damage-control-write.js');
|
|
796
|
+
|
|
797
|
+
success('Damage control PreToolUse hooks enabled');
|
|
798
|
+
|
|
799
|
+
// Update metadata with protection level
|
|
800
|
+
updateMetadata({
|
|
801
|
+
features: {
|
|
802
|
+
damagecontrol: {
|
|
803
|
+
enabled: true,
|
|
804
|
+
protectionLevel: level,
|
|
805
|
+
version: VERSION,
|
|
806
|
+
at: new Date().toISOString(),
|
|
807
|
+
},
|
|
808
|
+
},
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
writeJSON('.claude/settings.json', settings);
|
|
812
|
+
updateGitignore();
|
|
813
|
+
|
|
814
|
+
return true;
|
|
815
|
+
}
|
|
816
|
+
|
|
688
817
|
writeJSON('.claude/settings.json', settings);
|
|
689
818
|
updateMetadata({
|
|
690
819
|
features: { [feature]: { enabled: true, version: VERSION, at: new Date().toISOString() } },
|
|
@@ -764,6 +893,45 @@ function disableFeature(feature) {
|
|
|
764
893
|
return true; // Skip settings.json write for this feature
|
|
765
894
|
}
|
|
766
895
|
|
|
896
|
+
// Disable damage control (PreToolUse hooks)
|
|
897
|
+
if (feature === 'damagecontrol') {
|
|
898
|
+
if (settings.hooks?.PreToolUse && Array.isArray(settings.hooks.PreToolUse)) {
|
|
899
|
+
const before = settings.hooks.PreToolUse.length;
|
|
900
|
+
|
|
901
|
+
// Remove damage-control hooks (Bash, Edit, Write matchers with damage-control scripts)
|
|
902
|
+
settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter(h => {
|
|
903
|
+
const isDamageControlHook = h.hooks?.some(hk => hk.command?.includes('damage-control'));
|
|
904
|
+
return !isDamageControlHook;
|
|
905
|
+
});
|
|
906
|
+
|
|
907
|
+
const after = settings.hooks.PreToolUse.length;
|
|
908
|
+
|
|
909
|
+
if (before > after) {
|
|
910
|
+
success(`Removed ${before - after} damage control PreToolUse hook(s)`);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
// If no more PreToolUse hooks, remove the entire array
|
|
914
|
+
if (settings.hooks.PreToolUse.length === 0) {
|
|
915
|
+
delete settings.hooks.PreToolUse;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// Update metadata
|
|
920
|
+
updateMetadata({
|
|
921
|
+
features: {
|
|
922
|
+
damagecontrol: {
|
|
923
|
+
enabled: false,
|
|
924
|
+
version: VERSION,
|
|
925
|
+
at: new Date().toISOString(),
|
|
926
|
+
},
|
|
927
|
+
},
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
writeJSON('.claude/settings.json', settings);
|
|
931
|
+
success('Damage control disabled');
|
|
932
|
+
return true;
|
|
933
|
+
}
|
|
934
|
+
|
|
767
935
|
writeJSON('.claude/settings.json', settings);
|
|
768
936
|
updateMetadata({
|
|
769
937
|
features: { [feature]: { enabled: false, version: VERSION, at: new Date().toISOString() } },
|
|
@@ -1228,9 +1396,10 @@ ${c.cyan}Feature Control:${c.reset}
|
|
|
1228
1396
|
--enable=<list> Enable features (comma-separated)
|
|
1229
1397
|
--disable=<list> Disable features (comma-separated)
|
|
1230
1398
|
|
|
1231
|
-
Features: sessionstart, precompact, ralphloop, selfimprove, archival, statusline
|
|
1399
|
+
Features: sessionstart, precompact, ralphloop, selfimprove, archival, statusline, damagecontrol
|
|
1232
1400
|
|
|
1233
1401
|
Stop hooks (ralphloop, selfimprove) run when Claude completes/pauses
|
|
1402
|
+
Damage control (damagecontrol) uses PreToolUse hooks to block dangerous commands
|
|
1234
1403
|
|
|
1235
1404
|
${c.cyan}Statusline Components:${c.reset}
|
|
1236
1405
|
--show=<list> Show statusline components (comma-separated)
|
|
@@ -1293,6 +1462,9 @@ ${c.cyan}Examples:${c.reset}
|
|
|
1293
1462
|
|
|
1294
1463
|
# Repair scripts for a specific feature
|
|
1295
1464
|
node .agileflow/scripts/agileflow-configure.js --repair=statusline
|
|
1465
|
+
|
|
1466
|
+
# Enable damage control (PreToolUse hooks to block dangerous commands)
|
|
1467
|
+
node .agileflow/scripts/agileflow-configure.js --enable=damagecontrol
|
|
1296
1468
|
`);
|
|
1297
1469
|
}
|
|
1298
1470
|
|
|
@@ -193,40 +193,75 @@ fi
|
|
|
193
193
|
# ============================================================================
|
|
194
194
|
input=$(cat)
|
|
195
195
|
|
|
196
|
-
# Parse model info
|
|
197
|
-
MODEL_DISPLAY=$(echo "$input" | jq -r '.model.display_name //
|
|
196
|
+
# Parse model info (fallback to empty if no input)
|
|
197
|
+
MODEL_DISPLAY=$(echo "$input" | jq -r '.model.display_name // empty' 2>/dev/null)
|
|
198
|
+
[ -z "$MODEL_DISPLAY" ] && MODEL_DISPLAY=""
|
|
198
199
|
|
|
199
|
-
#
|
|
200
|
-
|
|
201
|
-
|
|
200
|
+
# ============================================================================
|
|
201
|
+
# Context Window Usage (reads from session JSONL file like cc-statusline)
|
|
202
|
+
# ============================================================================
|
|
203
|
+
# Claude Code doesn't pass context usage via stdin reliably, so we read it
|
|
204
|
+
# directly from the session's JSONL file (same approach as cc-statusline)
|
|
202
205
|
|
|
203
206
|
CTX_DISPLAY=""
|
|
204
207
|
CTX_BAR_DISPLAY=""
|
|
205
208
|
CTX_COLOR="$CTX_GREEN"
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
209
|
+
PERCENT_USED=0
|
|
210
|
+
|
|
211
|
+
# Get session_id and current_dir from input
|
|
212
|
+
SESSION_ID=$(echo "$input" | jq -r '.session_id // empty' 2>/dev/null)
|
|
213
|
+
CURRENT_DIR=$(echo "$input" | jq -r '.workspace.current_dir // .cwd // empty' 2>/dev/null)
|
|
214
|
+
|
|
215
|
+
# Determine max context based on model (all modern models use 200K)
|
|
216
|
+
get_max_context() {
|
|
217
|
+
local model="$1"
|
|
218
|
+
case "$model" in
|
|
219
|
+
*"Claude 3 Haiku"*|*"claude 3 haiku"*)
|
|
220
|
+
echo "100000" # 100K for original Claude 3 Haiku
|
|
221
|
+
;;
|
|
222
|
+
*)
|
|
223
|
+
echo "200000" # 200K for Opus, Sonnet, modern Haiku
|
|
224
|
+
;;
|
|
225
|
+
esac
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
MAX_CONTEXT=$(get_max_context "$MODEL_DISPLAY")
|
|
229
|
+
|
|
230
|
+
if [ -n "$SESSION_ID" ] && [ -n "$CURRENT_DIR" ]; then
|
|
231
|
+
# Convert current dir to session file path (same as cc-statusline)
|
|
232
|
+
# e.g., /home/coder/AgileFlow -> home-coder-AgileFlow
|
|
233
|
+
PROJECT_DIR=$(echo "$CURRENT_DIR" | sed "s|^$HOME|~|g" | sed "s|~|$HOME|g" | sed 's|/|-|g' | sed 's|^-||')
|
|
234
|
+
SESSION_FILE="$HOME/.claude/projects/-${PROJECT_DIR}/${SESSION_ID}.jsonl"
|
|
221
235
|
|
|
222
|
-
|
|
236
|
+
if [ -f "$SESSION_FILE" ]; then
|
|
237
|
+
# Get the latest input token count from the session file (last 20 lines)
|
|
238
|
+
LATEST_TOKENS=$(tail -20 "$SESSION_FILE" | jq -r 'select(.message.usage) | .message.usage | ((.input_tokens // 0) + (.cache_read_input_tokens // 0))' 2>/dev/null | tail -1)
|
|
223
239
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
240
|
+
if [ -n "$LATEST_TOKENS" ] && [ "$LATEST_TOKENS" -gt 0 ] 2>/dev/null; then
|
|
241
|
+
PERCENT_USED=$((LATEST_TOKENS * 100 / MAX_CONTEXT))
|
|
242
|
+
# Cap at 100%
|
|
243
|
+
[ "$PERCENT_USED" -gt 100 ] && PERCENT_USED=100
|
|
244
|
+
fi
|
|
227
245
|
fi
|
|
228
246
|
fi
|
|
229
247
|
|
|
248
|
+
# Color based on usage level (using vibrant 256-color palette)
|
|
249
|
+
if [ "$PERCENT_USED" -ge 80 ]; then
|
|
250
|
+
CTX_COLOR="$CTX_RED" # Coral red - critical
|
|
251
|
+
elif [ "$PERCENT_USED" -ge 60 ]; then
|
|
252
|
+
CTX_COLOR="$CTX_ORANGE" # Peach - high usage
|
|
253
|
+
elif [ "$PERCENT_USED" -ge 40 ]; then
|
|
254
|
+
CTX_COLOR="$CTX_YELLOW" # Peach - moderate
|
|
255
|
+
else
|
|
256
|
+
CTX_COLOR="$CTX_GREEN" # Mint green - healthy
|
|
257
|
+
fi
|
|
258
|
+
|
|
259
|
+
CTX_DISPLAY="${CTX_COLOR}${PERCENT_USED}%${RESET}"
|
|
260
|
+
|
|
261
|
+
# Generate progress bar (8 chars wide for compactness)
|
|
262
|
+
CTX_BAR=$(progress_bar "$PERCENT_USED" 8)
|
|
263
|
+
CTX_BAR_DISPLAY="${DIM}[${RESET}${CTX_COLOR}${CTX_BAR}${RESET}${DIM}]${RESET}"
|
|
264
|
+
|
|
230
265
|
# Parse cost
|
|
231
266
|
TOTAL_COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
|
|
232
267
|
COST_DISPLAY=""
|
|
@@ -241,64 +276,122 @@ if [ "$TOTAL_COST" != "0" ] && [ "$TOTAL_COST" != "null" ]; then
|
|
|
241
276
|
fi
|
|
242
277
|
|
|
243
278
|
# ============================================================================
|
|
244
|
-
# Session Time Remaining (
|
|
279
|
+
# Session Time Remaining (Native - reads Claude Code's local JSONL files)
|
|
245
280
|
# ============================================================================
|
|
281
|
+
# Claude Code uses 5-hour billing blocks. Block starts with first message activity.
|
|
282
|
+
# We scan ~/.claude/projects/*/*.jsonl to find the earliest message in the current
|
|
283
|
+
# 5-hour window and calculate remaining time.
|
|
284
|
+
#
|
|
285
|
+
# Algorithm (like ccusage):
|
|
286
|
+
# 1. Find all timestamps from JSONL files modified in last 5 hours
|
|
287
|
+
# 2. Sort and find the earliest timestamp >= (now - 5 hours)
|
|
288
|
+
# 3. That's the block start; block end = block start + 5 hours
|
|
289
|
+
# 4. Remaining = block end - now
|
|
290
|
+
#
|
|
291
|
+
# Optimization: Cache result for 60 seconds to avoid repeated scans
|
|
246
292
|
SESSION_DISPLAY=""
|
|
247
293
|
if [ "$SHOW_SESSION_TIME" = "true" ]; then
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
294
|
+
SESSION_CACHE="/tmp/agileflow-session-cache"
|
|
295
|
+
CACHE_MAX_AGE=60 # seconds
|
|
296
|
+
NOW_SEC=$(date +%s)
|
|
297
|
+
|
|
298
|
+
# Check cache first
|
|
299
|
+
CACHED_BLOCK_START=""
|
|
300
|
+
if [ -f "$SESSION_CACHE" ]; then
|
|
301
|
+
CACHE_DATA=$(cat "$SESSION_CACHE" 2>/dev/null)
|
|
302
|
+
CACHE_TIME=$(echo "$CACHE_DATA" | cut -d: -f1)
|
|
303
|
+
CACHE_VALUE=$(echo "$CACHE_DATA" | cut -d: -f2)
|
|
304
|
+
if [ -n "$CACHE_TIME" ] && [ $((NOW_SEC - CACHE_TIME)) -lt "$CACHE_MAX_AGE" ] 2>/dev/null; then
|
|
305
|
+
CACHED_BLOCK_START="$CACHE_VALUE"
|
|
306
|
+
fi
|
|
307
|
+
fi
|
|
308
|
+
|
|
309
|
+
BLOCK_START_SEC=""
|
|
310
|
+
|
|
311
|
+
if [ -n "$CACHED_BLOCK_START" ] && [ "$CACHED_BLOCK_START" != "none" ]; then
|
|
312
|
+
BLOCK_START_SEC="$CACHED_BLOCK_START"
|
|
313
|
+
elif [ "$CACHED_BLOCK_START" != "none" ]; then
|
|
314
|
+
# Need to scan - use Python for speed (handles ISO timestamps natively)
|
|
315
|
+
CLAUDE_DATA_DIR="$HOME/.claude/projects"
|
|
316
|
+
BLOCK_DURATION=$((5 * 60 * 60)) # 5 hours in seconds
|
|
317
|
+
WINDOW_START=$((NOW_SEC - BLOCK_DURATION))
|
|
318
|
+
|
|
319
|
+
if [ -d "$CLAUDE_DATA_DIR" ]; then
|
|
320
|
+
BLOCK_START_SEC=$(python3 - "$CLAUDE_DATA_DIR" "$WINDOW_START" <<'PYTHON' 2>/dev/null
|
|
321
|
+
import sys, os, json, glob
|
|
322
|
+
from datetime import datetime
|
|
323
|
+
|
|
324
|
+
data_dir = sys.argv[1]
|
|
325
|
+
window_start = int(sys.argv[2])
|
|
326
|
+
|
|
327
|
+
earliest = None
|
|
328
|
+
# Only check files modified in last 5 hours (300 minutes)
|
|
329
|
+
for jsonl_path in glob.glob(f"{data_dir}/*/*.jsonl"):
|
|
330
|
+
try:
|
|
331
|
+
mtime = os.path.getmtime(jsonl_path)
|
|
332
|
+
if mtime < window_start:
|
|
333
|
+
continue # Skip old files
|
|
334
|
+
with open(jsonl_path, 'r') as f:
|
|
335
|
+
for line in f:
|
|
336
|
+
if '"timestamp"' not in line:
|
|
337
|
+
continue
|
|
338
|
+
try:
|
|
339
|
+
data = json.loads(line)
|
|
340
|
+
ts_str = data.get('timestamp', '')
|
|
341
|
+
if ts_str:
|
|
342
|
+
# Parse ISO timestamp
|
|
343
|
+
ts_str = ts_str.replace('Z', '+00:00')
|
|
344
|
+
dt = datetime.fromisoformat(ts_str)
|
|
345
|
+
epoch = int(dt.timestamp())
|
|
346
|
+
if epoch >= window_start:
|
|
347
|
+
if earliest is None or epoch < earliest:
|
|
348
|
+
earliest = epoch
|
|
349
|
+
except:
|
|
350
|
+
pass
|
|
351
|
+
except:
|
|
352
|
+
pass
|
|
353
|
+
|
|
354
|
+
if earliest:
|
|
355
|
+
print(earliest)
|
|
356
|
+
PYTHON
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
# Cache the result
|
|
360
|
+
if [ -n "$BLOCK_START_SEC" ] && [ "$BLOCK_START_SEC" -gt 0 ] 2>/dev/null; then
|
|
361
|
+
echo "${NOW_SEC}:${BLOCK_START_SEC}" > "$SESSION_CACHE"
|
|
362
|
+
else
|
|
363
|
+
echo "${NOW_SEC}:none" > "$SESSION_CACHE"
|
|
299
364
|
fi
|
|
300
365
|
fi
|
|
301
366
|
fi
|
|
367
|
+
|
|
368
|
+
# Calculate remaining time if we found a block start
|
|
369
|
+
if [ -n "$BLOCK_START_SEC" ] && [ "$BLOCK_START_SEC" -gt 0 ] 2>/dev/null; then
|
|
370
|
+
BLOCK_DURATION=$((5 * 60 * 60))
|
|
371
|
+
BLOCK_END_SEC=$((BLOCK_START_SEC + BLOCK_DURATION))
|
|
372
|
+
REMAINING=$((BLOCK_END_SEC - NOW_SEC))
|
|
373
|
+
[ "$REMAINING" -lt 0 ] && REMAINING=0
|
|
374
|
+
|
|
375
|
+
# Format remaining time
|
|
376
|
+
RH=$((REMAINING / 3600))
|
|
377
|
+
RM=$(((REMAINING % 3600) / 60))
|
|
378
|
+
|
|
379
|
+
# Color based on time remaining (using vibrant 256-color palette)
|
|
380
|
+
if [ "$RH" -eq 0 ] && [ "$RM" -lt 30 ]; then
|
|
381
|
+
SESSION_COLOR="$SESSION_RED" # Light pink - critical
|
|
382
|
+
elif [ "$RH" -eq 0 ]; then
|
|
383
|
+
SESSION_COLOR="$SESSION_YELLOW" # Light yellow - getting low
|
|
384
|
+
else
|
|
385
|
+
SESSION_COLOR="$SESSION_GREEN" # Light green - plenty of time
|
|
386
|
+
fi
|
|
387
|
+
|
|
388
|
+
# Build compact display: "⏱2h15m" or "⏱45m"
|
|
389
|
+
if [ "$RH" -gt 0 ]; then
|
|
390
|
+
SESSION_DISPLAY="${SESSION_COLOR}⏱${RH}h${RM}m${RESET}"
|
|
391
|
+
else
|
|
392
|
+
SESSION_DISPLAY="${SESSION_COLOR}⏱${RM}m${RESET}"
|
|
393
|
+
fi
|
|
394
|
+
fi
|
|
302
395
|
fi
|
|
303
396
|
|
|
304
397
|
# ============================================================================
|
|
@@ -440,8 +533,8 @@ if [ "$SHOW_AGILEFLOW" = "true" ] && [ -n "$AGILEFLOW_DISPLAY" ]; then
|
|
|
440
533
|
OUTPUT="${AGILEFLOW_DISPLAY}"
|
|
441
534
|
fi
|
|
442
535
|
|
|
443
|
-
# Model with subtle styling (if enabled)
|
|
444
|
-
if [ "$SHOW_MODEL" = "true" ]; then
|
|
536
|
+
# Model with subtle styling (if enabled and available)
|
|
537
|
+
if [ "$SHOW_MODEL" = "true" ] && [ -n "$MODEL_DISPLAY" ]; then
|
|
445
538
|
[ -n "$OUTPUT" ] && OUTPUT="${OUTPUT}${SEP}"
|
|
446
539
|
OUTPUT="${OUTPUT}${DIM}[${RESET}${BOLD}${MODEL_DISPLAY}${RESET}${DIM}]${RESET}"
|
|
447
540
|
fi
|