@haposoft/cafekit 0.16.7 → 0.18.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.
Files changed (231) hide show
  1. package/README.md +14 -22
  2. package/bin/__tests__/codex-hooks-ownership.test.js +2 -2
  3. package/bin/__tests__/codex-hooks.test.js +169 -272
  4. package/bin/__tests__/codex-native.test.js +100 -202
  5. package/bin/__tests__/develop-contract.test.js +122 -1413
  6. package/bin/__tests__/docs-sync-on-demand.test.js +93 -0
  7. package/bin/__tests__/grok-envelope.test.js +0 -56
  8. package/bin/__tests__/hook-payload.test.js +4 -6
  9. package/bin/__tests__/legacy-authority-decoupled.test.js +154 -0
  10. package/bin/__tests__/legacy-hooks-retired.test.js +221 -0
  11. package/bin/__tests__/omp-bridge.test.js +20 -17
  12. package/bin/__tests__/omp-hooks.test.js +0 -19
  13. package/bin/__tests__/optional-skill-inventory.test.js +112 -4
  14. package/bin/__tests__/orca-session.test.js +0 -2
  15. package/bin/__tests__/package-inventory.test.js +90 -925
  16. package/bin/__tests__/runtime-dir.test.js +0 -3
  17. package/bin/__tests__/runtime-schema.test.js +3 -2
  18. package/bin/__tests__/skill-routing-source.test.js +27 -15
  19. package/bin/__tests__/spec-narrowing.test.js +30 -161
  20. package/bin/__tests__/specs-v2-execution-closeout.test.js +0 -437
  21. package/bin/__tests__/test-runtime-dependency-closure.cjs +1 -2
  22. package/bin/__tests__/ui-ux-skill.test.js +101 -0
  23. package/bin/__tests__/usage-hook-retired.test.js +114 -0
  24. package/bin/__tests__/validator-grounding.test.js +18 -2949
  25. package/bin/lib/managed-writer.js +2 -3
  26. package/bin/phases/codex-runtime.js +27 -0
  27. package/bin/phases/copy-payload.js +13 -28
  28. package/bin/phases/omp-runtime.js +25 -0
  29. package/package.json +2 -3
  30. package/src/claude/CLAUDE.md +3 -10
  31. package/src/claude/agents/brainstormer.md +7 -3
  32. package/src/claude/agents/code-auditor.md +44 -56
  33. package/src/claude/agents/debugger.md +8 -88
  34. package/src/claude/agents/deployer.md +3 -5
  35. package/src/claude/agents/docs-keeper.md +6 -14
  36. package/src/claude/agents/git-ops.md +2 -4
  37. package/src/claude/agents/implementer.md +12 -16
  38. package/src/claude/agents/inspector.md +4 -3
  39. package/src/claude/agents/project-manager.md +9 -17
  40. package/src/claude/agents/researcher.md +9 -3
  41. package/src/claude/agents/spec-maker.md +15 -15
  42. package/src/claude/agents/test-runner.md +8 -11
  43. package/src/claude/agents/ui-ux-designer.md +0 -1
  44. package/src/claude/hooks/__tests__/fixtures/statusline-default.golden +1 -1
  45. package/src/claude/hooks/__tests__/spec-gate.test.js +244 -333
  46. package/src/claude/hooks/__tests__/spec-state-touched.test.js +160 -0
  47. package/src/claude/hooks/__tests__/state.test.js +68 -0
  48. package/src/claude/hooks/__tests__/statusline.test.js +140 -11
  49. package/src/claude/hooks/agent.cjs +2 -1
  50. package/src/claude/hooks/lib/hook-payload.cjs +2 -2
  51. package/src/claude/hooks/rules.cjs +2 -1
  52. package/src/claude/hooks/session.cjs +0 -1
  53. package/src/claude/hooks/spec-gate.cjs +17 -83
  54. package/src/claude/hooks/spec-state.cjs +142 -39
  55. package/src/claude/hooks/state.cjs +21 -2
  56. package/src/claude/migration-manifest.json +42 -22
  57. package/src/claude/output-styles/coding-level-0-eli5.md +9 -9
  58. package/src/claude/output-styles/coding-level-1-junior.md +3 -3
  59. package/src/claude/output-styles/coding-level-2-mid.md +3 -3
  60. package/src/claude/output-styles/coding-level-3-senior.md +2 -2
  61. package/src/claude/output-styles/coding-level-4-lead.md +5 -5
  62. package/src/claude/output-styles/coding-level-5-god.md +6 -6
  63. package/src/claude/rules/ai-dev-rules.md +2 -3
  64. package/src/claude/rules/hook-protocols.md +8 -15
  65. package/src/claude/rules/manage-docs.md +9 -23
  66. package/src/claude/rules/orchestrator.md +3 -4
  67. package/src/claude/rules/review-audit-self-decision.md +3 -3
  68. package/src/claude/rules/skill-domain-routing.md +1 -3
  69. package/src/claude/rules/skill-workflow-routing.md +4 -5
  70. package/src/claude/rules/state-sync.md +3 -12
  71. package/src/claude/rules/workflow.md +11 -19
  72. package/src/claude/runtime.schema.json +5 -3
  73. package/src/claude/scripts/generate-skill-catalog.cjs +7 -6
  74. package/src/claude/scripts/provenance.cjs +23 -5
  75. package/src/claude/scripts/spec-receipt.cjs +24 -121
  76. package/src/claude/scripts/spec-resolver.cjs +41 -458
  77. package/src/claude/scripts/workflow-policy.cjs +3 -1801
  78. package/src/claude/settings/settings.json +1 -32
  79. package/src/claude/skills/agent-browser/SKILL.md +1 -1
  80. package/src/claude/skills/{question → ask}/SKILL.md +9 -75
  81. package/src/claude/skills/brainstorm/SKILL.md +43 -131
  82. package/src/claude/skills/chrome-devtools/SKILL.md +14 -31
  83. package/src/claude/skills/code-review/SKILL.md +29 -81
  84. package/src/claude/skills/code-review/references/spec-compliance-review.md +4 -8
  85. package/src/claude/skills/debug/SKILL.md +29 -149
  86. package/src/claude/skills/develop/SKILL.md +26 -83
  87. package/src/claude/skills/develop/references/parallel-waves.md +0 -8
  88. package/src/claude/skills/develop/references/quality-gate.md +15 -13
  89. package/src/claude/skills/develop/references/subagent-patterns.md +0 -6
  90. package/src/claude/skills/docs/SKILL.md +2 -2
  91. package/src/claude/skills/docs/references/update-workflow.md +1 -1
  92. package/src/claude/skills/fix/SKILL.md +153 -0
  93. package/src/claude/skills/{hotfix → fix}/references/diagnosis-protocol.md +2 -2
  94. package/src/claude/skills/{hotfix → fix}/references/parallel-patterns.md +2 -2
  95. package/src/claude/skills/{hotfix → fix}/references/prevention-gate.md +3 -3
  96. package/src/claude/skills/{hotfix → fix}/references/review-cycle.md +1 -1
  97. package/src/claude/skills/git/SKILL.md +48 -9
  98. package/src/claude/skills/git/references/commit-protocols.md +35 -16
  99. package/src/claude/skills/git/references/worktree-blueprint.md +42 -6
  100. package/src/claude/skills/orca/SKILL.md +9 -8
  101. package/src/claude/skills/research/SKILL.md +17 -46
  102. package/src/claude/skills/scout/SKILL.md +61 -0
  103. package/src/claude/skills/{inspect → scout}/references/internal-inspection.md +26 -10
  104. package/src/claude/skills/specs/SKILL.md +22 -29
  105. package/src/claude/skills/specs/references/review.md +8 -6
  106. package/src/claude/skills/specs/references/templates.md +34 -41
  107. package/src/claude/skills/sync/SKILL.md +16 -45
  108. package/src/claude/skills/sync/references/rebind-and-audit.md +59 -0
  109. package/src/claude/skills/sync/references/sync-protocols.md +4 -13
  110. package/src/claude/skills/test/SKILL.md +39 -104
  111. package/src/claude/skills/test/references/execution-strategy.md +93 -21
  112. package/src/claude/skills/test/references/failure-triage.md +1 -1
  113. package/src/claude/skills/ui-ux/LICENSE +21 -0
  114. package/src/claude/skills/ui-ux/SKILL.md +497 -0
  115. package/src/claude/skills/ui-ux/references/brand-tokens.md +90 -0
  116. package/src/claude/skills/ui-ux/references/budgets.md +113 -0
  117. package/src/claude/skills/ui-ux/references/checklist.md +271 -0
  118. package/src/claude/skills/ui-ux/references/components/accordion.md +246 -0
  119. package/src/claude/skills/ui-ux/references/components/avatar.md +153 -0
  120. package/src/claude/skills/ui-ux/references/components/banner.md +43 -0
  121. package/src/claude/skills/ui-ux/references/components/breadcrumb.md +201 -0
  122. package/src/claude/skills/ui-ux/references/components/button.md +142 -0
  123. package/src/claude/skills/ui-ux/references/components/card.md +113 -0
  124. package/src/claude/skills/ui-ux/references/components/charts.md +376 -0
  125. package/src/claude/skills/ui-ux/references/components/chat.md +132 -0
  126. package/src/claude/skills/ui-ux/references/components/choice-controls.md +369 -0
  127. package/src/claude/skills/ui-ux/references/components/comment-thread.md +17 -0
  128. package/src/claude/skills/ui-ux/references/components/description-list.md +116 -0
  129. package/src/claude/skills/ui-ux/references/components/empty-state.md +98 -0
  130. package/src/claude/skills/ui-ux/references/components/file-upload.md +107 -0
  131. package/src/claude/skills/ui-ux/references/components/inline-edit.md +128 -0
  132. package/src/claude/skills/ui-ux/references/components/input.md +161 -0
  133. package/src/claude/skills/ui-ux/references/components/list-row.md +56 -0
  134. package/src/claude/skills/ui-ux/references/components/loading.md +198 -0
  135. package/src/claude/skills/ui-ux/references/components/logo.md +147 -0
  136. package/src/claude/skills/ui-ux/references/components/otp-input.md +43 -0
  137. package/src/claude/skills/ui-ux/references/components/quantity-input.md +100 -0
  138. package/src/claude/skills/ui-ux/references/components/range-slider.md +16 -0
  139. package/src/claude/skills/ui-ux/references/components/small-controls.md +340 -0
  140. package/src/claude/skills/ui-ux/references/components/sortable-header.md +86 -0
  141. package/src/claude/skills/ui-ux/references/components/tag-input.md +14 -0
  142. package/src/claude/skills/ui-ux/references/components/timeline.md +38 -0
  143. package/src/claude/skills/ui-ux/references/components/tree.md +30 -0
  144. package/src/claude/skills/ui-ux/references/design-process.md +650 -0
  145. package/src/claude/skills/ui-ux/references/layouts/app-kanban.html +303 -0
  146. package/src/claude/skills/ui-ux/references/layouts/app.md +1369 -0
  147. package/src/claude/skills/ui-ux/references/layouts/form.md +430 -0
  148. package/src/claude/skills/ui-ux/references/layouts/overlay.md +552 -0
  149. package/src/claude/skills/ui-ux/references/layouts/pricing.md +204 -0
  150. package/src/claude/skills/ui-ux/references/locked-rules.md +35 -0
  151. package/src/claude/skills/ui-ux/references/principles.md +408 -0
  152. package/src/claude/skills/ui-ux/references/refactor.md +245 -0
  153. package/src/claude/skills/ui-ux/references/responsive.md +195 -0
  154. package/src/claude/skills/ui-ux/references/review.md +528 -0
  155. package/src/claude/skills/ui-ux/references/rules-color.md +623 -0
  156. package/src/claude/skills/ui-ux/references/rules-form.md +235 -0
  157. package/src/claude/skills/ui-ux/references/rules-state.md +680 -0
  158. package/src/claude/skills/ui-ux/references/rules-type.md +369 -0
  159. package/src/claude/skills/ui-ux/references/styles.md +311 -0
  160. package/src/claude/skills/ui-ux/references/system.md +219 -0
  161. package/src/claude/skills/ui-ux/references/tailwind-v4-traps.md +237 -0
  162. package/src/claude/skills/ui-ux/references/tokens.css +362 -0
  163. package/src/claude/skills/ui-ux/scripts/lint-skill.mjs +216 -0
  164. package/src/claude/skills/ui-ux/scripts/package.json +9 -0
  165. package/src/claude/skills/ui-ux/scripts/probe.mjs +4447 -0
  166. package/src/claude/skills/ui-ux-pro-max/SKILL.md +25 -80
  167. package/src/claude/skills/web-testing/SKILL.md +2 -20
  168. package/src/claude/status.cjs +149 -144
  169. package/src/codex/AGENTS.md +8 -13
  170. package/src/codex/hooks/agent.cjs +2 -1
  171. package/src/codex/hooks/lib/spec-receipt.cjs +4 -32
  172. package/src/codex/hooks/lib/spec-utils.cjs +6 -52
  173. package/src/codex/hooks/rules.cjs +2 -1
  174. package/src/codex/hooks/session.cjs +0 -2
  175. package/src/codex/hooks/spec-gate.cjs +13 -81
  176. package/src/codex/hooks/spec-state.cjs +37 -41
  177. package/src/codex/hooks.json +0 -20
  178. package/src/codex/rules/state-sync.md +3 -12
  179. package/src/common/AGENTS.md +6 -12
  180. package/src/omp/extensions/cafekit-bridge.mjs +6 -6
  181. package/bin/__tests__/change-firewall.semantic-firewall.test.js +0 -2133
  182. package/bin/__tests__/finalizer-pass-gate.semantic-firewall.test.js +0 -342
  183. package/bin/__tests__/release-preflight.semantic-firewall.test.js +0 -586
  184. package/bin/__tests__/review-receipt-c2.semantic-firewall.test.js +0 -378
  185. package/bin/__tests__/specs-v2-policy-and-scaffold.test.js +0 -665
  186. package/bin/__tests__/specs-v2-validator-grounder-contract.test.js +0 -1132
  187. package/src/claude/hooks/__tests__/completion-authority.test.js +0 -990
  188. package/src/claude/hooks/__tests__/semantic-review-authority.test.js +0 -259
  189. package/src/claude/hooks/completion-authority-check.cjs +0 -56
  190. package/src/claude/hooks/completion-authority-state.cjs +0 -527
  191. package/src/claude/hooks/completion-authority.cjs +0 -163
  192. package/src/claude/hooks/lib/context.cjs +0 -616
  193. package/src/claude/hooks/lib/detect.cjs +0 -474
  194. package/src/claude/hooks/semantic-review-authority.cjs +0 -154
  195. package/src/claude/hooks/task-scaffold-guard.cjs +0 -114
  196. package/src/claude/hooks/usage.cjs +0 -197
  197. package/src/claude/scripts/change-firewall.cjs +0 -1748
  198. package/src/claude/scripts/spec-authoring-digest.cjs +0 -151
  199. package/src/claude/scripts/spec-authoring-validation.cjs +0 -126
  200. package/src/claude/scripts/spec-final-state.cjs +0 -290
  201. package/src/claude/scripts/spec-ground.cjs +0 -650
  202. package/src/claude/scripts/spec-readiness.cjs +0 -425
  203. package/src/claude/scripts/spec-scaffold.cjs +0 -2317
  204. package/src/claude/scripts/spec-semantic-model.cjs +0 -396
  205. package/src/claude/scripts/validate-spec-output.cjs +0 -3941
  206. package/src/claude/skills/code-review/references/verification-gate.md +0 -46
  207. package/src/claude/skills/delegate/SKILL.md +0 -124
  208. package/src/claude/skills/delegate/references/codex-delegation.md +0 -71
  209. package/src/claude/skills/delegate/references/grok-delegation.md +0 -84
  210. package/src/claude/skills/hotfix/SKILL.md +0 -289
  211. package/src/claude/skills/inspect/SKILL.md +0 -142
  212. package/src/claude/skills/loop/SKILL.md +0 -114
  213. package/src/claude/skills/loop/references/bounded-loop-protocol.md +0 -104
  214. package/src/claude/skills/loop/references/metric-and-guard-contract.md +0 -76
  215. package/src/claude/skills/route/SKILL.md +0 -94
  216. package/src/claude/skills/route/references/agent-timing.md +0 -46
  217. package/src/claude/skills/route/references/chaining-patterns.md +0 -53
  218. package/src/claude/skills/route/references/task-taxonomy.md +0 -48
  219. package/src/claude/skills/specs/templates/design.md +0 -82
  220. package/src/claude/skills/specs/templates/requirements-init.md +0 -8
  221. package/src/claude/skills/specs/templates/requirements.md +0 -42
  222. package/src/claude/skills/specs/templates/spec-state.json +0 -56
  223. package/src/claude/skills/specs/templates/task.md +0 -47
  224. package/src/codex/hooks/completion-authority-check.cjs +0 -67
  225. package/src/codex/hooks/completion-authority-state.cjs +0 -527
  226. package/src/codex/hooks/completion-authority.cjs +0 -109
  227. package/src/codex/hooks/semantic-review-authority.cjs +0 -130
  228. package/src/codex/hooks/task-scaffold-guard.cjs +0 -63
  229. /package/src/claude/skills/{question → ask}/templates/question.md +0 -0
  230. /package/src/claude/skills/{hotfix → fix}/references/escalation-tactics.md +0 -0
  231. /package/src/claude/skills/{hotfix → fix}/references/workflow-specialized.md +0 -0
@@ -1,114 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Copyright (c) 2026 Haposoft. MIT License.
4
- *
5
- * PreToolUse Hook — task-scaffold-guard.cjs
6
- * Implements: https://docs.anthropic.com/en/docs/claude-code/hooks
7
- *
8
- * Forces spec task files to be GENERATED via spec-scaffold.cjs instead of
9
- * hand-written. Field tests showed the model dodges the (opt-in) scaffold step
10
- * and raw-`Write`s every task file — the single biggest output-token cost of a
11
- * spec run. This hook blocks `Write` to `specs/<feature>/tasks/task-*.md`, so
12
- * the only path to a task file is: scaffold (creates the stub) -> Edit (fills it).
13
- *
14
- * Scope is deliberately narrow — it only ever blocks the `Write` tool on a task
15
- * file path. `Edit`/`MultiEdit` (filling a stub), and `Write` to any other file
16
- * (requirements.md, design.md, source code) are never touched. The scaffold
17
- * script itself writes via Node fs through a Bash call, not the `Write` tool, so
18
- * it is never blocked.
19
- *
20
- * Safety valves:
21
- * 1. fail-open when spec-scaffold.cjs is absent — you cannot force a tool that
22
- * is not installed, so a hook shipped without its script must not deadlock.
23
- * 2. actionable block message — prints the exact scaffold command to run.
24
- * 3. escape hatch — "spec": { "scaffold_guard": false } in <runtime>/runtime.json.
25
- *
26
- * Exit: 0 = allow, 2 = block.
27
- */
28
-
29
- try {
30
- const fs = require('fs');
31
- const path = require('path');
32
- const { runtimeDirName, runtimeDir, runtimePath } = require('./lib/runtime-dir.cjs');
33
-
34
- /**
35
- * Deny the call with a reason every host can read.
36
- *
37
- * The reason is the JSON `permissionDecisionReason`, which Claude Code reads natively and
38
- * grok honours "regardless of exit code"; the same text also goes to stderr, because on
39
- * an exit-2 denial that is the channel Claude Code feeds back, and grok reads only the
40
- * first stderr line. Exit 2 stays so a host that reads neither still blocks.
41
- */
42
- function denyWithReason(reason) {
43
- process.stdout.write(JSON.stringify({
44
- hookSpecificOutput: {
45
- hookEventName: 'PreToolUse',
46
- permissionDecision: 'deny',
47
- permissionDecisionReason: reason
48
- }
49
- }) + '\n');
50
- process.stderr.write(reason + '\n');
51
- process.exit(2);
52
- }
53
-
54
- const stdin = fs.readFileSync(0, 'utf8').trim();
55
- if (!stdin) process.exit(0);
56
-
57
- const { normalizeHookPayload } = require('./lib/hook-payload.cjs');
58
- const data = normalizeHookPayload(JSON.parse(stdin));
59
- const toolName = data.tool_name || '';
60
- const toolInput = data.tool_input || {};
61
- const cwd = data.cwd || process.cwd();
62
-
63
- // Only `Write` can create a file from scratch. Edit/MultiEdit require an
64
- // existing file, so filling a scaffolded stub is always allowed.
65
- if (toolName !== 'Write') process.exit(0);
66
-
67
- const filePath = toolInput.file_path || toolInput.path || '';
68
- if (!filePath) process.exit(0);
69
-
70
- // Match a spec task file: .../specs/<feature>/tasks/task-<...>.md
71
- const norm = filePath.replace(/\\/g, '/');
72
- const isTaskFile = /(^|\/)specs\/[^/]+\/tasks\/task-[^/]+\.md$/.test(norm);
73
- if (!isTaskFile) process.exit(0);
74
-
75
- // Valve 3: explicit escape hatch via <runtime>/runtime.json (fail-closed: a
76
- // missing/broken runtime.json keeps the guard ON).
77
- let runtime = {};
78
- try {
79
- const rp = runtimePath(cwd, 'runtime.json');
80
- if (fs.existsSync(rp)) runtime = JSON.parse(fs.readFileSync(rp, 'utf8'));
81
- } catch { /* malformed runtime.json -> keep guard on */ }
82
- if (runtime.spec && runtime.spec.scaffold_guard === false) process.exit(0);
83
-
84
- // Valve 1: fail-open if the scaffold script is not installed next to this hook
85
- // (hook lives in <runtime>/hooks/, script in <runtime>/scripts/). Forcing a tool
86
- // that does not exist would deadlock task creation.
87
- const scaffold = path.join(__dirname, '..', 'scripts', 'spec-scaffold.cjs');
88
- if (!fs.existsSync(scaffold)) process.exit(0);
89
-
90
- // Valve 2: block with an actionable message carrying the exact command.
91
- // NOTE: deliberately does NOT mention the runtime.json escape hatch — an
92
- // Smoke testing proved the model reads the advertised override and
93
- // simply disables the guard itself. The hatch stays functional for humans.
94
- const m = norm.match(/(^|\/)specs\/([^/]+)\/tasks\//);
95
- const feature = m ? m[2] : '<feature>';
96
- denyWithReason(
97
- `TASK SCAFFOLD REQUIRED: task files must be generated, not hand-written.\n` +
98
- `Blocked Write: ${filePath}\n\n` +
99
- `Generate the stub(s), then Edit-fill the {{...}} placeholders:\n` +
100
- ` node ${runtimeDirName()}/scripts/spec-scaffold.cjs ${feature} --tasks "R0-01-slug,R1-01-slug,..." --tasks-only\n` +
101
- `Then use Edit (not Write) on each tasks/task-*.md stub.`
102
- );
103
-
104
- } catch (e) {
105
- // Never let a hook crash block the user — log and fail-open.
106
- try {
107
- const fs = require('fs'), p = require('path');
108
- const d = require('./lib/hook-state-dir.cjs').hookStateDir();
109
- if (!fs.existsSync(d)) fs.mkdirSync(d, { recursive: true });
110
- fs.appendFileSync(p.join(d, 'hook-log.jsonl'),
111
- JSON.stringify({ ts: new Date().toISOString(), hook: 'task-scaffold-guard', status: 'crash', error: e.message }) + '\n');
112
- } catch (_) {}
113
- process.exit(0);
114
- }
@@ -1,197 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Copyright (c) 2026 Haposoft. MIT License.
4
- *
5
- * UserPromptSubmit & PostToolUse Hook — usage.cjs
6
- * Implements: https://docs.anthropic.com/en/docs/claude-code/hooks
7
- *
8
- * Fetches Claude Code usage limits from Anthropic OAuth API and writes to cache.
9
- * Cache is read by status.cjs for statusline display.
10
- *
11
- * ⚠ EXPERIMENTAL / UNDOCUMENTED API: api.anthropic.com/api/oauth/usage with the
12
- * "oauth-2025-04-20" beta header is not a public contract and may break or
13
- * change shape without notice. Every failure path degrades to
14
- * status:"unavailable" (statusline hides the segment) — never block on it.
15
- * Disable entirely with "usage": { "enabled": false } if it misbehaves.
16
- *
17
- * Features:
18
- * - Cross-platform credential retrieval (macOS Keychain, file-based)
19
- * - Throttled API calls: 1 min (prompt) / 5 min (tool use)
20
- * - Disable: set "usage": { "enabled": false } in <runtime>/runtime.json
21
- */
22
-
23
- // Crash wrapper
24
- try {
25
- const fs = require("fs");
26
- const path = require("path");
27
- const os = require("os");
28
- const { execSync } = require("child_process");
29
-
30
- const { runtimeDirName, runtimeDir, runtimePath } = require('./lib/runtime-dir.cjs');
31
- function readRuntime(cwd) {
32
- try {
33
- const rp = runtimePath(cwd, 'runtime.json');
34
- if (fs.existsSync(rp)) {
35
- return JSON.parse(fs.readFileSync(rp, 'utf-8'));
36
- }
37
- } catch { /* fail-open */ }
38
- return {};
39
- }
40
-
41
- // Cache configuration
42
- const USAGE_CACHE_FILE = path.join(os.tmpdir(), "ck-usage-limits-cache.json");
43
- const CACHE_TTL_MS = 60000; // 60 seconds
44
- const FETCH_INTERVAL_MS = 300000; // 5 minutes for PostToolUse
45
- const FETCH_INTERVAL_PROMPT_MS = 60000; // 1 minute for UserPromptSubmit
46
-
47
- /**
48
- * Get Claude OAuth credentials (cross-platform)
49
- */
50
- function getClaudeCredentials() {
51
- // macOS: Try Keychain first
52
- if (os.platform() === "darwin") {
53
- try {
54
- const result = execSync('security find-generic-password -s "Claude Code-credentials" -w', {
55
- timeout: 5000,
56
- encoding: "utf-8",
57
- stdio: ["pipe", "pipe", "ignore"],
58
- }).trim();
59
- const parsed = JSON.parse(result);
60
- if (parsed.claudeAiOauth?.accessToken) {
61
- return parsed.claudeAiOauth.accessToken;
62
- }
63
- } catch {
64
- // Fallback to file-based
65
- }
66
- }
67
-
68
- // File-based credentials (Linux/Windows, or macOS fallback)
69
- const credPath = path.join(os.homedir(), ".claude", ".credentials.json");
70
- try {
71
- const content = fs.readFileSync(credPath, "utf-8");
72
- const parsed = JSON.parse(content);
73
- return parsed.claudeAiOauth?.accessToken || null;
74
- } catch {
75
- return null;
76
- }
77
- }
78
-
79
- /**
80
- * Check if we should fetch (throttled)
81
- */
82
- function shouldFetch(isUserPrompt = false) {
83
- const interval = isUserPrompt ? FETCH_INTERVAL_PROMPT_MS : FETCH_INTERVAL_MS;
84
-
85
- try {
86
- if (fs.existsSync(USAGE_CACHE_FILE)) {
87
- const cache = JSON.parse(fs.readFileSync(USAGE_CACHE_FILE, "utf-8"));
88
- if (Date.now() - cache.timestamp < interval) {
89
- return false;
90
- }
91
- }
92
- } catch {}
93
- return true;
94
- }
95
-
96
- /**
97
- * Write cache with status (available or unavailable)
98
- */
99
- function writeCache(status, data = null) {
100
- fs.writeFileSync(
101
- USAGE_CACHE_FILE,
102
- JSON.stringify({
103
- timestamp: Date.now(),
104
- status,
105
- data,
106
- })
107
- );
108
- }
109
-
110
- /**
111
- * Fetch usage limits from Anthropic OAuth API and write to cache
112
- * Always writes status to cache (available or unavailable) for statusline fallback
113
- */
114
- async function fetchAndCacheUsageLimits() {
115
- const token = getClaudeCredentials();
116
- if (!token) {
117
- writeCache("unavailable");
118
- return false;
119
- }
120
-
121
- try {
122
- const response = await fetch("https://api.anthropic.com/api/oauth/usage", {
123
- method: "GET",
124
- headers: {
125
- Accept: "application/json",
126
- "Content-Type": "application/json",
127
- Authorization: `Bearer ${token}`,
128
- "anthropic-beta": "oauth-2025-04-20",
129
- "User-Agent": "cafekit/1.0",
130
- },
131
- });
132
-
133
- if (!response.ok) {
134
- writeCache("unavailable");
135
- return false;
136
- }
137
-
138
- const data = await response.json();
139
- writeCache("available", data);
140
- return true;
141
- } catch {
142
- writeCache("unavailable");
143
- return false;
144
- }
145
- }
146
-
147
- /**
148
- * Main hook execution - just fetch and cache, no injection
149
- */
150
- async function main() {
151
- // Always allow operation to continue
152
- const result = { continue: true };
153
-
154
- try {
155
- // Read hook input
156
- let inputStr = "";
157
- try {
158
- inputStr = fs.readFileSync(0, "utf-8");
159
- } catch {}
160
-
161
- const input = JSON.parse(inputStr || "{}");
162
- const cwd = input.cwd || process.cwd();
163
- const runtime = readRuntime(cwd);
164
- if (runtime.usage?.enabled === false) {
165
- console.log(JSON.stringify(result));
166
- return;
167
- }
168
-
169
- // Detect hook type
170
- const isUserPrompt = typeof input.prompt === "string";
171
-
172
- // Check if we should fetch (throttled)
173
- if (shouldFetch(isUserPrompt)) {
174
- await fetchAndCacheUsageLimits();
175
- }
176
- } catch {}
177
-
178
- // Output result (no injection, just continue)
179
- console.log(JSON.stringify(result));
180
- }
181
-
182
- main().catch(() => {
183
- console.log(JSON.stringify({ continue: true }));
184
- process.exit(0);
185
- });
186
- } catch (e) {
187
- // Minimal crash logging (zero deps — only Node builtins)
188
- try {
189
- const fs = require('fs');
190
- const p = require('path');
191
- const logDir = require('./lib/hook-state-dir.cjs').hookStateDir();
192
- if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
193
- fs.appendFileSync(p.join(logDir, 'hook-log.jsonl'),
194
- JSON.stringify({ ts: new Date().toISOString(), hook: p.basename(__filename, '.cjs'), status: 'crash', error: e.message }) + '\n');
195
- } catch (_) {}
196
- process.exit(0); // fail-open
197
- }