xtrm-tools 0.7.17 → 0.7.19
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/config/hooks.json +2 -0
- package/.xtrm/config/instructions/agents-top.md +2 -1
- package/.xtrm/registry.json +429 -712
- package/.xtrm/skills/default/creating-service-skills/scripts/bootstrap.py +82 -156
- package/.xtrm/skills/default/creating-service-skills/scripts/scaffolder.py +73 -121
- package/.xtrm/skills/default/hook-development/references/patterns.md +1 -1
- package/.xtrm/skills/default/last30days/scripts/test-v1-vs-v2.sh +2 -2
- package/.xtrm/skills/default/planning/SKILL.md +75 -29
- package/.xtrm/skills/default/releasing/SKILL.md +163 -57
- package/.xtrm/skills/default/security-pipeline/SKILL.md +192 -0
- package/.xtrm/skills/default/security-pipeline/scripts/security-bootstrap.sh +294 -0
- package/.xtrm/skills/default/security-pipeline/templates/.githooks/pre-push.template +39 -0
- package/.xtrm/skills/default/security-pipeline/templates/.github/workflows/gitleaks.yml +33 -0
- package/.xtrm/skills/default/security-pipeline/templates/.github/workflows/osv-scanner.yml +33 -0
- package/.xtrm/skills/default/security-pipeline/templates/.github/workflows/semgrep.yml +41 -0
- package/.xtrm/skills/default/security-pipeline/templates/.gitleaks.toml +44 -0
- package/.xtrm/skills/default/security-pipeline/templates/.pre-commit-config.yaml +67 -0
- package/.xtrm/skills/default/security-pipeline/templates/.semgrepignore +46 -0
- package/.xtrm/skills/default/security-pipeline/templates/scripts/security-scan.sh +57 -0
- package/.xtrm/skills/default/security-pipeline/templates/scripts/semgrep-diff.sh +68 -0
- package/.xtrm/skills/default/session-close-report/SKILL.md +167 -6
- package/.xtrm/skills/default/sync-docs/SKILL.md +1 -1
- package/.xtrm/skills/default/update-xt/SKILL.md +270 -4
- package/.xtrm/skills/default/updating-service-skills/scripts/drift_detector.py +22 -0
- package/.xtrm/skills/default/using-script-specialists/SKILL.md +7 -5
- package/.xtrm/skills/default/using-specialists/SKILL.md +13 -12
- package/.xtrm/skills/default/using-specialists-auto/SKILL.md +137 -0
- package/.xtrm/skills/default/using-specialists-v2/SKILL.md +14 -21
- package/.xtrm/skills/default/using-specialists-v3/SKILL.md +533 -21
- package/.xtrm/skills/default/vaultctl/SKILL.md +2 -2
- package/CHANGELOG.md +87 -3
- package/cli/dist/index.cjs +12429 -3769
- package/cli/dist/index.cjs.map +1 -1
- package/cli/package.json +9 -3
- package/package.json +27 -7
- package/packages/pi-extensions/package.json +1 -1
- package/.xtrm/skills/default/planning/evals/evals.json +0 -19
- package/.xtrm/skills/default/quality-gates/evals/evals.json +0 -181
- package/.xtrm/skills/default/quality-gates/workspace/iteration-1/FINAL-EVAL-SUMMARY.md +0 -75
- package/.xtrm/skills/default/quality-gates/workspace/iteration-1/edge-case-auto-fix-verification/with_skill/outputs/response.md +0 -59
- package/.xtrm/skills/default/quality-gates/workspace/iteration-1/edge-case-mixed-language-project/with_skill/outputs/response.md +0 -60
- package/.xtrm/skills/default/quality-gates/workspace/iteration-1/eval-summary.md +0 -105
- package/.xtrm/skills/default/quality-gates/workspace/iteration-1/partial-install-python-only/with_skill/outputs/response.md +0 -93
- package/.xtrm/skills/default/quality-gates/workspace/iteration-1/python-refactor-request/with_skill/outputs/response.md +0 -104
- package/.xtrm/skills/default/quality-gates/workspace/iteration-1/quality-gate-error-fix/with_skill/outputs/response.md +0 -74
- package/.xtrm/skills/default/quality-gates/workspace/iteration-1/should-not-trigger-general-chat/with_skill/outputs/response.md +0 -18
- package/.xtrm/skills/default/quality-gates/workspace/iteration-1/should-not-trigger-math-question/with_skill/outputs/response.md +0 -18
- package/.xtrm/skills/default/quality-gates/workspace/iteration-1/should-not-trigger-unrelated-coding/with_skill/outputs/response.md +0 -56
- package/.xtrm/skills/default/quality-gates/workspace/iteration-1/tdd-guard-blocking-confusion/with_skill/outputs/response.md +0 -67
- package/.xtrm/skills/default/quality-gates/workspace/iteration-1/typescript-feature-with-tests/with_skill/outputs/response.md +0 -97
- package/.xtrm/skills/default/sync-docs/evals/evals.json +0 -89
- package/.xtrm/skills/default/test-planning/evals/evals.json +0 -23
- package/.xtrm/skills/default/using-specialists/SKILL.safe.md +0 -1082
- package/.xtrm/skills/default/using-specialists/SKILL.ultra.md +0 -1082
- package/.xtrm/skills/default/using-specialists/evals/evals.json +0 -68
- package/.xtrm/skills/default/using-specialists-v3/evals/evals.json +0 -89
- package/packages/pi-extensions/.serena/project.yml +0 -130
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Run semgrep against the diff between HEAD and origin/main.
|
|
3
|
+
# Used by pre-push hook so pre-existing debt doesn't block unrelated pushes.
|
|
4
|
+
# CI's full scan remains the source of truth for absolute findings.
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
if ! command -v semgrep >/dev/null; then
|
|
9
|
+
echo "semgrep not installed — skipping (CI covers it)"
|
|
10
|
+
exit 0
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
# Derive base ref dynamically. Order:
|
|
14
|
+
# 1. branch's tracked upstream ('@{u}') — most reliable
|
|
15
|
+
# 2. common default branches if their *remote* version exists (origin/*)
|
|
16
|
+
# 3. local default branches IF different from current branch
|
|
17
|
+
# We refuse to use the current branch as its own baseline because then
|
|
18
|
+
# merge-base resolves to HEAD and --baseline-commit=HEAD silently scans
|
|
19
|
+
# nothing on every push.
|
|
20
|
+
HEAD_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || true)
|
|
21
|
+
HEAD_SHA=$(git rev-parse HEAD)
|
|
22
|
+
|
|
23
|
+
upstream=$(git rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>/dev/null || true)
|
|
24
|
+
BASE_REF=""
|
|
25
|
+
if [ -n "$upstream" ]; then
|
|
26
|
+
BASE_REF="$upstream"
|
|
27
|
+
else
|
|
28
|
+
for cand in origin/main origin/master main master; do
|
|
29
|
+
git rev-parse --verify "$cand" >/dev/null 2>&1 || continue
|
|
30
|
+
# Skip a local-branch candidate that IS the current branch
|
|
31
|
+
[ "$cand" = "$HEAD_BRANCH" ] && continue
|
|
32
|
+
BASE_REF="$cand"
|
|
33
|
+
break
|
|
34
|
+
done
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
[ -n "$BASE_REF" ] && git fetch "${BASE_REF%%/*}" "${BASE_REF#*/}" --quiet 2>/dev/null || true
|
|
38
|
+
|
|
39
|
+
SEMGREP_BASELINE_ARGS=()
|
|
40
|
+
if [ -n "$BASE_REF" ]; then
|
|
41
|
+
BASE=$(git merge-base HEAD "$BASE_REF" 2>/dev/null || true)
|
|
42
|
+
# merge-base==HEAD here means branch is at upstream tip — legitimate empty
|
|
43
|
+
# diff. Pass --baseline-commit so semgrep produces an empty result rather
|
|
44
|
+
# than falling back to a full scan.
|
|
45
|
+
[ -n "$BASE" ] && SEMGREP_BASELINE_ARGS=(--baseline-commit="$BASE")
|
|
46
|
+
fi
|
|
47
|
+
# Last-resort: no upstream resolved at all. rev-list can equal HEAD on single
|
|
48
|
+
# -commit histories; reject and full-scan in that case.
|
|
49
|
+
if [ ${#SEMGREP_BASELINE_ARGS[@]} -eq 0 ]; then
|
|
50
|
+
BASE=$(git rev-list HEAD --max-count=50 | tail -1)
|
|
51
|
+
if [ -n "$BASE" ] && [ "$BASE" != "$HEAD_SHA" ]; then
|
|
52
|
+
SEMGREP_BASELINE_ARGS=(--baseline-commit="$BASE")
|
|
53
|
+
else
|
|
54
|
+
echo "[semgrep-diff] no usable baseline (no upstream, single-commit branch, or pushing default branch directly) — running full scan"
|
|
55
|
+
fi
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
exec semgrep scan \
|
|
59
|
+
--config=p/default \
|
|
60
|
+
--config=p/security-audit \
|
|
61
|
+
--config=p/secrets \
|
|
62
|
+
--config=p/python \
|
|
63
|
+
--config=p/dockerfile \
|
|
64
|
+
--config=p/github-actions \
|
|
65
|
+
"${SEMGREP_BASELINE_ARGS[@]}" \
|
|
66
|
+
--error \
|
|
67
|
+
--quiet \
|
|
68
|
+
--skip-unknown-extensions
|
|
@@ -41,6 +41,52 @@ are factually superseded.
|
|
|
41
41
|
|
|
42
42
|
## Workflow
|
|
43
43
|
|
|
44
|
+
### 0. Cleanup before reporting (MANDATORY)
|
|
45
|
+
|
|
46
|
+
A report on a dirty session is misleading. Before selecting or generating any
|
|
47
|
+
report, verify and clean up everything this session opened. The report must
|
|
48
|
+
reflect a clean terminal state.
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# 0a. Worktrees opened during the session
|
|
52
|
+
git worktree list # any feature/fix/chore worktrees still here?
|
|
53
|
+
# Remove every worktree this session created (or that a stopped specialist left):
|
|
54
|
+
git worktree remove <path> # for each stale entry
|
|
55
|
+
git branch -D <branch> # only after confirming merged or abandoned
|
|
56
|
+
git worktree prune # drop stale metadata
|
|
57
|
+
|
|
58
|
+
# 0b. Specialist jobs still running or waiting
|
|
59
|
+
sp ps # MUST be empty (or only intentionally kept-alive jobs)
|
|
60
|
+
sp stop <job-id> # for any leftover running/waiting job
|
|
61
|
+
# After every sp stop, re-check sp ps and git worktree list — sp stop should
|
|
62
|
+
# clean its worktree, but verify.
|
|
63
|
+
|
|
64
|
+
# 0c. Stale background processes from the session
|
|
65
|
+
ps -ef | grep -E '(serena|gitnexus|specialists|sp-serve|sp-script|pi[ -]|claude)' | grep -v grep
|
|
66
|
+
# Kill anything you launched that is still running and no longer needed.
|
|
67
|
+
# Be especially careful with:
|
|
68
|
+
# - serena MCP servers (often leak when an MCP host crashes)
|
|
69
|
+
# - gitnexus index processes (`npx gitnexus analyze` can outlive its terminal)
|
|
70
|
+
# - sp-serve / sp-script tmux sessions
|
|
71
|
+
# - orphaned `pi` or `claude` processes from interactive sessions
|
|
72
|
+
|
|
73
|
+
tmux ls 2>/dev/null # any sp-* or xt-* tmux sessions left?
|
|
74
|
+
tmux kill-session -t <name> # for each stale session
|
|
75
|
+
|
|
76
|
+
# 0d. Tmp dirs the session created (only if large or sensitive)
|
|
77
|
+
ls -la /tmp/sp-serve-* /tmp/sp-script-* 2>/dev/null
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Do not skip any sub-step. If a process refuses to stop cleanly, document it in
|
|
81
|
+
the **Problems Encountered** section of the report so the next agent knows.
|
|
82
|
+
|
|
83
|
+
A clean session ends with:
|
|
84
|
+
- `git worktree list` showing only the main worktree (plus any intentional ones)
|
|
85
|
+
- `sp ps` showing 0 jobs (or only intentional keep-alive)
|
|
86
|
+
- no leaked `serena` / `gitnexus` / `specialists` / `sp-serve` / `sp-script`
|
|
87
|
+
processes from this session
|
|
88
|
+
- no orphaned tmux sessions matching `sp-*` or `xt-*`
|
|
89
|
+
|
|
44
90
|
### 1. Select report: update existing or generate new
|
|
45
91
|
|
|
46
92
|
For same-day update:
|
|
@@ -115,8 +161,8 @@ delete this section. If prior dispatches exist, keep and extend them.
|
|
|
115
161
|
#### Problems Encountered
|
|
116
162
|
Every problem hit during the session. Root Cause and Resolution columns are
|
|
117
163
|
mandatory. Include: bugs discovered, wrong approaches tried, blockers hit,
|
|
118
|
-
tooling failures
|
|
119
|
-
this section entirely.
|
|
164
|
+
tooling failures, and any cleanup-step failures from Step 0 above. If no
|
|
165
|
+
problems exist anywhere in the same-day report, delete this section entirely.
|
|
120
166
|
|
|
121
167
|
#### Code Changes
|
|
122
168
|
The skeleton lists files. Add narrative:
|
|
@@ -127,7 +173,8 @@ The skeleton lists files. Add narrative:
|
|
|
127
173
|
to the final pushed stack
|
|
128
174
|
|
|
129
175
|
#### Documentation Updates
|
|
130
|
-
List doc changes, skill updates, memory saves, CHANGELOG entries
|
|
176
|
+
List doc changes, skill updates, memory saves, CHANGELOG entries
|
|
177
|
+
(see Step 5 — due-diligence sweep — and Step 6 — CHANGELOG sync).
|
|
131
178
|
Delete if no doc work happened.
|
|
132
179
|
|
|
133
180
|
#### Open Issues with Context
|
|
@@ -162,12 +209,108 @@ Ensure all frontmatter counts are accurate after filling/updating:
|
|
|
162
209
|
- `issues_closed` — actual closed issue count represented in the report
|
|
163
210
|
- `commits` — commit count represented in the report, if known
|
|
164
211
|
|
|
165
|
-
### 5.
|
|
212
|
+
### 5. Due-diligence sweep (paranoid mode — assume you forgot something)
|
|
213
|
+
|
|
214
|
+
Step 0 cleaned the *process* state. This step audits the *content* state.
|
|
215
|
+
Cleanup work the orchestrator usually forgets at session close, ranked by
|
|
216
|
+
how often it gets missed:
|
|
217
|
+
|
|
218
|
+
- **Service skills**: did this session touch any code under a service
|
|
219
|
+
registered in `.claude/skills/service-registry.json` (or equivalent
|
|
220
|
+
registry)? If yes, the service skill's SKILL.md or diagnostic scripts
|
|
221
|
+
are likely drifted. Run `/updating-service-skills` (or
|
|
222
|
+
`service-skills-sync` specialist) and let it scan. If no registry exists,
|
|
223
|
+
skip — but check whether the project keeps service skills under
|
|
224
|
+
`.xtrm/skills/user/packs/<service>/` and treat them the same.
|
|
225
|
+
- **Docs SSOT**: did the session change architecture, migrations, public
|
|
226
|
+
APIs, or service ownership? If yes, run `/sync-docs` (or the
|
|
227
|
+
`sync-docs` specialist) for any drifted doc. Skip if changes are
|
|
228
|
+
pure-internal (refactors with no observable surface change).
|
|
229
|
+
- **Memories**: every `bd close` should have triggered a memory-gate ack.
|
|
230
|
+
Run `bd memories <topic>` to confirm anything genuinely novel landed.
|
|
231
|
+
If you saw a real surprise but acked "nothing novel" out of haste,
|
|
232
|
+
go back and `bd remember` it now.
|
|
233
|
+
- **CLAUDE.md / project guide**: did this session add or remove a
|
|
234
|
+
service, change a key port, change a top-level workflow command, or
|
|
235
|
+
change how tools are wired? If yes, append/correct in CLAUDE.md before
|
|
236
|
+
commit — the file is loaded automatically by every future session.
|
|
237
|
+
- **Evidence artifacts**: did the session generate reports, dashboards,
|
|
238
|
+
CSVs, or figures intended to be persisted (`scripts/outputs/`,
|
|
239
|
+
`docs/review/`, etc.)? Confirm they are committed; otherwise either
|
|
240
|
+
commit them or document in the report why they were not kept.
|
|
241
|
+
- **Decisions**: did the session make a non-obvious architectural call
|
|
242
|
+
(deprecating a service, schema choice, dependency swap)? Record via
|
|
243
|
+
`bd decision` if the project uses it, otherwise note in the report.
|
|
244
|
+
- **Tests**: did new behavior land without tests? If yes, file a test
|
|
245
|
+
follow-up bead (`discovered-from:<impl-id>`) before closing — do not
|
|
246
|
+
let untested behavior leave the session silent.
|
|
247
|
+
- **Skill packs (`.xtrm/skills/`)**: did you edit a skill in this
|
|
248
|
+
project? If the canonical version lives in xtrm-tools, mirror the edit
|
|
249
|
+
there too (or note that `xt update` will overwrite the local mirror on
|
|
250
|
+
next sync, which makes the local edit ephemeral).
|
|
251
|
+
- **Open beads created mid-session**: every bead filed this session
|
|
252
|
+
should be either closed, scheduled with a parent, or marked with clear
|
|
253
|
+
context. Run `bd list --status=open --created-by=me` (or equivalent)
|
|
254
|
+
and confirm none are floating without a parent or follow-up note.
|
|
255
|
+
|
|
256
|
+
If any item above turns up real work, do it now or file a follow-up bead
|
|
257
|
+
linked `discovered-from:<this-session-root>` so the next agent picks it up.
|
|
258
|
+
A clean session means none of these were forgotten — the report should be
|
|
259
|
+
able to honestly claim "due-diligence sweep clean."
|
|
260
|
+
|
|
261
|
+
### 6. Sync CHANGELOG.md (MANDATORY when user-facing changes shipped)
|
|
262
|
+
|
|
263
|
+
The session report is for the next *agent*; CHANGELOG.md is for downstream
|
|
264
|
+
*consumers*. Both must stay in sync — the report alone is not enough.
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
ls CHANGELOG.md 2>/dev/null # confirm the project keeps one
|
|
268
|
+
git tag --sort=-v:refname | head -3 # last release tag
|
|
269
|
+
git log <last-tag>..HEAD --oneline # what is missing
|
|
270
|
+
```
|
|
166
271
|
|
|
167
|
-
|
|
272
|
+
Decision tree:
|
|
273
|
+
|
|
274
|
+
- **A release was cut this session** (new tag, e.g. via `/releasing` or
|
|
275
|
+
`changelog-keeper`): the new version section already exists. Verify it
|
|
276
|
+
contains every user-facing change from the session and that
|
|
277
|
+
`[Unreleased]` is empty. Stop — release flow owns CHANGELOG.
|
|
278
|
+
- **No release was cut**: append every user-facing change from the session
|
|
279
|
+
to the existing `[Unreleased]` block at the top of CHANGELOG.md. Use
|
|
280
|
+
Keep a Changelog categories: `### Added` / `### Changed` / `### Deprecated`
|
|
281
|
+
/ `### Removed` / `### Fixed` / `### Security`. One bullet per change,
|
|
282
|
+
lead with the affected subsystem or symbol, include the bead ID(s) when
|
|
283
|
+
available — same prose density as prior `[Unreleased]` entries.
|
|
284
|
+
- **No user-facing change shipped** (pure orchestration, doc-only edits to
|
|
285
|
+
internal-handoff files like reports/skills, refactors with no observable
|
|
286
|
+
effect): skip — do not pollute `[Unreleased]` with internal noise. Note
|
|
287
|
+
the skip in the Documentation Updates section so it is auditable.
|
|
288
|
+
|
|
289
|
+
What counts as user-facing for `[Unreleased]`:
|
|
290
|
+
- new or removed CLI flags, commands, env vars, config keys
|
|
291
|
+
- new or removed services / containers / jobs an operator deploys
|
|
292
|
+
- schema migrations that downstream consumers see
|
|
293
|
+
- new or removed API/MCP/REST endpoints, tools, or response fields
|
|
294
|
+
- bug fixes that change observable behavior
|
|
295
|
+
- security-relevant changes
|
|
296
|
+
|
|
297
|
+
What does NOT belong in `[Unreleased]`:
|
|
298
|
+
- session reports themselves
|
|
299
|
+
- skill or memory edits that only affect agents
|
|
300
|
+
- refactors with byte-identical observable behavior
|
|
301
|
+
- per-issue notes that already live in beads
|
|
302
|
+
|
|
303
|
+
If the project has no CHANGELOG.md, skip silently — do not create one
|
|
304
|
+
without operator direction.
|
|
305
|
+
|
|
306
|
+
### 7. Commit the report (and CHANGELOG if updated)
|
|
307
|
+
|
|
308
|
+
Reports are versioned handoff artifacts and should be tracked. If Step 5
|
|
309
|
+
modified `CHANGELOG.md`, fold it into the same commit so the report and
|
|
310
|
+
changelog ship together.
|
|
168
311
|
|
|
169
312
|
```bash
|
|
170
|
-
git add .xtrm/reports/
|
|
313
|
+
git add .xtrm/reports/ CHANGELOG.md # CHANGELOG.md only if changed
|
|
171
314
|
git commit -m "session report: <date>"
|
|
172
315
|
```
|
|
173
316
|
|
|
@@ -175,11 +318,29 @@ If you updated an existing same-day report after an earlier report commit, commi
|
|
|
175
318
|
that update with the same message style or fold it into the current final commit
|
|
176
319
|
before push.
|
|
177
320
|
|
|
321
|
+
### 8. Final cleanup verification (MANDATORY)
|
|
322
|
+
|
|
323
|
+
After committing, re-run the Step 0 checks one more time:
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
git worktree list
|
|
327
|
+
sp ps
|
|
328
|
+
ps -ef | grep -E '(serena|gitnexus|specialists|sp-serve|sp-script)' | grep -v grep
|
|
329
|
+
tmux ls 2>/dev/null
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
If any of these show session-leaked artifacts, stop them now or document them
|
|
333
|
+
in the report. Do not consider the session "closed" until this verification is
|
|
334
|
+
clean.
|
|
335
|
+
|
|
178
336
|
## Quality bar
|
|
179
337
|
|
|
180
338
|
The reference is `~/projects/specialists/.xtrm/reports/2026-03-30-orchestration-session.md`.
|
|
181
339
|
Every report must match that level of detail. Specifically:
|
|
182
340
|
|
|
341
|
+
- Step 0 cleanup performed before report generation; Step 8 verification clean.
|
|
342
|
+
- Step 5 due-diligence sweep performed; service skills, docs, memories, CLAUDE.md, evidence, decisions, tests, and skill mirrors checked (or skipped with reason).
|
|
343
|
+
- Step 6 CHANGELOG sync performed when user-facing changes shipped (or skip noted).
|
|
183
344
|
- No empty `<!-- FILL -->` markers left in the final output
|
|
184
345
|
- No duplicate same-day reports unless explicitly requested by the operator
|
|
185
346
|
- Every closed issue has context, not just an ID
|
|
@@ -78,7 +78,7 @@ and stop.
|
|
|
78
78
|
|
|
79
79
|
```bash
|
|
80
80
|
python3 .xtrm/skills/default/sync-docs/scripts/drift_detector.py scan --json \
|
|
81
|
-
| jq '[.[] | select(.
|
|
81
|
+
| jq '[.stale[]? | select(.doc == "<YOUR_DOC>")]'
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
If your doc reports stale, capture the list of commits since `synced_at` — those are your candidate commits for Phase 3.
|
|
@@ -28,7 +28,8 @@ This is what a correctly installed project looks like. Check each item.
|
|
|
28
28
|
| `.xtrm/skills/active/` | Flat directory of symlinks to `../default/<skill>` |
|
|
29
29
|
| `active/pi/` subdirectory | Must NOT exist (stale — old runtime split) |
|
|
30
30
|
| `active/claude/` subdirectory | Must NOT exist (stale — old runtime split) |
|
|
31
|
-
| `.pi/settings.json` `.skills` array | Must include `"../.xtrm/skills/active"` |
|
|
31
|
+
| `.pi/settings.json` `.skills` array | Must include `"../.xtrm/skills/active"` (project-local, wins) |
|
|
32
|
+
| `.pi/settings.json` `.skills` array | Must include `"~/.xtrm/skills/default"` (user-level fallback — xtrm-4h6u) |
|
|
32
33
|
| `.pi/settings.json` `.skills` array | Must NOT include `"../.xtrm/skills/active/pi"` (old path) |
|
|
33
34
|
|
|
34
35
|
### Hooks wiring
|
|
@@ -65,10 +66,10 @@ readlink .claude/skills
|
|
|
65
66
|
ls .xtrm/skills/active/pi 2>/dev/null && echo "STALE: active/pi exists"
|
|
66
67
|
ls .xtrm/skills/active/claude 2>/dev/null && echo "STALE: active/claude exists"
|
|
67
68
|
|
|
68
|
-
# 5. Pi settings skills
|
|
69
|
+
# 5. Pi settings skills entries (both must be present since xtrm-4h6u)
|
|
69
70
|
node -e "const s=require('./.pi/settings.json'); console.log(s.skills)" 2>/dev/null
|
|
70
|
-
# Expected to include: ../.xtrm/skills/active
|
|
71
|
-
# Stale if includes: ../.xtrm/skills/active/pi
|
|
71
|
+
# Expected to include BOTH: ../.xtrm/skills/active AND ~/.xtrm/skills/default
|
|
72
|
+
# Stale if only first entry present, or if includes: ../.xtrm/skills/active/pi
|
|
72
73
|
|
|
73
74
|
# 6. Active view integrity (all entries must be valid symlinks)
|
|
74
75
|
for f in .xtrm/skills/active/*; do [ -L "$f" ] || echo "NOT A SYMLINK: $f"; done
|
|
@@ -167,6 +168,16 @@ cd cli && npm run build
|
|
|
167
168
|
xt init -y # now runs with updated code
|
|
168
169
|
```
|
|
169
170
|
|
|
171
|
+
**Worktree caveat**: `npm run build` from inside `.xtrm/worktrees/<name>/cli/` is blocked by a guard script — building from a worktree contaminates dist with worktree-specific absolute paths. If you're working in a worktree, build from a detached worktree outside `.xtrm/`:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
git worktree add --detach /tmp/xt-build HEAD
|
|
175
|
+
cd /tmp/xt-build/cli && npm ci && npm run build
|
|
176
|
+
cp dist/index.cjs <worktree-root>/cli/dist/index.cjs
|
|
177
|
+
cp dist/index.cjs.map <worktree-root>/cli/dist/index.cjs.map
|
|
178
|
+
git worktree remove /tmp/xt-build --force
|
|
179
|
+
```
|
|
180
|
+
|
|
170
181
|
## Verification
|
|
171
182
|
|
|
172
183
|
After all fixes, confirm canonical state is restored:
|
|
@@ -198,6 +209,261 @@ If `xt status` still shows drift after targeted fixes, run the full sync:
|
|
|
198
209
|
xt init
|
|
199
210
|
```
|
|
200
211
|
|
|
212
|
+
## Multi-Repo Sweep (Fleet Update)
|
|
213
|
+
|
|
214
|
+
For updating **many repos at once** after an xtrm-tools upgrade — much lighter than
|
|
215
|
+
running `xt init -y` per repo. The right pattern when you've just rebuilt xtrm-tools
|
|
216
|
+
locally or pulled a new tag.
|
|
217
|
+
|
|
218
|
+
### Dry-run discovery first
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
xt update --root ~/dev # walk the tree
|
|
222
|
+
xt update --root ~/projects/mercury # walk another tree
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Output classifies each discovered repo by `.xtrm/` state:
|
|
226
|
+
|
|
227
|
+
| Status | Meaning | Action |
|
|
228
|
+
|--------|---------|--------|
|
|
229
|
+
| `refreshed` | `.xtrm/registry.json` present; drift vs current package detected | `--apply` will reinstall managed assets |
|
|
230
|
+
| `already-current` | `.xtrm/registry.json` present; no drift | no action |
|
|
231
|
+
| `incomplete` | `.xtrm/` directory exists but `.xtrm/registry.json` is missing | `xt init -y` now seeds registry.json automatically (xtrm-ya2i, xtrm-tools ≥ 0.7.18). Older `.xtrm/` dirs created before that fix still need the recipe below. |
|
|
232
|
+
| `failed` | Hard error during drift check or install | inspect reason — common: PACK metadata drift, missing source files, fs-extra refusing to copy onto a symlink |
|
|
233
|
+
|
|
234
|
+
Transient worktree paths under `.worktrees/` (specialists) or `.xtrm/worktrees/`
|
|
235
|
+
(`xt claude` / `xt pi`) are **skipped** automatically — they're not real repos to
|
|
236
|
+
refresh.
|
|
237
|
+
|
|
238
|
+
### Apply
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
xt update --apply --root ~/dev
|
|
242
|
+
xt update --apply --root ~/projects/mercury
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
What `--apply` does for each managed repo:
|
|
246
|
+
- Runs the install flow with `force=true` — refreshes `.xtrm/config`, `.xtrm/hooks`, `.xtrm/skills/default` (mirror), `.pi/settings.json`, `.mcp.json`.
|
|
247
|
+
- Writes `dolt.shared-server: true` into `.beads/config.yaml` if not already set (so the worktree's bd routes to the shared dolt server instead of spawning per-worktree subprocesses).
|
|
248
|
+
- Globally installs any missing xt-managed Pi packages.
|
|
249
|
+
- Does NOT touch `incomplete` repos (deliberate — auto-fix would be destructive).
|
|
250
|
+
|
|
251
|
+
### Bootstrapping `incomplete` repos
|
|
252
|
+
|
|
253
|
+
Two scenarios:
|
|
254
|
+
|
|
255
|
+
**A. The repo legitimately needs full xtrm management:**
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
cd <repo>
|
|
259
|
+
xt init -y # scaffolds .xtrm/{config,hooks,skills} AND seeds registry.json
|
|
260
|
+
xt update --apply --repo . # bring everything in sync (registry-driven)
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
`xt init -y` now snapshots `.xtrm/registry.json` from the installed xtrm-tools package automatically (xtrm-ya2i). The previous manual `cp /path/to/xtrm-tools/.xtrm/registry.json .xtrm/` step is no longer needed on xtrm-tools ≥ 0.7.18. If you're on an older version (or the registry is missing for some other reason), fall back to:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
cp "$(npm root -g)/xtrm-tools/.xtrm/registry.json" .xtrm/
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
**B. The repo is intentionally not xtrm-managed.** Leave the `.xtrm/` partial dir
|
|
270
|
+
alone; `incomplete` is just a status row, not an error. If you want it to stop
|
|
271
|
+
appearing, remove the orphaned `.xtrm/` directory.
|
|
272
|
+
|
|
273
|
+
### When a repo fails
|
|
274
|
+
|
|
275
|
+
Common failure modes and fixes:
|
|
276
|
+
|
|
277
|
+
| Error | Cause | Fix |
|
|
278
|
+
|-------|-------|-----|
|
|
279
|
+
| `Source and destination must not be the same` | `npm link`'d xtrm-tools + repo has symlinked `.xtrm/skills/default → xtrm-tools` (link chain collapses to same canonical path) | Functionally fine — repo is already in sync via the live symlinks, not a real failure. If you want to **fully decouple** the project from the dev tree, follow the migration recipe below. |
|
|
280
|
+
| `PACK_METADATA_MISMATCH: metadata-only: X, filesystem-only: Y` | A user-skill-pack (`.xtrm/skills/user/packs/<name>/PACK.json`) lists a skill that has been renamed on disk | Edit `PACK.json` so the listed skill names match the directory names; re-run. |
|
|
281
|
+
| `Cannot read properties of null (reading 'dolt')` | Repo's `.beads/config.yaml` is comments-only (fresh `bd init` default); pre-`xtrm-16ec` xtrm crashes parsing it | Upgrade xtrm-tools to ≥ 0.7.18; the parse result is coerced to `{}` defensively now. |
|
|
282
|
+
|
|
283
|
+
## Migrating a dev-linked project to a real consumer install
|
|
284
|
+
|
|
285
|
+
A project ends up with `.xtrm/skills/default` (or another `.xtrm/` asset) as a **symlink** back to the dev tree when:
|
|
286
|
+
- xtrm-tools was `npm link`-ed globally (`/home/<user>/.nvm/.../node_modules/xtrm-tools` → `/home/<user>/dev/xtrm-tools/`), AND
|
|
287
|
+
- the project's `.xtrm/skills/default` was manually replaced with a symlink to the npm-global path (common dev-loop shortcut so skill edits propagate instantly).
|
|
288
|
+
|
|
289
|
+
`installFromRegistry`'s `scaffoldSkillsDefaultFromPackage` has an intentional branch (`registry-scaffold.ts:104`): *"if target is a symlink whose realpath equals the package realpath → noop"*. This **preserves the dev symlink** on every `xt update`. The arrangement is functional but the project is invisibly coupled to whatever lives in the dev tree (or whatever the global npm path points to).
|
|
290
|
+
|
|
291
|
+
### When to migrate
|
|
292
|
+
|
|
293
|
+
- Before publishing a consumer-facing release of the dependent project.
|
|
294
|
+
- Before handing the project to another developer / machine.
|
|
295
|
+
- When you want `xt update --apply` to actually *write files into the project* rather than no-op.
|
|
296
|
+
|
|
297
|
+
### Detection
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
# Is .xtrm/skills/default a symlink, and where does it point?
|
|
301
|
+
readlink <repo>/.xtrm/skills/default
|
|
302
|
+
# If empty / not-a-symlink: nothing to migrate.
|
|
303
|
+
# If points anywhere outside <repo>/: needs migration.
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Recipe
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
cd <repo>
|
|
310
|
+
|
|
311
|
+
# 1. Remove the symlink (does NOT touch the real files in the dev tree).
|
|
312
|
+
rm .xtrm/skills/default
|
|
313
|
+
|
|
314
|
+
# 2. Re-run init — copies real files from the installed xtrm-tools package
|
|
315
|
+
# into .xtrm/skills/default/ AND seeds .xtrm/registry.json (xtrm-ya2i).
|
|
316
|
+
xt init -y
|
|
317
|
+
|
|
318
|
+
# 3. If the symlink was committed (git ls-files showed it as mode 120000),
|
|
319
|
+
# flip the tracked entry to a real directory:
|
|
320
|
+
git rm --cached .xtrm/skills/default 2>/dev/null # ok if it was untracked
|
|
321
|
+
git add .xtrm/skills/default
|
|
322
|
+
git commit -m "chore: replace dev symlink with real xtrm skills payload"
|
|
323
|
+
|
|
324
|
+
# 4. Optional sanity: confirm no more symlinks point outside the repo.
|
|
325
|
+
find .xtrm -type l -lname '/*' -o -type l ! -lname '../*' -a ! -lname './*'
|
|
326
|
+
# Empty output means clean.
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### What `npm install -g xtrm-tools` alone does
|
|
330
|
+
|
|
331
|
+
Replacing the `npm link` with a real npm install (`npm install -g xtrm-tools`) breaks the dev-tree coupling — the global path becomes real files at the published version — **but it does not remove the project's symlink.** The symlink still points at the global npm path, which now resolves to immutable published files. The project keeps working but stays pinned to the npm-installed version forever, and `.xtrm/skills/default` remains a symlink on disk.
|
|
332
|
+
|
|
333
|
+
To get true isolation (real files inside `<repo>/.xtrm/skills/default/`), the recipe above is still required.
|
|
334
|
+
|
|
335
|
+
## Worktree hygiene: `.beads/` and `core.hooksPath`
|
|
336
|
+
|
|
337
|
+
Modern bd 1.0.3 stores `core.hooksPath` as an **absolute parent path** at `bd init`
|
|
338
|
+
time (e.g. `$HOME/repo/.beads/hooks`), so worktrees inherit parent hooks via
|
|
339
|
+
shared git config — no on-disk `.beads/` is needed inside a worktree. Since
|
|
340
|
+
`xtrm-cbjo` (xtrm-tools commit `937b151`) and `unitAI-yvqmf` (specialists commit
|
|
341
|
+
`986bc8e4`), `xt claude` / `xt pi` / `sp run` worktrees do **not** create a
|
|
342
|
+
`.beads/` symlink; they `rm -rf <worktree>/.beads` and `git update-index
|
|
343
|
+
--skip-worktree --` on tracked `.beads/*` paths. This eliminates the
|
|
344
|
+
squash-merge `.beads`-wipe hazard documented in projects/infra PR #39.
|
|
345
|
+
|
|
346
|
+
### Audit your `core.hooksPath` once (xtrm-2s44)
|
|
347
|
+
|
|
348
|
+
If your bd was installed before 1.0.3, `core.hooksPath` may be the relative
|
|
349
|
+
string `.beads/hooks`, which would resolve against a worktree's cwd — i.e.,
|
|
350
|
+
the (now-missing) worktree-local `.beads/hooks/`. To survey:
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
for r in ~/dev/*/ ~/projects/*/*/; do
|
|
354
|
+
[ -d "$r/.git" ] && [ -d "$r/.beads" ] || continue
|
|
355
|
+
hp=$(git -C "$r" config core.hooksPath 2>/dev/null || echo "<unset>")
|
|
356
|
+
case "$hp" in
|
|
357
|
+
/*) cat="ABSOLUTE" ;;
|
|
358
|
+
"<unset>") cat="UNSET" ;;
|
|
359
|
+
.beads/hooks) cat="RELATIVE-BD <- needs fix" ;;
|
|
360
|
+
*) cat="OTHER (project .githooks chain — leave alone)" ;;
|
|
361
|
+
esac
|
|
362
|
+
printf "%-50s %s\n" "${r#$HOME/} $cat" "$hp"
|
|
363
|
+
done
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Classification:
|
|
367
|
+
- `ABSOLUTE` — correct, no action.
|
|
368
|
+
- `RELATIVE-BD` (literal `.beads/hooks` or `./.beads/hooks`) — rewrite once:
|
|
369
|
+
```bash
|
|
370
|
+
git -C <repo> config core.hooksPath "$(realpath <repo>/.beads/hooks)"
|
|
371
|
+
```
|
|
372
|
+
- `OTHER` like `.githooks` — project-specific hook chain, leave alone. bd in
|
|
373
|
+
these repos works via direct invocation (not git hooks), so worktree hygiene
|
|
374
|
+
is unaffected.
|
|
375
|
+
- `UNSET` — no hooks wired anywhere; same outcome as `OTHER`.
|
|
376
|
+
|
|
377
|
+
Survey across `~/dev` + `~/projects/mercury` on 2026-05-12 returned **0 repos
|
|
378
|
+
needing the fix**. The safety net in `launchWorktreeSession` /
|
|
379
|
+
`provisionWorktree` (`normalizeParentHooksPath`) auto-rewrites on next worktree
|
|
380
|
+
creation if a relative `.beads/hooks` ever does appear, so the survey is mostly
|
|
381
|
+
defensive.
|
|
382
|
+
|
|
383
|
+
### Worktree-internal artifact inventory (xtrm-x80f)
|
|
384
|
+
|
|
385
|
+
A worktree is a partial clone with extras: bd metadata, npm caches, runtime
|
|
386
|
+
state, per-worktree settings. None of these belong on a chain branch — but
|
|
387
|
+
the moment any of them get staged via `git add -A` or a checkpoint commit,
|
|
388
|
+
they can ride a PR into `main`. The matrix below documents what is protected
|
|
389
|
+
by which mechanism. Audit it whenever you add a new per-worktree artifact.
|
|
390
|
+
|
|
391
|
+
| Artifact | Source | Mechanism in a worktree | Status |
|
|
392
|
+
|----------|--------|-------------------------|--------|
|
|
393
|
+
| `.beads/*` | bd tracked dir | rm + `skip-worktree` (xtrm-cbjo) | ✅ |
|
|
394
|
+
| `.beads-credential-key`, `.beads/dolt-monitor.pid`, `.beads/dolt-server.activity` | bd runtime | gitignored at parent | ✅ |
|
|
395
|
+
| `.pi/npm/` | npm cache | gitignored + symlink to parent | ✅ |
|
|
396
|
+
| `.pi/extensions/` | pi runtime | gitignored under `.xtrm/extensions/**/.pi/` | ✅ |
|
|
397
|
+
| `.specialists/default` | (xtrm-tools: untracked) | symlink to parent in worktree | ✅ |
|
|
398
|
+
| `.specialists/user` | tracked (.json overrides) | symlink to parent in worktree | ⚠️ merge-hazard candidate, tracked at follow-up bead |
|
|
399
|
+
| `.specialists/{jobs,ready,trace.jsonl,db/*}` | runtime state | gitignored at parent | ✅ |
|
|
400
|
+
| `.claude/skills` | install symlink | gitignored | ✅ |
|
|
401
|
+
| `.claude/settings.local.json` | per-worktree write (`launchWorktreeSession`) | gitignored (user-global + project) | ✅ |
|
|
402
|
+
| `.claude/worktrees/`, `.claude/tdd-guard/data/` | runtime | gitignored | ✅ |
|
|
403
|
+
| `.xtrm/worktrees/`, `.xtrm/skills/active/`, `.xtrm/session-meta.json`, `.xtrm/statusline-claim`, `.xtrm/debug.db` | runtime | gitignored | ✅ |
|
|
404
|
+
| `AGENTS.md`, `CLAUDE.md` | tracked | gitnexus stat-counter scrubbed (xtrm-c6sf), build-gate prevents reintroduction | ✅ |
|
|
405
|
+
| `pnpm-workspace.yaml`, `cli/pnpm-workspace.yaml` | generated by pnpm in an npm-workspaces repo when specialist tooling shells out to pnpm | gitignored (xtrm-ombq) | ✅ |
|
|
406
|
+
| `.gitnexus/` | runtime | gitignored | ✅ |
|
|
407
|
+
| `.dolt/`, `*.db` | runtime | gitignored | ✅ |
|
|
408
|
+
|
|
409
|
+
The remaining ⚠️ is `.specialists/user/*.json`: the symlink swap in
|
|
410
|
+
`ensureWorktreeSpecialists` has the same shape as the pre-fix `.beads`
|
|
411
|
+
problem — a chain-branch checkpoint could capture the dir→symlink delta and
|
|
412
|
+
squash-merge would wipe the parent's `.specialists/user/`. Lower urgency
|
|
413
|
+
than `.beads` (smaller blast radius, files are intentional overrides) but
|
|
414
|
+
worth resolving with the same skip-worktree pattern when convenient.
|
|
415
|
+
|
|
416
|
+
The defense-in-depth pre-push guard in `xt end`
|
|
417
|
+
(`findBeadsSymlinkIntroductions`) currently only checks `.beads/*`. Extend
|
|
418
|
+
to `.specialists/*` if/when the symlink swap there becomes the next chain
|
|
419
|
+
of work.
|
|
420
|
+
|
|
421
|
+
## Pre-Release Validation Methodology
|
|
422
|
+
|
|
423
|
+
Before publishing a new xtrm-tools version, validate the operator-facing CLI locally
|
|
424
|
+
against every consumer repo. This is the procedure that surfaced two release-blockers
|
|
425
|
+
in 2026-05-12 alone (`xtrm-16ec` yaml-null crash, `xtrm-ny61` worktree over-discovery).
|
|
426
|
+
|
|
427
|
+
### Procedure
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
# 1. Build dist from the local checkout
|
|
431
|
+
cd /path/to/xtrm-tools && npm run build --workspace cli
|
|
432
|
+
|
|
433
|
+
# 2. Link globally so `xt` runs local source
|
|
434
|
+
npm link
|
|
435
|
+
|
|
436
|
+
# 3. Sweep across all consumer trees (dry-run first)
|
|
437
|
+
xt update --root ~/dev
|
|
438
|
+
xt update --root ~/projects/mercury
|
|
439
|
+
|
|
440
|
+
# 4. Identify failed/incomplete rows. Fix any real bugs in xtrm-tools FIRST,
|
|
441
|
+
# then re-build + re-link + re-sweep.
|
|
442
|
+
|
|
443
|
+
# 5. Once dry-run is clean, apply across the fleet:
|
|
444
|
+
xt update --apply --root ~/dev
|
|
445
|
+
xt update --apply --root ~/projects/mercury
|
|
446
|
+
|
|
447
|
+
# 6. Cut the public release only after the local apply succeeds end-to-end.
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
### Why this beats publishing first and patching later
|
|
451
|
+
|
|
452
|
+
- A published `0.7.X` that crashes on a default-config consumer repo wastes a
|
|
453
|
+
version number — users see "upgrade and immediately break" and lose trust.
|
|
454
|
+
- Bugs that only manifest on real consumer state (comments-only YAML, transient
|
|
455
|
+
worktrees, drifted PACK metadata) are invisible from xtrm-tools' own test
|
|
456
|
+
suite — only a real sweep catches them.
|
|
457
|
+
- `npm link` flips between local-source-globally and published-version-globally
|
|
458
|
+
in seconds (`npm unlink` reverts), so the validation cost is minimal.
|
|
459
|
+
|
|
460
|
+
### Watch-fors during the sweep
|
|
461
|
+
|
|
462
|
+
- **Pi packages shown as `missing` when `npm ls -g` confirms them installed** —
|
|
463
|
+
detection bug, filed at `xtrm-ntf8`. Not a real problem; packages work.
|
|
464
|
+
- **xtrm-tools itself appearing as `failed` with "Source and destination..."** —
|
|
465
|
+
expected when xtrm-tools is npm-linked into itself; not a release blocker.
|
|
466
|
+
|
|
201
467
|
## Reporting to the user
|
|
202
468
|
|
|
203
469
|
After completing detection + remediation + verification, give the user a concise
|
|
@@ -28,6 +28,7 @@ from bootstrap import ( # noqa: E402
|
|
|
28
28
|
RootResolutionError,
|
|
29
29
|
find_service_for_path,
|
|
30
30
|
get_project_root,
|
|
31
|
+
get_registry_path,
|
|
31
32
|
get_service,
|
|
32
33
|
load_registry,
|
|
33
34
|
save_registry,
|
|
@@ -110,6 +111,22 @@ def check_drift_from_hook_stdin() -> None:
|
|
|
110
111
|
sys.exit(0)
|
|
111
112
|
|
|
112
113
|
|
|
114
|
+
def _print_missing_registry_hint(project_root: str | None = None) -> None:
|
|
115
|
+
if project_root is None:
|
|
116
|
+
try:
|
|
117
|
+
project_root = get_project_root()
|
|
118
|
+
except RootResolutionError:
|
|
119
|
+
project_root = "."
|
|
120
|
+
|
|
121
|
+
root = Path(project_root)
|
|
122
|
+
expected = (
|
|
123
|
+
f"Registry not found. Expected one of: {root / 'service-registry.json'}, "
|
|
124
|
+
f"{root / '.claude/skills/service-registry.json'}, "
|
|
125
|
+
f"{root / '.xtrm/skills/user/packs/*/service-registry.json'}"
|
|
126
|
+
)
|
|
127
|
+
print(expected, file=sys.stderr)
|
|
128
|
+
|
|
129
|
+
|
|
113
130
|
def update_sync_time(service_id: str, project_root: str | None = None) -> bool:
|
|
114
131
|
"""Update last_sync timestamp for a service in the registry."""
|
|
115
132
|
try:
|
|
@@ -140,6 +157,11 @@ def scan_drift(project_root: str | None = None) -> list[dict]:
|
|
|
140
157
|
return []
|
|
141
158
|
|
|
142
159
|
root = Path(project_root)
|
|
160
|
+
registry_path = get_registry_path(project_root)
|
|
161
|
+
if not registry_path.exists():
|
|
162
|
+
_print_missing_registry_hint(project_root)
|
|
163
|
+
return []
|
|
164
|
+
|
|
143
165
|
registry = load_registry(project_root)
|
|
144
166
|
drifted: list[dict] = []
|
|
145
167
|
|
|
@@ -9,7 +9,9 @@ description: >
|
|
|
9
9
|
output immediately, or when the work is a single LLM call with structured
|
|
10
10
|
input/output. Do NOT use for tracked agent work — that belongs to
|
|
11
11
|
`using-specialists-v2`.
|
|
12
|
-
version: 1.0
|
|
12
|
+
version: 1.1.0
|
|
13
|
+
updated: 2026-05-06
|
|
14
|
+
synced_at: a0e54d0c
|
|
13
15
|
---
|
|
14
16
|
|
|
15
17
|
# Script-Class Specialists
|
|
@@ -54,7 +56,7 @@ A spec is rejected at request time (`specialist_load_error`) if any of:
|
|
|
54
56
|
- `execution.interactive` is `true`
|
|
55
57
|
- `execution.requires_worktree` is `true`
|
|
56
58
|
- `execution.permission_required` is anything other than `READ_ONLY`
|
|
57
|
-
- `skills.scripts` is non-empty
|
|
59
|
+
- `skills.scripts` is non-empty (always rejected; no `--allow-local-scripts` bypass)
|
|
58
60
|
- `prompt.task_template` is missing
|
|
59
61
|
- a referenced `$var` in the chosen template is not supplied (`template_variable_missing`)
|
|
60
62
|
|
|
@@ -101,9 +103,9 @@ sp script <specialist-name> \
|
|
|
101
103
|
Behaviour:
|
|
102
104
|
|
|
103
105
|
- Loads the spec via `SpecialistLoader` (same loader as `sp run`).
|
|
104
|
-
- Renders `prompt.task_template` (or named template) with `--vars
|
|
105
|
-
-
|
|
106
|
-
|
|
106
|
+
- Renders `prompt.task_template` (or named template) with `--vars`, then feeds the rendered prompt via stdin.
|
|
107
|
+
- `--db-path /path/to/observability.db` is an exact SQLite file path; omit it to use the project default `.specialists/db/observability.db`.
|
|
108
|
+
- Spawns `pi` in JSON mode with no session, no extensions, no tools, and offline; forwards the resolved model, optional `--thinking`, and `--system-prompt` when `prompt.system` is set (full override, not append).
|
|
107
109
|
- Returns the final assistant text on stdout. With `--json`, returns the full
|
|
108
110
|
`ScriptGenerateResult` envelope.
|
|
109
111
|
- Writes one row to `.specialists/db/observability.db` (same writer as `sp run`).
|