safeword 0.59.0 → 0.61.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/dist/{architecture-32CAOL4P.js → architecture-WFRA4ITA.js} +6 -6
- package/dist/{check-QZPVKHZF.js → check-KHWH36AK.js} +11 -11
- package/dist/chunk-B6KVKEZA.js +65 -0
- package/dist/chunk-B6KVKEZA.js.map +1 -0
- package/dist/{chunk-WUJKWUMR.js → chunk-C4LNUGHF.js} +7 -7
- package/dist/{chunk-PHR2K2Y3.js → chunk-CXGL65PS.js} +6 -1
- package/dist/chunk-CXGL65PS.js.map +1 -0
- package/dist/{chunk-JSTWB4AX.js → chunk-GEC7AHRF.js} +56 -6
- package/dist/chunk-GEC7AHRF.js.map +1 -0
- package/dist/{chunk-XDT3W5MH.js → chunk-ILTC7KBQ.js} +66 -27
- package/dist/chunk-ILTC7KBQ.js.map +1 -0
- package/dist/{chunk-HKX3Z32Y.js → chunk-LBFUREUF.js} +6 -6
- package/dist/{chunk-NWCBLTIE.js → chunk-O2ULQPXW.js} +2 -1
- package/dist/{chunk-GS3TBFXU.js → chunk-OY7F4YNQ.js} +2 -2
- package/dist/chunk-U2GSR4OM.js +144 -0
- package/dist/chunk-U2GSR4OM.js.map +1 -0
- package/dist/{chunk-N7PFYE7Z.js → chunk-XTGWCHWI.js} +8 -8
- package/dist/{chunk-MT4WBU2P.js → chunk-YYBLVWZZ.js} +45 -8
- package/dist/chunk-YYBLVWZZ.js.map +1 -0
- package/dist/cli.js +38 -18
- package/dist/cli.js.map +1 -1
- package/dist/{codify-S4DJVZ4R.js → codify-BXBG5XPK.js} +3 -3
- package/dist/{diff-TFNDFYCX.js → diff-K3SYBGB7.js} +10 -10
- package/dist/github-rest-36XJI5HD.js +78 -0
- package/dist/github-rest-36XJI5HD.js.map +1 -0
- package/dist/index.js +2 -2
- package/dist/output-7MPCY3U7.js +23 -0
- package/dist/presets/typescript/index.d.ts +49 -1
- package/dist/presets/typescript/index.js +4 -2
- package/dist/{reset-W5GEXNGF.js → reset-4IGPE6TD.js} +8 -8
- package/dist/retro-FCGFUDEA.js +603 -0
- package/dist/retro-FCGFUDEA.js.map +1 -0
- package/dist/retro-extract-XXZI3DFW.js +30 -0
- package/dist/retro-extract-XXZI3DFW.js.map +1 -0
- package/dist/{self-report-CQLAESVT.js → self-report-AQZKGDAS.js} +4 -3
- package/dist/{self-report-CQLAESVT.js.map → self-report-AQZKGDAS.js.map} +1 -1
- package/dist/self-report-XLBFP2NT.js +36 -0
- package/dist/self-report-XLBFP2NT.js.map +1 -0
- package/dist/{setup-4KLD7PGB.js → setup-SECXSZ5K.js} +13 -13
- package/dist/{sync-config-IUOYVDVQ.js → sync-config-VJFZJATR.js} +3 -3
- package/dist/sync-config-VJFZJATR.js.map +1 -0
- package/dist/{sync-learnings-XTHHJDPR.js → sync-learnings-BVNUNLPD.js} +3 -3
- package/dist/{sync-tickets-IQ4AACIH.js → sync-tickets-4TOJBDHW.js} +3 -3
- package/dist/{sync-tracker-2TGUIMAB.js → sync-tracker-U4TKFX4R.js} +7 -7
- package/dist/{test-plan-6X56C5JZ.js → test-plan-VMQRP2WF.js} +3 -3
- package/dist/{ticket-new-UIOKBXK2.js → ticket-new-PIHZGZGG.js} +5 -5
- package/dist/{upgrade-ZWRVFHXV.js → upgrade-S5DTXDLC.js} +19 -19
- package/package.json +3 -1
- package/templates/codex/config.toml +8 -0
- package/templates/commands/audit.md +4 -3
- package/templates/guides/retro.md +81 -0
- package/templates/guides/self-report-filing.md +26 -0
- package/templates/hooks/codex/stop.ts +102 -0
- package/templates/hooks/cursor/stop.ts +54 -10
- package/templates/hooks/lib/jsonl-spool.ts +107 -0
- package/templates/hooks/lib/quality-state.ts +10 -0
- package/templates/hooks/lib/retro-draft-spool.ts +160 -0
- package/templates/hooks/lib/retro-extract.ts +309 -0
- package/templates/hooks/lib/retro-nudge.ts +86 -0
- package/templates/hooks/lib/retro-trigger.ts +477 -0
- package/templates/hooks/lib/self-report.ts +12 -35
- package/templates/hooks/prompt-retro-nudge.ts +48 -0
- package/templates/hooks/stop-retro.ts +77 -0
- package/templates/hooks/write-review-stamp.ts +37 -17
- package/templates/skills/audit/SKILL.md +8 -7
- package/dist/chunk-JSTWB4AX.js.map +0 -1
- package/dist/chunk-MT4WBU2P.js.map +0 -1
- package/dist/chunk-PHR2K2Y3.js.map +0 -1
- package/dist/chunk-XDT3W5MH.js.map +0 -1
- /package/dist/{architecture-32CAOL4P.js.map → architecture-WFRA4ITA.js.map} +0 -0
- /package/dist/{check-QZPVKHZF.js.map → check-KHWH36AK.js.map} +0 -0
- /package/dist/{chunk-WUJKWUMR.js.map → chunk-C4LNUGHF.js.map} +0 -0
- /package/dist/{chunk-HKX3Z32Y.js.map → chunk-LBFUREUF.js.map} +0 -0
- /package/dist/{chunk-NWCBLTIE.js.map → chunk-O2ULQPXW.js.map} +0 -0
- /package/dist/{chunk-GS3TBFXU.js.map → chunk-OY7F4YNQ.js.map} +0 -0
- /package/dist/{chunk-N7PFYE7Z.js.map → chunk-XTGWCHWI.js.map} +0 -0
- /package/dist/{codify-S4DJVZ4R.js.map → codify-BXBG5XPK.js.map} +0 -0
- /package/dist/{diff-TFNDFYCX.js.map → diff-K3SYBGB7.js.map} +0 -0
- /package/dist/{sync-config-IUOYVDVQ.js.map → output-7MPCY3U7.js.map} +0 -0
- /package/dist/{reset-W5GEXNGF.js.map → reset-4IGPE6TD.js.map} +0 -0
- /package/dist/{setup-4KLD7PGB.js.map → setup-SECXSZ5K.js.map} +0 -0
- /package/dist/{sync-learnings-XTHHJDPR.js.map → sync-learnings-BVNUNLPD.js.map} +0 -0
- /package/dist/{sync-tickets-IQ4AACIH.js.map → sync-tickets-4TOJBDHW.js.map} +0 -0
- /package/dist/{sync-tracker-2TGUIMAB.js.map → sync-tracker-U4TKFX4R.js.map} +0 -0
- /package/dist/{test-plan-6X56C5JZ.js.map → test-plan-VMQRP2WF.js.map} +0 -0
- /package/dist/{ticket-new-UIOKBXK2.js.map → ticket-new-PIHZGZGG.js.map} +0 -0
- /package/dist/{upgrade-ZWRVFHXV.js.map → upgrade-S5DTXDLC.js.map} +0 -0
|
@@ -3,17 +3,17 @@ import {
|
|
|
3
3
|
isArchitectureDocumentEnforcementEnabled,
|
|
4
4
|
resolveGeneratedArchitecturePath
|
|
5
5
|
} from "./chunk-HI7ANNQI.js";
|
|
6
|
-
import {
|
|
7
|
-
error,
|
|
8
|
-
success,
|
|
9
|
-
warn
|
|
10
|
-
} from "./chunk-NWCBLTIE.js";
|
|
11
6
|
import {
|
|
12
7
|
exists,
|
|
13
8
|
isDirectory,
|
|
14
9
|
readFileSafe,
|
|
15
10
|
readJson
|
|
16
11
|
} from "./chunk-2KELQTLE.js";
|
|
12
|
+
import {
|
|
13
|
+
error,
|
|
14
|
+
success,
|
|
15
|
+
warn
|
|
16
|
+
} from "./chunk-O2ULQPXW.js";
|
|
17
17
|
|
|
18
18
|
// src/commands/architecture.ts
|
|
19
19
|
import { execFileSync } from "child_process";
|
|
@@ -976,4 +976,4 @@ function architectureCheck(cwd) {
|
|
|
976
976
|
export {
|
|
977
977
|
architecture
|
|
978
978
|
};
|
|
979
|
-
//# sourceMappingURL=architecture-
|
|
979
|
+
//# sourceMappingURL=architecture-WFRA4ITA.js.map
|
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildIndexConflictListMessage
|
|
3
3
|
} from "./chunk-ZESHX2BU.js";
|
|
4
|
-
import {
|
|
5
|
-
fetchRegistryLatestVersion,
|
|
6
|
-
isNewerVersion
|
|
7
|
-
} from "./chunk-LRYWFRPD.js";
|
|
8
4
|
import {
|
|
9
5
|
checkHealth,
|
|
10
6
|
reportHealthSummary
|
|
11
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-XTGWCHWI.js";
|
|
12
8
|
import "./chunk-HWOJR33A.js";
|
|
13
9
|
import "./chunk-XTLCJKGE.js";
|
|
14
|
-
import
|
|
10
|
+
import {
|
|
11
|
+
fetchRegistryLatestVersion,
|
|
12
|
+
isNewerVersion
|
|
13
|
+
} from "./chunk-LRYWFRPD.js";
|
|
14
|
+
import "./chunk-GEC7AHRF.js";
|
|
15
15
|
import {
|
|
16
16
|
detectPackageManager
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-OY7F4YNQ.js";
|
|
18
18
|
import {
|
|
19
19
|
syncTickets
|
|
20
20
|
} from "./chunk-D4UGNHLW.js";
|
|
21
21
|
import "./chunk-NHXVS5FL.js";
|
|
22
22
|
import "./chunk-HI7ANNQI.js";
|
|
23
|
-
import "./chunk-
|
|
23
|
+
import "./chunk-CXGL65PS.js";
|
|
24
24
|
import "./chunk-HSC7TELY.js";
|
|
25
|
+
import "./chunk-2KELQTLE.js";
|
|
25
26
|
import {
|
|
26
27
|
header,
|
|
27
28
|
info,
|
|
28
29
|
keyValue,
|
|
29
30
|
success,
|
|
30
31
|
warn
|
|
31
|
-
} from "./chunk-
|
|
32
|
-
import "./chunk-2KELQTLE.js";
|
|
32
|
+
} from "./chunk-O2ULQPXW.js";
|
|
33
33
|
|
|
34
34
|
// src/commands/check.ts
|
|
35
35
|
import process from "process";
|
|
@@ -125,4 +125,4 @@ async function check(options) {
|
|
|
125
125
|
export {
|
|
126
126
|
check
|
|
127
127
|
};
|
|
128
|
-
//# sourceMappingURL=check-
|
|
128
|
+
//# sourceMappingURL=check-KHWH36AK.js.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// templates/hooks/lib/jsonl-spool.ts
|
|
2
|
+
import { appendFileSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
|
|
3
|
+
import nodePath from "path";
|
|
4
|
+
function* iterateJsonlEntries(text) {
|
|
5
|
+
for (const line of text.split("\n")) {
|
|
6
|
+
if (line.trim().length === 0) continue;
|
|
7
|
+
let value;
|
|
8
|
+
try {
|
|
9
|
+
value = JSON.parse(line);
|
|
10
|
+
} catch {
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
yield value;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function countJsonlRecords(filePath) {
|
|
17
|
+
try {
|
|
18
|
+
return readFileSync(filePath, "utf8").split("\n").filter((line) => line.trim().length > 0).length;
|
|
19
|
+
} catch {
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function readJsonlRecords(filePath, parse) {
|
|
24
|
+
let raw;
|
|
25
|
+
try {
|
|
26
|
+
raw = readFileSync(filePath, "utf8");
|
|
27
|
+
} catch {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
const records = [];
|
|
31
|
+
for (const value of iterateJsonlEntries(raw)) {
|
|
32
|
+
try {
|
|
33
|
+
const record = parse(value);
|
|
34
|
+
if (record !== void 0) records.push(record);
|
|
35
|
+
} catch {
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return records;
|
|
39
|
+
}
|
|
40
|
+
function appendJsonlRecords(filePath, lines, cap) {
|
|
41
|
+
try {
|
|
42
|
+
const room = cap - countJsonlRecords(filePath);
|
|
43
|
+
if (room <= 0) return;
|
|
44
|
+
const toWrite = lines.slice(0, room);
|
|
45
|
+
if (toWrite.length === 0) return;
|
|
46
|
+
mkdirSync(nodePath.dirname(filePath), { recursive: true });
|
|
47
|
+
appendFileSync(filePath, `${toWrite.join("\n")}
|
|
48
|
+
`);
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function atomicWriteFile(file, contents) {
|
|
53
|
+
const temporary = `${file}.${process.pid}.tmp`;
|
|
54
|
+
mkdirSync(nodePath.dirname(file), { recursive: true });
|
|
55
|
+
writeFileSync(temporary, contents);
|
|
56
|
+
renameSync(temporary, file);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export {
|
|
60
|
+
iterateJsonlEntries,
|
|
61
|
+
readJsonlRecords,
|
|
62
|
+
appendJsonlRecords,
|
|
63
|
+
atomicWriteFile
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=chunk-B6KVKEZA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../templates/hooks/lib/jsonl-spool.ts"],"sourcesContent":["// Per-session JSONL spool I/O — the shared machinery under the self-report spool\n// (lib/self-report.ts) and the retro draft spool (lib/retro-draft-spool.ts).\n//\n// Both persist sanitized, per-session records to `.safeword/<dir>/<session>.jsonl`\n// and had their own copy of: count non-blank lines, read-and-skip-torn-lines,\n// cap-aware append, and atomic (temp+rename) rewrite. That machinery is\n// correctness-sensitive (torn-tolerance, fail-open, whole-or-nothing writes), so it\n// lives here ONCE. Callers keep what genuinely differs — the subdir, the filename\n// sanitizer, the record schema (parse/serialize), and the cap value — and pass a\n// resolved file path in.\n//\n// Self-contained (node:* only) so the CLI's src/ and the customer-repo hooks can\n// both run it, like the two spools it backs.\n\nimport { appendFileSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';\nimport nodePath from 'node:path';\n\n/**\n * Yield the JSON-parsed value of each non-blank JSONL line in `text`, skipping any\n * blank or torn (unparseable) line — never throws. The single home for the\n * split / skip-blank / parse-or-skip skeleton shared by the spool reader here and\n * the transcript scanners (retro-trigger's counters, retro-extract's digest);\n * callers supply only their per-entry handling.\n */\nexport function* iterateJsonlEntries(text: string): Generator<unknown> {\n for (const line of text.split('\\n')) {\n if (line.trim().length === 0) continue;\n let value: unknown;\n try {\n value = JSON.parse(line);\n } catch {\n continue; // skip a torn/malformed JSONL line\n }\n yield value;\n }\n}\n\n/** Count non-blank JSONL records in a spool file (0 when absent/unreadable). */\nexport function countJsonlRecords(filePath: string): number {\n try {\n return readFileSync(filePath, 'utf8')\n .split('\\n')\n .filter(line => line.trim().length > 0).length;\n } catch {\n return 0;\n }\n}\n\n/**\n * Read every JSONL line, JSON.parse it, and map it through `parse` — skipping any\n * blank, torn (unparseable), or rejected (`parse` → undefined) line. Fail-open: a\n * missing or unreadable file yields `[]`, never a throw, so a filing/read path never\n * crashes on a bad spool.\n */\nexport function readJsonlRecords<T>(\n filePath: string,\n parse: (value: unknown) => T | undefined,\n): T[] {\n let raw: string;\n try {\n raw = readFileSync(filePath, 'utf8');\n } catch {\n return [];\n }\n const records: T[] = [];\n for (const value of iterateJsonlEntries(raw)) {\n try {\n const record = parse(value);\n if (record !== undefined) records.push(record);\n } catch {\n // a parse callback that rejects a malformed record is skipped, not fatal\n }\n }\n return records;\n}\n\n/**\n * Append pre-serialized JSONL lines, capped: once the file already holds `cap`\n * records, drop the overflow rather than growing unbounded (bounds a crash-looping\n * or runaway session). BEST-EFFORT — never throws, so a spool-write failure can't\n * break the host path that called it.\n */\nexport function appendJsonlRecords(filePath: string, lines: readonly string[], cap: number): void {\n try {\n const room = cap - countJsonlRecords(filePath);\n if (room <= 0) return;\n const toWrite = lines.slice(0, room);\n if (toWrite.length === 0) return;\n mkdirSync(nodePath.dirname(filePath), { recursive: true });\n appendFileSync(filePath, `${toWrite.join('\\n')}\\n`);\n } catch {\n // Self-observation must never break the host. Swallow.\n }\n}\n\n/**\n * Write `contents` to `file` atomically: write a pid-unique temp sibling, then\n * rename onto the target, so a concurrent reader sees the whole old or whole new\n * file — never a half-written one. Creates the parent dir. Throws on I/O error;\n * callers that must stay fail-open wrap it in their own try/catch.\n */\nexport function atomicWriteFile(file: string, contents: string): void {\n const temporary = `${file}.${process.pid}.tmp`;\n mkdirSync(nodePath.dirname(file), { recursive: true });\n writeFileSync(temporary, contents);\n renameSync(temporary, file);\n}\n"],"mappings":";AAcA,SAAS,gBAAgB,WAAW,cAAc,YAAY,qBAAqB;AACnF,OAAO,cAAc;AASd,UAAU,oBAAoB,MAAkC;AACrE,aAAW,QAAQ,KAAK,MAAM,IAAI,GAAG;AACnC,QAAI,KAAK,KAAK,EAAE,WAAW,EAAG;AAC9B,QAAI;AACJ,QAAI;AACF,cAAQ,KAAK,MAAM,IAAI;AAAA,IACzB,QAAQ;AACN;AAAA,IACF;AACA,UAAM;AAAA,EACR;AACF;AAGO,SAAS,kBAAkB,UAA0B;AAC1D,MAAI;AACF,WAAO,aAAa,UAAU,MAAM,EACjC,MAAM,IAAI,EACV,OAAO,UAAQ,KAAK,KAAK,EAAE,SAAS,CAAC,EAAE;AAAA,EAC5C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQO,SAAS,iBACd,UACA,OACK;AACL,MAAI;AACJ,MAAI;AACF,UAAM,aAAa,UAAU,MAAM;AAAA,EACrC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACA,QAAM,UAAe,CAAC;AACtB,aAAW,SAAS,oBAAoB,GAAG,GAAG;AAC5C,QAAI;AACF,YAAM,SAAS,MAAM,KAAK;AAC1B,UAAI,WAAW,OAAW,SAAQ,KAAK,MAAM;AAAA,IAC/C,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAQO,SAAS,mBAAmB,UAAkB,OAA0B,KAAmB;AAChG,MAAI;AACF,UAAM,OAAO,MAAM,kBAAkB,QAAQ;AAC7C,QAAI,QAAQ,EAAG;AACf,UAAM,UAAU,MAAM,MAAM,GAAG,IAAI;AACnC,QAAI,QAAQ,WAAW,EAAG;AAC1B,cAAU,SAAS,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AACzD,mBAAe,UAAU,GAAG,QAAQ,KAAK,IAAI,CAAC;AAAA,CAAI;AAAA,EACpD,QAAQ;AAAA,EAER;AACF;AAQO,SAAS,gBAAgB,MAAc,UAAwB;AACpE,QAAM,YAAY,GAAG,IAAI,IAAI,QAAQ,GAAG;AACxC,YAAU,SAAS,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AACrD,gBAAc,WAAW,QAAQ;AACjC,aAAW,WAAW,IAAI;AAC5B;","names":[]}
|
|
@@ -4,20 +4,20 @@ import {
|
|
|
4
4
|
detectLanguages,
|
|
5
5
|
isGitRepo,
|
|
6
6
|
isPackInstalled
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-GEC7AHRF.js";
|
|
8
8
|
import {
|
|
9
9
|
SAFEWORD_PEER_DEPENDENCIES
|
|
10
10
|
} from "./chunk-HSC7TELY.js";
|
|
11
|
+
import {
|
|
12
|
+
exists,
|
|
13
|
+
readJson
|
|
14
|
+
} from "./chunk-2KELQTLE.js";
|
|
11
15
|
import {
|
|
12
16
|
info,
|
|
13
17
|
listItem,
|
|
14
18
|
success,
|
|
15
19
|
warn
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import {
|
|
18
|
-
exists,
|
|
19
|
-
readJson
|
|
20
|
-
} from "./chunk-2KELQTLE.js";
|
|
20
|
+
} from "./chunk-O2ULQPXW.js";
|
|
21
21
|
|
|
22
22
|
// src/packs/install.ts
|
|
23
23
|
function installPack(packId, cwd) {
|
|
@@ -546,4 +546,4 @@ export {
|
|
|
546
546
|
getEslintPeerMismatchWarning,
|
|
547
547
|
maybeAutoPatchOrNudge
|
|
548
548
|
};
|
|
549
|
-
//# sourceMappingURL=chunk-
|
|
549
|
+
//# sourceMappingURL=chunk-C4LNUGHF.js.map
|
|
@@ -106,6 +106,10 @@ function hasVitest(dependencies) {
|
|
|
106
106
|
function hasPlaywright(dependencies) {
|
|
107
107
|
return PLAYWRIGHT_PACKAGES.some((pkg) => Object.hasOwn(dependencies, pkg));
|
|
108
108
|
}
|
|
109
|
+
function hasBunTest(dependencies, cwd) {
|
|
110
|
+
if ("bun-types" in dependencies || "@types/bun" in dependencies) return true;
|
|
111
|
+
return existsSync(path.join(cwd, "bun.lock")) || existsSync(path.join(cwd, "bun.lockb"));
|
|
112
|
+
}
|
|
109
113
|
function hasTurbo(dependencies) {
|
|
110
114
|
return "turbo" in dependencies;
|
|
111
115
|
}
|
|
@@ -226,6 +230,7 @@ var detect = {
|
|
|
226
230
|
hasTanstackQuery,
|
|
227
231
|
hasVitest,
|
|
228
232
|
hasPlaywright,
|
|
233
|
+
hasBunTest,
|
|
229
234
|
hasTurbo,
|
|
230
235
|
hasStorybook,
|
|
231
236
|
// Monorepo detection
|
|
@@ -239,4 +244,4 @@ var detect = {
|
|
|
239
244
|
export {
|
|
240
245
|
detect
|
|
241
246
|
};
|
|
242
|
-
//# sourceMappingURL=chunk-
|
|
247
|
+
//# sourceMappingURL=chunk-CXGL65PS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/presets/typescript/detect.ts"],"sourcesContent":["/**\n * Framework detection utilities for safeword ESLint preset\n *\n * Single source of truth for package detection. Used by:\n * - Generated eslint.config.mjs at lint time\n * - CLI's project-detector at init time\n */\n\nimport { existsSync, readdirSync, readFileSync } from 'node:fs';\nimport path from 'node:path';\n\n/**\n * Read workspace glob patterns from a package.json. Inlined here (rather than\n * importing from `utils/workspaces.ts`) so the presets package stays\n * self-contained — see `cli-presets-self-contained` rule in\n * `.dependency-cruiser.cjs`.\n */\nfunction getWorkspacePatterns(cwd: string): string[] {\n const pkgPath = path.join(cwd, 'package.json');\n if (!existsSync(pkgPath)) return [];\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as {\n workspaces?: string[] | { packages?: string[] };\n };\n if (!pkg.workspaces) return [];\n return Array.isArray(pkg.workspaces) ? pkg.workspaces : (pkg.workspaces.packages ?? []);\n } catch {\n return [];\n }\n}\n\n/**\n * TanStack Query package names across all supported frameworks.\n */\nconst TANSTACK_QUERY_PACKAGES = [\n '@tanstack/react-query',\n '@tanstack/vue-query',\n '@tanstack/solid-query',\n '@tanstack/svelte-query',\n '@tanstack/angular-query-experimental',\n] as const;\n\n/**\n * Tailwind CSS package names (v3 and v4 installation methods).\n */\nconst TAILWIND_PACKAGES = ['tailwindcss', '@tailwindcss/vite', '@tailwindcss/postcss'] as const;\n\n/**\n * Playwright test package names.\n */\nconst PLAYWRIGHT_PACKAGES = ['@playwright/test', 'playwright'] as const;\n\n/**\n * Storybook package names (core CLI + common framework integrations).\n */\nconst STORYBOOK_PACKAGES = [\n 'storybook',\n '@storybook/react',\n '@storybook/vue3',\n '@storybook/svelte',\n '@storybook/angular',\n '@storybook/web-components',\n '@storybook/html',\n] as const;\n\n/**\n * Non-Prettier formatter config files (Biome, dprint, Rome, oxfmt, deno).\n * Used to detect if project uses an alternative formatter.\n * Prettier is safeword's default, so its presence doesn't skip config creation.\n *\n * Kept in sync by hand with ALTERNATIVE_FORMATTER_FILES in the hook's\n * `lib/lint-config.ts` (the `cli-presets-self-contained` rule forbids sharing\n * one module). oxfmt config set per oxc docs; deno uses deno.json(c).\n */\nconst ALTERNATIVE_FORMATTER_FILES = [\n // Biome (and legacy Rome)\n 'biome.json',\n 'biome.jsonc',\n 'rome.json',\n // dprint\n 'dprint.json',\n '.dprint.json',\n 'dprint.jsonc',\n '.dprint.jsonc',\n // oxfmt (oxc formatter)\n '.oxfmtrc.json',\n '.oxfmtrc.jsonc',\n 'oxfmt.config.js',\n 'oxfmt.config.cjs',\n 'oxfmt.config.mjs',\n 'oxfmt.config.ts',\n 'oxfmt.config.cts',\n 'oxfmt.config.mts',\n // deno fmt\n 'deno.json',\n 'deno.jsonc',\n] as const;\n\ntype DependenciesRecord = Record<string, string | undefined>;\ntype ScriptsRecord = Record<string, string | undefined>;\n\n/**\n * Read dependencies from a package.json file.\n */\nfunction readPackageDependencies(pkgPath: string): DependenciesRecord {\n try {\n if (!existsSync(pkgPath)) return {};\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));\n return { ...pkg.dependencies, ...pkg.devDependencies };\n } catch {\n return {};\n }\n}\n\n/**\n * Get all monorepo workspace patterns (from package.json + common directories).\n */\nfunction getMonorepoPatterns(rootDirectory: string): string[] {\n const workspacePatterns = getWorkspacePatterns(rootDirectory);\n const commonPatterns = ['apps/*', 'packages/*'];\n return [...new Set([...workspacePatterns, ...commonPatterns])];\n}\n\n/**\n * Scan a workspace directory for package.json files.\n */\nfunction scanWorkspaceDirectory(rootDirectory: string, pattern: string): DependenciesRecord {\n if (!pattern.endsWith('/*')) return {};\n\n const baseDirectory = path.join(rootDirectory, pattern.slice(0, -2));\n if (!existsSync(baseDirectory)) return {};\n\n const allDependencies: DependenciesRecord = {};\n try {\n const entries = readdirSync(baseDirectory, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n Object.assign(\n allDependencies,\n readPackageDependencies(path.join(baseDirectory, entry.name, 'package.json')),\n );\n }\n }\n } catch {\n // Ignore read errors\n }\n return allDependencies;\n}\n\n/**\n * Collect all dependencies from root and workspace package.json files.\n * Supports npm/yarn workspaces and common monorepo patterns.\n */\nfunction collectAllDependencies(rootDirectory: string): DependenciesRecord {\n const rootPackagePath = path.join(rootDirectory, 'package.json');\n const allDependencies = readPackageDependencies(rootPackagePath);\n\n // Scan each workspace pattern\n for (const pattern of getMonorepoPatterns(rootDirectory)) {\n Object.assign(allDependencies, scanWorkspaceDirectory(rootDirectory, pattern));\n }\n\n return allDependencies;\n}\n\n/**\n * Check if Tailwind CSS is installed.\n */\nfunction hasTailwind(dependencies: DependenciesRecord): boolean {\n return TAILWIND_PACKAGES.some(pkg => Object.hasOwn(dependencies, pkg));\n}\n\n/**\n * Check if TanStack Query is installed.\n */\nfunction hasTanstackQuery(dependencies: DependenciesRecord): boolean {\n return TANSTACK_QUERY_PACKAGES.some(pkg => Object.hasOwn(dependencies, pkg));\n}\n\n/**\n * Check if Vitest is installed.\n */\nfunction hasVitest(dependencies: DependenciesRecord): boolean {\n return 'vitest' in dependencies;\n}\n\n/**\n * Check if Playwright is installed.\n */\nfunction hasPlaywright(dependencies: DependenciesRecord): boolean {\n return PLAYWRIGHT_PACKAGES.some(pkg => Object.hasOwn(dependencies, pkg));\n}\n\n/**\n * Check if the project uses Bun's built-in test runner (`bun:test`).\n *\n * Two independent signals, either is sufficient:\n * - `bun-types`/`@types/bun` devDependency — the typed-project signal.\n * - A Bun lockfile at the project root — catches plain-JS Bun projects that\n * have no reason to install type packages. This is the case that actually\n * needs the fix: Bun's transpiler injects `describe`/`test`/`expect`/etc.\n * without an import, which only TypeScript's `eslint-recommended` override\n * (no-undef: off for .ts) already tolerates — plain .js/.jsx test files\n * still false-positive on `no-undef` (ticket #513).\n *\n * Lockfile presence alone doesn't prove Bun is the *test* runner (it could\n * be package-manager-only), but over-detecting here only widens a globals\n * allowlist to Bun's real `bun:test` export names — harmless if unused.\n *\n * The `bun.lock`/`bun.lockb` filenames are kept in sync by hand with the\n * package-manager check in `utils/install.ts` (the `cli-presets-self-contained`\n * rule forbids importing it from here).\n */\nfunction hasBunTest(dependencies: DependenciesRecord, cwd: string): boolean {\n if ('bun-types' in dependencies || '@types/bun' in dependencies) return true;\n return existsSync(path.join(cwd, 'bun.lock')) || existsSync(path.join(cwd, 'bun.lockb'));\n}\n\n/**\n * Check if Turborepo is installed.\n */\nfunction hasTurbo(dependencies: DependenciesRecord): boolean {\n return 'turbo' in dependencies;\n}\n\n/**\n * Check if Storybook is installed.\n */\nfunction hasStorybook(dependencies: DependenciesRecord): boolean {\n return STORYBOOK_PACKAGES.some(pkg => Object.hasOwn(dependencies, pkg));\n}\n\n/**\n * Detect base framework for config selection.\n * Returns the most specific framework detected.\n */\nfunction detectFramework(\n dependencies: DependenciesRecord,\n): 'next' | 'react' | 'astro' | 'typescript' | 'javascript' {\n if ('next' in dependencies) return 'next';\n if ('astro' in dependencies) return 'astro'; // Check before React (Astro+React has both)\n if ('react' in dependencies) return 'react';\n if ('typescript' in dependencies || 'typescript-eslint' in dependencies) return 'typescript';\n return 'javascript';\n}\n\n/**\n * Standard ignore patterns for ESLint flat config.\n * Includes build output, tool caches, and non-JS ecosystems.\n *\n * `.safeword/` is broadly ignored — once installed in a customer repo,\n * safeword fully owns that directory (hooks, scripts, statusline,\n * version file). Customers can't productively modify it (safeword\n * setup/upgrade regenerates the contents), so linting those files\n * would just surface noise the customer can't act on. The leading\n * glob-anywhere prefix catches the directory at any depth — important\n * for monorepos where each workspace may have its own safeword install.\n */\nfunction getIgnores(): string[] {\n return [\n '**/node_modules/',\n '**/dist/',\n '**/build/',\n '**/coverage/',\n // Safeword-owned directory — customers can't affect it; ignore at any depth.\n '**/.safeword/**/*',\n // Build output directories — always ignore even if framework not currently installed\n '**/.next/',\n '**/.astro/',\n // Non-JS ecosystems that may contain JS files\n '**/.venv/',\n // Tool caches\n '**/.turbo/',\n ];\n}\n\n/**\n * Check if project has an existing linter setup.\n * True if package.json has a \"lint\" script.\n */\nfunction hasExistingLinter(scripts: ScriptsRecord): boolean {\n return 'lint' in scripts;\n}\n\n/**\n * Check if project uses an alternative formatter (Biome, dprint, Rome).\n * Returns false for Prettier since it's safeword's default.\n *\n * We don't check for \"format\" script because safeword adds that itself.\n */\nfunction hasExistingFormatter(cwd: string, _scripts: ScriptsRecord): boolean {\n return ALTERNATIVE_FORMATTER_FILES.some(file => existsSync(path.join(cwd, file)));\n}\n\n// Prettier config filenames, by the exact set prettier resolves — NOT a loose\n// prefix. Prefix-matching (`.prettierrc*`) false-positives on a disabled/backup\n// config (`.prettierrc.bak`, `prettier.config.js.disabled`): prettier won't load\n// those, so they must read as \"no config\". Mirror of `PRETTIER_CONFIG_FILES` in\n// the session-lint-check hook (`hooks/lib/lint-config.ts`, ticket 1J6JKP); the\n// `cli-presets-self-contained` rule forbids importing it, so keep the two in\n// sync. Extension lists verified against prettier 3.8 docs.\nconst PRETTIER_RC_EXTENSIONS = [\n 'json',\n 'yaml',\n 'yml',\n 'json5',\n 'toml',\n 'js',\n 'cjs',\n 'mjs',\n 'ts',\n 'cts',\n 'mts',\n];\nconst PRETTIER_CONFIG_EXTENSIONS = ['js', 'cjs', 'mjs', 'ts', 'cts', 'mts'];\nconst PRETTIER_CONFIG_FILES = new Set<string>([\n '.prettierrc',\n ...PRETTIER_RC_EXTENSIONS.map(extension => `.prettierrc.${extension}`),\n ...PRETTIER_CONFIG_EXTENSIONS.map(extension => `prettier.config.${extension}`),\n]);\n\n/**\n * Check if the project already has its own Prettier config, in any form prettier\n * recognizes: a `.prettierrc` / `.prettierrc.*` / `prettier.config.*` file, or a\n * `\"prettier\"` key in package.json.\n *\n * Unlike {@link hasExistingFormatter} (alternative formatters only), this exists\n * to stop safeword from dropping its own `.prettierrc`/`.safeword/.prettierrc`\n * on top of a config it can't merge into — a bare `.prettierrc` resolves ahead\n * of `prettier.config.mjs` in prettier's search order, so writing one silently\n * shadows the customer's style.\n */\nfunction hasExistingPrettierConfig(cwd: string): boolean {\n let entries: string[];\n try {\n entries = readdirSync(cwd);\n } catch {\n entries = [];\n }\n if (entries.some(name => PRETTIER_CONFIG_FILES.has(name))) return true;\n\n const pkgPath = path.join(cwd, 'package.json');\n if (!existsSync(pkgPath)) return false;\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as { prettier?: unknown };\n return pkg.prettier !== undefined;\n } catch {\n return false;\n }\n}\n\n/**\n * Next.js config file names to look for.\n */\nconst NEXT_CONFIG_FILES = ['next.config.js', 'next.config.mjs', 'next.config.ts'] as const;\n\n/**\n * Check if a directory contains a Next.js config file.\n */\nfunction hasNextConfig(directory: string): boolean {\n return NEXT_CONFIG_FILES.some(file => existsSync(path.join(directory, file)));\n}\n\n/**\n * Scan a workspace directory for Next.js config files.\n * Returns relative glob patterns for directories containing Next.js configs.\n */\nfunction scanDirectoryForNextConfigs(rootDirectory: string, workspacePattern: string): string[] {\n if (!workspacePattern.endsWith('/*')) return [];\n\n const baseDirectory = workspacePattern.slice(0, -2); // Remove '/*'\n const fullBaseDirectory = path.join(rootDirectory, baseDirectory);\n\n if (!existsSync(fullBaseDirectory)) return [];\n\n const nextPaths: string[] = [];\n try {\n const entries = readdirSync(fullBaseDirectory, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const packageDirectory = path.join(fullBaseDirectory, entry.name);\n if (hasNextConfig(packageDirectory)) {\n nextPaths.push(`${baseDirectory}/${entry.name}/**/*.{ts,tsx}`);\n }\n }\n } catch {\n // Ignore read errors\n }\n return nextPaths;\n}\n\n/**\n * Find Next.js config paths for ESLint rule scoping in monorepos.\n *\n * Returns:\n * - `undefined` if Next.js config exists at root (single app, no scoping needed)\n * - `string[]` of glob patterns for directories containing Next.js apps\n * - Empty array if no Next.js configs found anywhere\n *\n * Used to scope Next.js-specific ESLint rules to only the packages that use Next.js,\n * allowing other packages (React, Astro, etc.) to avoid irrelevant Next.js rules.\n */\nfunction findNextConfigPaths(rootDirectory: string): string[] | undefined {\n // Check for root Next.js config - means single app, no scoping needed\n if (hasNextConfig(rootDirectory)) {\n return undefined;\n }\n\n // Scan each workspace pattern for Next.js configs\n return getMonorepoPatterns(rootDirectory).flatMap(pattern =>\n scanDirectoryForNextConfigs(rootDirectory, pattern),\n );\n}\n\n/**\n * All detection utilities bundled together.\n */\nexport const detect = {\n // Package lists\n TAILWIND_PACKAGES,\n TANSTACK_QUERY_PACKAGES,\n PLAYWRIGHT_PACKAGES,\n STORYBOOK_PACKAGES,\n ALTERNATIVE_FORMATTER_FILES,\n NEXT_CONFIG_FILES,\n\n // Core utilities\n collectAllDeps: collectAllDependencies,\n detectFramework,\n getIgnores,\n\n // Feature detection\n hasTailwind,\n hasTanstackQuery,\n hasVitest,\n hasPlaywright,\n hasBunTest,\n hasTurbo,\n hasStorybook,\n\n // Monorepo detection\n findNextConfigPaths,\n\n // Existing tooling detection\n hasExistingLinter,\n hasExistingFormatter,\n hasExistingPrettierConfig,\n};\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
MCP_SERVERS
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-OY7F4YNQ.js";
|
|
4
4
|
import {
|
|
5
5
|
NAMESPACE_ROOT_LEGACY,
|
|
6
6
|
readConfiguredPath,
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from "./chunk-HI7ANNQI.js";
|
|
9
9
|
import {
|
|
10
10
|
detect
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-CXGL65PS.js";
|
|
12
12
|
import {
|
|
13
13
|
VERSION
|
|
14
14
|
} from "./chunk-HSC7TELY.js";
|
|
@@ -1991,6 +1991,7 @@ const scopedNextConfigs = nextPaths?.flatMap((filePath) =>
|
|
|
1991
1991
|
}
|
|
1992
1992
|
var OPTIONAL_CONFIGS_SNIPPET = ` // Testing configs - only if detected (plugins have framework peer deps)
|
|
1993
1993
|
...(detect.hasVitest(deps) ? configs.vitest : []),
|
|
1994
|
+
...(detect.hasBunTest(deps, __dirname) ? configs.bunTest : []),
|
|
1994
1995
|
...(detect.hasPlaywright(deps) ? configs.playwright : []),
|
|
1995
1996
|
// Storybook - only if detected (v10+ requires storybook peer dep)
|
|
1996
1997
|
...(detect.hasStorybook(deps) ? configs.storybook : []),
|
|
@@ -2225,6 +2226,9 @@ function hook(command) {
|
|
|
2225
2226
|
function asyncRewakeHook(command) {
|
|
2226
2227
|
return { hooks: [{ type: "command", command, asyncRewake: true }] };
|
|
2227
2228
|
}
|
|
2229
|
+
function asyncHook(command) {
|
|
2230
|
+
return { hooks: [{ type: "command", command, async: true }] };
|
|
2231
|
+
}
|
|
2228
2232
|
function matchedHook(matcher, command) {
|
|
2229
2233
|
return { matcher, hooks: [{ type: "command", command }] };
|
|
2230
2234
|
}
|
|
@@ -2251,12 +2255,18 @@ var SETTINGS_HOOKS = {
|
|
|
2251
2255
|
],
|
|
2252
2256
|
UserPromptSubmit: [
|
|
2253
2257
|
hook(`bun ${HOOKS_DIR}/prompt-timestamp.ts`),
|
|
2254
|
-
hook(`bun ${HOOKS_DIR}/prompt-questions.ts`)
|
|
2258
|
+
hook(`bun ${HOOKS_DIR}/prompt-questions.ts`),
|
|
2259
|
+
// Cloud-retro filing nudge (BNGK9W): when the async Stop hook spooled unfiled
|
|
2260
|
+
// drafts (REST 401 in cloud), surface one factual line so the agent files them.
|
|
2261
|
+
hook(`bun ${HOOKS_DIR}/prompt-retro-nudge.ts`)
|
|
2255
2262
|
],
|
|
2256
2263
|
Stop: [
|
|
2257
2264
|
hook(`bun ${HOOKS_DIR}/stop-quality.ts`),
|
|
2258
2265
|
hook(`bun ${HOOKS_DIR}/stop-reentry.ts`),
|
|
2259
|
-
hook(`bun ${HOOKS_DIR}/stop-self-report.ts`)
|
|
2266
|
+
hook(`bun ${HOOKS_DIR}/stop-self-report.ts`),
|
|
2267
|
+
// Invisible retro (ZFGWS1): async so repeated delta fires never block Stop and
|
|
2268
|
+
// run fully in the background; the inner spawnSync stays sync within that tree.
|
|
2269
|
+
asyncHook(`bun ${HOOKS_DIR}/stop-retro.ts`)
|
|
2260
2270
|
],
|
|
2261
2271
|
PreToolUse: [
|
|
2262
2272
|
matchedHook("Bash", `bun ${HOOKS_DIR}/pre-tool-dependency-readiness.ts`),
|
|
@@ -2933,6 +2943,15 @@ command = 'bun "$(git rev-parse --show-toplevel)/.safeword/hooks/codex/pre-tool-
|
|
|
2933
2943
|
timeout = 30
|
|
2934
2944
|
statusMessage = "Checking safeword PreToolUse gates"
|
|
2935
2945
|
`;
|
|
2946
|
+
var CODEX_STOP_HOOK_PATCH = `
|
|
2947
|
+
[[hooks.Stop]]
|
|
2948
|
+
|
|
2949
|
+
[[hooks.Stop.hooks]]
|
|
2950
|
+
type = "command"
|
|
2951
|
+
command = 'bun "$(git rev-parse --show-toplevel)/.safeword/hooks/codex/stop.ts"'
|
|
2952
|
+
timeout = 30
|
|
2953
|
+
statusMessage = "Checking safeword Stop nudges"
|
|
2954
|
+
`;
|
|
2936
2955
|
var CODEX_POST_TOOL_SKILL_NUDGE_HOOK_PATCH = `
|
|
2937
2956
|
[[hooks.PostToolUse]]
|
|
2938
2957
|
matcher = "^(apply_patch|Edit|Write|MultiEdit|NotebookEdit)$"
|
|
@@ -3076,6 +3095,9 @@ var SAFEWORD_SCHEMA = {
|
|
|
3076
3095
|
preservedDirs: [
|
|
3077
3096
|
".safeword-project/learnings",
|
|
3078
3097
|
".safeword/logs",
|
|
3098
|
+
// Runtime cloud-filing spool (BNGK9W) — per-session drafts + nudge markers the
|
|
3099
|
+
// retro writes at runtime; user/runtime data the schema does not own.
|
|
3100
|
+
".safeword/retro-drafts",
|
|
3079
3101
|
".safeword-project/tickets",
|
|
3080
3102
|
".safeword-project/tickets/completed",
|
|
3081
3103
|
".safeword-project/tmp"
|
|
@@ -3257,7 +3279,12 @@ var SAFEWORD_SCHEMA = {
|
|
|
3257
3279
|
template: "hooks/lib/dependency-readiness.ts"
|
|
3258
3280
|
},
|
|
3259
3281
|
".safeword/hooks/lib/done-gate.ts": { template: "hooks/lib/done-gate.ts" },
|
|
3282
|
+
".safeword/hooks/lib/jsonl-spool.ts": { template: "hooks/lib/jsonl-spool.ts" },
|
|
3260
3283
|
".safeword/hooks/lib/namespace-root.ts": { template: "hooks/lib/namespace-root.ts" },
|
|
3284
|
+
".safeword/hooks/lib/retro-draft-spool.ts": { template: "hooks/lib/retro-draft-spool.ts" },
|
|
3285
|
+
".safeword/hooks/lib/retro-extract.ts": { template: "hooks/lib/retro-extract.ts" },
|
|
3286
|
+
".safeword/hooks/lib/retro-nudge.ts": { template: "hooks/lib/retro-nudge.ts" },
|
|
3287
|
+
".safeword/hooks/lib/retro-trigger.ts": { template: "hooks/lib/retro-trigger.ts" },
|
|
3261
3288
|
".safeword/hooks/lib/self-report.ts": { template: "hooks/lib/self-report.ts" },
|
|
3262
3289
|
".safeword/hooks/lib/skill-invocation-log.ts": {
|
|
3263
3290
|
template: "hooks/lib/skill-invocation-log.ts"
|
|
@@ -3328,6 +3355,9 @@ var SAFEWORD_SCHEMA = {
|
|
|
3328
3355
|
".safeword/hooks/prompt-questions.ts": {
|
|
3329
3356
|
template: "hooks/prompt-questions.ts"
|
|
3330
3357
|
},
|
|
3358
|
+
".safeword/hooks/prompt-retro-nudge.ts": {
|
|
3359
|
+
template: "hooks/prompt-retro-nudge.ts"
|
|
3360
|
+
},
|
|
3331
3361
|
".safeword/hooks/post-tool-lint.ts": {
|
|
3332
3362
|
template: "hooks/post-tool-lint.ts"
|
|
3333
3363
|
},
|
|
@@ -3355,6 +3385,9 @@ var SAFEWORD_SCHEMA = {
|
|
|
3355
3385
|
".safeword/hooks/codex/pre-tool-quality-helpers.ts": {
|
|
3356
3386
|
template: "hooks/codex/pre-tool-quality-helpers.ts"
|
|
3357
3387
|
},
|
|
3388
|
+
".safeword/hooks/codex/stop.ts": {
|
|
3389
|
+
template: "hooks/codex/stop.ts"
|
|
3390
|
+
},
|
|
3358
3391
|
".safeword/hooks/codex/post-tool-skill-nudge.ts": {
|
|
3359
3392
|
template: "hooks/codex/post-tool-skill-nudge.ts"
|
|
3360
3393
|
},
|
|
@@ -3381,6 +3414,7 @@ var SAFEWORD_SCHEMA = {
|
|
|
3381
3414
|
},
|
|
3382
3415
|
".safeword/hooks/stop-quality.ts": { template: "hooks/stop-quality.ts" },
|
|
3383
3416
|
".safeword/hooks/stop-reentry.ts": { template: "hooks/stop-reentry.ts" },
|
|
3417
|
+
".safeword/hooks/stop-retro.ts": { template: "hooks/stop-retro.ts" },
|
|
3384
3418
|
".safeword/hooks/stop-self-report.ts": { template: "hooks/stop-self-report.ts" },
|
|
3385
3419
|
".safeword/hooks/session-start-reentry.ts": {
|
|
3386
3420
|
template: "hooks/session-start-reentry.ts"
|
|
@@ -3398,6 +3432,9 @@ var SAFEWORD_SCHEMA = {
|
|
|
3398
3432
|
".safeword/guides/self-report-filing.md": {
|
|
3399
3433
|
template: "guides/self-report-filing.md"
|
|
3400
3434
|
},
|
|
3435
|
+
".safeword/guides/retro.md": {
|
|
3436
|
+
template: "guides/retro.md"
|
|
3437
|
+
},
|
|
3401
3438
|
".safeword/guides/cold-start-check.md": {
|
|
3402
3439
|
template: "guides/cold-start-check.md"
|
|
3403
3440
|
},
|
|
@@ -3777,10 +3814,23 @@ ${managedPrettierPaths(ctx).join("\n")}
|
|
|
3777
3814
|
unpatchContent: [
|
|
3778
3815
|
CODEX_SESSION_START_HOOK_PATCH,
|
|
3779
3816
|
CODEX_LEGACY_CONTEXT_SESSION_START_HOOK_PATCH,
|
|
3780
|
-
CODEX_PRE_TOOL_QUALITY_HOOK_PATCH
|
|
3817
|
+
CODEX_PRE_TOOL_QUALITY_HOOK_PATCH,
|
|
3818
|
+
CODEX_STOP_HOOK_PATCH
|
|
3781
3819
|
],
|
|
3782
3820
|
removeFileIfContentEquals: [CODEX_CONFIG_SCAFFOLD_WITHOUT_HOOKS]
|
|
3783
3821
|
},
|
|
3822
|
+
// Codex Stop hook retrofit (53DQJZ): add-if-missing, guarded to safeword
|
|
3823
|
+
// scaffolds. Marker is the adapter path so an existing entry suppresses the
|
|
3824
|
+
// append. Uninstall cleanup is owned by the primary patch's unpatchContent.
|
|
3825
|
+
{
|
|
3826
|
+
operation: "append",
|
|
3827
|
+
content: CODEX_STOP_HOOK_PATCH,
|
|
3828
|
+
marker: ".safeword/hooks/codex/stop.ts",
|
|
3829
|
+
applyWhenContentIncludes: [
|
|
3830
|
+
"# Safeword Codex project configuration.",
|
|
3831
|
+
".safeword/hooks/codex/pre-tool-quality.ts"
|
|
3832
|
+
]
|
|
3833
|
+
},
|
|
3784
3834
|
// Migrate existing installs (auto-upgrade-codex follow-up to #433): swap the
|
|
3785
3835
|
// legacy context-only SessionStart hook for the auto-upgrade dispatcher.
|
|
3786
3836
|
// Codex runs same-event hooks concurrently with no ordering, so this must
|
|
@@ -4165,4 +4215,4 @@ export {
|
|
|
4165
4215
|
untrackIgnoredFiles,
|
|
4166
4216
|
createProjectContext
|
|
4167
4217
|
};
|
|
4168
|
-
//# sourceMappingURL=chunk-
|
|
4218
|
+
//# sourceMappingURL=chunk-GEC7AHRF.js.map
|