xtctx 0.3.2 → 0.11.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 (308) hide show
  1. package/CHANGELOG.md +317 -122
  2. package/LICENSE +21 -21
  3. package/README.md +154 -121
  4. package/dist/src/cli/disconnect.d.ts +8 -0
  5. package/dist/src/cli/disconnect.d.ts.map +1 -0
  6. package/dist/src/cli/disconnect.js +42 -0
  7. package/dist/src/cli/disconnect.js.map +1 -0
  8. package/dist/src/cli/hook.d.ts +7 -0
  9. package/dist/src/cli/hook.d.ts.map +1 -0
  10. package/dist/src/cli/hook.js +32 -0
  11. package/dist/src/cli/hook.js.map +1 -0
  12. package/dist/src/cli/index.d.ts.map +1 -1
  13. package/dist/src/cli/index.js +86 -33
  14. package/dist/src/cli/index.js.map +1 -1
  15. package/dist/src/cli/setup.d.ts +8 -0
  16. package/dist/src/cli/setup.d.ts.map +1 -0
  17. package/dist/src/cli/setup.js +76 -0
  18. package/dist/src/cli/setup.js.map +1 -0
  19. package/dist/src/cli/status.d.ts +7 -0
  20. package/dist/src/cli/status.d.ts.map +1 -0
  21. package/dist/src/cli/status.js +133 -0
  22. package/dist/src/cli/status.js.map +1 -0
  23. package/dist/src/config/disconnect.d.ts +34 -0
  24. package/dist/src/config/disconnect.d.ts.map +1 -0
  25. package/dist/src/config/disconnect.js +386 -0
  26. package/dist/src/config/disconnect.js.map +1 -0
  27. package/dist/src/config/managed-block.d.ts +29 -0
  28. package/dist/src/config/managed-block.d.ts.map +1 -0
  29. package/dist/src/config/managed-block.js +67 -0
  30. package/dist/src/config/managed-block.js.map +1 -0
  31. package/dist/src/config/mcp-config.d.ts +93 -0
  32. package/dist/src/config/mcp-config.d.ts.map +1 -0
  33. package/dist/src/config/mcp-config.js +556 -0
  34. package/dist/src/config/mcp-config.js.map +1 -0
  35. package/dist/src/config/setup.d.ts +39 -0
  36. package/dist/src/config/setup.d.ts.map +1 -0
  37. package/dist/src/config/setup.js +379 -0
  38. package/dist/src/config/setup.js.map +1 -0
  39. package/dist/src/config/skills.d.ts +73 -0
  40. package/dist/src/config/skills.d.ts.map +1 -0
  41. package/dist/src/config/skills.js +427 -0
  42. package/dist/src/config/skills.js.map +1 -0
  43. package/dist/src/handoff/embeddings.d.ts +26 -0
  44. package/dist/src/handoff/embeddings.d.ts.map +1 -0
  45. package/dist/src/handoff/embeddings.js +132 -0
  46. package/dist/src/handoff/embeddings.js.map +1 -0
  47. package/dist/src/handoff/sqlite-index.d.ts +52 -0
  48. package/dist/src/handoff/sqlite-index.d.ts.map +1 -0
  49. package/dist/src/handoff/sqlite-index.js +905 -0
  50. package/dist/src/handoff/sqlite-index.js.map +1 -0
  51. package/dist/src/handoff/types.d.ts +63 -0
  52. package/dist/src/handoff/types.d.ts.map +1 -0
  53. package/dist/src/handoff/types.js +2 -0
  54. package/dist/src/handoff/types.js.map +1 -0
  55. package/dist/src/handoff/vector.d.ts +4 -0
  56. package/dist/src/handoff/vector.d.ts.map +1 -0
  57. package/dist/src/handoff/vector.js +31 -0
  58. package/dist/src/handoff/vector.js.map +1 -0
  59. package/dist/src/mcp/server.d.ts +3 -20
  60. package/dist/src/mcp/server.d.ts.map +1 -1
  61. package/dist/src/mcp/server.js +90 -205
  62. package/dist/src/mcp/server.js.map +1 -1
  63. package/dist/src/mcp/tools/continuity.d.ts +21 -0
  64. package/dist/src/mcp/tools/continuity.d.ts.map +1 -0
  65. package/dist/src/mcp/tools/continuity.js +55 -0
  66. package/dist/src/mcp/tools/continuity.js.map +1 -0
  67. package/dist/src/mcp/tools/manifest.d.ts +40 -0
  68. package/dist/src/mcp/tools/manifest.d.ts.map +1 -0
  69. package/dist/src/mcp/tools/manifest.js +105 -0
  70. package/dist/src/mcp/tools/manifest.js.map +1 -0
  71. package/dist/src/mcp/tools/sessions.d.ts +18 -31
  72. package/dist/src/mcp/tools/sessions.d.ts.map +1 -1
  73. package/dist/src/mcp/tools/sessions.js +109 -16
  74. package/dist/src/mcp/tools/sessions.js.map +1 -1
  75. package/dist/src/runtime/services.d.ts +11 -38
  76. package/dist/src/runtime/services.d.ts.map +1 -1
  77. package/dist/src/runtime/services.js +61 -200
  78. package/dist/src/runtime/services.js.map +1 -1
  79. package/dist/src/scrapers/antigravity.d.ts +52 -0
  80. package/dist/src/scrapers/antigravity.d.ts.map +1 -0
  81. package/dist/src/scrapers/antigravity.js +869 -0
  82. package/dist/src/scrapers/antigravity.js.map +1 -0
  83. package/dist/src/scrapers/base.d.ts +53 -1
  84. package/dist/src/scrapers/base.d.ts.map +1 -1
  85. package/dist/src/scrapers/base.js +93 -4
  86. package/dist/src/scrapers/base.js.map +1 -1
  87. package/dist/src/scrapers/claude-code.d.ts +37 -6
  88. package/dist/src/scrapers/claude-code.d.ts.map +1 -1
  89. package/dist/src/scrapers/claude-code.js +214 -40
  90. package/dist/src/scrapers/claude-code.js.map +1 -1
  91. package/dist/src/scrapers/codex.d.ts +36 -6
  92. package/dist/src/scrapers/codex.d.ts.map +1 -1
  93. package/dist/src/scrapers/codex.js +283 -60
  94. package/dist/src/scrapers/codex.js.map +1 -1
  95. package/dist/src/scrapers/copilot-cli.d.ts +34 -0
  96. package/dist/src/scrapers/copilot-cli.d.ts.map +1 -0
  97. package/dist/src/scrapers/copilot-cli.js +342 -0
  98. package/dist/src/scrapers/copilot-cli.js.map +1 -0
  99. package/dist/src/scrapers/copilot.d.ts +41 -8
  100. package/dist/src/scrapers/copilot.d.ts.map +1 -1
  101. package/dist/src/scrapers/copilot.js +256 -118
  102. package/dist/src/scrapers/copilot.js.map +1 -1
  103. package/dist/src/scrapers/cursor.d.ts +24 -9
  104. package/dist/src/scrapers/cursor.d.ts.map +1 -1
  105. package/dist/src/scrapers/cursor.js +259 -152
  106. package/dist/src/scrapers/cursor.js.map +1 -1
  107. package/dist/src/scrapers/index.d.ts +23 -0
  108. package/dist/src/scrapers/index.d.ts.map +1 -0
  109. package/dist/src/scrapers/index.js +22 -0
  110. package/dist/src/scrapers/index.js.map +1 -0
  111. package/dist/src/scrapers/opencode.d.ts +37 -0
  112. package/dist/src/scrapers/opencode.d.ts.map +1 -0
  113. package/dist/src/scrapers/opencode.js +277 -0
  114. package/dist/src/scrapers/opencode.js.map +1 -0
  115. package/dist/src/scrapers/registry.d.ts +1 -0
  116. package/dist/src/scrapers/registry.d.ts.map +1 -1
  117. package/dist/src/scrapers/registry.js +3 -0
  118. package/dist/src/scrapers/registry.js.map +1 -1
  119. package/dist/src/tools/sources.d.ts +28 -0
  120. package/dist/src/tools/sources.d.ts.map +1 -0
  121. package/dist/src/tools/sources.js +149 -0
  122. package/dist/src/tools/sources.js.map +1 -0
  123. package/dist/src/types/config.d.ts +24 -37
  124. package/dist/src/types/config.d.ts.map +1 -1
  125. package/dist/src/types/index.d.ts +0 -2
  126. package/dist/src/types/index.d.ts.map +1 -1
  127. package/dist/src/types/index.js +0 -2
  128. package/dist/src/types/index.js.map +1 -1
  129. package/dist/src/types/scraper.d.ts +34 -5
  130. package/dist/src/types/scraper.d.ts.map +1 -1
  131. package/dist/src/utils/atomic-file.d.ts +8 -0
  132. package/dist/src/utils/atomic-file.d.ts.map +1 -0
  133. package/dist/src/utils/atomic-file.js +15 -0
  134. package/dist/src/utils/atomic-file.js.map +1 -0
  135. package/dist/src/utils/errors.d.ts +7 -0
  136. package/dist/src/utils/errors.d.ts.map +1 -0
  137. package/dist/src/utils/errors.js +16 -0
  138. package/dist/src/utils/errors.js.map +1 -0
  139. package/dist/src/utils/package-info.d.ts +11 -0
  140. package/dist/src/utils/package-info.d.ts.map +1 -0
  141. package/dist/src/utils/package-info.js +30 -0
  142. package/dist/src/utils/package-info.js.map +1 -0
  143. package/dist/src/utils/project-scope.d.ts +19 -0
  144. package/dist/src/utils/project-scope.d.ts.map +1 -0
  145. package/dist/src/utils/project-scope.js +61 -0
  146. package/dist/src/utils/project-scope.js.map +1 -0
  147. package/package.json +70 -66
  148. package/dist/src/api/routes/config.d.ts +0 -4
  149. package/dist/src/api/routes/config.d.ts.map +0 -1
  150. package/dist/src/api/routes/config.js +0 -61
  151. package/dist/src/api/routes/config.js.map +0 -1
  152. package/dist/src/api/routes/knowledge.d.ts +0 -4
  153. package/dist/src/api/routes/knowledge.d.ts.map +0 -1
  154. package/dist/src/api/routes/knowledge.js +0 -40
  155. package/dist/src/api/routes/knowledge.js.map +0 -1
  156. package/dist/src/api/routes/search.d.ts +0 -7
  157. package/dist/src/api/routes/search.d.ts.map +0 -1
  158. package/dist/src/api/routes/search.js +0 -108
  159. package/dist/src/api/routes/search.js.map +0 -1
  160. package/dist/src/api/routes/sources.d.ts +0 -12
  161. package/dist/src/api/routes/sources.d.ts.map +0 -1
  162. package/dist/src/api/routes/sources.js +0 -70
  163. package/dist/src/api/routes/sources.js.map +0 -1
  164. package/dist/src/api/server.d.ts +0 -26
  165. package/dist/src/api/server.d.ts.map +0 -1
  166. package/dist/src/api/server.js +0 -312
  167. package/dist/src/api/server.js.map +0 -1
  168. package/dist/src/cli/ingest.d.ts +0 -6
  169. package/dist/src/cli/ingest.d.ts.map +0 -1
  170. package/dist/src/cli/ingest.js +0 -11
  171. package/dist/src/cli/ingest.js.map +0 -1
  172. package/dist/src/cli/init.d.ts +0 -6
  173. package/dist/src/cli/init.d.ts.map +0 -1
  174. package/dist/src/cli/init.js +0 -105
  175. package/dist/src/cli/init.js.map +0 -1
  176. package/dist/src/cli/serve.d.ts +0 -7
  177. package/dist/src/cli/serve.d.ts.map +0 -1
  178. package/dist/src/cli/serve.js +0 -100
  179. package/dist/src/cli/serve.js.map +0 -1
  180. package/dist/src/cli/sync.d.ts +0 -5
  181. package/dist/src/cli/sync.d.ts.map +0 -1
  182. package/dist/src/cli/sync.js +0 -13
  183. package/dist/src/cli/sync.js.map +0 -1
  184. package/dist/src/compaction/pipeline.d.ts +0 -19
  185. package/dist/src/compaction/pipeline.d.ts.map +0 -1
  186. package/dist/src/compaction/pipeline.js +0 -37
  187. package/dist/src/compaction/pipeline.js.map +0 -1
  188. package/dist/src/compaction/providers/cli.d.ts +0 -13
  189. package/dist/src/compaction/providers/cli.d.ts.map +0 -1
  190. package/dist/src/compaction/providers/cli.js +0 -60
  191. package/dist/src/compaction/providers/cli.js.map +0 -1
  192. package/dist/src/compaction/rule-based.d.ts +0 -8
  193. package/dist/src/compaction/rule-based.d.ts.map +0 -1
  194. package/dist/src/compaction/rule-based.js +0 -120
  195. package/dist/src/compaction/rule-based.js.map +0 -1
  196. package/dist/src/config/loader.d.ts +0 -12
  197. package/dist/src/config/loader.d.ts.map +0 -1
  198. package/dist/src/config/loader.js +0 -76
  199. package/dist/src/config/loader.js.map +0 -1
  200. package/dist/src/config/mappings/claude-code.d.ts +0 -3
  201. package/dist/src/config/mappings/claude-code.d.ts.map +0 -1
  202. package/dist/src/config/mappings/claude-code.js +0 -31
  203. package/dist/src/config/mappings/claude-code.js.map +0 -1
  204. package/dist/src/config/mappings/codex.d.ts +0 -3
  205. package/dist/src/config/mappings/codex.d.ts.map +0 -1
  206. package/dist/src/config/mappings/codex.js +0 -31
  207. package/dist/src/config/mappings/codex.js.map +0 -1
  208. package/dist/src/config/mappings/copilot.d.ts +0 -3
  209. package/dist/src/config/mappings/copilot.d.ts.map +0 -1
  210. package/dist/src/config/mappings/copilot.js +0 -33
  211. package/dist/src/config/mappings/copilot.js.map +0 -1
  212. package/dist/src/config/mappings/cursor.d.ts +0 -3
  213. package/dist/src/config/mappings/cursor.d.ts.map +0 -1
  214. package/dist/src/config/mappings/cursor.js +0 -29
  215. package/dist/src/config/mappings/cursor.js.map +0 -1
  216. package/dist/src/config/sync.d.ts +0 -13
  217. package/dist/src/config/sync.d.ts.map +0 -1
  218. package/dist/src/config/sync.js +0 -96
  219. package/dist/src/config/sync.js.map +0 -1
  220. package/dist/src/ingestion/coordinator.d.ts +0 -37
  221. package/dist/src/ingestion/coordinator.d.ts.map +0 -1
  222. package/dist/src/ingestion/coordinator.js +0 -127
  223. package/dist/src/ingestion/coordinator.js.map +0 -1
  224. package/dist/src/ingestion/daemon.d.ts +0 -17
  225. package/dist/src/ingestion/daemon.d.ts.map +0 -1
  226. package/dist/src/ingestion/daemon.js +0 -35
  227. package/dist/src/ingestion/daemon.js.map +0 -1
  228. package/dist/src/ingestion/watcher.d.ts +0 -19
  229. package/dist/src/ingestion/watcher.d.ts.map +0 -1
  230. package/dist/src/ingestion/watcher.js +0 -70
  231. package/dist/src/ingestion/watcher.js.map +0 -1
  232. package/dist/src/knowledge/autotag.d.ts +0 -3
  233. package/dist/src/knowledge/autotag.d.ts.map +0 -1
  234. package/dist/src/knowledge/autotag.js +0 -24
  235. package/dist/src/knowledge/autotag.js.map +0 -1
  236. package/dist/src/knowledge/dedup.d.ts +0 -7
  237. package/dist/src/knowledge/dedup.d.ts.map +0 -1
  238. package/dist/src/knowledge/dedup.js +0 -15
  239. package/dist/src/knowledge/dedup.js.map +0 -1
  240. package/dist/src/knowledge/repository.d.ts +0 -12
  241. package/dist/src/knowledge/repository.d.ts.map +0 -1
  242. package/dist/src/knowledge/repository.js +0 -74
  243. package/dist/src/knowledge/repository.js.map +0 -1
  244. package/dist/src/mcp/tools/config.d.ts +0 -36
  245. package/dist/src/mcp/tools/config.d.ts.map +0 -1
  246. package/dist/src/mcp/tools/config.js +0 -54
  247. package/dist/src/mcp/tools/config.js.map +0 -1
  248. package/dist/src/mcp/tools/knowledge.d.ts +0 -17
  249. package/dist/src/mcp/tools/knowledge.d.ts.map +0 -1
  250. package/dist/src/mcp/tools/knowledge.js +0 -46
  251. package/dist/src/mcp/tools/knowledge.js.map +0 -1
  252. package/dist/src/mcp/tools/search.d.ts +0 -22
  253. package/dist/src/mcp/tools/search.d.ts.map +0 -1
  254. package/dist/src/mcp/tools/search.js +0 -30
  255. package/dist/src/mcp/tools/search.js.map +0 -1
  256. package/dist/src/mcp/tools/write.d.ts +0 -38
  257. package/dist/src/mcp/tools/write.d.ts.map +0 -1
  258. package/dist/src/mcp/tools/write.js +0 -97
  259. package/dist/src/mcp/tools/write.js.map +0 -1
  260. package/dist/src/runtime/ingestion.d.ts +0 -17
  261. package/dist/src/runtime/ingestion.d.ts.map +0 -1
  262. package/dist/src/runtime/ingestion.js +0 -106
  263. package/dist/src/runtime/ingestion.js.map +0 -1
  264. package/dist/src/scrapers/gemini.d.ts +0 -17
  265. package/dist/src/scrapers/gemini.d.ts.map +0 -1
  266. package/dist/src/scrapers/gemini.js +0 -245
  267. package/dist/src/scrapers/gemini.js.map +0 -1
  268. package/dist/src/store/embeddings.d.ts +0 -9
  269. package/dist/src/store/embeddings.d.ts.map +0 -1
  270. package/dist/src/store/embeddings.js +0 -28
  271. package/dist/src/store/embeddings.js.map +0 -1
  272. package/dist/src/store/lance.d.ts +0 -25
  273. package/dist/src/store/lance.d.ts.map +0 -1
  274. package/dist/src/store/lance.js +0 -74
  275. package/dist/src/store/lance.js.map +0 -1
  276. package/dist/src/store/search.d.ts +0 -18
  277. package/dist/src/store/search.d.ts.map +0 -1
  278. package/dist/src/store/search.js +0 -55
  279. package/dist/src/store/search.js.map +0 -1
  280. package/dist/src/types/compaction.d.ts +0 -29
  281. package/dist/src/types/compaction.d.ts.map +0 -1
  282. package/dist/src/types/compaction.js +0 -2
  283. package/dist/src/types/compaction.js.map +0 -1
  284. package/dist/src/types/context.d.ts +0 -33
  285. package/dist/src/types/context.d.ts.map +0 -1
  286. package/dist/src/types/context.js +0 -18
  287. package/dist/src/types/context.js.map +0 -1
  288. package/dist/src/utils/retry.d.ts +0 -11
  289. package/dist/src/utils/retry.d.ts.map +0 -1
  290. package/dist/src/utils/retry.js +0 -46
  291. package/dist/src/utils/retry.js.map +0 -1
  292. package/dist/src/utils/shutdown.d.ts +0 -25
  293. package/dist/src/utils/shutdown.d.ts.map +0 -1
  294. package/dist/src/utils/shutdown.js +0 -56
  295. package/dist/src/utils/shutdown.js.map +0 -1
  296. package/dist/web/apple-touch-icon.png +0 -0
  297. package/dist/web/assets/index-Nuadr5ox.css +0 -1
  298. package/dist/web/assets/index-aA0u8eXA.js +0 -3504
  299. package/dist/web/assets/primeicons-C6QP2o4f.woff2 +0 -0
  300. package/dist/web/assets/primeicons-DMOk5skT.eot +0 -0
  301. package/dist/web/assets/primeicons-Dr5RGzOO.svg +0 -345
  302. package/dist/web/assets/primeicons-MpK4pl85.ttf +0 -0
  303. package/dist/web/assets/primeicons-WjwUDZjB.woff +0 -0
  304. package/dist/web/favicon.ico +0 -0
  305. package/dist/web/favicon.svg +0 -21
  306. package/dist/web/index.html +0 -21
  307. package/dist/web/site.webmanifest +0 -19
  308. package/dist/web/vite.svg +0 -1
@@ -0,0 +1,76 @@
1
+ import { createInterface } from "node:readline/promises";
2
+ import { stdin as input, stdout as output } from "node:process";
3
+ import { resolve } from "node:path";
4
+ import { describeSetupPlan, runSetup as setup } from "../config/setup.js";
5
+ import { BUILT_IN_SKILL_ID, discoverProjectSkills } from "../config/skills.js";
6
+ export async function runSetup(options = {}) {
7
+ const selectedSkillIds = options.yes ? undefined : await promptSkillSelection(options.projectPath);
8
+ if (!options.yes) {
9
+ const confirmed = await confirmSetup(options, selectedSkillIds);
10
+ if (!confirmed) {
11
+ process.stdout.write("xtctx setup cancelled.\n");
12
+ return;
13
+ }
14
+ }
15
+ const result = await setup({
16
+ projectPath: options.projectPath,
17
+ yes: options.yes,
18
+ repair: options.repair,
19
+ selectedSkillIds,
20
+ includeGlobalMcp: options.includeGlobalMcp,
21
+ });
22
+ if (result.failures.length > 0) {
23
+ process.exitCode = 1;
24
+ }
25
+ }
26
+ async function promptSkillSelection(projectPath) {
27
+ if (input.isTTY !== true || output.isTTY !== true) {
28
+ return undefined;
29
+ }
30
+ const projectRoot = resolve(projectPath ?? process.cwd());
31
+ const skills = (await discoverProjectSkills({ projectRoot }))
32
+ .filter((skill) => skill.id !== BUILT_IN_SKILL_ID)
33
+ .sort((left, right) => left.id.localeCompare(right.id));
34
+ process.stdout.write("xtctx will sync the built-in xtctx-handoff skill.\n");
35
+ if (skills.length === 0) {
36
+ process.stdout.write("No additional project or user skills were discovered.\n");
37
+ return [BUILT_IN_SKILL_ID];
38
+ }
39
+ process.stdout.write("Additional skills discovered:\n");
40
+ skills.forEach((skill, index) => {
41
+ process.stdout.write(` ${String(index + 1).padStart(2)} ${skill.id} (${skill.source})\n`);
42
+ });
43
+ const rl = createInterface({ input, output });
44
+ try {
45
+ const answer = await rl.question("Add skills by number, comma separated. Press Enter for built-in only. ");
46
+ const selected = answer
47
+ .split(",")
48
+ .map((part) => Number.parseInt(part.trim(), 10))
49
+ .filter((value) => Number.isInteger(value) && value > 0 && value <= skills.length)
50
+ .map((value) => skills[value - 1]?.id)
51
+ .filter((value) => Boolean(value));
52
+ return [BUILT_IN_SKILL_ID, ...selected];
53
+ }
54
+ finally {
55
+ rl.close();
56
+ }
57
+ }
58
+ async function confirmSetup(options, selectedSkillIds) {
59
+ const plan = describeSetupPlan(options.projectPath, selectedSkillIds, options.includeGlobalMcp);
60
+ process.stdout.write(`xtctx setup will configure handoff for ${plan.projectRoot}\n`);
61
+ for (const write of plan.writes) {
62
+ process.stdout.write(` ${write.kind.padEnd(22)} ${write.path}\n`);
63
+ }
64
+ if (input.isTTY !== true || output.isTTY !== true) {
65
+ throw new Error("Refusing non-interactive setup without --yes.");
66
+ }
67
+ const rl = createInterface({ input, output });
68
+ try {
69
+ const answer = await rl.question("Apply these changes? [y/N] ");
70
+ return answer.trim().toLowerCase() === "y" || answer.trim().toLowerCase() === "yes";
71
+ }
72
+ finally {
73
+ rl.close();
74
+ }
75
+ }
76
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../../src/cli/setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,KAAK,IAAI,KAAK,EAAE,MAAM,IAAI,MAAM,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,QAAQ,IAAI,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAS/E,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,UAA2B,EAAE;IAC1D,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,oBAAoB,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACnG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QACjB,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;QAChE,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;YACjD,OAAO;QACT,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC;QACzB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,gBAAgB;QAChB,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;KAC3C,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,oBAAoB,CAAC,WAAoB;IACtD,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;QAClD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAG,CAAC,MAAM,qBAAqB,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC;SAC1D,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,iBAAiB,CAAC;SACjD,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IAE1D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qDAAqD,CAAC,CAAC;IAC5E,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yDAAyD,CAAC,CAAC;QAChF,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC7B,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACxD,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QAC9B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,KAAK,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC;IAC7F,CAAC,CAAC,CAAC;IAEH,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,wEAAwE,CAAC,CAAC;QAC3G,MAAM,QAAQ,GAAG,MAAM;aACpB,KAAK,CAAC,GAAG,CAAC;aACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;aAC/C,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC;aACjF,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC;aACrC,MAAM,CAAC,CAAC,KAAK,EAAmB,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;QACtD,OAAO,CAAC,iBAAiB,EAAE,GAAG,QAAQ,CAAC,CAAC;IAC1C,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;AACH,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,OAAwB,EAAE,gBAA2B;IAC/E,MAAM,IAAI,GAAG,iBAAiB,CAAC,OAAO,CAAC,WAAW,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAChG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,0CAA0C,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC;IACrF,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;IACrE,CAAC;IAED,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,6BAA6B,CAAC,CAAC;QAChE,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC;IACtF,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { type ProjectServices } from "../runtime/services.js";
2
+ export interface StatusOptions {
3
+ projectPath?: string;
4
+ }
5
+ export declare function runStatus(options?: StatusOptions): Promise<void>;
6
+ export declare function renderStatusBlock(services: ProjectServices): Promise<string>;
7
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/cli/status.ts"],"names":[],"mappings":"AAGA,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAIrF,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,SAAS,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ1E;AAED,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAqGlF"}
@@ -0,0 +1,133 @@
1
+ import { join, resolve } from "node:path";
2
+ import { inspectManagedFile, pathExists } from "../config/setup.js";
3
+ import { inspectSkillStatus } from "../config/skills.js";
4
+ import { createProjectServices } from "../runtime/services.js";
5
+ import { SUPPORTED_TOOLS } from "../tools/sources.js";
6
+ import { readXtctxPackage } from "../utils/package-info.js";
7
+ export async function runStatus(options = {}) {
8
+ const projectRoot = resolve(options.projectPath ?? process.cwd());
9
+ const services = await createProjectServices(projectRoot);
10
+ try {
11
+ process.stdout.write((await renderStatusBlock(services)) + "\n");
12
+ }
13
+ finally {
14
+ await services.sessions.close().catch(() => { });
15
+ }
16
+ }
17
+ export async function renderStatusBlock(services) {
18
+ const { version } = readXtctxPackage(import.meta.url);
19
+ const status = await services.sessions.getStatus();
20
+ const skills = await inspectSkillStatus(services.projectRoot, services.configPath);
21
+ const configPresent = await pathExists(services.configPath);
22
+ const managed = await Promise.all(managedTargets(services.projectRoot).map(async (target) => ({
23
+ ...target,
24
+ ...(await inspectManagedFile(target.path)),
25
+ })));
26
+ const lines = [];
27
+ lines.push(`xtctx ${version} - handoff status`);
28
+ lines.push("");
29
+ lines.push(`Project ${services.projectRoot}`);
30
+ lines.push(`Config ${configPresent ? services.configPath : "missing (run xtctx setup)"}`);
31
+ lines.push(`Index ${services.dbPath}`);
32
+ lines.push(`MCP npx -y xtctx`);
33
+ lines.push(`Scan ${status.last_scan_at ?? "never"}`);
34
+ if (status.embedding_error) {
35
+ lines.push(`Search semantic unavailable (keyword only): ${status.embedding_error}`);
36
+ }
37
+ lines.push(`Data ${status.sessions} sessions, ${status.messages} messages, ` +
38
+ `${status.retrieval_units} retrieval windows, ${status.vectorized_units} vectorized`);
39
+ lines.push("");
40
+ lines.push("Tools:");
41
+ for (const tool of status.tools) {
42
+ const definition = SUPPORTED_TOOLS.find((item) => item.id === tool.tool);
43
+ const hook = definition?.hookMode ?? "mcp-only";
44
+ const marker = tool.detected ? "+" : "-";
45
+ const error = tool.last_error ? `; last scrape error: ${tool.last_error}` : "";
46
+ lines.push(` ${marker} ${tool.tool.padEnd(13)} ${tool.detected ? "detected" : "not detected"}; ` +
47
+ `${tool.indexed_sessions} sessions; hook: ${hook}${error}`);
48
+ // `.xtctx/config.yaml` is committable, so a cloned repo can point a
49
+ // scraper at any directory on disk. Overrides stay legal but visible.
50
+ const custom = customStorePaths(definition, tool.store_paths);
51
+ for (const path of custom) {
52
+ lines.push(` custom store path (not the ${tool.tool} default): ${path}`);
53
+ }
54
+ }
55
+ lines.push("");
56
+ lines.push("Skills:");
57
+ lines.push(` Source ${skills.sourceDir}`);
58
+ for (const skill of skills.selected) {
59
+ const marker = skill.exists ? "ok" : "missing";
60
+ const hash = skill.hash ? ` ${skill.hash.slice(0, 18)}` : "";
61
+ lines.push(` ${marker.padEnd(8)} ${skill.id}${hash}`);
62
+ }
63
+ for (const target of skills.targets) {
64
+ const skillPart = target.skillId ? ` ${target.skillId}` : "";
65
+ const pathPart = target.path ? ` ${target.path}` : "";
66
+ lines.push(` ${target.state.padEnd(13)} ${target.tool} ${target.mode}${skillPart}${pathPart}`);
67
+ }
68
+ lines.push("");
69
+ lines.push("Managed files:");
70
+ for (const file of managed) {
71
+ const state = !file.exists
72
+ ? "missing"
73
+ : file.blockCount === 1 && file.staleReferences.length === 0
74
+ ? "ok"
75
+ : "needs repair";
76
+ const details = [];
77
+ if (file.blockCount !== 1) {
78
+ details.push(`${file.blockCount} xtctx blocks`);
79
+ }
80
+ if (file.staleReferences.length > 0) {
81
+ details.push(`stale: ${file.staleReferences.join(", ")}`);
82
+ }
83
+ lines.push(` ${state.padEnd(12)} ${file.label} ${file.path}${details.length ? ` (${details.join("; ")})` : ""}`);
84
+ }
85
+ // Status always ends with one concrete next step, as ux-walkthrough.md
86
+ // promises. Repairing wiring outranks indexing advice: a drifted managed
87
+ // file or missing skill target is why an agent would see nothing at all.
88
+ const needsRepair = !configPresent ||
89
+ managed.some((file) => file.exists && (file.blockCount !== 1 || file.staleReferences.length > 0)) ||
90
+ skills.selected.some((skill) => !skill.exists) ||
91
+ // Only `missing` and `drift` are faults. `managed-block` and
92
+ // `unsupported` are the normal, healthy states for tools that carry
93
+ // skills inside their instruction file or not at all.
94
+ skills.targets.some((target) => target.state === "missing" || target.state === "drift");
95
+ lines.push("");
96
+ if (needsRepair) {
97
+ lines.push("Next Wiring has drifted. Run: xtctx setup --repair");
98
+ }
99
+ else if (status.sessions === 0) {
100
+ lines.push("Next No sessions are indexed yet. Ask a configured agent to call xtctx_recent_sessions.");
101
+ }
102
+ else {
103
+ lines.push("Next Handoff is wired. Ask a configured agent to call xtctx_recent_sessions.");
104
+ }
105
+ return lines.join("\n");
106
+ }
107
+ function managedTargets(projectRoot) {
108
+ return [
109
+ { label: "codex/opencode", path: join(projectRoot, "AGENTS.md") },
110
+ { label: "claude-code", path: join(projectRoot, "CLAUDE.md") },
111
+ { label: "antigravity", path: join(projectRoot, "GEMINI.md") },
112
+ { label: "cursor", path: join(projectRoot, ".cursor", "rules", "xtctx.mdc") },
113
+ { label: "copilot", path: join(projectRoot, ".github", "copilot-instructions.md") },
114
+ ];
115
+ }
116
+ /** Store paths that differ from the tool's built-in default location. */
117
+ function customStorePaths(definition, storePaths) {
118
+ if (!definition) {
119
+ return [];
120
+ }
121
+ let fallback;
122
+ try {
123
+ fallback = normalizePath(definition.defaultStorePath());
124
+ }
125
+ catch {
126
+ return [];
127
+ }
128
+ return storePaths.filter((path) => normalizePath(path) !== fallback);
129
+ }
130
+ function normalizePath(value) {
131
+ return value.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
132
+ }
133
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/cli/status.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { type ToolId } from "../tools/sources.js";
2
+ export interface DisconnectOptions {
3
+ projectPath?: string;
4
+ tool?: string;
5
+ all?: boolean;
6
+ homeDir?: string;
7
+ }
8
+ export interface DisconnectResult {
9
+ projectRoot: string;
10
+ tools: ToolId[];
11
+ writes: Array<{
12
+ path: string;
13
+ kind: string;
14
+ changed: boolean;
15
+ /** What happened to the file. Defaults to "removed" for real removals. */
16
+ action?: "removed" | "updated";
17
+ note?: string;
18
+ }>;
19
+ warnings: string[];
20
+ }
21
+ export interface PlannedDisconnectWrite {
22
+ path: string;
23
+ kind: string;
24
+ note?: string;
25
+ }
26
+ export declare function describeDisconnectPlan(options?: DisconnectOptions): {
27
+ projectRoot: string;
28
+ tools: ToolId[];
29
+ writes: PlannedDisconnectWrite[];
30
+ warnings: string[];
31
+ };
32
+ export declare function disconnectProject(options?: DisconnectOptions): Promise<DisconnectResult>;
33
+ export declare function printDisconnectResult(result: DisconnectResult): void;
34
+ //# sourceMappingURL=disconnect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"disconnect.d.ts","sourceRoot":"","sources":["../../../src/config/disconnect.ts"],"names":[],"mappings":"AAOA,OAAO,EAAsC,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAiBtF,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,KAAK,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,OAAO,CAAC;QACjB,0EAA0E;QAC1E,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;QAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;IACH,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,iBAAsB,GAAG;IACvE,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,sBAAsB,EAAE,CAAC;IACjC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAiCA;AAED,wBAAsB,iBAAiB,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA4ElG;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAapE"}
@@ -0,0 +1,386 @@
1
+ import { readFile, rm, stat } from "node:fs/promises";
2
+ import { join, resolve } from "node:path";
3
+ import { writeFileAtomic } from "../utils/atomic-file.js";
4
+ import { matchLineEndings, normalizeNewlines, removeManagedBlocks } from "./managed-block.js";
5
+ import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
6
+ import { removeMcpServerConfigs } from "./mcp-config.js";
7
+ import { BUILT_IN_SKILL_ID, removeSyncedSkillsForTools } from "./skills.js";
8
+ import { SUPPORTED_TOOLS, getToolDefinition } from "../tools/sources.js";
9
+ const TOOL_ALIASES = {
10
+ claude: "claude-code",
11
+ "claude_code": "claude-code",
12
+ "claude-code": "claude-code",
13
+ cursor: "cursor",
14
+ codex: "codex",
15
+ copilot: "copilot",
16
+ "github-copilot": "copilot",
17
+ antigravity: "antigravity",
18
+ opencode: "opencode",
19
+ "open-code": "opencode",
20
+ "copilot-cli": "copilot-cli",
21
+ "github-copilot-cli": "copilot-cli",
22
+ };
23
+ export function describeDisconnectPlan(options = {}) {
24
+ const projectRoot = resolve(options.projectPath ?? process.cwd());
25
+ const tools = resolveToolIds(options);
26
+ const writes = [
27
+ { path: join(projectRoot, ".xtctx", "config.yaml"), kind: "config" },
28
+ ];
29
+ const warnings = [];
30
+ for (const tool of tools) {
31
+ const definition = getToolDefinition(tool);
32
+ if (!definition)
33
+ continue;
34
+ writes.push(...plannedMcpWrites(projectRoot, tool, options.homeDir));
35
+ if (tool === "claude-code") {
36
+ writes.push({ path: join(projectRoot, ".claude", "settings.json"), kind: "hook:claude-code" });
37
+ writes.push({ path: join(projectRoot, ".claude", "hooks.json"), kind: "hook:claude-code" });
38
+ }
39
+ writes.push(...plannedSkillWrites(projectRoot, tool));
40
+ if (tool === "antigravity") {
41
+ warnings.push("Antigravity stores MCP config at app level, so disconnect removes xtctx from Antigravity globally.");
42
+ }
43
+ }
44
+ for (const path of memoryPathsToDisconnect(projectRoot, tools, options.all === true)) {
45
+ writes.push({ path, kind: "memory" });
46
+ }
47
+ return { projectRoot, tools, writes: dedupePlannedWrites(writes), warnings };
48
+ }
49
+ export async function disconnectProject(options = {}) {
50
+ const projectRoot = resolve(options.projectPath ?? process.cwd());
51
+ const tools = resolveToolIds(options);
52
+ const warnings = [];
53
+ const writes = [];
54
+ const configPath = join(projectRoot, ".xtctx", "config.yaml");
55
+ writes.push({
56
+ path: configPath,
57
+ kind: "config",
58
+ // config.yaml is rewritten with the tools disabled, never deleted — it is
59
+ // the record that xtctx was disconnected.
60
+ action: "updated",
61
+ changed: await disableToolsInProjectConfig(configPath, tools),
62
+ });
63
+ const mcpSummary = await removeMcpServerConfigs(projectRoot, "xtctx", tools, options.homeDir ? { homeDir: options.homeDir } : {});
64
+ for (const result of mcpSummary.results) {
65
+ writes.push({
66
+ path: result.path,
67
+ kind: `mcp:${result.tool}`,
68
+ changed: result.removed,
69
+ note: result.scope === "global" ? "global config" : undefined,
70
+ });
71
+ if (result.warning)
72
+ warnings.push(result.warning);
73
+ }
74
+ for (const path of memoryPathsToDisconnect(projectRoot, tools, options.all === true)) {
75
+ writes.push({
76
+ path,
77
+ kind: "memory",
78
+ changed: await removeManagedBlocksFromFile(path),
79
+ });
80
+ }
81
+ writes.push(...(await removeSyncedSkillsForTools(projectRoot, tools)));
82
+ if (options.all === true) {
83
+ // Nothing is left managing skills, so the synced source and the ignore
84
+ // file setup wrote are xtctx's own scaffolding, not user content.
85
+ // Transcript data under .xtctx/state is deliberately untouched.
86
+ for (const path of [
87
+ join(projectRoot, ".xtctx", "skills"),
88
+ join(projectRoot, ".xtctx", ".gitignore"),
89
+ ]) {
90
+ writes.push({
91
+ path,
92
+ kind: "skill-source",
93
+ changed: await removeIfPresent(path),
94
+ });
95
+ }
96
+ }
97
+ if (tools.includes("claude-code")) {
98
+ const settingsPath = join(projectRoot, ".claude", "settings.json");
99
+ writes.push({
100
+ path: settingsPath,
101
+ kind: "hook:claude-code",
102
+ changed: await removeClaudeHookFromSettings(settingsPath),
103
+ });
104
+ const legacyHooksPath = join(projectRoot, ".claude", "hooks.json");
105
+ writes.push({
106
+ path: legacyHooksPath,
107
+ kind: "hook:claude-code",
108
+ changed: await removeClaudeHook(legacyHooksPath),
109
+ note: "legacy hook file",
110
+ });
111
+ }
112
+ if (tools.includes("antigravity")) {
113
+ warnings.push("Antigravity MCP config is app-level; xtctx was removed from the Antigravity config for this user account.");
114
+ }
115
+ return { projectRoot, tools, writes, warnings };
116
+ }
117
+ export function printDisconnectResult(result) {
118
+ const changed = result.writes.filter((write) => write.changed).length;
119
+ process.stdout.write(`xtctx disconnect complete (${changed} changed, ${result.writes.length - changed} unchanged)\n`);
120
+ process.stdout.write(`Project: ${result.projectRoot}\n`);
121
+ process.stdout.write(`Tools: ${result.tools.join(", ")}\n`);
122
+ for (const write of result.writes) {
123
+ const marker = write.changed ? (write.action ?? "removed") : "ok";
124
+ const note = write.note ? ` (${write.note})` : "";
125
+ process.stdout.write(` ${marker.padEnd(8)} ${write.kind} ${write.path}${note}\n`);
126
+ }
127
+ for (const warning of result.warnings) {
128
+ process.stdout.write(` warning ${warning}\n`);
129
+ }
130
+ }
131
+ function resolveToolIds(options) {
132
+ if (options.all && options.tool) {
133
+ throw new Error("Use either a tool name or --all, not both.");
134
+ }
135
+ if (options.all) {
136
+ return SUPPORTED_TOOLS.map((tool) => tool.id);
137
+ }
138
+ if (!options.tool) {
139
+ throw new Error(`Choose a tool to disconnect, or use --all. Supported tools: ${supportedToolList()}`);
140
+ }
141
+ const canonical = TOOL_ALIASES[options.tool.trim().toLowerCase()];
142
+ if (!canonical || !getToolDefinition(canonical)) {
143
+ throw new Error(`Unknown tool "${options.tool}". Supported tools: ${supportedToolList()}`);
144
+ }
145
+ return [canonical];
146
+ }
147
+ function supportedToolList() {
148
+ return SUPPORTED_TOOLS.map((tool) => tool.id).join(", ");
149
+ }
150
+ async function disableToolsInProjectConfig(configPath, tools) {
151
+ const raw = await readUtf8IfExists(configPath);
152
+ if (raw === null) {
153
+ return false;
154
+ }
155
+ const parsed = parseYaml(raw);
156
+ const config = isRecord(parsed) ? parsed : {};
157
+ const currentTools = isRecord(config.tools) ? { ...config.tools } : {};
158
+ let changed = false;
159
+ for (const tool of tools) {
160
+ const existing = isRecord(currentTools[tool]) ? { ...currentTools[tool] } : {};
161
+ if (existing.enabled !== false) {
162
+ existing.enabled = false;
163
+ changed = true;
164
+ }
165
+ currentTools[tool] = existing;
166
+ }
167
+ if (!changed) {
168
+ return false;
169
+ }
170
+ config.tools = currentTools;
171
+ await writeFileAtomic(configPath, stringifyYaml(config));
172
+ return true;
173
+ }
174
+ function memoryPathsToDisconnect(projectRoot, tools, all) {
175
+ const targetTools = new Set(tools);
176
+ const sharedPaths = new Map();
177
+ for (const tool of SUPPORTED_TOOLS) {
178
+ for (const path of tool.memoryTargets) {
179
+ const absolute = join(projectRoot, path);
180
+ sharedPaths.set(absolute, [...(sharedPaths.get(absolute) ?? []), tool.id]);
181
+ }
182
+ }
183
+ const paths = [];
184
+ for (const [path, owners] of sharedPaths.entries()) {
185
+ const ownerSet = new Set(owners);
186
+ const selectedOwners = owners.filter((owner) => targetTools.has(owner));
187
+ if (selectedOwners.length === 0)
188
+ continue;
189
+ if (!all && [...ownerSet].some((owner) => !targetTools.has(owner)))
190
+ continue;
191
+ paths.push(path);
192
+ }
193
+ return paths;
194
+ }
195
+ function plannedMcpWrites(projectRoot, tool, homeDir) {
196
+ const home = homeDir ?? process.env.USERPROFILE ?? process.env.HOME;
197
+ const writes = [];
198
+ switch (tool) {
199
+ case "claude-code":
200
+ writes.push({ path: join(projectRoot, ".mcp.json"), kind: "mcp:claude-code" });
201
+ break;
202
+ case "cursor":
203
+ writes.push({ path: join(projectRoot, ".cursor", "mcp.json"), kind: "mcp:cursor" });
204
+ break;
205
+ case "copilot":
206
+ writes.push({ path: join(projectRoot, ".vscode", "mcp.json"), kind: "mcp:copilot" });
207
+ break;
208
+ case "codex":
209
+ writes.push({ path: join(projectRoot, ".codex", "config.toml"), kind: "mcp:codex" });
210
+ break;
211
+ case "opencode":
212
+ writes.push({ path: join(projectRoot, "opencode.json"), kind: "mcp:opencode" });
213
+ break;
214
+ case "antigravity":
215
+ if (home) {
216
+ writes.push({
217
+ path: join(home, ".gemini", "antigravity", "mcp_config.json"),
218
+ kind: "mcp:antigravity",
219
+ note: "global config",
220
+ });
221
+ }
222
+ break;
223
+ case "copilot-cli":
224
+ if (home) {
225
+ writes.push({
226
+ path: join(home, ".copilot", "mcp-config.json"),
227
+ kind: "mcp:copilot-cli",
228
+ note: "global config",
229
+ });
230
+ }
231
+ break;
232
+ default: {
233
+ const _exhaustive = tool;
234
+ void _exhaustive;
235
+ break;
236
+ }
237
+ }
238
+ return writes;
239
+ }
240
+ function plannedSkillWrites(projectRoot, tool) {
241
+ const definition = getToolDefinition(tool);
242
+ const capability = definition?.skillSync;
243
+ if (!capability || capability.mode === "managed-block" || capability.mode === "unsupported") {
244
+ return [];
245
+ }
246
+ const targetPath = capability.targetPath?.(projectRoot, BUILT_IN_SKILL_ID);
247
+ if (!targetPath) {
248
+ return [];
249
+ }
250
+ const writes = [{ path: targetPath, kind: `skill:${tool}:${BUILT_IN_SKILL_ID}` }];
251
+ return writes;
252
+ }
253
+ async function removeManagedBlocksFromFile(filePath) {
254
+ const existing = await readUtf8IfExists(filePath);
255
+ if (existing === null) {
256
+ return false;
257
+ }
258
+ const repaired = removeManagedBlocks(existing).trimEnd();
259
+ if (normalizeNewlines(existing).trimEnd() === repaired) {
260
+ return false;
261
+ }
262
+ if (!repaired || isOnlyFrontmatter(repaired)) {
263
+ // The file held nothing but the xtctx block — or the YAML frontmatter
264
+ // xtctx itself wrote above it, which Cursor would keep loading as an
265
+ // xtctx rule. Either way setup created it and disconnect owns removing
266
+ // it, rather than leaving a stub behind.
267
+ await rm(filePath, { force: true });
268
+ return true;
269
+ }
270
+ // Put the author's line endings back: removal must not reformat the file.
271
+ await writeFileAtomic(filePath, matchLineEndings(`${repaired}\n`, existing));
272
+ return true;
273
+ }
274
+ async function removeIfPresent(path) {
275
+ try {
276
+ await stat(path);
277
+ }
278
+ catch {
279
+ return false;
280
+ }
281
+ await rm(path, { recursive: true, force: true });
282
+ return true;
283
+ }
284
+ /** True when nothing survives but a single YAML frontmatter block. */
285
+ function isOnlyFrontmatter(content) {
286
+ const trimmed = content.trim();
287
+ if (!trimmed.startsWith("---")) {
288
+ return false;
289
+ }
290
+ const end = trimmed.indexOf("\n---", 3);
291
+ return end !== -1 && trimmed.slice(end + 4).trim().length === 0;
292
+ }
293
+ /** Strip xtctx SessionStart matcher groups from .claude/settings.json. */
294
+ async function removeClaudeHookFromSettings(settingsPath) {
295
+ const raw = await readUtf8IfExists(settingsPath);
296
+ if (raw === null) {
297
+ return false;
298
+ }
299
+ let parsed;
300
+ try {
301
+ parsed = JSON.parse(raw);
302
+ }
303
+ catch {
304
+ return false;
305
+ }
306
+ if (!isRecord(parsed) || !isRecord(parsed.hooks)) {
307
+ return false;
308
+ }
309
+ const sessionStart = Array.isArray(parsed.hooks.SessionStart) ? parsed.hooks.SessionStart : [];
310
+ const kept = sessionStart
311
+ .map((group) => {
312
+ if (!isRecord(group) || !Array.isArray(group.hooks)) {
313
+ return group;
314
+ }
315
+ const hooks = group.hooks.filter((hook) => !isRecord(hook) ||
316
+ typeof hook.command !== "string" ||
317
+ !hook.command.includes("xtctx --hook session-start"));
318
+ return hooks.length === group.hooks.length ? group : { ...group, hooks };
319
+ })
320
+ .filter((group) => !isRecord(group) || !Array.isArray(group.hooks) || group.hooks.length > 0);
321
+ if (JSON.stringify(kept) === JSON.stringify(sessionStart)) {
322
+ return false;
323
+ }
324
+ parsed.hooks.SessionStart = kept;
325
+ // A settings file left holding nothing but an empty SessionStart list was
326
+ // created by setup for that hook alone; remove it rather than leave litter.
327
+ const hooksOnly = Object.keys(parsed).length === 1 &&
328
+ Object.keys(parsed.hooks).length === 1 &&
329
+ kept.length === 0;
330
+ if (hooksOnly) {
331
+ await rm(settingsPath, { force: true });
332
+ return true;
333
+ }
334
+ await writeFileAtomic(settingsPath, JSON.stringify(parsed, null, 2) + "\n");
335
+ return true;
336
+ }
337
+ async function removeClaudeHook(hooksPath) {
338
+ const raw = await readUtf8IfExists(hooksPath);
339
+ if (raw === null) {
340
+ return false;
341
+ }
342
+ let parsed;
343
+ try {
344
+ parsed = JSON.parse(raw);
345
+ }
346
+ catch {
347
+ return false;
348
+ }
349
+ if (!isRecord(parsed) || !isRecord(parsed.hooks)) {
350
+ return false;
351
+ }
352
+ const sessionStart = Array.isArray(parsed.hooks.SessionStart) ? parsed.hooks.SessionStart : [];
353
+ const nextSessionStart = sessionStart.filter((entry) => !isRecord(entry) ||
354
+ typeof entry.command !== "string" ||
355
+ !entry.command.includes("xtctx --hook session-start"));
356
+ if (nextSessionStart.length === sessionStart.length) {
357
+ return false;
358
+ }
359
+ parsed.hooks.SessionStart = nextSessionStart;
360
+ await writeFileAtomic(hooksPath, JSON.stringify(parsed, null, 2) + "\n");
361
+ return true;
362
+ }
363
+ async function readUtf8IfExists(filePath) {
364
+ try {
365
+ return await readFile(filePath, "utf-8");
366
+ }
367
+ catch {
368
+ return null;
369
+ }
370
+ }
371
+ function isRecord(value) {
372
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
373
+ }
374
+ function dedupePlannedWrites(writes) {
375
+ const seen = new Set();
376
+ const result = [];
377
+ for (const write of writes) {
378
+ const key = `${write.kind}\0${write.path}`;
379
+ if (seen.has(key))
380
+ continue;
381
+ seen.add(key);
382
+ result.push(write);
383
+ }
384
+ return result;
385
+ }
386
+ //# sourceMappingURL=disconnect.js.map