aw-ecc 1.4.31 → 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 (259) 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/codex-home.js +7 -0
  58. package/scripts/lib/install-targets/cursor-project.js +3 -0
  59. package/scripts/lib/install-targets/helpers.js +20 -3
  60. package/skills/aw-adk/SKILL.md +317 -0
  61. package/skills/aw-adk/agents/analyzer.md +113 -0
  62. package/skills/aw-adk/agents/comparator.md +113 -0
  63. package/skills/aw-adk/agents/grader.md +115 -0
  64. package/skills/aw-adk/assets/eval_review.html +76 -0
  65. package/skills/aw-adk/eval-viewer/generate_review.py +164 -0
  66. package/skills/aw-adk/eval-viewer/viewer.html +181 -0
  67. package/skills/aw-adk/evals/eval-colocated-placement.md +84 -0
  68. package/skills/aw-adk/evals/eval-create-agent.md +90 -0
  69. package/skills/aw-adk/evals/eval-create-command.md +98 -0
  70. package/skills/aw-adk/evals/eval-create-eval.md +89 -0
  71. package/skills/aw-adk/evals/eval-create-rule.md +99 -0
  72. package/skills/aw-adk/evals/eval-create-skill.md +97 -0
  73. package/skills/aw-adk/evals/eval-delete-agent.md +79 -0
  74. package/skills/aw-adk/evals/eval-delete-command.md +89 -0
  75. package/skills/aw-adk/evals/eval-delete-rule.md +86 -0
  76. package/skills/aw-adk/evals/eval-delete-skill.md +90 -0
  77. package/skills/aw-adk/evals/eval-meta-eval-coverage.md +78 -0
  78. package/skills/aw-adk/evals/eval-meta-eval-determinism.md +81 -0
  79. package/skills/aw-adk/evals/eval-meta-eval-false-pass.md +81 -0
  80. package/skills/aw-adk/evals/eval-score-accuracy.md +95 -0
  81. package/skills/aw-adk/evals/eval-type-redirect.md +68 -0
  82. package/skills/aw-adk/evals/evals.json +96 -0
  83. package/skills/aw-adk/references/artifact-wiring.md +162 -0
  84. package/skills/aw-adk/references/cross-ide-mapping.md +71 -0
  85. package/skills/aw-adk/references/eval-placement-guide.md +183 -0
  86. package/skills/aw-adk/references/external-resources.md +75 -0
  87. package/skills/aw-adk/references/getting-started.md +66 -0
  88. package/skills/aw-adk/references/registry-structure.md +152 -0
  89. package/skills/aw-adk/references/rubric-agent.md +36 -0
  90. package/skills/aw-adk/references/rubric-command.md +36 -0
  91. package/skills/aw-adk/references/rubric-eval.md +36 -0
  92. package/skills/aw-adk/references/rubric-meta-eval.md +132 -0
  93. package/skills/aw-adk/references/rubric-rule.md +36 -0
  94. package/skills/aw-adk/references/rubric-skill.md +36 -0
  95. package/skills/aw-adk/references/schemas.md +222 -0
  96. package/skills/aw-adk/references/template-agent.md +251 -0
  97. package/skills/aw-adk/references/template-command.md +279 -0
  98. package/skills/aw-adk/references/template-eval.md +176 -0
  99. package/skills/aw-adk/references/template-rule.md +119 -0
  100. package/skills/aw-adk/references/template-skill.md +123 -0
  101. package/skills/aw-adk/references/type-classifier.md +98 -0
  102. package/skills/aw-adk/references/writing-good-agents.md +227 -0
  103. package/skills/aw-adk/references/writing-good-commands.md +258 -0
  104. package/skills/aw-adk/references/writing-good-evals.md +271 -0
  105. package/skills/aw-adk/references/writing-good-rules.md +214 -0
  106. package/skills/aw-adk/references/writing-good-skills.md +159 -0
  107. package/skills/aw-adk/scripts/aggregate-benchmark.py +190 -0
  108. package/skills/aw-adk/scripts/lint-artifact.sh +211 -0
  109. package/skills/aw-adk/scripts/score-artifact.sh +179 -0
  110. package/skills/aw-adk/scripts/trigger-eval.py +192 -0
  111. package/skills/aw-build/SKILL.md +19 -2
  112. package/skills/aw-deploy/SKILL.md +65 -3
  113. package/skills/aw-design/SKILL.md +156 -0
  114. package/skills/aw-design/references/highrise-tokens.md +394 -0
  115. package/skills/aw-design/references/micro-interactions.md +76 -0
  116. package/skills/aw-design/references/prompt-template.md +160 -0
  117. package/skills/aw-design/references/quality-checklist.md +70 -0
  118. package/skills/aw-design/references/self-review.md +497 -0
  119. package/skills/aw-design/references/stitch-workflow.md +127 -0
  120. package/skills/aw-feature/SKILL.md +293 -0
  121. package/skills/aw-investigate/SKILL.md +17 -0
  122. package/skills/aw-plan/SKILL.md +34 -3
  123. package/skills/aw-publish/SKILL.md +300 -0
  124. package/skills/aw-publish/evals/eval-confirmation-gate.md +60 -0
  125. package/skills/aw-publish/evals/eval-intent-detection.md +111 -0
  126. package/skills/aw-publish/evals/eval-push-modes.md +67 -0
  127. package/skills/aw-publish/evals/eval-rules-push.md +60 -0
  128. package/skills/aw-publish/evals/evals.json +29 -0
  129. package/skills/aw-publish/references/push-modes.md +38 -0
  130. package/skills/aw-review/SKILL.md +88 -9
  131. package/skills/aw-rules-review/SKILL.md +124 -0
  132. package/skills/aw-rules-review/agents/openai.yaml +3 -0
  133. package/skills/aw-rules-review/scripts/generate-review-template.mjs +323 -0
  134. package/skills/aw-ship/SKILL.md +16 -0
  135. package/skills/aw-spec/SKILL.md +15 -0
  136. package/skills/aw-tasks/SKILL.md +15 -0
  137. package/skills/aw-test/SKILL.md +16 -0
  138. package/skills/aw-yolo/SKILL.md +4 -0
  139. package/skills/diagnose/SKILL.md +121 -0
  140. package/skills/diagnose/scripts/hitl-loop.template.sh +41 -0
  141. package/skills/finish-only-when-green/SKILL.md +265 -0
  142. package/skills/grill-me/SKILL.md +24 -0
  143. package/skills/grill-with-docs/SKILL.md +92 -0
  144. package/skills/grill-with-docs/adr-format.md +47 -0
  145. package/skills/grill-with-docs/context-format.md +67 -0
  146. package/skills/improve-codebase-architecture/SKILL.md +75 -0
  147. package/skills/improve-codebase-architecture/deepening.md +37 -0
  148. package/skills/improve-codebase-architecture/interface-design.md +44 -0
  149. package/skills/improve-codebase-architecture/language.md +53 -0
  150. package/skills/local-ghl-setup-from-screenshot/SKILL.md +538 -0
  151. package/skills/tdd/SKILL.md +115 -0
  152. package/skills/tdd/deep-modules.md +33 -0
  153. package/skills/tdd/interface-design.md +31 -0
  154. package/skills/tdd/mocking.md +59 -0
  155. package/skills/tdd/refactoring.md +10 -0
  156. package/skills/tdd/tests.md +61 -0
  157. package/skills/to-issues/SKILL.md +62 -0
  158. package/skills/to-prd/SKILL.md +75 -0
  159. package/skills/using-aw-skills/SKILL.md +170 -237
  160. package/skills/using-aw-skills/hooks/session-start.sh +11 -41
  161. package/skills/zoom-out/SKILL.md +24 -0
  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
@@ -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
+ };