ctxr-cli 0.33.0 → 0.34.0

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 (273) hide show
  1. package/README.md +15 -1
  2. package/dist/commands/archive.d.ts +5 -4
  3. package/dist/commands/archive.d.ts.map +1 -1
  4. package/dist/commands/archive.js +7 -7
  5. package/dist/commands/archive.js.map +1 -1
  6. package/dist/commands/catalog-show.d.ts +8 -0
  7. package/dist/commands/catalog-show.d.ts.map +1 -1
  8. package/dist/commands/catalog-show.js +29 -2
  9. package/dist/commands/catalog-show.js.map +1 -1
  10. package/dist/commands/context-gather.d.ts.map +1 -1
  11. package/dist/commands/context-gather.js +3 -2
  12. package/dist/commands/context-gather.js.map +1 -1
  13. package/dist/commands/doctor.d.ts +5 -1
  14. package/dist/commands/doctor.d.ts.map +1 -1
  15. package/dist/commands/doctor.js +4 -0
  16. package/dist/commands/doctor.js.map +1 -1
  17. package/dist/commands/graph-build.d.ts +4 -1
  18. package/dist/commands/graph-build.d.ts.map +1 -1
  19. package/dist/commands/graph-build.js +14 -5
  20. package/dist/commands/graph-build.js.map +1 -1
  21. package/dist/commands/land.d.ts +27 -7
  22. package/dist/commands/land.d.ts.map +1 -1
  23. package/dist/commands/land.js +307 -32
  24. package/dist/commands/land.js.map +1 -1
  25. package/dist/commands/publish-gather.d.ts.map +1 -1
  26. package/dist/commands/publish-gather.js +2 -3
  27. package/dist/commands/publish-gather.js.map +1 -1
  28. package/dist/commands/review-list.d.ts +19 -5
  29. package/dist/commands/review-list.d.ts.map +1 -1
  30. package/dist/commands/review-list.js +55 -6
  31. package/dist/commands/review-list.js.map +1 -1
  32. package/dist/commands/rollup-gather.d.ts.map +1 -1
  33. package/dist/commands/rollup-gather.js +2 -3
  34. package/dist/commands/rollup-gather.js.map +1 -1
  35. package/dist/commands/second-opinion.d.ts +33 -0
  36. package/dist/commands/second-opinion.d.ts.map +1 -0
  37. package/dist/commands/second-opinion.js +56 -0
  38. package/dist/commands/second-opinion.js.map +1 -0
  39. package/dist/commands/serve.d.ts.map +1 -1
  40. package/dist/commands/serve.js +4 -0
  41. package/dist/commands/serve.js.map +1 -1
  42. package/dist/commands/session-discard.d.ts +18 -0
  43. package/dist/commands/session-discard.d.ts.map +1 -0
  44. package/dist/commands/session-discard.js +31 -0
  45. package/dist/commands/session-discard.js.map +1 -0
  46. package/dist/commands/session-list.d.ts +17 -6
  47. package/dist/commands/session-list.d.ts.map +1 -1
  48. package/dist/commands/session-list.js +31 -11
  49. package/dist/commands/session-list.js.map +1 -1
  50. package/dist/commands/session-rebase.d.ts +18 -0
  51. package/dist/commands/session-rebase.d.ts.map +1 -0
  52. package/dist/commands/session-rebase.js +43 -0
  53. package/dist/commands/session-rebase.js.map +1 -0
  54. package/dist/commands/session-start.d.ts +8 -0
  55. package/dist/commands/session-start.d.ts.map +1 -1
  56. package/dist/commands/session-start.js +45 -5
  57. package/dist/commands/session-start.js.map +1 -1
  58. package/dist/commands/session-status.d.ts +17 -0
  59. package/dist/commands/session-status.d.ts.map +1 -0
  60. package/dist/commands/session-status.js +38 -0
  61. package/dist/commands/session-status.js.map +1 -0
  62. package/dist/commands/session-submit.d.ts +23 -0
  63. package/dist/commands/session-submit.d.ts.map +1 -0
  64. package/dist/commands/session-submit.js +42 -0
  65. package/dist/commands/session-submit.js.map +1 -0
  66. package/dist/commands/shared-stores-status.d.ts +24 -0
  67. package/dist/commands/shared-stores-status.d.ts.map +1 -0
  68. package/dist/commands/shared-stores-status.js +51 -0
  69. package/dist/commands/shared-stores-status.js.map +1 -0
  70. package/dist/commands/shared-stores-sync.d.ts +27 -0
  71. package/dist/commands/shared-stores-sync.d.ts.map +1 -0
  72. package/dist/commands/shared-stores-sync.js +115 -0
  73. package/dist/commands/shared-stores-sync.js.map +1 -0
  74. package/dist/commands/verify.d.ts.map +1 -1
  75. package/dist/commands/verify.js +2 -4
  76. package/dist/commands/verify.js.map +1 -1
  77. package/dist/config/schema.d.ts +16 -0
  78. package/dist/config/schema.d.ts.map +1 -1
  79. package/dist/config/schema.js +32 -0
  80. package/dist/config/schema.js.map +1 -1
  81. package/dist/core/agents-doc.d.ts +10 -0
  82. package/dist/core/agents-doc.d.ts.map +1 -1
  83. package/dist/core/agents-doc.js +26 -1
  84. package/dist/core/agents-doc.js.map +1 -1
  85. package/dist/core/approval/decision.d.ts +39 -1
  86. package/dist/core/approval/decision.d.ts.map +1 -1
  87. package/dist/core/approval/decision.js +53 -6
  88. package/dist/core/approval/decision.js.map +1 -1
  89. package/dist/core/approval/session-group.d.ts +156 -0
  90. package/dist/core/approval/session-group.d.ts.map +1 -0
  91. package/dist/core/approval/session-group.js +359 -0
  92. package/dist/core/approval/session-group.js.map +1 -0
  93. package/dist/core/approval/session-marker.d.ts +79 -0
  94. package/dist/core/approval/session-marker.d.ts.map +1 -0
  95. package/dist/core/approval/session-marker.js +146 -0
  96. package/dist/core/approval/session-marker.js.map +1 -0
  97. package/dist/core/approval/store-forges.d.ts +15 -0
  98. package/dist/core/approval/store-forges.d.ts.map +1 -0
  99. package/dist/core/approval/store-forges.js +33 -0
  100. package/dist/core/approval/store-forges.js.map +1 -0
  101. package/dist/core/browse/approve.d.ts +8 -0
  102. package/dist/core/browse/approve.d.ts.map +1 -1
  103. package/dist/core/browse/approve.js +366 -48
  104. package/dist/core/browse/approve.js.map +1 -1
  105. package/dist/core/browse/link-resolver.d.ts +9 -4
  106. package/dist/core/browse/link-resolver.d.ts.map +1 -1
  107. package/dist/core/browse/link-resolver.js +12 -7
  108. package/dist/core/browse/link-resolver.js.map +1 -1
  109. package/dist/core/browse/review.d.ts +5 -0
  110. package/dist/core/browse/review.d.ts.map +1 -1
  111. package/dist/core/browse/review.js +8 -0
  112. package/dist/core/browse/review.js.map +1 -1
  113. package/dist/core/checks/integrity-checks.d.ts.map +1 -1
  114. package/dist/core/checks/integrity-checks.js +7 -1
  115. package/dist/core/checks/integrity-checks.js.map +1 -1
  116. package/dist/core/checks/manifest.d.ts.map +1 -1
  117. package/dist/core/checks/manifest.js +2 -0
  118. package/dist/core/checks/manifest.js.map +1 -1
  119. package/dist/core/checks/shared-stores-checks.d.ts +17 -0
  120. package/dist/core/checks/shared-stores-checks.d.ts.map +1 -0
  121. package/dist/core/checks/shared-stores-checks.js +84 -0
  122. package/dist/core/checks/shared-stores-checks.js.map +1 -0
  123. package/dist/core/errors.d.ts +17 -0
  124. package/dist/core/errors.d.ts.map +1 -1
  125. package/dist/core/errors.js +21 -0
  126. package/dist/core/errors.js.map +1 -1
  127. package/dist/core/forge/github.d.ts +14 -0
  128. package/dist/core/forge/github.d.ts.map +1 -1
  129. package/dist/core/forge/github.js +17 -0
  130. package/dist/core/forge/github.js.map +1 -1
  131. package/dist/core/git/fast-forward.d.ts +23 -0
  132. package/dist/core/git/fast-forward.d.ts.map +1 -0
  133. package/dist/core/git/fast-forward.js +38 -0
  134. package/dist/core/git/fast-forward.js.map +1 -0
  135. package/dist/core/git/repo.d.ts +11 -0
  136. package/dist/core/git/repo.d.ts.map +1 -1
  137. package/dist/core/git/repo.js +25 -0
  138. package/dist/core/git/repo.js.map +1 -1
  139. package/dist/core/graph/model.d.ts +27 -0
  140. package/dist/core/graph/model.d.ts.map +1 -1
  141. package/dist/core/graph/model.js +68 -2
  142. package/dist/core/graph/model.js.map +1 -1
  143. package/dist/core/graph/persist.d.ts.map +1 -1
  144. package/dist/core/graph/persist.js +3 -2
  145. package/dist/core/graph/persist.js.map +1 -1
  146. package/dist/core/mcp/sessions.d.ts +11 -14
  147. package/dist/core/mcp/sessions.d.ts.map +1 -1
  148. package/dist/core/mcp/sessions.js +23 -43
  149. package/dist/core/mcp/sessions.js.map +1 -1
  150. package/dist/core/notes/list.d.ts.map +1 -1
  151. package/dist/core/notes/list.js +5 -0
  152. package/dist/core/notes/list.js.map +1 -1
  153. package/dist/core/reconcile.d.ts.map +1 -1
  154. package/dist/core/reconcile.js +2 -1
  155. package/dist/core/reconcile.js.map +1 -1
  156. package/dist/core/retrieval/pass.d.ts.map +1 -1
  157. package/dist/core/retrieval/pass.js +5 -4
  158. package/dist/core/retrieval/pass.js.map +1 -1
  159. package/dist/core/rollup.d.ts.map +1 -1
  160. package/dist/core/rollup.js +2 -3
  161. package/dist/core/rollup.js.map +1 -1
  162. package/dist/core/root.d.ts.map +1 -1
  163. package/dist/core/root.js +33 -2
  164. package/dist/core/root.js.map +1 -1
  165. package/dist/core/second-opinion/environment.d.ts +21 -0
  166. package/dist/core/second-opinion/environment.d.ts.map +1 -0
  167. package/dist/core/second-opinion/environment.js +70 -0
  168. package/dist/core/second-opinion/environment.js.map +1 -0
  169. package/dist/core/second-opinion/judge.d.ts +29 -0
  170. package/dist/core/second-opinion/judge.d.ts.map +1 -0
  171. package/dist/core/second-opinion/judge.js +55 -0
  172. package/dist/core/second-opinion/judge.js.map +1 -0
  173. package/dist/core/second-opinion/launch.d.ts +16 -0
  174. package/dist/core/second-opinion/launch.d.ts.map +1 -0
  175. package/dist/core/second-opinion/launch.js +75 -0
  176. package/dist/core/second-opinion/launch.js.map +1 -0
  177. package/dist/core/second-opinion/lineup.d.ts +82 -0
  178. package/dist/core/second-opinion/lineup.d.ts.map +1 -0
  179. package/dist/core/second-opinion/lineup.js +113 -0
  180. package/dist/core/second-opinion/lineup.js.map +1 -0
  181. package/dist/core/second-opinion/modes.d.ts +98 -0
  182. package/dist/core/second-opinion/modes.d.ts.map +1 -0
  183. package/dist/core/second-opinion/modes.js +224 -0
  184. package/dist/core/second-opinion/modes.js.map +1 -0
  185. package/dist/core/second-opinion/prompts.d.ts +18 -0
  186. package/dist/core/second-opinion/prompts.d.ts.map +1 -0
  187. package/dist/core/second-opinion/prompts.js +65 -0
  188. package/dist/core/second-opinion/prompts.js.map +1 -0
  189. package/dist/core/session/commit.d.ts +29 -0
  190. package/dist/core/session/commit.d.ts.map +1 -0
  191. package/dist/core/session/commit.js +22 -0
  192. package/dist/core/session/commit.js.map +1 -0
  193. package/dist/core/session/discard.d.ts +24 -0
  194. package/dist/core/session/discard.d.ts.map +1 -0
  195. package/dist/core/session/discard.js +68 -0
  196. package/dist/core/session/discard.js.map +1 -0
  197. package/dist/core/session/list.d.ts +48 -0
  198. package/dist/core/session/list.d.ts.map +1 -0
  199. package/dist/core/session/list.js +55 -0
  200. package/dist/core/session/list.js.map +1 -0
  201. package/dist/core/session/rebase.d.ts +34 -0
  202. package/dist/core/session/rebase.d.ts.map +1 -0
  203. package/dist/core/session/rebase.js +87 -0
  204. package/dist/core/session/rebase.js.map +1 -0
  205. package/dist/core/session/remote.d.ts +80 -0
  206. package/dist/core/session/remote.d.ts.map +1 -0
  207. package/dist/core/session/remote.js +102 -0
  208. package/dist/core/session/remote.js.map +1 -0
  209. package/dist/core/session/resolve.d.ts +41 -0
  210. package/dist/core/session/resolve.d.ts.map +1 -0
  211. package/dist/core/session/resolve.js +109 -0
  212. package/dist/core/session/resolve.js.map +1 -0
  213. package/dist/core/session/status.d.ts +47 -0
  214. package/dist/core/session/status.d.ts.map +1 -0
  215. package/dist/core/session/status.js +55 -0
  216. package/dist/core/session/status.js.map +1 -0
  217. package/dist/core/session/submit.d.ts +67 -0
  218. package/dist/core/session/submit.d.ts.map +1 -0
  219. package/dist/core/session/submit.js +254 -0
  220. package/dist/core/session/submit.js.map +1 -0
  221. package/dist/core/shared-stores-location.d.ts +17 -0
  222. package/dist/core/shared-stores-location.d.ts.map +1 -0
  223. package/dist/core/shared-stores-location.js +22 -0
  224. package/dist/core/shared-stores-location.js.map +1 -0
  225. package/dist/core/shared-stores.d.ts +69 -0
  226. package/dist/core/shared-stores.d.ts.map +1 -0
  227. package/dist/core/shared-stores.js +100 -0
  228. package/dist/core/shared-stores.js.map +1 -0
  229. package/dist/core/skills.d.ts.map +1 -1
  230. package/dist/core/skills.js +14 -10
  231. package/dist/core/skills.js.map +1 -1
  232. package/dist/run.d.ts.map +1 -1
  233. package/dist/run.js +122 -6
  234. package/dist/run.js.map +1 -1
  235. package/dist/version.d.ts +1 -1
  236. package/dist/version.js +1 -1
  237. package/package.json +1 -1
  238. package/templates/agents/shared-stores.md +11 -0
  239. package/templates/approve/copies-missing.md +1 -0
  240. package/templates/approve/elsewhere-heading.md +1 -0
  241. package/templates/approve/elsewhere-intro.md +1 -0
  242. package/templates/approve/homed-elsewhere.md +1 -0
  243. package/templates/approve/not-decider.md +1 -0
  244. package/templates/approve/part-heading.md +1 -0
  245. package/templates/approve/parts-changed.md +1 -0
  246. package/templates/approve/session-decision-note.md +1 -0
  247. package/templates/approve/session-intro.md +1 -0
  248. package/templates/approve/session-no-decision.md +1 -0
  249. package/templates/approve/store-unreachable.md +1 -0
  250. package/templates/land/resolver.md +2 -0
  251. package/templates/second-opinion/poll-answer.md +7 -0
  252. package/templates/second-opinion/taste-panel.md +14 -0
  253. package/templates/session/pages/bare-route.md +2 -0
  254. package/templates/session/pages/block.md +8 -0
  255. package/templates/session/pages/condition-preview.md +1 -0
  256. package/templates/session/pages/condition.md +1 -0
  257. package/templates/session/pages/moved.md +1 -0
  258. package/templates/session/pages/stops-working.md +1 -0
  259. package/templates/session/pages/table-head-preview.md +2 -0
  260. package/templates/session/pages/table-head.md +2 -0
  261. package/templates/skills/ctxr-connection-finding.md +4 -1
  262. package/templates/skills/ctxr-land.md +26 -7
  263. package/templates/skills/ctxr-placement.md +10 -0
  264. package/templates/skills/ctxr-rollup.md +3 -0
  265. package/templates/skills/ctxr-second-opinion.md +52 -36
  266. package/templates/skills/ctxr-session-lifecycle.md +60 -33
  267. package/templates/skills/ctxr-submit.md +65 -56
  268. package/templates/skills/ctxr-second-opinion/run.mjs +0 -542
  269. /package/templates/{skills/ctxr-second-opinion → second-opinion}/critic-contract.md +0 -0
  270. /package/templates/{skills/ctxr-second-opinion → second-opinion}/lenses.md +0 -0
  271. /package/templates/{skills/ctxr-second-opinion → second-opinion}/personas/architect.md +0 -0
  272. /package/templates/{skills/ctxr-second-opinion → second-opinion}/personas/pragmatist.md +0 -0
  273. /package/templates/{skills/ctxr-second-opinion → second-opinion}/personas/skeptic.md +0 -0
@@ -0,0 +1,65 @@
1
+ import { SecondOpinionUsageError } from '../errors.js';
2
+ import { packagedTemplate, substituteBlock } from '../templates.js';
3
+ /**
4
+ * run-the-second-opinion-from-the-cli D4: the critic contract, the persona
5
+ * mandates, the lens roster and the poll scaffolds are package templates
6
+ * under `templates/second-opinion/`, reviewable as markdown. This module only
7
+ * assembles them; it carries no prompt prose of its own.
8
+ */
9
+ const template = (name) => packagedTemplate('second-opinion', name);
10
+ export function critiquePrompt(role, plan) {
11
+ return `${template('critic-contract').trimEnd()}\n\n---\n${template(`personas/${role}`).trimEnd()}\n\nPLAN:\n${plan}\n`;
12
+ }
13
+ /** The built-in lenses, by name, from the roster's `- **name** — description` lines. */
14
+ export function lensRoster() {
15
+ const roster = new Map();
16
+ for (const m of template('lenses').matchAll(/^- \*\*([a-z-]+)\*\* — (.+)$/gm))
17
+ roster.set(m[1], m[2].trim());
18
+ return roster;
19
+ }
20
+ const INLINE_LENS = /^[^:,]{1,40}:/;
21
+ /**
22
+ * Splits `--lens` into one string per lens. Lenses are comma-separated, but an inline description may itself
23
+ * contain commas: a fragment starts a NEW lens only when it is exactly a roster name or begins `Name:`;
24
+ * any other fragment continues the lens before it. So `minimalist,House taste:blunt, anti-slogan` is two
25
+ * lenses, and a description must not contain a colon (that would read as the start of another lens).
26
+ */
27
+ export function splitLenses(spec, roster) {
28
+ const lenses = [];
29
+ for (const fragment of spec.split(',').map((f) => f.trim()).filter(Boolean)) {
30
+ const startsLens = roster.has(fragment.toLowerCase()) || INLINE_LENS.test(fragment);
31
+ if (startsLens || lenses.length === 0)
32
+ lenses.push(fragment);
33
+ else
34
+ lenses[lenses.length - 1] += `, ${fragment}`;
35
+ }
36
+ return lenses;
37
+ }
38
+ /** One `- name — description` line per lens; refuses an unknown lens and a count outside two to three. */
39
+ export function lensLines(spec) {
40
+ const roster = lensRoster();
41
+ const described = [];
42
+ for (const s of splitLenses(spec, roster)) {
43
+ const known = roster.get(s.toLowerCase());
44
+ if (known !== undefined)
45
+ described.push(`- ${s.toLowerCase()} — ${known}`);
46
+ else if (INLINE_LENS.test(s)) {
47
+ const [name, ...rest] = s.split(':');
48
+ described.push(`- ${name.trim()} — ${rest.join(':').trim()}`);
49
+ }
50
+ else {
51
+ throw new SecondOpinionUsageError(`unknown lens "${s}" (known: ${[...roster.keys()].join(', ')}; or pass "Name:description")`);
52
+ }
53
+ }
54
+ if (described.length < 2 || described.length > 3)
55
+ throw new SecondOpinionUsageError('--lens takes two or three lenses; keep them orthogonal');
56
+ return described;
57
+ }
58
+ export function pollPrompt(question, opts) {
59
+ if (opts.lens) {
60
+ const withLenses = substituteBlock(template('taste-panel'), '__LENSES__', lensLines(opts.lens));
61
+ return `${substituteBlock(withLenses, '__QUESTION__', question.split('\n'))}\n`;
62
+ }
63
+ return opts.wrap ? `${question}\n\n${template('poll-answer')}` : question;
64
+ }
65
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../../src/core/second-opinion/prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGpE;;;;;GAKG;AACH,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;AAEpF,MAAM,UAAU,cAAc,CAAC,IAAU,EAAE,IAAY;IACrD,OAAO,GAAG,QAAQ,CAAC,iBAAiB,CAAC,CAAC,OAAO,EAAE,YAAY,QAAQ,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,cAAc,IAAI,IAAI,CAAC;AAC1H,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,UAAU;IACxB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,gCAAgC,CAAC;QAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/G,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,WAAW,GAAG,eAAe,CAAC;AAEpC;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,MAAmC;IAC3E,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5E,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpF,IAAI,UAAU,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;;YACxD,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;IACpD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,0GAA0G;AAC1G,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QAC1C,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,MAAM,KAAK,EAAE,CAAC,CAAC;aACtE,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7B,MAAM,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACrC,SAAS,CAAC,IAAI,CAAC,KAAK,IAAK,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACjE,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,uBAAuB,CAAC,iBAAiB,CAAC,aAAa,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QACjI,CAAC;IACH,CAAC;IACD,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,uBAAuB,CAAC,wDAAwD,CAAC,CAAC;IAC9I,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,QAAgB,EAAE,IAAsC;IACjF,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,UAAU,GAAG,eAAe,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,YAAY,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAChG,OAAO,GAAG,eAAe,CAAC,UAAU,EAAE,cAAc,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;IAClF,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,QAAQ,OAAO,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC5E,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { GitResult, GitRunner } from '../git/exec.js';
2
+ /**
3
+ * bring-the-session-lifecycle-into-the-cli D4: one commit call for both
4
+ * surfaces. Locally it commits as the clone's own git identity, exactly as a
5
+ * hand-run `git commit` does. The MCP surface passes an author override (the
6
+ * member's verified email) with a trailer naming them, and the recording
7
+ * login as committer when the host has no identity of its own — the same
8
+ * fallback `ctxr land` uses. The store's hooks run either way.
9
+ */
10
+ export interface CommitOptions {
11
+ message: string;
12
+ /** The commit's author, in place of the clone's identity. */
13
+ author?: {
14
+ name: string;
15
+ email: string;
16
+ };
17
+ /** A trailer paragraph, e.g. `Edited-by: <email>`. */
18
+ trailer?: {
19
+ key: string;
20
+ value: string;
21
+ };
22
+ /** The committer when neither git config nor the environment names one; omitted, git decides. */
23
+ fallbackCommitter?: string;
24
+ }
25
+ /** The committer: the host's git identity when it has one, else the given login's noreply address. */
26
+ export declare function committerEnv(git: GitRunner, cwd: string, env: Readonly<Record<string, string | undefined>>, login: string): Promise<Record<string, string>>;
27
+ /** Commits what is staged in `cwd`. Resolves with git's result; the caller decides what a refusal means on its surface. */
28
+ export declare function commitStaged(git: GitRunner, cwd: string, env: Readonly<Record<string, string | undefined>>, opts: CommitOptions): Promise<GitResult>;
29
+ //# sourceMappingURL=commit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commit.d.ts","sourceRoot":"","sources":["../../../src/core/session/commit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3D;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,sDAAsD;IACtD,OAAO,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,iGAAiG;IACjG,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,sGAAsG;AACtG,wBAAsB,YAAY,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAKjK;AAED,2HAA2H;AAC3H,wBAAsB,YAAY,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,CAAC,CAQ1J"}
@@ -0,0 +1,22 @@
1
+ /** The committer: the host's git identity when it has one, else the given login's noreply address. */
2
+ export async function committerEnv(git, cwd, env, login) {
3
+ const email = (await git.run(['config', '--get', 'user.email'], { cwd, allowFailure: true })).stdout.trim();
4
+ if (email !== '' || env.GIT_COMMITTER_EMAIL || env.EMAIL)
5
+ return {};
6
+ const address = `${login.replace(/\[bot\]$/, '')}@users.noreply.github.com`;
7
+ return { GIT_COMMITTER_NAME: login, GIT_COMMITTER_EMAIL: address };
8
+ }
9
+ /** Commits what is staged in `cwd`. Resolves with git's result; the caller decides what a refusal means on its surface. */
10
+ export async function commitStaged(git, cwd, env, opts) {
11
+ const identity = {};
12
+ if (opts.fallbackCommitter !== undefined)
13
+ Object.assign(identity, await committerEnv(git, cwd, env, opts.fallbackCommitter));
14
+ if (opts.author !== undefined)
15
+ Object.assign(identity, { GIT_AUTHOR_NAME: opts.author.name, GIT_AUTHOR_EMAIL: opts.author.email });
16
+ const args = ['commit', '--quiet', '-m', opts.message];
17
+ if (opts.trailer !== undefined)
18
+ args.push('-m', `${opts.trailer.key}: ${opts.trailer.value}`);
19
+ const overridden = Object.keys(identity).length > 0;
20
+ return git.run(args, { cwd, allowFailure: true, ...(overridden ? { env: { ...env, ...identity } } : {}) });
21
+ }
22
+ //# sourceMappingURL=commit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commit.js","sourceRoot":"","sources":["../../../src/core/session/commit.ts"],"names":[],"mappings":"AAoBA,sGAAsG;AACtG,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAc,EAAE,GAAW,EAAE,GAAiD,EAAE,KAAa;IAC9H,MAAM,KAAK,GAAG,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,YAAY,CAAC,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC5G,IAAI,KAAK,KAAK,EAAE,IAAI,GAAG,CAAC,mBAAmB,IAAI,GAAG,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IACpE,MAAM,OAAO,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,2BAA2B,CAAC;IAC5E,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAE,mBAAmB,EAAE,OAAO,EAAE,CAAC;AACrE,CAAC;AAED,2HAA2H;AAC3H,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAc,EAAE,GAAW,EAAE,GAAiD,EAAE,IAAmB;IACpI,MAAM,QAAQ,GAA2B,EAAE,CAAC;IAC5C,IAAI,IAAI,CAAC,iBAAiB,KAAK,SAAS;QAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC;IAC7H,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;QAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,eAAe,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,gBAAgB,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IACnI,MAAM,IAAI,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACvD,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS;QAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IAC9F,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IACpD,OAAO,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,GAAG,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;AAC7G,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { type RemoteDeps } from './remote.js';
2
+ import { type SessionRef } from './resolve.js';
3
+ /**
4
+ * write-lifecycle spec (*`ctxr session discard` removes only a session, and
5
+ * only clean unless forced*): #31's `abandon` could force-remove any worktree
6
+ * (bring-the-session-lifecycle-into-the-cli D1, point 4). `resolveSession`
7
+ * has already refused anything that is not a session; this refuses, without
8
+ * `--force`, a session whose removal would lose work — uncommitted changes, or
9
+ * commits its remote branch lacks — and closes a pull request only when asked.
10
+ */
11
+ export interface DiscardResult {
12
+ session: string;
13
+ worktree: string;
14
+ branch: string;
15
+ removed: boolean;
16
+ forced: boolean;
17
+ /** The pull request `--close-pull-request` closed; null when none was asked for or none was open. */
18
+ closed_pull_request: number | null;
19
+ }
20
+ export declare function discardSession(deps: RemoteDeps, session: SessionRef, opts?: {
21
+ force?: boolean;
22
+ closePullRequest?: boolean;
23
+ }): Promise<DiscardResult>;
24
+ //# sourceMappingURL=discard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discard.d.ts","sourceRoot":"","sources":["../../../src/core/session/discard.ts"],"names":[],"mappings":"AAIA,OAAO,EAAgD,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAC5F,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/D;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,qGAAqG;IACrG,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAiCD,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CAqC9J"}
@@ -0,0 +1,68 @@
1
+ import { baseRef, forgeRead } from '../approval/sessions.js';
2
+ import { SessionRefusedError } from '../errors.js';
3
+ import { ExitCode } from '../exit-codes.js';
4
+ import { ForgeRequestError } from '../forge/github.js';
5
+ import { removeSession, remoteTip, SessionRemoteError } from './remote.js';
6
+ import { workingChanges } from './resolve.js';
7
+ /**
8
+ * The session's commits that are on no remote ref: not on its remote branch,
9
+ * nor any other this clone has fetched. A session whose pull request was
10
+ * squash-merged and whose branch was then deleted carries commits on no
11
+ * remote ref, so a merged pull request at exactly this head counts as published.
12
+ */
13
+ async function unpublishedCommits(deps, session) {
14
+ const run = (args) => deps.git.run(args, { cwd: session.path, allowFailure: true });
15
+ const tip = (await run(['rev-parse', 'HEAD'])).stdout.trim();
16
+ let remote = null;
17
+ try {
18
+ remote = await remoteTip(deps, session.path, session.branch);
19
+ }
20
+ catch (err) {
21
+ if (!(err instanceof SessionRemoteError))
22
+ throw err;
23
+ }
24
+ if (remote === tip)
25
+ return 0;
26
+ const exclude = remote !== null && (await run(['cat-file', '-e', `${remote}^{commit}`])).exitCode === 0 ? [`^${remote}`] : [];
27
+ // The default branch itself never counts: a session's base is already on it.
28
+ const base = await baseRef(deps.git, session.path, deps.config.git.default_branch);
29
+ const counted = await run(['rev-list', '--count', 'HEAD', ...exclude, `^${base}`, '--not', '--remotes=origin']);
30
+ const count = counted.exitCode === 0 ? Number.parseInt(counted.stdout.trim(), 10) || 0 : 0;
31
+ if (count === 0)
32
+ return 0;
33
+ try {
34
+ const pulls = await deps.forge.pullsForHead(session.branch);
35
+ if (pulls.some((p) => p.merged && p.headSha === tip))
36
+ return 0;
37
+ }
38
+ catch (err) {
39
+ if (!(err instanceof ForgeRequestError))
40
+ throw err;
41
+ }
42
+ return count;
43
+ }
44
+ export async function discardSession(deps, session, opts = {}) {
45
+ const force = opts.force === true;
46
+ if (!force) {
47
+ const dirty = await workingChanges(deps.git, session.path);
48
+ if (dirty.length > 0) {
49
+ throw new SessionRefusedError(ExitCode.CheckFailed, 'discard_uncommitted', `"${session.name}" has uncommitted changes (${dirty.map((c) => c.path).join(', ')}); discarding it would destroy them. Pass --force to discard them anyway.`, { session: session.name, paths: dirty.map((c) => c.path) }, session.name);
50
+ }
51
+ const unpublished = await unpublishedCommits(deps, session);
52
+ if (unpublished > 0) {
53
+ throw new SessionRefusedError(ExitCode.CheckFailed, 'discard_unpushed', `"${session.name}" carries ${unpublished} commit(s) its remote branch lacks; discarding it would destroy them. Pass --force to discard them anyway.`, { session: session.name, commits: unpublished }, session.name);
54
+ }
55
+ }
56
+ let closed = null;
57
+ if (opts.closePullRequest === true) {
58
+ // Before anything is removed, so a forge that cannot be reached leaves the session as it was.
59
+ const open = (await forgeRead(() => deps.forge.pullsForHead(session.branch))).find((p) => p.state === 'open');
60
+ if (open !== undefined) {
61
+ await forgeRead(() => deps.forge.updatePull(open.number, { state: 'closed' }));
62
+ closed = open.number;
63
+ }
64
+ }
65
+ await removeSession(deps.git, session.canonical, session);
66
+ return { session: session.name, worktree: session.path, branch: session.branch, removed: true, forced: force, closed_pull_request: closed };
67
+ }
68
+ //# sourceMappingURL=discard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discard.js","sourceRoot":"","sources":["../../../src/core/session/discard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import type { StoreConfig } from '../../config/schema.js';
2
+ import type { ReviewStateName } from '../approval/decision.js';
3
+ import { type ForgeClient } from '../forge/github.js';
4
+ import type { GitRunner } from '../git/exec.js';
5
+ /**
6
+ * write-lifecycle spec (*`ctxr session list` reports each session's state*):
7
+ * each session's branch, worktree, head, uncommitted state and commits ahead,
8
+ * all read locally. With `review`, each one's open pull request and, when the
9
+ * store declares change approval, the decision `ctxr review list` reports for
10
+ * it. The forge stays behind the option so a plain list never waits on the
11
+ * network; an unreachable forge reads `unknown`, because the list is an
12
+ * inventory, not a check (bring-the-session-lifecycle-into-the-cli D3,
13
+ * revision 1).
14
+ */
15
+ export interface SessionListEntry {
16
+ /** The worktree's directory name: what the other session commands take. */
17
+ name: string;
18
+ branch: string;
19
+ worktree: string;
20
+ head: string;
21
+ uncommitted: boolean;
22
+ ahead: number;
23
+ /** `review` only: the open pull request, null when none is open. */
24
+ pull_request?: {
25
+ number: number | null;
26
+ state: 'open' | 'unknown';
27
+ url: string | null;
28
+ } | null;
29
+ /** `review` with change approval declared: the decision on the open pull request. */
30
+ review?: {
31
+ state: ReviewStateName | 'unknown';
32
+ comment: string | null;
33
+ } | null;
34
+ }
35
+ export interface ListReview {
36
+ forge: ForgeClient;
37
+ /** `change_approval.recording_login`; null when the store declares no change approval. */
38
+ recordingLogin: string | null;
39
+ }
40
+ export interface SessionList {
41
+ sessions: SessionListEntry[];
42
+ /** `review` only: whether the forge answered. */
43
+ forge_reachable?: boolean;
44
+ }
45
+ export declare function listSessions(git: GitRunner, storeRoot: string, config: StoreConfig, opts?: {
46
+ review?: ListReview;
47
+ }): Promise<SessionList>;
48
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/core/session/list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,EAAqB,KAAK,WAAW,EAAoB,MAAM,oBAAoB,CAAC;AAC3F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAGhD;;;;;;;;;GASG;AACH,MAAM,WAAW,gBAAgB;IAC/B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,OAAO,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,YAAY,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;IAC/F,qFAAqF;IACrF,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,eAAe,GAAG,SAAS,CAAC;QAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;CAChF;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,WAAW,CAAC;IACnB,0FAA0F;IAC1F,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,iDAAiD;IACjD,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAWD,wBAAsB,YAAY,CAAC,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,UAAU,CAAA;CAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CA0CnJ"}
@@ -0,0 +1,55 @@
1
+ import { baseRef, reviewStateOf, sessionWorktrees } from '../approval/sessions.js';
2
+ import { ForgeRequestError } from '../forge/github.js';
3
+ import { commitsAhead, workingChanges } from './resolve.js';
4
+ async function forgeOr(call) {
5
+ try {
6
+ return await call();
7
+ }
8
+ catch (err) {
9
+ if (err instanceof ForgeRequestError)
10
+ return 'unknown';
11
+ throw err;
12
+ }
13
+ }
14
+ export async function listSessions(git, storeRoot, config, opts = {}) {
15
+ const worktrees = await sessionWorktrees(git, storeRoot, config);
16
+ const base = await baseRef(git, storeRoot, config.git.default_branch);
17
+ const sessions = await Promise.all(worktrees.map(async (w) => ({
18
+ name: w.name,
19
+ branch: w.branch,
20
+ worktree: w.path,
21
+ head: (await git.run(['rev-parse', 'HEAD'], { cwd: w.path, allowFailure: true })).stdout.trim(),
22
+ uncommitted: (await workingChanges(git, w.path)).length > 0,
23
+ ahead: await commitsAhead(git, w.path, base),
24
+ })));
25
+ if (opts.review === undefined)
26
+ return { sessions };
27
+ const { forge, recordingLogin } = opts.review;
28
+ const open = await forgeOr(() => forge.openPulls());
29
+ let reachable = open !== 'unknown';
30
+ await Promise.all(sessions.map(async (entry) => {
31
+ if (open === 'unknown') {
32
+ entry.pull_request = { number: null, state: 'unknown', url: null };
33
+ if (recordingLogin !== null)
34
+ entry.review = { state: 'unknown', comment: null };
35
+ return;
36
+ }
37
+ const pull = open.find((p) => p.headRef === entry.branch);
38
+ entry.pull_request = pull === undefined ? null : { number: pull.number, state: 'open', url: pull.url };
39
+ if (recordingLogin === null)
40
+ return;
41
+ if (pull === undefined) {
42
+ entry.review = null;
43
+ return;
44
+ }
45
+ const review = await forgeOr(() => reviewStateOf(git, entry.worktree, base, forge, recordingLogin, pull));
46
+ if (review === 'unknown') {
47
+ reachable = false;
48
+ entry.review = { state: 'unknown', comment: null };
49
+ return;
50
+ }
51
+ entry.review = { state: review.state, comment: review.state === 'changes-requested' ? (review.decision?.comment ?? null) : null };
52
+ }));
53
+ return { sessions, forge_reachable: reachable };
54
+ }
55
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/core/session/list.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { ForgeCredentials } from '../forge/credentials.js';
2
+ import { type SessionRef } from './resolve.js';
3
+ import { type StatusDeps } from './status.js';
4
+ /**
5
+ * write-lifecycle spec (*`ctxr session rebase` brings a session onto the
6
+ * default branch*): the conflict playbook's mechanics. The judgment — reading
7
+ * both sides and writing a version that keeps both meanings — stays the
8
+ * agent's; this fetches, rebases, reports each conflict with both sides, and
9
+ * stages and continues once the agent has resolved them
10
+ * (bring-the-session-lifecycle-into-the-cli D3).
11
+ */
12
+ export interface Conflict {
13
+ /** Relative to the worktree's top level. */
14
+ path: string;
15
+ /** The default branch's version, with the session's commits applied so far; null when that side deleted the file. */
16
+ default_branch: string | null;
17
+ /** The version in the session commit being replayed; null when it deleted the file. */
18
+ session: string | null;
19
+ }
20
+ export type RebaseOutcome = 'rebased' | 'up-to-date' | 'conflict' | 'aborted';
21
+ export interface RebaseResult {
22
+ session: string;
23
+ branch: string;
24
+ onto: string;
25
+ fetched: boolean;
26
+ outcome: RebaseOutcome;
27
+ head: string;
28
+ conflicts: Conflict[];
29
+ }
30
+ export type RebaseMode = 'start' | 'continue' | 'abort';
31
+ export declare function rebaseSession(deps: StatusDeps & {
32
+ credentials: ForgeCredentials;
33
+ }, session: SessionRef, mode: RebaseMode): Promise<RebaseResult>;
34
+ //# sourceMappingURL=rebase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rebase.d.ts","sourceRoot":"","sources":["../../../src/core/session/rebase.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,OAAO,EAAoC,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AACjF,OAAO,EAAsB,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAElE;;;;;;;GAOG;AACH,MAAM,WAAW,QAAQ;IACvB,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,qHAAqH;IACrH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,uFAAuF;IACvF,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,YAAY,GAAG,UAAU,GAAG,SAAS,CAAC;AAE9E,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,aAAa,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,QAAQ,EAAE,CAAC;CACvB;AAED,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,OAAO,CAAC;AA0BxD,wBAAsB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG;IAAE,WAAW,EAAE,gBAAgB,CAAA;CAAE,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CAsDtJ"}
@@ -0,0 +1,87 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { baseRef } from '../approval/sessions.js';
4
+ import { SessionRefusedError } from '../errors.js';
5
+ import { ExitCode } from '../exit-codes.js';
6
+ import { rebaseInProgress, workingChanges } from './resolve.js';
7
+ import { fetchDefaultBranch } from './status.js';
8
+ const MARKER = /^(<{7}|>{7})( |$)/m;
9
+ async function stageContent(git, cwd, stage, file) {
10
+ const shown = await git.run(['show', `:${stage}:${file}`], { cwd, allowFailure: true });
11
+ return shown.exitCode === 0 ? shown.stdout : null;
12
+ }
13
+ async function conflictedPaths(git, cwd) {
14
+ const { stdout } = await git.run(['diff', '--name-only', '--diff-filter=U', '-z'], { cwd, allowFailure: true });
15
+ return stdout.split('\0').filter((p) => p !== '');
16
+ }
17
+ async function conflicts(git, cwd) {
18
+ const found = [];
19
+ for (const file of await conflictedPaths(git, cwd)) {
20
+ found.push({ path: file, default_branch: await stageContent(git, cwd, 2, file), session: await stageContent(git, cwd, 3, file) });
21
+ }
22
+ return found;
23
+ }
24
+ async function head(git, cwd) {
25
+ return (await git.run(['rev-parse', 'HEAD'], { cwd })).stdout.trim();
26
+ }
27
+ export async function rebaseSession(deps, session, mode) {
28
+ const { git } = deps;
29
+ const cwd = session.path;
30
+ const inProgress = await rebaseInProgress(git, cwd);
31
+ const result = async (outcome, onto, fetched) => ({
32
+ session: session.name,
33
+ branch: session.branch,
34
+ onto,
35
+ fetched,
36
+ outcome,
37
+ head: await head(git, cwd),
38
+ conflicts: outcome === 'conflict' ? await conflicts(git, cwd) : [],
39
+ });
40
+ const onto = await baseRef(git, cwd, deps.config.git.default_branch);
41
+ // A rebase replays commits, so its editor must never wait on a person.
42
+ const rebase = (args) => git.run(['-c', 'core.editor=true', 'rebase', ...args], { cwd, allowFailure: true, env: { ...deps.env, GIT_EDITOR: 'true' } });
43
+ if (mode === 'abort') {
44
+ if (!inProgress)
45
+ throw new SessionRefusedError(ExitCode.Usage, 'no_rebase', `No rebase is in progress in "${session.name}", so there is nothing to abort.`, { session: session.name });
46
+ await rebase(['--abort']);
47
+ return result('aborted', onto, false);
48
+ }
49
+ if (mode === 'continue') {
50
+ if (!inProgress)
51
+ throw new SessionRefusedError(ExitCode.Usage, 'no_rebase', `No rebase is in progress in "${session.name}", so there is nothing to continue.`, { session: session.name });
52
+ const unresolved = [];
53
+ for (const file of await conflictedPaths(git, cwd)) {
54
+ const working = await readFile(path.join(cwd, file), 'utf8').catch(() => null);
55
+ if (working !== null && MARKER.test(working))
56
+ unresolved.push(file);
57
+ }
58
+ if (unresolved.length > 0) {
59
+ throw new SessionRefusedError(ExitCode.CheckFailed, 'conflict_unresolved', `Conflict markers remain in ${unresolved.map((f) => `"${f}"`).join(', ')}; resolve them before continuing.`, { session: session.name, paths: unresolved });
60
+ }
61
+ for (const file of await conflictedPaths(git, cwd))
62
+ await git.run(['add', '-A', '--', file], { cwd, allowFailure: true });
63
+ const continued = await rebase(['--continue']);
64
+ if (continued.exitCode === 0)
65
+ return result('rebased', onto, false);
66
+ if (await rebaseInProgress(git, cwd))
67
+ return result('conflict', onto, false);
68
+ throw new SessionRefusedError(ExitCode.CheckFailed, 'rebase_failed', `The rebase could not continue: ${continued.stderr.trim() || continued.stdout.trim()}`, { session: session.name });
69
+ }
70
+ if (inProgress) {
71
+ throw new SessionRefusedError(ExitCode.Usage, 'rebase_in_progress', `A rebase is already in progress in "${session.name}"; finish it with \`ctxr session rebase ${session.name} --continue\` or undo it with \`--abort\`.`, { session: session.name });
72
+ }
73
+ const dirty = await workingChanges(git, cwd);
74
+ if (dirty.length > 0) {
75
+ throw new SessionRefusedError(ExitCode.CheckFailed, 'uncommitted_changes', `"${session.name}" has uncommitted changes (${dirty.map((c) => c.path).join(', ')}); submit or set them aside before rebasing.`, { session: session.name, paths: dirty.map((c) => c.path) });
76
+ }
77
+ const fetched = await fetchDefaultBranch(deps, cwd);
78
+ const base = await baseRef(git, cwd, deps.config.git.default_branch);
79
+ const before = await head(git, cwd);
80
+ const rebased = await rebase(['--quiet', base]);
81
+ if (rebased.exitCode === 0)
82
+ return result((await head(git, cwd)) === before ? 'up-to-date' : 'rebased', base, fetched);
83
+ if (await rebaseInProgress(git, cwd))
84
+ return result('conflict', base, fetched);
85
+ throw new SessionRefusedError(ExitCode.CheckFailed, 'rebase_failed', `The rebase could not start: ${rebased.stderr.trim() || rebased.stdout.trim()}`, { session: session.name });
86
+ }
87
+ //# sourceMappingURL=rebase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rebase.js","sourceRoot":"","sources":["../../../src/core/session/rebase.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ import type { StoreConfig } from '../../config/schema.js';
2
+ import { type ForgeCredentials } from '../forge/credentials.js';
3
+ import type { ForgeClient, PullRequest } from '../forge/github.js';
4
+ import type { GitRunner } from '../git/exec.js';
5
+ /**
6
+ * bring-the-session-lifecycle-into-the-cli (design.md Migration Plan, step 2):
7
+ * the session's remote side — push, publish (push plus the pull request, found
8
+ * by head before one is opened) and remove — moved here from the MCP surface's
9
+ * `sessions.ts`, so `ctxr session submit`, `discard` and the MCP tools run one
10
+ * implementation. Failures are thrown as `SessionRemoteError`, naming the step,
11
+ * and each caller words them for its own surface.
12
+ */
13
+ export interface RemoteDeps {
14
+ git: GitRunner;
15
+ config: StoreConfig;
16
+ credentials: ForgeCredentials;
17
+ forge: ForgeClient;
18
+ env: Readonly<Record<string, string | undefined>>;
19
+ }
20
+ /** The worktree and branch a remote step acts on. */
21
+ export interface SessionTarget {
22
+ path: string;
23
+ branch: string;
24
+ }
25
+ export type PullState = {
26
+ kind: 'open';
27
+ pull: PullRequest;
28
+ } | {
29
+ kind: 'merged';
30
+ } | {
31
+ kind: 'closed-unmerged';
32
+ } | {
33
+ kind: 'none';
34
+ };
35
+ export type RemoteStep = 'push' | 'pull_request';
36
+ /** A push or forge call that did not succeed; everything before it stays in place, so the same call can be retried. */
37
+ export declare class SessionRemoteError extends Error {
38
+ readonly step: RemoteStep;
39
+ readonly detail: string;
40
+ constructor(step: RemoteStep, detail: string);
41
+ }
42
+ export declare function firstLines(text: string, count?: number): string;
43
+ /** What the forge says about the pull requests for `branch`: the open one, else merged, else closed, else none. */
44
+ export declare function pullStateFor(forge: ForgeClient, branch: string): Promise<PullState>;
45
+ /** The remote branch's tip, or null when the remote has no such branch. Throws when the remote cannot be read at all. */
46
+ export declare function remoteTip(deps: RemoteDeps, cwd: string, branch: string): Promise<string | null>;
47
+ export type PushOutcome = 'up-to-date' | 'pushed' | 'pushed-with-lease';
48
+ /**
49
+ * Pushes the session's HEAD to its branch when the remote lacks it. Read
50
+ * first, so a retry after a push whose response was lost pushes nothing. A
51
+ * fast-forward is a plain push. A rewritten history (after `ctxr session
52
+ * rebase`) is pushed with a lease on the remote-tracking ref this clone last
53
+ * saw — never on the tip just read, which would overwrite a push made by
54
+ * someone else since (bring-the-session-lifecycle-into-the-cli D3).
55
+ */
56
+ export declare function pushSession(deps: RemoteDeps, session: SessionTarget): Promise<PushOutcome>;
57
+ export interface PublishResult {
58
+ pull: PullRequest;
59
+ push: PushOutcome;
60
+ /** `created` when this call opened it, `updated` when it rewrote an open one's title or body, else `found`. */
61
+ pullRequest: 'created' | 'updated' | 'found';
62
+ }
63
+ /**
64
+ * Publishes whatever the session holds that the forge does not: its branch,
65
+ * pushed when the remote lacks the local tip, and a pull request, found by
66
+ * head before one is opened. Idempotent, so a retry after any partial failure
67
+ * converges on exactly one pull request (serve-the-store-over-mcp D9, D13).
68
+ * With `update`, an open pull request whose title or body differs is rewritten
69
+ * to the given ones — how a resubmitted session answers requested changes on
70
+ * the same pull request (bring-the-session-lifecycle-into-the-cli D3).
71
+ */
72
+ export declare function publishSession(deps: RemoteDeps, session: SessionTarget, pull: {
73
+ title: string;
74
+ body: string;
75
+ }, opts?: {
76
+ update?: boolean;
77
+ }): Promise<PublishResult>;
78
+ /** Removes a session: its worktree and its local branch. The remote branch and any pull request are left as they are. */
79
+ export declare function removeSession(git: GitRunner, canonicalRoot: string, session: SessionTarget): Promise<void>;
80
+ //# sourceMappingURL=remote.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../../../src/core/session/remote.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAgB,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAGhD;;;;;;;GAOG;AAEH,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,SAAS,CAAC;IACf,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,gBAAgB,CAAC;IAC9B,KAAK,EAAE,WAAW,CAAC;IACnB,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;CACnD;AAED,qDAAqD;AACrD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,iBAAiB,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAElI,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,cAAc,CAAC;AAEjD,uHAAuH;AACvH,qBAAa,kBAAmB,SAAQ,KAAK;IAEzC,QAAQ,CAAC,IAAI,EAAE,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM;IAFzB,YACW,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,EAIxB;CACF;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,MAAM,CAE1D;AAED,mHAAmH;AACnH,wBAAsB,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAOzF;AAED,yHAAyH;AACzH,wBAAsB,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAKrG;AAED,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,QAAQ,GAAG,mBAAmB,CAAC;AAMxE;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,WAAW,CAAC,CAqBhG;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,WAAW,CAAC;IAClB,+GAA+G;IAC/G,WAAW,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;CAC9C;AAED;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CAc7K;AAED,yHAAyH;AACzH,wBAAsB,aAAa,CAAC,GAAG,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhH"}