finitemonkey 0.1.0-next.24 → 0.1.0-next.25
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 +246 -82
- package/package.json +1 -1
package/dist/finitemonkey.mjs
CHANGED
|
@@ -3239,6 +3239,36 @@ var deadlineFor = (event, raw) => {
|
|
|
3239
3239
|
return EVENT_DEADLINES[event] ?? DEFAULT_HOOK_DEADLINE_MS;
|
|
3240
3240
|
};
|
|
3241
3241
|
|
|
3242
|
+
// src/gate/hooks/common.ts
|
|
3243
|
+
import { dirname as dirname5, join as join11 } from "node:path";
|
|
3244
|
+
var DEFAULT_GATE_BUDGET_MS = 3e5;
|
|
3245
|
+
var sessionCwd = (input) => input.payload.cwd === void 0 ? input.cwd : toAbsolutePath(input.payload.cwd, input.cwd);
|
|
3246
|
+
var MAX_DEPTH = 64;
|
|
3247
|
+
var findRepoRoot = (runtime, start) => {
|
|
3248
|
+
let current2 = start;
|
|
3249
|
+
for (let depth = 0; depth < MAX_DEPTH; depth += 1) {
|
|
3250
|
+
if (runtime.stat(toAbsolutePath(join11(current2, ".git"))) !== void 0) return current2;
|
|
3251
|
+
const parent = toAbsolutePath(dirname5(current2));
|
|
3252
|
+
if (parent === current2) return void 0;
|
|
3253
|
+
current2 = parent;
|
|
3254
|
+
}
|
|
3255
|
+
return void 0;
|
|
3256
|
+
};
|
|
3257
|
+
var projectPaths = (input) => {
|
|
3258
|
+
const root = findRepoRoot(input.runtime, sessionCwd(input));
|
|
3259
|
+
return root === void 0 ? void 0 : fmPaths(root);
|
|
3260
|
+
};
|
|
3261
|
+
var gateBudget = (input) => {
|
|
3262
|
+
const parsed = Number.parseInt(input.env["FINITEMONKEY_GATE_BUDGET_MS"] ?? "", 10);
|
|
3263
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_GATE_BUDGET_MS;
|
|
3264
|
+
};
|
|
3265
|
+
var allowWith = (output) => ({ kind: "allow", output });
|
|
3266
|
+
var contextOutput = (event, additionalContext, systemMessage) => ({
|
|
3267
|
+
hookSpecificOutput: { hookEventName: event, additionalContext },
|
|
3268
|
+
...systemMessage === void 0 ? {} : { systemMessage }
|
|
3269
|
+
});
|
|
3270
|
+
var human = (text8) => `finitemonkey: ${text8}`;
|
|
3271
|
+
|
|
3242
3272
|
// src/hooks/fields.ts
|
|
3243
3273
|
var textField = (record3, ...names) => {
|
|
3244
3274
|
for (const name of names) {
|
|
@@ -4005,7 +4035,7 @@ var unwrap = (initial, stdin) => {
|
|
|
4005
4035
|
};
|
|
4006
4036
|
|
|
4007
4037
|
// src/shell/normalize.ts
|
|
4008
|
-
var
|
|
4038
|
+
var MAX_DEPTH2 = 8;
|
|
4009
4039
|
var ASSIGNMENT2 = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/s;
|
|
4010
4040
|
var PS_ENV = /^\$env:([A-Za-z_][A-Za-z0-9_]*)=?(.*)$/is;
|
|
4011
4041
|
var CHANGES_DIR = /* @__PURE__ */ new Set(["cd", "pushd", "set-location", "push-location"]);
|
|
@@ -4081,7 +4111,7 @@ var visit = (raw, language, context, depth, collector) => {
|
|
|
4081
4111
|
for (const extra of result.extra) emit(extra, raw, language, context, collector);
|
|
4082
4112
|
};
|
|
4083
4113
|
var collect = (text8, language, context, depth, collector) => {
|
|
4084
|
-
if (depth >
|
|
4114
|
+
if (depth > MAX_DEPTH2) {
|
|
4085
4115
|
collector.incomplete = true;
|
|
4086
4116
|
return;
|
|
4087
4117
|
}
|
|
@@ -4310,7 +4340,7 @@ var selfGuard = (input) => {
|
|
|
4310
4340
|
};
|
|
4311
4341
|
|
|
4312
4342
|
// src/guards/tests/hook.ts
|
|
4313
|
-
import { join as
|
|
4343
|
+
import { join as join12, relative as relative3 } from "node:path";
|
|
4314
4344
|
|
|
4315
4345
|
// src/guards/tests/patterns.ts
|
|
4316
4346
|
var JS_TEST = /(^|\/)(__tests__|__test__)\/|\.(test|spec)\.[cm]?[jt]sx?$|(^|\/)tests?\/.*\.[cm]?[jt]sx?$/;
|
|
@@ -4500,7 +4530,7 @@ var shellViolations = (input, root) => {
|
|
|
4500
4530
|
return parseShell(command).flatMap(
|
|
4501
4531
|
(simple) => writtenPaths(simple).flatMap((path) => {
|
|
4502
4532
|
const local = localPath(root, path);
|
|
4503
|
-
const exists = input.runtime.stat(toAbsolutePath(
|
|
4533
|
+
const exists = input.runtime.stat(toAbsolutePath(join12(root, local))) !== void 0;
|
|
4504
4534
|
if (!exists) return [];
|
|
4505
4535
|
if (isProtectedConfig(local)) return [{ kind: "config_changed", path: local }];
|
|
4506
4536
|
return isTestFile(local) ? [{ kind: "test_removed", path: local }] : [];
|
|
@@ -4521,38 +4551,6 @@ var testsGuard = (input) => {
|
|
|
4521
4551
|
|
|
4522
4552
|
// src/look/reads.ts
|
|
4523
4553
|
import { relative as relative4 } from "node:path";
|
|
4524
|
-
|
|
4525
|
-
// src/gate/hooks/common.ts
|
|
4526
|
-
import { dirname as dirname5, join as join12 } from "node:path";
|
|
4527
|
-
var DEFAULT_GATE_BUDGET_MS = 3e5;
|
|
4528
|
-
var sessionCwd = (input) => input.payload.cwd === void 0 ? input.cwd : toAbsolutePath(input.payload.cwd, input.cwd);
|
|
4529
|
-
var MAX_DEPTH2 = 64;
|
|
4530
|
-
var findRepoRoot = (runtime, start) => {
|
|
4531
|
-
let current2 = start;
|
|
4532
|
-
for (let depth = 0; depth < MAX_DEPTH2; depth += 1) {
|
|
4533
|
-
if (runtime.stat(toAbsolutePath(join12(current2, ".git"))) !== void 0) return current2;
|
|
4534
|
-
const parent = toAbsolutePath(dirname5(current2));
|
|
4535
|
-
if (parent === current2) return void 0;
|
|
4536
|
-
current2 = parent;
|
|
4537
|
-
}
|
|
4538
|
-
return void 0;
|
|
4539
|
-
};
|
|
4540
|
-
var projectPaths = (input) => {
|
|
4541
|
-
const root = findRepoRoot(input.runtime, sessionCwd(input));
|
|
4542
|
-
return root === void 0 ? void 0 : fmPaths(root);
|
|
4543
|
-
};
|
|
4544
|
-
var gateBudget = (input) => {
|
|
4545
|
-
const parsed = Number.parseInt(input.env["FINITEMONKEY_GATE_BUDGET_MS"] ?? "", 10);
|
|
4546
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_GATE_BUDGET_MS;
|
|
4547
|
-
};
|
|
4548
|
-
var allowWith = (output) => ({ kind: "allow", output });
|
|
4549
|
-
var contextOutput = (event, additionalContext, systemMessage) => ({
|
|
4550
|
-
hookSpecificOutput: { hookEventName: event, additionalContext },
|
|
4551
|
-
...systemMessage === void 0 ? {} : { systemMessage }
|
|
4552
|
-
});
|
|
4553
|
-
var human = (text8) => `finitemonkey: ${text8}`;
|
|
4554
|
-
|
|
4555
|
-
// src/look/reads.ts
|
|
4556
4554
|
var READS_FILE = "look-reads.json";
|
|
4557
4555
|
var KEEP = 500;
|
|
4558
4556
|
var readLookReads = (runtime, paths) => {
|
|
@@ -6585,7 +6583,19 @@ var en_default = {
|
|
|
6585
6583
|
"statusline.gate.red": "gate red",
|
|
6586
6584
|
"statusline.gate.incomplete": "gate incomplete",
|
|
6587
6585
|
"statusline.gate.did_not_run": "gate did not run",
|
|
6588
|
-
"statusline.context": "ctx {percent}%"
|
|
6586
|
+
"statusline.context": "ctx {percent}%",
|
|
6587
|
+
"command.gain.summary": "What the guards did: saves next to false alarms and gate failures",
|
|
6588
|
+
"gain.title": "Finite Monkey, last {days, plural, one {# day} other {# days}} (from .finitemonkey/log.jsonl and the evidence ledgers):",
|
|
6589
|
+
"gain.row.blocks": "{count, plural, one {# block} other {# blocks}} by the guards",
|
|
6590
|
+
"gain.row.saves": "{count, plural, one {# block} other {# blocks}} followed by a green gate within a day",
|
|
6591
|
+
"gain.row.false_positives": "{count, plural, one {# false alarm} other {# false alarms}} (releases typed by the person, and false positives recorded)",
|
|
6592
|
+
"gain.row.gate_runs": "{count, plural, one {# gate run} other {# gate runs}}",
|
|
6593
|
+
"gain.row.gate_failed": "{count, plural, one {# gate run} other {# gate runs}} failed",
|
|
6594
|
+
"gain.row.gate_unverified": "{count, plural, one {# gate run} other {# gate runs}} ended not verified",
|
|
6595
|
+
"gain.row.gate_time": "{minutes, plural, one {# minute} other {# minutes}} spent in the gate",
|
|
6596
|
+
"gain.up": "up {delta} on the window before",
|
|
6597
|
+
"gain.down": "down {delta} on the window before",
|
|
6598
|
+
"gain.footer": "Counts come from local files only; a save is a block followed by a passing gate, not proof that the block caused it."
|
|
6589
6599
|
};
|
|
6590
6600
|
|
|
6591
6601
|
// src/i18n/es.json
|
|
@@ -7243,7 +7253,19 @@ var es_default = {
|
|
|
7243
7253
|
"statusline.gate.red": "compuerta roja",
|
|
7244
7254
|
"statusline.gate.incomplete": "compuerta incompleta",
|
|
7245
7255
|
"statusline.gate.did_not_run": "la compuerta no se ejecutó",
|
|
7246
|
-
"statusline.context": "ctx {percent}%"
|
|
7256
|
+
"statusline.context": "ctx {percent}%",
|
|
7257
|
+
"command.gain.summary": "Lo que hicieron los bloqueos: aciertos junto a falsas alarmas y fallos de la compuerta",
|
|
7258
|
+
"gain.title": "Finite Monkey, {days, plural, one {último # día} other {últimos # días}} (de .finitemonkey/log.jsonl y los ledgers de evidencia):",
|
|
7259
|
+
"gain.row.blocks": "{count, plural, one {# bloqueo} other {# bloqueos}} de los guards",
|
|
7260
|
+
"gain.row.saves": "{count, plural, one {# bloqueo seguido} other {# bloqueos seguidos}} de una compuerta en verde en menos de un día",
|
|
7261
|
+
"gain.row.false_positives": "{count, plural, one {# falsa alarma} other {# falsas alarmas}} (liberaciones escritas por la persona y falsos positivos registrados)",
|
|
7262
|
+
"gain.row.gate_runs": "{count, plural, one {# ejecución de la compuerta} other {# ejecuciones de la compuerta}}",
|
|
7263
|
+
"gain.row.gate_failed": "{count, plural, one {# ejecución de la compuerta falló} other {# ejecuciones de la compuerta fallaron}}",
|
|
7264
|
+
"gain.row.gate_unverified": "{count, plural, one {# ejecución de la compuerta terminó} other {# ejecuciones de la compuerta terminaron}} sin verificar",
|
|
7265
|
+
"gain.row.gate_time": "{minutes, plural, one {# minuto} other {# minutos}} en la compuerta",
|
|
7266
|
+
"gain.up": "+{delta} respecto a la ventana anterior",
|
|
7267
|
+
"gain.down": "-{delta} respecto a la ventana anterior",
|
|
7268
|
+
"gain.footer": "Los conteos salen solo de archivos locales; un acierto es un bloqueo seguido de una compuerta en verde, no prueba de que el bloqueo lo causara."
|
|
7247
7269
|
};
|
|
7248
7270
|
|
|
7249
7271
|
// src/i18n/pt-BR.json
|
|
@@ -7901,7 +7923,19 @@ var pt_BR_default = {
|
|
|
7901
7923
|
"statusline.gate.red": "portão vermelho",
|
|
7902
7924
|
"statusline.gate.incomplete": "portão incompleto",
|
|
7903
7925
|
"statusline.gate.did_not_run": "portão não rodou",
|
|
7904
|
-
"statusline.context": "ctx {percent}%"
|
|
7926
|
+
"statusline.context": "ctx {percent}%",
|
|
7927
|
+
"command.gain.summary": "O que as travas fizeram: acertos ao lado dos alarmes falsos e das falhas do portão",
|
|
7928
|
+
"gain.title": "Finite Monkey, últimos {days, plural, one {# dia} other {# dias}} (de .finitemonkey/log.jsonl e dos ledgers de evidência):",
|
|
7929
|
+
"gain.row.blocks": "{count, plural, one {# bloqueio} other {# bloqueios}} das travas",
|
|
7930
|
+
"gain.row.saves": "{count, plural, one {# bloqueio seguido} other {# bloqueios seguidos}} de portão verde em até um dia",
|
|
7931
|
+
"gain.row.false_positives": "{count, plural, one {# alarme falso} other {# alarmes falsos}} (liberações digitadas pela pessoa e falsos positivos registrados)",
|
|
7932
|
+
"gain.row.gate_runs": "{count, plural, one {# execução do portão} other {# execuções do portão}}",
|
|
7933
|
+
"gain.row.gate_failed": "{count, plural, one {# execução do portão reprovou} other {# execuções do portão reprovaram}}",
|
|
7934
|
+
"gain.row.gate_unverified": "{count, plural, one {# execução do portão terminou} other {# execuções do portão terminaram}} sem verificar",
|
|
7935
|
+
"gain.row.gate_time": "{minutes, plural, one {# minuto} other {# minutos}} no portão",
|
|
7936
|
+
"gain.up": "+{delta} em relação à janela anterior",
|
|
7937
|
+
"gain.down": "-{delta} em relação à janela anterior",
|
|
7938
|
+
"gain.footer": "As contagens vêm só de arquivos locais; um acerto é um bloqueio seguido de portão verde, não prova de que o bloqueio causou isso."
|
|
7905
7939
|
};
|
|
7906
7940
|
|
|
7907
7941
|
// src/i18n/review.json
|
|
@@ -8560,7 +8594,19 @@ var review_default = {
|
|
|
8560
8594
|
"statusline.gate.red": "machine",
|
|
8561
8595
|
"statusline.gate.incomplete": "machine",
|
|
8562
8596
|
"statusline.gate.did_not_run": "machine",
|
|
8563
|
-
"statusline.context": "machine"
|
|
8597
|
+
"statusline.context": "machine",
|
|
8598
|
+
"command.gain.summary": "machine",
|
|
8599
|
+
"gain.title": "machine",
|
|
8600
|
+
"gain.row.blocks": "machine",
|
|
8601
|
+
"gain.row.saves": "machine",
|
|
8602
|
+
"gain.row.false_positives": "machine",
|
|
8603
|
+
"gain.row.gate_runs": "machine",
|
|
8604
|
+
"gain.row.gate_failed": "machine",
|
|
8605
|
+
"gain.row.gate_unverified": "machine",
|
|
8606
|
+
"gain.row.gate_time": "machine",
|
|
8607
|
+
"gain.up": "machine",
|
|
8608
|
+
"gain.down": "machine",
|
|
8609
|
+
"gain.footer": "machine"
|
|
8564
8610
|
},
|
|
8565
8611
|
es: {
|
|
8566
8612
|
"command.init.summary": "machine",
|
|
@@ -9216,7 +9262,19 @@ var review_default = {
|
|
|
9216
9262
|
"statusline.gate.red": "machine",
|
|
9217
9263
|
"statusline.gate.incomplete": "machine",
|
|
9218
9264
|
"statusline.gate.did_not_run": "machine",
|
|
9219
|
-
"statusline.context": "machine"
|
|
9265
|
+
"statusline.context": "machine",
|
|
9266
|
+
"command.gain.summary": "machine",
|
|
9267
|
+
"gain.title": "machine",
|
|
9268
|
+
"gain.row.blocks": "machine",
|
|
9269
|
+
"gain.row.saves": "machine",
|
|
9270
|
+
"gain.row.false_positives": "machine",
|
|
9271
|
+
"gain.row.gate_runs": "machine",
|
|
9272
|
+
"gain.row.gate_failed": "machine",
|
|
9273
|
+
"gain.row.gate_unverified": "machine",
|
|
9274
|
+
"gain.row.gate_time": "machine",
|
|
9275
|
+
"gain.up": "machine",
|
|
9276
|
+
"gain.down": "machine",
|
|
9277
|
+
"gain.footer": "machine"
|
|
9220
9278
|
}
|
|
9221
9279
|
};
|
|
9222
9280
|
|
|
@@ -14958,7 +15016,18 @@ var mergeOutputs = (left, right) => {
|
|
|
14958
15016
|
if (specific !== void 0) merged2["hookSpecificOutput"] = specific;
|
|
14959
15017
|
return merged2;
|
|
14960
15018
|
};
|
|
15019
|
+
var recordBlock = (input, decision) => {
|
|
15020
|
+
const paths = projectPaths(input);
|
|
15021
|
+
if (paths === void 0) return;
|
|
15022
|
+
logEvent(input.runtime, paths, "guard_block", {
|
|
15023
|
+
hook: input.event,
|
|
15024
|
+
tool: toolName2(input.payload),
|
|
15025
|
+
sessionId: input.payload.sessionId ?? null,
|
|
15026
|
+
claim: (decision.reason.split("\n")[0] ?? "").slice(0, 200)
|
|
15027
|
+
});
|
|
15028
|
+
};
|
|
14961
15029
|
var shaped = (input, decision) => {
|
|
15030
|
+
recordBlock(input, decision);
|
|
14962
15031
|
if (input.event !== "PreToolUse" || decision.output !== void 0) return decision;
|
|
14963
15032
|
const reason = capReason(decision.reason);
|
|
14964
15033
|
return {
|
|
@@ -15914,6 +15983,100 @@ var doctorCommand = {
|
|
|
15914
15983
|
run: (ctx, args2) => Promise.resolve(runDoctor(ctx, args2.values["fix"] === true, args2.values["report"] === true))
|
|
15915
15984
|
};
|
|
15916
15985
|
|
|
15986
|
+
// src/gain/gain.ts
|
|
15987
|
+
import { join as join39 } from "node:path";
|
|
15988
|
+
var GAIN_SCHEMA = "finitemonkey/gain@1";
|
|
15989
|
+
var DAY_MS2 = 864e5;
|
|
15990
|
+
var SAVE_WINDOW_MS = DAY_MS2;
|
|
15991
|
+
var readLog = (runtime, paths) => {
|
|
15992
|
+
const text8 = runtime.readText(paths.log);
|
|
15993
|
+
if (!text8.ok) return [];
|
|
15994
|
+
return text8.value.split("\n").flatMap((line2) => {
|
|
15995
|
+
const parsed = parseJsonObject(line2);
|
|
15996
|
+
if (!parsed.ok || !isRecord(parsed.value)) return [];
|
|
15997
|
+
const { at, event } = parsed.value;
|
|
15998
|
+
const time = typeof at === "string" ? Date.parse(at) : Number.NaN;
|
|
15999
|
+
return typeof event === "string" && Number.isFinite(time) ? [{ at: time, event }] : [];
|
|
16000
|
+
});
|
|
16001
|
+
};
|
|
16002
|
+
var readGates = (runtime, paths) => {
|
|
16003
|
+
const listed4 = runtime.listDir(toAbsolutePath(join39(paths.dir, "evidence")));
|
|
16004
|
+
const files = listed4.ok ? listed4.value.filter((name) => name.endsWith(".jsonl")) : [];
|
|
16005
|
+
return files.flatMap((name) => readLedger(runtime, paths.evidence(name.replace(/\.jsonl$/, "")))).filter((entry2) => entry2.check === GATE_CHECK);
|
|
16006
|
+
};
|
|
16007
|
+
var countsIn = (log, gates, from, to) => {
|
|
16008
|
+
const inWindow = (at) => at >= from && at < to;
|
|
16009
|
+
const lines3 = log.filter((line2) => inWindow(line2.at));
|
|
16010
|
+
const runs = gates.filter((entry2) => inWindow(Date.parse(entry2.at)));
|
|
16011
|
+
const passedAt = gates.filter((entry2) => entry2.status === "passed").map((entry2) => Date.parse(entry2.at));
|
|
16012
|
+
const blocks = lines3.filter((line2) => line2.event === "guard_block");
|
|
16013
|
+
return {
|
|
16014
|
+
blocks: blocks.length,
|
|
16015
|
+
saves: blocks.filter((block4) => passedAt.some((at) => at > block4.at && at - block4.at <= SAVE_WINDOW_MS)).length,
|
|
16016
|
+
releases: lines3.filter((line2) => line2.event === "person.allow").length,
|
|
16017
|
+
falsePositives: lines3.filter((line2) => line2.event === "person.allow").length + lines3.filter((line2) => line2.event === "guard_false_positive").length,
|
|
16018
|
+
gateRuns: runs.length,
|
|
16019
|
+
gateFailed: runs.filter((entry2) => entry2.status === "failed").length,
|
|
16020
|
+
gateUnverified: runs.filter((entry2) => entry2.status === "unverified").length,
|
|
16021
|
+
gateMs: runs.reduce((sum, entry2) => sum + (entry2.durationMs ?? 0), 0)
|
|
16022
|
+
};
|
|
16023
|
+
};
|
|
16024
|
+
var gainReport = (runtime, paths, days) => {
|
|
16025
|
+
const to = Date.parse(runtime.now());
|
|
16026
|
+
const from = to - days * DAY_MS2;
|
|
16027
|
+
const log = readLog(runtime, paths);
|
|
16028
|
+
const gates = readGates(runtime, paths);
|
|
16029
|
+
return {
|
|
16030
|
+
schema: GAIN_SCHEMA,
|
|
16031
|
+
days,
|
|
16032
|
+
from: new Date(from).toISOString(),
|
|
16033
|
+
to: new Date(to).toISOString(),
|
|
16034
|
+
current: countsIn(log, gates, from, to + 1),
|
|
16035
|
+
previous: countsIn(log, gates, from - days * DAY_MS2, from)
|
|
16036
|
+
};
|
|
16037
|
+
};
|
|
16038
|
+
|
|
16039
|
+
// src/commands/gain.ts
|
|
16040
|
+
var DEFAULT_DAYS = 30;
|
|
16041
|
+
var trend = (ctx, now, before) => now === before ? "" : ` (${ctx.t(now > before ? "gain.up" : "gain.down", { delta: Math.abs(now - before) })})`;
|
|
16042
|
+
var ROWS = [
|
|
16043
|
+
{ key: "gain.row.blocks", pick: (counts) => counts.blocks },
|
|
16044
|
+
{ key: "gain.row.saves", pick: (counts) => counts.saves },
|
|
16045
|
+
{ key: "gain.row.false_positives", pick: (counts) => counts.falsePositives },
|
|
16046
|
+
{ key: "gain.row.gate_runs", pick: (counts) => counts.gateRuns },
|
|
16047
|
+
{ key: "gain.row.gate_failed", pick: (counts) => counts.gateFailed },
|
|
16048
|
+
{ key: "gain.row.gate_unverified", pick: (counts) => counts.gateUnverified }
|
|
16049
|
+
];
|
|
16050
|
+
var linesOf = (ctx, report2) => [
|
|
16051
|
+
ctx.t("gain.title", { days: report2.days }),
|
|
16052
|
+
...ROWS.map(
|
|
16053
|
+
({ key, pick: pick4 }) => ` ${ctx.t(key, { count: pick4(report2.current) })}${trend(ctx, pick4(report2.current), pick4(report2.previous))}`
|
|
16054
|
+
),
|
|
16055
|
+
` ${ctx.t("gain.row.gate_time", { minutes: Math.round(report2.current.gateMs / 6e4) })}`,
|
|
16056
|
+
ctx.t("gain.footer")
|
|
16057
|
+
];
|
|
16058
|
+
var runGain = (ctx, rawDays) => {
|
|
16059
|
+
const days = rawDays === void 0 ? DEFAULT_DAYS : Number.parseInt(rawDays, 10);
|
|
16060
|
+
if (!Number.isInteger(days) || days < 1 || days > 365) {
|
|
16061
|
+
return failureFrom(
|
|
16062
|
+
ctx.t,
|
|
16063
|
+
usageProblem("invalid_arguments", "error.invalid_arguments", "finitemonkey gain --days 30", {
|
|
16064
|
+
detail: "--days 1-365"
|
|
16065
|
+
})
|
|
16066
|
+
);
|
|
16067
|
+
}
|
|
16068
|
+
const report2 = gainReport(ctx.runtime, fmPaths(findRepoRoot(ctx.runtime, ctx.cwd) ?? ctx.cwd), days);
|
|
16069
|
+
return success(report2, linesOf(ctx, report2));
|
|
16070
|
+
};
|
|
16071
|
+
var gainCommand = {
|
|
16072
|
+
path: ["gain"],
|
|
16073
|
+
summaryKey: "command.gain.summary",
|
|
16074
|
+
usage: "finitemonkey gain [--days 1-365]",
|
|
16075
|
+
status: "ready",
|
|
16076
|
+
options: { days: { type: "string" } },
|
|
16077
|
+
run: (ctx, args2) => Promise.resolve(runGain(ctx, stringOption(args2.values, "days")))
|
|
16078
|
+
};
|
|
16079
|
+
|
|
15917
16080
|
// src/gate/baseline-run.ts
|
|
15918
16081
|
var runBaseline = (options) => {
|
|
15919
16082
|
const ready = prepareGate({ ...options, noSkip: true });
|
|
@@ -16355,11 +16518,11 @@ var mergeVerifyCommand = {
|
|
|
16355
16518
|
var REFLOG_LIMIT = 300;
|
|
16356
16519
|
var LIST_LIMIT = 50;
|
|
16357
16520
|
var SHA = /^[0-9a-f]{40,64}$/;
|
|
16358
|
-
var
|
|
16521
|
+
var linesOf2 = (text8) => text8.split("\n").filter((line2) => line2.trim() !== "");
|
|
16359
16522
|
var reflogLines = (runtime, repo) => {
|
|
16360
16523
|
const result = runtime.git(["reflog", "show", "--all", `-n${String(REFLOG_LIMIT)}`, "--format=%H %gD"], repo.root);
|
|
16361
16524
|
if (!result.ok) return [];
|
|
16362
|
-
return
|
|
16525
|
+
return linesOf2(result.stdout).flatMap((line2) => {
|
|
16363
16526
|
const [sha, ref] = line2.split(" ");
|
|
16364
16527
|
return sha === void 0 || ref === void 0 || !SHA.test(sha) || ref.startsWith("refs/finitemonkey/") ? [] : [{ sha, ref }];
|
|
16365
16528
|
});
|
|
@@ -16367,11 +16530,11 @@ var reflogLines = (runtime, repo) => {
|
|
|
16367
16530
|
var unreachable = (runtime, repo, shas) => {
|
|
16368
16531
|
if (shas.length === 0) return /* @__PURE__ */ new Set();
|
|
16369
16532
|
const result = runtime.git(["rev-list", ...shas, "--not", "--branches", "--tags", "--remotes"], repo.root);
|
|
16370
|
-
return new Set(result.ok ?
|
|
16533
|
+
return new Set(result.ok ? linesOf2(result.stdout) : []);
|
|
16371
16534
|
};
|
|
16372
16535
|
var fsckDangling = (runtime, repo) => {
|
|
16373
16536
|
const result = runtime.git(["fsck", "--no-reflogs", "--no-progress", "--dangling"], repo.root);
|
|
16374
|
-
const lines3 =
|
|
16537
|
+
const lines3 = linesOf2(result.stdout);
|
|
16375
16538
|
const pick4 = (kind) => lines3.flatMap((line2) => {
|
|
16376
16539
|
const [label2, type, sha] = line2.split(" ");
|
|
16377
16540
|
return label2 === "dangling" && type === kind && sha !== void 0 ? [sha] : [];
|
|
@@ -16382,7 +16545,7 @@ var describe = (runtime, repo, shas) => {
|
|
|
16382
16545
|
if (shas.length === 0) return /* @__PURE__ */ new Map();
|
|
16383
16546
|
const result = runtime.git(["log", "--no-walk=unsorted", "--format=%H%x09%cr%x09%s", ...shas], repo.root);
|
|
16384
16547
|
return new Map(
|
|
16385
|
-
|
|
16548
|
+
linesOf2(result.stdout).flatMap((line2) => {
|
|
16386
16549
|
const [sha, when, ...subject2] = line2.split(" ");
|
|
16387
16550
|
return sha === void 0 ? [] : [[sha, [when ?? "", subject2.join(" ")]]];
|
|
16388
16551
|
})
|
|
@@ -16476,7 +16639,7 @@ var rescueCommand = {
|
|
|
16476
16639
|
};
|
|
16477
16640
|
|
|
16478
16641
|
// src/git/undo.ts
|
|
16479
|
-
import { basename as basename3, dirname as dirname8, join as
|
|
16642
|
+
import { basename as basename3, dirname as dirname8, join as join40 } from "node:path";
|
|
16480
16643
|
var INDEX_SUBJECT = "finitemonkey index:";
|
|
16481
16644
|
var gitLine = (runtime, repo, args2) => {
|
|
16482
16645
|
const result = runtime.git(args2, repo.root);
|
|
@@ -16519,7 +16682,7 @@ var findSnapshot = (views, wanted) => {
|
|
|
16519
16682
|
};
|
|
16520
16683
|
var stampOf2 = (iso) => iso.replace(/[:.]/g, "-");
|
|
16521
16684
|
var worktreePath = (runtime, repo) => {
|
|
16522
|
-
const base =
|
|
16685
|
+
const base = join40(dirname8(repo.root), `${basename3(repo.root)}-undo-${stampOf2(runtime.now())}`);
|
|
16523
16686
|
return toAbsolutePath(runtime.stat(toAbsolutePath(base)) === void 0 ? base : `${base}-2`);
|
|
16524
16687
|
};
|
|
16525
16688
|
var intoWorktree = (runtime, repo, view2) => {
|
|
@@ -16754,7 +16917,7 @@ var hookCommand = {
|
|
|
16754
16917
|
};
|
|
16755
16918
|
|
|
16756
16919
|
// src/commands/i18n-check.ts
|
|
16757
|
-
import { join as
|
|
16920
|
+
import { join as join41 } from "node:path";
|
|
16758
16921
|
|
|
16759
16922
|
// src/i18n/message.ts
|
|
16760
16923
|
var ARG = /^\s*([a-zA-Z][a-zA-Z0-9_]*)\s*(?:,\s*(plural|select)\s*,)?/;
|
|
@@ -16980,7 +17143,7 @@ var unreadable = (dir, file, detail) => ({
|
|
|
16980
17143
|
exitCode: ExitCode.NotConfigured
|
|
16981
17144
|
});
|
|
16982
17145
|
var readCatalog = (ctx, dir, lang) => {
|
|
16983
|
-
const file = toAbsolutePath(
|
|
17146
|
+
const file = toAbsolutePath(join41(dir, `${lang}.json`));
|
|
16984
17147
|
const text8 = ctx.runtime.readText(file);
|
|
16985
17148
|
if (!text8.ok) return reject(unreadable(dir, file, text8.error));
|
|
16986
17149
|
const parsed = parseJsonObject(text8.value);
|
|
@@ -17003,7 +17166,7 @@ var describeProblem = (ctx, problem4) => {
|
|
|
17003
17166
|
return ` ${problem4.lang} ${problem4.key} ${kind}`;
|
|
17004
17167
|
};
|
|
17005
17168
|
var loadReview = (ctx, dir) => {
|
|
17006
|
-
const text8 = ctx.runtime.readText(toAbsolutePath(
|
|
17169
|
+
const text8 = ctx.runtime.readText(toAbsolutePath(join41(dir, "review.json")));
|
|
17007
17170
|
const parsed = text8.ok ? parseJsonObject(text8.value) : void 0;
|
|
17008
17171
|
if (parsed?.ok !== true) return void 0;
|
|
17009
17172
|
const statuses = (lang) => {
|
|
@@ -17044,7 +17207,7 @@ var i18nCheckCommand = {
|
|
|
17044
17207
|
};
|
|
17045
17208
|
|
|
17046
17209
|
// src/history/look-back.ts
|
|
17047
|
-
import { join as
|
|
17210
|
+
import { join as join42 } from "node:path";
|
|
17048
17211
|
|
|
17049
17212
|
// src/history/patterns.ts
|
|
17050
17213
|
import { posix as posix5 } from "node:path";
|
|
@@ -17180,7 +17343,7 @@ var parseTranscript = (jsonl) => {
|
|
|
17180
17343
|
// src/history/look-back.ts
|
|
17181
17344
|
var SAMPLES = 3;
|
|
17182
17345
|
var projectLogKey = (root) => root.replace(/[^A-Za-z0-9-]/g, "-");
|
|
17183
|
-
var projectLogDir = (runtime, root, env) =>
|
|
17346
|
+
var projectLogDir = (runtime, root, env) => join42(env["CLAUDE_CONFIG_DIR"] ?? join42(runtime.homeDir(), ".claude"), "projects", projectLogKey(root));
|
|
17184
17347
|
var lookBack = (runtime, root, env) => {
|
|
17185
17348
|
const dir = projectLogDir(runtime, root, env);
|
|
17186
17349
|
const listed4 = runtime.listDir(toAbsolutePath(dir));
|
|
@@ -17201,7 +17364,7 @@ var lookBack = (runtime, root, env) => {
|
|
|
17201
17364
|
};
|
|
17202
17365
|
let turns = 0;
|
|
17203
17366
|
for (const name of files) {
|
|
17204
|
-
const text8 = runtime.readText(toAbsolutePath(
|
|
17367
|
+
const text8 = runtime.readText(toAbsolutePath(join42(dir, name)));
|
|
17205
17368
|
if (!text8.ok) continue;
|
|
17206
17369
|
for (const turn of parseTranscript(text8.value)) {
|
|
17207
17370
|
turns += 1;
|
|
@@ -17453,7 +17616,7 @@ var applyAllows = (findings, allows, decided) => {
|
|
|
17453
17616
|
|
|
17454
17617
|
// src/look/node-capture.ts
|
|
17455
17618
|
import { mkdirSync as mkdirSync3 } from "node:fs";
|
|
17456
|
-
import { join as
|
|
17619
|
+
import { join as join43 } from "node:path";
|
|
17457
17620
|
|
|
17458
17621
|
// src/look/measure.ts
|
|
17459
17622
|
var measurePage = (doc) => {
|
|
@@ -17529,7 +17692,7 @@ var captureOne = async (browser, request, route, viewport) => {
|
|
|
17529
17692
|
const context = await browser.newContext({ viewport: { width: viewport, height: HEIGHT }, reducedMotion: "reduce" });
|
|
17530
17693
|
const page = await context.newPage();
|
|
17531
17694
|
const events = watch(page);
|
|
17532
|
-
const path =
|
|
17695
|
+
const path = join43(request.dir, `${slugOf(route)}-${String(viewport)}.png`);
|
|
17533
17696
|
const loadError = await loadAndShoot(page, `${request.baseUrl}${route}`, path, request.timeoutMs);
|
|
17534
17697
|
const measure = loadError === void 0 ? parseMeasure(await page.evaluate(MEASURE_SCRIPT)) : void 0;
|
|
17535
17698
|
await context.close();
|
|
@@ -17546,7 +17709,7 @@ var captureAll = async (browser, request) => {
|
|
|
17546
17709
|
|
|
17547
17710
|
// src/look/node-playwright.ts
|
|
17548
17711
|
import { createRequire } from "node:module";
|
|
17549
|
-
import { join as
|
|
17712
|
+
import { join as join44 } from "node:path";
|
|
17550
17713
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
17551
17714
|
var NAMES = ["playwright", "playwright-core"];
|
|
17552
17715
|
var isPlaywright = (value) => isRecord(value) && isRecord(value["chromium"]) && typeof value["chromium"]["launch"] === "function";
|
|
@@ -17561,7 +17724,7 @@ var tryLoad = async (base, name) => {
|
|
|
17561
17724
|
}
|
|
17562
17725
|
};
|
|
17563
17726
|
var loadPlaywright = async (root) => {
|
|
17564
|
-
for (const base of [
|
|
17727
|
+
for (const base of [join44(root, "package.json"), fileURLToPath3(import.meta.url)]) {
|
|
17565
17728
|
for (const name of NAMES) {
|
|
17566
17729
|
const found = await tryLoad(base, name);
|
|
17567
17730
|
if (found !== void 0) return found;
|
|
@@ -17582,7 +17745,7 @@ var launchBrowser = async (playwright) => {
|
|
|
17582
17745
|
|
|
17583
17746
|
// src/look/node-sheet.ts
|
|
17584
17747
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
17585
|
-
import { join as
|
|
17748
|
+
import { join as join45 } from "node:path";
|
|
17586
17749
|
var escapeHtml = (text8) => text8.replace(/[&<>"]/g, (char) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[char] ?? char);
|
|
17587
17750
|
var card = (result) => {
|
|
17588
17751
|
const image = (() => {
|
|
@@ -17605,7 +17768,7 @@ var contactSheet = async (browser, results, dir) => {
|
|
|
17605
17768
|
if (shown3.length === 0) return void 0;
|
|
17606
17769
|
const page = await browser.newPage({ viewport: { width: 1600, height: 900 } });
|
|
17607
17770
|
await page.setContent(`<!doctype html><style>${STYLE}</style>${shown3.map(card).join("")}`);
|
|
17608
|
-
const path =
|
|
17771
|
+
const path = join45(dir, "contact-sheet.png");
|
|
17609
17772
|
await page.screenshot({ path, fullPage: true });
|
|
17610
17773
|
await page.close();
|
|
17611
17774
|
return path;
|
|
@@ -17945,12 +18108,12 @@ var proseCheckCommand = {
|
|
|
17945
18108
|
};
|
|
17946
18109
|
|
|
17947
18110
|
// src/review/packet.ts
|
|
17948
|
-
import { join as
|
|
18111
|
+
import { join as join46 } from "node:path";
|
|
17949
18112
|
var REVIEW_STATE_FILE = ".finitemonkey/review/state.json";
|
|
17950
18113
|
var FULL_ROUNDS = 3;
|
|
17951
18114
|
var REVIEW_FILE_MAX_LINES = 200;
|
|
17952
18115
|
var LEDGER_MAX_LINES = 100;
|
|
17953
|
-
var statePath = (root) => toAbsolutePath(
|
|
18116
|
+
var statePath = (root) => toAbsolutePath(join46(root, REVIEW_STATE_FILE));
|
|
17954
18117
|
var readReviewState = (runtime, root) => {
|
|
17955
18118
|
const raw = runtime.readText(statePath(root));
|
|
17956
18119
|
const parsed = raw.ok ? parseJsonObject(raw.value) : void 0;
|
|
@@ -17972,7 +18135,7 @@ var taskSections = (runtime, root) => {
|
|
|
17972
18135
|
const active = readState(runtime, paths).active;
|
|
17973
18136
|
if (active === void 0)
|
|
17974
18137
|
return [section("Contract", void 0, "(no active task)"), section("Evidence ledger", void 0, "(none)")];
|
|
17975
|
-
const contract = runtime.readText(toAbsolutePath(
|
|
18138
|
+
const contract = runtime.readText(toAbsolutePath(join46(root, taskRelative(active))));
|
|
17976
18139
|
const ledger = runtime.readText(paths.evidence(active));
|
|
17977
18140
|
return [
|
|
17978
18141
|
section("Contract", contract.ok ? contract.value : void 0, "(contract file missing)"),
|
|
@@ -18002,7 +18165,7 @@ var buildPacket = (runtime, root, base, head) => {
|
|
|
18002
18165
|
...taskSections(runtime, root),
|
|
18003
18166
|
...owners === void 0 ? [] : [section("REVIEW.md (base branch)", owners, "")]
|
|
18004
18167
|
].join("\n");
|
|
18005
|
-
runtime.mkdir(toAbsolutePath(
|
|
18168
|
+
runtime.mkdir(toAbsolutePath(join46(root, ".finitemonkey", "review")));
|
|
18006
18169
|
runtime.writeAtomic(statePath(root), `${JSON.stringify(state, null, 2)}
|
|
18007
18170
|
`);
|
|
18008
18171
|
return { round: state.rounds, base, head, range, text: text8 };
|
|
@@ -18058,14 +18221,14 @@ var gitUnavailable = (detail) => ({
|
|
|
18058
18221
|
});
|
|
18059
18222
|
|
|
18060
18223
|
// src/commands/secrets-accept.ts
|
|
18061
|
-
var
|
|
18224
|
+
var DAY_MS3 = 864e5;
|
|
18062
18225
|
var daysOf = (values) => {
|
|
18063
18226
|
const raw = stringOption(values, "days");
|
|
18064
18227
|
if (raw === void 0) return EXCEPTION_MAX_DAYS;
|
|
18065
18228
|
const days = Number.parseInt(raw, 10);
|
|
18066
18229
|
return Number.isInteger(days) && days > 0 && days <= EXCEPTION_MAX_DAYS ? days : void 0;
|
|
18067
18230
|
};
|
|
18068
|
-
var plusDays = (iso, days) => new Date(Date.parse(iso) + days *
|
|
18231
|
+
var plusDays = (iso, days) => new Date(Date.parse(iso) + days * DAY_MS3).toISOString();
|
|
18069
18232
|
var failure3 = (ctx, problem4) => failureFrom(ctx.t, problem4);
|
|
18070
18233
|
var inputProblem = (ctx, id, reason, days) => {
|
|
18071
18234
|
const fix = "finitemonkey secrets accept <id> --reason <why> [--days 1-90] [--false-positive]";
|
|
@@ -18157,7 +18320,7 @@ var secretsAcceptCommand = {
|
|
|
18157
18320
|
};
|
|
18158
18321
|
|
|
18159
18322
|
// src/secrets/runbooks.ts
|
|
18160
|
-
import { dirname as dirname9, join as
|
|
18323
|
+
import { dirname as dirname9, join as join47 } from "node:path";
|
|
18161
18324
|
var PROVIDER_OF_RULE = {
|
|
18162
18325
|
"aws-access-token": "aws",
|
|
18163
18326
|
"aws-secret-access-key": "aws",
|
|
@@ -18179,7 +18342,7 @@ var RUNBOOKS_URL = "https://github.com/gabrielvieiraf/finitemonkey/blob/main/run
|
|
|
18179
18342
|
var runbookProvider = (rule) => PROVIDER_OF_RULE[rule] ?? "generic";
|
|
18180
18343
|
var runbookLink = (runtime, rule) => {
|
|
18181
18344
|
const file = `${runbookProvider(rule)}.md`;
|
|
18182
|
-
const local = toAbsolutePath(
|
|
18345
|
+
const local = toAbsolutePath(join47(dirname9(dirname9(runtime.selfPath)), "runbooks", file));
|
|
18183
18346
|
return runtime.stat(local)?.kind === "file" ? local : `${RUNBOOKS_URL}/${file}`;
|
|
18184
18347
|
};
|
|
18185
18348
|
|
|
@@ -18739,7 +18902,7 @@ var statuslineCommand = {
|
|
|
18739
18902
|
};
|
|
18740
18903
|
|
|
18741
18904
|
// src/task/create.ts
|
|
18742
|
-
import { join as
|
|
18905
|
+
import { join as join48 } from "node:path";
|
|
18743
18906
|
|
|
18744
18907
|
// src/core/yaml/write.ts
|
|
18745
18908
|
var PLAIN_SAFE = /^[A-Za-z_./][A-Za-z0-9_./ ()-]*$/;
|
|
@@ -18828,10 +18991,10 @@ var explicitChecks = (draft) => [
|
|
|
18828
18991
|
var draftChecks = (runtime, paths, draft) => {
|
|
18829
18992
|
const disk = {
|
|
18830
18993
|
read: (relative11) => {
|
|
18831
|
-
const text8 = runtime.readText(toAbsolutePath(
|
|
18994
|
+
const text8 = runtime.readText(toAbsolutePath(join48(paths.root, relative11)));
|
|
18832
18995
|
return text8.ok ? text8.value : void 0;
|
|
18833
18996
|
},
|
|
18834
|
-
exists: (relative11) => runtime.stat(toAbsolutePath(
|
|
18997
|
+
exists: (relative11) => runtime.stat(toAbsolutePath(join48(paths.root, relative11))) !== void 0
|
|
18835
18998
|
};
|
|
18836
18999
|
const detected = detectChecks(disk);
|
|
18837
19000
|
const base = draft.runs.length > 0 || draft.tests !== void 0 ? explicitChecks(draft) : detected;
|
|
@@ -18846,7 +19009,7 @@ var draftChecks = (runtime, paths, draft) => {
|
|
|
18846
19009
|
{ id: "merge", kind: "merge" },
|
|
18847
19010
|
{ id: "callers", kind: "callers" },
|
|
18848
19011
|
...draft.paths.length > 0 || draft.budget !== void 0 ? [{ id: "diff", kind: "diff" }] : [],
|
|
18849
|
-
...runtime.stat(toAbsolutePath(
|
|
19012
|
+
...runtime.stat(toAbsolutePath(join48(paths.root, LOOK_CONFIG))) === void 0 ? [] : [{ id: "look", kind: "look" }],
|
|
18850
19013
|
...draft.git.length > 0 ? [{ id: "git", kind: "git", require: draft.git }] : [],
|
|
18851
19014
|
{ id: "pr", kind: "pr", requireOpen: draft.git.includes("pr_open") },
|
|
18852
19015
|
...draft.manual.map((description, index) => ({
|
|
@@ -19126,7 +19289,7 @@ var dataOf3 = (root, plan, applied) => ({
|
|
|
19126
19289
|
kept: plan.items.filter((item2) => item2.action.startsWith("keep")).map(({ path }) => path),
|
|
19127
19290
|
outside: OUTSIDE.map(({ command }) => command)
|
|
19128
19291
|
});
|
|
19129
|
-
var
|
|
19292
|
+
var linesOf3 = (ctx, data) => [
|
|
19130
19293
|
ctx.t("install.uninstall.title", { root: data.root }),
|
|
19131
19294
|
...data.actions.map(({ path, action }) => ` ${action.padEnd(15)}${path}`),
|
|
19132
19295
|
...data.purge ? [ctx.t("install.uninstall.purge")] : [ctx.t("install.uninstall.keeps_state")],
|
|
@@ -19147,16 +19310,16 @@ var runUninstall = (ctx, args2) => {
|
|
|
19147
19310
|
const options = { purge: args2.values["purge"] === true, keepGenerated: args2.values["keep-generated"] === true };
|
|
19148
19311
|
const plan = planRemoval(project, lock.value, options);
|
|
19149
19312
|
const preview = dataOf3(project.root, plan, false);
|
|
19150
|
-
if (ctx.dryRun) return success(preview, [...
|
|
19313
|
+
if (ctx.dryRun) return success(preview, [...linesOf3(ctx, preview), ctx.t("install.dry_run")]);
|
|
19151
19314
|
if (!ctx.yes) {
|
|
19152
19315
|
const next2 = fixFor(args2);
|
|
19153
19316
|
const problem4 = confirmProblem("uninstall", ctx.t("install.uninstall.question"), next2);
|
|
19154
|
-
return { ...failureFrom(ctx.t, { ...problem4, data: preview },
|
|
19317
|
+
return { ...failureFrom(ctx.t, { ...problem4, data: preview }, linesOf3(ctx, preview)), next: next2 };
|
|
19155
19318
|
}
|
|
19156
19319
|
const removed = applyRemoval(project, plan);
|
|
19157
19320
|
if (!removed.ok) return failureFrom(ctx.t, applyProblem(removed.error));
|
|
19158
19321
|
const data = dataOf3(project.root, plan, true);
|
|
19159
|
-
return success(data, [...
|
|
19322
|
+
return success(data, [...linesOf3(ctx, data), ctx.t("install.uninstall.done")]);
|
|
19160
19323
|
};
|
|
19161
19324
|
var uninstallCommand = {
|
|
19162
19325
|
path: ["uninstall"],
|
|
@@ -19236,6 +19399,7 @@ var REGISTRY = [
|
|
|
19236
19399
|
policyFalsePositiveCommand,
|
|
19237
19400
|
offCommand,
|
|
19238
19401
|
onCommand,
|
|
19402
|
+
gainCommand,
|
|
19239
19403
|
secretsIncidentCommand,
|
|
19240
19404
|
secretsLiveCommand,
|
|
19241
19405
|
proseCheckCommand,
|
|
@@ -19255,7 +19419,7 @@ var REGISTRY = [
|
|
|
19255
19419
|
];
|
|
19256
19420
|
|
|
19257
19421
|
// src/i18n/lang-settings.ts
|
|
19258
|
-
import { join as
|
|
19422
|
+
import { join as join49 } from "node:path";
|
|
19259
19423
|
var read3 = (runtime, path) => {
|
|
19260
19424
|
const text8 = runtime.readText(toAbsolutePath(path));
|
|
19261
19425
|
return text8.ok ? text8.value : void 0;
|
|
@@ -19270,14 +19434,14 @@ var languageIn = (text8) => {
|
|
|
19270
19434
|
const value = parsed.ok && isRecord(parsed.value) ? parsed.value["language"] : void 0;
|
|
19271
19435
|
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
19272
19436
|
};
|
|
19273
|
-
var claudeDir = (runtime, env) => env["CLAUDE_CONFIG_DIR"] ??
|
|
19437
|
+
var claudeDir = (runtime, env) => env["CLAUDE_CONFIG_DIR"] ?? join49(runtime.homeDir(), ".claude");
|
|
19274
19438
|
var langSettings = (runtime, cwd, env, platform) => {
|
|
19275
19439
|
const root = findGitRoot(cwd, runtime) ?? cwd;
|
|
19276
|
-
const config = parseConfigYaml(read3(runtime,
|
|
19440
|
+
const config = parseConfigYaml(read3(runtime, join49(root, CONFIG_FILE)) ?? "");
|
|
19277
19441
|
const claudeFiles = [
|
|
19278
|
-
|
|
19279
|
-
|
|
19280
|
-
|
|
19442
|
+
join49(root, ".claude", "settings.local.json"),
|
|
19443
|
+
join49(root, ".claude", "settings.json"),
|
|
19444
|
+
join49(claudeDir(runtime, env), "settings.json")
|
|
19281
19445
|
];
|
|
19282
19446
|
return {
|
|
19283
19447
|
config: configText(config, "lang"),
|
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.25",
|
|
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",
|