@golden-frijoles/kit 0.16.0 → 0.18.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.
@@ -110,8 +110,57 @@ export function siblingDocs(readmeRelPath) {
110
110
 
111
111
  // ── Individual checkers — each returns a list of { rule, detail } offenses for one file's content ──
112
112
 
113
+ // An HTML comment that never closes hides EVERYTHING after it when the doc renders — the whole epic README, its
114
+ // architecture lock included (think-skills and sketch-specs both shipped one, 2026-09-30, from an edit anchored on a
115
+ // heading the old template's comment named). Code is skipped first: a `<!-- jev:` inside backticks or a fence is an
116
+ // example, not a comment, and several sprint docs quote one.
117
+ // A code span opens with a run of backticks and closes with the same run (``<!-- x`` included).
118
+ const CODE_SPAN = /(`+)[\s\S]*?\1/g;
119
+
120
+ // A later `-->` closes an open comment wherever it appears (a mermaid `A --> B` included) — that is what CommonMark
121
+ // renders, so the rule agrees with the page; the text in between is still hidden, and only reading it shows that.
122
+ export function unclosedComments(content) {
123
+ const lines = content.split('\n');
124
+ let fence = null;
125
+ let open = null; // the line an unclosed <!-- started on, while we are inside one
126
+ for (let i = 0; i < lines.length; i++) {
127
+ // The whole run of marks: a ```` fence is closed only by ```` or longer, never by the ``` it quotes.
128
+ const fenceMark = lines[i].match(/^ {0,3}(`{3,}|~{3,})/);
129
+ if (open === null && fenceMark) {
130
+ const mark = fenceMark[1];
131
+ if (fence === null) fence = mark;
132
+ else if (mark[0] === fence[0] && mark.length >= fence.length) fence = null;
133
+ continue;
134
+ }
135
+ if (fence !== null) continue;
136
+ let rest = open === null ? lines[i].replace(CODE_SPAN, '') : lines[i];
137
+ for (;;) {
138
+ if (open === null) {
139
+ const at = rest.indexOf('<!--');
140
+ if (at === -1) break;
141
+ open = i;
142
+ rest = rest.slice(at + 4);
143
+ } else {
144
+ const at = rest.indexOf('-->');
145
+ if (at === -1) break;
146
+ open = null;
147
+ rest = rest.slice(at + 3).replace(CODE_SPAN, '');
148
+ }
149
+ }
150
+ }
151
+ return open === null
152
+ ? []
153
+ : [
154
+ {
155
+ rule: 'unclosed-html-comment',
156
+ detail: `line ${open + 1} opens <!-- and nothing closes it — everything after it is hidden when the doc renders`,
157
+ },
158
+ ];
159
+ }
160
+
113
161
  export function checkEpicReadme(content, { slug, exists = existsRelative } = {}) {
114
162
  const offenses = [];
163
+ offenses.push(...unclosedComments(content));
115
164
  // Many epics predate the seeds/ convention and genuinely have no seed file to link — that's an
116
165
  // accepted state (Sprint 2 sweep decision), not drift. Only require a **Scope seed:** field when a
117
166
  // real seed file exists for this epic's slug; a seed that exists but isn't linked IS still flagged.
@@ -230,6 +279,7 @@ export function bodyStartIndex(lines) {
230
279
 
231
280
  export function checkSprintDoc(content) {
232
281
  const offenses = [];
282
+ offenses.push(...unclosedComments(content));
233
283
  const all = content.split('\n');
234
284
  const statusLine = all
235
285
  .slice(bodyStartIndex(all))
@@ -331,6 +381,7 @@ export const RETRO_SECTION_STEMS = [
331
381
  // same date — that is fixing drift, not relaxing the check.
332
382
  export function checkRetrospective(content) {
333
383
  const offenses = [];
384
+ offenses.push(...unclosedComments(content));
334
385
  const closedLine = content.split('\n').find((l) => /closed/i.test(l) && l.trim() !== '');
335
386
  if (!closedLine) {
336
387
  offenses.push({ rule: 'retro-closed-missing', detail: 'no "Closed" date line found near the top' });
@@ -70,7 +70,9 @@ export const ROUTES = Object.freeze({
70
70
  sequence: 'sequence',
71
71
  container_diagram: 'container diagram',
72
72
  spike: 'spike',
73
- think_chain: 'think chain (answer by hand until think-skills ships)',
73
+ // think-skills D9: the label only. The `think_chain` choice text Jev reads is a stamped wording in intent.json.
74
+ think_chain:
75
+ 'think chain (worth doing? run `pmf-narrative`, then `risk-validation`; any other trade-off, reason it in writing)',
74
76
  });
75
77
 
76
78
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@golden-frijoles/kit",
3
- "version": "0.16.0",
3
+ "version": "0.18.0",
4
4
  "description": "The scripts the Golden Frijoles skills run, packaged so they work in any repo: planning, build-order, reporting and verification rails. Zero dependencies.",
5
5
  "type": "module",
6
6
  "bin": {