aw-ecc 1.4.32 → 1.4.47

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 (258) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex/hooks/aw-post-tool-use.sh +8 -2
  3. package/.codex/hooks/aw-session-start.sh +11 -4
  4. package/.codex/hooks/aw-stop.sh +8 -2
  5. package/.codex/hooks/aw-user-prompt-submit.sh +10 -2
  6. package/.codex/hooks.json +8 -8
  7. package/.cursor/INSTALL.md +7 -5
  8. package/.cursor/hooks/adapter.js +41 -4
  9. package/.cursor/hooks/after-agent-response.js +62 -0
  10. package/.cursor/hooks/before-submit-prompt.js +7 -1
  11. package/.cursor/hooks/post-tool-use-failure.js +21 -0
  12. package/.cursor/hooks/post-tool-use.js +39 -0
  13. package/.cursor/hooks/shared/aw-phase-definitions.js +53 -0
  14. package/.cursor/hooks/shared/aw-phase-runner.js +3 -1
  15. package/.cursor/hooks/subagent-start.js +22 -4
  16. package/.cursor/hooks/subagent-stop.js +18 -1
  17. package/.cursor/hooks.json +23 -2
  18. package/.opencode/package.json +1 -1
  19. package/AGENTS.md +3 -3
  20. package/README.md +5 -5
  21. package/commands/adk.md +52 -0
  22. package/commands/build.md +22 -9
  23. package/commands/deploy.md +12 -0
  24. package/commands/execute.md +9 -0
  25. package/commands/feature.md +333 -0
  26. package/commands/investigate.md +18 -5
  27. package/commands/plan.md +23 -9
  28. package/commands/publish.md +65 -0
  29. package/commands/review.md +12 -0
  30. package/commands/ship.md +12 -0
  31. package/commands/test.md +12 -0
  32. package/commands/verify.md +9 -0
  33. package/hooks/hooks.json +36 -0
  34. package/manifests/install-components.json +8 -0
  35. package/manifests/install-modules.json +83 -0
  36. package/manifests/install-profiles.json +7 -0
  37. package/package.json +1 -1
  38. package/scripts/ci/validate-rules.js +51 -0
  39. package/scripts/cursor-aw-home/hooks.json +23 -2
  40. package/scripts/cursor-aw-hooks/adapter.js +41 -4
  41. package/scripts/cursor-aw-hooks/before-submit-prompt.js +7 -1
  42. package/scripts/hooks/aw-usage-commit-created.js +32 -0
  43. package/scripts/hooks/aw-usage-post-tool-use-failure.js +56 -0
  44. package/scripts/hooks/aw-usage-post-tool-use.js +242 -0
  45. package/scripts/hooks/aw-usage-prompt-submit.js +112 -0
  46. package/scripts/hooks/aw-usage-session-start.js +48 -0
  47. package/scripts/hooks/aw-usage-stop.js +182 -0
  48. package/scripts/hooks/aw-usage-telemetry-send.js +84 -0
  49. package/scripts/hooks/cost-tracker.js +3 -23
  50. package/scripts/hooks/shared/aw-phase-definitions.js +53 -0
  51. package/scripts/hooks/shared/aw-phase-runner.js +3 -1
  52. package/scripts/lib/aw-hook-contract.js +2 -2
  53. package/scripts/lib/aw-pricing.js +306 -0
  54. package/scripts/lib/aw-usage-telemetry.js +472 -0
  55. package/scripts/lib/codex-hook-config.js +8 -8
  56. package/scripts/lib/cursor-hook-config.js +25 -10
  57. package/scripts/lib/install-targets/cursor-project.js +3 -0
  58. package/scripts/lib/install-targets/helpers.js +20 -3
  59. package/skills/aw-adk/SKILL.md +317 -0
  60. package/skills/aw-adk/agents/analyzer.md +113 -0
  61. package/skills/aw-adk/agents/comparator.md +113 -0
  62. package/skills/aw-adk/agents/grader.md +115 -0
  63. package/skills/aw-adk/assets/eval_review.html +76 -0
  64. package/skills/aw-adk/eval-viewer/generate_review.py +164 -0
  65. package/skills/aw-adk/eval-viewer/viewer.html +181 -0
  66. package/skills/aw-adk/evals/eval-colocated-placement.md +84 -0
  67. package/skills/aw-adk/evals/eval-create-agent.md +90 -0
  68. package/skills/aw-adk/evals/eval-create-command.md +98 -0
  69. package/skills/aw-adk/evals/eval-create-eval.md +89 -0
  70. package/skills/aw-adk/evals/eval-create-rule.md +99 -0
  71. package/skills/aw-adk/evals/eval-create-skill.md +97 -0
  72. package/skills/aw-adk/evals/eval-delete-agent.md +79 -0
  73. package/skills/aw-adk/evals/eval-delete-command.md +89 -0
  74. package/skills/aw-adk/evals/eval-delete-rule.md +86 -0
  75. package/skills/aw-adk/evals/eval-delete-skill.md +90 -0
  76. package/skills/aw-adk/evals/eval-meta-eval-coverage.md +78 -0
  77. package/skills/aw-adk/evals/eval-meta-eval-determinism.md +81 -0
  78. package/skills/aw-adk/evals/eval-meta-eval-false-pass.md +81 -0
  79. package/skills/aw-adk/evals/eval-score-accuracy.md +95 -0
  80. package/skills/aw-adk/evals/eval-type-redirect.md +68 -0
  81. package/skills/aw-adk/evals/evals.json +96 -0
  82. package/skills/aw-adk/references/artifact-wiring.md +162 -0
  83. package/skills/aw-adk/references/cross-ide-mapping.md +71 -0
  84. package/skills/aw-adk/references/eval-placement-guide.md +183 -0
  85. package/skills/aw-adk/references/external-resources.md +75 -0
  86. package/skills/aw-adk/references/getting-started.md +66 -0
  87. package/skills/aw-adk/references/registry-structure.md +152 -0
  88. package/skills/aw-adk/references/rubric-agent.md +36 -0
  89. package/skills/aw-adk/references/rubric-command.md +36 -0
  90. package/skills/aw-adk/references/rubric-eval.md +36 -0
  91. package/skills/aw-adk/references/rubric-meta-eval.md +132 -0
  92. package/skills/aw-adk/references/rubric-rule.md +36 -0
  93. package/skills/aw-adk/references/rubric-skill.md +36 -0
  94. package/skills/aw-adk/references/schemas.md +222 -0
  95. package/skills/aw-adk/references/template-agent.md +251 -0
  96. package/skills/aw-adk/references/template-command.md +279 -0
  97. package/skills/aw-adk/references/template-eval.md +176 -0
  98. package/skills/aw-adk/references/template-rule.md +119 -0
  99. package/skills/aw-adk/references/template-skill.md +123 -0
  100. package/skills/aw-adk/references/type-classifier.md +98 -0
  101. package/skills/aw-adk/references/writing-good-agents.md +227 -0
  102. package/skills/aw-adk/references/writing-good-commands.md +258 -0
  103. package/skills/aw-adk/references/writing-good-evals.md +271 -0
  104. package/skills/aw-adk/references/writing-good-rules.md +214 -0
  105. package/skills/aw-adk/references/writing-good-skills.md +159 -0
  106. package/skills/aw-adk/scripts/aggregate-benchmark.py +190 -0
  107. package/skills/aw-adk/scripts/lint-artifact.sh +211 -0
  108. package/skills/aw-adk/scripts/score-artifact.sh +179 -0
  109. package/skills/aw-adk/scripts/trigger-eval.py +192 -0
  110. package/skills/aw-build/SKILL.md +19 -2
  111. package/skills/aw-deploy/SKILL.md +65 -3
  112. package/skills/aw-design/SKILL.md +156 -0
  113. package/skills/aw-design/references/highrise-tokens.md +394 -0
  114. package/skills/aw-design/references/micro-interactions.md +76 -0
  115. package/skills/aw-design/references/prompt-template.md +160 -0
  116. package/skills/aw-design/references/quality-checklist.md +70 -0
  117. package/skills/aw-design/references/self-review.md +497 -0
  118. package/skills/aw-design/references/stitch-workflow.md +127 -0
  119. package/skills/aw-feature/SKILL.md +293 -0
  120. package/skills/aw-investigate/SKILL.md +17 -0
  121. package/skills/aw-plan/SKILL.md +34 -3
  122. package/skills/aw-publish/SKILL.md +300 -0
  123. package/skills/aw-publish/evals/eval-confirmation-gate.md +60 -0
  124. package/skills/aw-publish/evals/eval-intent-detection.md +111 -0
  125. package/skills/aw-publish/evals/eval-push-modes.md +67 -0
  126. package/skills/aw-publish/evals/eval-rules-push.md +60 -0
  127. package/skills/aw-publish/evals/evals.json +29 -0
  128. package/skills/aw-publish/references/push-modes.md +38 -0
  129. package/skills/aw-review/SKILL.md +88 -9
  130. package/skills/aw-rules-review/SKILL.md +124 -0
  131. package/skills/aw-rules-review/agents/openai.yaml +3 -0
  132. package/skills/aw-rules-review/scripts/generate-review-template.mjs +323 -0
  133. package/skills/aw-ship/SKILL.md +16 -0
  134. package/skills/aw-spec/SKILL.md +15 -0
  135. package/skills/aw-tasks/SKILL.md +15 -0
  136. package/skills/aw-test/SKILL.md +16 -0
  137. package/skills/aw-yolo/SKILL.md +4 -0
  138. package/skills/diagnose/SKILL.md +121 -0
  139. package/skills/diagnose/scripts/hitl-loop.template.sh +41 -0
  140. package/skills/finish-only-when-green/SKILL.md +265 -0
  141. package/skills/grill-me/SKILL.md +24 -0
  142. package/skills/grill-with-docs/SKILL.md +92 -0
  143. package/skills/grill-with-docs/adr-format.md +47 -0
  144. package/skills/grill-with-docs/context-format.md +67 -0
  145. package/skills/improve-codebase-architecture/SKILL.md +75 -0
  146. package/skills/improve-codebase-architecture/deepening.md +37 -0
  147. package/skills/improve-codebase-architecture/interface-design.md +44 -0
  148. package/skills/improve-codebase-architecture/language.md +53 -0
  149. package/skills/local-ghl-setup-from-screenshot/SKILL.md +538 -0
  150. package/skills/tdd/SKILL.md +115 -0
  151. package/skills/tdd/deep-modules.md +33 -0
  152. package/skills/tdd/interface-design.md +31 -0
  153. package/skills/tdd/mocking.md +59 -0
  154. package/skills/tdd/refactoring.md +10 -0
  155. package/skills/tdd/tests.md +61 -0
  156. package/skills/to-issues/SKILL.md +62 -0
  157. package/skills/to-prd/SKILL.md +75 -0
  158. package/skills/using-aw-skills/SKILL.md +170 -237
  159. package/skills/using-aw-skills/hooks/session-start.sh +11 -41
  160. package/skills/zoom-out/SKILL.md +24 -0
  161. package/.cursor/rules/common-agents.md +0 -53
  162. package/.cursor/rules/common-aw-routing.md +0 -43
  163. package/.cursor/rules/common-coding-style.md +0 -52
  164. package/.cursor/rules/common-development-workflow.md +0 -33
  165. package/.cursor/rules/common-git-workflow.md +0 -28
  166. package/.cursor/rules/common-hooks.md +0 -34
  167. package/.cursor/rules/common-patterns.md +0 -35
  168. package/.cursor/rules/common-performance.md +0 -59
  169. package/.cursor/rules/common-security.md +0 -33
  170. package/.cursor/rules/common-testing.md +0 -33
  171. package/.cursor/skills/api-and-interface-design/SKILL.md +0 -75
  172. package/.cursor/skills/article-writing/SKILL.md +0 -85
  173. package/.cursor/skills/aw-brainstorm/SKILL.md +0 -115
  174. package/.cursor/skills/aw-build/SKILL.md +0 -152
  175. package/.cursor/skills/aw-build/evals/build-stage-cases.json +0 -28
  176. package/.cursor/skills/aw-debug/SKILL.md +0 -49
  177. package/.cursor/skills/aw-deploy/SKILL.md +0 -101
  178. package/.cursor/skills/aw-deploy/evals/deploy-stage-cases.json +0 -32
  179. package/.cursor/skills/aw-execute/SKILL.md +0 -47
  180. package/.cursor/skills/aw-execute/references/mode-code.md +0 -47
  181. package/.cursor/skills/aw-execute/references/mode-docs.md +0 -28
  182. package/.cursor/skills/aw-execute/references/mode-infra.md +0 -44
  183. package/.cursor/skills/aw-execute/references/mode-migration.md +0 -58
  184. package/.cursor/skills/aw-execute/references/worker-implementer.md +0 -26
  185. package/.cursor/skills/aw-execute/references/worker-parallel-worker.md +0 -23
  186. package/.cursor/skills/aw-execute/references/worker-quality-reviewer.md +0 -23
  187. package/.cursor/skills/aw-execute/references/worker-spec-reviewer.md +0 -23
  188. package/.cursor/skills/aw-execute/scripts/build-worker-bundle.js +0 -229
  189. package/.cursor/skills/aw-finish/SKILL.md +0 -111
  190. package/.cursor/skills/aw-investigate/SKILL.md +0 -109
  191. package/.cursor/skills/aw-plan/SKILL.md +0 -368
  192. package/.cursor/skills/aw-prepare/SKILL.md +0 -118
  193. package/.cursor/skills/aw-review/SKILL.md +0 -118
  194. package/.cursor/skills/aw-ship/SKILL.md +0 -115
  195. package/.cursor/skills/aw-spec/SKILL.md +0 -104
  196. package/.cursor/skills/aw-tasks/SKILL.md +0 -138
  197. package/.cursor/skills/aw-test/SKILL.md +0 -118
  198. package/.cursor/skills/aw-verify/SKILL.md +0 -51
  199. package/.cursor/skills/aw-yolo/SKILL.md +0 -111
  200. package/.cursor/skills/browser-testing-with-devtools/SKILL.md +0 -81
  201. package/.cursor/skills/bun-runtime/SKILL.md +0 -84
  202. package/.cursor/skills/ci-cd-and-automation/SKILL.md +0 -71
  203. package/.cursor/skills/code-simplification/SKILL.md +0 -74
  204. package/.cursor/skills/content-engine/SKILL.md +0 -88
  205. package/.cursor/skills/context-engineering/SKILL.md +0 -74
  206. package/.cursor/skills/deprecation-and-migration/SKILL.md +0 -75
  207. package/.cursor/skills/documentation-and-adrs/SKILL.md +0 -75
  208. package/.cursor/skills/documentation-lookup/SKILL.md +0 -90
  209. package/.cursor/skills/frontend-slides/SKILL.md +0 -184
  210. package/.cursor/skills/frontend-slides/STYLE_PRESETS.md +0 -330
  211. package/.cursor/skills/frontend-ui-engineering/SKILL.md +0 -68
  212. package/.cursor/skills/git-workflow-and-versioning/SKILL.md +0 -75
  213. package/.cursor/skills/idea-refine/SKILL.md +0 -84
  214. package/.cursor/skills/incremental-implementation/SKILL.md +0 -75
  215. package/.cursor/skills/investor-materials/SKILL.md +0 -96
  216. package/.cursor/skills/investor-outreach/SKILL.md +0 -76
  217. package/.cursor/skills/market-research/SKILL.md +0 -75
  218. package/.cursor/skills/mcp-server-patterns/SKILL.md +0 -67
  219. package/.cursor/skills/nextjs-turbopack/SKILL.md +0 -44
  220. package/.cursor/skills/performance-optimization/SKILL.md +0 -77
  221. package/.cursor/skills/security-and-hardening/SKILL.md +0 -70
  222. package/.cursor/skills/using-aw-skills/SKILL.md +0 -290
  223. package/.cursor/skills/using-aw-skills/evals/skill-trigger-cases.tsv +0 -25
  224. package/.cursor/skills/using-aw-skills/evals/test-skill-triggers.sh +0 -171
  225. package/.cursor/skills/using-aw-skills/hooks/hooks.json +0 -9
  226. package/.cursor/skills/using-aw-skills/hooks/session-start.sh +0 -67
  227. package/.cursor/skills/using-platform-skills/SKILL.md +0 -163
  228. package/.cursor/skills/using-platform-skills/evals/platform-selection-cases.json +0 -52
  229. /package/.cursor/rules/{golang-coding-style.md → golang-coding-style.mdc} +0 -0
  230. /package/.cursor/rules/{golang-hooks.md → golang-hooks.mdc} +0 -0
  231. /package/.cursor/rules/{golang-patterns.md → golang-patterns.mdc} +0 -0
  232. /package/.cursor/rules/{golang-security.md → golang-security.mdc} +0 -0
  233. /package/.cursor/rules/{golang-testing.md → golang-testing.mdc} +0 -0
  234. /package/.cursor/rules/{kotlin-coding-style.md → kotlin-coding-style.mdc} +0 -0
  235. /package/.cursor/rules/{kotlin-hooks.md → kotlin-hooks.mdc} +0 -0
  236. /package/.cursor/rules/{kotlin-patterns.md → kotlin-patterns.mdc} +0 -0
  237. /package/.cursor/rules/{kotlin-security.md → kotlin-security.mdc} +0 -0
  238. /package/.cursor/rules/{kotlin-testing.md → kotlin-testing.mdc} +0 -0
  239. /package/.cursor/rules/{php-coding-style.md → php-coding-style.mdc} +0 -0
  240. /package/.cursor/rules/{php-hooks.md → php-hooks.mdc} +0 -0
  241. /package/.cursor/rules/{php-patterns.md → php-patterns.mdc} +0 -0
  242. /package/.cursor/rules/{php-security.md → php-security.mdc} +0 -0
  243. /package/.cursor/rules/{php-testing.md → php-testing.mdc} +0 -0
  244. /package/.cursor/rules/{python-coding-style.md → python-coding-style.mdc} +0 -0
  245. /package/.cursor/rules/{python-hooks.md → python-hooks.mdc} +0 -0
  246. /package/.cursor/rules/{python-patterns.md → python-patterns.mdc} +0 -0
  247. /package/.cursor/rules/{python-security.md → python-security.mdc} +0 -0
  248. /package/.cursor/rules/{python-testing.md → python-testing.mdc} +0 -0
  249. /package/.cursor/rules/{swift-coding-style.md → swift-coding-style.mdc} +0 -0
  250. /package/.cursor/rules/{swift-hooks.md → swift-hooks.mdc} +0 -0
  251. /package/.cursor/rules/{swift-patterns.md → swift-patterns.mdc} +0 -0
  252. /package/.cursor/rules/{swift-security.md → swift-security.mdc} +0 -0
  253. /package/.cursor/rules/{swift-testing.md → swift-testing.mdc} +0 -0
  254. /package/.cursor/rules/{typescript-coding-style.md → typescript-coding-style.mdc} +0 -0
  255. /package/.cursor/rules/{typescript-hooks.md → typescript-hooks.mdc} +0 -0
  256. /package/.cursor/rules/{typescript-patterns.md → typescript-patterns.mdc} +0 -0
  257. /package/.cursor/rules/{typescript-security.md → typescript-security.mdc} +0 -0
  258. /package/.cursor/rules/{typescript-testing.md → typescript-testing.mdc} +0 -0
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Usage telemetry — PostToolUseFailure hook.
4
+ *
5
+ * Claude: error (string/object) + is_interrupt (boolean).
6
+ * Cursor: error_message (string) + failure_type ("timeout"/"error"/"permission_denied").
7
+ * Codex: does NOT support PostToolUseFailure — this hook won't fire.
8
+ *
9
+ * Outputs {} on stdout.
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ const { buildEvent, sendAsync } = require('../lib/aw-usage-telemetry');
15
+
16
+ const MAX_STDIN = 1024 * 1024;
17
+ let raw = '';
18
+
19
+ process.stdin.setEncoding('utf8');
20
+ process.stdin.on('data', chunk => {
21
+ if (raw.length < MAX_STDIN) {
22
+ raw += chunk.substring(0, MAX_STDIN - raw.length);
23
+ }
24
+ });
25
+
26
+ process.stdin.on('end', () => {
27
+ try {
28
+ const input = JSON.parse(raw);
29
+ const toolName = input.tool_name || 'unknown';
30
+
31
+ // Normalize error fields across Claude and Cursor
32
+ let errorMessage;
33
+ let failureType;
34
+
35
+ if (input.error_message !== undefined) {
36
+ // Cursor format
37
+ errorMessage = String(input.error_message || '');
38
+ failureType = input.failure_type || 'error';
39
+ } else {
40
+ // Claude format
41
+ const err = input.error;
42
+ errorMessage = typeof err === 'string' ? err : (err?.message || JSON.stringify(err) || 'unknown');
43
+ failureType = input.is_interrupt ? 'interrupt' : 'error';
44
+ }
45
+
46
+ sendAsync(buildEvent(input, 'tool_error', {
47
+ tool_name: toolName,
48
+ error_message: errorMessage.slice(0, 500),
49
+ failure_type: failureType,
50
+ }));
51
+ } catch {
52
+ // Non-blocking.
53
+ }
54
+
55
+ process.stdout.write('{}');
56
+ });
@@ -0,0 +1,242 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Usage telemetry — PostToolUse hook.
4
+ *
5
+ * Detects: skill invocations, agent spawns, skill pushes.
6
+ * PR contribution is tracked via Co-Authored-By trailer in prepare-commit-msg hook.
7
+ * Outputs {} on stdout (Claude/Codex parse stdout as JSON).
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ const {
13
+ buildEvent,
14
+ detectHarness,
15
+ sendAsync,
16
+ readSessionSkill,
17
+ tryAcquireDedupe,
18
+ } = require('../lib/aw-usage-telemetry');
19
+
20
+ const MAX_STDIN = 1024 * 1024;
21
+ function parseMaybeJsonObject(value) {
22
+ if (typeof value !== 'string') return value;
23
+ const trimmed = value.trim();
24
+ if (!trimmed || !/^[{[]/.test(trimmed)) return value;
25
+ try {
26
+ return JSON.parse(trimmed);
27
+ } catch {
28
+ return value;
29
+ }
30
+ }
31
+
32
+ function toObject(value) {
33
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
34
+ }
35
+
36
+ function getSessionId(input) {
37
+ return input?.session_id || input?.conversation_id || null;
38
+ }
39
+
40
+ function getCommand(input) {
41
+ return String(
42
+ input?.tool_input?.command
43
+ || input?.tool_input?.args?.command
44
+ || ''
45
+ );
46
+ }
47
+
48
+ function normalizeToolResult(input) {
49
+ const rawToolResponse = parseMaybeJsonObject(input?.tool_response);
50
+ const rawToolOutput = parseMaybeJsonObject(input?.tool_output);
51
+ const toolResponse = toObject(rawToolResponse);
52
+ const toolOutput = toObject(rawToolOutput);
53
+ const exitCode = [
54
+ input?.exit_code,
55
+ toolResponse.exit_code,
56
+ toolResponse.exitCode,
57
+ toolOutput.exit_code,
58
+ toolOutput.exitCode,
59
+ ].find(value => value !== undefined && value !== null && value !== '');
60
+ const messageParts = [
61
+ toolResponse.stderr,
62
+ toolOutput.stderr,
63
+ toolResponse.output,
64
+ toolOutput.output,
65
+ typeof rawToolResponse === 'string' ? rawToolResponse : '',
66
+ typeof rawToolOutput === 'string' ? rawToolOutput : '',
67
+ input?.stderr,
68
+ input?.output,
69
+ ].filter(value => typeof value === 'string' && value.trim());
70
+
71
+ return {
72
+ exitCode: exitCode === undefined ? null : Number(exitCode),
73
+ errorMessage: messageParts.join('\n').slice(0, 500),
74
+ };
75
+ }
76
+
77
+ function isExplicitFailureExitCode(exitCode) {
78
+ return exitCode !== null && Number.isFinite(exitCode) && exitCode !== 0;
79
+ }
80
+
81
+ function inferShellFailureFromMessage(toolName, errorMessage) {
82
+ if (toolName !== 'Shell' && toolName !== 'Bash') return false;
83
+ if (typeof errorMessage !== 'string' || !errorMessage.trim()) return false;
84
+ const patterns = [
85
+ /(?:^|\n)[^:\n]+:\s.*\bNo such file or directory\b/i,
86
+ /(?:^|\n)[^:\n]+:\s.*\bPermission denied\b/i,
87
+ /(?:^|\n)[^:\n]+:\s.*\bOperation not permitted\b/i,
88
+ /(?:^|\n)(?:bash|zsh|sh): .*?\bcommand not found\b/i,
89
+ /(?:^|\n)[^:\n]+:\s.*\bcannot access\b/i,
90
+ /(?:^|\n)[^:\n]+:\s.*\bis a directory\b/i,
91
+ ];
92
+ return patterns.some(pattern => pattern.test(errorMessage));
93
+ }
94
+
95
+ function shouldEmitSkillName(skillName) {
96
+ return Boolean(skillName) && skillName !== 'using-aw-skills';
97
+ }
98
+
99
+ function collectPostToolUseEvents(input, options = {}) {
100
+ const events = [];
101
+ const toolName = input?.tool_name || '';
102
+ const promptSkillOverride = options.promptSkillOverride || null;
103
+
104
+ // Skill detection: Claude uses tool_name='Skill', Cursor reads SKILL.md via 'Read' tool.
105
+ const filePath = input?.tool_input?.file_path || '';
106
+ const isSkillRead = toolName === 'Read' && /\/SKILL\.md$/i.test(filePath);
107
+
108
+ if (toolName === 'Skill' || isSkillRead) {
109
+ let skillName = input?.tool_input?.skill || input?.tool_input?.args?.skill || '';
110
+ // For Cursor: extract skill name from path (e.g. .../skills/<skill-name>/SKILL.md)
111
+ if (!skillName && isSkillRead) {
112
+ const pathMatch = filePath.match(/\/skills\/([^/]+)\/SKILL\.md$/i);
113
+ skillName = pathMatch ? pathMatch[1] : filePath.split('/').slice(-2, -1)[0] || '';
114
+ }
115
+ if (shouldEmitSkillName(skillName)) {
116
+ events.push({
117
+ eventType: 'skill_invoked',
118
+ payload: {
119
+ skill_name: skillName,
120
+ args: input?.tool_input?.args || '',
121
+ },
122
+ });
123
+ }
124
+ } else if (toolName === 'Agent') {
125
+ events.push({
126
+ eventType: 'agent_spawned',
127
+ payload: {
128
+ agent_type: input?.tool_input?.subagent_type || 'general-purpose',
129
+ description: input?.tool_input?.description || '',
130
+ },
131
+ });
132
+ }
133
+
134
+ const toolResult = normalizeToolResult(input);
135
+ if (isExplicitFailureExitCode(toolResult.exitCode)
136
+ || inferShellFailureFromMessage(toolName, toolResult.errorMessage)) {
137
+ const payload = {
138
+ tool_name: toolName || 'unknown',
139
+ error_message: toolResult.errorMessage,
140
+ failure_type: 'error',
141
+ };
142
+ if (isExplicitFailureExitCode(toolResult.exitCode)) {
143
+ payload.exit_code = toolResult.exitCode;
144
+ }
145
+ events.push({
146
+ eventType: 'tool_error',
147
+ payload,
148
+ });
149
+ }
150
+
151
+ if (toolName === 'Shell' || toolName === 'Bash') {
152
+ const cmd = getCommand(input);
153
+ // Codex skill detection: Bash commands that read SKILL.md files.
154
+ if (!promptSkillOverride) {
155
+ const skillCmdMatch = cmd.match(/\/skills\/([^/]+)\/SKILL\.md/i);
156
+ if (skillCmdMatch && shouldEmitSkillName(skillCmdMatch[1])) {
157
+ events.push({
158
+ eventType: 'skill_invoked',
159
+ payload: {
160
+ skill_name: skillCmdMatch[1],
161
+ args: '',
162
+ },
163
+ });
164
+ }
165
+ }
166
+ if (/\baw\s+push\b/.test(cmd)) {
167
+ const skillMatch = cmd.match(/aw\s+push\s+(\S+)/);
168
+ events.push({
169
+ eventType: 'skill_pushed',
170
+ payload: {
171
+ skill_name: skillMatch ? skillMatch[1] : '',
172
+ },
173
+ });
174
+ }
175
+ }
176
+
177
+ return events;
178
+ }
179
+
180
+ function processPostToolUseInput(input, deps = {}) {
181
+ const emit = typeof deps.emit === 'function' ? deps.emit : () => {};
182
+ const promptSkillOverride = deps.promptSkillOverride || readSessionSkill(
183
+ getSessionId(input),
184
+ input?.turn_id || null,
185
+ );
186
+ const events = collectPostToolUseEvents(input, { promptSkillOverride });
187
+ for (const event of events) {
188
+ emit(event.eventType, event.payload);
189
+ }
190
+ return events;
191
+ }
192
+
193
+ function main() {
194
+ let raw = '';
195
+
196
+ process.stdin.setEncoding('utf8');
197
+ process.stdin.on('data', chunk => {
198
+ if (raw.length < MAX_STDIN) {
199
+ raw += chunk.substring(0, MAX_STDIN - raw.length);
200
+ }
201
+ });
202
+
203
+ process.stdin.on('end', () => {
204
+ try {
205
+ const input = JSON.parse(raw);
206
+ const shouldDedup = detectHarness(input) === 'codex' || Boolean(input?.tool_use_id);
207
+ if (!shouldDedup || tryAcquireDedupe('post-tool-use', [
208
+ getSessionId(input),
209
+ input?.turn_id || '',
210
+ input?.tool_use_id || '',
211
+ input?.tool_name || '',
212
+ getCommand(input),
213
+ input?.tool_input?.file_path || '',
214
+ input?.tool_input?.description || '',
215
+ ])) {
216
+ processPostToolUseInput(input, {
217
+ emit(eventType, payload) {
218
+ sendAsync(buildEvent(input, eventType, payload));
219
+ },
220
+ });
221
+ }
222
+ } catch {
223
+ // Non-blocking — never fail the hook.
224
+ }
225
+
226
+ process.stdout.write('{}');
227
+ });
228
+ }
229
+
230
+ if (require.main === module) {
231
+ main();
232
+ }
233
+
234
+ module.exports = {
235
+ collectPostToolUseEvents,
236
+ inferShellFailureFromMessage,
237
+ isExplicitFailureExitCode,
238
+ normalizeToolResult,
239
+ parseMaybeJsonObject,
240
+ processPostToolUseInput,
241
+ shouldEmitSkillName,
242
+ };
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Usage telemetry — UserPromptSubmit hook.
4
+ *
5
+ * Emits prompt_submitted as a boundary marker.
6
+ * No matchers on any harness — always fires.
7
+ *
8
+ * Outputs {} on stdout.
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const {
14
+ buildEvent,
15
+ sendAsync,
16
+ persistSessionSkill,
17
+ resolvePromptText,
18
+ tryAcquireDedupe,
19
+ isCodexInternalTaskTitlePrompt,
20
+ } = require('../lib/aw-usage-telemetry');
21
+
22
+ const MAX_STDIN = 1024 * 1024;
23
+
24
+ function extractAwSlashCommand(input) {
25
+ const prompt = resolvePromptText(input).trim();
26
+ const match = prompt.match(/^\/(aw:[a-z0-9-]+)(?:\s+([\s\S]*\S))?\s*$/i);
27
+ if (!match) return null;
28
+ return {
29
+ skill_name: match[1].toLowerCase(),
30
+ args: match[2] || '',
31
+ source: 'user_prompt',
32
+ };
33
+ }
34
+
35
+ function getSessionId(input) {
36
+ return input?.session_id || input?.conversation_id || 'unknown';
37
+ }
38
+
39
+ function shouldSkipPromptSubmitTelemetry(input) {
40
+ return isCodexInternalTaskTitlePrompt(input);
41
+ }
42
+
43
+ function processPromptSubmitInput(input, deps = {}) {
44
+ const emit = typeof deps.emit === 'function' ? deps.emit : () => {};
45
+ const persistSkill = typeof deps.persistSkill === 'function' ? deps.persistSkill : () => {};
46
+ const events = [];
47
+
48
+ events.push({ eventType: 'prompt_submitted', payload: {} });
49
+
50
+ const skill = extractAwSlashCommand(input);
51
+ if (skill) {
52
+ persistSkill(getSessionId(input), input?.turn_id || null, skill);
53
+ events.push({
54
+ eventType: 'skill_invoked',
55
+ payload: {
56
+ skill_name: skill.skill_name,
57
+ args: skill.args,
58
+ },
59
+ });
60
+ }
61
+
62
+ for (const event of events) {
63
+ emit(event.eventType, event.payload);
64
+ }
65
+
66
+ return events;
67
+ }
68
+
69
+ function main() {
70
+ let raw = '';
71
+
72
+ process.stdin.setEncoding('utf8');
73
+ process.stdin.on('data', chunk => {
74
+ if (raw.length < MAX_STDIN) {
75
+ raw += chunk.substring(0, MAX_STDIN - raw.length);
76
+ }
77
+ });
78
+
79
+ process.stdin.on('end', () => {
80
+ try {
81
+ const input = JSON.parse(raw);
82
+ if (!shouldSkipPromptSubmitTelemetry(input)
83
+ && tryAcquireDedupe('prompt-submit', [
84
+ getSessionId(input),
85
+ input?.turn_id || '',
86
+ resolvePromptText(input),
87
+ ])) {
88
+ processPromptSubmitInput(input, {
89
+ emit(eventType, payload) {
90
+ sendAsync(buildEvent(input, eventType, payload));
91
+ },
92
+ persistSkill: persistSessionSkill,
93
+ });
94
+ }
95
+ } catch {
96
+ // Non-blocking.
97
+ }
98
+
99
+ process.stdout.write('{}');
100
+ });
101
+ }
102
+
103
+ if (require.main === module) {
104
+ main();
105
+ }
106
+
107
+ module.exports = {
108
+ extractAwSlashCommand,
109
+ processPromptSubmitInput,
110
+ resolvePromptText,
111
+ shouldSkipPromptSubmitTelemetry,
112
+ };
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Usage telemetry — SessionStart hook.
4
+ *
5
+ * Captures session_start event and persists the model for later hooks.
6
+ * Claude sends model on SessionStart only — this is the only chance to capture it.
7
+ *
8
+ * Outputs {} on stdout.
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const { buildEvent, sendAsync, persistSessionModel, pruneStaleSessionFiles } = require('../lib/aw-usage-telemetry');
14
+
15
+ const MAX_STDIN = 1024 * 1024;
16
+ let raw = '';
17
+
18
+ process.stdin.setEncoding('utf8');
19
+ process.stdin.on('data', chunk => {
20
+ if (raw.length < MAX_STDIN) {
21
+ raw += chunk.substring(0, MAX_STDIN - raw.length);
22
+ }
23
+ });
24
+
25
+ process.stdin.on('end', () => {
26
+ try {
27
+ const input = JSON.parse(raw);
28
+ const sessionId = input.session_id
29
+ || input._cursor?.conversation_id
30
+ || input.conversation_id
31
+ || null;
32
+ const model = input.model || input._cursor?.model || null;
33
+
34
+ // Prune stale session files (>24h) to prevent unbounded growth
35
+ pruneStaleSessionFiles();
36
+
37
+ // Persist model so PostToolUse/Stop hooks can read it
38
+ persistSessionModel(sessionId, model);
39
+
40
+ sendAsync(buildEvent(input, 'session_start', {
41
+ model: model || null,
42
+ }));
43
+ } catch {
44
+ // Non-blocking.
45
+ }
46
+
47
+ process.stdout.write('{}');
48
+ });
@@ -0,0 +1,182 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Usage telemetry — Stop hook.
4
+ *
5
+ * Captures response_completed per turn (fires after each Claude/Cursor/Codex response).
6
+ *
7
+ * No harness provides token usage in hook input directly. All three provide
8
+ * transcript_path — we parse the transcript JSONL to extract model, stop_reason,
9
+ * and usage for any harness.
10
+ *
11
+ * Pricing is resolved dynamically via OpenRouter API (24h cached) with a
12
+ * hardcoded fallback for when the API is unreachable and no cache exists.
13
+ *
14
+ * Outputs {} on stdout.
15
+ */
16
+
17
+ 'use strict';
18
+
19
+ const {
20
+ buildEvent,
21
+ sendAsync,
22
+ detectHarness,
23
+ persistSessionModel,
24
+ readLastAssistantFromTranscript,
25
+ tryAcquireDedupe,
26
+ isCodexInternalTaskTitleCompletion,
27
+ } = require('../lib/aw-usage-telemetry');
28
+ const { estimateCost, toNumber } = require('../lib/aw-pricing');
29
+
30
+ const MAX_STDIN = 1024 * 1024;
31
+ function pickFirstNumber(...values) {
32
+ for (const value of values) {
33
+ const n = toNumber(value);
34
+ if (n !== 0) return n;
35
+ }
36
+ return 0;
37
+ }
38
+
39
+ function buildResponseCompletedData(input) {
40
+ const harness = detectHarness(input);
41
+
42
+ // Parse transcript for all harnesses — all three provide transcript_path.
43
+ // The shared parser now understands both Claude/Cursor JSONL and Codex
44
+ // response_item + token_count transcript shapes.
45
+ let transcriptData = null;
46
+ if (input.transcript_path) {
47
+ transcriptData = readLastAssistantFromTranscript(input.transcript_path);
48
+ }
49
+
50
+ // Normalize stop reason across harnesses.
51
+ let stopReason;
52
+ if (harness === 'cursor') {
53
+ // Cursor adapter maps sessionEnd.reason / stop.status to stop_reason.
54
+ stopReason = input.stop_reason || input.status || input.reason || 'unknown';
55
+ } else if (harness === 'codex') {
56
+ stopReason = input.last_assistant_message ? 'completed' : 'unknown';
57
+ } else {
58
+ // Claude: prefer hook input, fall back to transcript.
59
+ stopReason = input.stop_reason
60
+ || (transcriptData && transcriptData.stop_reason)
61
+ || 'unknown';
62
+ }
63
+
64
+ // Token usage — prefer top-level fields (Cursor sends these directly),
65
+ // then input.usage, then transcript as last resort.
66
+ const hookUsage = input.usage || {};
67
+ const txUsage = (transcriptData && transcriptData.usage) || {};
68
+ const usage = Object.keys(hookUsage).length > 0 ? hookUsage : txUsage;
69
+
70
+ const inputTokens = pickFirstNumber(
71
+ input.input_tokens,
72
+ usage.input_tokens,
73
+ usage.prompt_tokens,
74
+ );
75
+ const outputTokens = pickFirstNumber(
76
+ input.output_tokens,
77
+ usage.output_tokens,
78
+ usage.completion_tokens,
79
+ );
80
+ const cacheReadTokens = pickFirstNumber(
81
+ input.cache_read_tokens,
82
+ usage.cache_read_input_tokens,
83
+ usage.cached_input_tokens,
84
+ );
85
+ const cacheCreateTokens = pickFirstNumber(
86
+ input.cache_write_tokens,
87
+ usage.cache_creation_input_tokens,
88
+ );
89
+
90
+ // Model: prefer hook input → transcript → session file.
91
+ const model = input.model
92
+ || input._cursor?.model
93
+ || (transcriptData && transcriptData.model)
94
+ || null;
95
+
96
+ // Persist model so PostToolUse/UserPromptSubmit hooks can read it.
97
+ // Stop fires every turn (before those hooks), so this stays fresh.
98
+ const sessionId = input.session_id || input.conversation_id || 'unknown';
99
+ if (model && sessionId !== 'unknown') {
100
+ persistSessionModel(sessionId, model);
101
+ }
102
+
103
+ const payload = { stop_reason: stopReason };
104
+
105
+ if (model) {
106
+ payload.model = model;
107
+ }
108
+
109
+ if (inputTokens || outputTokens) {
110
+ payload.input_tokens = inputTokens;
111
+ payload.output_tokens = outputTokens;
112
+ payload.estimated_cost_usd = estimateCost(model, inputTokens, outputTokens, {
113
+ cacheReadTokens,
114
+ cacheWriteTokens: cacheCreateTokens,
115
+ });
116
+ }
117
+
118
+ if (cacheReadTokens || cacheCreateTokens) {
119
+ payload.cache_read_tokens = cacheReadTokens;
120
+ if (cacheCreateTokens) {
121
+ payload.cache_create_tokens = cacheCreateTokens;
122
+ }
123
+ }
124
+
125
+ return { model, payload, sessionId };
126
+ }
127
+
128
+ function shouldSkipResponseCompleted(input) {
129
+ return isCodexInternalTaskTitleCompletion(input);
130
+ }
131
+
132
+ function main() {
133
+ let raw = '';
134
+
135
+ process.stdin.setEncoding('utf8');
136
+ process.stdin.on('data', chunk => {
137
+ if (raw.length < MAX_STDIN) {
138
+ raw += chunk.substring(0, MAX_STDIN - raw.length);
139
+ }
140
+ });
141
+
142
+ process.stdin.on('end', () => {
143
+ try {
144
+ const input = JSON.parse(raw);
145
+ if (!shouldSkipResponseCompleted(input)) {
146
+ const { model, payload, sessionId } = buildResponseCompletedData(input);
147
+
148
+ if (tryAcquireDedupe('response-completed', [
149
+ sessionId,
150
+ input?.turn_id || '',
151
+ input?.transcript_path || '',
152
+ input?.last_assistant_message || '',
153
+ input?.model || '',
154
+ payload.stop_reason || '',
155
+ payload.input_tokens || '',
156
+ payload.output_tokens || '',
157
+ ])) {
158
+ // Override model in the event envelope too (buildEvent reads from hook input
159
+ // which doesn't have model for Claude — inject it so the top-level field is set).
160
+ const event = buildEvent(input, 'response_completed', payload);
161
+ if (model && !event.model) {
162
+ event.model = model;
163
+ }
164
+ sendAsync(event);
165
+ }
166
+ }
167
+ } catch {
168
+ // Non-blocking.
169
+ }
170
+
171
+ process.stdout.write('{}');
172
+ });
173
+ }
174
+
175
+ if (require.main === module) {
176
+ main();
177
+ }
178
+
179
+ module.exports = {
180
+ buildResponseCompletedData,
181
+ shouldSkipResponseCompleted,
182
+ };