@liangjie559567/ultrapower 5.0.23 → 5.1.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/.claude-plugin/plugin.json +1 -1
- package/dist/__tests__/compatibility.test.js +6 -6
- package/dist/__tests__/doctor-conflicts.test.js +26 -24
- package/dist/__tests__/doctor-conflicts.test.js.map +1 -1
- package/dist/__tests__/hooks-json-paths.test.d.ts +9 -0
- package/dist/__tests__/hooks-json-paths.test.d.ts.map +1 -0
- package/dist/__tests__/hooks-json-paths.test.js +91 -0
- package/dist/__tests__/hooks-json-paths.test.js.map +1 -0
- package/dist/__tests__/live-data.test.js +9 -9
- package/dist/__tests__/rate-limit-wait/integration.test.js +68 -68
- package/dist/__tests__/rate-limit-wait/tmux-detector.test.js +33 -33
- package/dist/agents/prompt-sections/index.js +36 -36
- package/dist/cli/index.js +226 -226
- package/dist/features/rate-limit-wait/daemon.js +5 -5
- package/dist/hooks/autopilot/enforcement.js +19 -19
- package/dist/hooks/autopilot/validation.js +58 -58
- package/dist/hooks/axiom-boot/index.d.ts.map +1 -1
- package/dist/hooks/axiom-boot/index.js +2 -1
- package/dist/hooks/axiom-boot/index.js.map +1 -1
- package/dist/hooks/axiom-boot/storage.d.ts +1 -0
- package/dist/hooks/axiom-boot/storage.d.ts.map +1 -1
- package/dist/hooks/axiom-boot/storage.js +37 -1
- package/dist/hooks/axiom-boot/storage.js.map +1 -1
- package/dist/hooks/bridge.d.ts.map +1 -1
- package/dist/hooks/bridge.js +110 -100
- package/dist/hooks/bridge.js.map +1 -1
- package/dist/hooks/keyword-detector/__tests__/index.test.js +7 -7
- package/dist/hooks/learner/learning-queue.d.ts.map +1 -1
- package/dist/hooks/learner/learning-queue.js +2 -1
- package/dist/hooks/learner/learning-queue.js.map +1 -1
- package/dist/hooks/learner/session-reflector.d.ts +22 -0
- package/dist/hooks/learner/session-reflector.d.ts.map +1 -0
- package/dist/hooks/learner/session-reflector.js +52 -0
- package/dist/hooks/learner/session-reflector.js.map +1 -0
- package/dist/hooks/learner/usage-tracker.d.ts +45 -0
- package/dist/hooks/learner/usage-tracker.d.ts.map +1 -0
- package/dist/hooks/learner/usage-tracker.js +143 -0
- package/dist/hooks/learner/usage-tracker.js.map +1 -0
- package/dist/hooks/omc-orchestrator/index.js +20 -20
- package/dist/hooks/session-end/callbacks.js +10 -10
- package/dist/hooks/session-end/index.d.ts.map +1 -1
- package/dist/hooks/session-end/index.js +17 -0
- package/dist/hooks/session-end/index.js.map +1 -1
- package/dist/hooks/think-mode/__tests__/index.test.js +6 -6
- package/dist/installer/index.d.ts.map +1 -1
- package/dist/installer/index.js +6 -11
- package/dist/installer/index.js.map +1 -1
- package/dist/mcp/codex-core.js +26 -26
- package/dist/mcp/gemini-core.js +2 -2
- package/dist/skills/__tests__/ax-context-init.test.d.ts +2 -0
- package/dist/skills/__tests__/ax-context-init.test.d.ts.map +1 -0
- package/dist/skills/__tests__/ax-context-init.test.js +36 -0
- package/dist/skills/__tests__/ax-context-init.test.js.map +1 -0
- package/dist/team/mcp-team-bridge.js +26 -26
- package/docs/CLAUDE.md +1 -1
- package/package.json +1 -1
- package/scripts/persistent-mode.cjs +605 -605
- package/scripts/plugin-setup.mjs +44 -0
- package/skills/ax-context/SKILL.md +22 -10
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultrapower",
|
|
3
3
|
"description": "Disciplined multi-agent orchestration: workflow enforcement + parallel execution. Combines superpowers' TDD/debugging discipline with OMC's multi-agent execution capabilities.",
|
|
4
|
-
"version": "5.0
|
|
4
|
+
"version": "5.1.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Jesse Vincent & oh-my-claudecode contributors"
|
|
7
7
|
},
|
|
@@ -36,12 +36,12 @@ function createTestPlugin(name, manifest) {
|
|
|
36
36
|
// Create a sample skill
|
|
37
37
|
const sampleSkillDir = join(skillsDir, 'sample-skill');
|
|
38
38
|
mkdirSync(sampleSkillDir, { recursive: true });
|
|
39
|
-
writeFileSync(join(sampleSkillDir, 'SKILL.md'), `---
|
|
40
|
-
name: sample-skill
|
|
41
|
-
description: A sample skill for testing
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
This is a sample skill.
|
|
39
|
+
writeFileSync(join(sampleSkillDir, 'SKILL.md'), `---
|
|
40
|
+
name: sample-skill
|
|
41
|
+
description: A sample skill for testing
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
This is a sample skill.
|
|
45
45
|
`);
|
|
46
46
|
}
|
|
47
47
|
return pluginDir;
|
|
@@ -7,31 +7,33 @@
|
|
|
7
7
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
8
8
|
import { existsSync, mkdirSync, writeFileSync, rmSync } from 'fs';
|
|
9
9
|
import { join } from 'path';
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
const TEST_PROJECT_DIR = join(homedir(), '.claude-test-doctor-project');
|
|
13
|
-
const TEST_PROJECT_CLAUDE_DIR = join(TEST_PROJECT_DIR, '.claude');
|
|
10
|
+
import { tmpdir } from 'os';
|
|
11
|
+
import { randomBytes } from 'crypto';
|
|
14
12
|
// Mock getClaudeConfigDir before importing the module under test
|
|
13
|
+
// Use a factory so each test suite worker gets its own path via the closure
|
|
14
|
+
let testClaudeDir = '';
|
|
15
15
|
vi.mock('../utils/paths.js', () => ({
|
|
16
|
-
getClaudeConfigDir: () =>
|
|
16
|
+
getClaudeConfigDir: () => testClaudeDir,
|
|
17
17
|
}));
|
|
18
18
|
// Import after mock setup
|
|
19
19
|
import { checkHookConflicts, runConflictCheck } from '../cli/commands/doctor-conflicts.js';
|
|
20
20
|
describe('doctor-conflicts: hook ownership classification', () => {
|
|
21
21
|
let cwdSpy;
|
|
22
|
+
let testProjectDir;
|
|
23
|
+
let testProjectClaudeDir;
|
|
22
24
|
beforeEach(() => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
mkdirSync(
|
|
29
|
-
mkdirSync(
|
|
30
|
-
cwdSpy = vi.spyOn(process, 'cwd').mockReturnValue(
|
|
25
|
+
// Use unique directories per test to avoid cross-test filesystem races
|
|
26
|
+
const uid = randomBytes(6).toString('hex');
|
|
27
|
+
testClaudeDir = join(tmpdir(), `omc-test-doctor-${uid}`);
|
|
28
|
+
testProjectDir = join(tmpdir(), `omc-test-project-${uid}`);
|
|
29
|
+
testProjectClaudeDir = join(testProjectDir, '.claude');
|
|
30
|
+
mkdirSync(testClaudeDir, { recursive: true });
|
|
31
|
+
mkdirSync(testProjectClaudeDir, { recursive: true });
|
|
32
|
+
cwdSpy = vi.spyOn(process, 'cwd').mockReturnValue(testProjectDir);
|
|
31
33
|
});
|
|
32
34
|
afterEach(() => {
|
|
33
35
|
cwdSpy.mockRestore();
|
|
34
|
-
for (const dir of [
|
|
36
|
+
for (const dir of [testClaudeDir, testProjectDir]) {
|
|
35
37
|
if (existsSync(dir)) {
|
|
36
38
|
rmSync(dir, { recursive: true, force: true });
|
|
37
39
|
}
|
|
@@ -73,7 +75,7 @@ describe('doctor-conflicts: hook ownership classification', () => {
|
|
|
73
75
|
}],
|
|
74
76
|
},
|
|
75
77
|
};
|
|
76
|
-
writeFileSync(join(
|
|
78
|
+
writeFileSync(join(testClaudeDir, 'settings.json'), JSON.stringify(settings));
|
|
77
79
|
const conflicts = checkHookConflicts();
|
|
78
80
|
// All hooks should be classified as OMC-owned
|
|
79
81
|
expect(conflicts.length).toBeGreaterThan(0);
|
|
@@ -92,7 +94,7 @@ describe('doctor-conflicts: hook ownership classification', () => {
|
|
|
92
94
|
}],
|
|
93
95
|
},
|
|
94
96
|
};
|
|
95
|
-
writeFileSync(join(
|
|
97
|
+
writeFileSync(join(testClaudeDir, 'settings.json'), JSON.stringify(settings));
|
|
96
98
|
const conflicts = checkHookConflicts();
|
|
97
99
|
expect(conflicts).toHaveLength(1);
|
|
98
100
|
expect(conflicts[0].isOmc).toBe(true);
|
|
@@ -108,7 +110,7 @@ describe('doctor-conflicts: hook ownership classification', () => {
|
|
|
108
110
|
}],
|
|
109
111
|
},
|
|
110
112
|
};
|
|
111
|
-
writeFileSync(join(
|
|
113
|
+
writeFileSync(join(testClaudeDir, 'settings.json'), JSON.stringify(settings));
|
|
112
114
|
const conflicts = checkHookConflicts();
|
|
113
115
|
expect(conflicts).toHaveLength(1);
|
|
114
116
|
expect(conflicts[0].isOmc).toBe(false);
|
|
@@ -130,7 +132,7 @@ describe('doctor-conflicts: hook ownership classification', () => {
|
|
|
130
132
|
}],
|
|
131
133
|
},
|
|
132
134
|
};
|
|
133
|
-
writeFileSync(join(
|
|
135
|
+
writeFileSync(join(testClaudeDir, 'settings.json'), JSON.stringify(settings));
|
|
134
136
|
const conflicts = checkHookConflicts();
|
|
135
137
|
expect(conflicts).toHaveLength(2);
|
|
136
138
|
const preTool = conflicts.find(c => c.event === 'PreToolUse');
|
|
@@ -150,7 +152,7 @@ describe('doctor-conflicts: hook ownership classification', () => {
|
|
|
150
152
|
}],
|
|
151
153
|
},
|
|
152
154
|
};
|
|
153
|
-
writeFileSync(join(
|
|
155
|
+
writeFileSync(join(testClaudeDir, 'settings.json'), JSON.stringify(omcOnlySettings));
|
|
154
156
|
const omcReport = runConflictCheck();
|
|
155
157
|
// hasConflicts should be false when all hooks are OMC-owned
|
|
156
158
|
expect(omcReport.hookConflicts.every(h => h.isOmc)).toBe(true);
|
|
@@ -168,7 +170,7 @@ describe('doctor-conflicts: hook ownership classification', () => {
|
|
|
168
170
|
}],
|
|
169
171
|
},
|
|
170
172
|
};
|
|
171
|
-
writeFileSync(join(
|
|
173
|
+
writeFileSync(join(testProjectClaudeDir, 'settings.json'), JSON.stringify(projectSettings));
|
|
172
174
|
const conflicts = checkHookConflicts();
|
|
173
175
|
expect(conflicts).toHaveLength(1);
|
|
174
176
|
expect(conflicts[0].event).toBe('PreToolUse');
|
|
@@ -195,8 +197,8 @@ describe('doctor-conflicts: hook ownership classification', () => {
|
|
|
195
197
|
}],
|
|
196
198
|
},
|
|
197
199
|
};
|
|
198
|
-
writeFileSync(join(
|
|
199
|
-
writeFileSync(join(
|
|
200
|
+
writeFileSync(join(testClaudeDir, 'settings.json'), JSON.stringify(profileSettings));
|
|
201
|
+
writeFileSync(join(testProjectClaudeDir, 'settings.json'), JSON.stringify(projectSettings));
|
|
200
202
|
const conflicts = checkHookConflicts();
|
|
201
203
|
expect(conflicts).toHaveLength(2);
|
|
202
204
|
const sessionStart = conflicts.find(c => c.event === 'SessionStart');
|
|
@@ -216,8 +218,8 @@ describe('doctor-conflicts: hook ownership classification', () => {
|
|
|
216
218
|
},
|
|
217
219
|
};
|
|
218
220
|
// Same hook in both profile and project settings
|
|
219
|
-
writeFileSync(join(
|
|
220
|
-
writeFileSync(join(
|
|
221
|
+
writeFileSync(join(testClaudeDir, 'settings.json'), JSON.stringify(sharedHook));
|
|
222
|
+
writeFileSync(join(testProjectClaudeDir, 'settings.json'), JSON.stringify(sharedHook));
|
|
221
223
|
const conflicts = checkHookConflicts();
|
|
222
224
|
// Should appear only once, not twice
|
|
223
225
|
expect(conflicts).toHaveLength(1);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor-conflicts.test.js","sourceRoot":"","sources":["../../src/__tests__/doctor-conflicts.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,
|
|
1
|
+
{"version":3,"file":"doctor-conflicts.test.js","sourceRoot":"","sources":["../../src/__tests__/doctor-conflicts.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for hooks/hooks.json path correctness (issue: stop-continuation.mjs MODULE_NOT_FOUND)
|
|
3
|
+
*
|
|
4
|
+
* hooks/hooks.json uses ${CLAUDE_PLUGIN_ROOT}/templates/hooks/*.mjs
|
|
5
|
+
* The plugin cache must contain templates/hooks/ for these paths to resolve.
|
|
6
|
+
* plugin-setup.mjs must copy templates/hooks/ to the plugin cache on install.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=hooks-json-paths.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks-json-paths.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/hooks-json-paths.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for hooks/hooks.json path correctness (issue: stop-continuation.mjs MODULE_NOT_FOUND)
|
|
3
|
+
*
|
|
4
|
+
* hooks/hooks.json uses ${CLAUDE_PLUGIN_ROOT}/templates/hooks/*.mjs
|
|
5
|
+
* The plugin cache must contain templates/hooks/ for these paths to resolve.
|
|
6
|
+
* plugin-setup.mjs must copy templates/hooks/ to the plugin cache on install.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect } from 'vitest';
|
|
9
|
+
import { readFileSync, existsSync } from 'fs';
|
|
10
|
+
import { join, dirname } from 'path';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
const PROJECT_ROOT = join(__dirname, '..', '..');
|
|
15
|
+
function loadHooksJson() {
|
|
16
|
+
const hooksJsonPath = join(PROJECT_ROOT, 'hooks', 'hooks.json');
|
|
17
|
+
return JSON.parse(readFileSync(hooksJsonPath, 'utf-8'));
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Extract the relative path after ${CLAUDE_PLUGIN_ROOT}/ from a command string.
|
|
21
|
+
* e.g. "node ${CLAUDE_PLUGIN_ROOT}/templates/hooks/foo.mjs" -> "templates/hooks/foo.mjs"
|
|
22
|
+
*/
|
|
23
|
+
function extractPluginRelativePath(command) {
|
|
24
|
+
const match = command.match(/\$\{CLAUDE_PLUGIN_ROOT\}\/(.+\.mjs)/);
|
|
25
|
+
return match ? match[1] : null;
|
|
26
|
+
}
|
|
27
|
+
describe('hooks/hooks.json path correctness', () => {
|
|
28
|
+
it('all hook commands reference files that exist in the project source', () => {
|
|
29
|
+
const hooksJson = loadHooksJson();
|
|
30
|
+
const missing = [];
|
|
31
|
+
for (const [event, groups] of Object.entries(hooksJson.hooks)) {
|
|
32
|
+
for (const group of groups) {
|
|
33
|
+
for (const hook of group.hooks) {
|
|
34
|
+
if (hook.type !== 'command')
|
|
35
|
+
continue;
|
|
36
|
+
const relPath = extractPluginRelativePath(hook.command);
|
|
37
|
+
if (!relPath)
|
|
38
|
+
continue;
|
|
39
|
+
const fullPath = join(PROJECT_ROOT, relPath);
|
|
40
|
+
if (!existsSync(fullPath)) {
|
|
41
|
+
missing.push(`${event}: ${hook.command} -> ${fullPath} (NOT FOUND)`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
expect(missing).toEqual([]);
|
|
47
|
+
});
|
|
48
|
+
it('Stop hook command references templates/hooks/stop-continuation.mjs', () => {
|
|
49
|
+
const hooksJson = loadHooksJson();
|
|
50
|
+
const stopGroups = hooksJson.hooks['Stop'] ?? [];
|
|
51
|
+
expect(stopGroups.length).toBeGreaterThan(0);
|
|
52
|
+
for (const group of stopGroups) {
|
|
53
|
+
for (const hook of group.hooks) {
|
|
54
|
+
if (hook.type !== 'command')
|
|
55
|
+
continue;
|
|
56
|
+
expect(hook.command).toContain('templates/hooks/stop-continuation.mjs');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
it('all hook .mjs files referenced in hooks.json exist under templates/hooks/', () => {
|
|
61
|
+
const hooksJson = loadHooksJson();
|
|
62
|
+
const templatesHooksDir = join(PROJECT_ROOT, 'templates', 'hooks');
|
|
63
|
+
const missing = [];
|
|
64
|
+
for (const [event, groups] of Object.entries(hooksJson.hooks)) {
|
|
65
|
+
for (const group of groups) {
|
|
66
|
+
for (const hook of group.hooks) {
|
|
67
|
+
if (hook.type !== 'command')
|
|
68
|
+
continue;
|
|
69
|
+
const relPath = extractPluginRelativePath(hook.command);
|
|
70
|
+
if (!relPath)
|
|
71
|
+
continue;
|
|
72
|
+
if (!relPath.startsWith('templates/hooks/'))
|
|
73
|
+
continue;
|
|
74
|
+
const filename = relPath.replace('templates/hooks/', '');
|
|
75
|
+
const fullPath = join(templatesHooksDir, filename);
|
|
76
|
+
if (!existsSync(fullPath)) {
|
|
77
|
+
missing.push(`${event}: ${filename} (NOT FOUND in templates/hooks/)`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
expect(missing).toEqual([]);
|
|
83
|
+
});
|
|
84
|
+
it('plugin-setup.mjs copies templates/hooks/ to plugin cache', () => {
|
|
85
|
+
// Verify that plugin-setup.mjs contains logic to copy templates/hooks/ to cache
|
|
86
|
+
const setupScript = readFileSync(join(PROJECT_ROOT, 'scripts', 'plugin-setup.mjs'), 'utf-8');
|
|
87
|
+
// Must have cpSync call referencing srcTemplatesHooks (not just a comment)
|
|
88
|
+
expect(setupScript).toMatch(/cpSync\s*\(\s*srcTemplatesHooks/);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
//# sourceMappingURL=hooks-json-paths.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks-json-paths.test.js","sourceRoot":"","sources":["../../src/__tests__/hooks-json-paths.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AACtC,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAiBjD,SAAS,aAAa;IACpB,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;IAChE,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED;;;GAGG;AACH,SAAS,yBAAyB,CAAC,OAAe;IAChD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACnE,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACjC,CAAC;AAED,QAAQ,CAAC,mCAAmC,EAAE,GAAG,EAAE;IACjD,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,MAAM,SAAS,GAAG,aAAa,EAAE,CAAC;QAClC,MAAM,OAAO,GAAa,EAAE,CAAC;QAE7B,KAAK,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9D,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;oBAC/B,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;wBAAE,SAAS;oBACtC,MAAM,OAAO,GAAG,yBAAyB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;oBACxD,IAAI,CAAC,OAAO;wBAAE,SAAS;oBAEvB,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;oBAC7C,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;wBAC1B,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,KAAK,IAAI,CAAC,OAAO,OAAO,QAAQ,cAAc,CAAC,CAAC;oBACvE,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,MAAM,SAAS,GAAG,aAAa,EAAE,CAAC;QAClC,MAAM,UAAU,GAAG,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAEjD,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAE7C,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;YAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;gBAC/B,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;oBAAE,SAAS;gBACtC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,uCAAuC,CAAC,CAAC;YAC1E,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2EAA2E,EAAE,GAAG,EAAE;QACnF,MAAM,SAAS,GAAG,aAAa,EAAE,CAAC;QAClC,MAAM,iBAAiB,GAAG,IAAI,CAAC,YAAY,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;QACnE,MAAM,OAAO,GAAa,EAAE,CAAC;QAE7B,KAAK,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9D,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;oBAC/B,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;wBAAE,SAAS;oBACtC,MAAM,OAAO,GAAG,yBAAyB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;oBACxD,IAAI,CAAC,OAAO;wBAAE,SAAS;oBACvB,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,kBAAkB,CAAC;wBAAE,SAAS;oBAEtD,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;oBACzD,MAAM,QAAQ,GAAG,IAAI,CAAC,iBAAiB,EAAE,QAAQ,CAAC,CAAC;oBACnD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;wBAC1B,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,KAAK,QAAQ,kCAAkC,CAAC,CAAC;oBACxE,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,gFAAgF;QAChF,MAAM,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC,YAAY,EAAE,SAAS,EAAE,kBAAkB,CAAC,EAAE,OAAO,CAAC,CAAC;QAC7F,2EAA2E;QAC3E,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -253,15 +253,15 @@ describe('resolveLiveData - output formats', () => {
|
|
|
253
253
|
expect(result).toContain('format="table"');
|
|
254
254
|
});
|
|
255
255
|
it('!diff adds format="diff" with file/add/del stats', () => {
|
|
256
|
-
const diffOutput = `diff --git a/src/main.ts b/src/main.ts
|
|
257
|
-
--- a/src/main.ts
|
|
258
|
-
+++ b/src/main.ts
|
|
259
|
-
@@ -1,3 +1,5 @@
|
|
260
|
-
+import { foo } from 'bar';
|
|
261
|
-
+import { baz } from 'qux';
|
|
262
|
-
const x = 1;
|
|
263
|
-
-const y = 2;
|
|
264
|
-
const z = 3;
|
|
256
|
+
const diffOutput = `diff --git a/src/main.ts b/src/main.ts
|
|
257
|
+
--- a/src/main.ts
|
|
258
|
+
+++ b/src/main.ts
|
|
259
|
+
@@ -1,3 +1,5 @@
|
|
260
|
+
+import { foo } from 'bar';
|
|
261
|
+
+import { baz } from 'qux';
|
|
262
|
+
const x = 1;
|
|
263
|
+
-const y = 2;
|
|
264
|
+
const z = 3;
|
|
265
265
|
`;
|
|
266
266
|
mockedExecSync.mockReturnValue(diffOutput);
|
|
267
267
|
const result = resolveLiveData('!diff git diff');
|
|
@@ -95,20 +95,20 @@ describe('Rate Limit Wait Integration Tests', () => {
|
|
|
95
95
|
});
|
|
96
96
|
describe('Scenario: tmux pane analysis accuracy', () => {
|
|
97
97
|
it('should correctly identify Claude Code rate limit message', () => {
|
|
98
|
-
const realWorldContent = `
|
|
99
|
-
╭─────────────────────────────────────────────────────────────────╮
|
|
100
|
-
│ Claude Code │
|
|
101
|
-
╰─────────────────────────────────────────────────────────────────╯
|
|
102
|
-
|
|
103
|
-
You've reached your usage limit for the 5-hour period.
|
|
104
|
-
Your limit will reset at 3:45 PM.
|
|
105
|
-
|
|
106
|
-
What would you like to do?
|
|
107
|
-
|
|
108
|
-
[1] Wait and continue automatically when limit resets
|
|
109
|
-
[2] Switch to a different conversation
|
|
110
|
-
[3] Exit
|
|
111
|
-
|
|
98
|
+
const realWorldContent = `
|
|
99
|
+
╭─────────────────────────────────────────────────────────────────╮
|
|
100
|
+
│ Claude Code │
|
|
101
|
+
╰─────────────────────────────────────────────────────────────────╯
|
|
102
|
+
|
|
103
|
+
You've reached your usage limit for the 5-hour period.
|
|
104
|
+
Your limit will reset at 3:45 PM.
|
|
105
|
+
|
|
106
|
+
What would you like to do?
|
|
107
|
+
|
|
108
|
+
[1] Wait and continue automatically when limit resets
|
|
109
|
+
[2] Switch to a different conversation
|
|
110
|
+
[3] Exit
|
|
111
|
+
|
|
112
112
|
> `;
|
|
113
113
|
const result = analyzePaneContent(realWorldContent);
|
|
114
114
|
expect(result.hasClaudeCode).toBe(true);
|
|
@@ -118,18 +118,18 @@ What would you like to do?
|
|
|
118
118
|
expect(result.confidence).toBeGreaterThanOrEqual(0.8);
|
|
119
119
|
});
|
|
120
120
|
it('should correctly identify weekly rate limit message', () => {
|
|
121
|
-
const weeklyLimitContent = `
|
|
122
|
-
Claude Code v1.0.0
|
|
123
|
-
|
|
124
|
-
⚠️ Weekly usage limit reached
|
|
125
|
-
|
|
126
|
-
You've used your weekly allocation of tokens.
|
|
127
|
-
Limit resets on Monday at 12:00 AM UTC.
|
|
128
|
-
|
|
129
|
-
Options:
|
|
130
|
-
[1] Continue when limit resets
|
|
131
|
-
[2] Exit
|
|
132
|
-
|
|
121
|
+
const weeklyLimitContent = `
|
|
122
|
+
Claude Code v1.0.0
|
|
123
|
+
|
|
124
|
+
⚠️ Weekly usage limit reached
|
|
125
|
+
|
|
126
|
+
You've used your weekly allocation of tokens.
|
|
127
|
+
Limit resets on Monday at 12:00 AM UTC.
|
|
128
|
+
|
|
129
|
+
Options:
|
|
130
|
+
[1] Continue when limit resets
|
|
131
|
+
[2] Exit
|
|
132
|
+
|
|
133
133
|
Enter choice: `;
|
|
134
134
|
const result = analyzePaneContent(weeklyLimitContent);
|
|
135
135
|
expect(result.hasClaudeCode).toBe(true);
|
|
@@ -138,18 +138,18 @@ Enter choice: `;
|
|
|
138
138
|
expect(result.rateLimitType).toBe('weekly');
|
|
139
139
|
});
|
|
140
140
|
it('should NOT flag normal Claude Code output as blocked', () => {
|
|
141
|
-
const normalContent = `
|
|
142
|
-
Claude Code
|
|
143
|
-
|
|
144
|
-
> What would you like to build today?
|
|
145
|
-
|
|
146
|
-
I can help you with:
|
|
147
|
-
- Writing code
|
|
148
|
-
- Debugging
|
|
149
|
-
- Refactoring
|
|
150
|
-
- Documentation
|
|
151
|
-
|
|
152
|
-
Just describe what you need!
|
|
141
|
+
const normalContent = `
|
|
142
|
+
Claude Code
|
|
143
|
+
|
|
144
|
+
> What would you like to build today?
|
|
145
|
+
|
|
146
|
+
I can help you with:
|
|
147
|
+
- Writing code
|
|
148
|
+
- Debugging
|
|
149
|
+
- Refactoring
|
|
150
|
+
- Documentation
|
|
151
|
+
|
|
152
|
+
Just describe what you need!
|
|
153
153
|
`;
|
|
154
154
|
const result = analyzePaneContent(normalContent);
|
|
155
155
|
expect(result.hasClaudeCode).toBe(true);
|
|
@@ -157,12 +157,12 @@ Just describe what you need!
|
|
|
157
157
|
expect(result.isBlocked).toBe(false);
|
|
158
158
|
});
|
|
159
159
|
it('should NOT flag unrelated rate limit messages', () => {
|
|
160
|
-
const unrelatedContent = `
|
|
161
|
-
$ curl https://api.github.com/users/test
|
|
162
|
-
{
|
|
163
|
-
"message": "API rate limit exceeded for IP",
|
|
164
|
-
"documentation_url": "https://docs.github.com"
|
|
165
|
-
}
|
|
160
|
+
const unrelatedContent = `
|
|
161
|
+
$ curl https://api.github.com/users/test
|
|
162
|
+
{
|
|
163
|
+
"message": "API rate limit exceeded for IP",
|
|
164
|
+
"documentation_url": "https://docs.github.com"
|
|
165
|
+
}
|
|
166
166
|
$ `;
|
|
167
167
|
const result = analyzePaneContent(unrelatedContent);
|
|
168
168
|
expect(result.hasClaudeCode).toBe(false);
|
|
@@ -172,18 +172,18 @@ $ `;
|
|
|
172
172
|
it('should handle edge case: old rate limit message scrolled up', () => {
|
|
173
173
|
// Only last 15 lines should be analyzed
|
|
174
174
|
// Rate limit message from earlier should be ignored if not in recent content
|
|
175
|
-
const scrolledContent = `
|
|
176
|
-
User: fix the bug
|
|
177
|
-
Assistant: I'll fix that for you.
|
|
178
|
-
[Edit] src/main.ts
|
|
179
|
-
Done! The bug is fixed.
|
|
180
|
-
|
|
181
|
-
User: thanks
|
|
182
|
-
Assistant: You're welcome!
|
|
183
|
-
|
|
184
|
-
User: what else?
|
|
185
|
-
Assistant: I can help with more tasks.
|
|
186
|
-
|
|
175
|
+
const scrolledContent = `
|
|
176
|
+
User: fix the bug
|
|
177
|
+
Assistant: I'll fix that for you.
|
|
178
|
+
[Edit] src/main.ts
|
|
179
|
+
Done! The bug is fixed.
|
|
180
|
+
|
|
181
|
+
User: thanks
|
|
182
|
+
Assistant: You're welcome!
|
|
183
|
+
|
|
184
|
+
User: what else?
|
|
185
|
+
Assistant: I can help with more tasks.
|
|
186
|
+
|
|
187
187
|
> `;
|
|
188
188
|
const result = analyzePaneContent(scrolledContent);
|
|
189
189
|
expect(result.isBlocked).toBe(false);
|
|
@@ -311,25 +311,25 @@ Assistant: I can help with more tasks.
|
|
|
311
311
|
});
|
|
312
312
|
describe('Scenario: Confidence scoring', () => {
|
|
313
313
|
it('should give higher confidence for multiple indicators', () => {
|
|
314
|
-
const highConfidenceContent = `
|
|
315
|
-
Claude Code
|
|
316
|
-
Rate limit reached
|
|
317
|
-
5-hour usage limit
|
|
318
|
-
[1] Continue
|
|
319
|
-
[2] Exit
|
|
314
|
+
const highConfidenceContent = `
|
|
315
|
+
Claude Code
|
|
316
|
+
Rate limit reached
|
|
317
|
+
5-hour usage limit
|
|
318
|
+
[1] Continue
|
|
319
|
+
[2] Exit
|
|
320
320
|
`;
|
|
321
|
-
const lowConfidenceContent = `
|
|
322
|
-
Claude
|
|
323
|
-
rate limit
|
|
321
|
+
const lowConfidenceContent = `
|
|
322
|
+
Claude
|
|
323
|
+
rate limit
|
|
324
324
|
`;
|
|
325
325
|
const highResult = analyzePaneContent(highConfidenceContent);
|
|
326
326
|
const lowResult = analyzePaneContent(lowConfidenceContent);
|
|
327
327
|
expect(highResult.confidence).toBeGreaterThan(lowResult.confidence);
|
|
328
328
|
});
|
|
329
329
|
it('should require minimum confidence to mark as blocked', () => {
|
|
330
|
-
const ambiguousContent = `
|
|
331
|
-
some claude reference
|
|
332
|
-
limit mentioned
|
|
330
|
+
const ambiguousContent = `
|
|
331
|
+
some claude reference
|
|
332
|
+
limit mentioned
|
|
333
333
|
`;
|
|
334
334
|
const result = analyzePaneContent(ambiguousContent);
|
|
335
335
|
// Even if some patterns match, confidence should be too low
|
|
@@ -15,11 +15,11 @@ describe('tmux-detector', () => {
|
|
|
15
15
|
});
|
|
16
16
|
describe('analyzePaneContent', () => {
|
|
17
17
|
it('should detect rate limit messages with Claude Code context', () => {
|
|
18
|
-
const content = `
|
|
19
|
-
Claude Code v1.2.3
|
|
20
|
-
You've reached your rate limit. Please wait for the limit to reset.
|
|
21
|
-
[1] Continue when ready
|
|
22
|
-
[2] Exit
|
|
18
|
+
const content = `
|
|
19
|
+
Claude Code v1.2.3
|
|
20
|
+
You've reached your rate limit. Please wait for the limit to reset.
|
|
21
|
+
[1] Continue when ready
|
|
22
|
+
[2] Exit
|
|
23
23
|
`;
|
|
24
24
|
const result = analyzePaneContent(content);
|
|
25
25
|
expect(result.hasClaudeCode).toBe(true);
|
|
@@ -28,38 +28,38 @@ describe('tmux-detector', () => {
|
|
|
28
28
|
expect(result.confidence).toBeGreaterThan(0.5);
|
|
29
29
|
});
|
|
30
30
|
it('should detect 5-hour rate limit', () => {
|
|
31
|
-
const content = `
|
|
32
|
-
Claude Code assistant
|
|
33
|
-
5-hour usage limit reached
|
|
34
|
-
[1] Wait for reset
|
|
31
|
+
const content = `
|
|
32
|
+
Claude Code assistant
|
|
33
|
+
5-hour usage limit reached
|
|
34
|
+
[1] Wait for reset
|
|
35
35
|
`;
|
|
36
36
|
const result = analyzePaneContent(content);
|
|
37
37
|
expect(result.hasRateLimitMessage).toBe(true);
|
|
38
38
|
expect(result.rateLimitType).toBe('five_hour');
|
|
39
39
|
});
|
|
40
40
|
it('should detect weekly rate limit', () => {
|
|
41
|
-
const content = `
|
|
42
|
-
Claude Code
|
|
43
|
-
Weekly usage quota exceeded
|
|
44
|
-
Please try again later
|
|
41
|
+
const content = `
|
|
42
|
+
Claude Code
|
|
43
|
+
Weekly usage quota exceeded
|
|
44
|
+
Please try again later
|
|
45
45
|
`;
|
|
46
46
|
const result = analyzePaneContent(content);
|
|
47
47
|
expect(result.hasRateLimitMessage).toBe(true);
|
|
48
48
|
expect(result.rateLimitType).toBe('weekly');
|
|
49
49
|
});
|
|
50
50
|
it('should not flag content without Claude Code indicators', () => {
|
|
51
|
-
const content = `
|
|
52
|
-
vim test.js
|
|
53
|
-
Hello World
|
|
51
|
+
const content = `
|
|
52
|
+
vim test.js
|
|
53
|
+
Hello World
|
|
54
54
|
`;
|
|
55
55
|
const result = analyzePaneContent(content);
|
|
56
56
|
expect(result.hasClaudeCode).toBe(false);
|
|
57
57
|
expect(result.isBlocked).toBe(false);
|
|
58
58
|
});
|
|
59
59
|
it('should not flag rate limit messages in non-Claude contexts', () => {
|
|
60
|
-
const content = `
|
|
61
|
-
curl api.example.com
|
|
62
|
-
Error: rate limit exceeded
|
|
60
|
+
const content = `
|
|
61
|
+
curl api.example.com
|
|
62
|
+
Error: rate limit exceeded
|
|
63
63
|
`;
|
|
64
64
|
const result = analyzePaneContent(content);
|
|
65
65
|
expect(result.hasClaudeCode).toBe(false);
|
|
@@ -74,25 +74,25 @@ describe('tmux-detector', () => {
|
|
|
74
74
|
expect(result.confidence).toBe(0);
|
|
75
75
|
});
|
|
76
76
|
it('should detect waiting patterns', () => {
|
|
77
|
-
const content = `
|
|
78
|
-
Claude assistant
|
|
79
|
-
Rate limit reached
|
|
80
|
-
[1] Continue
|
|
81
|
-
[2] Cancel
|
|
77
|
+
const content = `
|
|
78
|
+
Claude assistant
|
|
79
|
+
Rate limit reached
|
|
80
|
+
[1] Continue
|
|
81
|
+
[2] Cancel
|
|
82
82
|
`;
|
|
83
83
|
const result = analyzePaneContent(content);
|
|
84
84
|
expect(result.confidence).toBeGreaterThan(0.6);
|
|
85
85
|
});
|
|
86
86
|
it('should detect Claude limit screen phrasing: hit your limit + numeric menu', () => {
|
|
87
|
-
const content = `
|
|
88
|
-
Claude Code
|
|
89
|
-
You've hit your limit · resets Feb 17 at 2pm (Asia/Seoul)
|
|
90
|
-
What do you want to do?
|
|
91
|
-
|
|
92
|
-
❯ 1. Stop and wait for limit to reset
|
|
93
|
-
2. Request more
|
|
94
|
-
|
|
95
|
-
Enter to confirm · Esc to cancel
|
|
87
|
+
const content = `
|
|
88
|
+
Claude Code
|
|
89
|
+
You've hit your limit · resets Feb 17 at 2pm (Asia/Seoul)
|
|
90
|
+
What do you want to do?
|
|
91
|
+
|
|
92
|
+
❯ 1. Stop and wait for limit to reset
|
|
93
|
+
2. Request more
|
|
94
|
+
|
|
95
|
+
Enter to confirm · Esc to cancel
|
|
96
96
|
`;
|
|
97
97
|
const result = analyzePaneContent(content);
|
|
98
98
|
expect(result.hasClaudeCode).toBe(true);
|