finitemonkey 0.1.0-next.7 → 0.1.0-next.9
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/dist/finitemonkey.mjs +129 -24
- package/package.json +1 -1
package/dist/finitemonkey.mjs
CHANGED
|
@@ -4441,11 +4441,11 @@ var requirement2 = (want, ctx) => {
|
|
|
4441
4441
|
};
|
|
4442
4442
|
var gitCheck = (check2, ctx) => {
|
|
4443
4443
|
const wants = check2.require ?? ["committed"];
|
|
4444
|
-
const
|
|
4444
|
+
const missing2 = wants.flatMap((want) => {
|
|
4445
4445
|
const problem4 = requirement2(want, ctx);
|
|
4446
4446
|
return problem4 === void 0 ? [] : [problem4];
|
|
4447
4447
|
});
|
|
4448
|
-
return
|
|
4448
|
+
return missing2.length > 0 ? failed(missing2) : passed([note("gate.git.ok", { require: wants.join(", ") })]);
|
|
4449
4449
|
};
|
|
4450
4450
|
|
|
4451
4451
|
// src/guards/release/grants.ts
|
|
@@ -4611,6 +4611,7 @@ var LOCK_FILE = ".finitemonkey/lock.json";
|
|
|
4611
4611
|
var BACKUPS_DIR = ".finitemonkey/backups";
|
|
4612
4612
|
var LOCKS_DIR = ".finitemonkey/locks";
|
|
4613
4613
|
var INSTALL_LOCK = ".finitemonkey/locks/install.lock";
|
|
4614
|
+
var VERIFY_SKILL = ".claude/skills/verify/SKILL.md";
|
|
4614
4615
|
var LOCAL_BIN = "node_modules/finitemonkey/dist/finitemonkey.mjs";
|
|
4615
4616
|
var VENDOR_BIN = ".finitemonkey/runtime/dist/finitemonkey.mjs";
|
|
4616
4617
|
var VENDOR_PACKAGE = ".finitemonkey/runtime/package.json";
|
|
@@ -6506,10 +6507,10 @@ var symbolFindings = (context, pair, path, text6) => {
|
|
|
6506
6507
|
var verifyFile = (context, pair) => {
|
|
6507
6508
|
const path = resultPathOf(context, pair);
|
|
6508
6509
|
if (pair.ours?.binary === true || pair.theirs?.binary === true) return binaryFindings(context, pair, path);
|
|
6509
|
-
const
|
|
6510
|
+
const missing2 = ["ours", "theirs"].flatMap(
|
|
6510
6511
|
(side) => created2(pair[side]) && path === void 0 ? [finding("missing-file", pair.key, side, pair.key)] : []
|
|
6511
6512
|
);
|
|
6512
|
-
if (
|
|
6513
|
+
if (missing2.length > 0) return missing2;
|
|
6513
6514
|
const copied = [pair.ours, pair.theirs].find((diff) => diff?.status === "copied");
|
|
6514
6515
|
const baseSource = copied?.oldPath ?? (created2(pair.ours) || created2(pair.theirs) ? void 0 : pair.key);
|
|
6515
6516
|
const baseText = baseSource === void 0 ? void 0 : blobText(context.runtime, context.repo, context.inputs.base, baseSource);
|
|
@@ -10816,34 +10817,49 @@ var mergeHooks = (hooks, isOwn, wanted) => {
|
|
|
10816
10817
|
var withHooks = (settings, hooks) => {
|
|
10817
10818
|
return setKey(settings, "hooks", Object.keys(hooks).length === 0 ? void 0 : hooks);
|
|
10818
10819
|
};
|
|
10819
|
-
var
|
|
10820
|
+
var BLOCK_READS = "blockReadsOutsideWorkingDirectories";
|
|
10821
|
+
var withPermissions = (settings, lists) => {
|
|
10820
10822
|
const permissions = asRecord(settings["permissions"]);
|
|
10821
|
-
const
|
|
10823
|
+
const withDeny = setKey(permissions, "deny", lists.deny.length === 0 ? void 0 : lists.deny);
|
|
10824
|
+
const withAsk = setKey(withDeny, "ask", lists.ask.length === 0 ? void 0 : lists.ask);
|
|
10825
|
+
const next2 = setKey(withAsk, BLOCK_READS, lists.blockReads);
|
|
10822
10826
|
return setKey(settings, "permissions", Object.keys(next2).length === 0 ? void 0 : next2);
|
|
10823
10827
|
};
|
|
10828
|
+
var missing = (wanted, existing) => wanted.filter((rule) => !existing.includes(rule));
|
|
10829
|
+
var withoutAdded = (existing, added) => existing.filter((rule) => typeof rule !== "string" || !added.includes(rule));
|
|
10824
10830
|
var ownHookMatcher = (recorded) => (handler) => isOwnHandler(handler) || recorded.some((own) => JSON.stringify(own) === JSON.stringify(handler));
|
|
10825
10831
|
var mergeSettings = (current, wanted, recorded) => {
|
|
10826
10832
|
const schema = Object.keys(current).length === 0;
|
|
10827
10833
|
const base = schema ? { $schema: SETTINGS_SCHEMA_URL } : current;
|
|
10828
10834
|
const hooks = mergeHooks(asRecord(base["hooks"]), ownHookMatcher(recorded), wanted.hooks);
|
|
10829
|
-
const
|
|
10830
|
-
const
|
|
10835
|
+
const permissions = asRecord(base["permissions"]);
|
|
10836
|
+
const existingDeny = asList(permissions["deny"]);
|
|
10837
|
+
const existingAsk = asList(permissions["ask"]);
|
|
10838
|
+
const deny2 = missing(wanted.deny, existingDeny);
|
|
10839
|
+
const ask = missing(wanted.ask, existingAsk);
|
|
10840
|
+
const blockReads = !(BLOCK_READS in permissions);
|
|
10831
10841
|
const attribution = wanted.attributionOff && !("attribution" in base);
|
|
10832
10842
|
const marketplace = wanted.marketplace && !hasEntry(base, "extraKnownMarketplaces", MARKETPLACE_KEY);
|
|
10833
10843
|
const plugin = wanted.marketplace && !hasEntry(base, "enabledPlugins", PLUGIN_ID);
|
|
10834
|
-
let settings = withPermissions(withHooks(base, hooks),
|
|
10844
|
+
let settings = withPermissions(withHooks(base, hooks), {
|
|
10845
|
+
deny: [...existingDeny, ...deny2],
|
|
10846
|
+
ask: [...existingAsk, ...ask],
|
|
10847
|
+
blockReads: blockReads ? true : permissions[BLOCK_READS]
|
|
10848
|
+
});
|
|
10835
10849
|
if (attribution) settings = { ...settings, attribution: ATTRIBUTION_OFF };
|
|
10836
10850
|
if (marketplace) settings = withEntry(settings, "extraKnownMarketplaces", MARKETPLACE_KEY, MARKETPLACE_ENTRY);
|
|
10837
10851
|
if (plugin) settings = withEntry(settings, "enabledPlugins", PLUGIN_ID, true);
|
|
10838
|
-
return { settings, added: { schema, deny: deny2, attribution, marketplace, plugin } };
|
|
10852
|
+
return { settings, added: { schema, deny: deny2, ask, blockReads, attribution, marketplace, plugin } };
|
|
10839
10853
|
};
|
|
10840
10854
|
var sameAttribution = (value) => JSON.stringify(value) === JSON.stringify(ATTRIBUTION_OFF);
|
|
10841
10855
|
var unmergeSettings = (current, added, recorded) => {
|
|
10842
10856
|
const hooks = mergeHooks(asRecord(current["hooks"]), ownHookMatcher(recorded), {});
|
|
10843
|
-
const
|
|
10844
|
-
|
|
10845
|
-
|
|
10846
|
-
|
|
10857
|
+
const permissions = asRecord(current["permissions"]);
|
|
10858
|
+
let settings = withPermissions(withHooks(current, hooks), {
|
|
10859
|
+
deny: withoutAdded(asList(permissions["deny"]), added.deny),
|
|
10860
|
+
ask: withoutAdded(asList(permissions["ask"]), added.ask),
|
|
10861
|
+
blockReads: added.blockReads && permissions[BLOCK_READS] === true ? void 0 : permissions[BLOCK_READS]
|
|
10862
|
+
});
|
|
10847
10863
|
if (added.attribution && sameAttribution(settings["attribution"])) {
|
|
10848
10864
|
settings = setKey(settings, "attribution", void 0);
|
|
10849
10865
|
}
|
|
@@ -11049,9 +11065,11 @@ var parseAdded = (value) => {
|
|
|
11049
11065
|
const deny2 = strings2(value["deny"]);
|
|
11050
11066
|
const { schema, attribution } = value;
|
|
11051
11067
|
if (deny2 === void 0 || typeof schema !== "boolean" || typeof attribution !== "boolean") return void 0;
|
|
11068
|
+
const ask = strings2(value["ask"]) ?? [];
|
|
11069
|
+
const blockReads = value["blockReads"] === true;
|
|
11052
11070
|
const marketplace = value["marketplace"] === true;
|
|
11053
11071
|
const plugin = value["plugin"] === true;
|
|
11054
|
-
return { schema, attribution, deny: deny2, marketplace, plugin };
|
|
11072
|
+
return { schema, attribution, deny: deny2, ask, blockReads, marketplace, plugin };
|
|
11055
11073
|
};
|
|
11056
11074
|
var parseEntry2 = (value) => {
|
|
11057
11075
|
if (!isRecord(value)) return void 0;
|
|
@@ -11098,6 +11116,42 @@ var parseLock = (text6) => {
|
|
|
11098
11116
|
var renderLock = (lock) => `${JSON.stringify(lock, null, 2)}
|
|
11099
11117
|
`;
|
|
11100
11118
|
|
|
11119
|
+
// src/install/ask-floor.ts
|
|
11120
|
+
var DEFAULT_BRANCHES = ["main", "master"];
|
|
11121
|
+
var pushTo = (branch) => [
|
|
11122
|
+
`Bash(git push * ${branch})`,
|
|
11123
|
+
`Bash(git push * ${branch} *)`,
|
|
11124
|
+
`Bash(git push *:${branch})`,
|
|
11125
|
+
`Bash(git push *:${branch} *)`
|
|
11126
|
+
];
|
|
11127
|
+
var ASK_FLOOR = {
|
|
11128
|
+
push: DEFAULT_BRANCHES.flatMap(pushTo),
|
|
11129
|
+
deploy: [
|
|
11130
|
+
"Bash(npm publish *)",
|
|
11131
|
+
"Bash(pnpm publish *)",
|
|
11132
|
+
"Bash(yarn publish *)",
|
|
11133
|
+
"Bash(yarn npm publish *)",
|
|
11134
|
+
"Bash(bun publish *)",
|
|
11135
|
+
"Bash(vercel * --prod*)",
|
|
11136
|
+
"Bash(vercel --prod*)",
|
|
11137
|
+
"Bash(netlify deploy * --prod*)",
|
|
11138
|
+
"Bash(fly deploy *)",
|
|
11139
|
+
"Bash(flyctl deploy *)",
|
|
11140
|
+
"Bash(firebase deploy *)",
|
|
11141
|
+
"Bash(wrangler deploy *)",
|
|
11142
|
+
"Bash(serverless deploy *)",
|
|
11143
|
+
"Bash(sls deploy *)",
|
|
11144
|
+
"Bash(cdk deploy *)",
|
|
11145
|
+
"Bash(terraform apply *)",
|
|
11146
|
+
"Bash(tofu apply *)",
|
|
11147
|
+
"Bash(pulumi up *)",
|
|
11148
|
+
"Bash(kubectl apply *)",
|
|
11149
|
+
"Bash(helm install *)",
|
|
11150
|
+
"Bash(helm upgrade *)"
|
|
11151
|
+
]
|
|
11152
|
+
};
|
|
11153
|
+
var ASK_RULES = [...ASK_FLOOR.push, ...ASK_FLOOR.deploy];
|
|
11154
|
+
|
|
11101
11155
|
// src/install/claude-version.ts
|
|
11102
11156
|
var ON_FAILURE_MIN = [2, 1, 295];
|
|
11103
11157
|
var PROBE_MS = 8e3;
|
|
@@ -11119,6 +11173,23 @@ var detectClaude = (probe) => {
|
|
|
11119
11173
|
return { version: parsed.join("."), failClosed: atLeast(parsed, ON_FAILURE_MIN) };
|
|
11120
11174
|
};
|
|
11121
11175
|
|
|
11176
|
+
// src/install/claudeignore.ts
|
|
11177
|
+
var CLAUDEIGNORE_FILES = [".claudeignore", ".claude/.claudeignore"];
|
|
11178
|
+
var ruleFor = (raw) => {
|
|
11179
|
+
const line2 = withoutCarriageReturn(raw).trim();
|
|
11180
|
+
if (line2 === "" || line2.startsWith("#") || line2.startsWith("!") || line2.includes(")")) return void 0;
|
|
11181
|
+
const anchored = line2.startsWith("/");
|
|
11182
|
+
const body = (anchored ? line2.slice(1) : line2).replace(/\/+$/, "");
|
|
11183
|
+
if (body === "" || body === "*" || body === "**") return void 0;
|
|
11184
|
+
const path = line2.endsWith("/") ? `${body}/**` : body;
|
|
11185
|
+
return `Read(${anchored || body.includes("/") ? "./" : ""}${path})`;
|
|
11186
|
+
};
|
|
11187
|
+
var claudeignoreRules = (texts) => [
|
|
11188
|
+
...new Set(
|
|
11189
|
+
texts.flatMap((text6) => (text6 ?? "").split("\n")).map(ruleFor).filter((rule) => rule !== void 0)
|
|
11190
|
+
)
|
|
11191
|
+
];
|
|
11192
|
+
|
|
11122
11193
|
// src/install/config-template.ts
|
|
11123
11194
|
var configTemplate = (t, preset, lang) => [
|
|
11124
11195
|
`# ${GENERATED_MARKER}`,
|
|
@@ -11277,6 +11348,33 @@ var withoutBlock = (text6) => {
|
|
|
11277
11348
|
return [...lines3.slice(0, range.start), ...lines3.slice(range.end + 1)].join(eol);
|
|
11278
11349
|
};
|
|
11279
11350
|
|
|
11351
|
+
// src/install/skill-template.ts
|
|
11352
|
+
var gateCommand2 = (runner) => {
|
|
11353
|
+
const args = "gate --fast --json";
|
|
11354
|
+
if (runner.kind === "local") return `node ${LOCAL_BIN} ${args}`;
|
|
11355
|
+
if (runner.kind === "vendored") return `node ${VENDOR_BIN} ${args}`;
|
|
11356
|
+
return isScript(runner.path) ? `node "${runner.path}" ${args}` : `"${runner.path}" ${args}`;
|
|
11357
|
+
};
|
|
11358
|
+
var verifySkillTemplate = (runner) => [
|
|
11359
|
+
"---",
|
|
11360
|
+
"name: verify",
|
|
11361
|
+
"description: >-",
|
|
11362
|
+
" Use before every commit and before telling the person something works. Runs the fast gate through Finite Monkey so the result is recorded as evidence for the current tree instead of being taken on your word.",
|
|
11363
|
+
"when_to_use: >-",
|
|
11364
|
+
" Trigger phrases include commit, run the tests, check it works, roda os testes, confere, verifica, prueba.",
|
|
11365
|
+
"---",
|
|
11366
|
+
"",
|
|
11367
|
+
"# Verify: evidence, not claims",
|
|
11368
|
+
"",
|
|
11369
|
+
`1. From the project root, run \`${gateCommand2(runner)}\`.`,
|
|
11370
|
+
"2. Read `data`; for every failing item, fix the cause, never the check.",
|
|
11371
|
+
"3. Quote the command and its exit code when you report a result. A result from before your last edit no longer counts.",
|
|
11372
|
+
"",
|
|
11373
|
+
`<!-- ${GENERATED_MARKER} -->`,
|
|
11374
|
+
""
|
|
11375
|
+
].join("\n");
|
|
11376
|
+
var isOwnSkill = hasMarker;
|
|
11377
|
+
|
|
11280
11378
|
// src/install/vendor.ts
|
|
11281
11379
|
var isBundle = (text6) => text6.startsWith("#!/usr/bin/env node") && text6.includes("finitemonkey");
|
|
11282
11380
|
var isOwnPackage = (text6) => {
|
|
@@ -11318,6 +11416,8 @@ var joinAdded = (previous, next2) => previous === void 0 ? next2 : {
|
|
|
11318
11416
|
schema: previous.schema || next2.schema,
|
|
11319
11417
|
attribution: previous.attribution || next2.attribution,
|
|
11320
11418
|
deny: union(previous.deny, next2.deny),
|
|
11419
|
+
ask: union(previous.ask, next2.ask),
|
|
11420
|
+
blockReads: previous.blockReads || next2.blockReads,
|
|
11321
11421
|
marketplace: previous.marketplace || next2.marketplace,
|
|
11322
11422
|
plugin: previous.plugin || next2.plugin
|
|
11323
11423
|
};
|
|
@@ -11331,7 +11431,8 @@ var settingsItem = (project, detection, input, previous) => {
|
|
|
11331
11431
|
const hooks = wantedHooks(detection);
|
|
11332
11432
|
const wanted = {
|
|
11333
11433
|
hooks,
|
|
11334
|
-
deny: DENY_RULES,
|
|
11434
|
+
deny: [...DENY_RULES, ...claudeignoreRules(CLAUDEIGNORE_FILES.map((path) => project.read(path)))],
|
|
11435
|
+
ask: ASK_RULES,
|
|
11335
11436
|
attributionOff: input.preset === "gabriel",
|
|
11336
11437
|
marketplace: input.marketplace
|
|
11337
11438
|
};
|
|
@@ -11365,18 +11466,21 @@ var generatedItem = (project, file, previous) => {
|
|
|
11365
11466
|
}
|
|
11366
11467
|
return { ...base, action: actionFor(before, template, "update"), after: template };
|
|
11367
11468
|
};
|
|
11368
|
-
var generatedFiles = (input, vendor) => {
|
|
11369
|
-
const
|
|
11370
|
-
|
|
11469
|
+
var generatedFiles = (input, runner, vendor) => {
|
|
11470
|
+
const own = [
|
|
11471
|
+
{ path: CONFIG_FILE, template: configTemplate(input.t, input.preset, input.lang), isOurs: hasMarker },
|
|
11472
|
+
{ path: VERIFY_SKILL, template: verifySkillTemplate(runner), isOurs: isOwnSkill }
|
|
11473
|
+
];
|
|
11474
|
+
if (!vendor) return accept(own);
|
|
11371
11475
|
if (input.bundle === void 0) return reject({ kind: "bundle", detail: "the running finitemonkey is not a bundle" });
|
|
11372
|
-
return accept([
|
|
11476
|
+
return accept([...own, ...vendoredFiles(input.bundle, input.version)]);
|
|
11373
11477
|
};
|
|
11374
11478
|
var entryFor = (lock, path) => lock?.entries.find((entry2) => entry2.path === path);
|
|
11375
11479
|
var buildPlan = (project, detection, input, previous) => {
|
|
11376
11480
|
const settings = settingsItem(project, detection, input, entryFor(previous, SETTINGS_FILE));
|
|
11377
11481
|
if (!settings.ok) return settings;
|
|
11378
11482
|
const vendor = detection.runner.kind === "vendored" || entryFor(previous, VENDOR_BIN) !== void 0;
|
|
11379
|
-
const files = generatedFiles(input, vendor);
|
|
11483
|
+
const files = generatedFiles(input, detection.runner, vendor);
|
|
11380
11484
|
if (!files.ok) return files;
|
|
11381
11485
|
const generated = files.value.map((file) => generatedItem(project, file, entryFor(previous, file.path)));
|
|
11382
11486
|
return accept({
|
|
@@ -11433,7 +11537,7 @@ var nextLock = (plan, version, now, backupDir, createdDirs) => ({
|
|
|
11433
11537
|
preset: plan.preset,
|
|
11434
11538
|
installedAt: plan.previous?.installedAt ?? now,
|
|
11435
11539
|
updatedAt: now,
|
|
11436
|
-
createdDirs: plan.previous?.createdDirs ?? createdDirs,
|
|
11540
|
+
createdDirs: [.../* @__PURE__ */ new Set([...plan.previous?.createdDirs ?? [], ...createdDirs])],
|
|
11437
11541
|
entries: plan.items.filter((item) => item.action !== "skip-foreign").map((item) => entryOf2(plan, item, backupDir))
|
|
11438
11542
|
});
|
|
11439
11543
|
var needsBackup = (plan, item) => item.before !== void 0 && plan.previous?.entries.find((entry2) => entry2.path === item.path)?.backup === void 0;
|
|
@@ -11449,7 +11553,8 @@ var writeAll = (project, items) => {
|
|
|
11449
11553
|
}
|
|
11450
11554
|
return accept(void 0);
|
|
11451
11555
|
};
|
|
11452
|
-
var
|
|
11556
|
+
var OWN_DIRS = [dirname6(dirname6(VERIFY_SKILL)), dirname6(VERIFY_SKILL), ".claude", STATE_DIR];
|
|
11557
|
+
var createdBy = (project) => OWN_DIRS.filter((dir) => !project.exists(dir));
|
|
11453
11558
|
var commit = (project, plan, version, createdDirs) => {
|
|
11454
11559
|
const now = project.runtime.now();
|
|
11455
11560
|
const changing = plan.items.filter((item) => CHANGING.includes(item.action));
|
|
@@ -12539,7 +12644,7 @@ var removalFor = (project, entry2, options) => {
|
|
|
12539
12644
|
var planRemoval = (project, lock, options) => ({
|
|
12540
12645
|
items: lock.entries.map((entry2) => removalFor(project, entry2, options)),
|
|
12541
12646
|
purge: options.purge,
|
|
12542
|
-
removeDirs: lock.createdDirs.filter((dir) => dir !== STATE_DIR)
|
|
12647
|
+
removeDirs: lock.createdDirs.filter((dir) => dir !== STATE_DIR).toSorted((a, b) => b.split("/").length - a.split("/").length)
|
|
12543
12648
|
});
|
|
12544
12649
|
var applyItem = (project, item) => {
|
|
12545
12650
|
if (item.action === "delete") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "finitemonkey",
|
|
3
|
-
"version": "0.1.0-next.
|
|
3
|
+
"version": "0.1.0-next.9",
|
|
4
4
|
"description": "Claude Code hooks that stop a coding agent from saying done before the tests pass: a machine-checked definition of done, plus guardrails for secrets, git safety and production. Pre-release.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|