safeword 0.79.6 → 0.80.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/codex-plugin/.codex-plugin/plugin.json +1 -1
- package/codex-plugin/hooks.json +5 -5
- package/codex-plugin/skills/audit/SKILL.md +5 -5
- package/codex-plugin/skills/bdd/SKILL.md +1 -1
- package/codex-plugin/skills/bdd/references/PLAN_IMPLEMENTATION.md +2 -2
- package/codex-plugin/skills/bdd/references/TDD.md +1 -1
- package/codex-plugin/skills/explain/SKILL.md +1 -1
- package/codex-plugin/skills/quality-review/SKILL.md +2 -2
- package/codex-plugin/skills/retro-filer/SKILL.md +2 -2
- package/codex-plugin/skills/review-spec/SKILL.md +2 -2
- package/codex-plugin/skills/self-review/SKILL.md +1 -1
- package/dist/{architecture-PKFEYKF5.js → architecture-MQGZ5IMW.js} +7 -7
- package/dist/{architecture-document-TJCHWOJK.js → architecture-document-VZ4DSQFG.js} +2 -2
- package/dist/{architecture-monorepo-AO32KPUV.js → architecture-monorepo-R4XJEUFG.js} +2 -2
- package/dist/{boundary-N5KK4A6Z.js → boundary-E5NPK5DT.js} +2 -2
- package/dist/{chunk-23BNG6FE.js → chunk-4L4NYOC3.js} +4 -4
- package/dist/chunk-4L4NYOC3.js.map +1 -0
- package/dist/chunk-4VORK37W.js +12 -0
- package/dist/chunk-4VORK37W.js.map +1 -0
- package/dist/chunk-556WOVCD.js +72 -0
- package/dist/chunk-556WOVCD.js.map +1 -0
- package/dist/{chunk-RKQDXUWG.js → chunk-56ZUJOIZ.js} +10 -10
- package/dist/{chunk-2TM5WZQC.js → chunk-6Z2DOROQ.js} +7 -7
- package/dist/chunk-A3QLTTGK.js +69 -0
- package/dist/chunk-A3QLTTGK.js.map +1 -0
- package/dist/chunk-BUJI55OX.js +136 -0
- package/dist/chunk-BUJI55OX.js.map +1 -0
- package/dist/chunk-FFITFQ2V.js +1007 -0
- package/dist/chunk-FFITFQ2V.js.map +1 -0
- package/dist/{chunk-WGZWXTTK.js → chunk-HCDPIYP4.js} +40 -98
- package/dist/{chunk-WGZWXTTK.js.map → chunk-HCDPIYP4.js.map} +1 -1
- package/dist/{chunk-G757F3EU.js → chunk-HWKYTJJS.js} +4 -4
- package/dist/{chunk-C5CLFFHY.js → chunk-JEFU3MAX.js} +8 -8
- package/dist/{chunk-HJH6WBOE.js → chunk-KKA5R2J4.js} +58 -30
- package/dist/chunk-KKA5R2J4.js.map +1 -0
- package/dist/{codex-hook-GKRJPUNY.js → chunk-KRJQOJ4M.js} +38 -19
- package/dist/chunk-KRJQOJ4M.js.map +1 -0
- package/dist/{chunk-O3GZ77F7.js → chunk-OJYTKG6J.js} +2 -2
- package/dist/{chunk-R3PPYLUW.js → chunk-PQZHKEVY.js} +418 -123
- package/dist/chunk-PQZHKEVY.js.map +1 -0
- package/dist/{chunk-ZESNDCRI.js → chunk-RNV4W4AV.js} +7 -7
- package/dist/chunk-U5UEBBFW.js +291 -0
- package/dist/chunk-U5UEBBFW.js.map +1 -0
- package/dist/{chunk-L3SMWGOE.js → chunk-UINRVLLX.js} +2 -2
- package/dist/{chunk-WPHE4CVS.js → chunk-VBJXIMMS.js} +2 -2
- package/dist/{chunk-WPHE4CVS.js.map → chunk-VBJXIMMS.js.map} +1 -1
- package/dist/{chunk-XAMYX76Z.js → chunk-WKST3I6W.js} +2 -2
- package/dist/{chunk-VIPT6ILW.js → chunk-YM24LWXW.js} +25 -242
- package/dist/chunk-YM24LWXW.js.map +1 -0
- package/dist/{cleanup-6L62LCDG.js → cleanup-R3P6CAVP.js} +5 -5
- package/dist/{cleanup-command-2WKL5BRW.js → cleanup-command-IHK2F2FQ.js} +12 -12
- package/dist/cli.js +115 -87
- package/dist/cli.js.map +1 -1
- package/dist/{clients-XVKTYXEE.js → clients-RERVT25J.js} +2 -2
- package/dist/{codex-bootstrap-7SSVALDH.js → codex-bootstrap-7II6YGNM.js} +24 -22
- package/dist/{codex-bootstrap-7SSVALDH.js.map → codex-bootstrap-7II6YGNM.js.map} +1 -1
- package/dist/codex-hook-UHLOCGHO.js +25 -0
- package/dist/{codify-LNYGFHNV.js → codify-GOGG2BZ4.js} +2 -2
- package/dist/{commands-3IU6AYOC.js → commands-AO57I5W3.js} +161 -197
- package/dist/commands-AO57I5W3.js.map +1 -0
- package/dist/{config-QZNMTUOI.js → config-VPISJRFL.js} +2 -2
- package/dist/{configured-paths-FUIGDAVZ.js → configured-paths-G74S7UH4.js} +2 -2
- package/dist/conformance-T2HBB4BQ.js +597 -0
- package/dist/conformance-T2HBB4BQ.js.map +1 -0
- package/dist/{contract-7QIHCA4W.js → contract-OHV6QEIR.js} +2 -2
- package/dist/{coordinator-TAAXZQMR.js → coordinator-RTYQ4TCP.js} +2 -2
- package/dist/{corpus-MHWRUDM4.js → corpus-YW77W5KC.js} +2 -2
- package/dist/cursor-NJJPK7JC.js +29 -0
- package/dist/{doctor-SJS2BPSG.js → doctor-KK3JW66E.js} +19 -16
- package/dist/{doctor-SJS2BPSG.js.map → doctor-KK3JW66E.js.map} +1 -1
- package/dist/{drain-retro-spool-XI4CVY7S.js → drain-retro-spool-65NOZ6TR.js} +2 -2
- package/dist/{feature-directories-6MPL5ZRF.js → feature-directories-YYRA3LLW.js} +2 -2
- package/dist/{finalization-PY7G42IH.js → finalization-T7DMHVKB.js} +4 -4
- package/dist/{gh-cli-HVKU4I6Y.js → gh-cli-F4FBKDUP.js} +2 -2
- package/dist/{github-rest-54VTCFIW.js → github-rest-I3EJQ3WV.js} +2 -2
- package/dist/index.js +3 -3
- package/dist/{job-4M25USS7.js → job-O3CM5A7K.js} +2 -2
- package/dist/{learning-sync-7VVFZ3OD.js → learning-sync-KR6GVLLW.js} +2 -2
- package/dist/{legacy-global-guidance-V4JCERWT.js → legacy-global-guidance-MKILI7T6.js} +2 -2
- package/dist/{lint-gherkin-ETTY3MXC.js → lint-gherkin-5WGF7TV4.js} +2 -2
- package/dist/{namespace-root-43RV2IFR.js → namespace-root-KG7GUS7S.js} +2 -2
- package/dist/opencode/dispatcher.d.ts +2 -0
- package/dist/opencode/dispatcher.js +13854 -0
- package/dist/{operations-IQIDWWPZ.js → operations-GSE7XLKX.js} +15 -13
- package/dist/{output-QMPSPK63.js → output-IVLRBG4Q.js} +2 -2
- package/dist/{packet-BXY5QGOH.js → packet-GLBNGKBV.js} +2 -2
- package/dist/presets/typescript/index.js +3 -3
- package/dist/profile-537DW45F.js +25 -0
- package/dist/{profile-ZDGVS7K4.js → profile-KIKCCKJ3.js} +6 -6
- package/dist/{prompt-PIJUM242.js → prompt-BPXJQZAT.js} +2 -2
- package/dist/{public-retros-LUZPH4SH.js → public-retros-IWWZ4AC5.js} +3 -3
- package/dist/{remove-MU6LEEDE.js → remove-ETC5BFOL.js} +9 -8
- package/dist/{retro-IM2FTTHU.js → retro-OOFVBHMC.js} +12 -12
- package/dist/{retro-draft-spool-3JN6FWYY.js → retro-draft-spool-NEWUXGIR.js} +2 -2
- package/dist/{retro-drain-AR3LXXVW.js → retro-drain-NOFN26TH.js} +3 -3
- package/dist/{retro-extract-QHOKLHJZ.js → retro-extract-5YWNK4AA.js} +2 -2
- package/dist/{review-knowledge-PQTVPBNO.js → review-knowledge-SELT4FXY.js} +2 -2
- package/dist/{review-pr-TCYNHFBV.js → review-pr-XSTA42PS.js} +2 -2
- package/dist/{review-pr-publication-EBC4H5PX.js → review-pr-publication-N2UZGCPS.js} +2 -2
- package/dist/{run-3PYMWPO3.js → run-JJKOMASC.js} +5 -5
- package/dist/schema-KIIFQO6J.js +23 -0
- package/dist/{self-report-VVKFLU2M.js → self-report-GI7FWSKM.js} +2 -2
- package/dist/{status-KXYK4CCR.js → status-EXHLQNDM.js} +8 -8
- package/dist/{status-GOFUSYCW.js → status-QNRTXIPM.js} +17 -14
- package/dist/{sync-config-7ZHN42PO.js → sync-config-JY6APUYB.js} +2 -2
- package/dist/sync-config-JY6APUYB.js.map +1 -0
- package/dist/{sync-tracker-EZVDE2SK.js → sync-tracker-OO42E5YB.js} +17 -17
- package/dist/{test-execution-HH7YOBMA.js → test-execution-BZC73YP4.js} +3 -3
- package/dist/{test-plan-3IPYL6YK.js → test-plan-PF3BEVP7.js} +3 -3
- package/dist/{ticket-new-2PTLPHXK.js → ticket-new-XFEXFJ6R.js} +7 -7
- package/dist/{ticket-sync-VSEH3NGG.js → ticket-sync-EP6AGXSK.js} +2 -2
- package/dist/ticket-sync-EP6AGXSK.js.map +1 -0
- package/dist/{tracker-map-S5BIOIGC.js → tracker-map-XXUR47MO.js} +2 -2
- package/dist/tracker-map-XXUR47MO.js.map +1 -0
- package/dist/{tracker-sync-YU7IKTNB.js → tracker-sync-JXU52I2J.js} +2 -2
- package/dist/tracker-sync-JXU52I2J.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-23BNG6FE.js.map +0 -1
- package/dist/chunk-HJH6WBOE.js.map +0 -1
- package/dist/chunk-R3PPYLUW.js.map +0 -1
- package/dist/chunk-UAFM77KW.js +0 -11
- package/dist/chunk-UAFM77KW.js.map +0 -1
- package/dist/chunk-VIPT6ILW.js.map +0 -1
- package/dist/codex-hook-GKRJPUNY.js.map +0 -1
- package/dist/commands-3IU6AYOC.js.map +0 -1
- /package/dist/{architecture-PKFEYKF5.js.map → architecture-MQGZ5IMW.js.map} +0 -0
- /package/dist/{architecture-document-TJCHWOJK.js.map → architecture-document-VZ4DSQFG.js.map} +0 -0
- /package/dist/{architecture-monorepo-AO32KPUV.js.map → architecture-monorepo-R4XJEUFG.js.map} +0 -0
- /package/dist/{boundary-N5KK4A6Z.js.map → boundary-E5NPK5DT.js.map} +0 -0
- /package/dist/{chunk-RKQDXUWG.js.map → chunk-56ZUJOIZ.js.map} +0 -0
- /package/dist/{chunk-2TM5WZQC.js.map → chunk-6Z2DOROQ.js.map} +0 -0
- /package/dist/{chunk-G757F3EU.js.map → chunk-HWKYTJJS.js.map} +0 -0
- /package/dist/{chunk-C5CLFFHY.js.map → chunk-JEFU3MAX.js.map} +0 -0
- /package/dist/{chunk-O3GZ77F7.js.map → chunk-OJYTKG6J.js.map} +0 -0
- /package/dist/{chunk-ZESNDCRI.js.map → chunk-RNV4W4AV.js.map} +0 -0
- /package/dist/{chunk-L3SMWGOE.js.map → chunk-UINRVLLX.js.map} +0 -0
- /package/dist/{chunk-XAMYX76Z.js.map → chunk-WKST3I6W.js.map} +0 -0
- /package/dist/{cleanup-6L62LCDG.js.map → cleanup-R3P6CAVP.js.map} +0 -0
- /package/dist/{cleanup-command-2WKL5BRW.js.map → cleanup-command-IHK2F2FQ.js.map} +0 -0
- /package/dist/{clients-XVKTYXEE.js.map → clients-RERVT25J.js.map} +0 -0
- /package/dist/{config-QZNMTUOI.js.map → codex-hook-UHLOCGHO.js.map} +0 -0
- /package/dist/{codify-LNYGFHNV.js.map → codify-GOGG2BZ4.js.map} +0 -0
- /package/dist/{configured-paths-FUIGDAVZ.js.map → config-VPISJRFL.js.map} +0 -0
- /package/dist/{contract-7QIHCA4W.js.map → configured-paths-G74S7UH4.js.map} +0 -0
- /package/dist/{corpus-MHWRUDM4.js.map → contract-OHV6QEIR.js.map} +0 -0
- /package/dist/{coordinator-TAAXZQMR.js.map → coordinator-RTYQ4TCP.js.map} +0 -0
- /package/dist/{finalization-PY7G42IH.js.map → corpus-YW77W5KC.js.map} +0 -0
- /package/dist/{gh-cli-HVKU4I6Y.js.map → cursor-NJJPK7JC.js.map} +0 -0
- /package/dist/{drain-retro-spool-XI4CVY7S.js.map → drain-retro-spool-65NOZ6TR.js.map} +0 -0
- /package/dist/{feature-directories-6MPL5ZRF.js.map → feature-directories-YYRA3LLW.js.map} +0 -0
- /package/dist/{legacy-global-guidance-V4JCERWT.js.map → finalization-T7DMHVKB.js.map} +0 -0
- /package/dist/{operations-IQIDWWPZ.js.map → gh-cli-F4FBKDUP.js.map} +0 -0
- /package/dist/{github-rest-54VTCFIW.js.map → github-rest-I3EJQ3WV.js.map} +0 -0
- /package/dist/{job-4M25USS7.js.map → job-O3CM5A7K.js.map} +0 -0
- /package/dist/{learning-sync-7VVFZ3OD.js.map → learning-sync-KR6GVLLW.js.map} +0 -0
- /package/dist/{output-QMPSPK63.js.map → legacy-global-guidance-MKILI7T6.js.map} +0 -0
- /package/dist/{lint-gherkin-ETTY3MXC.js.map → lint-gherkin-5WGF7TV4.js.map} +0 -0
- /package/dist/{namespace-root-43RV2IFR.js.map → namespace-root-KG7GUS7S.js.map} +0 -0
- /package/dist/{packet-BXY5QGOH.js.map → operations-GSE7XLKX.js.map} +0 -0
- /package/dist/{profile-ZDGVS7K4.js.map → output-IVLRBG4Q.js.map} +0 -0
- /package/dist/{prompt-PIJUM242.js.map → packet-GLBNGKBV.js.map} +0 -0
- /package/dist/{remove-MU6LEEDE.js.map → profile-537DW45F.js.map} +0 -0
- /package/dist/{retro-draft-spool-3JN6FWYY.js.map → profile-KIKCCKJ3.js.map} +0 -0
- /package/dist/{retro-extract-QHOKLHJZ.js.map → prompt-BPXJQZAT.js.map} +0 -0
- /package/dist/{public-retros-LUZPH4SH.js.map → public-retros-IWWZ4AC5.js.map} +0 -0
- /package/dist/{self-report-VVKFLU2M.js.map → remove-ETC5BFOL.js.map} +0 -0
- /package/dist/{retro-IM2FTTHU.js.map → retro-OOFVBHMC.js.map} +0 -0
- /package/dist/{status-GOFUSYCW.js.map → retro-draft-spool-NEWUXGIR.js.map} +0 -0
- /package/dist/{retro-drain-AR3LXXVW.js.map → retro-drain-NOFN26TH.js.map} +0 -0
- /package/dist/{status-KXYK4CCR.js.map → retro-extract-5YWNK4AA.js.map} +0 -0
- /package/dist/{review-knowledge-PQTVPBNO.js.map → review-knowledge-SELT4FXY.js.map} +0 -0
- /package/dist/{review-pr-TCYNHFBV.js.map → review-pr-XSTA42PS.js.map} +0 -0
- /package/dist/{review-pr-publication-EBC4H5PX.js.map → review-pr-publication-N2UZGCPS.js.map} +0 -0
- /package/dist/{run-3PYMWPO3.js.map → run-JJKOMASC.js.map} +0 -0
- /package/dist/{sync-config-7ZHN42PO.js.map → schema-KIIFQO6J.js.map} +0 -0
- /package/dist/{ticket-sync-VSEH3NGG.js.map → self-report-GI7FWSKM.js.map} +0 -0
- /package/dist/{tracker-map-S5BIOIGC.js.map → status-EXHLQNDM.js.map} +0 -0
- /package/dist/{tracker-sync-YU7IKTNB.js.map → status-QNRTXIPM.js.map} +0 -0
- /package/dist/{sync-tracker-EZVDE2SK.js.map → sync-tracker-OO42E5YB.js.map} +0 -0
- /package/dist/{test-execution-HH7YOBMA.js.map → test-execution-BZC73YP4.js.map} +0 -0
- /package/dist/{test-plan-3IPYL6YK.js.map → test-plan-PF3BEVP7.js.map} +0 -0
- /package/dist/{ticket-new-2PTLPHXK.js.map → ticket-new-XFEXFJ6R.js.map} +0 -0
package/codex-plugin/hooks.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"hooks": [
|
|
7
7
|
{
|
|
8
8
|
"type": "command",
|
|
9
|
-
"command": "bunx --bun safeword@0.
|
|
9
|
+
"command": "bunx --bun safeword@0.80.0 hook codex session-start --plugin-hook",
|
|
10
10
|
"timeout": 120,
|
|
11
11
|
"statusMessage": "Loading Safeword standing instructions"
|
|
12
12
|
}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"hooks": [
|
|
20
20
|
{
|
|
21
21
|
"type": "command",
|
|
22
|
-
"command": "bunx --bun safeword@0.
|
|
22
|
+
"command": "bunx --bun safeword@0.80.0 hook codex pre-tool-use --plugin-hook",
|
|
23
23
|
"timeout": 30,
|
|
24
24
|
"statusMessage": "Checking Safeword edit gates"
|
|
25
25
|
}
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"hooks": [
|
|
33
33
|
{
|
|
34
34
|
"type": "command",
|
|
35
|
-
"command": "bunx --bun safeword@0.
|
|
35
|
+
"command": "bunx --bun safeword@0.80.0 hook codex post-tool-use --plugin-hook",
|
|
36
36
|
"timeout": 30,
|
|
37
37
|
"statusMessage": "Surfacing Safeword post-tool context"
|
|
38
38
|
}
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"hooks": [
|
|
46
46
|
{
|
|
47
47
|
"type": "command",
|
|
48
|
-
"command": "bunx --bun safeword@0.
|
|
48
|
+
"command": "bunx --bun safeword@0.80.0 hook codex user-prompt-submit --plugin-hook",
|
|
49
49
|
"timeout": 30,
|
|
50
50
|
"statusMessage": "Checking queued Safeword prompt context"
|
|
51
51
|
}
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"hooks": [
|
|
59
59
|
{
|
|
60
60
|
"type": "command",
|
|
61
|
-
"command": "bunx --bun safeword@0.
|
|
61
|
+
"command": "bunx --bun safeword@0.80.0 hook codex stop --plugin-hook",
|
|
62
62
|
"timeout": 600,
|
|
63
63
|
"statusMessage": "Checking Safeword stop continuation"
|
|
64
64
|
}
|
|
@@ -557,7 +557,7 @@ Changed project learnings in the resolved namespace root's `learnings/*.md` must
|
|
|
557
557
|
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2> /dev/null || pwd)}"
|
|
558
558
|
source "$PROJECT_DIR/.safeword/hooks/lib/audit-scope.sh"
|
|
559
559
|
audit_scope_initialize "$PROJECT_DIR"
|
|
560
|
-
NS_ROOT="$(bunx --bun safeword@0.
|
|
560
|
+
NS_ROOT="$(bunx --bun safeword@0.80.0 project namespace-root --cwd "$PROJECT_DIR")"
|
|
561
561
|
|
|
562
562
|
learning_is_in_audit_scope() {
|
|
563
563
|
[ "$AUDIT_SCOPE_MODE" = "repository" ] && return 0
|
|
@@ -717,13 +717,13 @@ audit_scope_initialize "$PROJECT_DIR"
|
|
|
717
717
|
|
|
718
718
|
# Resolve the namespace root (honors config paths.projectRoot in real runs).
|
|
719
719
|
# Fall back on directory existence — robust when the resolver hook is absent.
|
|
720
|
-
NS_ROOT="$(bunx --bun safeword@0.
|
|
720
|
+
NS_ROOT="$(bunx --bun safeword@0.80.0 project namespace-root --cwd "$PROJECT_DIR" 2> /dev/null)"
|
|
721
721
|
[ -d "$NS_ROOT" ] || {
|
|
722
722
|
if [ -d "$PROJECT_DIR/.project" ]; then NS_ROOT="$PROJECT_DIR/.project"; else NS_ROOT="$PROJECT_DIR/.safeword-project"; fi
|
|
723
723
|
}
|
|
724
|
-
PERSONAS_FILE="$(bunx --bun safeword@0.
|
|
725
|
-
SURFACES_FILE="$(bunx --bun safeword@0.
|
|
726
|
-
GLOSSARY_FILE="$(bunx --bun safeword@0.
|
|
724
|
+
PERSONAS_FILE="$(bunx --bun safeword@0.80.0 project namespace-root --cwd "$PROJECT_DIR" --key personas 2> /dev/null)"
|
|
725
|
+
SURFACES_FILE="$(bunx --bun safeword@0.80.0 project namespace-root --cwd "$PROJECT_DIR" --key surfaces 2> /dev/null)"
|
|
726
|
+
GLOSSARY_FILE="$(bunx --bun safeword@0.80.0 project namespace-root --cwd "$PROJECT_DIR" --key glossary 2> /dev/null)"
|
|
727
727
|
[ -n "$PERSONAS_FILE" ] || PERSONAS_FILE="$NS_ROOT/personas.md"
|
|
728
728
|
[ -n "$SURFACES_FILE" ] || SURFACES_FILE="$NS_ROOT/surfaces.md"
|
|
729
729
|
[ -n "$GLOSSARY_FILE" ] || GLOSSARY_FILE="$NS_ROOT/glossary.md"
|
|
@@ -53,7 +53,7 @@ and ticket scope; its typed verdict decides. Resolve a review-capable Safeword
|
|
|
53
53
|
CLI first; source checkouts do not guarantee a bare `safeword` on `PATH`:
|
|
54
54
|
|
|
55
55
|
```bash
|
|
56
|
-
SAFEWORD_REVIEW_PROGRESS=1 bunx --bun safeword@0.
|
|
56
|
+
SAFEWORD_REVIEW_PROGRESS=1 bunx --bun safeword@0.80.0 review run scenario-gate feature-file ticket-spec [legacy-test-definitions] --agent-handoff --json
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
The coordinator prefers the opposite headless agent and labels a permitted
|
|
@@ -82,10 +82,10 @@ Scaffold from `.safeword/templates/impl-plan-template.md` (sibling to `ticket.md
|
|
|
82
82
|
|
|
83
83
|
## Exit: review, then (optionally) the user
|
|
84
84
|
|
|
85
|
-
1. **Independent review first.** At review time, run `bunx --bun safeword@0.
|
|
85
|
+
1. **Independent review first.** At review time, run `bunx --bun safeword@0.80.0 project review-knowledge --json`. Resolve a review-capable Safeword CLI, then invoke the coordinator with the current files identified by the resolver:
|
|
86
86
|
|
|
87
87
|
```bash
|
|
88
|
-
SAFEWORD_REVIEW_PROGRESS=1 bunx --bun safeword@0.
|
|
88
|
+
SAFEWORD_REVIEW_PROGRESS=1 bunx --bun safeword@0.80.0 review run plan-implementation impl-plan.md spec.md ticket.md feature-file principles-file personas-file surfaces-file --agent-handoff --json
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
The shared coordinator sends that bounded packet to the opposite headless agent when available; its typed verdict, failure classification, and independence level are authoritative. Only when that typed result is `REVIEW_ROUTES_EXHAUSTED`, invoke `$safeword:finish-review` immediately with the original result and the same accepted targets; return every other result unchanged and do not substitute another private subagent. Give the reviewer the current `spec.md`, configured principles file, configured personas file, and configured surfaces file in that packet. The reviewer refutes the plan: challenge whether it selected the actually applicable principles, whether each concrete consequence follows from its principle, whether the proposed proof can prove that consequence, whether conflicts belong in Known deviations, whether the design fulfills each persona's JTBD, and whether any affected surface was omitted or lacks credible proof. Also check wrong-direction design, missed scenarios, and editorial padding via the deletion test. Fix findings, re-resolve the sources, re-review, then stamp the exit with the returned agent provenance (`write-review-stamp.ts --author-agent "author-agent" --reviewer-agent "actual-reviewer" --independence "independence" --phase plan-implementation`, where the review gate is enabled). Add `--model` only when the executed reviewer reports a verifiable model identifier; the coordinator never invents one. Human handoff happens **only after** this review passes — raw planning output is never presented for approval. Exception, any time: information only the user has (intent, priorities, constraints not in code or docs) routes to the user the moment the gap appears — `$safeword:elicit`.
|
|
@@ -196,7 +196,7 @@ Off by default. When `.safeword/config.json` sets `architectureReviewGate: true`
|
|
|
196
196
|
2. **A fresh-context review.** Resolve a review-capable Safeword CLI, then run the shared coordinator with only the bounded design evidence:
|
|
197
197
|
|
|
198
198
|
```bash
|
|
199
|
-
SAFEWORD_REVIEW_PROGRESS=1 bunx --bun safeword@0.
|
|
199
|
+
SAFEWORD_REVIEW_PROGRESS=1 bunx --bun safeword@0.80.0 review run plan-implementation impl-plan.md ticket-spec feature-file --agent-handoff --json
|
|
200
200
|
```
|
|
201
201
|
|
|
202
202
|
The shared coordinator prefers the opposite headless agent. Only when its typed result is `REVIEW_ROUTES_EXHAUSTED`, invoke `$safeword:finish-review` with the original result and the same accepted targets; return every other result unchanged. Degraded findings cannot satisfy a required independent-review gate. On an independent pass, stamp it:
|
|
@@ -39,7 +39,7 @@ Gather the durable trail safeword already keeps, then narrate it. Run:
|
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
41
|
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2> /dev/null || pwd)}"
|
|
42
|
-
NS_ROOT="$(bunx --bun safeword@0.
|
|
42
|
+
NS_ROOT="$(bunx --bun safeword@0.80.0 project namespace-root --cwd "$PROJECT_DIR")"
|
|
43
43
|
# What you're on now: the last re-entry line names the current ticket + Next
|
|
44
44
|
tail -3 "$NS_ROOT/re-entry.md" 2> /dev/null
|
|
45
45
|
# Fallback when re-entry is empty: in_progress tickets (not epics)
|
|
@@ -50,7 +50,7 @@ If in a BDD workflow, read the current ticket from `<namespace-root>/tickets/` a
|
|
|
50
50
|
|
|
51
51
|
### Project-principle challenge
|
|
52
52
|
|
|
53
|
-
For a BDD ticket, run `bunx --bun safeword@0.
|
|
53
|
+
For a BDD ticket, run `bunx --bun safeword@0.80.0 project review-knowledge --json` at the
|
|
54
54
|
start of each pass and read the current `principles`, `personas`, and `surfaces`
|
|
55
55
|
paths and content it returns (including overrides such as `paths.principles`).
|
|
56
56
|
Do not substitute labels or intake-era content.
|
|
@@ -175,7 +175,7 @@ Each pass:
|
|
|
175
175
|
CLI first; source checkouts do not guarantee a bare `safeword` on `PATH`:
|
|
176
176
|
|
|
177
177
|
```bash
|
|
178
|
-
SAFEWORD_REVIEW_PROGRESS=1 bunx --bun safeword@0.
|
|
178
|
+
SAFEWORD_REVIEW_PROGRESS=1 bunx --bun safeword@0.80.0 review run quality-review changed-file [more-changed-files...] --agent-handoff --json
|
|
179
179
|
```
|
|
180
180
|
|
|
181
181
|
A healthy deep review may return `REVIEW_PENDING` after its foreground
|
|
@@ -17,7 +17,7 @@ a caller-nominated path. The spool contains sanitized safeword findings for
|
|
|
17
17
|
## Procedure
|
|
18
18
|
|
|
19
19
|
1. Before reading or making any tracker call, run
|
|
20
|
-
`bunx --bun safeword@0.
|
|
20
|
+
`bunx --bun safeword@0.80.0 project retro-drain "<spool-path>" --validated-jsonl`.
|
|
21
21
|
Use only its JSONL stdout as the filing input. A nonzero exit means validation
|
|
22
22
|
failed: make no search, comment, or create call, leave the spool unchanged,
|
|
23
23
|
and report `retro-filer: cannot file - draft validation failed`. If its output
|
|
@@ -60,7 +60,7 @@ a caller-nominated path. The spool contains sanitized safeword findings for
|
|
|
60
60
|
the draft only when the append succeeded and the exact ack is visible. If the
|
|
61
61
|
append or verification fails, leave the draft in place.
|
|
62
62
|
5. Create at most five new issues per run. Drain only by running
|
|
63
|
-
`bunx --bun safeword@0.
|
|
63
|
+
`bunx --bun safeword@0.80.0 project retro-drain "<spool-path>"`; never rewrite or
|
|
64
64
|
delete the spool directly. The helper removes only drafts whose valid ack is
|
|
65
65
|
reader-visible, so unfiled or unacknowledged drafts remain. If tracker write
|
|
66
66
|
access is unavailable, leave the spool unchanged and report
|
|
@@ -16,7 +16,7 @@ Adversarially review a ticket's scenarios: treat them as if you're trying to bre
|
|
|
16
16
|
- **Manual re-run** — invoke `$safeword:review-spec` anytime after `define-behavior` (e.g., scenarios changed during implement and you want to re-validate). Allowed on a closed ticket too — a post-hoc audit is still readable.
|
|
17
17
|
|
|
18
18
|
Read the active ticket's `.feature` source first. At review time, run
|
|
19
|
-
`bunx --bun safeword@0.
|
|
19
|
+
`bunx --bun safeword@0.80.0 project review-knowledge --json` and read the current
|
|
20
20
|
`principles`, `personas`, and `surfaces` source paths and content it returns, so
|
|
21
21
|
the review is grounded in project knowledge rather than labels or stale intake
|
|
22
22
|
context. The resolver honors `paths.principles`, `paths.personas`, and
|
|
@@ -35,7 +35,7 @@ Resolve a review-capable Safeword CLI first; source checkouts do not guarantee
|
|
|
35
35
|
a bare `safeword` on `PATH`:
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
SAFEWORD_REVIEW_PROGRESS=1 bunx --bun safeword@0.
|
|
38
|
+
SAFEWORD_REVIEW_PROGRESS=1 bunx --bun safeword@0.80.0 review run scenario-gate feature-file ticket-spec [legacy-test-definitions] --agent-handoff --json
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
The coordinator's assigned/actual reviewer, failure classification, and
|
|
@@ -45,7 +45,7 @@ installed helper — report it to the user and resolve before retrying.
|
|
|
45
45
|
## Review the spec (do this now, with the stamp written)
|
|
46
46
|
|
|
47
47
|
The stamp records that a review was invoked; the actual scrutiny is yours. At
|
|
48
|
-
review time, run `bunx --bun safeword@0.
|
|
48
|
+
review time, run `bunx --bun safeword@0.80.0 project review-knowledge --json` and use its
|
|
49
49
|
current `principles`, `personas`, and `surfaces` source paths and content—not
|
|
50
50
|
labels remembered from intake. These resolve from `paths.principles`,
|
|
51
51
|
`paths.personas`, and `paths.surfaces` when configured. Read those sources with the active ticket's
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
error,
|
|
3
|
-
success,
|
|
4
|
-
warn
|
|
5
|
-
} from "./chunk-QSLVO4XF.js";
|
|
6
1
|
import {
|
|
7
2
|
isSafewordOwned,
|
|
8
3
|
isWouldChangeAction,
|
|
@@ -10,6 +5,11 @@ import {
|
|
|
10
5
|
selfHealProject,
|
|
11
6
|
selfHealProjectPreservingProse
|
|
12
7
|
} from "./chunk-XJIFKUKN.js";
|
|
8
|
+
import {
|
|
9
|
+
error,
|
|
10
|
+
success,
|
|
11
|
+
warn
|
|
12
|
+
} from "./chunk-QSLVO4XF.js";
|
|
13
13
|
import {
|
|
14
14
|
GENERATED_ARCHITECTURE_FILENAME,
|
|
15
15
|
isArchitectureDocumentEnforcementEnabled,
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
readJson,
|
|
26
26
|
writeJson
|
|
27
27
|
} from "./chunk-HF3DXZEA.js";
|
|
28
|
-
import "./chunk-
|
|
28
|
+
import "./chunk-VBJXIMMS.js";
|
|
29
29
|
|
|
30
30
|
// src/commands/architecture.ts
|
|
31
31
|
import { execFileSync } from "child_process";
|
|
@@ -650,4 +650,4 @@ export {
|
|
|
650
650
|
architectureStage,
|
|
651
651
|
architectureStaged
|
|
652
652
|
};
|
|
653
|
-
//# sourceMappingURL=architecture-
|
|
653
|
+
//# sourceMappingURL=architecture-MQGZ5IMW.js.map
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
import "./chunk-NEOV7ZY3.js";
|
|
12
12
|
import "./chunk-VZ5B2SIO.js";
|
|
13
13
|
import "./chunk-HF3DXZEA.js";
|
|
14
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-VBJXIMMS.js";
|
|
15
15
|
export {
|
|
16
16
|
isSafewordOwned,
|
|
17
17
|
isWouldChangeAction,
|
|
@@ -22,4 +22,4 @@ export {
|
|
|
22
22
|
selfHealProject,
|
|
23
23
|
selfHealProjectPreservingProse
|
|
24
24
|
};
|
|
25
|
-
//# sourceMappingURL=architecture-document-
|
|
25
|
+
//# sourceMappingURL=architecture-document-VZ4DSQFG.js.map
|
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
monorepoFingerprintOf
|
|
6
6
|
} from "./chunk-VZ5B2SIO.js";
|
|
7
7
|
import "./chunk-HF3DXZEA.js";
|
|
8
|
-
import "./chunk-
|
|
8
|
+
import "./chunk-VBJXIMMS.js";
|
|
9
9
|
export {
|
|
10
10
|
discoverUnreadableWorkspaces,
|
|
11
11
|
discoverWorkspaces,
|
|
12
12
|
extractMonorepoArchitectureSnapshot,
|
|
13
13
|
monorepoFingerprintOf
|
|
14
14
|
};
|
|
15
|
-
//# sourceMappingURL=architecture-monorepo-
|
|
15
|
+
//# sourceMappingURL=architecture-monorepo-R4XJEUFG.js.map
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
import {
|
|
27
27
|
readFileSafe
|
|
28
28
|
} from "./chunk-HF3DXZEA.js";
|
|
29
|
-
import "./chunk-
|
|
29
|
+
import "./chunk-VBJXIMMS.js";
|
|
30
30
|
|
|
31
31
|
// src/commands/boundary.ts
|
|
32
32
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
@@ -627,4 +627,4 @@ function boundary(options) {
|
|
|
627
627
|
export {
|
|
628
628
|
boundary
|
|
629
629
|
};
|
|
630
|
-
//# sourceMappingURL=boundary-
|
|
630
|
+
//# sourceMappingURL=boundary-E5NPK5DT.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/cli-protocol/agent-selection.ts
|
|
2
|
-
var SUPPORTED_AGENT_INTEGRATIONS = ["claude", "codex", "cursor"];
|
|
2
|
+
var SUPPORTED_AGENT_INTEGRATIONS = ["claude", "codex", "opencode", "cursor"];
|
|
3
3
|
var DEFAULT_AGENT_INTEGRATIONS = Object.freeze([
|
|
4
4
|
"claude",
|
|
5
5
|
"codex"
|
|
@@ -17,7 +17,7 @@ function parseAgentSelection(value) {
|
|
|
17
17
|
ok: false,
|
|
18
18
|
error: {
|
|
19
19
|
code: "AGENT_SELECTION_INVALID",
|
|
20
|
-
message:
|
|
20
|
+
message: `\`none\` must be used alone; supported values are ${AGENT_SELECTION_DESCRIPTION}.`
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
23
|
}
|
|
@@ -34,7 +34,7 @@ function invalidSelection() {
|
|
|
34
34
|
ok: false,
|
|
35
35
|
error: {
|
|
36
36
|
code: "AGENT_SELECTION_INVALID",
|
|
37
|
-
message:
|
|
37
|
+
message: `Supported agent values are ${AGENT_SELECTION_DESCRIPTION}.`
|
|
38
38
|
}
|
|
39
39
|
};
|
|
40
40
|
}
|
|
@@ -44,4 +44,4 @@ export {
|
|
|
44
44
|
AGENT_SELECTION_DESCRIPTION,
|
|
45
45
|
parseAgentSelection
|
|
46
46
|
};
|
|
47
|
-
//# sourceMappingURL=chunk-
|
|
47
|
+
//# sourceMappingURL=chunk-4L4NYOC3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli-protocol/agent-selection.ts"],"sourcesContent":["export type AgentIntegration = 'claude' | 'codex' | 'cursor' | 'opencode';\n\n/** Project configuration is always in scope; `agents` names the integrations. */\ninterface AgentSelection {\n readonly agents: readonly AgentIntegration[];\n}\n\nexport interface AgentSelectionError {\n readonly code: 'AGENT_SELECTION_INVALID';\n readonly message: string;\n}\n\nexport type AgentSelectionResult =\n | { readonly ok: true; readonly selection: AgentSelection }\n | { readonly ok: false; readonly error: AgentSelectionError };\n\nconst SUPPORTED_AGENT_INTEGRATIONS = ['claude', 'codex', 'opencode', 'cursor'] as const;\nexport const DEFAULT_AGENT_INTEGRATIONS: readonly AgentIntegration[] = Object.freeze([\n 'claude',\n 'codex',\n]);\nexport const AGENT_SELECTION_DESCRIPTION = `${SUPPORTED_AGENT_INTEGRATIONS.join(', ')}, or none`;\n\nexport function parseAgentSelection(value: unknown): AgentSelectionResult {\n if (value === undefined) {\n return { ok: true, selection: { agents: DEFAULT_AGENT_INTEGRATIONS } };\n }\n if (typeof value !== 'string') return invalidSelection();\n\n const values = value\n .split(',')\n .map(agent => agent.trim().toLowerCase())\n .filter(agent => agent !== '');\n if (values.length === 0) return invalidSelection();\n if (values.includes('none')) {\n return values.length === 1\n ? { ok: true, selection: { agents: [] } }\n : {\n ok: false,\n error: {\n code: 'AGENT_SELECTION_INVALID',\n message: `\\`none\\` must be used alone; supported values are ${AGENT_SELECTION_DESCRIPTION}.`,\n },\n };\n }\n if (values.some(agent => !SUPPORTED_AGENT_INTEGRATIONS.includes(agent as AgentIntegration))) {\n return invalidSelection();\n }\n return {\n ok: true,\n selection: { agents: [...new Set(values as AgentIntegration[])] },\n };\n}\n\nfunction invalidSelection(): AgentSelectionResult {\n return {\n ok: false,\n error: {\n code: 'AGENT_SELECTION_INVALID',\n message: `Supported agent values are ${AGENT_SELECTION_DESCRIPTION}.`,\n },\n };\n}\n"],"mappings":";AAgBA,IAAM,+BAA+B,CAAC,UAAU,SAAS,YAAY,QAAQ;AACtE,IAAM,6BAA0D,OAAO,OAAO;AAAA,EACnF;AAAA,EACA;AACF,CAAC;AACM,IAAM,8BAA8B,GAAG,6BAA6B,KAAK,IAAI,CAAC;AAE9E,SAAS,oBAAoB,OAAsC;AACxE,MAAI,UAAU,QAAW;AACvB,WAAO,EAAE,IAAI,MAAM,WAAW,EAAE,QAAQ,2BAA2B,EAAE;AAAA,EACvE;AACA,MAAI,OAAO,UAAU,SAAU,QAAO,iBAAiB;AAEvD,QAAM,SAAS,MACZ,MAAM,GAAG,EACT,IAAI,WAAS,MAAM,KAAK,EAAE,YAAY,CAAC,EACvC,OAAO,WAAS,UAAU,EAAE;AAC/B,MAAI,OAAO,WAAW,EAAG,QAAO,iBAAiB;AACjD,MAAI,OAAO,SAAS,MAAM,GAAG;AAC3B,WAAO,OAAO,WAAW,IACrB,EAAE,IAAI,MAAM,WAAW,EAAE,QAAQ,CAAC,EAAE,EAAE,IACtC;AAAA,MACE,IAAI;AAAA,MACJ,OAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAS,qDAAqD,2BAA2B;AAAA,MAC3F;AAAA,IACF;AAAA,EACN;AACA,MAAI,OAAO,KAAK,WAAS,CAAC,6BAA6B,SAAS,KAAyB,CAAC,GAAG;AAC3F,WAAO,iBAAiB;AAAA,EAC1B;AACA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,WAAW,EAAE,QAAQ,CAAC,GAAG,IAAI,IAAI,MAA4B,CAAC,EAAE;AAAA,EAClE;AACF;AAEA,SAAS,mBAAyC;AAChD,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,OAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS,8BAA8B,2BAA2B;AAAA,IACpE;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// src/version.ts
|
|
2
|
+
import { createRequire } from "module";
|
|
3
|
+
var require2 = createRequire(import.meta.url);
|
|
4
|
+
var builtVersion = typeof __SAFEWORD_VERSION__ === "string" ? __SAFEWORD_VERSION__ : void 0;
|
|
5
|
+
var pkg = builtVersion === void 0 ? require2("../package.json") : { version: builtVersion };
|
|
6
|
+
var VERSION = pkg.version;
|
|
7
|
+
var SAFEWORD_PEER_DEPENDENCIES = pkg.peerDependencies ?? {};
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
VERSION
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=chunk-4VORK37W.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/version.ts"],"sourcesContent":["import { createRequire } from 'node:module';\n\ndeclare const __SAFEWORD_VERSION__: string | undefined;\n\nconst require = createRequire(import.meta.url);\n// The relative path is intentional and must stay at one `..` — version.ts is at\n// src/ (depth 1 from packages/cli/), and tsup bundles it to dist/ (also depth 1),\n// so `../package.json` resolves correctly in both contexts. Helpers nested under\n// src/utils/ cannot read package.json directly without breaking once bundled —\n// they should import the metadata from here instead.\nconst builtVersion = typeof __SAFEWORD_VERSION__ === 'string' ? __SAFEWORD_VERSION__ : undefined;\nconst pkg =\n builtVersion === undefined\n ? (require('../package.json') as {\n version: string;\n peerDependencies?: Record<string, string>;\n })\n : { version: builtVersion };\n\nexport const VERSION = pkg.version;\nexport const SAFEWORD_PEER_DEPENDENCIES = pkg.peerDependencies ?? {};\n"],"mappings":";AAAA,SAAS,qBAAqB;AAI9B,IAAMA,WAAU,cAAc,YAAY,GAAG;AAM7C,IAAM,eAAe,OAAO,yBAAyB,WAAW,uBAAuB;AACvF,IAAM,MACJ,iBAAiB,SACZA,SAAQ,iBAAiB,IAI1B,EAAE,SAAS,aAAa;AAEvB,IAAM,UAAU,IAAI;AACpB,IAAM,6BAA6B,IAAI,oBAAoB,CAAC;","names":["require"]}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {
|
|
2
|
+
projectLifecycleSchema
|
|
3
|
+
} from "./chunk-BUJI55OX.js";
|
|
4
|
+
import {
|
|
5
|
+
createResult
|
|
6
|
+
} from "./chunk-FOYGZ4MK.js";
|
|
7
|
+
import {
|
|
8
|
+
isCursorProjectPath
|
|
9
|
+
} from "./chunk-YM24LWXW.js";
|
|
10
|
+
|
|
11
|
+
// src/lifecycle/cursor.ts
|
|
12
|
+
import { existsSync } from "fs";
|
|
13
|
+
import nodePath from "path";
|
|
14
|
+
function cursorOwnedFiles(schema) {
|
|
15
|
+
return [...Object.keys(schema.ownedFiles), ...Object.keys(schema.managedFiles)].filter(
|
|
16
|
+
(path) => isCursorProjectPath(path)
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
function hasCursorProjectAssets(cwd, schema) {
|
|
20
|
+
return cursorOwnedFiles(schema).some((path) => existsSync(nodePath.join(cwd, path)));
|
|
21
|
+
}
|
|
22
|
+
function observeCursorProject(cwd, schema) {
|
|
23
|
+
const owned = cursorOwnedFiles(schema);
|
|
24
|
+
const missing = owned.filter((path) => !existsSync(nodePath.join(cwd, path)));
|
|
25
|
+
if (missing.length === 0) {
|
|
26
|
+
return createResult({
|
|
27
|
+
state: "healthy",
|
|
28
|
+
data: {
|
|
29
|
+
command: "cursor status",
|
|
30
|
+
coverage: "project-owned Cursor assets",
|
|
31
|
+
owned_files: owned.length
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return createResult({
|
|
36
|
+
state: "action_required",
|
|
37
|
+
findings: [
|
|
38
|
+
{
|
|
39
|
+
code: "CURSOR_ASSETS_MISSING",
|
|
40
|
+
message: `${missing.length} Safeword-owned Cursor file(s) are missing; run \`safeword install --agents=cursor\`.`,
|
|
41
|
+
severity: "warning"
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
nextActions: [
|
|
45
|
+
{ command: "safeword install --agents=cursor", mutates: true, requiresHuman: false }
|
|
46
|
+
],
|
|
47
|
+
data: {
|
|
48
|
+
command: "cursor status",
|
|
49
|
+
coverage: "project-owned Cursor assets",
|
|
50
|
+
owned_files: owned.length,
|
|
51
|
+
missing_files: missing.length
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function unselectedCursorFinding(cwd, agents) {
|
|
56
|
+
if (agents.includes("cursor")) return [];
|
|
57
|
+
if (!hasCursorProjectAssets(cwd, projectLifecycleSchema(cwd, ["cursor"]))) return [];
|
|
58
|
+
return [
|
|
59
|
+
{
|
|
60
|
+
code: "CURSOR_NOT_SELECTED",
|
|
61
|
+
message: "This project has Safeword-owned Cursor assets that the current selection excludes; include `--agents=cursor` to keep them current.",
|
|
62
|
+
severity: "info"
|
|
63
|
+
}
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export {
|
|
68
|
+
hasCursorProjectAssets,
|
|
69
|
+
observeCursorProject,
|
|
70
|
+
unselectedCursorFinding
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=chunk-556WOVCD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lifecycle/cursor.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport nodePath from 'node:path';\n\nimport { type CliResult, createResult } from '../cli-protocol/result.js';\nimport { isCursorProjectPath, type SafewordSchema } from '../schema.js';\nimport { projectLifecycleSchema } from './schema.js';\n\n/**\n * Safeword-owned Cursor files declared by the schema. Cursor has no host\n * process to interrogate, so its project-local assets are the only surface\n * evidence available.\n */\nfunction cursorOwnedFiles(schema: SafewordSchema): readonly string[] {\n return [...Object.keys(schema.ownedFiles), ...Object.keys(schema.managedFiles)].filter(path =>\n isCursorProjectPath(path),\n );\n}\n\n/** True when this project already carries Safeword-owned Cursor assets. */\nexport function hasCursorProjectAssets(cwd: string, schema: SafewordSchema): boolean {\n return cursorOwnedFiles(schema).some(path => existsSync(nodePath.join(cwd, path)));\n}\n\n/**\n * Observe the Cursor surface from its own assets rather than mirroring the\n * project result, so a Cursor-only gap cannot be reported as healthy.\n */\nexport function observeCursorProject(cwd: string, schema: SafewordSchema): CliResult {\n const owned = cursorOwnedFiles(schema);\n const missing = owned.filter(path => !existsSync(nodePath.join(cwd, path)));\n if (missing.length === 0) {\n return createResult({\n state: 'healthy',\n data: {\n command: 'cursor status',\n coverage: 'project-owned Cursor assets',\n owned_files: owned.length,\n },\n });\n }\n return createResult({\n state: 'action_required',\n findings: [\n {\n code: 'CURSOR_ASSETS_MISSING',\n message: `${missing.length} Safeword-owned Cursor file(s) are missing; run \\`safeword install --agents=cursor\\`.`,\n severity: 'warning',\n },\n ],\n nextActions: [\n { command: 'safeword install --agents=cursor', mutates: true, requiresHuman: false },\n ],\n data: {\n command: 'cursor status',\n coverage: 'project-owned Cursor assets',\n owned_files: owned.length,\n missing_files: missing.length,\n },\n });\n}\n\n/**\n * Advise when a project carries Cursor assets that the current selection\n * excludes, so an upgrade cannot silently leave them at an older release.\n * Resolves the Cursor-inclusive schema itself — the selection's own schema has\n * every Cursor path filtered out and could never detect them.\n */\nexport function unselectedCursorFinding(\n cwd: string,\n agents: readonly string[],\n): CliResult['findings'] {\n if (agents.includes('cursor')) return [];\n if (!hasCursorProjectAssets(cwd, projectLifecycleSchema(cwd, ['cursor']))) return [];\n return [\n {\n code: 'CURSOR_NOT_SELECTED',\n message:\n 'This project has Safeword-owned Cursor assets that the current selection excludes; include `--agents=cursor` to keep them current.',\n severity: 'info',\n },\n ];\n}\n"],"mappings":";;;;;;;;;;;AAAA,SAAS,kBAAkB;AAC3B,OAAO,cAAc;AAWrB,SAAS,iBAAiB,QAA2C;AACnE,SAAO,CAAC,GAAG,OAAO,KAAK,OAAO,UAAU,GAAG,GAAG,OAAO,KAAK,OAAO,YAAY,CAAC,EAAE;AAAA,IAAO,UACrF,oBAAoB,IAAI;AAAA,EAC1B;AACF;AAGO,SAAS,uBAAuB,KAAa,QAAiC;AACnF,SAAO,iBAAiB,MAAM,EAAE,KAAK,UAAQ,WAAW,SAAS,KAAK,KAAK,IAAI,CAAC,CAAC;AACnF;AAMO,SAAS,qBAAqB,KAAa,QAAmC;AACnF,QAAM,QAAQ,iBAAiB,MAAM;AACrC,QAAM,UAAU,MAAM,OAAO,UAAQ,CAAC,WAAW,SAAS,KAAK,KAAK,IAAI,CAAC,CAAC;AAC1E,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,aAAa;AAAA,MAClB,OAAO;AAAA,MACP,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,UAAU;AAAA,QACV,aAAa,MAAM;AAAA,MACrB;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO,aAAa;AAAA,IAClB,OAAO;AAAA,IACP,UAAU;AAAA,MACR;AAAA,QACE,MAAM;AAAA,QACN,SAAS,GAAG,QAAQ,MAAM;AAAA,QAC1B,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,IACA,aAAa;AAAA,MACX,EAAE,SAAS,oCAAoC,SAAS,MAAM,eAAe,MAAM;AAAA,IACrF;AAAA,IACA,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,UAAU;AAAA,MACV,aAAa,MAAM;AAAA,MACnB,eAAe,QAAQ;AAAA,IACzB;AAAA,EACF,CAAC;AACH;AAQO,SAAS,wBACd,KACA,QACuB;AACvB,MAAI,OAAO,SAAS,QAAQ,EAAG,QAAO,CAAC;AACvC,MAAI,CAAC,uBAAuB,KAAK,uBAAuB,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAG,QAAO,CAAC;AACnF,SAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
legacyObservationIsEmpty,
|
|
3
|
-
observeClaudeLegacy
|
|
4
|
-
} from "./chunk-IEO5TIRE.js";
|
|
5
1
|
import {
|
|
6
2
|
observeApplicableClaudePlugins
|
|
7
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-JEFU3MAX.js";
|
|
8
4
|
import {
|
|
9
5
|
canonicalClaudeProjectRoot
|
|
10
6
|
} from "./chunk-7XELS325.js";
|
|
7
|
+
import {
|
|
8
|
+
legacyObservationIsEmpty,
|
|
9
|
+
observeClaudeLegacy
|
|
10
|
+
} from "./chunk-IEO5TIRE.js";
|
|
11
11
|
import {
|
|
12
12
|
CLAUDE_MIGRATION_SCHEMA,
|
|
13
13
|
claudeConfigDirectory
|
|
14
14
|
} from "./chunk-WVA4Q6BX.js";
|
|
15
|
-
import {
|
|
16
|
-
SETTINGS_HOOKS
|
|
17
|
-
} from "./chunk-W32GLIFH.js";
|
|
18
15
|
import {
|
|
19
16
|
createResult
|
|
20
17
|
} from "./chunk-FOYGZ4MK.js";
|
|
18
|
+
import {
|
|
19
|
+
SETTINGS_HOOKS
|
|
20
|
+
} from "./chunk-W32GLIFH.js";
|
|
21
21
|
import {
|
|
22
22
|
VERSION
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-4VORK37W.js";
|
|
24
24
|
|
|
25
25
|
// src/claude-plugin/status.ts
|
|
26
26
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -306,4 +306,4 @@ export {
|
|
|
306
306
|
equivalentClaudeInstallations,
|
|
307
307
|
observeClaudeStatus
|
|
308
308
|
};
|
|
309
|
-
//# sourceMappingURL=chunk-
|
|
309
|
+
//# sourceMappingURL=chunk-56ZUJOIZ.js.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
canonicalClaudeProjectRoot
|
|
3
|
+
} from "./chunk-7XELS325.js";
|
|
1
4
|
import {
|
|
2
5
|
assertSafeClaudeCleanupTarget,
|
|
3
6
|
containedClaudeCleanupPath,
|
|
4
7
|
observeClaudeLegacy
|
|
5
8
|
} from "./chunk-IEO5TIRE.js";
|
|
6
|
-
import {
|
|
7
|
-
canonicalClaudeProjectRoot
|
|
8
|
-
} from "./chunk-7XELS325.js";
|
|
9
9
|
import {
|
|
10
10
|
CLAUDE_MIGRATION_SCHEMA,
|
|
11
11
|
advisoryStateDigest,
|
|
@@ -15,6 +15,9 @@ import {
|
|
|
15
15
|
writeClaudeMigrationAttention,
|
|
16
16
|
writeClaudePluginMode
|
|
17
17
|
} from "./chunk-WVA4Q6BX.js";
|
|
18
|
+
import {
|
|
19
|
+
createResult
|
|
20
|
+
} from "./chunk-FOYGZ4MK.js";
|
|
18
21
|
import {
|
|
19
22
|
writeDurableFile,
|
|
20
23
|
writeDurableFileExclusive
|
|
@@ -24,9 +27,6 @@ import {
|
|
|
24
27
|
isAcceptedHistoricalFile,
|
|
25
28
|
isAcceptedHistoricalHook
|
|
26
29
|
} from "./chunk-XICREN7F.js";
|
|
27
|
-
import {
|
|
28
|
-
createResult
|
|
29
|
-
} from "./chunk-FOYGZ4MK.js";
|
|
30
30
|
|
|
31
31
|
// src/claude-plugin/cleanup.ts
|
|
32
32
|
import { createHash, randomUUID } from "crypto";
|
|
@@ -833,4 +833,4 @@ export {
|
|
|
833
833
|
migrateClaudeLegacyAutomatically,
|
|
834
834
|
recoverClaudeCleanup
|
|
835
835
|
};
|
|
836
|
-
//# sourceMappingURL=chunk-
|
|
836
|
+
//# sourceMappingURL=chunk-6Z2DOROQ.js.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// src/utils/profile-lock.ts
|
|
2
|
+
import { randomUUID } from "crypto";
|
|
3
|
+
import { mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from "fs";
|
|
4
|
+
import nodePath from "path";
|
|
5
|
+
var DEFAULT_MAX_AGE_MS = 10 * 60 * 1e3;
|
|
6
|
+
function readRecord(path) {
|
|
7
|
+
try {
|
|
8
|
+
const value = JSON.parse(
|
|
9
|
+
readFileSync(nodePath.join(path, "owner.json"), "utf8")
|
|
10
|
+
);
|
|
11
|
+
return typeof value.owner === "string" && typeof value.acquired_at === "number" ? { owner: value.owner, acquired_at: value.acquired_at } : void 0;
|
|
12
|
+
} catch {
|
|
13
|
+
return void 0;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function lockAge(path, now) {
|
|
17
|
+
const record = readRecord(path);
|
|
18
|
+
if (record !== void 0) return now - record.acquired_at;
|
|
19
|
+
try {
|
|
20
|
+
return now - statSync(path).mtimeMs;
|
|
21
|
+
} catch {
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function createLock(path, owner, now) {
|
|
26
|
+
try {
|
|
27
|
+
mkdirSync(path);
|
|
28
|
+
writeFileSync(
|
|
29
|
+
nodePath.join(path, "owner.json"),
|
|
30
|
+
`${JSON.stringify({ owner, acquired_at: now })}
|
|
31
|
+
`,
|
|
32
|
+
{ encoding: "utf8", flag: "wx", mode: 384 }
|
|
33
|
+
);
|
|
34
|
+
return { path, owner };
|
|
35
|
+
} catch {
|
|
36
|
+
return void 0;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function acquireProfileLock(path, options = {}) {
|
|
40
|
+
const owner = options.owner ?? randomUUID();
|
|
41
|
+
const now = (options.now ?? Date.now)();
|
|
42
|
+
mkdirSync(nodePath.dirname(path), { recursive: true });
|
|
43
|
+
const gate = `${path}.acquire`;
|
|
44
|
+
try {
|
|
45
|
+
mkdirSync(gate);
|
|
46
|
+
} catch {
|
|
47
|
+
return void 0;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
const acquired = createLock(path, owner, now);
|
|
51
|
+
if (acquired !== void 0) return acquired;
|
|
52
|
+
if (lockAge(path, now) <= (options.maxAgeMs ?? DEFAULT_MAX_AGE_MS)) return void 0;
|
|
53
|
+
rmSync(path, { recursive: true, force: true });
|
|
54
|
+
return createLock(path, owner, now);
|
|
55
|
+
} finally {
|
|
56
|
+
rmSync(gate, { recursive: true, force: true });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function releaseProfileLock(lock) {
|
|
60
|
+
if (readRecord(lock.path)?.owner !== lock.owner) return false;
|
|
61
|
+
rmSync(lock.path, { recursive: true, force: true });
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export {
|
|
66
|
+
acquireProfileLock,
|
|
67
|
+
releaseProfileLock
|
|
68
|
+
};
|
|
69
|
+
//# sourceMappingURL=chunk-A3QLTTGK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/profile-lock.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';\nimport nodePath from 'node:path';\n\nconst DEFAULT_MAX_AGE_MS = 10 * 60 * 1000;\n\ninterface LockRecord {\n readonly owner: string;\n readonly acquired_at: number;\n}\n\nexport interface ProfileLock {\n readonly path: string;\n readonly owner: string;\n}\n\nexport interface ProfileLockOptions {\n readonly owner?: string;\n readonly now?: () => number;\n readonly maxAgeMs?: number;\n}\n\nfunction readRecord(path: string): LockRecord | undefined {\n try {\n const value = JSON.parse(\n readFileSync(nodePath.join(path, 'owner.json'), 'utf8'),\n ) as Partial<LockRecord>;\n return typeof value.owner === 'string' && typeof value.acquired_at === 'number'\n ? { owner: value.owner, acquired_at: value.acquired_at }\n : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction lockAge(path: string, now: number): number {\n const record = readRecord(path);\n if (record !== undefined) return now - record.acquired_at;\n try {\n return now - statSync(path).mtimeMs;\n } catch {\n return 0;\n }\n}\n\nfunction createLock(path: string, owner: string, now: number): ProfileLock | undefined {\n try {\n mkdirSync(path);\n writeFileSync(\n nodePath.join(path, 'owner.json'),\n `${JSON.stringify({ owner, acquired_at: now })}\\n`,\n { encoding: 'utf8', flag: 'wx', mode: 0o600 },\n );\n return { path, owner };\n } catch {\n return undefined;\n }\n}\n\nexport function acquireProfileLock(\n path: string,\n options: ProfileLockOptions = {},\n): ProfileLock | undefined {\n const owner = options.owner ?? randomUUID();\n const now = (options.now ?? Date.now)();\n mkdirSync(nodePath.dirname(path), { recursive: true });\n const gate = `${path}.acquire`;\n try {\n mkdirSync(gate);\n } catch {\n return undefined;\n }\n try {\n const acquired = createLock(path, owner, now);\n if (acquired !== undefined) return acquired;\n if (lockAge(path, now) <= (options.maxAgeMs ?? DEFAULT_MAX_AGE_MS)) return undefined;\n rmSync(path, { recursive: true, force: true });\n return createLock(path, owner, now);\n } finally {\n rmSync(gate, { recursive: true, force: true });\n }\n}\n\nexport function releaseProfileLock(lock: ProfileLock): boolean {\n if (readRecord(lock.path)?.owner !== lock.owner) return false;\n rmSync(lock.path, { recursive: true, force: true });\n return true;\n}\n"],"mappings":";AAAA,SAAS,kBAAkB;AAC3B,SAAS,WAAW,cAAc,QAAQ,UAAU,qBAAqB;AACzE,OAAO,cAAc;AAErB,IAAM,qBAAqB,KAAK,KAAK;AAkBrC,SAAS,WAAW,MAAsC;AACxD,MAAI;AACF,UAAM,QAAQ,KAAK;AAAA,MACjB,aAAa,SAAS,KAAK,MAAM,YAAY,GAAG,MAAM;AAAA,IACxD;AACA,WAAO,OAAO,MAAM,UAAU,YAAY,OAAO,MAAM,gBAAgB,WACnE,EAAE,OAAO,MAAM,OAAO,aAAa,MAAM,YAAY,IACrD;AAAA,EACN,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,QAAQ,MAAc,KAAqB;AAClD,QAAM,SAAS,WAAW,IAAI;AAC9B,MAAI,WAAW,OAAW,QAAO,MAAM,OAAO;AAC9C,MAAI;AACF,WAAO,MAAM,SAAS,IAAI,EAAE;AAAA,EAC9B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,WAAW,MAAc,OAAe,KAAsC;AACrF,MAAI;AACF,cAAU,IAAI;AACd;AAAA,MACE,SAAS,KAAK,MAAM,YAAY;AAAA,MAChC,GAAG,KAAK,UAAU,EAAE,OAAO,aAAa,IAAI,CAAC,CAAC;AAAA;AAAA,MAC9C,EAAE,UAAU,QAAQ,MAAM,MAAM,MAAM,IAAM;AAAA,IAC9C;AACA,WAAO,EAAE,MAAM,MAAM;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,mBACd,MACA,UAA8B,CAAC,GACN;AACzB,QAAM,QAAQ,QAAQ,SAAS,WAAW;AAC1C,QAAM,OAAO,QAAQ,OAAO,KAAK,KAAK;AACtC,YAAU,SAAS,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AACrD,QAAM,OAAO,GAAG,IAAI;AACpB,MAAI;AACF,cAAU,IAAI;AAAA,EAChB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI;AACF,UAAM,WAAW,WAAW,MAAM,OAAO,GAAG;AAC5C,QAAI,aAAa,OAAW,QAAO;AACnC,QAAI,QAAQ,MAAM,GAAG,MAAM,QAAQ,YAAY,oBAAqB,QAAO;AAC3E,WAAO,MAAM,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAC7C,WAAO,WAAW,MAAM,OAAO,GAAG;AAAA,EACpC,UAAE;AACA,WAAO,MAAM,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,EAC/C;AACF;AAEO,SAAS,mBAAmB,MAA4B;AAC7D,MAAI,WAAW,KAAK,IAAI,GAAG,UAAU,KAAK,MAAO,QAAO;AACxD,SAAO,KAAK,MAAM,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAClD,SAAO;AACT;","names":[]}
|