xtrm-tools 2.4.1 → 2.4.3

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.
Files changed (126) hide show
  1. package/README.md +15 -6
  2. package/cli/dist/index.cjs +738 -239
  3. package/cli/dist/index.cjs.map +1 -1
  4. package/cli/package.json +1 -1
  5. package/config/hooks.json +10 -0
  6. package/config/pi/extensions/core/adapter.ts +2 -14
  7. package/config/pi/extensions/core/guard-rules.ts +70 -0
  8. package/config/pi/extensions/core/session-state.ts +59 -0
  9. package/config/pi/extensions/main-guard.ts +10 -14
  10. package/config/pi/extensions/plan-mode/README.md +65 -0
  11. package/config/pi/extensions/plan-mode/index.ts +340 -0
  12. package/config/pi/extensions/plan-mode/utils.ts +168 -0
  13. package/config/pi/extensions/service-skills.ts +51 -7
  14. package/config/pi/extensions/session-flow.ts +117 -0
  15. package/hooks/beads-claim-sync.mjs +140 -14
  16. package/hooks/beads-compact-restore.mjs +41 -9
  17. package/hooks/beads-compact-save.mjs +36 -5
  18. package/hooks/beads-gate-messages.mjs +27 -1
  19. package/hooks/beads-memory-gate.mjs +24 -16
  20. package/hooks/beads-stop-gate.mjs +58 -8
  21. package/hooks/guard-rules.mjs +117 -0
  22. package/hooks/hooks.json +28 -18
  23. package/hooks/main-guard.mjs +22 -22
  24. package/hooks/quality-check.cjs +1286 -0
  25. package/hooks/quality-check.py +345 -0
  26. package/hooks/session-state.mjs +138 -0
  27. package/package.json +2 -1
  28. package/project-skills/quality-gates/.claude/settings.json +1 -24
  29. package/skills/creating-service-skills/SKILL.md +433 -0
  30. package/skills/creating-service-skills/references/script_quality_standards.md +425 -0
  31. package/skills/creating-service-skills/references/service_skill_system_guide.md +278 -0
  32. package/skills/creating-service-skills/scripts/bootstrap.py +326 -0
  33. package/skills/creating-service-skills/scripts/deep_dive.py +304 -0
  34. package/skills/creating-service-skills/scripts/scaffolder.py +482 -0
  35. package/skills/scoping-service-skills/SKILL.md +231 -0
  36. package/skills/scoping-service-skills/scripts/scope.py +74 -0
  37. package/skills/sync-docs/SKILL.md +235 -0
  38. package/skills/sync-docs/evals/evals.json +89 -0
  39. package/skills/sync-docs/references/doc-structure.md +104 -0
  40. package/skills/sync-docs/references/schema.md +103 -0
  41. package/skills/sync-docs/scripts/context_gatherer.py +246 -0
  42. package/skills/sync-docs/scripts/doc_structure_analyzer.py +495 -0
  43. package/skills/sync-docs/scripts/validate_doc.py +365 -0
  44. package/skills/sync-docs-workspace/iteration-1/benchmark.json +293 -0
  45. package/skills/sync-docs-workspace/iteration-1/benchmark.md +13 -0
  46. package/skills/sync-docs-workspace/iteration-1/eval-doc-audit/eval_metadata.json +27 -0
  47. package/skills/sync-docs-workspace/iteration-1/eval-doc-audit/with_skill/outputs/result.md +210 -0
  48. package/skills/sync-docs-workspace/iteration-1/eval-doc-audit/with_skill/run-1/grading.json +28 -0
  49. package/skills/sync-docs-workspace/iteration-1/eval-doc-audit/with_skill/run-1/timing.json +1 -0
  50. package/skills/sync-docs-workspace/iteration-1/eval-doc-audit/without_skill/outputs/result.md +101 -0
  51. package/skills/sync-docs-workspace/iteration-1/eval-doc-audit/without_skill/run-1/grading.json +28 -0
  52. package/skills/sync-docs-workspace/iteration-1/eval-doc-audit/without_skill/run-1/timing.json +5 -0
  53. package/skills/sync-docs-workspace/iteration-1/eval-doc-audit/without_skill/timing.json +5 -0
  54. package/skills/sync-docs-workspace/iteration-1/eval-fix-mode/eval_metadata.json +27 -0
  55. package/skills/sync-docs-workspace/iteration-1/eval-fix-mode/with_skill/outputs/result.md +198 -0
  56. package/skills/sync-docs-workspace/iteration-1/eval-fix-mode/with_skill/run-1/grading.json +28 -0
  57. package/skills/sync-docs-workspace/iteration-1/eval-fix-mode/with_skill/run-1/timing.json +1 -0
  58. package/skills/sync-docs-workspace/iteration-1/eval-fix-mode/without_skill/outputs/result.md +94 -0
  59. package/skills/sync-docs-workspace/iteration-1/eval-fix-mode/without_skill/run-1/grading.json +28 -0
  60. package/skills/sync-docs-workspace/iteration-1/eval-fix-mode/without_skill/run-1/timing.json +1 -0
  61. package/skills/sync-docs-workspace/iteration-1/eval-sprint-closeout/eval_metadata.json +27 -0
  62. package/skills/sync-docs-workspace/iteration-1/eval-sprint-closeout/with_skill/outputs/result.md +237 -0
  63. package/skills/sync-docs-workspace/iteration-1/eval-sprint-closeout/with_skill/run-1/grading.json +28 -0
  64. package/skills/sync-docs-workspace/iteration-1/eval-sprint-closeout/with_skill/run-1/timing.json +1 -0
  65. package/skills/sync-docs-workspace/iteration-1/eval-sprint-closeout/without_skill/outputs/result.md +134 -0
  66. package/skills/sync-docs-workspace/iteration-1/eval-sprint-closeout/without_skill/run-1/grading.json +28 -0
  67. package/skills/sync-docs-workspace/iteration-1/eval-sprint-closeout/without_skill/run-1/timing.json +1 -0
  68. package/skills/sync-docs-workspace/iteration-2/benchmark.json +297 -0
  69. package/skills/sync-docs-workspace/iteration-2/benchmark.md +13 -0
  70. package/skills/sync-docs-workspace/iteration-2/eval-doc-audit/eval_metadata.json +27 -0
  71. package/skills/sync-docs-workspace/iteration-2/eval-doc-audit/with_skill/outputs/result.md +137 -0
  72. package/skills/sync-docs-workspace/iteration-2/eval-doc-audit/with_skill/run-1/grading.json +92 -0
  73. package/skills/sync-docs-workspace/iteration-2/eval-doc-audit/with_skill/run-1/timing.json +1 -0
  74. package/skills/sync-docs-workspace/iteration-2/eval-doc-audit/without_skill/outputs/result.md +134 -0
  75. package/skills/sync-docs-workspace/iteration-2/eval-doc-audit/without_skill/run-1/grading.json +86 -0
  76. package/skills/sync-docs-workspace/iteration-2/eval-doc-audit/without_skill/run-1/timing.json +1 -0
  77. package/skills/sync-docs-workspace/iteration-2/eval-fix-mode/eval_metadata.json +27 -0
  78. package/skills/sync-docs-workspace/iteration-2/eval-fix-mode/with_skill/outputs/result.md +193 -0
  79. package/skills/sync-docs-workspace/iteration-2/eval-fix-mode/with_skill/run-1/grading.json +72 -0
  80. package/skills/sync-docs-workspace/iteration-2/eval-fix-mode/with_skill/run-1/timing.json +1 -0
  81. package/skills/sync-docs-workspace/iteration-2/eval-fix-mode/without_skill/outputs/result.md +211 -0
  82. package/skills/sync-docs-workspace/iteration-2/eval-fix-mode/without_skill/run-1/grading.json +91 -0
  83. package/skills/sync-docs-workspace/iteration-2/eval-fix-mode/without_skill/run-1/timing.json +5 -0
  84. package/skills/sync-docs-workspace/iteration-2/eval-sprint-closeout/eval_metadata.json +27 -0
  85. package/skills/sync-docs-workspace/iteration-2/eval-sprint-closeout/with_skill/outputs/result.md +182 -0
  86. package/skills/sync-docs-workspace/iteration-2/eval-sprint-closeout/with_skill/run-1/grading.json +95 -0
  87. package/skills/sync-docs-workspace/iteration-2/eval-sprint-closeout/with_skill/run-1/timing.json +1 -0
  88. package/skills/sync-docs-workspace/iteration-2/eval-sprint-closeout/without_skill/outputs/result.md +222 -0
  89. package/skills/sync-docs-workspace/iteration-2/eval-sprint-closeout/without_skill/run-1/grading.json +88 -0
  90. package/skills/sync-docs-workspace/iteration-2/eval-sprint-closeout/without_skill/run-1/timing.json +5 -0
  91. package/skills/sync-docs-workspace/iteration-3/benchmark.json +298 -0
  92. package/skills/sync-docs-workspace/iteration-3/benchmark.md +13 -0
  93. package/skills/sync-docs-workspace/iteration-3/eval-doc-audit/eval_metadata.json +27 -0
  94. package/skills/sync-docs-workspace/iteration-3/eval-doc-audit/with_skill/outputs/result.md +125 -0
  95. package/skills/sync-docs-workspace/iteration-3/eval-doc-audit/with_skill/run-1/grading.json +97 -0
  96. package/skills/sync-docs-workspace/iteration-3/eval-doc-audit/with_skill/run-1/timing.json +5 -0
  97. package/skills/sync-docs-workspace/iteration-3/eval-doc-audit/without_skill/outputs/result.md +144 -0
  98. package/skills/sync-docs-workspace/iteration-3/eval-doc-audit/without_skill/run-1/grading.json +78 -0
  99. package/skills/sync-docs-workspace/iteration-3/eval-doc-audit/without_skill/run-1/timing.json +5 -0
  100. package/skills/sync-docs-workspace/iteration-3/eval-fix-mode/eval_metadata.json +27 -0
  101. package/skills/sync-docs-workspace/iteration-3/eval-fix-mode/with_skill/outputs/result.md +104 -0
  102. package/skills/sync-docs-workspace/iteration-3/eval-fix-mode/with_skill/run-1/grading.json +91 -0
  103. package/skills/sync-docs-workspace/iteration-3/eval-fix-mode/with_skill/run-1/timing.json +5 -0
  104. package/skills/sync-docs-workspace/iteration-3/eval-fix-mode/without_skill/outputs/result.md +79 -0
  105. package/skills/sync-docs-workspace/iteration-3/eval-fix-mode/without_skill/run-1/grading.json +82 -0
  106. package/skills/sync-docs-workspace/iteration-3/eval-fix-mode/without_skill/run-1/timing.json +5 -0
  107. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/eval_metadata.json +27 -0
  108. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/with_skill/outputs/phase1_context.json +302 -0
  109. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/with_skill/outputs/phase2_drift.txt +33 -0
  110. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/with_skill/outputs/phase3_analysis.json +114 -0
  111. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/with_skill/outputs/phase4_fix.txt +118 -0
  112. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/with_skill/outputs/phase5_validate.txt +38 -0
  113. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/with_skill/outputs/result.md +158 -0
  114. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/with_skill/run-1/grading.json +95 -0
  115. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/with_skill/run-1/timing.json +5 -0
  116. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/without_skill/outputs/result.md +71 -0
  117. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/without_skill/run-1/grading.json +90 -0
  118. package/skills/sync-docs-workspace/iteration-3/eval-sprint-closeout/without_skill/run-1/timing.json +5 -0
  119. package/skills/updating-service-skills/SKILL.md +136 -0
  120. package/skills/updating-service-skills/scripts/drift_detector.py +222 -0
  121. package/skills/using-quality-gates/SKILL.md +254 -0
  122. package/skills/using-service-skills/SKILL.md +108 -0
  123. package/skills/using-service-skills/scripts/cataloger.py +74 -0
  124. package/skills/using-service-skills/scripts/skill_activator.py +152 -0
  125. package/skills/using-service-skills/scripts/test_skill_activator.py +58 -0
  126. package/skills/using-xtrm/SKILL.md +34 -38
@@ -0,0 +1,246 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Gather project context for documentation sync.
4
+
5
+ Collects:
6
+ - Recently closed bd issues (if .beads/ exists)
7
+ - Recently merged PRs (via git log)
8
+ - bd memories persisted this cycle (bd kv list)
9
+ - Stale Serena memories (via drift_detector.py)
10
+
11
+ Outputs JSON to stdout. Safe to run in any project — degrades gracefully
12
+ when bd or Serena are unavailable.
13
+
14
+ Usage:
15
+ context_gatherer.py [--since=30]
16
+
17
+ --since=N Look back N commits for git context (default: 30)
18
+ """
19
+
20
+ import sys
21
+ import json
22
+ import subprocess
23
+ import time
24
+ from pathlib import Path
25
+ from datetime import datetime, timezone
26
+
27
+
28
+ def run(cmd: list, cwd: str | None = None, timeout: int = 8) -> str | None:
29
+ """Run a command, return stdout or None on failure."""
30
+ try:
31
+ result = subprocess.run(
32
+ cmd, cwd=cwd, capture_output=True, text=True, timeout=timeout
33
+ )
34
+ if result.returncode == 0:
35
+ return result.stdout.strip()
36
+ return None
37
+ except Exception:
38
+ return None
39
+
40
+
41
+ def find_project_root() -> Path:
42
+ """Walk up from cwd looking for .git."""
43
+ p = Path.cwd()
44
+ for parent in [p, *p.parents]:
45
+ if (parent / ".git").exists():
46
+ return parent
47
+ return p
48
+
49
+
50
+ def find_main_repo_root(root: Path) -> Path:
51
+ """For git worktrees, resolve the main repo root from the .git file.
52
+
53
+ In a worktree, .git is a file: "gitdir: /path/to/main/.git/worktrees/<name>"
54
+ Navigate up two levels to reach the main .git, then one more for the repo root.
55
+ """
56
+ git_path = root / ".git"
57
+ if git_path.is_file():
58
+ content = git_path.read_text(encoding="utf-8").strip()
59
+ if content.startswith("gitdir:"):
60
+ worktree_git = Path(content[len("gitdir:"):].strip())
61
+ # worktree_git = /path/to/main/.git/worktrees/<name>
62
+ # main .git = /path/to/main/.git (two levels up)
63
+ # main repo = /path/to/main (one more level)
64
+ main_git = worktree_git.parent.parent
65
+ return main_git.parent
66
+ return root
67
+
68
+
69
+ def ensure_dolt_server(cwd: str) -> bool:
70
+ """Ensure the Dolt server is running. Start it if not. Returns True if ready."""
71
+ # Quick connection test
72
+ test = run(["bd", "dolt", "test"], cwd=cwd, timeout=5)
73
+ if test is not None:
74
+ return True
75
+
76
+ # Server not responding — try to start it
77
+ try:
78
+ subprocess.run(
79
+ ["bd", "dolt", "start"],
80
+ cwd=cwd, capture_output=True, text=True, timeout=15
81
+ )
82
+ except Exception:
83
+ return False
84
+
85
+ # Wait up to 6 seconds for it to become ready
86
+ for _ in range(6):
87
+ time.sleep(1)
88
+ if run(["bd", "dolt", "test"], cwd=cwd, timeout=3) is not None:
89
+ return True
90
+
91
+ return False
92
+
93
+
94
+ def has_beads(root: Path) -> bool:
95
+ return (root / ".beads").exists()
96
+
97
+
98
+ def gather_bd_closed(cwd: str) -> list[dict]:
99
+ """Get recently closed bd issues."""
100
+ out = run(["bd", "list", "--status=closed"], cwd=cwd)
101
+ if not out:
102
+ return []
103
+
104
+ issues = []
105
+ for line in out.splitlines():
106
+ line = line.strip()
107
+ # bd list output: "✓ <id> ● <priority> <title>"
108
+ if line.startswith("✓") or "closed" in line.lower():
109
+ parts = line.lstrip("✓ ").split()
110
+ if len(parts) >= 2:
111
+ issue_id = parts[0]
112
+ title_start = 2 if len(parts) > 2 and parts[1].startswith("P") else 1
113
+ title = " ".join(parts[title_start:])
114
+ issues.append({"id": issue_id, "title": title})
115
+
116
+ return issues[:20]
117
+
118
+
119
+ def gather_bd_memories(cwd: str) -> list[dict]:
120
+ """Read bd memories via bd kv list, filtering memory.* keys."""
121
+ out = run(["bd", "kv", "list"], cwd=cwd)
122
+ if not out:
123
+ return []
124
+
125
+ memories = []
126
+ for line in out.splitlines():
127
+ stripped = line.strip()
128
+ if not stripped.startswith("memory."):
129
+ continue
130
+ if " = " in stripped:
131
+ key, _, value = stripped.partition(" = ")
132
+ memories.append({"key": key.strip(), "value": value.strip()})
133
+ else:
134
+ memories.append({"key": stripped, "value": ""})
135
+
136
+ return memories[:20]
137
+
138
+
139
+ def gather_merged_prs(root: Path, since_n: int) -> list[dict]:
140
+ """Get merged PRs from git log."""
141
+ out = run(
142
+ ["git", "log", f"-{since_n}", "--merges", "--oneline", "--format=%H|%s|%ci"],
143
+ cwd=str(root),
144
+ )
145
+ if not out:
146
+ return []
147
+
148
+ prs = []
149
+ for line in out.splitlines():
150
+ parts = line.split("|", 2)
151
+ if len(parts) == 3:
152
+ sha, subject, date = parts
153
+ prs.append({"sha": sha[:8], "subject": subject.strip(), "date": date.strip()})
154
+ return prs[:10]
155
+
156
+
157
+ def gather_recent_commits(root: Path, since_n: int) -> list[dict]:
158
+ """Get recent non-merge commits for context."""
159
+ out = run(
160
+ ["git", "log", f"-{since_n}", "--no-merges", "--oneline", "--format=%H|%s|%ci"],
161
+ cwd=str(root),
162
+ )
163
+ if not out:
164
+ return []
165
+
166
+ commits = []
167
+ for line in out.splitlines():
168
+ parts = line.split("|", 2)
169
+ if len(parts) == 3:
170
+ sha, subject, date = parts
171
+ commits.append({"sha": sha[:8], "subject": subject.strip(), "date": date.strip()})
172
+ return commits[:15]
173
+
174
+
175
+ def gather_serena_drift(root: Path) -> dict:
176
+ """Run drift_detector.py and capture its output."""
177
+ candidates = [
178
+ Path.home() / ".claude/skills/documenting/scripts/drift_detector.py",
179
+ root / "skills/documenting/scripts/drift_detector.py",
180
+ ]
181
+ detector = next((p for p in candidates if p.exists()), None)
182
+ if not detector:
183
+ return {"available": False, "stale": []}
184
+
185
+ out = run([sys.executable, str(detector), "scan"], cwd=str(root))
186
+ if out is None:
187
+ return {"available": False, "stale": []}
188
+
189
+ stale = []
190
+ for line in out.splitlines():
191
+ if line.strip().startswith(tuple("abcdefghijklmnopqrstuvwxyz_")):
192
+ name = line.strip()
193
+ if name and not name.startswith("Modified:") and not name.startswith("Last"):
194
+ stale.append(name)
195
+
196
+ return {
197
+ "available": True,
198
+ "stale": stale,
199
+ "raw": out,
200
+ }
201
+
202
+
203
+ def main() -> None:
204
+ since_n = 30
205
+ for arg in sys.argv[1:]:
206
+ if arg.startswith("--since="):
207
+ try:
208
+ since_n = int(arg.split("=", 1)[1])
209
+ except ValueError:
210
+ pass
211
+
212
+ root = find_project_root()
213
+ # In a git worktree, bd needs the main repo's .beads/ — resolve it
214
+ main_root = find_main_repo_root(root)
215
+ bd_cwd = str(main_root)
216
+ bd_available = has_beads(main_root)
217
+
218
+ dolt_ready = False
219
+ dolt_warning: str | None = None
220
+ if bd_available:
221
+ dolt_ready = ensure_dolt_server(bd_cwd)
222
+ if not dolt_ready:
223
+ dolt_warning = (
224
+ "Dolt server could not be started — bd data unavailable. "
225
+ "Run 'bd dolt start' manually from the project root and retry."
226
+ )
227
+
228
+ report = {
229
+ "generated_at": datetime.now(timezone.utc).isoformat(),
230
+ "project_root": str(root),
231
+ "bd_available": bd_available,
232
+ "bd_closed_issues": gather_bd_closed(bd_cwd) if dolt_ready else [],
233
+ "bd_memories": gather_bd_memories(bd_cwd) if dolt_ready else [],
234
+ "merged_prs": gather_merged_prs(root, since_n),
235
+ "recent_commits": gather_recent_commits(root, since_n),
236
+ "serena_drift": gather_serena_drift(root),
237
+ }
238
+
239
+ if dolt_warning:
240
+ report["warnings"] = [dolt_warning]
241
+
242
+ print(json.dumps(report, indent=2))
243
+
244
+
245
+ if __name__ == "__main__":
246
+ main()