@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.
- package/CHANGELOG.md +74 -0
- package/README.md +275 -18
- package/bin/claude-packs.mjs +18 -10
- package/configurations/base/config.json +1 -1
- package/configurations/cpp/config.json +2 -1
- package/configurations/full-stack/config.json +2 -1
- package/configurations/go/config.json +3 -1
- package/configurations/node/config.json +3 -1
- package/configurations/python/config.json +3 -1
- package/configurations/rust/config.json +106 -0
- package/configurations/synth-creator/config.json +1 -1
- package/global/hooks/lib/shell-writes.mjs +2 -293
- package/lib/build-queue.mjs +22 -0
- package/lib/claude-md.mjs +4 -2
- package/lib/config-cli.mjs +1 -1
- package/lib/coverage.mjs +10 -2
- package/lib/doctor.mjs +14 -3
- package/lib/fix-plan.mjs +2 -1
- package/lib/found-folders.mjs +25 -0
- package/lib/global.mjs +3 -3
- package/lib/hook-proof.mjs +24 -7
- package/lib/kit-command.mjs +14 -0
- package/lib/layout.mjs +27 -3
- package/lib/logs.mjs +45 -5
- package/lib/new.mjs +1 -1
- package/lib/packs.mjs +4 -2
- package/lib/rule-tests.mjs +42 -0
- package/lib/scan-detect.mjs +41 -5
- package/lib/scan.mjs +5 -2
- package/lib/suggest.mjs +16 -3
- package/lib/sync.mjs +5 -2
- package/lib/workorder.mjs +26 -0
- package/package.json +1 -1
- package/packs/audio-core/hooks/param-id-lock.mjs +59 -16
- package/packs/audio-core/pack.json +5 -0
- package/packs/audio-core/rules/a-control-is-proved-through-its-input.md +26 -0
- package/packs/audio-core/rules/cpu-worst-case-per-block.md +3 -1
- package/packs/audio-core/rules/house-control-behaviours.md +1 -1
- package/packs/audio-core/rules/scoped-no-denormals.md +1 -1
- package/packs/audio-core/rules/straight-on-and-aligned.md +1 -1
- package/packs/audio-core/rules/ui-matches-reference-look.md +2 -0
- package/packs/audio-core/skills/analyse-reference-look/SKILL.md +25 -11
- package/packs/audio-core/skills/prove-control/SKILL.md +2 -0
- package/packs/base/checks/one-walk-per-template.json +1 -1
- package/packs/base/hooks/code-shape.mjs +10 -6
- package/packs/base/hooks/dispatch-once.mjs +58 -21
- package/packs/base/hooks/folder-law.mjs +3 -0
- package/packs/base/hooks/git-guard.mjs +22 -109
- package/packs/base/hooks/lesson-checks.mjs +9 -1
- package/packs/base/hooks/lib/code-shapes.mjs +11 -6
- package/packs/base/hooks/lib/crash-policy.mjs +47 -0
- package/packs/base/hooks/lib/dup-index.mjs +6 -1
- package/packs/base/hooks/lib/dup.mjs +2 -2
- package/packs/base/hooks/lib/event-log.mjs +4 -3
- package/packs/base/hooks/lib/git-adds.mjs +2 -1
- package/packs/base/hooks/lib/git-auth.mjs +44 -0
- package/packs/base/hooks/lib/git-guard-selftest.mjs +93 -0
- package/packs/base/hooks/lib/languages.mjs +5 -2
- package/packs/base/hooks/lib/layout.mjs +6 -2
- package/packs/base/hooks/lib/making-check-selftest.mjs +3 -0
- package/packs/base/hooks/lib/near-copies.mjs +1 -1
- package/packs/base/hooks/lib/program-read.mjs +6 -3
- package/packs/base/hooks/lib/rust-scan.mjs +139 -0
- package/packs/base/hooks/lib/shape-lists.mjs +31 -0
- package/packs/base/hooks/lib/shape-prose.mjs +78 -0
- package/packs/base/hooks/lib/shape.mjs +18 -39
- package/packs/base/hooks/lib/turn.mjs +6 -1
- package/packs/base/hooks/lib/unread-writes.mjs +24 -0
- package/packs/base/hooks/making-check.mjs +12 -7
- package/packs/base/hooks/run-hooks.mjs +15 -7
- package/packs/base/hooks/shape-law.mjs +11 -4
- package/packs/base/hooks/suite-once.mjs +22 -6
- package/packs/base/rules/a-control-is-seen-to-fire.md +1 -1
- package/packs/base/rules/behaviour-lives-with-its-kind.md +1 -1
- package/packs/base/rules/bug-fix-starts-red.md +1 -1
- package/packs/base/rules/field-map-over-if-runs.md +1 -1
- package/packs/base/rules/instances-by-index-identities-by-id.md +2 -2
- package/packs/base/rules/lookup-over-if-chains.md +1 -1
- package/packs/base/rules/plan-and-logs.md +1 -1
- package/packs/base/rules/refusals-are-judged.md +1 -0
- package/packs/base/rules/test-the-template-once.md +2 -1
- package/packs/base/rules/unattended-runs.md +15 -0
- package/packs/base/skills/new-project/SKILL.md +23 -0
- package/packs/base/skills/write-a-workorder/SKILL.md +41 -0
- package/packs/cpp/checks/cpp-find-with-the-library.json +2 -2
- package/packs/go/pack.json +5 -0
- package/packs/hook-proof/rules/a-refusal-is-exit-2.md +2 -2
- package/packs/instrument-shell/rules/render-the-extremes.md +2 -2
- package/packs/midi/checks/position-not-integrated.json +5 -1
- package/packs/postgres/checks/pg-bind-values.json +33 -0
- package/packs/postgres/checks/pg-concurrently-in-txn.json +18 -0
- package/packs/postgres/checks/pg-fk-not-valid.json +18 -0
- package/packs/postgres/checks/pg-index-concurrently.json +18 -0
- package/packs/postgres/checks/pg-no-float-money.json +19 -0
- package/packs/postgres/checks/pg-no-money.json +18 -0
- package/packs/postgres/checks/pg-no-required-column.json +18 -0
- package/packs/postgres/checks/pg-no-volatile-default.json +18 -0
- package/packs/postgres/checks/pg-not-exists.json +18 -0
- package/packs/postgres/checks/pg-timestamptz.json +18 -0
- package/packs/postgres/hooks/db-wipe-guard.mjs +95 -0
- package/packs/postgres/pack.json +48 -0
- package/packs/postgres/rules/postgres-migrations-never-stop-traffic.md +28 -0
- package/packs/postgres/rules/postgres-one-pool-per-process.md +21 -0
- package/packs/postgres/rules/postgres-queries-are-null-safe-and-bounded.md +26 -0
- package/packs/postgres/rules/postgres-types-say-what-they-hold.md +22 -0
- package/packs/postgres/rules/postgres-values-are-bound.md +130 -0
- package/packs/postgres/skills/postgres-safe-migration/SKILL.md +45 -0
- package/packs/reverse-engineering/rules/addresses-name-their-space.md +1 -0
- package/packs/reverse-engineering/rules/reproduction-or-original.md +1 -1
- package/packs/reverse-engineering/skills/gather-sources/SKILL.md +1 -0
- package/packs/rust/checks/rust-allow-needs-reason.json +16 -0
- package/packs/rust/checks/rust-blocking-in-async.json +32 -0
- package/packs/rust/checks/rust-if-chain-is-a-match.json +32 -0
- package/packs/rust/checks/rust-static-mut.json +32 -0
- package/packs/rust/checks/rust-string-as-error.json +34 -0
- package/packs/rust/checks/rust-stub-left-in.json +32 -0
- package/packs/rust/checks/rust-unsafe-needs-safety.json +31 -0
- package/packs/rust/checks/rust-unwrap-in-library.json +34 -0
- package/packs/rust/checks/rust-wildcard-dependency.json +16 -0
- package/packs/rust/clippy.toml +5 -0
- package/packs/rust/pack.json +27 -0
- package/packs/rust/rules/rust-async-never-blocks.md +20 -0
- package/packs/rust/rules/rust-borrow-before-you-clone.md +19 -0
- package/packs/rust/rules/rust-errors-are-returned.md +22 -0
- package/packs/rust/rules/rust-lints-are-the-gate.md +23 -0
- package/packs/rust/rules/rust-repeats-become-templates.md +28 -0
- package/packs/rust/rules/rust-unsafe-is-proved.md +21 -0
- package/packs/rust/skills/rust-extract-template/SKILL.md +50 -0
- package/packs/rust/skills/rust-port/SKILL.md +22 -0
- package/packs/rust/skills/rust-review/SKILL.md +24 -0
- package/packs/rust/tools/rust-lints.toml +19 -0
- package/packs/sound-design/skills/bounce-a-plugin/SKILL.md +40 -0
- package/packs/sound-design/tools/vst3host/build.sh +73 -0
- package/packs/sound-design/tools/vst3host/host_events.h +101 -0
- package/packs/sound-design/tools/vst3host/options.h +172 -0
- package/packs/sound-design/tools/vst3host/render.cpp +245 -0
- package/packs/sound-design/tools/vst3host/session.h +241 -0
- package/packs/synth-creator/hooks/build-guard.mjs +7 -7
- package/packs/synth-creator/hooks/protect-vendor.mjs +2 -1
- package/packs/synth-creator/hooks/stage-guard.mjs +1 -1
- package/packs/synth-creator/rules/timing-from-synthcore.md +1 -1
- package/packs/synth-creator/skills/build-through-the-queue/SKILL.md +4 -2
- package/packs/synth-creator/tools/buildq.mjs +74 -0
- package/prompts/workorder/PROMPT.md +53 -0
- package/prompts/workorder/run.sh +36 -0
- package/prompts/workorder/workorder.md +59 -0
- package/global/hooks/lib/program-read.mjs +0 -196
- 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
|
-
}
|