claude-token-saver 3.44.0 → 3.55.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/LICENSE +202 -21
- package/NOTICE +35 -0
- package/README.ko.md +150 -48
- package/README.md +205 -192
- package/bin/cli.js +108 -39
- package/docs/CODEX.ko.md +184 -0
- package/docs/CODEX.md +497 -0
- package/examples/statusline-command.ps1 +11 -5
- package/examples/statusline-command.sh +9 -4
- package/package.json +20 -6
- package/presets/delegation/bounds.md +29 -0
- package/presets/model-rules.json +50 -9
- package/presets/ratchet-rules.json +4 -1
- package/src/agent.js +33 -0
- package/src/caps-cache.js +6 -2
- package/src/changelog.js +70 -0
- package/src/cli-name.js +25 -0
- package/src/codex-brief.js +89 -0
- package/src/codex-budget.js +73 -0
- package/src/codex-cache-policy.js +203 -0
- package/src/codex-cache.js +46 -0
- package/src/codex-delegation.js +154 -0
- package/src/codex-doc2md-ledger.js +45 -0
- package/src/codex-doc2md.js +99 -0
- package/src/codex-doctor.js +53 -0
- package/src/codex-harness.js +200 -0
- package/src/codex-hooks.js +138 -0
- package/src/codex-installer.js +79 -0
- package/src/codex-ledger.js +143 -0
- package/src/codex-panel-launcher.js +48 -0
- package/src/codex-panel-runner.js +74 -0
- package/src/codex-panel-session.js +35 -0
- package/src/codex-panel-setup.js +98 -0
- package/src/codex-panel-shell.js +51 -0
- package/src/codex-panel-state.js +40 -0
- package/src/codex-panel.js +448 -0
- package/src/codex-parser.js +397 -0
- package/src/codex-route-scan.js +171 -0
- package/src/commands/brief.js +24 -1
- package/src/commands/codex-delegate.js +59 -0
- package/src/commands/codex.js +579 -0
- package/src/commands/delegate.js +133 -0
- package/src/commands/harness.js +16 -3
- package/src/commands/install.js +109 -12
- package/src/commands/korean.js +2 -2
- package/src/commands/mode.js +22 -2
- package/src/commands/profile-map.js +164 -0
- package/src/commands/route-scan.js +74 -7
- package/src/commands/seed.js +16 -12
- package/src/commands/uninstall.js +28 -4
- package/src/commands/upgrade.js +13 -0
- package/src/compact-window.js +7 -1
- package/src/config.js +22 -3
- package/src/cost.js +34 -0
- package/src/delegation-guard.js +278 -0
- package/src/doc2md-ledger.cjs +3 -3
- package/src/doc2md.cjs +72 -28
- package/src/effort-palette.js +145 -0
- package/src/formatters/statusline.js +352 -123
- package/src/formatters/table.js +5 -4
- package/src/gateway-auth.js +86 -0
- package/src/glyphs.js +103 -0
- package/src/handoff.js +27 -7
- package/src/harness-analyzer.cjs +7 -2
- package/src/harness-templates.js +33 -1
- package/src/harness.js +51 -17
- package/src/hook-manager.js +42 -14
- package/src/hook.cjs +34 -2
- package/src/installer.js +300 -16
- package/src/litellm-budget.js +38 -67
- package/src/litellm-models.js +264 -0
- package/src/model-alias.js +170 -16
- package/src/model-rules.js +53 -17
- package/src/parser.js +89 -1
- package/src/preset-ratchet.js +133 -0
- package/src/route-inject.js +161 -0
- package/src/route-scan.js +68 -8
- package/src/seed-rules.js +117 -55
- package/src/session-paths.js +242 -0
- package/src/statusline-mode.js +106 -0
- package/src/stdin-payload.js +40 -0
- package/src/ultracode.js +202 -0
- package/src/update-check.js +217 -3
- package/src/window-labels.js +19 -8
package/bin/cli.js
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* npx sprag --format csv # CSV output
|
|
11
11
|
* npx sprag --project myproj # filter by project
|
|
12
12
|
* npx sprag route-scan # detect recurring easy work → haiku-delegation candidates
|
|
13
|
-
* npx sprag install # set up
|
|
14
|
-
* npx sprag install --
|
|
13
|
+
* npx sprag install # set up everything, presets and Codex included, without asking
|
|
14
|
+
* npx sprag install --manual # confirm each optional step
|
|
15
15
|
* npx sprag --install-hook # install PostToolUse hook
|
|
16
16
|
* npx sprag --uninstall-hook # remove hook
|
|
17
17
|
* npx claude-token-saver --hook-run # internal: called by hook
|
|
@@ -34,9 +34,13 @@ import {
|
|
|
34
34
|
readStdinJson,
|
|
35
35
|
extractCaps,
|
|
36
36
|
extractContextUsage,
|
|
37
|
+
extractEffort,
|
|
37
38
|
extractModel,
|
|
39
|
+
extractTranscriptPath,
|
|
38
40
|
} from '../src/stdin-payload.js';
|
|
39
41
|
|
|
42
|
+
import { effortChipLevel } from '../src/ultracode.js';
|
|
43
|
+
|
|
40
44
|
import { parseAllSessions, getLastUserMessageTime } from '../src/parser.js';
|
|
41
45
|
import {
|
|
42
46
|
dailyTrend,
|
|
@@ -48,13 +52,18 @@ import {
|
|
|
48
52
|
sessionMetrics,
|
|
49
53
|
diagnoseSession,
|
|
50
54
|
} from '../src/stats.js';
|
|
51
|
-
import {
|
|
55
|
+
import { estimateCostAcross } from '../src/cost.js';
|
|
52
56
|
import { chipForIssues } from '../src/advice.js';
|
|
53
57
|
import { debug } from '../src/debug.js';
|
|
54
58
|
import { createArgs } from '../src/cli-args.js';
|
|
55
59
|
import { updateStatus, maybeSpawnUpdateCheck } from '../src/update-check.js';
|
|
60
|
+
import { selectAgent } from '../src/agent.js';
|
|
56
61
|
|
|
57
|
-
const
|
|
62
|
+
const selection = (() => {
|
|
63
|
+
try { return selectAgent(process.argv.slice(2)); }
|
|
64
|
+
catch (e) { console.error(`Error: ${e.message}`); process.exit(1); }
|
|
65
|
+
})();
|
|
66
|
+
const { args, agent } = selection;
|
|
58
67
|
|
|
59
68
|
const PKG_VERSION = (() => {
|
|
60
69
|
try {
|
|
@@ -86,11 +95,11 @@ function readUpdateChip() {
|
|
|
86
95
|
// Subcommands this build knows how to run. Used only by the guard below.
|
|
87
96
|
const KNOWN_SUBCOMMANDS = new Set([
|
|
88
97
|
'last', 'brief', 'history', 'handoff', 'install', 'uninstall', 'mode', 'korean', 'cohesion',
|
|
89
|
-
'doc2md', 'harness', 'route-scan', 'compact-window', 'update-check', 'upgrade',
|
|
90
|
-
'seed', 'litellm-budget', 'feedback',
|
|
98
|
+
'doc2md', 'delegate', 'harness', 'route-scan', 'compact-window', 'update-check', 'upgrade',
|
|
99
|
+
'seed', 'litellm-budget', 'profile-map', 'feedback',
|
|
91
100
|
]);
|
|
92
101
|
|
|
93
|
-
const USAGE = `sprag — Claude Code
|
|
102
|
+
const USAGE = `sprag — Claude Code and Codex agent harness
|
|
94
103
|
|
|
95
104
|
Usage:
|
|
96
105
|
sprag default report (last 30 days)
|
|
@@ -98,9 +107,24 @@ Usage:
|
|
|
98
107
|
sprag --format json JSON output
|
|
99
108
|
sprag --format csv CSV output
|
|
100
109
|
sprag --project myproj filter by project
|
|
110
|
+
sprag --agent codex Codex token report (table/json/csv)
|
|
111
|
+
sprag --statusline --agent codex one-shot compact terminal status
|
|
112
|
+
sprag doctor --agent codex diagnose hooks, config, harness, and session logs
|
|
113
|
+
sprag capabilities --agent codex full support matrix and limitations
|
|
114
|
+
sprag panel --agent codex live companion panel in a separate terminal
|
|
115
|
+
sprag panel auto on --agent codex open panel on Codex startup/resume (macOS)
|
|
116
|
+
sprag panel --open --agent codex open/reuse a macOS Terminal panel now
|
|
117
|
+
sprag panel run --agent codex -- run Codex above a compact panel (tmux)
|
|
118
|
+
sprag panel doctor --agent codex check registration, hook execution, and rendering
|
|
119
|
+
--project <path> / --session <id> / --interval 2 / --once / --no-color
|
|
120
|
+
sprag install --agent codex install Codex hooks + AGENTS.md harness
|
|
121
|
+
sprag harness init --agent codex initialize this project's Codex harness
|
|
122
|
+
sprag uninstall --agent codex remove the global Codex integration
|
|
123
|
+
--agent claude|codex (default: claude; accepted before or after subcommands)
|
|
101
124
|
sprag route-scan detect recurring easy work → delegation candidates
|
|
102
|
-
sprag
|
|
103
|
-
sprag install
|
|
125
|
+
sprag profile-map show/refresh the gateway model map (LiteLLM /model/info)
|
|
126
|
+
sprag install set up everything (presets, and Codex when present) without asking
|
|
127
|
+
sprag install --manual confirm each optional step
|
|
104
128
|
sprag uninstall remove everything install added
|
|
105
129
|
sprag harness check score the harness setup in CLAUDE.md
|
|
106
130
|
sprag harness analyze run the harness transcript analysis manually
|
|
@@ -119,6 +143,10 @@ Bug reports & feature requests: https://github.com/rootstudioyaml/sprag/issues
|
|
|
119
143
|
`;
|
|
120
144
|
|
|
121
145
|
async function main() {
|
|
146
|
+
if (agent === 'codex') {
|
|
147
|
+
if (hasFlag('--version') || hasFlag('-v')) { console.log(PKG_VERSION); return; }
|
|
148
|
+
return (await import('../src/commands/codex.js')).run({ args, version: PKG_VERSION });
|
|
149
|
+
}
|
|
122
150
|
// Help must never fall through to the default report — that runs a full
|
|
123
151
|
// 30-day scan, which is the opposite of what someone asking for help wants.
|
|
124
152
|
if (hasFlag('--help') || hasFlag('-h') || args[0] === 'help') {
|
|
@@ -242,6 +270,14 @@ async function main() {
|
|
|
242
270
|
return (await import('../src/commands/doc2md.js')).run({ args, hasFlag });
|
|
243
271
|
}
|
|
244
272
|
|
|
273
|
+
// Subcommand: delegate — rewrite Task/Agent delegation prompts so a
|
|
274
|
+
// subagent gets bounds, Korean guidance when the prompt needs it, and paths
|
|
275
|
+
// this session already read, instead of starting from nothing.
|
|
276
|
+
// sprag delegate on | off | --hook
|
|
277
|
+
if (args[0] === 'delegate') {
|
|
278
|
+
return (await import('../src/commands/delegate.js')).run({ args, hasFlag });
|
|
279
|
+
}
|
|
280
|
+
|
|
245
281
|
// Subcommand: seed — register the bundled starter rules (model-fitting
|
|
246
282
|
// presets + curated ratchet rules), one answer at a time.
|
|
247
283
|
// sprag seed | seed accept <id> --global|--project | seed skip <id>
|
|
@@ -296,12 +332,22 @@ async function main() {
|
|
|
296
332
|
// sprag litellm-budget --json # 캐시 원본 JSON 출력
|
|
297
333
|
// sprag litellm-budget --refresh # 지금 프록시에 물어봄 (detached 자식이 사용)
|
|
298
334
|
if (args[0] === 'litellm-budget') {
|
|
299
|
-
const { gatewayBase, readBudgetState, refreshBudgetState, formatBudgetReport } =
|
|
335
|
+
const { gatewayBase, readBudgetState, refreshBudgetState, formatBudgetReport, resolveKey } =
|
|
300
336
|
await import('../src/litellm-budget.js');
|
|
301
337
|
const quiet = hasFlag('--quiet');
|
|
302
338
|
if (hasFlag('--refresh')) {
|
|
303
339
|
try {
|
|
304
|
-
|
|
340
|
+
// apiKeyHelper 가 토큰 수명(TTL)을 소유합니다. 헬퍼를 한 번만 불러 그
|
|
341
|
+
// 키를 예산 갱신과 게이트웨이 모델맵 갱신 양쪽에 재사용합니다. 갱신
|
|
342
|
+
// 경로마다 resolveKey() 를 다시 부르면 사본을 만드는 셈이 되고, 헬퍼
|
|
343
|
+
// 호출 횟수도 그만큼 늘어납니다.
|
|
344
|
+
const key = resolveKey();
|
|
345
|
+
let next = null;
|
|
346
|
+
if (key) {
|
|
347
|
+
next = await refreshBudgetState(process.env, fetch, key);
|
|
348
|
+
const { maybeRefreshGatewayModelMap } = await import('../src/litellm-models.js');
|
|
349
|
+
await maybeRefreshGatewayModelMap({ base: gatewayBase(), key });
|
|
350
|
+
}
|
|
305
351
|
if (!quiet) console.log(JSON.stringify(next, null, 2));
|
|
306
352
|
} catch (e) {
|
|
307
353
|
debug('litellm-budget:refresh', e);
|
|
@@ -325,10 +371,24 @@ async function main() {
|
|
|
325
371
|
console.log('예산 캐시가 비어 있습니다. `sprag litellm-budget --refresh` 로 먼저 조회하십시오.');
|
|
326
372
|
return;
|
|
327
373
|
}
|
|
328
|
-
|
|
374
|
+
// Same resolver the statusline uses: this report draws a gauge too, and a
|
|
375
|
+
// glyph the terminal's font lacks garbles here exactly as it does there.
|
|
376
|
+
const { resolveLabelMode } = await import('../src/statusline-mode.js');
|
|
377
|
+
const { statuslineDefaults } = await import('../src/config.js');
|
|
378
|
+
const { mode: labelMode } = resolveLabelMode({ hasFlag, cfg: statuslineDefaults() });
|
|
379
|
+
for (const line of formatBudgetReport(state, new Date(), labelMode)) console.log(line);
|
|
329
380
|
return;
|
|
330
381
|
}
|
|
331
382
|
|
|
383
|
+
// Subcommand: profile-map · LiteLLM 게이트웨이의 프로파일 ID → 모델 별칭
|
|
384
|
+
// 매핑을 보여 주거나(GET /model/info) 갱신합니다.
|
|
385
|
+
// sprag profile-map # 캐시된 게이트웨이 모델맵 출력
|
|
386
|
+
// sprag profile-map --json # 캐시 원본 JSON 출력
|
|
387
|
+
// sprag profile-map --refresh # 지금 프록시에 물어봄 (사람이 직접 실행)
|
|
388
|
+
if (args[0] === 'profile-map') {
|
|
389
|
+
return (await import('../src/commands/profile-map.js')).run({ args, hasFlag });
|
|
390
|
+
}
|
|
391
|
+
|
|
332
392
|
// Subcommand: upgrade — run the install command that matches how this copy
|
|
333
393
|
// got here, then confirm the new version.
|
|
334
394
|
// sprag upgrade # install the latest release
|
|
@@ -397,9 +457,10 @@ async function main() {
|
|
|
397
457
|
process.exit(1);
|
|
398
458
|
}
|
|
399
459
|
const { formatReport } = await import('../src/formatters/statusline.js');
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
460
|
+
// Same resolver as the real statusline path: a demo that renders emoji
|
|
461
|
+
// where the real line would not is worse than no demo at all.
|
|
462
|
+
const { resolveLabelMode } = await import('../src/statusline-mode.js');
|
|
463
|
+
const { mode: labelMode } = resolveLabelMode({ hasFlag, cfg });
|
|
403
464
|
const isVerbose = hasFlag('--verbose')
|
|
404
465
|
? true
|
|
405
466
|
: (hasFlag('--no-verbose') || hasFlag('--compact') ? false : cfg.verbose);
|
|
@@ -413,7 +474,7 @@ async function main() {
|
|
|
413
474
|
color: colorOk,
|
|
414
475
|
verbose: isVerbose,
|
|
415
476
|
timer: showTimer,
|
|
416
|
-
mode:
|
|
477
|
+
mode: labelMode,
|
|
417
478
|
segments,
|
|
418
479
|
singleLine: hasFlag('--single-line'),
|
|
419
480
|
});
|
|
@@ -480,12 +541,17 @@ async function main() {
|
|
|
480
541
|
const { statuslineDefaults } = await import('../src/config.js');
|
|
481
542
|
const cfg = statuslineDefaults();
|
|
482
543
|
const colorOk = !hasFlag('--no-color') && !process.env.NO_COLOR && cfg.color;
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
544
|
+
// Third caller of the same decision — the no-session line has to agree
|
|
545
|
+
// with the populated one, or IntelliJ users watch icons appear and
|
|
546
|
+
// disappear as the analysis window empties and fills.
|
|
547
|
+
const { resolveLabelMode } = await import('../src/statusline-mode.js');
|
|
548
|
+
const { mode: labelMode } = resolveLabelMode({ hasFlag, cfg });
|
|
486
549
|
const stdinJson = readStdinJson();
|
|
487
550
|
const caps = extractCaps(stdinJson);
|
|
488
551
|
const model = extractModel(stdinJson);
|
|
552
|
+
// Resolved the same way as on the populated line below, so the chip does
|
|
553
|
+
// not change its mind about the level when the analysis window empties.
|
|
554
|
+
const effort = effortChipLevel(extractEffort(stdinJson), extractTranscriptPath(stdinJson));
|
|
489
555
|
if (caps || model) {
|
|
490
556
|
try {
|
|
491
557
|
const { persistSnapshot } = await import('../src/caps-cache.js');
|
|
@@ -493,8 +559,8 @@ async function main() {
|
|
|
493
559
|
} catch (e) { debug('caps-cache:persist', e); }
|
|
494
560
|
}
|
|
495
561
|
console.log(formatNoSession(
|
|
496
|
-
{ caps, model, windowLabel, version: PKG_VERSION, update: readUpdateChip() },
|
|
497
|
-
{ color: colorOk, mode:
|
|
562
|
+
{ caps, model, effort, windowLabel, version: PKG_VERSION, update: readUpdateChip() },
|
|
563
|
+
{ color: colorOk, mode: labelMode },
|
|
498
564
|
));
|
|
499
565
|
return;
|
|
500
566
|
}
|
|
@@ -526,7 +592,7 @@ async function main() {
|
|
|
526
592
|
const ttl = ttlBreakdown(sessions);
|
|
527
593
|
const sum = summary(sessions);
|
|
528
594
|
const anomalies = detectAnomalies(trend);
|
|
529
|
-
const cost =
|
|
595
|
+
const cost = estimateCostAcross(sessions);
|
|
530
596
|
const spikeReport = detectSpikes(sessions, { recentHours: 24, multiplier: 3 });
|
|
531
597
|
const contextWindow = detectContextWindow(sessions, { recentHours: 24 });
|
|
532
598
|
|
|
@@ -539,6 +605,18 @@ async function main() {
|
|
|
539
605
|
const stdinJson = readStdinJson();
|
|
540
606
|
let caps = extractCaps(stdinJson);
|
|
541
607
|
let model = extractModel(stdinJson);
|
|
608
|
+
// Not persisted to the caps cache the way caps/model are: the cache exists so
|
|
609
|
+
// the table view (which pipes no stdin) can still show them, and a level the
|
|
610
|
+
// user has since changed would be worse there than no chip at all. The effort
|
|
611
|
+
// chip is a live-statusline reading only.
|
|
612
|
+
//
|
|
613
|
+
// `ultracode` is the one level the payload cannot name — Claude Code raises it
|
|
614
|
+
// to xhigh and reports that — so the transcript settles which of the two this
|
|
615
|
+
// session is on. Only an xhigh payload can resolve to ultracode, so a session
|
|
616
|
+
// on any other level pays nothing for the check (see src/ultracode.js).
|
|
617
|
+
const effort = isStatusline
|
|
618
|
+
? effortChipLevel(extractEffort(stdinJson), extractTranscriptPath(stdinJson))
|
|
619
|
+
: extractEffort(stdinJson);
|
|
542
620
|
const ctxLive = extractContextUsage(stdinJson);
|
|
543
621
|
if (isStatusline && (caps || model)) {
|
|
544
622
|
try {
|
|
@@ -730,6 +808,7 @@ async function main() {
|
|
|
730
808
|
spikeChip,
|
|
731
809
|
caps,
|
|
732
810
|
model,
|
|
811
|
+
effort,
|
|
733
812
|
delegationSaved,
|
|
734
813
|
delegationTotals,
|
|
735
814
|
doc2mdTotals,
|
|
@@ -749,21 +828,11 @@ async function main() {
|
|
|
749
828
|
const { statuslineDefaults } = await import('../src/config.js');
|
|
750
829
|
const cfg = statuslineDefaults();
|
|
751
830
|
|
|
752
|
-
//
|
|
753
|
-
//
|
|
754
|
-
//
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
// Force text mode unconditionally inside IntelliJ — even past an explicit
|
|
758
|
-
// `--icon` flag, since wrappers commonly hardcode `--icon` and the user
|
|
759
|
-
// can't easily edit them; icon mode is just broken there.
|
|
760
|
-
const isIntelliJ = process.env.TERMINAL_EMULATOR === 'JetBrains-JediTerm';
|
|
761
|
-
// CLI flags take precedence; otherwise fall back to persisted config.
|
|
762
|
-
const isIcon = isIntelliJ
|
|
763
|
-
? false
|
|
764
|
-
: (hasFlag('--icon')
|
|
765
|
-
? true
|
|
766
|
-
: (hasFlag('--no-icon') || hasFlag('--text') ? false : cfg.icon));
|
|
831
|
+
// Icon vs text is decided in one module shared with the `--demo` path, so
|
|
832
|
+
// the two cannot drift apart again. It also owns the IntelliJ downgrade and
|
|
833
|
+
// the escape hatch out of it (SPRAG_ICON=force / `sprag mode icon-force`).
|
|
834
|
+
const { resolveLabelMode } = await import('../src/statusline-mode.js');
|
|
835
|
+
const { mode: labelMode } = resolveLabelMode({ hasFlag, cfg });
|
|
767
836
|
const isVerbose = hasFlag('--verbose')
|
|
768
837
|
? true
|
|
769
838
|
: (hasFlag('--no-verbose') || hasFlag('--compact') ? false : cfg.verbose);
|
|
@@ -779,7 +848,7 @@ async function main() {
|
|
|
779
848
|
color: colorOk,
|
|
780
849
|
verbose: isVerbose,
|
|
781
850
|
timer: showTimer,
|
|
782
|
-
mode:
|
|
851
|
+
mode: labelMode,
|
|
783
852
|
segments,
|
|
784
853
|
// macOS builds of Claude Code have rendered only the first line of a
|
|
785
854
|
// multi-line statusline in some versions (anthropics/claude-code#35176)
|
|
@@ -796,7 +865,7 @@ async function main() {
|
|
|
796
865
|
|
|
797
866
|
main().catch((err) => {
|
|
798
867
|
// Statusline mode must never spam multi-line errors (called every ~300ms)
|
|
799
|
-
const isStatusline = process.argv.includes('--statusline') || process.argv.includes('statusline');
|
|
868
|
+
const isStatusline = agent === 'claude' && (process.argv.includes('--statusline') || process.argv.includes('statusline'));
|
|
800
869
|
if (isStatusline) {
|
|
801
870
|
const colorOk = !process.argv.includes('--no-color') && !process.env.NO_COLOR;
|
|
802
871
|
const red = colorOk ? '\x1b[31m' : '';
|
package/docs/CODEX.ko.md
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Codex 연동
|
|
2
|
+
|
|
3
|
+
[← README](../README.ko.md) · [English](./CODEX.md)
|
|
4
|
+
|
|
5
|
+
Sprag는 Codex의 기본 훅과 `AGENTS.md`를 사용합니다. Claude Code와 함께 설치할 수 있으며,
|
|
6
|
+
명령에 `--agent codex`를 붙이면 Codex 기록과 설정을 선택합니다. 이 옵션은 전역 기본값을 바꾸지 않습니다.
|
|
7
|
+
|
|
8
|
+
## 설치
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
npm i -g sprag-cli
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Codex가 설치되어 있으면 패키지 설치가 Claude Code 연동, 추천 룰과 함께 Codex도 설정합니다.
|
|
15
|
+
설치 중에 묻는 것은 없으며, 단계마다 확인하려면 `sprag install --manual`을 쓰십시오.
|
|
16
|
+
Claude Code 설정을 건드리지 않고 Codex만 쓰려면 postinstall을 건너뛰고 Codex 설치를 직접 실행합니다.
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
npm i -g sprag-cli --ignore-scripts
|
|
20
|
+
sprag install --agent codex
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`sprag doctor --agent codex`는 문제가 생겼을 때 쓰는 진단 명령입니다. Codex의 `/hooks`에서 등록된 명령을
|
|
24
|
+
검토하고 신뢰해야 훅이 동작합니다. 설치는 훅을 등록할 뿐 신뢰 설정을 바꾸지 않습니다.
|
|
25
|
+
관리자 정책이나 `features.hooks = false` 설정이 훅을 막을 수도 있습니다.
|
|
26
|
+
|
|
27
|
+
설정 위치는 `$CODEX_HOME`이며, 지정하지 않으면 `~/.codex`입니다. 설치는 `hooks.json`과
|
|
28
|
+
전역 `AGENTS.md`에 Sprag 항목을 추가하고 기존 내용을 보존합니다. 비어 있지 않은
|
|
29
|
+
`AGENTS.override.md`가 있으면 그 파일을 사용하며, 변경 전 사본을 남깁니다.
|
|
30
|
+
Claude 설정이나 `config.toml`은 고치지 않습니다.
|
|
31
|
+
|
|
32
|
+
macOS zsh 환경에서는 설치가 tmux 설치 여부를 먼저 확인합니다. tmux가 없으면 Homebrew로 설치하고,
|
|
33
|
+
`CTS_NO_TMUX=1`을 지정하면 이 단계를 건너뜁니다. 이어서 zsh 연동을 추가해 `codex`를 입력하면 같은
|
|
34
|
+
터미널 안에 패널이 인라인으로 붙으며, 이때는 별도 창을 여는 훅을 등록하지 않습니다. tmux를 설치할 수
|
|
35
|
+
없거나 로그인 셸이 zsh가 아니거나 이미 사용자의 `codex` 별칭·함수가 있으면 별도 Terminal 창 방식으로
|
|
36
|
+
대신 동작합니다. `sprag panel shell remove --agent codex`로 끈 설정은 재설치와 업그레이드에도 그대로
|
|
37
|
+
유지되며, `--no-panel`은 tmux 설정과 zsh 연동을 모두 건너뜁니다.
|
|
38
|
+
`sprag panel auto off --agent codex`로 끈 설정은 재설치해도 유지합니다.
|
|
39
|
+
패널 실행 여부는 `sprag panel doctor --agent codex`로 확인하십시오.
|
|
40
|
+
|
|
41
|
+
## 지원 기능
|
|
42
|
+
|
|
43
|
+
| 기능 | Codex 동작 |
|
|
44
|
+
|---|---|
|
|
45
|
+
| 하네스 | 전역·프로젝트 AGENTS.md에 다섯 원칙을 등록합니다 |
|
|
46
|
+
| 래칫 규칙 | 전역·프로젝트 규칙을 세션 시작과 재개 시 전달합니다 |
|
|
47
|
+
| 반복 실패 탐지 | 최근 명령·패치 실패에서 `ratchet?` 후보를 찾습니다 |
|
|
48
|
+
| 위임 후보 | 프로젝트·공급자·상위 모델별로 반복된 단순 작업을 찾습니다 |
|
|
49
|
+
| seed | 호환되는 래칫 프리셋을 제안하고 응답을 별도 저장합니다 |
|
|
50
|
+
| 문서 변환 | PDF·Office·Figma 문서를 Markdown으로 변환하고 캐시 덮어쓰기를 막습니다 |
|
|
51
|
+
| 한국어 검사 | 파일 쓰기 후 문체 위반을 검사하며 block·warn·off를 지원합니다 |
|
|
52
|
+
| 상태 표시 | 별도 패널, tmux 패널, 일회성 상태줄을 제공합니다 |
|
|
53
|
+
| 브리핑·이력 | 최신 컨텍스트와 사용량 경고를 알리고 세션별로 저장합니다 |
|
|
54
|
+
| 위임 원장 | Sprag가 위임한 하위 실행과 LiteLLM 단가로 예상 차액을 계산합니다 |
|
|
55
|
+
|
|
56
|
+
## 패널과 보고서
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
sprag panel --agent codex
|
|
60
|
+
sprag panel run --agent codex --
|
|
61
|
+
sprag --statusline --text --agent codex
|
|
62
|
+
sprag --agent codex --days 7 --format json
|
|
63
|
+
sprag brief --agent codex
|
|
64
|
+
sprag history --agent codex --days 7
|
|
65
|
+
sprag handoff --agent codex
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
별도 패널은 현재 프로젝트의 최신 기본 세션을 따릅니다. `--session ID`를 붙이면 특정 세션을
|
|
69
|
+
고정합니다. tmux 패널은 실행마다 별도 연결 파일을 사용하므로 같은 프로젝트의 다른 세션이
|
|
70
|
+
화면을 차지하지 않습니다. 연결 전에는 대기 상태를 표시합니다. 일부 Codex 버전은 첫 프롬프트를
|
|
71
|
+
제출한 뒤에 SessionStart 훅을 실행하므로 그때 패널 연결이 이루어집니다.
|
|
72
|
+
|
|
73
|
+
tmux 실행은 `--no-daemon`과 호출별 상태줄 설정을 사용합니다. Codex의 모델·추론 수준과
|
|
74
|
+
설정 파일은 바꾸지 않으며, `--keep-native-statusline`으로 기존 상태줄을 유지할 수 있습니다.
|
|
75
|
+
패널은 Codex 기본 상태줄에 셸 명령을 넣는 방식이 아닙니다.
|
|
76
|
+
|
|
77
|
+
설치는 이제 이 연동을 기본으로 추가합니다. macOS zsh에서 `codex`를 입력할 때 tmux 패널을 함께 열려면
|
|
78
|
+
다음 명령을 사용하십시오. 기존 별칭이나 함수는 덮어쓰지 않습니다.
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
sprag panel shell install --agent codex
|
|
82
|
+
sprag panel shell remove --agent codex
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
사용량은 로컬 `sessions`와 `archived_sessions`의 rollout 기록에서 읽습니다. 중복된 누적 토큰을
|
|
86
|
+
다시 더하지 않으며, 캐시 입력과 추론 출력도 각각 입력·출력에 이미 포함된 부분으로 처리합니다.
|
|
87
|
+
기록은 안정된 공개 API가 아니므로 일부가 빠지거나 형식이 바뀌면 값이 불완전할 수 있습니다.
|
|
88
|
+
사용량 갱신 횟수는 정확한 API 호출 횟수가 아닙니다.
|
|
89
|
+
|
|
90
|
+
## 위임 후보와 프리셋
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
sprag route-scan --refresh --agent codex
|
|
94
|
+
sprag delegate rules add R1 --project --agent codex
|
|
95
|
+
sprag route-scan dismiss R1 --agent codex
|
|
96
|
+
sprag delegate on --agent codex
|
|
97
|
+
sprag seed --agent codex
|
|
98
|
+
sprag seed accept all --project --agent codex
|
|
99
|
+
sprag seed skip all --agent codex
|
|
100
|
+
sprag seed reset --agent codex
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
위임 후보에는 같은 유형의 완료된 단순 작업이 세 번 이상 필요합니다. 중단된 작업과 이미 위임한
|
|
104
|
+
작업, 오류가 많은 작업, 도중에 사용자가 방향을 바꾼 작업은 제외합니다. 같은 세션의 보관 사본도
|
|
105
|
+
중복 집계하지 않습니다. 등록 전에는 사용자가 전역과 프로젝트 중 적용 범위를 정해야 합니다.
|
|
106
|
+
|
|
107
|
+
단가와 실행 근거를 확인할 수 있으면 더 저렴한 모델을 제안합니다. 단가가 없으면 후보를 유지하되
|
|
108
|
+
등록할 때 `--model ID`를 요구합니다. 모델 사용 가능 여부는 실제 공급자 설정에 달려 있습니다.
|
|
109
|
+
명시한 모델과 사용자 정의 역할은 유지하며, 사용할 수 없는 하위 에이전트 도구를 만들어 내지 않습니다.
|
|
110
|
+
|
|
111
|
+
SessionStart는 캐시된 후보를 읽고 필요할 때 별도 프로세스에서 갱신합니다. 명시적인 `--refresh`는
|
|
112
|
+
대기 주기를 건너뜁니다. `seed`는 Claude의 Haiku·Sonnet 프리셋을 가져오지 않으며,
|
|
113
|
+
Codex의 수락·거절 기록도 Claude 기록과 분리합니다.
|
|
114
|
+
|
|
115
|
+
## 래칫 규칙
|
|
116
|
+
|
|
117
|
+
```sh
|
|
118
|
+
sprag harness init --project --agent codex
|
|
119
|
+
sprag harness promote "Run the changed module's tests before completion." --project --agent codex
|
|
120
|
+
sprag harness list --project --agent codex
|
|
121
|
+
sprag harness promote 1 --session SESSION_ID --project --agent codex
|
|
122
|
+
sprag harness prune --global --older-than 6 --dry-run --agent codex
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
규칙은 `<프로젝트>/.codex/ratchet.md`나 `$CODEX_HOME/ratchet.md`에 저장합니다.
|
|
126
|
+
반복 실패 탐지는 최근 2 MiB, 도구가 실행된 30턴, 30분 범위를 확인합니다. 검색 결과가 없다는
|
|
127
|
+
종료 코드 등은 제외합니다. 숫자로 후보를 등록하면 원인과 예방책을 적을 TODO가 생기므로,
|
|
128
|
+
원인을 확인한 뒤 완전한 조건·행동 규칙으로 다듬어야 합니다. 후보 번호는 세션별 임시 값입니다.
|
|
129
|
+
|
|
130
|
+
## 문서와 문체
|
|
131
|
+
|
|
132
|
+
```sh
|
|
133
|
+
sprag doc2md install-converter --agent codex
|
|
134
|
+
sprag doc2md report.pdf --agent codex
|
|
135
|
+
sprag korean on --agent codex
|
|
136
|
+
sprag korean lint warn --agent codex
|
|
137
|
+
sprag cohesion on --agent codex
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
문서 변환기는 Claude와 공유하지만 Codex는 별도 Markdown 뷰와 문서 누계를 사용합니다.
|
|
141
|
+
변환 캐시를 고쳐도 원본에는 반영되지 않습니다. 문서를 수정하려면 원본을 복사하고 형식을
|
|
142
|
+
지원하는 라이브러리로 복사본을 편집한 뒤 다시 변환하십시오. 텍스트 추출만으로 차트·이미지·배치를
|
|
143
|
+
검증했다고 말할 수 없습니다. 실패와 잘린 결과는 그대로 보고해야 합니다.
|
|
144
|
+
|
|
145
|
+
훅은 지원되는 파일 읽기와 단순한 `cat`·`head`·`tail`, 인식 가능한 쓰기 대상을 처리합니다.
|
|
146
|
+
임의의 추출 스크립트나 복합 셸 명령을 모두 가로채지는 않습니다. `doc2md off`는 Codex의 자동
|
|
147
|
+
변환과 문서 쓰기 보호를 끕니다. 문체 설정은 공유하지만 기능을 켤 때는 Codex 훅을 먼저 등록합니다.
|
|
148
|
+
|
|
149
|
+
## 단가와 한계
|
|
150
|
+
|
|
151
|
+
```sh
|
|
152
|
+
sprag route-scan savings --refresh --agent codex
|
|
153
|
+
sprag cache status --refresh --agent codex
|
|
154
|
+
sprag capabilities --agent codex
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
위임 원장은 Sprag가 실행 시 기록한 route ID와 하위 세션 기록이 일치할 때만 집계합니다.
|
|
158
|
+
하위 실행의 토큰을 고정하고 부모·자식 단가를 각각 적용한 차이이므로, 실제 청구서의 절감액은
|
|
159
|
+
아닙니다. 손해는 음수로 남기며 단가나 사용량이 없으면 0 대신 미상으로 표시합니다.
|
|
160
|
+
|
|
161
|
+
단가는 설정된 LiteLLM의 `/model/info`에서 읽습니다. 공급자가 다른 동명 모델을 섞지 않고,
|
|
162
|
+
같은 별칭 아래 배포 단가가 다르면 계산하지 않습니다. 단가 캐시는 7일 동안 사용하며 이미
|
|
163
|
+
계산한 실행은 단가 사본을 보존합니다. 직접 OpenAI 연결이나 구독 요금은 추정하지 않습니다.
|
|
164
|
+
위임 후 `--refresh`로 원장을 갱신할 수 있고, 아직 일치하지 않은 기록은 7일 동안 찾습니다.
|
|
165
|
+
|
|
166
|
+
캐시 타이머는 확인된 공급자 정책과 기록된 캐시 사용 시각을 보여 줍니다. 정확한 만료 시각이나
|
|
167
|
+
다음 요청의 적중을 보장하지 않습니다. 패널의 LiteLLM 예산·단가 조회는 기존 인증 정보를
|
|
168
|
+
사용하며, 원본 세션 내용을 전송하지 않습니다. 일회성 상태줄은 저장된 정보만 읽습니다.
|
|
169
|
+
|
|
170
|
+
## 제거와 업그레이드
|
|
171
|
+
|
|
172
|
+
```sh
|
|
173
|
+
sprag uninstall --agent codex
|
|
174
|
+
npm i -g sprag-cli --ignore-scripts
|
|
175
|
+
sprag install --agent codex
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
제거는 Codex 훅과 전역 하네스 블록만 대상으로 합니다. 프로젝트 하네스, 래칫 규칙, 백업과
|
|
179
|
+
공유 상태는 남습니다. 일반 npm 설치는 Claude postinstall을 실행하므로 Codex만 사용한다면
|
|
180
|
+
업그레이드에도 `--ignore-scripts`를 유지하십시오.
|
|
181
|
+
|
|
182
|
+
공식 인터페이스는 [Codex 훅](https://developers.openai.com/codex/hooks/)과
|
|
183
|
+
[AGENTS.md 안내](https://developers.openai.com/codex/guides/agents-md/),
|
|
184
|
+
[설정 참조](https://developers.openai.com/codex/config-reference/)에서 확인할 수 있습니다.
|