ctxr-cli 0.23.0 → 0.25.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 (197) hide show
  1. package/README.md +65 -5
  2. package/dist/commands/graph-diff.d.ts +33 -0
  3. package/dist/commands/graph-diff.d.ts.map +1 -0
  4. package/dist/commands/graph-diff.js +52 -0
  5. package/dist/commands/graph-diff.js.map +1 -0
  6. package/dist/commands/land.d.ts +50 -0
  7. package/dist/commands/land.d.ts.map +1 -0
  8. package/dist/commands/land.js +414 -0
  9. package/dist/commands/land.js.map +1 -0
  10. package/dist/commands/publish-check.d.ts +6 -0
  11. package/dist/commands/publish-check.d.ts.map +1 -1
  12. package/dist/commands/publish-check.js +78 -3
  13. package/dist/commands/publish-check.js.map +1 -1
  14. package/dist/commands/publish-gather.d.ts.map +1 -1
  15. package/dist/commands/publish-gather.js +4 -1
  16. package/dist/commands/publish-gather.js.map +1 -1
  17. package/dist/commands/publish-new.d.ts +5 -0
  18. package/dist/commands/publish-new.d.ts.map +1 -1
  19. package/dist/commands/publish-new.js +30 -10
  20. package/dist/commands/publish-new.js.map +1 -1
  21. package/dist/commands/publish-stamp.d.ts +32 -0
  22. package/dist/commands/publish-stamp.d.ts.map +1 -0
  23. package/dist/commands/publish-stamp.js +78 -0
  24. package/dist/commands/publish-stamp.js.map +1 -0
  25. package/dist/commands/publish-urls.d.ts +13 -0
  26. package/dist/commands/publish-urls.d.ts.map +1 -1
  27. package/dist/commands/publish-urls.js +23 -6
  28. package/dist/commands/publish-urls.js.map +1 -1
  29. package/dist/commands/review-list.d.ts +34 -0
  30. package/dist/commands/review-list.d.ts.map +1 -0
  31. package/dist/commands/review-list.js +49 -0
  32. package/dist/commands/review-list.js.map +1 -0
  33. package/dist/commands/serve.d.ts +3 -6
  34. package/dist/commands/serve.d.ts.map +1 -1
  35. package/dist/commands/serve.js +106 -9
  36. package/dist/commands/serve.js.map +1 -1
  37. package/dist/config/schema.d.ts +40 -0
  38. package/dist/config/schema.d.ts.map +1 -1
  39. package/dist/config/schema.js +39 -0
  40. package/dist/config/schema.js.map +1 -1
  41. package/dist/core/approval/decision.d.ts +73 -0
  42. package/dist/core/approval/decision.d.ts.map +1 -0
  43. package/dist/core/approval/decision.js +166 -0
  44. package/dist/core/approval/decision.js.map +1 -0
  45. package/dist/core/approval/file-diff.d.ts +32 -0
  46. package/dist/core/approval/file-diff.d.ts.map +1 -0
  47. package/dist/core/approval/file-diff.js +83 -0
  48. package/dist/core/approval/file-diff.js.map +1 -0
  49. package/dist/core/approval/identity.d.ts +34 -0
  50. package/dist/core/approval/identity.d.ts.map +1 -0
  51. package/dist/core/approval/identity.js +105 -0
  52. package/dist/core/approval/identity.js.map +1 -0
  53. package/dist/core/approval/landing-record.d.ts +17 -0
  54. package/dist/core/approval/landing-record.d.ts.map +1 -0
  55. package/dist/core/approval/landing-record.js +30 -0
  56. package/dist/core/approval/landing-record.js.map +1 -0
  57. package/dist/core/approval/resolver.d.ts +15 -0
  58. package/dist/core/approval/resolver.d.ts.map +1 -0
  59. package/dist/core/approval/resolver.js +41 -0
  60. package/dist/core/approval/resolver.js.map +1 -0
  61. package/dist/core/approval/sessions.d.ts +36 -0
  62. package/dist/core/approval/sessions.d.ts.map +1 -0
  63. package/dist/core/approval/sessions.js +76 -0
  64. package/dist/core/approval/sessions.js.map +1 -0
  65. package/dist/core/browse/approve.d.ts +45 -0
  66. package/dist/core/browse/approve.d.ts.map +1 -0
  67. package/dist/core/browse/approve.js +239 -0
  68. package/dist/core/browse/approve.js.map +1 -0
  69. package/dist/core/browse/diagram-assets.d.ts +15 -0
  70. package/dist/core/browse/diagram-assets.d.ts.map +1 -0
  71. package/dist/core/browse/diagram-assets.js +35 -0
  72. package/dist/core/browse/diagram-assets.js.map +1 -0
  73. package/dist/core/browse/page-url.d.ts +22 -0
  74. package/dist/core/browse/page-url.d.ts.map +1 -1
  75. package/dist/core/browse/page-url.js +31 -0
  76. package/dist/core/browse/page-url.js.map +1 -1
  77. package/dist/core/browse/render.d.ts +10 -1
  78. package/dist/core/browse/render.d.ts.map +1 -1
  79. package/dist/core/browse/render.js +23 -1
  80. package/dist/core/browse/render.js.map +1 -1
  81. package/dist/core/browse/review.d.ts +21 -0
  82. package/dist/core/browse/review.d.ts.map +1 -0
  83. package/dist/core/browse/review.js +52 -0
  84. package/dist/core/browse/review.js.map +1 -0
  85. package/dist/core/browse/templates.d.ts +4 -1
  86. package/dist/core/browse/templates.d.ts.map +1 -1
  87. package/dist/core/browse/templates.js +18 -2
  88. package/dist/core/browse/templates.js.map +1 -1
  89. package/dist/core/checks/organize-checks.d.ts +17 -0
  90. package/dist/core/checks/organize-checks.d.ts.map +1 -1
  91. package/dist/core/checks/organize-checks.js +116 -0
  92. package/dist/core/checks/organize-checks.js.map +1 -1
  93. package/dist/core/errors.d.ts +67 -0
  94. package/dist/core/errors.d.ts.map +1 -1
  95. package/dist/core/errors.js +160 -0
  96. package/dist/core/errors.js.map +1 -1
  97. package/dist/core/forge/credentials.d.ts +69 -0
  98. package/dist/core/forge/credentials.d.ts.map +1 -0
  99. package/dist/core/forge/credentials.js +197 -0
  100. package/dist/core/forge/credentials.js.map +1 -0
  101. package/dist/core/forge/github.d.ts +73 -0
  102. package/dist/core/forge/github.d.ts.map +1 -0
  103. package/dist/core/forge/github.js +101 -0
  104. package/dist/core/forge/github.js.map +1 -0
  105. package/dist/core/git/exec.d.ts +25 -0
  106. package/dist/core/git/exec.d.ts.map +1 -1
  107. package/dist/core/git/exec.js +100 -2
  108. package/dist/core/git/exec.js.map +1 -1
  109. package/dist/core/graph/diff-block.d.ts +6 -0
  110. package/dist/core/graph/diff-block.d.ts.map +1 -0
  111. package/dist/core/graph/diff-block.js +63 -0
  112. package/dist/core/graph/diff-block.js.map +1 -0
  113. package/dist/core/graph/diff-document.d.ts +38 -0
  114. package/dist/core/graph/diff-document.d.ts.map +1 -0
  115. package/dist/core/graph/diff-document.js +117 -0
  116. package/dist/core/graph/diff-document.js.map +1 -0
  117. package/dist/core/graph/diff.d.ts +123 -0
  118. package/dist/core/graph/diff.d.ts.map +1 -0
  119. package/dist/core/graph/diff.js +253 -0
  120. package/dist/core/graph/diff.js.map +1 -0
  121. package/dist/core/graph/tree.d.ts +24 -0
  122. package/dist/core/graph/tree.d.ts.map +1 -0
  123. package/dist/core/graph/tree.js +45 -0
  124. package/dist/core/graph/tree.js.map +1 -0
  125. package/dist/core/notes/list.d.ts +10 -0
  126. package/dist/core/notes/list.d.ts.map +1 -1
  127. package/dist/core/notes/list.js +35 -12
  128. package/dist/core/notes/list.js.map +1 -1
  129. package/dist/core/publish/filing.d.ts +12 -12
  130. package/dist/core/publish/filing.d.ts.map +1 -1
  131. package/dist/core/publish/filing.js +20 -18
  132. package/dist/core/publish/filing.js.map +1 -1
  133. package/dist/core/publish/lifespan.d.ts +37 -0
  134. package/dist/core/publish/lifespan.d.ts.map +1 -0
  135. package/dist/core/publish/lifespan.js +59 -0
  136. package/dist/core/publish/lifespan.js.map +1 -0
  137. package/dist/core/publish/represents.d.ts +72 -0
  138. package/dist/core/publish/represents.d.ts.map +1 -0
  139. package/dist/core/publish/represents.js +83 -0
  140. package/dist/core/publish/represents.js.map +1 -0
  141. package/dist/run.d.ts.map +1 -1
  142. package/dist/run.js +60 -4
  143. package/dist/run.js.map +1 -1
  144. package/dist/version.d.ts +1 -1
  145. package/dist/version.js +1 -1
  146. package/package.json +2 -1
  147. package/templates/approve/approve-button.md +1 -0
  148. package/templates/approve/author-heading.md +1 -0
  149. package/templates/approve/author-note.md +1 -0
  150. package/templates/approve/comment-label.md +1 -0
  151. package/templates/approve/comment-required.md +1 -0
  152. package/templates/approve/commits-heading.md +1 -0
  153. package/templates/approve/decision-heading.md +1 -0
  154. package/templates/approve/decision-note.md +1 -0
  155. package/templates/approve/diff-binary.md +1 -0
  156. package/templates/approve/diff-empty.md +1 -0
  157. package/templates/approve/diff-heading.md +1 -0
  158. package/templates/approve/facts-heading.md +1 -0
  159. package/templates/approve/forge-unreachable.md +1 -0
  160. package/templates/approve/github-app-manifest.json +15 -0
  161. package/templates/approve/head-missing.md +1 -0
  162. package/templates/approve/index-empty.md +1 -0
  163. package/templates/approve/index-heading.md +1 -0
  164. package/templates/approve/index-intro.md +1 -0
  165. package/templates/approve/landed.md +1 -0
  166. package/templates/approve/moved-head.md +1 -0
  167. package/templates/approve/no-pull.md +1 -0
  168. package/templates/approve/not-open.md +1 -0
  169. package/templates/approve/pages-heading.md +1 -0
  170. package/templates/approve/pages-note.md +1 -0
  171. package/templates/approve/record-failed.md +1 -0
  172. package/templates/approve/request-changes-button.md +1 -0
  173. package/templates/approve/state-line.md +1 -0
  174. package/templates/conventions/baseline-conventions.md +6 -0
  175. package/templates/graph/knowledge-changes/archived-item.md +1 -0
  176. package/templates/graph/knowledge-changes/archived.md +1 -0
  177. package/templates/graph/knowledge-changes/bare-route.md +2 -0
  178. package/templates/graph/knowledge-changes/block.md +10 -0
  179. package/templates/graph/knowledge-changes/counts.md +1 -0
  180. package/templates/graph/knowledge-changes/dangling-item.md +1 -0
  181. package/templates/graph/knowledge-changes/dangling.md +1 -0
  182. package/templates/graph/knowledge-changes/empty.md +1 -0
  183. package/templates/graph/knowledge-changes/reason-ambiguous.md +1 -0
  184. package/templates/graph/knowledge-changes/reason-not_found.md +1 -0
  185. package/templates/graph/knowledge-changes/review.md +4 -0
  186. package/templates/graph/review/moved-head.md +1 -0
  187. package/templates/land/resolver.md +12 -0
  188. package/templates/serve/diagrams.mjs +136 -0
  189. package/templates/serve/shell.html +1 -1
  190. package/templates/serve/style.css +90 -0
  191. package/templates/skills/ctxr-land.md +37 -31
  192. package/templates/skills/ctxr-organize-audit.md +19 -0
  193. package/templates/skills/ctxr-publish.md +100 -45
  194. package/templates/skills/ctxr-second-opinion/run.mjs +28 -17
  195. package/templates/skills/ctxr-second-opinion.md +19 -14
  196. package/templates/skills/ctxr-session-lifecycle.md +9 -0
  197. package/templates/skills/ctxr-submit.md +28 -17
@@ -22,6 +22,13 @@ export interface Filing {
22
22
  /** The segment naming the subject note, present only when D3 inserts one. */
23
23
  subject_segment: string | null;
24
24
  moves: FilingMove[];
25
+ /**
26
+ * Why no path derives, when `prefix` is null: no folder path names the
27
+ * subject (`store-root`), or the derived path's first segment is the one
28
+ * reserved for disposable pages (`reserved-segment`,
29
+ * two-kinds-of-published-page D2). Null whenever a path derives.
30
+ */
31
+ no_path_reason: 'store-root' | 'reserved-segment' | null;
25
32
  }
26
33
  /**
27
34
  * Re-exported from `core/slug.ts`, which owns the one normalization rule; the
@@ -30,18 +37,11 @@ export interface Filing {
30
37
  */
31
38
  export { slugifyPath, slugifySegment };
32
39
  /**
33
- * design.md D2: the group folder comes from the SELECTOR, never from where the
34
- * resolved notes live. The deepest common ancestor of a real store's resolved
35
- * set collapses to the store root — a page cites the role, the person, and
36
- * three reference notes — which would name no folder at all, file the page flat
37
- * at the publish root, and make `publish check` fail a page this command had
38
- * just told the author to create.
39
- *
40
- * D3: the segment naming the subject note is inserted exactly when that subject
41
- * already has a page, so a one-page subject stays shallow and a multi-page one
42
- * collects; and when the folder path is empty, so a subject at the store root
43
- * still yields a path one level deep and satisfies `checkPageLocation`'s floor
44
- * by construction.
40
+ * two-kinds-of-published-page D2: the first segment under the publish path
41
+ * named `disposable` is reserved for disposable pages, and a store folder named
42
+ * "Disposable" slugifies to exactly it. A path derived into it is reported as no
43
+ * path at all, with the reservation as the reason — the existing "no path
44
+ * derives" outcome with a second reason, so gather still gates nothing.
45
45
  */
46
46
  export declare function deriveFiling(storeRoot: string, publishPath: string, subject: FilingSubject): Promise<Filing>;
47
47
  //# sourceMappingURL=filing.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"filing.d.ts","sourceRoot":"","sources":["../../../src/core/publish/filing.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEzD,8GAA8G;AAC9G,MAAM,WAAW,aAAa;IAC5B,+FAA+F;IAC/F,SAAS,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,oHAAoH;IACpH,UAAU,EAAE,aAAa,GAAG,WAAW,CAAC;CACzC;AAED,MAAM,WAAW,MAAM;IACrB,kHAAkH;IAClH,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,oGAAoG;IACpG,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,uHAAuH;IACvH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,6EAA6E;IAC7E,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAED;;;;GAIG;AACH,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC;AAsFvC;;;;;;;;;;;;;GAaG;AACH,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAmClH"}
1
+ {"version":3,"file":"filing.d.ts","sourceRoot":"","sources":["../../../src/core/publish/filing.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAIzD,8GAA8G;AAC9G,MAAM,WAAW,aAAa;IAC5B,+FAA+F;IAC/F,SAAS,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,oHAAoH;IACpH,UAAU,EAAE,aAAa,GAAG,WAAW,CAAC;CACzC;AAED,MAAM,WAAW,MAAM;IACrB,kHAAkH;IAClH,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,oGAAoG;IACpG,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,uHAAuH;IACvH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,6EAA6E;IAC7E,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB;;;;;OAKG;IACH,cAAc,EAAE,YAAY,GAAG,kBAAkB,GAAG,IAAI,CAAC;CAC1D;AAED;;;;GAIG;AACH,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC;AAsGvC;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAOlH"}
@@ -2,9 +2,9 @@ import { existsSync } from 'node:fs';
2
2
  import { readFile, readdir } from 'node:fs/promises';
3
3
  import path from 'node:path';
4
4
  import { PUBLISH_INDEX_FILE } from '../browse/routes.js';
5
- import { extractLinkTargets } from '../graph/model.js';
6
- import { parseNoteText } from '../notes/parse.js';
7
5
  import { slugifyPath, slugifySegment } from '../slug.js';
6
+ import { DISPOSABLE_SEGMENT } from './lifespan.js';
7
+ import { readLeadLink } from './represents.js';
8
8
  /**
9
9
  * Re-exported from `core/slug.ts`, which owns the one normalization rule; the
10
10
  * filing module was where it was first written and is still where most callers
@@ -47,10 +47,10 @@ async function readmeLeadsWithStem(absolutePageDirectory, stem) {
47
47
  catch {
48
48
  return false;
49
49
  }
50
- // Parsed rather than grepped so frontmatter is excluded, matching how
51
- // `publish check` reads the same file and how the graph resolves a wikilink.
52
- // `extractLinkTargets` reports occurrences in body order, so [0] is the lead.
53
- return extractLinkTargets(parseNoteText(raw, readmePath).body)[0] === stem;
50
+ // two-kinds-of-published-page D4: the one reading of a README's lead link,
51
+ // shared with `publish check`'s represented-note check so the two cannot
52
+ // diverge on whose page a page is.
53
+ return readLeadLink(raw, readmePath)?.target === stem;
54
54
  }
55
55
  async function pagesForNote(publishRoot, group, subjectSlug, noteStem) {
56
56
  const subjectDirectory = group === '' ? subjectSlug : `${group}/${subjectSlug}`;
@@ -69,20 +69,22 @@ async function pagesForNote(publishRoot, group, subjectSlug, noteStem) {
69
69
  return found;
70
70
  }
71
71
  /**
72
- * design.md D2: the group folder comes from the SELECTOR, never from where the
73
- * resolved notes live. The deepest common ancestor of a real store's resolved
74
- * set collapses to the store root — a page cites the role, the person, and
75
- * three reference notes — which would name no folder at all, file the page flat
76
- * at the publish root, and make `publish check` fail a page this command had
77
- * just told the author to create.
78
- *
79
- * D3: the segment naming the subject note is inserted exactly when that subject
80
- * already has a page, so a one-page subject stays shallow and a multi-page one
81
- * collects; and when the folder path is empty, so a subject at the store root
82
- * still yields a path one level deep and satisfies `checkPageLocation`'s floor
83
- * by construction.
72
+ * two-kinds-of-published-page D2: the first segment under the publish path
73
+ * named `disposable` is reserved for disposable pages, and a store folder named
74
+ * "Disposable" slugifies to exactly it. A path derived into it is reported as no
75
+ * path at all, with the reservation as the reason — the existing "no path
76
+ * derives" outcome with a second reason, so gather still gates nothing.
84
77
  */
85
78
  export async function deriveFiling(storeRoot, publishPath, subject) {
79
+ const filing = await deriveUnreservedFiling(storeRoot, publishPath, subject);
80
+ if (filing.prefix === null)
81
+ return { ...filing, no_path_reason: 'store-root' };
82
+ if (filing.prefix.split('/')[0] === DISPOSABLE_SEGMENT) {
83
+ return { prefix: null, path: null, group: filing.group, subject_segment: null, moves: [], no_path_reason: 'reserved-segment' };
84
+ }
85
+ return { ...filing, no_path_reason: null };
86
+ }
87
+ async function deriveUnreservedFiling(storeRoot, publishPath, subject) {
86
88
  const publishPrefix = publishPath.split(path.sep).join('/').replace(/\/+$/, '');
87
89
  const publishRoot = path.join(storeRoot, ...publishPrefix.split('/'));
88
90
  const group = slugifyPath(subject.directory);
@@ -1 +1 @@
1
- {"version":3,"file":"filing.js","sourceRoot":"","sources":["../../../src/core/publish/filing.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"filing.js","sourceRoot":"","sources":["../../../src/core/publish/filing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * two-kinds-of-published-page: a page's lifespan, read from where it is filed
3
+ * and from nothing else (design.md D3). A page whose folder's first segment
4
+ * under the configured publish path is `disposable` is disposable; every
5
+ * other page is permanent, so every page that predates this change is
6
+ * permanent without being touched.
7
+ */
8
+ export type PageLifespan = 'permanent' | 'disposable';
9
+ /**
10
+ * D2: a fixed segment, not a config key — a key would have to be opt-in with
11
+ * no shipped default, which makes the feature off in every store. The plain
12
+ * word can collide with a store folder named "Disposable" (it slugifies to
13
+ * exactly this), so the name is RESERVED rather than unreachable: `publish new`
14
+ * refuses a slug spelled into it without `--disposable`, `publish gather`
15
+ * derives no filing path into it, and `publish check` reads anything filed
16
+ * there as disposable and fails its shape. `_disposable` would have made the
17
+ * collision structurally impossible, and lost on how it reads in a URL.
18
+ */
19
+ export declare const DISPOSABLE_SEGMENT = "disposable";
20
+ /** D6: the window after which lint lists a disposable page as a removal candidate. A code constant, not config. */
21
+ export declare const DISPOSABLE_REVIEW_DAYS = 30;
22
+ /**
23
+ * The lifespan of a page (or any file under it), given its store-relative
24
+ * path. `null` when the path is not under the configured publish path — such
25
+ * a file is not a published page and has no lifespan.
26
+ */
27
+ export declare function lifespanOf(relativePath: string, publishPath: string): PageLifespan | null;
28
+ /**
29
+ * The calendar date leading a disposable page's folder name, as `YYYY-MM-DD`,
30
+ * or `null` when the name carries none or the date is not a real one
31
+ * (`2026-13-45`, `2026-02-30`). Round-tripping through `Date.UTC` is what
32
+ * rejects a day the month does not have.
33
+ */
34
+ export declare function parseDisposableDate(folderName: string): string | null;
35
+ /** Whole days from `date` to `today`, both `YYYY-MM-DD`. */
36
+ export declare function daysBetween(date: string, today: string): number;
37
+ //# sourceMappingURL=lifespan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifespan.d.ts","sourceRoot":"","sources":["../../../src/core/publish/lifespan.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,YAAY,CAAC;AAEtD;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,eAAe,CAAC;AAE/C,mHAAmH;AACnH,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAMzC;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAMzF;AAKD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOrE;AAED,4DAA4D;AAC5D,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/D"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * two-kinds-of-published-page: a page's lifespan, read from where it is filed
3
+ * and from nothing else (design.md D3). A page whose folder's first segment
4
+ * under the configured publish path is `disposable` is disposable; every
5
+ * other page is permanent, so every page that predates this change is
6
+ * permanent without being touched.
7
+ */
8
+ /**
9
+ * D2: a fixed segment, not a config key — a key would have to be opt-in with
10
+ * no shipped default, which makes the feature off in every store. The plain
11
+ * word can collide with a store folder named "Disposable" (it slugifies to
12
+ * exactly this), so the name is RESERVED rather than unreachable: `publish new`
13
+ * refuses a slug spelled into it without `--disposable`, `publish gather`
14
+ * derives no filing path into it, and `publish check` reads anything filed
15
+ * there as disposable and fails its shape. `_disposable` would have made the
16
+ * collision structurally impossible, and lost on how it reads in a URL.
17
+ */
18
+ export const DISPOSABLE_SEGMENT = 'disposable';
19
+ /** D6: the window after which lint lists a disposable page as a removal candidate. A code constant, not config. */
20
+ export const DISPOSABLE_REVIEW_DAYS = 30;
21
+ function normalizedPrefix(publishPath) {
22
+ return publishPath.replace(/\\/g, '/').replace(/\/+$/, '');
23
+ }
24
+ /**
25
+ * The lifespan of a page (or any file under it), given its store-relative
26
+ * path. `null` when the path is not under the configured publish path — such
27
+ * a file is not a published page and has no lifespan.
28
+ */
29
+ export function lifespanOf(relativePath, publishPath) {
30
+ const prefix = normalizedPrefix(publishPath);
31
+ const normalized = relativePath.replace(/\\/g, '/');
32
+ if (!normalized.startsWith(`${prefix}/`))
33
+ return null;
34
+ const firstSegment = normalized.slice(prefix.length + 1).split('/')[0];
35
+ return firstSegment === DISPOSABLE_SEGMENT ? 'disposable' : 'permanent';
36
+ }
37
+ /** The `YYYY-MM-DD-` lead a disposable page's folder name carries, stamped by `ctxr publish new --disposable`. */
38
+ const DISPOSABLE_DATE_RE = /^(\d{4})-(\d{2})-(\d{2})-./;
39
+ /**
40
+ * The calendar date leading a disposable page's folder name, as `YYYY-MM-DD`,
41
+ * or `null` when the name carries none or the date is not a real one
42
+ * (`2026-13-45`, `2026-02-30`). Round-tripping through `Date.UTC` is what
43
+ * rejects a day the month does not have.
44
+ */
45
+ export function parseDisposableDate(folderName) {
46
+ const match = DISPOSABLE_DATE_RE.exec(folderName);
47
+ if (!match)
48
+ return null;
49
+ const [year, month, day] = [Number(match[1]), Number(match[2]), Number(match[3])];
50
+ const date = new Date(Date.UTC(year, month - 1, day));
51
+ if (date.getUTCFullYear() !== year || date.getUTCMonth() !== month - 1 || date.getUTCDate() !== day)
52
+ return null;
53
+ return `${match[1]}-${match[2]}-${match[3]}`;
54
+ }
55
+ /** Whole days from `date` to `today`, both `YYYY-MM-DD`. */
56
+ export function daysBetween(date, today) {
57
+ return Math.round((Date.parse(`${today}T00:00:00Z`) - Date.parse(`${date}T00:00:00Z`)) / 86_400_000);
58
+ }
59
+ //# sourceMappingURL=lifespan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifespan.js","sourceRoot":"","sources":["../../../src/core/publish/lifespan.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,YAAY,CAAC;AAE/C,mHAAmH;AACnH,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAEzC,SAAS,gBAAgB,CAAC,WAAmB;IAC3C,OAAO,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,YAAoB,EAAE,WAAmB;IAClE,MAAM,MAAM,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAC7C,MAAM,UAAU,GAAG,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACpD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACtD,MAAM,YAAY,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,OAAO,YAAY,KAAK,kBAAkB,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC;AAC1E,CAAC;AAED,kHAAkH;AAClH,MAAM,kBAAkB,GAAG,4BAA4B,CAAC;AAExD;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,UAAkB;IACpD,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAClD,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClF,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IACtD,IAAI,IAAI,CAAC,cAAc,EAAE,KAAK,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,KAAK,KAAK,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IACjH,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAC/C,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,KAAa;IACrD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,KAAK,YAAY,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,YAAY,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC;AACvG,CAAC"}
@@ -0,0 +1,72 @@
1
+ import type { Note } from '../notes/list.js';
2
+ /**
3
+ * two-kinds-of-published-page design.md D4: the note a permanent page
4
+ * represents is the FIRST wikilink in its README's body, frontmatter
5
+ * excluded. `filing.ts` reads the same link to decide whose page a page is,
6
+ * so the reading lives here once and both call it — a page `gather` counts as
7
+ * a subject's and a page `check` says represents that subject cannot disagree.
8
+ */
9
+ export interface LeadLink {
10
+ /** The wikilink target as written: the stem before any `|alias` or `#heading`. */
11
+ target: string;
12
+ /** Zero-based index of the raw README line holding it, frontmatter lines counted. */
13
+ line: number;
14
+ }
15
+ /**
16
+ * Parsed rather than grepped so frontmatter is excluded, matching how the graph
17
+ * resolves a wikilink. `extractLinkTargets` reports occurrences in body order,
18
+ * so the first line that yields one holds the lead.
19
+ */
20
+ export declare function readLeadLink(readmeRaw: string, readmePath: string): LeadLink | null;
21
+ export type RepresentedResolution = {
22
+ note: Note;
23
+ } | {
24
+ none: true;
25
+ } | {
26
+ ambiguous: string[];
27
+ };
28
+ /**
29
+ * By filename stem, through the one stem index every wikilink resolution in the
30
+ * system reads (`buildStemIndex`): exactly one note resolves, zero or two-plus
31
+ * do not. The candidates are named so the author can qualify the link.
32
+ */
33
+ export declare function resolveRepresented(notes: readonly Note[], stem: string): RepresentedResolution;
34
+ export declare function readHashMarker(line: string): string | null;
35
+ /**
36
+ * D5: the marker is written once and never overwritten. A line that already
37
+ * carries one comes back unchanged — confirming a page against a changed note
38
+ * takes removing the marker by hand and stamping again.
39
+ */
40
+ export declare function appendHashMarker(line: string, hash: string): string;
41
+ /** The represented note's hash — `contentHashOfBody`, imported and never re-spelled. */
42
+ export declare function representedNoteHash(note: Note): string;
43
+ export type Representation = {
44
+ status: 'no-link';
45
+ } | {
46
+ status: 'not-found';
47
+ target: string;
48
+ } | {
49
+ status: 'ambiguous';
50
+ target: string;
51
+ candidates: string[];
52
+ } | {
53
+ status: 'not-stamped';
54
+ note: string;
55
+ hash: string;
56
+ lead: LeadLink;
57
+ } | {
58
+ status: 'stale';
59
+ note: string;
60
+ hash: string;
61
+ recorded: string;
62
+ lead: LeadLink;
63
+ } | {
64
+ status: 'current';
65
+ note: string;
66
+ hash: string;
67
+ lead: LeadLink;
68
+ };
69
+ /** Where a permanent page stands against its note — one reading for `publish check`, `publish stamp` and lint. */
70
+ export declare function assessRepresentation(readmeRaw: string, readmePath: string, notes: readonly Note[]): Representation;
71
+ export declare function readReadme(readmeAbsolutePath: string): Promise<string | null>;
72
+ //# sourceMappingURL=represents.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"represents.d.ts","sourceRoot":"","sources":["../../../src/core/publish/represents.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAG7C;;;;;;GAMG;AACH,MAAM,WAAW,QAAQ;IACvB,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;IACf,qFAAqF;IACrF,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CASnF;AAED,MAAM,MAAM,qBAAqB,GAAG;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAE9F;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,qBAAqB,CAK9F;AAKD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE1D;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAKnE;AAED,wFAAwF;AACxF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAEtD;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GACrB;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,GAC7D;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GACrE;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GACjF;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEtE,kHAAkH;AAClH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,cAAc,CAWlH;AAED,wBAAsB,UAAU,CAAC,kBAAkB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOnF"}
@@ -0,0 +1,83 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { contentHashOfBody } from '../content/canonicalize.js';
3
+ import { buildStemIndex, extractLinkTargets } from '../graph/model.js';
4
+ import { parseNoteText } from '../notes/parse.js';
5
+ /**
6
+ * Parsed rather than grepped so frontmatter is excluded, matching how the graph
7
+ * resolves a wikilink. `extractLinkTargets` reports occurrences in body order,
8
+ * so the first line that yields one holds the lead.
9
+ */
10
+ export function readLeadLink(readmeRaw, readmePath) {
11
+ const { body } = parseNoteText(readmeRaw, readmePath);
12
+ const frontmatterLines = readmeRaw.slice(0, readmeRaw.length - body.length).split('\n').length - 1;
13
+ const lines = body.split('\n');
14
+ for (let i = 0; i < lines.length; i++) {
15
+ const target = extractLinkTargets(lines[i])[0];
16
+ if (target !== undefined)
17
+ return { target, line: frontmatterLines + i };
18
+ }
19
+ return null;
20
+ }
21
+ /**
22
+ * By filename stem, through the one stem index every wikilink resolution in the
23
+ * system reads (`buildStemIndex`): exactly one note resolves, zero or two-plus
24
+ * do not. The candidates are named so the author can qualify the link.
25
+ */
26
+ export function resolveRepresented(notes, stem) {
27
+ const paths = buildStemIndex(notes).get(stem) ?? [];
28
+ if (paths.length === 0)
29
+ return { none: true };
30
+ if (paths.length > 1)
31
+ return { ambiguous: [...paths].sort() };
32
+ return { note: notes.find((note) => note.path === paths[0]) };
33
+ }
34
+ /** The catalog's frozen-hash form, so an agent that has learned one has learned both (D5). */
35
+ const HASH_MARKER_RE = /\s*<!-- hash:([0-9a-f]{16}) -->/;
36
+ export function readHashMarker(line) {
37
+ return HASH_MARKER_RE.exec(line)?.[1] ?? null;
38
+ }
39
+ /**
40
+ * D5: the marker is written once and never overwritten. A line that already
41
+ * carries one comes back unchanged — confirming a page against a changed note
42
+ * takes removing the marker by hand and stamping again.
43
+ */
44
+ export function appendHashMarker(line, hash) {
45
+ if (readHashMarker(line) !== null)
46
+ return line;
47
+ // A CRLF file's line keeps its `\r`, so nothing but the marker changes.
48
+ const ending = line.endsWith('\r') ? '\r' : '';
49
+ return `${line.replace(/\s+$/, '')} <!-- hash:${hash} -->${ending}`;
50
+ }
51
+ /** The represented note's hash — `contentHashOfBody`, imported and never re-spelled. */
52
+ export function representedNoteHash(note) {
53
+ return contentHashOfBody(note.body);
54
+ }
55
+ /** Where a permanent page stands against its note — one reading for `publish check`, `publish stamp` and lint. */
56
+ export function assessRepresentation(readmeRaw, readmePath, notes) {
57
+ const lead = readLeadLink(readmeRaw, readmePath);
58
+ if (lead === null)
59
+ return { status: 'no-link' };
60
+ const resolved = resolveRepresented(notes, lead.target);
61
+ if ('none' in resolved)
62
+ return { status: 'not-found', target: lead.target };
63
+ if ('ambiguous' in resolved)
64
+ return { status: 'ambiguous', target: lead.target, candidates: resolved.ambiguous };
65
+ const hash = representedNoteHash(resolved.note);
66
+ const recorded = readHashMarker(readmeRaw.split('\n')[lead.line]);
67
+ if (recorded === null)
68
+ return { status: 'not-stamped', note: resolved.note.path, hash, lead };
69
+ if (recorded !== hash)
70
+ return { status: 'stale', note: resolved.note.path, hash, recorded, lead };
71
+ return { status: 'current', note: resolved.note.path, hash, lead };
72
+ }
73
+ export async function readReadme(readmeAbsolutePath) {
74
+ try {
75
+ return await readFile(readmeAbsolutePath, 'utf8');
76
+ }
77
+ catch (err) {
78
+ if (err.code === 'ENOENT')
79
+ return null;
80
+ throw err;
81
+ }
82
+ }
83
+ //# sourceMappingURL=represents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"represents.js","sourceRoot":"","sources":["../../../src/core/publish/represents.ts"],"names":[],"mappings":"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"}
package/dist/run.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAQhD;;;;;GAKG;AACH,eAAO,MAAM,aAAa,YAAI,MAAM,EAAE,QAAQ,EAAE,SAAS,CAAU,CAAC;AAuIpE,wBAAsB,GAAG,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAgjBjF"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAQhD;;;;;GAKG;AACH,eAAO,MAAM,aAAa,YAAI,MAAM,EAAE,QAAQ,EAAE,SAAS,CAAU,CAAC;AAuIpE,wBAAsB,GAAG,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAymBjF"}
package/dist/run.js CHANGED
@@ -6,9 +6,11 @@ import * as adaptersGenerateCommand from './commands/adapters-generate.js';
6
6
  import * as archiveCommand from './commands/archive.js';
7
7
  import * as doctorCommand from './commands/doctor.js';
8
8
  import * as ingestCommand from './commands/ingest.js';
9
+ import * as landCommand from './commands/land.js';
9
10
  import * as lintCommand from './commands/lint.js';
10
11
  import * as contextGatherCommand from './commands/context-gather.js';
11
12
  import * as graphBuildCommand from './commands/graph-build.js';
13
+ import * as graphDiffCommand from './commands/graph-diff.js';
12
14
  import * as entryAppendCommand from './commands/entry-append.js';
13
15
  import * as graphQueryCommand from './commands/graph-query.js';
14
16
  import * as initCommand from './commands/init.js';
@@ -16,6 +18,8 @@ import * as publishGatherCommand from './commands/publish-gather.js';
16
18
  import * as publishCheckCommand from './commands/publish-check.js';
17
19
  import * as publishUrlsCommand from './commands/publish-urls.js';
18
20
  import * as publishNewCommand from './commands/publish-new.js';
21
+ import * as publishStampCommand from './commands/publish-stamp.js';
22
+ import * as reviewListCommand from './commands/review-list.js';
19
23
  import * as sessionCaptureCommand from './commands/session-capture.js';
20
24
  import * as sessionListCommand from './commands/session-list.js';
21
25
  import * as serveCommand from './commands/serve.js';
@@ -282,6 +286,23 @@ export async function run(argv, env) {
282
286
  return graphBuildCommand.execute(store, { emitRecords: cmdOpts.emitRecords });
283
287
  });
284
288
  });
289
+ graphCommand
290
+ .command('diff')
291
+ .description('how the knowledge changed between two commits: notes, links, dangling links, inbound links, rollups and newly cited sources — read from git, writing nothing')
292
+ .option('--since <ref>', 'compare from the merge base of this ref and the head (required)')
293
+ .option('--until <ref>', 'the head commit to compare (default: HEAD)')
294
+ .option('--format <form>', `the human output: ${graphDiffCommand.GRAPH_DIFF_FORMATS.join(' or ')} (markdown is the pull request's Knowledge changes block; not with --json)`)
295
+ .action(async (cmdOpts, cmd) => {
296
+ const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
297
+ const flags = { since: cmdOpts.since, until: cmdOpts.until, format: cmdOpts.format };
298
+ // review-the-knowledge-diff (D9): `--format markdown` is the whole stdout — the
299
+ // block rides as the human summary, which the reporter writes verbatim and alone.
300
+ result = await runCommand('graph.diff', runEnv, jsonMode, async () => {
301
+ graphDiffCommand.checkFlags(flags, jsonMode);
302
+ const store = await openStore(runEnv, { root });
303
+ return graphDiffCommand.execute(store, flags, jsonMode);
304
+ });
305
+ });
285
306
  const graphQueryCommandGroup = graphCommand.command('query').description('query the built graph');
286
307
  graphQueryCommandGroup
287
308
  .command('neighbors <node>')
@@ -499,17 +520,28 @@ export async function run(argv, env) {
499
520
  });
500
521
  publishCommand
501
522
  .command('new <slug>')
502
- .description('scaffold a page folder with a sibling README, and name the address it is served at — the slug may name a path of folders under the publish path — refusing a reserved or already-existing slug')
503
- .action(async (slug, _cmdOpts, cmd) => {
523
+ .description('scaffold a page folder with a sibling README, and name the address it is served at and its lifespan — the slug may name a path of folders under the publish path — refusing a reserved or already-existing slug; with --disposable, a throwaway page at disposable/<today>-<slug>')
524
+ .option('--disposable', 'scaffold a disposable page at disposable/<YYYY-MM-DD>-<slug>, dated today by the command (two-kinds-of-published-page)')
525
+ .action(async (slug, cmdOpts, cmd) => {
504
526
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
505
527
  result = await runCommand('publish.new', runEnv, jsonMode, async () => {
506
528
  const store = await openStore(runEnv, { root });
507
- return publishNewCommand.execute(store, { slug });
529
+ return publishNewCommand.execute(store, { slug, disposable: cmdOpts.disposable === true });
530
+ });
531
+ });
532
+ publishCommand
533
+ .command('stamp <path>')
534
+ .description("record the hash of the note a permanent page represents (its README's lead link) as a marker on that line — once; never overwrites a marker, and refuses a disposable page")
535
+ .action(async (pagePath, _cmdOpts, cmd) => {
536
+ const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
537
+ result = await runCommand('publish.stamp', runEnv, jsonMode, async () => {
538
+ const store = await openStore(runEnv, { root });
539
+ return publishStampCommand.execute(store, { path: pagePath });
508
540
  });
509
541
  });
510
542
  publishCommand
511
543
  .command('check <path>')
512
- .description('the mechanized structural checks a published page must pass (no external references, viewport meta, print rule, provenance, sibling README, script syntax), and the address it is served at')
544
+ .description("the mechanized structural checks a published page must pass (no external references, viewport meta, print rule, provenance, sibling README, script syntax; a disposable page's shape; a permanent page's represented note and its hash), and the page's address and lifespan")
513
545
  .action(async (pagePath, _cmdOpts, cmd) => {
514
546
  const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
515
547
  result = await runCommand('publish.check', runEnv, jsonMode, async () => {
@@ -585,6 +617,30 @@ export async function run(argv, env) {
585
617
  return verifyCommand.execute(runEnv, store, { portable: cmdOpts.portable });
586
618
  });
587
619
  });
620
+ program
621
+ .command('land')
622
+ .description('land a session pull request: check consent for its head, catch it up with the default branch, merge, confirm, sync the canonical clone, and remove the worktree')
623
+ .argument('[pr]', 'the pull request number to land', (v) => Number.parseInt(v, 10))
624
+ .option('--pending', 'land every open session pull request whose current head carries an approval from the approval page')
625
+ .option('--confirm', 'the caller already has an explicit go for this pull request; land it without a recorded approval')
626
+ .action(async (pr, cmdOpts, cmd) => {
627
+ const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
628
+ result = await runCommand('land', runEnv, jsonMode, async () => {
629
+ const store = await openStore(runEnv, { root });
630
+ return landCommand.execute(runEnv, store, { pr, pending: Boolean(cmdOpts.pending), confirm: Boolean(cmdOpts.confirm) });
631
+ });
632
+ });
633
+ const reviewCommand = program.command('review').description('read the decisions people made on session pull requests from the approval page');
634
+ reviewCommand
635
+ .command('list')
636
+ .description('report each open session pull request with its decision state: awaiting, approved, changes-requested, needs-reapproval or blocked')
637
+ .action(async (_cmdOpts, cmd) => {
638
+ const { runEnv, jsonMode, root } = deriveRunEnv(env, cmd);
639
+ result = await runCommand('review.list', runEnv, jsonMode, async () => {
640
+ const store = await openStore(runEnv, { root });
641
+ return reviewListCommand.execute(runEnv, store);
642
+ });
643
+ });
588
644
  const sessionCommand = program.command('session').description('manage session worktrees');
589
645
  sessionCommand
590
646
  .command('start')