@hivehub/rulebook 5.3.2 → 5.4.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 (76) hide show
  1. package/README.md +393 -354
  2. package/dist/cli/commands/compress.d.ts +18 -0
  3. package/dist/cli/commands/compress.d.ts.map +1 -0
  4. package/dist/cli/commands/compress.js +100 -0
  5. package/dist/cli/commands/compress.js.map +1 -0
  6. package/dist/cli/commands/index.d.ts +1 -0
  7. package/dist/cli/commands/index.d.ts.map +1 -1
  8. package/dist/cli/commands/index.js +1 -0
  9. package/dist/cli/commands/index.js.map +1 -1
  10. package/dist/cli/commands/init.d.ts.map +1 -1
  11. package/dist/cli/commands/init.js +2 -0
  12. package/dist/cli/commands/init.js.map +1 -1
  13. package/dist/cli/commands/update.d.ts.map +1 -1
  14. package/dist/cli/commands/update.js +2 -0
  15. package/dist/cli/commands/update.js.map +1 -1
  16. package/dist/core/claude-settings-manager.d.ts +7 -0
  17. package/dist/core/claude-settings-manager.d.ts.map +1 -1
  18. package/dist/core/claude-settings-manager.js +31 -14
  19. package/dist/core/claude-settings-manager.js.map +1 -1
  20. package/dist/core/compress/compressor.d.ts +60 -0
  21. package/dist/core/compress/compressor.d.ts.map +1 -0
  22. package/dist/core/compress/compressor.js +232 -0
  23. package/dist/core/compress/compressor.js.map +1 -0
  24. package/dist/core/compress/discover.d.ts +19 -0
  25. package/dist/core/compress/discover.d.ts.map +1 -0
  26. package/dist/core/compress/discover.js +100 -0
  27. package/dist/core/compress/discover.js.map +1 -0
  28. package/dist/core/compress/validator.d.ts +47 -0
  29. package/dist/core/compress/validator.d.ts.map +1 -0
  30. package/dist/core/compress/validator.js +131 -0
  31. package/dist/core/compress/validator.js.map +1 -0
  32. package/dist/core/doctor.d.ts.map +1 -1
  33. package/dist/core/doctor.js +66 -0
  34. package/dist/core/doctor.js.map +1 -1
  35. package/dist/core/generator.d.ts +16 -0
  36. package/dist/core/generator.d.ts.map +1 -1
  37. package/dist/core/generator.js +38 -69
  38. package/dist/core/generator.js.map +1 -1
  39. package/dist/core/merger.js +2 -2
  40. package/dist/core/merger.js.map +1 -1
  41. package/dist/hooks/safe-flag-io.d.ts +77 -0
  42. package/dist/hooks/safe-flag-io.d.ts.map +1 -0
  43. package/dist/hooks/safe-flag-io.js +169 -0
  44. package/dist/hooks/safe-flag-io.js.map +1 -0
  45. package/dist/hooks/terse-activate.d.ts +59 -0
  46. package/dist/hooks/terse-activate.d.ts.map +1 -0
  47. package/dist/hooks/terse-activate.js +149 -0
  48. package/dist/hooks/terse-activate.js.map +1 -0
  49. package/dist/hooks/terse-config.d.ts +51 -0
  50. package/dist/hooks/terse-config.d.ts.map +1 -0
  51. package/dist/hooks/terse-config.js +130 -0
  52. package/dist/hooks/terse-config.js.map +1 -0
  53. package/dist/hooks/terse-mode-tracker.d.ts +78 -0
  54. package/dist/hooks/terse-mode-tracker.d.ts.map +1 -0
  55. package/dist/hooks/terse-mode-tracker.js +213 -0
  56. package/dist/hooks/terse-mode-tracker.js.map +1 -0
  57. package/dist/index.js +11 -1
  58. package/dist/index.js.map +1 -1
  59. package/dist/mcp/rulebook-server.d.ts.map +1 -1
  60. package/dist/mcp/rulebook-server.js +236 -0
  61. package/dist/mcp/rulebook-server.js.map +1 -1
  62. package/dist/types.d.ts +4 -0
  63. package/dist/types.d.ts.map +1 -1
  64. package/package.json +2 -1
  65. package/templates/hooks/terse-activate.ps1 +143 -0
  66. package/templates/hooks/terse-activate.sh +197 -0
  67. package/templates/hooks/terse-mode-tracker.ps1 +153 -0
  68. package/templates/hooks/terse-mode-tracker.sh +187 -0
  69. package/templates/modules/RULEBOOK_MCP.md +52 -0
  70. package/templates/skills/core/rulebook-terse/SKILL.md +116 -0
  71. package/templates/skills/core/rulebook-terse-commit/SKILL.md +96 -0
  72. package/templates/skills/core/rulebook-terse-review/SKILL.md +112 -0
  73. package/dist/cli/commands.d.ts +0 -225
  74. package/dist/cli/commands.d.ts.map +0 -1
  75. package/dist/cli/commands.js +0 -3984
  76. package/dist/cli/commands.js.map +0 -1
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Deterministic prose compressor for `rulebook compress`.
3
+ *
4
+ * Applies only SAFE transformations — filler-word removal, pleasantry
5
+ * stripping, and redundant-phrase replacement. Never restructures
6
+ * sentences, never touches code, URLs, paths, commands, dates, or
7
+ * version numbers. That conservative scope is what makes the
8
+ * validator in `validator.ts` a sufficient safety net.
9
+ *
10
+ * When richer compression is wanted (useMemo → stabilize, "implement
11
+ * a solution for" → "fix"), the user can layer Claude-powered
12
+ * rewriting on top of this module. The validator contract is
13
+ * orthogonal to the rewriter — any future LLM-backed compressor gets
14
+ * graded against the same invariants.
15
+ *
16
+ * Grounded in `.rulebook/specs/RULEBOOK_TERSE.md` §Compression
17
+ * companion.
18
+ */
19
+ import { type ValidationResult } from './validator.js';
20
+ export interface CompressResult {
21
+ output: string;
22
+ retries: number;
23
+ validation: ValidationResult;
24
+ }
25
+ export interface CompressOptions {
26
+ /** Max retry count after validation failure. Defaults to 2. */
27
+ maxRetries?: number;
28
+ }
29
+ interface Segment {
30
+ protected: boolean;
31
+ text: string;
32
+ }
33
+ /**
34
+ * Split input into protected and prose segments.
35
+ * Protected: fenced code blocks, inline code, URLs.
36
+ * Everything else is prose.
37
+ */
38
+ declare function segment(input: string): Segment[];
39
+ declare function compressProse(prose: string): string;
40
+ /**
41
+ * Compress `input` using the deterministic rewriter. Returns the
42
+ * compressed output along with the validation result and retry count.
43
+ *
44
+ * The retry loop is defensive: on this conservative algorithm we
45
+ * should never produce a validation failure (we only touch prose
46
+ * segments), so a failure means either (a) a prose segment
47
+ * contained a protected pattern we missed, or (b) an unusual edge
48
+ * case in the splitter. Retries progressively disable classes of
49
+ * transformations to preserve as much safety as possible.
50
+ */
51
+ export declare function compress(input: string, options?: CompressOptions): CompressResult;
52
+ /**
53
+ * Exported for tests.
54
+ */
55
+ export declare const __test__: {
56
+ compressProse: typeof compressProse;
57
+ segment: typeof segment;
58
+ };
59
+ export {};
60
+ //# sourceMappingURL=compressor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compressor.d.ts","sourceRoot":"","sources":["../../../src/core/compress/compressor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAwB,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAE7E,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA8DD,UAAU,OAAO;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,iBAAS,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE,CA6CzC;AAaD,iBAAS,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CA8B5C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,cAAc,CA0BrF;AAyCD;;GAEG;AACH,eAAO,MAAM,QAAQ;;;CAA6B,CAAC"}
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Deterministic prose compressor for `rulebook compress`.
3
+ *
4
+ * Applies only SAFE transformations — filler-word removal, pleasantry
5
+ * stripping, and redundant-phrase replacement. Never restructures
6
+ * sentences, never touches code, URLs, paths, commands, dates, or
7
+ * version numbers. That conservative scope is what makes the
8
+ * validator in `validator.ts` a sufficient safety net.
9
+ *
10
+ * When richer compression is wanted (useMemo → stabilize, "implement
11
+ * a solution for" → "fix"), the user can layer Claude-powered
12
+ * rewriting on top of this module. The validator contract is
13
+ * orthogonal to the rewriter — any future LLM-backed compressor gets
14
+ * graded against the same invariants.
15
+ *
16
+ * Grounded in `.rulebook/specs/RULEBOOK_TERSE.md` §Compression
17
+ * companion.
18
+ */
19
+ import { extractors, validate } from './validator.js';
20
+ // ── Transformation rules — order matters (phrase replacements first) ──
21
+ /** Redundant phrases → shorter synonym. Applied to prose only. */
22
+ const PHRASE_REPLACEMENTS = [
23
+ [/\bin order to\b/gi, 'to'],
24
+ [/\bmake sure to\b/gi, 'ensure'],
25
+ [/\bmake sure that\b/gi, 'ensure'],
26
+ [/\bthe reason is because\b/gi, 'because'],
27
+ [/\bthe reason why\b/gi, 'why'],
28
+ [/\bat this point in time\b/gi, 'now'],
29
+ [/\bwith regard to\b/gi, 'on'],
30
+ [/\bwith respect to\b/gi, 'on'],
31
+ [/\bdue to the fact that\b/gi, 'because'],
32
+ [/\ba number of\b/gi, 'several'],
33
+ [/\bfor the purpose of\b/gi, 'to'],
34
+ [/\bit is important to note that\b/gi, ''],
35
+ [/\bit should be noted that\b/gi, ''],
36
+ ];
37
+ /** Leading pleasantries to strip from prose segments. */
38
+ const PLEASANTRY_PREFIXES = [
39
+ /^\s*(sure|certainly|of course)[,!.]?\s+/i,
40
+ /^\s*(happy to help|great question|good point)[,!.]?\s+/i,
41
+ /^\s*(i'd|i would) be happy to[^.]*\.\s+/i,
42
+ /^\s*(let me)[^.]*\.\s+/i,
43
+ ];
44
+ /** Filler words removed when they appear between word-boundaries. */
45
+ const FILLER_WORDS = [
46
+ 'just',
47
+ 'really',
48
+ 'basically',
49
+ 'actually',
50
+ 'simply',
51
+ 'essentially',
52
+ 'generally',
53
+ 'frankly',
54
+ 'honestly',
55
+ 'literally',
56
+ 'very',
57
+ 'quite',
58
+ 'perhaps',
59
+ 'maybe',
60
+ ];
61
+ /** Hedging phrases to remove. */
62
+ const HEDGING_PATTERNS = [
63
+ /\bit might be worth\b/gi,
64
+ /\byou could consider\b/gi,
65
+ /\byou (?:may|might) want to\b/gi,
66
+ /\bi (?:think|believe|feel) that\b/gi,
67
+ // Lookahead uses `\s*\w` to tolerate whitespace collapsed by earlier
68
+ // substitutions (e.g. "I think consider" after a "you might want to"
69
+ // strip leaves a double space before the next word).
70
+ /\bi (?:think|believe|feel)\s+(?=\w)/gi,
71
+ /\bin my opinion,?\s*/gi,
72
+ ];
73
+ /**
74
+ * Split input into protected and prose segments.
75
+ * Protected: fenced code blocks, inline code, URLs.
76
+ * Everything else is prose.
77
+ */
78
+ function segment(input) {
79
+ const segments = [];
80
+ // Layered matches: fenced code, inline code, URLs. First we build a
81
+ // mask of protected character ranges; prose is the complement.
82
+ const mask = new Array(input.length).fill(false);
83
+ const markRanges = (re) => {
84
+ const matches = input.matchAll(re);
85
+ for (const m of matches) {
86
+ if (m.index === undefined)
87
+ continue;
88
+ for (let i = m.index; i < m.index + m[0].length; i++)
89
+ mask[i] = true;
90
+ }
91
+ };
92
+ // Capture fenced code FIRST so inline-code inside ``` blocks doesn't
93
+ // get double-marked (it's already inside a fenced region).
94
+ markRanges(extractors.FENCED_CODE_RE);
95
+ // Inline code — recomputed on a version where fenced blocks are
96
+ // masked out so we don't match inside them.
97
+ const stripped = input
98
+ .split('')
99
+ .map((c, i) => (mask[i] ? ' ' : c))
100
+ .join('');
101
+ const inlineMatches = stripped.matchAll(extractors.INLINE_CODE_RE);
102
+ for (const m of inlineMatches) {
103
+ if (m.index === undefined)
104
+ continue;
105
+ for (let i = m.index; i < m.index + m[0].length; i++)
106
+ mask[i] = true;
107
+ }
108
+ markRanges(extractors.URL_RE);
109
+ markRanges(extractors.PATH_RE);
110
+ markRanges(extractors.DATE_RE);
111
+ markRanges(extractors.VERSION_RE);
112
+ // Now scan the mask to build contiguous segments.
113
+ let i = 0;
114
+ while (i < input.length) {
115
+ const isProtected = mask[i];
116
+ let j = i;
117
+ while (j < input.length && mask[j] === isProtected)
118
+ j++;
119
+ segments.push({ protected: isProtected, text: input.slice(i, j) });
120
+ i = j;
121
+ }
122
+ return segments;
123
+ }
124
+ // ── Prose rewriter ────────────────────────────────────────────────────
125
+ function stripFillerWord(text, word) {
126
+ // Remove ", just," / " just " / " just." patterns but preserve
127
+ // sentence boundaries. Matches word only when surrounded by word
128
+ // boundaries and whitespace, not at the start of a capitalized
129
+ // sentence (where removal changes meaning more aggressively).
130
+ const pattern = new RegExp(`(?<=\\s|^)${word}\\s+(?=[a-zA-Z])`, 'gi');
131
+ return text.replace(pattern, '');
132
+ }
133
+ function compressProse(prose) {
134
+ let out = prose;
135
+ // 1. Strip leading pleasantries.
136
+ for (const re of PLEASANTRY_PREFIXES) {
137
+ out = out.replace(re, '');
138
+ }
139
+ // 2. Remove hedging phrases.
140
+ for (const re of HEDGING_PATTERNS) {
141
+ out = out.replace(re, '');
142
+ }
143
+ // 3. Apply phrase replacements.
144
+ for (const [re, repl] of PHRASE_REPLACEMENTS) {
145
+ out = out.replace(re, repl);
146
+ }
147
+ // 4. Remove filler words (as standalone mid-sentence tokens).
148
+ for (const word of FILLER_WORDS) {
149
+ out = stripFillerWord(out, word);
150
+ }
151
+ // 5. Normalize whitespace artifacts introduced by deletions.
152
+ out = out
153
+ .replace(/\s+([,.;:!?])/g, '$1') // collapse space-before-punct
154
+ .replace(/[ \t]{2,}/g, ' ') // collapse multiple spaces
155
+ .replace(/\n[ \t]+\n/g, '\n\n'); // trim trailing whitespace on blank lines
156
+ return out;
157
+ }
158
+ /**
159
+ * Compress `input` using the deterministic rewriter. Returns the
160
+ * compressed output along with the validation result and retry count.
161
+ *
162
+ * The retry loop is defensive: on this conservative algorithm we
163
+ * should never produce a validation failure (we only touch prose
164
+ * segments), so a failure means either (a) a prose segment
165
+ * contained a protected pattern we missed, or (b) an unusual edge
166
+ * case in the splitter. Retries progressively disable classes of
167
+ * transformations to preserve as much safety as possible.
168
+ */
169
+ export function compress(input, options = {}) {
170
+ const maxRetries = options.maxRetries ?? 2;
171
+ let retries = 0;
172
+ let output = applyCompression(input, {
173
+ pleasantries: true,
174
+ hedging: true,
175
+ phraseReplacements: true,
176
+ fillerWords: true,
177
+ });
178
+ let validation = validate(input, output);
179
+ while (!validation.ok && retries < maxRetries) {
180
+ retries++;
181
+ // On retry 1 → disable filler words (most aggressive class).
182
+ // On retry 2 → also disable hedging.
183
+ // If still failing, fall through and return the failure state.
184
+ output = applyCompression(input, {
185
+ pleasantries: true,
186
+ hedging: retries < 2,
187
+ phraseReplacements: true,
188
+ fillerWords: false,
189
+ });
190
+ validation = validate(input, output);
191
+ }
192
+ return { output, retries, validation };
193
+ }
194
+ function applyCompression(input, enabled) {
195
+ const segs = segment(input);
196
+ const pieces = [];
197
+ for (const s of segs) {
198
+ if (s.protected) {
199
+ // Protected segments MUST pass through byte-identically.
200
+ // No whitespace normalization, no substitutions.
201
+ pieces.push(s.text);
202
+ continue;
203
+ }
204
+ let rewritten = s.text;
205
+ if (enabled.pleasantries) {
206
+ for (const re of PLEASANTRY_PREFIXES)
207
+ rewritten = rewritten.replace(re, '');
208
+ }
209
+ if (enabled.hedging) {
210
+ for (const re of HEDGING_PATTERNS)
211
+ rewritten = rewritten.replace(re, '');
212
+ }
213
+ if (enabled.phraseReplacements) {
214
+ for (const [re, repl] of PHRASE_REPLACEMENTS)
215
+ rewritten = rewritten.replace(re, repl);
216
+ }
217
+ if (enabled.fillerWords) {
218
+ for (const word of FILLER_WORDS)
219
+ rewritten = stripFillerWord(rewritten, word);
220
+ }
221
+ // Whitespace normalization INSIDE prose segments only. Keep
222
+ // inter-paragraph blank lines (don't collapse \n\n → \n).
223
+ rewritten = rewritten.replace(/ +([,.;:!?])/g, '$1').replace(/[ \t]{2,}/g, ' ');
224
+ pieces.push(rewritten);
225
+ }
226
+ return pieces.join('');
227
+ }
228
+ /**
229
+ * Exported for tests.
230
+ */
231
+ export const __test__ = { compressProse, segment };
232
+ //# sourceMappingURL=compressor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compressor.js","sourceRoot":"","sources":["../../../src/core/compress/compressor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Discover candidate markdown memory files in a project.
3
+ *
4
+ * Caveman-compress is advertised as a tool for the files the model
5
+ * READS on every session, so this helper surfaces the same class of
6
+ * files: project-root memory markdown (`CLAUDE.md`, `AGENTS.md`,
7
+ * `AGENTS.override.md`) plus long-lived `.rulebook/` notes and
8
+ * knowledge/learning entries.
9
+ */
10
+ export interface Candidate {
11
+ relPath: string;
12
+ absPath: string;
13
+ bytes: number;
14
+ hasBackup: boolean;
15
+ backupBytes?: number;
16
+ backupRatio?: number;
17
+ }
18
+ export declare function listCompressCandidates(projectRoot: string): Promise<Candidate[]>;
19
+ //# sourceMappingURL=discover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../../../src/core/compress/discover.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AA0CD,wBAAsB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAmDtF"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Discover candidate markdown memory files in a project.
3
+ *
4
+ * Caveman-compress is advertised as a tool for the files the model
5
+ * READS on every session, so this helper surfaces the same class of
6
+ * files: project-root memory markdown (`CLAUDE.md`, `AGENTS.md`,
7
+ * `AGENTS.override.md`) plus long-lived `.rulebook/` notes and
8
+ * knowledge/learning entries.
9
+ */
10
+ import { readdir, stat } from 'node:fs/promises';
11
+ import { join } from 'node:path';
12
+ const ROOT_FILE_GLOB = ['CLAUDE.md', 'CLAUDE.local.md', 'AGENTS.md', 'AGENTS.override.md'];
13
+ const RULEBOOK_SUBPATHS = [
14
+ '.rulebook/PLANS.md',
15
+ '.rulebook/STATE.md',
16
+ '.rulebook/knowledge',
17
+ '.rulebook/learnings',
18
+ ];
19
+ async function safeStat(path) {
20
+ try {
21
+ const s = await stat(path);
22
+ return { exists: true, size: s.size };
23
+ }
24
+ catch {
25
+ return { exists: false, size: 0 };
26
+ }
27
+ }
28
+ function backupPathFor(filePath) {
29
+ return filePath.replace(/\.md$/i, '.original.md');
30
+ }
31
+ async function walkMarkdown(dir, out) {
32
+ let entries;
33
+ try {
34
+ entries = await readdir(dir, { withFileTypes: true });
35
+ }
36
+ catch {
37
+ return;
38
+ }
39
+ for (const e of entries) {
40
+ if (e.name.startsWith('.'))
41
+ continue;
42
+ const full = join(dir, e.name);
43
+ if (e.isDirectory()) {
44
+ await walkMarkdown(full, out);
45
+ }
46
+ else if (e.name.endsWith('.md') && !e.name.endsWith('.original.md')) {
47
+ out.push(full);
48
+ }
49
+ }
50
+ }
51
+ export async function listCompressCandidates(projectRoot) {
52
+ const found = [];
53
+ for (const name of ROOT_FILE_GLOB) {
54
+ const p = join(projectRoot, name);
55
+ if ((await safeStat(p)).exists)
56
+ found.push(p);
57
+ }
58
+ for (const sub of RULEBOOK_SUBPATHS) {
59
+ const p = join(projectRoot, sub);
60
+ const s = await safeStat(p);
61
+ if (!s.exists)
62
+ continue;
63
+ try {
64
+ const statRes = await stat(p);
65
+ if (statRes.isDirectory()) {
66
+ await walkMarkdown(p, found);
67
+ }
68
+ else {
69
+ found.push(p);
70
+ }
71
+ }
72
+ catch {
73
+ /* ignore */
74
+ }
75
+ }
76
+ const candidates = [];
77
+ for (const abs of found) {
78
+ const fileStat = await safeStat(abs);
79
+ const backupPath = backupPathFor(abs);
80
+ const backupStat = await safeStat(backupPath);
81
+ const relPath = abs.startsWith(projectRoot)
82
+ ? abs.slice(projectRoot.length).replace(/^[\\/]/, '')
83
+ : abs;
84
+ const candidate = {
85
+ relPath,
86
+ absPath: abs,
87
+ bytes: fileStat.size,
88
+ hasBackup: backupStat.exists,
89
+ };
90
+ if (backupStat.exists && backupStat.size > 0) {
91
+ candidate.backupBytes = backupStat.size;
92
+ candidate.backupRatio = fileStat.size / backupStat.size;
93
+ }
94
+ candidates.push(candidate);
95
+ }
96
+ // Sort by largest first so the caller sees the biggest win targets.
97
+ candidates.sort((a, b) => b.bytes - a.bytes);
98
+ return candidates;
99
+ }
100
+ //# sourceMappingURL=discover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.js","sourceRoot":"","sources":["../../../src/core/compress/discover.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Validator for `rulebook compress`.
3
+ *
4
+ * The compressor may only rewrite prose. Everything that carries
5
+ * technical substance MUST round-trip byte-identically between the
6
+ * original file and the compressed output. This module defines the
7
+ * protected-region extractors and the diff-checker that compares
8
+ * them.
9
+ *
10
+ * Grounded in `.rulebook/specs/RULEBOOK_TERSE.md` §Compression
11
+ * companion and `docs/analysis/caveman/02-skill-design.md`.
12
+ */
13
+ export type ViolationKind = 'heading-missing' | 'heading-text-changed' | 'fenced-code-changed' | 'inline-code-changed' | 'url-changed' | 'path-changed' | 'command-changed' | 'date-changed' | 'version-changed';
14
+ export interface Violation {
15
+ kind: ViolationKind;
16
+ detail: string;
17
+ }
18
+ export interface ValidationResult {
19
+ ok: boolean;
20
+ violations: Violation[];
21
+ stats: {
22
+ originalBytes: number;
23
+ compressedBytes: number;
24
+ ratio: number;
25
+ };
26
+ }
27
+ /** Extract atomic markdown heading entries as "level\ttext" strings. */
28
+ declare function extractHeadings(source: string): string[];
29
+ /** Extract inline-code bodies (the text inside backticks). */
30
+ declare function extractInlineCode(source: string): string[];
31
+ export declare function validate(original: string, compressed: string): ValidationResult;
32
+ /**
33
+ * Exposed for tests and the compressor's retry logic.
34
+ */
35
+ export declare const extractors: {
36
+ FENCED_CODE_RE: RegExp;
37
+ INLINE_CODE_RE: RegExp;
38
+ URL_RE: RegExp;
39
+ DATE_RE: RegExp;
40
+ VERSION_RE: RegExp;
41
+ HEADING_RE: RegExp;
42
+ PATH_RE: RegExp;
43
+ extractHeadings: typeof extractHeadings;
44
+ extractInlineCode: typeof extractInlineCode;
45
+ };
46
+ export {};
47
+ //# sourceMappingURL=validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../src/core/compress/validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,aAAa,GACrB,iBAAiB,GACjB,sBAAsB,GACtB,qBAAqB,GACrB,qBAAqB,GACrB,aAAa,GACb,cAAc,GACd,iBAAiB,GACjB,cAAc,GACd,iBAAiB,CAAC;AAEtB,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,KAAK,EAAE;QACL,aAAa,EAAE,MAAM,CAAC;QACtB,eAAe,EAAE,MAAM,CAAC;QACxB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AA8BD,wEAAwE;AACxE,iBAAS,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAKjD;AAED,8DAA8D;AAC9D,iBAAS,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAOnD;AAsCD,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,gBAAgB,CAoD/E;AAED;;GAEG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;CAUtB,CAAC"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Validator for `rulebook compress`.
3
+ *
4
+ * The compressor may only rewrite prose. Everything that carries
5
+ * technical substance MUST round-trip byte-identically between the
6
+ * original file and the compressed output. This module defines the
7
+ * protected-region extractors and the diff-checker that compares
8
+ * them.
9
+ *
10
+ * Grounded in `.rulebook/specs/RULEBOOK_TERSE.md` §Compression
11
+ * companion and `docs/analysis/caveman/02-skill-design.md`.
12
+ */
13
+ // ── Regex patterns — all non-greedy, multiline where needed ───────────
14
+ const FENCED_CODE_RE = /```[\s\S]*?```/g;
15
+ const INLINE_CODE_RE = /(?<!`)`([^`\n]+)`(?!`)/g;
16
+ // URL must not consume trailing sentence-ending punctuation (. , ; : ! ?).
17
+ const URL_RE = /\bhttps?:\/\/[^\s)>\]"'`]*[^\s)>\]"'`.,;:!?]/g;
18
+ const DATE_RE = /\b\d{4}-\d{2}-\d{2}\b/g;
19
+ // Version numbers: optional `v`, then X.Y or X.Y.Z(-suffix).
20
+ const VERSION_RE = /\bv?\d+\.\d+(?:\.\d+)?(?:-[\w.]+)?\b/g;
21
+ // ATX-style markdown headings.
22
+ const HEADING_RE = /^(#{1,6})\s+(.+?)\s*$/gm;
23
+ // File paths. Three alternatives:
24
+ // relative: `./foo/bar` or `../baz`
25
+ // Windows : `C:\Users\...`
26
+ // absolute Unix: `/usr/local/bin/node` — requires start-of-line or whitespace/paren prefix.
27
+ const PATH_RE = /(?:\.{1,2}\/[\w./\\-]+|[A-Za-z]:\\[\w./\\-]+|(?<=^|[\s(])\/[a-zA-Z][\w./\\-]+)/gm;
28
+ function normalize(arr) {
29
+ return arr.slice().sort();
30
+ }
31
+ function extract(source, re) {
32
+ const out = [];
33
+ const matches = source.matchAll(re);
34
+ for (const m of matches)
35
+ out.push(m[0]);
36
+ return out;
37
+ }
38
+ /** Extract atomic markdown heading entries as "level\ttext" strings. */
39
+ function extractHeadings(source) {
40
+ const out = [];
41
+ const matches = source.matchAll(HEADING_RE);
42
+ for (const m of matches)
43
+ out.push(`${m[1].length}\t${m[2]}`);
44
+ return out;
45
+ }
46
+ /** Extract inline-code bodies (the text inside backticks). */
47
+ function extractInlineCode(source) {
48
+ const out = [];
49
+ // Strip fenced blocks first so we don't double-count code inside ```.
50
+ const scrubbed = source.replace(FENCED_CODE_RE, '');
51
+ const matches = scrubbed.matchAll(INLINE_CODE_RE);
52
+ for (const m of matches)
53
+ out.push(m[1]);
54
+ return out;
55
+ }
56
+ function diff(label, original, compressed) {
57
+ const violations = [];
58
+ const normalOrig = normalize(original);
59
+ const normalComp = normalize(compressed);
60
+ // Everything in original must appear in compressed, same count.
61
+ const compCount = new Map();
62
+ for (const v of normalComp)
63
+ compCount.set(v, (compCount.get(v) ?? 0) + 1);
64
+ for (const v of normalOrig) {
65
+ const n = compCount.get(v) ?? 0;
66
+ if (n === 0) {
67
+ violations.push({
68
+ kind: label,
69
+ detail: `missing from compressed output: ${JSON.stringify(v).slice(0, 120)}`,
70
+ });
71
+ }
72
+ else {
73
+ compCount.set(v, n - 1);
74
+ }
75
+ }
76
+ // Anything LEFT in compCount that came from compressed but wasn't in
77
+ // original means the compressor introduced a new protected item (for
78
+ // example by mistranscribing a URL). That is also a violation.
79
+ for (const [v, n] of compCount) {
80
+ if (n > 0) {
81
+ violations.push({
82
+ kind: label,
83
+ detail: `introduced in compressed output: ${JSON.stringify(v).slice(0, 120)}`,
84
+ });
85
+ }
86
+ }
87
+ return violations;
88
+ }
89
+ export function validate(original, compressed) {
90
+ const violations = [];
91
+ // Heading structure (level + text) must match exactly.
92
+ violations.push(...diff('heading-missing', extractHeadings(original), extractHeadings(compressed)));
93
+ // Fenced code blocks — byte-for-byte, ordered.
94
+ violations.push(...diff('fenced-code-changed', extract(original, FENCED_CODE_RE), extract(compressed, FENCED_CODE_RE)));
95
+ // Inline code bodies.
96
+ violations.push(...diff('inline-code-changed', extractInlineCode(original), extractInlineCode(compressed)));
97
+ // URLs.
98
+ violations.push(...diff('url-changed', extract(original, URL_RE), extract(compressed, URL_RE)));
99
+ // File paths.
100
+ violations.push(...diff('path-changed', extract(original, PATH_RE), extract(compressed, PATH_RE)));
101
+ // Dates.
102
+ violations.push(...diff('date-changed', extract(original, DATE_RE), extract(compressed, DATE_RE)));
103
+ // Version numbers.
104
+ violations.push(...diff('version-changed', extract(original, VERSION_RE), extract(compressed, VERSION_RE)));
105
+ const originalBytes = Buffer.byteLength(original, 'utf8');
106
+ const compressedBytes = Buffer.byteLength(compressed, 'utf8');
107
+ return {
108
+ ok: violations.length === 0,
109
+ violations,
110
+ stats: {
111
+ originalBytes,
112
+ compressedBytes,
113
+ ratio: originalBytes > 0 ? compressedBytes / originalBytes : 1,
114
+ },
115
+ };
116
+ }
117
+ /**
118
+ * Exposed for tests and the compressor's retry logic.
119
+ */
120
+ export const extractors = {
121
+ FENCED_CODE_RE,
122
+ INLINE_CODE_RE,
123
+ URL_RE,
124
+ DATE_RE,
125
+ VERSION_RE,
126
+ HEADING_RE,
127
+ PATH_RE,
128
+ extractHeadings,
129
+ extractInlineCode,
130
+ };
131
+ //# sourceMappingURL=validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.js","sourceRoot":"","sources":["../../../src/core/compress/validator.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/core/doctor.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AAEH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAKD,wBAAsB,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAiB1E"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/core/doctor.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AAEH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAKD,wBAAsB,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAmB1E"}