claude-nexus 0.28.1 → 0.29.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/VERSION +1 -1
- package/agents/architect.md +1 -1
- package/agents/designer.md +1 -1
- package/agents/engineer.md +1 -1
- package/agents/lead.md +1 -1
- package/agents/postdoc.md +1 -1
- package/agents/researcher.md +1 -1
- package/agents/reviewer.md +1 -1
- package/agents/strategist.md +1 -1
- package/agents/tester.md +1 -1
- package/agents/writer.md +1 -1
- package/dist/hooks/agent-bootstrap.js +146 -13
- package/dist/hooks/agent-finalize.js +19 -3
- package/dist/hooks/post-tool-telemetry.js +71 -0
- package/dist/hooks/prompt-router.js +39 -3
- package/dist/hooks/session-init.js +32 -3
- package/hooks/hooks.json +12 -0
- package/package.json +2 -2
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.29.0
|
package/agents/architect.md
CHANGED
package/agents/designer.md
CHANGED
package/agents/engineer.md
CHANGED
package/agents/lead.md
CHANGED
package/agents/postdoc.md
CHANGED
package/agents/researcher.md
CHANGED
package/agents/reviewer.md
CHANGED
package/agents/strategist.md
CHANGED
package/agents/tester.md
CHANGED
package/agents/writer.md
CHANGED
|
@@ -1,21 +1,124 @@
|
|
|
1
|
+
// src/shared/json-store.js
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import { constants as fsConstants, appendFileSync, mkdirSync } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
6
|
+
var inProcessQueues = new Map;
|
|
7
|
+
async function runWithInProcessLock(filePath, action) {
|
|
8
|
+
const previous = inProcessQueues.get(filePath) ?? Promise.resolve();
|
|
9
|
+
let release = () => {};
|
|
10
|
+
const gate = new Promise((resolve) => {
|
|
11
|
+
release = resolve;
|
|
12
|
+
});
|
|
13
|
+
const entry = previous.then(() => gate);
|
|
14
|
+
inProcessQueues.set(filePath, entry);
|
|
15
|
+
await previous;
|
|
16
|
+
try {
|
|
17
|
+
return await action();
|
|
18
|
+
} finally {
|
|
19
|
+
release();
|
|
20
|
+
entry.finally(() => {
|
|
21
|
+
if (inProcessQueues.get(filePath) === entry) {
|
|
22
|
+
inProcessQueues.delete(filePath);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
var LOCK_RETRY_INTERVAL_MS = 100;
|
|
28
|
+
var LOCK_MAX_RETRIES = 50;
|
|
29
|
+
var LOCK_STALE_MS = 30000;
|
|
30
|
+
function lockPath(filePath) {
|
|
31
|
+
return `${filePath}.lock`;
|
|
32
|
+
}
|
|
33
|
+
async function acquireFsLock(filePath) {
|
|
34
|
+
const lp = lockPath(filePath);
|
|
35
|
+
for (let attempt = 0;attempt <= LOCK_MAX_RETRIES; attempt++) {
|
|
36
|
+
try {
|
|
37
|
+
const fd = await fs.open(lp, fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL);
|
|
38
|
+
await fd.close();
|
|
39
|
+
return;
|
|
40
|
+
} catch (err) {
|
|
41
|
+
const e = err;
|
|
42
|
+
if (e.code !== "EEXIST")
|
|
43
|
+
throw err;
|
|
44
|
+
try {
|
|
45
|
+
const stat = await fs.stat(lp);
|
|
46
|
+
const ageMs = Date.now() - stat.mtimeMs;
|
|
47
|
+
if (ageMs > LOCK_STALE_MS) {
|
|
48
|
+
await fs.unlink(lp).catch(() => {
|
|
49
|
+
return;
|
|
50
|
+
});
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (attempt === LOCK_MAX_RETRIES) {
|
|
57
|
+
throw new Error(`Failed to acquire lock for "${filePath}" after ${LOCK_MAX_RETRIES} retries`);
|
|
58
|
+
}
|
|
59
|
+
await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_INTERVAL_MS));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async function releaseFsLock(filePath) {
|
|
64
|
+
await fs.unlink(lockPath(filePath)).catch(() => {
|
|
65
|
+
return;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async function readJsonFile(filePath, defaultValue) {
|
|
69
|
+
let raw;
|
|
70
|
+
try {
|
|
71
|
+
raw = await fs.readFile(filePath, "utf8");
|
|
72
|
+
} catch (err) {
|
|
73
|
+
const e = err;
|
|
74
|
+
if (e.code === "ENOENT")
|
|
75
|
+
return defaultValue;
|
|
76
|
+
throw err;
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
return JSON.parse(raw);
|
|
80
|
+
} catch {
|
|
81
|
+
return defaultValue;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async function writeJsonFile(filePath, data) {
|
|
85
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
86
|
+
const tmpPath = `${filePath}.tmp.${process.pid}.${Date.now()}.${randomUUID()}`;
|
|
87
|
+
await fs.writeFile(tmpPath, JSON.stringify(data, null, 2) + `
|
|
88
|
+
`, "utf8");
|
|
89
|
+
await fs.rename(tmpPath, filePath);
|
|
90
|
+
}
|
|
91
|
+
async function updateJsonFileLocked(filePath, defaultValue, updater) {
|
|
92
|
+
return runWithInProcessLock(filePath, async () => {
|
|
93
|
+
await acquireFsLock(filePath);
|
|
94
|
+
try {
|
|
95
|
+
const current = await readJsonFile(filePath, defaultValue);
|
|
96
|
+
const next = await updater(current);
|
|
97
|
+
await writeJsonFile(filePath, next);
|
|
98
|
+
return next;
|
|
99
|
+
} finally {
|
|
100
|
+
await releaseFsLock(filePath);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
var APPEND_SIZE_WARN_THRESHOLD = 4 * 1024;
|
|
105
|
+
|
|
1
106
|
// assets/hooks/agent-bootstrap/handler.ts
|
|
2
107
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
3
108
|
import { join } from "node:path";
|
|
4
109
|
var CORE_INDEX_SIZE_LIMIT = 2 * 1024;
|
|
5
110
|
function loadValidRoles(cwd) {
|
|
111
|
+
const inlined = globalThis.__NEXUS_INLINE_AGENT_ROLES__;
|
|
112
|
+
if (Array.isArray(inlined))
|
|
113
|
+
return inlined;
|
|
6
114
|
const agentsDir = join(cwd, "assets/agents");
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
if (entry.isDirectory())
|
|
11
|
-
roles.push(entry.name);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
return roles;
|
|
115
|
+
if (!existsSync(agentsDir))
|
|
116
|
+
return [];
|
|
117
|
+
return readdirSync(agentsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
15
118
|
}
|
|
16
|
-
function readFirstLine(
|
|
119
|
+
function readFirstLine(path2) {
|
|
17
120
|
try {
|
|
18
|
-
const content = readFileSync(
|
|
121
|
+
const content = readFileSync(path2, "utf-8");
|
|
19
122
|
const firstNonEmpty = content.split(`
|
|
20
123
|
`).find((l) => l.trim().length > 0) ?? "";
|
|
21
124
|
return firstNonEmpty.replace(/^#+\s*/, "").slice(0, 80);
|
|
@@ -76,6 +179,19 @@ var handler = async (input) => {
|
|
|
76
179
|
const validRoles = loadValidRoles(cwd);
|
|
77
180
|
if (!validRoles.includes(agent_type))
|
|
78
181
|
return;
|
|
182
|
+
const trackerPath = join(cwd, ".nexus/state", session_id, "agent-tracker.json");
|
|
183
|
+
await updateJsonFileLocked(trackerPath, [], (tracker) => {
|
|
184
|
+
const list = Array.isArray(tracker) ? tracker : [];
|
|
185
|
+
if (list.find((e) => e["agent_id"] === agent_id))
|
|
186
|
+
return list;
|
|
187
|
+
list.push({
|
|
188
|
+
agent_id,
|
|
189
|
+
agent_type,
|
|
190
|
+
started_at: new Date().toISOString(),
|
|
191
|
+
status: "running"
|
|
192
|
+
});
|
|
193
|
+
return list;
|
|
194
|
+
});
|
|
79
195
|
const parts = [];
|
|
80
196
|
const coreIndex = buildCoreIndex(cwd);
|
|
81
197
|
if (coreIndex) {
|
|
@@ -100,6 +216,23 @@ ${ruleContent}
|
|
|
100
216
|
`) };
|
|
101
217
|
};
|
|
102
218
|
var handler_default = handler;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
};
|
|
219
|
+
|
|
220
|
+
// ../../../../../tmp/nexus-hook-entry-agent-bootstrap-1776690665703/agent-bootstrap-entry.ts
|
|
221
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
222
|
+
globalThis.__NEXUS_INLINE_AGENT_ROLES__ = ["architect", "designer", "engineer", "reviewer", "strategist", "researcher", "postdoc", "lead", "tester", "writer"];
|
|
223
|
+
async function main() {
|
|
224
|
+
let raw = "";
|
|
225
|
+
try {
|
|
226
|
+
raw = readFileSync2(0, "utf-8");
|
|
227
|
+
} catch {}
|
|
228
|
+
const input = raw ? JSON.parse(raw) : {};
|
|
229
|
+
const result = await handler_default(input);
|
|
230
|
+
if (result != null && result !== undefined) {
|
|
231
|
+
process.stdout.write(JSON.stringify(result));
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
main().then(() => process.exit(0), (err) => {
|
|
235
|
+
process.stderr.write(String(err?.stack ?? err) + `
|
|
236
|
+
`);
|
|
237
|
+
process.exit(1);
|
|
238
|
+
});
|
|
@@ -159,6 +159,22 @@ Subagent "${agent_type}" finished. Tasks still pending with this role: ${ids}. R
|
|
|
159
159
|
}
|
|
160
160
|
};
|
|
161
161
|
var handler_default = handler;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
};
|
|
162
|
+
|
|
163
|
+
// ../../../../../tmp/nexus-hook-entry-agent-finalize-1776690665695/agent-finalize-entry.ts
|
|
164
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
165
|
+
async function main() {
|
|
166
|
+
let raw = "";
|
|
167
|
+
try {
|
|
168
|
+
raw = readFileSync2(0, "utf-8");
|
|
169
|
+
} catch {}
|
|
170
|
+
const input = raw ? JSON.parse(raw) : {};
|
|
171
|
+
const result = await handler_default(input);
|
|
172
|
+
if (result != null && result !== undefined) {
|
|
173
|
+
process.stdout.write(JSON.stringify(result));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
main().then(() => process.exit(0), (err) => {
|
|
177
|
+
process.stderr.write(String(err?.stack ?? err) + `
|
|
178
|
+
`);
|
|
179
|
+
process.exit(1);
|
|
180
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// src/shared/json-store.js
|
|
2
|
+
import { constants as fsConstants, appendFileSync, mkdirSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
var inProcessQueues = new Map;
|
|
5
|
+
var APPEND_SIZE_WARN_THRESHOLD = 4 * 1024;
|
|
6
|
+
function appendJsonLine(filePath, record) {
|
|
7
|
+
const line = JSON.stringify(record) + `
|
|
8
|
+
`;
|
|
9
|
+
if (line.length > APPEND_SIZE_WARN_THRESHOLD) {
|
|
10
|
+
console.error(`[json-store] appendJsonLine line exceeds ${APPEND_SIZE_WARN_THRESHOLD} bytes ` + `(${line.length}) — write may not be atomic on some filesystems. path=${filePath}`);
|
|
11
|
+
}
|
|
12
|
+
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
13
|
+
appendFileSync(filePath, line);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// assets/hooks/post-tool-telemetry/handler.ts
|
|
17
|
+
import { join, resolve, relative } from "node:path";
|
|
18
|
+
var EDIT_TOOLS = new Set(["Edit", "Write", "MultiEdit", "ApplyPatch", "NotebookEdit"]);
|
|
19
|
+
function isWithinMemory(filePath, projectRoot) {
|
|
20
|
+
const memRoot = resolve(projectRoot, ".nexus/memory");
|
|
21
|
+
const abs = resolve(filePath);
|
|
22
|
+
return abs.startsWith(memRoot + "/") || abs === memRoot;
|
|
23
|
+
}
|
|
24
|
+
var handler = async (input) => {
|
|
25
|
+
if (input.hook_event_name !== "PostToolUse")
|
|
26
|
+
return;
|
|
27
|
+
const { cwd, session_id, tool_name, agent_id } = input;
|
|
28
|
+
const toolInput = input.tool_input ?? {};
|
|
29
|
+
if (tool_name === "Read") {
|
|
30
|
+
const filePath = toolInput.file_path;
|
|
31
|
+
if (filePath && isWithinMemory(filePath, cwd)) {
|
|
32
|
+
appendJsonLine(join(cwd, ".nexus/memory-access.jsonl"), {
|
|
33
|
+
path: relative(cwd, resolve(filePath)),
|
|
34
|
+
accessed_at: new Date().toISOString(),
|
|
35
|
+
agent: agent_id ?? null
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (EDIT_TOOLS.has(tool_name) && agent_id) {
|
|
40
|
+
const filePath = toolInput.file_path ?? toolInput.notebook_path;
|
|
41
|
+
if (filePath) {
|
|
42
|
+
appendJsonLine(join(cwd, ".nexus/state", session_id, "tool-log.jsonl"), {
|
|
43
|
+
ts: new Date().toISOString(),
|
|
44
|
+
agent_id,
|
|
45
|
+
tool: tool_name,
|
|
46
|
+
file: relative(cwd, resolve(filePath)),
|
|
47
|
+
status: "ok"
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var handler_default = handler;
|
|
53
|
+
|
|
54
|
+
// ../../../../../tmp/nexus-hook-entry-post-tool-telemetry-1776690665643/post-tool-telemetry-entry.ts
|
|
55
|
+
import { readFileSync } from "node:fs";
|
|
56
|
+
async function main() {
|
|
57
|
+
let raw = "";
|
|
58
|
+
try {
|
|
59
|
+
raw = readFileSync(0, "utf-8");
|
|
60
|
+
} catch {}
|
|
61
|
+
const input = raw ? JSON.parse(raw) : {};
|
|
62
|
+
const result = await handler_default(input);
|
|
63
|
+
if (result != null && result !== undefined) {
|
|
64
|
+
process.stdout.write(JSON.stringify(result));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
main().then(() => process.exit(0), (err) => {
|
|
68
|
+
process.stderr.write(String(err?.stack ?? err) + `
|
|
69
|
+
`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
});
|
|
@@ -7141,6 +7141,11 @@ var _invocationsCache = null;
|
|
|
7141
7141
|
function loadInvocations() {
|
|
7142
7142
|
if (_invocationsCache)
|
|
7143
7143
|
return _invocationsCache;
|
|
7144
|
+
const inlined = globalThis.__NEXUS_INLINE_INVOCATIONS__;
|
|
7145
|
+
if (inlined) {
|
|
7146
|
+
_invocationsCache = inlined;
|
|
7147
|
+
return inlined;
|
|
7148
|
+
}
|
|
7144
7149
|
const selfDir = new URL(".", import.meta.url).pathname;
|
|
7145
7150
|
let dir = selfDir;
|
|
7146
7151
|
while (dir !== "/") {
|
|
@@ -7175,6 +7180,9 @@ function expand(template, harness) {
|
|
|
7175
7180
|
return expandInvocations(template, harness, loadInvocations());
|
|
7176
7181
|
}
|
|
7177
7182
|
function loadValidRuleTargets(cwd) {
|
|
7183
|
+
const inlined = globalThis.__NEXUS_INLINE_RULE_TARGETS__;
|
|
7184
|
+
if (inlined && inlined.length > 0)
|
|
7185
|
+
return inlined;
|
|
7178
7186
|
const targets = [];
|
|
7179
7187
|
for (const dir of ["assets/agents", "assets/skills"]) {
|
|
7180
7188
|
const absDir = join(cwd, dir);
|
|
@@ -7295,6 +7303,34 @@ var handler = async (input) => {
|
|
|
7295
7303
|
`) };
|
|
7296
7304
|
};
|
|
7297
7305
|
var handler_default = handler;
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
};
|
|
7306
|
+
|
|
7307
|
+
// ../../../../../tmp/nexus-hook-entry-prompt-router-1776690665662/prompt-router-entry.ts
|
|
7308
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
7309
|
+
globalThis.__NEXUS_INLINE_INVOCATIONS__ = { subagent_spawn: { args: ["target_role", "prompt", "name"], templates: { claude: 'Agent({ subagent_type: "{target_role}", prompt: "{prompt}", description: "{name}" })', opencode: 'task({ subagent_type: "{target_role}", prompt: "{prompt}", description: "{name}" })', codex: 'spawn_agent("{target_role}", "{prompt}")' }, notes: { claude: `description field is optional; omit when name arg is absent. model field may be added to override the spawned agent's model.
|
|
7310
|
+
`, opencode: `description is required by OpenCode's Zod schema — use target_role as fallback when name is absent. task_id param enables session resume (§15).
|
|
7311
|
+
`, codex: `Agent role must be pre-registered in config.toml [agents.<target_role>]. No description equivalent in spawn_agent call signature.
|
|
7312
|
+
` } }, skill_activation: { args: ["skill", "mode"], templates: { claude: 'Skill({ command: "{skill}" })', opencode: 'skill({ name: "{skill}" })', codex: "${skill}" }, notes: { claude: "The Skill tool accepts only the `command` string; mode/args cannot be passed as a separate parameter. Mode must be embedded in the SKILL.md body via $ARGUMENTS placeholder substitution.\n", opencode: "The skill tool accepts only `name`; args passing is unconfirmed per source review. Design skills to be args-free or embed defaults in SKILL.md body.\n", codex: `Positional args: "$skill-name {mode}". Named args: "$skill-name MODE={mode}". Confirmed only for Custom Prompts; treat as best-effort for Skills. $ prefix is literal in composer input, not a tool call.
|
|
7313
|
+
` } }, task_register: { args: ["label", "state"], templates: { claude: 'TaskCreate({ subject: "{label}" }) then nx_task_update({ taskId, status: "{state}" })', opencode: 'nx_task_add({ subject: "{label}" }) then nx_task_update({ taskId, status: "{state}" })', codex: 'update_plan([{ name: "{label}", state: "{state}" }])' }, notes: { claude: `TaskCreate is a Claude Code native tool (not MCP). nx_task_update is the nexus-core MCP tool — full name mcp__plugin_claude-nexus_nx__nx_task_update. taskId is returned by TaskCreate and must be threaded to the update call.
|
|
7314
|
+
`, opencode: `Both nx_task_add and nx_task_update are nexus-core MCP tools exposed via the nexus MCP server. task_id returned by nx_task_add must be threaded to update.
|
|
7315
|
+
`, codex: `update_plan subsumes creation and update in a single call via plan/step/status fields. No separate create/update step is needed.
|
|
7316
|
+
` } }, user_question: { args: ["question", "options"], templates: { claude: 'AskUserQuestion({ questions: [{ question: "{question}", options: {options} }] })', opencode: 'question({ question: "{question}", choices: {options} })', codex: 'request_user_input({ prompt: "{question}", options: {options} })' }, notes: { claude: `options is a JSON array of strings. Omit the options field (not an empty array) for free-text responses. Multiple questions can be batched in the questions[] array.
|
|
7317
|
+
`, opencode: `choices field name (not options). Omit choices for free-text input.
|
|
7318
|
+
`, codex: `Exact request_user_input schema (options field name, types) is not fully documented — treat as best-effort. Verified as a native Codex tool in external-codex-hooks-tools.md §7-4.
|
|
7319
|
+
` } } };
|
|
7320
|
+
globalThis.__NEXUS_INLINE_RULE_TARGETS__ = ["architect", "designer", "engineer", "reviewer", "strategist", "researcher", "postdoc", "lead", "tester", "writer", "nx-run", "nx-init", "nx-sync", "nx-plan"];
|
|
7321
|
+
async function main() {
|
|
7322
|
+
let raw = "";
|
|
7323
|
+
try {
|
|
7324
|
+
raw = readFileSync2(0, "utf-8");
|
|
7325
|
+
} catch {}
|
|
7326
|
+
const input = raw ? JSON.parse(raw) : {};
|
|
7327
|
+
const result = await handler_default(input);
|
|
7328
|
+
if (result != null && result !== undefined) {
|
|
7329
|
+
process.stdout.write(JSON.stringify(result));
|
|
7330
|
+
}
|
|
7331
|
+
}
|
|
7332
|
+
main().then(() => process.exit(0), (err) => {
|
|
7333
|
+
process.stderr.write(String(err?.stack ?? err) + `
|
|
7334
|
+
`);
|
|
7335
|
+
process.exit(1);
|
|
7336
|
+
});
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
// assets/hooks/session-init/handler.ts
|
|
2
2
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join, basename } from "node:path";
|
|
4
|
+
|
|
5
|
+
// src/shared/paths.ts
|
|
6
|
+
function getParentPid() {
|
|
7
|
+
const testOverride = parseInt(process.env["NEXUS_TEST_PPID"] ?? "");
|
|
8
|
+
return testOverride || process.ppid;
|
|
9
|
+
}
|
|
10
|
+
var byPpidCache = new Map;
|
|
11
|
+
|
|
12
|
+
// assets/hooks/session-init/handler.ts
|
|
4
13
|
var handler = async (input) => {
|
|
5
14
|
if (input.hook_event_name !== "SessionStart")
|
|
6
15
|
return;
|
|
@@ -14,8 +23,28 @@ var handler = async (input) => {
|
|
|
14
23
|
mkdirSync(sessionDir, { recursive: true });
|
|
15
24
|
writeFileSync(join(sessionDir, "agent-tracker.json"), "[]");
|
|
16
25
|
writeFileSync(join(sessionDir, "tool-log.jsonl"), "");
|
|
26
|
+
const ppid = getParentPid();
|
|
27
|
+
const byPpidDir = join(input.cwd, ".nexus/state/runtime/by-ppid");
|
|
28
|
+
mkdirSync(byPpidDir, { recursive: true });
|
|
29
|
+
writeFileSync(join(byPpidDir, `${ppid}.json`), JSON.stringify({ session_id: input.session_id, updated_at: new Date().toISOString(), cwd: input.cwd }));
|
|
17
30
|
};
|
|
18
31
|
var handler_default = handler;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
};
|
|
32
|
+
|
|
33
|
+
// ../../../../../tmp/nexus-hook-entry-session-init-1776690665653/session-init-entry.ts
|
|
34
|
+
import { readFileSync } from "node:fs";
|
|
35
|
+
async function main() {
|
|
36
|
+
let raw = "";
|
|
37
|
+
try {
|
|
38
|
+
raw = readFileSync(0, "utf-8");
|
|
39
|
+
} catch {}
|
|
40
|
+
const input = raw ? JSON.parse(raw) : {};
|
|
41
|
+
const result = await handler_default(input);
|
|
42
|
+
if (result != null && result !== undefined) {
|
|
43
|
+
process.stdout.write(JSON.stringify(result));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
main().then(() => process.exit(0), (err) => {
|
|
47
|
+
process.stderr.write(String(err?.stack ?? err) + `
|
|
48
|
+
`);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
});
|
package/hooks/hooks.json
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"hooks": {
|
|
3
|
+
"PostToolUse": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "Read|Edit|Write|MultiEdit|NotebookEdit",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/dist/hooks/post-tool-telemetry.js",
|
|
10
|
+
"timeout": 5
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
],
|
|
3
15
|
"SessionStart": [
|
|
4
16
|
{
|
|
5
17
|
"matcher": "*",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-nexus",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Agent orchestration plugin for Claude Code — optimized context injection per role",
|
|
6
6
|
"author": "kih",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"VERSION"
|
|
37
37
|
],
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@moreih29/nexus-core": "^0.
|
|
39
|
+
"@moreih29/nexus-core": "^0.17.0"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {}
|
|
42
42
|
}
|