claude-nexus 0.24.1 → 0.25.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/README.en.md +28 -5
- package/README.md +28 -5
- package/VERSION +1 -1
- package/agents/architect.md +1 -0
- package/agents/designer.md +1 -0
- package/agents/engineer.md +1 -0
- package/agents/postdoc.md +1 -0
- package/agents/researcher.md +1 -0
- package/agents/reviewer.md +1 -0
- package/agents/strategist.md +1 -0
- package/agents/tester.md +1 -0
- package/agents/writer.md +1 -0
- package/bridge/mcp-server.cjs +39 -4
- package/bridge/mcp-server.cjs.map +2 -2
- package/package.json +1 -1
- package/scripts/gate.cjs +130 -54
- package/scripts/gate.cjs.map +4 -4
- package/skills/nx-plan/SKILL.md +17 -3
- package/skills/nx-run/SKILL.md +17 -4
package/README.en.md
CHANGED
|
@@ -88,12 +88,13 @@ Typical flow: `[plan]` to discuss and align → `[d]` to decide (within plan)
|
|
|
88
88
|
|
|
89
89
|
Claude-callable tools exposed by the Nexus MCP server.
|
|
90
90
|
|
|
91
|
-
### Core (
|
|
91
|
+
### Core (11 tools)
|
|
92
92
|
|
|
93
93
|
| Tool | Purpose |
|
|
94
94
|
|------|---------|
|
|
95
95
|
| `nx_context` | Current session state lookup (branch, tasks, plan) |
|
|
96
96
|
| `nx_task_list/add/update/close` | Task management + history.json archiving |
|
|
97
|
+
| `nx_history_search` | Search past plan/task cycles (topic/decision query, last N) |
|
|
97
98
|
| `nx_artifact_write` | Save artifacts (branch-isolated) |
|
|
98
99
|
| `nx_plan_start` | Start plan session (topic + issues + research summary) |
|
|
99
100
|
| `nx_plan_status` | Query plan state |
|
|
@@ -127,11 +128,12 @@ Nexus registers a single Gate module as a Claude Code hook.
|
|
|
127
128
|
|
|
128
129
|
| Event | Role |
|
|
129
130
|
|-------|------|
|
|
130
|
-
| `SessionStart` | Initialize `.nexus/` structure, reset agent-tracker |
|
|
131
|
+
| `SessionStart` | Initialize `.nexus/` structure, reset agent-tracker, write `runtime.json` (teams_enabled), init `tool-log.jsonl` |
|
|
131
132
|
| `UserPromptSubmit` | Tag detection → mode activation + TASK_PIPELINE injection + additionalContext guidance |
|
|
132
133
|
| `PreToolUse` | Edit/Write: blocks when incomplete tasks exist |
|
|
133
|
-
| `
|
|
134
|
-
| `
|
|
134
|
+
| `PostToolUse` | On Edit/Write/NotebookEdit, append subagent file edits to `tool-log.jsonl` (only when agent_id present) |
|
|
135
|
+
| `SubagentStart` | Auto-inject role-filtered core knowledge index (lazy-read). Upsert `resume_count`/`last_resumed_at` when agent_id recurs |
|
|
136
|
+
| `SubagentStop` | Record agent completion + aggregate `tool-log.jsonl` → inject `files_touched` into agent-tracker entry |
|
|
135
137
|
| `Stop` | Blocks exit with pending tasks. Forces nx_task_close when all completed |
|
|
136
138
|
| `PostCompact` | Snapshot session state (mode, plan, agent status) |
|
|
137
139
|
|
|
@@ -166,8 +168,29 @@ Runtime state is stored under `.nexus/state/` and is excluded from git.
|
|
|
166
168
|
.nexus/state/
|
|
167
169
|
├── tasks.json ← Task list ([run] cycle)
|
|
168
170
|
├── plan.json ← Planning session ([plan] cycle)
|
|
169
|
-
├── agent-tracker.json ← Subagent lifecycle tracking
|
|
171
|
+
├── agent-tracker.json ← Subagent lifecycle tracking (resume_count, files_touched)
|
|
172
|
+
├── runtime.json ← teams_enabled flag + session_started_at + plugin_version
|
|
173
|
+
├── tool-log.jsonl ← Subagent Edit/Write/NotebookEdit call log (append-only)
|
|
170
174
|
└── artifacts/ ← Artifacts
|
|
171
175
|
```
|
|
172
176
|
|
|
173
177
|
</details>
|
|
178
|
+
|
|
179
|
+
<details>
|
|
180
|
+
<summary>Subagent Resume (resume_tier)</summary>
|
|
181
|
+
|
|
182
|
+
Completed subagents can be resumed within the same parent session via `SendMessage`. Each agent's `resume_tier` frontmatter classifies its policy.
|
|
183
|
+
|
|
184
|
+
| Tier | Policy | Agents |
|
|
185
|
+
|------|--------|--------|
|
|
186
|
+
| **persistent** | Default-resume within same issue, Lead opt-in across issues, forced fresh on counter-evidence/review | architect, designer, postdoc, strategist, researcher |
|
|
187
|
+
| **bounded** | Conditional resume only when working on the same artifact (file); forced fresh on loop/feedback cycles | engineer, writer |
|
|
188
|
+
| **ephemeral** | Always fresh spawn (preserves verification independence) | tester, reviewer |
|
|
189
|
+
|
|
190
|
+
**Activation requirement**: environment variable `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1`. When absent, automatically falls back to fresh spawn (no errors).
|
|
191
|
+
|
|
192
|
+
**Rationale**: Agents are classified by their dominant work surface. Reasoning surface (exists only in agent context) → `persistent` (HOW + Researcher). Mixed with artifact surface (persisted to filesystem) → `bounded` (Engineer, Writer). Verification independence as quality metric → `ephemeral` (Tester, Reviewer). See `.nexus/memory/persistence-surface-theory.md` for details.
|
|
193
|
+
|
|
194
|
+
**Tracking**: `PostToolUse` hook appends subagent file edits to `tool-log.jsonl` → `SubagentStop` aggregates them into `files_touched` on `agent-tracker.json` → Lead uses this for bounded-tier conditional resume decisions.
|
|
195
|
+
|
|
196
|
+
</details>
|
package/README.md
CHANGED
|
@@ -75,12 +75,13 @@ claude plugin install claude-nexus@nexus
|
|
|
75
75
|
|
|
76
76
|
Claude가 직접 호출하는 도구입니다.
|
|
77
77
|
|
|
78
|
-
### Core (
|
|
78
|
+
### Core (11개)
|
|
79
79
|
|
|
80
80
|
| 도구 | 용도 |
|
|
81
81
|
|------|------|
|
|
82
82
|
| `nx_context` | 현재 세션 상태 조회 (브랜치, 태스크, 플랜) |
|
|
83
83
|
| `nx_task_list/add/update/close` | `.nexus/state/tasks.json` 기반 태스크 관리 + `.nexus/history.json` 아카이브 |
|
|
84
|
+
| `nx_history_search` | 과거 plan/task 사이클 검색 (topic/decision 검색, 최근 N개) |
|
|
84
85
|
| `nx_artifact_write` | 팀 산출물 저장 (`.nexus/state/artifacts/`) |
|
|
85
86
|
| `nx_plan_start` | 플랜 세션 시작 (토픽 + 논점 + 리서치 요약 등록) |
|
|
86
87
|
| `nx_plan_status` | 플랜 상태 조회 |
|
|
@@ -114,11 +115,12 @@ Gate 단일 모듈로 동작합니다.
|
|
|
114
115
|
|
|
115
116
|
| 이벤트 | 역할 |
|
|
116
117
|
|--------|------|
|
|
117
|
-
| `SessionStart` | `.nexus/` 구조 초기화, agent-tracker
|
|
118
|
+
| `SessionStart` | `.nexus/` 구조 초기화, agent-tracker 리셋, `runtime.json` 기록 (teams_enabled), `tool-log.jsonl` 초기화 |
|
|
118
119
|
| `UserPromptSubmit` | 태그 감지 → 모드 활성화 + TASK_PIPELINE 주입 + additionalContext 안내 |
|
|
119
120
|
| `PreToolUse` | Edit/Write: tasks.json 미완료 시 차단 |
|
|
120
|
-
| `
|
|
121
|
-
| `
|
|
121
|
+
| `PostToolUse` | Edit/Write/NotebookEdit 호출 시 서브에이전트의 파일 수정을 `tool-log.jsonl`에 append (agent_id 있을 때만) |
|
|
122
|
+
| `SubagentStart` | 에이전트 역할별 코어 지식 인덱스 자동 주입 (lazy-read). 기존 agent_id 재발 시 `resume_count`/`last_resumed_at` upsert |
|
|
123
|
+
| `SubagentStop` | 에이전트 완료 기록 + `tool-log.jsonl` 집계 → `files_touched` 주입 |
|
|
122
124
|
| `Stop` | pending 태스크 있으면 종료 차단. all completed면 nx_task_close 강제 |
|
|
123
125
|
| `PostCompact` | 세션 상태 스냅샷 (모드, 플랜, 에이전트 현황) |
|
|
124
126
|
|
|
@@ -153,8 +155,29 @@ Gate 단일 모듈로 동작합니다.
|
|
|
153
155
|
.nexus/state/
|
|
154
156
|
├── tasks.json ← 태스크 목록 ([run] 사이클)
|
|
155
157
|
├── plan.json ← 플랜 세션 ([plan] 사이클)
|
|
156
|
-
├── agent-tracker.json ← 서브에이전트 라이프사이클
|
|
158
|
+
├── agent-tracker.json ← 서브에이전트 라이프사이클 (resume_count, files_touched 포함)
|
|
159
|
+
├── runtime.json ← teams_enabled 플래그 + session_started_at + plugin_version
|
|
160
|
+
├── tool-log.jsonl ← 서브에이전트 Edit/Write/NotebookEdit 호출 로그 (append-only)
|
|
157
161
|
└── artifacts/ ← 산출물
|
|
158
162
|
```
|
|
159
163
|
|
|
160
164
|
</details>
|
|
165
|
+
|
|
166
|
+
<details>
|
|
167
|
+
<summary>에이전트 Resume (resume_tier)</summary>
|
|
168
|
+
|
|
169
|
+
같은 부모 세션 내에서 종료된 서브에이전트를 `SendMessage`로 재개할 수 있습니다. 에이전트별 `resume_tier` frontmatter로 정책을 분류합니다.
|
|
170
|
+
|
|
171
|
+
| Tier | 정책 | 에이전트 |
|
|
172
|
+
|------|------|---------|
|
|
173
|
+
| **persistent** | 같은 이슈 내 default-resume, 이슈 간 Lead 명시적 opt-in, 반증/재검토는 강제 fresh | architect, designer, postdoc, strategist, researcher |
|
|
174
|
+
| **bounded** | 같은 artifact(파일) 연속 작업 시 conditional-resume, loop/feedback 사이클은 강제 fresh | engineer, writer |
|
|
175
|
+
| **ephemeral** | 항상 fresh spawn (검증 독립성 보장) | tester, reviewer |
|
|
176
|
+
|
|
177
|
+
**활성화 요구사항**: 환경 변수 `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1`. 미감지 시 자동 fresh spawn fallback (에러 없음).
|
|
178
|
+
|
|
179
|
+
**이론 근거**: 에이전트의 본질적 작업 층위를 두 surface로 구분합니다. Reasoning surface(에이전트 컨텍스트에만 존재)가 지배적인 HOW/Researcher는 `persistent`, Artifact surface(파일 시스템에 persist)에 걸친 Engineer/Writer는 `bounded`, 검증 독립성이 품질 지표인 Tester/Reviewer는 `ephemeral`. 자세한 내용은 `.nexus/memory/persistence-surface-theory.md`.
|
|
180
|
+
|
|
181
|
+
**연계 추적**: `PostToolUse` 훅이 서브에이전트의 파일 수정을 `tool-log.jsonl`에 append → `SubagentStop`이 집계하여 `agent-tracker.json`의 `files_touched`에 주입 → Lead가 bounded tier 조건부 resume 판단에 활용.
|
|
182
|
+
|
|
183
|
+
</details>
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.25.0
|
package/agents/architect.md
CHANGED
package/agents/designer.md
CHANGED
package/agents/engineer.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
package/bridge/mcp-server.cjs
CHANGED
|
@@ -22040,9 +22040,10 @@ function registerPlanTools(server2) {
|
|
|
22040
22040
|
issue_id: external_exports.number().describe("\uACB0\uC815\uD560 \uC548\uAC74 ID"),
|
|
22041
22041
|
summary: external_exports.string().describe("\uACB0\uC815 \uC694\uC57D"),
|
|
22042
22042
|
how_agents: external_exports.array(external_exports.string()).optional().describe('\uC774\uC288 \uBD84\uC11D\uC5D0 \uCC38\uC5EC\uD55C HOW \uC5D0\uC774\uC804\uD2B8 \uC774\uB984 \uBAA9\uB85D (\uC608: ["architect", "designer"])'),
|
|
22043
|
-
how_summary: external_exports.record(external_exports.string(), external_exports.string()).optional().describe('\uC5D0\uC774\uC804\uD2B8\uBCC4 \uD575\uC2EC \uC758\uACAC \uC694\uC57D (\uC608: { "architect": "...", "designer": "..." })')
|
|
22043
|
+
how_summary: external_exports.record(external_exports.string(), external_exports.string()).optional().describe('\uC5D0\uC774\uC804\uD2B8\uBCC4 \uD575\uC2EC \uC758\uACAC \uC694\uC57D (\uC608: { "architect": "...", "designer": "..." })'),
|
|
22044
|
+
how_agent_ids: external_exports.record(external_exports.string(), external_exports.string()).optional().describe('\uC5D0\uC774\uC804\uD2B8 \uC774\uB984 \u2192 agentId \uB9E4\uD551 (resume\uC6A9). \uC608: { "architect": "ac01819f1cd295cb8" }')
|
|
22044
22045
|
},
|
|
22045
|
-
async ({ issue_id, summary, how_agents, how_summary }) => {
|
|
22046
|
+
async ({ issue_id, summary, how_agents, how_summary, how_agent_ids }) => {
|
|
22046
22047
|
const data = await readPlan();
|
|
22047
22048
|
if (!data) {
|
|
22048
22049
|
return textResult({ error: "No active plan session" });
|
|
@@ -22055,6 +22056,7 @@ function registerPlanTools(server2) {
|
|
|
22055
22056
|
issue2.decision = summary;
|
|
22056
22057
|
if (how_agents !== void 0) issue2.how_agents = how_agents;
|
|
22057
22058
|
if (how_summary !== void 0) issue2.how_summary = how_summary;
|
|
22059
|
+
if (how_agent_ids !== void 0) issue2.how_agent_ids = how_agent_ids;
|
|
22058
22060
|
await writePlan(data);
|
|
22059
22061
|
const allComplete = data.issues.every((i) => i.status === "decided");
|
|
22060
22062
|
if (allComplete) {
|
|
@@ -22128,9 +22130,11 @@ function registerTaskTools(server2) {
|
|
|
22128
22130
|
plan_issue: external_exports.number().optional().describe("plan issue ID this task originates from \u2014 used for tracing back to the plan session"),
|
|
22129
22131
|
goal: external_exports.string().optional().describe("Set or update the goal for this task list"),
|
|
22130
22132
|
decisions: external_exports.array(external_exports.string()).optional().describe("Top-level decisions from [plan] session to append"),
|
|
22131
|
-
owner: external_exports.string().optional().describe("Assignee agent name for this task")
|
|
22133
|
+
owner: external_exports.string().optional().describe("Assignee agent name for this task"),
|
|
22134
|
+
owner_agent_id: external_exports.string().optional().describe("\uD2B9\uC815 agentId\uB85C resume\uD560 \uB54C \uC9C0\uC815. \uBBF8\uC124\uC815 \uC2DC fresh spawn."),
|
|
22135
|
+
owner_reuse_policy: external_exports.enum(["fresh", "resume_if_same_artifact", "resume"]).optional().describe("resume \uD310\uB2E8 \uC815\uCC45. fresh=\uAC15\uC81C \uC0C8 \uC2A4\uD3F0, resume_if_same_artifact=\uC774\uC804 owner\uAC00 \uAC19\uC740 artifact(target file) \uB9CC\uC84C\uC744 \uB54C\uB9CC resume, resume=\uBB34\uC870\uAC74 resume \uC2DC\uB3C4. bounded tier\uB294 resume_if_same_artifact \uAD8C\uC7A5.")
|
|
22132
22136
|
},
|
|
22133
|
-
async ({ title, context, deps, approach, acceptance, risk, plan_issue, goal, decisions, owner }) => {
|
|
22137
|
+
async ({ title, context, deps, approach, acceptance, risk, plan_issue, goal, decisions, owner, owner_agent_id, owner_reuse_policy }) => {
|
|
22134
22138
|
let data = await readTasks();
|
|
22135
22139
|
if (!data) {
|
|
22136
22140
|
data = { goal: "", decisions: [], tasks: [] };
|
|
@@ -22153,6 +22157,8 @@ function registerTaskTools(server2) {
|
|
|
22153
22157
|
deps: deps ?? [],
|
|
22154
22158
|
plan_issue,
|
|
22155
22159
|
owner,
|
|
22160
|
+
owner_agent_id,
|
|
22161
|
+
owner_reuse_policy,
|
|
22156
22162
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
22157
22163
|
};
|
|
22158
22164
|
data.tasks.push(newTask);
|
|
@@ -22242,6 +22248,35 @@ function registerTaskTools(server2) {
|
|
|
22242
22248
|
});
|
|
22243
22249
|
}
|
|
22244
22250
|
);
|
|
22251
|
+
server2.tool(
|
|
22252
|
+
"nx_history_search",
|
|
22253
|
+
"Search past plan/task cycles in history.json",
|
|
22254
|
+
{
|
|
22255
|
+
query: external_exports.string().optional().describe("Search term to match against topic, decisions, research_summary"),
|
|
22256
|
+
last_n: external_exports.number().optional().describe("Return only the last N cycles (default: 10)")
|
|
22257
|
+
},
|
|
22258
|
+
async ({ query, last_n }) => {
|
|
22259
|
+
const historyPath = (0, import_path8.join)(NEXUS_ROOT, "history.json");
|
|
22260
|
+
if (!(0, import_fs8.existsSync)(historyPath)) return textResult({ cycles: [], total: 0 });
|
|
22261
|
+
const raw = await (0, import_promises3.readFile)(historyPath, "utf-8");
|
|
22262
|
+
const history = JSON.parse(raw);
|
|
22263
|
+
let cycles = history.cycles || [];
|
|
22264
|
+
if (query) {
|
|
22265
|
+
const q = query.toLowerCase();
|
|
22266
|
+
cycles = cycles.filter((c) => JSON.stringify(c).toLowerCase().includes(q));
|
|
22267
|
+
}
|
|
22268
|
+
const total = cycles.length;
|
|
22269
|
+
const limit = last_n || 10;
|
|
22270
|
+
const results = cycles.slice(-limit).map((c) => ({
|
|
22271
|
+
completed_at: c.completed_at,
|
|
22272
|
+
branch: c.branch,
|
|
22273
|
+
topic: c.plan?.topic,
|
|
22274
|
+
decisions: c.plan?.issues?.filter((i) => i.status === "decided").map((i) => ({ title: i.title, decision: i.decision })),
|
|
22275
|
+
task_count: c.tasks?.length
|
|
22276
|
+
}));
|
|
22277
|
+
return textResult({ total, showing: results.length, cycles: results });
|
|
22278
|
+
}
|
|
22279
|
+
);
|
|
22245
22280
|
}
|
|
22246
22281
|
|
|
22247
22282
|
// src/mcp/tools/artifact.ts
|