claude-nexus 0.10.0 → 0.12.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.
@@ -7,7 +7,7 @@
7
7
  {
8
8
  "name": "claude-nexus",
9
9
  "description": "Agent orchestration plugin for Claude Code. Injects optimized context per agent role with minimal overhead.",
10
- "version": "0.10.0",
10
+ "version": "0.12.0",
11
11
  "author": {
12
12
  "name": "kih"
13
13
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-nexus",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "Agent orchestration plugin for Claude Code — optimized context injection per role",
5
5
  "author": {
6
6
  "name": "kih"
package/README.en.md CHANGED
@@ -80,14 +80,18 @@ Typical flow: use `[consult]` to discuss and align → decide → use `[dev]` or
80
80
 
81
81
  Claude-callable tools exposed by the Nexus MCP server.
82
82
 
83
- ### Core (5 tools)
83
+ ### Core (9 tools)
84
84
 
85
85
  | Tool | Purpose |
86
86
  |------|---------|
87
87
  | `nx_knowledge_read/write` | Project knowledge management (git-tracked) |
88
- | `nx_context` | Current session state lookup |
88
+ | `nx_context` | Current session state lookup (branch, tasks, decisions) |
89
89
  | `nx_task_list/add/update/clear` | Task management backed by tasks.json |
90
90
  | `nx_decision_add` | Record architecture decisions |
91
+ | `nx_artifact_write` | Save team artifacts (branch-isolated) |
92
+ | `nx_consult_start` | Start consultation session (topic + issues) |
93
+ | `nx_consult_status` | Query consultation state |
94
+ | `nx_consult_decide` | Record issue decision (consult.json + decisions.json) |
91
95
 
92
96
  ### Code Intelligence (10 tools)
93
97
 
@@ -142,7 +146,8 @@ Runtime state is stored under `.nexus/` and is excluded from git.
142
146
  │ └── {branch}/
143
147
  │ ├── tasks.json ← Task list
144
148
  │ ├── decisions.json ← Architecture decision list
145
- └── reports/ Research outputs
149
+ ├── consult.json Consultation issue tracker (exists only during consult)
150
+ │ └── artifacts/ ← Team artifacts
146
151
  └── sync-state.json ← Last sync commit
147
152
  ```
148
153
 
package/README.md CHANGED
@@ -78,14 +78,18 @@ claude plugin install claude-nexus@nexus
78
78
 
79
79
  Claude가 직접 호출하는 도구입니다.
80
80
 
81
- ### Core (5개)
81
+ ### Core (9개)
82
82
 
83
83
  | 도구 | 용도 |
84
84
  |------|------|
85
85
  | `nx_knowledge_read/write` | 프로젝트 지식 관리 (git 추적) |
86
- | `nx_context` | 현재 세션 상태 조회 |
86
+ | `nx_context` | 현재 세션 상태 조회 (브랜치, 태스크, 결정) |
87
87
  | `nx_task_list/add/update/clear` | tasks.json 기반 태스크 관리 |
88
88
  | `nx_decision_add` | 아키텍처 결정 기록 |
89
+ | `nx_artifact_write` | 팀 산출물 저장 (브랜치별 격리) |
90
+ | `nx_consult_start` | 상담 세션 시작 (토픽 + 논점 등록) |
91
+ | `nx_consult_status` | 상담 상태 조회 |
92
+ | `nx_consult_decide` | 논점 결정 처리 (consult.json + decisions.json) |
89
93
 
90
94
  ### Code Intelligence (10개)
91
95
 
@@ -140,7 +144,8 @@ Gate 단일 모듈로 동작합니다.
140
144
  │ └── {branch}/
141
145
  │ ├── tasks.json ← 태스크 목록
142
146
  │ ├── decisions.json ← 아키텍처 결정 목록
143
- └── reports/ 리서치 산출물
147
+ ├── consult.json 상담 논점 추적 (상담 중에만 존재)
148
+ │ └── artifacts/ ← 팀 산출물
144
149
  └── sync-state.json ← 마지막 sync 커밋
145
150
  ```
146
151
 
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.10.0
1
+ 0.12.0
@@ -2,6 +2,7 @@
2
2
  name: architect
3
3
  model: opus
4
4
  description: Technical design — evaluates How, reviews architecture, advises on implementation approach
5
+ task: "Architecture, technical design, code review"
5
6
  maxTurns: 20
6
7
  disallowedTools: [Edit, Write, NotebookEdit]
7
8
  tags: [architecture, design, review, technical]
@@ -2,6 +2,7 @@
2
2
  name: director
3
3
  model: opus
4
4
  description: Project direction — analyzes Why/What, owns task lifecycle, decides scope and priorities
5
+ task: "Project direction, scope, priorities"
5
6
  maxTurns: 30
6
7
  disallowedTools: [Edit, Write, NotebookEdit]
7
8
  tags: [direction, planning, task-management]
@@ -2,6 +2,7 @@
2
2
  name: engineer
3
3
  model: sonnet
4
4
  description: Implementation — writes code, debugs issues, follows specifications from director and architect
5
+ task: "Code implementation, edits, debugging"
5
6
  maxTurns: 25
6
7
  disallowedTools: []
7
8
  tags: [implementation, coding, debugging]
package/agents/postdoc.md CHANGED
@@ -2,6 +2,7 @@
2
2
  name: postdoc
3
3
  model: opus
4
4
  description: Research methodology and synthesis — designs investigation approach, evaluates evidence quality, writes synthesis documents
5
+ task: "Research methodology, evidence synthesis"
5
6
  maxTurns: 25
6
7
  disallowedTools: [Edit, Bash, NotebookEdit]
7
8
  tags: [research, synthesis, methodology]
@@ -63,6 +64,9 @@ When researcher submits findings:
63
64
  - Ask clarifying questions if findings are ambiguous
64
65
  - Escalate to principal if researcher's findings reveal the original question was malformed
65
66
 
67
+ ## Saving Artifacts
68
+ When writing synthesis documents or other deliverables, use `nx_artifact_write` (filename, content) instead of Write. This ensures the file is saved to the correct branch workspace.
69
+
66
70
  ## What You Do NOT Do
67
71
  - Run shell commands or modify the codebase
68
72
  - Create or update tasks (advise principal, who owns tasks)
@@ -2,6 +2,7 @@
2
2
  name: principal
3
3
  model: opus
4
4
  description: Research direction — owns research agenda, task lifecycle, and consensus with postdoc to prevent confirmation bias
5
+ task: "Research direction, agenda, bias prevention"
5
6
  maxTurns: 25
6
7
  disallowedTools: [Edit, Write, Bash, NotebookEdit]
7
8
  tags: [research, direction, task-management]
package/agents/qa.md CHANGED
@@ -2,6 +2,7 @@
2
2
  name: qa
3
3
  model: sonnet
4
4
  description: Quality assurance — tests, verifies, validates stability and security of implementations
5
+ task: "Testing, verification, security review"
5
6
  maxTurns: 20
6
7
  disallowedTools: []
7
8
  tags: [verification, testing, security, quality]
@@ -74,6 +75,9 @@ When encountering structural issues that are difficult to assess technically:
74
75
  - Escalate to architect via SendMessage for technical assessment
75
76
  - If the issue is a design flaw (not just a bug), notify both architect and director
76
77
 
78
+ ## Saving Artifacts
79
+ When writing verification reports or other deliverables to a file, use `nx_artifact_write` (filename, content) instead of Write. This ensures the file is saved to the correct branch workspace.
80
+
77
81
  ## What You Do NOT Do
78
82
  - Fix application code yourself — only test code (test files) may be edited
79
83
  - Call nx_task_add or nx_task_update directly — report to director, who owns tasks
@@ -2,6 +2,7 @@
2
2
  name: researcher
3
3
  model: sonnet
4
4
  description: Independent investigation — conducts web searches, gathers evidence, and reports findings with citations
5
+ task: "Web search, independent investigation"
5
6
  maxTurns: 20
6
7
  disallowedTools: []
7
8
  tags: [research, investigation, web-search, analysis]
@@ -63,6 +64,9 @@ If a research question is ambiguous or contradicts itself:
63
64
  - If the question itself seems malformed, flag it to principal via postdoc
64
65
  - Do not guess at intent — ask
65
66
 
67
+ ## Saving Artifacts
68
+ When writing findings reports or other deliverables to a file, use `nx_artifact_write` (filename, content) instead of Write. This ensures the file is saved to the correct branch workspace.
69
+
66
70
  ## What You Do NOT Do
67
71
  - Present findings stronger than the evidence supports
68
72
  - Omit contradicting evidence because it's inconvenient
@@ -21045,6 +21045,11 @@ function migrateLegacyBranchDir(branchName) {
21045
21045
  }
21046
21046
  migrateLegacyBranchDir(CURRENT_BRANCH);
21047
21047
  var BRANCH_ROOT = (0, import_path.join)(RUNTIME_ROOT, "branches", sanitizeBranch(CURRENT_BRANCH));
21048
+ function getBranchRoot() {
21049
+ const branch = getCurrentBranch();
21050
+ migrateLegacyBranchDir(branch);
21051
+ return (0, import_path.join)(RUNTIME_ROOT, "branches", sanitizeBranch(branch));
21052
+ }
21048
21053
 
21049
21054
  // src/mcp/tools/knowledge.ts
21050
21055
  var import_path2 = require("path");
@@ -21139,7 +21144,7 @@ function registerContextTool(server2) {
21139
21144
  {},
21140
21145
  async () => {
21141
21146
  let teamStatus = { activeMode: null };
21142
- const tasksFile = (0, import_path3.join)(BRANCH_ROOT, "tasks.json");
21147
+ const tasksFile = (0, import_path3.join)(getBranchRoot(), "tasks.json");
21143
21148
  if ((0, import_fs3.existsSync)(tasksFile)) {
21144
21149
  try {
21145
21150
  const data = JSON.parse(await (0, import_promises2.readFile)(tasksFile, "utf-8"));
@@ -21155,9 +21160,19 @@ function registerContextTool(server2) {
21155
21160
  } catch {
21156
21161
  }
21157
21162
  }
21163
+ let decisions = [];
21164
+ const decisionsFile = (0, import_path3.join)(getBranchRoot(), "decisions.json");
21165
+ if ((0, import_fs3.existsSync)(decisionsFile)) {
21166
+ try {
21167
+ const data = JSON.parse(await (0, import_promises2.readFile)(decisionsFile, "utf-8"));
21168
+ decisions = Array.isArray(data.decisions) ? data.decisions : [];
21169
+ } catch {
21170
+ }
21171
+ }
21158
21172
  const result = {
21159
21173
  branch: getCurrentBranch2(),
21160
- ...teamStatus
21174
+ ...teamStatus,
21175
+ decisions
21161
21176
  };
21162
21177
  return { content: [{ type: "text", text: JSON.stringify(result) }] };
21163
21178
  }
@@ -22047,15 +22062,19 @@ function registerAstTools(server2) {
22047
22062
  var import_fs7 = require("fs");
22048
22063
  var import_promises3 = require("fs/promises");
22049
22064
  var import_path7 = require("path");
22050
- var TASKS_PATH = (0, import_path7.join)(BRANCH_ROOT, "tasks.json");
22065
+ function tasksPath() {
22066
+ return (0, import_path7.join)(getBranchRoot(), "tasks.json");
22067
+ }
22051
22068
  async function readTasks() {
22052
- if (!(0, import_fs7.existsSync)(TASKS_PATH)) return null;
22053
- const raw = await (0, import_promises3.readFile)(TASKS_PATH, "utf-8");
22069
+ const p = tasksPath();
22070
+ if (!(0, import_fs7.existsSync)(p)) return null;
22071
+ const raw = await (0, import_promises3.readFile)(p, "utf-8");
22054
22072
  return JSON.parse(raw);
22055
22073
  }
22056
22074
  async function writeTasks(data) {
22057
- ensureDir(BRANCH_ROOT);
22058
- await (0, import_promises3.writeFile)(TASKS_PATH, JSON.stringify(data, null, 2));
22075
+ const root = getBranchRoot();
22076
+ ensureDir(root);
22077
+ await (0, import_promises3.writeFile)((0, import_path7.join)(root, "tasks.json"), JSON.stringify(data, null, 2));
22059
22078
  }
22060
22079
  function computeSummary(tasks) {
22061
22080
  const total = tasks.length;
@@ -22158,11 +22177,11 @@ function registerTaskTools(server2) {
22158
22177
  "Delete .nexus/tasks.json to abort the current plan and release the nonstop block",
22159
22178
  {},
22160
22179
  async () => {
22161
- if (!(0, import_fs7.existsSync)(TASKS_PATH)) {
22180
+ if (!(0, import_fs7.existsSync)(tasksPath())) {
22162
22181
  return { content: [{ type: "text", text: JSON.stringify({ cleared: false, reason: "tasks.json not found" }) }] };
22163
22182
  }
22164
22183
  try {
22165
- (0, import_fs7.unlinkSync)(TASKS_PATH);
22184
+ (0, import_fs7.unlinkSync)(tasksPath());
22166
22185
  return { content: [{ type: "text", text: JSON.stringify({ cleared: true }) }] };
22167
22186
  } catch (e) {
22168
22187
  return { content: [{ type: "text", text: JSON.stringify({ cleared: false, reason: String(e) }) }] };
@@ -22175,17 +22194,21 @@ function registerTaskTools(server2) {
22175
22194
  var import_fs8 = require("fs");
22176
22195
  var import_promises4 = require("fs/promises");
22177
22196
  var import_path8 = require("path");
22178
- var DECISIONS_PATH = (0, import_path8.join)(BRANCH_ROOT, "decisions.json");
22197
+ function decisionsPath() {
22198
+ return (0, import_path8.join)(getBranchRoot(), "decisions.json");
22199
+ }
22179
22200
  async function readDecisions() {
22180
- if (!(0, import_fs8.existsSync)(DECISIONS_PATH)) {
22201
+ const p = decisionsPath();
22202
+ if (!(0, import_fs8.existsSync)(p)) {
22181
22203
  return { decisions: [] };
22182
22204
  }
22183
- const raw = await (0, import_promises4.readFile)(DECISIONS_PATH, "utf-8");
22205
+ const raw = await (0, import_promises4.readFile)(p, "utf-8");
22184
22206
  return JSON.parse(raw);
22185
22207
  }
22186
22208
  async function writeDecisions(data) {
22187
- ensureDir(BRANCH_ROOT);
22188
- await (0, import_promises4.writeFile)(DECISIONS_PATH, JSON.stringify(data, null, 2));
22209
+ const root = getBranchRoot();
22210
+ ensureDir(root);
22211
+ await (0, import_promises4.writeFile)((0, import_path8.join)(root, "decisions.json"), JSON.stringify(data, null, 2));
22189
22212
  }
22190
22213
  function registerDecisionTools(server2) {
22191
22214
  server2.tool(
@@ -22210,6 +22233,166 @@ function registerDecisionTools(server2) {
22210
22233
  );
22211
22234
  }
22212
22235
 
22236
+ // src/mcp/tools/artifact.ts
22237
+ var import_promises5 = require("fs/promises");
22238
+ var import_path9 = require("path");
22239
+ function registerArtifactTools(server2) {
22240
+ server2.tool(
22241
+ "nx_artifact_write",
22242
+ "Write a team artifact (report, synthesis, analysis) to the current branch workspace",
22243
+ {
22244
+ filename: external_exports.string().describe('Filename to write (e.g., "findings.md", "synthesis.md")'),
22245
+ content: external_exports.string().describe("File content to write")
22246
+ },
22247
+ async ({ filename, content }) => {
22248
+ const artifactsDir = (0, import_path9.join)(getBranchRoot(), "artifacts");
22249
+ ensureDir(artifactsDir);
22250
+ const path = (0, import_path9.join)(artifactsDir, filename);
22251
+ await (0, import_promises5.writeFile)(path, content);
22252
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, path }) }] };
22253
+ }
22254
+ );
22255
+ }
22256
+
22257
+ // src/mcp/tools/consult.ts
22258
+ var import_fs9 = require("fs");
22259
+ var import_promises6 = require("fs/promises");
22260
+ var import_path10 = require("path");
22261
+ function consultPath() {
22262
+ return (0, import_path10.join)(getBranchRoot(), "consult.json");
22263
+ }
22264
+ async function readConsult() {
22265
+ const p = consultPath();
22266
+ if (!(0, import_fs9.existsSync)(p)) return null;
22267
+ const raw = await (0, import_promises6.readFile)(p, "utf-8");
22268
+ return JSON.parse(raw);
22269
+ }
22270
+ async function writeConsult(data) {
22271
+ const root = getBranchRoot();
22272
+ ensureDir(root);
22273
+ await (0, import_promises6.writeFile)((0, import_path10.join)(root, "consult.json"), JSON.stringify(data, null, 2));
22274
+ }
22275
+ function decisionsPath2() {
22276
+ return (0, import_path10.join)(getBranchRoot(), "decisions.json");
22277
+ }
22278
+ async function readDecisions2() {
22279
+ const p = decisionsPath2();
22280
+ if (!(0, import_fs9.existsSync)(p)) return { decisions: [] };
22281
+ const raw = await (0, import_promises6.readFile)(p, "utf-8");
22282
+ return JSON.parse(raw);
22283
+ }
22284
+ async function writeDecisions2(data) {
22285
+ const root = getBranchRoot();
22286
+ ensureDir(root);
22287
+ await (0, import_promises6.writeFile)((0, import_path10.join)(root, "decisions.json"), JSON.stringify(data, null, 2));
22288
+ }
22289
+ function registerConsultTools(server2) {
22290
+ server2.tool(
22291
+ "nx_consult_start",
22292
+ "Start a new consultation session with topic and issues to discuss",
22293
+ {
22294
+ topic: external_exports.string().describe("Consultation topic"),
22295
+ issues: external_exports.array(external_exports.string()).describe("List of issue titles to discuss")
22296
+ },
22297
+ async ({ topic, issues }) => {
22298
+ const data = {
22299
+ topic,
22300
+ issues: issues.map((title, i) => ({
22301
+ id: i + 1,
22302
+ title,
22303
+ status: "pending"
22304
+ }))
22305
+ };
22306
+ await writeConsult(data);
22307
+ return {
22308
+ content: [{
22309
+ type: "text",
22310
+ text: JSON.stringify({ created: true, topic, issueCount: issues.length })
22311
+ }]
22312
+ };
22313
+ }
22314
+ );
22315
+ server2.tool(
22316
+ "nx_consult_status",
22317
+ "Get current consultation status: topic, issues, and their statuses",
22318
+ {},
22319
+ async () => {
22320
+ const data = await readConsult();
22321
+ if (!data) {
22322
+ return { content: [{ type: "text", text: JSON.stringify({ active: false }) }] };
22323
+ }
22324
+ const pending = data.issues.filter((i) => i.status === "pending").length;
22325
+ const discussing = data.issues.filter((i) => i.status === "discussing").length;
22326
+ const decided = data.issues.filter((i) => i.status === "decided").length;
22327
+ return {
22328
+ content: [{
22329
+ type: "text",
22330
+ text: JSON.stringify({
22331
+ active: true,
22332
+ topic: data.topic,
22333
+ issues: data.issues,
22334
+ summary: { total: data.issues.length, pending, discussing, decided }
22335
+ })
22336
+ }]
22337
+ };
22338
+ }
22339
+ );
22340
+ server2.tool(
22341
+ "nx_consult_decide",
22342
+ "Mark a consultation issue as decided and record the decision",
22343
+ {
22344
+ issue_id: external_exports.number().describe("Issue ID to mark as decided"),
22345
+ summary: external_exports.string().describe("Decision summary to record")
22346
+ },
22347
+ async ({ issue_id, summary }) => {
22348
+ const data = await readConsult();
22349
+ if (!data) {
22350
+ return { content: [{ type: "text", text: JSON.stringify({ error: "No active consultation" }) }] };
22351
+ }
22352
+ const issue2 = data.issues.find((i) => i.id === issue_id);
22353
+ if (!issue2) {
22354
+ return { content: [{ type: "text", text: JSON.stringify({ error: `Issue ${issue_id} not found` }) }] };
22355
+ }
22356
+ issue2.status = "decided";
22357
+ const decisions = await readDecisions2();
22358
+ decisions.decisions.push(summary);
22359
+ await writeDecisions2(decisions);
22360
+ const allDecided = data.issues.every((i) => i.status === "decided");
22361
+ if (allDecided) {
22362
+ try {
22363
+ (0, import_fs9.unlinkSync)(consultPath());
22364
+ } catch {
22365
+ }
22366
+ return {
22367
+ content: [{
22368
+ type: "text",
22369
+ text: JSON.stringify({
22370
+ decided: true,
22371
+ issue: issue2.title,
22372
+ allComplete: true,
22373
+ message: "All issues decided. consult.json removed.",
22374
+ decisions: decisions.decisions
22375
+ })
22376
+ }]
22377
+ };
22378
+ }
22379
+ await writeConsult(data);
22380
+ const remaining = data.issues.filter((i) => i.status !== "decided");
22381
+ return {
22382
+ content: [{
22383
+ type: "text",
22384
+ text: JSON.stringify({
22385
+ decided: true,
22386
+ issue: issue2.title,
22387
+ allComplete: false,
22388
+ remaining: remaining.map((i) => ({ id: i.id, title: i.title, status: i.status }))
22389
+ })
22390
+ }]
22391
+ };
22392
+ }
22393
+ );
22394
+ }
22395
+
22213
22396
  // src/mcp/server.ts
22214
22397
  var server = new McpServer({
22215
22398
  name: "nx",
@@ -22222,6 +22405,8 @@ registerLspTools(server);
22222
22405
  registerAstTools(server);
22223
22406
  registerTaskTools(server);
22224
22407
  registerDecisionTools(server);
22408
+ registerArtifactTools(server);
22409
+ registerConsultTools(server);
22225
22410
  async function main() {
22226
22411
  const transport = new StdioServerTransport();
22227
22412
  await server.connect(transport);