xtrm-tools 0.7.10 → 0.7.11
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/.xtrm/skills/default/update-xt/SKILL.md +185 -0
- package/.xtrm/skills/default/using-nodes/SKILL.md +127 -43
- package/.xtrm/skills/default/using-specialists/SKILL.md +144 -54
- package/cli/dist/index.cjs +47 -43
- package/cli/dist/index.cjs.map +1 -1
- package/cli/package.json +1 -1
- package/package.json +1 -1
- package/packages/pi-extensions/package.json +1 -1
|
@@ -9,8 +9,8 @@ description: >
|
|
|
9
9
|
workflow, --context-depth, background jobs, MCP tool (`use_specialist`),
|
|
10
10
|
or specialists doctor. Don't wait for the user to say
|
|
11
11
|
"use a specialist" — proactively evaluate whether delegation makes sense.
|
|
12
|
-
version: 4.
|
|
13
|
-
synced_at:
|
|
12
|
+
version: 4.8
|
|
13
|
+
synced_at: a58a4dda
|
|
14
14
|
---
|
|
15
15
|
|
|
16
16
|
# Specialists Usage
|
|
@@ -29,13 +29,34 @@ Specialists are autonomous AI agents that run independently — fresh context, d
|
|
|
29
29
|
|
|
30
30
|
---
|
|
31
31
|
|
|
32
|
+
## Response Style Policy
|
|
33
|
+
|
|
34
|
+
- Be direct, concise, and professional.
|
|
35
|
+
- Answer the user's actual question first, in the first sentence when possible.
|
|
36
|
+
- Do not append conversational filler like:
|
|
37
|
+
- "If you want, I can..."
|
|
38
|
+
- "I can also..."
|
|
39
|
+
- "Let me know if you want..."
|
|
40
|
+
unless the user explicitly asked for options.
|
|
41
|
+
- Do not restate context the user already provided unless needed to resolve ambiguity.
|
|
42
|
+
- Prefer short conclusions over long explanatory structures.
|
|
43
|
+
- Use bullets only when they improve clarity; otherwise respond in plain prose.
|
|
44
|
+
- Do not hedge unnecessarily. If the answer is clear, state it plainly.
|
|
45
|
+
- Do not give a recommendation section unless the user asked for recommendations or a decision.
|
|
46
|
+
- Do not propose next steps automatically after every answer.
|
|
47
|
+
- When reporting status, give:
|
|
48
|
+
1. current state
|
|
49
|
+
2. blocker or result
|
|
50
|
+
3. only the next action if action is already implied or necessary
|
|
51
|
+
- Default to terse operational language, not coaching language.
|
|
52
|
+
|
|
32
53
|
## Hard Rules
|
|
33
54
|
|
|
34
55
|
1. **Zero implementation by orchestrator.** When this skill is active for substantial work, you do not implement the solution yourself.
|
|
35
56
|
2. **Never explore yourself.** All discovery, codebase mapping, and read-only investigation go through **explorer** (or **debugger** for root-cause analysis).
|
|
36
57
|
3. **Run explorer before executor when context is lacking.** If the bead already has clear scope — files, symbols, approach — send executor directly. Only run explorer first when the issue lacks a clear track.
|
|
37
58
|
4. **For tracked work, the bead is the prompt.** The bead description, notes, and parent context are the instruction surface.
|
|
38
|
-
5. **`--bead`
|
|
59
|
+
5. **`--bead` is the only prompt.** Never use `--prompt`. If you need to refine instructions, update the bead notes first.
|
|
39
60
|
6. **Chains belong to epics.** A chain is a worktree lineage (executor → reviewer → fix). An epic is the merge-gated identity that owns chains. Use `sp epic merge <epic>` to publish — never merge individual chains that belong to an unresolved epic.
|
|
40
61
|
7. **Merge through epics, not manual git.** Use `sp epic merge <epic-id>` for wave-bound chains or `sp merge <chain-root-bead>` for standalone chains. Never use manual `git merge` for specialist work.
|
|
41
62
|
8. **No destructive operations by specialists.** No `rm -rf`, no force pushes, no database drops, no credential rotation, no mass deletes, no history rewrites. Surface destructive requirements to the user.
|
|
@@ -72,7 +93,7 @@ specialists run <name> --bead <id> --background # background run
|
|
|
72
93
|
specialists run <name> --bead <id> --worktree # isolated worktree (edit-capable specialists)
|
|
73
94
|
specialists run <name> --bead <id> --job <job-id> # reuse another job's worktree
|
|
74
95
|
specialists run <name> --bead <id> --epic <epic-id> # explicitly declare epic membership
|
|
75
|
-
specialists run <name> --
|
|
96
|
+
specialists run <name> --bead <id> --force-stale-base # bypass stale-base guard
|
|
76
97
|
specialists run <name> --bead <id> --keep-alive # keep session alive after first turn
|
|
77
98
|
specialists run <name> --bead <id> --context-depth 2 # inject parent bead context
|
|
78
99
|
|
|
@@ -87,7 +108,9 @@ specialists status --job <job-id> # single-job detail view (legacy
|
|
|
87
108
|
# Epic lifecycle (canonical publication path)
|
|
88
109
|
specialists epic list [--unresolved] # list epics with lifecycle state
|
|
89
110
|
specialists epic status <epic-id> # show chains, blockers, readiness
|
|
111
|
+
specialists epic sync <epic-id> [--apply] # reconcile DB vs live state (dry-run default)
|
|
90
112
|
specialists epic resolve <epic-id> # transition open -> resolving
|
|
113
|
+
specialists epic abandon <epic-id> --reason <text> [--force] # terminal transition for stuck epics
|
|
91
114
|
specialists epic merge <epic-id> [--pr] # publish all epic-owned chains
|
|
92
115
|
|
|
93
116
|
# Merge (for standalone chains only)
|
|
@@ -100,11 +123,15 @@ specialists end [--pr] # close session, publish via merge
|
|
|
100
123
|
specialists steer <job-id> "new direction" # redirect ANY running job mid-run
|
|
101
124
|
specialists resume <job-id> "next task" # resume a waiting keep-alive job
|
|
102
125
|
specialists stop <job-id> # cancel a job
|
|
126
|
+
specialists stop <job-id> --force # 5s SIGTERM timeout, then pgroup kill + error status
|
|
103
127
|
|
|
104
128
|
# Management
|
|
105
129
|
specialists edit <name> # edit specialist config (dot-path, --preset)
|
|
106
130
|
specialists clean # purge old job dirs + worktree GC
|
|
107
131
|
specialists clean --processes # kill all running/starting specialist jobs
|
|
132
|
+
specialists db vacuum # compact SQLite storage (refuses if jobs running)
|
|
133
|
+
specialists db prune --before <iso|duration> --dry-run|--apply # prune old events/results/terminal jobs
|
|
134
|
+
specialists doctor orphans # integrity scan: orphan, stale-pointer, integrity-violation
|
|
108
135
|
specialists init --sync-skills # re-sync skills only (no full init)
|
|
109
136
|
specialists init --no-xtrm-check # skip xtrm prerequisite check (CI/testing)
|
|
110
137
|
```
|
|
@@ -177,7 +204,7 @@ via `--context-depth 2`. The bead chain IS the context chain — zero manual wir
|
|
|
177
204
|
task-abc: "Fix auth token refresh"
|
|
178
205
|
└── abc-exp: explorer (READ_ONLY — auto-appends output to abc-exp notes)
|
|
179
206
|
└── abc-impl: executor (self-appends output to abc-impl notes, closes bead)
|
|
180
|
-
└── abc-rev: reviewer (
|
|
207
|
+
└── abc-rev: reviewer (auto-appends verdict to abc-rev notes via --job <exec-job>)
|
|
181
208
|
└── abc-fix: executor (if reviewer PARTIAL — fix bead, same worktree via --job)
|
|
182
209
|
```
|
|
183
210
|
|
|
@@ -187,7 +214,7 @@ task-abc: "Fix auth token refresh"
|
|
|
187
214
|
|------|----------------|-----|
|
|
188
215
|
| abc-exp | abc-exp (own) + task-abc (parent) | `--bead abc-exp --context-depth 2` |
|
|
189
216
|
| abc-impl | abc-impl (own) + abc-exp (explorer findings in notes) + task-abc | `--bead abc-impl --context-depth 2` |
|
|
190
|
-
|
|
|
217
|
+
| abc-rev | abc-rev (own) + abc-impl (executor output in notes) + task-abc | `--bead abc-rev --job <exec-job> --context-depth 2` |
|
|
191
218
|
| abc-fix | abc-fix (own) + abc-impl (executor output + reviewer verdict) + abc-exp | `--bead abc-fix --job <exec-job> --context-depth 2` |
|
|
192
219
|
|
|
193
220
|
- No copy-paste, no manual note injection between steps
|
|
@@ -228,11 +255,15 @@ specialists run executor --worktree --bead abc-impl --context-depth 2 --backgrou
|
|
|
228
255
|
# 6. [MERGE] Merge impl worktree branch into master
|
|
229
256
|
sp merge abc-impl --rebuild
|
|
230
257
|
|
|
231
|
-
# 7. Wave 3 — Reviewer (
|
|
232
|
-
|
|
258
|
+
# 7. Wave 3 — Reviewer (own bead, enters executor's worktree via --job)
|
|
259
|
+
bd create --title "Review: token refresh fix" --type task --priority 2
|
|
260
|
+
# -> unitAI-abc-rev
|
|
261
|
+
bd dep add abc-rev abc-impl
|
|
262
|
+
|
|
263
|
+
specialists run reviewer --bead abc-rev --job a1b2c3 --context-depth 2 --keep-alive --background
|
|
233
264
|
# -> Job started: r4v5w6
|
|
234
|
-
# Reviewer
|
|
235
|
-
# Reviewer auto-appends verdict to
|
|
265
|
+
# Reviewer sees: abc-rev + abc-impl (with executor output in notes) + abc via context-depth
|
|
266
|
+
# Reviewer auto-appends verdict to abc-rev notes
|
|
236
267
|
specialists result r4v5w6
|
|
237
268
|
# -> PASS: close task bead. PARTIAL/FAIL: go to step 8.
|
|
238
269
|
|
|
@@ -304,8 +335,8 @@ Reads `worktree_path` from the target job's `status.json` and uses that director
|
|
|
304
335
|
The caller's own `--bead` remains authoritative — `--job` only selects the workspace.
|
|
305
336
|
|
|
306
337
|
```bash
|
|
307
|
-
# Reviewer enters executor's worktree
|
|
308
|
-
specialists run reviewer --job 49adda --keep-alive --background
|
|
338
|
+
# Reviewer enters executor's worktree with its own bead
|
|
339
|
+
specialists run reviewer --bead unitAI-rev --job 49adda --context-depth 2 --keep-alive --background
|
|
309
340
|
|
|
310
341
|
# Fix executor re-enters same worktree (--bead provides new fix bead, --job provides workspace)
|
|
311
342
|
specialists run executor --bead hgpu.3-fix --job 49adda --context-depth 2 --background
|
|
@@ -336,7 +367,7 @@ Use when the caller explicitly accepts concurrent write risk (e.g., target job k
|
|
|
336
367
|
| Scenario | Flag to use |
|
|
337
368
|
|----------|------------|
|
|
338
369
|
| First executor run for a task | `--worktree --bead <impl-bead>` |
|
|
339
|
-
| Reviewer on executor's output | `--job <exec-job-id
|
|
370
|
+
| Reviewer on executor's output | `--bead <review-bead> --job <exec-job-id> --context-depth 2` |
|
|
340
371
|
| Fix executor after reviewer PARTIAL | `--bead <fix-bead> --job <exec-job-id>` |
|
|
341
372
|
| Force entry to blocked worktree | `--bead <fix-bead> --job <exec-job-id> --force-job` |
|
|
342
373
|
| Prep job belonging to epic (non-epic parent) | `--bead <prep-bead> --epic <epic-id>` |
|
|
@@ -367,35 +398,36 @@ Map bead dependencies to match the execution pipeline. The dep graph IS the wave
|
|
|
367
398
|
|
|
368
399
|
### Simple bug fix
|
|
369
400
|
```
|
|
370
|
-
task → explore → impl
|
|
371
|
-
|
|
372
|
-
└── fix (if PARTIAL) → child of impl
|
|
401
|
+
task → explore → impl → review
|
|
402
|
+
└── fix (if PARTIAL) → child of impl
|
|
373
403
|
```
|
|
374
404
|
```bash
|
|
375
405
|
bd dep add explore task
|
|
376
406
|
bd dep add impl explore
|
|
377
|
-
|
|
407
|
+
bd dep add review impl
|
|
408
|
+
# reviewer: specialists run reviewer --bead review --job <impl-job> --context-depth 2
|
|
378
409
|
# fix: bd dep add fix impl
|
|
379
410
|
```
|
|
380
411
|
|
|
381
412
|
### Complex feature (overthinker)
|
|
382
413
|
```
|
|
383
|
-
task → explore → design → impl →
|
|
414
|
+
task → explore → design → impl → review → [fix if PARTIAL]
|
|
384
415
|
```
|
|
385
416
|
```bash
|
|
386
417
|
bd dep add explore task
|
|
387
418
|
bd dep add design explore
|
|
388
419
|
bd dep add impl design
|
|
389
|
-
|
|
420
|
+
bd dep add review impl
|
|
421
|
+
# reviewer: specialists run reviewer --bead review --job <impl-job> --context-depth 2
|
|
390
422
|
```
|
|
391
423
|
|
|
392
424
|
### Epic with N children
|
|
393
425
|
Each child gets its own explore → impl chain. Reviewer runs via `--job` per impl.
|
|
394
426
|
```
|
|
395
427
|
epic
|
|
396
|
-
├── child-1 → explore-1 → impl-1 (reviewer
|
|
397
|
-
├── child-2 → explore-2 → impl-2 (reviewer
|
|
398
|
-
└── child-N → explore-N → impl-N (reviewer
|
|
428
|
+
├── child-1 → explore-1 → impl-1 → review-1 (reviewer --bead review-1 --job impl-1-job)
|
|
429
|
+
├── child-2 → explore-2 → impl-2 → review-2 (reviewer --bead review-2 --job impl-2-job)
|
|
430
|
+
└── child-N → explore-N → impl-N → review-N (reviewer --bead review-N --job impl-N-job)
|
|
399
431
|
```
|
|
400
432
|
Children (chains) within the same epic can run **in parallel** if they own disjoint files.
|
|
401
433
|
|
|
@@ -430,16 +462,15 @@ The review → fix loop is the mechanism for iterative quality improvement withi
|
|
|
430
462
|
1. Executor provisions --worktree, implements, enters waiting.
|
|
431
463
|
-> Job: exec-job (KEEP ALIVE — do not stop)
|
|
432
464
|
|
|
433
|
-
2. Reviewer enters same worktree via --job exec-job.
|
|
465
|
+
2. Reviewer enters same worktree via --bead <review-bead> --job exec-job --context-depth 2.
|
|
434
466
|
-> sp ps shows the chain:
|
|
435
467
|
feature/unitAI-impl-executor · unitAI-impl
|
|
436
468
|
◐ exec-job executor waiting
|
|
437
469
|
└ ◐ rev-job reviewer starting
|
|
438
|
-
-> Auto-appends verdict (PASS/PARTIAL/FAIL) to bead notes.
|
|
470
|
+
-> Auto-appends verdict (PASS/PARTIAL/FAIL) to review bead notes.
|
|
439
471
|
|
|
440
472
|
3a. PASS:
|
|
441
|
-
->
|
|
442
|
-
-> Verify commit landed on branch (git log)
|
|
473
|
+
-> Verify auto-commit landed on branch (git log)
|
|
443
474
|
-> Stop reviewer, then stop executor
|
|
444
475
|
-> Merge via sp merge
|
|
445
476
|
|
|
@@ -460,14 +491,17 @@ specialists run executor --worktree --bead unitAI-impl --context-depth 2 --backg
|
|
|
460
491
|
# -> Job started: exec-job (e.g. 49adda)
|
|
461
492
|
# DO NOT sp stop — executor stays alive for the entire review cycle
|
|
462
493
|
|
|
463
|
-
# Step 2 —
|
|
464
|
-
|
|
494
|
+
# Step 2 — Create reviewer bead and dispatch
|
|
495
|
+
bd create --title "Review: impl changes" --type task --priority 2
|
|
496
|
+
# -> unitAI-rev
|
|
497
|
+
bd dep add rev impl
|
|
498
|
+
specialists run reviewer --bead unitAI-rev --job 49adda --context-depth 2 --keep-alive --background
|
|
465
499
|
# -> Job started: rev-job
|
|
466
500
|
specialists result rev-job
|
|
467
501
|
|
|
468
|
-
# Step 3a — PASS:
|
|
469
|
-
|
|
470
|
-
#
|
|
502
|
+
# Step 3a — PASS: verify auto-commit landed, then stop both
|
|
503
|
+
# Executor auto-commits substantive changes on each turn completion
|
|
504
|
+
# Verify with: git log feature/unitAI-impl-executor --oneline -1
|
|
471
505
|
specialists stop rev-job
|
|
472
506
|
specialists stop 49adda
|
|
473
507
|
sp merge unitAI-impl --rebuild
|
|
@@ -475,8 +509,11 @@ sp merge unitAI-impl --rebuild
|
|
|
475
509
|
# Step 3b — PARTIAL: resume executor with fix instructions (same session, full context)
|
|
476
510
|
specialists resume 49adda "Reviewer PARTIAL. Fix: <paste specific findings here>"
|
|
477
511
|
# Executor applies fixes, enters waiting again
|
|
478
|
-
# Dispatch new reviewer:
|
|
479
|
-
|
|
512
|
+
# Dispatch new reviewer (new bead for each re-review):
|
|
513
|
+
bd create --title "Re-review: impl after fix" --type task --priority 2
|
|
514
|
+
# -> unitAI-rev2
|
|
515
|
+
bd dep add rev2 impl
|
|
516
|
+
specialists run reviewer --bead unitAI-rev2 --job 49adda --context-depth 2 --keep-alive --background
|
|
480
517
|
# Repeat until PASS
|
|
481
518
|
|
|
482
519
|
# After final PASS + commit + stop:
|
|
@@ -496,10 +533,10 @@ Only dispatch a new fix executor when the original specialist is dead (crashed,
|
|
|
496
533
|
|
|
497
534
|
### Key invariants
|
|
498
535
|
- **Never stop the executor/debugger before reviewer verdict.** The specialist stays in `waiting` throughout the review cycle. Stopping prematurely kills the resume path and risks uncommitted changes.
|
|
499
|
-
- **Executors
|
|
500
|
-
- Each fix iteration uses `resume` on the same
|
|
536
|
+
- **Executors auto-commit substantive changes** on each turn completion (via `auto_commit: checkpoint_on_waiting`). After reviewer PASS, verify the commit landed on the branch before stopping.
|
|
537
|
+
- Each fix iteration uses `resume` on the same executor — not a new child bead or new executor.
|
|
501
538
|
- Multiple reviewer → resume → re-review cycles are expected. The worktree and specialist session are stable across all cycles.
|
|
502
|
-
- Only stop after: (1) reviewer PASS, (2)
|
|
539
|
+
- Only stop after: (1) reviewer PASS, (2) auto-commit verified on branch.
|
|
503
540
|
|
|
504
541
|
---
|
|
505
542
|
|
|
@@ -534,8 +571,7 @@ sp stop exec-job # ✗ kills resume path, risks uncommitted work
|
|
|
534
571
|
sp stop overthinker-job # ✗ loses context if follow-up questions arise
|
|
535
572
|
|
|
536
573
|
# GOOD — chain completes naturally
|
|
537
|
-
|
|
538
|
-
# verify commit...
|
|
574
|
+
# verify auto-commit landed on branch...
|
|
539
575
|
sp merge unitAI-impl # publishes branch
|
|
540
576
|
# THEN stop members (future: auto-stopped by merge)
|
|
541
577
|
sp stop rev-job
|
|
@@ -671,7 +707,7 @@ The specialist reads:
|
|
|
671
707
|
|
|
672
708
|
This prevents specialists from rediscovering known gotchas on every run.
|
|
673
709
|
|
|
674
|
-
`--prompt
|
|
710
|
+
**Never use `--prompt`.** For tracked work, always use `--bead`. When you need to give a specialist
|
|
675
711
|
specific instructions beyond what's in the bead description, update the bead notes first:
|
|
676
712
|
|
|
677
713
|
```bash
|
|
@@ -714,9 +750,9 @@ Run `specialists list` to see what's available. Match by task type:
|
|
|
714
750
|
### Specialist selection notes
|
|
715
751
|
|
|
716
752
|
- **executor does not run tests** — it runs `lint + tsc` only. Tests belong to the reviewer or test-runner phase.
|
|
717
|
-
- **executor enters `waiting` after first turn** — `interactive: true` is now default. **Never stop the executor before reviewer verdict.** Keep it alive so you can
|
|
718
|
-
- **explorer** is READ_ONLY —
|
|
719
|
-
- **reviewer**
|
|
753
|
+
- **executor enters `waiting` after first turn** — `interactive: true` is now default. **Never stop the executor before reviewer verdict.** Keep it alive so you can resume with fix instructions if reviewer says PARTIAL. Executors auto-commit substantive changes on each turn via `auto_commit: checkpoint_on_waiting`. Only `sp stop` after reviewer PASS and commit verified on the branch.
|
|
754
|
+
- **explorer** is READ_ONLY — output auto-appends to the input bead's notes. No implementation.
|
|
755
|
+
- **reviewer** always gets its own bead: `--bead <review-bead> --job <exec-job> --context-depth 2`. The reviewer sees the executor's output via auto-appended bead notes + context-depth. Never use `--prompt`.
|
|
720
756
|
- **debugger** over **explorer** when you need root cause analysis — GitNexus call-chain tracing, ranked hypotheses, evidence-backed remediation.
|
|
721
757
|
- **overthinker** before **executor** for any non-trivial task — surfaces edge cases, challenges assumptions, produces solution direction. Cheap relative to wrong implementation.
|
|
722
758
|
- **researcher** is the docs specialist — never look up library docs yourself, delegate to researcher.
|
|
@@ -731,7 +767,7 @@ specialists run debugger --bead unitAI-bug --context-depth 2 --background
|
|
|
731
767
|
specialists run planner --bead unitAI-scope --context-depth 2 --background
|
|
732
768
|
specialists run overthinker --bead unitAI-design --context-depth 2 --keep-alive --background
|
|
733
769
|
specialists run executor --worktree --bead unitAI-impl --context-depth 2 --background
|
|
734
|
-
specialists run reviewer --job <exec-job-id> --keep-alive --background
|
|
770
|
+
specialists run reviewer --bead unitAI-rev --job <exec-job-id> --context-depth 2 --keep-alive --background
|
|
735
771
|
specialists run sync-docs --bead unitAI-docs --context-depth 2 --keep-alive --background
|
|
736
772
|
specialists run test-runner --bead unitAI-tests --context-depth 2 --background
|
|
737
773
|
specialists run specialists-creator --bead unitAI-skill --context-depth 2 --background
|
|
@@ -852,8 +888,8 @@ specialists steer a1b2c3 "Do NOT audit. Write the actual file to disk now."
|
|
|
852
888
|
> before killing a keep-alive job.**
|
|
853
889
|
|
|
854
890
|
> **Critical:** Never stop an executor or debugger before the reviewer delivers its verdict.
|
|
855
|
-
> Stopping prematurely: (1) kills the resume path for fix loops, (2)
|
|
856
|
-
>
|
|
891
|
+
> Stopping prematurely: (1) kills the resume path for fix loops, and (2) forces dispatching a
|
|
892
|
+
> new specialist instead of resuming. Executors auto-commit substantive changes on each turn.
|
|
857
893
|
|
|
858
894
|
```bash
|
|
859
895
|
# Check before stopping
|
|
@@ -917,7 +953,7 @@ bd create --title "Explore: map job run architecture" --type task --priority 2
|
|
|
917
953
|
bd dep add exp 3f7b
|
|
918
954
|
bd create --title "Implement: worktree isolation" --type task --priority 2 # -> unitAI-impl
|
|
919
955
|
bd dep add impl exp
|
|
920
|
-
# Note: reviewer
|
|
956
|
+
# Note: reviewer gets own bead, enters via --job, inherits epic from bead.parent
|
|
921
957
|
|
|
922
958
|
# Stage 1 — Explorer (prep job, declares epic explicitly)
|
|
923
959
|
specialists run explorer --bead unitAI-exp --epic unitAI-3f7b --context-depth 2 --background
|
|
@@ -932,8 +968,10 @@ specialists run executor --worktree --bead unitAI-impl --context-depth 2 --backg
|
|
|
932
968
|
# epic_id = bead.parent (unitAI-3f7b)
|
|
933
969
|
specialists result job2
|
|
934
970
|
|
|
935
|
-
# Stage 3 — Reviewer (uses --job
|
|
936
|
-
|
|
971
|
+
# Stage 3 — Reviewer (own bead, uses --job for same worktree)
|
|
972
|
+
bd create --title "Review: worktree isolation impl" --type task --priority 2 # -> unitAI-rev
|
|
973
|
+
bd dep add rev impl
|
|
974
|
+
specialists run reviewer --bead unitAI-rev --job job2 --context-depth 2 --keep-alive --background
|
|
937
975
|
# -> Job started: job3
|
|
938
976
|
specialists result job3
|
|
939
977
|
# PASS → ready for epic merge. PARTIAL → fix loop.
|
|
@@ -942,8 +980,10 @@ specialists result job3
|
|
|
942
980
|
bd create --title "Fix: reviewer gaps on impl" --type bug --priority 1 # -> unitAI-fix1
|
|
943
981
|
bd dep add fix1 impl
|
|
944
982
|
specialists run executor --bead fix1 --job job2 --context-depth 2 --background
|
|
945
|
-
# Re-review
|
|
946
|
-
|
|
983
|
+
# Re-review (new reviewer bead)
|
|
984
|
+
bd create --title "Re-review: impl after fix" --type task --priority 2 # -> unitAI-rev2
|
|
985
|
+
bd dep add rev2 impl
|
|
986
|
+
specialists run reviewer --bead unitAI-rev2 --job job2 --context-depth 2 --keep-alive --background
|
|
947
987
|
|
|
948
988
|
# [MERGE] Publish epic
|
|
949
989
|
sp epic status unitAI-3f7b # verify readiness: merge_ready, all chains PASS
|
|
@@ -1018,8 +1058,8 @@ MCP is intentionally minimal. Use CLI for orchestration, monitoring, steering, r
|
|
|
1018
1058
|
|
|
1019
1059
|
## Known Issues
|
|
1020
1060
|
|
|
1021
|
-
- **
|
|
1022
|
-
- **`--
|
|
1061
|
+
- **All specialist output auto-appends** to the input bead notes on every `run_complete` (via Supervisor). Status-aware headers: `[WAITING]` vs `[DONE]`. Output also available via `specialists result`.
|
|
1062
|
+
- **`--prompt` is deprecated for tracked work.** Always use `--bead`. Update bead notes for additional instructions: `bd update <id> --notes "INSTRUCTION: ..."`
|
|
1023
1063
|
- **Job in `waiting` now shows magenta status** with resume hint in `status`, WAIT banner in `feed`, and resume footer in `result`. Always check before stopping a keep-alive job.
|
|
1024
1064
|
- **Explorer (qwen) may produce empty output** — the model sometimes completes tool calls but fails to emit a final text summary. The bead notes will be empty. If this happens, either re-run with a different model or do the investigation yourself.
|
|
1025
1065
|
- **`specialists init` requires xtrm** — `.xtrm/` directory and `xt` CLI must exist. Use `--no-xtrm-check` to bypass in CI/testing.
|
|
@@ -1031,10 +1071,60 @@ MCP is intentionally minimal. Use CLI for orchestration, monitoring, steering, r
|
|
|
1031
1071
|
|
|
1032
1072
|
```bash
|
|
1033
1073
|
specialists doctor # health check: hooks, MCP, zombie jobs, skill drift detection
|
|
1074
|
+
specialists doctor orphans # integrity scan for orphan/stale-pointer/integrity-violation
|
|
1034
1075
|
specialists edit <name> # edit specialist config (dot-path, --preset)
|
|
1035
1076
|
specialists clean --processes # kill stale/zombie specialist processes
|
|
1036
1077
|
```
|
|
1037
1078
|
|
|
1079
|
+
## Stuck-State Recovery
|
|
1080
|
+
|
|
1081
|
+
Use this flow when epic/job state disagrees with live runtime or close path loops.
|
|
1082
|
+
|
|
1083
|
+
### 1) Reconcile epic state first (safe default)
|
|
1084
|
+
|
|
1085
|
+
```bash
|
|
1086
|
+
sp epic status <epic-id>
|
|
1087
|
+
sp epic sync <epic-id> # dry-run default, inspect planned fixes
|
|
1088
|
+
sp epic sync <epic-id> --apply # apply reconciliation after review
|
|
1089
|
+
```
|
|
1090
|
+
|
|
1091
|
+
`sp epic sync` is primary recovery command. It reconciles DB state against live job/worktree state.
|
|
1092
|
+
|
|
1093
|
+
### 2) Terminally abandon unrecoverable epic
|
|
1094
|
+
|
|
1095
|
+
```bash
|
|
1096
|
+
sp epic abandon <epic-id> --reason "stuck chain with unrecoverable state"
|
|
1097
|
+
# If guard blocks due to active pointers you intentionally want cleared:
|
|
1098
|
+
sp epic abandon <epic-id> --reason "manual recovery" --force
|
|
1099
|
+
```
|
|
1100
|
+
|
|
1101
|
+
Use only when epic cannot be restored to valid resolving/merge path.
|
|
1102
|
+
|
|
1103
|
+
### 3) Restore DB hygiene after recovery
|
|
1104
|
+
|
|
1105
|
+
```bash
|
|
1106
|
+
sp doctor orphans
|
|
1107
|
+
sp db vacuum
|
|
1108
|
+
sp db prune --before 30d --dry-run
|
|
1109
|
+
sp db prune --before 30d --apply
|
|
1110
|
+
```
|
|
1111
|
+
|
|
1112
|
+
- `sp db vacuum` compacts SQLite file, refuses while jobs running.
|
|
1113
|
+
- `sp db prune` removes old rows from events/results/terminal jobs; dry-run first.
|
|
1114
|
+
|
|
1115
|
+
### 4) Hard-stop wedged jobs when normal stop fails
|
|
1116
|
+
|
|
1117
|
+
```bash
|
|
1118
|
+
sp stop <job-id>
|
|
1119
|
+
sp stop <job-id> --force
|
|
1120
|
+
```
|
|
1121
|
+
|
|
1122
|
+
`--force` waits 5s for SIGTERM, then kills process group and records explicit error status.
|
|
1123
|
+
|
|
1124
|
+
### 5) `sp end` open-state loop fix
|
|
1125
|
+
|
|
1126
|
+
If `sp end` detects open-state mismatch, tool now suggests `sp epic resolve <epic-id>` as next command (no redirect loop).
|
|
1127
|
+
|
|
1038
1128
|
- **RPC timeout on worktree job start** (30s, `command id=1`) → pi runs `npm install` in fresh
|
|
1039
1129
|
worktrees if `.pi/settings.json` lists local packages. Root cause: worktree gets a stale copy
|
|
1040
1130
|
of `.pi/settings.json` from the branch point. Fix: ensure `.pi/settings.json` has
|
|
@@ -1047,10 +1137,10 @@ specialists clean --processes # kill stale/zombie specialist processes
|
|
|
1047
1137
|
- **Job hangs** → `specialists steer <id> "finish up"` or `specialists stop <id>`
|
|
1048
1138
|
- **Config skipped** → stderr shows `[specialists] skipping <file>: <reason>`
|
|
1049
1139
|
- **Stall timeout** → specialist hit 120s inactivity. Check `specialists feed <id>`, then retry or switch.
|
|
1050
|
-
-
|
|
1140
|
+
- **Never use `--prompt`** → use bead notes: `bd update <id> --notes "INSTRUCTION: ..."` then `--bead` only.
|
|
1051
1141
|
- **Worktree already exists** → it will be reused (not recreated). Safe to re-run.
|
|
1052
1142
|
- **`--job` fails: worktree_path missing** → target job was not started with `--worktree`. Use `--worktree` on the next run.
|
|
1053
|
-
- **`--job` without `--
|
|
1143
|
+
- **`--job` without `--bead`** → reviewer/executor requires `--bead`. Create a reviewer bead first, then use `--bead <review-bead> --job <exec-job> --context-depth 2`.
|
|
1054
1144
|
- **Stale specialist processes** → SessionStart hook warns about old binary versions. Run `specialists clean --processes` to kill them all.
|
|
1055
1145
|
- **`specialists init` fails with xtrm error** → xtrm must be installed first: `npm install -g xtrm-tools && xt install`. Use `--no-xtrm-check` in CI.
|
|
1056
1146
|
- **Skill drift detected by doctor** → Run `specialists init --sync-skills` to re-sync canonical skills to `.xtrm/skills/default/` and refresh active symlinks.
|
package/cli/dist/index.cjs
CHANGED
|
@@ -29866,8 +29866,8 @@ function resolveOptionalTierRoot(skillsRoot) {
|
|
|
29866
29866
|
function resolveUserPacksRoot(skillsRoot) {
|
|
29867
29867
|
return import_node_path.default.join(skillsRoot, "user", "packs");
|
|
29868
29868
|
}
|
|
29869
|
-
function resolveActiveRuntimeRoot(skillsRoot,
|
|
29870
|
-
return import_node_path.default.join(skillsRoot, "active"
|
|
29869
|
+
function resolveActiveRuntimeRoot(skillsRoot, _runtime) {
|
|
29870
|
+
return import_node_path.default.join(skillsRoot, "active");
|
|
29871
29871
|
}
|
|
29872
29872
|
function resolveStateFilePath(skillsRoot) {
|
|
29873
29873
|
return import_node_path.default.join(skillsRoot, STATE_FILE_NAME);
|
|
@@ -43891,13 +43891,6 @@ async function setRuntimeEnabledPacks(skillsRoot, runtime, packNames) {
|
|
|
43891
43891
|
}
|
|
43892
43892
|
|
|
43893
43893
|
// src/core/skills-materializer.ts
|
|
43894
|
-
var PI_NPM_PROVIDED_SKILLS = /* @__PURE__ */ new Set([
|
|
43895
|
-
"gitnexus-debugging",
|
|
43896
|
-
"gitnexus-exploring",
|
|
43897
|
-
"gitnexus-impact-analysis",
|
|
43898
|
-
"gitnexus-pr-review",
|
|
43899
|
-
"gitnexus-refactoring"
|
|
43900
|
-
]);
|
|
43901
43894
|
function sortByName(entries) {
|
|
43902
43895
|
return [...entries].sort((a, b) => a.name.localeCompare(b.name));
|
|
43903
43896
|
}
|
|
@@ -43936,12 +43929,11 @@ async function selectRuntimeSkills(runtime, skillsRoot) {
|
|
|
43936
43929
|
const defaultSkills = await discoverDefaultSkills(skillsRoot);
|
|
43937
43930
|
const enabledPackSkills = await collectEnabledPackSkills(skillsRoot, enabledPacks);
|
|
43938
43931
|
const allSkills = sortByName([...defaultSkills, ...enabledPackSkills]);
|
|
43939
|
-
|
|
43940
|
-
assertNoRuntimeCollisions(runtime, filteredSkills);
|
|
43932
|
+
assertNoRuntimeCollisions(runtime, allSkills);
|
|
43941
43933
|
return {
|
|
43942
43934
|
runtime,
|
|
43943
43935
|
enabledPacks: [...enabledPacks],
|
|
43944
|
-
skills:
|
|
43936
|
+
skills: allSkills
|
|
43945
43937
|
};
|
|
43946
43938
|
}
|
|
43947
43939
|
async function buildRuntimeTempView(runtime, skillsRoot, selectedSkills) {
|
|
@@ -44017,11 +44009,31 @@ async function rebuildRuntimeActiveView(runtime, skillsRoot) {
|
|
|
44017
44009
|
};
|
|
44018
44010
|
}
|
|
44019
44011
|
async function rebuildAllRuntimeActiveViews(skillsRoot) {
|
|
44020
|
-
const
|
|
44021
|
-
|
|
44022
|
-
|
|
44012
|
+
const state = await readSkillsState(skillsRoot);
|
|
44013
|
+
const mergedEnabledPacks = [.../* @__PURE__ */ new Set([
|
|
44014
|
+
...state.enabledPacks.claude,
|
|
44015
|
+
...state.enabledPacks.pi
|
|
44016
|
+
])].sort((a, b) => a.localeCompare(b));
|
|
44017
|
+
const defaultSkills = await discoverDefaultSkills(skillsRoot);
|
|
44018
|
+
const enabledPackSkills = await collectEnabledPackSkills(skillsRoot, mergedEnabledPacks);
|
|
44019
|
+
const mergedSkills = sortByName([...defaultSkills, ...enabledPackSkills]);
|
|
44020
|
+
assertNoRuntimeCollisions("claude", mergedSkills);
|
|
44021
|
+
const activeRuntimeRoot = resolveActiveRuntimeRoot(skillsRoot, "claude");
|
|
44022
|
+
await import_fs_extra6.default.ensureDir(import_node_path4.default.dirname(activeRuntimeRoot));
|
|
44023
|
+
const nonSymlinkEntryNames = await findNonSymlinkEntries(activeRuntimeRoot);
|
|
44024
|
+
if (nonSymlinkEntryNames.length > 0) {
|
|
44025
|
+
console.log(
|
|
44026
|
+
`[xtrm] Warning: ${activeRuntimeRoot} contains non-symlink entries (${nonSymlinkEntryNames.join(", ")}). These entries will be evicted during runtime view rebuild. Do not write skills to .claude/skills directly; write to .xtrm/skills/default or packs.`
|
|
44027
|
+
);
|
|
44023
44028
|
}
|
|
44024
|
-
|
|
44029
|
+
const tempRoot = await buildRuntimeTempView("claude", skillsRoot, mergedSkills);
|
|
44030
|
+
await atomicSwapDirectory(tempRoot, activeRuntimeRoot);
|
|
44031
|
+
return [{
|
|
44032
|
+
runtime: "claude",
|
|
44033
|
+
enabledPackCount: mergedEnabledPacks.length,
|
|
44034
|
+
discoveredSkillCount: mergedSkills.length,
|
|
44035
|
+
symlinkNames: mergedSkills.map((skill) => skill.name)
|
|
44036
|
+
}];
|
|
44025
44037
|
}
|
|
44026
44038
|
|
|
44027
44039
|
// src/core/skills-scaffold.ts
|
|
@@ -44062,16 +44074,16 @@ async function ensureAgentsSkillsSymlink(projectRoot) {
|
|
|
44062
44074
|
throw new Error(`Skills invariants failed. ${summary}`);
|
|
44063
44075
|
}
|
|
44064
44076
|
const materializedViews = await rebuildAllRuntimeActiveViews(skillsRoot);
|
|
44065
|
-
const activatedClaudeSkills = materializedViews
|
|
44066
|
-
const activatedPiSkills =
|
|
44077
|
+
const activatedClaudeSkills = materializedViews[0]?.discoveredSkillCount ?? 0;
|
|
44078
|
+
const activatedPiSkills = activatedClaudeSkills;
|
|
44067
44079
|
await ensureSkillsSymlink(
|
|
44068
44080
|
import_path3.default.join(projectRoot, ".claude", "skills"),
|
|
44069
|
-
import_path3.default.join("..", ".xtrm", "skills", "active"
|
|
44081
|
+
import_path3.default.join("..", ".xtrm", "skills", "active"),
|
|
44070
44082
|
".claude/skills"
|
|
44071
44083
|
);
|
|
44072
44084
|
const agentsSkillsPath = import_path3.default.join(projectRoot, ".agents", "skills");
|
|
44073
44085
|
if (await import_fs_extra7.default.pathExists(agentsSkillsPath)) {
|
|
44074
|
-
console.log(kleur_default.dim(" \u25CB .agents/skills is deprecated; runtime skills are generated under .xtrm/skills/active
|
|
44086
|
+
console.log(kleur_default.dim(" \u25CB .agents/skills is deprecated; runtime skills are generated under .xtrm/skills/active"));
|
|
44075
44087
|
}
|
|
44076
44088
|
return {
|
|
44077
44089
|
activatedClaudeSkills,
|
|
@@ -44221,7 +44233,7 @@ async function launchWorktreeSession(opts) {
|
|
|
44221
44233
|
const message = error48 instanceof Error ? error48.message : String(error48);
|
|
44222
44234
|
console.log(kleur_default.dim(` warning: could not rebuild active Claude skills view (${message})`));
|
|
44223
44235
|
const wtSkillsDir = import_node_path5.default.join(claudeDir, "skills");
|
|
44224
|
-
const claudeSkillsTarget = import_node_path5.default.join("..", ".xtrm", "skills", "active"
|
|
44236
|
+
const claudeSkillsTarget = import_node_path5.default.join("..", ".xtrm", "skills", "active");
|
|
44225
44237
|
try {
|
|
44226
44238
|
const existing = (0, import_node_fs.lstatSync)(wtSkillsDir);
|
|
44227
44239
|
if (!existing.isSymbolicLink() || (0, import_node_fs.readlinkSync)(wtSkillsDir) !== claudeSkillsTarget) {
|
|
@@ -44807,7 +44819,7 @@ var PI_AGENT_DIR = process.env.PI_AGENT_DIR || import_path4.default.join((0, imp
|
|
|
44807
44819
|
var PI_MCP_ADAPTER_OVERRIDE_DIR = import_path4.default.join(PI_AGENT_DIR, "extensions", "pi-mcp-adapter");
|
|
44808
44820
|
var PI_MCP_ADAPTER_REQUIRED_ENTRY = "commands.js";
|
|
44809
44821
|
var PROJECT_EXTENSIONS_ENTRY = "../.xtrm/extensions";
|
|
44810
|
-
var PROJECT_SKILLS_ENTRY = "../.xtrm/skills/active
|
|
44822
|
+
var PROJECT_SKILLS_ENTRY = "../.xtrm/skills/active";
|
|
44811
44823
|
var PROJECT_EXTENSION_PACKAGE_ID = "npm:@jaggerxtrm/pi-extensions";
|
|
44812
44824
|
var CONFLICTING_PI_PACKAGE_IDS = /* @__PURE__ */ new Set(["npm:pi-dex"]);
|
|
44813
44825
|
var LEGACY_PROJECT_EXTENSION_ENTRIES = /* @__PURE__ */ new Set([
|
|
@@ -46555,8 +46567,8 @@ var import_path10 = __toESM(require("path"), 1);
|
|
|
46555
46567
|
// src/core/skills-runtime-views.ts
|
|
46556
46568
|
var import_fs_extra15 = __toESM(require_lib(), 1);
|
|
46557
46569
|
var import_node_path8 = __toESM(require("path"), 1);
|
|
46558
|
-
var CLAUDE_POINTER_TARGET = import_node_path8.default.join("..", ".xtrm", "skills", "active"
|
|
46559
|
-
var PI_SKILLS_ENTRY = "../.xtrm/skills/active
|
|
46570
|
+
var CLAUDE_POINTER_TARGET = import_node_path8.default.join("..", ".xtrm", "skills", "active");
|
|
46571
|
+
var PI_SKILLS_ENTRY = "../.xtrm/skills/active";
|
|
46560
46572
|
async function readSymlinkTarget(linkPath) {
|
|
46561
46573
|
const stat = await import_fs_extra15.default.lstat(linkPath).catch(() => null);
|
|
46562
46574
|
if (!stat?.isSymbolicLink()) {
|
|
@@ -46597,32 +46609,26 @@ async function hasPiSkillsPointer(projectRoot) {
|
|
|
46597
46609
|
return Array.isArray(settings.skills) && settings.skills.includes(PI_SKILLS_ENTRY);
|
|
46598
46610
|
}
|
|
46599
46611
|
async function checkRuntimeSkillsViews(projectRoot) {
|
|
46600
|
-
const
|
|
46601
|
-
const
|
|
46602
|
-
const
|
|
46603
|
-
const activePiEntries = await listRuntimeEntries(activePiRoot);
|
|
46604
|
-
const activeClaudeReady = activeClaudeEntries.length > 0 && await hasOnlyValidSymlinkEntries(activeClaudeRoot, activeClaudeEntries);
|
|
46605
|
-
const activePiReady = activePiEntries.length > 0 && await hasOnlyValidSymlinkEntries(activePiRoot, activePiEntries);
|
|
46612
|
+
const activeRoot = import_node_path8.default.join(projectRoot, ".xtrm", "skills", "active");
|
|
46613
|
+
const activeEntries = await listRuntimeEntries(activeRoot);
|
|
46614
|
+
const activeReady = activeEntries.length > 0 && await hasOnlyValidSymlinkEntries(activeRoot, activeEntries);
|
|
46606
46615
|
const claudePointerReady = await readSymlinkTarget(import_node_path8.default.join(projectRoot, ".claude", "skills")) === CLAUDE_POINTER_TARGET;
|
|
46607
46616
|
const piPointerReady = await hasPiSkillsPointer(projectRoot);
|
|
46608
46617
|
const hasDeprecatedAgentsSkillsPath = await import_fs_extra15.default.pathExists(import_node_path8.default.join(projectRoot, ".agents", "skills"));
|
|
46609
46618
|
return {
|
|
46610
|
-
|
|
46611
|
-
activePiReady,
|
|
46619
|
+
activeReady,
|
|
46612
46620
|
claudePointerReady,
|
|
46613
46621
|
piPointerReady,
|
|
46614
|
-
|
|
46615
|
-
activePiEntries,
|
|
46622
|
+
activeEntries,
|
|
46616
46623
|
hasDeprecatedAgentsSkillsPath
|
|
46617
46624
|
};
|
|
46618
46625
|
}
|
|
46619
46626
|
async function assertRuntimeSkillsViews(projectRoot) {
|
|
46620
46627
|
const check2 = await checkRuntimeSkillsViews(projectRoot);
|
|
46621
46628
|
const failures = [];
|
|
46622
|
-
if (!check2.
|
|
46623
|
-
if (!check2.
|
|
46624
|
-
if (!check2.
|
|
46625
|
-
if (!check2.piPointerReady) failures.push(".pi/settings.json.skills does not include ../.xtrm/skills/active/pi");
|
|
46629
|
+
if (!check2.activeReady) failures.push("active view is missing, empty, or contains invalid links");
|
|
46630
|
+
if (!check2.claudePointerReady) failures.push(".claude/skills is not linked to ../.xtrm/skills/active");
|
|
46631
|
+
if (!check2.piPointerReady) failures.push(".pi/settings.json.skills does not include ../.xtrm/skills/active");
|
|
46626
46632
|
if (failures.length > 0) {
|
|
46627
46633
|
throw new Error(`Runtime skills view validation failed: ${failures.join("; ")}`);
|
|
46628
46634
|
}
|
|
@@ -46734,7 +46740,7 @@ async function runInitVerification(projectRoot) {
|
|
|
46734
46740
|
const piPlan = await verifyPiRuntime(projectRoot);
|
|
46735
46741
|
const projectResult = verifyProjectBootstrap(projectRoot);
|
|
46736
46742
|
const skillsRuntimeResult = await checkRuntimeSkillsViews(projectRoot);
|
|
46737
|
-
const allPassed = machinePlan.allRequiredPresent && claudeResult.hooksWired && piPlan.allRequiredPresent && skillsRuntimeResult.
|
|
46743
|
+
const allPassed = machinePlan.allRequiredPresent && claudeResult.hooksWired && piPlan.allRequiredPresent && skillsRuntimeResult.activeReady && skillsRuntimeResult.claudePointerReady && skillsRuntimeResult.piPointerReady && projectResult.beadsInitialized;
|
|
46738
46744
|
return {
|
|
46739
46745
|
machineBootstrap: {
|
|
46740
46746
|
allRequiredPresent: machinePlan.allRequiredPresent,
|
|
@@ -46747,8 +46753,7 @@ async function runInitVerification(projectRoot) {
|
|
|
46747
46753
|
missingPackages: piPlan.missingPackages.filter((s) => s.pkg.required).map((s) => s.pkg.displayName)
|
|
46748
46754
|
},
|
|
46749
46755
|
skillsRuntime: {
|
|
46750
|
-
|
|
46751
|
-
activePiReady: skillsRuntimeResult.activePiReady,
|
|
46756
|
+
activeReady: skillsRuntimeResult.activeReady,
|
|
46752
46757
|
claudePointerReady: skillsRuntimeResult.claudePointerReady,
|
|
46753
46758
|
piPointerReady: skillsRuntimeResult.piPointerReady,
|
|
46754
46759
|
hasDeprecatedAgentsSkillsPath: skillsRuntimeResult.hasDeprecatedAgentsSkillsPath
|
|
@@ -46790,8 +46795,7 @@ function renderVerificationSummary(result) {
|
|
|
46790
46795
|
console.log(` ${prIcon} ${prLabel} \u2014 ${parts.join("; ")}`);
|
|
46791
46796
|
}
|
|
46792
46797
|
const skillsParts = [];
|
|
46793
|
-
if (!result.skillsRuntime.
|
|
46794
|
-
if (!result.skillsRuntime.activePiReady) skillsParts.push("active/pi");
|
|
46798
|
+
if (!result.skillsRuntime.activeReady) skillsParts.push("active");
|
|
46795
46799
|
if (!result.skillsRuntime.claudePointerReady) skillsParts.push(".claude/skills pointer");
|
|
46796
46800
|
if (!result.skillsRuntime.piPointerReady) skillsParts.push(".pi settings skills pointer");
|
|
46797
46801
|
const srIcon = skillsParts.length === 0 ? sym.ok : sym.warn;
|