claude-nexus 0.32.1 → 0.33.1
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 +6 -1
- package/README.md +6 -1
- package/package.json +2 -2
- package/scripts/statusline.mjs +177 -17
- package/skills/nx-auto-plan/SKILL.md +13 -5
- package/skills/nx-plan/SKILL.md +1 -0
package/README.en.md
CHANGED
|
@@ -51,7 +51,12 @@ With the plugin enabled, each new Claude Code session runs the `lead` agent as t
|
|
|
51
51
|
|
|
52
52
|
## Optional: statusline
|
|
53
53
|
|
|
54
|
-
The plugin ships a two-line statusline script. Line one shows `◆Nexus vX.Y.Z`, the model, the project, and the git branch with staged/unstaged counts. Line two shows context-window usage plus
|
|
54
|
+
The plugin ships a two-line statusline script. Line one shows `◆Nexus vX.Y.Z`, the model, the project, and the git branch with staged/unstaged counts. Line two shows context-window usage plus mode-specific information:
|
|
55
|
+
|
|
56
|
+
- **OAuth session (Claude Pro / Max)** — 5-hour and 7-day Claude usage gauges with the time until each resets. `$CLAUDE_CONFIG_DIR/.usage_cache` (defaults to `~/.claude/.usage_cache`) is shared across local sessions so concurrent Claude Code windows never re-fetch.
|
|
57
|
+
- **API mode (`ANTHROPIC_API_KEY` set)** — `API $X.XX today` showing the cost incurred today (UTC midnight boundary). Claude Code's local jsonl session logs are scanned directly to sum tokens per model and convert to USD using Anthropic's published pricing — no admin key setup required.
|
|
58
|
+
|
|
59
|
+
When you use `CLAUDE_CONFIG_DIR` to separate multiple OAuth accounts, the statusline's cache path, keychain query, and cost scan all branch automatically using the same algorithm as Claude Code itself.
|
|
55
60
|
|
|
56
61
|
Claude Code does not let a plugin auto-configure the user's `statusLine`, so register the `claude-nexus-statusline` CLI (shipped with the same npm package) from your own `~/.claude/settings.json`.
|
|
57
62
|
|
package/README.md
CHANGED
|
@@ -51,7 +51,12 @@ Claude Code 안에서 플러그인 마켓플레이스로 설치한다.
|
|
|
51
51
|
|
|
52
52
|
## 선택: statusline
|
|
53
53
|
|
|
54
|
-
플러그인은 2줄 statusline 스크립트를 함께 배포한다. 첫 줄은 `◆Nexus vX.Y.Z`·모델·프로젝트·git 브랜치(staged/unstaged), 둘째 줄은 컨텍스트 사용률과
|
|
54
|
+
플러그인은 2줄 statusline 스크립트를 함께 배포한다. 첫 줄은 `◆Nexus vX.Y.Z`·모델·프로젝트·git 브랜치(staged/unstaged), 둘째 줄은 컨텍스트 사용률과 모드별 사용량 정보:
|
|
55
|
+
|
|
56
|
+
- **OAuth 세션 (Claude Pro·Max)** — 5h/7d 사용 한도 게이지(리셋까지 남은 시간). 로컬의 여러 Claude 세션이 `$CLAUDE_CONFIG_DIR/.usage_cache`(미설정 시 `~/.claude/.usage_cache`)를 공유하므로 API 중복 호출 없이 경합이 방지된다.
|
|
57
|
+
- **API 모드 (`ANTHROPIC_API_KEY` 설정)** — `API $X.XX today` 형식으로 오늘(UTC 자정 기준) 발생한 비용을 표시. Claude Code가 기록하는 로컬 jsonl 세션 로그를 직접 스캔해 모델별 토큰을 합산하고 Anthropic 공식 가격표 기반으로 USD 환산하므로, 별도 admin key 셋업이 불필요하다.
|
|
58
|
+
|
|
59
|
+
`CLAUDE_CONFIG_DIR` 환경변수로 다중 OAuth 계정을 분리해 쓰는 경우, statusline의 캐시·키체인 조회·비용 스캔이 모두 본체와 동일한 알고리즘으로 자동 분기된다.
|
|
55
60
|
|
|
56
61
|
Claude Code는 플러그인이 사용자 `statusLine`을 자동 등록하는 걸 허용하지 않으므로, 별도 CLI로 배포된 `claude-nexus`를 본인의 `~/.claude/settings.json`에 등록한다.
|
|
57
62
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-nexus",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Claude Code plugin for nexus-core agent orchestration",
|
|
6
6
|
"author": "kih",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"settings.json"
|
|
41
41
|
],
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@moreih29/nexus-core": "^0.20.
|
|
43
|
+
"@moreih29/nexus-core": "^0.20.1",
|
|
44
44
|
"@types/bun": "^1.3.0",
|
|
45
45
|
"@types/node": "^22.0.0",
|
|
46
46
|
"typescript": "^5.6.0"
|
package/scripts/statusline.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/statusline/statusline.ts
|
|
4
|
-
import { existsSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { existsSync, readdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { basename, dirname, join, resolve } from "node:path";
|
|
6
6
|
import { execSync, spawn } from "node:child_process";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
|
+
import { createHash } from "node:crypto";
|
|
9
10
|
var stdinRaw = "";
|
|
10
11
|
try {
|
|
11
12
|
stdinRaw = readFileSync(0, "utf-8");
|
|
@@ -29,7 +30,16 @@ function findProjectRoot(start) {
|
|
|
29
30
|
}
|
|
30
31
|
var PROJECT_ROOT = findProjectRoot(getVal("cwd") || process.cwd());
|
|
31
32
|
var HOME = homedir();
|
|
33
|
+
var CLAUDE_CONFIG_DIR = process.env.CLAUDE_CONFIG_DIR || join(HOME, ".claude");
|
|
32
34
|
var PLUGIN_ROOT = process.env.CLAUDE_PLUGIN_ROOT || "";
|
|
35
|
+
var KEYCHAIN_SERVICE = (() => {
|
|
36
|
+
const envDir = process.env.CLAUDE_CONFIG_DIR;
|
|
37
|
+
if (!envDir)
|
|
38
|
+
return "Claude Code-credentials";
|
|
39
|
+
const normalized = envDir.normalize("NFC");
|
|
40
|
+
const suffix = createHash("sha256").update(normalized).digest("hex").slice(0, 8);
|
|
41
|
+
return `Claude Code-credentials-${suffix}`;
|
|
42
|
+
})();
|
|
33
43
|
function getPluginVersion() {
|
|
34
44
|
if (PLUGIN_ROOT) {
|
|
35
45
|
try {
|
|
@@ -72,7 +82,7 @@ function makeBar(pct, width) {
|
|
|
72
82
|
function meter(label, pct, width) {
|
|
73
83
|
return `${DIM}${label}${RESET} ${pctColor(pct)}${makeBar(pct, width)} ${Math.round(pct)}%${RESET}`;
|
|
74
84
|
}
|
|
75
|
-
var VERSION_CACHE_PATH = join(
|
|
85
|
+
var VERSION_CACHE_PATH = join(CLAUDE_CONFIG_DIR, ".nexus_version_cache");
|
|
76
86
|
var VERSION_CACHE_TTL = 86400;
|
|
77
87
|
function updateAvailable(current) {
|
|
78
88
|
if (!current)
|
|
@@ -142,7 +152,7 @@ function buildLine1() {
|
|
|
142
152
|
const nexusTag = `\x1B[38;5;141m◆Nexus${versionStr}${RESET}${updateTag}`;
|
|
143
153
|
return `${nexusTag} ${SEP} ${modelColor}${BOLD}${model}${RESET} ${SEP} \x1B[36m${project}${RESET} ${SEP} ${gitPart}`;
|
|
144
154
|
}
|
|
145
|
-
var USAGE_CACHE_PATH = join(
|
|
155
|
+
var USAGE_CACHE_PATH = join(CLAUDE_CONFIG_DIR, ".usage_cache");
|
|
146
156
|
var CACHE_TTL_DEFAULT = 60;
|
|
147
157
|
var FETCH_BACKOFF = 300;
|
|
148
158
|
var STALE_THRESHOLD = 300;
|
|
@@ -166,9 +176,9 @@ ${cachedData}`);
|
|
|
166
176
|
try {
|
|
167
177
|
let tokenCmd = "";
|
|
168
178
|
if (process.platform === "darwin") {
|
|
169
|
-
tokenCmd = `TOKEN=$(security find-generic-password -s "
|
|
179
|
+
tokenCmd = `TOKEN=$(security find-generic-password -s "${KEYCHAIN_SERVICE}" -w 2>/dev/null | grep -o '"accessToken":"[^"]*"' | sed 's/"accessToken":"//;s/"//')`;
|
|
170
180
|
} else {
|
|
171
|
-
const credFile = join(
|
|
181
|
+
const credFile = join(CLAUDE_CONFIG_DIR, ".credentials.json");
|
|
172
182
|
tokenCmd = `TOKEN=$(grep -o '"accessToken":"[^"]*"' "${credFile}" 2>/dev/null | sed 's/"accessToken":"//;s/"//')`;
|
|
173
183
|
}
|
|
174
184
|
const script = `
|
|
@@ -218,12 +228,12 @@ function readUsage() {
|
|
|
218
228
|
try {
|
|
219
229
|
let credJson = "";
|
|
220
230
|
if (process.platform === "darwin") {
|
|
221
|
-
credJson = execSync(
|
|
231
|
+
credJson = execSync(`security find-generic-password -s "${KEYCHAIN_SERVICE}" -w`, {
|
|
222
232
|
encoding: "utf-8",
|
|
223
233
|
stdio: ["pipe", "pipe", "pipe"]
|
|
224
234
|
}).trim();
|
|
225
235
|
} else {
|
|
226
|
-
const credFile = join(
|
|
236
|
+
const credFile = join(CLAUDE_CONFIG_DIR, ".credentials.json");
|
|
227
237
|
if (existsSync(credFile))
|
|
228
238
|
credJson = readFileSync(credFile, "utf-8");
|
|
229
239
|
}
|
|
@@ -274,27 +284,177 @@ function resetRemain(parsed, section) {
|
|
|
274
284
|
function isApiMode() {
|
|
275
285
|
return !!process.env.ANTHROPIC_API_KEY;
|
|
276
286
|
}
|
|
277
|
-
|
|
287
|
+
var COST_CACHE_PATH = join(CLAUDE_CONFIG_DIR, ".api_cost_cache");
|
|
288
|
+
var COST_CACHE_TTL = 60;
|
|
289
|
+
var COST_STALE_THRESHOLD = 300;
|
|
290
|
+
function priceFor(model) {
|
|
291
|
+
const m = model.toLowerCase();
|
|
292
|
+
const TABLE = [
|
|
293
|
+
[/opus-4-[5-9]/, 5, 25],
|
|
294
|
+
[/opus-(?:4-[01]|4)(?:[-_]|$)/, 15, 75],
|
|
295
|
+
[/opus-3/, 15, 75],
|
|
296
|
+
[/sonnet-4(?:-\d+)?(?:[-_]|$)|4-sonnet/, 3, 15],
|
|
297
|
+
[/sonnet-3-7|3-7-sonnet/, 3, 15],
|
|
298
|
+
[/sonnet-3-5|3-5-sonnet/, 3, 15],
|
|
299
|
+
[/haiku-4-5/, 1, 5],
|
|
300
|
+
[/haiku-3-5|3-5-haiku/, 0.8, 4],
|
|
301
|
+
[/haiku-3/, 0.25, 1.25]
|
|
302
|
+
];
|
|
303
|
+
for (const [re, inp, out] of TABLE) {
|
|
304
|
+
if (re.test(m)) {
|
|
305
|
+
const inputPerToken = inp / 1e6;
|
|
306
|
+
return {
|
|
307
|
+
input: inputPerToken,
|
|
308
|
+
output: out / 1e6,
|
|
309
|
+
cache5m: inputPerToken * 1.25,
|
|
310
|
+
cache1h: inputPerToken * 2,
|
|
311
|
+
cacheRead: inputPerToken * 0.1
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
function turnCostUsd(model, usage) {
|
|
318
|
+
const rates = priceFor(model);
|
|
319
|
+
if (!rates)
|
|
320
|
+
return 0;
|
|
321
|
+
const inp = usage.input_tokens ?? 0;
|
|
322
|
+
const out = usage.output_tokens ?? 0;
|
|
323
|
+
const cacheRead = usage.cache_read_input_tokens ?? 0;
|
|
324
|
+
const c5m = usage.cache_creation?.ephemeral_5m_input_tokens;
|
|
325
|
+
const c1h = usage.cache_creation?.ephemeral_1h_input_tokens;
|
|
326
|
+
let cacheWriteCost = 0;
|
|
327
|
+
if (c5m !== undefined || c1h !== undefined) {
|
|
328
|
+
cacheWriteCost = (c5m ?? 0) * rates.cache5m + (c1h ?? 0) * rates.cache1h;
|
|
329
|
+
} else {
|
|
330
|
+
cacheWriteCost = (usage.cache_creation_input_tokens ?? 0) * rates.cache5m;
|
|
331
|
+
}
|
|
332
|
+
return inp * rates.input + out * rates.output + cacheRead * rates.cacheRead + cacheWriteCost;
|
|
333
|
+
}
|
|
334
|
+
function scanLocalCostUsd() {
|
|
335
|
+
const projectsRoot = join(CLAUDE_CONFIG_DIR, "projects");
|
|
336
|
+
if (!existsSync(projectsRoot))
|
|
337
|
+
return null;
|
|
338
|
+
const todayStart = new Date;
|
|
339
|
+
todayStart.setUTCHours(0, 0, 0, 0);
|
|
340
|
+
const todayStartMs = todayStart.getTime();
|
|
341
|
+
let total = 0;
|
|
342
|
+
let projectDirs;
|
|
278
343
|
try {
|
|
279
|
-
|
|
280
|
-
const resp = execSync(`curl -s --max-time 3 "https://api.anthropic.com/v1/organizations/cost_report?start_date=${today}&end_date=${today}" -H "x-api-key: ${adminKey}" -H "anthropic-version: 2023-06-01"`, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
281
|
-
const m = resp.match(/"total_cost"\s*:\s*([0-9.]+)/);
|
|
282
|
-
return m ? parseFloat(m[1]) : null;
|
|
344
|
+
projectDirs = readdirSync(projectsRoot);
|
|
283
345
|
} catch {
|
|
284
346
|
return null;
|
|
285
347
|
}
|
|
348
|
+
for (const proj of projectDirs) {
|
|
349
|
+
const projPath = join(projectsRoot, proj);
|
|
350
|
+
let entries;
|
|
351
|
+
try {
|
|
352
|
+
entries = readdirSync(projPath);
|
|
353
|
+
} catch {
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
for (const file of entries) {
|
|
357
|
+
if (!file.endsWith(".jsonl"))
|
|
358
|
+
continue;
|
|
359
|
+
const fp = join(projPath, file);
|
|
360
|
+
try {
|
|
361
|
+
const st = statSync(fp);
|
|
362
|
+
if (st.mtimeMs < todayStartMs)
|
|
363
|
+
continue;
|
|
364
|
+
} catch {
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
let raw;
|
|
368
|
+
try {
|
|
369
|
+
raw = readFileSync(fp, "utf-8");
|
|
370
|
+
} catch {
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
const lines = raw.split(`
|
|
374
|
+
`);
|
|
375
|
+
for (const line of lines) {
|
|
376
|
+
if (!line || !line.includes('"assistant"'))
|
|
377
|
+
continue;
|
|
378
|
+
let entry;
|
|
379
|
+
try {
|
|
380
|
+
entry = JSON.parse(line);
|
|
381
|
+
} catch {
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
if (entry.type !== "assistant")
|
|
385
|
+
continue;
|
|
386
|
+
const ts = entry.timestamp ? Date.parse(entry.timestamp) : NaN;
|
|
387
|
+
if (!Number.isFinite(ts) || ts < todayStartMs)
|
|
388
|
+
continue;
|
|
389
|
+
const model = entry.message?.model;
|
|
390
|
+
const usage = entry.message?.usage;
|
|
391
|
+
if (!model || !usage)
|
|
392
|
+
continue;
|
|
393
|
+
total += turnCostUsd(model, usage);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return total;
|
|
398
|
+
}
|
|
399
|
+
function writeCostCacheAtomic(content) {
|
|
400
|
+
try {
|
|
401
|
+
writeFileSync(COST_CACHE_PATH + ".tmp", content);
|
|
402
|
+
renameSync(COST_CACHE_PATH + ".tmp", COST_CACHE_PATH);
|
|
403
|
+
} catch {
|
|
404
|
+
try {
|
|
405
|
+
unlinkSync(COST_CACHE_PATH + ".tmp");
|
|
406
|
+
} catch {}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
function readApiCost() {
|
|
410
|
+
const now = Math.floor(Date.now() / 1000);
|
|
411
|
+
let dataTimestamp = 0;
|
|
412
|
+
let nextRescanAfter = 0;
|
|
413
|
+
let cachedValue = "";
|
|
414
|
+
if (existsSync(COST_CACHE_PATH)) {
|
|
415
|
+
try {
|
|
416
|
+
const lines = readFileSync(COST_CACHE_PATH, "utf-8").split(`
|
|
417
|
+
`);
|
|
418
|
+
dataTimestamp = parseInt(lines[0]) || 0;
|
|
419
|
+
nextRescanAfter = parseInt(lines[1]) || 0;
|
|
420
|
+
cachedValue = (lines[2] || "").trim();
|
|
421
|
+
} catch {}
|
|
422
|
+
}
|
|
423
|
+
const age = dataTimestamp > 0 ? now - dataTimestamp : 0;
|
|
424
|
+
const parseCached = () => {
|
|
425
|
+
if (!cachedValue)
|
|
426
|
+
return null;
|
|
427
|
+
const n = parseFloat(cachedValue);
|
|
428
|
+
return Number.isFinite(n) ? n : null;
|
|
429
|
+
};
|
|
430
|
+
if (cachedValue && now < nextRescanAfter) {
|
|
431
|
+
return { cost: parseCached(), stale: age >= COST_STALE_THRESHOLD, ageSeconds: age };
|
|
432
|
+
}
|
|
433
|
+
const cost = scanLocalCostUsd();
|
|
434
|
+
if (cost === null) {
|
|
435
|
+
return { cost: null, stale: false, ageSeconds: 0 };
|
|
436
|
+
}
|
|
437
|
+
writeCostCacheAtomic(`${now}
|
|
438
|
+
${now + COST_CACHE_TTL}
|
|
439
|
+
${cost}`);
|
|
440
|
+
return { cost, stale: false, ageSeconds: 0 };
|
|
286
441
|
}
|
|
287
442
|
function buildLine2() {
|
|
288
443
|
const BAR_WIDTH = 6;
|
|
289
444
|
const ctxPct = Math.round(getNum("used_percentage"));
|
|
290
445
|
const ctx = meter("ctx", ctxPct, BAR_WIDTH);
|
|
291
446
|
if (isApiMode()) {
|
|
292
|
-
const
|
|
293
|
-
if (
|
|
294
|
-
|
|
295
|
-
if (
|
|
296
|
-
|
|
447
|
+
const { cost, stale, ageSeconds } = readApiCost();
|
|
448
|
+
if (cost !== null) {
|
|
449
|
+
let stalePart2 = "";
|
|
450
|
+
if (stale) {
|
|
451
|
+
const ageMin = Math.floor(ageSeconds / 60);
|
|
452
|
+
const hh = Math.floor(ageMin / 60);
|
|
453
|
+
const mm = ageMin % 60;
|
|
454
|
+
const ageStr = hh > 0 ? `${hh}h${mm}m` : `${mm}m`;
|
|
455
|
+
stalePart2 = ` ${SEP} \x1B[33m${ageStr} ago\x1B[0m`;
|
|
297
456
|
}
|
|
457
|
+
return `${ctx} ${SEP} ${DIM}API${RESET} ${pctColor(0)}$${cost.toFixed(2)} today${RESET}${stalePart2}`;
|
|
298
458
|
}
|
|
299
459
|
return `${ctx} ${SEP} ${DIM}API mode${RESET}`;
|
|
300
460
|
}
|
|
@@ -7,17 +7,25 @@ triggers:
|
|
|
7
7
|
---
|
|
8
8
|
## Role
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
For each issue, collaborate with HOW subagents (architect/designer/postdoc/strategist), researcher, and explore to gather multi-angle analysis, then synthesize the results so Lead records the decision directly without waiting for a user response.
|
|
11
|
+
|
|
12
|
+
The flow is as follows:
|
|
13
|
+
|
|
14
|
+
1. For each issue, dynamically spawn the HOW subagent(s) matching its domain to receive independent analysis.
|
|
15
|
+
2. Use explore when codebase orientation is needed and researcher when external investigation is needed.
|
|
16
|
+
3. Lead synthesizes the gathered analysis, compares candidate options, and selects the most reasonable one.
|
|
17
|
+
4. Decisions are recorded by Lead directly via `nx_plan_decide` without user confirmation; once all issues are decided, brief the user in a single pass.
|
|
11
18
|
|
|
12
19
|
This skill does not execute. Execution is handled separately by the `[run]` flow. It is also the path `[run]` invokes internally when tasks.json is absent.
|
|
13
20
|
|
|
14
21
|
## Core Rules — Absolute Rules
|
|
15
22
|
|
|
16
|
-
The
|
|
23
|
+
The four rules below are the identity of this skill. **Violating even one departs from auto-plan's intended form.**
|
|
17
24
|
|
|
18
|
-
1. **
|
|
19
|
-
2. **
|
|
20
|
-
3. **NEVER
|
|
25
|
+
1. **Collaborate with HOW/researcher/explore to analyze each issue.** Spawning the HOW subagent matching the issue's domain is the default; bring in explore for code understanding and researcher for external investigation. Do NOT settle issues by Lead's solo reasoning — to skip collaboration, state the reason (e.g., a trivial issue Lead can decide alone, or identical analysis already present in `.nexus/memory`/`context`/`history`) explicitly in the analysis text.
|
|
26
|
+
2. **Lead decides autonomously.** NEVER ask the user for option choices, delegate decision authority, or request acceptance. All decisions are recorded directly by Lead via `nx_plan_decide` after internal deliberation grounded in the collaboration results.
|
|
27
|
+
3. **NEVER produce output that asks the user to decide.** Do not emit comparison tables, A/B/C option enumerations, or questions like "which option would you prefer?" to the user. However, **the comparison work and per-issue analysis records themselves are normal activity** — candidate comparison happens in Lead's internal deliberation, and its core findings and dismissal rationale are written into the decision text in prose form. They are not externalized, but that does not mean they must not be produced.
|
|
28
|
+
4. **NEVER stop for user confirmation.** Proceed from issue analysis → `nx_plan_decide` → next issue without seeking confirmation or sending intermediate approval requests immediately after individual decisions. The user-facing report happens only once at the Step 7 briefing after all issues are decided. **Waiting for HOW subagent results is not stopping** — when the issue's depth requires it, spawn HOW and wait for the results before deciding. What must not stop is "user confirmation," not "analytical depth."
|
|
21
29
|
|
|
22
30
|
## Supplementary Rules
|
|
23
31
|
|
package/skills/nx-plan/SKILL.md
CHANGED
|
@@ -29,6 +29,7 @@ If the user requests full delegation such as "you decide" or "whatever you think
|
|
|
29
29
|
- NEVER execute — this skill's purpose is planning and decision alignment.
|
|
30
30
|
- MUST handle one issue at a time. NEVER present multiple issues simultaneously.
|
|
31
31
|
- NEVER ask groundless questions. MUST investigate code, existing knowledge, and prior decisions first.
|
|
32
|
+
- **MUST gather multi-angle evidence before presenting a recommendation.** Bring in the HOW subagent matching the issue's domain, explore for code understanding, and researcher for external investigation to collect independent analysis before forming the recommendation. NEVER form a recommendation from Lead's solo reasoning.
|
|
32
33
|
- MUST present a comparison table when requesting a decision. NEVER describe options in prose alone.
|
|
33
34
|
- Lead is synthesizer and participant — form independent recommendations and push back when warranted, not merely relay subagent results. **But never take over final decision authority.**
|
|
34
35
|
|