@uzysjung/agent-harness 26.169.0 → 26.171.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/index.js CHANGED
@@ -37,20 +37,18 @@ import {
37
37
  assetCliSupport,
38
38
  assetReachesCli,
39
39
  assetTrustTier,
40
- backupContent,
41
40
  backupFile,
41
+ backupFromCopy,
42
42
  backupIfLossyUtf8,
43
43
  backupOriginal,
44
44
  buildAssetEntries,
45
45
  buildAssetSpec,
46
46
  buildInstallLog,
47
47
  buildManifest,
48
+ claimBackupDir,
48
49
  claudeRecorded,
49
50
  claudeTargets,
50
- collectPolicyHashes,
51
- collectSkillHashes,
52
51
  continuousNoteEnd,
53
- copyDir,
54
52
  copyFile,
55
53
  corruptedInstallLogMessage,
56
54
  createOutsideGuard,
@@ -70,7 +68,6 @@ import {
70
68
  isAssetSelected,
71
69
  isCliBase,
72
70
  isCliNeutralTarget,
73
- isHarnessOwned,
74
71
  isInstallScope,
75
72
  isKeyId,
76
73
  isOutsideProject,
@@ -104,7 +101,7 @@ import {
104
101
  targetsInclude,
105
102
  upsertHarnessImport,
106
103
  writeInstallLog
107
- } from "./chunk-SU2RUN2W.js";
104
+ } from "./chunk-U4X4Z333.js";
108
105
 
109
106
  // node_modules/sisteransi/src/index.js
110
107
  var require_src = __commonJS({
@@ -802,6 +799,594 @@ import { join as join2 } from "path";
802
799
  init_esm_shims();
803
800
  import { existsSync, readFileSync, writeFileSync } from "fs";
804
801
  import { join } from "path";
802
+
803
+ // src/legacy-fingerprints.ts
804
+ init_esm_shims();
805
+ import { createHash } from "crypto";
806
+ function legacySectionKey(lines2) {
807
+ const out = lines2.map((l) => l.replace(/\r$/, "").trimEnd());
808
+ while (out.length > 1 && out.at(-1) === "") out.pop();
809
+ return createHash("sha256").update(out.join("\n")).digest("hex");
810
+ }
811
+ function legacyPreambleKey(text) {
812
+ const pre = [];
813
+ for (const line of text.split("\n")) {
814
+ if (/^## /.test(line)) break;
815
+ pre.push(line);
816
+ }
817
+ const head = pre.filter((l) => !/^# /.test(l));
818
+ return head.some((l) => l.trim() !== "") ? legacySectionKey(head) : null;
819
+ }
820
+ function rows(text) {
821
+ return text.split("\n").filter((l) => l !== "").map((l) => l.split(" "));
822
+ }
823
+ function keySet(text) {
824
+ return new Set(rows(text).map((r) => r[0] ?? ""));
825
+ }
826
+ var FILES = `
827
+ .claude/CLAUDE.md c8a0f19e0c84faec6fc0a999efbf205d72270a1f8bcb8d28894d564431405b52
828
+ .claude/CLAUDE.md c833b9a2b3e762563bdf8984520257579408de387ce3021ac2c606551f39aa75
829
+ .claude/CLAUDE.md 5a167acf64d246b278f878228f07cf76fe2a808e201d3d82c1c0274f50d736de
830
+ .claude/CLAUDE.md 7fbfe6999a82a880a3a0d34bdaf8e263cfd9c927ef5fb33873059182094d3f7f
831
+ .claude/CLAUDE.md 1dc65a4d8c0f2c47d541852f767d85dca41637d7b4009db6dbf41a0beb20ff68
832
+ .claude/CLAUDE.md db2c8bc13b4d9882c71ab1977e5fc5aa66fb031d5943faba587ce9eba100a0b8
833
+ .claude/CLAUDE.md dca32656dfd75c8676504836501607b7c821115a6dd14cba7baafb5c631cb1ff
834
+ .claude/CLAUDE.md 3aeeea1767e0f910adff08340895a524df829240df93d298bb3d0e327d0711e0
835
+ .claude/CLAUDE.md 16347e86e7c1699db212586685a640135891ec5787ba486e0a12562d85bc9b60
836
+ .claude/CLAUDE.md 71db56ff054601a03f8e287a965c1eb1be64d9db1fccc3b03c1f499c2f23119a
837
+ .claude/CLAUDE.md d92c6bbc4c2c1c7a2547e697f7f128b7918d4e2062c9af958b729e7cb0c58308
838
+ .claude/CLAUDE.md 7f94cca4a8ca2f257164ba9b44360d10f99030d3b636c667f65fd0a1771e123c
839
+ .claude/agents/build-error-resolver.md 28597d1e97a68701999704b789a97a20a28b121b7c27ee6a6e80ab43bcd514b8
840
+ .claude/agents/build-error-resolver.md a050a74ed31acba06b36e8c34bc35bc0e684b6b7ab01c891dfc2d13ba94f9824
841
+ .claude/agents/build-error-resolver.md 59f9eb1ae55c6a943d43610cc4e74d05018d681758ac19cb31d874c51605914e
842
+ .claude/agents/code-reviewer.md e112e5e0bbc501788e8a3bfd2771d34e31a8564bbd87391bff7401af1509782c
843
+ .claude/agents/data-analyst.md 1d75c07c51b8a000e536cd8a1c23e0609d93ea51b02cd48f9877610082cb2475
844
+ .claude/agents/implementer.md ba0ee71bf5ae6f8aeb461b0d6161fe0869a7c45820d49145b2abf27264af7dd2
845
+ .claude/agents/implementer.md a12f9132a55526208837e4960c96705df595c0e1cc2870eba1e56f2a87632de1
846
+ .claude/agents/plan-checker.md 405be32ad0d95d8d4cbc8cddb94e7b2be74981a4dadc160aa9200156b56d40df
847
+ .claude/agents/plan-checker.md 467ddec15189cc4273adacd08f97e7eabe1cc164901882f2dd5bba81fbf72327
848
+ .claude/agents/plan-checker.md f605005ba6ebf1e3494e47e257d68a2d34317d15a47b5c8b52317d9cf914b004
849
+ .claude/agents/plan-checker.md 86ad22297e6f1d75d42146685d759af10698cf763feb1c9484d1f0437262e833
850
+ .claude/agents/plan-checker.md 451181ce44f67f747f7190aa53932565df1ce6c934da4584f03f339502fb9265
851
+ .claude/agents/reviewer.md 4036905c0b8deff6555c9f6cbe962609c243d063c417196cca57fc6f87139cd6
852
+ .claude/agents/reviewer.md 1a0153a7fcc8a170f3f08f2a4e392ca72117d413d690441b930028ce923a6094
853
+ .claude/agents/reviewer.md 98c63bfe35afafb28b2543e5b3c662a5df58b66b877b753efc54b9af32208298
854
+ .claude/agents/security-reviewer.md eacaa50c2b2fdee398ba2e3c896c018ea8887d34701e5b841278ea2c1472cc19
855
+ .claude/agents/silent-failure-hunter.md 004ce0389356e0666d80c89cef39ad895ab326b3f7d19d34d23c70ab249a795d
856
+ .claude/agents/strategist.md b217c7bd273e672de44d4df123201cc5c10203fa9bafad84a370d4b7d32b7fc6
857
+ .claude/agents/strategist.md b8d0b387748b5403ee6a489c7e9969f864ebad9544d3d34ee9ff1b4d40f4662f
858
+ .claude/commands/ecc/checkpoint.md 7462e42241b4e38ca4826750567002420f014089b81406279248e22e5879f608
859
+ .claude/commands/ecc/e2e.md fdc040d9d32ca1c4bcc53ccca32042983f9132240fcaca5144de51e4385b9479
860
+ .claude/commands/ecc/eval.md 586ffc66cf889428ff46a86c0d8067f925d58899758eea5f129c9fa62ca55f5f
861
+ .claude/commands/ecc/evolve.md b4a635e1fc9e70f5f53c8bb36a1e8a385e249b5802afaffcc54e17cbacc1c8ca
862
+ .claude/commands/ecc/harness-audit.md ba76cdd30f6fdcf9b6286de753881eebe0e15306829bc3cf53af1591e4126771
863
+ .claude/commands/ecc/instinct-status.md 642b16d6b155f635b76800d78618f4525fc7db9fa298e947e7651e5b3d5fe85c
864
+ .claude/commands/ecc/promote.md b8e6e79dd43053a183b426e88e31631dfe6c812efd37241e26798b88b3febdab
865
+ .claude/commands/ecc/security-scan.md 440cb02294593a8cc86bb1e707fe43a28506f2833de6e20ec811cab12a543db3
866
+ .claude/commands/uzys/auto.md 39e06b3164527fc59bd1cdbf156cdf1f953aab188c68603bce7a396f51921b40
867
+ .claude/commands/uzys/build.md 3eed86d0f9bd8b60724d829a95578e7febab0043a6bdd6353993b2a55021a908
868
+ .claude/commands/uzys/plan.md e1c11ecaa804b6a333f669a8a375b06fc8ba245d9683aed0f172c3fdadf8b4b7
869
+ .claude/commands/uzys/review.md af4e3a4af894bc161f966c64fda60cdc83d815c174f3ad1f9c0a0d40d76a07b6
870
+ .claude/commands/uzys/ship.md 572a644f37c41e1844b95701fe116b2b6e71f22d0ef8593725ab2de24d61207d
871
+ .claude/commands/uzys/spec.md 91ec49031ec683dcd9c5a2befa9b8aca3c95ffb415928b95a2fefa113e102554
872
+ .claude/commands/uzys/test.md 63e58f69bb431a3995b9baae6f6c55f958ae0c46a3d89740ec551fab4b3bb920
873
+ .claude/hooks/agentshield-gate.sh 9493cb98409a5b7b4368307ecf535f39c32d933f8f012f79c9c2b6c2fa788012
874
+ .claude/hooks/checkpoint-snapshot.sh 8ef77c09baa3e24740b19a7d119b83e58559cafcd8e6ab4edfaacc4e42caffef
875
+ .claude/hooks/checkpoint-snapshot.sh f86e7f445cad48569353b9cbce6e162074fdb5224f4b0781734f5a200b94868c
876
+ .claude/hooks/gate-check.sh f04831db7fbce538cc9c9cbcada62a7917e621e6275365686c4849e39da14583
877
+ .claude/hooks/hito-counter.sh eb79ca20fcbaa070c621d5c985422e68e5dbe6b9f4c25f7bf81ae82fe77f6d49
878
+ .claude/hooks/karpathy-gate.sh e72ea3c24cbb953ea7786504362a45f8ec07473a89aeb2006bde2fe0fb6b51c7
879
+ .claude/hooks/mcp-pre-exec.sh 3a7b64149a45dca9e1c15a793e1e84cc28d1f7675d7b519c38cf4b4b27ef5219
880
+ .claude/hooks/mcp-pre-exec.sh ad7f79a13eae3b59e530f00c5ca37b05b01df6629d2c09034bb4f3ac55429120
881
+ .claude/hooks/protect-files.sh f18aef8b355be30afc14e097e8f152a0f00351342e9c1505859f400b445af242
882
+ .claude/hooks/protect-files.sh 0190071614b9466bd4f29189b99f839eceecb08b480f397e5937f8087e6aac08
883
+ .claude/hooks/protect-files.sh 416780a048c3bf674e4348e8982906ae768ca81480b46b56c0fad800d9f21d55
884
+ .claude/hooks/protect-files.sh 5408a423eaade935d984d187f6f93387869cba9423b4644120f30025df4a7b5d
885
+ .claude/hooks/session-start.sh 1c2ffddd804bc82bd456b6cf6e1566084f6c5b43d3596b418e982d073bdfdfb1
886
+ .claude/hooks/session-start.sh b82d19dda66419967708a9937746d03bdd099886f9e7ed9103254209017f1147
887
+ .claude/hooks/session-start.sh 894ebdc240b80b37da93b4124994471c1cb0a26d8592d7a621d8fa3a748e8c79
888
+ .claude/hooks/session-start.sh dfad4b1add755a7efa89cacd370ba418c629f64911d1e0da1bcb19502bbc5948
889
+ .claude/hooks/session-start.sh dcea99eab4c03341b2c571185e8433379e3c5bcfa5bed35d8f1979eee3a45c1a
890
+ .claude/hooks/session-start.sh 490b1d99f6d901d63e48a8122d67225f5c48cc835db0f410fed91c364666ba2e
891
+ .claude/hooks/session-start.sh 613156711d285ae2dc3ac6b1c26d72b7f92f90c80639c6aed1c5fe982bc68114
892
+ .claude/hooks/session-start.sh 238e4a9565b2cde0809fed3b49059fa5b3189ad1c2e86d4963fcbf5e73de7152
893
+ .claude/hooks/spec-drift-check.sh 48e2c5ed391086c842a9a647dcafe50d757513b993561c3137c9cf4ecf96f7b8
894
+ .claude/hooks/spec-drift-check.sh d5bbbfbc3da3bc3eb2d3ac06cc10dc02426ec9472f4fba68300b895c3ffb24ec
895
+ .claude/hooks/spec-drift-check.sh 43f01059a5cc1d779c9e80eb4207fb4e263d853f5a367dbfd45b0b77685d0e28
896
+ .claude/hooks/spec-drift-check.sh d979fffa4f45c4ac03940a70abfa63860bf2a22fbdac9469a43f64565a6abdd2
897
+ .claude/hooks/spec-drift-check.sh 1c7c81e4009866b204e03fd7c4eac87a1b66d7b467a747393dca2bab6793eee2
898
+ .claude/hooks/task-brief-nudge.sh a5bca92a610efab0d770c96e12d3f6996cb184ae0175005724635a9a920fefac
899
+ .claude/rules/api-contract.md ae12cbcacd6380d41d13ca87d0e6f5d589df9f8561f408618cb365e144afced3
900
+ .claude/rules/benchmark-parity.md 595149aec4ed190e7ea96c11c271fa6b743f7ad8c38910dffd2cc180d9a51e8f
901
+ .claude/rules/benchmark-parity.md ee33155ed0ac2c267d0ebacb5f89335cca7dd679c5b2d54ffec81c4738bd9be3
902
+ .claude/rules/benchmark-parity.md b602bc9fc5627ec1e964915240f15748bd13e5bd99737346dbd8d3d2e7c633a8
903
+ .claude/rules/benchmark-parity.md f98f9429c3250ba860b1d39ca304854de0c9e905cdd12fd068d57025e40350e3
904
+ .claude/rules/change-management.md d21082487eeef59664899f85df6098bd46e3c74dbba3f2ebf9e49cf5bab64c34
905
+ .claude/rules/change-management.md 62b7932f691664459d2d7b0971c6aaee22cfeb5f9103ca1145cc55631a072077
906
+ .claude/rules/change-management.md 264b79b82de252cfc933d63708ac8bdf9f18c74f388063d62960456dfa764e2f
907
+ .claude/rules/change-management.md 611bb3a89d47d5b43ac22de8be59edbdf9b700959901d1fa878dc0f89d9ec40e
908
+ .claude/rules/change-management.md d2977a144259dc744835d9bb6c3cfa8d1b390f5393c34d883ba0f67a6bc4dc3e
909
+ .claude/rules/change-management.md 20c548a1380f1e5e9aad5821e0bce0ae999a2dbcfc1f564a990bb30f3590d418
910
+ .claude/rules/change-management.md 0c46f2fe8a9045c14a93895a1614b068d04a4b0e96c183d46ab688e57885c6a6
911
+ .claude/rules/cli-development.md a0bfd2fe69336e8b3bf329a2029d926c96192bc24e784f22f7b99861358829b7
912
+ .claude/rules/cli-development.md b116324d53c7ed3b36d51474f17bd61eede610dff7abea4d3533656dd5cf9a34
913
+ .claude/rules/cli-development.md d5a267f7e22c9d292693857577c2e5819811147b561a38c15b450740d4dce98b
914
+ .claude/rules/cli-development.md 158b76f589d0588f07453b4c47bf9368117467843cda37ddc8bec7197cd671b3
915
+ .claude/rules/cli-development.md cc0545a04706c18304eb92c365d691986d511f02436f405894306e829b4f5b8e
916
+ .claude/rules/cli-development.md 27f7696b1ed13ae15f9e0bfb4bdf2206325f42d87cca6e1a6efcc76f6c9d32bf
917
+ .claude/rules/cli-development.md 73813a5f6f0111507430c1f83c9a099b870bc2a03c16a391817dc74444597eaa
918
+ .claude/rules/code-style.md 2e67fc49d4c8eacaa6dac9064cf566d7f3039509a5b6c5f03b5b2126b0043def
919
+ .claude/rules/data-analysis.md 81b87e41919f7a7a4b06ff459a127b1c44ff6804876856eac0b17da8b0568695
920
+ .claude/rules/database.md a421fe0ffea76ec480cd9cc90ef753ceef7bff3ad4f49ee7837786960b634ba5
921
+ .claude/rules/design-workflow.md 5b6a40c9004355420de2f647c782728e47eb6b01722d7453acf570b6aa002dc7
922
+ .claude/rules/design-workflow.md 49fc2c7efedac467d52031b3bedd7d33fec9c73dff45d20071a5456b82015cd3
923
+ .claude/rules/doc-governance.md 0618fa6f93d245cb46d42862b881fa840e54b196e759d748937d7098c60eed71
924
+ .claude/rules/doc-governance.md e3b2197e19609724e6301ff6035ec9d34e5a60b5385bb4f48a2b633c3c3ca2d5
925
+ .claude/rules/doc-governance.md 5569bb742cc2c0666b2431e3eb662d5fdeb99f8f7224506e0379ce618c4bdf61
926
+ .claude/rules/doc-governance.md d83292a77146ae96e7adcb2dd796811ad8af979526e8d81b1a33bce8cebe6b7b
927
+ .claude/rules/doc-governance.md 54157849bbf9897969745f1d162181b93f4dd9260ae18d4a890b6ad014232797
928
+ .claude/rules/doc-governance.md 8d508bf15bbc1e87b8d0820a6e54a6d5de4b243ae8859ee98b6657d60dfbc7c7
929
+ .claude/rules/doc-governance.md c6f2bb6210ae076acf15e6928a8bb20adbd9d40727433c462d5e1b5eb73803de
930
+ .claude/rules/doc-governance.md 5257f74d94188b18977670a4299658673e00d7b227c54ebb19e00ab019d9adfb
931
+ .claude/rules/doc-governance.md 364c147096985e1fad1a03457ac864a6e4839d8d9487a5c7f20fbaafeeb43eaa
932
+ .claude/rules/doc-governance.md 589e70962b426503098c5d1a60da4b11f9a0c97a654d3ccac900d58f02b7fb52
933
+ .claude/rules/doc-governance.md 7a79bba801a1fb3695566bc19c9bd2f89456219560d76ac4f2bcf405c238b6a1
934
+ .claude/rules/doc-governance.md 3360574fb55f332cf3de18ea32506326fb2880651a593c0bec0246337633fe80
935
+ .claude/rules/doc-governance.md c14e70d6fc5b0ca8e9385d293cccdc39512ec6e73fac6f34ed723572acbebd97
936
+ .claude/rules/error-handling.md 5ea6a188e3a925d5f1f38c1defb766bf71af7a408b58ecce208c7fd3059aa843
937
+ .claude/rules/gates-taxonomy.md 2276e03e41463f946d5b4867c953579aab91445bb781a4d24ecd9337d2f67e3d
938
+ .claude/rules/gates-taxonomy.md 8a5162bd03f20c92230e2bd84a879de5f5d24a5746e6489f3dc895891055f4a2
939
+ .claude/rules/git-policy.md 932e24935cdd20f088b4a3a75a493cb454e065c75e294283fd01a7a768bf9059
940
+ .claude/rules/git-policy.md 3c0cf97e7ec6d921cb8b066d6d34a9f70f57fbdb0c78e9ab11cb919a4251da0d
941
+ .claude/rules/git-policy.md 0d8d01c7b471b4623be31221e773b7298bf230aa102394e0384859db5393b0c6
942
+ .claude/rules/git-policy.md 1c83e7e9acd5967d31406fec70a231cd8de09d5931eb7ca8ebed68f428c3d081
943
+ .claude/rules/git-policy.md 421e837c323bed48fd7e51d02e3c27dd479037d67ecce9dbcc0da3b8c1e764b3
944
+ .claude/rules/git-policy.md 02e127647bee532d0ffc5b994eb2f554f42e96b10529618f953e9b27a3ec0ab2
945
+ .claude/rules/git-policy.md c4f5c1f4b33f3f91aebe673b736b3dc76db785a225d0214f29ab6ceccec26550
946
+ .claude/rules/git-policy.md e1ebe8b7abf6e61015bef44c6f848f13ae8cafe16384b765b35905efecf0a21b
947
+ .claude/rules/git-policy.md 469077325ea8c4a67dfe827014370bc099494998f55aa76f864f6411b40b07d8
948
+ .claude/rules/htmx.md 4faf811dcf5b2b85f899507b249db4853b328c73b2d45760106c1b2132b53c83
949
+ .claude/rules/nextjs.md 855eb2f49b830a89ebd9b92f29a05ea3348d6dda032d2a267c15a5c762371ea5
950
+ .claude/rules/no-false-ship.md 964853f056a2ed898b09648faaf5302d471d3e723dc8e15d387e32aba82eee14
951
+ .claude/rules/no-false-ship.md 2d760fd1f3387233703c04127bc595874a835ea46a50055d7c506cdbc7ece5cf
952
+ .claude/rules/no-false-ship.md 9098f58fe799367cb6cfc02779715b02c4f0a2e5c24539fb2d65f5eb36332762
953
+ .claude/rules/playwright-launch.md 770232379a9205f0466804b83edb4f53e1ccef2fcbc5e82b8e0c5b0a96fac783
954
+ .claude/rules/playwright-launch.md 1bf90d35a050a1f0d712f406329d687bf9bc7661ed0a183e83613bfaef895820
955
+ .claude/rules/playwright-launch.md 783be44da36bf9eaef56d0117d6cd19b7c0c1ef9242745b3f3f87721e84f2664
956
+ .claude/rules/pyside6.md 19c092d5fd7691818e9a759bb23173c9aa1f511c4766a67a9e7fa918b7062852
957
+ .claude/rules/shadcn.md af1fc4624cdede22a35d087b8a7fb4ef042302e2c998373c73f018736e68e5a4
958
+ .claude/rules/ship-checklist.md 14319e7639aa4827690099efd5ad228c997e56a9002eeba6c90e7f274c44bb12
959
+ .claude/rules/ship-checklist.md d371f417f9af32a72afa99e38378d5a95a085ea5c8ad106328cf41a5f61cf117
960
+ .claude/rules/ship-checklist.md 27d3885cc3944a1a4d92dbd8e0d51ac943d7971d2284db77244deafe007c2906
961
+ .claude/rules/ship-checklist.md 286fe3b971c1aa72bb41f610c576ef6d23a9778ed4ab4cbbb493d1f241494a44
962
+ .claude/rules/ship-checklist.md e5d1dc357c54026f473ffba52b6ae3746ef49173b8270fea6faf16fbc2ec7b10
963
+ .claude/rules/ship-checklist.md 6825d1ce40a817f9fa004256b17b0685eb1ccb69ea27641c97e53dd5e323e755
964
+ .claude/rules/ship-checklist.md e713a2738dad8a03d16f0cb1b24a7b207bb4a1445766f883391a0b4a8ad6a766
965
+ .claude/rules/ship-checklist.md 0d9988b3d6b387dd13738c5f1667a8f07e72929f5515168e2ef6e94ad4c0da88
966
+ .claude/rules/ship-checklist.md 1b42169da6e29bfee8ea695831154f672d7cabd8816d1993678c4955a43ed1e4
967
+ .claude/rules/ship-checklist.md 9423f862188ea0ddcb5ec9594e457988c23dc80a0bdba7ed6cc7630f8cc4623e
968
+ .claude/rules/ship-checklist.md be7e93da56b688c9b90d5392dc29b16197d5d4857152f073eb39d1c737952a02
969
+ .claude/rules/tauri.md dd429a144c71f1205384abb5c305b373908e8f6c2c4ae7d6b4dfa9fa707e972d
970
+ .claude/rules/test-policy.md d775453a9b0c3fd7f65eba039e512ec6fc92eda448a2218738da6bb1c85a3023
971
+ .claude/rules/test-policy.md a106e6952520eaa2cebedb9a20a47ee12a8bd0069639077f324cc1d93e3d5501
972
+ .claude/rules/test-policy.md 1b6f0c2fab4842c974c11dd53b22320fb5593870390e0c375625ec584196e580
973
+ .claude/rules/test-policy.md cc73a7e656c237c4313bfa814a9eadab5a44542ff8dbf250fceaf97d6ae1f05b
974
+ .claude/rules/test-policy.md 87660a29cbf43f49e24fd7f53124f0383c94c8333138d1cc1ab9dc233b5e8a4a
975
+ .claude/rules/test-policy.md 54426764c08cadc8c1b31bdaa5817f5c4aefc359d263bd13051949aec7858c0b
976
+ .claude/rules/test-policy.md 9f1e71e12db41a3b20653854c66adcb224c47e86d6a5815d3b7002418cae458b
977
+ .claude/rules/test-policy.md 95b9c6af6a63e4cc85488e109f42440b7f7d1892c73d5b96b7ab387354303cf4
978
+ .claude/rules/test-policy.md 3460db5bbfaea281e417c55b5a76236b10c64c38cf38fe16b934f6799c1268b5
979
+ .claude/rules/ui-components.md 836b66be19c773b3cfa2ee92ed29bd802d8883a55da21cf0be9b73e25a2c8ebe
980
+ .claude/skills/agent-introspection-debugging/SKILL.md aff0d52785d3013d9f98de95ca778bfcab7c566db20352ead42852a1659292d2
981
+ .claude/skills/agent-introspection-debugging/SKILL.md f03dc3838ba8b81ad6a87959a2d666f8d5915d3516cea60d12a65ce74685c54e
982
+ .claude/skills/asis-tobe-decision/SKILL.md 8880ea604b8458acd30b8dc515efc54a6a1640590c572d5f4be7034cbd2d7242
983
+ .claude/skills/audit-harness-fit/README.md 885c756b8fbe51d23747667d3d96ba8c76e1c0e5fb48030cd394b0dad4c304f5
984
+ .claude/skills/audit-harness-fit/README.md e1295cd42a0671d373d7d071374918abdc37aa294052ed6bde8511ac07e80039
985
+ .claude/skills/audit-harness-fit/README.md 2aa6eb69e9991d0156b140f5c4ca36dd87b32ad006aa8dc176f173e7a864ecb9
986
+ .claude/skills/audit-harness-fit/SKILL.md 6919e5c6fce93b4262392ddd9e0476b700e921e39a91ab646bf48f05f4fbe39a
987
+ .claude/skills/audit-harness-fit/SKILL.md ccf5817e3ebcc7575e409b4a1c397f2ef23d690e355490487b2bd829736d1ab5
988
+ .claude/skills/audit-harness-fit/SKILL.md 85c489aec23eb0b9f907c3b10395dabbe03add8300f6d365e886e02f833fd769
989
+ .claude/skills/audit-harness-fit/SKILL.md 5f20eaac20e77c3207978857e2424bcb86d5681362528eed7cc3a09afdffb845
990
+ .claude/skills/audit-harness-fit/SKILL.md 3c348fe635f8b389a349db3f686ebca4f49e1ef7058aa3b4c6e0211e51c12562
991
+ .claude/skills/audit-harness-fit/SKILL.md 9dfd4565013a630c0aa66c9396b4d4073fe70f8601bff89989ebe162da7beb76
992
+ .claude/skills/audit-harness-fit/SKILL.md a23c98c78fd9c5f4660dd73058d58eaf78aa49c107bbc0e3c60de73231f5c163
993
+ .claude/skills/audit-harness-fit/evals/scenarios.yaml 016257464f98f62433e2aba5a064405f41128b54eba05e3702cf44ce93d97ae6
994
+ .claude/skills/audit-harness-fit/evals/scenarios.yaml ac6355013fb7be658196e135ac449bfa5afdc564adb1d5d1ed10d65a64d16e04
995
+ .claude/skills/audit-harness-fit/evals/scenarios.yaml 714fe2ef457dd38fc9b3b354b7f2d616c5d7121770ef1dd889d008eaec661159
996
+ .claude/skills/audit-harness-fit/references/apply.md e4803a5d6d6c0d32c9808172a03f0f555320ef29fdf73277dd4fc535c4474d77
997
+ .claude/skills/audit-harness-fit/references/apply.md 97886bd748ef2e2d9a979dd0cf65c5acf81a90c2a19d08851ce87c66df933f92
998
+ .claude/skills/audit-harness-fit/references/apply.md 6f38cb6673546bfe6609ff6ae18436e5487f93386ac1bfb4625f9c85997c36e6
999
+ .claude/skills/audit-harness-fit/references/audit.md 39d077f20624e69582ac0e2df28d192c08e8246f8c7dfb8e471e7c461f1cc5f0
1000
+ .claude/skills/audit-harness-fit/references/audit.md 973605514d4eb560b79ff267058e6dbd47bd61b53752f3f91e7f49a4904eac4f
1001
+ .claude/skills/audit-harness-fit/references/audit.md 328d620140846005bf326998387e9305371bf95b5e95cb3a9e7272377e26277a
1002
+ .claude/skills/audit-harness-fit/references/audit.md 4b4eef5a9060ad0a7fb264b261c69802c1c3f0e7c61787ccca18be898c84c091
1003
+ .claude/skills/audit-harness-fit/references/audit.md b93eba13d00481e391fc05e70d894164661ae689f28b08d47514e2c35ebfc790
1004
+ .claude/skills/audit-harness-fit/references/official-criteria.md b80c77f12fdf1752f671eb1c0f8edb6f4f34c3777d6753b9af713f91cff77222
1005
+ .claude/skills/audit-harness-fit/references/populate.md 48227ddac79899f725769050f6105aff0b352d47f72b01341e836362db943ad3
1006
+ .claude/skills/audit-harness-fit/references/populate.md eabe235e5e097ad44a42588201ed89dca359e9fd1a301e29be02deb0338d38cf
1007
+ .claude/skills/audit-harness-fit/references/verification.md deff70dc0f419a760e8b280a2ae57b43a0ae859c17fb6627ea749741b855ff62
1008
+ .claude/skills/audit-harness-fit/references/verification.md aa367e0655a98ce575919e5c30687713187346a7976b97fc1081a99bbbef6ce3
1009
+ .claude/skills/audit-harness-fit/references/verification.md 18bd5f577b85cb043229ca9df160ec9c0f3fb70d01c3db5abb5ccbba7ae6dab1
1010
+ .claude/skills/audit-harness-fit/references/verification.md 695c1f08c6bee5de28d0edfec796df12ca5fbff8cdb473bafbda6fb7f3cff79a
1011
+ .claude/skills/audit-service-gaps/SKILL.md aefd2a47a0b9c2b6bd1846151e60cef0147d0bfbb4b1933da2431f980a0f2922
1012
+ .claude/skills/audit-service-gaps/SKILL.md ba9ac6df2315538935aff945f25a7596b3a9e478c4cdcb3a218ff404f4b612d7
1013
+ .claude/skills/audit-service-gaps/SKILL.md 81323cc7d4bda9f3739e8a72f566699d209f888aa216f77f106e77db975f231d
1014
+ .claude/skills/audit-service-gaps/SKILL.md bbc7782ea688ae74f12934145c1041f58a029eb07d9979c316849cb9fb88f56e
1015
+ .claude/skills/audit-service-gaps/references/modes-and-evidence.md fa73b3edf2c55f5caf47f1f2d930d8c2ccd737af0a2def5f40a1db942029275d
1016
+ .claude/skills/audit-service-gaps/references/worked-example.md e5f31f498e864dcaabe2a0d6a05fe1d64cd8be6a1b79a18de78bcbb42e06d658
1017
+ .claude/skills/chatgpt-delegate/SKILL.md 0e614e643bb8bad52db3663ff9394f77ef6c89a6c72c529796deeed824eca0f0
1018
+ .claude/skills/clear-korean-communication/SKILL.md a8afb5e0114332fd7831de12a12cd981e95726458df61f1a3fdb6160086aedc5
1019
+ .claude/skills/clear-korean-communication/SKILL.md fe480eb8357a79abcc1f6c5954c61f0a8b3e7479b9835b7fbdead18a9dac4b46
1020
+ .claude/skills/clear-korean-communication/SKILL.md a8a41bd88b2b04c931047f08614f8c431eb8e401fe366d110291201a9fb571e8
1021
+ .claude/skills/clear-korean-communication/references/pre-send-checklist.md 65f93177e9a442332c2c90f4e905c4d558b6349e47d5c86eee7705b76b71a1b9
1022
+ .claude/skills/clear-korean-communication/references/why-it-works.md d4439db6a1765fab5fa6875d0528fe715366c270ea35853ad8bb1404388200cb
1023
+ .claude/skills/clear-korean-communication/references/worked-examples.md d53369cf02c62147510bae3a0cfa878ce89013bed11a30c32cc675dd0a0df825
1024
+ .claude/skills/codex-consult/SKILL.md 0af4f6cd00739e62d73f7adc9c8ea85b0db3ee53432bc5605fbf6bf788a0cd13
1025
+ .claude/skills/codex-consult/SKILL.md 288d6d5f4135b8f3e1d8b06b42b68c238e63c9257f973ecdc4f86445d5619be1
1026
+ .claude/skills/codex-consult/SKILL.md 767b0d65ff96f0a10c2cd81335a5ded2a85394b8e8ab3aa1c7737c6296f22582
1027
+ .claude/skills/codex-consult/scripts/codex-ask.sh 64835a9e11094bc24d9e8f9044a98a699ca45155d516dba926ffb8c145cbf1f6
1028
+ .claude/skills/compaction-handoff/SKILL.md 1b19203ae45869dd43b7ca4b28c63bb5c4267aefc9a62abb17d29bdceee316a9
1029
+ .claude/skills/compaction-handoff/SKILL.md b484a4408f25f4b78bf2e7cd88b1b3fec7b969d6c4a03de8a3b8e072719fc74f
1030
+ .claude/skills/compaction-handoff/SKILL.md 42d8300cddc4bbe78b353c8ff846b5fd8ed2b4a68f1998982900fd31596c1755
1031
+ .claude/skills/compaction-handoff/SKILL.md 59191dc216233b74e5f5a72ed1041d8c236504a386e8b5df81dbf52062dbaad9
1032
+ .claude/skills/compaction-handoff/SKILL.md fe70142b0bbf21b5a7b6e9e5af8f0011e6b8e654746f97f4391384d5143c9701
1033
+ .claude/skills/continuous-learning-v2/SKILL.md 7629ed1a18ce078b57f22f407115a41a7f404aee73de17667ec5571b0001a8e4
1034
+ .claude/skills/continuous-learning-v2/SKILL.md 75485ddb37e2d1d6f056b404d9939bf46a2b409b1286aec209a510112e5b64b1
1035
+ .claude/skills/continuous-learning-v2/agents/observer-loop.sh 25ceefcc260c3599b6d12148d1a573110ace3091c723bdee3a3cf10692e5d4e2
1036
+ .claude/skills/continuous-learning-v2/agents/observer.md 320d50ff2965efeabd6f07c2be134e11da160293e958c1bc27c309d613839ad2
1037
+ .claude/skills/continuous-learning-v2/agents/session-guardian.sh ee6e4c0990f8d538cae1c85aa194e01e9ca2336e5d993ef68eb608b96cb0d82c
1038
+ .claude/skills/continuous-learning-v2/agents/start-observer.sh 6b1966e38eb0c96d61de5d204b87097592884364be59e5bea6007289e8068936
1039
+ .claude/skills/continuous-learning-v2/config.json 6f17e7a99aafe252ecd0b9212fb5f606be50b8ce1685f5a6435e46e87ffeb970
1040
+ .claude/skills/continuous-learning-v2/hooks/observe.sh 285c673af5a5f5fcc0839e924ccdb4b7326c593f9198f690c635b2fa7091feb6
1041
+ .claude/skills/continuous-learning-v2/hooks/observe.sh e632c2f486248b8b141059c23586d5ca0187df8c34f53d7597cd845713e06948
1042
+ .claude/skills/continuous-learning-v2/scripts/detect-project.sh 527917b87d8b35b38baf01ca96c6fc4cbeb91ed9a24c6f9c09e4f4d290555295
1043
+ .claude/skills/continuous-learning-v2/scripts/detect-project.sh a39677edd0ce1deb7628ed31610baafc16090a710233963d2b6a55db64167cf9
1044
+ .claude/skills/continuous-learning-v2/scripts/instinct-cli.py 777193745515c8dce35676b1696c90a85e2610705157205a0b54e8e453b69942
1045
+ .claude/skills/continuous-learning-v2/scripts/instinct-cli.py 0b060a762f0fc4ad56e45cbaa14c14fb9ab3f468763f6a32a17bb864fbd3fc30
1046
+ .claude/skills/continuous-learning-v2/scripts/lib/homunculus-dir.sh 99dfa99ae0f7c6e2e15ed5b5bf3c46e91a5f089f4060ba93d5da5e85e04fd308
1047
+ .claude/skills/continuous-learning-v2/scripts/migrate-homunculus.sh 34f121c143a676d330f06209618aae8cb08f8155298cb5fa70c893314bf9b303
1048
+ .claude/skills/continuous-learning-v2/scripts/test_parse_instinct.py acf556e8a8023347b35988bb490927a4e9f65f2579b03f26cad610687656605b
1049
+ .claude/skills/deep-research/SKILL.md 2d631427fc9aba27a318735f930bdb49f5c6ce416506340025eac01648f1df09
1050
+ .claude/skills/deep-research/SKILL.md 69be70af34e27112f192cbe201210c4cf541c24952670487213de715aa297b40
1051
+ .claude/skills/deep-research/SKILL.md b852bebb31908f352c5397bde6de06ee7ca3c0e1c866fae4dedbd5c4fbb27323
1052
+ .claude/skills/deep-research/agents/openai.yaml cb877e559cfe405d0e12bfc9bcaef657ab05a30ef698e6f3e486e39c5e0a6d2d
1053
+ .claude/skills/e2e-testing/SKILL.md f84ae93c159bf6ecef5a60c9735dc82d96bc5f977ef52eb71678b66722acf684
1054
+ .claude/skills/e2e-testing/agents/openai.yaml 5f50508a300616d77f39dc50a51de10cabd05ef7ae770e7948ef6c28f340cdf2
1055
+ .claude/skills/eval-harness/SKILL.md 03fc47842b35861231efdd1d15de291942915c372b8ffcc05a22e942d1233ced
1056
+ .claude/skills/eval-harness/SKILL.md 98f10c6cfc7fd041a358093f5c700b489d431879b90db778db7623a3c1c5404f
1057
+ .claude/skills/eval-harness/SKILL.md 12399b9de9e044273fd4dd6a8177bc4f17db008a33df83557ef56770b1466a56
1058
+ .claude/skills/eval-harness/agents/openai.yaml 6f2c19eb59062ca776286c2f6a6a7f1b552b4f927a2b48de888a7dbb2eeec6bd
1059
+ .claude/skills/explain-plainly/SKILL.md 7838e4f78b7d4d46d31a7c932f069fc66ec4bcd873e49fb57f37923be188fd0d
1060
+ .claude/skills/external-model-consult/SKILL.md b1d7db909a459708ed721f5a7eeeeebe48fb3d57448f763e7e0000df5426a241
1061
+ .claude/skills/external-model-consult/SKILL.md c33d6d2e02aa9631fcb4e91aae83b56834307b0290a984af55378d2f0011d7dd
1062
+ .claude/skills/external-model-consult/SKILL.md 24cafc7b50fde21eb874fc2178d3ebd7eca3b1c54e9278d60a088f9079b7b534
1063
+ .claude/skills/external-model-consult/SKILL.md 2d115e29ad53d778ac4d4f942ec2039b441ecc571d612d27cc725695641f4db1
1064
+ .claude/skills/external-model-consult/SKILL.md a4d429bcd4737fbb2a90de6b9beb9cdf0488fe385eb0f6fb395f0e76322dbb0e
1065
+ .claude/skills/external-model-consult/references/direct-calls.md 6bf094fe54bcb09285ba552371eabf1d743545eccdb118157a8aea987b7c8147
1066
+ .claude/skills/external-model-consult/references/direct-calls.md 966db9c1f80c367ffc81842b83326a67190cc97287e7d624c0c16ee65fd4b16e
1067
+ .claude/skills/external-model-consult/references/model-evidence.md 27dfc0eaac4bbf97dbe4430852e947d99a7bd895aeaa7f1d565915c2a2fdef1d
1068
+ .claude/skills/external-model-consult/references/prompt-templates.md 2277839d5eb9ecbf8535f51a1f1ca97d55816e47b6ea578f34e9637280b1b5ea
1069
+ .claude/skills/external-model-consult/scripts/codex-ask.sh 64835a9e11094bc24d9e8f9044a98a699ca45155d516dba926ffb8c145cbf1f6
1070
+ .claude/skills/external-model-consult/scripts/codex-ask.sh 75f24da435dff5ea7ed7636d8b121456d1e91806e9a114ea993862e173d84d72
1071
+ .claude/skills/external-model-consult/scripts/codex-ask.sh c644272c25f3d0d42ab63bc8c4acf81c1ae90ce1af5f7780977dd63f464fda7b
1072
+ .claude/skills/external-model-consult/scripts/gemini-ask.sh 6736c394de1d059908ebed770221b298f37d09e01aa3eb58991265c2d45915cc
1073
+ .claude/skills/external-model-consult/scripts/gemini-ask.sh a598276b4e2d9e4a54b704229822d8da49a6c54497c8f2471af5420b4c3017c3
1074
+ .claude/skills/external-model-consult/scripts/gemini-ask.sh 9c5eb72c61d140228dff796ff599f88982b76763abbf52aaa31486ddeb03e9e8
1075
+ .claude/skills/gap-analysis-e2e/SKILL.md ef28d245d354c07de60244778469fcc344385a584eddba33c67e9b096654ba4f
1076
+ .claude/skills/gap-analysis-e2e/SKILL.md 739e9a181eac99c749b585fc4f2b64a30dcd4783083422afde64ea85a9a2b062
1077
+ .claude/skills/gemini-consult/SKILL.md f096ad4e6a16e4e6d33ff7d817382e3f8c07311fd7c1f809ae91a951a9f58856
1078
+ .claude/skills/gemini-consult/SKILL.md 24c7f1a8855f5f44447015990991c47c80dc14fd9e373dd818084f8870ff8004
1079
+ .claude/skills/gemini-consult/SKILL.md 9ec900de7b699b73addd0e6259b4a2fc8bbf559d22662842424a1402cb581793
1080
+ .claude/skills/gemini-consult/SKILL.md 81cf4563f9fc790a64796a24e6f85f3b33bffc7e31989dbaebedcc2452984805
1081
+ .claude/skills/gemini-consult/scripts/gemini-ask.sh d9f4cc371e3e3b1919dd773d41b351484c9a3612487bee1ba314378a8486a39c
1082
+ .claude/skills/gemini-consult/scripts/gemini-ask.sh 87b065bec8c13b28dcc451235dcf9679d97af4f68ec678153e23ee14f7c94d0a
1083
+ .claude/skills/gemini-consult/scripts/gemini-ask.sh 6736c394de1d059908ebed770221b298f37d09e01aa3eb58991265c2d45915cc
1084
+ .claude/skills/gemini-delegate/SKILL.md e3d569add39fecf8c412d76c287e88054710f70b87ac38d88c20ad7b00b301fa
1085
+ .claude/skills/genspark-delegate/SKILL.md 3f495d55f1968730c9ce66009745530c16adee5110267e7b735f6a0333e2bcf2
1086
+ .claude/skills/gh-issue-workflow/ISSUE.template.md e004bcbf6199486d527feea43aa68be108bfc8fd3b13800bc543baa778291dc0
1087
+ .claude/skills/gh-issue-workflow/ISSUE.template.md c30fbe41d1d6ca15a82c031441c8b20a6db20cabd3d52c9c2c7ec70ca97d60fa
1088
+ .claude/skills/gh-issue-workflow/ISSUE.template.md 6c897058f7cfbc7b3eb5a3688f21a106fda39b7ded0f05570cff349214aca427
1089
+ .claude/skills/gh-issue-workflow/SKILL.md f2b906076945adce0cfd80dd9331420eea0bd1c1758b76e1f300c82811b06f50
1090
+ .claude/skills/gh-issue-workflow/SKILL.md 99736619cdb450870fb8b51e9ef3375fdd4c625b5d23e37c75078e40b1f1dd32
1091
+ .claude/skills/gh-issue-workflow/SKILL.md 00dc33d939b034dac6c653a07a64da5b362e350bd9133def2f11420c94353008
1092
+ .claude/skills/gh-issue-workflow/SKILL.md ea1012246121662e0604e296913b63e7ce5a02b902182c86102dabe5e13563fd
1093
+ .claude/skills/gh-issue-workflow/SKILL.md 0b1ce6a696c1388e928240ec787b449dd0b0a92749789fe28f8627fbb9aee880
1094
+ .claude/skills/gh-issue-workflow/SKILL.md 578c25a3687c7c6557c2bd115489f44a49d5668e2cb97e114a009d7066db7a72
1095
+ .claude/skills/gh-issue-workflow/SKILL.md 7128c3faddf72ce6285b3a0e7179da01d6a434659ccef6f9b51851b3f5f50d17
1096
+ .claude/skills/gh-issue-workflow/references/hierarchy.md 0e16e38d5919e5eed0aa044899895fb13c3846a333f3470d8001bda09ba959c4
1097
+ .claude/skills/gh-issue-workflow/references/operating-model.md c6feec997887d05d3385441258c464609e52c4bbebb7c0b7390a5d6016ad9a0a
1098
+ .claude/skills/gh-issue-workflow/references/readiness.md 08df321b014d96f8d5b5e0c7af7f36d09d719fb098bdaec7928af4a77c1866cb
1099
+ .claude/skills/harness-health-audit/SKILL.md 12932ba2e5b77a7efab0ae2d0fca334bf249503a583de5a3933105fba89c94ef
1100
+ .claude/skills/harness-health-audit/SKILL.md 6a843a002f9a7ca9c04816214509caccc1cc3f3661595939cc4096366847f09e
1101
+ .claude/skills/harness-health-audit/SKILL.md 16501e7db181780684612f4f38db80861e775cc1204f444169efdbc5d9165bb3
1102
+ .claude/skills/harness-health-audit/SKILL.md b481d7844303896a24a3a95269a035ee57690d8c15c5ad5e85f16227fa5f7f27
1103
+ .claude/skills/harness-health-audit/scripts/observation-digest.mjs cfedf96b819819142f2d69bdd6663bcbdd56301c429545b4f31cc2f358249cb8
1104
+ .claude/skills/harness-health-audit/scripts/observation-digest.mjs 7c9c42fed4c3ab1a0df6363386c34e03776a53f6b32aa1bef3c6899dcd77aa5f
1105
+ .claude/skills/humanize-korean/SKILL.md 7d0d77c25f5990f203480abc6a02772d534a40ebfff4ab4086cf5da3ed1b7afd
1106
+ .claude/skills/investor-materials/SKILL.md 0541ac8ede8d39c59a646834481d0de86312e598331b86dc7027ece0def3c193
1107
+ .claude/skills/investor-outreach/SKILL.md ab71ee37a1ff5f9b7f27908d33fc47f03e7897e6c87be3f8d7c0e4a8d73fefdd
1108
+ .claude/skills/market-research/SKILL.md 7ef3fa13d6272fd295eeb2aca9f6a2f09f6a8270cf33b0675fc5a0018cf6e406
1109
+ .claude/skills/market-research/agents/openai.yaml 42e1b1bf676506bc50027131cd8226b151f48a57551197308102dd01fd080943
1110
+ .claude/skills/model-orchestration/SKILL.md da6b448d0119e7dfd441f47c9afa267c227ac9301561ddf07c3fdc81bf494ff6
1111
+ .claude/skills/model-orchestration/SKILL.md d4ec28fa8e7bcf48607a17dc84a9fb873dd9f58f6ffef38ae5324f94477160a1
1112
+ .claude/skills/model-orchestration/SKILL.md 2282ac0ca6c06006d36616295dbbb78bb593b93e06712baf8be6b6466426c540
1113
+ .claude/skills/model-orchestration/SKILL.md 7a60184798f2c7ab28d81af37bf61d4785b0831836f0be1e933d279c1b552161
1114
+ .claude/skills/model-orchestration/SKILL.md 2210704bbb4ae972e74f1062512accd53882a405fb91adb0461ad9a2325774a6
1115
+ .claude/skills/model-orchestration/SKILL.md a4447594e45e598028c5f614cc46d1555c6cbf20e0edce1c9d8a30ca17798268
1116
+ .claude/skills/model-orchestration/SKILL.md d8d1982fbc76d572b2f6a3ffd07cba944d3e734987fc5bc66002de5f86accd3b
1117
+ .claude/skills/model-orchestration/SKILL.md a67b5948e8072a87669621feda866198ffadd7c76979fdc3a0b1d8cf2a4ac8bd
1118
+ .claude/skills/model-orchestration/SKILL.md 34add0d50a3ed2687b4ac092a4146928b841f5f6a8bbbfd09a42e2b59f4ef2cf
1119
+ .claude/skills/model-orchestration/SKILL.md 6320632e46180275ee36a9e0897fd616ceb1b8ce7ffdf0647c871b293ae5c846
1120
+ .claude/skills/model-orchestration/SKILL.md a22190744eccc76d1f2d8995a1d66395e4ee173935b2114a334eff95239c234b
1121
+ .claude/skills/model-orchestration/SKILL.md 19d64858ca267987eb9be129a588b7e76ed86b7f788accf019e3f2c2db493117
1122
+ .claude/skills/model-orchestration/SKILL.md 65f0e657979e6b6c0fdacdd5b2cc73f73bb0cdb866c7c8ae1f099c54747c46fc
1123
+ .claude/skills/model-orchestration/SKILL.md 78cb2ac0f153afc4deb3ccc88b40abc7bab1f2b9ab8138d2fd3998082f380178
1124
+ .claude/skills/multi-model-debate/SKILL.md b6e4e4855ca36bcc2a720d926be68008259e0604c8c9dcc8b00cdd65c2bc48ec
1125
+ .claude/skills/multi-model-debate/agents/openai.yaml 64eb836095ff28669ebfd41d133a63e29faec0542dfc90862e10ed7fd275071e
1126
+ .claude/skills/multi-model-debate/references/adapters.md 06d54103576b4de93b9856c26f773842cfbb81b9491fe95c55354bd324fe4c51
1127
+ .claude/skills/multi-model-debate/references/evaluation.md 34f14eeecf6912c8afe5eefa1431744351b7911525d0f6eb612120d31170b290
1128
+ .claude/skills/multi-model-debate/references/prompts.md 45b348552563a499e221edd6fd75fb1d8e77b389aa25a9cc56688547e68434dc
1129
+ .claude/skills/multi-model-debate/references/research.md d99201f4774c7b3648e5bb3953c2e2979af752c1ccd8fe5013c8b6bea155e949
1130
+ .claude/skills/multi-persona-review/SKILL.md 8fa6a0d476b50e1c6b14fc62c31b03c8ea983711d24cc21351589ebc8f1dcd57
1131
+ .claude/skills/multi-persona-review/SKILL.md f0ac01160ebf176617cbe9d606a2b258eef8741c05b4a972f813ac90dd6e9dba
1132
+ .claude/skills/multi-persona-review/SKILL.md 4f221fec547e838d4698c4e2eaf82d6ebabbc52004be8933131e84add66fb397
1133
+ .claude/skills/multi-persona-review/SKILL.md 5339755ba6c7d61b82a1e06f1dde8ef46f5ee263092a7cc5cc6d300ae9bc79b0
1134
+ .claude/skills/multi-persona-review/SKILL.md b23879e303b7d0c95d30a9369f20b1928f93c24114b891a29f8bb17816cfac20
1135
+ .claude/skills/multi-persona-review/SKILL.md 29b1022bf403005764c95670b6d29ea3695ca935261afbf248c452058b2e2d56
1136
+ .claude/skills/multi-persona-review/SKILL.md 17f9f1a0c224168fb4c773f87f8ad84a4d7db9082088e096262aa71e1916f0f6
1137
+ .claude/skills/multi-persona-review/SKILL.md 6da7f5ccfae30d3d7e4ee77f90a06d7de30a532b590482a01d779addd912864e
1138
+ .claude/skills/multi-persona-review/references/reviewer-design.md 327b70bdedff29868b6b3b88d2279db572a811eb19defe8ec0c724e3a007cb45
1139
+ .claude/skills/multi-persona-review/references/reviewer-design.md 4afc9d327f91139b4d272a54e1b1992766b7e0482b4479c76ca9b5284e28d7c8
1140
+ .claude/skills/multi-persona-review/references/worked-example.md 95b48adac8ae5a91cb60fb9d5c32c90365734225775f0f85f26fb72404345943
1141
+ .claude/skills/natural-korean/SKILL.md 0057cef32a4accff3efa155a4916ac65fc283ee766abb2951227da91fa8bdee0
1142
+ .claude/skills/nextjs-turbopack/SKILL.md f65da15aabeb9dca7ae08525739e72ab16e79a78ff4004f7cab6ad2de1c2a79b
1143
+ .claude/skills/north-star/NORTH_STAR.template.md f778da89c561cf92b987bc1d46e5eb8c979f2422882714b0a0602ccce62a7b5d
1144
+ .claude/skills/north-star/NORTH_STAR.template.md 401ac922beb41b5b3c6d19313d5a46184cc90808aa330e0e288108355b5f8aff
1145
+ .claude/skills/north-star/NORTH_STAR.template.md 23a071af1893e3be7860111d448d02a55f965519811bdadafdba4231607d66c3
1146
+ .claude/skills/north-star/NORTH_STAR.template.md 741efd7d4c477f95ecd1fca85656d4582a694b3753ece77378ddab8ba0f0b69e
1147
+ .claude/skills/north-star/SKILL.md f73b7ce6a73bb3666ff3c18557ba37248a844107d54ffbaf5bb3d562fc2e7f61
1148
+ .claude/skills/north-star/SKILL.md e335733ad2c6dab82d104f868c5ce3d3de99ee1f139330eba9ff497529bd9485
1149
+ .claude/skills/north-star/SKILL.md 5b1f8176bb264e5a426594704e640eb69605d6482d6ba93f53329e9fe08dadcb
1150
+ .claude/skills/north-star/SKILL.md e37ed854c29f26f64a96ccd4847e71723f5bab9a56d3c8f53a4f9815120085fe
1151
+ .claude/skills/north-star/SKILL.md b11245dfa084bcd8b1fa4f7eb186e7a6ed97b30905a1507992c35448af45cc62
1152
+ .claude/skills/north-star/SKILL.md fa4427fb57243aed96d639362d97c69202170dfa949bfb0818f4600b1cd8432f
1153
+ .claude/skills/north-star/SKILL.md 0616424076bb18c4388d9e48ac4e4bacb51106b0b8bdcf6be223e413e0c43bf9
1154
+ .claude/skills/north-star/references/roadmap-method.md 78b6d7e05e014c5c7ecd5680767ed2469c6a147faf053b09f448f63cebda9da7
1155
+ .claude/skills/north-star/references/roadmap-method.md fd58a20159eb0b14b69781ea2536be1fdebd78c9eb83658c2b76de78efd21bab
1156
+ .claude/skills/north-star/references/roadmap-method.md 32e812eac7356e15704d0eff162f3fe3d93f1de880c7107017c1d802463190b6
1157
+ .claude/skills/northstar-roadmap/SKILL.md 489e7a70f81d80c296dd9160debe7ef69381506f9ac00874cfb48e2b61e58736
1158
+ .claude/skills/northstar-roadmap/SKILL.md 9a632a49e6bbf877fee61d448046444d57127b75b4f76f9b8ddf8946c8b7990b
1159
+ .claude/skills/objective-brief/SKILL.md 4df0bbd72dd7ea698d7d9d3b10476c4c4ff3e6bc3d9a1d29531e083c9e9bf59a
1160
+ .claude/skills/objective-brief/SKILL.md 51fa706442849b5f74e71c23fab8952526dd3a60ab1d78a3fe53d2631f24e47d
1161
+ .claude/skills/objective-brief/SKILL.md 531c8f0f09492e4a1643a5de38dbc293be9981a5c81ebc7f783620d5380a5806
1162
+ .claude/skills/python-patterns/SKILL.md df0c0abbbd0e6a01811d1b1d10d8fa7a8a19ce73afdc9432d24843f62649cdd2
1163
+ .claude/skills/python-testing/SKILL.md b304a8c503af32644f97a889c5545ee4f90e91980462ad4ce33fee3dea6f414f
1164
+ .claude/skills/recurrence-prevention/SKILL.md ab537b5f35be053ffa930f99c5b3d972b80fd621d18eda8390506d01503f6d78
1165
+ .claude/skills/recurrence-prevention/SKILL.md 905324644b719f2cfc2744be0bb21922c9e4e6534a83c6b8cad980d6327b92be
1166
+ .claude/skills/recurrence-prevention/SKILL.md 40b8e5a14136cecf12a63cc2f709de2d80290e352aee88aa9a482200a3fd7295
1167
+ .claude/skills/recurrence-prevention/SKILL.md 8a26b9d530305497fe11b39e31eb7806d835953c2585fc15fcf51488d7fe6279
1168
+ .claude/skills/recurrence-prevention/SKILL.md 62fe94e2cd34d9c3c40871766619d5c37c957e89247c5d281e62c85de51326de
1169
+ .claude/skills/recurrence-prevention/SKILL.md a626f5760ffb7446695a578104baef9ba9259b3b41115b00d699c43d260c0295
1170
+ .claude/skills/recurrence-prevention/SKILL.md 9e44542d54006e02ae1d906346a90c0ad0e26c1797496e791c83f7bb0e867cc6
1171
+ .claude/skills/recurrence-prevention/SKILL.md c7c163aceb457535adb3c3052fe41355048716db2cbb0b4cc768ca6fd4533f26
1172
+ .claude/skills/recurrence-prevention/SKILL.md 46614922e61e79235493c80664da2f9e7d95d034c58c9a646b948a7c2a1f70b2
1173
+ .claude/skills/recurrence-prevention/references/failure-analysis.md 1dd840db9a2bc662ec9688833988d93e80d0be245e7edd727c2d065a9d7d9eca
1174
+ .claude/skills/recurrence-prevention/references/failure-analysis.md 5fc80d28b3f52e92843e9ef431a9dab74d8b43cb3cfd7ed5d2aa359ad00586bc
1175
+ .claude/skills/self-hosted-github-runner/SKILL.md 4e5b86b9c7c8527599cfd8be61f4ed3ab67d439bb791f4068d85e52c9327e5df
1176
+ .claude/skills/self-hosted-github-runner/SKILL.md a1394fde762c88fddc88db1502b15aabe88bdfc1c565e77bd3cdbb0512352d10
1177
+ .claude/skills/spec-scaling/SKILL.md fd28afb5558e2ba4168b0b16bc19b1c4d2fb732741649010a1601f751f9eafc5
1178
+ .claude/skills/spec-scaling/SKILL.md 93fe3ed443754bd368a45e71145434c1fd7efd349e25b0179bbb63774ac4ca0e
1179
+ .claude/skills/strategic-compact/SKILL.md c8e861a39fb84968aefa82783c1b25e61d1b625c29c0f5b2bb301f4652c93e9d
1180
+ .claude/skills/strategic-compact/SKILL.md 6bfc63c380eb89886335ad033f759a4ff3f1b1ae2c5a143598bb628e38c6e769
1181
+ .claude/skills/strategic-compact/suggest-compact.sh 62af1630b000713842e363447675ad5a94f72cddf47241df68fc9883de686689
1182
+ .claude/skills/task-brief/SKILL.md 69125822c0c7da8daa6e67117f0845e3388284b0d4b7cc2b69f440c9b865b454
1183
+ .claude/skills/task-brief/SKILL.md 0859fc0b6cf760edb83b4c41cb8156cc90b7c62f1d5b5dd364acd4c5f7935cc2
1184
+ .claude/skills/ui-visual-review/SKILL.md 4016297b69844f05a51c4669e62083b8d27a734f0daa24b3a03039979d6a28c7
1185
+ .claude/skills/ui-visual-review/SKILL.md 4c92c01e4d437150fe115406044d2ea9b8c942c2026f9e0d8d446667c4db7912
1186
+ .claude/skills/ui-visual-review/SKILL.md e9689dadb419979e2b62a3f1c84069f90b91ae95dd0a061dc759b3a60598aa32
1187
+ .claude/skills/ui-visual-review/SKILL.md 39371f46b46b7d8f51da0b9a28ca710156de634b6e066217e9618b1ecbe3f5ee
1188
+ .claude/skills/ui-visual-review/SKILL.md 2e23c9ba4e40a13db152ee750a33cadb6e32169daf51417c9a0a97b0ed021e45
1189
+ .claude/skills/ui-visual-review/SKILL.md 8d5c23460b4ba78c9b8077d9e6de59af5a38ea3035ca65f5cf645140ba523741
1190
+ .claude/skills/ultracode-service-audit/SKILL.md bdb3d151ca74e6129e547339b17192ba415701b46d889e0a1bf9f7fd0195663c
1191
+ .claude/skills/ultracode-service-audit/SKILL.md c7ca76cbda2f8d6b5750b68f524c92927ea375fb47a910ed02332e3a6fa1bca0
1192
+ .claude/skills/user-centered-explanation/SKILL.md eab47a91badce5d6314f63889950c69c43fa5ea4660d544828794ada3684e0b8
1193
+ .claude/skills/verification-loop/SKILL.md 01a9ae310ad4426bb05660dda6cefdeaac9513417585c09c8bb1b94738649a78
1194
+ .claude/skills/verification-loop/SKILL.md 41f481c1146b0b29ecbcf5c633090a6db3749ca4a814499d72b5b8941db694f5
1195
+ .claude/skills/verification-loop/SKILL.md 566fc81da559464d70af80a125dac60991ba5e26464643886c1dee04dd78db95
1196
+ .claude/skills/verification-loop/SKILL.md 1744f867f7868b83b480341c2e8d08bff3848c2408255871d3aba0c2f7eff2f2
1197
+ .claude/skills/verification-loop/agents/openai.yaml 1907fd04fea5c83d15faf14acfc978de90076d4351341cf9259639aa0c3b7bd3
1198
+ .claude/skills/verification-loop/references/tracks.md de11792d447c2e8ea67e8367c6655bd94646b1ba3f29184c4521cae01facc6ec
1199
+ .uzys-agent-harness/check-absence.sh b9bbc7bbdc69d51eac2a4e4748ee7c137dd585b4f3ca1d9bd2404eab9694c9b4
1200
+ .uzys-agent-harness/check-absence.sh c08d3a1df4188e5f37ddf7f7b53901184d87bdbed63c5c99afbddd0ef6a0442d
1201
+ .uzys-agent-harness/check-absence.sh 3655faf030e319e26a6386b316df004f4e2fee963fe0ebacb661030e85cda92e
1202
+ .uzys-agent-harness/protect-branch.sh 3356c4941ef1d5c174b10fd8858887f00b3b06c851788064e0e3cc6a6c8c56fd
1203
+ .uzys-agent-harness/protect-branch.sh ecd46450beae58c085a0d407c34441ae470092d9963bd4a37f09416a22cfad35
1204
+ .uzys-agent-harness/spec-drift-check.sh f35a64dd9954a776c3eabf356c0cda017db30e129d08022341a07c5a9d2b6f9a
1205
+ .uzys-agent-harness/spec-drift-check.sh 3c6925b03775454ec3a81e63dda400511824a9ac6e3f8f30b302ce1edf39d062
1206
+ `;
1207
+ var LEGACY_HARNESS_FILES = (() => {
1208
+ const m = /* @__PURE__ */ new Map();
1209
+ for (const [path = "", sha256 = ""] of rows(FILES)) {
1210
+ const s = m.get(path) ?? /* @__PURE__ */ new Set();
1211
+ s.add(sha256);
1212
+ m.set(path, s);
1213
+ }
1214
+ return m;
1215
+ })();
1216
+ function isLegacyHarnessFile(path, sha256) {
1217
+ const key = path.startsWith(".agents/skills/") ? `.claude/skills/${path.slice(".agents/skills/".length)}` : path;
1218
+ return LEGACY_HARNESS_FILES.get(key)?.has(sha256) === true;
1219
+ }
1220
+ var LEGACY_AGENTS_MD_SECTION_KEYS = keySet(`
1221
+ 241678cd4951efaf8ee7bb0059e739c397af6b90b75fea636361501b7ae1cbe7 ## Workflow Gates (26.70.0)
1222
+ 1e19e112dc2c9c85f31d7b8c1b65b5e35e8563db7f31115ba03af8c6ef1d5ea6 ## Command Reference (Antigravity workflows) (26.70.0)
1223
+ fe66384910daab3a0275c7c98d9a944f5f771d58ec2a9addcaf5f16a8d001ef9 ## Protected Files (DO NOT EDIT) (26.70.0)
1224
+ f7fbcaa082ffb260b11f48813a8f97f243663082aadb37e2137d2a99a143f65f ## Scopes (26.70.0)
1225
+ 62225c82dd65fc0c01dba1fd6dbe8e9accb8d46d0f580f4d2a281dead3346997 ## Workflow Gates (26.70.0)
1226
+ 0d509a214b74e5b3966722390279fe84fa20c8337fa7c9853c31fef0b42bf85f ## Session Start (26.70.0)
1227
+ fd29a37cc8231393739123a79728b31c0c4acde371ce502709550ed43837a2d4 ## Protected Files (DO NOT EDIT) (26.70.0)
1228
+ 9c4a901cbad93ba04a5d511952e2e47213130c92ae2122e5f5649a17328d8915 ## Git Policy (26.70.0)
1229
+ c24a96fd321a9286debd618c9d95684c344c061d6a8e0558dce1595aa84e3a9f ## Command Reference (Codex slash) (26.70.0)
1230
+ 23c25f2516a46dec187877d5d85f2e92257752193e287978650b3b112df083dd ## Agents (subagent, multi_agent stable) (26.70.0)
1231
+ dc230c795b307261ea4ee23126e6a209cc73ed474c97857d59e03a134232c4ec ## Hooks \uD604\uD669 (Codex 0.124.0 \uC2E4\uCE21 \uC81C\uC57D) (26.70.0)
1232
+ 462ce33464e58a8ebd95ea5ed0f4cce167153fb2805ea6ba609734174a9027a2 ## Experience Accumulation (26.70.0)
1233
+ dc5629ad55f9800e4898a698650c8dbef5578505e7d5891745f0065f01d650a3 ## Context Management (26.70.0)
1234
+ c7d793aa3bf374b8bd390baf25bfc2c23c2d050377f673e72e53fe14f821c1f4 ## Workflow Gates (26.70.0)
1235
+ cf4cf430b633cb68b4128b03797999ed325f76ed92132fb8adcb60cccbc4e0c2 ## Session Start (26.70.0)
1236
+ ef2c745821688a463d4435affaaa6baf404feaec69f081c68a794d847f72a9e7 ## Protected Files (DO NOT EDIT) (26.70.0)
1237
+ f328397dafd8eaff1eeff91dcda315db3da289e646eca49a6b977725867cea3d ## Command Reference (OpenCode slash) (26.70.0)
1238
+ e1dd38e41e5df06efea65b349376a1ea668ce74a408efaf5c1ce91f08063a666 ## Agents (subagent) (26.70.0)
1239
+ 1ca6e8025be0d099df42fce9248673722fe327be2a77a0d9971bacc18a99a10e ## Hooks (Plugin lifecycle) (26.70.0)
1240
+ ad834e2800b86a486d50efb9526657898a03919ee1b3fbf0cb0aaa89c9a6c095 ## Experience Accumulation (26.70.0)
1241
+ 2cade5a88d6241fa7a50289fe0336b64cccd08d2c81b65e2df72acbdbc436a62 ## Context Management (26.70.0)
1242
+ b2532f7ae8302ee8aa5e0316f7bd9755abe06abeaa9639f7a9a062fc7d89b47f ## Scopes (26.89.0)
1243
+ c144f72dc32d524ae00b6026322439a5a3f2083d4e1781b927f374fe2b20d7c8 ## Session Start (26.89.0)
1244
+ 0c719706bef388d3ac81a62431659bf817b0d4384846a0068d5768c8b3965e36 ## Protected Files (DO NOT EDIT) (26.89.0)
1245
+ 6502c5abcf4cb9148c6dc5227401f7046b7dc8ab97ff7c292824de568b603942 ## Experience Accumulation (26.89.0)
1246
+ 37106505641d9de7ae9e3ac5cc86efc3221abe5c969323325acc11e9b7d7cd04 ## Experience Accumulation (26.115.0)
1247
+ 25baf8cc482b473d7f61e13ff95232484f95cc22063bb2ce9587f43ad242d5dc ## Scopes (26.141.0)
1248
+ d48603cb329fce58fd7ca9eb3b01dde3b223bdbfe79ee583627fbc15e22ba703 ## Session Start (26.141.0)
1249
+ 29cb109e9a2cb4e3037a470d25d7dce4928d0e690c2c7c478d452f2133b65cd5 ## Git Policy (26.141.0)
1250
+ d1affcbe314d8ccd39ca956d467fbd43bcc859b4ffc23bf1d1f8f2f0a3e10f4e ## Context Management (26.141.0)
1251
+ 9203c6d359a7ffc5a968be0645e1b827d6b4940312c27edb1c5fad6a68c9ca7e ## Session Start (26.141.0)
1252
+ 39fdaaed39e0a39c537ebc3dcf5d349fb867740b126822eaa9333833d0de68eb ## Context Management (26.141.0)
1253
+ 6f4bae4fb31bc54074d1fed021f4fd6dc3939844ac701eac8427904404b6daf3 ## Context Management (26.147.0)
1254
+ 0569e0fb18d55a474fc8239dd60bb5684d22245c1ffa6f198cf6af468b5a3794 ## Protected Files (26.151.0)
1255
+ 624b8878a9a17a5d4046b180297c53b27270ea75ed7d892d527939a7580adb19 ## Session Start (26.151.0)
1256
+ 0f6658376e8bba3c31ffca010f24e9acee9f32953c00a41183dca9eaaa4f7913 ## Protected Files (26.151.0)
1257
+ 55412b2ae275e9660f09c7cc286d3e92236e4ae61626cbf68727ae222edcb0e0 ## Protected Files (26.151.0)
1258
+ `);
1259
+ var LEGACY_AGENTS_MD_NAMED_KEYS = keySet(`
1260
+ 92dce1a73d484490c943ec3418f10b2370079ae42aa8d93159596f45d12e699a ## Project Rules (26.70.0)
1261
+ f59bdc7177a12aa86f235ada16b2407c40bd55ae9de8e96a851b77571bdde989 ## Command Reference (Antigravity workflows) (26.70.0)
1262
+ c7f9fa755bd66d80c2df496c6c759e8accd736cd8fef64460feb7ae9cd4c8006 ## Context Management (26.70.0)
1263
+ dc030a35a6e638aba5447b4d4e79ae028fafd1419fbae872b902661fa7787a58 ## Command Reference (Codex slash) (26.70.0)
1264
+ df73823c02e6eee000388444964acfec233ffeec0e20fdc5d31eb018d2ca93c1 ## Context Management (26.77.0)
1265
+ 6c900db77cb104f400091b44a7414e96427003860306a3007651b7a8b91a547c ## Project Rules (26.140.0)
1266
+ d9e3680462e64506ba4ef2df4f12e666855c3f458c1feeeeb623d62de2a5fa17 ## Project Rules (26.145.0)
1267
+ 2da8ca3600045d85a9607ce270f9bc2d77b5ee54fe80cd0765caf7658f51127c ## Harness Rules (26.147.0)
1268
+ bbe5e1d9d368b15221269319b53dde4f87424904b80cd5af46a28de3d8ac923d ## Harness Rules (26.147.0)
1269
+ 41eae7d6a8bb327e71cea2dbaa331d808f879b5ff4074c0bbdbb90bcfe066d9f ## Change Boundaries (rule 26.147.0)
1270
+ bef15b624ea34d08dc5833ceacd710f3c687385050958e5cd103378cf2fcf457 ## Change Boundaries (rule 26.147.0)
1271
+ 9648c5a118c77a23a1a04ddcacd7708be162d65587769d47f51d2405b74c2f72 ## Shell Safety (rule 26.147.0)
1272
+ c2dee88e05c98e1482c99c1c01f59cafc6c6816fda1269d68179707f4ede0b8f ## Shell Safety (rule 26.147.0)
1273
+ a6c3b31f60c80906e8bdb7a5ba699a4c1062b2e2f3e94f9f56b5064f74a722ef ## Documentation Boundaries (rule 26.147.0)
1274
+ b8fbe3bb2d5d790015ddbcff85c68922a122d91705e1cc946a14b30480bfb22b ## Documentation Boundaries (rule 26.147.0)
1275
+ 0a0b44e4c8d10aca52f63a4dba40d9166ea067eb1599886f850dc37ec2bba2f2 ## Git Safety (rule 26.147.0)
1276
+ 05efdb03d3e9d9fbcfd652ddf778d25bb1ab5ce237dd33f17b0be82cad08db5b ## Session Cleanup (rule 26.147.0)
1277
+ 10bdfa993f15639a319e852cf59f7b279b7397d26a94c54e6ca6a94416cc6834 ## Enforcement (rule 26.147.0)
1278
+ b72afd685f3c63e481dc856890c8a9d89c4e36ca3163ab172661e9fd5c3658a5 ## Enforcement (rule 26.147.0)
1279
+ 126a9a57aa42007c9c0bd3cb7f0ce75f2ad6a7497b464e7f6c7f45f3ae5f40d2 ## Delivery (rule 26.147.0)
1280
+ edfa8459a9863e355a080c759419f6f5ffb0ebd5fc418914fd38d79622e73131 ## Delivery (rule 26.147.0)
1281
+ 55295cda2a572e0aab061449248123d9d43543b3045b1b156b95fe2fb6aae509 ## Testing (rule 26.147.0)
1282
+ 6364a5d8727d33db914c7d7a1ac4108cff9ed1dbefffb212bd84c1fcd7584284 ## Testing (rule 26.147.0)
1283
+ 8d407c73c2de630d77d605447f605a6a2511fc4598d500ac7a1ff1374e2e457d ## Delivery (rule 26.148.0)
1284
+ 2c3f8e7c8408f103006e4ef8029dce4da1995bca01bd1e6baf7ca3f4e46e58fa ## Delivery (rule 26.148.0)
1285
+ ae930fa7c8fa6f942d20715a51de43f33461aa5589f469f13a31ab90da1904e7 ## Delivery (rule 26.149.0)
1286
+ 5b97fa550beb24e86c04af7fba2c97d8ff51702fd25abf223d97350db2a19ff1 ## Delivery (rule 26.149.0)
1287
+ e9b5814787b1e1cc066c217da0e344604477edfc0eb17304a5aeca73b0846637 ## Testing (rule 26.149.0)
1288
+ bafced984a2acf13f6fc7a3ca7c79edb99b5cecd4b21e351bd093f260d32fb02 ## Testing (rule 26.149.0)
1289
+ 4972faf000e3433c0c07f7bb36e6b0463453be308dea31cfc177d692e23e3700 ## Project Rules (26.151.0)
1290
+ 381a1d00394f803118575ecde42946585466c2da2ec36239fd6208a3c1b6bab8 ## Change Boundaries (rule 26.151.0)
1291
+ 1a9dda0c66c7d82bdeb29c169f749a44c03b9bc112b3a1146898826b7d77b04e ## Change Boundaries (rule 26.151.0)
1292
+ e0d9ecb6916b563cfd0eaaf3f977106826136c4aaef518e705508c60f17b3cac ## Shell Safety (rule 26.151.0)
1293
+ 053642f8951a2da28bca7d8d985a01c8fc2a6a91223a8cdfcd5e7ba5ef78af62 ## Shell Safety (rule 26.151.0)
1294
+ 6543ff93b6c1f27f6d76f2a2a7599fd7ed0d1004f5419d5082321d1f4729bd1e ## Documentation Boundaries (rule 26.151.0)
1295
+ 1741bf4e931d799c850ae45944ac2a9985800240d011a670ddb9400013b14856 ## Documentation Boundaries (rule 26.151.0)
1296
+ e527b3630786a074e321e1375b44f0870df3f7d4f124d5fdca9bbbd7e66f9315 ## Session Cleanup (rule 26.151.0)
1297
+ 8ec2a06cfe3fa51e8187b218c290cf9ddc7680eac85427e2f6cf2a4a1a03ed4c ## Delivery (rule 26.151.0)
1298
+ d7fad0202a32aedb45717177ef9d3647109a0e77ee13f37b4c4a42f892182d4c ## Delivery (rule 26.151.0)
1299
+ 2332b78bb1eb206ba1b08e28b3114d4919fb1384a9a5378f55dead61fdc3105a ## Testing (rule 26.151.0)
1300
+ d172feb48f54338d5d81e8b390debe3537538f0d2214f51e590272547e113cd2 ## Testing (rule 26.151.0)
1301
+ 99bdd67b5ae37c10588f4ff852525ebe2ab59368723f56bd317818a89161c59a ## Change Boundaries (rule 26.152.0)
1302
+ a07c989310055284328b4ca441e201740fc8158a38572e56ec9968dac44ced13 ## Change Boundaries (rule 26.152.0)
1303
+ e4d2c89e6c2189748bef2dccf8eb506b7f8cf264d5bf8626fbe4446c330e0b63 ## Shell Safety (rule 26.152.0)
1304
+ 028c8514fa466e7f053088e37968ef07ce1bcb08d4c224e76ec807d0f01e4449 ## Shell Safety (rule 26.152.0)
1305
+ 9f0ac9e1c32c486e4f318061e3be3c853217d03b3f837b6c6e425f016c8707e9 ## Documentation Boundaries (rule 26.152.0)
1306
+ 992ca24bd1f15314fb53943af2856af73f0d31ad8c6cf98b25b840ebd017b86a ## Documentation Boundaries (rule 26.152.0)
1307
+ 49ec458da7c57727c16ec8ffa01cb2e2631eb769f6041b3bc07d912ec1a2994b ## Project Rules (26.153.0)
1308
+ 155abf346fea6c63a081d4db1a1e44d537c4a94a479a2affc96345b18928fde3 ## Testing (rule 26.157.0)
1309
+ 675da728d15da072c17fe5e9ba3ca8b21811e99a09f390fed4a53f19dd12c555 ## Testing (rule 26.157.0)
1310
+ `);
1311
+ var LEGACY_AGENTS_MD_PREAMBLE_KEYS = keySet(`
1312
+ 720645dd313be16e6204290bfe0451fb237f77b8a7498a8a8af77dfc44ac0828 > **Generated from**: '.claude/CLAUDE.md' via TS CLI 'src/antigravity/transform.ts' (26.70.0)
1313
+ f95170e7780965de8562db0b5c7846d8e75ac7902a4fab695373c691333c7ff4 > **Generated from**: '.claude/CLAUDE.md' via 'scripts/claude-to-codex.sh' (Phase C) (26.70.0)
1314
+ fe621e2687dd489f3e9fb6ca1b97d480a83af435d99117315c24534d9c4bc169 > **Generated from**: '.claude/CLAUDE.md' via TS CLI 'src/opencode/transform.ts' (Phase C) (26.70.0)
1315
+ 0d05490eb1360e2601d7e63b79c901a10da1dcfd6c9437151e4fb1ce00416bfc > **Generated from**: 'templates/CLAUDE.md' (4-CLI \uB2E8\uC77C \uC6D0\uBCF8) via TS CLI 'src/antigravity/transform.ts' (26.141.0)
1316
+ 1979722e483fba4441ffd4818134b3942cc97f21e39689316d6143e4732c98a7 > **Generated from**: 'templates/CLAUDE.md' (4-CLI \uB2E8\uC77C \uC6D0\uBCF8) via 'scripts/claude-to-codex.sh' (Phase C) (26.141.0)
1317
+ 230413c8211b1a9c3b204e315ff63f6ac3a7d7e5d01c8dd3d4ed294bf3c49f5c > **Generated from**: 'templates/CLAUDE.md' (4-CLI \uB2E8\uC77C \uC6D0\uBCF8) via TS CLI 'src/opencode/transform.ts' (Phase C) (26.141.0)
1318
+ `);
1319
+ var RETIRED_ANCHOR_SECTION_KEYS = keySet(`
1320
+ 522bac15bc17949697e291b837c0d9624f5e0c7666400a3baf94cdef7cfab8b5 ## Rule 1 \u2014 Think Before Coding (26.70.0)
1321
+ ea10b7cd9b84a71949a071fae15256f490b3fe407159d7bddcf3f882310fa835 ## Rule 2 \u2014 Simplicity First (26.70.0)
1322
+ c486465d9554f2de1711cd056437f6a95a5d3243264b82d29a6957f24b5a6de3 ## Rule 3 \u2014 Surgical Changes (26.70.0)
1323
+ 3d080574cc7ae81eda75c4aa019445b38b6f69c5f9eeeda9097e10b6041e7e94 ## Rule 4 \u2014 Goal-Driven Execution (26.70.0)
1324
+ 11e43547f6b1613a5dabb3edaf5131d696a3fd375b3c22558f03555a0f5cf2ef ## Rule 5 \u2014 Use the model only for judgment calls (26.70.0)
1325
+ 60d91718de6ae16ec86b50e7c9a939a6ca5fc0721422b592700cc9972f55aca2 ## Rule 6 \u2014 Token budgets are not advisory (26.70.0)
1326
+ 9d482c7307cbcb02acc6fb35cd57b24baa9fce04528e2bd21e1e877603f8b85b ## Rule 7 \u2014 Surface conflicts, don't average them (26.70.0)
1327
+ abca09f13833ce18bd0ffc0793d59780c35447ca79c2a512afcbc9d7a7116ed8 ## Rule 8 \u2014 Read before you write (26.70.0)
1328
+ 5b4eae6c3dc2d92cfb1772889650882a4a2115f08a36602da9a1d96697059b91 ## Rule 9 \u2014 Tests verify intent, not just behavior (26.70.0)
1329
+ 632670b8897dc373d4a12f611c2436d9a8e86d8cdab917eecabc4bf2a56f8030 ## Rule 10 \u2014 Checkpoint after every significant step (26.70.0)
1330
+ 252e99f25b05a47e5ce884ac9f1e1d34156116b9a76331e2510b1354a6c98cd4 ## Rule 11 \u2014 Match the codebase's conventions, even if you disagree (26.70.0)
1331
+ c28a12b589b6576379793c51fd01dbfaf91d391ac773f84ae0477ddb60a1e3e8 ## Rule 12 \u2014 Fail loud (26.70.0)
1332
+ 72feaf96f998989351f8083edb891c7b0c3bccca65dbea2732fb2864b8f98bd4 ## \uC758\uC0AC\uACB0\uC815 \uBC0F \uCEE8\uD38C \uC694\uCCAD \uC2DC (26.70.0)
1333
+ 95c932ecb4f4fdcdd8d483f9d02166b906f6eb045ef33ee474a16913d0c3e6cd ## Phase/\uC791\uC5C5 \uC644\uB8CC \uC2DC Self-Audit \uC2E4\uD589: (26.70.0)
1334
+ c7f9fa755bd66d80c2df496c6c759e8accd736cd8fef64460feb7ae9cd4c8006 ## Context Management (26.70.0)
1335
+ 6ec0b415a8f86c9940bccd3e6db895ea4a110185e76fef223a348f9553fd897c ## Rule 12 \u2014 Fail loud (26.77.0)
1336
+ a78f0ffa6b51c9b45c964ecb92db951534336f7e558a3d39d11f015ee6c3466c ## Anti-Patterns (Forbidden) (26.77.0)
1337
+ 50d00a511e87571d7ff85f6584b87b9f5c2eef06122d8ac5fdccf3f047643f12 ## When Requesting Decisions or Confirmation (26.77.0)
1338
+ 574519e07f48bcc04f0e3d505c0f9c157ddd426b5eda2baa99656cd4712dad2b ## Run Self-Audit on Phase/Task Completion (26.77.0)
1339
+ df73823c02e6eee000388444964acfec233ffeec0e20fdc5d31eb018d2ca93c1 ## Context Management (26.77.0)
1340
+ 8f49f0a592575a65eadd3e33950c96616223aa8bb116707103affeef71db28c6 ## Delegate the Building, Keep the Deciding (26.139.0)
1341
+ 6115eccc3b2702f436a9db7d6af9803fa7b1fbf7b9ff1110ea658fadd67ec5ea ## 1. Think before coding (26.140.0)
1342
+ 15b120395ec8e757ecbe3e65775371a9120953617fb37eb062e0c176b52f045a ## 2. Prefer the simplest sufficient solution (26.140.0)
1343
+ cc466b4781444ca54aedebd20ba3aa531c3aa84cc438953df84b133bd32a0e1e ## 3. Make surgical changes and preserve existing work (26.140.0)
1344
+ f6d1f6f02cb3c19e61becf329f20335c54e684bccb06b33e4b7f66eb49f9c6b6 ## 4. Define success before editing (26.140.0)
1345
+ 372bf30b282b861c6a89e355c94ffd239bb0202e2ef928f8af99d640579710f0 ## 5. Report evidence, not confidence (26.140.0)
1346
+ 66ca0e3aae98d24c65afbc418645df29d8c570be81918c42192eb1beecd21f23 ## 6. Do not cross high-impact boundaries alone (26.140.0)
1347
+ 9c6c27eb57a9790273fa3fdf0df3628d927b025f870f09933692d1b522d73a25 ## Decisions and explanations (26.140.0)
1348
+ 922d100c2dc6ef8a53989a920c3554afa7efd148255bc144412403c0289439e6 ## Decisions and explanations (26.141.0)
1349
+ 330a67775a28557431aebf36a44018fc5194a4b929bf2cf95184b1f0828a6360 ## 1. Understand First (26.145.0)
1350
+ 3b2f0147dc7bd0f01fd12485790199ef4ee96a6b723e244ffbf3dc96933a1ab8 ## 2. Define Success and Keep It Simple (26.145.0)
1351
+ 8802fb25c05879c98dbf8407001d239c410d25b4fa2521b11d9d7cc108f73b29 ## 3. Preserve Sound Boundaries (26.145.0)
1352
+ 1d0d9bf05d8870dd0dc62e388ceffc98923f1dd2074ef8038f80a50c9b5b9a81 ## 4. Make Surgical Changes (26.145.0)
1353
+ 05e08e2318061141464ae714d6770d35527576affa787740d9c6d9345d3462dd ## 5. Verify and Review (26.145.0)
1354
+ 7b39f1f92a72517d6ae52599becfef29f675e48185a3483dc993d7accd4aee7c ## 6. Protect High-Impact Boundaries (26.145.0)
1355
+ d5d93c55de33e4cdcb38f76c6d6d70a6e0beac7a176c8aa1daa09905ccc5fe39 ## 7. Report Evidence (26.145.0)
1356
+ cefc8bb6194d4f1b6302345e96ffb3a28b8b992032fd40f9f5b4f18997657e7a ## Presenting a decision (26.145.0)
1357
+ 7385ff5df34148278a8f4b164c11ba5f9355626abcc13a822dd1ebb206321c2f ## Skills that apply continuously (26.145.0)
1358
+ ad9fd1ff225c57c9801a7d7472bf4ddf9c952c602f4a93eae86fbcb3bcf468bc ## 7. Report Evidence (26.146.0)
1359
+ 868fe823fc40011d08816c9f1105ef6df7ca7f2479779303ce9583bba1ce6e0d ## Skills that apply continuously (26.146.0)
1360
+ 3f8259ca7c92d79cca1a36bba38f36d4ef5a96fd6cafbd0edb5a79492082f46f ## Presenting a decision (26.147.0)
1361
+ ecfdcd50e97723af77cfe6889bbad784cdeb86dd0c52af3ff98ddf6adf80f445 ## 1. Resolve what matters, then act (26.151.0)
1362
+ 9321cb1312a080bcf560940b8f86d3d59b39c667add4b8f3ad785ddae9bc8b20 ## 2. Choose the simplest sufficient solution (26.151.0)
1363
+ a4c345996a8578cc24b62fde27084e5848a61e4a40d91b0c72a95da46203b43c ## 3. Keep changes focused and preserve existing work (26.151.0)
1364
+ 1adbf1bf88bb8288744e1c1d73518ffdec12a5b668d5a225e2bedd006844466c ## 4. Define success and verify proportionally (26.151.0)
1365
+ 7636e57a877d54039f0c2829fde84202c65378bd36b8c8abb7274a29eaa5f8ac ## 5. Report evidence and stop unproductive loops (26.151.0)
1366
+ ddf2be9e04c04bf53ad9b5476e45639b33a60bf49bd3a83835796632052b17b4 ## 6. Keep authority explicit (26.151.0)
1367
+ 16b0edc0f4886a9f9ec94e6fb97206c385286b2c9d5563b2324b7a2f4a434435 ## 1. Understand what success means (26.153.0)
1368
+ b10ab1eafe25bb7ba3b20b907b1d7eedde03e52fbd49905f24e192e30b4e1b9a ## 2. Choose and adapt the approach (26.153.0)
1369
+ 69bcfb8b37ac184b1a30945a0dea59108ef1242c2f40fb778be118cbd524aea7 ## 3. Keep changes focused and coherent (26.153.0)
1370
+ f22a5a13c5ed8460c9370212d91354d0fea2fefc7f420a1eb3088504ae8da6c0 ## 4. Verify outcomes in proportion to consequences (26.153.0)
1371
+ 7a335cde69e8055ee5e137a9a5c09fd7f7c2e551ab099a2cda87f0c3dcd95a19 ## 5. Let evidence determine readiness (26.153.0)
1372
+ 79797568bb71bd0f7b14393f72e61721a4bfe99ecfb6126ffa38a6c03c53d765 ## 6. Act within clear authority (26.153.0)
1373
+ `);
1374
+ var LEGACY_HOOK_COMMANDS = /* @__PURE__ */ new Set([
1375
+ 'bash "$CLAUDE_PROJECT_DIR/.claude/hooks/agentshield-gate.sh"',
1376
+ 'bash "$CLAUDE_PROJECT_DIR/.claude/hooks/gate-check.sh"',
1377
+ 'bash "$CLAUDE_PROJECT_DIR/.claude/hooks/hito-counter.sh"',
1378
+ 'bash "$CLAUDE_PROJECT_DIR/.claude/hooks/mcp-pre-exec.sh"',
1379
+ 'bash "$CLAUDE_PROJECT_DIR/.claude/hooks/protect-files.sh"',
1380
+ 'bash "$CLAUDE_PROJECT_DIR/.claude/hooks/session-start.sh"',
1381
+ 'bash "$CLAUDE_PROJECT_DIR/.claude/hooks/task-brief-nudge.sh"',
1382
+ 'bash "$CLAUDE_PROJECT_DIR/.claude/skills/strategic-compact/suggest-compact.sh"'
1383
+ ]);
1384
+ var LEGACY_STATUSLINE_VALUES = keySet(`
1385
+ f3de524065fd30f26e2d14b31fad0edc4c6689fb95b8ea6c59b1d48c5a90adc5 statusLine (26.70.0)
1386
+ 94442f9703e367a12f6dc16612ad65ae5f0cd0c73b96dd683e584a18ac91eaea statusLine (26.164.0)
1387
+ `);
1388
+
1389
+ // src/hook-ref.ts
805
1390
  function projectAnchoredRef(command, claudeDir) {
806
1391
  const anchors = [
807
1392
  "$CLAUDE_PROJECT_DIR/.claude/",
@@ -820,12 +1405,21 @@ function projectAnchoredRef(command, claudeDir) {
820
1405
  }
821
1406
  return void 0;
822
1407
  }
1408
+ var LEGACY_HOOK_REFS = [...LEGACY_HOOK_COMMANDS].flatMap(
1409
+ (c3) => projectAnchoredRef(c3) ?? []
1410
+ );
1411
+ function isLegacyHookLine(command, claudeDir) {
1412
+ if (typeof command !== "string") return false;
1413
+ const ref = projectAnchoredRef(command, claudeDir);
1414
+ if (ref === void 0) return false;
1415
+ return !LEGACY_HOOK_REFS.includes(ref) || LEGACY_HOOK_COMMANDS.has(command);
1416
+ }
823
1417
  function keepHookRef(hook, claudeDir, removed) {
824
1418
  const relPath = projectAnchoredRef(hook?.command ?? "", claudeDir);
825
1419
  if (relPath === void 0) return true;
826
- const exists = existsSync(join(claudeDir, relPath));
827
- if (!exists && !removed.includes(relPath)) removed.push(relPath);
828
- return exists;
1420
+ const exists2 = existsSync(join(claudeDir, relPath));
1421
+ if (!exists2 && !removed.includes(relPath)) removed.push(relPath);
1422
+ return exists2;
829
1423
  }
830
1424
  function findStaleHookRefs(settingsPath, claudeDir, spare) {
831
1425
  let settings;
@@ -858,6 +1452,7 @@ function removeRetiredHookRefs(settingsPath, claudeDir, refs, opts = {}) {
858
1452
  const isRetiredDead = (hook) => {
859
1453
  const rel = projectAnchoredRef(hook?.command ?? "", claudeDir);
860
1454
  if (rel === void 0 || !refs.includes(rel)) return false;
1455
+ if (!isLegacyHookLine(hook?.command, claudeDir)) return false;
861
1456
  if (opts.evenIfScriptExists !== true && existsSync(join(claudeDir, rel))) return false;
862
1457
  if (!removed.includes(rel)) removed.push(rel);
863
1458
  return true;
@@ -943,13 +1538,14 @@ function planUpsert(args) {
943
1538
  }
944
1539
  function planStrip(args) {
945
1540
  const alwaysStrip2 = args.alwaysStrip ?? (() => false);
946
- const plan = { remove: [], kept: [], portions: /* @__PURE__ */ new Map() };
1541
+ const plan = { remove: [], removedEdited: [], kept: [], portions: /* @__PURE__ */ new Map() };
947
1542
  for (const [key, sha] of args.recorded) {
948
1543
  if (args.excluded.has(key)) continue;
949
1544
  const now = args.present.get(key);
950
1545
  if (now === void 0) continue;
951
1546
  if (now === sha || alwaysStrip2(key)) {
952
1547
  plan.remove.push(key);
1548
+ if (now !== sha) plan.removedEdited.push(key);
953
1549
  } else {
954
1550
  plan.kept.push(key);
955
1551
  plan.portions.set(key, sha);
@@ -1235,6 +1831,7 @@ var jsonKeys = {
1235
1831
  text: changed ? serialize(root) : existing,
1236
1832
  changed,
1237
1833
  removed: plan.remove,
1834
+ removedEdited: plan.removedEdited,
1238
1835
  kept: plan.kept,
1239
1836
  portions: plan.portions,
1240
1837
  empty: Object.keys(root).length === 0
@@ -1426,6 +2023,7 @@ var lines = {
1426
2023
  text,
1427
2024
  changed: text !== existing,
1428
2025
  removed: plan.remove,
2026
+ removedEdited: plan.removedEdited,
1429
2027
  kept: plan.kept,
1430
2028
  portions: plan.portions,
1431
2029
  empty: text.trim() === ""
@@ -1438,125 +2036,6 @@ init_esm_shims();
1438
2036
 
1439
2037
  // src/agents-md-merge.ts
1440
2038
  init_esm_shims();
1441
-
1442
- // src/retired-anchor-sections.ts
1443
- init_esm_shims();
1444
- import { createHash } from "crypto";
1445
- function retiredAnchorSectionKey(lines2) {
1446
- const out = lines2.map((l) => l.replace(/\r$/, "").trimEnd());
1447
- while (out.length > 1 && out.at(-1) === "") out.pop();
1448
- return createHash("sha256").update(out.join("\n")).digest("hex");
1449
- }
1450
- var RETIRED_ANCHOR_SECTION_KEYS = /* @__PURE__ */ new Set([
1451
- "522bac15bc17949697e291b837c0d9624f5e0c7666400a3baf94cdef7cfab8b5",
1452
- // ## Rule 1 — Think Before Coding
1453
- "ea10b7cd9b84a71949a071fae15256f490b3fe407159d7bddcf3f882310fa835",
1454
- // ## Rule 2 — Simplicity First
1455
- "c486465d9554f2de1711cd056437f6a95a5d3243264b82d29a6957f24b5a6de3",
1456
- // ## Rule 3 — Surgical Changes
1457
- "3d080574cc7ae81eda75c4aa019445b38b6f69c5f9eeeda9097e10b6041e7e94",
1458
- // ## Rule 4 — Goal-Driven Execution
1459
- "11e43547f6b1613a5dabb3edaf5131d696a3fd375b3c22558f03555a0f5cf2ef",
1460
- // ## Rule 5 — Use the model only for judgment calls
1461
- "60d91718de6ae16ec86b50e7c9a939a6ca5fc0721422b592700cc9972f55aca2",
1462
- // ## Rule 6 — Token budgets are not advisory
1463
- "9d482c7307cbcb02acc6fb35cd57b24baa9fce04528e2bd21e1e877603f8b85b",
1464
- // ## Rule 7 — Surface conflicts, don't average them
1465
- "abca09f13833ce18bd0ffc0793d59780c35447ca79c2a512afcbc9d7a7116ed8",
1466
- // ## Rule 8 — Read before you write
1467
- "5b4eae6c3dc2d92cfb1772889650882a4a2115f08a36602da9a1d96697059b91",
1468
- // ## Rule 9 — Tests verify intent, not just behavior
1469
- "632670b8897dc373d4a12f611c2436d9a8e86d8cdab917eecabc4bf2a56f8030",
1470
- // ## Rule 10 — Checkpoint after every significant step
1471
- "252e99f25b05a47e5ce884ac9f1e1d34156116b9a76331e2510b1354a6c98cd4",
1472
- // ## Rule 11 — Match the codebase's conventions, even if you disagree
1473
- "c28a12b589b6576379793c51fd01dbfaf91d391ac773f84ae0477ddb60a1e3e8",
1474
- // ## Rule 12 — Fail loud
1475
- "72feaf96f998989351f8083edb891c7b0c3bccca65dbea2732fb2864b8f98bd4",
1476
- // ## 의사결정 및 컨펌 요청 시
1477
- "95c932ecb4f4fdcdd8d483f9d02166b906f6eb045ef33ee474a16913d0c3e6cd",
1478
- // ## Phase/작업 완료 시 Self-Audit 실행:
1479
- "c7f9fa755bd66d80c2df496c6c759e8accd736cd8fef64460feb7ae9cd4c8006",
1480
- // ## Context Management
1481
- "6ec0b415a8f86c9940bccd3e6db895ea4a110185e76fef223a348f9553fd897c",
1482
- // ## Rule 12 — Fail loud
1483
- "a78f0ffa6b51c9b45c964ecb92db951534336f7e558a3d39d11f015ee6c3466c",
1484
- // ## Anti-Patterns (Forbidden)
1485
- "50d00a511e87571d7ff85f6584b87b9f5c2eef06122d8ac5fdccf3f047643f12",
1486
- // ## When Requesting Decisions or Confirmation
1487
- "574519e07f48bcc04f0e3d505c0f9c157ddd426b5eda2baa99656cd4712dad2b",
1488
- // ## Run Self-Audit on Phase/Task Completion
1489
- "df73823c02e6eee000388444964acfec233ffeec0e20fdc5d31eb018d2ca93c1",
1490
- // ## Context Management
1491
- "8f49f0a592575a65eadd3e33950c96616223aa8bb116707103affeef71db28c6",
1492
- // ## Delegate the Building, Keep the Deciding
1493
- "6115eccc3b2702f436a9db7d6af9803fa7b1fbf7b9ff1110ea658fadd67ec5ea",
1494
- // ## 1. Think before coding
1495
- "15b120395ec8e757ecbe3e65775371a9120953617fb37eb062e0c176b52f045a",
1496
- // ## 2. Prefer the simplest sufficient solution
1497
- "cc466b4781444ca54aedebd20ba3aa531c3aa84cc438953df84b133bd32a0e1e",
1498
- // ## 3. Make surgical changes and preserve existing work
1499
- "f6d1f6f02cb3c19e61becf329f20335c54e684bccb06b33e4b7f66eb49f9c6b6",
1500
- // ## 4. Define success before editing
1501
- "372bf30b282b861c6a89e355c94ffd239bb0202e2ef928f8af99d640579710f0",
1502
- // ## 5. Report evidence, not confidence
1503
- "66ca0e3aae98d24c65afbc418645df29d8c570be81918c42192eb1beecd21f23",
1504
- // ## 6. Do not cross high-impact boundaries alone
1505
- "9c6c27eb57a9790273fa3fdf0df3628d927b025f870f09933692d1b522d73a25",
1506
- // ## Decisions and explanations
1507
- "922d100c2dc6ef8a53989a920c3554afa7efd148255bc144412403c0289439e6",
1508
- // ## Decisions and explanations
1509
- "330a67775a28557431aebf36a44018fc5194a4b929bf2cf95184b1f0828a6360",
1510
- // ## 1. Understand First
1511
- "3b2f0147dc7bd0f01fd12485790199ef4ee96a6b723e244ffbf3dc96933a1ab8",
1512
- // ## 2. Define Success and Keep It Simple
1513
- "8802fb25c05879c98dbf8407001d239c410d25b4fa2521b11d9d7cc108f73b29",
1514
- // ## 3. Preserve Sound Boundaries
1515
- "1d0d9bf05d8870dd0dc62e388ceffc98923f1dd2074ef8038f80a50c9b5b9a81",
1516
- // ## 4. Make Surgical Changes
1517
- "05e08e2318061141464ae714d6770d35527576affa787740d9c6d9345d3462dd",
1518
- // ## 5. Verify and Review
1519
- "7b39f1f92a72517d6ae52599becfef29f675e48185a3483dc993d7accd4aee7c",
1520
- // ## 6. Protect High-Impact Boundaries
1521
- "d5d93c55de33e4cdcb38f76c6d6d70a6e0beac7a176c8aa1daa09905ccc5fe39",
1522
- // ## 7. Report Evidence
1523
- "cefc8bb6194d4f1b6302345e96ffb3a28b8b992032fd40f9f5b4f18997657e7a",
1524
- // ## Presenting a decision
1525
- "7385ff5df34148278a8f4b164c11ba5f9355626abcc13a822dd1ebb206321c2f",
1526
- // ## Skills that apply continuously
1527
- "ad9fd1ff225c57c9801a7d7472bf4ddf9c952c602f4a93eae86fbcb3bcf468bc",
1528
- // ## 7. Report Evidence
1529
- "868fe823fc40011d08816c9f1105ef6df7ca7f2479779303ce9583bba1ce6e0d",
1530
- // ## Skills that apply continuously
1531
- "3f8259ca7c92d79cca1a36bba38f36d4ef5a96fd6cafbd0edb5a79492082f46f",
1532
- // ## Presenting a decision
1533
- "ecfdcd50e97723af77cfe6889bbad784cdeb86dd0c52af3ff98ddf6adf80f445",
1534
- // ## 1. Resolve what matters, then act
1535
- "9321cb1312a080bcf560940b8f86d3d59b39c667add4b8f3ad785ddae9bc8b20",
1536
- // ## 2. Choose the simplest sufficient solution
1537
- "a4c345996a8578cc24b62fde27084e5848a61e4a40d91b0c72a95da46203b43c",
1538
- // ## 3. Keep changes focused and preserve existing work
1539
- "1adbf1bf88bb8288744e1c1d73518ffdec12a5b668d5a225e2bedd006844466c",
1540
- // ## 4. Define success and verify proportionally
1541
- "7636e57a877d54039f0c2829fde84202c65378bd36b8c8abb7274a29eaa5f8ac",
1542
- // ## 5. Report evidence and stop unproductive loops
1543
- "ddf2be9e04c04bf53ad9b5476e45639b33a60bf49bd3a83835796632052b17b4",
1544
- // ## 6. Keep authority explicit
1545
- "16b0edc0f4886a9f9ec94e6fb97206c385286b2c9d5563b2324b7a2f4a434435",
1546
- // ## 1. Understand what success means
1547
- "b10ab1eafe25bb7ba3b20b907b1d7eedde03e52fbd49905f24e192e30b4e1b9a",
1548
- // ## 2. Choose and adapt the approach
1549
- "69bcfb8b37ac184b1a30945a0dea59108ef1242c2f40fb778be118cbd524aea7",
1550
- // ## 3. Keep changes focused and coherent
1551
- "f22a5a13c5ed8460c9370212d91354d0fea2fefc7f420a1eb3088504ae8da6c0",
1552
- // ## 4. Verify outcomes in proportion to consequences
1553
- "7a335cde69e8055ee5e137a9a5c09fd7f7c2e551ab099a2cda87f0c3dcd95a19",
1554
- // ## 5. Let evidence determine readiness
1555
- "79797568bb71bd0f7b14393f72e61721a4bfe99ecfb6126ffa38a6c03c53d765"
1556
- // ## 6. Act within clear authority
1557
- ]);
1558
-
1559
- // src/agents-md-merge.ts
1560
2039
  var marker = (name) => ({
1561
2040
  start: `<!-- uzys-harness:${name}:start -->`,
1562
2041
  end: `<!-- uzys-harness:${name}:end -->`
@@ -1627,6 +2106,29 @@ function trimTrailingBlanks(lines2) {
1627
2106
  while (out.length > 0 && (out.at(-1) ?? "").trim() === "") out.pop();
1628
2107
  return out;
1629
2108
  }
2109
+ function isLegacyHarnessSection(lines2) {
2110
+ const key = legacySectionKey(lines2);
2111
+ return RETIRED_ANCHOR_SECTION_KEYS.has(key) || LEGACY_AGENTS_MD_SECTION_KEYS.has(key) || LEGACY_AGENTS_MD_NAMED_KEYS.has(key);
2112
+ }
2113
+ function withoutAgentsBlock(text) {
2114
+ const lines2 = text.split("\n");
2115
+ const at5 = blockRange(lines2, AGENTS_BLOCK);
2116
+ if (at5 === null) return text;
2117
+ const head = trimTrailingBlanks(lines2.slice(0, at5[0]));
2118
+ const tail = lines2.slice(at5[1] + 1);
2119
+ while (tail.length > 0 && (tail[0] ?? "").trim() === "") tail.shift();
2120
+ return tail.length > 0 ? [...head, "", ...tail].join("\n") : `${head.join("\n")}
2121
+ `;
2122
+ }
2123
+ function isLegacyHarnessAgentsMd(text) {
2124
+ const body = withoutAgentsBlock(text);
2125
+ const lines2 = body.split("\n");
2126
+ if (blockRange(lines2, ANCHOR_BLOCK) !== null || blockRange(lines2, SKILLS_BLOCK) !== null) {
2127
+ return false;
2128
+ }
2129
+ const key = legacyPreambleKey(body);
2130
+ return key !== null && LEGACY_AGENTS_MD_PREAMBLE_KEYS.has(key);
2131
+ }
1630
2132
  function refreshBlock(existing, rendered, m, legacyHeading) {
1631
2133
  const at5 = blockRange(rendered, m);
1632
2134
  const fresh = at5 === null ? null : rendered.slice(at5[0], at5[1] + 1);
@@ -1679,9 +2181,11 @@ function renderAgentsBlock(params) {
1679
2181
  trimBlankEdges(harness)
1680
2182
  ].filter((part) => part.length > 0).map((part) => part.join("\n")).join("\n\n");
1681
2183
  }
1682
- function mergeAgentsMd(params) {
2184
+ function mergeAgentsMdDetailed(params) {
1683
2185
  const { rendered, existing, template } = params;
1684
- if (existing === null) return rendered;
2186
+ if (existing === null) {
2187
+ return { text: rendered, droppedLegacySections: 0, editedLegacySections: [] };
2188
+ }
1685
2189
  const names = sectionNames(template);
1686
2190
  const bodies = /* @__PURE__ */ new Map();
1687
2191
  const contextNow = sectionBody(existing, names, CONTEXT_SECTION);
@@ -1703,18 +2207,32 @@ function mergeAgentsMd(params) {
1703
2207
  }
1704
2208
  const merged = bodies.size === 0 ? rendered : assemble(rendered, names, bodies);
1705
2209
  const mergedText = merged.replace(/\r/g, "");
1706
- const extras = extraTopLevelSections(
2210
+ const candidates = extraTopLevelSections(
1707
2211
  existing,
1708
2212
  names,
1709
2213
  rendered,
1710
2214
  params.harnessRuleHeadings ?? /* @__PURE__ */ new Set(),
1711
2215
  replacedRulesHeadings
1712
- ).filter((e2) => !mergedText.includes(e2.join("\n").replace(/\r/g, "")));
1713
- if (extras.length === 0) return merged;
1714
- return `${merged.replace(/\n+$/, "\n")}${extras.map((e2) => e2.join("\n")).join("\n")}`.replace(
1715
- /\n+$/,
1716
- "\n"
1717
2216
  );
2217
+ const droppedLegacySections = candidates.legacy;
2218
+ const editedLegacySections = isLegacyHarnessAgentsMd(existing) ? installerSectionsInHarnessArea(
2219
+ existing,
2220
+ names,
2221
+ /* @__PURE__ */ new Set([...params.harnessRuleHeadings ?? [], ...renderedRuleHeadings(rendered, names)]),
2222
+ true
2223
+ ).keptEdited : [];
2224
+ const extras = candidates.sections.filter(
2225
+ (e2) => !mergedText.includes(e2.join("\n").replace(/\r/g, ""))
2226
+ );
2227
+ if (extras.length === 0) return { text: merged, droppedLegacySections, editedLegacySections };
2228
+ return {
2229
+ text: `${merged.replace(/\n+$/, "\n")}${extras.map((e2) => e2.join("\n")).join("\n")}`.replace(
2230
+ /\n+$/,
2231
+ "\n"
2232
+ ),
2233
+ droppedLegacySections,
2234
+ editedLegacySections
2235
+ };
1718
2236
  }
1719
2237
  function headingOwner(bare, ctx) {
1720
2238
  const name = /^## (.+?)\s*$/.exec(bare)?.[1];
@@ -1737,7 +2255,6 @@ function extraTopLevelSections(existing, names, rendered, harnessRuleHeadings, r
1737
2255
  ...renderedRuleHeadings(rendered, names)
1738
2256
  ]);
1739
2257
  const out = [];
1740
- const replacedSections = /* @__PURE__ */ new Set();
1741
2258
  let current = null;
1742
2259
  let region = null;
1743
2260
  for (const line of existing.split("\n")) {
@@ -1749,19 +2266,18 @@ function extraTopLevelSections(existing, names, rendered, harnessRuleHeadings, r
1749
2266
  const owner = headingOwner(bare, { region, names, harnessHeadings });
1750
2267
  const replacedBody = owner === "body" && region === RULES_SECTION && replacedRulesHeadings !== null && !replacedRulesHeadings.has(bare.trim());
1751
2268
  current = owner === "installer" || replacedBody ? [line] : null;
1752
- if (replacedBody && current !== null) replacedSections.add(current);
1753
2269
  continue;
1754
2270
  }
1755
2271
  if (current !== null) current.push(line);
1756
2272
  }
1757
2273
  if (current !== null) out.push(current);
1758
- return out.filter(
1759
- (lines2) => !replacedSections.has(lines2) || !RETIRED_ANCHOR_SECTION_KEYS.has(retiredAnchorSectionKey(lines2))
1760
- ).map((lines2) => {
2274
+ const legacy = out.filter(isLegacyHarnessSection).length;
2275
+ const sections = out.filter((lines2) => !isLegacyHarnessSection(lines2)).map((lines2) => {
1761
2276
  const trimmed = [...lines2];
1762
2277
  while (trimmed.length > 1 && (trimmed.at(-1) ?? "").trim() === "") trimmed.pop();
1763
2278
  return trimmed;
1764
2279
  }).filter((lines2) => lines2.length > 1 || /^## /.test(lines2[0] ?? ""));
2280
+ return { sections, legacy };
1765
2281
  }
1766
2282
  function installerLines(body, m) {
1767
2283
  if (body === null) return [];
@@ -1781,7 +2297,7 @@ function projectContextInstallerLines(params) {
1781
2297
  const body = sectionBody(existing, sectionNames(template), CONTEXT_SECTION);
1782
2298
  return body === null ? null : installerLines(body, SKILLS_BLOCK);
1783
2299
  }
1784
- function installerSectionsInHarnessArea(existing, names, harnessRuleHeadings) {
2300
+ function installerSectionsInHarnessArea(existing, names, harnessRuleHeadings, legacyFile = false) {
1785
2301
  const out = [];
1786
2302
  let current = null;
1787
2303
  let region = null;
@@ -1792,31 +2308,60 @@ function installerSectionsInHarnessArea(existing, names, harnessRuleHeadings) {
1792
2308
  if (current !== null) out.push(current);
1793
2309
  if (names.has(name)) region = name;
1794
2310
  const owner = headingOwner(bare, { region, names, harnessHeadings: harnessRuleHeadings });
1795
- current = owner === "installer" ? [bare] : null;
2311
+ if (!legacyFile) current = owner === "installer" ? { lines: [bare], named: false } : null;
2312
+ else if (region === CONTEXT_SECTION) current = null;
2313
+ else current = { lines: [bare], named: owner === "harness" };
1796
2314
  continue;
1797
2315
  }
1798
- if (current !== null) current.push(bare);
2316
+ if (current !== null) current.lines.push(bare);
1799
2317
  }
1800
2318
  if (current !== null) out.push(current);
1801
- return out.map(trimTrailingBlanks);
2319
+ const sections = [];
2320
+ const keptEdited = [];
2321
+ let legacy = 0;
2322
+ for (const { lines: lines2, named } of out) {
2323
+ if (isLegacyHarnessSection(lines2)) {
2324
+ legacy++;
2325
+ continue;
2326
+ }
2327
+ if (named) {
2328
+ if (lines2.slice(1).every((l) => l.trim() === "")) continue;
2329
+ keptEdited.push((lines2[0] ?? "").replace(/^## /, "").trim());
2330
+ }
2331
+ sections.push(trimTrailingBlanks(lines2));
2332
+ }
2333
+ return { sections, legacy, keptEdited };
1802
2334
  }
1803
- function stripHarnessFromAgentsMd(params) {
2335
+ function stripAgentsMd(params) {
1804
2336
  const { existing, template } = params;
1805
2337
  const lines2 = existing.split("\n");
2338
+ const names = sectionNames(template);
2339
+ const headings = params.harnessRuleHeadings ?? /* @__PURE__ */ new Set();
1806
2340
  if (blockRange(lines2, ANCHOR_BLOCK) === null && blockRange(lines2, SKILLS_BLOCK) === null) {
1807
- return null;
2341
+ if (!isLegacyHarnessAgentsMd(existing)) {
2342
+ return {
2343
+ remainder: null,
2344
+ legacyRemoved: false,
2345
+ legacySections: 0,
2346
+ keptEdited: []
2347
+ };
2348
+ }
2349
+ const body = withoutAgentsBlock(existing);
2350
+ return stripLegacyAgentsMd(body, names, headings);
1808
2351
  }
1809
- const names = sectionNames(template);
1810
2352
  const context = installerLines(sectionBody(existing, names, CONTEXT_SECTION), SKILLS_BLOCK);
1811
2353
  const rules = installerLines(sectionBody(existing, names, RULES_SECTION), ANCHOR_BLOCK);
1812
2354
  const contextFilled = !isUnfilledScaffold(context);
1813
2355
  const rulesFilled = compact(rules).length > 0;
1814
- const extras = installerSectionsInHarnessArea(
1815
- existing,
1816
- names,
1817
- params.harnessRuleHeadings ?? /* @__PURE__ */ new Set()
1818
- );
1819
- if (!contextFilled && !rulesFilled && extras.length === 0) return null;
2356
+ const extras = installerSectionsInHarnessArea(existing, names, headings);
2357
+ const legacy = {
2358
+ legacyRemoved: extras.legacy > 0,
2359
+ legacySections: extras.legacy,
2360
+ keptEdited: []
2361
+ };
2362
+ if (!contextFilled && !rulesFilled && extras.sections.length === 0) {
2363
+ return { remainder: null, ...legacy };
2364
+ }
1820
2365
  const out = [];
1821
2366
  const title = (lines2[0] ?? "").replace(/\r$/, "");
1822
2367
  if (/^# /.test(title)) out.push(title, "");
@@ -1824,8 +2369,26 @@ function stripHarnessFromAgentsMd(params) {
1824
2369
  out.push(`## ${CONTEXT_SECTION}`, "", ...trimBlankEdges(context), "");
1825
2370
  }
1826
2371
  if (rulesFilled) out.push(`## ${RULES_SECTION}`, "", ...trimBlankEdges(rules), "");
1827
- for (const section of extras) out.push(...section, "");
1828
- return out.join("\n");
2372
+ for (const section of extras.sections) out.push(...section, "");
2373
+ return { remainder: out.join("\n"), ...legacy };
2374
+ }
2375
+ function stripLegacyAgentsMd(existing, names, headings) {
2376
+ const contextBody = sectionBody(existing, names, CONTEXT_SECTION);
2377
+ const context = dropHarnessQuoteBlocks(contextBody ?? []);
2378
+ const contextFilled = contextBody !== null && !isUnfilledScaffold(context);
2379
+ const extras = installerSectionsInHarnessArea(existing, names, headings, true);
2380
+ const result = {
2381
+ legacyRemoved: true,
2382
+ legacySections: extras.legacy,
2383
+ keptEdited: extras.keptEdited
2384
+ };
2385
+ if (!contextFilled && extras.sections.length === 0) return { remainder: null, ...result };
2386
+ const out = [];
2387
+ const title = (existing.split("\n")[0] ?? "").replace(/\r$/, "");
2388
+ if (/^# /.test(title)) out.push(title, "");
2389
+ if (contextFilled) out.push(`## ${CONTEXT_SECTION}`, "", ...trimBlankEdges(context), "");
2390
+ for (const section of extras.sections) out.push(...section, "");
2391
+ return { remainder: out.join("\n"), ...result };
1829
2392
  }
1830
2393
 
1831
2394
  // src/adapters/marker-md.ts
@@ -1915,6 +2478,7 @@ var markerMd = {
1915
2478
  text,
1916
2479
  changed: text !== existing,
1917
2480
  removed: plan.remove,
2481
+ removedEdited: plan.removedEdited,
1918
2482
  kept: plan.kept,
1919
2483
  portions: plan.portions,
1920
2484
  empty: text.trim() === ""
@@ -2951,6 +3515,7 @@ var tomlRegion = {
2951
3515
  text,
2952
3516
  changed: text !== existing,
2953
3517
  removed: plan.remove,
3518
+ removedEdited: plan.removedEdited,
2954
3519
  kept: plan.kept,
2955
3520
  portions: plan.portions,
2956
3521
  empty: text.trim() === ""
@@ -3283,7 +3848,7 @@ function harnessWrite(input) {
3283
3848
  const update = input.run === "update";
3284
3849
  if (input.excluded) return j("leave", "", "keep");
3285
3850
  if (disk === null) {
3286
- if (rec.state === "none") return j("create", update ? LINES.newInRelease : LINES.wrote, "sha");
3851
+ if (rec.state !== "sha") return j("create", update ? LINES.newInRelease : LINES.wrote, "sha");
3287
3852
  return j("create", update ? LINES.restored : LINES.wrote, "sha");
3288
3853
  }
3289
3854
  if (rec.state === "none") {
@@ -3402,7 +3967,7 @@ function parseRemote(raw) {
3402
3967
  return { type: "http", url, auth: { bearerEnv } };
3403
3968
  }
3404
3969
  function parseTrackMcpMap(raw) {
3405
- const rows = [];
3970
+ const rows2 = [];
3406
3971
  for (const line of raw.split(/\r?\n/)) {
3407
3972
  const trimmed = line.trim();
3408
3973
  if (!trimmed || trimmed.startsWith("#")) {
@@ -3418,7 +3983,7 @@ function parseTrackMcpMap(raw) {
3418
3983
  }
3419
3984
  if (command === "url") {
3420
3985
  const server = parseRemote(argsJson ?? "");
3421
- if (server !== null) rows.push({ name, pattern, server });
3986
+ if (server !== null) rows2.push({ name, pattern, server });
3422
3987
  continue;
3423
3988
  }
3424
3989
  let args;
@@ -3430,9 +3995,9 @@ function parseTrackMcpMap(raw) {
3430
3995
  if (!Array.isArray(args) || !args.every((a) => typeof a === "string")) {
3431
3996
  continue;
3432
3997
  }
3433
- rows.push({ name, pattern, server: { type: "stdio", command, args } });
3998
+ rows2.push({ name, pattern, server: { type: "stdio", command, args } });
3434
3999
  }
3435
- return rows;
4000
+ return rows2;
3436
4001
  }
3437
4002
  function toClaudeServer(cfg) {
3438
4003
  if (cfg.type === "http") {
@@ -3450,12 +4015,12 @@ function toClaudeServer(cfg) {
3450
4015
  if (cfg.env && Object.keys(cfg.env).length > 0) out.env = { ...cfg.env };
3451
4016
  return out;
3452
4017
  }
3453
- function mergeMcpServers(base, rows, tracks, chosen = [], dropped = []) {
4018
+ function mergeMcpServers(base, rows2, tracks, chosen = [], dropped = []) {
3454
4019
  const out = {
3455
4020
  ...base,
3456
4021
  mcpServers: { ...base.mcpServers }
3457
4022
  };
3458
- for (const row of rows) {
4023
+ for (const row of rows2) {
3459
4024
  if (dropped.includes(row.name)) {
3460
4025
  continue;
3461
4026
  }
@@ -3473,27 +4038,27 @@ function mergeMcpServers(base, rows, tracks, chosen = [], dropped = []) {
3473
4038
  function composeMcpJson(opts) {
3474
4039
  const base = JSON.parse(readFileSync3(opts.templateMcpPath, "utf8"));
3475
4040
  const mapRaw = existsSync4(opts.trackMapPath) ? readFileSync3(opts.trackMapPath, "utf8") : "";
3476
- const rows = parseTrackMcpMap(mapRaw);
3477
- return mergeMcpServers(base, rows, opts.tracks, opts.chosen(rows), opts.dropped?.(rows) ?? []);
4041
+ const rows2 = parseTrackMcpMap(mapRaw);
4042
+ return mergeMcpServers(base, rows2, opts.tracks, opts.chosen(rows2), opts.dropped?.(rows2) ?? []);
3478
4043
  }
3479
- function selectableMcpServers(rows) {
3480
- const names = new Set(rows.map((r) => r.name));
4044
+ function selectableMcpServers(rows2) {
4045
+ const names = new Set(rows2.map((r) => r.name));
3481
4046
  const out = /* @__PURE__ */ new Map();
3482
4047
  for (const a of EXTERNAL_ASSETS) {
3483
4048
  if (a.method.kind === "internal" && names.has(a.method.key)) out.set(a.method.key, a.id);
3484
4049
  }
3485
4050
  return out;
3486
4051
  }
3487
- function selectedMcpServers(rows, choice) {
4052
+ function selectedMcpServers(rows2, choice) {
3488
4053
  const recorded2 = new Set(
3489
4054
  (choice.previousLog?.portions ?? []).filter((p2) => p2.path === ".mcp.json").map((p2) => p2.key)
3490
4055
  );
3491
4056
  const legacy = new Set(
3492
- choice.projectDir === void 0 ? [] : legacyMcpServers(choice.projectDir, choice.previousLog, rows)
4057
+ choice.projectDir === void 0 ? [] : legacyMcpServers(choice.projectDir, choice.previousLog, rows2)
3493
4058
  );
3494
- const dropped = new Set(droppedMcpServers(rows, choice.excluded));
4059
+ const dropped = new Set(droppedMcpServers(rows2, choice.excluded));
3495
4060
  const out = [];
3496
- for (const [name, assetId] of selectableMcpServers(rows)) {
4061
+ for (const [name, assetId] of selectableMcpServers(rows2)) {
3497
4062
  if (dropped.has(name)) continue;
3498
4063
  const key = `mcpServers.${name}`;
3499
4064
  if (isAssetSelected(assetId, choice.spec) || recorded2.has(key) || legacy.has(name)) {
@@ -3502,7 +4067,7 @@ function selectedMcpServers(rows, choice) {
3502
4067
  }
3503
4068
  return out;
3504
4069
  }
3505
- function legacyMcpServers(projectDir, previousLog, rows) {
4070
+ function legacyMcpServers(projectDir, previousLog, rows2) {
3506
4071
  if (previousLog === null || previousLog.records === "writer") return [];
3507
4072
  if ((previousLog.portions ?? []).some((p2) => p2.path === ".mcp.json")) return [];
3508
4073
  const created = (previousLog.rootFiles ?? []).some(
@@ -3512,10 +4077,10 @@ function legacyMcpServers(projectDir, previousLog, rows) {
3512
4077
  const servers = readMcpServers(join5(projectDir, ".mcp.json"));
3513
4078
  if (servers === null) return [];
3514
4079
  const out = [];
3515
- for (const name of selectableMcpServers(rows).keys()) {
4080
+ for (const name of selectableMcpServers(rows2).keys()) {
3516
4081
  if (!Object.hasOwn(servers, name)) continue;
3517
4082
  const disk = jsonSha(servers[name]);
3518
- const row = rows.find((r) => r.name === name);
4083
+ const row = rows2.find((r) => r.name === name);
3519
4084
  const known = [...row ? [row.server] : [], ...priorMcpValues(name)];
3520
4085
  if (known.some((v2) => jsonSha(toClaudeServer(v2)) === disk)) out.push(name);
3521
4086
  }
@@ -3533,8 +4098,8 @@ function readMcpServers(path) {
3533
4098
  return null;
3534
4099
  }
3535
4100
  }
3536
- function droppedMcpServers(rows, excluded) {
3537
- return [...selectableMcpServers(rows)].filter(([name, assetId]) => {
4101
+ function droppedMcpServers(rows2, excluded) {
4102
+ return [...selectableMcpServers(rows2)].filter(([name, assetId]) => {
3538
4103
  const id = keyId(".mcp.json", `mcpServers.${name}`);
3539
4104
  return excluded.has(assetId) || id !== null && excluded.has(id);
3540
4105
  }).map(([name]) => name);
@@ -3703,7 +4268,11 @@ function scanFieldOf(log, path) {
3703
4268
  ({ dir, ext }) => path.startsWith(`${CLAUDE_DIR}${dir}/`) && path.endsWith(ext)
3704
4269
  );
3705
4270
  if (policy === void 0) return "outside";
3706
- return present2((log.policyFiles ?? []).some((f) => f.path.startsWith(`${policy.dir}/`)));
4271
+ return present2(
4272
+ (log.policyFiles ?? []).some(
4273
+ (f) => f.path.startsWith(`${policy.dir}/`) && !RETIRED_PATHS.includes(f.path)
4274
+ )
4275
+ );
3707
4276
  }
3708
4277
  function isExternalOnly(path) {
3709
4278
  const owners = ownersOf(path);
@@ -3739,6 +4308,7 @@ function createInstallWriter(args) {
3739
4308
  const portionPaths = [];
3740
4309
  const backups = [];
3741
4310
  const judged = [];
4311
+ const writes = [];
3742
4312
  const shared2 = [];
3743
4313
  const outside = createOutsideGuard(projectDir);
3744
4314
  const rel = (abs) => relative(projectDir, abs).split(sep).join("/");
@@ -3753,12 +4323,15 @@ function createInstallWriter(args) {
3753
4323
  if (outside.skip(abs)) return null;
3754
4324
  const next = "source" in from ? readFileSync4(from.source, "utf8") : from.content;
3755
4325
  const disk = existsSync6(abs) ? readFileSync4(abs, "utf8") : null;
4326
+ const rec = recorded(previousLog, path, opts);
4327
+ const diskSha = rec.state === "no-sha" && disk !== null ? hashContent(disk) : null;
3756
4328
  const j4 = judge({
3757
4329
  op: "write",
3758
4330
  kind: "harness",
3759
- rec: recorded(previousLog, path, opts),
4331
+ rec: diskSha !== null && isLegacyHarnessFile(path, diskSha) ? { state: "sha", sha256: diskSha } : rec,
3760
4332
  disk,
3761
- next
4333
+ next,
4334
+ ...args.run === void 0 ? {} : { run: args.run }
3762
4335
  });
3763
4336
  let backupAbs;
3764
4337
  const write = () => {
@@ -3800,13 +4373,11 @@ function createInstallWriter(args) {
3800
4373
  default:
3801
4374
  throw new Error(`install: '${j4.record}' is not a write record (${path})`);
3802
4375
  }
4376
+ const line = backup === void 0 ? j4.line : j4.line.replace(BACKUP_PLACEHOLDER, backup);
4377
+ const backupPair = backup === void 0 || backupAbs === void 0 ? {} : { backup, backupAbs };
4378
+ writes.push({ path, verdict: j4.verdict, record: j4.record, line, ...backupPair });
3803
4379
  if (j4.line === "" || j4.verdict === "create" || j4.verdict === "overwrite") return null;
3804
- const out = {
3805
- path,
3806
- verdict: j4.verdict,
3807
- line: backup === void 0 ? j4.line : j4.line.replace(BACKUP_PLACEHOLDER, backup),
3808
- ...backup === void 0 || backupAbs === void 0 ? {} : { backup, backupAbs }
3809
- };
4380
+ const out = { path, verdict: j4.verdict, line, ...backupPair };
3810
4381
  judged.push(out);
3811
4382
  return out;
3812
4383
  }
@@ -3867,7 +4438,12 @@ function createInstallWriter(args) {
3867
4438
  if (disk === null && ![...res.portions.keys()].some((k2) => !isContainerKey(k2))) {
3868
4439
  return result("leave", "");
3869
4440
  }
4441
+ let backupAbs;
3870
4442
  if (res.changed) {
4443
+ if (disk !== null && res.removedEdited.length > 0) {
4444
+ backupAbs = backupFile(abs, now);
4445
+ backups.push(backupAbs);
4446
+ }
3871
4447
  mkdirSync(dirname(abs), { recursive: true });
3872
4448
  writeFileSync3(abs, res.text);
3873
4449
  }
@@ -3889,7 +4465,7 @@ function createInstallWriter(args) {
3889
4465
  const ids = (keys) => [...keys].flatMap((k2) => keyId(path, k2) ?? []);
3890
4466
  const out = excludedKeys(path, excluded);
3891
4467
  const removedOut = res.removed.filter((k2) => out.has(k2));
3892
- const removedEdited = res.removedEdited.filter((k2) => out.has(k2));
4468
+ const removedEdited = res.removedEdited;
3893
4469
  const keptOut = res.kept.filter((k2) => out.has(k2));
3894
4470
  const onlyRemoved = disk !== null && res.removed.length > 0 && res.replaced.length === 0 && addedKeys.length === 0 && restoredKeys.size === 0;
3895
4471
  const wrote = onlyRemoved ? "removed the harness part" : j4.line;
@@ -3897,6 +4473,7 @@ function createInstallWriter(args) {
3897
4473
  const retired = names(res.removed.filter((k2) => !out.has(k2) && !render.has(k2)));
3898
4474
  const replaced = names(res.replaced);
3899
4475
  return result(j4.verdict, line, {
4476
+ ...backupAbs === void 0 ? {} : { backup: rel(backupAbs), backupAbs },
3900
4477
  ...retired.length > 0 ? { retired } : {},
3901
4478
  ...replaced.length > 0 ? { replaced } : {},
3902
4479
  changed: res.changed,
@@ -3924,6 +4501,7 @@ function createInstallWriter(args) {
3924
4501
  portionPaths: [...portionPaths],
3925
4502
  backups: [...backups],
3926
4503
  judged: [...judged],
4504
+ writes: [...writes],
3927
4505
  shared: [...shared2],
3928
4506
  outside: outside.list()
3929
4507
  })
@@ -4088,7 +4666,7 @@ function hookScript(handler, claudeDir) {
4088
4666
  const script = ref.slice("hooks/".length);
4089
4667
  return script.includes("/") ? null : script;
4090
4668
  }
4091
- function legacySettingsSeed(text, render, projectDir) {
4669
+ function legacySettingsSeed(text, render, projectDir, opts = {}) {
4092
4670
  const seed = /* @__PURE__ */ new Map();
4093
4671
  let root;
4094
4672
  try {
@@ -4098,6 +4676,7 @@ function legacySettingsSeed(text, render, projectDir) {
4098
4676
  }
4099
4677
  const claudeDir = join7(projectDir, ".claude");
4100
4678
  const candidates = new Set([...render.keys()].filter((k2) => k2.startsWith("hooks.")));
4679
+ const legacyLines = /* @__PURE__ */ new Set();
4101
4680
  const hooks = isObject2(root) && isObject2(root.hooks) ? root.hooks : {};
4102
4681
  for (const [event, groups] of Object.entries(hooks)) {
4103
4682
  if (!Array.isArray(groups)) continue;
@@ -4105,14 +4684,16 @@ function legacySettingsSeed(text, render, projectDir) {
4105
4684
  if (!isObject2(group) || !Array.isArray(group.hooks)) continue;
4106
4685
  for (const handler of group.hooks) {
4107
4686
  const script = hookScript(handler, claudeDir);
4108
- if (script !== null && RETIRED_PATHS.includes(`hooks/${script}`)) {
4109
- candidates.add(`hooks.${event}#${script}`);
4110
- }
4687
+ if (script === null) continue;
4688
+ const key = `hooks.${event}#${script}`;
4689
+ if (RETIRED_PATHS.includes(`hooks/${script}`)) candidates.add(key);
4690
+ if (isObject2(handler) && isLegacyHookLine(handler.command, claudeDir)) legacyLines.add(key);
4111
4691
  }
4112
4692
  }
4113
4693
  }
4114
4694
  const present2 = ADAPTERS["json-keys"].read(text, candidates, projectDir) ?? /* @__PURE__ */ new Map();
4115
4695
  for (const [key, sha] of present2) {
4696
+ if (!legacyLines.has(key) && opts.edited !== true) continue;
4116
4697
  const rendered = render.get(key);
4117
4698
  const handler = isObject2(rendered) && Array.isArray(rendered.hooks) ? rendered.hooks[0] : void 0;
4118
4699
  seed.set(key, handler === void 0 ? sha : jsonSha(handler));
@@ -4209,8 +4790,8 @@ function legacyGitignoreSeed(text, render, previous) {
4209
4790
 
4210
4791
  // src/installer.ts
4211
4792
  init_esm_shims();
4212
- import { chmodSync as chmodSync3, existsSync as existsSync22, readdirSync as readdirSync4, readFileSync as readFileSync22, writeFileSync as writeFileSync9 } from "fs";
4213
- import { basename as basename4, join as join30, relative as relative3, resolve, sep as sep4 } from "path";
4793
+ import { chmodSync as chmodSync3, existsSync as existsSync22, readdirSync as readdirSync5, readFileSync as readFileSync22, writeFileSync as writeFileSync8 } from "fs";
4794
+ import { basename as basename4, join as join31, relative as relative5, resolve, sep as sep6 } from "path";
4214
4795
 
4215
4796
  // src/anchor-seed.ts
4216
4797
  init_esm_shims();
@@ -4489,37 +5070,34 @@ function createOwnedWriter(projectDir, baseline, options = {}) {
4489
5070
  return false;
4490
5071
  }
4491
5072
  if (outside.skip(absPath)) return false;
4492
- const digest = hashContent(content);
4493
- if (!existsSync9(absPath)) {
4494
- const recordedPath = baseline.has(rel);
4495
- if (refreshOnly && !opts?.createInRefresh && !recordedPath) return false;
5073
+ const recordedSha = baseline.get(rel);
5074
+ const rec = recordedSha !== void 0 ? { state: "sha", sha256: recordedSha } : { state: owned(rel) ? "no-sha" : "none" };
5075
+ const disk = existsSync9(absPath) ? readFileSync6(absPath, "utf8") : null;
5076
+ if (disk === null && refreshOnly && !opts?.createInRefresh && rec.state !== "sha")
5077
+ return false;
5078
+ const { verdict, record } = judge({ op: "write", kind: "harness", rec, disk, next: content });
5079
+ let backupRel = null;
5080
+ if (verdict === "backup+overwrite" || verdict === "overwrite" && opts?.backupFirst === true) {
5081
+ const backup = backupFile(absPath, now);
5082
+ backupPaths.push(backup);
5083
+ journal?.backup(backup);
5084
+ backupRel = relative2(projectDir, backup).split(sep3).join("/");
5085
+ if (verdict === "backup+overwrite" && rec.state !== "none") backedUp.push(rel);
5086
+ }
5087
+ if (record === "displaced" || record === "displaced+sha") {
5088
+ displace({ path: rel, change: "displaced", notes: backupRel === null ? [] : [backupRel] });
5089
+ }
5090
+ if (verdict !== "leave") {
4496
5091
  mkdirSync2(dirname2(absPath), { recursive: true });
4497
5092
  writeFileSync4(absPath, content);
4498
5093
  updated++;
4499
- if (refreshOnly && recordedPath) restored.push(rel);
4500
- } else {
4501
- const current = readFileSync6(absPath, "utf8");
4502
- const firstContact = !baseline.has(rel) && !owned(rel);
4503
- if (current === content && firstContact) {
4504
- displace({ path: rel, change: "displaced", notes: [] });
4505
- return true;
4506
- }
4507
- if (current !== content) {
4508
- if (!isHarnessOwned(baseline, rel, current)) {
4509
- const backup = backupFile(absPath, now);
4510
- backupPaths.push(backup);
4511
- journal?.backup(backup);
4512
- if (firstContact) {
4513
- const backupRel = relative2(projectDir, backup).split(sep3).join("/");
4514
- displace({ path: rel, change: "displaced", notes: [backupRel] });
4515
- } else backedUp.push(rel);
4516
- }
4517
- writeFileSync4(absPath, content);
4518
- updated++;
4519
- }
5094
+ if (refreshOnly && verdict === "create" && rec.state === "sha") restored.push(rel);
5095
+ }
5096
+ if (record === "sha" || record === "displaced+sha") {
5097
+ const digest = hashContent(content);
5098
+ written.set(rel, digest);
5099
+ journal?.file({ path: rel, sha256: digest });
4520
5100
  }
4521
- written.set(rel, digest);
4522
- journal?.file({ path: rel, sha256: digest });
4523
5101
  return true;
4524
5102
  },
4525
5103
  ...journal === void 0 ? {} : { journal },
@@ -4594,7 +5172,7 @@ function renderRulesBlock(rules) {
4594
5172
  // src/shared-write.ts
4595
5173
  init_esm_shims();
4596
5174
  import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync8, rmSync, statSync as statSync3, writeFileSync as writeFileSync5 } from "fs";
4597
- import { dirname as dirname3, join as join13 } from "path";
5175
+ import { dirname as dirname3, join as join13, relative as relative3, sep as sep4 } from "path";
4598
5176
  function toPortions(path, portions) {
4599
5177
  const adapter = adapterFor(path);
4600
5178
  if (adapter === null) throw new Error(`shared-write: ${path} is not a shared file`);
@@ -4723,28 +5301,43 @@ function hasMarkerLine(disk, markers) {
4723
5301
  }
4724
5302
  function agentsMdModel(disk, recordedByHarness, recordedBlock = false) {
4725
5303
  if (disk === null) return recordedBlock && !recordedByHarness ? "block" : "sections";
4726
- if (hasMarkerLine(disk, [AGENTS_BLOCK.start, AGENTS_BLOCK.end])) return "block";
5304
+ const legacy = isLegacyHarnessAgentsMd(disk);
5305
+ if (hasMarkerLine(disk, [AGENTS_BLOCK.start, AGENTS_BLOCK.end]) && !legacy) return "block";
4727
5306
  if (recordedByHarness) return "sections";
4728
5307
  if (hasMarkerLine(disk, [ANCHOR_BLOCK.start, SKILLS_BLOCK.start])) return "sections";
5308
+ if (legacy) return "sections";
4729
5309
  return "block";
4730
5310
  }
4731
5311
  function writeAgentsMd(params) {
4732
5312
  const abs = join13(params.projectDir, AGENTS_MD);
4733
5313
  const disk = existsSync11(abs) ? readFileSync8(abs, "utf8") : null;
4734
5314
  const recordedBlock = (params.record.portions ?? []).some((p2) => p2.path === AGENTS_MD);
4735
- if (agentsMdModel(disk, params.baseline.has(AGENTS_MD), recordedBlock) === "sections") {
4736
- params.writer.write(
4737
- abs,
4738
- mergeAgentsMd({
4739
- rendered: params.rendered,
4740
- existing: disk,
4741
- template: params.template,
4742
- ...params.replaceUnfilledContext === true ? { replaceUnfilledContext: true } : {},
4743
- ...params.harnessRuleHeadings === void 0 ? {} : { harnessRuleHeadings: params.harnessRuleHeadings }
4744
- }),
4745
- params.createInRefresh === true ? { createInRefresh: true } : void 0
4746
- );
4747
- return { model: "sections", shared: null };
5315
+ const recordedByHarness = params.baseline.has(AGENTS_MD);
5316
+ if (agentsMdModel(disk, recordedByHarness, recordedBlock) === "sections") {
5317
+ const legacyUnrecorded = disk !== null && !recordedByHarness && isLegacyHarnessAgentsMd(disk);
5318
+ const merged = mergeAgentsMdDetailed({
5319
+ rendered: params.rendered,
5320
+ // N2 — 첫 접촉 블록은 떼고 병합한다(블록 안은 지금 렌더가 다시 쓴다)
5321
+ existing: disk === null ? null : withoutAgentsBlock(disk),
5322
+ template: params.template,
5323
+ ...params.replaceUnfilledContext === true || legacyUnrecorded ? { replaceUnfilledContext: true } : {},
5324
+ ...params.harnessRuleHeadings === void 0 ? {} : { harnessRuleHeadings: params.harnessRuleHeadings }
5325
+ });
5326
+ const dropped = merged.droppedLegacySections;
5327
+ const edited = merged.editedLegacySections;
5328
+ params.writer.write(abs, merged.text, {
5329
+ ...params.createInRefresh === true ? { createInRefresh: true } : {},
5330
+ // ADR-111 — 떨어뜨린 절은 현행 번들에 없는 바이트다 · 고친 이름 절은 설치자 글이다: 기준선 그대로인 파일이라도 1회 백업
5331
+ ...dropped > 0 || edited.length > 0 ? { backupFirst: true } : {}
5332
+ });
5333
+ if (recordedBlock) params.writer.journal?.portions(AGENTS_MD, []);
5334
+ return {
5335
+ model: "sections",
5336
+ shared: null,
5337
+ ...dropped > 0 ? { droppedLegacySections: dropped } : {},
5338
+ ...edited.length > 0 ? { editedLegacySections: edited } : {},
5339
+ ...recordedBlock ? { blockRecordDropped: true } : {}
5340
+ };
4748
5341
  }
4749
5342
  const shared2 = writeShared({
4750
5343
  projectDir: params.projectDir,
@@ -4817,6 +5410,10 @@ function stripShared(params) {
4817
5410
  });
4818
5411
  }
4819
5412
  const fileRemoved = params.removeIfEmpty === true && res.removed.length > 0 && res.empty;
5413
+ const edited = res.removedEdited.length === 0 ? void 0 : {
5414
+ keys: res.removedEdited,
5415
+ backup: params.write ? relative3(projectDir, backupFile(abs)).split(sep4).join("/") : `${path}.backup-<time>`
5416
+ };
4820
5417
  if (params.write && fileRemoved) rmSync(abs);
4821
5418
  else if (params.write && res.changed) writeFileSync5(abs, res.text);
4822
5419
  const action = res.removed.length > 0 ? "removed" : res.kept.length > 0 ? "left" : "none";
@@ -4824,6 +5421,7 @@ function stripShared(params) {
4824
5421
  removed: res.removed,
4825
5422
  kept: res.kept,
4826
5423
  portions: toPortions(path, res.portions),
5424
+ ...edited === void 0 ? {} : { edited },
4827
5425
  ...fileRemoved ? { fileRemoved } : {}
4828
5426
  });
4829
5427
  }
@@ -4834,10 +5432,15 @@ function runAgentsMdTransform(params) {
4834
5432
  const { harnessRoot, projectDir, baseline, shared: shared2 = {} } = params;
4835
5433
  const refreshOnly = params.refreshOnly === true;
4836
5434
  const clis = agentsMdClis(params.clis);
5435
+ const agentsMdPath = join14(projectDir, "AGENTS.md");
5436
+ const exists2 = existsSync12(agentsMdPath);
5437
+ const diskBefore = exists2 ? readFileSync9(agentsMdPath, "utf8") : null;
5438
+ const legacyAgentsMd = diskBefore !== null && isLegacyHarnessAgentsMd(diskBefore);
5439
+ const owned = (rel) => params.owned?.(rel) === true || rel === "AGENTS.md" && legacyAgentsMd;
4837
5440
  const writer = createOwnedWriter(projectDir, baseline, {
4838
5441
  refreshOnly,
4839
5442
  ...params.journal === void 0 ? {} : { journal: params.journal },
4840
- ...params.owned === void 0 ? {} : { owned: params.owned }
5443
+ owned
4841
5444
  });
4842
5445
  const claudeMd = readRequired(join14(harnessRoot, "templates/CLAUDE.md"));
4843
5446
  const template = readRequired(join14(harnessRoot, AGENTS_MD_TEMPLATE));
@@ -4847,9 +5450,7 @@ function runAgentsMdTransform(params) {
4847
5450
  refreshOnly,
4848
5451
  writer
4849
5452
  });
4850
- const agentsMdPath = join14(projectDir, "AGENTS.md");
4851
- const exists = existsSync12(agentsMdPath);
4852
- const creates = !exists && (!refreshOnly || params.installedByRecord === true);
5453
+ const creates = !exists2 && (!refreshOnly || params.installedByRecord === true);
4853
5454
  const legacy = params.legacyContext ?? null;
4854
5455
  const fromRoot = creates ? seedAgentsMdProjectContext(projectDir) : null;
4855
5456
  const seed = fromRoot ?? legacy;
@@ -4867,7 +5468,6 @@ function runAgentsMdTransform(params) {
4867
5468
  harnessRules: renderRulesBlock(portRules(harnessRoot, params.rules)),
4868
5469
  clis
4869
5470
  });
4870
- const diskBefore = exists ? readFileSync9(agentsMdPath, "utf8") : null;
4871
5471
  const agentsMd = writeAgentsMd({
4872
5472
  projectDir,
4873
5473
  rendered,
@@ -5564,14 +6164,14 @@ function renderHarnessMcp(harnessRoot, choice) {
5564
6164
  return composeMcpJson({
5565
6165
  ...mcpSources(harnessRoot),
5566
6166
  tracks: choice.spec.tracks,
5567
- chosen: (rows) => selectedMcpServers(rows, choice),
5568
- dropped: (rows) => droppedMcpServers(rows, choice.excluded)
6167
+ chosen: (rows2) => selectedMcpServers(rows2, choice),
6168
+ dropped: (rows2) => droppedMcpServers(rows2, choice.excluded)
5569
6169
  });
5570
6170
  }
5571
6171
  function trackDefaultMcpAssetIds(harnessRoot, tracks) {
5572
- const rows = trackRows(harnessRoot);
5573
- const selectable = selectableMcpServers(rows);
5574
- return rows.flatMap((r) => {
6172
+ const rows2 = trackRows(harnessRoot);
6173
+ const selectable = selectableMcpServers(rows2);
6174
+ return rows2.flatMap((r) => {
5575
6175
  const id = selectable.get(r.name);
5576
6176
  return id !== void 0 && anyTrack(tracks, r.pattern) ? [id] : [];
5577
6177
  });
@@ -5592,13 +6192,13 @@ function renderEverySelectableMcp(harnessRoot, tracks) {
5592
6192
  return composeMcpJson({
5593
6193
  ...mcpSources(harnessRoot),
5594
6194
  tracks,
5595
- chosen: (rows) => [...selectableMcpServers(rows).keys()]
6195
+ chosen: (rows2) => [...selectableMcpServers(rows2).keys()]
5596
6196
  });
5597
6197
  }
5598
6198
  function adapterExcluded(harnessRoot, excluded) {
5599
- const rows = trackRows(harnessRoot);
6199
+ const rows2 = trackRows(harnessRoot);
5600
6200
  const out = new Set(excluded);
5601
- for (const [name, assetId] of selectableMcpServers(rows)) {
6201
+ for (const [name, assetId] of selectableMcpServers(rows2)) {
5602
6202
  const id = keyId(".mcp.json", `mcpServers.${name}`);
5603
6203
  if (excluded.has(assetId) && id !== null) out.add(id);
5604
6204
  }
@@ -5770,15 +6370,62 @@ function runCliTransforms(params) {
5770
6370
  externalOutside,
5771
6371
  sharedFiles,
5772
6372
  portions: sharedFiles.flatMap((r) => r.portions ?? []),
5773
- portionPaths: sharedFiles.filter((r) => r.portions !== null).map((r) => r.path),
6373
+ // #774 (N2) — 블록 모델이던 `AGENTS.md` 를 절 모델로 바꿨으면 그 파일의 몫 기록을 비운다(판정한 경로 · 몫 없음)
6374
+ portionPaths: [
6375
+ ...sharedFiles.filter((r) => r.portions !== null).map((r) => r.path),
6376
+ ...agentsMd?.agentsMd.blockRecordDropped === true ? ["AGENTS.md"] : []
6377
+ ],
5774
6378
  restoredFiles
5775
6379
  };
5776
6380
  }
5777
6381
 
6382
+ // src/displaced.ts
6383
+ init_esm_shims();
6384
+ import { lstatSync as lstatSync3, mkdirSync as mkdirSync5, renameSync } from "fs";
6385
+ import { dirname as dirname5, join as join23 } from "path";
6386
+ function exists(abs) {
6387
+ try {
6388
+ lstatSync3(abs);
6389
+ return true;
6390
+ } catch {
6391
+ return false;
6392
+ }
6393
+ }
6394
+ function restoreDisplaced(log, projectDir, opts) {
6395
+ const out = [];
6396
+ for (const f of log.rootFiles ?? []) {
6397
+ if (f.change !== "displaced" || !opts.has(f.path)) continue;
6398
+ const slot = join23(projectDir, f.path);
6399
+ if (outsideProjectTarget(projectDir, slot) !== null) continue;
6400
+ const backup = f.notes[0] ?? null;
6401
+ const d2 = judgeDisplaced({
6402
+ backup,
6403
+ backupExists: backup !== null && exists(join23(projectDir, backup)),
6404
+ slotEmpty: !exists(slot) || !opts.write && opts.willBeGone?.has(f.path) === true
6405
+ });
6406
+ if (opts.write && d2.verdict === "restore" && backup !== null) {
6407
+ mkdirSync5(dirname5(slot), { recursive: true });
6408
+ renameSync(join23(projectDir, backup), slot);
6409
+ }
6410
+ out.push({
6411
+ path: f.path,
6412
+ backup,
6413
+ verdict: d2.verdict,
6414
+ line: d2.line,
6415
+ forget: d2.record === "forget"
6416
+ });
6417
+ }
6418
+ return out;
6419
+ }
6420
+ function withoutRestored(rootFiles, outcomes) {
6421
+ const settled = new Set(outcomes.filter((d2) => d2.forget).map((d2) => d2.path));
6422
+ return (rootFiles ?? []).filter((f) => !(f.change === "displaced" && settled.has(f.path)));
6423
+ }
6424
+
5778
6425
  // src/excluded-still-there.ts
5779
6426
  init_esm_shims();
5780
6427
  import { existsSync as existsSync18 } from "fs";
5781
- import { join as join23 } from "path";
6428
+ import { join as join24 } from "path";
5782
6429
  function excludedStillThere(projectDir, excluded, baselineOnDisk, previousLog) {
5783
6430
  const out = [];
5784
6431
  const add = (id, catalog) => {
@@ -5791,7 +6438,7 @@ function excludedStillThere(projectDir, excluded, baselineOnDisk, previousLog) {
5791
6438
  for (const id of excluded) {
5792
6439
  if (INTERNAL_BUNDLED_SKILL_IDS.includes(id)) {
5793
6440
  const there = [".claude/skills", ".agents/skills"].some(
5794
- (d2) => existsSync18(join23(projectDir, d2, id))
6441
+ (d2) => existsSync18(join24(projectDir, d2, id))
5795
6442
  );
5796
6443
  if (there) add(id, false);
5797
6444
  } else {
@@ -5805,17 +6452,27 @@ function excludedStillThere(projectDir, excluded, baselineOnDisk, previousLog) {
5805
6452
  function assetStillThere(projectDir, asset) {
5806
6453
  if (asset.method !== "skill" || asset.scope === "global") return true;
5807
6454
  if (asset.files !== void 0)
5808
- return asset.files.some((f) => existsSync18(join23(projectDir, f.path)));
6455
+ return asset.files.some((f) => existsSync18(join24(projectDir, f.path)));
5809
6456
  const dir = asset.detail.skill ?? asset.id;
5810
- return [".claude/skills", ".agents/skills"].some((d2) => existsSync18(join23(projectDir, d2, dir)));
6457
+ return [".claude/skills", ".agents/skills"].some((d2) => existsSync18(join24(projectDir, d2, dir)));
5811
6458
  }
5812
6459
 
5813
6460
  // src/external-installer.ts
5814
6461
  init_esm_shims();
5815
6462
  import { spawnSync } from "child_process";
5816
- import { existsSync as existsSync19, mkdirSync as mkdirSync5, readFileSync as readFileSync17, writeFileSync as writeFileSync7 } from "fs";
6463
+ import {
6464
+ accessSync,
6465
+ existsSync as existsSync19,
6466
+ constants as fsConstants,
6467
+ mkdirSync as mkdirSync6,
6468
+ readdirSync as readdirSync3,
6469
+ readFileSync as readFileSync17,
6470
+ renameSync as renameSync2,
6471
+ rmdirSync,
6472
+ rmSync as rmSync3
6473
+ } from "fs";
5817
6474
  import { homedir as homedir2 } from "os";
5818
- import { dirname as dirname5, join as join24 } from "path";
6475
+ import { dirname as dirname6, join as join25, relative as relative4, sep as sep5 } from "path";
5819
6476
  var SKILL_ROOTS = [".claude/skills", ".agents/skills"];
5820
6477
  function skillRootOf(cli) {
5821
6478
  return cli === "claude" ? ".claude/skills" : ".agents/skills";
@@ -5823,11 +6480,11 @@ function skillRootOf(cli) {
5823
6480
  function snapshotSkillRoots(projectDir) {
5824
6481
  const out = /* @__PURE__ */ new Map();
5825
6482
  for (const root of SKILL_ROOTS) {
5826
- if (outsideProjectTarget(projectDir, join24(projectDir, root)) !== null) continue;
5827
- for (const rel of listFilesRecursive(join24(projectDir, root))) {
6483
+ if (outsideProjectTarget(projectDir, join25(projectDir, root)) !== null) continue;
6484
+ for (const rel of listFilesRecursive(join25(projectDir, root))) {
5828
6485
  const path = `${root}/${rel}`;
5829
6486
  try {
5830
- out.set(path, hashContent(readFileSync17(join24(projectDir, path), "utf8")));
6487
+ out.set(path, hashContent(readFileSync17(join25(projectDir, path), "utf8")));
5831
6488
  } catch {
5832
6489
  }
5833
6490
  }
@@ -5904,7 +6561,7 @@ function installOne(asset, ctx) {
5904
6561
  return runSpawn(asset, ctx.spawn, "npx", buildSkillArgs(method, ctx.cli, ctx.scope), cwd);
5905
6562
  }
5906
6563
  const outside = SKILL_ROOTS.flatMap((root) => {
5907
- const hit = outsideProjectTarget(cwd, join24(cwd, root));
6564
+ const hit = outsideProjectTarget(cwd, join25(cwd, root));
5908
6565
  return hit === null ? [] : [{ root, target: hit.target }];
5909
6566
  });
5910
6567
  const outRoots = new Set(outside.map((o) => o.root));
@@ -5982,32 +6639,46 @@ function refreshExternalSkills(projectDir, deps = {}) {
5982
6639
  return { ...none, notInCatalog, unknown: false };
5983
6640
  }
5984
6641
  const targetIds = new Set(targets.map((a) => a.id));
5985
- const settle = holdSkillSlots(
5986
- projectDir,
5987
- log.assets.filter((a) => targetIds.has(a.id))
5988
- );
5989
- const report = (deps.run ?? runExternalInstall)(
5990
- {
5991
- tracks: log.spec.tracks,
5992
- // `forceInclude` 가 조건 판정을 앞지르므로 옵션 값은 결과에 영향을 주지 않는다.
5993
- options: DEFAULT_OPTIONS,
5994
- // #528 — 대상 CLI 는 **깔린 집합**(`installedClis`)이다. `spec.cli` 는 마지막 install 의
5995
- // 요청이라 `uninstall --cli` 뒤에도 뺀 CLI 를 말하고, 반대로 그걸 비우면 `buildSkillArgs` 가
5996
- // `--agent`·`--copy` 를 못 붙여 Claude 몫이 조용히 빠진다(재리뷰 BLOCKER-3).
5997
- cli: [...installedClis(log)],
5998
- userOverride: { forceInclude: [...installedIds], forceExclude: [] },
5999
- scope: log.scope,
6000
- projectDir
6001
- },
6002
- {
6003
- assets: targets,
6004
- ...deps.spawn ? { spawn: deps.spawn } : {},
6005
- ...deps.log ? { log: deps.log } : {},
6006
- ...deps.warn ? { warn: deps.warn } : {}
6642
+ const clis = [...installedClis(log)];
6643
+ const settle = holdSkillSlots(projectDir, {
6644
+ recorded: log.assets.filter((a) => targetIds.has(a.id)),
6645
+ slots: skillSlotsFor(targets, clis, { log, scope: log.scope }),
6646
+ log,
6647
+ clis,
6648
+ scope: log.scope
6649
+ });
6650
+ const run = deps.run ?? runExternalInstall;
6651
+ let ran;
6652
+ try {
6653
+ ran = run(
6654
+ {
6655
+ tracks: log.spec.tracks,
6656
+ // `forceInclude` 가 조건 판정을 앞지르므로 옵션 값은 결과에 영향을 주지 않는다.
6657
+ options: DEFAULT_OPTIONS,
6658
+ // #528 — 대상 CLI 는 **깔린 집합**(`installedClis`)이다. `spec.cli` 는 마지막 install 의
6659
+ // 요청이라 `uninstall --cli` 뒤에도 뺀 CLI 를 말하고, 반대로 그걸 비우면 `buildSkillArgs` 가
6660
+ // `--agent`·`--copy` 를 못 붙여 Claude 몫이 조용히 빠진다(재리뷰 BLOCKER-3).
6661
+ cli: clis,
6662
+ userOverride: { forceInclude: [...installedIds], forceExclude: [] },
6663
+ scope: log.scope,
6664
+ projectDir
6665
+ },
6666
+ {
6667
+ assets: targets,
6668
+ ...deps.spawn ? { spawn: deps.spawn } : {},
6669
+ ...deps.log ? { log: deps.log } : {},
6670
+ ...deps.warn ? { warn: deps.warn } : {}
6671
+ }
6672
+ );
6673
+ } catch (e2) {
6674
+ try {
6675
+ settle(null);
6676
+ } catch {
6007
6677
  }
6008
- );
6009
- settle();
6010
- recordRefreshedFiles(projectDir, log, report, deps.writeLog ?? writeInstallLog);
6678
+ throw e2;
6679
+ }
6680
+ const { report, displaced } = settle(ran);
6681
+ recordRefreshedFiles(projectDir, log, report, displaced, deps.writeLog ?? writeInstallLog);
6011
6682
  return {
6012
6683
  attempted: report.attempted.length,
6013
6684
  refreshed: report.succeeded,
@@ -6016,53 +6687,160 @@ function refreshExternalSkills(projectDir, deps = {}) {
6016
6687
  unknown: false
6017
6688
  };
6018
6689
  }
6019
- function holdSkillSlots(projectDir, assets) {
6020
- const held = assets.flatMap((a) => holdSlotFiles(projectDir, a.files));
6021
- return () => held.flatMap((h2) => settleHeld(h2) ?? []);
6690
+ function holdSkillSlots(projectDir, hold) {
6691
+ const now = /* @__PURE__ */ new Date();
6692
+ const asides = /* @__PURE__ */ new Map();
6693
+ const moved = [];
6694
+ try {
6695
+ for (const f of slotFilesToKeep(projectDir, hold)) {
6696
+ const root = dirname6(f.slot);
6697
+ let dir = asides.get(root);
6698
+ if (dir === void 0) {
6699
+ dir = claimBackupDir(join25(projectDir, root), now);
6700
+ asides.set(root, dir);
6701
+ }
6702
+ const aside = join25(dir, f.rel.slice(root.length + 1));
6703
+ mkdirSync6(dirname6(aside), { recursive: true });
6704
+ renameSync2(f.abs, aside);
6705
+ moved.push({ ...f, aside });
6706
+ }
6707
+ } catch (e2) {
6708
+ for (const f of moved) renameSync2(f.aside, f.abs);
6709
+ for (const dir of asides.values()) pruneEmptyDirs(dir);
6710
+ throw new Error(
6711
+ `could not move your files aside before the skill tool runs \u2014 nothing was changed: ${e2 instanceof Error ? e2.message : String(e2)}`
6712
+ );
6713
+ }
6714
+ return (report) => {
6715
+ const failedSlots = report === null ? null : skillSlotsFor(
6716
+ report.attempted.filter((r) => !r.ok).map((r) => r.asset),
6717
+ hold.clis,
6718
+ { log: hold.log, scope: hold.scope }
6719
+ );
6720
+ const out = { backups: [], displaced: [] };
6721
+ const back = /* @__PURE__ */ new Set();
6722
+ const failed = [];
6723
+ for (const f of moved) {
6724
+ const restore = failedSlots === null || failedSlots.some((s) => f.rel === s || f.rel.startsWith(`${s}/`));
6725
+ try {
6726
+ const r = settleAside(projectDir, f, restore);
6727
+ if (r === "back") {
6728
+ back.add(f.rel);
6729
+ continue;
6730
+ }
6731
+ out.backups.push(r.backup);
6732
+ if (r.displaced !== null) out.displaced.push(r.displaced);
6733
+ } catch (e2) {
6734
+ failed.push(`${f.rel} (${e2 instanceof Error ? e2.message : String(e2)})`);
6735
+ }
6736
+ }
6737
+ for (const dir of asides.values()) pruneEmptyDirs(dir);
6738
+ if (failed.length > 0) {
6739
+ const at5 = [...asides.values()].map((d2) => `${toProjectRel(projectDir, d2)}/`).join(", ");
6740
+ throw new Error(
6741
+ `could not put back ${failed.length} file(s) moved aside before the skill tool ran \u2014 they stay in ${at5}: ${failed.join(", ")}`
6742
+ );
6743
+ }
6744
+ return { ...out, report: report === null ? null : withoutCreated(report, back) };
6745
+ };
6022
6746
  }
6023
- function holdSlotFiles(projectDir, files) {
6024
- if (files === void 0) return [];
6025
- const sha = new Map(files.map((f) => [f.path, f.sha256]));
6026
- const slots = new Set(files.map((f) => f.path.split("/").slice(0, 3).join("/")));
6747
+ function skillSlotsFor(assets, clis, ctx) {
6748
+ if (resolveScope(ctx.scope) === "global") return [];
6749
+ const roots = clis.length === 0 ? [...SKILL_ROOTS] : [...new Set(clis.map(skillRootOf))];
6750
+ const out = /* @__PURE__ */ new Set();
6751
+ for (const a of assets) {
6752
+ if (a.method.kind !== "skill") continue;
6753
+ const prior = ctx.log?.assets.find((r) => r.id === a.id);
6754
+ if (prior !== void 0 && prior.files === void 0) continue;
6755
+ const skill = a.method.skill;
6756
+ for (const root of roots) out.add(skill === void 0 ? root : `${root}/${skill}`);
6757
+ }
6758
+ return [...out];
6759
+ }
6760
+ function slotFilesToKeep(projectDir, hold) {
6761
+ const mine = hold.recorded.flatMap((a) => a.files ?? []);
6762
+ const sha = new Map(mine.map((f) => [f.path, f.sha256]));
6763
+ const targetIds = new Set(hold.recorded.map((a) => a.id));
6764
+ const others = new Set(
6765
+ (hold.log?.assets ?? []).filter((a) => !targetIds.has(a.id)).flatMap((a) => (a.files ?? []).map((f) => f.path))
6766
+ );
6767
+ const slots = /* @__PURE__ */ new Set([
6768
+ ...mine.map((f) => f.path.split("/").slice(0, 3).join("/")),
6769
+ ...hold.slots
6770
+ ]);
6027
6771
  const out = [];
6772
+ const seen = /* @__PURE__ */ new Set();
6028
6773
  for (const slot of slots) {
6029
- if (outsideProjectTarget(projectDir, join24(projectDir, slot)) !== null) continue;
6030
- for (const rel of listFilesRecursive(join24(projectDir, slot))) {
6774
+ if (outsideProjectTarget(projectDir, join25(projectDir, slot)) !== null) continue;
6775
+ for (const rel of listFilesRecursive(join25(projectDir, slot))) {
6031
6776
  const path = `${slot}/${rel}`;
6032
- const abs = join24(projectDir, path);
6033
- let content;
6777
+ if (seen.has(path)) continue;
6778
+ seen.add(path);
6779
+ const recordedSha = sha.get(path);
6780
+ if (recordedSha === void 0 && (others.has(path) || recorded(hold.log, path).state !== "none"))
6781
+ continue;
6782
+ const abs = join25(projectDir, path);
6034
6783
  try {
6035
- content = readFileSync17(abs);
6784
+ if (recordedSha !== void 0 && hashContent(readFileSync17(abs, "utf8")) === recordedSha)
6785
+ continue;
6786
+ accessSync(abs, fsConstants.R_OK);
6036
6787
  } catch {
6037
6788
  continue;
6038
6789
  }
6039
- const recordedSha = sha.get(path);
6040
- if (recordedSha === void 0) out.push({ abs, content, recorded: false });
6041
- else if (hashContent(content.toString("utf8")) !== recordedSha)
6042
- out.push({ abs, content, recorded: true });
6790
+ out.push({ abs, rel: path, slot, recorded: recordedSha !== void 0 });
6043
6791
  }
6044
6792
  }
6045
6793
  return out;
6046
6794
  }
6047
- function settleHeld(h2) {
6795
+ function settleAside(projectDir, f, restore) {
6048
6796
  let now = null;
6049
6797
  try {
6050
- now = readFileSync17(h2.abs);
6051
- } catch {
6798
+ now = readFileSync17(f.abs);
6799
+ } catch (e2) {
6800
+ const code = e2.code;
6801
+ if (code !== "ENOENT" && code !== "ENOTDIR") throw e2;
6052
6802
  }
6053
- if (now?.equals(h2.content)) return null;
6054
- if (!h2.recorded && now === null) {
6055
- mkdirSync5(dirname5(h2.abs), { recursive: true });
6056
- writeFileSync7(h2.abs, h2.content);
6057
- return null;
6803
+ if (restore || (now === null ? !f.recorded : now.equals(readFileSync17(f.aside)))) {
6804
+ mkdirSync6(dirname6(f.abs), { recursive: true });
6805
+ renameSync2(f.aside, f.abs);
6806
+ return "back";
6807
+ }
6808
+ const backup = backupFromCopy(f.abs, f.aside);
6809
+ rmSync3(f.aside, { force: true });
6810
+ if (f.recorded || now === null) return { backup, displaced: null };
6811
+ return {
6812
+ backup,
6813
+ displaced: { path: f.rel, change: "displaced", notes: [toProjectRel(projectDir, backup)] }
6814
+ };
6815
+ }
6816
+ function withoutCreated(report, paths) {
6817
+ if (paths.size === 0) return report;
6818
+ return {
6819
+ ...report,
6820
+ attempted: report.attempted.map(
6821
+ (r) => r.files === void 0 ? r : {
6822
+ ...r,
6823
+ files: { ...r.files, created: r.files.created.filter((x) => !paths.has(x.path)) }
6824
+ }
6825
+ )
6826
+ };
6827
+ }
6828
+ function pruneEmptyDirs(dir) {
6829
+ try {
6830
+ for (const e2 of readdirSync3(dir, { withFileTypes: true }))
6831
+ if (e2.isDirectory()) pruneEmptyDirs(join25(dir, e2.name));
6832
+ rmdirSync(dir);
6833
+ } catch {
6058
6834
  }
6059
- return backupContent(h2.abs, h2.content);
6060
6835
  }
6061
- function recordRefreshedFiles(projectDir, log, report, writeLog) {
6836
+ function toProjectRel(projectDir, abs) {
6837
+ return relative4(projectDir, abs).split(sep5).join("/");
6838
+ }
6839
+ function recordRefreshedFiles(projectDir, log, report, displaced, writeLog) {
6062
6840
  const next = new Map(
6063
6841
  buildAssetEntries(report, log.scope, log.assets).filter((e2) => e2.files !== void 0).map((e2) => [e2.id, e2.files])
6064
6842
  );
6065
- let changed = false;
6843
+ let changed = displaced.length > 0;
6066
6844
  const assets = log.assets.map((a) => {
6067
6845
  const files = next.get(a.id);
6068
6846
  if (files === void 0 || JSON.stringify(files) === JSON.stringify(a.files)) return a;
@@ -6071,7 +6849,11 @@ function recordRefreshedFiles(projectDir, log, report, writeLog) {
6071
6849
  });
6072
6850
  if (!changed) return;
6073
6851
  try {
6074
- writeLog(projectDir, { ...log, assets });
6852
+ writeLog(projectDir, {
6853
+ ...log,
6854
+ assets,
6855
+ ...displaced.length > 0 ? { rootFiles: mergeRootFiles(log.rootFiles, displaced) } : {}
6856
+ });
6075
6857
  } catch {
6076
6858
  }
6077
6859
  }
@@ -6151,7 +6933,7 @@ function detectVersion(method, spawn, scope, projectDir) {
6151
6933
  case "plugin":
6152
6934
  return detectPluginVersion(method.pluginId, scope, projectDir);
6153
6935
  case "npm": {
6154
- const pkgJson = scope === "global" ? detectGlobalNpmPackageJsonPath(method.pkg, spawn) : join24(projectDir, "node_modules", method.pkg, "package.json");
6936
+ const pkgJson = scope === "global" ? detectGlobalNpmPackageJsonPath(method.pkg, spawn) : join25(projectDir, "node_modules", method.pkg, "package.json");
6155
6937
  if (!pkgJson || !existsSync19(pkgJson)) return void 0;
6156
6938
  const parsed = JSON.parse(readFileSync17(pkgJson, "utf8"));
6157
6939
  return parsed.version;
@@ -6165,7 +6947,7 @@ function detectVersion(method, spawn, scope, projectDir) {
6165
6947
  }
6166
6948
  function detectPluginVersion(pluginId, scope, projectDir) {
6167
6949
  const claudeScope = scope === "global" ? "user" : "project";
6168
- const path = join24(homedir2(), ".claude/plugins/installed_plugins.json");
6950
+ const path = join25(homedir2(), ".claude/plugins/installed_plugins.json");
6169
6951
  if (!existsSync19(path)) return void 0;
6170
6952
  const data = JSON.parse(readFileSync17(path, "utf8"));
6171
6953
  const entries = data.plugins?.[pluginId] ?? [];
@@ -6180,7 +6962,7 @@ function detectPluginVersion(pluginId, scope, projectDir) {
6180
6962
  }
6181
6963
  function detectGlobalNpmPackageJsonPath(pkg, spawn) {
6182
6964
  const npmRoot = getNpmGlobalRoot(spawn);
6183
- return npmRoot ? join24(npmRoot, pkg, "package.json") : void 0;
6965
+ return npmRoot ? join25(npmRoot, pkg, "package.json") : void 0;
6184
6966
  }
6185
6967
  var npmGlobalRootCache;
6186
6968
  function getNpmGlobalRoot(spawn) {
@@ -6200,7 +6982,7 @@ function getNpmGlobalRoot(spawn) {
6200
6982
  // src/key-ids.ts
6201
6983
  init_esm_shims();
6202
6984
  import { readFileSync as readFileSync18 } from "fs";
6203
- import { join as join25 } from "path";
6985
+ import { join as join26 } from "path";
6204
6986
  function renderedKeyIds(harnessRoot, input, clis) {
6205
6987
  const out = /* @__PURE__ */ new Set();
6206
6988
  const add = (path, key) => {
@@ -6223,7 +7005,7 @@ function renderedKeyIds(harnessRoot, input, clis) {
6223
7005
  for (const name of Object.keys(mcp.mcpServers)) add(".mcp.json", `mcpServers.${name}`);
6224
7006
  for (const key of gitignoreRender().keys()) add(".gitignore", key);
6225
7007
  if (clis.includes("claude")) {
6226
- const template = readFileSync18(join25(harnessRoot, "templates/settings.json"), "utf8");
7008
+ const template = readFileSync18(join26(harnessRoot, "templates/settings.json"), "utf8");
6227
7009
  for (const key of renderSettingsPortion(template, () => true).keys()) {
6228
7010
  add(".claude/settings.json", key);
6229
7011
  }
@@ -6264,7 +7046,7 @@ function withoutAccepts(harnessRoot, spec, previous) {
6264
7046
 
6265
7047
  // src/linked-skill-bodies.ts
6266
7048
  init_esm_shims();
6267
- import { join as join26 } from "path";
7049
+ import { join as join27 } from "path";
6268
7050
  function refreshLinkedSkillBodies(params) {
6269
7051
  const { harnessRoot, projectDir, ids, baseline, journal } = params;
6270
7052
  const bodyKey = (id) => `.agents/skills/${id}/SKILL.md`;
@@ -6275,7 +7057,7 @@ function refreshLinkedSkillBodies(params) {
6275
7057
  );
6276
7058
  const result = writer.result();
6277
7059
  return {
6278
- updated: ours.filter((id) => written.has(join26(projectDir, bodyKey(id)))),
7060
+ updated: ours.filter((id) => written.has(join27(projectDir, bodyKey(id)))),
6279
7061
  notOurs: ids.filter((id) => !baseline.has(bodyKey(id))),
6280
7062
  files: result.files,
6281
7063
  backupPaths: result.backupPaths,
@@ -6287,8 +7069,8 @@ function refreshLinkedSkillBodies(params) {
6287
7069
 
6288
7070
  // src/out-of-track.ts
6289
7071
  init_esm_shims();
6290
- import { lstatSync as lstatSync3, readFileSync as readFileSync19, rmSync as rmSync3 } from "fs";
6291
- import { join as join27 } from "path";
7072
+ import { lstatSync as lstatSync4, readFileSync as readFileSync19, rmSync as rmSync4 } from "fs";
7073
+ import { join as join28 } from "path";
6292
7074
  var AGENTS_RULE = /^\.agents\/rules\/([^/]+)\.md$/;
6293
7075
  var ANCHOR_RULE = "uzys-harness";
6294
7076
  function reclaimOutOfTrack(args) {
@@ -6308,9 +7090,9 @@ function reclaimOutOfTrack(args) {
6308
7090
  const name = AGENTS_RULE.exec(f.path)?.[1];
6309
7091
  if (name === void 0 || name === ANCHOR_RULE || rendered.has(name)) continue;
6310
7092
  if (isBaselineExcluded(`.claude/rules/${name}.md`, args.excluded)) continue;
6311
- const abs = join27(projectDir, f.path);
7093
+ const abs = join28(projectDir, f.path);
6312
7094
  if (args.outside.skip(abs)) continue;
6313
- const stat = lstatSync3(abs, { throwIfNoEntry: false });
7095
+ const stat = lstatSync4(abs, { throwIfNoEntry: false });
6314
7096
  if (stat !== void 0 && !stat.isFile()) continue;
6315
7097
  if (stat !== void 0 && claudeCopy.has(`rules/${name}.md`)) {
6316
7098
  out.kept.push(f.path);
@@ -6330,11 +7112,11 @@ function reclaimOutOfTrack(args) {
6330
7112
  out.bringBack[f.path] = tracksBringing(name);
6331
7113
  }
6332
7114
  if (verdict.verdict === "remove") {
6333
- rmSync3(abs);
7115
+ rmSync4(abs);
6334
7116
  out.removed.push(f.path);
6335
7117
  } else if (verdict.verdict === "backup+remove") {
6336
7118
  const backup = backupFile(abs, args.now);
6337
- rmSync3(abs);
7119
+ rmSync4(abs);
6338
7120
  out.backedUp.push({ path: f.path, backup });
6339
7121
  }
6340
7122
  }
@@ -6350,14 +7132,14 @@ function tracksBringing(rule) {
6350
7132
  // src/state.ts
6351
7133
  init_esm_shims();
6352
7134
  import { existsSync as existsSync20, readFileSync as readFileSync20 } from "fs";
6353
- import { join as join28 } from "path";
7135
+ import { join as join29 } from "path";
6354
7136
  var META_FILE = ".claude/.installed-tracks";
6355
7137
  var LEGACY_ANCHOR_FILE = ".claude/CLAUDE.md";
6356
7138
  var TRACE_RULES = ["htmx", "nextjs", "data-analysis", "pyside6", "cli-development"].map(
6357
7139
  (r) => `.claude/rules/${r}.md`
6358
7140
  );
6359
7141
  function detectInstallState(projectDir) {
6360
- const hasClaudeDir = existsSync20(join28(projectDir, ".claude"));
7142
+ const hasClaudeDir = existsSync20(join29(projectDir, ".claude"));
6361
7143
  const read = readInstallLogStatus(projectDir);
6362
7144
  if (read.status === "ok") {
6363
7145
  return {
@@ -6377,7 +7159,7 @@ function tracksFromLog(tracks) {
6377
7159
  return [...new Set(tracks.filter(isTrack))].sort();
6378
7160
  }
6379
7161
  function collectTraces(projectDir) {
6380
- const at5 = (rel) => join28(projectDir, rel);
7162
+ const at5 = (rel) => join29(projectDir, rel);
6381
7163
  const contains = (rel, needle) => existsSync20(at5(rel)) && readFileSync20(at5(rel), "utf8").includes(needle);
6382
7164
  const traces = [];
6383
7165
  if (existsSync20(at5(META_FILE)))
@@ -6437,16 +7219,14 @@ init_esm_shims();
6437
7219
  import {
6438
7220
  copyFileSync,
6439
7221
  existsSync as existsSync21,
6440
- lstatSync as lstatSync4,
6441
- mkdirSync as mkdirSync6,
6442
- readdirSync as readdirSync3,
7222
+ readdirSync as readdirSync4,
6443
7223
  readFileSync as readFileSync21,
6444
- rmdirSync,
7224
+ rmdirSync as rmdirSync2,
6445
7225
  statSync as statSync4,
6446
7226
  unlinkSync as unlinkSync2,
6447
- writeFileSync as writeFileSync8
7227
+ writeFileSync as writeFileSync7
6448
7228
  } from "fs";
6449
- import { basename as basename3, dirname as dirname6, join as join29 } from "path";
7229
+ import { basename as basename3, dirname as dirname7, join as join30 } from "path";
6450
7230
  var LEGACY_ANCHOR_FILE2 = ".claude/CLAUDE.md";
6451
7231
  function buildUpdateSpec(projectDir, tracks, only) {
6452
7232
  const log = readInstallLog(projectDir);
@@ -6468,7 +7248,7 @@ function parseUpdateOnly(raw) {
6468
7248
  return { ok: true, groups: [...new Set(raw)] };
6469
7249
  }
6470
7250
  function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}, only) {
6471
- const claudeDir = join29(projectDir, ".claude");
7251
+ const claudeDir = join30(projectDir, ".claude");
6472
7252
  const startedAt = Date.now();
6473
7253
  const backupsAtStart = new Set(
6474
7254
  collectRunBackups(projectDir, Number.NEGATIVE_INFINITY).map((b2) => b2.backup)
@@ -6505,87 +7285,12 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}, only) {
6505
7285
  demotedAgents: [],
6506
7286
  mcpAllowlistRetired: null
6507
7287
  };
6508
- const fresh = installNewAssets(projectDir, templatesDir, wants, outside);
6509
- report.installedNew = fresh.installed;
6510
- report.restored = fresh.restored;
6511
7288
  const logAtStart = readInstallLog(projectDir);
6512
7289
  const claudeManaged = logAtStart === null || installedClis(logAtStart).includes("claude");
6513
- const recordOf = recordedStateOf(logAtStart);
6514
- const claudeDisplaced = [];
6515
7290
  report.claudeUnrecorded = !claudeManaged && logAtStart !== null && existsSync21(claudeDir) ? recordClaudeCommand(logAtStart, installedTracks(projectDir)) : null;
6516
- const policyBase = policyBaseline(projectDir);
6517
- for (const { dir, ext } of POLICY_DIRS) {
6518
- if (!claudeManaged || !wants(dir === "hooks" ? "hooks" : "rules")) continue;
6519
- const target = join29(claudeDir, dir);
6520
- const source = join29(templatesDir, dir);
6521
- const label = `.claude/${dir}`;
6522
- const ctx = {
6523
- prefix: dir,
6524
- baseline: policyBase,
6525
- outside,
6526
- excluded: excludedIds(logAtStart),
6527
- // 0단계가 방금 깐 파일은 기록 전이지만 하네스 것이다 — 첫 접촉으로 읽지 않는다
6528
- justWritten: new Set(fresh.installed),
6529
- ...recordOf === void 0 ? {} : { recordOf }
6530
- };
6531
- const synced = updateDir(target, source, ext, ctx);
6532
- report.updated[label] = synced.updated;
6533
- report.policyBackedUp.push(...synced.backedUp);
6534
- claudeDisplaced.push(...synced.displaced);
6535
- report.pruned[label] = pruneOrphans(target, source, ext, ctx);
6536
- }
6537
- const syncedDirs = POLICY_DIRS.filter((d2) => wants(d2.dir === "hooks" ? "hooks" : "rules")).map(
6538
- (d2) => d2.dir
6539
- );
6540
- if (claudeManaged && syncedDirs.length > 0)
6541
- refreshPolicyBaseline(
6542
- projectDir,
6543
- templatesDir,
6544
- syncedDirs,
6545
- outsidePaths(outside, ".claude/"),
6546
- notOursUnder(claudeDisplaced, ".claude/")
6547
- );
6548
- const skillTargets = logAtStart === null ? void 0 : recordedSkillIds(projectDir, logAtStart);
6549
- const skillSync = claudeManaged && wants("skills") ? syncSkills(
6550
- join29(claudeDir, "skills"),
6551
- join29(templatesDir, "skills"),
6552
- skillBaseline(projectDir),
6553
- /* @__PURE__ */ new Date(),
6554
- (relInSkills) => foreignOwnedTarget(projectDir, `.claude/skills/${relInSkills}`),
6555
- outside,
6556
- excludedSkillOf(logAtStart),
6557
- {
6558
- ...recordOf === void 0 ? {} : { recordOf },
6559
- ...skillTargets === void 0 ? {} : { targets: skillTargets }
6560
- }
6561
- ) : {
6562
- updated: 0,
6563
- backedUp: [],
6564
- skippedLinks: [],
6565
- foreignOwned: [],
6566
- pruned: [],
6567
- displaced: []
6568
- };
6569
- claudeDisplaced.push(...skillSync.displaced);
6570
- if (wants("skills")) report.updated[".claude/skills"] = skillSync.updated;
6571
- report.skillsBackedUp = skillSync.backedUp;
6572
- report.skillsSkippedLinks = skillSync.skippedLinks;
6573
- report.skillsPruned = skillSync.pruned;
6574
- const newSkills = claudeManaged && wants("new-skills") ? installNewSkillDirs(projectDir, templatesDir, installedTracks(projectDir), outside) : { installed: [], restored: [], foreignOwned: [] };
6575
- report.installedNew.push(...newSkills.installed);
6576
- report.restored.push(...newSkills.restored);
6577
- const copiedSkills = [...newSkills.installed, ...newSkills.restored];
6578
- if (claudeManaged && wants("skills"))
6579
- refreshSkillBaseline(
6580
- projectDir,
6581
- templatesDir,
6582
- outsidePaths(outside, ".claude/skills/"),
6583
- skillTargets,
6584
- notOursUnder(claudeDisplaced, ".claude/skills/")
6585
- );
6586
- else if (claudeManaged && copiedSkills.length > 0)
6587
- recordNewSkillBaseline(projectDir, templatesDir, copiedSkills);
6588
- if (claudeDisplaced.length > 0) recordDisplaced(projectDir, claudeDisplaced);
7291
+ const harness = logAtStart === null ? { displaced: [], restored: [], foreignOwned: [], outside: [] } : updateHarnessFiles({ projectDir, templatesDir, log: logAtStart, wants, outside, report });
7292
+ const claudeDisplaced = harness.displaced;
7293
+ const prunedRestored = harness.restored;
6589
7294
  if (existsSync21(claudeDir) && claudeManaged) {
6590
7295
  if (wants("anchor")) syncHarnessAnchor(projectDir, templatesDir, report, outside);
6591
7296
  }
@@ -6605,7 +7310,7 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}, only) {
6605
7310
  droppedKeys: legacyDroppedKeys(logAtStart),
6606
7311
  harnessMcp
6607
7312
  });
6608
- const settingsPath = join29(claudeDir, "settings.json");
7313
+ const settingsPath = join30(claudeDir, "settings.json");
6609
7314
  if (claudeManaged && wants("hooks") && existsSync21(settingsPath) && !outside.skip(settingsPath)) {
6610
7315
  report.retiredHookRefsRemoved = removeRetiredHookRefs(
6611
7316
  settingsPath,
@@ -6621,8 +7326,6 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}, only) {
6621
7326
  if (wants("hooks")) report.mcpAllowlistRetired = retireMcpAllowlist(projectDir);
6622
7327
  report.retiredAgents = staleAgentFiles(claudeDir);
6623
7328
  report.demotedAgents = demotedAgentFiles(claudeDir, installedTracks(projectDir));
6624
- if (logAtStart !== null && wants("rules"))
6625
- refreshNeutralHelpers(projectDir, templatesDir, report);
6626
7329
  const external = wants("external") ? refreshExternalCli(projectDir, harnessRoot, legacyDroppedKeys(logAtStart), harnessMcp) : {
6627
7330
  externalUpdated: 0,
6628
7331
  externalBackedUp: [],
@@ -6638,18 +7341,31 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}, only) {
6638
7341
  restoredFiles: [],
6639
7342
  legacyRestored: [],
6640
7343
  anchorRetired: null,
7344
+ anchorDisplaced: [],
6641
7345
  agentsMd: null,
7346
+ agentsMdLegacyDropped: 0,
7347
+ agentsMdLegacyEdited: [],
6642
7348
  excludedKeys: [],
6643
7349
  sharedLeft: []
6644
7350
  };
7351
+ const outOfTrackRestored = [];
6645
7352
  if (wants("external")) {
6646
- const outOfTrack = reclaimOutOfTrackRecorded(projectDir, outside);
7353
+ const { reclaim: outOfTrack, displaced } = reclaimOutOfTrackRecorded(projectDir, outside);
6647
7354
  if (outOfTrack.removed.length + outOfTrack.backedUp.length + outOfTrack.kept.length > 0)
6648
7355
  report.outOfTrack = outOfTrack;
7356
+ outOfTrackRestored.push(...displaced);
6649
7357
  }
7358
+ const displacedRestored = [...prunedRestored, ...external.anchorDisplaced, ...outOfTrackRestored];
7359
+ if (displacedRestored.length > 0) report.displacedRestored = displacedRestored;
6650
7360
  report.externalUpdated = external.externalUpdated;
6651
7361
  if (external.anchorRetired !== null) report.anchorRetired = external.anchorRetired;
6652
7362
  if (external.agentsMd !== null) report.agentsMd = external.agentsMd;
7363
+ if (external.agentsMdLegacyDropped > 0) {
7364
+ report.agentsMdLegacyDropped = external.agentsMdLegacyDropped;
7365
+ }
7366
+ if (external.agentsMdLegacyEdited.length > 0) {
7367
+ report.agentsMdLegacyEdited = external.agentsMdLegacyEdited;
7368
+ }
6653
7369
  if (external.sharedLeft.length > 0) report.sharedLeft = external.sharedLeft;
6654
7370
  report.restoredKeys = external.restoredKeys;
6655
7371
  report.restoredFiles = external.restoredFiles;
@@ -6681,12 +7397,7 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}, only) {
6681
7397
  report.restoredWithout = restoredWithoutArgs(report.restored, installedTracks(projectDir));
6682
7398
  const saidBySlotRow = new Set(report.skillsSkippedLinks.map((id) => `.claude/skills/${id}`));
6683
7399
  const merged = [];
6684
- for (const f of [
6685
- ...skillSync.foreignOwned,
6686
- ...fresh.foreignOwned,
6687
- ...newSkills.foreignOwned,
6688
- ...external.externalForeignOwned
6689
- ]) {
7400
+ for (const f of [...harness.foreignOwned, ...external.externalForeignOwned]) {
6690
7401
  if (!saidBySlotRow.has(f) && !merged.includes(f)) merged.push(f);
6691
7402
  }
6692
7403
  report.foreignOwned = merged;
@@ -6716,7 +7427,12 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}, only) {
6716
7427
  ).filter((id) => !skillRefresh.notInCatalog.includes(id))
6717
7428
  ].filter((id) => !saidAsRemoved.has(id));
6718
7429
  report.externalSkillsUnknown = skillRefresh.unknown;
6719
- const outsideLinks = mergeOutside(outside.list(), sharedWrites.outside, external.externalOutside);
7430
+ const outsideLinks = mergeOutside(
7431
+ harness.outside,
7432
+ outside.list(),
7433
+ sharedWrites.outside,
7434
+ external.externalOutside
7435
+ );
6720
7436
  if (outsideLinks.length > 0) report.outsideLinks = outsideLinks;
6721
7437
  const displacedAll = [...claudeDisplaced, ...external.displaced];
6722
7438
  const displacedMoved = displacedAll.filter((f) => f.notes.length > 0).map((f) => f.path);
@@ -6788,18 +7504,21 @@ function reclaimOutOfTrackRecorded(projectDir, outside) {
6788
7504
  excluded: excludedIds(log),
6789
7505
  outside
6790
7506
  });
7507
+ const left = /* @__PURE__ */ new Set([...result.removed, ...result.backedUp.map((b2) => b2.path)]);
7508
+ const displaced = log === null || left.size === 0 ? [] : restoreDisplaced(log, projectDir, { has: (p2) => left.has(p2), write: true });
6791
7509
  if (log !== null && result.forget.length > 0) {
6792
7510
  const forget = new Set(result.forget);
6793
7511
  const next = { ...log };
6794
7512
  const kept = (log.externalFiles ?? []).filter((f) => !forget.has(f.path));
6795
7513
  if (kept.length > 0) next.externalFiles = kept;
6796
7514
  else delete next.externalFiles;
7515
+ if (displaced.some((d2) => d2.forget)) next.rootFiles = withoutRestored(log.rootFiles, displaced);
6797
7516
  try {
6798
7517
  writeInstallLog(projectDir, next);
6799
7518
  } catch {
6800
7519
  }
6801
7520
  }
6802
- return result;
7521
+ return { reclaim: result, displaced };
6803
7522
  }
6804
7523
  function checksumOnRecord(log) {
6805
7524
  if (log === null) return () => false;
@@ -6807,7 +7526,9 @@ function checksumOnRecord(log) {
6807
7526
  ...(log.policyFiles ?? []).map((f) => `.claude/${f.path}`),
6808
7527
  ...(log.skillFiles ?? []).map((f) => `.claude/skills/${f.path}`),
6809
7528
  ...(log.externalFiles ?? []).map((f) => f.path),
6810
- ...log.assets.flatMap((a) => a.files ?? []).map((f) => f.path)
7529
+ ...log.assets.flatMap((a) => a.files ?? []).map((f) => f.path),
7530
+ // #800 — 함께 쓰는 파일은 몫마다 sha 가 있다 — 그 백업은 설치자 편집이 확실하다
7531
+ ...(log.portions ?? []).map((p2) => p2.path)
6811
7532
  ]);
6812
7533
  const anchor = log.templates.rootClaudeMd;
6813
7534
  if (anchor !== void 0) paths.add(anchor.path);
@@ -6820,7 +7541,7 @@ function retiredFeaturesPart(projectDir, f) {
6820
7541
  }
6821
7542
  function legacyHookOnDisk(projectDir, rel) {
6822
7543
  try {
6823
- return hasLegacyHarnessHook(readFileSync21(join29(projectDir, rel), "utf8"));
7544
+ return hasLegacyHarnessHook(readFileSync21(join30(projectDir, rel), "utf8"));
6824
7545
  } catch {
6825
7546
  return false;
6826
7547
  }
@@ -6846,7 +7567,7 @@ function writeUpdateSharedFiles(args) {
6846
7567
  excluded: adapterExcluded(harnessRoot, excluded)
6847
7568
  });
6848
7569
  const tracks = installedTracks(projectDir);
6849
- const settingsSource = join29(templatesDir, "settings.json");
7570
+ const settingsSource = join30(templatesDir, "settings.json");
6850
7571
  if (args.claudeManaged && wants("hooks") && existsSync21(settingsSource)) {
6851
7572
  const spec = buildAssetSpec({ tracks, options: DEFAULT_OPTIONS });
6852
7573
  const manifest = buildManifest(spec);
@@ -6857,7 +7578,7 @@ function writeUpdateSharedFiles(args) {
6857
7578
  log,
6858
7579
  (script) => {
6859
7580
  const target = `.claude/hooks/${script}`;
6860
- return manifest.some((e2) => e2.target === target && e2.applies(spec)) && !isBaselineExcluded(target, excluded) && existsSync21(join29(projectDir, target));
7581
+ return manifest.some((e2) => e2.target === target && e2.applies(spec)) && !isBaselineExcluded(target, excluded) && existsSync21(join30(projectDir, target));
6861
7582
  }
6862
7583
  // 기록이 claude 를 깔린 CLI 로 말한다 = install 이 이 파일을 썼다 — 없으면 만든다(훅 배선이 여기 산다)
6863
7584
  );
@@ -6921,102 +7642,255 @@ function catalogRemovedOnRecord(log) {
6921
7642
  }
6922
7643
  function staleSkillDirs(projectDir, claudeDir, clis) {
6923
7644
  const stale = (id) => RENAMED_SKILL_IDS[id] !== void 0 || RETIRED_SKILL_IDS.includes(id);
6924
- const dirs = [join29(claudeDir, "skills")];
6925
- if (clis.some((cli) => cli !== "claude")) dirs.push(join29(projectDir, ".agents", "skills"));
7645
+ const dirs = [join30(claudeDir, "skills")];
7646
+ if (clis.some((cli) => cli !== "claude")) dirs.push(join30(projectDir, ".agents", "skills"));
6926
7647
  const ids = /* @__PURE__ */ new Set();
6927
7648
  for (const dir of dirs) {
6928
7649
  if (!existsSync21(dir)) continue;
6929
- for (const e2 of readdirSync3(dir, { withFileTypes: true })) {
7650
+ for (const e2 of readdirSync4(dir, { withFileTypes: true })) {
6930
7651
  if (e2.isDirectory() && stale(e2.name)) ids.add(e2.name);
6931
7652
  }
6932
7653
  }
6933
7654
  return [...ids];
6934
7655
  }
6935
7656
  function staleAgentFiles(claudeDir) {
6936
- const dir = join29(claudeDir, "agents");
7657
+ const dir = join30(claudeDir, "agents");
6937
7658
  if (!existsSync21(dir)) return [];
6938
- return readdirSync3(dir, { withFileTypes: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".md")).map((e2) => basename3(e2.name, ".md")).filter((id) => RETIRED_AGENT_IDS.includes(id));
7659
+ return readdirSync4(dir, { withFileTypes: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".md")).map((e2) => basename3(e2.name, ".md")).filter((id) => RETIRED_AGENT_IDS.includes(id));
6939
7660
  }
6940
7661
  function demotedAgentFiles(claudeDir, tracks) {
6941
7662
  if (tracks.length === 0) return [];
6942
- const dir = join29(claudeDir, "agents");
7663
+ const dir = join30(claudeDir, "agents");
6943
7664
  if (!existsSync21(dir)) return [];
6944
7665
  const demoted = TRACK_AGENTS.filter(([, pattern]) => !anyTrack(tracks, pattern)).map(
6945
7666
  ([id]) => id
6946
7667
  );
6947
- return readdirSync3(dir, { withFileTypes: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".md")).map((e2) => basename3(e2.name, ".md")).filter((id) => demoted.includes(id));
7668
+ return readdirSync4(dir, { withFileTypes: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".md")).map((e2) => basename3(e2.name, ".md")).filter((id) => demoted.includes(id));
6948
7669
  }
6949
- function installNewAssets(projectDir, templatesDir, wants, outside) {
6950
- const installed = [];
6951
- const restored = [];
6952
- const foreignOwned = [];
6953
- const log = readInstallLog(projectDir);
6954
- const claudeSelected = installedClis(log).includes("claude");
6955
- const baselineExcluded = excludedIds(log);
6956
- const priorBaseline = policyBaseline(projectDir);
6957
- const wiredBefore = new Set(
6958
- (log?.portions ?? []).filter((p2) => p2.path === SETTINGS_TARGET).flatMap((p2) => p2.key.split("#")[1]?.replace(/\{\}$/, "") ?? [])
6959
- );
6960
- for (const entry of trackOnlyFileAssets(installedTracks(projectDir))) {
6961
- const group = entry.target.startsWith(".claude/hooks/") || entry.target === ".claude/settings.json" ? "hooks" : "rules";
6962
- if (!wants(group)) continue;
6963
- if (entry.target === HARNESS_ANCHOR_FILE) continue;
6964
- if (isBaselineExcluded(entry.target, baselineExcluded)) continue;
6965
- const target = join29(projectDir, entry.target);
6966
- const foreign = foreignOwnedTarget(projectDir, entry.target);
6967
- if (foreign !== null) {
6968
- if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);
7670
+ function updateHarnessTargets(projectDir, templatesDir, log, wants, report, foreignOwned) {
7671
+ const files = [];
7672
+ const claude = installedClis(log).includes("claude");
7673
+ const excluded = excludedIds(log);
7674
+ for (const e2 of trackOnlyFileAssets(installedTracks(projectDir))) {
7675
+ if (e2.target === HARNESS_ANCHOR_FILE || e2.target === SETTINGS_TARGET) continue;
7676
+ if (!wants(e2.target.startsWith(".claude/hooks/") ? "hooks" : "rules")) continue;
7677
+ if (e2.target.startsWith(".claude/") && !claude) continue;
7678
+ if (isBaselineExcluded(e2.target, excluded)) continue;
7679
+ const source = join30(templatesDir, e2.source);
7680
+ if (!existsSync21(source)) continue;
7681
+ const policy = POLICY_DIRS.find((d2) => e2.target.startsWith(`.claude/${d2.dir}/`));
7682
+ files.push({
7683
+ path: e2.target,
7684
+ source,
7685
+ unit: e2.target,
7686
+ unitExisted: existsSync21(join30(projectDir, e2.target)),
7687
+ label: policy === void 0 ? e2.target : `.claude/${policy.dir}`
7688
+ });
7689
+ }
7690
+ const refreshedSkills = [];
7691
+ const skillsSource = join30(templatesDir, "skills");
7692
+ if (!claude || !(wants("skills") || wants("new-skills")) || !existsSync21(skillsSource)) {
7693
+ return { files, refreshedSkills };
7694
+ }
7695
+ const ids = recordedSkillIds(projectDir, log);
7696
+ const creatable = claudeSkillIds(projectDir, log);
7697
+ const excludedSkill = excludedSkillOf(log);
7698
+ for (const d2 of readdirSync4(skillsSource, { withFileTypes: true })) {
7699
+ if (!d2.isDirectory()) continue;
7700
+ const unit = `.claude/skills/${d2.name}`;
7701
+ if (occupiedByNonDirectory(join30(projectDir, unit))) {
7702
+ report.skillsSkippedLinks.push(d2.name);
6969
7703
  continue;
6970
7704
  }
6971
- if (existsSync21(target)) continue;
6972
- const source = join29(templatesDir, entry.source);
6973
- if (!existsSync21(source)) continue;
6974
- if (outside.skip(target)) continue;
6975
- if (entry.target.startsWith(".claude/")) {
6976
- if (!claudeSelected) continue;
6977
- if (entry.target === SETTINGS_TARGET) continue;
7705
+ if (!ids.has(d2.name) || excludedSkill(d2.name)) continue;
7706
+ const existed = existsSync21(join30(projectDir, unit));
7707
+ if (existed ? !wants("skills") : !wants("new-skills") || !creatable.has(d2.name)) continue;
7708
+ for (const rel of listFilesRecursive(join30(skillsSource, d2.name))) {
7709
+ const foreign = foreignOwnedTarget(projectDir, `${unit}/${rel}`);
7710
+ if (foreign !== null) {
7711
+ if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);
7712
+ continue;
7713
+ }
7714
+ const source = join30(skillsSource, d2.name, rel);
7715
+ files.push({
7716
+ path: `${unit}/${rel}`,
7717
+ source,
7718
+ unit,
7719
+ unitExisted: existed,
7720
+ label: ".claude/skills"
7721
+ });
6978
7722
  }
6979
- mkdirSync6(dirname6(target), { recursive: true });
6980
- copyFileSync(source, target);
6981
- const recordedAs = entry.target.startsWith(".claude/") ? entry.target.slice(8) : entry.target;
6982
- const wired = entry.target.startsWith(".claude/hooks/") && wiredBefore.has(entry.target.slice(".claude/hooks/".length));
6983
- if (priorBaseline.has(recordedAs) || wired) restored.push(entry.target);
6984
- else installed.push(entry.target);
7723
+ if (existed) refreshedSkills.push(d2.name);
6985
7724
  }
6986
- return { installed, restored, foreignOwned };
7725
+ return { files, refreshedSkills };
6987
7726
  }
6988
- var BACKUP_SUFFIX = /\.backup-\d{8}T\d{6}(-\d+)?$/;
6989
- var BACKUP_SCAN_DIRS = [
6990
- ...new Set(
6991
- Object.values(CLI_OWNERSHIP).flat().map((owned) => owned.path).filter((path) => path.includes("/")).map((path) => path.slice(0, path.indexOf("/")))
6992
- )
6993
- ];
6994
- function collectRunBackups(projectDir, startedAt, exclude) {
6995
- const out = [];
6996
- const consider = (rel) => {
6997
- if (!BACKUP_SUFFIX.test(rel)) return;
7727
+ function updateHarnessFiles(args) {
7728
+ const { projectDir, templatesDir, log, wants, outside, report } = args;
7729
+ const now = /* @__PURE__ */ new Date();
7730
+ const claude = installedClis(log).includes("claude");
7731
+ const claims = retiredClaims(log, projectDir, log.policyFiles ?? []);
7732
+ const isTarget = recordedTrackTargets(log);
7733
+ const recordOf = (path) => recorded(log, path, { isTarget });
7734
+ const writer = createInstallWriter({
7735
+ projectDir,
7736
+ previousLog: log,
7737
+ excluded: excludedIds(log),
7738
+ now,
7739
+ run: "update"
7740
+ });
7741
+ const foreignOwned = [];
7742
+ const targets = updateHarnessTargets(projectDir, templatesDir, log, wants, report, foreignOwned);
7743
+ for (const t of targets.files) writer.harness(t.path, { source: t.source }, { isTarget });
7744
+ const prunedPolicy = /* @__PURE__ */ new Set();
7745
+ for (const { dir, ext } of POLICY_DIRS) {
7746
+ if (!claude || !wants(dir === "hooks" ? "hooks" : "rules")) continue;
7747
+ const label = `.claude/${dir}`;
7748
+ report.updated[label] = 0;
7749
+ const ctx = { prefix: dir, outside, recordOf };
7750
+ report.pruned[label] = pruneOrphans(
7751
+ join30(projectDir, label),
7752
+ join30(templatesDir, dir),
7753
+ ext,
7754
+ ctx,
7755
+ now
7756
+ );
7757
+ for (const f of report.pruned[label]) prunedPolicy.add(`${dir}/${f}`);
7758
+ }
7759
+ if (claude && wants("skills")) report.updated[".claude/skills"] = 0;
7760
+ const skillPrune = pruneSkillExtras(
7761
+ projectDir,
7762
+ templatesDir,
7763
+ targets.refreshedSkills,
7764
+ outside,
7765
+ recordOf,
7766
+ now
7767
+ );
7768
+ report.skillsPruned = skillPrune.pruned;
7769
+ report.skillsBackedUp.push(...skillPrune.backedUp);
7770
+ const restored = prunedPolicy.size === 0 ? [] : restoreDisplaced(log, projectDir, {
7771
+ has: (p2) => p2.startsWith(".claude/") && prunedPolicy.has(p2.slice(".claude/".length)),
7772
+ write: true
7773
+ });
7774
+ const ledger = writer.ledger();
7775
+ recordHarnessWrites(projectDir, ledger, {
7776
+ claims,
7777
+ prunedPolicy,
7778
+ prunedSkill: new Set(skillPrune.pruned),
7779
+ restored,
7780
+ walkedSkills: wants("skills")
7781
+ });
7782
+ const byPath = new Map(targets.files.map((t) => [t.path, t]));
7783
+ const created = /* @__PURE__ */ new Map();
7784
+ for (const w3 of ledger.writes) {
7785
+ const t = byPath.get(w3.path);
7786
+ if (t === void 0) continue;
7787
+ if (!t.unitExisted) {
7788
+ if (w3.verdict === "create")
7789
+ created.set(t.unit, created.get(t.unit) === true || w3.line === LINES.restored);
7790
+ continue;
7791
+ }
7792
+ if (w3.verdict !== "leave") report.updated[t.label] = (report.updated[t.label] ?? 0) + 1;
7793
+ if (w3.verdict !== "backup+overwrite" || w3.record !== "sha") continue;
7794
+ if (w3.path.startsWith(".claude/skills/"))
7795
+ report.skillsBackedUp.push(w3.path.slice(".claude/skills/".length));
7796
+ else if (w3.path.startsWith(".claude/"))
7797
+ report.policyBackedUp.push(w3.path.slice(".claude/".length));
7798
+ }
7799
+ for (const [unit, wasRestored] of created)
7800
+ (wasRestored ? report.restored : report.installedNew).push(unit);
7801
+ return {
7802
+ displaced: ledger.rootFiles.filter((f) => f.change === "displaced"),
7803
+ restored,
7804
+ foreignOwned,
7805
+ outside: ledger.outside
7806
+ };
7807
+ }
7808
+ function pruneSkillExtras(projectDir, templatesDir, ids, outside, recordOf, now) {
7809
+ const pruned = [];
7810
+ const backedUp = [];
7811
+ for (const id of ids) {
7812
+ const targetSkill = join30(projectDir, ".claude/skills", id);
7813
+ const bundled = new Set(listFilesRecursive(join30(templatesDir, "skills", id)));
7814
+ for (const rel of listFilesRecursive(targetSkill)) {
7815
+ if (bundled.has(rel) || basename3(rel).includes(".backup-")) continue;
7816
+ if (foreignOwnedTarget(projectDir, `.claude/skills/${id}/${rel}`) !== null) continue;
7817
+ const targetFile = join30(targetSkill, rel);
7818
+ if (outside.skip(targetFile)) continue;
7819
+ const rec = recordOf(`.claude/skills/${id}/${rel}`);
7820
+ if (rec.state !== "sha" || hashContent(readFileSync21(targetFile, "utf8")) !== rec.sha256) {
7821
+ backupFile(targetFile, now);
7822
+ backedUp.push(`${id}/${rel}`);
7823
+ }
7824
+ unlinkSync2(targetFile);
7825
+ pruned.push(`${id}/${rel}`);
7826
+ for (let d2 = dirname7(targetFile); d2 !== targetSkill && readdirSync4(d2).length === 0; d2 = dirname7(d2))
7827
+ rmdirSync2(d2);
7828
+ }
7829
+ }
7830
+ return { pruned, backedUp };
7831
+ }
7832
+ function recordHarnessWrites(projectDir, ledger, x) {
7833
+ const log = readInstallLog(projectDir);
7834
+ if (!log) return;
7835
+ const skillLedger = log.skillFiles === void 0 && !x.walkedSkills ? [] : ledger.skillFiles;
7836
+ const policy = mergeExternalFiles(log.policyFiles, ledger.policyFiles).filter(
7837
+ (f) => !x.prunedPolicy.has(f.path)
7838
+ );
7839
+ const have = new Set(policy.map((f) => f.path));
7840
+ const next = {
7841
+ ...log,
7842
+ policyFiles: [
7843
+ ...policy,
7844
+ ...x.claims.filter((c3) => !x.prunedPolicy.has(c3.path) && !have.has(c3.path))
7845
+ ],
7846
+ skillFiles: mergeExternalFiles(log.skillFiles, skillLedger).filter(
7847
+ (f) => !x.prunedSkill.has(f.path)
7848
+ ),
7849
+ externalFiles: mergeExternalFiles(log.externalFiles, ledger.externalFiles),
7850
+ rootFiles: withoutRestored(
7851
+ mergeRootFiles(log.rootFiles, withoutLostBackups(log.rootFiles, ledger.rootFiles)),
7852
+ x.restored
7853
+ )
7854
+ };
7855
+ for (const key of ["policyFiles", "skillFiles", "externalFiles", "rootFiles"])
7856
+ if (next[key]?.length === 0) delete next[key];
7857
+ try {
7858
+ writeInstallLog(projectDir, next);
7859
+ } catch {
7860
+ }
7861
+ }
7862
+ var BACKUP_SUFFIX = /\.backup-\d{8}T\d{6}(-\d+)?$/;
7863
+ var BACKUP_SCAN_DIRS = [
7864
+ ...new Set(
7865
+ Object.values(CLI_OWNERSHIP).flat().map((owned) => owned.path).filter((path) => path.includes("/")).map((path) => path.slice(0, path.indexOf("/")))
7866
+ )
7867
+ ];
7868
+ function collectRunBackups(projectDir, startedAt, exclude) {
7869
+ const out = [];
7870
+ const consider = (rel) => {
7871
+ if (!BACKUP_SUFFIX.test(rel)) return;
6998
7872
  if (exclude?.has(rel)) return;
6999
- const abs = join29(projectDir, rel);
7873
+ const abs = join30(projectDir, rel);
7000
7874
  if (statSync4(abs).mtimeMs < startedAt - 1e3) return;
7001
7875
  out.push({ path: rel.replace(BACKUP_SUFFIX, ""), backup: rel });
7002
7876
  };
7003
- for (const e2 of readdirSync3(projectDir, { withFileTypes: true }))
7877
+ for (const e2 of readdirSync4(projectDir, { withFileTypes: true }))
7004
7878
  if (e2.isFile()) consider(e2.name);
7005
7879
  for (const dir of BACKUP_SCAN_DIRS) {
7006
- for (const rel of listFilesRecursive(join29(projectDir, dir))) consider(`${dir}/${rel}`);
7880
+ for (const rel of listFilesRecursive(join30(projectDir, dir))) consider(`${dir}/${rel}`);
7007
7881
  }
7008
7882
  return out.sort((a, b2) => a.path.localeCompare(b2.path));
7009
7883
  }
7010
7884
  function writeBackupList(projectDir, backups, noChecksum = /* @__PURE__ */ new Set()) {
7011
- const dir = join29(projectDir, ".uzys-agent-harness");
7012
- const file = join29(dir, "update-backups.json");
7885
+ const dir = join30(projectDir, ".uzys-agent-harness");
7886
+ const file = join30(dir, "update-backups.json");
7013
7887
  try {
7014
7888
  if (backups.length === 0) {
7015
7889
  if (existsSync21(file)) unlinkSync2(file);
7016
7890
  return;
7017
7891
  }
7018
7892
  if (!existsSync21(dir)) return;
7019
- writeFileSync8(
7893
+ writeFileSync7(
7020
7894
  file,
7021
7895
  `${JSON.stringify(
7022
7896
  {
@@ -7032,51 +7906,6 @@ function writeBackupList(projectDir, backups, noChecksum = /* @__PURE__ */ new S
7032
7906
  } catch {
7033
7907
  }
7034
7908
  }
7035
- function installNewSkillDirs(projectDir, templatesDir, tracks, outside) {
7036
- const installed = [];
7037
- const restored = [];
7038
- const foreignOwned = [];
7039
- const log = readInstallLog(projectDir);
7040
- const priorIds = new Set((log?.skillFiles ?? []).map((f) => f.path.split("/")[0] ?? ""));
7041
- const excluded = excludedIds(log);
7042
- const skillExcluded = excluded;
7043
- if (log !== null && !installedClis(log).includes("claude"))
7044
- return { installed, restored, foreignOwned };
7045
- const spec = buildAssetSpec({ tracks, options: DEFAULT_OPTIONS });
7046
- for (const entry of buildManifest(spec)) {
7047
- if (entry.type !== "dir" || !entry.target.startsWith(".claude/skills/")) continue;
7048
- if (!entry.applies(spec) || isBaselineExcluded(entry.target, excluded)) continue;
7049
- if (skillExcluded.has(entry.target.slice(".claude/skills/".length))) continue;
7050
- const target = join29(projectDir, entry.target);
7051
- const foreign = foreignOwnedTarget(projectDir, entry.target);
7052
- if (foreign !== null) {
7053
- if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);
7054
- continue;
7055
- }
7056
- if (existsSync21(target)) continue;
7057
- const source = join29(templatesDir, entry.source);
7058
- if (!existsSync21(source)) continue;
7059
- if (outside.skip(target)) continue;
7060
- copyDir(source, target, (rel) => foreignOwnedTarget(projectDir, `${entry.target}/${rel}`));
7061
- if (priorIds.has(entry.target.slice(".claude/skills/".length))) restored.push(entry.target);
7062
- else installed.push(entry.target);
7063
- }
7064
- return { installed, restored, foreignOwned };
7065
- }
7066
- function recordNewSkillBaseline(projectDir, templatesDir, targets) {
7067
- const log = readInstallLog(projectDir);
7068
- if (!log) return;
7069
- if (log.skillFiles === void 0) return;
7070
- const ids = new Set(targets.map((t) => t.slice(".claude/skills/".length)));
7071
- const added = collectSkillHashes(projectDir, templatesDir).filter(
7072
- (f) => ids.has(f.path.split("/")[0] ?? "")
7073
- );
7074
- const kept = (log.skillFiles ?? []).filter((f) => !ids.has(f.path.split("/")[0] ?? ""));
7075
- try {
7076
- writeInstallLog(projectDir, { ...log, skillFiles: [...kept, ...added] });
7077
- } catch {
7078
- }
7079
- }
7080
7909
  function trackOnlyFileAssets(tracks) {
7081
7910
  const base = { tracks, selectedInternalSkills: [] };
7082
7911
  const optedOut = { ...base, withTauri: false };
@@ -7086,12 +7915,12 @@ function trackOnlyFileAssets(tracks) {
7086
7915
  );
7087
7916
  }
7088
7917
  function syncHarnessAnchor(projectDir, templatesDir, report, outside, now = /* @__PURE__ */ new Date()) {
7089
- if (existsSync21(join29(projectDir, LEGACY_ANCHOR_FILE2))) {
7918
+ if (existsSync21(join30(projectDir, LEGACY_ANCHOR_FILE2))) {
7090
7919
  report.legacyAnchor = LEGACY_ANCHOR_FILE2;
7091
7920
  }
7092
- const templateMd = join29(templatesDir, "CLAUDE.md");
7921
+ const templateMd = join30(templatesDir, "CLAUDE.md");
7093
7922
  if (!existsSync21(templateMd)) return;
7094
- const anchor = join29(projectDir, HARNESS_ANCHOR_FILE);
7923
+ const anchor = join30(projectDir, HARNESS_ANCHOR_FILE);
7095
7924
  if (outside.skip(anchor)) return;
7096
7925
  const existed = existsSync21(anchor);
7097
7926
  if (existed) {
@@ -7117,21 +7946,21 @@ function syncHarnessAnchor(projectDir, templatesDir, report, outside, now = /* @
7117
7946
  recordAnchorBaseline(projectDir, anchor);
7118
7947
  }
7119
7948
  function upsertRootImport(projectDir, outside) {
7120
- const target = join29(projectDir, "CLAUDE.md");
7949
+ const target = join30(projectDir, "CLAUDE.md");
7121
7950
  if (outside.skip(target)) return false;
7122
7951
  const existing = existsSync21(target) ? readFileSync21(target, "utf8") : null;
7123
7952
  const next = upsertHarnessImport(existing, {
7124
7953
  projectName: basename3(projectDir),
7125
7954
  tracks: installedTracks(projectDir),
7126
- // ADR-085 — update 는 선택 목록을 인자로 받지 않는다. `syncSkills` 와 같은 판정을 쓴다:
7955
+ // ADR-085 — update 는 선택 목록을 인자로 받지 않는다. 앞 단계(하네스 파일 쓰기)가 지나간 디스크로 판정한다:
7127
7956
  // `.claude/skills/<id>` 가 있으면 그 스킬은 이 설치가 고른 것이다.
7128
7957
  continuousSkills: CONTINUOUS_SKILLS.map((s) => s.id).filter(
7129
- (id) => existsSync21(join29(projectDir, ".claude/skills", id))
7958
+ (id) => existsSync21(join30(projectDir, ".claude/skills", id))
7130
7959
  )
7131
7960
  });
7132
7961
  if (next === existing) return false;
7133
7962
  if (existing !== null) backupIfLossyUtf8(target);
7134
- writeFileSync8(target, next);
7963
+ writeFileSync7(target, next);
7135
7964
  return true;
7136
7965
  }
7137
7966
  function installedTracks(projectDir) {
@@ -7161,7 +7990,7 @@ function excludedBaselineOnDisk(projectDir, log) {
7161
7990
  if (excluded.size === 0) return [];
7162
7991
  const spec = buildAssetSpec({ tracks: installedTracks(projectDir), options: DEFAULT_OPTIONS });
7163
7992
  return buildManifest(spec).filter(
7164
- (e2) => e2.applies(spec) && isBaselineExcluded(e2.target, excluded) && existsSync21(join29(projectDir, e2.target))
7993
+ (e2) => e2.applies(spec) && isBaselineExcluded(e2.target, excluded) && existsSync21(join30(projectDir, e2.target))
7165
7994
  ).map((e2) => e2.target);
7166
7995
  }
7167
7996
  function excludedSkillOf(log) {
@@ -7189,23 +8018,27 @@ function recordedRenderInputs(projectDir, log) {
7189
8018
  function recordedBundledSkills(projectDir, log) {
7190
8019
  if (log === null) {
7191
8020
  return INTERNAL_BUNDLED_SKILL_IDS.filter(
7192
- (id) => existsSync21(join29(projectDir, ".claude/skills", id)) || existsSync21(join29(projectDir, ".agents/skills", id))
8021
+ (id) => existsSync21(join30(projectDir, ".claude/skills", id)) || existsSync21(join30(projectDir, ".agents/skills", id))
7193
8022
  );
7194
8023
  }
7195
8024
  const ids = recordedSkillIds(projectDir, log);
7196
8025
  return INTERNAL_BUNDLED_SKILL_IDS.filter((id) => ids.has(id));
7197
8026
  }
7198
8027
  function recordedSkillIds(projectDir, log) {
8028
+ const ids = claudeSkillIds(projectDir, log);
8029
+ for (const f of log.externalFiles ?? []) {
8030
+ const id = SHARED_SKILL_ID.exec(f.path)?.[1];
8031
+ if (id !== void 0) ids.add(id);
8032
+ }
8033
+ return ids;
8034
+ }
8035
+ function claudeSkillIds(projectDir, log) {
7199
8036
  const isTarget = recordedTrackTargets(log);
7200
8037
  const ids = new Set(bundledSkillTargets(projectDir, log));
7201
8038
  for (const f of log.skillFiles ?? []) {
7202
8039
  const id = f.path.split("/")[0] ?? "";
7203
8040
  if (recorded(log, `.claude/skills/${f.path}`, { isTarget }).state !== "none") ids.add(id);
7204
8041
  }
7205
- for (const f of log.externalFiles ?? []) {
7206
- const id = SHARED_SKILL_ID.exec(f.path)?.[1];
7207
- if (id !== void 0) ids.add(id);
7208
- }
7209
8042
  return ids;
7210
8043
  }
7211
8044
  var SHARED_SKILL_ID = /^\.agents\/skills\/([^/]+)\//;
@@ -7236,9 +8069,9 @@ var SHARED_SKILLS_DIR = ".agents/skills";
7236
8069
  var SHARED_SKILL_FILE = /^\.agents\/skills\/([^/]+)\//;
7237
8070
  var SHARED_SKILL_MD = /^\.agents\/skills\/([^/]+)\/SKILL\.md$/;
7238
8071
  function sharedSkillIdsOnDisk(projectDir) {
7239
- const root = join29(projectDir, SHARED_SKILLS_DIR);
8072
+ const root = join30(projectDir, SHARED_SKILLS_DIR);
7240
8073
  if (!existsSync21(root)) return /* @__PURE__ */ new Set();
7241
- return new Set(readdirSync3(root).filter((id) => existsSync21(join29(root, id, "SKILL.md"))));
8074
+ return new Set(readdirSync4(root).filter((id) => existsSync21(join30(root, id, "SKILL.md"))));
7242
8075
  }
7243
8076
  var CREATED_SHARED_HOW = {
7244
8077
  "opencode.json": "created from template (harness part + template defaults)",
@@ -7265,14 +8098,14 @@ function refreshExternalCli(projectDir, harnessRoot, droppedKeys, harnessMcp) {
7265
8098
  const log = readInstallLog(projectDir);
7266
8099
  const presentBefore = sharedSkillIdsOnDisk(projectDir);
7267
8100
  const rulesBefore = /* @__PURE__ */ new Set();
7268
- const rulesDir = join29(projectDir, ".agents", "rules");
8101
+ const rulesDir = join30(projectDir, ".agents", "rules");
7269
8102
  if (existsSync21(rulesDir)) {
7270
8103
  try {
7271
- for (const f of readdirSync3(rulesDir)) rulesBefore.add(f);
8104
+ for (const f of readdirSync4(rulesDir)) rulesBefore.add(f);
7272
8105
  } catch {
7273
8106
  }
7274
8107
  }
7275
- const agentsMdBefore = existsSync21(join29(projectDir, "AGENTS.md"));
8108
+ const agentsMdBefore = existsSync21(join30(projectDir, "AGENTS.md"));
7276
8109
  const result = runCliTransforms({
7277
8110
  harnessRoot,
7278
8111
  projectDir,
@@ -7290,9 +8123,11 @@ function refreshExternalCli(projectDir, harnessRoot, droppedKeys, harnessMcp) {
7290
8123
  // 기록 없음: CLI 경로로는 도달 불가 — 진입 판정(#699)이 먼저 거절. 엔진 단위 방어(빈 집합).
7291
8124
  recordedClis: log === null ? [] : installedClis(log),
7292
8125
  // #761 B-1 — 외부 기록이 아예 없던 옛 판의 산출물은 첫 접촉이 아니라 체크섬 없는 하네스 파일이다(install 과 같은 판정).
7293
- // 앞 단계(`refreshNeutralHelpers`)가 헬퍼만 든 `externalFiles` 를 적어도 같은 답이다 — 헬퍼는 외부 자리로 세지 않는다(B-2)
8126
+ // 앞 단계(하네스 파일 쓰기)가 헬퍼만 든 `externalFiles` 를 적어도 같은 답이다 — 헬퍼는 외부 자리로 세지 않는다(B-2)
7294
8127
  owned: recordedOwner(log)
7295
8128
  });
8129
+ const anchorPath = result.anchorRetired?.action === "removed" ? result.anchorRetired.path : null;
8130
+ const anchorDisplaced = log === null || anchorPath === null ? [] : restoreDisplaced(log, projectDir, { has: (p2) => p2 === anchorPath, write: true });
7296
8131
  if (log) {
7297
8132
  const retiredPath = result.anchorRetired?.forget === true ? result.anchorRetired.path : null;
7298
8133
  const merged = mergeExternalFiles(log.externalFiles, result.externalFiles).filter(
@@ -7311,6 +8146,8 @@ function refreshExternalCli(projectDir, harnessRoot, droppedKeys, harnessMcp) {
7311
8146
  const created = result.sharedFiles.filter((f) => f.createdAsRoot === true).map((f) => ({ path: f.path, change: "created", notes: ["\uD558\uB124\uC2A4 \uBAAB\uB9CC(\uBE14\uB85D)"] }));
7312
8147
  const roots = [...created, ...withoutLostBackups(log.rootFiles, result.displaced ?? [])];
7313
8148
  if (roots.length > 0) next.rootFiles = mergeRootFiles(log.rootFiles, roots);
8149
+ if (anchorDisplaced.some((d2) => d2.forget))
8150
+ next.rootFiles = withoutRestored(next.rootFiles, anchorDisplaced);
7314
8151
  try {
7315
8152
  writeInstallLog(projectDir, next);
7316
8153
  } catch {
@@ -7390,7 +8227,10 @@ function refreshExternalCli(projectDir, harnessRoot, droppedKeys, harnessMcp) {
7390
8227
  )
7391
8228
  ],
7392
8229
  anchorRetired: result.anchorRetired,
8230
+ anchorDisplaced,
7393
8231
  agentsMd: agentsMdNews(result.agentsMd, agentsMdBefore, priorPaths),
8232
+ agentsMdLegacyDropped: result.agentsMd?.agentsMd.droppedLegacySections ?? 0,
8233
+ agentsMdLegacyEdited: result.agentsMd?.agentsMd.editedLegacySections ?? [],
7394
8234
  // #625 — install 화면(`sharedRow`)이 말하는 "남긴 것" 을 update 도 말한다. 고친 구간과 기록이 없어 모르는 구간은
7395
8235
  // 다른 사실이라 기록(`portions`)으로 가른다 — 어댑터는 기록된 키가 고쳐졌으면 남기고 기록 sha 를 잇는다(계약 ⓑ)
7396
8236
  sharedLeft: result.sharedFiles.filter((f) => f.action === "left" || f.leftAsIs.length + f.kept.length > 0).map((f) => {
@@ -7410,233 +8250,33 @@ function refreshExternalCli(projectDir, harnessRoot, droppedKeys, harnessMcp) {
7410
8250
  })
7411
8251
  };
7412
8252
  }
7413
- function updateDir(target, source, ext, ctx, now = /* @__PURE__ */ new Date()) {
7414
- if (!existsSync21(target) || !existsSync21(source))
7415
- return { updated: 0, backedUp: [], displaced: [] };
7416
- let updated = 0;
7417
- const backedUp = [];
7418
- const displaced = [];
7419
- for (const file of readdirSync3(target)) {
7420
- if (!file.endsWith(ext)) continue;
7421
- const targetFile = join29(target, file);
7422
- const sourceFile = join29(source, file);
7423
- if (!existsSync21(sourceFile)) continue;
7424
- if (ctx.outside?.skip(targetFile)) continue;
7425
- const projectRel = `.claude/${ctx.prefix}/${file}`;
7426
- if (ctx.excluded && isBaselineExcluded(projectRel, ctx.excluded)) continue;
7427
- if (ctx.justWritten?.has(projectRel)) continue;
7428
- const next = readFileSync21(sourceFile, "utf8");
7429
- const rec = ctx.recordOf?.(projectRel) ?? baselineRecord(ctx.baseline, `${ctx.prefix}/${file}`);
7430
- const w3 = judgedUpdateWrite(targetFile, rec, next, now);
7431
- if (w3.changed) updated++;
7432
- if (w3.kind === "edited") backedUp.push(`${ctx.prefix}/${file}`);
7433
- const d2 = displacedEntry(w3, projectRel, targetFile);
7434
- if (d2 !== null) displaced.push(d2);
7435
- }
7436
- return { updated, backedUp, displaced };
7437
- }
7438
- function syncSkills(targetDir, sourceDir, baseline, now = /* @__PURE__ */ new Date(), foreignOf, outside, excludedSkill, opts = {}) {
7439
- if (!existsSync21(targetDir) || !existsSync21(sourceDir))
7440
- return {
7441
- updated: 0,
7442
- backedUp: [],
7443
- skippedLinks: [],
7444
- foreignOwned: [],
7445
- pruned: [],
7446
- displaced: []
7447
- };
7448
- let updated = 0;
7449
- const backedUp = [];
7450
- const skippedLinks = [];
7451
- const foreignOwned = [];
7452
- const pruned = [];
7453
- const displaced = [];
7454
- for (const skill of readdirSync3(sourceDir, { withFileTypes: true })) {
7455
- if (!skill.isDirectory()) continue;
7456
- const targetSkill = join29(targetDir, skill.name);
7457
- if (occupiedByNonDirectory(targetSkill)) {
7458
- skippedLinks.push(skill.name);
7459
- continue;
7460
- }
7461
- if (opts.targets !== void 0 && !opts.targets.has(skill.name)) continue;
7462
- if (!existsSync21(targetSkill)) continue;
7463
- if (excludedSkill?.(skill.name)) continue;
7464
- for (const rel of listFilesRecursive(join29(sourceDir, skill.name))) {
7465
- const targetFile = join29(targetSkill, rel);
7466
- const foreign = foreignOf(`${skill.name}/${rel}`);
7467
- if (foreign !== null) {
7468
- if (!foreignOwned.includes(foreign)) foreignOwned.push(foreign);
7469
- continue;
7470
- }
7471
- if (outside?.skip(targetFile)) continue;
7472
- const next = readFileSync21(join29(sourceDir, skill.name, rel), "utf8");
7473
- const projectRel = `.claude/skills/${skill.name}/${rel}`;
7474
- const rec = opts.recordOf?.(projectRel) ?? baselineRecord(baseline, `${skill.name}/${rel}`);
7475
- const w3 = judgedUpdateWrite(targetFile, rec, next, now);
7476
- if (w3.changed) updated++;
7477
- if (w3.kind === "edited") backedUp.push(`${skill.name}/${rel}`);
7478
- const d2 = displacedEntry(w3, projectRel, targetFile);
7479
- if (d2 !== null) displaced.push(d2);
7480
- }
7481
- const bundled = new Set(listFilesRecursive(join29(sourceDir, skill.name)));
7482
- for (const rel of listFilesRecursive(targetSkill)) {
7483
- if (bundled.has(rel) || basename3(rel).includes(".backup-")) continue;
7484
- if (foreignOf(`${skill.name}/${rel}`) !== null) continue;
7485
- const targetFile = join29(targetSkill, rel);
7486
- if (outside?.skip(targetFile)) continue;
7487
- const rec = opts.recordOf?.(`.claude/skills/${skill.name}/${rel}`) ?? baselineRecord(baseline, `${skill.name}/${rel}`);
7488
- if (rec.state !== "sha" || hashContent(readFileSync21(targetFile, "utf8")) !== rec.sha256) {
7489
- backupFile(targetFile, now);
7490
- backedUp.push(`${skill.name}/${rel}`);
7491
- }
7492
- unlinkSync2(targetFile);
7493
- pruned.push(`${skill.name}/${rel}`);
7494
- for (let d2 = dirname6(targetFile); d2 !== targetSkill && readdirSync3(d2).length === 0; d2 = dirname6(d2))
7495
- rmdirSync(d2);
7496
- }
7497
- }
7498
- return { updated, backedUp, skippedLinks, foreignOwned, pruned, displaced };
7499
- }
7500
- function judgedUpdateWrite(abs, rec, next, now) {
7501
- const disk = existsSync21(abs) ? readFileSync21(abs, "utf8") : null;
7502
- const j4 = judge({ op: "write", kind: "harness", rec, disk, next, run: "update" });
7503
- let backup;
7504
- switch (j4.verdict) {
7505
- case "leave":
7506
- break;
7507
- case "create":
7508
- mkdirSync6(dirname6(abs), { recursive: true });
7509
- writeFileSync8(abs, next);
7510
- break;
7511
- case "overwrite":
7512
- writeFileSync8(abs, next);
7513
- break;
7514
- case "backup+overwrite":
7515
- backup = backupFile(abs, now);
7516
- writeFileSync8(abs, next);
7517
- break;
7518
- default:
7519
- throw new Error(`update: '${j4.verdict}' is not a write verdict (${abs})`);
7520
- }
7521
- const changed = j4.verdict !== "leave";
7522
- if (j4.record === "displaced") return { kind: "displacedSame", changed };
7523
- if (backup === void 0) return { kind: changed ? "written" : "same", changed };
7524
- return { kind: j4.record === "displaced+sha" ? "displaced" : "edited", changed, backup };
7525
- }
7526
- function displacedEntry(w3, projectRel, abs) {
7527
- if (w3.kind === "displacedSame") return { path: projectRel, change: "displaced", notes: [] };
7528
- if (w3.kind !== "displaced" || w3.backup === void 0) return null;
7529
- const backupRel = `${projectRel}${w3.backup.slice(abs.length)}`;
7530
- return { path: projectRel, change: "displaced", notes: [backupRel] };
7531
- }
7532
- function baselineRecord(baseline, key) {
7533
- const sha256 = baseline.get(key);
7534
- return sha256 === void 0 ? { state: "no-sha" } : { state: "sha", sha256 };
7535
- }
7536
- function recordedStateOf(log) {
7537
- if (log === null) return void 0;
7538
- const isTarget = recordedTrackTargets(log);
7539
- return (projectRel) => recorded(log, projectRel, { isTarget });
7540
- }
7541
- function notOursUnder(displaced, prefix) {
7542
- return new Set(
7543
- displaced.filter((f) => f.notes.length === 0 && f.path.startsWith(prefix)).map((f) => f.path.slice(prefix.length))
7544
- );
7545
- }
7546
8253
  function withoutLostBackups(prior, add) {
7547
8254
  const backedUp = new Set(
7548
8255
  (prior ?? []).filter((f) => f.change === "displaced" && f.notes.length > 0).map((f) => f.path)
7549
8256
  );
7550
8257
  return add.filter((f) => f.notes.length > 0 || !backedUp.has(f.path));
7551
8258
  }
7552
- function recordDisplaced(projectDir, displaced) {
7553
- const log = readInstallLog(projectDir);
7554
- if (!log) return;
7555
- const add = withoutLostBackups(log.rootFiles, displaced);
7556
- if (add.length === 0) return;
7557
- try {
7558
- writeInstallLog(projectDir, { ...log, rootFiles: mergeRootFiles(log.rootFiles, add) });
7559
- } catch {
7560
- }
7561
- }
7562
- function skillBaseline(projectDir) {
7563
- const log = readInstallLog(projectDir);
7564
- return new Map((log?.skillFiles ?? []).map((f) => [f.path, f.sha256]));
7565
- }
7566
- function policyBaseline(projectDir) {
7567
- const log = readInstallLog(projectDir);
7568
- return new Map((log?.policyFiles ?? []).map((f) => [f.path, f.sha256]));
7569
- }
7570
- function refreshPolicyBaseline(projectDir, templatesDir, dirs, outside, notOurs = /* @__PURE__ */ new Set()) {
7571
- const log = readInstallLog(projectDir);
7572
- if (!log) return;
7573
- const excluded = excludedIds(log);
7574
- const inDirs = (path) => (dirs === void 0 || dirs.some((d2) => path.startsWith(`${d2}/`))) && !outside.has(path) && !isBaselineExcluded(`.claude/${path}`, excluded);
7575
- const rewritten = [
7576
- ...(log.policyFiles ?? []).filter((f) => !inDirs(f.path)),
7577
- ...collectPolicyHashes(projectDir, templatesDir).filter(
7578
- (f) => inDirs(f.path) && !notOurs.has(f.path)
7579
- )
7580
- ];
7581
- const policyFiles = [...rewritten, ...retiredClaims(log, projectDir, rewritten)];
7582
- const next = { ...log };
7583
- if (policyFiles.length > 0) next.policyFiles = policyFiles;
7584
- else delete next.policyFiles;
7585
- try {
7586
- writeInstallLog(projectDir, next);
7587
- } catch {
7588
- }
7589
- }
7590
- function outsidePaths(outside, prefix) {
7591
- return new Set(
7592
- outside.list().filter((o) => o.path.startsWith(prefix)).map((o) => o.path.slice(prefix.length))
7593
- );
7594
- }
7595
- function refreshSkillBaseline(projectDir, templatesDir, outside, targets, notOurs = /* @__PURE__ */ new Set()) {
7596
- const log = readInstallLog(projectDir);
7597
- if (!log) return;
7598
- const excludedSkill = excludedSkillOf(log);
7599
- const keepPrior = (path) => outside.has(path) || excludedSkill(path.split("/")[0] ?? "");
7600
- const ours = (path) => (targets === void 0 || targets.has(path.split("/")[0] ?? "")) && !notOurs.has(path);
7601
- const skillFiles = [
7602
- ...collectSkillHashes(projectDir, templatesDir).filter(
7603
- (f) => !keepPrior(f.path) && ours(f.path)
7604
- ),
7605
- ...(log.skillFiles ?? []).filter((f) => keepPrior(f.path))
7606
- ];
7607
- const next = { ...log };
7608
- if (skillFiles.length > 0) next.skillFiles = skillFiles;
7609
- else delete next.skillFiles;
7610
- try {
7611
- writeInstallLog(projectDir, next);
7612
- } catch {
7613
- }
7614
- }
7615
- function pruneOrphans(target, source, ext, ctx, now = /* @__PURE__ */ new Date()) {
8259
+ function pruneOrphans(target, source, ext, ctx, now) {
7616
8260
  if (!existsSync21(target) || !existsSync21(source)) return [];
7617
8261
  const removed = [];
7618
- for (const file of readdirSync3(target)) {
7619
- if (!file.endsWith(ext)) continue;
7620
- const sourceFile = join29(source, file);
7621
- if (!existsSync21(sourceFile)) {
7622
- const targetFile = join29(target, file);
7623
- const key = `${ctx.prefix}/${file}`;
7624
- if (!ctx.baseline.has(key)) continue;
7625
- if (ctx.outside?.skip(targetFile)) continue;
7626
- try {
7627
- const current = readFileSync21(targetFile, "utf8");
7628
- const owned = ctx.baseline.get(key) === hashContent(current) && ctx.recordOf?.(`.claude/${key}`).state !== "no-sha";
7629
- if (!owned) backupFile(targetFile, now);
7630
- unlinkSync2(targetFile);
7631
- removed.push(file);
7632
- } catch {
7633
- }
8262
+ for (const file of readdirSync4(target)) {
8263
+ if (!file.endsWith(ext) || existsSync21(join30(source, file))) continue;
8264
+ const targetFile = join30(target, file);
8265
+ const rec = ctx.recordOf(`.claude/${ctx.prefix}/${file}`);
8266
+ try {
8267
+ const disk = readFileSync21(targetFile, "utf8");
8268
+ const { verdict } = judge({ op: "remove", kind: "harness", rec, disk, next: null });
8269
+ if (verdict === "leave" || ctx.outside.skip(targetFile)) continue;
8270
+ if (verdict === "backup+remove") backupFile(targetFile, now);
8271
+ unlinkSync2(targetFile);
8272
+ removed.push(file);
8273
+ } catch {
7634
8274
  }
7635
8275
  }
7636
8276
  return removed;
7637
8277
  }
7638
8278
  function retireMcpAllowlist(projectDir, now = /* @__PURE__ */ new Date()) {
7639
- const target = join29(projectDir, ".mcp-allowlist");
8279
+ const target = join30(projectDir, ".mcp-allowlist");
7640
8280
  if (!existsSync21(target)) return null;
7641
8281
  try {
7642
8282
  const backup = backupFile(target, now);
@@ -7646,53 +8286,6 @@ function retireMcpAllowlist(projectDir, now = /* @__PURE__ */ new Date()) {
7646
8286
  return null;
7647
8287
  }
7648
8288
  }
7649
- function refreshNeutralHelpers(projectDir, templatesDir, report) {
7650
- const spec = buildAssetSpec({ tracks: installedTracks(projectDir), options: DEFAULT_OPTIONS });
7651
- const refreshed = /* @__PURE__ */ new Map();
7652
- for (const entry of buildManifest(spec)) {
7653
- if (entry.type !== "file" || !isCliNeutralTarget(entry.target)) continue;
7654
- if (!entry.applies(spec)) continue;
7655
- const abs = join29(projectDir, entry.target);
7656
- const source = join29(templatesDir, entry.source);
7657
- if (!existsSync21(source)) continue;
7658
- let st2;
7659
- try {
7660
- st2 = lstatSync4(abs);
7661
- } catch {
7662
- continue;
7663
- }
7664
- if (st2.isSymbolicLink()) {
7665
- report.helpersKept = [...report.helpersKept ?? [], entry.target];
7666
- continue;
7667
- }
7668
- if (!st2.isFile()) continue;
7669
- const log2 = readInstallLog(projectDir);
7670
- const recorded2 = (log2?.externalFiles ?? []).find((f) => f.path === entry.target);
7671
- const diskSha = hashContent(readFileSync21(abs, "utf8"));
7672
- const freshContent = readFileSync21(source, "utf8");
7673
- const freshSha = hashContent(freshContent);
7674
- if (diskSha === freshSha) {
7675
- if (recorded2?.sha256 !== freshSha) refreshed.set(entry.target, freshSha);
7676
- continue;
7677
- }
7678
- if (recorded2 === void 0 || recorded2.sha256 !== diskSha) backupFile(abs);
7679
- writeFileSync8(abs, freshContent, "utf8");
7680
- refreshed.set(entry.target, freshSha);
7681
- report.updated[entry.target] = (report.updated[entry.target] ?? 0) + 1;
7682
- }
7683
- if (refreshed.size === 0) return;
7684
- const log = readInstallLog(projectDir);
7685
- if (log === null) return;
7686
- const next = (log.externalFiles ?? []).map(
7687
- (f) => refreshed.has(f.path) ? { ...f, sha256: refreshed.get(f.path) } : f
7688
- );
7689
- for (const [path, sha256] of refreshed)
7690
- if (!next.some((f) => f.path === path)) next.push({ path, sha256 });
7691
- try {
7692
- writeInstallLog(projectDir, { ...log, externalFiles: next });
7693
- } catch {
7694
- }
7695
- }
7696
8289
  function recordClaudeCommand(log, tracks) {
7697
8290
  const trackArgs = (tracks.length > 0 ? tracks : log.spec.tracks).map((t) => `--track ${t}`);
7698
8291
  return `agent-harness install ${trackArgs.join(" ")} --cli claude --scope ${log.scope}`.replace(
@@ -7705,11 +8298,11 @@ function recordClaudeCommand(log, tracks) {
7705
8298
  function runInstall(ctx) {
7706
8299
  const { harnessRoot, projectDir } = ctx;
7707
8300
  const mode = ctx.mode ?? "fresh";
7708
- const templatesDir = join30(harnessRoot, "templates");
8301
+ const templatesDir = join31(harnessRoot, "templates");
7709
8302
  if (!existsSync22(templatesDir)) {
7710
8303
  throw new Error(`Templates dir not found: ${templatesDir}`);
7711
8304
  }
7712
- const claudeDir = join30(projectDir, ".claude");
8305
+ const claudeDir = join31(projectDir, ".claude");
7713
8306
  const previousLog = readInstallLog(projectDir);
7714
8307
  const legacyAnchor = mode === "update" ? null : legacyAnchorTrace(projectDir, ctx.spec);
7715
8308
  if (mode === "update" && previousLog === null) {
@@ -7734,13 +8327,13 @@ function runInstall(ctx) {
7734
8327
  const manifestSpec = buildManifestSpec(spec);
7735
8328
  const baselineExcluded = new Set(spec.baselineExclude ?? []);
7736
8329
  if (spec.cli.includes("claude")) {
7737
- const settingsPath = join30(projectDir, ".claude", "settings.json");
8330
+ const settingsPath = join31(projectDir, ".claude", "settings.json");
7738
8331
  if (existsSync22(settingsPath)) {
7739
8332
  try {
7740
8333
  JSON.parse(readFileSync22(settingsPath, "utf8"));
7741
8334
  } catch {
7742
8335
  throw new Error(
7743
- `${relative3(process.cwd(), settingsPath)} is not valid JSON \u2014 left untouched, so no hooks were wired. Fix or delete that file, then run the install again`
8336
+ `${relative5(process.cwd(), settingsPath)} is not valid JSON \u2014 left untouched, so no hooks were wired. Fix or delete that file, then run the install again`
7744
8337
  );
7745
8338
  }
7746
8339
  }
@@ -7874,10 +8467,17 @@ function runInstallStages(ctx, args) {
7874
8467
  ...outOfTrack.forget,
7875
8468
  ...anchorRetired?.forget === true ? [anchorRetired.path] : []
7876
8469
  ]);
8470
+ const left = /* @__PURE__ */ new Set([
8471
+ ...outOfTrack.removed,
8472
+ ...outOfTrack.backedUp.map((b2) => b2.path),
8473
+ ...anchorRetired?.action === "removed" ? [anchorRetired.path] : []
8474
+ ]);
8475
+ const displacedRestored = previousLog === null || left.size === 0 ? [] : restoreDisplaced(previousLog, projectDir, { has: (p2) => left.has(p2), write: true });
7877
8476
  const logBase = previousLog === null || forgotten.size === 0 ? previousLog : {
7878
8477
  ...previousLog,
7879
8478
  spec: { ...previousLog.spec, clis: [...installedClis(previousLog)] },
7880
- externalFiles: (previousLog.externalFiles ?? []).filter((f) => !forgotten.has(f.path))
8479
+ externalFiles: (previousLog.externalFiles ?? []).filter((f) => !forgotten.has(f.path)),
8480
+ ...displacedRestored.some((d2) => d2.forget) ? { rootFiles: withoutRestored(previousLog.rootFiles, displacedRestored) } : {}
7881
8481
  };
7882
8482
  stage.envExampleCreated = writeEnvExample(projectDir, spec.tracks);
7883
8483
  const envFiles = writeEnvironmentFiles(writer, stage.envExampleCreated, previousLog);
@@ -7937,10 +8537,15 @@ function runInstallStages(ctx, args) {
7937
8537
  linked.outside,
7938
8538
  reclaimOutside.list()
7939
8539
  ),
7940
- ...outOfTrack.removed.length + outOfTrack.backedUp.length + outOfTrack.kept.length > 0 ? { outOfTrack } : {}
8540
+ ...outOfTrack.removed.length + outOfTrack.backedUp.length + outOfTrack.kept.length > 0 ? { outOfTrack } : {},
8541
+ ...displacedRestored.length > 0 ? { displacedRestored } : {}
7941
8542
  };
7942
8543
  ctx.onProgress?.({ type: "baseline-complete", baseline });
7943
- const { report: external, backups: externalSkillBackups } = runExternalPhase(ctx, previousLog);
8544
+ const {
8545
+ report: external,
8546
+ backups: externalSkillBackups,
8547
+ displaced: externalSkillDisplaced
8548
+ } = runExternalPhase(ctx, previousLog);
7944
8549
  writeInstallLogSafe(
7945
8550
  ctx,
7946
8551
  // 링크 본문의 기준선도 같은 필드다 — 같은 경로면 뒤(이번에 쓴 값)가 이긴다.
@@ -7956,7 +8561,9 @@ function runInstallStages(ctx, args) {
7956
8561
  ...cliSharedFiles.filter((f) => f.createdAsRoot === true).map((f) => ({ path: f.path, change: "created", notes: ["\uD558\uB124\uC2A4 \uBAAB\uB9CC(\uBE14\uB85D)"] })),
7957
8562
  // #761 — 외부 변환 · 링크 본문이 첫 접촉으로 비켜 둔 설치자 파일. 하네스가 떠날 때 되돌리는 근거다
7958
8563
  ...cliDisplaced,
7959
- ...linked.displaced
8564
+ ...linked.displaced,
8565
+ // #780 — 외부 스킬 도구가 덮은 설치자 파일. 그 자리의 도구 파일은 `buildAssetEntries` 가 이 경로로 자산 몫에 넣는다
8566
+ ...externalSkillDisplaced
7960
8567
  ],
7961
8568
  cliTransforms.codexOptIn
7962
8569
  );
@@ -8018,7 +8625,7 @@ function releasedThisRun(args) {
8018
8625
  }
8019
8626
  if (id.startsWith(BASELINE_PREFIX)) {
8020
8627
  const again2 = manifest.some(
8021
- (e2) => e2.applies(manifestSpec) && classifyBaselineTarget(e2.target)?.id === id && existsSync22(join30(projectDir, e2.target))
8628
+ (e2) => e2.applies(manifestSpec) && classifyBaselineTarget(e2.target)?.id === id && existsSync22(join31(projectDir, e2.target))
8022
8629
  );
8023
8630
  return { id, again: again2, tail: "the harness manages it again" };
8024
8631
  }
@@ -8027,7 +8634,7 @@ function releasedThisRun(args) {
8027
8634
  options: spec.options,
8028
8635
  ...spec.userOverride ? { userOverride: spec.userOverride } : {}
8029
8636
  });
8030
- const recorded2 = (previousLog?.assets.some((a) => a.id === id) ?? false) || [".claude/skills", ".agents/skills"].some((d2) => existsSync22(join30(projectDir, d2, id)));
8637
+ const recorded2 = (previousLog?.assets.some((a) => a.id === id) ?? false) || [".claude/skills", ".agents/skills"].some((d2) => existsSync22(join31(projectDir, d2, id)));
8031
8638
  return {
8032
8639
  id,
8033
8640
  again,
@@ -8039,10 +8646,10 @@ function pendingKeyExcludes(projectDir, keyExclude, touched) {
8039
8646
  const done = new Set(touched);
8040
8647
  return keyExclude.flatMap((id) => {
8041
8648
  const path = sharedPathOfKeyId(id);
8042
- if (path === null || done.has(path) || !existsSync22(join30(projectDir, path))) return [];
8649
+ if (path === null || done.has(path) || !existsSync22(join31(projectDir, path))) return [];
8043
8650
  const name = adapterFor(path);
8044
8651
  const keys = [...excludedKeys(path, [id])];
8045
- const present2 = name === null ? null : ADAPTERS[name].read(readFileSync22(join30(projectDir, path), "utf8"), keys, projectDir);
8652
+ const present2 = name === null ? null : ADAPTERS[name].read(readFileSync22(join31(projectDir, path), "utf8"), keys, projectDir);
8046
8653
  return present2 !== null && present2.size === 0 ? [] : [{ id, path }];
8047
8654
  });
8048
8655
  }
@@ -8086,7 +8693,7 @@ function installCliNeutralAssets(manifestSpec, templatesDir, baselineExcluded, w
8086
8693
  continue;
8087
8694
  }
8088
8695
  if (!isCliNeutralTarget(entry.target)) continue;
8089
- const source = join30(templatesDir, entry.source);
8696
+ const source = join31(templatesDir, entry.source);
8090
8697
  if (!existsSync22(source)) {
8091
8698
  result.skipped += 1;
8092
8699
  continue;
@@ -8107,13 +8714,13 @@ function installClaudeBaseline(manifestSpec, projectDir, templatesDir, baselineE
8107
8714
  if (!entry.applies(manifestSpec)) {
8108
8715
  continue;
8109
8716
  }
8110
- const target = join30(projectDir, entry.target);
8717
+ const target = join31(projectDir, entry.target);
8111
8718
  if (isBaselineExcluded(entry.target, baselineExcluded)) {
8112
8719
  result.excluded.push(entry.target);
8113
8720
  if (existsSync22(target)) result.excludedOnDisk.push(entry.target);
8114
8721
  continue;
8115
8722
  }
8116
- const source = join30(templatesDir, entry.source);
8723
+ const source = join31(templatesDir, entry.source);
8117
8724
  if (!existsSync22(source)) {
8118
8725
  result.skipped += 1;
8119
8726
  continue;
@@ -8144,18 +8751,18 @@ function installClaudeBaseline(manifestSpec, projectDir, templatesDir, baselineE
8144
8751
  if (!result.foreignOwned.includes(slot)) result.foreignOwned.push(slot);
8145
8752
  continue;
8146
8753
  }
8147
- if (writer.skipOutside(join30(projectDir, entry.target, rel))) {
8754
+ if (writer.skipOutside(join31(projectDir, entry.target, rel))) {
8148
8755
  leftOutside += 1;
8149
8756
  continue;
8150
8757
  }
8151
- writer.harness(`${entry.target}/${rel}`, { source: join30(source, rel) });
8758
+ writer.harness(`${entry.target}/${rel}`, { source: join31(source, rel) });
8152
8759
  }
8153
8760
  if (leftOutside > 0) continue;
8154
8761
  result.dirsCopied += 1;
8155
8762
  }
8156
8763
  accumulateCategory(result.categories, entry);
8157
8764
  }
8158
- const hookDir = join30(projectDir, ".claude/hooks");
8765
+ const hookDir = join31(projectDir, ".claude/hooks");
8159
8766
  if (existsSync22(hookDir)) {
8160
8767
  chmodHooksSync(hookDir, projectDir);
8161
8768
  }
@@ -8172,12 +8779,12 @@ function installClaudeBaseline(manifestSpec, projectDir, templatesDir, baselineE
8172
8779
  if (settingsSource !== null) {
8173
8780
  writeSettingsPortion(writer, settingsSource, projectDir, previousLog, (script) => {
8174
8781
  const target = `${HOOKS_PREFIX}${script}`;
8175
- return manifest.some((e2) => e2.target === target && e2.applies(manifestSpec)) && !isBaselineExcluded(target, baselineExcluded) && existsSync22(join30(projectDir, target));
8782
+ return manifest.some((e2) => e2.target === target && e2.applies(manifestSpec)) && !isBaselineExcluded(target, baselineExcluded) && existsSync22(join31(projectDir, target));
8176
8783
  });
8177
8784
  result.keptHookRefs.push(
8178
8785
  ...findStaleHookRefs(
8179
- join30(projectDir, ".claude", "settings.json"),
8180
- join30(projectDir, ".claude")
8786
+ join31(projectDir, ".claude", "settings.json"),
8787
+ join31(projectDir, ".claude")
8181
8788
  )
8182
8789
  );
8183
8790
  }
@@ -8187,7 +8794,7 @@ function installClaudeBaseline(manifestSpec, projectDir, templatesDir, baselineE
8187
8794
  // ADR-085 · update(`upsertRootImport`)와 같은 판정 — 실제로 깔린 스킬만 안내한다. 밖 링크라 쓰지 않은
8188
8795
  // 스킬(ADR-098)을 "installed" 라 적으면 다음 update 가 그 절을 걷어 내 두 동작이 서로 반대로 쓴다.
8189
8796
  (manifestSpec.selectedInternalSkills ?? []).filter(
8190
- (id) => existsSync22(join30(projectDir, ".claude", "skills", id))
8797
+ (id) => existsSync22(join31(projectDir, ".claude", "skills", id))
8191
8798
  ),
8192
8799
  harnessRoot,
8193
8800
  writer
@@ -8228,7 +8835,7 @@ function writeEnvironmentFiles(writer, envExampleCreated, previousLog) {
8228
8835
  }
8229
8836
  function runExternalPhase(ctx, previousLog) {
8230
8837
  if (ctx.runExternal === null) {
8231
- return { report: null, backups: [] };
8838
+ return { report: null, backups: [], displaced: [] };
8232
8839
  }
8233
8840
  const { projectDir, spec } = ctx;
8234
8841
  const runExt = ctx.runExternal ?? runExternalInstall;
@@ -8251,22 +8858,38 @@ function runExternalPhase(ctx, previousLog) {
8251
8858
  };
8252
8859
  const { targets } = selectExternalTargets(EXTERNAL_ASSETS, { ...filterCtx, cli: spec.cli });
8253
8860
  ctx.onProgress?.({ type: "external-start", assetCount: targets.length });
8254
- const skillIds = new Set(targets.filter((a) => a.method.kind === "skill").map((a) => a.id));
8255
- const settle = holdSkillSlots(
8256
- projectDir,
8257
- (previousLog?.assets ?? []).filter((a) => a.method === "skill" && skillIds.has(a.id))
8258
- );
8259
- const external = runExt(
8260
- { ...filterCtx, cli: spec.cli, projectDir, ...spec.scope ? { scope: spec.scope } : {} },
8261
- externalDeps
8262
- );
8263
- const backups = settle();
8861
+ const skills = targets.filter((a) => a.method.kind === "skill");
8862
+ const skillIds = new Set(skills.map((a) => a.id));
8863
+ const settle = holdSkillSlots(projectDir, {
8864
+ recorded: (previousLog?.assets ?? []).filter((a) => a.method === "skill" && skillIds.has(a.id)),
8865
+ slots: skillSlotsFor(skills, spec.cli, { log: previousLog, scope: spec.scope }),
8866
+ log: previousLog,
8867
+ clis: spec.cli,
8868
+ scope: spec.scope
8869
+ });
8870
+ let ran;
8871
+ try {
8872
+ ran = runExt(
8873
+ { ...filterCtx, cli: spec.cli, projectDir, ...spec.scope ? { scope: spec.scope } : {} },
8874
+ externalDeps
8875
+ );
8876
+ } catch (e2) {
8877
+ try {
8878
+ settle(null);
8879
+ } catch {
8880
+ }
8881
+ throw e2;
8882
+ }
8883
+ const { report: external, backups, displaced } = settle(ran);
8884
+ const displacedBackups = new Set(displaced.map((f) => join31(projectDir, f.notes[0] ?? "")));
8885
+ const editBackups = backups.filter((b2) => !displacedBackups.has(b2));
8264
8886
  ctx.onProgress?.({
8265
8887
  type: "external-complete",
8266
8888
  report: external,
8267
- ...backups.length > 0 ? { backups } : {}
8889
+ ...editBackups.length > 0 ? { backups: editBackups } : {},
8890
+ ...displaced.length > 0 ? { displaced: displaced.map((f) => ({ path: f.path, backup: f.notes[0] ?? "" })) } : {}
8268
8891
  });
8269
- return { report: external, backups };
8892
+ return { report: external, backups, displaced };
8270
8893
  }
8271
8894
  var InstallInterruptedError = class extends Error {
8272
8895
  constructor(message, written, record, backups = [], hadInstall = false) {
@@ -8331,7 +8954,7 @@ function recordInterruptedInstall(ctx, error, run) {
8331
8954
  const codexTrust = registeredTrust(journal.codexOptIn, ctx.projectDir);
8332
8955
  const written = writtenSoFar(ledger, cliFiles, rootFiles, stage.rootImportWritten);
8333
8956
  if (codexTrust) written.push(`${codexTrust.configPath} (Codex trust entry for this folder)`);
8334
- const projectRel = (abs) => relative3(ctx.projectDir, abs).split(sep4).join("/");
8957
+ const projectRel = (abs) => relative5(ctx.projectDir, abs).split(sep6).join("/");
8335
8958
  const writtenSet = new Set(written);
8336
8959
  const backups = [...own.backups, ...journal.backups].map((abs) => {
8337
8960
  const original = projectRel(backupOriginal(abs) ?? abs);
@@ -8471,7 +9094,7 @@ function accumulateCategory(cats, entry) {
8471
9094
  }
8472
9095
  }
8473
9096
  function writeRootClaudeMd(projectDir, tracks, continuousSkills, harnessRoot, writer) {
8474
- const target = join30(projectDir, "CLAUDE.md");
9097
+ const target = join31(projectDir, "CLAUDE.md");
8475
9098
  if (writer.skipOutside(target)) return null;
8476
9099
  const existing = existsSync22(target) ? readFileSync22(target, "utf-8") : null;
8477
9100
  const seeded = existing === null ? seedRootClaudeProjectContext(projectDir, harnessRoot) : null;
@@ -8483,7 +9106,7 @@ function writeRootClaudeMd(projectDir, tracks, continuousSkills, harnessRoot, wr
8483
9106
  });
8484
9107
  if (content !== existing) {
8485
9108
  if (existing !== null) backupIfLossyUtf8(target);
8486
- writeFileSync9(target, content);
9109
+ writeFileSync8(target, content);
8487
9110
  }
8488
9111
  return {
8489
9112
  created: existing === null,
@@ -8501,14 +9124,14 @@ function chmodHooksSync(hookDir, projectDir) {
8501
9124
  }
8502
9125
  }
8503
9126
  function listHookFiles(hookDir) {
8504
- return readdirSync4(hookDir, { withFileTypes: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".sh")).map((e2) => resolve(hookDir, e2.name));
9127
+ return readdirSync5(hookDir, { withFileTypes: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".sh")).map((e2) => resolve(hookDir, e2.name));
8505
9128
  }
8506
9129
 
8507
9130
  // src/commands/install-render.ts
8508
9131
  init_esm_shims();
8509
9132
  import { homedir as homedir3 } from "os";
8510
- import { existsSync as existsSync24, readdirSync as readdirSync5, readFileSync as readFileSync24 } from "fs";
8511
- import { join as join32, relative as relative4 } from "path";
9133
+ import { existsSync as existsSync24, readdirSync as readdirSync6, readFileSync as readFileSync24 } from "fs";
9134
+ import { join as join33, relative as relative6 } from "path";
8512
9135
 
8513
9136
  // src/preset-recommend.ts
8514
9137
  init_esm_shims();
@@ -8545,9 +9168,9 @@ function groupAssetsByCategory(assetIds) {
8545
9168
  // src/resident-entries.ts
8546
9169
  init_esm_shims();
8547
9170
  import { existsSync as existsSync23, readFileSync as readFileSync23 } from "fs";
8548
- import { basename as basename5, join as join31 } from "path";
9171
+ import { basename as basename5, join as join32 } from "path";
8549
9172
  function residentEntries(spec) {
8550
- const skillThere = (id) => INTERNAL_BUNDLED_SKILL_IDS.includes(id) && [".claude/skills", ".agents/skills"].some((d2) => existsSync23(join31(spec.projectDir, d2, id)));
9173
+ const skillThere = (id) => INTERNAL_BUNDLED_SKILL_IDS.includes(id) && [".claude/skills", ".agents/skills"].some((d2) => existsSync23(join32(spec.projectDir, d2, id)));
8551
9174
  const forceExclude = (spec.userOverride?.forceExclude ?? []).filter((id) => !skillThere(id));
8552
9175
  const assetSpec = buildManifestSpec(
8553
9176
  spec.userOverride ? { ...spec, userOverride: { ...spec.userOverride, forceExclude } } : spec
@@ -8573,7 +9196,7 @@ function residentEntries(spec) {
8573
9196
  function residentCostFor(spec, entries = residentEntries(spec), contextFiles) {
8574
9197
  if (contextFiles !== void 0) return residentCost(entries, void 0, spec.cli, contextFiles);
8575
9198
  const cost = residentCost(entries, void 0, spec.cli, plannedClaudeContext(spec));
8576
- if (anchorSurface(spec.cli).scaffold === "agents-md" && existsSync23(join31(spec.projectDir, "AGENTS.md"))) {
9199
+ if (anchorSurface(spec.cli).scaffold === "agents-md" && existsSync23(join32(spec.projectDir, "AGENTS.md"))) {
8577
9200
  return makeResidentCost({
8578
9201
  ...cost,
8579
9202
  contextNote: "planned, not counting text already in AGENTS.md"
@@ -8584,7 +9207,7 @@ function residentCostFor(spec, entries = residentEntries(spec), contextFiles) {
8584
9207
  function plannedClaudeContext(spec) {
8585
9208
  if (!spec.cli.includes("claude")) return void 0;
8586
9209
  const root = resolveBundleRoot();
8587
- const rootClaude = join31(spec.projectDir, "CLAUDE.md");
9210
+ const rootClaude = join32(spec.projectDir, "CLAUDE.md");
8588
9211
  const existing = existsSync23(rootClaude) ? readFileSync23(rootClaude, "utf-8") : null;
8589
9212
  const seeded = existing === null ? seedRootClaudeProjectContext(spec.projectDir, root) : null;
8590
9213
  if (existing === null && seeded === null) return void 0;
@@ -8595,14 +9218,14 @@ function plannedClaudeContext(spec) {
8595
9218
  continuousSkills: manifestSpec.selectedInternalSkills ?? [],
8596
9219
  ...seeded === null ? {} : { projectContext: seeded }
8597
9220
  });
8598
- return [join31(root, "templates", "CLAUDE.md"), { text }];
9221
+ return [join32(root, "templates", "CLAUDE.md"), { text }];
8599
9222
  }
8600
9223
  function onDiskTargets(spec, assetSpec) {
8601
9224
  const out = /* @__PURE__ */ new Set();
8602
9225
  for (const e2 of buildManifest(assetSpec)) {
8603
9226
  if (!e2.applies(assetSpec)) continue;
8604
9227
  const t = classifyBaselineTarget(e2.target);
8605
- if (t !== null && existsSync23(join31(spec.projectDir, e2.target))) out.add(t.id);
9228
+ if (t !== null && existsSync23(join32(spec.projectDir, e2.target))) out.add(t.id);
8606
9229
  }
8607
9230
  return out;
8608
9231
  }
@@ -8665,6 +9288,14 @@ function createInstallRenderer(log, spec, verbose) {
8665
9288
  phase2HeaderPrinted = true;
8666
9289
  } else if (event.type === "external-complete") {
8667
9290
  for (const b2 of event.backups ?? []) log(assetRow("success", "backup", shortenPath(b2)));
9291
+ for (const d2 of event.displaced ?? [])
9292
+ log(
9293
+ assetRow(
9294
+ "skip",
9295
+ d2.path,
9296
+ `had a file with this name \u2014 saved as ${d2.backup}, put back on uninstall`
9297
+ )
9298
+ );
8668
9299
  const excluded = event.report.excludedByCli;
8669
9300
  if (excluded.length > 0) {
8670
9301
  if (!phase2HeaderPrinted) {
@@ -8754,23 +9385,23 @@ function residentEntriesOnDisk(spec) {
8754
9385
  const hasClaude = spec.cli.includes("claude");
8755
9386
  const surface = anchorSurface(spec.cli).scaffold;
8756
9387
  const mdFiles = (dir, kind, skip) => {
8757
- const abs = join32(spec.projectDir, dir);
8758
- return existsSync24(abs) ? readdirSync5(abs, { withFileTypes: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".md") && e2.name !== skip).map((e2) => ({
9388
+ const abs = join33(spec.projectDir, dir);
9389
+ return existsSync24(abs) ? readdirSync6(abs, { withFileTypes: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".md") && e2.name !== skip).map((e2) => ({
8759
9390
  source: `${kind}/${e2.name}`,
8760
9391
  // `target` 은 `residentCost` 의 분류 키다 — 실제 자리는 `file` 이 말한다.
8761
9392
  target: `.claude/${kind}/${e2.name}`,
8762
9393
  // 배포판이 아니라 **이 프로젝트의 파일**을 잰다 — 사용자가 고친 본문도,
8763
9394
  // 배포판에 더는 없는 파일도 실제로 상주하는 것은 이쪽이다.
8764
- file: join32(abs, e2.name)
9395
+ file: join33(abs, e2.name)
8765
9396
  })) : [];
8766
9397
  };
8767
- const rules = surface === "claude" ? mdFiles(join32(".claude", "rules"), "rules") : rulesInline(spec.cli) ? planned.filter((e2) => e2.target.startsWith(".claude/rules/")) : mdFiles(join32(".agents", "rules"), "rules", "uzys-harness.md");
8768
- const slot = hasClaude ? join32(".claude", "skills") : join32(".agents", "skills");
9398
+ const rules = surface === "claude" ? mdFiles(join33(".claude", "rules"), "rules") : rulesInline(spec.cli) ? planned.filter((e2) => e2.target.startsWith(".claude/rules/")) : mdFiles(join33(".agents", "rules"), "rules", "uzys-harness.md");
9399
+ const slot = hasClaude ? join33(".claude", "skills") : join33(".agents", "skills");
8769
9400
  const skills = planned.filter((e2) => e2.target.startsWith(".claude/skills/")).map((e2) => ({
8770
9401
  ...e2,
8771
- file: join32(spec.projectDir, slot, e2.target.slice(".claude/skills/".length))
9402
+ file: join33(spec.projectDir, slot, e2.target.slice(".claude/skills/".length))
8772
9403
  })).filter((e2) => existsSync24(e2.file));
8773
- const agents = hasClaude ? mdFiles(join32(".claude", "agents"), "agents") : [];
9404
+ const agents = hasClaude ? mdFiles(join33(".claude", "agents"), "agents") : [];
8774
9405
  return [
8775
9406
  ...planned.filter(
8776
9407
  (e2) => !e2.target.startsWith(".claude/rules/") && !e2.target.startsWith(".claude/agents/") && !e2.target.startsWith(".claude/skills/")
@@ -8782,8 +9413,8 @@ function residentEntriesOnDisk(spec) {
8782
9413
  }
8783
9414
  function contextFilesOnDisk(spec) {
8784
9415
  const surface = anchorSurface(spec.cli).scaffold;
8785
- const files = surface === "claude" ? [HARNESS_ANCHOR_FILE, "CLAUDE.md", join32(".claude", "CLAUDE.md")] : ["AGENTS.md"];
8786
- return files.map((f) => join32(spec.projectDir, f));
9416
+ const files = surface === "claude" ? [HARNESS_ANCHOR_FILE, "CLAUDE.md", join33(".claude", "CLAUDE.md")] : ["AGENTS.md"];
9417
+ return files.map((f) => join33(spec.projectDir, f));
8787
9418
  }
8788
9419
  function rulesInsideAgentsMd(spec, cost) {
8789
9420
  if (anchorSurface(spec.cli).scaffold !== "agents-md" || !rulesInline(spec.cli)) return cost;
@@ -9037,8 +9668,8 @@ function renderFinalSummary(log, spec, report, fromWizard) {
9037
9668
  const codexTrusted = report.codexOptIn?.trustEntry.status === "registered" || report.codexOptIn?.trustEntry.status === "already-present";
9038
9669
  const alreadyTrustedGlobally = (() => {
9039
9670
  try {
9040
- const home = process.env.CODEX_HOME ?? join32(homedir3(), ".codex");
9041
- const configPath = join32(home, "config.toml");
9671
+ const home = process.env.CODEX_HOME ?? join33(homedir3(), ".codex");
9672
+ const configPath = join33(home, "config.toml");
9042
9673
  if (!existsSync24(configPath)) return false;
9043
9674
  return hasTrustEntry(readFileSync24(configPath, "utf8"), spec.projectDir);
9044
9675
  } catch {
@@ -9065,7 +9696,7 @@ function renderFinalSummary(log, spec, report, fromWizard) {
9065
9696
  const remote = (report.shared ?? []).find((f) => f.path === ".mcp.json")?.remote ?? [];
9066
9697
  for (const line of remoteMcpNextLines(remote, spec.cli)) log(`${" ".repeat(16)} ${line}`);
9067
9698
  const scaffoldFiles = scaffoldFilesForCli(spec.cli).filter(
9068
- (f) => f === "CLAUDE.md" && report.rootClaudeMd?.created === true || hasFillPrompt(join32(spec.projectDir, f))
9699
+ (f) => f === "CLAUDE.md" && report.rootClaudeMd?.created === true || hasFillPrompt(join33(spec.projectDir, f))
9069
9700
  );
9070
9701
  if (scaffoldFiles.length > 0) {
9071
9702
  const auditInstalled = buildManifestSpec(spec).selectedInternalSkills?.includes("audit-harness-fit");
@@ -9205,6 +9836,7 @@ function renderPhase1Rows(log, baseline, verbose = false) {
9205
9836
  const retired = anchorRetireRow(baseline.updateMode.anchorRetired ?? null);
9206
9837
  if (retired) log(retired);
9207
9838
  for (const row of outOfTrackRows(baseline.updateMode.outOfTrack)) log(row);
9839
+ for (const row of displacedRows(baseline.updateMode.displacedRestored)) log(row);
9208
9840
  for (const f of baseline.updateMode.sharedWrites ?? []) {
9209
9841
  const row = sharedFileRow(f);
9210
9842
  if (row !== null) log(row);
@@ -9311,15 +9943,6 @@ function renderPhase1Rows(log, baseline, verbose = false) {
9311
9943
  )
9312
9944
  );
9313
9945
  }
9314
- if (baseline.updateMode.helpersKept?.length) {
9315
- log(
9316
- assetRow(
9317
- "skip",
9318
- "helper is a link",
9319
- `${baseline.updateMode.helpersKept.join(", ")} \xB7 \uB9C1\uD06C\uB77C \uAC31\uC2E0\uD558\uC9C0 \uC54A\uACE0 \uB0A8\uACBC\uB2E4`
9320
- )
9321
- );
9322
- }
9323
9946
  for (const row of outsideLinkRows(baseline.updateMode.outsideLinks ?? [])) log(row);
9324
9947
  if (baseline.updateMode.foreignOwned.length > 0) {
9325
9948
  log(
@@ -9411,6 +10034,26 @@ function renderPhase1Rows(log, baseline, verbose = false) {
9411
10034
  )
9412
10035
  );
9413
10036
  }
10037
+ const legacyDropped = baseline.updateMode.agentsMdLegacyDropped ?? 0;
10038
+ if (legacyDropped > 0) {
10039
+ log(
10040
+ assetRow(
10041
+ "success",
10042
+ "AGENTS.md",
10043
+ `removed ${legacyDropped} section(s) written by an older harness version (saved a copy as AGENTS.md.backup-<time>)`
10044
+ )
10045
+ );
10046
+ }
10047
+ const legacyEdited = baseline.updateMode.agentsMdLegacyEdited ?? [];
10048
+ if (legacyEdited.length > 0) {
10049
+ log(
10050
+ assetRow(
10051
+ "skip",
10052
+ "AGENTS.md",
10053
+ `${legacyEdited.length} harness section(s) you had edited (${legacyEdited.join(" \xB7 ")}) now hold the current version \u2014 your edit is in the copy AGENTS.md.backup-<time>`
10054
+ )
10055
+ );
10056
+ }
9414
10057
  const externalEdited = baseline.updateMode.externalBackedUp.filter((p2) => !noChecksum.has(p2));
9415
10058
  if (externalEdited.length > 0) {
9416
10059
  log(
@@ -9497,8 +10140,12 @@ function renderPhase1Rows(log, baseline, verbose = false) {
9497
10140
  if (j4.backupAbs !== void 0) judgedBackups.add(j4.backupAbs);
9498
10141
  log(judgedRow(j4));
9499
10142
  }
10143
+ for (const f of baseline.shared ?? []) {
10144
+ if (f.backupAbs !== void 0 && f.removedEdited.length > 0) judgedBackups.add(f.backupAbs);
10145
+ }
9500
10146
  for (const row of outsideLinkRows(baseline.outsideLinks ?? [])) log(row);
9501
10147
  for (const row of outOfTrackRows(baseline.outOfTrack)) log(row);
10148
+ for (const row of displacedRows(baseline.displacedRestored)) log(row);
9502
10149
  if (baseline.backups) {
9503
10150
  for (const b2 of baseline.backups) {
9504
10151
  if (judgedBackups.has(b2)) continue;
@@ -9638,6 +10285,11 @@ function renderPhase1Rows(log, baseline, verbose = false) {
9638
10285
  function judgedRow(j4) {
9639
10286
  return j4.backup !== void 0 ? ` ${c.green(symbol.success)} backed up ${j4.path} \u2014 ${j4.line}` : ` ${c.yellow(symbol.skip)} ${j4.path} \u2014 ${j4.line}`;
9640
10287
  }
10288
+ function displacedRows(outcomes) {
10289
+ return (outcomes ?? []).flatMap(
10290
+ (d2) => d2.verdict === "restore" ? [assetRow("success", d2.path, `yours \u2014 put back (from ${d2.backup})`)] : d2.verdict === "advise" ? [assetRow("skip", d2.path, d2.line)] : []
10291
+ );
10292
+ }
9641
10293
  function outOfTrackRows(r) {
9642
10294
  if (r === void 0) return [];
9643
10295
  const recorded2 = r.recordedTracks.join(", ");
@@ -9674,16 +10326,16 @@ function outOfTrackRows(r) {
9674
10326
  function outsideLinkRows(links) {
9675
10327
  const groups = /* @__PURE__ */ new Map();
9676
10328
  for (const o of links) groups.set(o.link, [...groups.get(o.link) ?? [], o]);
9677
- const rows = [];
10329
+ const rows2 = [];
9678
10330
  for (const [link, group] of groups) {
9679
10331
  const first = group[0];
9680
10332
  if (first === void 0) continue;
9681
10333
  const fileLink = group.length === 1 && first.path === link;
9682
- rows.push(
10334
+ rows2.push(
9683
10335
  fileLink ? ` ${c.yellow(symbol.skip)} ${link} \u2014 left as is: the link points outside the project (${first.linkTarget}). Not written; replace the link with a regular file to get the harness version.` : ` ${c.yellow(symbol.skip)} ${link}/ \u2014 left as is: the link points outside the project (${first.linkTarget}), ${group.length} file(s) not written. Replace the link with a regular folder to get the harness version.`
9684
10336
  );
9685
10337
  }
9686
- return rows;
10338
+ return rows2;
9687
10339
  }
9688
10340
  function sharedFileRow(f) {
9689
10341
  if (f.line === "") return null;
@@ -9751,8 +10403,9 @@ function excludedKeyParts(f) {
9751
10403
  parts.push(`removed the harness part: ${f.removedOut.join(", ")} (you asked: ${asked})`);
9752
10404
  }
9753
10405
  if (f.removedEdited.length > 0) {
10406
+ const where = f.backup === void 0 ? "went with it" : `are in the copy: ${f.backup}`;
9754
10407
  parts.push(
9755
- `your edits to ${f.removedEdited.join(", ")} went with it (a hook entry calls a harness script \u2014 left alone it would call a script that is no longer wired)`
10408
+ `your edits to ${f.removedEdited.join(", ")} ${where} (a hook entry calls a harness script \u2014 left alone it would call a script that is no longer wired)`
9756
10409
  );
9757
10410
  }
9758
10411
  if (f.keptOut.length > 0) {
@@ -9872,7 +10525,7 @@ function renderInterruptedInstall(err, e2, cwd = process.cwd()) {
9872
10525
  }
9873
10526
  const { path, error } = e2.record;
9874
10527
  err(
9875
- path !== null ? ` Already in place before it stopped \u2014 recorded in ${relative4(cwd, path) || path}:` : " Already in place before it stopped:"
10528
+ path !== null ? ` Already in place before it stopped \u2014 recorded in ${relative6(cwd, path) || path}:` : " Already in place before it stopped:"
9876
10529
  );
9877
10530
  for (const [key, n] of groups) {
9878
10531
  err(` ${key.endsWith("/") ? `${key} (${n} file${n === 1 ? "" : "s"})` : key}`);
@@ -10196,7 +10849,7 @@ function registerInstallCommand(cli) {
10196
10849
  // src/commands/list.ts
10197
10850
  init_esm_shims();
10198
10851
  import { existsSync as existsSync25, readFileSync as readFileSync25 } from "fs";
10199
- import { join as join33, resolve as resolve3 } from "path";
10852
+ import { join as join34, resolve as resolve3 } from "path";
10200
10853
  function listAction(options = {}, deps = {}) {
10201
10854
  const log = deps.log ?? console.log;
10202
10855
  const err = deps.err ?? console.error;
@@ -10247,33 +10900,33 @@ function listAction(options = {}, deps = {}) {
10247
10900
  }
10248
10901
  var SELECTIONS_SHOWN = 10;
10249
10902
  function formatSelectionRows(log, projectDir) {
10250
- const rows = [];
10903
+ const rows2 = [];
10251
10904
  const excluded = [...excludedIds(log)].sort();
10252
- if (excluded.length === 0) rows.push(c.dim(" excluded: (none)"));
10905
+ if (excluded.length === 0) rows2.push(c.dim(" excluded: (none)"));
10253
10906
  const still = new Set(
10254
10907
  excludedStillThere(projectDir, new Set(excluded), [], log).map((e2) => e2.id)
10255
10908
  );
10256
10909
  for (const id of excluded) {
10257
10910
  const kind = id.startsWith("baseline:") ? "baseline" : isKeyId(id) ? "harness part" : "asset";
10258
- rows.push(` \u2298 ${id} ${c.dim(`(${kind}${still.has(id) ? " \xB7 still on disk" : ""})`)}`);
10911
+ rows2.push(` \u2298 ${id} ${c.dim(`(${kind}${still.has(id) ? " \xB7 still on disk" : ""})`)}`);
10259
10912
  }
10260
10913
  const history = (log.selections ?? []).slice(-SELECTIONS_SHOWN);
10261
- if (history.length > 0) rows.push(c.dim(` history (latest ${history.length}):`));
10914
+ if (history.length > 0) rows2.push(c.dim(` history (latest ${history.length}):`));
10262
10915
  for (const ev of history) {
10263
10916
  const day = ev.at.slice(0, 10);
10264
10917
  const parts = [];
10265
10918
  if (ev.by === "migration") {
10266
10919
  if (ev.released?.length) parts.push(`released ${ev.released.join(", ")}`);
10267
10920
  if (ev.kept?.length) parts.push(`kept ${ev.kept.join(", ")}`);
10268
- rows.push(` ${day} migration (26.162\u201326.163 record): ${parts.join(" \xB7 ")}`);
10921
+ rows2.push(` ${day} migration (26.162\u201326.163 record): ${parts.join(" \xB7 ")}`);
10269
10922
  continue;
10270
10923
  }
10271
10924
  if (ev.without?.length) parts.push(`--without ${ev.without.join(", ")}`);
10272
10925
  if (ev.with?.length) parts.push(`re-added ${ev.with.join(", ")}`);
10273
10926
  const tags = [ev.via === "wizard" ? "wizard" : "", ev.interrupted ? "interrupted" : ""].filter(Boolean).join(", ");
10274
- rows.push(` ${day} install ${parts.join(" \xB7 ")}${tags ? ` (${tags})` : ""}`);
10927
+ rows2.push(` ${day} install ${parts.join(" \xB7 ")}${tags ? ` (${tags})` : ""}`);
10275
10928
  }
10276
- return rows;
10929
+ return rows2;
10277
10930
  }
10278
10931
  function formatAssetRows(assets) {
10279
10932
  const idWidth = Math.max(0, ...assets.map((a) => a.id.length));
@@ -10296,16 +10949,16 @@ function harnessFileTops(log) {
10296
10949
  }
10297
10950
  function formatHarnessFileRows(log, projectDir) {
10298
10951
  const tops = harnessFileTops(log);
10299
- const rows = [` ${c.dim(tops.length > 0 ? tops.join(" ") : "(none recorded)")}`];
10952
+ const rows2 = [` ${c.dim(tops.length > 0 ? tops.join(" ") : "(none recorded)")}`];
10300
10953
  const rootMd = log.templates.rootClaudeMd;
10301
10954
  if (rootMd) {
10302
- const path = join33(projectDir, rootMd.path);
10955
+ const path = join34(projectDir, rootMd.path);
10303
10956
  const modified = existsSync25(path) && hashContent(readFileSync25(path, "utf8")) !== rootMd.sha256;
10304
- rows.push(
10957
+ rows2.push(
10305
10958
  modified ? ` ${c.dim(rootMd.path)} ${c.yellow("(\uC218\uC815\uB428 \u2014 uninstall \uC2DC \uBCF4\uC874)")}` : ` ${c.dim(rootMd.path)}`
10306
10959
  );
10307
10960
  }
10308
- return rows;
10961
+ return rows2;
10309
10962
  }
10310
10963
  var ROOT_FILE_CHANGE = {
10311
10964
  created: "\uC0DD\uC131",
@@ -10314,7 +10967,7 @@ var ROOT_FILE_CHANGE = {
10314
10967
  displaced: "\uBE44\uCF1C \uB460"
10315
10968
  };
10316
10969
  function formatRootFileRows(rootFiles, projectDir) {
10317
- const present2 = rootFiles.filter((f) => existsSync25(join33(projectDir, f.path)));
10970
+ const present2 = rootFiles.filter((f) => existsSync25(join34(projectDir, f.path)));
10318
10971
  const width = Math.max(0, ...present2.map((f) => f.path.length));
10319
10972
  return present2.map(
10320
10973
  (f) => ` ${c.dim(padDisplay(f.path, width))} ${c.dim(ROOT_FILE_CHANGE[f.change])} ${c.dim(f.notes.join(" / "))}`
@@ -10332,22 +10985,20 @@ function registerListCommand(cli) {
10332
10985
  init_esm_shims();
10333
10986
  import { spawnSync as spawnSync2 } from "child_process";
10334
10987
  import {
10335
- accessSync,
10988
+ accessSync as accessSync2,
10336
10989
  existsSync as existsSync26,
10337
- constants as fsConstants,
10990
+ constants as fsConstants2,
10338
10991
  lstatSync as lstatSync5,
10339
- mkdirSync as mkdirSync7,
10340
- readdirSync as readdirSync6,
10992
+ readdirSync as readdirSync7,
10341
10993
  readFileSync as readFileSync26,
10342
10994
  readlinkSync,
10343
10995
  realpathSync as realpathSync3,
10344
- renameSync,
10345
- rmdirSync as rmdirSync2,
10346
- rmSync as rmSync4,
10996
+ rmdirSync as rmdirSync3,
10997
+ rmSync as rmSync5,
10347
10998
  statSync as statSync5,
10348
- writeFileSync as writeFileSync10
10999
+ writeFileSync as writeFileSync9
10349
11000
  } from "fs";
10350
- import { dirname as dirname7, join as join34, relative as relative5, resolve as resolve4, sep as sep5 } from "path";
11001
+ import { dirname as dirname8, join as join35, relative as relative7, resolve as resolve4, sep as sep7 } from "path";
10351
11002
 
10352
11003
  // src/uninstall-interactive.ts
10353
11004
  init_esm_shims();
@@ -10537,11 +11188,11 @@ var getClosingCode = (openingCode) => {
10537
11188
  };
10538
11189
  var wrapAnsiCode = (code) => `${ESC}${ANSI_CSI}${code}${ANSI_SGR_TERMINATOR}`;
10539
11190
  var wrapAnsiHyperlink = (url) => `${ESC}${ANSI_ESCAPE_LINK}${url}${ANSI_ESCAPE_BELL}`;
10540
- var wrapWord = (rows, word, columns) => {
11191
+ var wrapWord = (rows2, word, columns) => {
10541
11192
  const characters = word[Symbol.iterator]();
10542
11193
  let isInsideEscape = false;
10543
11194
  let isInsideLinkEscape = false;
10544
- let lastRow = rows.at(-1);
11195
+ let lastRow = rows2.at(-1);
10545
11196
  let visible = lastRow === void 0 ? 0 : dist_default2(lastRow);
10546
11197
  let currentCharacter = characters.next();
10547
11198
  let nextCharacter = characters.next();
@@ -10550,9 +11201,9 @@ var wrapWord = (rows, word, columns) => {
10550
11201
  const character = currentCharacter.value;
10551
11202
  const characterLength = dist_default2(character);
10552
11203
  if (visible + characterLength <= columns) {
10553
- rows[rows.length - 1] += character;
11204
+ rows2[rows2.length - 1] += character;
10554
11205
  } else {
10555
- rows.push(character);
11206
+ rows2.push(character);
10556
11207
  visible = 0;
10557
11208
  }
10558
11209
  if (character === ESC || character === CSI) {
@@ -10571,7 +11222,7 @@ var wrapWord = (rows, word, columns) => {
10571
11222
  } else {
10572
11223
  visible += characterLength;
10573
11224
  if (visible === columns && !nextCharacter.done) {
10574
- rows.push("");
11225
+ rows2.push("");
10575
11226
  visible = 0;
10576
11227
  }
10577
11228
  }
@@ -10579,9 +11230,9 @@ var wrapWord = (rows, word, columns) => {
10579
11230
  nextCharacter = characters.next();
10580
11231
  rawCharacterIndex += character.length;
10581
11232
  }
10582
- lastRow = rows.at(-1);
10583
- if (!visible && lastRow !== void 0 && lastRow.length && rows.length > 1) {
10584
- rows[rows.length - 2] += rows.pop();
11233
+ lastRow = rows2.at(-1);
11234
+ if (!visible && lastRow !== void 0 && lastRow.length && rows2.length > 1) {
11235
+ rows2[rows2.length - 2] += rows2.pop();
10585
11236
  }
10586
11237
  };
10587
11238
  var stringVisibleTrimSpacesRight = (string) => {
@@ -10606,25 +11257,25 @@ var exec = (string, columns, options = {}) => {
10606
11257
  let escapeCode;
10607
11258
  let escapeUrl;
10608
11259
  const words = string.split(" ");
10609
- let rows = [""];
11260
+ let rows2 = [""];
10610
11261
  let rowLength = 0;
10611
11262
  for (let index = 0; index < words.length; index++) {
10612
11263
  const word = words[index];
10613
11264
  if (options.trim !== false) {
10614
- const row = rows.at(-1) ?? "";
11265
+ const row = rows2.at(-1) ?? "";
10615
11266
  const trimmed = row.trimStart();
10616
11267
  if (row.length !== trimmed.length) {
10617
- rows[rows.length - 1] = trimmed;
11268
+ rows2[rows2.length - 1] = trimmed;
10618
11269
  rowLength = dist_default2(trimmed);
10619
11270
  }
10620
11271
  }
10621
11272
  if (index !== 0) {
10622
11273
  if (rowLength >= columns && (options.wordWrap === false || options.trim === false)) {
10623
- rows.push("");
11274
+ rows2.push("");
10624
11275
  rowLength = 0;
10625
11276
  }
10626
11277
  if (rowLength || options.trim === false) {
10627
- rows[rows.length - 1] += " ";
11278
+ rows2[rows2.length - 1] += " ";
10628
11279
  rowLength++;
10629
11280
  }
10630
11281
  }
@@ -10634,33 +11285,33 @@ var exec = (string, columns, options = {}) => {
10634
11285
  const breaksStartingThisLine = 1 + Math.floor((wordLength - remainingColumns - 1) / columns);
10635
11286
  const breaksStartingNextLine = Math.floor((wordLength - 1) / columns);
10636
11287
  if (breaksStartingNextLine < breaksStartingThisLine) {
10637
- rows.push("");
11288
+ rows2.push("");
10638
11289
  }
10639
- wrapWord(rows, word, columns);
10640
- rowLength = dist_default2(rows.at(-1) ?? "");
11290
+ wrapWord(rows2, word, columns);
11291
+ rowLength = dist_default2(rows2.at(-1) ?? "");
10641
11292
  continue;
10642
11293
  }
10643
11294
  if (rowLength + wordLength > columns && rowLength && wordLength) {
10644
11295
  if (options.wordWrap === false && rowLength < columns) {
10645
- wrapWord(rows, word, columns);
10646
- rowLength = dist_default2(rows.at(-1) ?? "");
11296
+ wrapWord(rows2, word, columns);
11297
+ rowLength = dist_default2(rows2.at(-1) ?? "");
10647
11298
  continue;
10648
11299
  }
10649
- rows.push("");
11300
+ rows2.push("");
10650
11301
  rowLength = 0;
10651
11302
  }
10652
11303
  if (rowLength + wordLength > columns && options.wordWrap === false) {
10653
- wrapWord(rows, word, columns);
10654
- rowLength = dist_default2(rows.at(-1) ?? "");
11304
+ wrapWord(rows2, word, columns);
11305
+ rowLength = dist_default2(rows2.at(-1) ?? "");
10655
11306
  continue;
10656
11307
  }
10657
- rows[rows.length - 1] += word;
11308
+ rows2[rows2.length - 1] += word;
10658
11309
  rowLength += wordLength;
10659
11310
  }
10660
11311
  if (options.trim !== false) {
10661
- rows = rows.map((row) => stringVisibleTrimSpacesRight(row));
11312
+ rows2 = rows2.map((row) => stringVisibleTrimSpacesRight(row));
10662
11313
  }
10663
- const preString = rows.join("\n");
11314
+ const preString = rows2.join("\n");
10664
11315
  let inSurrogate = false;
10665
11316
  for (let i = 0; i < preString.length; i++) {
10666
11317
  const character = preString[i];
@@ -11544,8 +12195,8 @@ function assertPagesCoverAllCategories(pages) {
11544
12195
  }
11545
12196
  assertPagesCoverAllCategories(INSTALL_TARGET_PAGES);
11546
12197
  function viewportItems(itemCount) {
11547
- const rows = process.stdout.rows ?? 24;
11548
- return Math.max(8, Math.min(itemCount, rows - 10));
12198
+ const rows2 = process.stdout.rows ?? 24;
12199
+ return Math.max(8, Math.min(itemCount, rows2 - 10));
11549
12200
  }
11550
12201
  var defaultPrompts = {
11551
12202
  intro: (msg) => ge(msg),
@@ -11854,11 +12505,11 @@ async function runInteractiveUninstall(projectDir, deps = {}) {
11854
12505
  const log = readLog(projectDir);
11855
12506
  if (!log) return { ok: false, reason: "no-log" };
11856
12507
  if (!deps.embedded) prompts.intro("uzys-agent-harness \xB7 uninstall");
11857
- const rows = buildRemovableRows(log.assets);
12508
+ const rows2 = buildRemovableRows(log.assets);
11858
12509
  const clis = installedClis(log);
11859
12510
  const mode = await prompts.selectMode(
11860
- buildUninstallModeChoices(clis, rows.length),
11861
- `uzys-agent-harness \xB7 uninstall installed: ${clis.join(", ") || "(none)"} \xB7 assets ${rows.length}`
12511
+ buildUninstallModeChoices(clis, rows2.length),
12512
+ `uzys-agent-harness \xB7 uninstall installed: ${clis.join(", ") || "(none)"} \xB7 assets ${rows2.length}`
11862
12513
  );
11863
12514
  if (mode === null) {
11864
12515
  prompts.cancel("Cancelled.");
@@ -11885,7 +12536,7 @@ async function runInteractiveUninstall(projectDir, deps = {}) {
11885
12536
  }
11886
12537
  return { ok: true, options: { projectDir } };
11887
12538
  }
11888
- const picked = await prompts.selectAssets(rows);
12539
+ const picked = await prompts.selectAssets(rows2);
11889
12540
  if (picked === null) {
11890
12541
  prompts.cancel("Cancelled.");
11891
12542
  return { ok: false, reason: "cancelled" };
@@ -11944,18 +12595,18 @@ function defaultUninstallPrompts() {
11944
12595
  });
11945
12596
  return q(r) ? null : r;
11946
12597
  },
11947
- selectCli: async (rows) => {
12598
+ selectCli: async (rows2) => {
11948
12599
  const r = await Ee({
11949
12600
  message: 'Which CLI? (the last remaining CLI cannot be removed this way \u2014 use "Remove everything")',
11950
- options: rows.map((x) => ({ value: x.value, label: x.label, hint: x.hint }))
12601
+ options: rows2.map((x) => ({ value: x.value, label: x.label, hint: x.hint }))
11951
12602
  });
11952
12603
  return q(r) ? null : r;
11953
12604
  },
11954
- selectAssets: async (rows) => {
11955
- if (rows.length === 0) return [];
12605
+ selectAssets: async (rows2) => {
12606
+ if (rows2.length === 0) return [];
11956
12607
  const r = await ve({
11957
12608
  message: "Assets to remove (Space toggle \xB7 Enter confirm \xB7 ESC cancel)",
11958
- options: rows.map((x) => ({ value: x.value, label: x.label, hint: x.hint })),
12609
+ options: rows2.map((x) => ({ value: x.value, label: x.label, hint: x.hint })),
11959
12610
  required: false
11960
12611
  });
11961
12612
  return q(r) ? null : r;
@@ -12087,9 +12738,20 @@ function uninstallAction(options, deps = {}) {
12087
12738
  }
12088
12739
  const logSurvives = selectedIds !== null;
12089
12740
  let reversed;
12741
+ let onlyDisplaced = [];
12090
12742
  try {
12091
12743
  reversed = executeReverse(plan, log, logSurvives, journal);
12092
- if (!assetsOnly) {
12744
+ if (assetsOnly) {
12745
+ const removed = new Set(reversed.removedIds);
12746
+ onlyDisplaced = restoreDisplaced(installLog, projectDir, {
12747
+ has: inAssetSlots(installLog.assets.filter((a) => removed.has(a.id))),
12748
+ write: true
12749
+ });
12750
+ for (const d2 of onlyDisplaced) {
12751
+ if (d2.verdict === "restore") journal.push(`put back ${d2.path}`);
12752
+ }
12753
+ for (const line of displacedLines(onlyDisplaced, false)) log(line);
12754
+ } else {
12093
12755
  const done = removeHarnessFiles(installLog, projectDir, FULL_SCOPE, {
12094
12756
  rm: rmJ,
12095
12757
  write: true,
@@ -12125,7 +12787,7 @@ function uninstallAction(options, deps = {}) {
12125
12787
  }
12126
12788
  const { succeeded, failed, removedIds } = reversed;
12127
12789
  const logWriteFailed = settleLog(
12128
- { installLog, projectDir, selectedIds, removedIds },
12790
+ { installLog, projectDir, selectedIds, removedIds, displaced: onlyDisplaced },
12129
12791
  { log, err, rm, writeLog }
12130
12792
  );
12131
12793
  for (const line of advisoryLines(plan, projectDir, rootFiles, globalTrust)) {
@@ -12154,11 +12816,11 @@ function unwritablePaths(ctx) {
12154
12816
  const project = ctx.targetAssets.filter((a) => a.scope === "project");
12155
12817
  if (project.some((a) => a.method === "npm")) need.add(".");
12156
12818
  const parentOf = (path) => {
12157
- const abs = join34(projectDir, path);
12819
+ const abs = join35(projectDir, path);
12158
12820
  if (!safeExists(abs)) return;
12159
12821
  const real = safeRealpath(abs);
12160
12822
  if (real !== null && isOutsideProject(projectDir, real)) return;
12161
- need.add(dirname7(path));
12823
+ need.add(dirname8(path));
12162
12824
  };
12163
12825
  for (const a of project)
12164
12826
  if (a.method === "skill") for (const f of a.files ?? []) parentOf(f.path);
@@ -12166,16 +12828,17 @@ function unwritablePaths(ctx) {
12166
12828
  const recordedShared = new Set((installLog.portions ?? []).map((p2) => p2.path));
12167
12829
  const made = new Set((installLog.externalFiles ?? []).map((f) => f.path));
12168
12830
  for (const p2 of UNINSTALL_SHARED) {
12169
- if (recordedShared.has(p2) && existsSync26(join34(projectDir, p2))) need.add(p2);
12831
+ if (recordedShared.has(p2) && existsSync26(join35(projectDir, p2))) need.add(p2);
12170
12832
  if (made.has(p2)) parentOf(p2);
12171
12833
  }
12172
12834
  for (const p2 of harnessCandidates(installLog)) parentOf(p2);
12835
+ for (const p2 of legacyFileCandidates()) parentOf(p2);
12173
12836
  for (const f of installLog.rootFiles ?? []) if (f.change === "displaced") parentOf(f.path);
12174
12837
  if (hasRootImport(projectDir)) need.add("CLAUDE.md");
12175
12838
  }
12176
12839
  return [...need].filter((p2) => {
12177
12840
  try {
12178
- accessSync(join34(projectDir, p2), fsConstants.W_OK);
12841
+ accessSync2(join35(projectDir, p2), fsConstants2.W_OK);
12179
12842
  return false;
12180
12843
  } catch (e2) {
12181
12844
  return e2.code !== "ENOENT";
@@ -12249,8 +12912,9 @@ function settleLog(ctx, io) {
12249
12912
  const { installLog, projectDir, selectedIds, removedIds } = ctx;
12250
12913
  if (selectedIds) {
12251
12914
  const remaining = installLog.assets.filter((a) => !removedIds.includes(a.id));
12915
+ const rootFiles = ctx.displaced.some((d2) => d2.forget) ? { rootFiles: withoutRestored(installLog.rootFiles, ctx.displaced) } : {};
12252
12916
  try {
12253
- io.writeLog(projectDir, { ...installLog, assets: remaining });
12917
+ io.writeLog(projectDir, { ...installLog, assets: remaining, ...rootFiles });
12254
12918
  io.log(` ${status.success(`install log updated (${remaining.length} asset(s) remain)`)}`);
12255
12919
  } catch (e2) {
12256
12920
  io.err(status.failure(c.red(`ERROR: install log \uAC31\uC2E0 \uC2E4\uD328 \u2014 ${installLogPath(projectDir)}`)));
@@ -12259,10 +12923,20 @@ function settleLog(ctx, io) {
12259
12923
  return true;
12260
12924
  }
12261
12925
  } else {
12262
- io.rm(join34(projectDir, INSTALL_LOG_DIR));
12926
+ const recordDir = join35(projectDir, INSTALL_LOG_DIR);
12927
+ if (outsideLinkAt(projectDir, INSTALL_LOG_DIR) !== null) io.rm(recordDir);
12928
+ else {
12929
+ for (const rel of [
12930
+ `${INSTALL_LOG_DIR}/${INSTALL_LOG_FILENAME}`,
12931
+ ...HARNESS_RUNTIME_IGNORES
12932
+ ]) {
12933
+ io.rm(join35(projectDir, rel));
12934
+ }
12935
+ pruneEmptyDirsUpward(projectDir, recordDir);
12936
+ }
12263
12937
  if (outsideLinkAt(projectDir, LEGACY_INSTALL_LOG_DIR) === null) {
12264
12938
  io.rm(legacyInstallLogPath(projectDir));
12265
- pruneEmptyDirsUpward(projectDir, join34(projectDir, LEGACY_INSTALL_LOG_DIR));
12939
+ pruneEmptyDirsUpward(projectDir, join35(projectDir, LEGACY_INSTALL_LOG_DIR));
12266
12940
  }
12267
12941
  io.log(` ${status.success("install log removed")}`);
12268
12942
  }
@@ -12274,7 +12948,9 @@ function dryRunLines(plan, installLog, projectDir, assetsOnly, rootFiles, harnes
12274
12948
  lines2.push(c.dim(" (no project-scope assets to reverse)"));
12275
12949
  }
12276
12950
  lines2.push(...plan.reverseSteps.map((s) => ` \u25CB ${s.label}`));
12277
- lines2.push(" \u25CB remove install record (.uzys-agent-harness/ \u2014 \uB85C\uADF8\xB7\uD5EC\uD37C \uC2A4\uD06C\uB9BD\uD2B8\xB7\uCC28\uB2E8 \uB85C\uADF8)");
12951
+ lines2.push(
12952
+ " \u25CB remove install record (.uzys-agent-harness/ \u2014 \uC124\uCE58 \uAE30\uB85D\xB7\uCC28\uB2E8 \uB85C\uADF8\xB7update \uBC31\uC5C5 \uBAA9\uB85D)"
12953
+ );
12278
12954
  lines2.push(
12279
12955
  ...plan.noReversePath.flatMap((a) => [
12280
12956
  c.dim(` \u2298 ${a.id} (${a.method}) \u2014 \uC790\uB3D9 \uB418\uB3CC\uB9AC\uAE30 \uACBD\uB85C \uC5C6\uC74C, \uAE30\uB85D \uC720\uC9C0`),
@@ -12286,9 +12962,17 @@ function dryRunLines(plan, installLog, projectDir, assetsOnly, rootFiles, harnes
12286
12962
  rm: () => {
12287
12963
  },
12288
12964
  write: false,
12289
- harnessRoot
12965
+ harnessRoot,
12966
+ assetFilesGone: plannedAssetFiles(plan, installLog)
12290
12967
  });
12291
12968
  lines2.push(...harnessRemovalLines(preview, installLog, projectDir, FULL_SCOPE, true));
12969
+ } else {
12970
+ const preview = restoreDisplaced(installLog, projectDir, {
12971
+ has: inAssetSlots(plannedAssets(plan, installLog)),
12972
+ willBeGone: new Set(plannedAssetFiles(plan, installLog)),
12973
+ write: false
12974
+ });
12975
+ lines2.push(...displacedLines(preview, true));
12292
12976
  }
12293
12977
  lines2.push(...advisoryLines(plan, projectDir, rootFiles, globalTrust), "");
12294
12978
  return lines2;
@@ -12320,7 +13004,7 @@ function advisoryLines(plan, projectDir, rootFiles, globalTrust) {
12320
13004
  }
12321
13005
  function backupArtifactAdvisoryLines(projectDir) {
12322
13006
  const found = [];
12323
- for (const e2 of readdirSync6(projectDir, { withFileTypes: true })) {
13007
+ for (const e2 of readdirSync7(projectDir, { withFileTypes: true })) {
12324
13008
  if (CLI_FOLDERS2.some((d2) => e2.name.startsWith(`${d2}.backup-`))) {
12325
13009
  found.push(e2.name);
12326
13010
  } else if (e2.isFile() && BACKUP_SUFFIX.test(e2.name)) {
@@ -12328,8 +13012,7 @@ function backupArtifactAdvisoryLines(projectDir) {
12328
13012
  }
12329
13013
  }
12330
13014
  for (const dir of BACKUP_SCAN_DIRS) {
12331
- if (dir === INSTALL_LOG_DIR) continue;
12332
- const abs = join34(projectDir, dir);
13015
+ const abs = join35(projectDir, dir);
12333
13016
  if (!existsSync26(abs) || outsideLinkAt(projectDir, dir) !== null) continue;
12334
13017
  for (const rel of listFilesRecursive(abs)) {
12335
13018
  if (BACKUP_SUFFIX.test(rel)) found.push(`${dir}/${rel}`);
@@ -12345,11 +13028,11 @@ function backupArtifactAdvisoryLines(projectDir) {
12345
13028
  }
12346
13029
  function rootFileAdvisoryLines(rootFiles, projectDir) {
12347
13030
  const header = "[ROOT] `.claude/` \uBC16\uC5D0 \uB0A8\uB294 \uAC83 (\uC790\uB3D9\uC73C\uB85C \uC9C0\uC6B0\uC9C0 \uC54A\uB294\uB2E4):";
12348
- const present2 = rootFiles.filter((f) => existsSync26(join34(projectDir, f.path)));
13031
+ const present2 = rootFiles.filter((f) => existsSync26(join35(projectDir, f.path)));
12349
13032
  if (present2.length === 0) return [];
12350
13033
  const outside = /* @__PURE__ */ new Map();
12351
13034
  const inside = present2.filter((f) => {
12352
- const o = outsideProjectTarget(projectDir, join34(projectDir, f.path));
13035
+ const o = outsideProjectTarget(projectDir, join35(projectDir, f.path));
12353
13036
  if (o === null) return true;
12354
13037
  const group = outside.get(o.link) ?? { link: o, files: [] };
12355
13038
  group.files.push(f.path);
@@ -12388,7 +13071,7 @@ function rootFileMeaning(f, projectDir) {
12388
13071
  case "displaced": {
12389
13072
  const backup = f.notes[0];
12390
13073
  if (backup === void 0) return "\uD558\uB124\uC2A4 \uD310\uACFC \uAC19\uC740 \uC124\uCE58\uC790 \uD30C\uC77C\uC774 \uC788\uB358 \uC790\uB9AC (\uADF8\uB300\uB85C \uB450\uC5C8\uB2E4)";
12391
- return existsSync26(join34(projectDir, backup)) ? `\uD558\uB124\uC2A4\uAC00 \uC4F4 \uC790\uB9AC \u2014 \uC6D0\uB798 \uC788\uB358 \uC124\uCE58\uC790 \uD30C\uC77C\uC740 ${backup} \uC5D0 \uC788\uB2E4` : `\uD558\uB124\uC2A4\uAC00 \uC4F4 \uC790\uB9AC \u2014 \uC6D0\uB798 \uC788\uB358 \uC124\uCE58\uC790 \uD30C\uC77C\uC758 \uBC31\uC5C5(${backup})\uC774 \uB354\uB294 \uC5C6\uB2E4`;
13074
+ return existsSync26(join35(projectDir, backup)) ? `\uD558\uB124\uC2A4\uAC00 \uC4F4 \uC790\uB9AC \u2014 \uC6D0\uB798 \uC788\uB358 \uC124\uCE58\uC790 \uD30C\uC77C\uC740 ${backup} \uC5D0 \uC788\uB2E4` : `\uD558\uB124\uC2A4\uAC00 \uC4F4 \uC790\uB9AC \u2014 \uC6D0\uB798 \uC788\uB358 \uC124\uCE58\uC790 \uD30C\uC77C\uC758 \uBC31\uC5C5(${backup})\uC774 \uB354\uB294 \uC5C6\uB2E4`;
12392
13075
  }
12393
13076
  }
12394
13077
  }
@@ -12491,9 +13174,19 @@ function agentsMdRerenderSet(target, remaining, log, projectDir) {
12491
13174
  const set = agentsMdClis(remaining);
12492
13175
  if (set.length === 0) return null;
12493
13176
  const recorded2 = (log.externalFiles ?? []).some((f) => f.path === AGENTS_MD2) || (log.portions ?? []).some((p2) => p2.path === AGENTS_MD2);
12494
- if (!recorded2 || !existsSync26(join34(projectDir, AGENTS_MD2))) return null;
13177
+ if (!existsSync26(join35(projectDir, AGENTS_MD2))) return null;
13178
+ if (!recorded2 && !isLegacyHarnessAgentsMd(readTextOrEmpty(join35(projectDir, AGENTS_MD2)))) {
13179
+ return null;
13180
+ }
12495
13181
  return set;
12496
13182
  }
13183
+ function readTextOrEmpty(abs) {
13184
+ try {
13185
+ return readFileSync26(abs, "utf8");
13186
+ } catch {
13187
+ return "";
13188
+ }
13189
+ }
12497
13190
  function rerenderAgentsMd(ctx, set) {
12498
13191
  const { installLog, projectDir, harnessRoot } = ctx;
12499
13192
  return runAgentsMdTransform({
@@ -12512,10 +13205,11 @@ function withRerender(next, r) {
12512
13205
  const files = mergeExternalFiles(next.externalFiles, r.ownership.files);
12513
13206
  if (files.length > 0) out.externalFiles = files;
12514
13207
  const block = r.agentsMd.shared;
12515
- if (block !== null && block.portions !== null) {
13208
+ const blockPortions = block !== null && block.portions !== null ? block.portions : r.agentsMd.blockRecordDropped === true ? [] : null;
13209
+ if (blockPortions !== null) {
12516
13210
  const portions = [
12517
13211
  ...(next.portions ?? []).filter((p2) => p2.path !== AGENTS_MD2),
12518
- ...block.portions
13212
+ ...blockPortions
12519
13213
  ];
12520
13214
  if (portions.length > 0) out.portions = portions;
12521
13215
  else delete out.portions;
@@ -12555,11 +13249,8 @@ function settleCliLog(ctx, target, remaining, done, anchorRemoved) {
12555
13249
  const survivors = (installLog.externalFiles ?? []).filter((f) => !done.forgotten.has(f.path));
12556
13250
  if (survivors.length > 0) next.externalFiles = survivors;
12557
13251
  else delete next.externalFiles;
12558
- const settled = new Set(done.displaced.filter((d2) => d2.forget).map((d2) => d2.path));
12559
13252
  if (installLog.rootFiles !== void 0) {
12560
- next.rootFiles = installLog.rootFiles.filter(
12561
- (f) => !(f.change === "displaced" && settled.has(f.path))
12562
- );
13253
+ next.rootFiles = withoutRestored(installLog.rootFiles, done.displaced);
12563
13254
  }
12564
13255
  const touched = new Set(done.shared.map((r) => r.path));
12565
13256
  const portions = [
@@ -12621,20 +13312,21 @@ var SKILLS_LOCK = "skills-lock.json";
12621
13312
  function withSkillsLock(rootFiles, assets, projectDir) {
12622
13313
  const out = [...rootFiles];
12623
13314
  const skills = assets.some((a) => a.method === "skill" && a.scope === "project");
12624
- if (skills && !out.some((f) => f.path === SKILLS_LOCK) && existsSync26(join34(projectDir, SKILLS_LOCK))) {
13315
+ if (skills && !out.some((f) => f.path === SKILLS_LOCK) && existsSync26(join35(projectDir, SKILLS_LOCK))) {
12625
13316
  out.push({ path: SKILLS_LOCK, change: "advisory", notes: [] });
12626
13317
  }
12627
13318
  return out;
12628
13319
  }
12629
- function remnantFor(path, log, harnessRoot, projectDir) {
13320
+ function remnantFor(path, log, harnessRoot, projectDir, wiringGone = []) {
12630
13321
  const clis = installedClis(log);
12631
13322
  if (path === SETTINGS_TARGET) {
13323
+ const gone = (k2) => wiringGone.includes(`hooks/${k2.split("#")[1] ?? ""}`);
12632
13324
  return (disk) => {
12633
13325
  try {
12634
- const template = readFileSync26(join34(harnessRoot, "templates", "settings.json"), "utf8");
13326
+ const template = readFileSync26(join35(harnessRoot, "templates", "settings.json"), "utf8");
12635
13327
  const render = renderSettingsPortion(template, () => true);
12636
- return [...legacySettingsSeed(disk, render, projectDir).keys()].filter(
12637
- (k2) => !k2.endsWith("{}")
13328
+ return [...legacySettingsSeed(disk, render, projectDir, { edited: true }).keys()].filter(
13329
+ (k2) => !k2.endsWith("{}") && !gone(k2)
12638
13330
  );
12639
13331
  } catch {
12640
13332
  return [];
@@ -12716,15 +13408,24 @@ function sharedStripLines(results, preview) {
12716
13408
  return id === null ? [] : [id.slice(id.indexOf(":") + 1)];
12717
13409
  };
12718
13410
  const names = (keys) => keys.flatMap(display).join(" \xB7 ");
13411
+ const stays = r.edited === void 0 ? " (yours stays)" : "";
12719
13412
  if (r.fileRemoved) {
13413
+ const only = (verb) => r.edited === void 0 ? ` and only its part ${verb} in it` : "";
12720
13414
  lines2.push(
12721
- preview ? ` \u25CB remove ${r.path} (the harness created it and only its part is in it)` : ` ${status.success(`${r.path} \u2014 removed (the harness created it and only its part was in it)`)}`
13415
+ preview ? ` \u25CB remove ${r.path} (the harness created it${only("is")})` : ` ${status.success(`${r.path} \u2014 removed (the harness created it${only("was")})`)}`
12722
13416
  );
12723
13417
  } else if (r.removed.length > 0) {
12724
13418
  const which = r.path === AGENTS_MD2 ? "" : `: ${names(r.removed)}`;
12725
13419
  const what = r.path === AGENTS_MD2 ? "the harness block" : "the harness part";
12726
13420
  lines2.push(
12727
- preview ? ` \u25CB remove ${what} from ${r.path}${which} (yours stays)` : ` ${status.success(`${r.path} \u2014 removed ${what}${which} (yours stays)`)}`
13421
+ preview ? ` \u25CB remove ${what} from ${r.path}${which}${stays}` : ` ${status.success(`${r.path} \u2014 removed ${what}${which}${stays}`)}`
13422
+ );
13423
+ }
13424
+ if (r.edited !== void 0) {
13425
+ const n = r.edited.keys.length;
13426
+ const edited = `you had edited ${n} harness hook line${n === 1 ? "" : "s"} (${names(r.edited.keys)})`;
13427
+ lines2.push(
13428
+ preview ? ` \u25CB ${r.path} \u2014 ${edited} \u2014 a copy is saved first as ${r.edited.backup}` : ` ${c.yellow("\u2298")} ${r.path} \u2014 ${edited} \u2014 your edit is in the copy: ${r.edited.backup}`
12728
13429
  );
12729
13430
  }
12730
13431
  if (r.kept.length > 0) {
@@ -12761,7 +13462,7 @@ function noReverseHints(asset, ctx) {
12761
13462
  }
12762
13463
  if (asset.method !== "skill") return [];
12763
13464
  const name = asset.detail.skill;
12764
- const left = name === void 0 ? [] : [".claude/skills", ".agents/skills"].map((root) => `${root}/${name}/`).filter((p2) => existsSync26(join34(ctx.projectDir, p2)));
13465
+ const left = name === void 0 ? [] : [".claude/skills", ".agents/skills"].map((root) => `${root}/${name}/`).filter((p2) => existsSync26(join35(ctx.projectDir, p2)));
12765
13466
  return [
12766
13467
  left.length > 0 ? `files it put down are not on record (installed by an older version) \u2014 left: ${left.join(" \xB7 ")} (delete by hand if the harness installed it)` : "files it put down are not on record (installed by an older version) \u2014 nothing removed"
12767
13468
  ];
@@ -12861,21 +13562,31 @@ function removeHarnessFiles(log, projectDir, scope, io) {
12861
13562
  forgotten: /* @__PURE__ */ new Set(),
12862
13563
  stripped: [],
12863
13564
  kept: [],
13565
+ legacyAgentsMd: [],
12864
13566
  unjudged: [],
12865
13567
  outside: [],
12866
13568
  displaced: [],
12867
13569
  shared: [],
12868
13570
  retiredWiring: [],
13571
+ legacyWiring: [],
12869
13572
  importStripped: false,
12870
13573
  anchor: { removed: false, lines: [] }
12871
13574
  };
12872
13575
  const pruneFrom = [];
12873
- stripSharedFiles(log, projectDir, scope.shared, io, out, pruneFrom);
12874
13576
  const isTarget = harnessTargets(log);
12875
- for (const path of harnessCandidates(log)) {
13577
+ const candidates = harnessCandidates(log);
13578
+ for (const path of candidates) {
12876
13579
  if (scope.has(path))
12877
13580
  removeRecordedFile(path, { log, projectDir, io, out, isTarget, pruneFrom });
12878
13581
  }
13582
+ const judged = new Set(candidates);
13583
+ for (const path of legacyFileCandidates()) {
13584
+ if (judged.has(path) || !scope.has(path) || !safeExists(join35(projectDir, path))) continue;
13585
+ if (recorded(log, path, { isTarget }).state !== "none") continue;
13586
+ removeRecordedFile(path, { log, projectDir, io, out, isTarget, pruneFrom });
13587
+ }
13588
+ out.legacyWiring = removeLegacyHookWiring(log, projectDir, scope, io, out.removed);
13589
+ stripSharedFiles(log, projectDir, scope.shared, io, out, pruneFrom);
12879
13590
  if (scope.importBlock) {
12880
13591
  out.importStripped = io.write ? stripRootImport(projectDir) : hasRootImport(projectDir);
12881
13592
  }
@@ -12884,11 +13595,19 @@ function removeHarnessFiles(log, projectDir, scope, io) {
12884
13595
  const anchorPath = log.templates?.rootClaudeMd?.path;
12885
13596
  if (out.anchor.removed && anchorPath !== void 0) out.removedAnchor = anchorPath;
12886
13597
  }
12887
- restoreDisplaced(log, projectDir, scope, io, out);
13598
+ out.displaced = restoreDisplaced(log, projectDir, {
13599
+ has: scope.has,
13600
+ willBeGone: /* @__PURE__ */ new Set([
13601
+ ...out.removed,
13602
+ ...out.removedAnchor === void 0 ? [] : [out.removedAnchor],
13603
+ ...io.assetFilesGone ?? []
13604
+ ]),
13605
+ write: io.write
13606
+ });
12888
13607
  if (io.write) {
12889
- for (const p2 of pruneFrom) pruneEmptyDirsUpward(projectDir, dirname7(join34(projectDir, p2)));
13608
+ for (const p2 of pruneFrom) pruneEmptyDirsUpward(projectDir, dirname8(join35(projectDir, p2)));
12890
13609
  for (const d2 of scope.skeleton) {
12891
- const abs = join34(projectDir, d2);
13610
+ const abs = join35(projectDir, d2);
12892
13611
  if (outsideProjectTarget(projectDir, abs) !== null) continue;
12893
13612
  pruneEmptyDirsUpward(projectDir, abs);
12894
13613
  }
@@ -12899,7 +13618,10 @@ function harnessCandidates(log) {
12899
13618
  const paths = [
12900
13619
  ...(log.policyFiles ?? []).map((f) => `${CLAUDE_DIR3}${f.path}`),
12901
13620
  ...(log.skillFiles ?? []).map((f) => `${SKILLS_DIR3}${f.path}`),
12902
- ...(log.externalFiles ?? []).map((f) => f.path).filter((p2) => !p2.startsWith(`${INSTALL_LOG_DIR}/`)),
13621
+ ...(log.externalFiles ?? []).map((f) => f.path),
13622
+ // 헬퍼 — writer 기록은 `externalFiles` 에 sha 로 적지만 옛 기록(≤v26.161.0)엔 항목이 없다. `recorded()` 가 가른다
13623
+ // (writer 기록에 없으면 `none` = 설치자 파일 · 옛 기록이면 `no-sha` = 번들 판과 같을 때만 백업 없이)
13624
+ ...helperSources().keys(),
12903
13625
  // 어느 필드에도 적힌 적 없는 옛 판의 하네스 파일 — `isTarget` 이 알려 준다(A8 · install 과 같은 방식)
12904
13626
  INSTALLED_TRACKS,
12905
13627
  // 스캔 필드가 아예 없는 옛 로그(v26.125.0 이하)는 필드가 하네스 파일을 열거하지 못한다 — manifest 의 하네스 자리 ·
@@ -12913,18 +13635,25 @@ function harnessTargets(log) {
12913
13635
  const tracked = recordedTrackTargets(log);
12914
13636
  return (p2) => p2 === INSTALLED_TRACKS || tracked(p2);
12915
13637
  }
13638
+ function helperSources() {
13639
+ return new Map(
13640
+ buildManifest({ tracks: [] }).filter((e2) => isCliNeutralTarget(e2.target)).map((e2) => [e2.target, e2.source])
13641
+ );
13642
+ }
12916
13643
  function templateNext(harnessRoot, log, path) {
12917
13644
  if (path === INSTALLED_TRACKS) return installedTracksText(log.spec.tracks.filter(isTrack));
12918
- if (!path.startsWith(CLAUDE_DIR3)) return null;
13645
+ const helper = helperSources().get(path);
13646
+ if (helper === void 0 && !path.startsWith(CLAUDE_DIR3)) return null;
12919
13647
  try {
12920
- return readFileSync26(join34(harnessRoot, "templates", path.slice(CLAUDE_DIR3.length)), "utf8");
13648
+ const source = helper ?? path.slice(CLAUDE_DIR3.length);
13649
+ return readFileSync26(join35(harnessRoot, "templates", source), "utf8");
12921
13650
  } catch {
12922
13651
  return null;
12923
13652
  }
12924
13653
  }
12925
13654
  function removeRecordedFile(path, ctx) {
12926
13655
  const { log, projectDir, io, out } = ctx;
12927
- const abs = join34(projectDir, path);
13656
+ const abs = join35(projectDir, path);
12928
13657
  let foreign;
12929
13658
  try {
12930
13659
  foreign = foreignOwnedTarget(projectDir, path);
@@ -12966,17 +13695,20 @@ function removeRecordedFile(path, ctx) {
12966
13695
  out.unjudged.push(path);
12967
13696
  return;
12968
13697
  }
13698
+ const rec = recorded(log, path, { isTarget: ctx.isTarget });
13699
+ const legacy = rec.state !== "sha" && isLegacyHarnessFile(path, hashContent(disk)) && (rec.state === "no-sha" || mayReclaimLegacy(log, path));
13700
+ const inBundle = legacy && ctx.isTarget(bundlePath(path));
12969
13701
  const verdict = judge({
12970
13702
  op: "remove",
12971
13703
  kind: "harness",
12972
- rec: recorded(log, path, { isTarget: ctx.isTarget }),
13704
+ rec: legacy ? { state: "no-sha" } : rec,
12973
13705
  disk,
12974
- next: templateNext(io.harnessRoot, log, path)
13706
+ next: legacy ? inBundle ? disk : null : templateNext(io.harnessRoot, log, path)
12975
13707
  });
12976
13708
  if (verdict.verdict !== "remove" && verdict.verdict !== "backup+remove") return;
12977
13709
  let backup = null;
12978
13710
  if (verdict.verdict === "backup+remove") {
12979
- backup = io.write ? relative5(projectDir, backupFile(effective)).split(sep5).join("/") : `${relative5(projectDir, effective).split(sep5).join("/")}.backup-<time>`;
13711
+ backup = io.write ? relative7(projectDir, backupFile(effective)).split(sep7).join("/") : `${relative7(projectDir, effective).split(sep7).join("/")}.backup-<time>`;
12980
13712
  }
12981
13713
  if (io.write) {
12982
13714
  if (effective !== abs) io.rm(abs);
@@ -12985,7 +13717,34 @@ function removeRecordedFile(path, ctx) {
12985
13717
  out.removed.push(path);
12986
13718
  out.forgotten.add(path);
12987
13719
  ctx.pruneFrom.push(path);
12988
- if (backup !== null) out.backedUp.push({ path, backup, line: verdict.line });
13720
+ if (backup !== null)
13721
+ out.backedUp.push({ path, backup, line: legacy && !inBundle ? LEGACY_SAVED : verdict.line });
13722
+ }
13723
+ var LEGACY_SAVED = "older harness version (byte-identical, not in the current bundle) \u2014 saved a copy, removed from live";
13724
+ function legacyFileCandidates() {
13725
+ return [...LEGACY_HARNESS_FILES.keys()].flatMap(
13726
+ (p2) => p2.startsWith(SKILLS_DIR3) ? [p2, `.agents/skills/${p2.slice(SKILLS_DIR3.length)}`] : [p2]
13727
+ );
13728
+ }
13729
+ function bundlePath(path) {
13730
+ return path.startsWith(".agents/skills/") ? `${SKILLS_DIR3}${path.slice(".agents/skills/".length)}` : path;
13731
+ }
13732
+ function mayReclaimLegacy(log, path) {
13733
+ const clis = installedClis(log);
13734
+ if (!ownersOf(path).some((c3) => clis.includes(c3))) return false;
13735
+ if (isExcludedPath(bundlePath(path), excludedIds(log))) return false;
13736
+ return !(log.rootFiles ?? []).some((f) => f.path === path && f.change === "displaced");
13737
+ }
13738
+ function removeLegacyHookWiring(log, projectDir, scope, io, removed) {
13739
+ if (!scope.has(SETTINGS_TARGET) || !installedClis(log).includes("claude")) return [];
13740
+ if ((log.portions ?? []).some((p2) => p2.path === SETTINGS_TARGET)) return [];
13741
+ const abs = join35(projectDir, SETTINGS_TARGET);
13742
+ if (!safeExists(abs) || outsideProjectTarget(projectDir, abs) !== null) return [];
13743
+ const claudeDir = join35(projectDir, ".claude");
13744
+ const gone = LEGACY_HOOK_REFS.filter(
13745
+ (r) => !RETIRED_HOOK_WIRING_REFS.includes(r) && (removed.includes(`${CLAUDE_DIR3}${r}`) || !safeExists(join35(claudeDir, r)))
13746
+ );
13747
+ return removeRetiredHookRefs(abs, claudeDir, gone, { evenIfScriptExists: true, write: io.write });
12989
13748
  }
12990
13749
  function stripSharedFiles(log, projectDir, paths, io, out, pruneFrom) {
12991
13750
  const made = new Map((log.externalFiles ?? []).map((f) => [f.path, f.sha256]));
@@ -13004,7 +13763,7 @@ function stripSharedFiles(log, projectDir, paths, io, out, pruneFrom) {
13004
13763
  };
13005
13764
  for (const path of paths) {
13006
13765
  if (!relevant(path)) continue;
13007
- const abs = join34(projectDir, path);
13766
+ const abs = join35(projectDir, path);
13008
13767
  if (safeExists(abs)) {
13009
13768
  const o = outsideProjectTarget(projectDir, abs);
13010
13769
  if (o !== null) {
@@ -13015,7 +13774,7 @@ function stripSharedFiles(log, projectDir, paths, io, out, pruneFrom) {
13015
13774
  if (path === SETTINGS_TARGET && safeExists(abs)) {
13016
13775
  out.retiredWiring = removeRetiredHookRefs(
13017
13776
  abs,
13018
- join34(projectDir, ".claude"),
13777
+ join35(projectDir, ".claude"),
13019
13778
  RETIRED_HOOK_WIRING_REFS,
13020
13779
  { evenIfScriptExists: true, write: io.write }
13021
13780
  );
@@ -13024,12 +13783,15 @@ function stripSharedFiles(log, projectDir, paths, io, out, pruneFrom) {
13024
13783
  if (sha !== void 0 && removeHarnessMadeShared(path, sha, projectDir, io, out, pruneFrom)) {
13025
13784
  continue;
13026
13785
  }
13786
+ if (path === AGENTS_MD2 && sha === void 0) {
13787
+ if (removeUnrecordedLegacyAgentsMd(path, projectDir, io, out, pruneFrom)) continue;
13788
+ }
13027
13789
  const r = stripShared({
13028
13790
  projectDir,
13029
13791
  path,
13030
13792
  portions: log.portions ?? [],
13031
13793
  excluded: [...excludedIds(log)],
13032
- remnant: remnantFor(path, log, io.harnessRoot, projectDir),
13794
+ remnant: remnantFor(path, log, io.harnessRoot, projectDir, out.legacyWiring),
13033
13795
  remnantLine: remnantLine(path),
13034
13796
  write: io.write,
13035
13797
  // 하네스가 만든 파일(#569 `created` · #13 `externalFiles`)은 몫을 걷고 남는 것이 없으면 파일째 지운다
@@ -13040,7 +13802,7 @@ function stripSharedFiles(log, projectDir, paths, io, out, pruneFrom) {
13040
13802
  }
13041
13803
  }
13042
13804
  function removeHarnessMadeShared(path, sha, projectDir, io, out, pruneFrom) {
13043
- const abs = join34(projectDir, path);
13805
+ const abs = join35(projectDir, path);
13044
13806
  if (!existsSync26(abs)) {
13045
13807
  out.forgotten.add(path);
13046
13808
  return true;
@@ -13061,7 +13823,15 @@ function removeHarnessMadeShared(path, sha, projectDir, io, out, pruneFrom) {
13061
13823
  }
13062
13824
  if (hashContent(current) !== sha) {
13063
13825
  if (path === AGENTS_MD2) {
13064
- out.kept.push(path);
13826
+ const verdict = isLegacyHarnessAgentsMd(current) ? agentsMdRemainder(current, io.harnessRoot) : null;
13827
+ if (verdict?.ok === true && verdict.strip.legacyRemoved) {
13828
+ removeLegacyAgentsMd(path, abs, effective, verdict.strip, {
13829
+ projectDir,
13830
+ io,
13831
+ out,
13832
+ pruneFrom
13833
+ });
13834
+ } else out.kept.push(path);
13065
13835
  return true;
13066
13836
  }
13067
13837
  out.forgotten.add(path);
@@ -13073,9 +13843,13 @@ function removeHarnessMadeShared(path, sha, projectDir, io, out, pruneFrom) {
13073
13843
  out.unjudged.push(path);
13074
13844
  return true;
13075
13845
  }
13846
+ if (verdict.strip.legacyRemoved) {
13847
+ removeLegacyAgentsMd(path, abs, effective, verdict.strip, { projectDir, io, out, pruneFrom });
13848
+ return true;
13849
+ }
13076
13850
  if (verdict.remainder !== null) {
13077
13851
  try {
13078
- if (io.write) writeFileSync10(effective, neutralizeScaffoldBanner(verdict.remainder), "utf8");
13852
+ if (io.write) writeFileSync9(effective, neutralizeScaffoldBanner(verdict.remainder), "utf8");
13079
13853
  out.stripped.push(path);
13080
13854
  out.forgotten.add(path);
13081
13855
  } catch {
@@ -13101,38 +13875,44 @@ function removeHarnessMadeShared(path, sha, projectDir, io, out, pruneFrom) {
13101
13875
  pruneFrom.push(path);
13102
13876
  return true;
13103
13877
  }
13104
- function restoreDisplaced(log, projectDir, scope, io, out) {
13105
- const willBeGone = /* @__PURE__ */ new Set([
13106
- ...out.removed,
13107
- ...out.removedAnchor === void 0 ? [] : [out.removedAnchor]
13108
- ]);
13109
- for (const f of log.rootFiles ?? []) {
13110
- if (f.change !== "displaced" || !scope.has(f.path)) continue;
13111
- const slot = join34(projectDir, f.path);
13112
- if (outsideProjectTarget(projectDir, slot) !== null) continue;
13113
- const backup = f.notes[0] ?? null;
13114
- const d2 = judgeDisplaced({
13115
- backup,
13116
- backupExists: backup !== null && safeExists(join34(projectDir, backup)),
13117
- slotEmpty: !safeExists(slot) || !io.write && willBeGone.has(f.path)
13118
- });
13119
- if (io.write && d2.verdict === "restore" && backup !== null) {
13120
- mkdirSync7(dirname7(slot), { recursive: true });
13121
- renameSync(join34(projectDir, backup), slot);
13122
- }
13123
- out.displaced.push({
13124
- path: f.path,
13125
- backup,
13126
- verdict: d2.verdict,
13127
- line: d2.line,
13128
- forget: d2.record === "forget"
13129
- });
13130
- }
13131
- }
13132
13878
  function topOf(path) {
13133
13879
  const i = path.indexOf("/");
13134
13880
  return i < 0 ? path : `${path.slice(0, i)}/`;
13135
13881
  }
13882
+ function displacedLines(displaced, preview) {
13883
+ const lines2 = [];
13884
+ const restore = displaced.filter((d2) => d2.verdict === "restore");
13885
+ if (preview && restore.length > 0) {
13886
+ lines2.push(
13887
+ ` \u25CB put back ${restore.length} file(s) of yours (displaced): ${restore.map((d2) => d2.path).join(" \xB7 ")}`
13888
+ );
13889
+ }
13890
+ for (const d2 of displaced) {
13891
+ if (d2.verdict === "restore" && !preview) {
13892
+ lines2.push(` ${status.success(`put back ${d2.path} (from ${d2.backup})`)}`);
13893
+ } else if (d2.verdict === "advise") {
13894
+ lines2.push(` ${c.yellow("\u2298")} ${d2.path} \u2014 ${d2.line}`);
13895
+ }
13896
+ }
13897
+ return lines2;
13898
+ }
13899
+ function plannedAssets(plan, log) {
13900
+ const ids = new Set(plan.reverseSteps.map((s) => s.assetId));
13901
+ return log.assets.filter((a) => ids.has(a.id));
13902
+ }
13903
+ function plannedAssetFiles(plan, log) {
13904
+ return plannedAssets(plan, log).flatMap(
13905
+ (a) => a.method === "skill" && a.scope === "project" ? (a.files ?? []).map((f) => f.path) : []
13906
+ );
13907
+ }
13908
+ function inAssetSlots(assets) {
13909
+ const slots = new Set(
13910
+ assets.flatMap(
13911
+ (a) => a.method === "skill" && a.scope === "project" ? (a.files ?? []).map((f) => `${f.path.split("/").slice(0, 3).join("/")}/`) : []
13912
+ )
13913
+ );
13914
+ return (path) => [...slots].some((s) => path.startsWith(s));
13915
+ }
13136
13916
  function harnessRemovalLines(r, log, projectDir, scope, preview) {
13137
13917
  const lines2 = [];
13138
13918
  const groups = /* @__PURE__ */ new Map();
@@ -13147,7 +13927,7 @@ function harnessRemovalLines(r, log, projectDir, scope, preview) {
13147
13927
  }
13148
13928
  if (preview) {
13149
13929
  for (const [top, g] of groups) {
13150
- const edited = g.backedUp > 0 ? ` (${g.backedUp} you edited \u2192 saved beside as *.backup-<time>)` : "";
13930
+ const edited = g.backedUp > 0 ? ` (${g.backedUp} with a copy saved beside as *.backup-<time>)` : "";
13151
13931
  lines2.push(` \u25CB remove ${g.removed} harness file(s) under ${top}${edited}`);
13152
13932
  }
13153
13933
  } else if (groups.size > 0) {
@@ -13166,24 +13946,24 @@ function harnessRemovalLines(r, log, projectDir, scope, preview) {
13166
13946
  );
13167
13947
  }
13168
13948
  lines2.push(...r.anchor.lines);
13169
- const restore = r.displaced.filter((d2) => d2.verdict === "restore");
13170
- if (preview && restore.length > 0) {
13171
- lines2.push(
13172
- ` \u25CB put back ${restore.length} file(s) of yours (displaced): ${restore.map((d2) => d2.path).join(" \xB7 ")}`
13173
- );
13174
- }
13175
- for (const d2 of r.displaced) {
13176
- if (d2.verdict === "restore" && !preview) {
13177
- lines2.push(` ${status.success(`put back ${d2.path} (from ${d2.backup})`)}`);
13178
- } else if (d2.verdict === "advise") {
13179
- lines2.push(` ${c.yellow("\u2298")} ${d2.path} \u2014 ${d2.line}`);
13180
- }
13181
- }
13949
+ lines2.push(...displacedLines(r.displaced, preview));
13182
13950
  for (const path of r.stripped) {
13183
13951
  lines2.push(
13184
13952
  preview ? ` \u25CB strip harness sections from ${path} (\uBCF8\uBB38 \uBCF4\uC874)` : ` ${status.success(`${path} \u2014 harness sections removed (\uBCF8\uBB38 \uBCF4\uC874)`)}`
13185
13953
  );
13186
13954
  }
13955
+ for (const l of r.legacyAgentsMd) {
13956
+ const kept = l.keptEdited.length > 0 ? ` \xB7 kept ${l.keptEdited.length} section(s) you edited (${l.keptEdited.join(" \xB7 ")})` : "";
13957
+ if (preview) {
13958
+ lines2.push(
13959
+ l.fileRemoved ? ` \u25CB remove ${l.path} (written by an older harness version \u2014 a copy is saved first)` : ` \u25CB remove the harness sections written by an older version from ${l.path} \u2014 yours stays${kept} (a copy is saved first)`
13960
+ );
13961
+ } else {
13962
+ lines2.push(
13963
+ l.fileRemoved ? ` ${status.success(`removed ${l.path} (written by an older harness version \u2014 saved a copy: ${l.backup})`)}` : ` ${status.success(`${l.path} \u2014 removed the harness sections written by an older version \u2014 yours stays${kept} (saved a copy: ${l.backup})`)}`
13964
+ );
13965
+ }
13966
+ }
13187
13967
  for (const path of r.unjudged) {
13188
13968
  lines2.push(
13189
13969
  path === AGENTS_MD2 ? ` ${c.yellow("\u2298")} ${path} kept \u2014 harness sections could not be separated (a file or template could not be read, or the write failed). Remove manually if intended.` : ` ${c.yellow("\u2298")} ${path} left \u2014 could not read it to judge. Remove manually if intended.`
@@ -13195,6 +13975,12 @@ function harnessRemovalLines(r, log, projectDir, scope, preview) {
13195
13975
  ` ${c.yellow("\u2298")} ${path} kept \u2014 modified since install. Remove manually if intended.`
13196
13976
  );
13197
13977
  }
13978
+ if (r.legacyWiring.length > 0) {
13979
+ const refs = r.legacyWiring.join(" \xB7 ");
13980
+ lines2.push(
13981
+ preview ? ` \u25CB remove harness hook wiring from an older version (script gone) from ${SETTINGS_TARGET}: ${refs}` : ` ${status.success(`${SETTINGS_TARGET} \u2014 removed harness hook wiring from an older version (script gone): ${refs}`)}`
13982
+ );
13983
+ }
13198
13984
  if (r.retiredWiring.length > 0) {
13199
13985
  const refs = r.retiredWiring.join(" \xB7 ");
13200
13986
  lines2.push(
@@ -13245,25 +14031,25 @@ function yoursUnder(projectDir, dir) {
13245
14031
  const walk2 = (abs2, prefix) => {
13246
14032
  let entries;
13247
14033
  try {
13248
- entries = readdirSync6(abs2, { withFileTypes: true });
14034
+ entries = readdirSync7(abs2, { withFileTypes: true });
13249
14035
  } catch {
13250
14036
  return;
13251
14037
  }
13252
14038
  for (const e2 of entries) {
13253
14039
  const name = `${prefix}${e2.name}`;
13254
- if (e2.isDirectory()) walk2(join34(abs2, e2.name), `${name}/`);
14040
+ if (e2.isDirectory()) walk2(join35(abs2, e2.name), `${name}/`);
13255
14041
  else if (BACKUP_SUFFIX.test(e2.name)) continue;
13256
14042
  else if (e2.isSymbolicLink()) {
13257
14043
  let target = "?";
13258
14044
  try {
13259
- target = readlinkSync(join34(abs2, e2.name));
14045
+ target = readlinkSync(join35(abs2, e2.name));
13260
14046
  } catch {
13261
14047
  }
13262
14048
  out.push({ name, shown: `${name} (your link \u2192 ${target})` });
13263
14049
  } else out.push({ name, shown: name });
13264
14050
  }
13265
14051
  };
13266
- const abs = join34(projectDir, dir);
14052
+ const abs = join35(projectDir, dir);
13267
14053
  try {
13268
14054
  if (!statSync5(abs).isDirectory()) return [];
13269
14055
  } catch {
@@ -13275,7 +14061,7 @@ function yoursUnder(projectDir, dir) {
13275
14061
  function assetsLeftLines(log, projectDir, scope) {
13276
14062
  const lines2 = [];
13277
14063
  for (const a of log.assets) {
13278
- const here = (a.files ?? []).map((f) => f.path).filter((p2) => scope.has(p2) && safeExists(join34(projectDir, p2)));
14064
+ const here = (a.files ?? []).map((f) => f.path).filter((p2) => scope.has(p2) && safeExists(join35(projectDir, p2)));
13279
14065
  if (here.length === 0) continue;
13280
14066
  const slots = [...new Set(here.map((p2) => p2.split("/").slice(0, 3).join("/")))].sort();
13281
14067
  lines2.push(
@@ -13287,7 +14073,7 @@ function assetsLeftLines(log, projectDir, scope) {
13287
14073
  function removeAnchor(log, projectDir, io) {
13288
14074
  const rootMd = log.templates?.rootClaudeMd;
13289
14075
  if (!rootMd) return { removed: false, lines: [] };
13290
- const abs = join34(projectDir, rootMd.path);
14076
+ const abs = join35(projectDir, rootMd.path);
13291
14077
  if (!existsSync26(abs)) return { removed: false, lines: [] };
13292
14078
  const outside = outsideProjectTarget(projectDir, abs);
13293
14079
  if (outside !== null) return { removed: false, lines: outsideLines([outside], log) };
@@ -13326,7 +14112,7 @@ function removeAnchor(log, projectDir, io) {
13326
14112
  ]
13327
14113
  };
13328
14114
  }
13329
- const backup = edited ? relative5(projectDir, backupFile(abs)).split(sep5).join("/") : null;
14115
+ const backup = edited ? relative7(projectDir, backupFile(abs)).split(sep7).join("/") : null;
13330
14116
  io.rm(abs);
13331
14117
  return {
13332
14118
  removed: true,
@@ -13338,7 +14124,7 @@ function removeAnchor(log, projectDir, io) {
13338
14124
  var AGENTS_MD2 = "AGENTS.md";
13339
14125
  function readAgentsMdTemplate(harnessRoot) {
13340
14126
  try {
13341
- return readFileSync26(join34(harnessRoot, AGENTS_MD_TEMPLATE), "utf8");
14127
+ return readFileSync26(join35(harnessRoot, AGENTS_MD_TEMPLATE), "utf8");
13342
14128
  } catch {
13343
14129
  return null;
13344
14130
  }
@@ -13356,21 +14142,67 @@ function neutralizeScaffoldBanner(text) {
13356
14142
  function agentsMdRemainder(current, harnessRoot) {
13357
14143
  const template = readAgentsMdTemplate(harnessRoot);
13358
14144
  if (template === null) return { ok: false };
13359
- return {
13360
- ok: true,
13361
- remainder: stripHarnessFromAgentsMd({
13362
- existing: current,
13363
- template,
13364
- harnessRuleHeadings: ruleHeadings(harnessRoot)
13365
- })
13366
- };
14145
+ const strip = stripAgentsMd({
14146
+ existing: current,
14147
+ template,
14148
+ harnessRuleHeadings: ruleHeadings(harnessRoot)
14149
+ });
14150
+ return { ok: true, remainder: strip.remainder, strip };
14151
+ }
14152
+ function removeUnrecordedLegacyAgentsMd(path, projectDir, io, out, pruneFrom) {
14153
+ const abs = join35(projectDir, path);
14154
+ let effective = abs;
14155
+ let current;
14156
+ try {
14157
+ const real = realpathSync3(abs);
14158
+ if (!statSync5(real).isFile()) return false;
14159
+ if (lstatSync5(abs).isSymbolicLink()) effective = real;
14160
+ current = readFileSync26(effective, "utf8");
14161
+ } catch {
14162
+ return false;
14163
+ }
14164
+ if (!isLegacyHarnessAgentsMd(current)) return false;
14165
+ const verdict = agentsMdRemainder(current, io.harnessRoot);
14166
+ if (!verdict.ok) {
14167
+ out.unjudged.push(path);
14168
+ return true;
14169
+ }
14170
+ removeLegacyAgentsMd(path, abs, effective, verdict.strip, { projectDir, io, out, pruneFrom });
14171
+ return true;
14172
+ }
14173
+ function removeLegacyAgentsMd(path, abs, effective, strip, ctx) {
14174
+ const { projectDir, io, out } = ctx;
14175
+ let backup = `${path}.backup-<time>`;
14176
+ try {
14177
+ if (io.write) {
14178
+ backup = relative7(projectDir, backupFile(effective)).split(sep7).join("/");
14179
+ if (strip.remainder !== null) {
14180
+ writeFileSync9(effective, neutralizeScaffoldBanner(strip.remainder), "utf8");
14181
+ } else {
14182
+ if (effective !== abs) io.rm(abs);
14183
+ io.rm(effective);
14184
+ }
14185
+ }
14186
+ } catch {
14187
+ out.unjudged.push(path);
14188
+ return;
14189
+ }
14190
+ if (strip.remainder === null) ctx.pruneFrom.push(path);
14191
+ out.legacyAgentsMd.push({
14192
+ path,
14193
+ backup,
14194
+ fileRemoved: strip.remainder === null,
14195
+ keptEdited: strip.keptEdited
14196
+ });
14197
+ out.forgotten.add(path);
14198
+ out.shared.push({ path, action: "none", line: "", removed: [], kept: [], portions: [] });
13367
14199
  }
13368
14200
  function removeToolFiles(files, projectDir, rm) {
13369
14201
  const notes = [];
13370
14202
  const removed = [];
13371
14203
  let failed = 0;
13372
14204
  for (const { path, sha256 } of files) {
13373
- const abs = join34(projectDir, path);
14205
+ const abs = join35(projectDir, path);
13374
14206
  let isLink;
13375
14207
  try {
13376
14208
  isLink = lstatSync5(abs).isSymbolicLink();
@@ -13389,7 +14221,7 @@ function removeToolFiles(files, projectDir, rm) {
13389
14221
  try {
13390
14222
  if (hashContent(readFileSync26(abs, "utf8")) !== sha256) {
13391
14223
  const backup = backupFile(abs);
13392
- notes.push(`backed up ${path} \u2192 ${relative5(projectDir, backup)} (changed since install)`);
14224
+ notes.push(`backed up ${path} \u2192 ${relative7(projectDir, backup)} (changed since install)`);
13393
14225
  }
13394
14226
  rm(abs);
13395
14227
  removed.push(path);
@@ -13398,21 +14230,21 @@ function removeToolFiles(files, projectDir, rm) {
13398
14230
  notes.push(`left ${path} \u2014 ${e2 instanceof Error ? e2.message : String(e2)}`);
13399
14231
  }
13400
14232
  }
13401
- for (const path of removed) pruneEmptyDirsUpward(projectDir, dirname7(join34(projectDir, path)));
14233
+ for (const path of removed) pruneEmptyDirsUpward(projectDir, dirname8(join35(projectDir, path)));
13402
14234
  return failed > 0 ? { ok: false, message: `${failed} file(s) could not be removed`, notes } : { ok: true, notes };
13403
14235
  }
13404
14236
  function pruneEmptyDirsUpward(projectDir, startDir) {
13405
14237
  let cursor = startDir;
13406
14238
  while (true) {
13407
- const rel = relative5(projectDir, cursor);
13408
- if (rel === "" || rel.startsWith(`..${sep5}`) || rel === "..") return;
14239
+ const rel = relative7(projectDir, cursor);
14240
+ if (rel === "" || rel.startsWith(`..${sep7}`) || rel === "..") return;
13409
14241
  try {
13410
- if (readdirSync6(cursor).length > 0) return;
13411
- rmdirSync2(cursor);
14242
+ if (readdirSync7(cursor).length > 0) return;
14243
+ rmdirSync3(cursor);
13412
14244
  } catch {
13413
14245
  return;
13414
14246
  }
13415
- cursor = dirname7(cursor);
14247
+ cursor = dirname8(cursor);
13416
14248
  }
13417
14249
  }
13418
14250
  function stripRootImport(projectDir) {
@@ -13420,8 +14252,8 @@ function stripRootImport(projectDir) {
13420
14252
  const stripped = current === null ? null : stripHarnessImport(current);
13421
14253
  if (stripped === null) return false;
13422
14254
  try {
13423
- backupIfLossyUtf8(join34(projectDir, "CLAUDE.md"));
13424
- writeFileSync10(join34(projectDir, "CLAUDE.md"), stripped, "utf8");
14255
+ backupIfLossyUtf8(join35(projectDir, "CLAUDE.md"));
14256
+ writeFileSync9(join35(projectDir, "CLAUDE.md"), stripped, "utf8");
13425
14257
  return true;
13426
14258
  } catch {
13427
14259
  return false;
@@ -13432,7 +14264,7 @@ function hasRootImport(projectDir) {
13432
14264
  return current !== null && stripHarnessImport(current) !== null;
13433
14265
  }
13434
14266
  function readRootClaudeMd(projectDir) {
13435
- const path = join34(projectDir, "CLAUDE.md");
14267
+ const path = join35(projectDir, "CLAUDE.md");
13436
14268
  if (!existsSync26(path)) return null;
13437
14269
  try {
13438
14270
  return readFileSync26(path, "utf8");
@@ -13445,7 +14277,7 @@ function defaultSpawn2(cmd, args) {
13445
14277
  }
13446
14278
  function defaultRm(path) {
13447
14279
  if (existsSync26(path)) {
13448
- rmSync4(path, { recursive: true, force: true });
14280
+ rmSync5(path, { recursive: true, force: true });
13449
14281
  }
13450
14282
  }
13451
14283
  function registerUninstallCommand(cli) {