@thedecipherist/claude-packs 1.0.0 → 1.1.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 (148) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/README.md +275 -18
  3. package/bin/claude-packs.mjs +18 -10
  4. package/configurations/base/config.json +1 -1
  5. package/configurations/cpp/config.json +2 -1
  6. package/configurations/full-stack/config.json +2 -1
  7. package/configurations/go/config.json +3 -1
  8. package/configurations/node/config.json +3 -1
  9. package/configurations/python/config.json +3 -1
  10. package/configurations/rust/config.json +106 -0
  11. package/configurations/synth-creator/config.json +1 -1
  12. package/global/hooks/lib/shell-writes.mjs +2 -293
  13. package/lib/build-queue.mjs +22 -0
  14. package/lib/claude-md.mjs +4 -2
  15. package/lib/config-cli.mjs +1 -1
  16. package/lib/coverage.mjs +10 -2
  17. package/lib/doctor.mjs +14 -3
  18. package/lib/fix-plan.mjs +2 -1
  19. package/lib/found-folders.mjs +25 -0
  20. package/lib/global.mjs +3 -3
  21. package/lib/hook-proof.mjs +24 -7
  22. package/lib/kit-command.mjs +14 -0
  23. package/lib/layout.mjs +27 -3
  24. package/lib/logs.mjs +45 -5
  25. package/lib/new.mjs +1 -1
  26. package/lib/packs.mjs +4 -2
  27. package/lib/rule-tests.mjs +42 -0
  28. package/lib/scan-detect.mjs +41 -5
  29. package/lib/scan.mjs +5 -2
  30. package/lib/suggest.mjs +16 -3
  31. package/lib/sync.mjs +5 -2
  32. package/lib/workorder.mjs +26 -0
  33. package/package.json +1 -1
  34. package/packs/audio-core/hooks/param-id-lock.mjs +59 -16
  35. package/packs/audio-core/pack.json +5 -0
  36. package/packs/audio-core/rules/a-control-is-proved-through-its-input.md +26 -0
  37. package/packs/audio-core/rules/cpu-worst-case-per-block.md +3 -1
  38. package/packs/audio-core/rules/house-control-behaviours.md +1 -1
  39. package/packs/audio-core/rules/scoped-no-denormals.md +1 -1
  40. package/packs/audio-core/rules/straight-on-and-aligned.md +1 -1
  41. package/packs/audio-core/rules/ui-matches-reference-look.md +2 -0
  42. package/packs/audio-core/skills/analyse-reference-look/SKILL.md +25 -11
  43. package/packs/audio-core/skills/prove-control/SKILL.md +2 -0
  44. package/packs/base/checks/one-walk-per-template.json +1 -1
  45. package/packs/base/hooks/code-shape.mjs +10 -6
  46. package/packs/base/hooks/dispatch-once.mjs +58 -21
  47. package/packs/base/hooks/folder-law.mjs +3 -0
  48. package/packs/base/hooks/git-guard.mjs +22 -109
  49. package/packs/base/hooks/lesson-checks.mjs +9 -1
  50. package/packs/base/hooks/lib/code-shapes.mjs +11 -6
  51. package/packs/base/hooks/lib/crash-policy.mjs +47 -0
  52. package/packs/base/hooks/lib/dup-index.mjs +6 -1
  53. package/packs/base/hooks/lib/dup.mjs +2 -2
  54. package/packs/base/hooks/lib/event-log.mjs +4 -3
  55. package/packs/base/hooks/lib/git-adds.mjs +2 -1
  56. package/packs/base/hooks/lib/git-auth.mjs +44 -0
  57. package/packs/base/hooks/lib/git-guard-selftest.mjs +93 -0
  58. package/packs/base/hooks/lib/languages.mjs +5 -2
  59. package/packs/base/hooks/lib/layout.mjs +6 -2
  60. package/packs/base/hooks/lib/making-check-selftest.mjs +3 -0
  61. package/packs/base/hooks/lib/near-copies.mjs +1 -1
  62. package/packs/base/hooks/lib/program-read.mjs +6 -3
  63. package/packs/base/hooks/lib/rust-scan.mjs +139 -0
  64. package/packs/base/hooks/lib/shape-lists.mjs +31 -0
  65. package/packs/base/hooks/lib/shape-prose.mjs +78 -0
  66. package/packs/base/hooks/lib/shape.mjs +18 -39
  67. package/packs/base/hooks/lib/turn.mjs +6 -1
  68. package/packs/base/hooks/lib/unread-writes.mjs +24 -0
  69. package/packs/base/hooks/making-check.mjs +12 -7
  70. package/packs/base/hooks/run-hooks.mjs +15 -7
  71. package/packs/base/hooks/shape-law.mjs +11 -4
  72. package/packs/base/hooks/suite-once.mjs +22 -6
  73. package/packs/base/rules/a-control-is-seen-to-fire.md +1 -1
  74. package/packs/base/rules/behaviour-lives-with-its-kind.md +1 -1
  75. package/packs/base/rules/bug-fix-starts-red.md +1 -1
  76. package/packs/base/rules/field-map-over-if-runs.md +1 -1
  77. package/packs/base/rules/instances-by-index-identities-by-id.md +2 -2
  78. package/packs/base/rules/lookup-over-if-chains.md +1 -1
  79. package/packs/base/rules/plan-and-logs.md +1 -1
  80. package/packs/base/rules/refusals-are-judged.md +1 -0
  81. package/packs/base/rules/test-the-template-once.md +2 -1
  82. package/packs/base/rules/unattended-runs.md +15 -0
  83. package/packs/base/skills/new-project/SKILL.md +23 -0
  84. package/packs/base/skills/write-a-workorder/SKILL.md +41 -0
  85. package/packs/cpp/checks/cpp-find-with-the-library.json +2 -2
  86. package/packs/go/pack.json +5 -0
  87. package/packs/hook-proof/rules/a-refusal-is-exit-2.md +2 -2
  88. package/packs/instrument-shell/rules/render-the-extremes.md +2 -2
  89. package/packs/midi/checks/position-not-integrated.json +5 -1
  90. package/packs/postgres/checks/pg-bind-values.json +33 -0
  91. package/packs/postgres/checks/pg-concurrently-in-txn.json +18 -0
  92. package/packs/postgres/checks/pg-fk-not-valid.json +18 -0
  93. package/packs/postgres/checks/pg-index-concurrently.json +18 -0
  94. package/packs/postgres/checks/pg-no-float-money.json +19 -0
  95. package/packs/postgres/checks/pg-no-money.json +18 -0
  96. package/packs/postgres/checks/pg-no-required-column.json +18 -0
  97. package/packs/postgres/checks/pg-no-volatile-default.json +18 -0
  98. package/packs/postgres/checks/pg-not-exists.json +18 -0
  99. package/packs/postgres/checks/pg-timestamptz.json +18 -0
  100. package/packs/postgres/hooks/db-wipe-guard.mjs +95 -0
  101. package/packs/postgres/pack.json +48 -0
  102. package/packs/postgres/rules/postgres-migrations-never-stop-traffic.md +28 -0
  103. package/packs/postgres/rules/postgres-one-pool-per-process.md +21 -0
  104. package/packs/postgres/rules/postgres-queries-are-null-safe-and-bounded.md +26 -0
  105. package/packs/postgres/rules/postgres-types-say-what-they-hold.md +22 -0
  106. package/packs/postgres/rules/postgres-values-are-bound.md +130 -0
  107. package/packs/postgres/skills/postgres-safe-migration/SKILL.md +45 -0
  108. package/packs/reverse-engineering/rules/addresses-name-their-space.md +1 -0
  109. package/packs/reverse-engineering/rules/reproduction-or-original.md +1 -1
  110. package/packs/reverse-engineering/skills/gather-sources/SKILL.md +1 -0
  111. package/packs/rust/checks/rust-allow-needs-reason.json +16 -0
  112. package/packs/rust/checks/rust-blocking-in-async.json +32 -0
  113. package/packs/rust/checks/rust-if-chain-is-a-match.json +32 -0
  114. package/packs/rust/checks/rust-static-mut.json +32 -0
  115. package/packs/rust/checks/rust-string-as-error.json +34 -0
  116. package/packs/rust/checks/rust-stub-left-in.json +32 -0
  117. package/packs/rust/checks/rust-unsafe-needs-safety.json +31 -0
  118. package/packs/rust/checks/rust-unwrap-in-library.json +34 -0
  119. package/packs/rust/checks/rust-wildcard-dependency.json +16 -0
  120. package/packs/rust/clippy.toml +5 -0
  121. package/packs/rust/pack.json +27 -0
  122. package/packs/rust/rules/rust-async-never-blocks.md +20 -0
  123. package/packs/rust/rules/rust-borrow-before-you-clone.md +19 -0
  124. package/packs/rust/rules/rust-errors-are-returned.md +22 -0
  125. package/packs/rust/rules/rust-lints-are-the-gate.md +23 -0
  126. package/packs/rust/rules/rust-repeats-become-templates.md +28 -0
  127. package/packs/rust/rules/rust-unsafe-is-proved.md +21 -0
  128. package/packs/rust/skills/rust-extract-template/SKILL.md +50 -0
  129. package/packs/rust/skills/rust-port/SKILL.md +22 -0
  130. package/packs/rust/skills/rust-review/SKILL.md +24 -0
  131. package/packs/rust/tools/rust-lints.toml +19 -0
  132. package/packs/sound-design/skills/bounce-a-plugin/SKILL.md +40 -0
  133. package/packs/sound-design/tools/vst3host/build.sh +73 -0
  134. package/packs/sound-design/tools/vst3host/host_events.h +101 -0
  135. package/packs/sound-design/tools/vst3host/options.h +172 -0
  136. package/packs/sound-design/tools/vst3host/render.cpp +245 -0
  137. package/packs/sound-design/tools/vst3host/session.h +241 -0
  138. package/packs/synth-creator/hooks/build-guard.mjs +7 -7
  139. package/packs/synth-creator/hooks/protect-vendor.mjs +2 -1
  140. package/packs/synth-creator/hooks/stage-guard.mjs +1 -1
  141. package/packs/synth-creator/rules/timing-from-synthcore.md +1 -1
  142. package/packs/synth-creator/skills/build-through-the-queue/SKILL.md +4 -2
  143. package/packs/synth-creator/tools/buildq.mjs +74 -0
  144. package/prompts/workorder/PROMPT.md +53 -0
  145. package/prompts/workorder/run.sh +36 -0
  146. package/prompts/workorder/workorder.md +59 -0
  147. package/global/hooks/lib/program-read.mjs +0 -196
  148. package/global/hooks/lib/program-writes.mjs +0 -177
@@ -1,177 +0,0 @@
1
- // program-writes: the files an inline program writes (KWB-8: `python3 - <<EOF ... open(p,'w').write(...)` got past every
2
- // write-time hook; the shell reader saw redirects, tee, sed -i, cp and heredocs, not a program's own writes).
3
- // programOf(exe, args, stdin) -> { lang, code } | null
4
- // an interpreter given its program inline: python -c / python - or python with a heredoc on stdin; node -e / -p /
5
- // node - or a heredoc; perl -e / -E (or a heredoc); ruby -e (or a heredoc). A script file (python3 tools/x.py,
6
- // node tools/x.mjs, python3 -m mod) is null: it is the project's own tool, reviewed like code, not judged as a write.
7
- // programWrites(program, at, readText) -> [{ path, op, content, from? }] (op and content as in shell-writes.mjs)
8
- // a write is a call that opens or makes a file for writing: python open(p, 'w'|'a'|'x'|'r+'), Path.write_text /
9
- // write_bytes / touch / unlink, shutil.copy* / move / copytree / rmtree, os.remove / rename / replace; node
10
- // fs.writeFileSync / appendFileSync / copyFileSync / cpSync / renameSync / rmSync / unlinkSync / createWriteStream;
11
- // perl open(FH, '>' ...), rename, unlink, File::Copy; ruby File.write / File.open(p, 'w') / FileUtils.*.
12
- // A read (open(p), open(p, 'r'), read_text, readFileSync) is never a write. content is the new text where the
13
- // program says it (a literal, or a file's text read and .replace'd, in a program with no loop or branch), else null.
14
- // A write whose path the reader cannot work out makes every path the program names (its path-like literals, less
15
- // the ones it only reads) a write of unknown content, so the path-based hooks still judge it.
16
- import { basename, join } from 'node:path';
17
- import { statSync } from 'node:fs';
18
- import { closeOf, evaluate, scan, splitArgs, statements } from './program-read.mjs';
19
-
20
- const isDir = (p) => { try { return statSync(p).isDirectory(); } catch { return false; } };
21
-
22
- export function programOf(exe, args, stdin) {
23
- const lang = /^python(\d+(\.\d+)*)?$|^pypy3?$/.test(exe) ? 'python' : /^(node|nodejs|bun)$/.test(exe) ? 'node' : exe === 'perl' ? 'perl' : exe === 'ruby' ? 'ruby' : null;
24
- if (!lang) return null;
25
- const inline = { python: /^-[A-Za-z]*c$/, node: /^(-e|--eval|-p|--print|-pe)$/, perl: /^-[A-Za-z]*[eE]$/, ruby: /^-[A-Za-z]*e$/ }[lang];
26
- const takesValue = { python: /^-[WXQ]$/, node: /^(-r|--require|--import|--loader|--experimental-loader|-C|--conditions)$/, perl: /^-[IMx]$/, ruby: /^-[Ir]$/ }[lang];
27
- const code = [];
28
- for (let k = 0; k < args.length; k += 1) {
29
- const a = args[k];
30
- if (inline.test(a)) { if (args[k + 1] !== undefined) code.push(args[k + 1]); k += 1; if (lang === 'python') break; continue; }
31
- if (lang === 'node' && /^--(eval|print)=/.test(a)) { code.push(a.slice(a.indexOf('=') + 1)); continue; }
32
- if (lang === 'python' && /^-[A-Za-z]*m$/.test(a)) return null;
33
- if (takesValue.test(a)) { k += 1; continue; }
34
- if (a.startsWith('-') && a !== '-') continue;
35
- if (code.length) break;
36
- return a === '-' && stdin !== null ? { lang, code: stdin } : null;
37
- }
38
- if (code.length) return { lang, code: code.join('\n') };
39
- return stdin !== null && stdin !== undefined ? { lang, code: stdin } : null;
40
- }
41
-
42
- // The receiver of a method call: the postfix expression that ends just before text[dot].
43
- function receiverBefore(text, dot) {
44
- let k = dot - 1;
45
- while (k >= 0) {
46
- const c = text[k];
47
- if (c === ')' || c === ']') {
48
- const open = c === ')' ? '(' : '[';
49
- let depth = 0;
50
- for (; k >= 0; k -= 1) { if (text[k] === c) depth += 1; else if (text[k] === open && --depth === 0) break; }
51
- k -= 1;
52
- } else if (c === '\x02') k = text.lastIndexOf('\x01', k) - 1;
53
- else if (/[\w$.]/.test(c)) k -= 1;
54
- else break;
55
- }
56
- return text.slice(k + 1, dot);
57
- }
58
-
59
- const modeOp = (m) => (m === null ? 'write' : /^\+?>>|a/.test(m) ? 'append' : /^\+</.test(m) || /r\+/.test(m) ? 'edit' : /[wx>]|\+/.test(m) ? 'write' : null);
60
-
61
- const SITES = {
62
- python: /(?<![\w.])(?:io\.|codecs\.)?open\s*\(|\.(open|write_text|write_bytes|touch|unlink|rmdir)\s*\(|\bshutil\.(copy|copy2|copyfile|move|copytree|rmtree)\s*\(|\bos\.(remove|unlink|rename|replace)\s*\(/g,
63
- node: /\b(writeFileSync|writeFile|appendFileSync|appendFile|copyFileSync|copyFile|cpSync|renameSync|rmSync|unlinkSync|createWriteStream|truncateSync|openSync|outputFileSync)\s*\(|\bBun\.(write)\s*\(/g,
64
- perl: /\bopen\s*(?=[\s(])|\b(rename|unlink|copy|move)\s*\(/g,
65
- ruby: /\b(?:File|IO)\.(write|binwrite|open|rename|delete|unlink)\s*\(|\bFileUtils\.(cp|copy|mv|move|rm|rm_f|rm_rf|touch|install)\s*\(/g,
66
- };
67
-
68
- const COPY = /^(copy|copy2|copyfile|copyFileSync|copyFile|cp|install)$/;
69
- const MOVE = /^(move|rename|replace|renameSync|mv)$/;
70
- const DELETE = /^(rmtree|remove|unlink|rmdir|rmSync|unlinkSync|delete|rm|rm_f|rm_rf)$/;
71
-
72
- export function programWrites({ lang, code: src }, at, readText) {
73
- const { code, lits } = scan(src, lang);
74
- const stmts = statements(code);
75
- const branchy = /\b(for|while|foreach|until|unless|lambda|function|each|map|if|elif|else|try|switch|case|def)\b|=>|\bdo\b/.test(code);
76
- const ASSIGN = /^(?:(?:const|let|var|my|our)\s+)?([$@%]?[A-Za-z_][\w.]*)\s*(\+?)=(?!=)\s*([\s\S]+)$/;
77
- const count = new Map();
78
- for (const s of stmts) { const m = s.match(ASSIGN); if (m) count.set(m[1], (count.get(m[1]) ?? 0) + 1); }
79
- const env = new Map();
80
- const reads = new Set();
81
- const ctx = { env, lits, lang, at, readText, reads };
82
- const val = (e) => (e === undefined ? null : evaluate(e, ctx));
83
- const pathOf = (e) => { const v = val(e); return v && (v.t === 'str' || v.t === 'path') && v.v ? at(v.v) : null; };
84
- const textOf = (e) => { const v = val(e); return !branchy && v && v.t === 'str' ? v.v : null; };
85
- const out = [];
86
- const handles = new Map();
87
- let unresolved = false;
88
- const add = (path, op, content, extra = {}) => { if (path === null) unresolved = true; else out.push({ path, op, content, ...extra }); return out.at(-1); };
89
- const kw = (args, name) => args.find((a) => new RegExp(`^${name}\\s*=`).test(a))?.replace(/^\w+\s*=\s*/, '');
90
- const modeArg = (e) => (e === undefined ? undefined : val(e)?.t === 'str' ? val(e).v : null);
91
-
92
- for (const s of stmts) {
93
- for (const m of s.matchAll(SITES[lang])) {
94
- const name = m.slice(1).find(Boolean) ?? 'open';
95
- const paren = s.indexOf('(', m.index + m[0].length - 1);
96
- let args;
97
- let end;
98
- if (lang === 'perl' && name === 'open' && s[paren] !== '(' ) {
99
- const rest = s.slice(m.index + m[0].length).split(/\s+(?:or|\|\|)\s+|\s+or$/)[0];
100
- args = splitArgs(rest.replace(/^\s*\(|\)\s*$/g, ''));
101
- end = s.length;
102
- } else {
103
- end = closeOf(s, paren);
104
- if (end < 0) continue;
105
- args = splitArgs(s.slice(paren + 1, end));
106
- }
107
- const pos = args.filter((a) => !/^\w+\s*=[^=]/.test(a));
108
- const method = m[0].startsWith('.');
109
- const recv = method ? receiverBefore(s, m.index) : null;
110
- let site = null;
111
- if (lang === 'perl' && name === 'open') {
112
- const mode = pos.length >= 3 ? modeArg(pos[1]) : modeArg(pos[1]);
113
- if (pos.length >= 3) { const op = modeOp(mode); if (op) site = add(pathOf(pos[2]), op, null); } else if (mode !== null && mode !== undefined) {
114
- const two = mode.match(/^\s*(\+?[<>]{1,2}|\|)?\s*(.*)$/);
115
- const op = two[1] && two[1] !== '|' ? modeOp(two[1]) : null;
116
- if (op) site = add(two[2] ? at(two[2]) : null, op, null);
117
- } else if (pos[1] !== undefined) site = add(null, 'write', null);
118
- } else if (name === 'open' || name === 'openSync') {
119
- if (method && val(recv)?.t !== 'path') continue;
120
- const target = method ? recv : pos[0];
121
- const mode = modeArg(method ? pos[0] ?? kw(args, 'mode') : pos[1] ?? kw(args, 'mode'));
122
- const op = mode === undefined ? null : modeOp(mode);
123
- if (!op) { const p = pathOf(target); if (p) reads.add(p); continue; }
124
- site = add(pathOf(target), op, op === 'edit' ? null : '');
125
- const after = s.slice(end + 1);
126
- const chained = after.match(/^\s*\.\s*write\s*\(/);
127
- const as = after.match(/^\s*as\s+([A-Za-z_]\w*)/) ?? s.slice(0, m.index).match(/^([A-Za-z_]\w*)\s*=\s*$/);
128
- if (site && chained) { const e = closeOf(after, after.indexOf('(')); site.content = e < 0 ? null : textOf(after.slice(after.indexOf('(') + 1, e)); }
129
- else if (site && as) handles.set(as[1], { site, parts: [], ok: !branchy && site.content !== null, uses: 0 });
130
- else if (site) site.content = null;
131
- } else if (name === 'write_text' || name === 'write' || name === 'binwrite' || /^(writeFile|outputFile)/.test(name) || /^appendFile/.test(name)) {
132
- const target = method ? recv : pos[0];
133
- site = add(pathOf(target), /^append/.test(name) ? 'append' : 'write', name === 'binwrite' ? null : textOf(method ? pos[0] : pos[1]));
134
- } else if (name === 'write_bytes' || name === 'createWriteStream' || name === 'truncateSync') site = add(pathOf(method ? recv : pos[0]), name === 'truncateSync' ? 'truncate' : 'write', null);
135
- else if (name === 'touch') site = add(pathOf(method ? recv : pos[0]), 'touch', null);
136
- else if (DELETE.test(name)) { if (method && val(recv)?.t !== 'path') continue; for (const a of method ? [recv] : pos.slice(0, lang === 'ruby' ? pos.length : 1)) add(pathOf(a), 'delete', null); }
137
- else if (COPY.test(name) || MOVE.test(name) || /^(copytree|cpSync)$/.test(name)) {
138
- if (method) continue;
139
- const from = pathOf(pos[0]);
140
- let to = pathOf(pos[1]);
141
- if (to && from && isDir(to) && !/^(copytree|cpSync)$/.test(name)) to = join(to, basename(from));
142
- const tree = /^(copytree|cpSync)$/.test(name);
143
- add(to, MOVE.test(name) ? 'move' : 'copy', tree || !from ? null : readText(from), from ? { from } : {});
144
- if (MOVE.test(name)) add(from, 'delete', null);
145
- }
146
- }
147
- for (const [h, rec] of handles) {
148
- for (const m of s.matchAll(new RegExp(`(?<![\\w.])${h.replace(/\$/g, '\\$')}\\b`, 'g'))) {
149
- rec.uses += 1;
150
- const after = s.slice(m.index + h.length);
151
- const w = after.match(/^\s*\.\s*(write|close)\s*\(/);
152
- if (!w) continue;
153
- rec.uses -= 1;
154
- if (w[1] === 'close') continue;
155
- const open = m.index + h.length + after.indexOf('(');
156
- const e = closeOf(s, open);
157
- const t = e < 0 ? null : textOf(s.slice(open + 1, e));
158
- if (t === null) rec.ok = false; else rec.parts.push(t);
159
- }
160
- }
161
- const a = s.match(ASSIGN);
162
- if (a) {
163
- const v = branchy && count.get(a[1]) > 1 ? null : a[2] ? ((x, y) => (x?.t === 'str' && y?.t === 'str' ? { t: 'str', v: x.v + y.v } : null))(env.get(a[1]), val(a[3])) : val(a[3]);
164
- if (v) env.set(a[1], v); else env.delete(a[1]);
165
- }
166
- }
167
- for (const rec of handles.values()) rec.site.content = rec.ok && rec.uses <= 1 ? rec.parts.join('') : null;
168
- if (unresolved) {
169
- const named = new Set(out.map((w) => w.path));
170
- for (const l of lits) {
171
- if (l === null || l.length > 300 || !/^[~\w.@+\-/]+$/.test(l) || /^[-.]+$|^-|^\d+(\.\d+)*$/.test(l) || !(l.includes('/') || /\.[A-Za-z][A-Za-z0-9]{0,7}$/.test(l))) continue;
172
- const p = at(l);
173
- if (!named.has(p) && !reads.has(p)) { named.add(p); out.push({ path: p, op: 'write', content: null, named: true }); }
174
- }
175
- }
176
- return out;
177
- }