@moreih29/nexus-core 0.12.0 → 0.13.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 (210) hide show
  1. package/README.md +48 -63
  2. package/assets/agents/architect/body.ko.md +177 -0
  3. package/{agents → assets/agents}/architect/body.md +16 -0
  4. package/assets/agents/designer/body.ko.md +125 -0
  5. package/{agents → assets/agents}/designer/body.md +16 -0
  6. package/assets/agents/engineer/body.ko.md +106 -0
  7. package/{agents → assets/agents}/engineer/body.md +14 -0
  8. package/assets/agents/lead/body.ko.md +70 -0
  9. package/assets/agents/lead/body.md +70 -0
  10. package/assets/agents/postdoc/body.ko.md +122 -0
  11. package/{agents → assets/agents}/postdoc/body.md +16 -0
  12. package/assets/agents/researcher/body.ko.md +137 -0
  13. package/{agents → assets/agents}/researcher/body.md +15 -0
  14. package/assets/agents/reviewer/body.ko.md +138 -0
  15. package/{agents → assets/agents}/reviewer/body.md +15 -0
  16. package/assets/agents/strategist/body.ko.md +116 -0
  17. package/{agents → assets/agents}/strategist/body.md +16 -0
  18. package/assets/agents/tester/body.ko.md +195 -0
  19. package/{agents → assets/agents}/tester/body.md +15 -0
  20. package/assets/agents/writer/body.ko.md +122 -0
  21. package/{agents → assets/agents}/writer/body.md +14 -0
  22. package/assets/capability-matrix.yml +198 -0
  23. package/assets/hooks/agent-bootstrap/handler.test.ts +368 -0
  24. package/assets/hooks/agent-bootstrap/handler.ts +119 -0
  25. package/assets/hooks/agent-bootstrap/meta.yml +10 -0
  26. package/assets/hooks/agent-finalize/handler.test.ts +368 -0
  27. package/assets/hooks/agent-finalize/handler.ts +76 -0
  28. package/assets/hooks/agent-finalize/meta.yml +10 -0
  29. package/assets/hooks/capability-matrix.yml +313 -0
  30. package/assets/hooks/post-tool-telemetry/handler.test.ts +302 -0
  31. package/assets/hooks/post-tool-telemetry/handler.ts +49 -0
  32. package/assets/hooks/post-tool-telemetry/meta.yml +11 -0
  33. package/assets/hooks/prompt-router/handler.test.ts +801 -0
  34. package/assets/hooks/prompt-router/handler.ts +261 -0
  35. package/assets/hooks/prompt-router/meta.yml +11 -0
  36. package/assets/hooks/session-init/handler.test.ts +274 -0
  37. package/assets/hooks/session-init/handler.ts +30 -0
  38. package/assets/hooks/session-init/meta.yml +9 -0
  39. package/assets/lsp-servers.json +55 -0
  40. package/assets/schema/lsp-servers.schema.json +67 -0
  41. package/assets/skills/nx-init/body.ko.md +197 -0
  42. package/{skills → assets/skills}/nx-init/body.md +11 -0
  43. package/assets/skills/nx-plan/body.ko.md +361 -0
  44. package/{skills → assets/skills}/nx-plan/body.md +13 -0
  45. package/assets/skills/nx-run/body.ko.md +161 -0
  46. package/{skills → assets/skills}/nx-run/body.md +11 -0
  47. package/assets/skills/nx-sync/body.ko.md +92 -0
  48. package/{skills → assets/skills}/nx-sync/body.md +10 -0
  49. package/assets/tools/tool-name-map.yml +353 -0
  50. package/dist/hooks/opencode-mount.d.ts +35 -0
  51. package/dist/hooks/opencode-mount.d.ts.map +1 -0
  52. package/dist/hooks/opencode-mount.js +332 -0
  53. package/dist/hooks/opencode-mount.js.map +1 -0
  54. package/dist/hooks/runtime.d.ts +37 -0
  55. package/dist/hooks/runtime.d.ts.map +1 -0
  56. package/dist/hooks/runtime.js +274 -0
  57. package/dist/hooks/runtime.js.map +1 -0
  58. package/dist/hooks/types.d.ts +196 -0
  59. package/dist/hooks/types.d.ts.map +1 -0
  60. package/dist/hooks/types.js +85 -0
  61. package/dist/hooks/types.js.map +1 -0
  62. package/dist/lsp/cache.d.ts +9 -0
  63. package/dist/lsp/cache.d.ts.map +1 -0
  64. package/dist/lsp/cache.js +216 -0
  65. package/dist/lsp/cache.js.map +1 -0
  66. package/dist/lsp/client.d.ts +24 -0
  67. package/dist/lsp/client.d.ts.map +1 -0
  68. package/dist/lsp/client.js +166 -0
  69. package/dist/lsp/client.js.map +1 -0
  70. package/dist/lsp/detect.d.ts +77 -0
  71. package/dist/lsp/detect.d.ts.map +1 -0
  72. package/dist/lsp/detect.js +116 -0
  73. package/dist/lsp/detect.js.map +1 -0
  74. package/dist/mcp/server.d.ts +5 -0
  75. package/dist/mcp/server.d.ts.map +1 -0
  76. package/dist/mcp/server.js +34 -0
  77. package/dist/mcp/server.js.map +1 -0
  78. package/dist/mcp/tools/artifact.d.ts +4 -0
  79. package/dist/mcp/tools/artifact.d.ts.map +1 -0
  80. package/dist/mcp/tools/artifact.js +36 -0
  81. package/dist/mcp/tools/artifact.js.map +1 -0
  82. package/dist/mcp/tools/history.d.ts +3 -0
  83. package/dist/mcp/tools/history.d.ts.map +1 -0
  84. package/dist/mcp/tools/history.js +29 -0
  85. package/dist/mcp/tools/history.js.map +1 -0
  86. package/dist/mcp/tools/lsp.d.ts +13 -0
  87. package/dist/mcp/tools/lsp.d.ts.map +1 -0
  88. package/dist/mcp/tools/lsp.js +225 -0
  89. package/dist/mcp/tools/lsp.js.map +1 -0
  90. package/dist/mcp/tools/plan.d.ts +3 -0
  91. package/dist/mcp/tools/plan.d.ts.map +1 -0
  92. package/dist/mcp/tools/plan.js +317 -0
  93. package/dist/mcp/tools/plan.js.map +1 -0
  94. package/dist/mcp/tools/task.d.ts +3 -0
  95. package/dist/mcp/tools/task.d.ts.map +1 -0
  96. package/dist/mcp/tools/task.js +252 -0
  97. package/dist/mcp/tools/task.js.map +1 -0
  98. package/dist/shared/invocations.d.ts +74 -0
  99. package/dist/shared/invocations.d.ts.map +1 -0
  100. package/dist/shared/invocations.js +247 -0
  101. package/dist/shared/invocations.js.map +1 -0
  102. package/dist/shared/json-store.d.ts +37 -0
  103. package/dist/shared/json-store.d.ts.map +1 -0
  104. package/dist/shared/json-store.js +163 -0
  105. package/dist/shared/json-store.js.map +1 -0
  106. package/dist/shared/mcp-utils.d.ts +3 -0
  107. package/dist/shared/mcp-utils.d.ts.map +1 -0
  108. package/dist/shared/mcp-utils.js +6 -0
  109. package/dist/shared/mcp-utils.js.map +1 -0
  110. package/dist/shared/paths.d.ts +21 -0
  111. package/dist/shared/paths.d.ts.map +1 -0
  112. package/dist/shared/paths.js +81 -0
  113. package/dist/shared/paths.js.map +1 -0
  114. package/dist/shared/tool-log.d.ts +8 -0
  115. package/dist/shared/tool-log.d.ts.map +1 -0
  116. package/dist/shared/tool-log.js +22 -0
  117. package/dist/shared/tool-log.js.map +1 -0
  118. package/dist/types/state.d.ts +862 -0
  119. package/dist/types/state.d.ts.map +1 -0
  120. package/dist/types/state.js +66 -0
  121. package/dist/types/state.js.map +1 -0
  122. package/docs/consuming/codex-lead-merge.md +106 -0
  123. package/docs/plugin-guide.md +360 -0
  124. package/docs/plugin-template/claude/.github/workflows/build.yml +60 -0
  125. package/docs/plugin-template/claude/README.md +110 -0
  126. package/docs/plugin-template/claude/package.json +16 -0
  127. package/docs/plugin-template/codex/.github/workflows/build.yml +51 -0
  128. package/docs/plugin-template/codex/README.md +147 -0
  129. package/docs/plugin-template/codex/package.json +17 -0
  130. package/docs/plugin-template/opencode/.github/workflows/build.yml +61 -0
  131. package/docs/plugin-template/opencode/README.md +121 -0
  132. package/docs/plugin-template/opencode/package.json +25 -0
  133. package/package.json +21 -21
  134. package/scripts/build-agents.test.ts +1279 -0
  135. package/scripts/build-agents.ts +978 -0
  136. package/scripts/build-hooks.test.ts +1385 -0
  137. package/scripts/build-hooks.ts +584 -0
  138. package/scripts/cli.test.ts +367 -0
  139. package/scripts/cli.ts +547 -0
  140. package/agents/architect/meta.yml +0 -13
  141. package/agents/designer/meta.yml +0 -13
  142. package/agents/engineer/meta.yml +0 -11
  143. package/agents/postdoc/meta.yml +0 -13
  144. package/agents/researcher/meta.yml +0 -12
  145. package/agents/reviewer/meta.yml +0 -12
  146. package/agents/strategist/meta.yml +0 -13
  147. package/agents/tester/meta.yml +0 -12
  148. package/agents/writer/meta.yml +0 -11
  149. package/conformance/README.md +0 -311
  150. package/conformance/examples/plan.extension.schema.example.json +0 -25
  151. package/conformance/lifecycle/README.md +0 -48
  152. package/conformance/lifecycle/agent-complete.json +0 -44
  153. package/conformance/lifecycle/agent-resume.json +0 -43
  154. package/conformance/lifecycle/agent-spawn.json +0 -36
  155. package/conformance/lifecycle/memory-access-record.json +0 -27
  156. package/conformance/lifecycle/session-end.json +0 -48
  157. package/conformance/scenarios/full-plan-cycle.json +0 -147
  158. package/conformance/scenarios/task-deps-ordering.json +0 -95
  159. package/conformance/schema/fixture.schema.json +0 -354
  160. package/conformance/state-schemas/agent-tracker.schema.json +0 -63
  161. package/conformance/state-schemas/history.schema.json +0 -134
  162. package/conformance/state-schemas/memory-access.schema.json +0 -36
  163. package/conformance/state-schemas/plan.schema.json +0 -77
  164. package/conformance/state-schemas/tasks.schema.json +0 -98
  165. package/conformance/tools/artifact-write.json +0 -97
  166. package/conformance/tools/context.json +0 -172
  167. package/conformance/tools/history-search.json +0 -219
  168. package/conformance/tools/plan-decide.json +0 -139
  169. package/conformance/tools/plan-start.json +0 -81
  170. package/conformance/tools/plan-status.json +0 -127
  171. package/conformance/tools/plan-update.json +0 -341
  172. package/conformance/tools/task-add.json +0 -156
  173. package/conformance/tools/task-close.json +0 -161
  174. package/conformance/tools/task-list.json +0 -177
  175. package/conformance/tools/task-update.json +0 -167
  176. package/docs/behavioral-contracts.md +0 -145
  177. package/docs/consumer-implementation-guide.md +0 -840
  178. package/docs/memory-lifecycle-contract.md +0 -119
  179. package/docs/nexus-layout.md +0 -224
  180. package/docs/nexus-outputs-contract.md +0 -344
  181. package/docs/nexus-state-overview.md +0 -170
  182. package/docs/nexus-tools-contract.md +0 -438
  183. package/manifest.json +0 -448
  184. package/schema/README.md +0 -69
  185. package/schema/agent.schema.json +0 -23
  186. package/schema/common.schema.json +0 -17
  187. package/schema/manifest.schema.json +0 -78
  188. package/schema/memory-policy.schema.json +0 -98
  189. package/schema/skill.schema.json +0 -54
  190. package/schema/task-exceptions.schema.json +0 -40
  191. package/schema/vocabulary.schema.json +0 -167
  192. package/scripts/.gitkeep +0 -0
  193. package/scripts/conformance-coverage.ts +0 -466
  194. package/scripts/import-from-claude-nexus.ts +0 -403
  195. package/scripts/lib/frontmatter.ts +0 -71
  196. package/scripts/lib/lint.ts +0 -348
  197. package/scripts/lib/structure.ts +0 -159
  198. package/scripts/lib/validate.ts +0 -796
  199. package/scripts/validate.ts +0 -90
  200. package/skills/nx-init/meta.yml +0 -8
  201. package/skills/nx-plan/meta.yml +0 -10
  202. package/skills/nx-run/meta.yml +0 -8
  203. package/skills/nx-sync/meta.yml +0 -7
  204. package/vocabulary/capabilities.yml +0 -65
  205. package/vocabulary/categories.yml +0 -11
  206. package/vocabulary/invocations.yml +0 -147
  207. package/vocabulary/memory_policy.yml +0 -88
  208. package/vocabulary/resume-tiers.yml +0 -11
  209. package/vocabulary/tags.yml +0 -60
  210. package/vocabulary/task-exceptions.yml +0 -29
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode-mount.d.ts","sourceRoot":"","sources":["../../src/hooks/opencode-mount.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,yBAAyB,EAAE,CAAC;CACpC;AAMD;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CACxB,SAAS,EAAE;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,EAChC,QAAQ,EAAE,oBAAoB,GAC7B,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,CA+JvD"}
@@ -0,0 +1,332 @@
1
+ /**
2
+ * OpenCode plugin adapter — mounts nexus hooks into OpenCode's plugin API.
3
+ *
4
+ * 결정 참조: plan.json Issue #1 (3-레이어 책임 분담),
5
+ * Issue #3 (OpenCode 2단 처리 — SubagentStart/Stop, agent-tracker),
6
+ * Issue #6 (additional_context 주입 우회 — SubagentStop output.output append)
7
+ */
8
+ import { spawn } from "node:child_process";
9
+ import { mkdirSync } from "node:fs";
10
+ import { dirname, join } from "node:path";
11
+ import { updateJsonFileLocked } from "../shared/json-store.js";
12
+ // ---------------------------------------------------------------------------
13
+ // mountHooks — primary export
14
+ // ---------------------------------------------------------------------------
15
+ /**
16
+ * Mount nexus hooks into OpenCode's plugin API.
17
+ *
18
+ * Returns an object whose keys are OpenCode plugin hook names. Each value is
19
+ * the async handler function OpenCode calls when the corresponding event fires.
20
+ *
21
+ * @param pluginCtx OpenCode plugin context (must expose `.directory: string`)
22
+ * @param manifest Hook manifest produced by build-hooks.ts
23
+ */
24
+ export function mountHooks(pluginCtx, manifest) {
25
+ // Buffer for additional_context produced by SessionStart / UserPromptSubmit hooks.
26
+ // Flushed into the system prompt on each LLM call via chat.system.transform.
27
+ const systemTransformBuffer = [];
28
+ return {
29
+ // ------------------------------------------------------------------
30
+ // session.created → SessionStart
31
+ // ------------------------------------------------------------------
32
+ "event": async (input) => {
33
+ const ev = input;
34
+ if (ev["event"]?.["type"] !== "session.created") {
35
+ return;
36
+ }
37
+ const sessionEvent = ev["event"];
38
+ const sessionId = sessionEvent["sessionID"] ?? "";
39
+ await dispatchEvent("SessionStart", manifest, {
40
+ hook_event_name: "SessionStart",
41
+ session_id: sessionId,
42
+ cwd: pluginCtx.directory,
43
+ source: "startup",
44
+ }, { systemTransformBuffer });
45
+ },
46
+ // ------------------------------------------------------------------
47
+ // chat.message → UserPromptSubmit
48
+ // ------------------------------------------------------------------
49
+ "chat.message": async (input) => {
50
+ const msg = input;
51
+ const sessionId = msg["sessionID"] ?? "";
52
+ const prompt = msg["message"] ?? "";
53
+ await dispatchEvent("UserPromptSubmit", manifest, {
54
+ hook_event_name: "UserPromptSubmit",
55
+ session_id: sessionId,
56
+ cwd: pluginCtx.directory,
57
+ prompt,
58
+ }, { systemTransformBuffer });
59
+ },
60
+ // ------------------------------------------------------------------
61
+ // tool.execute.before → PreToolUse (or SubagentStart when tool=task)
62
+ // ------------------------------------------------------------------
63
+ "tool.execute.before": async (input, output) => {
64
+ const inp = input;
65
+ const out = output;
66
+ const sessionId = inp["sessionID"] ?? "";
67
+ const toolName = inp["tool"] ?? "";
68
+ const isTask = toolName === "task";
69
+ const args = out["args"] ?? {};
70
+ if (isTask) {
71
+ await dispatchEvent("SubagentStart", manifest, {
72
+ hook_event_name: "SubagentStart",
73
+ session_id: sessionId,
74
+ cwd: pluginCtx.directory,
75
+ // agent_id is unknown until tool.execute.after resolves the subagent session
76
+ agent_id: "",
77
+ agent_type: args["subagent_type"] ?? "",
78
+ }, { outputArgsRef: args });
79
+ }
80
+ else {
81
+ await dispatchEvent("PreToolUse", manifest, {
82
+ hook_event_name: "PreToolUse",
83
+ session_id: sessionId,
84
+ cwd: pluginCtx.directory,
85
+ tool_name: normalizeOpenCodeToolName(toolName),
86
+ tool_input: args,
87
+ }, { outputArgsRef: args });
88
+ }
89
+ },
90
+ // ------------------------------------------------------------------
91
+ // tool.execute.after → PostToolUse (or SubagentStop when tool=task)
92
+ // ------------------------------------------------------------------
93
+ "tool.execute.after": async (input, output) => {
94
+ const inp = input;
95
+ const out = output;
96
+ const sessionId = inp["sessionID"] ?? "";
97
+ const toolName = inp["tool"] ?? "";
98
+ const isTask = toolName === "task";
99
+ const args = out["args"] ?? {};
100
+ const metadata = out["metadata"] ?? {};
101
+ const agentId = metadata["sessionId"] ?? "";
102
+ if (isTask) {
103
+ // Register / update agent-tracker before dispatching SubagentStop
104
+ await upsertAgentTracker(pluginCtx.directory, sessionId, agentId, args["subagent_type"] ?? "");
105
+ await dispatchEvent("SubagentStop", manifest, {
106
+ hook_event_name: "SubagentStop",
107
+ session_id: sessionId,
108
+ cwd: pluginCtx.directory,
109
+ agent_id: agentId,
110
+ agent_type: args["subagent_type"] ?? "",
111
+ last_assistant_message: out["output"] ?? "",
112
+ },
113
+ // additional_context appended to output.output (bypass #6)
114
+ { outputOutputRef: out });
115
+ }
116
+ else {
117
+ await dispatchEvent("PostToolUse", manifest, {
118
+ hook_event_name: "PostToolUse",
119
+ session_id: sessionId,
120
+ cwd: pluginCtx.directory,
121
+ tool_name: normalizeOpenCodeToolName(toolName),
122
+ tool_input: args,
123
+ tool_response: out["output"] ?? "",
124
+ }, {});
125
+ }
126
+ },
127
+ // ------------------------------------------------------------------
128
+ // experimental.chat.system.transform — flush buffer into system prompt
129
+ // ------------------------------------------------------------------
130
+ "experimental.chat.system.transform": async (_input, output) => {
131
+ const out = output;
132
+ if (!Array.isArray(out.system))
133
+ return;
134
+ while (systemTransformBuffer.length > 0) {
135
+ out.system.push(systemTransformBuffer.shift());
136
+ }
137
+ },
138
+ };
139
+ }
140
+ // ---------------------------------------------------------------------------
141
+ // dispatchEvent
142
+ // ---------------------------------------------------------------------------
143
+ async function dispatchEvent(nexusEventName, manifest, nexusInput, sideEffects) {
144
+ // Sort hooks by priority (ascending) so lower numbers run first
145
+ const candidates = manifest.hooks
146
+ .filter((h) => h.events.includes(nexusEventName))
147
+ .sort((a, b) => a.priority - b.priority);
148
+ for (const hook of candidates) {
149
+ const toolOrSource = nexusInput["tool_name"] ??
150
+ nexusInput["source"] ??
151
+ "";
152
+ if (!matchesPattern(hook.matcher, toolOrSource))
153
+ continue;
154
+ const timeoutMs = (hook.timeout ?? 30) * 1000;
155
+ const result = await spawnHandler(hook.handlerPath, nexusInput, timeoutMs);
156
+ if (result == null)
157
+ continue;
158
+ applyResult(result, nexusEventName, sideEffects);
159
+ }
160
+ }
161
+ // ---------------------------------------------------------------------------
162
+ // spawnHandler
163
+ // ---------------------------------------------------------------------------
164
+ async function spawnHandler(handlerPath, nexusInput, timeoutMs) {
165
+ return new Promise((resolve) => {
166
+ let child;
167
+ try {
168
+ child = spawn("node", [handlerPath], {
169
+ env: { ...process.env, NEXUS_HARNESS: "opencode" },
170
+ stdio: ["pipe", "pipe", "pipe"],
171
+ });
172
+ }
173
+ catch {
174
+ // spawn itself threw (e.g. node not found) — silent
175
+ resolve(null);
176
+ return;
177
+ }
178
+ let stdout = "";
179
+ let timedOut = false;
180
+ const timer = setTimeout(() => {
181
+ timedOut = true;
182
+ try {
183
+ child.kill();
184
+ }
185
+ catch {
186
+ // ignore
187
+ }
188
+ resolve(null);
189
+ }, timeoutMs);
190
+ if (!child.stdout || !child.stdin) {
191
+ clearTimeout(timer);
192
+ resolve(null);
193
+ return;
194
+ }
195
+ child.stdout.on("data", (chunk) => {
196
+ stdout += chunk.toString();
197
+ });
198
+ child.stdin.write(JSON.stringify(nexusInput));
199
+ child.stdin.end();
200
+ child.on("exit", () => {
201
+ clearTimeout(timer);
202
+ if (timedOut)
203
+ return;
204
+ try {
205
+ resolve(stdout ? JSON.parse(stdout) : null);
206
+ }
207
+ catch {
208
+ resolve(null);
209
+ }
210
+ });
211
+ child.on("error", () => {
212
+ clearTimeout(timer);
213
+ resolve(null); // spawn failure — silent, keep OpenCode turn alive
214
+ });
215
+ });
216
+ }
217
+ // ---------------------------------------------------------------------------
218
+ // applyResult — convert nexus output to OpenCode API side effects
219
+ // ---------------------------------------------------------------------------
220
+ function applyResult(result, nexusEventName, sideEffects) {
221
+ // decision:block → throw (OpenCode sees an error and halts the turn)
222
+ if (result["decision"] === "block") {
223
+ const reason = result["block_reason"] ?? "Blocked by nexus hook";
224
+ throw new Error(reason);
225
+ }
226
+ // continue:false → throw
227
+ if (result["continue"] === false) {
228
+ const msg = result["system_message"] ?? "Hook requested stop";
229
+ throw new Error(msg);
230
+ }
231
+ // additional_context
232
+ const additionalContext = result["additional_context"];
233
+ if (additionalContext) {
234
+ if (nexusEventName === "SessionStart" || nexusEventName === "UserPromptSubmit") {
235
+ // Push to system.transform buffer
236
+ sideEffects.systemTransformBuffer?.push(additionalContext);
237
+ }
238
+ else if (nexusEventName === "SubagentStart") {
239
+ // Prepend to args.prompt so the subagent receives the context
240
+ if (sideEffects.outputArgsRef) {
241
+ const existing = sideEffects.outputArgsRef["prompt"] ?? "";
242
+ sideEffects.outputArgsRef["prompt"] = additionalContext + "\n\n" + existing;
243
+ }
244
+ }
245
+ else if (nexusEventName === "SubagentStop") {
246
+ // Append to output.output (bypass #6)
247
+ if (sideEffects.outputOutputRef) {
248
+ const existing = sideEffects.outputOutputRef["output"] ?? "";
249
+ sideEffects.outputOutputRef["output"] = existing + "\n\n" + additionalContext;
250
+ }
251
+ }
252
+ }
253
+ // updated_input → mutate output.args (PreToolUse only)
254
+ const updatedInput = result["updated_input"];
255
+ if (updatedInput && nexusEventName === "PreToolUse" && sideEffects.outputArgsRef) {
256
+ Object.assign(sideEffects.outputArgsRef, updatedInput);
257
+ }
258
+ }
259
+ // ---------------------------------------------------------------------------
260
+ // upsertAgentTracker
261
+ // ---------------------------------------------------------------------------
262
+ async function upsertAgentTracker(cwd, sessionId, agentId, agentType) {
263
+ if (!agentId)
264
+ return; // agent_id not yet resolved — skip silently
265
+ const trackerPath = join(cwd, ".nexus", "state", sessionId, "agent-tracker.json");
266
+ try {
267
+ mkdirSync(dirname(trackerPath), { recursive: true });
268
+ await updateJsonFileLocked(trackerPath, [], (tracker) => {
269
+ const existing = tracker.find((e) => e.agent_id === agentId);
270
+ if (!existing) {
271
+ tracker.push({
272
+ agent_id: agentId,
273
+ agent_type: agentType,
274
+ started_at: new Date().toISOString(),
275
+ resume_count: 0,
276
+ status: "running",
277
+ });
278
+ }
279
+ else {
280
+ existing.resume_count = (existing.resume_count ?? 0) + 1;
281
+ existing.last_resumed_at = new Date().toISOString();
282
+ }
283
+ return tracker;
284
+ });
285
+ }
286
+ catch {
287
+ // tracker update failure must never disrupt the OpenCode turn
288
+ }
289
+ }
290
+ /**
291
+ * Test whether a nexus matcher pattern matches the given value.
292
+ * Pattern "*" matches everything.
293
+ * Otherwise the pattern is treated as a pipe-separated list of literals
294
+ * (e.g. "Read|Write|Edit") or a simple regex.
295
+ */
296
+ function matchesPattern(pattern, value) {
297
+ if (pattern === "*")
298
+ return true;
299
+ // Try literal pipe-separated list first (most common case)
300
+ const literals = pattern.split("|");
301
+ if (literals.every((l) => /^[A-Za-z0-9_-]+$/.test(l))) {
302
+ return literals.includes(value);
303
+ }
304
+ // Fall back to regex
305
+ try {
306
+ return new RegExp(pattern).test(value);
307
+ }
308
+ catch {
309
+ return false;
310
+ }
311
+ }
312
+ /**
313
+ * Normalize OpenCode lowercase tool names to nexus PascalCase equivalents.
314
+ * OpenCode uses snake_case/lowercase internally; nexus standard is PascalCase.
315
+ */
316
+ function normalizeOpenCodeToolName(name) {
317
+ const aliasMap = {
318
+ read: "Read",
319
+ edit: "Edit",
320
+ write: "Write",
321
+ bash: "Bash",
322
+ glob: "Glob",
323
+ grep: "Grep",
324
+ ls: "LS",
325
+ task: "Task",
326
+ web_fetch: "WebFetch",
327
+ web_search: "WebSearch",
328
+ patch: "Edit",
329
+ };
330
+ return aliasMap[name] ?? name;
331
+ }
332
+ //# sourceMappingURL=opencode-mount.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode-mount.js","sourceRoot":"","sources":["../../src/hooks/opencode-mount.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Nexus Hook shell wrapper entry — reads stdin, normalizes for harness,
3
+ * dispatches to handler, and serializes stdout per NEXUS_HARNESS.
4
+ *
5
+ * 결정 참조: plan.json Issue #1 (3-레이어 책임 분담),
6
+ * Issue #3 (Bash parse patterns), Issue #4 (Codex tool aliases)
7
+ */
8
+ import type { NexusHookInput, NexusHookOutput } from "./types.js";
9
+ export interface RunWrapperOptions {
10
+ /** Absolute path to the compiled handler .js file. */
11
+ handlerPath: string;
12
+ /** Optional absolute path to meta.yml; enables condition.state_file_exists check. */
13
+ metaPath?: string;
14
+ }
15
+ export declare function runWrapper(options: RunWrapperOptions): Promise<void>;
16
+ export interface ParsedBashCommand {
17
+ tool: string;
18
+ target?: string;
19
+ }
20
+ /** Parse a Bash command string into a nexus standard tool name.
21
+ * Returns null when the command is a compound/piped command or unrecognised. */
22
+ export declare function parseBashCommand(command: string): ParsedBashCommand | null;
23
+ /** Map Codex native tool aliases to nexus PascalCase equivalents. */
24
+ declare function normalizeCodexToolName(name: string): string;
25
+ /** When tool_name is "Bash" and tool_input.command is present, attempt to
26
+ * resolve a more specific nexus standard tool_name via bash_parse_patterns. */
27
+ declare function normalizeBashToolName(input: NexusHookInput, _harness: string): NexusHookInput;
28
+ declare function serializeForClaude(out: NexusHookOutput, eventName: string): Record<string, unknown>;
29
+ declare function serializeForCodex(out: NexusHookOutput, eventName: string): Record<string, unknown>;
30
+ export declare const __test: {
31
+ serializeForClaude: typeof serializeForClaude;
32
+ serializeForCodex: typeof serializeForCodex;
33
+ normalizeBashToolName: typeof normalizeBashToolName;
34
+ normalizeCodexToolName: typeof normalizeCodexToolName;
35
+ };
36
+ export {};
37
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/hooks/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAe,MAAM,YAAY,CAAC;AAU/E,MAAM,WAAW,iBAAiB;IAChC,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAC;IACpB,qFAAqF;IACrF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuC1E;AAMD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;iFACiF;AACjF,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAiB1E;AAqFD,qEAAqE;AACrE,iBAAS,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAoBpD;AAED;gFACgF;AAChF,iBAAS,qBAAqB,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,CAatF;AA2CD,iBAAS,kBAAkB,CAAC,GAAG,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA2B5F;AAED,iBAAS,iBAAiB,CAAC,GAAG,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAyB3F;AAmBD,eAAO,MAAM,MAAM;;;;;CAKlB,CAAC"}
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Nexus Hook shell wrapper entry — reads stdin, normalizes for harness,
3
+ * dispatches to handler, and serializes stdout per NEXUS_HARNESS.
4
+ *
5
+ * 결정 참조: plan.json Issue #1 (3-레이어 책임 분담),
6
+ * Issue #3 (Bash parse patterns), Issue #4 (Codex tool aliases)
7
+ */
8
+ import { NexusHookInputSchema, NexusHookOutputSchema, HookMetaSchema } from "./types.js";
9
+ import { readFileSync, existsSync } from "node:fs";
10
+ import { resolve, dirname } from "node:path";
11
+ import { createRequire } from "node:module";
12
+ import { parse as parseYaml } from "yaml";
13
+ export async function runWrapper(options) {
14
+ try {
15
+ const stdin = await readStdin();
16
+ const rawInput = JSON.parse(stdin);
17
+ const harness = resolveHarness();
18
+ const normalized = normalizeInput(rawInput, harness);
19
+ const validated = NexusHookInputSchema.parse(normalized);
20
+ if (await shouldSkipByCondition(options.metaPath, validated.cwd)) {
21
+ process.exit(0);
22
+ }
23
+ // Normalize Bash tool_name when applicable
24
+ const input = normalizeBashToolName(validated, harness);
25
+ // Dynamic import — must be file:// URL in ESM to handle absolute paths
26
+ const handlerUrl = options.handlerPath.startsWith("file://")
27
+ ? options.handlerPath
28
+ : `file://${options.handlerPath}`;
29
+ const handlerModule = await import(handlerUrl);
30
+ const handler = handlerModule.default ?? handlerModule.handler;
31
+ if (typeof handler !== "function") {
32
+ throw new Error(`handler export not found in ${options.handlerPath}`);
33
+ }
34
+ const rawOutput = await handler(input);
35
+ if (rawOutput == null) {
36
+ process.exit(0);
37
+ }
38
+ const output = NexusHookOutputSchema.parse(rawOutput);
39
+ const stdout = serializeForHarness(output, harness, input.hook_event_name);
40
+ process.stdout.write(JSON.stringify(stdout));
41
+ process.exit(0);
42
+ }
43
+ catch (err) {
44
+ process.stderr.write(`[nexus-hook] ${String(err)}\n`);
45
+ process.exit(0); // never kill the turn
46
+ }
47
+ }
48
+ /** Parse a Bash command string into a nexus standard tool name.
49
+ * Returns null when the command is a compound/piped command or unrecognised. */
50
+ export function parseBashCommand(command) {
51
+ const patterns = loadBashParsePatterns();
52
+ const trimmed = command.trim();
53
+ // Reject compound commands (pipes, &&, ||, ;) — best-effort single-entrypoint only
54
+ if (/[|;&]/.test(trimmed)) {
55
+ return null;
56
+ }
57
+ for (const [toolName, regexList] of Object.entries(patterns)) {
58
+ for (const regex of regexList) {
59
+ if (regex.test(trimmed)) {
60
+ return { tool: toolName };
61
+ }
62
+ }
63
+ }
64
+ return null;
65
+ }
66
+ // ---------------------------------------------------------------------------
67
+ // Internal helpers
68
+ // ---------------------------------------------------------------------------
69
+ /** Cached bash_parse_patterns from tool-name-map.yml */
70
+ let _bashParsePatternsCache = null;
71
+ function loadBashParsePatterns() {
72
+ if (_bashParsePatternsCache)
73
+ return _bashParsePatternsCache;
74
+ const ymlPath = resolveAssetPath("assets/tools/tool-name-map.yml");
75
+ const raw = readFileSync(ymlPath, "utf8");
76
+ const parsed = parseYaml(raw);
77
+ const result = {};
78
+ for (const [tool, patterns] of Object.entries(parsed.bash_parse_patterns ?? {})) {
79
+ result[tool] = patterns.map((p) => new RegExp(p));
80
+ }
81
+ _bashParsePatternsCache = result;
82
+ return result;
83
+ }
84
+ /** Resolve a path relative to the nexus-core package root.
85
+ * Works whether running from src/ (ts-node/bun) or dist/ (compiled). */
86
+ function resolveAssetPath(relativePath) {
87
+ // Try createRequire first (installed package case)
88
+ try {
89
+ const req = createRequire(import.meta.url);
90
+ const pkgJson = req.resolve("@moreih29/nexus-core/manifest.json").replace(/\/manifest\.json$/, "");
91
+ const candidate = resolve(pkgJson, relativePath);
92
+ if (existsSync(candidate))
93
+ return candidate;
94
+ }
95
+ catch {
96
+ // Package not installed or manifest.json not resolvable — fall through to walk-up
97
+ }
98
+ // Fallback: walk up from current file to find package root by locating package.json
99
+ const selfDir = new URL(".", import.meta.url).pathname;
100
+ let dir = selfDir;
101
+ while (dir !== "/") {
102
+ const pkgCandidate = resolve(dir, relativePath);
103
+ if (existsSync(pkgCandidate))
104
+ return pkgCandidate;
105
+ const parent = dirname(dir);
106
+ if (parent === dir)
107
+ break;
108
+ dir = parent;
109
+ }
110
+ throw new Error(`[nexus-hook] Cannot locate ${relativePath} from ${selfDir}`);
111
+ }
112
+ function resolveHarness() {
113
+ return process.env["NEXUS_HARNESS"] ?? "claude";
114
+ }
115
+ /** Normalise harness-native stdin to nexus standard NexusHookInput shape.
116
+ *
117
+ * - claude / codex: field names are already mostly compatible; handle known aliases
118
+ * - opencode: mountHooks already delivers nexus standard — pass-through
119
+ */
120
+ function normalizeInput(raw, harness) {
121
+ if (harness === "opencode") {
122
+ // pass-through — mountHooks already normalised
123
+ return raw;
124
+ }
125
+ if (harness === "codex") {
126
+ // Codex uses snake_case and sends tool_name; primary event shape is compatible.
127
+ // Remap known Codex-native tool aliases to nexus PascalCase names.
128
+ const out = { ...raw };
129
+ if (typeof out["tool_name"] === "string") {
130
+ out["tool_name"] = normalizeCodexToolName(out["tool_name"]);
131
+ }
132
+ return out;
133
+ }
134
+ // claude — direct pass-through; field names match nexus standard
135
+ return raw;
136
+ }
137
+ /** Map Codex native tool aliases to nexus PascalCase equivalents. */
138
+ function normalizeCodexToolName(name) {
139
+ const aliasMap = {
140
+ shell: "Bash",
141
+ shell_command: "Bash",
142
+ exec_command: "Bash",
143
+ exec: "Bash",
144
+ apply_patch: "Edit",
145
+ list_dir: "LS",
146
+ web_search: "WebSearch",
147
+ view_image: "ViewImage",
148
+ js_repl: "REPL",
149
+ js_repl_reset: "REPL",
150
+ tool_search: "ToolSearch",
151
+ tool_suggest: "ToolSearch",
152
+ spawn_agent: "Task",
153
+ update_plan: "TodoWrite",
154
+ request_user_input: "AskUserQuestion",
155
+ request_permissions: "RequestPermissions",
156
+ };
157
+ return aliasMap[name] ?? name;
158
+ }
159
+ /** When tool_name is "Bash" and tool_input.command is present, attempt to
160
+ * resolve a more specific nexus standard tool_name via bash_parse_patterns. */
161
+ function normalizeBashToolName(input, _harness) {
162
+ if ((input.hook_event_name === "PreToolUse" || input.hook_event_name === "PostToolUse") &&
163
+ input.tool_name === "Bash" &&
164
+ input.tool_input &&
165
+ typeof input.tool_input["command"] === "string") {
166
+ const parsed = parseBashCommand(input.tool_input["command"]);
167
+ if (parsed) {
168
+ return { ...input, tool_name: parsed.tool };
169
+ }
170
+ }
171
+ return input;
172
+ }
173
+ /** Check meta.yml condition.state_file_exists; returns true when the hook
174
+ * should be skipped (condition not met). */
175
+ async function shouldSkipByCondition(metaPath, cwd) {
176
+ if (!metaPath)
177
+ return false;
178
+ if (!existsSync(metaPath))
179
+ return false;
180
+ try {
181
+ const raw = readFileSync(metaPath, "utf8");
182
+ const meta = HookMetaSchema.parse(parseYaml(raw));
183
+ if (!meta.condition?.state_file_exists)
184
+ return false;
185
+ const stateFile = resolve(cwd, meta.condition.state_file_exists);
186
+ return !existsSync(stateFile);
187
+ }
188
+ catch {
189
+ // Malformed meta.yml — do not skip
190
+ return false;
191
+ }
192
+ }
193
+ /** Serialize NexusHookOutput into the harness-native stdout shape. */
194
+ function serializeForHarness(out, harness, eventName) {
195
+ if (harness === "opencode") {
196
+ // nexus JSON passed through — mountHooks will parse
197
+ return out;
198
+ }
199
+ if (harness === "codex") {
200
+ return serializeForCodex(out, eventName);
201
+ }
202
+ // Default: claude
203
+ return serializeForClaude(out, eventName);
204
+ }
205
+ function serializeForClaude(out, eventName) {
206
+ const result = {};
207
+ if (out.decision === "block") {
208
+ if (eventName === "PreToolUse") {
209
+ result["permissionDecision"] = "deny";
210
+ if (out.block_reason)
211
+ result["permissionDecisionReason"] = out.block_reason;
212
+ }
213
+ else {
214
+ result["decision"] = "block";
215
+ if (out.block_reason)
216
+ result["reason"] = out.block_reason;
217
+ }
218
+ }
219
+ if (out.additional_context) {
220
+ result["additionalContext"] = out.additional_context;
221
+ }
222
+ if (out.system_message) {
223
+ result["systemMessage"] = out.system_message;
224
+ }
225
+ if (out.continue === false) {
226
+ result["continue"] = false;
227
+ result["stopReason"] = "system_message";
228
+ }
229
+ return result;
230
+ }
231
+ function serializeForCodex(out, eventName) {
232
+ const result = {};
233
+ if (out.decision === "block") {
234
+ result["decision"] = "block";
235
+ if (out.block_reason)
236
+ result["reason"] = out.block_reason;
237
+ }
238
+ if (out.additional_context) {
239
+ result["hookSpecificOutput"] = {
240
+ hookEventName: eventName,
241
+ additionalContext: out.additional_context,
242
+ };
243
+ }
244
+ if (out.system_message) {
245
+ result["systemMessage"] = out.system_message;
246
+ }
247
+ if (out.continue === false) {
248
+ result["continue"] = false;
249
+ result["stopReason"] = "system_message";
250
+ }
251
+ return result;
252
+ }
253
+ /** Read all of stdin and return as string. */
254
+ async function readStdin() {
255
+ return new Promise((res, rej) => {
256
+ let data = "";
257
+ process.stdin.setEncoding("utf8");
258
+ process.stdin.on("data", (chunk) => {
259
+ data += chunk;
260
+ });
261
+ process.stdin.on("end", () => res(data));
262
+ process.stdin.on("error", rej);
263
+ });
264
+ }
265
+ // ---------------------------------------------------------------------------
266
+ // Test-only exports (do not use in production code)
267
+ // ---------------------------------------------------------------------------
268
+ export const __test = {
269
+ serializeForClaude,
270
+ serializeForCodex,
271
+ normalizeBashToolName,
272
+ normalizeCodexToolName,
273
+ };
274
+ //# sourceMappingURL=runtime.js.map