@massa-ai/cursor-plugin 1.6.0

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 (172) hide show
  1. package/.cursor-plugin/plugin.json +5 -0
  2. package/README.md +87 -0
  3. package/agents/massa-ai-architecture-specialist.md +65 -0
  4. package/agents/massa-ai-audit-specialist.md +81 -0
  5. package/agents/massa-ai-builder.md +67 -0
  6. package/agents/massa-ai-context-curator.md +67 -0
  7. package/agents/massa-ai-documentation-agent.md +65 -0
  8. package/agents/massa-ai-furps-analyst.md +71 -0
  9. package/agents/massa-ai-investigator.md +68 -0
  10. package/agents/massa-ai-mobile-specialist.md +82 -0
  11. package/agents/massa-ai-navigator.md +75 -0
  12. package/agents/massa-ai-plan-critic.md +90 -0
  13. package/agents/massa-ai-planner.md +65 -0
  14. package/agents/massa-ai-requirements-analyst.md +64 -0
  15. package/agents/massa-ai-reviewer.md +66 -0
  16. package/agents/massa-ai-test-engineer.md +66 -0
  17. package/agents/massa-ai-verification-agent.md +65 -0
  18. package/hooks/hooks.json +26 -0
  19. package/hooks/massa-ai-hook +298 -0
  20. package/install.sh +413 -0
  21. package/package.json +30 -0
  22. package/skills/agents/architecture-specialist/SKILL.md +69 -0
  23. package/skills/agents/audit-specialist/SKILL.md +85 -0
  24. package/skills/agents/builder/SKILL.md +71 -0
  25. package/skills/agents/context-curator/SKILL.md +71 -0
  26. package/skills/agents/documentation-agent/SKILL.md +69 -0
  27. package/skills/agents/furps-analyst/SKILL.md +74 -0
  28. package/skills/agents/investigator/SKILL.md +72 -0
  29. package/skills/agents/mobile-specialist/SKILL.md +86 -0
  30. package/skills/agents/navigator/SKILL.md +79 -0
  31. package/skills/agents/plan-critic/SKILL.md +93 -0
  32. package/skills/agents/planner/SKILL.md +69 -0
  33. package/skills/agents/requirements-analyst/SKILL.md +68 -0
  34. package/skills/agents/reviewer/SKILL.md +70 -0
  35. package/skills/agents/test-engineer/SKILL.md +70 -0
  36. package/skills/agents/verification-agent/SKILL.md +69 -0
  37. package/skills/def/SKILL.md +17 -0
  38. package/skills/find/SKILL.md +19 -0
  39. package/skills/graph/SKILL.md +16 -0
  40. package/skills/index/SKILL.md +19 -0
  41. package/skills/map/SKILL.md +24 -0
  42. package/skills/massa-ai/SKILL.md +315 -0
  43. package/skills/massa-ai/personas/README.md +35 -0
  44. package/skills/massa-ai/personas/ai-native-nodejs-cli-architect.md +76 -0
  45. package/skills/massa-ai/personas/catalog.json +157 -0
  46. package/skills/massa-ai/personas/context-skill-harness-engineer-architect.md +74 -0
  47. package/skills/massa-ai/personas/product-manager.md +67 -0
  48. package/skills/massa-ai/personas/senior-mobile-engineer.md +74 -0
  49. package/skills/massa-ai/personas/senior-mobile-qa-automation-engineer.md +75 -0
  50. package/skills/massa-ai/references/adr-authoring.md +189 -0
  51. package/skills/massa-ai/references/agent-orchestration.md +221 -0
  52. package/skills/massa-ai/references/architecture-coupling-lens.md +239 -0
  53. package/skills/massa-ai/references/architecture-deepening-lens.md +136 -0
  54. package/skills/massa-ai/references/architecture-domain-lens.md +186 -0
  55. package/skills/massa-ai/references/architecture-lenses.md +108 -0
  56. package/skills/massa-ai/references/audit-report-io.md +459 -0
  57. package/skills/massa-ai/references/audit-scope.md +103 -0
  58. package/skills/massa-ai/references/code-annotation.md +111 -0
  59. package/skills/massa-ai/references/codebase-investigation.md +96 -0
  60. package/skills/massa-ai/references/context-firewall.md +62 -0
  61. package/skills/massa-ai/references/conversation-feedback.md +104 -0
  62. package/skills/massa-ai/references/debug-diagnosis-loop.md +140 -0
  63. package/skills/massa-ai/references/decision-engine.md +73 -0
  64. package/skills/massa-ai/references/evidence-gate.md +53 -0
  65. package/skills/massa-ai/references/furps/analyst-role.md +49 -0
  66. package/skills/massa-ai/references/furps/checklist.md +92 -0
  67. package/skills/massa-ai/references/furps/intake.md +104 -0
  68. package/skills/massa-ai/references/furps/report-contract.md +140 -0
  69. package/skills/massa-ai/references/hook-enforcement.md +137 -0
  70. package/skills/massa-ai/references/implementation-delivery.md +101 -0
  71. package/skills/massa-ai/references/installation.md +110 -0
  72. package/skills/massa-ai/references/lessons.md +119 -0
  73. package/skills/massa-ai/references/maestro/artifacts-reports.md +69 -0
  74. package/skills/massa-ai/references/maestro/cli-device.md +65 -0
  75. package/skills/massa-ai/references/maestro/cloud.md +67 -0
  76. package/skills/massa-ai/references/maestro/config-env-output.md +76 -0
  77. package/skills/massa-ai/references/maestro/fact-ledger.md +71 -0
  78. package/skills/massa-ai/references/maestro/js-scripting.md +70 -0
  79. package/skills/massa-ai/references/maestro/mcp.md +59 -0
  80. package/skills/massa-ai/references/maestro/patterns.md +96 -0
  81. package/skills/massa-ai/references/maestro/selectors.md +91 -0
  82. package/skills/massa-ai/references/maestro/workspace-execution.md +81 -0
  83. package/skills/massa-ai/references/maestro/yaml-commands.md +203 -0
  84. package/skills/massa-ai/references/maestro.md +47 -0
  85. package/skills/massa-ai/references/mcp-tools.md +296 -0
  86. package/skills/massa-ai/references/memory-policy.md +103 -0
  87. package/skills/massa-ai/references/mobile-context.md +113 -0
  88. package/skills/massa-ai/references/mobile-diagnosis.md +106 -0
  89. package/skills/massa-ai/references/mobile-figma-matcher/ATTRIBUTION.md +5 -0
  90. package/skills/massa-ai/references/mobile-figma-matcher/android-compose.md +13 -0
  91. package/skills/massa-ai/references/mobile-figma-matcher/android-views.md +13 -0
  92. package/skills/massa-ai/references/mobile-figma-matcher/core.md +117 -0
  93. package/skills/massa-ai/references/mobile-figma-matcher/ios-swiftui.md +12 -0
  94. package/skills/massa-ai/references/mobile-figma-matcher/ios-uikit.md +12 -0
  95. package/skills/massa-ai/references/mobile-figma-matcher/kmp-compose-multiplatform.md +14 -0
  96. package/skills/massa-ai/references/mobile-figma-matcher/repository-detection.md +77 -0
  97. package/skills/massa-ai/references/naming-standards.md +47 -0
  98. package/skills/massa-ai/references/pr-task-fix.md +80 -0
  99. package/skills/massa-ai/references/project-context.md +76 -0
  100. package/skills/massa-ai/references/rfc/ATTRIBUTION.md +5 -0
  101. package/skills/massa-ai/references/rfc/discovery-and-sizing.md +120 -0
  102. package/skills/massa-ai/references/rfc/document-contract.md +85 -0
  103. package/skills/massa-ai/references/rfc/quality-and-lifecycle.md +101 -0
  104. package/skills/massa-ai/references/root-cause-scripts.md +97 -0
  105. package/skills/massa-ai/references/spec-driven/artifact-store.md +98 -0
  106. package/skills/massa-ai/references/spec-driven/code-analysis.md +119 -0
  107. package/skills/massa-ai/references/spec-driven/coding-principles.md +80 -0
  108. package/skills/massa-ai/references/spec-driven/context-limits.md +64 -0
  109. package/skills/massa-ai/references/spec-driven/design.md +257 -0
  110. package/skills/massa-ai/references/spec-driven/discuss.md +182 -0
  111. package/skills/massa-ai/references/spec-driven/execute.md +471 -0
  112. package/skills/massa-ai/references/spec-driven/lessons.md +5 -0
  113. package/skills/massa-ai/references/spec-driven/memory.md +214 -0
  114. package/skills/massa-ai/references/spec-driven/specify.md +283 -0
  115. package/skills/massa-ai/references/spec-driven/sub-agents.md +151 -0
  116. package/skills/massa-ai/references/spec-driven/tasks.md +494 -0
  117. package/skills/massa-ai/references/spec-driven/validate.md +397 -0
  118. package/skills/massa-ai/references/subagent-design.md +132 -0
  119. package/skills/massa-ai/references/synapse-policy.md +160 -0
  120. package/skills/massa-ai/references/tdd/calibrated-examples.md +54 -0
  121. package/skills/massa-ai/references/tdd/discovery-and-sizing.md +83 -0
  122. package/skills/massa-ai/references/tdd/document-contract.md +136 -0
  123. package/skills/massa-ai/references/tdd/quality-and-lifecycle.md +83 -0
  124. package/skills/massa-ai/references/the-fool/cognitive-bias-inventory.md +103 -0
  125. package/skills/massa-ai/references/the-fool/dialectic-synthesis.md +170 -0
  126. package/skills/massa-ai/references/the-fool/evidence-audit.md +202 -0
  127. package/skills/massa-ai/references/the-fool/mode-selection-guide.md +113 -0
  128. package/skills/massa-ai/references/the-fool/pre-mortem-analysis.md +200 -0
  129. package/skills/massa-ai/references/the-fool/red-team-adversarial.md +206 -0
  130. package/skills/massa-ai/references/the-fool/socratic-questioning.md +153 -0
  131. package/skills/massa-ai/references/ticket/atlassian-fix.md +130 -0
  132. package/skills/massa-ai/references/ticket/intake-and-sources.md +65 -0
  133. package/skills/massa-ai/references/ticket/templates-and-quality.md +129 -0
  134. package/skills/massa-ai/references/verification-ladder.md +62 -0
  135. package/skills/massa-ai/scripts/lessons.py +590 -0
  136. package/skills/massa-ai/workflows/adr.md +33 -0
  137. package/skills/massa-ai/workflows/architecture/architecture-audit.md +125 -0
  138. package/skills/massa-ai/workflows/architecture/architecture-fix.md +110 -0
  139. package/skills/massa-ai/workflows/bugs/bugs-audit.md +113 -0
  140. package/skills/massa-ai/workflows/bugs/bugs-fix.md +97 -0
  141. package/skills/massa-ai/workflows/code-quality/code-quality-audit.md +154 -0
  142. package/skills/massa-ai/workflows/code-quality/code-quality-fix.md +99 -0
  143. package/skills/massa-ai/workflows/commit.md +61 -0
  144. package/skills/massa-ai/workflows/debug.md +86 -0
  145. package/skills/massa-ai/workflows/design.md +54 -0
  146. package/skills/massa-ai/workflows/exploration.md +119 -0
  147. package/skills/massa-ai/workflows/feature.md +52 -0
  148. package/skills/massa-ai/workflows/general.md +46 -0
  149. package/skills/massa-ai/workflows/implementation/implementation-audit.md +87 -0
  150. package/skills/massa-ai/workflows/implementation/implementation-fix.md +90 -0
  151. package/skills/massa-ai/workflows/long-session.md +44 -0
  152. package/skills/massa-ai/workflows/maestro/maestro-audit.md +56 -0
  153. package/skills/massa-ai/workflows/maestro/maestro-fix.md +74 -0
  154. package/skills/massa-ai/workflows/maestro/maestro.md +68 -0
  155. package/skills/massa-ai/workflows/mobile-figma/mobile-figma-audit.md +68 -0
  156. package/skills/massa-ai/workflows/mobile-figma/mobile-figma-fix.md +74 -0
  157. package/skills/massa-ai/workflows/onboarding.md +23 -0
  158. package/skills/massa-ai/workflows/refactor.md +47 -0
  159. package/skills/massa-ai/workflows/refinement/furps-refinement.md +81 -0
  160. package/skills/massa-ai/workflows/requirements/requirements-audit.md +114 -0
  161. package/skills/massa-ai/workflows/requirements/requirements-fix.md +93 -0
  162. package/skills/massa-ai/workflows/rfc.md +55 -0
  163. package/skills/massa-ai/workflows/security/security-audit.md +113 -0
  164. package/skills/massa-ai/workflows/security/security-fix.md +97 -0
  165. package/skills/massa-ai/workflows/spec-driven.md +217 -0
  166. package/skills/massa-ai/workflows/tdd.md +71 -0
  167. package/skills/massa-ai/workflows/tests/tests-audit.md +114 -0
  168. package/skills/massa-ai/workflows/tests/tests-fix.md +96 -0
  169. package/skills/massa-ai/workflows/the-fool.md +82 -0
  170. package/skills/massa-ai/workflows/ticket.md +42 -0
  171. package/skills/persona-router/SKILL.md +158 -0
  172. package/skills/status/SKILL.md +15 -0
@@ -0,0 +1,298 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * massa-ai-hook — single typed Bun binary replacing 7 shell hook scripts.
4
+ *
5
+ * Wave 6 N30 (T20): reads JSON from stdin (Bun-native, no jq), resolves the
6
+ * project id through the per-session pin, and POSTs a lifecycle observation
7
+ * to the massa-ai hook endpoint. Silent-degrade: never blocks the agent
8
+ * (exit 0, no stdout).
9
+ *
10
+ * Subcommands (Claude Code hook event types):
11
+ * session-start → event: "session-start"
12
+ * user-prompt-submit → event: "user-prompt"
13
+ * post-tool-use → event: "post-tool-use"
14
+ * pre-compact → SPECIAL: TWO POSTs (observation + compact-snapshot)
15
+ * stop → event: "session-end"
16
+ *
17
+ * CRITICAL (pre-mortem F3): pre-compact does TWO POSTs:
18
+ * (1) observation to /api/v1/hook (3s timeout, observation body
19
+ * {event, projectId, sessionId, cwd, payload})
20
+ * (2) snapshot to /api/v1/hook/compact-snapshot (5s timeout, snapshot body
21
+ * {sessionId, projectId, persist, cwd})
22
+ * All other subcommands: single POST to /api/v1/hook (2s timeout).
23
+ *
24
+ * Pin resolution order (ported from _pin.sh):
25
+ * existing pin → env (MASSA_AI_PROJECT_ID) → git toplevel basename → cwd basename
26
+ *
27
+ * Terminal stdin (no pipe) → exit 0, no POST (same as shell [ -t 0 ] check).
28
+ */
29
+
30
+ import { spawnSync } from "child_process";
31
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, fstatSync } from "fs";
32
+ import path from "path";
33
+
34
+ // ── Event type mapping ──────────────────────────────────────────────────────
35
+
36
+ export const EVENT_MAP: Record<string, string> = {
37
+ "session-start": "session-start",
38
+ "user-prompt-submit": "user-prompt",
39
+ "pre-tool-use": "pre-tool-use",
40
+ "post-tool-use": "post-tool-use",
41
+ "pre-compact": "pre-compact",
42
+ stop: "session-end",
43
+ };
44
+
45
+ // ── Pin resolution (ported from _pin.sh) ─────────────────────────────────────
46
+
47
+ export function sanitizeSessionId(sessionId: string): string {
48
+ return sessionId.replace(/[^A-Za-z0-9._-]/g, "_");
49
+ }
50
+
51
+ export function getPinDir(): string {
52
+ return `${process.env.TMPDIR || "/tmp"}/massa-ai-hooks`;
53
+ }
54
+
55
+ export function getPinFile(sessionId: string): string | null {
56
+ if (!sessionId) return null;
57
+ const safe = sanitizeSessionId(sessionId);
58
+ if (safe === "." || safe === "..") return null;
59
+ return path.join(getPinDir(), safe);
60
+ }
61
+
62
+ export function resolveProjectId(sessionId: string, cwd: string): string {
63
+ // 1. Existing pin wins
64
+ if (sessionId) {
65
+ const pinFile = getPinFile(sessionId);
66
+ if (pinFile) {
67
+ try {
68
+ if (existsSync(pinFile)) {
69
+ const pinned = readFileSync(pinFile, "utf8").trim();
70
+ if (pinned) return pinned;
71
+ }
72
+ } catch {
73
+ // fall through to compute
74
+ }
75
+ }
76
+ }
77
+
78
+ // 2. Compute: env override > git toplevel basename > cwd basename
79
+ let computed = process.env.MASSA_AI_PROJECT_ID || "";
80
+ if (!computed) {
81
+ try {
82
+ const result = spawnSync("git", ["-C", cwd, "rev-parse", "--show-toplevel"], {
83
+ encoding: "utf8",
84
+ timeout: 2000,
85
+ });
86
+ const root = result.stdout?.trim();
87
+ if (root) {
88
+ computed = path.basename(root);
89
+ }
90
+ } catch {
91
+ // git not available
92
+ }
93
+ if (!computed) {
94
+ computed = path.basename(cwd);
95
+ }
96
+ }
97
+
98
+ // 3. Write pin for later events of this session (best effort)
99
+ if (sessionId) {
100
+ const pinFile = getPinFile(sessionId);
101
+ if (pinFile) {
102
+ try {
103
+ mkdirSync(getPinDir(), { recursive: true });
104
+ writeFileSync(pinFile, computed, { encoding: "utf8" });
105
+ } catch {
106
+ // silent-degrade: pin write failure is non-fatal
107
+ }
108
+ }
109
+ }
110
+
111
+ return computed;
112
+ }
113
+
114
+ // ── Stdin reading ───────────────────────────────────────────────────────────
115
+
116
+ export function readStdin(): string {
117
+ // Terminal stdin (no pipe) → exit 0, no POST (same as shell [ -t 0 ] check)
118
+ try {
119
+ const stats = fstatSync(0);
120
+ if (stats && stats.isCharacterDevice()) {
121
+ return ""; // terminal stdin → no payload
122
+ }
123
+ } catch {
124
+ // stat failed — assume no stdin available
125
+ return "";
126
+ }
127
+
128
+ try {
129
+ return readFileSync(0, "utf8");
130
+ } catch {
131
+ return "";
132
+ }
133
+ }
134
+
135
+ // ── POST helper ─────────────────────────────────────────────────────────────
136
+
137
+ export function postObservation(
138
+ url: string,
139
+ body: Record<string, unknown>,
140
+ timeoutMs: number,
141
+ hookType: string = "unknown",
142
+ ): Promise<void> {
143
+ // Fire-and-forget with bounded timeout; never rejects (silent-degrade).
144
+ // Returned promise lets main() await POSTs before process.exit so the
145
+ // event loop doesn't tear down pending sockets mid-flight. The caller's
146
+ // exit is still bounded by timeoutMs — never blocks the agent longer than
147
+ // the configured POST timeout.
148
+ try {
149
+ const headers: Record<string, string> = {
150
+ "Content-Type": "application/json",
151
+ };
152
+ const apiKey = process.env.MASSA_AI_API_KEY;
153
+ if (apiKey) {
154
+ headers["x-api-key"] = apiKey;
155
+ }
156
+
157
+ const startMs = Date.now();
158
+
159
+ return fetch(url, {
160
+ method: "POST",
161
+ headers,
162
+ body: JSON.stringify(body),
163
+ signal: AbortSignal.timeout(timeoutMs),
164
+ })
165
+ .then(() => {
166
+ // Breadcrumb-on-fire (W7-13): log to stderr when POST takes >80% of deadline.
167
+ const elapsedMs = Date.now() - startMs;
168
+ const pct = (elapsedMs / timeoutMs) * 100;
169
+ if (pct > 80) {
170
+ process.stderr.write(
171
+ JSON.stringify({
172
+ type: "breadcrumb",
173
+ hook: hookType,
174
+ elapsed: elapsedMs,
175
+ deadline: timeoutMs,
176
+ pct: Math.round(pct * 100) / 100,
177
+ }) + "\n",
178
+ );
179
+ }
180
+ // response body discarded; fire-and-forget
181
+ })
182
+ .catch((err) => {
183
+ // Breadcrumb-on-fire (W7-13): log deadline-on-fire on timeout.
184
+ const elapsedMs = Date.now() - startMs;
185
+ const isTimeout =
186
+ err instanceof DOMException &&
187
+ (err.name === "TimeoutError" || err.name === "AbortError");
188
+ if (isTimeout) {
189
+ process.stderr.write(
190
+ JSON.stringify({
191
+ type: "deadline-on-fire",
192
+ hook: hookType,
193
+ elapsed: elapsedMs,
194
+ deadline: timeoutMs,
195
+ reason: "timeout",
196
+ }) + "\n",
197
+ );
198
+ }
199
+ // silent-degrade: network failure is non-fatal
200
+ });
201
+ } catch {
202
+ // silent-degrade: fetch unavailable or immediate error is non-fatal
203
+ return Promise.resolve();
204
+ }
205
+ }
206
+
207
+ // ── Main ────────────────────────────────────────────────────────────────────
208
+
209
+ export async function main(stdinInput?: string): Promise<void> {
210
+ const subcommand = process.argv[2];
211
+ if (!subcommand || !EVENT_MAP[subcommand]) {
212
+ // Unknown or missing subcommand → exit 0 (silent-degrade)
213
+ return;
214
+ }
215
+
216
+ const rawStdin = stdinInput ?? readStdin();
217
+ // Whitespace-stripped check: empty payload → no POST (server rejects empty payload)
218
+ const stdinStripped = rawStdin.trim();
219
+ if (!stdinStripped) {
220
+ return;
221
+ }
222
+
223
+ // Parse JSON (Bun-native, no jq). Malformed JSON → exit 0, no POST.
224
+ let payload: Record<string, unknown>;
225
+ try {
226
+ const parsed = JSON.parse(stdinStripped);
227
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
228
+ return;
229
+ }
230
+ payload = parsed as Record<string, unknown>;
231
+ } catch {
232
+ // Malformed JSON → exit 0, no POST (same as shell jq failure)
233
+ return;
234
+ }
235
+
236
+ const event = EVENT_MAP[subcommand]!;
237
+ const cwd = process.cwd();
238
+
239
+ // Extract session id from payload (Claude Code sends "session_id"; accept "sessionId" too)
240
+ const sessionId =
241
+ (typeof payload.session_id === "string" ? payload.session_id : "") ||
242
+ (typeof payload.sessionId === "string" ? payload.sessionId : "") ||
243
+ "unknown";
244
+
245
+ const projectId = resolveProjectId(sessionId, cwd);
246
+
247
+ const baseUrl = process.env.MASSA_AI_API_BASE || "http://localhost:3333";
248
+ const hookUrl = `${baseUrl}/api/v1/hook`;
249
+
250
+ if (subcommand === "pre-compact") {
251
+ // CRITICAL (F3): pre-compact does TWO POSTs
252
+ // 1. Observation to /api/v1/hook (3s timeout, observation body)
253
+ const obsBody = {
254
+ event,
255
+ projectId,
256
+ sessionId,
257
+ cwd,
258
+ payload,
259
+ };
260
+ const obsP = postObservation(hookUrl, obsBody, 3000, subcommand);
261
+
262
+ // 2. Snapshot to /api/v1/hook/compact-snapshot (5s timeout, snapshot body)
263
+ const snapshotUrl = `${baseUrl}/api/v1/hook/compact-snapshot`;
264
+ const snapBody = {
265
+ sessionId,
266
+ projectId,
267
+ persist: true,
268
+ cwd,
269
+ };
270
+ const snapP = postObservation(snapshotUrl, snapBody, 5000, "compact-snapshot");
271
+
272
+ // Await both POSTs (bounded by their timeouts) before exit so the event
273
+ // loop doesn't tear down pending sockets. Silent-degrade: any rejection
274
+ // is swallowed inside postObservation; exit is still 0.
275
+ await Promise.all([obsP, snapP]);
276
+ } else {
277
+ // All other subcommands: single POST to /api/v1/hook (2s timeout, observation body)
278
+ // The body carries projectId (caller), sessionId, and cwd so the server-
279
+ // side AttributionResolver can run its explicit→sticky→containment→
280
+ // verbatim chain (AC6). The binary does NOT replicate that logic; it
281
+ // sends the raw inputs for server-side resolution.
282
+ const sessionField = sessionId && sessionId !== "unknown" ? { sessionId } : {};
283
+ const obsBody = {
284
+ event,
285
+ projectId,
286
+ ...sessionField,
287
+ cwd,
288
+ payload,
289
+ };
290
+ await postObservation(hookUrl, obsBody, 2000, subcommand);
291
+ }
292
+
293
+ // Always exit 0 (silent-degrade: never blocks the agent)
294
+ }
295
+
296
+ if (import.meta.main) {
297
+ main().catch(() => process.exit(0));
298
+ }