safeword 0.57.0 → 0.58.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/{check-IV6KC65F.js → check-6IHMMAS6.js} +34 -26
- package/dist/check-6IHMMAS6.js.map +1 -0
- package/dist/{chunk-D5H7VBXQ.js → chunk-DIJ35ZXH.js} +2 -2
- package/dist/chunk-LRYWFRPD.js +46 -0
- package/dist/chunk-LRYWFRPD.js.map +1 -0
- package/dist/{chunk-O3QF6QHX.js → chunk-Q7ALQS7T.js} +2 -2
- package/dist/{chunk-HGOG5ZLC.js → chunk-UYTOLZT2.js} +115 -29
- package/dist/chunk-UYTOLZT2.js.map +1 -0
- package/dist/cli.js +8 -8
- package/dist/cli.js.map +1 -1
- package/dist/{diff-55Y2SH4U.js → diff-5YTHPOAV.js} +36 -5
- package/dist/diff-5YTHPOAV.js.map +1 -0
- package/dist/{reset-FZRYTUFF.js → reset-OBM2VJQT.js} +2 -2
- package/dist/{setup-WKFBBSLJ.js → setup-ZFQDI5QT.js} +4 -4
- package/dist/{test-plan-HWRDWG2X.js → test-plan-MS6HRVBR.js} +9 -3
- package/dist/test-plan-MS6HRVBR.js.map +1 -0
- package/dist/{ticket-new-GXYCW5ML.js → ticket-new-S6IMADXY.js} +6 -2
- package/dist/{ticket-new-GXYCW5ML.js.map → ticket-new-S6IMADXY.js.map} +1 -1
- package/dist/{upgrade-MCBH6GTD.js → upgrade-N3WTG3KL.js} +6 -9
- package/dist/upgrade-N3WTG3KL.js.map +1 -0
- package/package.json +12 -11
- package/templates/codex/config.toml +3 -3
- package/templates/commands/verify.md +7 -1
- package/templates/cursor/rules/bdd-tdd.mdc +1 -1
- package/templates/cursor/rules/safeword-tdd-review.mdc +1 -1
- package/templates/guides/llm-evals-guide.md +485 -0
- package/templates/guides/testing-guide.md +35 -13
- package/templates/guides/verification-lanes-guide.md +574 -0
- package/templates/hooks/cursor/before-shell-execution.ts +20 -0
- package/templates/hooks/cursor/gate-adapter.ts +82 -12
- package/templates/hooks/cursor/post-tool-quality.ts +1 -1
- package/templates/hooks/cursor/pre-tool-quality.ts +8 -2
- package/templates/hooks/cursor/stop.ts +17 -0
- package/templates/hooks/lib/architecture-staged-scope.ts +130 -0
- package/templates/hooks/lib/auto-upgrade-lock.ts +89 -0
- package/templates/hooks/lib/auto-upgrade.ts +417 -0
- package/templates/hooks/lib/branch-staleness.ts +49 -0
- package/templates/hooks/lib/checkbox-transitions.ts +2 -2
- package/templates/hooks/lib/cursor-run-identity.ts +216 -0
- package/templates/hooks/lib/dependency-readiness.ts +83 -0
- package/templates/hooks/lib/done-gate.ts +9 -6
- package/templates/hooks/lib/ledger-git.ts +92 -0
- package/templates/hooks/lib/ledger-validation.ts +31 -24
- package/templates/hooks/lib/quality-state.ts +3 -7
- package/templates/hooks/lib/review-trigger.ts +4 -31
- package/templates/hooks/lib/safeword-context.ts +83 -0
- package/templates/hooks/post-tool-dependency-readiness.ts +84 -0
- package/templates/hooks/post-tool-quality.ts +6 -18
- package/templates/hooks/pre-tool-architecture-stage.ts +9 -0
- package/templates/hooks/pre-tool-quality.ts +22 -4
- package/templates/hooks/pre-tool-stale-main.ts +83 -0
- package/templates/hooks/record-skill-invocation.ts +32 -6
- package/templates/hooks/session-auto-upgrade.ts +13 -300
- package/templates/hooks/session-codex-start.ts +39 -0
- package/templates/hooks/session-cursor-auto-upgrade.ts +35 -0
- package/templates/hooks/session-dependency-readiness.ts +36 -0
- package/templates/hooks/session-safeword-context.ts +19 -81
- package/templates/hooks/stop-quality.ts +14 -32
- package/templates/skills/bdd/SCENARIOS.md +1 -1
- package/templates/skills/bdd/TDD.md +3 -1
- package/templates/skills/quality-review/SKILL.md +5 -0
- package/templates/skills/review-spec/SKILL.md +2 -1
- package/templates/skills/tdd-review/SKILL.md +4 -2
- package/templates/skills/testing/SKILL.md +33 -0
- package/templates/skills/ticket-system/SKILL.md +14 -2
- package/templates/skills/verify/SKILL.md +7 -1
- package/dist/check-IV6KC65F.js.map +0 -1
- package/dist/chunk-FJYRWU2V.js +0 -21
- package/dist/chunk-FJYRWU2V.js.map +0 -1
- package/dist/chunk-HGOG5ZLC.js.map +0 -1
- package/dist/diff-55Y2SH4U.js.map +0 -1
- package/dist/test-plan-HWRDWG2X.js.map +0 -1
- package/dist/upgrade-MCBH6GTD.js.map +0 -1
- /package/dist/{chunk-D5H7VBXQ.js.map → chunk-DIJ35ZXH.js.map} +0 -0
- /package/dist/{chunk-O3QF6QHX.js.map → chunk-Q7ALQS7T.js.map} +0 -0
- /package/dist/{reset-FZRYTUFF.js.map → reset-OBM2VJQT.js.map} +0 -0
- /package/dist/{setup-WKFBBSLJ.js.map → setup-ZFQDI5QT.js.map} +0 -0
|
@@ -2,16 +2,19 @@ import {
|
|
|
2
2
|
buildIndexConflictListMessage
|
|
3
3
|
} from "./chunk-ZESHX2BU.js";
|
|
4
4
|
import {
|
|
5
|
+
fetchRegistryLatestVersion,
|
|
5
6
|
isNewerVersion
|
|
6
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-LRYWFRPD.js";
|
|
7
8
|
import {
|
|
8
9
|
checkHealth,
|
|
9
10
|
reportHealthSummary
|
|
10
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-Q7ALQS7T.js";
|
|
11
12
|
import "./chunk-YXNI7W5D.js";
|
|
12
13
|
import "./chunk-XTLCJKGE.js";
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import
|
|
14
|
+
import "./chunk-UYTOLZT2.js";
|
|
15
|
+
import {
|
|
16
|
+
detectPackageManager
|
|
17
|
+
} from "./chunk-GS3TBFXU.js";
|
|
15
18
|
import {
|
|
16
19
|
syncTickets
|
|
17
20
|
} from "./chunk-HTDMZQKA.js";
|
|
@@ -30,26 +33,9 @@ import "./chunk-KIZYVSME.js";
|
|
|
30
33
|
|
|
31
34
|
// src/commands/check.ts
|
|
32
35
|
import process from "process";
|
|
33
|
-
async function checkLatestVersion(timeout = 3e3) {
|
|
34
|
-
try {
|
|
35
|
-
const controller = new AbortController();
|
|
36
|
-
const timeoutId = setTimeout(() => {
|
|
37
|
-
controller.abort();
|
|
38
|
-
}, timeout);
|
|
39
|
-
const response = await fetch("https://registry.npmjs.org/safeword/latest", {
|
|
40
|
-
signal: controller.signal
|
|
41
|
-
});
|
|
42
|
-
clearTimeout(timeoutId);
|
|
43
|
-
if (!response.ok) return void 0;
|
|
44
|
-
const data = await response.json();
|
|
45
|
-
return data.version ?? void 0;
|
|
46
|
-
} catch {
|
|
47
|
-
return void 0;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
36
|
async function reportUpdateStatus(health) {
|
|
51
37
|
info("\nChecking for updates...");
|
|
52
|
-
const latestVersion = await
|
|
38
|
+
const latestVersion = await fetchRegistryLatestVersion();
|
|
53
39
|
if (!latestVersion) {
|
|
54
40
|
warn("Couldn't check for updates (offline?)");
|
|
55
41
|
return;
|
|
@@ -63,11 +49,33 @@ async function reportUpdateStatus(health) {
|
|
|
63
49
|
success("CLI is up to date");
|
|
64
50
|
}
|
|
65
51
|
}
|
|
66
|
-
function
|
|
52
|
+
function isDigit(char) {
|
|
53
|
+
return char >= "0" && char <= "9";
|
|
54
|
+
}
|
|
55
|
+
function isPackageVersionSpecChar(char) {
|
|
56
|
+
return char >= "a" && char <= "z" || char >= "A" && char <= "Z" || isDigit(char) || char === "." || char === "-" || char === "+";
|
|
57
|
+
}
|
|
58
|
+
function isSafePackageVersion(version) {
|
|
59
|
+
if (version.length === 0 || !version.includes(".")) return false;
|
|
60
|
+
for (const char of version) {
|
|
61
|
+
if (!isPackageVersionSpecChar(char)) return false;
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
function reportVersionMismatch(health, cwd) {
|
|
67
66
|
if (!health.projectVersion) return;
|
|
68
67
|
if (isNewerVersion(health.cliVersion, health.projectVersion)) {
|
|
69
68
|
warn(`Project config (v${health.projectVersion}) is newer than CLI (v${health.cliVersion})`);
|
|
70
|
-
|
|
69
|
+
if (!isSafePackageVersion(health.projectVersion)) {
|
|
70
|
+
warn(
|
|
71
|
+
"Project version is not safe to use in a package install command; inspect .safeword/version."
|
|
72
|
+
);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const pm = detectPackageManager(cwd);
|
|
76
|
+
const runSafewordUpgrade = pm === "bun" || pm === "yarn" ? `${pm} run safeword upgrade` : `${pm} exec safeword upgrade`;
|
|
77
|
+
info("Update the project-local CLI first:");
|
|
78
|
+
info(`${pm} add -D safeword@${health.projectVersion} && ${runSafewordUpgrade}`);
|
|
71
79
|
} else if (isNewerVersion(health.projectVersion, health.cliVersion)) {
|
|
72
80
|
info(`
|
|
73
81
|
Upgrade available for project config`);
|
|
@@ -108,7 +116,7 @@ async function check(options) {
|
|
|
108
116
|
} else {
|
|
109
117
|
await reportUpdateStatus(health);
|
|
110
118
|
}
|
|
111
|
-
reportVersionMismatch(health);
|
|
119
|
+
reportVersionMismatch(health, cwd);
|
|
112
120
|
const hasIssues = reportHealthSummary(health);
|
|
113
121
|
if (hasIssues) {
|
|
114
122
|
process.exit(1);
|
|
@@ -117,4 +125,4 @@ async function check(options) {
|
|
|
117
125
|
export {
|
|
118
126
|
check
|
|
119
127
|
};
|
|
120
|
-
//# sourceMappingURL=check-
|
|
128
|
+
//# sourceMappingURL=check-6IHMMAS6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/check.ts"],"sourcesContent":["/**\n * Check command - Verify project health and configuration\n *\n * The config-health core lives in ../health.ts (shared with the setup/upgrade\n * self-verify, ticket 3293WH). This command adds the standalone-only\n * surfaces: npm update-check, version display, and ticket-index refresh.\n */\n\nimport process from 'node:process';\n\nimport { checkHealth, type HealthStatus, reportHealthSummary } from '../health.js';\nimport { syncTickets } from '../ticket-sync/index.js';\nimport { detectPackageManager } from '../utils/install.js';\nimport { header, info, keyValue, success, warn } from '../utils/output.js';\nimport { buildIndexConflictListMessage } from '../utils/ticket-index-warnings.js';\nimport { fetchRegistryLatestVersion, isNewerVersion } from '../utils/version.js';\n\ninterface CheckOptions {\n offline?: boolean;\n}\n\n/**\n * Check for CLI updates and report status\n * @param health\n */\nasync function reportUpdateStatus(health: HealthStatus): Promise<void> {\n info('\\nChecking for updates...');\n const latestVersion = await fetchRegistryLatestVersion();\n\n if (!latestVersion) {\n warn(\"Couldn't check for updates (offline?)\");\n return;\n }\n\n health.latestVersion = latestVersion;\n health.updateAvailable = isNewerVersion(health.cliVersion, latestVersion);\n\n if (health.updateAvailable) {\n warn(`Update available: v${latestVersion}`);\n info('Run `bunx safeword@latest upgrade` to upgrade');\n } else {\n success('CLI is up to date');\n }\n}\n\nfunction isDigit(char: string): boolean {\n return char >= '0' && char <= '9';\n}\n\nfunction isPackageVersionSpecChar(char: string): boolean {\n return (\n (char >= 'a' && char <= 'z') ||\n (char >= 'A' && char <= 'Z') ||\n isDigit(char) ||\n char === '.' ||\n char === '-' ||\n char === '+'\n );\n}\n\nfunction isSafePackageVersion(version: string): boolean {\n if (version.length === 0 || !version.includes('.')) return false;\n for (const char of version) {\n if (!isPackageVersionSpecChar(char)) return false;\n }\n return true;\n}\n\n/**\n * Compare project version vs CLI version and report\n * @param health\n */\nfunction reportVersionMismatch(health: HealthStatus, cwd: string): void {\n if (!health.projectVersion) return;\n\n if (isNewerVersion(health.cliVersion, health.projectVersion)) {\n warn(`Project config (v${health.projectVersion}) is newer than CLI (v${health.cliVersion})`);\n\n if (!isSafePackageVersion(health.projectVersion)) {\n warn(\n 'Project version is not safe to use in a package install command; inspect .safeword/version.',\n );\n return;\n }\n\n const pm = detectPackageManager(cwd);\n const runSafewordUpgrade =\n pm === 'bun' || pm === 'yarn' ? `${pm} run safeword upgrade` : `${pm} exec safeword upgrade`;\n info('Update the project-local CLI first:');\n info(`${pm} add -D safeword@${health.projectVersion} && ${runSafewordUpgrade}`);\n } else if (isNewerVersion(health.projectVersion, health.cliVersion)) {\n info(`\\nUpgrade available for project config`);\n info(\n `Run \\`safeword upgrade\\` to update from v${health.projectVersion} to v${health.cliVersion}`,\n );\n }\n}\n\n/**\n * Regenerate the ticket discovery index, swallowing any error — index\n * freshness must never block or fail a health check. Reports only when it\n * actually rewrote a file.\n * @param cwd\n */\nfunction regenerateTicketIndex(cwd: string): void {\n try {\n const result = syncTickets(cwd);\n if (result.wrote) {\n info('Regenerated ticket index (INDEX.md / INDEX-completed.md)');\n }\n if (result.indexConflicts.length > 0) {\n warn(buildIndexConflictListMessage(result.indexConflicts));\n }\n } catch (error: unknown) {\n // Best-effort: index freshness must never fail the health check. Surface\n // under DEBUG, then return — the deliberate swallow point.\n if (process.env.DEBUG) {\n console.error('[check] ticket index regen failed:', error);\n }\n return;\n }\n}\n\n/**\n *\n * @param options\n */\nexport async function check(options: CheckOptions): Promise<void> {\n const cwd = process.cwd();\n\n header('Safeword Health Check');\n\n const health = await checkHealth(cwd);\n\n // Not configured\n if (!health.configured) {\n info('Not configured. Run `safeword setup` to initialize.');\n return;\n }\n\n // Keep the ticket discovery index fresh at this checkpoint (best-effort —\n // never fail the health check on index regen). Ticket 1GGD28.\n regenerateTicketIndex(cwd);\n\n // Show versions\n keyValue('Safeword CLI', `v${health.cliVersion}`);\n keyValue('Project config', health.projectVersion ? `v${health.projectVersion}` : 'unknown');\n\n // Check for updates (unless offline)\n if (options.offline) {\n info('\\nSkipped update check (offline mode)');\n } else {\n await reportUpdateStatus(health);\n }\n\n reportVersionMismatch(health, cwd);\n const hasIssues = reportHealthSummary(health);\n\n if (hasIssues) {\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAQA,OAAO,aAAa;AAiBpB,eAAe,mBAAmB,QAAqC;AACrE,OAAK,2BAA2B;AAChC,QAAM,gBAAgB,MAAM,2BAA2B;AAEvD,MAAI,CAAC,eAAe;AAClB,SAAK,uCAAuC;AAC5C;AAAA,EACF;AAEA,SAAO,gBAAgB;AACvB,SAAO,kBAAkB,eAAe,OAAO,YAAY,aAAa;AAExE,MAAI,OAAO,iBAAiB;AAC1B,SAAK,sBAAsB,aAAa,EAAE;AAC1C,SAAK,+CAA+C;AAAA,EACtD,OAAO;AACL,YAAQ,mBAAmB;AAAA,EAC7B;AACF;AAEA,SAAS,QAAQ,MAAuB;AACtC,SAAO,QAAQ,OAAO,QAAQ;AAChC;AAEA,SAAS,yBAAyB,MAAuB;AACvD,SACG,QAAQ,OAAO,QAAQ,OACvB,QAAQ,OAAO,QAAQ,OACxB,QAAQ,IAAI,KACZ,SAAS,OACT,SAAS,OACT,SAAS;AAEb;AAEA,SAAS,qBAAqB,SAA0B;AACtD,MAAI,QAAQ,WAAW,KAAK,CAAC,QAAQ,SAAS,GAAG,EAAG,QAAO;AAC3D,aAAW,QAAQ,SAAS;AAC1B,QAAI,CAAC,yBAAyB,IAAI,EAAG,QAAO;AAAA,EAC9C;AACA,SAAO;AACT;AAMA,SAAS,sBAAsB,QAAsB,KAAmB;AACtE,MAAI,CAAC,OAAO,eAAgB;AAE5B,MAAI,eAAe,OAAO,YAAY,OAAO,cAAc,GAAG;AAC5D,SAAK,oBAAoB,OAAO,cAAc,yBAAyB,OAAO,UAAU,GAAG;AAE3F,QAAI,CAAC,qBAAqB,OAAO,cAAc,GAAG;AAChD;AAAA,QACE;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,KAAK,qBAAqB,GAAG;AACnC,UAAM,qBACJ,OAAO,SAAS,OAAO,SAAS,GAAG,EAAE,0BAA0B,GAAG,EAAE;AACtE,SAAK,qCAAqC;AAC1C,SAAK,GAAG,EAAE,oBAAoB,OAAO,cAAc,OAAO,kBAAkB,EAAE;AAAA,EAChF,WAAW,eAAe,OAAO,gBAAgB,OAAO,UAAU,GAAG;AACnE,SAAK;AAAA,qCAAwC;AAC7C;AAAA,MACE,4CAA4C,OAAO,cAAc,QAAQ,OAAO,UAAU;AAAA,IAC5F;AAAA,EACF;AACF;AAQA,SAAS,sBAAsB,KAAmB;AAChD,MAAI;AACF,UAAM,SAAS,YAAY,GAAG;AAC9B,QAAI,OAAO,OAAO;AAChB,WAAK,0DAA0D;AAAA,IACjE;AACA,QAAI,OAAO,eAAe,SAAS,GAAG;AACpC,WAAK,8BAA8B,OAAO,cAAc,CAAC;AAAA,IAC3D;AAAA,EACF,SAAS,OAAgB;AAGvB,QAAI,QAAQ,IAAI,OAAO;AACrB,cAAQ,MAAM,sCAAsC,KAAK;AAAA,IAC3D;AACA;AAAA,EACF;AACF;AAMA,eAAsB,MAAM,SAAsC;AAChE,QAAM,MAAM,QAAQ,IAAI;AAExB,SAAO,uBAAuB;AAE9B,QAAM,SAAS,MAAM,YAAY,GAAG;AAGpC,MAAI,CAAC,OAAO,YAAY;AACtB,SAAK,qDAAqD;AAC1D;AAAA,EACF;AAIA,wBAAsB,GAAG;AAGzB,WAAS,gBAAgB,IAAI,OAAO,UAAU,EAAE;AAChD,WAAS,kBAAkB,OAAO,iBAAiB,IAAI,OAAO,cAAc,KAAK,SAAS;AAG1F,MAAI,QAAQ,SAAS;AACnB,SAAK,uCAAuC;AAAA,EAC9C,OAAO;AACL,UAAM,mBAAmB,MAAM;AAAA,EACjC;AAEA,wBAAsB,QAAQ,GAAG;AACjC,QAAM,YAAY,oBAAoB,MAAM;AAE5C,MAAI,WAAW;AACb,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","names":[]}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
addInstalledPack,
|
|
4
4
|
isGitRepo,
|
|
5
5
|
isPackInstalled
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-UYTOLZT2.js";
|
|
7
7
|
import {
|
|
8
8
|
SAFEWORD_PEER_DEPENDENCIES
|
|
9
9
|
} from "./chunk-HSC7TELY.js";
|
|
@@ -392,4 +392,4 @@ export {
|
|
|
392
392
|
getEslintPeerMismatchWarning,
|
|
393
393
|
maybeAutoPatchOrNudge
|
|
394
394
|
};
|
|
395
|
-
//# sourceMappingURL=chunk-
|
|
395
|
+
//# sourceMappingURL=chunk-DIJ35ZXH.js.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// src/utils/version.ts
|
|
2
|
+
function compareVersions(a, b) {
|
|
3
|
+
const aParts = a.split(".").map(Number);
|
|
4
|
+
const bParts = b.split(".").map(Number);
|
|
5
|
+
for (let i = 0; i < 3; i++) {
|
|
6
|
+
const aValue = aParts[i] ?? 0;
|
|
7
|
+
const bValue = bParts[i] ?? 0;
|
|
8
|
+
if (aValue < bValue) return -1;
|
|
9
|
+
if (aValue > bValue) return 1;
|
|
10
|
+
}
|
|
11
|
+
return 0;
|
|
12
|
+
}
|
|
13
|
+
function isNewerVersion(current, latest) {
|
|
14
|
+
return compareVersions(current, latest) === -1;
|
|
15
|
+
}
|
|
16
|
+
var REGISTRY_TIMEOUT_MS = 3e3;
|
|
17
|
+
var VERSION_PATTERN = /^\d+\.\d+\.\d+$/;
|
|
18
|
+
function isComparableVersion(value) {
|
|
19
|
+
return typeof value === "string" && VERSION_PATTERN.test(value);
|
|
20
|
+
}
|
|
21
|
+
async function fetchRegistryLatestVersion(timeout = REGISTRY_TIMEOUT_MS) {
|
|
22
|
+
const controller = new AbortController();
|
|
23
|
+
const timeoutId = setTimeout(() => {
|
|
24
|
+
controller.abort();
|
|
25
|
+
}, timeout);
|
|
26
|
+
try {
|
|
27
|
+
const response = await fetch("https://registry.npmjs.org/safeword/latest", {
|
|
28
|
+
signal: controller.signal
|
|
29
|
+
});
|
|
30
|
+
if (!response.ok) return void 0;
|
|
31
|
+
const data = await response.json();
|
|
32
|
+
return isComparableVersion(data.version) ? data.version : void 0;
|
|
33
|
+
} catch {
|
|
34
|
+
return void 0;
|
|
35
|
+
} finally {
|
|
36
|
+
clearTimeout(timeoutId);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
compareVersions,
|
|
42
|
+
isNewerVersion,
|
|
43
|
+
isComparableVersion,
|
|
44
|
+
fetchRegistryLatestVersion
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=chunk-LRYWFRPD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/version.ts"],"sourcesContent":["/**\n * Version comparison utilities\n */\n\n/**\n * Compare two semver versions\n * @param a\n * @param b\n * @returns -1 if a < b, 0 if a == b, 1 if a > b\n */\nexport function compareVersions(a: string, b: string): -1 | 0 | 1 {\n const aParts = a.split('.').map(Number);\n const bParts = b.split('.').map(Number);\n\n for (let i = 0; i < 3; i++) {\n const aValue = aParts[i] ?? 0;\n const bValue = bParts[i] ?? 0;\n if (aValue < bValue) return -1;\n if (aValue > bValue) return 1;\n }\n\n return 0;\n}\n\n/**\n * Check if latest version is newer than current\n * @param current\n * @param latest\n */\nexport function isNewerVersion(current: string, latest: string): boolean {\n return compareVersions(current, latest) === -1;\n}\n\nconst REGISTRY_TIMEOUT_MS = 3000;\nconst VERSION_PATTERN = /^\\d+\\.\\d+\\.\\d+$/;\n\n/**\n * Type guard for a comparable semver string (e.g. `\"1.2.3\"`). Use before\n * passing a registry- or cache-sourced value into {@link compareVersions},\n * which silently produces `NaN` comparisons on malformed input.\n * @param value\n */\nexport function isComparableVersion(value: unknown): value is string {\n return typeof value === 'string' && VERSION_PATTERN.test(value);\n}\n\n/**\n * Fetch the latest published safeword version from the npm registry.\n * Returns undefined on network error, timeout, non-OK response, or an\n * unparseable/invalid version — callers treat undefined as \"unknown\".\n * @param timeout milliseconds before the request is aborted\n */\nexport async function fetchRegistryLatestVersion(\n timeout = REGISTRY_TIMEOUT_MS,\n): Promise<string | undefined> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => {\n controller.abort();\n }, timeout);\n\n try {\n const response = await fetch('https://registry.npmjs.org/safeword/latest', {\n signal: controller.signal,\n });\n if (!response.ok) return undefined;\n\n const data = (await response.json()) as { version?: unknown };\n return isComparableVersion(data.version) ? data.version : undefined;\n } catch {\n return undefined;\n } finally {\n clearTimeout(timeoutId);\n }\n}\n"],"mappings":";AAUO,SAAS,gBAAgB,GAAW,GAAuB;AAChE,QAAM,SAAS,EAAE,MAAM,GAAG,EAAE,IAAI,MAAM;AACtC,QAAM,SAAS,EAAE,MAAM,GAAG,EAAE,IAAI,MAAM;AAEtC,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,SAAS,OAAO,CAAC,KAAK;AAC5B,UAAM,SAAS,OAAO,CAAC,KAAK;AAC5B,QAAI,SAAS,OAAQ,QAAO;AAC5B,QAAI,SAAS,OAAQ,QAAO;AAAA,EAC9B;AAEA,SAAO;AACT;AAOO,SAAS,eAAe,SAAiB,QAAyB;AACvE,SAAO,gBAAgB,SAAS,MAAM,MAAM;AAC9C;AAEA,IAAM,sBAAsB;AAC5B,IAAM,kBAAkB;AAQjB,SAAS,oBAAoB,OAAiC;AACnE,SAAO,OAAO,UAAU,YAAY,gBAAgB,KAAK,KAAK;AAChE;AAQA,eAAsB,2BACpB,UAAU,qBACmB;AAC7B,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,YAAY,WAAW,MAAM;AACjC,eAAW,MAAM;AAAA,EACnB,GAAG,OAAO;AAEV,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,8CAA8C;AAAA,MACzE,QAAQ,WAAW;AAAA,IACrB,CAAC;AACD,QAAI,CAAC,SAAS,GAAI,QAAO;AAEzB,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,oBAAoB,KAAK,OAAO,IAAI,KAAK,UAAU;AAAA,EAC5D,QAAQ;AACN,WAAO;AAAA,EACT,UAAE;AACA,iBAAa,SAAS;AAAA,EACxB;AACF;","names":[]}
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
createProjectContext,
|
|
15
15
|
getMissingPacks,
|
|
16
16
|
reconcile
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-UYTOLZT2.js";
|
|
18
18
|
import {
|
|
19
19
|
findDanglingDependencies,
|
|
20
20
|
findTicketsInCycles,
|
|
@@ -723,4 +723,4 @@ export {
|
|
|
723
723
|
checkHealth,
|
|
724
724
|
reportHealthSummary
|
|
725
725
|
};
|
|
726
|
-
//# sourceMappingURL=chunk-
|
|
726
|
+
//# sourceMappingURL=chunk-Q7ALQS7T.js.map
|
|
@@ -1038,21 +1038,25 @@ function executeTextPatch(cwd, path, definition) {
|
|
|
1038
1038
|
throw new Error(`rerender text patch ${path} must use operation: 'append'`);
|
|
1039
1039
|
}
|
|
1040
1040
|
const fullPath = nodePath7.join(cwd, path);
|
|
1041
|
-
|
|
1041
|
+
const original = readFileSafe(fullPath) ?? "";
|
|
1042
|
+
const content = definition.supersedes === void 0 ? original : original.replace(definition.supersedes, "");
|
|
1042
1043
|
if (content.includes(definition.marker)) {
|
|
1043
|
-
|
|
1044
|
-
const stripped = stripRerenderBlock(content, definition);
|
|
1045
|
-
writeFile(fullPath, stripped + definition.content);
|
|
1046
|
-
return;
|
|
1047
|
-
}
|
|
1048
|
-
if (content.includes("\n\n---#")) {
|
|
1049
|
-
const healed = content.replaceAll("\n\n---#", "\n\n---\n\n#");
|
|
1050
|
-
writeFile(fullPath, healed);
|
|
1051
|
-
}
|
|
1044
|
+
healAlreadyPatchedFile(fullPath, original, content, definition);
|
|
1052
1045
|
return;
|
|
1053
1046
|
}
|
|
1054
|
-
|
|
1055
|
-
writeFile(fullPath,
|
|
1047
|
+
const patched = definition.operation === "prepend" ? definition.content + content : content + definition.content;
|
|
1048
|
+
writeFile(fullPath, patched);
|
|
1049
|
+
}
|
|
1050
|
+
function healAlreadyPatchedFile(fullPath, original, content, definition) {
|
|
1051
|
+
if (definition.rerender && !content.includes(definition.content)) {
|
|
1052
|
+
writeFile(fullPath, stripRerenderBlock(content, definition) + definition.content);
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
let healed = content;
|
|
1056
|
+
if (healed.includes("\n\n---#")) {
|
|
1057
|
+
healed = healed.replaceAll("\n\n---#", "\n\n---\n\n#");
|
|
1058
|
+
}
|
|
1059
|
+
if (healed !== original) writeFile(fullPath, healed);
|
|
1056
1060
|
}
|
|
1057
1061
|
function computeUnpatchedContent(content, definition) {
|
|
1058
1062
|
let unpatched = removeExactTextPatchContent(content, definition);
|
|
@@ -2149,9 +2153,12 @@ ${prettier.configEntry}
|
|
|
2149
2153
|
`;
|
|
2150
2154
|
}
|
|
2151
2155
|
var CURSOR_HOOKS = {
|
|
2152
|
-
// Observational: injects standing context
|
|
2153
|
-
// block the session from starting.
|
|
2154
|
-
sessionStart: [
|
|
2156
|
+
// Observational: injects standing context and checks for auto-upgrades.
|
|
2157
|
+
// Fail-open — neither hook may block the session from starting.
|
|
2158
|
+
sessionStart: [
|
|
2159
|
+
{ command: "bun ./.safeword/hooks/session-safeword-context.ts --agent=cursor" },
|
|
2160
|
+
{ command: "bun ./.safeword/hooks/session-cursor-auto-upgrade.ts" }
|
|
2161
|
+
],
|
|
2155
2162
|
// NOTE (F2TKR3): there is deliberately NO beforeSubmitPrompt gate. That hook
|
|
2156
2163
|
// fires at prompt-send time, where Cursor exposes only the prompt text — no tool
|
|
2157
2164
|
// name or file path — so it cannot tell "create test-definitions.md" from "write
|
|
@@ -2249,13 +2256,22 @@ var SETTINGS_HOOKS = {
|
|
|
2249
2256
|
"Bash",
|
|
2250
2257
|
"Bash(git commit*)",
|
|
2251
2258
|
`bun ${HOOKS_DIR}/pre-tool-architecture-stage.ts`
|
|
2252
|
-
)
|
|
2259
|
+
),
|
|
2260
|
+
// Warn (never block) before a checkout/switch to a branch behind its upstream,
|
|
2261
|
+
// so "catch up to main" doesn't silently serve stale content (#366). `if`
|
|
2262
|
+
// scopes the spawn to checkout/switch; the hook re-parses the target.
|
|
2263
|
+
matchedHookWithIf("Bash", "Bash(git checkout*)", `bun ${HOOKS_DIR}/pre-tool-stale-main.ts`),
|
|
2264
|
+
matchedHookWithIf("Bash", "Bash(git switch*)", `bun ${HOOKS_DIR}/pre-tool-stale-main.ts`)
|
|
2253
2265
|
],
|
|
2254
2266
|
PostToolUse: [
|
|
2255
2267
|
matchedHook(EDIT_TOOLS, `bun ${HOOKS_DIR}/post-tool-lint.ts`),
|
|
2256
2268
|
matchedHook(`${EDIT_TOOLS}|Bash`, `bun ${HOOKS_DIR}/post-tool-quality.ts`),
|
|
2257
2269
|
matchedHook(EDIT_TOOLS, `bun ${HOOKS_DIR}/post-tool-bypass-warn.ts`),
|
|
2258
|
-
matchedHook(EDIT_TOOLS, `bun ${HOOKS_DIR}/post-tool-sync-learnings.ts`)
|
|
2270
|
+
matchedHook(EDIT_TOOLS, `bun ${HOOKS_DIR}/post-tool-sync-learnings.ts`),
|
|
2271
|
+
// Stamp the dependency fingerprint after a successful install so the
|
|
2272
|
+
// recommended recovery command clears the readiness block (#380). Fast-exits
|
|
2273
|
+
// on non-install Bash commands.
|
|
2274
|
+
matchedHook("Bash", `bun ${HOOKS_DIR}/post-tool-dependency-readiness.ts`)
|
|
2259
2275
|
],
|
|
2260
2276
|
SessionEnd: [hook(`bun ${HOOKS_DIR}/session-cleanup-quality.ts`)]
|
|
2261
2277
|
};
|
|
@@ -2644,11 +2660,13 @@ var CLAUDE_MD_IMPORT_BLOCK = `@./.safeword/SAFEWORD.md
|
|
|
2644
2660
|
function isHookEntry(h) {
|
|
2645
2661
|
return typeof h === "object" && h !== null && "hooks" in h && Array.isArray(h.hooks);
|
|
2646
2662
|
}
|
|
2663
|
+
function isCursorHookEntry(h) {
|
|
2664
|
+
return typeof h === "object" && h !== null && typeof h.command === "string";
|
|
2665
|
+
}
|
|
2647
2666
|
function isSafewordHook(h) {
|
|
2667
|
+
if (isCursorHookEntry(h)) return h.command.includes(".safeword");
|
|
2648
2668
|
if (!isHookEntry(h)) return false;
|
|
2649
|
-
return h.hooks.some(
|
|
2650
|
-
(command) => typeof command.command === "string" && command.command.includes(".safeword")
|
|
2651
|
-
);
|
|
2669
|
+
return h.hooks.some((command) => command.command.includes(".safeword"));
|
|
2652
2670
|
}
|
|
2653
2671
|
function filterOutSafewordHooks(hooks) {
|
|
2654
2672
|
return hooks.filter((h) => !isSafewordHook(h));
|
|
@@ -2687,6 +2705,16 @@ var CODEX_SESSION_START_HOOK_PATCH = `
|
|
|
2687
2705
|
[[hooks.SessionStart]]
|
|
2688
2706
|
matcher = ""
|
|
2689
2707
|
|
|
2708
|
+
[[hooks.SessionStart.hooks]]
|
|
2709
|
+
type = "command"
|
|
2710
|
+
command = 'bun "$(git rev-parse --show-toplevel)/.safeword/hooks/session-codex-start.ts"'
|
|
2711
|
+
timeout = 120
|
|
2712
|
+
statusMessage = "Checking safeword updates and loading standing instructions"
|
|
2713
|
+
`;
|
|
2714
|
+
var CODEX_LEGACY_CONTEXT_SESSION_START_HOOK_PATCH = `
|
|
2715
|
+
[[hooks.SessionStart]]
|
|
2716
|
+
matcher = ""
|
|
2717
|
+
|
|
2690
2718
|
[[hooks.SessionStart.hooks]]
|
|
2691
2719
|
type = "command"
|
|
2692
2720
|
command = 'bun "$(git rev-parse --show-toplevel)/.safeword/hooks/session-safeword-context.ts" --agent=codex'
|
|
@@ -2758,6 +2786,7 @@ var CODEX_SKILL_OWNED_FILES = Object.fromEntries(
|
|
|
2758
2786
|
);
|
|
2759
2787
|
var NAMESPACE_TRANSIENT_BASENAMES = [
|
|
2760
2788
|
"quality-state*.json",
|
|
2789
|
+
"cursor-run-identity.json",
|
|
2761
2790
|
"failure-counts.json",
|
|
2762
2791
|
"skill-invocations.log",
|
|
2763
2792
|
"re-entry.md",
|
|
@@ -2970,7 +2999,14 @@ var SAFEWORD_SCHEMA = {
|
|
|
2970
2999
|
},
|
|
2971
3000
|
// Hooks shared library - TypeScript with Bun runtime
|
|
2972
3001
|
".safeword/hooks/lib/active-ticket.ts": { template: "hooks/lib/active-ticket.ts" },
|
|
3002
|
+
".safeword/hooks/lib/architecture-staged-scope.ts": {
|
|
3003
|
+
template: "hooks/lib/architecture-staged-scope.ts"
|
|
3004
|
+
},
|
|
3005
|
+
".safeword/hooks/lib/branch-staleness.ts": { template: "hooks/lib/branch-staleness.ts" },
|
|
2973
3006
|
".safeword/hooks/lib/blocked-on-gate.ts": { template: "hooks/lib/blocked-on-gate.ts" },
|
|
3007
|
+
".safeword/hooks/lib/cursor-run-identity.ts": {
|
|
3008
|
+
template: "hooks/lib/cursor-run-identity.ts"
|
|
3009
|
+
},
|
|
2974
3010
|
".safeword/hooks/lib/git-operation.ts": { template: "hooks/lib/git-operation.ts" },
|
|
2975
3011
|
".safeword/hooks/lib/re-entry.ts": { template: "hooks/lib/re-entry.ts" },
|
|
2976
3012
|
".safeword/hooks/lib/hierarchy.ts": { template: "hooks/lib/hierarchy.ts" },
|
|
@@ -2999,9 +3035,13 @@ var SAFEWORD_SCHEMA = {
|
|
|
2999
3035
|
},
|
|
3000
3036
|
".safeword/hooks/lib/review-trigger.ts": { template: "hooks/lib/review-trigger.ts" },
|
|
3001
3037
|
".safeword/hooks/lib/dogfood.ts": { template: "hooks/lib/dogfood.ts" },
|
|
3038
|
+
".safeword/hooks/lib/ledger-git.ts": { template: "hooks/lib/ledger-git.ts" },
|
|
3002
3039
|
".safeword/hooks/lib/ledger-validation.ts": { template: "hooks/lib/ledger-validation.ts" },
|
|
3003
3040
|
".safeword/hooks/lib/scenario-format.ts": { template: "hooks/lib/scenario-format.ts" },
|
|
3004
3041
|
".safeword/hooks/lib/test-runner.ts": { template: "hooks/lib/test-runner.ts" },
|
|
3042
|
+
".safeword/hooks/lib/auto-upgrade.ts": { template: "hooks/lib/auto-upgrade.ts" },
|
|
3043
|
+
".safeword/hooks/lib/auto-upgrade-lock.ts": { template: "hooks/lib/auto-upgrade-lock.ts" },
|
|
3044
|
+
".safeword/hooks/lib/safeword-context.ts": { template: "hooks/lib/safeword-context.ts" },
|
|
3005
3045
|
".safeword/hooks/lib/update-cache.ts": { template: "hooks/lib/update-cache.ts" },
|
|
3006
3046
|
".safeword/hooks/lib/version.ts": { template: "hooks/lib/version.ts" },
|
|
3007
3047
|
".safeword/hooks/lib/learning-verification-stamps.ts": {
|
|
@@ -3017,6 +3057,12 @@ var SAFEWORD_SCHEMA = {
|
|
|
3017
3057
|
".safeword/hooks/session-safeword-context.ts": {
|
|
3018
3058
|
template: "hooks/session-safeword-context.ts"
|
|
3019
3059
|
},
|
|
3060
|
+
".safeword/hooks/session-codex-start.ts": {
|
|
3061
|
+
template: "hooks/session-codex-start.ts"
|
|
3062
|
+
},
|
|
3063
|
+
".safeword/hooks/session-cursor-auto-upgrade.ts": {
|
|
3064
|
+
template: "hooks/session-cursor-auto-upgrade.ts"
|
|
3065
|
+
},
|
|
3020
3066
|
".safeword/hooks/session-dependency-readiness.ts": {
|
|
3021
3067
|
template: "hooks/session-dependency-readiness.ts"
|
|
3022
3068
|
},
|
|
@@ -3056,6 +3102,9 @@ var SAFEWORD_SCHEMA = {
|
|
|
3056
3102
|
".safeword/hooks/pre-tool-architecture-stage.ts": {
|
|
3057
3103
|
template: "hooks/pre-tool-architecture-stage.ts"
|
|
3058
3104
|
},
|
|
3105
|
+
".safeword/hooks/pre-tool-stale-main.ts": {
|
|
3106
|
+
template: "hooks/pre-tool-stale-main.ts"
|
|
3107
|
+
},
|
|
3059
3108
|
".safeword/hooks/codex/pre-tool-quality.ts": {
|
|
3060
3109
|
template: "hooks/codex/pre-tool-quality.ts"
|
|
3061
3110
|
},
|
|
@@ -3071,6 +3120,9 @@ var SAFEWORD_SCHEMA = {
|
|
|
3071
3120
|
".safeword/hooks/pre-tool-dependency-readiness.ts": {
|
|
3072
3121
|
template: "hooks/pre-tool-dependency-readiness.ts"
|
|
3073
3122
|
},
|
|
3123
|
+
".safeword/hooks/post-tool-dependency-readiness.ts": {
|
|
3124
|
+
template: "hooks/post-tool-dependency-readiness.ts"
|
|
3125
|
+
},
|
|
3074
3126
|
".safeword/hooks/pre-tool-git-bare-fix.sh": {
|
|
3075
3127
|
template: "hooks/pre-tool-git-bare-fix.sh"
|
|
3076
3128
|
},
|
|
@@ -3113,12 +3165,18 @@ var SAFEWORD_SCHEMA = {
|
|
|
3113
3165
|
".safeword/guides/llm-writing-guide.md": {
|
|
3114
3166
|
template: "guides/llm-writing-guide.md"
|
|
3115
3167
|
},
|
|
3168
|
+
".safeword/guides/llm-evals-guide.md": {
|
|
3169
|
+
template: "guides/llm-evals-guide.md"
|
|
3170
|
+
},
|
|
3116
3171
|
".safeword/guides/planning-guide.md": {
|
|
3117
3172
|
template: "guides/planning-guide.md"
|
|
3118
3173
|
},
|
|
3119
3174
|
".safeword/guides/testing-guide.md": {
|
|
3120
3175
|
template: "guides/testing-guide.md"
|
|
3121
3176
|
},
|
|
3177
|
+
".safeword/guides/verification-lanes-guide.md": {
|
|
3178
|
+
template: "guides/verification-lanes-guide.md"
|
|
3179
|
+
},
|
|
3122
3180
|
".safeword/guides/zombie-process-cleanup.md": {
|
|
3123
3181
|
template: "guides/zombie-process-cleanup.md"
|
|
3124
3182
|
},
|
|
@@ -3419,23 +3477,25 @@ var SAFEWORD_SCHEMA = {
|
|
|
3419
3477
|
],
|
|
3420
3478
|
removeFileIfEmpty: true,
|
|
3421
3479
|
merge: (existing) => {
|
|
3422
|
-
const
|
|
3480
|
+
const existingHooks = existing.hooks ?? {};
|
|
3481
|
+
const hooks = { ...existingHooks };
|
|
3482
|
+
for (const [event, newHooks] of Object.entries(CURSOR_HOOKS)) {
|
|
3483
|
+
const eventHooks = hooks[event] ?? [];
|
|
3484
|
+
const nonSafewordHooks = filterOutSafewordHooks(eventHooks);
|
|
3485
|
+
hooks[event] = [...nonSafewordHooks, ...newHooks];
|
|
3486
|
+
}
|
|
3423
3487
|
return {
|
|
3424
3488
|
...existing,
|
|
3425
3489
|
version: 1,
|
|
3426
3490
|
// Required by Cursor
|
|
3427
|
-
hooks
|
|
3428
|
-
...hooks,
|
|
3429
|
-
...CURSOR_HOOKS
|
|
3430
|
-
}
|
|
3491
|
+
hooks
|
|
3431
3492
|
};
|
|
3432
3493
|
},
|
|
3433
3494
|
unmerge: (existing) => {
|
|
3434
3495
|
const result = { ...existing };
|
|
3435
3496
|
const existingHooks = existing.hooks ?? {};
|
|
3436
|
-
const safewordHookNames = new Set(Object.keys(CURSOR_HOOKS));
|
|
3437
3497
|
const hooks = Object.fromEntries(
|
|
3438
|
-
Object.entries(existingHooks).
|
|
3498
|
+
Object.entries(existingHooks).map(([name, eventHooks]) => [name, filterOutSafewordHooks(eventHooks)]).filter(([, eventHooks]) => eventHooks.length > 0)
|
|
3439
3499
|
);
|
|
3440
3500
|
if (!assignOrPrune(result, "hooks", hooks)) {
|
|
3441
3501
|
delete result.version;
|
|
@@ -3494,9 +3554,35 @@ ${managedPrettierPaths(ctx).join("\n")}
|
|
|
3494
3554
|
"# Safeword Codex project configuration.",
|
|
3495
3555
|
".safeword/hooks/codex/pre-tool-quality.ts"
|
|
3496
3556
|
],
|
|
3497
|
-
unpatchContent: [
|
|
3557
|
+
unpatchContent: [
|
|
3558
|
+
CODEX_SESSION_START_HOOK_PATCH,
|
|
3559
|
+
CODEX_LEGACY_CONTEXT_SESSION_START_HOOK_PATCH,
|
|
3560
|
+
CODEX_PRE_TOOL_QUALITY_HOOK_PATCH
|
|
3561
|
+
],
|
|
3498
3562
|
removeFileIfContentEquals: [CODEX_CONFIG_SCAFFOLD_WITHOUT_HOOKS]
|
|
3499
3563
|
},
|
|
3564
|
+
// Migrate existing installs (auto-upgrade-codex follow-up to #433): swap the
|
|
3565
|
+
// legacy context-only SessionStart hook for the auto-upgrade dispatcher.
|
|
3566
|
+
// Codex runs same-event hooks concurrently with no ordering, so this must
|
|
3567
|
+
// REPLACE the legacy hook (appending a second SessionStart hook would
|
|
3568
|
+
// double-emit context) — hence `supersedes`. managedFiles is
|
|
3569
|
+
// create-if-missing, so a fresh install gets the dispatcher from the
|
|
3570
|
+
// template while every EXISTING config is skipped by managedFiles and
|
|
3571
|
+
// migrated here. Idempotent: skips when the dispatcher marker is already
|
|
3572
|
+
// present, and the strip no-ops when the legacy block is absent. Guarded to
|
|
3573
|
+
// safeword scaffolds; a user-modified legacy block won't byte-match and is
|
|
3574
|
+
// preserved. Uninstall cleanup is owned by the primary patch's
|
|
3575
|
+
// unpatchContent above.
|
|
3576
|
+
{
|
|
3577
|
+
operation: "append",
|
|
3578
|
+
content: CODEX_SESSION_START_HOOK_PATCH,
|
|
3579
|
+
marker: ".safeword/hooks/session-codex-start.ts",
|
|
3580
|
+
supersedes: CODEX_LEGACY_CONTEXT_SESSION_START_HOOK_PATCH,
|
|
3581
|
+
applyWhenContentIncludes: [
|
|
3582
|
+
"# Safeword Codex project configuration.",
|
|
3583
|
+
".safeword/hooks/codex/pre-tool-quality.ts"
|
|
3584
|
+
]
|
|
3585
|
+
},
|
|
3500
3586
|
// MCP-server retrofit (#269): add-if-missing parity with .mcp.json /
|
|
3501
3587
|
// .cursor/mcp.json. Marker is the context7 table header, so an existing
|
|
3502
3588
|
// (safeword- or user-authored) [mcp_servers.context7] suppresses the
|
|
@@ -3846,4 +3932,4 @@ export {
|
|
|
3846
3932
|
untrackIgnoredFiles,
|
|
3847
3933
|
createProjectContext
|
|
3848
3934
|
};
|
|
3849
|
-
//# sourceMappingURL=chunk-
|
|
3935
|
+
//# sourceMappingURL=chunk-UYTOLZT2.js.map
|