@piecemaker-legal/piecemaker 2.0.9 → 2.0.11

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.
Files changed (177) hide show
  1. package/dist/assets/FindReplaceDialog-KU5TEIBJ-BucOHLdm.js +1 -0
  2. package/dist/assets/FootnotePropertiesDialog-W3YHM4BV-D5UqbelV.js +1 -0
  3. package/dist/assets/HyperlinkDialog-ZYH7ZHRF-8yEhLigd.js +144 -0
  4. package/dist/assets/HyperlinkDialog-ZYH7ZHRF-qIPBOssF.css +1 -0
  5. package/dist/assets/ImagePositionDialog-HEWYR6Q3-wGIBYxCm.js +1 -0
  6. package/dist/assets/ImagePropertiesDialog-RAB47C6M-D86PckZ2.js +1 -0
  7. package/dist/assets/PageSetupDialog-AIGIXO3E-DLETP-7v.js +1 -0
  8. package/dist/assets/SplitCellDialog-IFBKYCD6-CwdQBXeb.js +1 -0
  9. package/dist/assets/TablePropertiesDialog-TMLKGOOW-j2nA7KJs.js +1 -0
  10. package/dist/assets/WatermarkDialog-KRL7WMIS-XFJ9gTkC.js +1 -0
  11. package/dist/assets/{abnfDiagram-VCTEODGH-WrsSAHOe.js → abnfDiagram-VCTEODGH-DiCrND_Y.js} +1 -1
  12. package/dist/assets/arc-DOjgbMYz.js +1 -0
  13. package/dist/assets/architectureDiagram-5GKGNRK7-brzIFVhl.js +36 -0
  14. package/dist/assets/{blockDiagram-I7D4REHJ-zGMnF7Ox.js → blockDiagram-I7D4REHJ-C8JhTjQQ.js} +1 -1
  15. package/dist/assets/{c4Diagram-7LVT6UL2-ClWw3bqt.js → c4Diagram-7LVT6UL2-jfYxccyk.js} +1 -1
  16. package/dist/assets/channel-Dajzu2W1.js +1 -0
  17. package/dist/assets/{chunk-2Q5K7J3B-C-9ICPHv.js → chunk-2Q5K7J3B-CrlEShp9.js} +1 -1
  18. package/dist/assets/{chunk-5VM5RSS4-Bawy3teT.js → chunk-5VM5RSS4-DzPlUHGq.js} +1 -1
  19. package/dist/assets/{chunk-F27PBJKO-DQ375RQO.js → chunk-F27PBJKO-Cp9cpeWU.js} +1 -1
  20. package/dist/assets/{chunk-IMKFNOWR-CflNF2f6.js → chunk-IMKFNOWR-BifUldEN.js} +1 -1
  21. package/dist/assets/{chunk-JWPE2WC7-CIXQv0gc.js → chunk-JWPE2WC7-ggPEIkWu.js} +1 -1
  22. package/dist/assets/{chunk-POPQ4Y6H-Cza5j6Qw.js → chunk-POPQ4Y6H-3h_nayRX.js} +1 -1
  23. package/dist/assets/{chunk-SVP7TREG-GQ_VvWZR.js → chunk-SVP7TREG-CH29aG6J.js} +1 -1
  24. package/dist/assets/{chunk-TICWLB2K-BEmU16W5.js → chunk-TICWLB2K-PluJUM5k.js} +1 -1
  25. package/dist/assets/{chunk-XXDRQBXY-C1KpkLYH.js → chunk-XXDRQBXY-DXU39Vwg.js} +1 -1
  26. package/dist/assets/classDiagram-ZZMXUADV-dDEP_giv.js +1 -0
  27. package/dist/assets/classDiagram-v2-VYDZK3BY-dDEP_giv.js +1 -0
  28. package/dist/assets/{cose-bilkent-JH36ORCC-CZE48TRG.js → cose-bilkent-JH36ORCC-Dz6roYrN.js} +1 -1
  29. package/dist/assets/{cynefin-OW5HDTMX-L-tx1eC-.js → cynefin-OW5HDTMX-BLzrpSit.js} +1 -1
  30. package/dist/assets/{cynefinDiagram-5FMLGOSQ-Coa8oJwR.js → cynefinDiagram-5FMLGOSQ-kfxnERDZ.js} +1 -1
  31. package/dist/assets/{dagre-GXQ25YYZ-Bu_3Y75K.js → dagre-GXQ25YYZ-DP57bp-N.js} +1 -1
  32. package/dist/assets/{diagram-S7CK7UJ4-CFiiV4es.js → diagram-S7CK7UJ4-DAlsrQVW.js} +1 -1
  33. package/dist/assets/{diagram-UQ7AKVKN-CCXheGAA.js → diagram-UQ7AKVKN-By5yrMer.js} +1 -1
  34. package/dist/assets/{diagram-VSXAHHWV-B70m4lr_.js → diagram-VSXAHHWV-Dx89kryN.js} +1 -1
  35. package/dist/assets/{diagram-VX7I27RA-CHkiMOVA.js → diagram-VX7I27RA-B9fARitZ.js} +1 -1
  36. package/dist/assets/{diagram-Z3DM3KII-_awpc4Bs.js → diagram-Z3DM3KII-DC15CEH1.js} +1 -1
  37. package/dist/assets/{ebnfDiagram-PWID7BFC-CZzjH86H.js → ebnfDiagram-PWID7BFC-BKQzghGF.js} +1 -1
  38. package/dist/assets/{erDiagram-RLTQ6QDP-9DgiHZg9.js → erDiagram-RLTQ6QDP-CPY6RM7G.js} +1 -1
  39. package/dist/assets/{flowDiagram-HODETNUW-DHY_503b.js → flowDiagram-HODETNUW-4dL4GJcf.js} +1 -1
  40. package/dist/assets/{ganttDiagram-EL5Y4UJY-DPiiYZ_l.js → ganttDiagram-EL5Y4UJY-D4-cVcfb.js} +1 -1
  41. package/dist/assets/{gitGraphDiagram-WWUBYQGX-Ddd_evm7.js → gitGraphDiagram-WWUBYQGX-BMeVIEiX.js} +1 -1
  42. package/dist/assets/{index-tibHq7Qp.js → index-DNtFDOni.js} +140 -140
  43. package/dist/assets/index-Dc9wNuS4.css +1 -0
  44. package/dist/assets/{infoDiagram-27XIBGKW-BPRNhfwb.js → infoDiagram-27XIBGKW-D5qMSUEU.js} +1 -1
  45. package/dist/assets/{ishikawaDiagram-5VMMS53U-BXx6aqkq.js → ishikawaDiagram-5VMMS53U-BDnyKRHm.js} +1 -1
  46. package/dist/assets/{journeyDiagram-3NMN7TZE-BK8TZM29.js → journeyDiagram-3NMN7TZE-dGz5ZGzq.js} +1 -1
  47. package/dist/assets/{kanban-definition-UXKFOSKX-CHioRmxz.js → kanban-definition-UXKFOSKX-D7UAz7o_.js} +6 -6
  48. package/dist/assets/layout-DKrmEGrz.js +1 -0
  49. package/dist/assets/{linear-CTEhUcKG.js → linear-CrTdMY-p.js} +1 -1
  50. package/dist/assets/{mermaid.core-DYUb_UUB.js → mermaid.core-CXQOT5G6.js} +7 -7
  51. package/dist/assets/{mindmap-definition-YA3MSWOX-D7ODZ4oS.js → mindmap-definition-YA3MSWOX-VgEiYfay.js} +1 -1
  52. package/dist/assets/{pegDiagram-XKGWAZYB-8J-_2_kw.js → pegDiagram-XKGWAZYB-9IqT8gGx.js} +1 -1
  53. package/dist/assets/{pieDiagram-E7YTZNPT-ebg0yfMi.js → pieDiagram-E7YTZNPT-D98pRE-t.js} +1 -1
  54. package/dist/assets/processTemplate-GPFDLQMX-B1sVV2O0.js +22 -0
  55. package/dist/assets/{quadrantDiagram-AXDQQJYC-BoWkDiFb.js → quadrantDiagram-AXDQQJYC-b9PwkYzL.js} +1 -1
  56. package/dist/assets/{railroadDiagram-O6MQD6OU-BRJ77ldz.js → railroadDiagram-O6MQD6OU-Dz87hsPE.js} +1 -1
  57. package/dist/assets/{requirementDiagram-BXWQKSXE-Z0JJgktA.js → requirementDiagram-BXWQKSXE-CkcV73dK.js} +1 -1
  58. package/dist/assets/sankeyDiagram-P5KCCOFB-UBN31Yrs.js +40 -0
  59. package/dist/assets/{sequenceDiagram-WJ2MYXX4-BPAjtXwp.js → sequenceDiagram-WJ2MYXX4-CpQxd5Za.js} +1 -1
  60. package/dist/assets/{server.browser-BzI1jrZw.js → server.browser-BY3bL-IX.js} +1 -1
  61. package/dist/assets/{sizeCapture-INFHLROL-BGZPCzM4.js → sizeCapture-INFHLROL-mCsFmyzw.js} +1 -1
  62. package/dist/assets/stateDiagram-D77RDMKH-B0MTBN-3.js +1 -0
  63. package/dist/assets/stateDiagram-v2-MP3YSRHH-DzBPN39W.js +1 -0
  64. package/dist/assets/{swimlanes-42K2YHIH-DvuB15qF.js → swimlanes-42K2YHIH-Dzydjm0e.js} +1 -1
  65. package/dist/assets/swimlanesDiagram-VR7AAH4N-BXQpbUMD.js +8 -0
  66. package/dist/assets/{timeline-definition-24CTP7MA-ZIziYCIF.js → timeline-definition-24CTP7MA-Dz0toOLu.js} +3 -3
  67. package/dist/assets/{vendor-codemirror-CjW0lHR5.js → vendor-codemirror-CN-sJePf.js} +2 -2
  68. package/dist/assets/{vendor-react-D809iwXR.js → vendor-react-B22wqN3J.js} +5 -5
  69. package/dist/assets/{vennDiagram-4TSXK5OY-C5_B9gWa.js → vennDiagram-4TSXK5OY-DOWp7dkf.js} +4 -4
  70. package/dist/assets/{wardleyDiagram-VM6X3IG4-DMlH9Afw.js → wardleyDiagram-VM6X3IG4-CUxsztUB.js} +1 -1
  71. package/dist/assets/{xychartDiagram-S5SC5T6Z-BldhJt7i.js → xychartDiagram-S5SC5T6Z-DS96vNED.js} +1 -1
  72. package/dist/index.html +4 -4
  73. package/dist/sw.js +1 -1
  74. package/dist-server/server/modules/providers/list/mistral/mistral-models.provider.js +4 -53
  75. package/dist-server/server/modules/providers/list/mistral/mistral-models.provider.js.map +1 -1
  76. package/dist-server/server/modules/providers/list/mistral/mistral-runtime.provider.js +2 -3
  77. package/dist-server/server/modules/providers/list/mistral/mistral-runtime.provider.js.map +1 -1
  78. package/dist-server/server/modules/providers/list/mistral/mistral-vibe-models.js +30 -0
  79. package/dist-server/server/modules/providers/list/mistral/mistral-vibe-models.js.map +1 -0
  80. package/dist-server/server/modules/providers/tests/mistral-vibe-models.test.js +24 -0
  81. package/dist-server/server/modules/providers/tests/mistral-vibe-models.test.js.map +1 -0
  82. package/dist-server/server/piecemaker/agent-instructions.js +148 -0
  83. package/dist-server/server/piecemaker/agent-instructions.js.map +1 -0
  84. package/dist-server/server/piecemaker/anonymizer/lifecycle.js +2 -3
  85. package/dist-server/server/piecemaker/anonymizer/lifecycle.js.map +1 -1
  86. package/dist-server/server/piecemaker/docx-document.js +50 -0
  87. package/dist-server/server/piecemaker/docx-document.js.map +1 -0
  88. package/dist-server/server/piecemaker/index.js +13 -2
  89. package/dist-server/server/piecemaker/index.js.map +1 -1
  90. package/dist-server/server/piecemaker/model-discovery/codex-catalog.js +46 -0
  91. package/dist-server/server/piecemaker/model-discovery/codex-catalog.js.map +1 -0
  92. package/dist-server/server/piecemaker/model-discovery/index.js +42 -0
  93. package/dist-server/server/piecemaker/model-discovery/index.js.map +1 -0
  94. package/dist-server/server/piecemaker/model-discovery/mistral-catalog.js +19 -0
  95. package/dist-server/server/piecemaker/model-discovery/mistral-catalog.js.map +1 -0
  96. package/dist-server/server/piecemaker/model-discovery/stdio-rpc.js +61 -0
  97. package/dist-server/server/piecemaker/model-discovery/stdio-rpc.js.map +1 -0
  98. package/dist-server/server/piecemaker/project-registry.js +29 -0
  99. package/dist-server/server/piecemaker/project-registry.js.map +1 -0
  100. package/package.json +12 -1
  101. package/scripts/piecemaker/cli/disable-anonymizer.mjs +19 -0
  102. package/scripts/piecemaker/cli/lib/config.mjs +0 -1
  103. package/scripts/piecemaker/cli/lib/plugins.mjs +1 -0
  104. package/scripts/piecemaker/cli/lib/services.mjs +1 -10
  105. package/scripts/piecemaker/cli/piecemaker.mjs +0 -3
  106. package/server/modules/providers/list/mistral/mistral-models.provider.ts +4 -53
  107. package/server/modules/providers/list/mistral/mistral-runtime.provider.ts +2 -3
  108. package/server/modules/providers/list/mistral/mistral-vibe-models.ts +43 -0
  109. package/server/modules/providers/tests/mistral-vibe-models.test.ts +27 -0
  110. package/server/piecemaker/agent-instructions.ts +148 -0
  111. package/server/piecemaker/anonymizer/client-config.cjs +92 -92
  112. package/server/piecemaker/anonymizer/dictionary.cjs +34 -3
  113. package/server/piecemaker/anonymizer/hudsucker-fixture.cjs +9 -16
  114. package/server/piecemaker/anonymizer/hudsucker-proxy/Cargo.toml +1 -1
  115. package/server/piecemaker/anonymizer/hudsucker-proxy/src/main.rs +251 -268
  116. package/server/piecemaker/anonymizer/hudsucker-smoke.cjs +343 -0
  117. package/server/piecemaker/anonymizer/lifecycle.ts +2 -3
  118. package/server/piecemaker/anonymizer/rewriter-bridge.cjs +13 -4
  119. package/server/piecemaker/anonymizer/rewriter-worker.cjs +85 -0
  120. package/server/piecemaker/anonymizer/service.cjs +109 -179
  121. package/server/piecemaker/anonymizer/sqlite-dictionary.cjs +12 -1
  122. package/server/piecemaker/docx-document.ts +54 -0
  123. package/server/piecemaker/harness/decisions.cjs +1 -1
  124. package/server/piecemaker/harness/index.cjs +2 -3
  125. package/server/piecemaker/harness/verification.cjs +1 -1
  126. package/server/piecemaker/index.ts +15 -3
  127. package/server/piecemaker/model-discovery/codex-catalog.ts +67 -0
  128. package/server/piecemaker/model-discovery/index.ts +69 -0
  129. package/server/piecemaker/model-discovery/mistral-catalog.ts +36 -0
  130. package/server/piecemaker/model-discovery/stdio-rpc.ts +79 -0
  131. package/server/piecemaker/project-registry.ts +34 -0
  132. package/server/piecemaker/router.cjs +6 -8
  133. package/server/piecemaker/vendor/installer/bin/piecemaker.mjs +31 -77
  134. package/server/piecemaker/vendor/installer/lib/codex-skills.mjs +2 -84
  135. package/server/piecemaker/vendor/installer/lib/service.mjs +4 -24
  136. package/server/piecemaker/vendor/installer/steps/02-dependances-node.mjs +5 -5
  137. package/server/piecemaker/vendor/installer/steps/06-hooks.mjs +10 -21
  138. package/server/piecemaker/vendor/installer/steps/09-claude-assets.mjs +0 -11
  139. package/server/piecemaker/vendor/installer/steps/09-codex-plugin.mjs +6 -36
  140. package/server/piecemaker/vendor/installer/steps/13-garde-secrets.mjs +30 -12
  141. package/server/piecemaker/vendor/piecemaker-plugin/README.md +2 -9
  142. package/server/piecemaker/vendor/piecemaker-plugin/scripts/billing-track.mjs +5 -5
  143. package/server/piecemaker/vendor/piecemaker-plugin/scripts/classify-ai-documents.mjs +8 -8
  144. package/server/piecemaker/vendor/piecemaker-plugin/scripts/commit-track.mjs +2 -2
  145. package/server/piecemaker/vendor/piecemaker-plugin/scripts/compile-recherche.mjs +4 -6
  146. package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/case-folder-structure.cjs +2 -90
  147. package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/case-folders.cjs +71 -38
  148. package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/mapping.cjs +8 -38
  149. package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/protection.cjs +82 -61
  150. package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/substitution.cjs +189 -41
  151. package/server/piecemaker/vendor/piecemaker-plugin/scripts/protect-originals.mjs +4 -55
  152. package/server/piecemaker/vendor/piecemaker-plugin/scripts/statusline.mjs +24 -117
  153. package/server/piecemaker/vendor/piecemaker-plugin/skills/ajout-de-fonctionnalites/SKILL.md +4 -18
  154. package/server/piecemaker/vendor/piecemaker-plugin/skills/tamponnage/SKILL.md +2 -2
  155. package/server/piecemaker/vendor/websocket-server/admin-routes.cjs +14 -148
  156. package/server/piecemaker/vendor/websocket-server/case-registry.cjs +16 -42
  157. package/server/piecemaker/vendor/websocket-server/claude-hooks.cjs +0 -73
  158. package/server/piecemaker/vendor/websocket-server/originals-pipeline.cjs +0 -7
  159. package/server/piecemaker/vendor/websocket-server/stamping-routes.cjs +3 -12
  160. package/server/piecemaker/vendor/websocket-server/workspace-paths.cjs +6 -6
  161. package/dist/assets/arc-DAA6wH-C.js +0 -1
  162. package/dist/assets/architectureDiagram-5GKGNRK7-BGNHr7ef.js +0 -36
  163. package/dist/assets/channel-BAQyfMll.js +0 -1
  164. package/dist/assets/classDiagram-ZZMXUADV-BNGCuvrR.js +0 -1
  165. package/dist/assets/classDiagram-v2-VYDZK3BY-BNGCuvrR.js +0 -1
  166. package/dist/assets/index-WwknP0l4.css +0 -1
  167. package/dist/assets/layout-DEnkyfTY.js +0 -1
  168. package/dist/assets/sankeyDiagram-P5KCCOFB-4PW5HTOP.js +0 -40
  169. package/dist/assets/stateDiagram-D77RDMKH-o-vEcKwz.js +0 -1
  170. package/dist/assets/stateDiagram-v2-MP3YSRHH-BsuDaKkN.js +0 -1
  171. package/dist/assets/swimlanesDiagram-VR7AAH4N-DcCAyKlT.js +0 -8
  172. package/scripts/piecemaker/cli/lib/piecemaker-anonymizer.mjs +0 -78
  173. package/scripts/piecemaker/hooks/proxy-guard.mjs +0 -251
  174. package/server/piecemaker/anonymizer/providers.cjs +0 -120
  175. package/server/piecemaker/vendor/installer/steps/08-telegram.mjs +0 -482
  176. package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/central-mapping.cjs +0 -0
  177. package/server/piecemaker/vendor/websocket-server/telegram-admin.cjs +0 -602
@@ -115,6 +115,157 @@ function byDescendingEntityLength(getEntity) {
115
115
  };
116
116
  }
117
117
 
118
+ function mappingSignature(entries) {
119
+ let signature = '';
120
+ for (const [key, value] of entries) signature += key + '\u0000' + value + '\u0001';
121
+ return signature;
122
+ }
123
+
124
+ function literalAnchor(value) {
125
+ const runs = String(value).match(/[A-Za-z0-9_]+/g) || [];
126
+ return runs.reduce((longest, run) => (run.length > longest.length ? run : longest), '');
127
+ }
128
+
129
+ const ANCHOR_WIDTH = 37;
130
+ const ANCHOR_SYMBOL = new Int8Array(128).fill(-1);
131
+ for (let letter = 0; letter < 26; letter += 1) {
132
+ ANCHOR_SYMBOL[65 + letter] = letter;
133
+ ANCHOR_SYMBOL[97 + letter] = letter;
134
+ }
135
+ for (let digit = 0; digit < 10; digit += 1) ANCHOR_SYMBOL[48 + digit] = 26 + digit;
136
+ ANCHOR_SYMBOL[95] = 36;
137
+ const KELVIN_SIGN = 0x212A;
138
+ const LONG_S = 0x017F;
139
+
140
+ function anchorSymbol(unit) {
141
+ if (unit < 128) return ANCHOR_SYMBOL[unit];
142
+ if (unit === KELVIN_SIGN) return ANCHOR_SYMBOL[107];
143
+ if (unit === LONG_S) return ANCHOR_SYMBOL[115];
144
+ return -1;
145
+ }
146
+
147
+ function buildAnchorScanner(anchors) {
148
+ let transitions = new Int32Array(ANCHOR_WIDTH * 16).fill(-1);
149
+ const outputs = [[]];
150
+ let size = 1;
151
+ anchors.forEach((anchor, index) => {
152
+ if (!anchor) return;
153
+ let node = 0;
154
+ for (let position = 0; position < anchor.length; position += 1) {
155
+ const slot = node * ANCHOR_WIDTH + ANCHOR_SYMBOL[anchor.charCodeAt(position)];
156
+ if (transitions[slot] === -1) {
157
+ if ((size + 1) * ANCHOR_WIDTH > transitions.length) {
158
+ const grown = new Int32Array(transitions.length * 2).fill(-1);
159
+ grown.set(transitions);
160
+ transitions = grown;
161
+ }
162
+ transitions[slot] = size;
163
+ outputs.push([]);
164
+ size += 1;
165
+ }
166
+ node = transitions[slot];
167
+ }
168
+ outputs[node].push(index);
169
+ });
170
+
171
+ const failure = new Int32Array(size);
172
+ const queue = [];
173
+ for (let symbol = 0; symbol < ANCHOR_WIDTH; symbol += 1) {
174
+ const child = transitions[symbol];
175
+ if (child === -1) transitions[symbol] = 0;
176
+ else queue.push(child);
177
+ }
178
+ for (let head = 0; head < queue.length; head += 1) {
179
+ const node = queue[head];
180
+ outputs[node] = outputs[node].concat(outputs[failure[node]]);
181
+ for (let symbol = 0; symbol < ANCHOR_WIDTH; symbol += 1) {
182
+ const slot = node * ANCHOR_WIDTH + symbol;
183
+ const child = transitions[slot];
184
+ if (child === -1) {
185
+ transitions[slot] = transitions[failure[node] * ANCHOR_WIDTH + symbol];
186
+ } else {
187
+ failure[child] = transitions[failure[node] * ANCHOR_WIDTH + symbol];
188
+ queue.push(child);
189
+ }
190
+ }
191
+ }
192
+
193
+ const next = new Int32Array(size * ANCHOR_WIDTH);
194
+ for (let slot = 0; slot < next.length; slot += 1) next[slot] = transitions[slot] * ANCHOR_WIDTH;
195
+ const emitted = new Array(size * ANCHOR_WIDTH).fill(null);
196
+ for (let node = 0; node < size; node += 1) if (outputs[node].length) emitted[node * ANCHOR_WIDTH] = outputs[node];
197
+
198
+ return (text) => {
199
+ const found = new Set();
200
+ let state = 0;
201
+ for (let position = 0; position < text.length; position += 1) {
202
+ const symbol = anchorSymbol(text.charCodeAt(position));
203
+ if (symbol === -1) {
204
+ state = 0;
205
+ continue;
206
+ }
207
+ state = next[state + symbol];
208
+ const matched = emitted[state];
209
+ if (matched !== null) for (const index of matched) found.add(index);
210
+ }
211
+ return found;
212
+ };
213
+ }
214
+
215
+ function compileReplacements(replacements) {
216
+ return { replacements, scan: buildAnchorScanner(replacements.map(({ anchor }) => anchor.toLowerCase())) };
217
+ }
218
+
219
+ function isDeeplyFrozen(object) {
220
+ return Object.isFrozen(object) && Object.values(object).every((value) => !Array.isArray(value) || Object.isFrozen(value));
221
+ }
222
+
223
+ const compiledMappings = new WeakMap();
224
+
225
+ function compileMapping(mapping) {
226
+ const cached = compiledMappings.get(mapping);
227
+ if (cached && cached.frozen) return cached;
228
+ const entries = Object.entries(mapping);
229
+ const signature = mappingSignature(entries);
230
+ if (cached && cached.signature === signature) return cached;
231
+
232
+ const codes = [...new Set(entries.map(([, code]) => String(code)).filter(Boolean))]
233
+ .sort((a, b) => b.length - a.length);
234
+ const replacements = [];
235
+ for (const [entity, code] of entries.sort(byDescendingEntityLength(([key]) => key))) {
236
+ const regex = buildEntityRegex(entity);
237
+ if (!regex) continue;
238
+ const trimmed = entity.trim();
239
+ replacements.push({ regex, code, anchor: literalAnchor(regex.flags.includes('i') ? trimmed : trimmed.toUpperCase()) });
240
+ }
241
+ const compiled = {
242
+ frozen: Object.isFrozen(mapping),
243
+ signature,
244
+ codes,
245
+ codeProbe: codes.length ? new RegExp(codes.map(escapeRegex).join('|')) : null,
246
+ replacements: compileReplacements(replacements),
247
+ };
248
+ compiledMappings.set(mapping, compiled);
249
+ return compiled;
250
+ }
251
+
252
+ function applyReplacements({ replacements, scan }, text) {
253
+ let output = text;
254
+ let present = null;
255
+ replacements.forEach(({ regex, code, anchor }, index) => {
256
+ if (anchor) {
257
+ if (present === null) present = scan(output);
258
+ if (!present.has(index)) return;
259
+ }
260
+ const next = output.replace(regex, code);
261
+ if (next !== output) {
262
+ output = next;
263
+ present = null;
264
+ }
265
+ });
266
+ return output;
267
+ }
268
+
118
269
  /**
119
270
  * Entité → code. Les entrées passent de la plus longue à la plus courte pour
120
271
  * qu'un nom contenu dans un autre ne consomme jamais le plus long en premier
@@ -122,41 +273,49 @@ function byDescendingEntityLength(getEntity) {
122
273
  */
123
274
  function applyMapping(text, mapping) {
124
275
  if (typeof text !== 'string' || !text) return text;
125
- const entries = Object.entries(mapping || {});
126
- if (!entries.length) return text;
127
-
128
- // Masquage préalable des codes déjà présents dans le texte. La substitution en
129
- // sous-chaîne (≥ 3 car.) et l'underscore-séparateur (acronymes 2 car.)
130
- // pourraient sinon réécrire l'intérieur d'un code — « SA » dans « URGOT SA »,
131
- // « Moral » dans « PERSONNE_MORALE_01 » — et le corrompre. Chaque code distinct
132
- // est remplacé par un caractère de zone privée (ni lettre ni chiffre, absent des
133
- // documents, jamais matché par une regex d'entité), on anonymise, puis on
134
- // restaure. C'est aussi ce qui garantit l'idempotence : réappliquer le mapping à
135
- // un texte déjà codé masque ses codes et ne touche à rien. Codes triés du plus
136
- // long au plus court pour qu'un code contenu dans un autre ne soit pas masqué
137
- // en premier.
138
- const codes = [...new Set(entries.map(([, code]) => String(code)).filter(Boolean))]
139
- .sort((a, b) => b.length - a.length);
276
+ if (!mapping || typeof mapping !== 'object') return text;
277
+ const compiled = compileMapping(mapping);
278
+ if (!compiled.replacements.replacements.length) return text;
279
+
140
280
  const restore = [];
141
281
  let masked = text;
142
- codes.forEach((code, idx) => {
143
- if (!masked.includes(code)) return;
144
- const token = String.fromCodePoint(0xE000 + idx);
145
- restore.push([token, code]);
146
- masked = masked.split(code).join(token);
147
- });
148
-
149
- let output = masked;
150
- for (const [entity, code] of entries.sort(byDescendingEntityLength(([key]) => key))) {
151
- const regex = buildEntityRegex(entity);
152
- if (!regex) continue;
153
- output = output.replace(regex, code);
282
+ if (compiled.codeProbe && compiled.codeProbe.test(text)) {
283
+ compiled.codes.forEach((code, idx) => {
284
+ if (!masked.includes(code)) return;
285
+ const token = String.fromCodePoint(0xE000 + idx);
286
+ restore.push([token, code]);
287
+ masked = masked.split(code).join(token);
288
+ });
154
289
  }
155
290
 
291
+ let output = applyReplacements(compiled.replacements, masked);
292
+ if (output === masked) return text;
293
+
156
294
  for (const [token, code] of restore) output = output.split(token).join(code);
157
295
  return output;
158
296
  }
159
297
 
298
+ const compiledReverseMappings = new WeakMap();
299
+
300
+ function compileReverseMapping(reverseMapping) {
301
+ const cached = compiledReverseMappings.get(reverseMapping);
302
+ if (cached && cached.frozen) return cached;
303
+ const entries = Object.entries(reverseMapping);
304
+ const signature = mappingSignature(entries.map(([code, variants]) => [code, Array.isArray(variants) ? variants[0] : variants]));
305
+ if (cached && cached.signature === signature) return cached;
306
+
307
+ const replacements = [];
308
+ for (const [code, variants] of entries.sort(byDescendingEntityLength(([key]) => key))) {
309
+ const canonical = Array.isArray(variants) ? variants[0] : variants;
310
+ if (!canonical) continue;
311
+ const regex = new RegExp(`${WORD_BOUNDARY_BEFORE}${escapeRegex(String(code))}${WORD_BOUNDARY_AFTER}`, 'giu');
312
+ replacements.push({ regex, code: String(canonical), anchor: literalAnchor(code) });
313
+ }
314
+ const compiled = { frozen: isDeeplyFrozen(reverseMapping), signature, replacements: compileReplacements(replacements) };
315
+ compiledReverseMappings.set(reverseMapping, compiled);
316
+ return compiled;
317
+ }
318
+
160
319
  /**
161
320
  * Code → entité. Le premier variant du tableau fait foi : c'est l'orthographe
162
321
  * canonique retenue par `consolidate_duplicate_entities`, celle qu'un humain
@@ -175,20 +334,9 @@ function applyMapping(text, mapping) {
175
334
  */
176
335
  function revertMapping(text, reverseMapping) {
177
336
  if (typeof text !== 'string' || !text) return text;
178
- const entries = Object.entries(reverseMapping || {});
179
- if (!entries.length) return text;
180
-
181
- let output = text;
182
- for (const [code, variants] of entries.sort(byDescendingEntityLength(([key]) => key))) {
183
- const canonical = Array.isArray(variants) ? variants[0] : variants;
184
- if (!canonical) continue;
185
- // Un code est un identifiant ASCII : pas de variantes Unicode à gérer, mais
186
- // les mêmes frontières de mots, pour ne pas réécrire un code cité dans un
187
- // mot plus long.
188
- const regex = new RegExp(`${WORD_BOUNDARY_BEFORE}${escapeRegex(String(code))}${WORD_BOUNDARY_AFTER}`, 'giu');
189
- output = output.replace(regex, String(canonical));
190
- }
191
- return output;
337
+ if (!reverseMapping || typeof reverseMapping !== 'object') return text;
338
+ const compiled = compileReverseMapping(reverseMapping);
339
+ return applyReplacements(compiled.replacements, text);
192
340
  }
193
341
 
194
342
  function normalizedPathName(value) {
@@ -21,19 +21,16 @@
21
21
  import fs from 'node:fs';
22
22
  import path from 'node:path';
23
23
  import { createRequire } from 'node:module';
24
- import { loadPieceMakerConfig, readHookPayload, runHook, noop } from './lib/hook-io.mjs';
24
+ import { readHookPayload, runHook, noop } from './lib/hook-io.mjs';
25
25
 
26
26
  const require = createRequire(import.meta.url);
27
- const { locateConfiguredCase } = require('./lib/case-folders.cjs');
28
- const { caseHasMapping } = require('./lib/mapping.cjs');
27
+ const { locateProjectCase } = require('./lib/case-folders.cjs');
29
28
  const {
30
29
  isMappingFile,
31
30
  isSecretFile,
32
31
  isProtectedFile,
33
32
  markdownCounterpart,
34
33
  readProtection,
35
- relativeKey,
36
- READABLE_EXTENSIONS,
37
34
  } = require('./lib/protection.cjs');
38
35
 
39
36
  /** Au-delà, on ne cherche pas de chemin : une commande pareille n'en cite pas un. */
@@ -81,36 +78,6 @@ function secretReason(absolute) {
81
78
  return `[PieceMaker] « ${path.basename(absolute)} » est un fichier de secrets d’environnement : il n’est jamais accessible à l’IA.`;
82
79
  }
83
80
 
84
- /**
85
- * Refus « dossier non anonymisé ». Sans `mapping_default.json`, le proxy n'a
86
- * rien à coder : lire une pièce livrerait des données personnelles en clair.
87
- * On l'explique synthétiquement au modèle, avec l'action qui débloque la lecture.
88
- */
89
- function missingMappingReason(caseName) {
90
- return `[PieceMaker] « ${caseName} » est un dossier PieceMaker sans mapping d’anonymisation (mapping_default.json absent) : sa lecture est bloquée car son contenu n’est pas encore anonymisable et partirait en clair. Lancez l’anonymisation du dossier dans l’administration PieceMaker avant toute lecture.`;
91
- }
92
-
93
- /**
94
- * Surface lisible dont le contenu serait normalement codé à la lecture (`.md`,
95
- * `.json` de pièce) : c'est précisément ce que `missingMappingReason` doit
96
- * protéger quand aucun mapping n'existe. On écarte les originaux (déjà refusés
97
- * par `isProtectedFile`), le mapping et les scans (refusés à part), la
98
- * configuration du dossier (`.piecemaker/…`, dotfiles) et les consignes
99
- * structurelles `CLAUDE.md`/`AGENTS.md` — jamais des pièces, et l'assistant du
100
- * dossier doit pouvoir charger sa consigne même avant anonymisation.
101
- */
102
- function isReadableCaseSurface(absolute, caseRoot) {
103
- const stat = statSafe(absolute);
104
- if (!stat || stat.isDirectory()) return false;
105
- const key = relativeKey(absolute, caseRoot);
106
- if (!key) return false;
107
- if (key.split('/').some((segment) => segment.startsWith('.'))) return false;
108
- if (!READABLE_EXTENSIONS.has(path.extname(key).toLowerCase())) return false;
109
- if (isMappingFile(absolute)) return false;
110
- const base = path.basename(key);
111
- return base !== 'CLAUDE.md' && base !== 'AGENTS.md';
112
- }
113
-
114
81
  function statSafe(target) {
115
82
  try {
116
83
  return fs.statSync(target);
@@ -189,23 +156,19 @@ function caseRootHasMapping(located) {
189
156
  async function main() {
190
157
  const payload = await readHookPayload(2000);
191
158
  if (!payload) return null;
192
- const config = loadPieceMakerConfig();
193
159
 
194
160
  const toolName = payload.tool_name;
195
161
  const cwd = payload.cwd || process.cwd();
196
162
 
197
163
  if (toolName === 'Bash') {
198
164
  for (const candidate of commandPaths(payload.tool_input?.command, cwd)) {
199
- const located = locateConfiguredCase(config, candidate);
165
+ const located = locateProjectCase(candidate);
200
166
  if (!located) continue;
201
167
  if (isMappingFile(candidate)) return deny(mappingReason(candidate));
202
168
  if (isSecretFile(candidate)) return deny(secretReason(candidate));
203
169
  if (isProtectedFile(candidate, located.caseRoot)) {
204
170
  return deny(protectionReason(candidate, located.caseRoot));
205
171
  }
206
- if (isReadableCaseSurface(candidate, located.caseRoot) && !caseHasMapping(located.caseRoot)) {
207
- return deny(missingMappingReason(located.caseName));
208
- }
209
172
  }
210
173
  return null;
211
174
  }
@@ -214,7 +177,7 @@ async function main() {
214
177
 
215
178
  const target = absolutePath(payload.tool_input?.file_path || payload.tool_input?.path, cwd);
216
179
  if (!target) return null;
217
- const located = locateConfiguredCase(config, target);
180
+ const located = locateProjectCase(target);
218
181
  if (!located) return null;
219
182
 
220
183
  // Un répertoire n'est pas une pièce : `Grep`/`Glob` en visent un couramment,
@@ -227,20 +190,6 @@ async function main() {
227
190
  return deny(protectionReason(target, located.caseRoot));
228
191
  }
229
192
 
230
- // Dossier PieceMaker sans mapping : la garantie « anonymisé à la lecture »
231
- // n'existe pas encore. Lire une surface lisible (`.md`/`.json` de pièce) la
232
- // ferait sortir en clair, et un `Grep` récursif à la racine en ferait autant
233
- // sur tout le dossier (un `Glob`, qui ne ramène que des noms, reste permis).
234
- // On calcule l'absence de mapping au plus tard, une fois les refus francs
235
- // (mapping, original) écartés — c'est le seul chemin qui paie la lecture du
236
- // mapping.
237
- if (isReadableCaseSurface(target, located.caseRoot) && !caseHasMapping(located.caseRoot)) {
238
- return deny(missingMappingReason(located.caseName));
239
- }
240
- if (toolName === 'Grep' && located.absolute === located.caseRoot && !caseHasMapping(located.caseRoot)) {
241
- return deny(missingMappingReason(located.caseName));
242
- }
243
-
244
193
  // Un `Grep` récursif ramène le *contenu* du mapping ; un `Glob` n'en ramène
245
194
  // que le nom, qui ne trahit rien — d'où la restriction à Grep ici.
246
195
  if (toolName === 'Grep' && caseRootHasMapping(located)) {
@@ -1,120 +1,46 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * statusLine Claude Code — bandeau permanent d'état de l'anonymisation PieceMaker.
4
- *
5
- * Contrat statusLine (type "command") : JSON sur stdin décrivant la session
6
- * (workspace, model...), une seule ligne sur stdout, exit 0. Appelé très
7
- * souvent (à chaque rendu de l'interface) : ce script doit rester rapide et
8
- * ne jamais bloquer ni jeter.
9
- *
10
- * Le badge reflète les deux mêmes conditions que `proxy-guard.mjs` (routage
11
- * + proxy vivant), mais sans autostart ni journalisation : une statusLine
12
- * n'est pas le bon endroit pour une action de fond, seulement pour un
13
- * affichage. Le sondage réseau est borné à 400 ms et son résultat est mis en
14
- * cache 5 s dans ~/.piecemaker/proxy-statusline.json pour éviter une requête
15
- * à chaque frappe.
16
- */
17
-
18
- import fs from 'node:fs';
19
2
  import http from 'node:http';
20
- import os from 'node:os';
21
3
  import path from 'node:path';
22
- import { HOME_DIR, loadPieceMakerConfig, readHookPayload } from './lib/hook-io.mjs';
4
+ import { loadPieceMakerConfig, readHookPayload } from './lib/hook-io.mjs';
23
5
 
24
- const CACHE_FILE = path.join(HOME_DIR, 'proxy-statusline.json');
25
- const CACHE_TTL_MS = 5000;
26
6
  const PROBE_TIMEOUT_MS = 400;
27
7
  const STDIN_TIMEOUT_MS = 1000;
28
- const DEFAULT_PORT = 4000;
29
8
  const FLUSH_TIMEOUT_MS = 1000;
9
+ const VERT = '\u001b[32m';
10
+ const ROUGE = '\u001b[31m';
11
+ const RESET = '\u001b[0m';
30
12
 
31
- const SETTINGS_FILE = path.join(os.homedir(), '.claude', 'settings.json');
32
-
33
- function validPort(value) {
34
- const port = Number(value);
35
- return Number.isInteger(port) && port >= 1024 && port <= 65535 ? port : DEFAULT_PORT;
36
- }
37
-
38
- /** Même définition du routage que proxy-guard.mjs — lecture directe, jamais de throw. */
39
- function readRouting() {
40
- try {
41
- const settings = JSON.parse(fs.readFileSync(SETTINGS_FILE, 'utf8'));
42
- const value = settings?.env?.HTTPS_PROXY;
43
- if (typeof value !== 'string' || !value) return false;
44
- const url = new URL(value);
45
- return ['127.0.0.1', 'localhost'].includes(url.hostname);
46
- } catch {
47
- return false;
48
- }
49
- }
50
-
51
- /** Cache court entre deux sondages. Un cache illisible/absent n'empêche pas de sonder. */
52
- function readCache() {
13
+ function routedProxy() {
53
14
  try {
54
- const parsed = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8'));
55
- if (typeof parsed?.ts !== 'number' || typeof parsed?.actif !== 'boolean') return null;
56
- if (Date.now() - parsed.ts > CACHE_TTL_MS) return null;
57
- return parsed;
15
+ const url = new URL(process.env.HTTPS_PROXY || process.env.https_proxy || '');
16
+ return ['127.0.0.1', 'localhost'].includes(url.hostname) ? url : null;
58
17
  } catch {
59
18
  return null;
60
19
  }
61
20
  }
62
21
 
63
- function writeCache(actif) {
64
- try {
65
- fs.mkdirSync(HOME_DIR, { recursive: true });
66
- fs.writeFileSync(CACHE_FILE, JSON.stringify({ ts: Date.now(), actif }), 'utf8');
67
- } catch {
68
- // Le cache est un confort de performance, pas une garantie.
69
- }
70
- }
71
-
72
- function probe(port, timeoutMs) {
22
+ function proxyHealthy(proxy) {
73
23
  return new Promise((resolve) => {
74
- let settled = false;
75
- const finish = (ok) => {
76
- if (settled) return;
77
- settled = true;
78
- resolve(ok);
79
- };
80
- try {
81
- const request = http.get({
82
- hostname: '127.0.0.1',
83
- port,
84
- path: '/health/liveliness',
85
- timeout: timeoutMs,
86
- }, (response) => {
87
- response.resume();
88
- finish(response.statusCode === 200);
89
- });
90
- request.on('timeout', () => { request.destroy(); finish(false); });
91
- request.on('error', () => finish(false));
92
- } catch {
93
- finish(false);
94
- }
24
+ const request = http.get({ hostname: proxy.hostname, port: proxy.port, path: '/health', timeout: PROBE_TIMEOUT_MS }, (response) => {
25
+ response.resume();
26
+ resolve(response.statusCode === 200);
27
+ });
28
+ request.on('timeout', () => { request.destroy(); resolve(false); });
29
+ request.on('error', () => resolve(false));
95
30
  });
96
31
  }
97
32
 
98
- /**
99
- * Couleur ANSI du badge : vert quand l'anonymisation est effective, rouge
100
- * sinon. Claude Code rend les séquences ANSI de la statusLine ; on n'en met
101
- * que sur le badge, jamais sur le dossier ni le modèle.
102
- */
103
- const VERT = '\u001b[32m';
104
- const ROUGE = '\u001b[31m';
105
- const RESET = '\u001b[0m';
106
-
107
33
  function colore(texte, couleur) {
108
34
  return process.env.NO_COLOR ? texte : `${couleur}${texte}${RESET}`;
109
35
  }
110
36
 
111
- function badge(routed, actif) {
112
- if (routed && actif) return colore('🔒 Anonymisation PieceMaker active ✓', VERT);
113
- if (routed) return colore('⚠️ Anonymisation PieceMaker inactive — proxy arrêté (piecemaker)', ROUGE);
114
- return colore('⚠️ Anonymisation PieceMaker inactive — accès direct', ROUGE);
37
+ async function badge() {
38
+ const proxy = routedProxy();
39
+ if (!proxy) return colore('⚠️ Anonymisation PieceMaker inactive — accès direct', ROUGE);
40
+ if (await proxyHealthy(proxy)) return colore('🔒 Anonymisation PieceMaker active ✓', VERT);
41
+ return colore('⛔ Anonymisation PieceMaker indisponible — requêtes bloquées (relancer piecemaker)', ROUGE);
115
42
  }
116
43
 
117
- /** Écrit la ligne et quitte proprement, en attendant le flush comme hook-io.mjs::emit. */
118
44
  function printAndExit(text) {
119
45
  const guard = setTimeout(() => process.exit(0), FLUSH_TIMEOUT_MS);
120
46
  guard.unref?.();
@@ -139,35 +65,16 @@ function safeCwdName(cwd) {
139
65
  }
140
66
 
141
67
  async function main() {
142
- let payload = null;
143
- try {
144
- payload = await readHookPayload(STDIN_TIMEOUT_MS);
145
- } catch {
146
- payload = null;
147
- }
148
-
68
+ const payload = await readHookPayload(STDIN_TIMEOUT_MS).catch(() => null);
149
69
  const dirName = safeCwdName(payload?.workspace?.current_dir || payload?.cwd);
150
70
  const modelName = typeof payload?.model?.display_name === 'string' ? payload.model.display_name : '';
151
-
152
- let config = {};
71
+ let disabled = false;
153
72
  try {
154
- config = loadPieceMakerConfig();
73
+ disabled = loadPieceMakerConfig()?.anonymizer?.enabled === false;
155
74
  } catch {
156
- config = {};
75
+ disabled = false;
157
76
  }
158
- const port = validPort(config.litellmPort);
159
- const routed = readRouting();
160
-
161
- const cached = readCache();
162
- let actif;
163
- if (cached) {
164
- actif = cached.actif;
165
- } else {
166
- actif = await probe(port, PROBE_TIMEOUT_MS);
167
- writeCache(actif);
168
- }
169
-
170
- const parts = [badge(routed, actif)];
77
+ const parts = [disabled ? 'Anonymisation PieceMaker désactivée' : await badge()];
171
78
  if (dirName) parts.push(dirName);
172
79
  if (modelName) parts.push(modelName);
173
80
  printAndExit(parts.join(' · '));
@@ -14,9 +14,9 @@ skill sert de guide pour ranger un ajout personnel au bon endroit.
14
14
 
15
15
  | Type d'ajout | Emplacement | Portée |
16
16
  | --- | --- | --- |
17
- | Instructions ou préférences globales (style, rappels, habitudes) | `/Users/tsardet/.piecemaker/custom/perso.md` | Toutes les affaires — ce fichier est importé automatiquement dans chaque dossier via `/Users/tsardet/.piecemaker/CLAUDE.md`. |
18
- | Skill personnelle réutilisable | Nouveau dossier `/Users/tsardet/.claude/skills/<nom>/SKILL.md` | Toutes les sessions Claude Code — découverte automatique, jamais écrasée par PieceMaker (un fichier créé à la main n'est jamais remplacé par l'application). |
19
- | Gabarit personnel (acte, courrier type…) | `/Users/tsardet/.piecemaker/templates/` | Disponible pour toute rédaction, indépendamment du dépôt de l'application. |
17
+ | Instructions ou préférences globales (style, rappels, habitudes) | `~/.piecemaker/custom/perso.md` | Toutes les affaires — ce fichier est importé automatiquement dans chaque dossier via `~/.piecemaker/CLAUDE.md`. |
18
+ | Skill personnelle réutilisable | Nouveau dossier `~/.claude/skills/<nom>/SKILL.md` | Toutes les sessions Claude Code — découverte automatique, jamais écrasée par PieceMaker (un fichier créé à la main n'est jamais remplacé par l'application). |
19
+ | Gabarit personnel (acte, courrier type…) | `~/.piecemaker/templates/` | Disponible pour toute rédaction, indépendamment du dépôt de l'application. |
20
20
  | Élément propre à une seule affaire (parties, juridiction, échéances) | Section « Ce dossier » du `CLAUDE.md` / `AGENTS.md` de ce dossier | Uniquement ce dossier-là. |
21
21
 
22
22
  Ces emplacements sont hors du dépôt PieceMaker : une mise à jour de PieceMaker
@@ -26,7 +26,7 @@ PieceMaker — la garantie va dans les deux sens.
26
26
 
27
27
  ## Format minimal d'une skill personnelle
28
28
 
29
- Un fichier `/Users/tsardet/.claude/skills/<nom>/SKILL.md` avec un en-tête
29
+ Un fichier `~/.claude/skills/<nom>/SKILL.md` avec un en-tête
30
30
  puis du texte libre :
31
31
 
32
32
  ```markdown
@@ -50,17 +50,3 @@ Instructions pour l'assistant, en français, centrées sur la tâche.
50
50
  3. **Créer ou éditer le fichier correspondant**, en respectant le format
51
51
  attendu (front matter pour une skill, section « Ce dossier » pour un
52
52
  élément local, etc.).
53
- 4. **Pour un nouveau dossier d'affaire** qui n'a pas encore de wrapper : sur
54
- demande, proposer et créer `CLAUDE.md` et `AGENTS.md` à la racine du
55
- dossier, avec exactement ce contenu (chemin absolu, jamais `~`) :
56
-
57
- ```
58
- @/Users/tsardet/.piecemaker/CLAUDE.md
59
-
60
- # Ce dossier
61
- <!-- Éléments propres à ce dossier : parties, juridiction, échéances, particularités. À compléter. -->
62
- ```
63
-
64
- Si l'un de ces deux fichiers existe déjà dans le dossier, ne jamais
65
- l'écraser : le lire, et s'il ne contient pas déjà cette ligne d'import,
66
- l'ajouter en tête sans supprimer le contenu existant.
@@ -40,8 +40,8 @@ dossier ni par utilisateur.
40
40
  haute définition : forme, contour, police, couleur et épaisseur, puis
41
41
  « Enregistrer le tampon »).
42
42
  2. **La liste des pièces vient des dossiers juridiques enregistrés** :
43
- `GET /api/admin/dossiers` → `listDossiers` bâtit la liste sur le registre
44
- `caseFolders` (quelle que soit sa place dans l'arborescence), et chaque
43
+ `GET /api/admin/dossiers` → `listDossiers` bâtit la liste sur les projets
44
+ publiés par le serveur (`~/.piecemaker/projects.json`), et chaque
45
45
  pièce est un **fichier original** du dossier (tout fichier qui n'est ni
46
46
  `.md` ni `.json`, via `listOriginals`), **identifié par son chemin relatif
47
47
  au dossier** — aucun `compilation_dossier_*.json` n'est requis. L'ancien