@piecemaker-legal/piecemaker 1.1.1 → 2.0.1

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 (135) hide show
  1. package/dist/assets/{abnfDiagram-VCTEODGH-DEXWh3LH.js → abnfDiagram-VCTEODGH-C2JDmlZx.js} +1 -1
  2. package/dist/assets/{arc-CoTRLXKW.js → arc-XhSg5Mrj.js} +1 -1
  3. package/dist/assets/{architectureDiagram-5GKGNRK7-Cg-jO9oR.js → architectureDiagram-5GKGNRK7-DKREuXxk.js} +1 -1
  4. package/dist/assets/{blockDiagram-I7D4REHJ-ChXwQSIq.js → blockDiagram-I7D4REHJ-CNK0Tp5d.js} +1 -1
  5. package/dist/assets/{c4Diagram-7LVT6UL2-Bw2VGggd.js → c4Diagram-7LVT6UL2-C8fJzWVp.js} +1 -1
  6. package/dist/assets/channel-DO5hoFdU.js +1 -0
  7. package/dist/assets/{chunk-2Q5K7J3B-C1WdqrLP.js → chunk-2Q5K7J3B-DlE8nLJo.js} +1 -1
  8. package/dist/assets/{chunk-5VM5RSS4-BerUZBa1.js → chunk-5VM5RSS4-CSm-2TZ4.js} +1 -1
  9. package/dist/assets/{chunk-F27PBJKO-5W7ax2gB.js → chunk-F27PBJKO-CSgnXT2c.js} +1 -1
  10. package/dist/assets/{chunk-IMKFNOWR-JgDN1h4T.js → chunk-IMKFNOWR-C6bkt6C1.js} +1 -1
  11. package/dist/assets/{chunk-JWPE2WC7-ulNHnEdQ.js → chunk-JWPE2WC7-jUR52MBO.js} +1 -1
  12. package/dist/assets/{chunk-POPQ4Y6H-D4aTDC2T.js → chunk-POPQ4Y6H-D0UAL7O7.js} +1 -1
  13. package/dist/assets/{chunk-SVP7TREG-BA5zX4XF.js → chunk-SVP7TREG-C9dtOEii.js} +1 -1
  14. package/dist/assets/{chunk-TICWLB2K-DhOULVhX.js → chunk-TICWLB2K-BEzWKT39.js} +1 -1
  15. package/dist/assets/{chunk-XXDRQBXY-CkpCgrVg.js → chunk-XXDRQBXY-uD1oklVY.js} +1 -1
  16. package/dist/assets/classDiagram-ZZMXUADV-BsQtwznI.js +1 -0
  17. package/dist/assets/classDiagram-v2-VYDZK3BY-BsQtwznI.js +1 -0
  18. package/dist/assets/{cose-bilkent-JH36ORCC-0xUSfjfV.js → cose-bilkent-JH36ORCC-aGjlSdMe.js} +1 -1
  19. package/dist/assets/{cynefin-OW5HDTMX-BE6fNu2_.js → cynefin-OW5HDTMX-D3G4OYOn.js} +1 -1
  20. package/dist/assets/{cynefinDiagram-5FMLGOSQ-rFMnZ9b3.js → cynefinDiagram-5FMLGOSQ-9swRDUnu.js} +1 -1
  21. package/dist/assets/{dagre-GXQ25YYZ-C1p4ffd7.js → dagre-GXQ25YYZ-lvaAGdqY.js} +1 -1
  22. package/dist/assets/{diagram-S7CK7UJ4-BV_3Xj4O.js → diagram-S7CK7UJ4-BaZ6YIkR.js} +1 -1
  23. package/dist/assets/{diagram-UQ7AKVKN-Bb_Evkrm.js → diagram-UQ7AKVKN-COJ2Gb38.js} +1 -1
  24. package/dist/assets/{diagram-VSXAHHWV-BzgupjxD.js → diagram-VSXAHHWV-CYZEkbzP.js} +1 -1
  25. package/dist/assets/{diagram-VX7I27RA-D1c67z76.js → diagram-VX7I27RA-fFeQJ4ae.js} +1 -1
  26. package/dist/assets/{diagram-Z3DM3KII-DsJAJGKE.js → diagram-Z3DM3KII-B7knszDV.js} +1 -1
  27. package/dist/assets/{ebnfDiagram-PWID7BFC-B1ge_obq.js → ebnfDiagram-PWID7BFC-Dk0l329Y.js} +1 -1
  28. package/dist/assets/{erDiagram-RLTQ6QDP-DrV-tggw.js → erDiagram-RLTQ6QDP-CtpYxui8.js} +1 -1
  29. package/dist/assets/{flowDiagram-HODETNUW-CVzDiSQ3.js → flowDiagram-HODETNUW-aVWueRJD.js} +1 -1
  30. package/dist/assets/{ganttDiagram-EL5Y4UJY-CGRdSN9D.js → ganttDiagram-EL5Y4UJY-DfrX-B7p.js} +1 -1
  31. package/dist/assets/{gitGraphDiagram-WWUBYQGX-CAfvFXzL.js → gitGraphDiagram-WWUBYQGX-BFB1ZPF3.js} +1 -1
  32. package/dist/assets/{index-DaxH81ZJ.js → index-DqRygn9E.js} +170 -169
  33. package/dist/assets/index-Ds4HaAjz.css +1 -0
  34. package/dist/assets/{infoDiagram-27XIBGKW-DTshEkGK.js → infoDiagram-27XIBGKW-CuYkzm43.js} +1 -1
  35. package/dist/assets/{ishikawaDiagram-5VMMS53U-DkbshVxp.js → ishikawaDiagram-5VMMS53U-EMiw8Akh.js} +1 -1
  36. package/dist/assets/{journeyDiagram-3NMN7TZE-v5qE8xpn.js → journeyDiagram-3NMN7TZE-DCoLV1f8.js} +1 -1
  37. package/dist/assets/{kanban-definition-UXKFOSKX-a9CNMFLt.js → kanban-definition-UXKFOSKX-CyA4Yinu.js} +1 -1
  38. package/dist/assets/{layout-DrW4R1TH.js → layout-BlaB0grA.js} +1 -1
  39. package/dist/assets/{linear-Cs3qpXT_.js → linear-DLX3dJDH.js} +1 -1
  40. package/dist/assets/{mermaid.core-C7Ti-Fty.js → mermaid.core-Cy51CT89.js} +6 -6
  41. package/dist/assets/{mindmap-definition-YA3MSWOX-B0VDdIcG.js → mindmap-definition-YA3MSWOX-OBXjfL7I.js} +1 -1
  42. package/dist/assets/{pegDiagram-XKGWAZYB-CV1L7MjP.js → pegDiagram-XKGWAZYB-C_BJjUh1.js} +1 -1
  43. package/dist/assets/{pieDiagram-E7YTZNPT-DSCuzVTV.js → pieDiagram-E7YTZNPT-jIpbr3Fi.js} +1 -1
  44. package/dist/assets/{quadrantDiagram-AXDQQJYC-hzX-9AKo.js → quadrantDiagram-AXDQQJYC-C1A15xI5.js} +1 -1
  45. package/dist/assets/{railroadDiagram-O6MQD6OU-BbmzIqcp.js → railroadDiagram-O6MQD6OU-Rslzn1pI.js} +1 -1
  46. package/dist/assets/{requirementDiagram-BXWQKSXE-CQCRRBSL.js → requirementDiagram-BXWQKSXE-LlfURO5-.js} +1 -1
  47. package/dist/assets/{sankeyDiagram-P5KCCOFB-kiaLcW6X.js → sankeyDiagram-P5KCCOFB-B5BPF4-O.js} +1 -1
  48. package/dist/assets/{sequenceDiagram-WJ2MYXX4-Bgx8ST7Q.js → sequenceDiagram-WJ2MYXX4-orYZe9zX.js} +1 -1
  49. package/dist/assets/{sizeCapture-INFHLROL-BTjJ-ygN.js → sizeCapture-INFHLROL-Co-K5Cfb.js} +1 -1
  50. package/dist/assets/{stateDiagram-D77RDMKH-D48RR1FL.js → stateDiagram-D77RDMKH-COEx1aH2.js} +1 -1
  51. package/dist/assets/{stateDiagram-v2-MP3YSRHH-B-XwSR-z.js → stateDiagram-v2-MP3YSRHH-DaeTZvmy.js} +1 -1
  52. package/dist/assets/{swimlanes-42K2YHIH-BhE-QuEZ.js → swimlanes-42K2YHIH-BHQaDhfZ.js} +1 -1
  53. package/dist/assets/swimlanesDiagram-VR7AAH4N-BHW8q5M5.js +8 -0
  54. package/dist/assets/{timeline-definition-24CTP7MA-Cm7EUWWt.js → timeline-definition-24CTP7MA-COqCRwGX.js} +1 -1
  55. package/dist/assets/{vennDiagram-4TSXK5OY-X-nC9wZ0.js → vennDiagram-4TSXK5OY-nym5XSnr.js} +1 -1
  56. package/dist/assets/{wardleyDiagram-VM6X3IG4-D33YvEQZ.js → wardleyDiagram-VM6X3IG4-DEPPey_W.js} +1 -1
  57. package/dist/assets/{xychartDiagram-S5SC5T6Z-lziwBKsM.js → xychartDiagram-S5SC5T6Z-uSJ404kL.js} +1 -1
  58. package/dist/index.html +2 -2
  59. package/dist/sw.js +1 -1
  60. package/dist-server/server/index.js +3 -1
  61. package/dist-server/server/index.js.map +1 -1
  62. package/dist-server/server/piecemaker/index.js +7 -3
  63. package/dist-server/server/piecemaker/index.js.map +1 -1
  64. package/dist-server/server/piecemaker/knowledge/index.js +13 -6
  65. package/dist-server/server/piecemaker/knowledge/index.js.map +1 -1
  66. package/dist-server/server/piecemaker/knowledge/local-routes.js +55 -0
  67. package/dist-server/server/piecemaker/knowledge/local-routes.js.map +1 -0
  68. package/dist-server/server/piecemaker/knowledge/scan-jobs.js +3 -1
  69. package/dist-server/server/piecemaker/knowledge/scan-jobs.js.map +1 -1
  70. package/dist-server/server/piecemaker/library/index.js +14 -41
  71. package/dist-server/server/piecemaker/library/index.js.map +1 -1
  72. package/dist-server/server/piecemaker/library/store.js +11 -3
  73. package/dist-server/server/piecemaker/library/store.js.map +1 -1
  74. package/dist-server/server/piecemaker/library/workspace-installation.js +26 -3
  75. package/dist-server/server/piecemaker/library/workspace-installation.js.map +1 -1
  76. package/dist-server/server/piecemaker/timesheet/service.js +8 -7
  77. package/dist-server/server/piecemaker/timesheet/service.js.map +1 -1
  78. package/dist-server/server/piecemaker/timesheet/store.js +5 -3
  79. package/dist-server/server/piecemaker/timesheet/store.js.map +1 -1
  80. package/package.json +1 -1
  81. package/product.config.json +2 -0
  82. package/scripts/piecemaker/build-app-icon.py +88 -0
  83. package/scripts/piecemaker/cli/lib/piecemaker-anonymizer.mjs +2 -2
  84. package/scripts/piecemaker/hooks/proxy-guard.mjs +19 -54
  85. package/scripts/release/assert-piecemaker-version.mjs +25 -0
  86. package/server/index.ts +4 -1
  87. package/server/piecemaker/anonymizer/client-config.cjs +45 -162
  88. package/server/piecemaker/anonymizer/hudsucker-fixture.cjs +127 -0
  89. package/server/piecemaker/anonymizer/hudsucker-proxy/Cargo.lock +2396 -0
  90. package/server/piecemaker/anonymizer/hudsucker-proxy/Cargo.toml +14 -0
  91. package/server/piecemaker/anonymizer/hudsucker-proxy/src/main.rs +501 -0
  92. package/server/piecemaker/anonymizer/providers.cjs +14 -135
  93. package/server/piecemaker/anonymizer/rewriter-bridge.cjs +221 -0
  94. package/server/piecemaker/anonymizer/service.cjs +144 -28
  95. package/server/piecemaker/index.ts +8 -3
  96. package/server/piecemaker/knowledge/index.ts +14 -6
  97. package/server/piecemaker/knowledge/local-routes.ts +72 -0
  98. package/server/piecemaker/knowledge/scan-jobs.ts +4 -1
  99. package/server/piecemaker/library/index.ts +14 -34
  100. package/server/piecemaker/library/store.ts +11 -3
  101. package/server/piecemaker/library/workspace-installation.ts +20 -3
  102. package/server/piecemaker/timesheet/service.ts +9 -7
  103. package/server/piecemaker/timesheet/store.ts +5 -3
  104. package/server/piecemaker/vendor/installer/assets/claude-hooks/piecemaker-guard-secrets.mjs +0 -1
  105. package/server/piecemaker/vendor/installer/assets/piecemaker.icns +0 -0
  106. package/server/piecemaker/vendor/installer/assets/piecemaker.ico +0 -0
  107. package/server/piecemaker/vendor/installer/bin/piecemaker.mjs +56 -206
  108. package/server/piecemaker/vendor/installer/lib/commandes.mjs +2 -4
  109. package/server/piecemaker/vendor/installer/lib/conversion-client.mjs +74 -0
  110. package/server/piecemaker/vendor/installer/lib/service.mjs +4 -358
  111. package/server/piecemaker/vendor/installer/lib/state.mjs +0 -6
  112. package/server/piecemaker/vendor/installer/steps/03-python-gliner.mjs +4 -4
  113. package/server/piecemaker/vendor/installer/steps/09-claude-assets.mjs +0 -31
  114. package/server/piecemaker/vendor/piecemaker-plugin/scripts/statusline.mjs +3 -4
  115. package/server/piecemaker/vendor/websocket-server/admin-routes.cjs +1 -49
  116. package/server/piecemaker/vendor/websocket-server/claude-hooks.cjs +14 -12
  117. package/server/piecemaker/vendor/websocket-server/originals-pipeline.cjs +17 -476
  118. package/server/piecemaker/vendor/websocket-server/scripts/convert_and_scan_pipeline.py +2 -2
  119. package/server/piecemaker/vendor/websocket-server/scripts/mineru_improved_1.py +11 -2
  120. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/scanner_worker.py +13 -37
  121. package/server/piecemaker/vendor/websocket-server/scripts/requirements.txt +1 -7
  122. package/server/piecemaker/vendor/websocket-server/scripts/warmup.py +14 -85
  123. package/dist/assets/channel-DdzrPZ6Y.js +0 -1
  124. package/dist/assets/classDiagram-ZZMXUADV-Dim2o8E5.js +0 -1
  125. package/dist/assets/classDiagram-v2-VYDZK3BY-Dim2o8E5.js +0 -1
  126. package/dist/assets/index-D_Dc_nQv.css +0 -1
  127. package/dist/assets/swimlanesDiagram-VR7AAH4N-DR525d5V.js +0 -8
  128. package/server/piecemaker/anonymizer/proxy.cjs +0 -365
  129. package/server/piecemaker/vendor/installer/assets/PieceMakerApp.swift +0 -189
  130. package/server/piecemaker/vendor/installer/lib/desktop-launcher.mjs +0 -305
  131. package/server/piecemaker/vendor/installer/steps/05-certificats.mjs +0 -171
  132. package/server/piecemaker/vendor/installer/steps/14-mxc-sandbox.mjs +0 -271
  133. package/server/piecemaker/vendor/installer/steps/15-pwa-desktop.mjs +0 -64
  134. package/server/piecemaker/vendor/installer/templates/root-CLAUDE.md +0 -56
  135. package/server/piecemaker/vendor/installer/templates/workspace-CLAUDE.md +0 -52
@@ -8,9 +8,6 @@
8
8
  *
9
9
  * Usage:
10
10
  * piecemaker menu interactif
11
- * piecemaker open démarre le serveur et ouvre l'interface web
12
- * piecemaker start|stop|restart gère le serveur local
13
- * piecemaker status|logs affiche l'état ou les journaux
14
11
  * piecemaker chronology affiche la chronologie du dossier courant
15
12
  * piecemaker chronology write --path <pièce> --correction-json <json>
16
13
  * crée une correction de chronologie pour une pièce
@@ -35,21 +32,12 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
35
32
 
36
33
  import { banner, title, log, write, blank, summary, spinner, badge, c } from '../lib/ui.mjs';
37
34
  import { select, confirm, multiSelect, pause, nonInteractive } from '../lib/prompt.mjs';
38
- import { HOME_DIR, REPO_ROOT, commandExists, findPython, venvPaths } from '../lib/platform.mjs';
35
+ import { GIT_REPO_ROOT, HOME_DIR, REPO_ROOT, commandExists, findPython } from '../lib/platform.mjs';
39
36
  import { COMMANDS, CHRONOLOGY_ACTIONS } from '../lib/commandes.mjs';
40
37
  import { loadConfig, readEnv, markStep, loadState, CONFIG_FILE } from '../lib/state.mjs';
41
38
  import { scheduleStepResume, selectStepsToResume } from '../lib/resume-steps.mjs';
42
- import {
43
- getServerStatus,
44
- openAdmin,
45
- readLogs,
46
- restartTelegramDaemon,
47
- startServer,
48
- stopServer,
49
- checkForUpdate,
50
- updateRepository,
51
- depositRootClaudeMd,
52
- } from '../lib/service.mjs';
39
+ import { appServerPort, readLocalScanJob, startLocalScan } from '../lib/conversion-client.mjs';
40
+ import { restartTelegramDaemon, checkForUpdate, updateRepository } from '../lib/service.mjs';
53
41
 
54
42
  const require = createRequire(import.meta.url);
55
43
  const CLAUDE_ASSETS_MODULE = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../websocket-server/claude-assets.cjs');
@@ -58,6 +46,7 @@ const CENTRAL_MAPPING_MODULE = path.resolve(path.dirname(fileURLToPath(import.me
58
46
  const DOCUMENT_INDEX_MODULE = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../websocket-server/document-index.cjs');
59
47
  const CASE_INSTRUCTIONS_MODULE = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../websocket-server/case-instructions.cjs');
60
48
  const ORIGINALS_PIPELINE_MODULE = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../websocket-server/originals-pipeline.cjs');
49
+ const APP_CLI_LIB = path.join(GIT_REPO_ROOT, 'scripts', 'piecemaker', 'cli', 'lib');
61
50
 
62
51
  /**
63
52
  * The bootstrap/update command is also distributed as an installer-only
@@ -87,17 +76,17 @@ function loadCentralMappingIntegration() {
87
76
  function reconcileCentralMapping() {
88
77
  let hooksRemoved = false;
89
78
  const claude = loadClaudeIntegrations();
90
- if (typeof claude?.removeDeprecatedMappingHooks === 'function') {
91
- const cleanup = claude.removeDeprecatedMappingHooks(os.homedir());
79
+ if (typeof claude?.removeDeprecatedHooks === 'function') {
80
+ const cleanup = claude.removeDeprecatedHooks(os.homedir());
92
81
  hooksRemoved = Boolean(cleanup.changed);
93
- if (!cleanup.ok) log.warn(`Anciens hooks de mapping non nettoyés (${cleanup.reason}).`);
94
- else if (cleanup.changed) log.ok('Anciens hooks de mapping Claude Code retirés.');
82
+ if (!cleanup.ok) log.warn(`Hooks hérités non nettoyés (${cleanup.reason}).`);
83
+ else if (cleanup.changed) log.ok('Hooks hérités Claude Code retirés.');
95
84
  }
96
85
 
97
86
  const integration = loadCentralMappingIntegration();
98
87
  const central = integration?.syncCentralMapping(loadConfig());
99
88
  if (central) log.ok(`Mapping central du proxy reconstruit (${central.entities || 0} entité(s)).`);
100
- else log.warn('Mapping central du proxy non reconstruit ; relancez « piecemaker start ».');
89
+ else log.warn('Mapping central du proxy non reconstruit ; relancez « piecemaker ».');
101
90
  return hooksRemoved;
102
91
  }
103
92
 
@@ -118,28 +107,6 @@ function reconcileCaseInstructions() {
118
107
  }
119
108
  }
120
109
 
121
- /** Rejoue la partie non interactive de l'étape 15 après une mise à jour, mais
122
- * seulement pour une application Bureau déjà installée. Le chargement reste
123
- * dynamique : il intervient après le reset Git et utilise donc la nouvelle
124
- * version de l'étape qui vient d'être téléchargée. */
125
- async function refreshDesktopApplicationAfterUpdate() {
126
- const stepPath = path.join(STEPS_DIR, '15-pwa-desktop.mjs');
127
- if (!fs.existsSync(stepPath)) return false;
128
- try {
129
- const step = await import(`${pathToFileURL(stepPath).href}?update=${Date.now()}`);
130
- if (typeof step.refreshInstalledDesktopApplication !== 'function') return false;
131
- const result = await step.refreshInstalledDesktopApplication();
132
- if (result.status !== 'skipped') markStep('15-pwa-desktop', result.status, result.note || '');
133
- if (result.status === 'done') log.ok('Application PieceMaker sur le Bureau mise à jour.');
134
- else if (result.status === 'partial') log.warn(`Application Bureau partiellement mise à jour : ${result.note}`);
135
- else if (result.status === 'failed') log.warn(`Application Bureau non mise à jour : ${result.note}`);
136
- return result.status === 'done';
137
- } catch (error) {
138
- log.warn(`Application Bureau non mise à jour (${error.message}).`);
139
- return false;
140
- }
141
- }
142
-
143
110
  /**
144
111
  * Reprend en tâche de fond les étapes d'installation restées incomplètes.
145
112
  *
@@ -381,12 +348,6 @@ async function runCheck(steps, ctx) {
381
348
  function printHelp() {
382
349
  write(` ${c.bold('piecemaker')} — PieceMaker local`);
383
350
  blank();
384
- write(' open démarre le serveur et ouvre l’interface web');
385
- write(' start démarre le serveur local en arrière-plan');
386
- write(' stop arrête le serveur local');
387
- write(' restart redémarre le serveur local');
388
- write(' status affiche l’état du serveur');
389
- write(' logs affiche les dernières lignes du journal');
390
351
  write(' chronology [read] affiche la chronologie pseudonymisée du dossier courant');
391
352
  write(' chronology write --path <pièce> --correction-json <json> crée une correction de chronologie');
392
353
  write(' chronology edit --path <pièce> --correction-json <json> modifie une correction existante');
@@ -449,17 +410,6 @@ async function installerMenu(steps, ctx, { allowBack = false } = {}) {
449
410
  }
450
411
  }
451
412
 
452
- function printServerStatus(status) {
453
- title('État local');
454
- const rows = [
455
- ['Serveur HTTPS', status.running ? badge.done : badge.todo, status.running ? `PID ${status.pid || 'externe'}` : 'arrêté'],
456
- ['Interface web', status.running ? badge.done : badge.todo, status.url],
457
- ['Journal', badge.todo, status.logFile],
458
- ];
459
- summary(rows);
460
- blank();
461
- }
462
-
463
413
  function formatChronologyText(chronology) {
464
414
  const lines = [];
465
415
  lines.push(`Chronologie : ${chronology.stats.documents} pièce(s), ${chronology.stats.dated} datée(s), ${chronology.stats.entities} entité(s).`);
@@ -626,139 +576,99 @@ function publicConversionResult(job) {
626
576
  return {
627
577
  id: job?.id || null,
628
578
  state: job?.state || 'error',
579
+ phase: job?.phase || null,
629
580
  processed: Number(job?.processed) || 0,
630
581
  total: Number(job?.total) || 0,
631
- skipped: Number(job?.skipped) || 0,
632
582
  result: {
633
- converted: Number(result.converted) || 0,
634
- scanned: Number(result.scanned) || 0,
635
- ...(Number.isFinite(result.mappingAdded) ? { mappingAdded: result.mappingAdded } : {}),
636
- ...(Number.isFinite(result.mappingTotal) ? { mappingTotal: result.mappingTotal } : {}),
637
- ...(result.upToDate ? { upToDate: true } : {}),
638
- ...(result.commit ? {
639
- commitCreated: Boolean(result.commit.created),
640
- commit: result.commit.hash || null,
641
- } : {}),
583
+ documents: Number(result.documents) || 0,
584
+ ...(Number.isFinite(result.nodes) ? { nodes: result.nodes } : {}),
585
+ ...(Number.isFinite(result.edges) ? { edges: result.edges } : {}),
642
586
  },
643
587
  };
644
588
  }
645
589
 
646
- async function waitForConversionJob(initialJob, getJob, { json = false } = {}) {
647
- let job = initialJob;
648
- let reportedState = '';
590
+ async function waitForConversionJob({ folder, id }, { json = false } = {}) {
591
+ let job = null;
649
592
  let reportedPercent = -10;
650
- while (job && ['queued', 'running'].includes(job.state)) {
651
- if (!json && (job.state !== reportedState || job.percent >= reportedPercent + 10)) {
652
- if (job.state === 'queued') log.info('Conversion en attente d’une place dans la file de traitement.');
653
- else log.info(`Conversion et analyse PII : ${job.percent || 0} % (${job.processed || 0}/${job.total || 0})`);
654
- reportedState = job.state;
593
+ do {
594
+ await new Promise((resolve) => setTimeout(resolve, 500));
595
+ ({ job } = await readLocalScanJob({ folder, id }));
596
+ if (!json && job && job.percent >= reportedPercent + 10) {
597
+ log.info(`Conversion et analyse PII : ${job.percent || 0} % (${job.processed || 0}/${job.total || 0})`);
655
598
  reportedPercent = job.percent || 0;
656
599
  }
657
- await new Promise((resolve) => setTimeout(resolve, 500));
658
- job = getJob(job.id);
659
- }
600
+ } while (job && job.state === 'running');
660
601
  if (!job) throw new Error('Le travail de conversion a expiré avant sa fin.');
661
602
  if (job.state === 'error') throw new Error(job.error || 'La conversion a échoué.');
603
+ if (job.state === 'cancelled') throw new Error('La conversion a été annulée.');
662
604
  return job;
663
605
  }
664
606
 
607
+ async function ensureServerForConversion({ json = false } = {}) {
608
+ const servicesModule = path.join(APP_CLI_LIB, 'services.mjs');
609
+ if (!fs.existsSync(servicesModule)) {
610
+ throw new Error(`Le serveur applicatif doit être démarré par la commande « piecemaker » avant la conversion (port ${appServerPort()}).`);
611
+ }
612
+ const { appServerReachable, startApplication } = await import(pathToFileURL(servicesModule).href);
613
+ if (await appServerReachable()) return;
614
+ if (!json) log.info(`Serveur applicatif arrêté : démarrage sur le port ${appServerPort()} avant conversion.`);
615
+ const { resolveNodeRuntime } = await import(pathToFileURL(path.join(APP_CLI_LIB, 'node-runtime.mjs')).href);
616
+ const outcome = await startApplication(resolveNodeRuntime(), {
617
+ step: (text) => { if (!json) log.info(text); },
618
+ });
619
+ if (outcome.serverUp === false) {
620
+ throw new Error(`Le serveur applicatif n'a pas démarré.${outcome.logTail ? `\n${outcome.logTail}` : ''}`);
621
+ }
622
+ }
623
+
665
624
  async function runConversionCommand(flags) {
666
625
  if (!fs.existsSync(ORIGINALS_PIPELINE_MODULE)) {
667
626
  throw new Error('Le module de conversion PieceMaker est introuvable.');
668
627
  }
669
- const config = loadConfig();
670
628
  const { locateConfiguredCase } = require('../../piecemaker-plugin/scripts/lib/case-folders.cjs');
671
- const located = locateConfiguredCase(config, flags.caseTarget || process.cwd());
629
+ const located = locateConfiguredCase(loadConfig(), flags.caseTarget || process.cwd());
672
630
  if (!located) {
673
631
  throw new Error('Lancez la commande depuis un dossier juridique enregistré ou passez --case <chemin>.');
674
632
  }
675
633
 
676
- // Le serveur choisit habituellement l’interpréteur au démarrage. La commande
677
- // autonome reproduit ce choix sans exiger que le serveur PieceMaker tourne.
678
- const configuredVenv = venvPaths(config.venvPath);
679
- const python = process.env.PYTHON_PATH
680
- || readEnv().PYTHON_PATH
681
- || config.pythonPath
682
- || (configuredVenv.exists ? configuredVenv.python : null)
683
- || findPython()?.command;
684
- if (!python) throw new Error('Aucun interpréteur Python 3.10+ n’est disponible pour la conversion.');
685
- process.env.PYTHON_PATH = python;
686
-
687
- const { getJob, listOriginals, startOriginalsJob } = require(ORIGINALS_PIPELINE_MODULE);
634
+ const { listOriginals } = require(ORIGINALS_PIPELINE_MODULE);
688
635
  const originals = await listOriginals(located.caseRoot);
689
- const requestedFiles = resolveConversionFiles(
636
+ const selected = resolveConversionFiles(
690
637
  originals,
691
638
  flags.conversionDocuments || [],
692
639
  located.caseRoot,
693
640
  );
641
+ const requestedFiles = selected.length || !flags.force
642
+ ? selected
643
+ : originals.map((original) => original.path);
694
644
  if (!flags.json) {
695
645
  log.info(requestedFiles.length
696
646
  ? `Conversion et pseudonymisation de ${requestedFiles.length} pièce(s) sélectionnée(s).`
697
647
  : 'Conversion et pseudonymisation des pièces qui ne sont pas encore prêtes.');
698
648
  }
699
- const initialJob = await startOriginalsJob({
700
- casesRoot: located.casesRoot,
701
- caseName: located.caseName,
702
- homeDir: HOME_DIR,
703
- action: 'anonymize',
704
- files: requestedFiles,
705
- options: { force: flags.force },
706
- });
707
- const job = await waitForConversionJob(initialJob, getJob, { json: flags.json });
649
+
650
+ await ensureServerForConversion({ json: flags.json });
651
+ const started = await startLocalScan({ folder: located.caseRoot, files: requestedFiles });
652
+ const job = await waitForConversionJob(
653
+ { folder: located.caseRoot, id: started.job?.id },
654
+ { json: flags.json },
655
+ );
708
656
  const result = publicConversionResult(job);
709
657
  if (flags.json) process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
710
- else if (result.result.upToDate) log.ok('Toutes les pièces sont déjà converties et pseudonymisées.');
711
- else log.ok(`Conversion et pseudonymisation terminées : ${result.result.scanned} pièce(s).`);
658
+ else log.ok(`Conversion et pseudonymisation terminées : ${result.result.documents} pièce(s).`);
712
659
  return 0;
713
660
  }
714
661
 
715
662
  async function runOperationalCommand(command, knownUpdate = null, flags = {}) {
716
663
  if (command === 'chronology') return runChronologyCommand(flags);
717
664
  if (command === 'conversion') return runConversionCommand(flags);
718
- if (command === 'open') {
719
- const status = await openAdmin();
720
- log.ok(`Interface ouverte : ${status.url}`);
721
- return 0;
722
- }
723
- if (command === 'start') {
724
- const status = await startServer();
725
- log.ok(status.started ? `Serveur démarré : ${status.url}` : `Serveur déjà actif : ${status.url}`);
726
- return 0;
727
- }
728
- if (command === 'stop') {
729
- const status = await stopServer();
730
- if (status.alreadyStopped) log.info('Le serveur est déjà arrêté.');
731
- else log.ok('Serveur arrêté.');
732
- return 0;
733
- }
734
- if (command === 'restart') {
735
- await stopServer();
736
- const status = await startServer();
737
- log.ok(`Serveur redémarré : ${status.url}`);
738
- return 0;
739
- }
740
- if (command === 'status') {
741
- const status = await getServerStatus();
742
- printServerStatus(status);
743
- return 0;
744
- }
745
- if (command === 'logs') {
746
- title('Journal du serveur HTTPS');
747
- const content = readLogs();
748
- write(content || ' Aucun journal disponible.');
749
- blank();
750
- return 0;
751
- }
752
665
  if (command === 'update') {
753
- // Look before stopping anything: an up-to-date install must not lose its
754
- // server for the duration of a no-op npm install.
755
666
  const pending = knownUpdate ?? checkForUpdate();
756
667
  if (!pending.available) {
757
668
  log.ok(`PieceMaker est déjà à jour (${pending.ref}, ${pending.current.slice(0, 7)}).`);
758
669
  reconcileCaseInstructions();
759
- await refreshDesktopApplicationAfterUpdate();
760
670
  if (reconcileCentralMapping()) {
761
- log.info('Rouvrez les sessions Claude Code actives pour oublier les anciens hooks de mapping.');
671
+ log.info('Rouvrez les sessions Claude Code actives pour oublier les hooks hérités.');
762
672
  }
763
673
  // « update » reste le geste de remise à niveau : même sans nouveau commit,
764
674
  // une étape d'installation restée incomplète repart en tâche de fond.
@@ -771,24 +681,10 @@ async function runOperationalCommand(command, knownUpdate = null, flags = {}) {
771
681
  log.info(`Restauration de l’installation depuis le dépôt distant (${pending.localChanges} fichier(s) localement modifié(s)).`);
772
682
  }
773
683
 
774
- // A live server on the port is a PieceMaker server (the health probe
775
- // answered 200), whether we started it or not — e.g. one launched by hand
776
- // from the dev clone, so with no PID file it comes back unmanaged. We adopt
777
- // it: stop whatever holds the port and bring a managed server back below,
778
- // rather than leaving the user to restart it themselves.
779
- const previous = await getServerStatus();
780
- if (previous.running) await stopServer();
781
684
  try {
782
685
  const result = updateRepository(pending);
783
686
  log.ok(`PieceMaker mis à jour (${result.ref}, ${result.target.slice(0, 7)}).`);
784
687
 
785
- // CLAUDE.md racine est gitignoré : « git reset --hard » ci-dessus le
786
- // supprime dès qu'un clone récupère le commit qui l'a dé-versionné. On
787
- // redépose donc la persona utilisateur depuis le gabarit (absent → écrit,
788
- // présent → intact), même source que l'étape d'installation 09.
789
- if (depositRootClaudeMd().status === 'deposited') {
790
- log.ok('CLAUDE.md (persona utilisateur) redéposé depuis le gabarit.');
791
- }
792
688
  reconcileCaseInstructions();
793
689
 
794
690
  if (result.pythonChanged) {
@@ -813,13 +709,8 @@ async function runOperationalCommand(command, knownUpdate = null, flags = {}) {
813
709
  }
814
710
 
815
711
  reconcileCentralMapping();
816
- await refreshDesktopApplicationAfterUpdate();
817
712
  log.info('Rouvrez les sessions Claude Code/Codex actives pour charger les hooks et le MCP mis à jour.');
818
713
  } finally {
819
- if (previous.running) {
820
- const restarted = await startServer();
821
- log.ok(`Serveur redémarré : ${restarted.url}`);
822
- }
823
714
  const daemon = restartTelegramDaemon();
824
715
  if (daemon.restarted) log.ok('Moniteur Telegram redémarré.');
825
716
  else if (daemon.reason && daemon.reason !== 'absent' && daemon.reason !== 'unsupported') {
@@ -859,49 +750,14 @@ function checkForUpdateOnOpen() {
859
750
  }
860
751
  }
861
752
 
862
- /**
863
- * Bare `piecemaker` should land on a live server. Start it if it is down, but
864
- * never block the menu on failure: a missing certificate or a boot error is
865
- * reported and the interactive installer below stays reachable to fix it.
866
- * The browser is left closed on purpose — the menu's "Ouvrir l'interface"
867
- * entry is how the admin pane gets opened.
868
- */
869
- async function ensureServerRunning() {
870
- let status;
871
- try {
872
- status = await getServerStatus();
873
- } catch {
874
- return;
875
- }
876
- if (status.running) return;
877
-
878
- const spin = spinner('Démarrage du serveur local...');
879
- try {
880
- const started = await startServer();
881
- spin.stop();
882
- log.ok(`Serveur démarré : ${started.url}`);
883
- } catch (error) {
884
- spin.stop();
885
- log.warn(`Serveur non démarré : ${error.message}`);
886
- log.detail('Réparez-le via « Installer ou réparer des composants » ci-dessous.');
887
- }
888
- blank();
889
- }
890
-
891
753
  async function mainMenu(steps, ctx, knownUpdate = null) {
892
754
  for (;;) {
893
- const status = await getServerStatus();
894
- printServerStatus(status);
895
755
  const choice = await select('Que voulez-vous faire ?', [
896
- { value: 'open', label: 'Ouvrir l’interface graphique', hint: 'paramètres, skills et agents' },
897
- { value: status.running ? 'stop' : 'start', label: status.running ? 'Arrêter le serveur local' : 'Démarrer le serveur local' },
898
- { value: 'status', label: 'Actualiser l’état' },
899
756
  { value: 'install', label: 'Installer ou réparer des composants' },
900
757
  {
901
758
  value: 'update',
902
759
  label: knownUpdate?.remoteAvailable ? 'Mettre à jour PieceMaker — MAJ disponible' : 'Mettre à jour PieceMaker',
903
760
  },
904
- { value: 'logs', label: 'Afficher les journaux' },
905
761
  { value: 'quit', label: 'Quitter' },
906
762
  ]);
907
763
 
@@ -918,7 +774,6 @@ async function mainMenu(steps, ctx, knownUpdate = null) {
918
774
  } catch (error) {
919
775
  log.error(error.message);
920
776
  }
921
- if (choice === 'open') return;
922
777
  await pause();
923
778
  }
924
779
  }
@@ -978,9 +833,7 @@ async function main() {
978
833
  return runOperationalCommand(flags.command, null, flags);
979
834
  }
980
835
 
981
- // `update` arrête puis relance lui-même le serveur et le proxy : il passe
982
- // avant ensureServerRunning() plus bas, pour ne pas démarrer un serveur juste
983
- // avant de le couper. Il enchaîne aussi sur la reprise des étapes.
836
+ // `update` enchaîne sur la reprise des étapes : il passe avant le menu.
984
837
  let autoUpdated = false;
985
838
  if (opensInteractiveInstaller && knownUpdate?.available) {
986
839
  try {
@@ -1050,10 +903,7 @@ async function main() {
1050
903
  }
1051
904
 
1052
905
  if (flags.command === 'install') await installerMenu(steps, ctx);
1053
- else {
1054
- await ensureServerRunning();
1055
- await mainMenu(steps, ctx, knownUpdate);
1056
- }
906
+ else await mainMenu(steps, ctx, knownUpdate);
1057
907
  return 0;
1058
908
  }
1059
909
 
@@ -4,10 +4,8 @@
4
4
  * Isolée dans son propre module parce que `installer/bin/piecemaker.mjs`
5
5
  * lance `main()` dès son import : rien ne peut l'importer sans ouvrir le menu
6
6
  * interactif et donc toucher aux services réels de la machine. Ce module-ci
7
- * est importable sans effet de bord, ce qui permet au test anti-dérive
8
- * (`test/commandes-templates.test.mjs`) de confronter cette surface aux
9
- * outils MCP et aux commandes citées dans les templates.
7
+ * est importable sans effet de bord.
10
8
  */
11
9
 
12
- export const COMMANDS = new Set(['open', 'start', 'stop', 'restart', 'status', 'logs', 'chronology', 'conversion', 'install', 'doctor', 'check', 'update']);
10
+ export const COMMANDS = new Set(['chronology', 'conversion', 'install', 'doctor', 'check', 'update']);
13
11
  export const CHRONOLOGY_ACTIONS = new Set(['read', 'write', 'edit']);
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Client HTTP de la conversion PieceMaker.
3
+ *
4
+ * La conversion et l'analyse PII n'ont qu'un seul point d'entrée : la route
5
+ * `knowledge/scan` du serveur applicatif. Le CLI en est un client, sur la
6
+ * boucle locale, via le montage `/api/piecemaker/local` placé avant
7
+ * l'authentification. Le serveur applicatif écoute en clair sur le port du
8
+ * CLI (`PIECEMAKER_APP_PORT`, 3003 par défaut) : aucun certificat n'entre en
9
+ * jeu et la requête ne sort jamais de 127.0.0.1.
10
+ */
11
+
12
+ import http from 'node:http';
13
+
14
+ const LOCAL_BASE = '/api/piecemaker/local';
15
+
16
+ export function appServerPort() {
17
+ const parsed = Number.parseInt(process.env.PIECEMAKER_APP_PORT || '', 10);
18
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : 3003;
19
+ }
20
+
21
+ function requestJson({ method, path, body }) {
22
+ const payload = body === undefined ? null : Buffer.from(JSON.stringify(body));
23
+ return new Promise((resolve, reject) => {
24
+ const request = http.request(
25
+ {
26
+ hostname: '127.0.0.1',
27
+ port: appServerPort(),
28
+ path,
29
+ method,
30
+ timeout: 30_000,
31
+ headers: {
32
+ Accept: 'application/json',
33
+ ...(payload ? { 'Content-Type': 'application/json', 'Content-Length': payload.length } : {}),
34
+ },
35
+ },
36
+ (response) => {
37
+ const chunks = [];
38
+ response.on('data', (chunk) => chunks.push(chunk));
39
+ response.on('end', () => {
40
+ const raw = Buffer.concat(chunks).toString('utf8');
41
+ let parsed = null;
42
+ try {
43
+ parsed = raw ? JSON.parse(raw) : null;
44
+ } catch {
45
+ reject(new Error(`Réponse illisible du serveur PieceMaker (${response.statusCode}).`));
46
+ return;
47
+ }
48
+ if (response.statusCode >= 400) {
49
+ reject(new Error(parsed?.error || `Le serveur PieceMaker a répondu ${response.statusCode}.`));
50
+ return;
51
+ }
52
+ resolve(parsed);
53
+ });
54
+ }
55
+ );
56
+ request.on('timeout', () => request.destroy(new Error('Le serveur PieceMaker n’a pas répondu à temps.')));
57
+ request.on('error', reject);
58
+ if (payload) request.write(payload);
59
+ request.end();
60
+ });
61
+ }
62
+
63
+ export function startLocalScan({ folder, files }) {
64
+ return requestJson({
65
+ method: 'POST',
66
+ path: `${LOCAL_BASE}/scan`,
67
+ body: { folder, ...(files?.length ? { files } : {}) },
68
+ });
69
+ }
70
+
71
+ export function readLocalScanJob({ folder, id }) {
72
+ const query = new URLSearchParams({ folder, ...(id ? { id } : {}) });
73
+ return requestJson({ method: 'GET', path: `${LOCAL_BASE}/scan/job?${query}` });
74
+ }