aw-ecc 1.4.32 → 1.4.48

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 (259) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.cursor/INSTALL.md +7 -5
  3. package/.cursor/hooks/adapter.js +41 -4
  4. package/.cursor/hooks/after-agent-response.js +62 -0
  5. package/.cursor/hooks/before-submit-prompt.js +7 -1
  6. package/.cursor/hooks/post-tool-use-failure.js +21 -0
  7. package/.cursor/hooks/post-tool-use.js +39 -0
  8. package/.cursor/hooks/shared/aw-phase-definitions.js +53 -0
  9. package/.cursor/hooks/shared/aw-phase-runner.js +3 -1
  10. package/.cursor/hooks/subagent-start.js +22 -4
  11. package/.cursor/hooks/subagent-stop.js +18 -1
  12. package/.cursor/hooks.json +23 -2
  13. package/.opencode/package.json +1 -1
  14. package/AGENTS.md +3 -3
  15. package/README.md +5 -5
  16. package/commands/adk.md +52 -0
  17. package/commands/build.md +22 -9
  18. package/commands/deploy.md +12 -0
  19. package/commands/execute.md +9 -0
  20. package/commands/feature.md +333 -0
  21. package/commands/investigate.md +18 -5
  22. package/commands/plan.md +23 -9
  23. package/commands/publish.md +65 -0
  24. package/commands/review.md +12 -0
  25. package/commands/ship.md +12 -0
  26. package/commands/test.md +12 -0
  27. package/commands/verify.md +9 -0
  28. package/hooks/hooks.json +36 -0
  29. package/manifests/install-components.json +8 -0
  30. package/manifests/install-modules.json +83 -0
  31. package/manifests/install-profiles.json +7 -0
  32. package/package.json +2 -2
  33. package/scripts/ci/validate-rules.js +51 -0
  34. package/scripts/cursor-aw-home/hooks.json +23 -2
  35. package/scripts/cursor-aw-hooks/adapter.js +41 -4
  36. package/scripts/cursor-aw-hooks/before-submit-prompt.js +7 -1
  37. package/scripts/hooks/aw-usage-commit-created.js +32 -0
  38. package/scripts/hooks/aw-usage-post-tool-use-failure.js +56 -0
  39. package/scripts/hooks/aw-usage-post-tool-use.js +242 -0
  40. package/scripts/hooks/aw-usage-prompt-submit.js +112 -0
  41. package/scripts/hooks/aw-usage-session-start.js +48 -0
  42. package/scripts/hooks/aw-usage-stop.js +182 -0
  43. package/scripts/hooks/aw-usage-telemetry-send.js +84 -0
  44. package/scripts/hooks/cost-tracker.js +3 -23
  45. package/scripts/hooks/shared/aw-phase-definitions.js +53 -0
  46. package/scripts/hooks/shared/aw-phase-runner.js +3 -1
  47. package/scripts/lib/aw-hook-contract.js +2 -2
  48. package/scripts/lib/aw-pricing.js +306 -0
  49. package/scripts/lib/aw-usage-telemetry.js +472 -0
  50. package/scripts/lib/codex-hook-config.js +8 -8
  51. package/scripts/lib/cursor-hook-config.js +25 -10
  52. package/scripts/lib/install-targets/cursor-project.js +3 -0
  53. package/scripts/lib/install-targets/helpers.js +20 -3
  54. package/skills/aw-adk/SKILL.md +317 -0
  55. package/skills/aw-adk/agents/analyzer.md +113 -0
  56. package/skills/aw-adk/agents/comparator.md +113 -0
  57. package/skills/aw-adk/agents/grader.md +115 -0
  58. package/skills/aw-adk/assets/eval_review.html +76 -0
  59. package/skills/aw-adk/eval-viewer/generate_review.py +164 -0
  60. package/skills/aw-adk/eval-viewer/viewer.html +181 -0
  61. package/skills/aw-adk/evals/eval-colocated-placement.md +84 -0
  62. package/skills/aw-adk/evals/eval-create-agent.md +90 -0
  63. package/skills/aw-adk/evals/eval-create-command.md +98 -0
  64. package/skills/aw-adk/evals/eval-create-eval.md +89 -0
  65. package/skills/aw-adk/evals/eval-create-rule.md +99 -0
  66. package/skills/aw-adk/evals/eval-create-skill.md +97 -0
  67. package/skills/aw-adk/evals/eval-delete-agent.md +79 -0
  68. package/skills/aw-adk/evals/eval-delete-command.md +89 -0
  69. package/skills/aw-adk/evals/eval-delete-rule.md +86 -0
  70. package/skills/aw-adk/evals/eval-delete-skill.md +90 -0
  71. package/skills/aw-adk/evals/eval-meta-eval-coverage.md +78 -0
  72. package/skills/aw-adk/evals/eval-meta-eval-determinism.md +81 -0
  73. package/skills/aw-adk/evals/eval-meta-eval-false-pass.md +81 -0
  74. package/skills/aw-adk/evals/eval-score-accuracy.md +95 -0
  75. package/skills/aw-adk/evals/eval-type-redirect.md +68 -0
  76. package/skills/aw-adk/evals/evals.json +96 -0
  77. package/skills/aw-adk/references/artifact-wiring.md +162 -0
  78. package/skills/aw-adk/references/cross-ide-mapping.md +71 -0
  79. package/skills/aw-adk/references/eval-placement-guide.md +183 -0
  80. package/skills/aw-adk/references/external-resources.md +75 -0
  81. package/skills/aw-adk/references/getting-started.md +66 -0
  82. package/skills/aw-adk/references/registry-structure.md +152 -0
  83. package/skills/aw-adk/references/rubric-agent.md +36 -0
  84. package/skills/aw-adk/references/rubric-command.md +36 -0
  85. package/skills/aw-adk/references/rubric-eval.md +36 -0
  86. package/skills/aw-adk/references/rubric-meta-eval.md +132 -0
  87. package/skills/aw-adk/references/rubric-rule.md +36 -0
  88. package/skills/aw-adk/references/rubric-skill.md +36 -0
  89. package/skills/aw-adk/references/schemas.md +222 -0
  90. package/skills/aw-adk/references/template-agent.md +251 -0
  91. package/skills/aw-adk/references/template-command.md +279 -0
  92. package/skills/aw-adk/references/template-eval.md +176 -0
  93. package/skills/aw-adk/references/template-rule.md +119 -0
  94. package/skills/aw-adk/references/template-skill.md +123 -0
  95. package/skills/aw-adk/references/type-classifier.md +98 -0
  96. package/skills/aw-adk/references/writing-good-agents.md +227 -0
  97. package/skills/aw-adk/references/writing-good-commands.md +258 -0
  98. package/skills/aw-adk/references/writing-good-evals.md +271 -0
  99. package/skills/aw-adk/references/writing-good-rules.md +214 -0
  100. package/skills/aw-adk/references/writing-good-skills.md +159 -0
  101. package/skills/aw-adk/scripts/aggregate-benchmark.py +190 -0
  102. package/skills/aw-adk/scripts/lint-artifact.sh +211 -0
  103. package/skills/aw-adk/scripts/score-artifact.sh +179 -0
  104. package/skills/aw-adk/scripts/trigger-eval.py +192 -0
  105. package/skills/aw-build/SKILL.md +19 -2
  106. package/skills/aw-deploy/SKILL.md +65 -3
  107. package/skills/aw-design/SKILL.md +156 -0
  108. package/skills/aw-design/references/highrise-tokens.md +394 -0
  109. package/skills/aw-design/references/micro-interactions.md +76 -0
  110. package/skills/aw-design/references/prompt-template.md +160 -0
  111. package/skills/aw-design/references/quality-checklist.md +70 -0
  112. package/skills/aw-design/references/self-review.md +497 -0
  113. package/skills/aw-design/references/stitch-workflow.md +127 -0
  114. package/skills/aw-feature/SKILL.md +293 -0
  115. package/skills/aw-investigate/SKILL.md +17 -0
  116. package/skills/aw-plan/SKILL.md +34 -3
  117. package/skills/aw-publish/SKILL.md +300 -0
  118. package/skills/aw-publish/evals/eval-confirmation-gate.md +60 -0
  119. package/skills/aw-publish/evals/eval-intent-detection.md +111 -0
  120. package/skills/aw-publish/evals/eval-push-modes.md +67 -0
  121. package/skills/aw-publish/evals/eval-rules-push.md +60 -0
  122. package/skills/aw-publish/evals/evals.json +29 -0
  123. package/skills/aw-publish/references/push-modes.md +38 -0
  124. package/skills/aw-review/SKILL.md +88 -9
  125. package/skills/aw-rules-review/SKILL.md +124 -0
  126. package/skills/aw-rules-review/agents/openai.yaml +3 -0
  127. package/skills/aw-rules-review/scripts/generate-review-template.mjs +323 -0
  128. package/skills/aw-ship/SKILL.md +16 -0
  129. package/skills/aw-spec/SKILL.md +15 -0
  130. package/skills/aw-tasks/SKILL.md +15 -0
  131. package/skills/aw-test/SKILL.md +16 -0
  132. package/skills/aw-yolo/SKILL.md +4 -0
  133. package/skills/diagnose/SKILL.md +121 -0
  134. package/skills/diagnose/scripts/hitl-loop.template.sh +41 -0
  135. package/skills/finish-only-when-green/SKILL.md +265 -0
  136. package/skills/grill-me/SKILL.md +24 -0
  137. package/skills/grill-with-docs/SKILL.md +92 -0
  138. package/skills/grill-with-docs/adr-format.md +47 -0
  139. package/skills/grill-with-docs/context-format.md +67 -0
  140. package/skills/improve-codebase-architecture/SKILL.md +75 -0
  141. package/skills/improve-codebase-architecture/deepening.md +37 -0
  142. package/skills/improve-codebase-architecture/interface-design.md +44 -0
  143. package/skills/improve-codebase-architecture/language.md +53 -0
  144. package/skills/local-ghl-setup-from-screenshot/SKILL.md +538 -0
  145. package/skills/tdd/SKILL.md +115 -0
  146. package/skills/tdd/deep-modules.md +33 -0
  147. package/skills/tdd/interface-design.md +31 -0
  148. package/skills/tdd/mocking.md +59 -0
  149. package/skills/tdd/refactoring.md +10 -0
  150. package/skills/tdd/tests.md +61 -0
  151. package/skills/to-issues/SKILL.md +62 -0
  152. package/skills/to-prd/SKILL.md +75 -0
  153. package/skills/using-aw-skills/SKILL.md +170 -237
  154. package/skills/using-aw-skills/hooks/session-start.sh +11 -41
  155. package/skills/zoom-out/SKILL.md +24 -0
  156. package/.codex/hooks/aw-post-tool-use.sh +0 -6
  157. package/.codex/hooks/aw-pre-tool-use.sh +0 -6
  158. package/.codex/hooks/aw-session-start.sh +0 -25
  159. package/.codex/hooks/aw-stop.sh +0 -6
  160. package/.codex/hooks/aw-user-prompt-submit.sh +0 -10
  161. package/.codex/hooks.json +0 -62
  162. package/.cursor/rules/common-agents.md +0 -53
  163. package/.cursor/rules/common-aw-routing.md +0 -43
  164. package/.cursor/rules/common-coding-style.md +0 -52
  165. package/.cursor/rules/common-development-workflow.md +0 -33
  166. package/.cursor/rules/common-git-workflow.md +0 -28
  167. package/.cursor/rules/common-hooks.md +0 -34
  168. package/.cursor/rules/common-patterns.md +0 -35
  169. package/.cursor/rules/common-performance.md +0 -59
  170. package/.cursor/rules/common-security.md +0 -33
  171. package/.cursor/rules/common-testing.md +0 -33
  172. package/.cursor/skills/api-and-interface-design/SKILL.md +0 -75
  173. package/.cursor/skills/article-writing/SKILL.md +0 -85
  174. package/.cursor/skills/aw-brainstorm/SKILL.md +0 -115
  175. package/.cursor/skills/aw-build/SKILL.md +0 -152
  176. package/.cursor/skills/aw-build/evals/build-stage-cases.json +0 -28
  177. package/.cursor/skills/aw-debug/SKILL.md +0 -49
  178. package/.cursor/skills/aw-deploy/SKILL.md +0 -101
  179. package/.cursor/skills/aw-deploy/evals/deploy-stage-cases.json +0 -32
  180. package/.cursor/skills/aw-execute/SKILL.md +0 -47
  181. package/.cursor/skills/aw-execute/references/mode-code.md +0 -47
  182. package/.cursor/skills/aw-execute/references/mode-docs.md +0 -28
  183. package/.cursor/skills/aw-execute/references/mode-infra.md +0 -44
  184. package/.cursor/skills/aw-execute/references/mode-migration.md +0 -58
  185. package/.cursor/skills/aw-execute/references/worker-implementer.md +0 -26
  186. package/.cursor/skills/aw-execute/references/worker-parallel-worker.md +0 -23
  187. package/.cursor/skills/aw-execute/references/worker-quality-reviewer.md +0 -23
  188. package/.cursor/skills/aw-execute/references/worker-spec-reviewer.md +0 -23
  189. package/.cursor/skills/aw-execute/scripts/build-worker-bundle.js +0 -229
  190. package/.cursor/skills/aw-finish/SKILL.md +0 -111
  191. package/.cursor/skills/aw-investigate/SKILL.md +0 -109
  192. package/.cursor/skills/aw-plan/SKILL.md +0 -368
  193. package/.cursor/skills/aw-prepare/SKILL.md +0 -118
  194. package/.cursor/skills/aw-review/SKILL.md +0 -118
  195. package/.cursor/skills/aw-ship/SKILL.md +0 -115
  196. package/.cursor/skills/aw-spec/SKILL.md +0 -104
  197. package/.cursor/skills/aw-tasks/SKILL.md +0 -138
  198. package/.cursor/skills/aw-test/SKILL.md +0 -118
  199. package/.cursor/skills/aw-verify/SKILL.md +0 -51
  200. package/.cursor/skills/aw-yolo/SKILL.md +0 -111
  201. package/.cursor/skills/browser-testing-with-devtools/SKILL.md +0 -81
  202. package/.cursor/skills/bun-runtime/SKILL.md +0 -84
  203. package/.cursor/skills/ci-cd-and-automation/SKILL.md +0 -71
  204. package/.cursor/skills/code-simplification/SKILL.md +0 -74
  205. package/.cursor/skills/content-engine/SKILL.md +0 -88
  206. package/.cursor/skills/context-engineering/SKILL.md +0 -74
  207. package/.cursor/skills/deprecation-and-migration/SKILL.md +0 -75
  208. package/.cursor/skills/documentation-and-adrs/SKILL.md +0 -75
  209. package/.cursor/skills/documentation-lookup/SKILL.md +0 -90
  210. package/.cursor/skills/frontend-slides/SKILL.md +0 -184
  211. package/.cursor/skills/frontend-slides/STYLE_PRESETS.md +0 -330
  212. package/.cursor/skills/frontend-ui-engineering/SKILL.md +0 -68
  213. package/.cursor/skills/git-workflow-and-versioning/SKILL.md +0 -75
  214. package/.cursor/skills/idea-refine/SKILL.md +0 -84
  215. package/.cursor/skills/incremental-implementation/SKILL.md +0 -75
  216. package/.cursor/skills/investor-materials/SKILL.md +0 -96
  217. package/.cursor/skills/investor-outreach/SKILL.md +0 -76
  218. package/.cursor/skills/market-research/SKILL.md +0 -75
  219. package/.cursor/skills/mcp-server-patterns/SKILL.md +0 -67
  220. package/.cursor/skills/nextjs-turbopack/SKILL.md +0 -44
  221. package/.cursor/skills/performance-optimization/SKILL.md +0 -77
  222. package/.cursor/skills/security-and-hardening/SKILL.md +0 -70
  223. package/.cursor/skills/using-aw-skills/SKILL.md +0 -290
  224. package/.cursor/skills/using-aw-skills/evals/skill-trigger-cases.tsv +0 -25
  225. package/.cursor/skills/using-aw-skills/evals/test-skill-triggers.sh +0 -171
  226. package/.cursor/skills/using-aw-skills/hooks/hooks.json +0 -9
  227. package/.cursor/skills/using-aw-skills/hooks/session-start.sh +0 -67
  228. package/.cursor/skills/using-platform-skills/SKILL.md +0 -163
  229. package/.cursor/skills/using-platform-skills/evals/platform-selection-cases.json +0 -52
  230. /package/.cursor/rules/{golang-coding-style.md → golang-coding-style.mdc} +0 -0
  231. /package/.cursor/rules/{golang-hooks.md → golang-hooks.mdc} +0 -0
  232. /package/.cursor/rules/{golang-patterns.md → golang-patterns.mdc} +0 -0
  233. /package/.cursor/rules/{golang-security.md → golang-security.mdc} +0 -0
  234. /package/.cursor/rules/{golang-testing.md → golang-testing.mdc} +0 -0
  235. /package/.cursor/rules/{kotlin-coding-style.md → kotlin-coding-style.mdc} +0 -0
  236. /package/.cursor/rules/{kotlin-hooks.md → kotlin-hooks.mdc} +0 -0
  237. /package/.cursor/rules/{kotlin-patterns.md → kotlin-patterns.mdc} +0 -0
  238. /package/.cursor/rules/{kotlin-security.md → kotlin-security.mdc} +0 -0
  239. /package/.cursor/rules/{kotlin-testing.md → kotlin-testing.mdc} +0 -0
  240. /package/.cursor/rules/{php-coding-style.md → php-coding-style.mdc} +0 -0
  241. /package/.cursor/rules/{php-hooks.md → php-hooks.mdc} +0 -0
  242. /package/.cursor/rules/{php-patterns.md → php-patterns.mdc} +0 -0
  243. /package/.cursor/rules/{php-security.md → php-security.mdc} +0 -0
  244. /package/.cursor/rules/{php-testing.md → php-testing.mdc} +0 -0
  245. /package/.cursor/rules/{python-coding-style.md → python-coding-style.mdc} +0 -0
  246. /package/.cursor/rules/{python-hooks.md → python-hooks.mdc} +0 -0
  247. /package/.cursor/rules/{python-patterns.md → python-patterns.mdc} +0 -0
  248. /package/.cursor/rules/{python-security.md → python-security.mdc} +0 -0
  249. /package/.cursor/rules/{python-testing.md → python-testing.mdc} +0 -0
  250. /package/.cursor/rules/{swift-coding-style.md → swift-coding-style.mdc} +0 -0
  251. /package/.cursor/rules/{swift-hooks.md → swift-hooks.mdc} +0 -0
  252. /package/.cursor/rules/{swift-patterns.md → swift-patterns.mdc} +0 -0
  253. /package/.cursor/rules/{swift-security.md → swift-security.mdc} +0 -0
  254. /package/.cursor/rules/{swift-testing.md → swift-testing.mdc} +0 -0
  255. /package/.cursor/rules/{typescript-coding-style.md → typescript-coding-style.mdc} +0 -0
  256. /package/.cursor/rules/{typescript-hooks.md → typescript-hooks.mdc} +0 -0
  257. /package/.cursor/rules/{typescript-patterns.md → typescript-patterns.mdc} +0 -0
  258. /package/.cursor/rules/{typescript-security.md → typescript-security.mdc} +0 -0
  259. /package/.cursor/rules/{typescript-testing.md → typescript-testing.mdc} +0 -0
@@ -13,32 +13,11 @@ const {
13
13
  appendFile,
14
14
  getClaudeDir,
15
15
  } = require('../lib/utils');
16
+ const { estimateCost, toNumber } = require('../lib/aw-pricing');
16
17
 
17
18
  const MAX_STDIN = 1024 * 1024;
18
19
  let raw = '';
19
20
 
20
- function toNumber(value) {
21
- const n = Number(value);
22
- return Number.isFinite(n) ? n : 0;
23
- }
24
-
25
- function estimateCost(model, inputTokens, outputTokens) {
26
- // Approximate per-1M-token blended rates. Conservative defaults.
27
- const table = {
28
- 'haiku': { in: 0.8, out: 4.0 },
29
- 'sonnet': { in: 3.0, out: 15.0 },
30
- 'opus': { in: 15.0, out: 75.0 },
31
- };
32
-
33
- const normalized = String(model || '').toLowerCase();
34
- let rates = table.sonnet;
35
- if (normalized.includes('haiku')) rates = table.haiku;
36
- if (normalized.includes('opus')) rates = table.opus;
37
-
38
- const cost = (inputTokens / 1_000_000) * rates.in + (outputTokens / 1_000_000) * rates.out;
39
- return Math.round(cost * 1e6) / 1e6;
40
- }
41
-
42
21
  process.stdin.setEncoding('utf8');
43
22
  process.stdin.on('data', chunk => {
44
23
  if (raw.length < MAX_STDIN) {
@@ -60,13 +39,14 @@ process.stdin.on('end', () => {
60
39
  const metricsDir = path.join(getClaudeDir(), 'metrics');
61
40
  ensureDir(metricsDir);
62
41
 
42
+ const cost = estimateCost(model, inputTokens, outputTokens);
63
43
  const row = {
64
44
  timestamp: new Date().toISOString(),
65
45
  session_id: sessionId,
66
46
  model,
67
47
  input_tokens: inputTokens,
68
48
  output_tokens: outputTokens,
69
- estimated_cost_usd: estimateCost(model, inputTokens, outputTokens),
49
+ estimated_cost_usd: cost,
70
50
  };
71
51
 
72
52
  appendFile(path.join(metricsDir, 'costs.jsonl'), `${JSON.stringify(row)}\n`);
@@ -1,3 +1,16 @@
1
+ const PHASE_NAMES = Object.freeze({
2
+ SESSION_START: 'session-start',
3
+ USER_PROMPT_SUBMIT: 'user-prompt-submit',
4
+ PRE_TOOL_USE_SHELL: 'pre-tool-use-shell',
5
+ PRE_TOOL_USE_MCP: 'pre-tool-use-mcp',
6
+ POST_TOOL_USE_SHELL: 'post-tool-use-shell',
7
+ POST_TOOL_USE_FILE_EDIT: 'post-tool-use-file-edit',
8
+ POST_TOOL_USE_MCP: 'post-tool-use-mcp',
9
+ PRE_COMPACT: 'pre-compact',
10
+ SESSION_END: 'session-end',
11
+ STOP: 'stop',
12
+ });
13
+
1
14
  const SHARED_AW_PHASE_STEPS = Object.freeze({
2
15
  'session-start': [
3
16
  {
@@ -8,6 +21,13 @@ const SHARED_AW_PHASE_STEPS = Object.freeze({
8
21
  payloadMode: 'raw',
9
22
  outputMode: 'cursor-session-start',
10
23
  },
24
+ {
25
+ hookId: 'telemetry:session-start',
26
+ allowedProfiles: ['minimal', 'standard', 'strict'],
27
+ runner: 'node',
28
+ relativeScriptPath: 'scripts/hooks/aw-usage-session-start.js',
29
+ payloadMode: 'claude',
30
+ },
11
31
  ],
12
32
  'user-prompt-submit': [
13
33
  {
@@ -15,6 +35,13 @@ const SHARED_AW_PHASE_STEPS = Object.freeze({
15
35
  relativeScriptPath: '.cursor/hooks/shared/user-prompt-submit.sh',
16
36
  payloadMode: 'raw',
17
37
  },
38
+ {
39
+ hookId: 'telemetry:prompt-submit',
40
+ allowedProfiles: ['minimal', 'standard', 'strict'],
41
+ runner: 'node',
42
+ relativeScriptPath: 'scripts/hooks/aw-usage-prompt-submit.js',
43
+ payloadMode: 'claude',
44
+ },
18
45
  ],
19
46
  'pre-tool-use-shell': [
20
47
  {
@@ -70,6 +97,13 @@ const SHARED_AW_PHASE_STEPS = Object.freeze({
70
97
  relativeScriptPath: 'scripts/hooks/post-bash-build-complete.js',
71
98
  payloadMode: 'claude',
72
99
  },
100
+ {
101
+ hookId: 'telemetry:post-tool-use',
102
+ allowedProfiles: ['minimal', 'standard', 'strict'],
103
+ runner: 'node',
104
+ relativeScriptPath: 'scripts/hooks/aw-usage-post-tool-use.js',
105
+ payloadMode: 'claude',
106
+ },
73
107
  ],
74
108
  'post-tool-use-file-edit': [
75
109
  {
@@ -100,6 +134,13 @@ const SHARED_AW_PHASE_STEPS = Object.freeze({
100
134
  relativeScriptPath: 'scripts/hooks/post-edit-console-warn.js',
101
135
  payloadMode: 'claude',
102
136
  },
137
+ {
138
+ hookId: 'telemetry:post-tool-use',
139
+ allowedProfiles: ['minimal', 'standard', 'strict'],
140
+ runner: 'node',
141
+ relativeScriptPath: 'scripts/hooks/aw-usage-post-tool-use.js',
142
+ payloadMode: 'claude',
143
+ },
103
144
  ],
104
145
  'post-tool-use-mcp': [
105
146
  {
@@ -109,6 +150,13 @@ const SHARED_AW_PHASE_STEPS = Object.freeze({
109
150
  relativeScriptPath: 'scripts/hooks/post-mcp-log.js',
110
151
  payloadMode: 'raw',
111
152
  },
153
+ {
154
+ hookId: 'telemetry:post-tool-use',
155
+ allowedProfiles: ['minimal', 'standard', 'strict'],
156
+ runner: 'node',
157
+ relativeScriptPath: 'scripts/hooks/aw-usage-post-tool-use.js',
158
+ payloadMode: 'claude',
159
+ },
112
160
  ],
113
161
  'pre-compact': [
114
162
  {
@@ -125,6 +173,8 @@ const SHARED_AW_PHASE_STEPS = Object.freeze({
125
173
  relativeScriptPath: 'scripts/hooks/session-end-marker.js',
126
174
  payloadMode: 'claude',
127
175
  },
176
+ // telemetry:session-end removed — afterAgentResponse hook now emits
177
+ // response_completed per turn with direct token/cost data from Cursor.
128
178
  ],
129
179
  stop: [
130
180
  {
@@ -155,6 +205,8 @@ const SHARED_AW_PHASE_STEPS = Object.freeze({
155
205
  relativeScriptPath: 'scripts/hooks/cost-tracker.js',
156
206
  payloadMode: 'claude',
157
207
  },
208
+ // telemetry:stop removed — Cursor fires both sessionEnd and stop,
209
+ // causing duplicate session_ended events. Telemetry is in session-end phase only.
158
210
  ],
159
211
  });
160
212
 
@@ -167,6 +219,7 @@ function getSharedAwPhaseSteps(phaseName) {
167
219
  }
168
220
 
169
221
  module.exports = {
222
+ PHASE_NAMES,
170
223
  SHARED_AW_PHASE_STEPS,
171
224
  getSharedAwPhaseSteps,
172
225
  };
@@ -39,7 +39,9 @@ function formatCursorSessionStartOutput(stdout, fallbackRaw) {
39
39
  if (typeof additionalContext === 'string' && additionalContext.trim() !== '') {
40
40
  return `${JSON.stringify({ additional_context: additionalContext }, null, 2)}\n`;
41
41
  }
42
- } catch {}
42
+ } catch (_error) {
43
+ return fallbackRaw;
44
+ }
43
45
 
44
46
  return fallbackRaw;
45
47
  }
@@ -32,7 +32,7 @@ const AW_HOOK_PHASES = [
32
32
  tier: 'core',
33
33
  claudeEvent: 'PostToolUse',
34
34
  codexEvent: 'PostToolUse',
35
- cursorEvents: ['afterShellExecution', 'afterFileEdit', 'afterMCPExecution'],
35
+ cursorEvents: ['afterShellExecution', 'afterFileEdit', 'afterMCPExecution', 'postToolUse'],
36
36
  },
37
37
  {
38
38
  phase: 'Stop',
@@ -53,7 +53,7 @@ const AW_HOOK_PHASES = [
53
53
  tier: 'extended',
54
54
  claudeEvent: 'PostToolUseFailure',
55
55
  codexEvent: null,
56
- cursorEvents: [],
56
+ cursorEvents: ['postToolUseFailure'],
57
57
  },
58
58
  {
59
59
  phase: 'PreCompact',
@@ -0,0 +1,306 @@
1
+ /**
2
+ * AW Pricing — dynamic LLM cost estimation.
3
+ *
4
+ * Resolution order:
5
+ * 1. In-process memory cache
6
+ * 2. Disk cache (~/.aw/telemetry/pricing-cache.json, 24h TTL)
7
+ * 3. OpenRouter /api/v1/models fetch (3s timeout, no auth)
8
+ * 4. Hardcoded FALLBACK_PRICING (last resort)
9
+ * 5. null (model not found anywhere)
10
+ *
11
+ * CJS module — consistent with existing aw-ecc hook ecosystem.
12
+ */
13
+
14
+ 'use strict';
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const os = require('os');
19
+ const https = require('https');
20
+
21
+ const CACHE_PATH = path.join(os.homedir(), '.aw', 'telemetry', 'pricing-cache.json');
22
+ const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours
23
+ const FETCH_TIMEOUT = 3000; // 3 seconds
24
+ const OPENROUTER_URL = 'https://openrouter.ai/api/v1/models';
25
+
26
+ // ── Hardcoded fallback (last resort when API + cache both fail) ──────
27
+
28
+ const FALLBACK_PRICING = {
29
+ // Anthropic Claude (latest generation pricing)
30
+ 'haiku': { in: 1.00, out: 5.00 },
31
+ 'sonnet': { in: 3.00, out: 15.00 },
32
+ 'opus': { in: 5.00, out: 25.00 },
33
+ // OpenAI — GPT
34
+ 'gpt-5': { in: 1.25, out: 10.00 },
35
+ 'gpt-5-mini': { in: 0.25, out: 2.00 },
36
+ 'gpt-4o': { in: 2.50, out: 10.00 },
37
+ 'gpt-4o-mini': { in: 0.15, out: 0.60 },
38
+ 'gpt-4.1': { in: 2.00, out: 8.00 },
39
+ 'gpt-4.1-mini': { in: 0.40, out: 1.60 },
40
+ 'gpt-4.1-nano': { in: 0.10, out: 0.40 },
41
+ // OpenAI — reasoning
42
+ 'o1': { in: 15.00, out: 60.00 },
43
+ 'o1-mini': { in: 1.10, out: 4.40 },
44
+ 'o3': { in: 2.00, out: 8.00 },
45
+ 'o3-mini': { in: 1.10, out: 4.40 },
46
+ 'o4-mini': { in: 1.10, out: 4.40 },
47
+ // OpenAI — Codex CLI
48
+ 'codex-mini': { in: 1.50, out: 6.00 },
49
+ 'codex-1': { in: 1.50, out: 6.00 },
50
+ // OpenAI — GPT Codex (agentic coding)
51
+ 'gpt-5.1-codex-mini': { in: 0.25, out: 2.00 },
52
+ 'gpt-5.1-codex': { in: 1.25, out: 10.00 },
53
+ 'gpt-5.2-codex': { in: 1.75, out: 14.00 },
54
+ 'gpt-5.3-codex': { in: 1.75, out: 14.00 },
55
+ // Google Gemini
56
+ 'gemini-2.5-pro': { in: 1.25, out: 10.00 },
57
+ 'gemini-2.5-flash': { in: 0.30, out: 2.50 },
58
+ 'gemini-2.5-flash-lite': { in: 0.10, out: 0.40 },
59
+ };
60
+
61
+ // ── Helpers ──────────────────────────────────────────────────────────
62
+
63
+ function toNumber(value) {
64
+ const n = Number(value);
65
+ return Number.isFinite(n) ? n : 0;
66
+ }
67
+
68
+ // ── Memory cache ─────────────────────────────────────────────────────
69
+
70
+ let _pricingCache = null; // { models: { [key]: { in, out } }, fetched_at: string }
71
+
72
+ // ── Disk cache ───────────────────────────────────────────────────────
73
+
74
+ function readDiskCache() {
75
+ try {
76
+ const raw = fs.readFileSync(CACHE_PATH, 'utf8');
77
+ const data = JSON.parse(raw);
78
+ if (data && data.models && data.fetched_at) {
79
+ return data;
80
+ }
81
+ } catch { /* missing or corrupt — non-blocking */ }
82
+ return null;
83
+ }
84
+
85
+ function writeDiskCache(data) {
86
+ try {
87
+ fs.mkdirSync(path.dirname(CACHE_PATH), { recursive: true });
88
+ fs.writeFileSync(CACHE_PATH, JSON.stringify(data, null, 2) + '\n');
89
+ } catch { /* best effort */ }
90
+ }
91
+
92
+ function isCacheStale(cacheData) {
93
+ if (!cacheData || !cacheData.fetched_at) return true;
94
+ const age = Date.now() - new Date(cacheData.fetched_at).getTime();
95
+ return age > CACHE_TTL;
96
+ }
97
+
98
+ // ── OpenRouter fetch ─────────────────────────────────────────────────
99
+
100
+ function normalizeOpenRouterResponse(data) {
101
+ const models = {};
102
+ if (!Array.isArray(data)) return models;
103
+
104
+ for (const entry of data) {
105
+ if (!entry.id || !entry.pricing) continue;
106
+ const prompt = parseFloat(entry.pricing.prompt);
107
+ const completion = parseFloat(entry.pricing.completion);
108
+ if (!Number.isFinite(prompt) || !Number.isFinite(completion)) continue;
109
+
110
+ // Per-token → per-1M-token
111
+ const inRate = prompt * 1_000_000;
112
+ const outRate = completion * 1_000_000;
113
+
114
+ // Store under full OpenRouter id (e.g. "anthropic/claude-3.5-sonnet")
115
+ models[entry.id] = { in: inRate, out: outRate };
116
+
117
+ // Also store under the model slug after the slash (e.g. "claude-3.5-sonnet")
118
+ const slash = entry.id.indexOf('/');
119
+ if (slash > 0) {
120
+ const slug = entry.id.substring(slash + 1);
121
+ // Don't overwrite if slug already exists (first provider wins)
122
+ if (!models[slug]) {
123
+ models[slug] = { in: inRate, out: outRate };
124
+ }
125
+ }
126
+ }
127
+
128
+ return models;
129
+ }
130
+
131
+ /**
132
+ * Fetch pricing from OpenRouter. Returns a Promise that resolves to
133
+ * { models, fetched_at } or null on failure.
134
+ */
135
+ function fetchPricing() {
136
+ return new Promise((resolve) => {
137
+ const req = https.get(OPENROUTER_URL, { timeout: FETCH_TIMEOUT }, (res) => {
138
+ if (res.statusCode !== 200) {
139
+ res.resume(); // drain
140
+ resolve(null);
141
+ return;
142
+ }
143
+
144
+ let body = '';
145
+ res.setEncoding('utf8');
146
+ res.on('data', (chunk) => { body += chunk; });
147
+ res.on('end', () => {
148
+ try {
149
+ const json = JSON.parse(body);
150
+ const models = normalizeOpenRouterResponse(json.data);
151
+ if (Object.keys(models).length === 0) {
152
+ resolve(null);
153
+ return;
154
+ }
155
+ const cacheData = {
156
+ fetched_at: new Date().toISOString(),
157
+ model_count: Object.keys(models).length,
158
+ models,
159
+ };
160
+ resolve(cacheData);
161
+ } catch {
162
+ resolve(null);
163
+ }
164
+ });
165
+ });
166
+
167
+ req.on('error', () => resolve(null));
168
+ req.on('timeout', () => { req.destroy(); resolve(null); });
169
+ });
170
+ }
171
+
172
+ /**
173
+ * Non-blocking background refresh. Fetches pricing and updates both
174
+ * disk and memory caches. Never throws, never blocks the caller.
175
+ */
176
+ function refreshPricingAsync() {
177
+ fetchPricing().then((data) => {
178
+ if (data) {
179
+ writeDiskCache(data);
180
+ _pricingCache = data;
181
+ }
182
+ }).catch(() => { /* swallow — non-blocking */ });
183
+ }
184
+
185
+ // ── Load pricing (sync) ─────────────────────────────────────────────
186
+
187
+ /**
188
+ * Returns cached pricing data or null. Never fetches synchronously.
189
+ * Triggers a background refresh when cache is stale.
190
+ */
191
+ function loadPricingSync() {
192
+ // 1. Memory cache
193
+ if (_pricingCache && !isCacheStale(_pricingCache)) {
194
+ return _pricingCache;
195
+ }
196
+
197
+ // 2. Disk cache
198
+ const diskData = readDiskCache();
199
+ if (diskData) {
200
+ _pricingCache = diskData;
201
+ if (isCacheStale(diskData)) {
202
+ // Use stale data now, refresh in background for next time
203
+ refreshPricingAsync();
204
+ }
205
+ return _pricingCache;
206
+ }
207
+
208
+ // 3. No cache at all — trigger background fetch for next time
209
+ refreshPricingAsync();
210
+ return null;
211
+ }
212
+
213
+ // ── Model matching ───────────────────────────────────────────────────
214
+
215
+ /**
216
+ * Fuzzy-match a model string against a pricing map.
217
+ * Tries exact match first, then substring includes.
218
+ */
219
+ function findRates(model, pricingMap) {
220
+ if (!model || !pricingMap) return null;
221
+ const normalized = String(model).toLowerCase();
222
+
223
+ // Exact match
224
+ if (pricingMap[normalized]) return pricingMap[normalized];
225
+
226
+ // Match against keys using includes (both directions)
227
+ for (const [key, rates] of Object.entries(pricingMap)) {
228
+ const lowerKey = key.toLowerCase();
229
+ if (normalized.includes(lowerKey) || lowerKey.includes(normalized)) {
230
+ return rates;
231
+ }
232
+ }
233
+
234
+ return null;
235
+ }
236
+
237
+ // ── estimateCost ─────────────────────────────────────────────────────
238
+
239
+ /**
240
+ * Estimate the cost in USD for a model call.
241
+ *
242
+ * @param {string} model - Model identifier (e.g. "claude-sonnet-4-20250514", "gpt-4o")
243
+ * @param {number} inputTokens - Total input/prompt token count (includes cache tokens)
244
+ * @param {number} outputTokens - Output/completion token count
245
+ * @param {object} [opts] - Optional cache token breakdown
246
+ * @param {number} [opts.cacheReadTokens] - Tokens served from cache (billed at ~10% of input rate)
247
+ * @param {number} [opts.cacheWriteTokens] - Tokens written to cache (billed at ~125% of input rate)
248
+ * @returns {number|null} Estimated cost in USD, or null if unknown model or no tokens
249
+ */
250
+ function estimateCost(model, inputTokens, outputTokens, opts) {
251
+ if (!inputTokens && !outputTokens) return null;
252
+
253
+ const cacheRead = (opts && opts.cacheReadTokens) || 0;
254
+ const cacheWrite = (opts && opts.cacheWriteTokens) || 0;
255
+
256
+ function computeCost(rates) {
257
+ if (!rates) return null;
258
+ const inRate = rates.in;
259
+ const outRate = rates.out;
260
+
261
+ if (cacheRead || cacheWrite) {
262
+ const nonCached = Math.max(0, inputTokens - cacheRead - cacheWrite);
263
+ const cost =
264
+ (nonCached / 1_000_000) * inRate +
265
+ (cacheRead / 1_000_000) * (inRate * 0.1) +
266
+ (cacheWrite / 1_000_000) * (inRate * 1.25) +
267
+ (outputTokens / 1_000_000) * outRate;
268
+ return Math.round(cost * 1e6) / 1e6;
269
+ }
270
+
271
+ const cost = (inputTokens / 1_000_000) * inRate + (outputTokens / 1_000_000) * outRate;
272
+ return Math.round(cost * 1e6) / 1e6;
273
+ }
274
+
275
+ // Try dynamic pricing first (OpenRouter cache)
276
+ const cached = loadPricingSync();
277
+ if (cached && cached.models) {
278
+ const result = computeCost(findRates(model, cached.models));
279
+ if (result !== null) return result;
280
+ }
281
+
282
+ // Fallback to hardcoded pricing
283
+ const result = computeCost(findRates(model, FALLBACK_PRICING));
284
+ if (result !== null) return result;
285
+
286
+ // Model not found anywhere
287
+ return null;
288
+ }
289
+
290
+ module.exports = {
291
+ estimateCost,
292
+ toNumber,
293
+ loadPricingSync,
294
+ refreshPricingAsync,
295
+ FALLBACK_PRICING,
296
+ // Exposed for testing
297
+ _test: {
298
+ readDiskCache,
299
+ writeDiskCache,
300
+ isCacheStale,
301
+ normalizeOpenRouterResponse,
302
+ findRates,
303
+ CACHE_PATH,
304
+ CACHE_TTL,
305
+ },
306
+ };