sprag-cli 3.41.0 → 3.42.1
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.ko.md +84 -84
- package/README.md +84 -84
- package/bin/cli.js +80 -80
- package/package.json +1 -1
- package/src/advice.js +3 -3
- package/src/brief.js +4 -4
- package/src/cohesion.js +2 -2
- package/src/commands/brief.js +7 -7
- package/src/commands/cohesion.js +6 -6
- package/src/commands/compact-window.js +9 -9
- package/src/commands/doc2md.js +6 -6
- package/src/commands/feedback.js +5 -5
- package/src/commands/harness.js +22 -22
- package/src/commands/install.js +23 -23
- package/src/commands/korean.js +14 -14
- package/src/commands/last.js +5 -5
- package/src/commands/mode.js +4 -4
- package/src/commands/route-scan.js +25 -25
- package/src/commands/seed.js +10 -10
- package/src/commands/update-check.js +6 -6
- package/src/doc2md.cjs +15 -15
- package/src/handoff.js +1 -1
- package/src/harness-templates.js +7 -7
- package/src/history.js +1 -1
- package/src/installer.js +21 -21
- package/src/korean-style.js +8 -4
- package/src/model-rules.js +6 -6
package/bin/cli.js
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* claude-token-saver
|
|
4
|
+
* sprag CLI (formerly claude-token-saver / claude-cache-monitor)
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
|
-
* npx
|
|
8
|
-
* npx
|
|
9
|
-
* npx
|
|
10
|
-
* npx
|
|
11
|
-
* npx
|
|
12
|
-
* npx
|
|
13
|
-
* npx
|
|
14
|
-
* npx
|
|
15
|
-
* npx
|
|
16
|
-
* npx
|
|
7
|
+
* npx sprag # default report (last 30 days)
|
|
8
|
+
* npx sprag --days 7 # last 7 days
|
|
9
|
+
* npx sprag --format json # JSON output
|
|
10
|
+
* npx sprag --format csv # CSV output
|
|
11
|
+
* npx sprag --project myproj # filter by project
|
|
12
|
+
* npx sprag route-scan # detect recurring easy work → haiku-delegation candidates
|
|
13
|
+
* npx sprag install # set up skill/hooks/statusline; asks about harness + Korean guidance
|
|
14
|
+
* npx sprag install --yes # take the defaults without asking (same as --no-input)
|
|
15
|
+
* npx sprag --install-hook # install PostToolUse hook
|
|
16
|
+
* npx sprag --uninstall-hook # remove hook
|
|
17
17
|
* npx claude-token-saver --hook-run # internal: called by hook
|
|
18
|
-
* npx
|
|
19
|
-
* npx
|
|
20
|
-
* npx
|
|
21
|
-
* npx
|
|
22
|
-
* npx
|
|
23
|
-
* npx
|
|
24
|
-
* npx
|
|
18
|
+
* npx sprag --statusline # one-line output for Claude Code statusline API
|
|
19
|
+
* npx sprag --statusline --verbose # longer labels
|
|
20
|
+
* npx sprag --statusline --no-color # strip ANSI colors
|
|
21
|
+
* npx sprag --statusline --icon # use 🧠 ⏳ 💰 icons
|
|
22
|
+
* npx sprag --statusline --no-timer # hide the TTL countdown
|
|
23
|
+
* npx sprag --statusline --single-line # legacy 1-line layout (no routing-totals headline)
|
|
24
|
+
* npx sprag --statusline --exclude-session <path>
|
|
25
25
|
* # exclude a JSONL path from lastActivity
|
|
26
26
|
* # (or set CACHE_MONITOR_EXCLUDE_SESSION env var)
|
|
27
27
|
*/
|
|
@@ -90,32 +90,32 @@ const KNOWN_SUBCOMMANDS = new Set([
|
|
|
90
90
|
'seed', 'litellm-budget', 'feedback',
|
|
91
91
|
]);
|
|
92
92
|
|
|
93
|
-
const USAGE = `
|
|
93
|
+
const USAGE = `sprag — Claude Code token usage, cache health, and model routing
|
|
94
94
|
|
|
95
95
|
Usage:
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
96
|
+
sprag default report (last 30 days)
|
|
97
|
+
sprag --days 7 last 7 days
|
|
98
|
+
sprag --format json JSON output
|
|
99
|
+
sprag --format csv CSV output
|
|
100
|
+
sprag --project myproj filter by project
|
|
101
|
+
sprag route-scan detect recurring easy work → delegation candidates
|
|
102
|
+
sprag install set up skill/hooks/statusline
|
|
103
|
+
sprag install --yes take the defaults without asking
|
|
104
|
+
sprag uninstall remove everything install added
|
|
105
|
+
sprag harness check score the harness setup in CLAUDE.md
|
|
106
|
+
sprag harness analyze run the harness transcript analysis manually
|
|
107
|
+
sprag last most recent warning + how to handle it
|
|
108
|
+
sprag history recent warning transitions
|
|
109
|
+
sprag handoff write a session handoff file
|
|
110
|
+
sprag feedback "<msg>" file a bug report / feature request (no browser needed)
|
|
111
|
+
sprag upgrade install the latest release
|
|
112
|
+
sprag --install-hook install cache-monitor PostToolUse hook
|
|
113
|
+
sprag --uninstall-hook remove that hook
|
|
114
|
+
sprag --statusline one-line output for Claude Code statusline
|
|
115
115
|
--verbose / --no-color / --icon / --no-timer / --single-line
|
|
116
116
|
|
|
117
117
|
Run any subcommand with --help for its own options where available.
|
|
118
|
-
Bug reports & feature requests: https://github.com/rootstudioyaml/
|
|
118
|
+
Bug reports & feature requests: https://github.com/rootstudioyaml/sprag/issues
|
|
119
119
|
`;
|
|
120
120
|
|
|
121
121
|
async function main() {
|
|
@@ -140,8 +140,8 @@ async function main() {
|
|
|
140
140
|
// Designed for the auto-trigger skill so the user immediately sees
|
|
141
141
|
// "what just fired and how to fix it" without having to read the whole
|
|
142
142
|
// history file.
|
|
143
|
-
//
|
|
144
|
-
//
|
|
143
|
+
// sprag last # search last 1 day
|
|
144
|
+
// sprag last --days 7 # widen the lookback
|
|
145
145
|
if (args[0] === 'last') {
|
|
146
146
|
return (await import('../src/commands/last.js')).run({ numArg });
|
|
147
147
|
}
|
|
@@ -149,9 +149,9 @@ async function main() {
|
|
|
149
149
|
// Subcommand: history — print recent warning transitions captured by the
|
|
150
150
|
// statusline. One markdown file per day, persisted under the platform-
|
|
151
151
|
// specific user-data dir.
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
152
|
+
// sprag history # last 7 days
|
|
153
|
+
// sprag history --days 30 # custom window
|
|
154
|
+
// sprag history --list # just list available dates
|
|
155
155
|
if (args[0] === 'history') {
|
|
156
156
|
return (await import('../src/commands/history.js')).run({ hasFlag, numArg });
|
|
157
157
|
}
|
|
@@ -161,8 +161,8 @@ async function main() {
|
|
|
161
161
|
// session can pick up where this one stopped. Pairs with the cap-warn chip:
|
|
162
162
|
// when statusline shows 🚨 5H 90%+, run this to back up state before the cap
|
|
163
163
|
// hits.
|
|
164
|
-
//
|
|
165
|
-
//
|
|
164
|
+
// sprag handoff # write to cwd
|
|
165
|
+
// sprag handoff --cwd PATH # custom directory
|
|
166
166
|
// Subcommand: feedback — file a bug report / feature request without a
|
|
167
167
|
// browser. Tries gh CLI, then an anonymous form POST, then a local save
|
|
168
168
|
// with a prefilled GitHub issue URL. See src/commands/feedback.js.
|
|
@@ -179,8 +179,8 @@ async function main() {
|
|
|
179
179
|
// user can just mention chip wording and Claude responds. v2.6.0 dropped
|
|
180
180
|
// the redundant /token-monitor slash command in favor of the skill alone;
|
|
181
181
|
// a legacy command file is removed automatically. Cross-platform.
|
|
182
|
-
//
|
|
183
|
-
//
|
|
182
|
+
// sprag install # install/update the skill
|
|
183
|
+
// sprag install --force # overwrite existing skill file
|
|
184
184
|
if (args[0] === 'install') {
|
|
185
185
|
return (await import('../src/commands/install.js')).run({ hasFlag });
|
|
186
186
|
}
|
|
@@ -194,22 +194,22 @@ async function main() {
|
|
|
194
194
|
|
|
195
195
|
// Subcommand: mode — persist statusline preferences so future runs pick
|
|
196
196
|
// them up without flags or wrapper edits.
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
//
|
|
197
|
+
// sprag mode # show current config
|
|
198
|
+
// sprag mode icon verbose # set icon + verbose
|
|
199
|
+
// sprag mode reset # clear back to defaults
|
|
200
200
|
if (args[0] === 'mode') {
|
|
201
201
|
return (await import('../src/commands/mode.js')).run({ args });
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
// Subcommand: route-scan — detect recurring easy work on expensive models
|
|
205
205
|
// and propose model-delegation ratchet rules. Zero token cost, fully local.
|
|
206
|
-
//
|
|
207
|
-
//
|
|
208
|
-
//
|
|
209
|
-
//
|
|
210
|
-
//
|
|
206
|
+
// sprag route-scan # scan (24h cache) + print candidates
|
|
207
|
+
// sprag route-scan --refresh # force rescan
|
|
208
|
+
// sprag route-scan --days 30 # wider lookback
|
|
209
|
+
// sprag route-scan --hook # SessionStart hook mode (context injection)
|
|
210
|
+
// sprag route-scan dismiss <N> # mute candidate R<N>
|
|
211
211
|
// Promote a candidate to a ratchet rule (scope is always explicit):
|
|
212
|
-
//
|
|
212
|
+
// sprag harness promote R<N> --project|--global
|
|
213
213
|
// brief --hook — UserPromptSubmit hook mode: per-session, change-triggered
|
|
214
214
|
// briefing of state the statusline can only chip (ctx tier crossings,
|
|
215
215
|
// mid-session route/rule-health changes). Silent when nothing changed.
|
|
@@ -223,39 +223,39 @@ async function main() {
|
|
|
223
223
|
|
|
224
224
|
// Subcommand: korean — Korean writing guidance injected at session start,
|
|
225
225
|
// so the rules apply in every project without an output-style switch.
|
|
226
|
-
//
|
|
226
|
+
// sprag korean on | off | status | show
|
|
227
227
|
if (args[0] === 'korean') {
|
|
228
228
|
return (await import('../src/commands/korean.js')).run({ args, hasFlag });
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
// Subcommand: cohesion — English sentence-connection guidance injected at
|
|
232
232
|
// session start; the language-neutral half of the Korean supplement.
|
|
233
|
-
//
|
|
233
|
+
// sprag cohesion on | off | status | show
|
|
234
234
|
if (args[0] === 'cohesion') {
|
|
235
235
|
return (await import('../src/commands/cohesion.js')).run({ args, hasFlag });
|
|
236
236
|
}
|
|
237
237
|
|
|
238
238
|
// Subcommand: doc2md — convert pptx/xlsx/pdf/docx to Markdown before the
|
|
239
239
|
// model reads them, so an unreadable binary never enters the context window.
|
|
240
|
-
//
|
|
240
|
+
// sprag doc2md on | off | <file> | --clean
|
|
241
241
|
if (args[0] === 'doc2md') {
|
|
242
242
|
return (await import('../src/commands/doc2md.js')).run({ args, hasFlag });
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
// Subcommand: seed — register the bundled starter rules (model-fitting
|
|
246
246
|
// presets + curated ratchet rules), one answer at a time.
|
|
247
|
-
//
|
|
247
|
+
// sprag seed | seed accept <id> --global|--project | seed skip <id>
|
|
248
248
|
if (args[0] === 'seed') {
|
|
249
249
|
return (await import('../src/commands/seed.js')).run({ args, hasFlag });
|
|
250
250
|
}
|
|
251
251
|
|
|
252
252
|
// Subcommand: harness — manage the project's CLAUDE.md harness rules.
|
|
253
|
-
//
|
|
254
|
-
//
|
|
255
|
-
//
|
|
256
|
-
//
|
|
257
|
-
//
|
|
258
|
-
//
|
|
253
|
+
// sprag harness init # write CLAUDE.md (5 sections) + ratchet.md
|
|
254
|
+
// sprag harness uninit # remove harness block from CLAUDE.md (backup kept)
|
|
255
|
+
// sprag harness check # show 🅷 N/5 + which sections are missing
|
|
256
|
+
// sprag harness promote "<rule>" # append a rule to ratchet.md
|
|
257
|
+
// sprag harness pull [--global|--project] # register the package's curated preset rules (default global)
|
|
258
|
+
// sprag harness off | on # toggle the statusline 🅷 segment
|
|
259
259
|
if (args[0] === 'harness') {
|
|
260
260
|
return (await import('../src/commands/harness.js')).run({ args, hasFlag });
|
|
261
261
|
}
|
|
@@ -263,10 +263,10 @@ async function main() {
|
|
|
263
263
|
// Subcommand: compact-window — audit / pin Claude Code's autoCompactWindow.
|
|
264
264
|
// On a 1M-context model, compaction only fires near 800k unless the window is
|
|
265
265
|
// capped; 200k sessions are exempt.
|
|
266
|
-
//
|
|
267
|
-
//
|
|
268
|
-
//
|
|
269
|
-
//
|
|
266
|
+
// sprag compact-window # status
|
|
267
|
+
// sprag compact-window set --global # pin 200k (~/.claude/settings.json)
|
|
268
|
+
// sprag compact-window set --project # pin 200k (<root>/.claude/settings.json)
|
|
269
|
+
// sprag compact-window off | on # toggle the statusline warning
|
|
270
270
|
if (args[0] === 'compact-window') {
|
|
271
271
|
return (await import('../src/commands/compact-window.js')).run({ args, hasFlag });
|
|
272
272
|
}
|
|
@@ -281,9 +281,9 @@ async function main() {
|
|
|
281
281
|
|
|
282
282
|
// Subcommand: update-check — the registry lookup behind the ⬆ statusline
|
|
283
283
|
// chip and the session-start upgrade offer.
|
|
284
|
-
//
|
|
285
|
-
//
|
|
286
|
-
//
|
|
284
|
+
// sprag update-check # print cached status
|
|
285
|
+
// sprag update-check --refresh # hit the registry now (detached child uses this)
|
|
286
|
+
// sprag update-check --dismiss # stop offering THIS version at session start
|
|
287
287
|
if (args[0] === 'update-check') {
|
|
288
288
|
return (await import('../src/commands/update-check.js')).run({
|
|
289
289
|
hasFlag,
|
|
@@ -292,8 +292,8 @@ async function main() {
|
|
|
292
292
|
}
|
|
293
293
|
|
|
294
294
|
// Subcommand: litellm-budget · LiteLLM 게이트웨이 키의 max_budget/spend 조회.
|
|
295
|
-
//
|
|
296
|
-
//
|
|
295
|
+
// sprag litellm-budget # 캐시된 예산 상태 출력
|
|
296
|
+
// sprag litellm-budget --refresh # 지금 프록시에 물어봄 (detached 자식이 사용)
|
|
297
297
|
if (args[0] === 'litellm-budget') {
|
|
298
298
|
const { gatewayEnv, readBudgetState, refreshBudgetState } = await import('../src/litellm-budget.js');
|
|
299
299
|
const quiet = hasFlag('--quiet');
|
|
@@ -319,8 +319,8 @@ async function main() {
|
|
|
319
319
|
|
|
320
320
|
// Subcommand: upgrade — run the install command that matches how this copy
|
|
321
321
|
// got here, then confirm the new version.
|
|
322
|
-
//
|
|
323
|
-
//
|
|
322
|
+
// sprag upgrade # install the latest release
|
|
323
|
+
// sprag upgrade --print # just show the command, run nothing
|
|
324
324
|
if (args[0] === 'upgrade') {
|
|
325
325
|
return (await import('../src/commands/upgrade.js')).run({
|
|
326
326
|
hasFlag,
|
|
@@ -356,7 +356,7 @@ async function main() {
|
|
|
356
356
|
// every scenario based on wall clock so a screen recorder picks them up.
|
|
357
357
|
const demoArg = getArg('--demo');
|
|
358
358
|
|
|
359
|
-
// `
|
|
359
|
+
// `sprag --demo table` (no --statusline) — full table view
|
|
360
360
|
// with all six issue drill-downs at once, for marketing screencasts.
|
|
361
361
|
if (!isStatusline && demoArg === 'table') {
|
|
362
362
|
const { buildTableDemoData } = await import('../src/demo.js');
|
|
@@ -592,7 +592,7 @@ async function main() {
|
|
|
592
592
|
}
|
|
593
593
|
}
|
|
594
594
|
// Persist transitions to ~/.config/claude-token-saver/history/YYYY-MM-DD.md
|
|
595
|
-
// so `
|
|
595
|
+
// so `sprag history` and the auto-skill can replay them.
|
|
596
596
|
try {
|
|
597
597
|
const { recordChip, recordCapTransition } = await import('../src/history.js');
|
|
598
598
|
recordChip(spikeChip, { detail: chipDetail });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sprag-cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.42.1",
|
|
4
4
|
"description": "Sprag - a quality ratchet harness for AI coding agents: model-fitting delegation, ratchet rules, doc2md, Korean style gate. Same tool as claude-token-saver, under its new name.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/advice.js
CHANGED
|
@@ -467,7 +467,7 @@ export const ISSUE_MESSAGES = {
|
|
|
467
467
|
* and how do I fix it" without re-running the tool.
|
|
468
468
|
*
|
|
469
469
|
* The full multi-step advice still lives in `ISSUE_MESSAGES[code].actions()`;
|
|
470
|
-
* `
|
|
470
|
+
* `sprag last` prints that long form on demand.
|
|
471
471
|
*/
|
|
472
472
|
export const ISSUE_TIPS = {
|
|
473
473
|
LARGE_INPUT_PER_REQUEST: {
|
|
@@ -536,8 +536,8 @@ export const CHIP_TO_CODES = {
|
|
|
536
536
|
* because once the cap blocks you, you need a fresh session to continue.
|
|
537
537
|
*/
|
|
538
538
|
export const CAP_TIPS = {
|
|
539
|
-
en: 'Run `
|
|
540
|
-
ko: '`
|
|
539
|
+
en: 'Run `sprag handoff` to back up state before the cap blocks you',
|
|
540
|
+
ko: '`sprag handoff` 실행해서 캡 도달 전에 상태 백업',
|
|
541
541
|
};
|
|
542
542
|
|
|
543
543
|
/**
|
package/src/brief.js
CHANGED
|
@@ -226,8 +226,8 @@ export async function runBrief({ sessionId, transcriptPath, now = Date.now() })
|
|
|
226
226
|
}
|
|
227
227
|
for (const [kind, x] of fresh) {
|
|
228
228
|
items.push(kind === 'route'
|
|
229
|
-
? `새 위임 후보가 감지되었습니다 — "${x.label}" 유형 ${x.count}회 반복(statusline의 route? R${x.id} 칩). 등록:
|
|
230
|
-
: `승인된 위임 룰의 최근 에러율이 기준(20%)을 넘었습니다 — "${x.label}" (${x.healthSource === 'delegated' ? `위임 실행 ${x.delegatedRuns}건 중 에러율 ${Math.round((x.delegatedErrRate || 0) * 100)}%` : `유사 에피소드 기준 에러율 ${Math.round((x.errRate || 0) * 100)}%`}, statusline의 rule-health 칩). 조건 좁히기/제거를 사용자와 상의하세요:
|
|
229
|
+
? `새 위임 후보가 감지되었습니다 — "${x.label}" 유형 ${x.count}회 반복(statusline의 route? R${x.id} 칩). 등록: sprag harness promote R${x.id} --project|--global (적용 범위는 사용자에게 확인) / 무시: route-scan dismiss ${x.id}`
|
|
230
|
+
: `승인된 위임 룰의 최근 에러율이 기준(20%)을 넘었습니다 — "${x.label}" (${x.healthSource === 'delegated' ? `위임 실행 ${x.delegatedRuns}건 중 에러율 ${Math.round((x.delegatedErrRate || 0) * 100)}%` : `유사 에피소드 기준 에러율 ${Math.round((x.errRate || 0) * 100)}%`}, statusline의 rule-health 칩). 조건 좁히기/제거를 사용자와 상의하세요: sprag route-scan rules`);
|
|
231
231
|
}
|
|
232
232
|
s.briefed = [...briefed];
|
|
233
233
|
} catch { /* caches unreadable — ctx briefing above still applies */ }
|
|
@@ -246,7 +246,7 @@ export async function runBrief({ sessionId, transcriptPath, now = Date.now() })
|
|
|
246
246
|
briefed.add(sig);
|
|
247
247
|
s.briefed = [...briefed];
|
|
248
248
|
const now = st.window ? `현재 ${fmtK(st.window)}` : '현재 미설정';
|
|
249
|
-
items.push(`1M 컨텍스트 모델(${st.model})인데 autoCompactWindow가 ${now}입니다 — 자동 압축이 80만 토큰 근처에서야 걸려 그전까지 모든 요청이 전체 컨텍스트를 재과금합니다. 1M은 너무 크니 ${fmtK(st.recommendedMin)}~${fmtK(st.recommendedMax)} 범위를 권장합니다(그 범위 안이면 경고하지 않습니다). 1M 창 자체는 그대로 두고 압축 시점만 앞당깁니다. 등록:
|
|
249
|
+
items.push(`1M 컨텍스트 모델(${st.model})인데 autoCompactWindow가 ${now}입니다 — 자동 압축이 80만 토큰 근처에서야 걸려 그전까지 모든 요청이 전체 컨텍스트를 재과금합니다. 1M은 너무 크니 ${fmtK(st.recommendedMin)}~${fmtK(st.recommendedMax)} 범위를 권장합니다(그 범위 안이면 경고하지 않습니다). 1M 창 자체는 그대로 두고 압축 시점만 앞당깁니다. 등록: sprag compact-window set --global|--project [--value ${fmtK(st.recommendedMax)}] (기본 ${fmtK(st.recommended)}, 적용 범위는 사용자에게 확인) / 끄기: compact-window off`);
|
|
250
250
|
}
|
|
251
251
|
}
|
|
252
252
|
} catch { /* settings unreadable — other briefings above still apply */ }
|
|
@@ -257,7 +257,7 @@ export async function runBrief({ sessionId, transcriptPath, now = Date.now() })
|
|
|
257
257
|
|
|
258
258
|
if (items.length === 0) return null;
|
|
259
259
|
const lines = [
|
|
260
|
-
'[
|
|
260
|
+
'[sprag 브리핑] 아래 상태 변화를 사용자에게 알려주세요. 진행 중인 답변 흐름을 끊지 말고, 답변 말미에 `※ [sprag]` 라벨을 달아 각 항목을 1~2줄로 요약해 전달하면 됩니다 (이 브리핑은 항목당 한 번만 주입됩니다):',
|
|
261
261
|
];
|
|
262
262
|
for (const it of items) lines.push(`- ${it}`);
|
|
263
263
|
return lines.join('\n');
|
package/src/cohesion.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Off by default for the same reason the Korean guidance is opt-in: a
|
|
11
11
|
* token-saving tool has no business silently billing ~0.5k tokens a session.
|
|
12
|
-
* Enable with `
|
|
12
|
+
* Enable with `sprag cohesion on`.
|
|
13
13
|
*
|
|
14
14
|
* When the Korean guidance is enabled, this block is NOT injected even if
|
|
15
15
|
* enabled: the Korean supplement already carries the cohesion rules, and
|
|
@@ -62,7 +62,7 @@ export async function cohesionInjection({ cfg = loadConfig() } = {}) {
|
|
|
62
62
|
const text = cohesionText();
|
|
63
63
|
if (!text) return null;
|
|
64
64
|
return [
|
|
65
|
-
'[
|
|
65
|
+
'[sprag cohesion] Follow this guidance for English prose in this session.',
|
|
66
66
|
'The user enabled it in claude-token-saver.',
|
|
67
67
|
'',
|
|
68
68
|
text,
|
package/src/commands/brief.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Subcommand: route-scan — detect recurring easy work on expensive models
|
|
3
3
|
* and propose model-delegation ratchet rules. Zero token cost, fully local.
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* sprag route-scan # scan (24h cache) + print candidates
|
|
5
|
+
* sprag route-scan --refresh # force rescan
|
|
6
|
+
* sprag route-scan --days 30 # wider lookback
|
|
7
|
+
* sprag route-scan --hook # SessionStart hook mode (context injection)
|
|
8
|
+
* sprag route-scan dismiss <N> # mute candidate R<N>
|
|
9
9
|
* Promote a candidate to a ratchet rule (scope is always explicit):
|
|
10
|
-
*
|
|
10
|
+
* sprag harness promote R<N> --project|--global
|
|
11
11
|
* brief --hook — UserPromptSubmit hook mode: per-session, change-triggered
|
|
12
12
|
* briefing of state the statusline can only chip (ctx tier crossings,
|
|
13
13
|
* mid-session route/rule-health changes). Silent when nothing changed.
|
|
@@ -18,7 +18,7 @@ import { debug } from '../debug.js';
|
|
|
18
18
|
|
|
19
19
|
export async function run({ hasFlag }) {
|
|
20
20
|
if (!hasFlag('--hook')) {
|
|
21
|
-
console.error('Usage:
|
|
21
|
+
console.error('Usage: sprag brief --hook (UserPromptSubmit hook mode)');
|
|
22
22
|
process.exit(1);
|
|
23
23
|
}
|
|
24
24
|
const ctx = readStdinJson() || {};
|
package/src/commands/cohesion.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Subcommand: cohesion — English sentence-connection guidance for every session.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* sprag cohesion on # inject at session start, all projects
|
|
4
|
+
* sprag cohesion off # stop injecting
|
|
5
|
+
* sprag cohesion status # current state and cost
|
|
6
|
+
* sprag cohesion show # print the guidance itself
|
|
7
7
|
*
|
|
8
8
|
* The English sibling of `korean on`, carrying only the language-neutral
|
|
9
9
|
* cohesion rules (given-before-new, one referent per pronoun, subject
|
|
@@ -51,9 +51,9 @@ export async function run({ args }) {
|
|
|
51
51
|
const suppressed = enabled && koreanStyleEnabled();
|
|
52
52
|
if (lang === 'ko') {
|
|
53
53
|
console.log(`cohesion: ${enabled ? 'on' : 'off'}${suppressed ? ' (korean 지침이 켜져 있어 주입은 생략됨)' : ''}`);
|
|
54
|
-
console.log('영어 산문의 문장 연결 지침을 세션 시작에 주입합니다. 켜기:
|
|
54
|
+
console.log('영어 산문의 문장 연결 지침을 세션 시작에 주입합니다. 켜기: sprag cohesion on');
|
|
55
55
|
} else {
|
|
56
56
|
console.log(`cohesion: ${enabled ? 'on' : 'off'}${suppressed ? ' (suppressed while the Korean guidance is on — it already carries these rules)' : ''}`);
|
|
57
|
-
console.log('Injects English cohesion guidance at session start. Enable with:
|
|
57
|
+
console.log('Injects English cohesion guidance at session start. Enable with: sprag cohesion on');
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Subcommand: compact-window — audit / pin Claude Code's `autoCompactWindow`.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
3
|
+
* sprag compact-window # status
|
|
4
|
+
* sprag compact-window set --global # pin 500k in ~/.claude/settings.json
|
|
5
|
+
* sprag compact-window set --project # pin 500k in <root>/.claude/settings.json
|
|
6
|
+
* sprag compact-window set --global --value 600k
|
|
7
|
+
* sprag compact-window off | on # toggle the statusline warning
|
|
8
8
|
*
|
|
9
9
|
* Scope is deliberately explicit for `set`: writing a global settings.json is
|
|
10
10
|
* not something to guess at, and the non-TTY hook environment cannot prompt.
|
|
@@ -80,14 +80,14 @@ export async function run({ args, hasFlag }) {
|
|
|
80
80
|
console.log(ko
|
|
81
81
|
? ` 그 전까지 모든 요청이 전체 컨텍스트를 재과금합니다. 1M은 너무 크니 ${fmt(s.recommendedMin)}~${fmt(s.recommendedMax)} 범위를 권장합니다 (기본값 ${fmt(s.recommended)}, --value로 조절). 1M 창 자체는 그대로 두고 압축 시점만 앞당깁니다.`
|
|
82
82
|
: ` Until then every request re-bills the whole context. 1M is too large — pick something in ${fmt(s.recommendedMin)}–${fmt(s.recommendedMax)} (default ${fmt(s.recommended)}, override with --value). The 1M window itself stays.`);
|
|
83
|
-
console.log(`\n
|
|
84
|
-
console.log(`
|
|
85
|
-
console.log(`
|
|
83
|
+
console.log(`\n sprag compact-window set --global (~/.claude/settings.json, ${fmt(s.recommended)})`);
|
|
84
|
+
console.log(` sprag compact-window set --project (<root>/.claude/settings.json, ${fmt(s.recommended)})`);
|
|
85
|
+
console.log(` sprag compact-window set --global --value ${fmt(s.recommendedMax)}`);
|
|
86
86
|
console.log(ko ? ' (적용 범위는 사용자에게 먼저 확인할 것)' : ' (confirm the scope with the user first)');
|
|
87
87
|
return;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
console.error(`Unknown compact-window subcommand: ${sub}`);
|
|
91
|
-
console.error('Usage:
|
|
91
|
+
console.error('Usage: sprag compact-window [status|set --global|--project [--value 500k]|off|on]');
|
|
92
92
|
process.exit(1);
|
|
93
93
|
}
|
package/src/commands/doc2md.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* Subcommand: doc2md — convert attached documents to Markdown before the
|
|
3
3
|
* model reads them.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* sprag doc2md # status
|
|
6
|
+
* sprag doc2md on|off # register / remove the Read hook
|
|
7
|
+
* sprag doc2md <file> # convert one file by hand
|
|
8
|
+
* sprag doc2md --clean # drop every cached conversion
|
|
9
|
+
* sprag doc2md --hook # PreToolUse entry point
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { createRequire } from 'node:module';
|
|
@@ -162,5 +162,5 @@ export async function run({ args, hasFlag }) {
|
|
|
162
162
|
console.log(` hook: ${hookRegistered() ? 'registered on Read' : 'not registered'}`);
|
|
163
163
|
console.log('');
|
|
164
164
|
if (!python) console.log(`Install the converter: ${doc2md.INSTALL_HINT}`);
|
|
165
|
-
console.log('Enable with:
|
|
165
|
+
console.log('Enable with: sprag doc2md on');
|
|
166
166
|
}
|
package/src/commands/feedback.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Subcommand: feedback — submit a bug report or feature request from the
|
|
3
3
|
* terminal (or from a Claude Code session) without opening a browser.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* sprag feedback "statusline이 IntelliJ에서 깨져요"
|
|
6
|
+
* sprag feedback --title "cache chip" "5m TTL 칩이 안 사라짐"
|
|
7
7
|
*
|
|
8
8
|
* Why this exists: GitHub issues require a logged-in browser session, and
|
|
9
9
|
* corporate networks often block github.com entirely. This command tries
|
|
@@ -25,13 +25,13 @@ import { join } from 'node:path';
|
|
|
25
25
|
import os from 'node:os';
|
|
26
26
|
import { debug } from '../debug.js';
|
|
27
27
|
|
|
28
|
-
const REPO = 'rootstudioyaml/
|
|
28
|
+
const REPO = 'rootstudioyaml/sprag';
|
|
29
29
|
const ISSUES_URL = `https://github.com/${REPO}/issues`;
|
|
30
30
|
|
|
31
31
|
// Anonymous submission endpoint (Google Form). A Google Form's formResponse
|
|
32
32
|
// URL accepts unauthenticated POSTs, which is exactly the property a
|
|
33
33
|
// login-free, GitHub-blocked-network path needs. Responses land in the
|
|
34
|
-
// maintainer's "
|
|
34
|
+
// maintainer's "sprag 피드백 (Feedback)" form (published
|
|
35
35
|
// 2026-09-13, responder access: anyone with the link).
|
|
36
36
|
// id — the /d/e/<id>/ segment of the form URL
|
|
37
37
|
// message — entry.NNNN field id of the message question
|
|
@@ -102,7 +102,7 @@ export async function run({ args, getArg, version, dataDir }) {
|
|
|
102
102
|
const explicitTitle = getArg('--title');
|
|
103
103
|
|
|
104
104
|
if (!message) {
|
|
105
|
-
console.log('usage:
|
|
105
|
+
console.log('usage: sprag feedback [--title "<제목>"] "<내용>"');
|
|
106
106
|
console.log(` (GitHub에서 직접 제보: ${ISSUES_URL})`);
|
|
107
107
|
process.exitCode = 1;
|
|
108
108
|
return;
|