@piecemaker-legal/piecemaker 2.0.10 → 2.0.12
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/assets/{FindReplaceDialog-KU5TEIBJ-CQ76sPaJ.js → FindReplaceDialog-KU5TEIBJ-BBxf1he7.js} +1 -1
- package/dist/assets/{FootnotePropertiesDialog-W3YHM4BV-BpgYQvvd.js → FootnotePropertiesDialog-W3YHM4BV-BYqp6U4x.js} +1 -1
- package/dist/assets/{HyperlinkDialog-ZYH7ZHRF-z08UbYbL.js → HyperlinkDialog-ZYH7ZHRF-BVC9zOMs.js} +4 -4
- package/dist/assets/{ImagePositionDialog-HEWYR6Q3-CFzOnzAt.js → ImagePositionDialog-HEWYR6Q3-Xn2mC64y.js} +1 -1
- package/dist/assets/{ImagePropertiesDialog-RAB47C6M-D-ThGU-b.js → ImagePropertiesDialog-RAB47C6M-CFcdqEif.js} +1 -1
- package/dist/assets/{PageSetupDialog-AIGIXO3E-_PYIO8eb.js → PageSetupDialog-AIGIXO3E-DECaeRS9.js} +1 -1
- package/dist/assets/{SplitCellDialog-IFBKYCD6-Cb9uQOB8.js → SplitCellDialog-IFBKYCD6-ByYa04H_.js} +1 -1
- package/dist/assets/{TablePropertiesDialog-TMLKGOOW-12vmjb6B.js → TablePropertiesDialog-TMLKGOOW-BuyqZB5A.js} +1 -1
- package/dist/assets/{WatermarkDialog-KRL7WMIS-C-d8rzt2.js → WatermarkDialog-KRL7WMIS-ByZ1Acr2.js} +1 -1
- package/dist/assets/{abnfDiagram-VCTEODGH-B4OsX7eF.js → abnfDiagram-VCTEODGH-BtttzdZA.js} +1 -1
- package/dist/assets/{arc-BK1oUIk7.js → arc-DN4m1tLs.js} +1 -1
- package/dist/assets/{architectureDiagram-5GKGNRK7-CfaFI6L7.js → architectureDiagram-5GKGNRK7-Bt-zFumG.js} +1 -1
- package/dist/assets/{blockDiagram-I7D4REHJ-DQEI2azY.js → blockDiagram-I7D4REHJ-D14V8v4j.js} +1 -1
- package/dist/assets/{c4Diagram-7LVT6UL2-DbravpVi.js → c4Diagram-7LVT6UL2-BynDo1-W.js} +1 -1
- package/dist/assets/channel-C4gJXzYa.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-BLpZ9huC.js → chunk-2Q5K7J3B-B8gKhSj9.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-BLlEdmX9.js → chunk-5VM5RSS4-GvOpglRR.js} +1 -1
- package/dist/assets/{chunk-F27PBJKO-D-ZNhI6q.js → chunk-F27PBJKO-CAgZy0qc.js} +1 -1
- package/dist/assets/{chunk-IMKFNOWR-vzSiKnhh.js → chunk-IMKFNOWR-gf8Ux8Zd.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-DfF0k4nb.js → chunk-JWPE2WC7-D4zFFRMp.js} +1 -1
- package/dist/assets/{chunk-POPQ4Y6H-BnwCYsPP.js → chunk-POPQ4Y6H-Ct8X_LaT.js} +1 -1
- package/dist/assets/{chunk-SVP7TREG-DRxc8fhq.js → chunk-SVP7TREG-g5iH1bs5.js} +1 -1
- package/dist/assets/{chunk-TICWLB2K-Dd2Q-md-.js → chunk-TICWLB2K-89-VFKTn.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-DBjkEUp_.js → chunk-XXDRQBXY-CaQbdObb.js} +1 -1
- package/dist/assets/classDiagram-ZZMXUADV-Cm08FeYk.js +1 -0
- package/dist/assets/classDiagram-v2-VYDZK3BY-Cm08FeYk.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-By6PS4oe.js → cose-bilkent-JH36ORCC-BiYOGJAU.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-YxWpjc5W.js → cynefin-OW5HDTMX-F6mJ-4tj.js} +1 -1
- package/dist/assets/{cynefinDiagram-5FMLGOSQ-DrtXzz3p.js → cynefinDiagram-5FMLGOSQ-q-Rggn6r.js} +1 -1
- package/dist/assets/{dagre-GXQ25YYZ-BDpTLKqZ.js → dagre-GXQ25YYZ-COqImSUH.js} +1 -1
- package/dist/assets/{diagram-S7CK7UJ4-C4LvqeCs.js → diagram-S7CK7UJ4-BAg1dyEc.js} +1 -1
- package/dist/assets/{diagram-UQ7AKVKN-BTpnMeHp.js → diagram-UQ7AKVKN-DFJS8qCV.js} +1 -1
- package/dist/assets/{diagram-VSXAHHWV-DucHHWev.js → diagram-VSXAHHWV-BfldL35r.js} +1 -1
- package/dist/assets/{diagram-VX7I27RA-DYHZNmmH.js → diagram-VX7I27RA-kLjovpYe.js} +1 -1
- package/dist/assets/{diagram-Z3DM3KII-BHIH2BGN.js → diagram-Z3DM3KII-Dhwvi3Iu.js} +1 -1
- package/dist/assets/{ebnfDiagram-PWID7BFC-CGiEkwj0.js → ebnfDiagram-PWID7BFC-Bp4YE1i7.js} +1 -1
- package/dist/assets/{erDiagram-RLTQ6QDP-AJMHi2Us.js → erDiagram-RLTQ6QDP-BKDdaSWz.js} +1 -1
- package/dist/assets/{flowDiagram-HODETNUW-BqzANBh2.js → flowDiagram-HODETNUW-zHProVxL.js} +1 -1
- package/dist/assets/{ganttDiagram-EL5Y4UJY-B0xPDY2_.js → ganttDiagram-EL5Y4UJY-B-B-BI6j.js} +1 -1
- package/dist/assets/{gitGraphDiagram-WWUBYQGX-CzPX5CNO.js → gitGraphDiagram-WWUBYQGX-BTHMg5CH.js} +1 -1
- package/dist/assets/index-C5slQNvE.css +1 -0
- package/dist/assets/{index-Bk2iXKnJ.js → index-CJP4dZ-c.js} +146 -146
- package/dist/assets/{infoDiagram-27XIBGKW-Cjc44H0O.js → infoDiagram-27XIBGKW-mabp4H9I.js} +1 -1
- package/dist/assets/{ishikawaDiagram-5VMMS53U-BWhr2v7r.js → ishikawaDiagram-5VMMS53U-CLNhcCrM.js} +1 -1
- package/dist/assets/{journeyDiagram-3NMN7TZE-Bl4r6RrP.js → journeyDiagram-3NMN7TZE-CGEi2RB6.js} +1 -1
- package/dist/assets/{kanban-definition-UXKFOSKX-FV-T0Vy1.js → kanban-definition-UXKFOSKX-B2fLX1fK.js} +1 -1
- package/dist/assets/{layout-CTfb1sAF.js → layout-CloKWsFq.js} +1 -1
- package/dist/assets/{linear-Dfgi6vlo.js → linear-BFyydI0c.js} +1 -1
- package/dist/assets/{mermaid.core-DOCGsdlo.js → mermaid.core-Dxl3LVAh.js} +6 -6
- package/dist/assets/{mindmap-definition-YA3MSWOX-CymQ86ir.js → mindmap-definition-YA3MSWOX-FT4pm55s.js} +1 -1
- package/dist/assets/{pegDiagram-XKGWAZYB-1VMq_vi0.js → pegDiagram-XKGWAZYB-okyIfJWM.js} +1 -1
- package/dist/assets/{pieDiagram-E7YTZNPT-C5GgXtJU.js → pieDiagram-E7YTZNPT-NF61CQz8.js} +1 -1
- package/dist/assets/{quadrantDiagram-AXDQQJYC-C6zpryYf.js → quadrantDiagram-AXDQQJYC-uVfvZzzt.js} +1 -1
- package/dist/assets/{railroadDiagram-O6MQD6OU-D-sv4SWi.js → railroadDiagram-O6MQD6OU-Dp8IpeoH.js} +1 -1
- package/dist/assets/{requirementDiagram-BXWQKSXE-CkjY-z4B.js → requirementDiagram-BXWQKSXE-D8VV0oQe.js} +1 -1
- package/dist/assets/{sankeyDiagram-P5KCCOFB-De-d8Q6L.js → sankeyDiagram-P5KCCOFB-D-xhxjob.js} +1 -1
- package/dist/assets/{sequenceDiagram-WJ2MYXX4-DCsGSJa9.js → sequenceDiagram-WJ2MYXX4-BAQBK95h.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-xINZhgER.js → sizeCapture-INFHLROL-C6rk9SVx.js} +1 -1
- package/dist/assets/{stateDiagram-D77RDMKH-CuLUTWSL.js → stateDiagram-D77RDMKH-DshqaEOj.js} +1 -1
- package/dist/assets/{stateDiagram-v2-MP3YSRHH-Cnbs-M92.js → stateDiagram-v2-MP3YSRHH-BohZIrk5.js} +1 -1
- package/dist/assets/{swimlanes-42K2YHIH-BwxARD7N.js → swimlanes-42K2YHIH-DgcPDy4l.js} +1 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-CuM3Su0K.js +8 -0
- package/dist/assets/{timeline-definition-24CTP7MA-BIOvnn-l.js → timeline-definition-24CTP7MA-OgpIe-gQ.js} +1 -1
- package/dist/assets/{vennDiagram-4TSXK5OY-BSD6Pfmt.js → vennDiagram-4TSXK5OY-BZzGd6jt.js} +1 -1
- package/dist/assets/{wardleyDiagram-VM6X3IG4-7Zz2iRkF.js → wardleyDiagram-VM6X3IG4-1tg-H7l_.js} +1 -1
- package/dist/assets/{xychartDiagram-S5SC5T6Z-Ctx17Y1D.js → xychartDiagram-S5SC5T6Z-ByTsX8g0.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/sw.js +1 -1
- package/dist-server/plugins/piecemaker-dossier/src/knowledge.js +61 -1
- package/dist-server/plugins/piecemaker-dossier/src/knowledge.js.map +1 -1
- package/dist-server/plugins/piecemaker-dossier/src/scan-result.js +0 -48
- package/dist-server/plugins/piecemaker-dossier/src/scan-result.js.map +1 -1
- package/dist-server/server/piecemaker/agent-instructions.js +148 -0
- package/dist-server/server/piecemaker/agent-instructions.js.map +1 -0
- package/dist-server/server/piecemaker/index.js +4 -0
- package/dist-server/server/piecemaker/index.js.map +1 -1
- package/dist-server/server/piecemaker/knowledge/pipeline.js +25 -29
- package/dist-server/server/piecemaker/knowledge/pipeline.js.map +1 -1
- package/dist-server/server/piecemaker/knowledge/service.js +1 -16
- package/dist-server/server/piecemaker/knowledge/service.js.map +1 -1
- package/dist-server/server/piecemaker/project-registry.js +5 -5
- package/dist-server/server/piecemaker/project-registry.js.map +1 -1
- package/package.json +1 -1
- package/scripts/piecemaker/cli/lib/plugins.mjs +1 -0
- package/server/piecemaker/agent-instructions.ts +148 -0
- package/server/piecemaker/anonymizer/dictionary.cjs +34 -3
- package/server/piecemaker/anonymizer/hudsucker-fixture.cjs +1 -1
- package/server/piecemaker/anonymizer/hudsucker-proxy/src/main.rs +2 -1
- package/server/piecemaker/anonymizer/hudsucker-smoke.cjs +343 -0
- package/server/piecemaker/anonymizer/rewrite.cjs +2 -2
- package/server/piecemaker/anonymizer/rewriter-bridge.cjs +7 -4
- package/server/piecemaker/anonymizer/rewriter-worker.cjs +85 -0
- package/server/piecemaker/anonymizer/service.cjs +10 -8
- package/server/piecemaker/anonymizer/sqlite-dictionary.cjs +15 -32
- package/server/piecemaker/index.ts +5 -1
- package/server/piecemaker/knowledge/pipeline.ts +24 -31
- package/server/piecemaker/knowledge/service.ts +1 -14
- package/server/piecemaker/project-registry.ts +6 -6
- package/server/piecemaker/router.cjs +0 -1
- package/server/piecemaker/vendor/installer/bin/piecemaker.mjs +9 -162
- package/server/piecemaker/vendor/installer/lib/commandes.mjs +1 -2
- package/server/piecemaker/vendor/installer/lib/conversion-client.mjs +62 -3
- package/server/piecemaker/vendor/installer/steps/02-dependances-node.mjs +5 -5
- package/server/piecemaker/vendor/installer/steps/06-hooks.mjs +23 -69
- package/server/piecemaker/vendor/installer/steps/12-mcp-piecemaker.mjs +3 -3
- package/server/piecemaker/vendor/mcp/README.md +1 -1
- package/server/piecemaker/vendor/mcp/piecemaker/server.mjs +12 -75
- package/server/piecemaker/vendor/piecemaker-plugin/hooks/hooks.json +0 -40
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/compile-recherche.mjs +2 -2
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/case-folder-structure.cjs +2 -90
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/case-folders.cjs +26 -7
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/commits.cjs +0 -28
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/hook-io.mjs +0 -13
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/institutional-terms.cjs +3 -3
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/mapping.cjs +10 -143
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/protection.cjs +1 -1
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/session-timing.cjs +0 -65
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/substitution.cjs +189 -41
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/protect-originals.mjs +11 -2
- package/server/piecemaker/vendor/piecemaker-plugin/skills/ajout-de-fonctionnalites/SKILL.md +4 -18
- package/server/piecemaker/vendor/piecemaker-plugin/skills/conversion-md/SKILL.md +9 -5
- package/server/piecemaker/vendor/websocket-server/admin-routes.cjs +3 -179
- package/server/piecemaker/vendor/websocket-server/case-registry.cjs +0 -10
- package/server/piecemaker/vendor/websocket-server/claude-hooks.cjs +3 -0
- package/server/piecemaker/vendor/websocket-server/originals-pipeline.cjs +23 -372
- package/server/piecemaker/vendor/websocket-server/renamed-originals.cjs +136 -0
- package/server/piecemaker/vendor/websocket-server/scripts/convert_and_scan_pipeline.py +115 -96
- package/dist/assets/channel-DVyNr2Yg.js +0 -1
- package/dist/assets/classDiagram-ZZMXUADV-2pGPHl5H.js +0 -1
- package/dist/assets/classDiagram-v2-VYDZK3BY-2pGPHl5H.js +0 -1
- package/dist/assets/index-Dc9wNuS4.css +0 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-D7p8qZI2.js +0 -8
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/billing-track.mjs +0 -194
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/commit-track.mjs +0 -71
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/session-commit-worker.mjs +0 -260
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/session-commit.mjs +0 -83
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import fs from 'node:fs';
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
import { log, spinner, columns } from '../lib/ui.mjs';
|
|
12
|
-
import { run, npmBin, npmEnv,
|
|
12
|
+
import { run, npmBin, npmEnv, GIT_REPO_ROOT } from '../lib/platform.mjs';
|
|
13
13
|
|
|
14
14
|
export const meta = {
|
|
15
15
|
id: '02-dependances-node',
|
|
@@ -36,7 +36,7 @@ function readPackageJson(dir) {
|
|
|
36
36
|
// Certaines distributions npm conservent spawn-helper de node-pty en 0644.
|
|
37
37
|
// Le module se charge alors normalement mais tout pty.spawn échoue avec
|
|
38
38
|
// « posix_spawnp failed ». Réparer le bit exécutable est idempotent.
|
|
39
|
-
export function repairNodePtySpawnHelpers(repoRoot =
|
|
39
|
+
export function repairNodePtySpawnHelpers(repoRoot = GIT_REPO_ROOT, { repair = true } = {}) {
|
|
40
40
|
if (process.platform === 'win32') return { found: 0, repaired: 0, ready: true };
|
|
41
41
|
const prebuilds = path.join(repoRoot, 'node_modules', 'node-pty', 'prebuilds');
|
|
42
42
|
if (!fs.existsSync(prebuilds)) return { found: 0, repaired: 0, ready: false };
|
|
@@ -88,7 +88,7 @@ async function npmInstall(dir, label, ctx) {
|
|
|
88
88
|
export async function install(ctx) {
|
|
89
89
|
const results = [];
|
|
90
90
|
|
|
91
|
-
results.push(['racine du projet', await npmInstall(
|
|
91
|
+
results.push(['racine du projet', await npmInstall(GIT_REPO_ROOT, 'Racine', ctx)]);
|
|
92
92
|
|
|
93
93
|
if (ctx.dryRun) {
|
|
94
94
|
return { status: 'skipped', note: 'Mode simulation — aucune installation effectuée.' };
|
|
@@ -116,8 +116,8 @@ export async function install(ctx) {
|
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
export async function check(ctx) {
|
|
119
|
-
const rootInstalled = fs.existsSync(path.join(
|
|
120
|
-
const ptyReady = repairNodePtySpawnHelpers(
|
|
119
|
+
const rootInstalled = fs.existsSync(path.join(GIT_REPO_ROOT, 'node_modules'));
|
|
120
|
+
const ptyReady = repairNodePtySpawnHelpers(GIT_REPO_ROOT, { repair: false }).ready;
|
|
121
121
|
|
|
122
122
|
if (rootInstalled && ptyReady) return { status: 'done', note: '' };
|
|
123
123
|
if (rootInstalled) {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Étape 06 — hooks Claude Code (protection,
|
|
2
|
+
* Étape 06 — hooks Claude Code (protection des pièces, recherche, documents IA).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* puis prouve que chaque script tourne proprement en lui envoyant une charge
|
|
4
|
+
* Prouve que chaque script tourne proprement en lui envoyant une charge
|
|
6
5
|
* utile synthétique sur stdin — le contrat exact qu'utilise Claude Code (voir
|
|
7
6
|
* piecemaker-plugin/scripts/lib/hook-io.mjs).
|
|
8
7
|
*
|
|
@@ -17,8 +16,7 @@ import os from 'node:os';
|
|
|
17
16
|
import path from 'node:path';
|
|
18
17
|
import { createRequire } from 'node:module';
|
|
19
18
|
import { log, spinner } from '../lib/ui.mjs';
|
|
20
|
-
import {
|
|
21
|
-
import { updateConfig } from '../lib/state.mjs';
|
|
19
|
+
import { HOME_DIR, REPO_ROOT, runCapture } from '../lib/platform.mjs';
|
|
22
20
|
|
|
23
21
|
const require = createRequire(import.meta.url);
|
|
24
22
|
const {
|
|
@@ -30,21 +28,17 @@ const {
|
|
|
30
28
|
|
|
31
29
|
export const meta = {
|
|
32
30
|
id: '06-hooks',
|
|
33
|
-
label: 'Hooks Claude Code (protection
|
|
34
|
-
description: 'Configure les garde-fous
|
|
31
|
+
label: 'Hooks Claude Code (protection des pièces)',
|
|
32
|
+
description: 'Configure les garde-fous et les hooks de recherche et de classement des documents IA',
|
|
35
33
|
};
|
|
36
34
|
|
|
37
35
|
const PLUGIN_ROOT = path.join(REPO_ROOT, 'piecemaker-plugin');
|
|
38
36
|
const SCRIPTS_DIR = path.join(PLUGIN_ROOT, 'scripts');
|
|
39
37
|
const HOOKS_JSON = path.join(PLUGIN_ROOT, 'hooks', 'hooks.json');
|
|
40
|
-
const BILLING_DIR = path.join(HOME_DIR, 'billing');
|
|
41
|
-
const SYNTHESE_DIR = path.join(BILLING_DIR, 'synthese');
|
|
42
38
|
|
|
43
39
|
const HOOK_SCRIPTS = {
|
|
44
40
|
protect: path.join(SCRIPTS_DIR, 'protect-originals.mjs'),
|
|
45
|
-
commit: path.join(SCRIPTS_DIR, 'commit-track.mjs'),
|
|
46
41
|
classify: path.join(SCRIPTS_DIR, 'classify-ai-documents.mjs'),
|
|
47
|
-
billing: path.join(SCRIPTS_DIR, 'billing-track.mjs'),
|
|
48
42
|
statusline: path.join(SCRIPTS_DIR, 'statusline.mjs'),
|
|
49
43
|
};
|
|
50
44
|
|
|
@@ -73,29 +67,12 @@ function runHookSelfTest(label, scriptPath, payload) {
|
|
|
73
67
|
return { label, ok: false, note: 'sortie stdout non-JSON — contrat de hook invalide' };
|
|
74
68
|
}
|
|
75
69
|
}
|
|
76
|
-
return { label, ok: true, note: result.stdout ? 'sortie JSON valide' : 'exit 0 (aucune sortie)' };
|
|
70
|
+
return { label, ok: true, stdout: result.stdout, note: result.stdout ? 'sortie JSON valide' : 'exit 0 (aucune sortie)' };
|
|
77
71
|
}
|
|
78
72
|
|
|
79
73
|
export async function install(ctx) {
|
|
80
|
-
log.step('Répertoires de facturation...');
|
|
81
|
-
if (!ctx.dryRun) {
|
|
82
|
-
ensureDir(BILLING_DIR);
|
|
83
|
-
ensureDir(SYNTHESE_DIR);
|
|
84
|
-
}
|
|
85
|
-
log.ok(`${BILLING_DIR}`);
|
|
86
|
-
|
|
87
74
|
if (ctx.dryRun) {
|
|
88
|
-
log.info('[simulation] configuration commits/facturation non écrite');
|
|
89
75
|
log.info('[simulation] hooks PieceMaker fusionnés directement dans ~/.claude/settings.json');
|
|
90
|
-
} else {
|
|
91
|
-
updateConfig({
|
|
92
|
-
commits: { enabled: true, timeoutMs: 8000 },
|
|
93
|
-
billing: { enabled: true },
|
|
94
|
-
});
|
|
95
|
-
log.ok('Configuration écrite dans ~/.piecemaker/config.json (commits, billing)');
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (ctx.dryRun) {
|
|
99
76
|
return { status: 'skipped', note: 'Mode simulation — hooks non vérifiés.' };
|
|
100
77
|
}
|
|
101
78
|
|
|
@@ -109,10 +86,14 @@ export async function install(ctx) {
|
|
|
109
86
|
// Un vrai dossier juridique factice, sans point de tête : les hooks
|
|
110
87
|
// ignorent les répertoires cachés, un bac de test caché ne prouverait donc
|
|
111
88
|
// rien du garde-fou. Comme seul un projet publié est un dossier juridique,
|
|
112
|
-
// on le publie le temps du test sous une source dédiée, retirée ensuite
|
|
113
|
-
|
|
89
|
+
// on le publie le temps du test sous une source dédiée, retirée ensuite,
|
|
90
|
+
// et comme anonymisé : le garde-fou n'agit que sous un bouclier vert.
|
|
91
|
+
// Les dossiers temporaires du système ne sont jamais des dossiers
|
|
92
|
+
// juridiques : le bac vit donc dans le répertoire PieceMaker.
|
|
93
|
+
fs.mkdirSync(HOME_DIR, { recursive: true });
|
|
94
|
+
testDir = fs.realpathSync(fs.mkdtempSync(path.join(HOME_DIR, 'hook-selftest-')));
|
|
114
95
|
fs.writeFileSync(path.join(testDir, 'piece-selftest.pdf'), 'PIECE DE TEST', 'utf8');
|
|
115
|
-
publishProjectSource('installer-selftest', [testDir]);
|
|
96
|
+
publishProjectSource('installer-selftest', [testDir], [testDir]);
|
|
116
97
|
selftestRegistered = true;
|
|
117
98
|
|
|
118
99
|
// 1. protect-originals.mjs — une pièce du bac de test : protégée par
|
|
@@ -127,38 +108,15 @@ export async function install(ctx) {
|
|
|
127
108
|
tool_input: { file_path: path.join(testDir, 'piece-selftest.pdf') },
|
|
128
109
|
tool_use_id: 'toolu_installer_originals_selftest',
|
|
129
110
|
});
|
|
111
|
+
if (protectedResult.ok && !/"permissionDecision"\s*:\s*"deny"/.test(protectedResult.stdout || '')) {
|
|
112
|
+
protectedResult.ok = false;
|
|
113
|
+
protectedResult.note = 'la pièce du bac de test n\'a pas été bloquée';
|
|
114
|
+
} else if (protectedResult.ok) {
|
|
115
|
+
protectedResult.note = 'pièce protégée bloquée';
|
|
116
|
+
}
|
|
130
117
|
testResults.push(protectedResult);
|
|
131
118
|
|
|
132
|
-
// 2.
|
|
133
|
-
// d'entrée/sortie sans créer de vrai commit pendant l'installation.
|
|
134
|
-
const commitResult = runHookSelfTest('commit-track.mjs', HOOK_SCRIPTS.commit, {
|
|
135
|
-
hook_event_name: 'PostToolUse',
|
|
136
|
-
session_id: 'installer-selftest',
|
|
137
|
-
cwd: REPO_ROOT,
|
|
138
|
-
transcript_path: '',
|
|
139
|
-
permission_mode: 'default',
|
|
140
|
-
tool_name: 'Edit',
|
|
141
|
-
tool_input: { file_path: path.join(REPO_ROOT, 'README.md') },
|
|
142
|
-
tool_response: { success: false },
|
|
143
|
-
tool_use_id: 'toolu_installer_commit_selftest',
|
|
144
|
-
});
|
|
145
|
-
testResults.push(commitResult);
|
|
146
|
-
|
|
147
|
-
// 3. billing-track.mjs — une vraie charge utile Stop : ajoute une ligne
|
|
148
|
-
// (clairement identifiée) au registre du mois et écrit une synthèse,
|
|
149
|
-
// ce qui prouve le chemin d'écriture de bout en bout.
|
|
150
|
-
const billingResult = runHookSelfTest('billing-track.mjs', HOOK_SCRIPTS.billing, {
|
|
151
|
-
hook_event_name: 'Stop',
|
|
152
|
-
session_id: 'installer-selftest',
|
|
153
|
-
cwd: REPO_ROOT,
|
|
154
|
-
transcript_path: path.join(testDir, 'transcript-selftest.jsonl'),
|
|
155
|
-
permission_mode: 'default',
|
|
156
|
-
last_assistant_message: 'Test d\'installation PieceMaker — vérification du hook de facturation.',
|
|
157
|
-
stop_reason: 'end_turn',
|
|
158
|
-
});
|
|
159
|
-
testResults.push(billingResult);
|
|
160
|
-
|
|
161
|
-
// 4. classify-ai-documents.mjs — un document « créé par l'IA » dans le bac
|
|
119
|
+
// 2. classify-ai-documents.mjs — un document « créé par l'IA » dans le bac
|
|
162
120
|
// de test enregistré doit ressortir classé « espace de travail » dans
|
|
163
121
|
// `.piecemaker/protection.json`, sans quoi l'IA ne pourrait pas se
|
|
164
122
|
// relire. On vérifie l'effet de bord, pas seulement le code de sortie.
|
|
@@ -201,7 +159,6 @@ export async function install(ctx) {
|
|
|
201
159
|
if (r.ok) log.ok(`${r.label} : ${r.note}`);
|
|
202
160
|
else log.error(`${r.label} : ${r.note}`);
|
|
203
161
|
}
|
|
204
|
-
log.detail(`Une ligne de test a été ajoutée à ${path.join(BILLING_DIR, `${new Date().toISOString().slice(0, 7)}.jsonl`)} (session "installer-selftest") — supprimez-la si besoin.`);
|
|
205
162
|
if (!fs.existsSync(HOOKS_JSON)) {
|
|
206
163
|
log.warn(`hooks/hooks.json introuvable dans le plugin : ${HOOKS_JSON}`);
|
|
207
164
|
}
|
|
@@ -232,12 +189,9 @@ export async function install(ctx) {
|
|
|
232
189
|
return { status: 'done', note: '' };
|
|
233
190
|
}
|
|
234
191
|
|
|
235
|
-
export async function check(
|
|
192
|
+
export async function check() {
|
|
236
193
|
const scriptsExist = Object.values(HOOK_SCRIPTS).every((p) => fs.existsSync(p));
|
|
237
194
|
const hooksJsonExists = fs.existsSync(HOOKS_JSON);
|
|
238
|
-
const dirsExist = fs.existsSync(BILLING_DIR) && fs.existsSync(SYNTHESE_DIR);
|
|
239
|
-
const cfg = ctx.config || {};
|
|
240
|
-
const configOk = Boolean(cfg.commits && cfg.billing);
|
|
241
195
|
const hooksRegistered = claudeHooksStatus(REPO_ROOT, os.homedir()).ok;
|
|
242
196
|
const statusLine = claudeStatusLineStatus(REPO_ROOT, os.homedir());
|
|
243
197
|
const statusLineOk = statusLine.ok || statusLine.conflict; // une statusLine personnelle n'est pas une anomalie
|
|
@@ -245,8 +199,8 @@ export async function check(ctx) {
|
|
|
245
199
|
if (!scriptsExist || !hooksJsonExists) {
|
|
246
200
|
return { status: 'failed', note: 'Fichiers du plugin manquants (scripts ou hooks.json) — réinstallez piecemaker-plugin/.' };
|
|
247
201
|
}
|
|
248
|
-
if (!
|
|
249
|
-
return { status: 'partial', note: 'Scripts présents mais
|
|
202
|
+
if (!hooksRegistered) {
|
|
203
|
+
return { status: 'partial', note: 'Scripts présents mais hooks non enregistrés dans Claude Code — relancez cette étape.' };
|
|
250
204
|
}
|
|
251
205
|
if (!statusLineOk) {
|
|
252
206
|
return { status: 'partial', note: `statusLine Claude Code absente ou périmée (${statusLine.reason}) — relancez cette étape.` };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Étape 12 — serveur MCP « piecemaker » (conversion
|
|
2
|
+
* Étape 12 — serveur MCP « piecemaker » (conversion).
|
|
3
3
|
*
|
|
4
4
|
* Les commandes de conversion sont aujourd'hui présentées au
|
|
5
5
|
* modèle par du texte injecté dans les templates, sans rien qui relie ce
|
|
@@ -21,8 +21,8 @@ import { REPO_ROOT, commandExists, run, runCapture } from '../lib/platform.mjs';
|
|
|
21
21
|
|
|
22
22
|
export const meta = {
|
|
23
23
|
id: '12-mcp-piecemaker',
|
|
24
|
-
label: 'Serveur MCP piecemaker (conversion
|
|
25
|
-
description: 'Enregistre dans Claude Code le serveur MCP qui expose la conversion
|
|
24
|
+
label: 'Serveur MCP piecemaker (conversion)',
|
|
25
|
+
description: 'Enregistre dans Claude Code le serveur MCP qui expose la conversion',
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
const SERVER_NAME = 'piecemaker';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# MCP PieceMaker
|
|
2
2
|
|
|
3
3
|
Point d'entrée du serveur MCP `piecemaker` (`piecemaker/server.mjs`), qui expose
|
|
4
|
-
|
|
4
|
+
l'outil `conversion` en pilotant
|
|
5
5
|
`installer/bin/piecemaker.mjs`.
|
|
6
6
|
|
|
7
7
|
- `REPO_ROOT`, calculé deux niveaux au-dessus de ce fichier, résout vers
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Serveur MCP « piecemaker » —
|
|
3
|
+
* Serveur MCP « piecemaker » — outil de conversion exposé
|
|
4
4
|
* à Claude Code (et à tout client MCP) sans passer
|
|
5
5
|
* par du texte injecté dans un CLAUDE.md.
|
|
6
6
|
*
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
* d'arguments, même localisation de dossier, une seule implémentation
|
|
11
11
|
* (voir `installer/bin/piecemaker.mjs`).
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* La commande lancée ici (`conversion --json`) court-circuite le bandeau, la
|
|
14
|
+
* vérification de mise à jour et le menu interactif. Elle confie le travail au
|
|
15
|
+
* serveur applicatif déjà lancé (application de bureau ou `piecemaker`) et ne
|
|
16
|
+
* démarre un serveur que si aucun ne répond.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
@@ -95,28 +95,6 @@ export function conversionArgs({ dossier, pieces, force }) {
|
|
|
95
95
|
return args;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
export function chronologyArgs({ dossier }) {
|
|
99
|
-
return ['chronology', '--json', '--case', dossier];
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export function chronologyCorrectionArgs(action, { dossier, path: piecePath, nature, dateIso, localisation, fields, entityAdditions, entityExclusions, reason }) {
|
|
103
|
-
const correction = {};
|
|
104
|
-
if (nature !== undefined) correction.nature = nature;
|
|
105
|
-
if (dateIso !== undefined) correction.dateIso = dateIso;
|
|
106
|
-
if (localisation !== undefined) correction.localisation = localisation;
|
|
107
|
-
if (fields !== undefined) correction.fields = fields;
|
|
108
|
-
if (reason !== undefined) correction.reason = reason;
|
|
109
|
-
if (entityAdditions !== undefined || entityExclusions !== undefined) {
|
|
110
|
-
correction.entityDecisions = {
|
|
111
|
-
additions: entityAdditions || [],
|
|
112
|
-
exclusions: entityExclusions || [],
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
const args = ['chronology', '--action', action, '--path', piecePath, '--correction-json', JSON.stringify(correction), '--json'];
|
|
116
|
-
if (dossier) args.push('--case', dossier);
|
|
117
|
-
return args;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
98
|
/** Dossier ciblé par un appel d'outil : celui demandé, sinon la session en cours. */
|
|
121
99
|
export function resolveDossier(dossier) {
|
|
122
100
|
return dossier && String(dossier).trim() ? dossier : process.cwd();
|
|
@@ -126,21 +104,6 @@ const DOSSIER_SCHEMA = z.string()
|
|
|
126
104
|
.optional()
|
|
127
105
|
.describe('Chemin absolu du dossier juridique ciblé. Par défaut, le répertoire de la session Claude Code en cours.');
|
|
128
106
|
|
|
129
|
-
const CHRONOLOGY_CORRECTION_SCHEMA = {
|
|
130
|
-
dossier: DOSSIER_SCHEMA,
|
|
131
|
-
path: z.string().min(1).describe('Chemin relatif de la pièce, tel qu\'indexé par la chronologie.'),
|
|
132
|
-
nature: z.string().optional().describe('Nature de l\'acte (ex. « Assignation », « Conclusions »).'),
|
|
133
|
-
dateIso: z.string().optional().describe('Date de la pièce au format ISO 8601 (AAAA-MM-JJ).'),
|
|
134
|
-
localisation: z.string().optional().describe('Juridiction ou localisation associée à la pièce.'),
|
|
135
|
-
fields: z.array(z.object({ label: z.string(), value: z.string() })).optional()
|
|
136
|
-
.describe('Champs personnalisés additionnels de la pièce.'),
|
|
137
|
-
entityAdditions: z.array(z.string()).optional()
|
|
138
|
-
.describe('Codes d\'entités (personnes/sociétés pseudonymisées) à rattacher manuellement à la pièce.'),
|
|
139
|
-
entityExclusions: z.array(z.string()).optional()
|
|
140
|
-
.describe('Codes d\'entités détectés automatiquement à exclure manuellement de la pièce.'),
|
|
141
|
-
reason: z.string().optional().describe('Motif de la correction, consigné dans l\'historique du dossier.'),
|
|
142
|
-
};
|
|
143
|
-
|
|
144
107
|
/**
|
|
145
108
|
* Construit le serveur MCP et y enregistre les outils. Séparé de
|
|
146
109
|
* `main()` pour rester testable sans jamais brancher de transport stdio.
|
|
@@ -160,45 +123,19 @@ export function createServer({ execFn } = {}) {
|
|
|
160
123
|
.describe('Noms ou chemins relatifs des pièces à convertir. Par défaut, toutes les pièces pas encore prêtes.'),
|
|
161
124
|
force: z.boolean().optional().describe('Reconvertit et rescanne même les pièces déjà prêtes.'),
|
|
162
125
|
},
|
|
126
|
+
annotations: {
|
|
127
|
+
title: 'Conversion et pseudonymisation des pièces',
|
|
128
|
+
readOnlyHint: false,
|
|
129
|
+
destructiveHint: false,
|
|
130
|
+
idempotentHint: true,
|
|
131
|
+
openWorldHint: false,
|
|
132
|
+
},
|
|
163
133
|
}, async ({ dossier, pieces, force }) => {
|
|
164
134
|
const resolved = resolveDossier(dossier);
|
|
165
135
|
const result = await run(conversionArgs({ dossier: resolved, pieces, force }), resolved);
|
|
166
136
|
return toToolResult(result);
|
|
167
137
|
});
|
|
168
138
|
|
|
169
|
-
server.registerTool('chronologie', {
|
|
170
|
-
description: 'Affiche la chronologie pseudonymisée du dossier juridique (dates, pièces, événements), en JSON.',
|
|
171
|
-
inputSchema: {
|
|
172
|
-
dossier: DOSSIER_SCHEMA,
|
|
173
|
-
},
|
|
174
|
-
}, async ({ dossier }) => {
|
|
175
|
-
const resolved = resolveDossier(dossier);
|
|
176
|
-
const result = await run(chronologyArgs({ dossier: resolved }), resolved);
|
|
177
|
-
return toToolResult(result);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
server.registerTool('write_chronology', {
|
|
181
|
-
description: 'Crée une correction manuelle de chronologie pour une pièce (date, nature d\'acte, '
|
|
182
|
-
+ 'localisation, personnes citées, champs custom). Échoue si une correction existe déjà pour cette '
|
|
183
|
-
+ 'pièce — utiliser edit_chronology pour la modifier.',
|
|
184
|
-
inputSchema: CHRONOLOGY_CORRECTION_SCHEMA,
|
|
185
|
-
}, async ({ dossier, ...params }) => {
|
|
186
|
-
const resolved = resolveDossier(dossier);
|
|
187
|
-
const result = await run(chronologyCorrectionArgs('write', { dossier: resolved, ...params }), resolved);
|
|
188
|
-
return toToolResult(result);
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
server.registerTool('edit_chronology', {
|
|
192
|
-
description: 'Modifie la correction manuelle de chronologie déjà enregistrée pour une pièce (date, '
|
|
193
|
-
+ 'nature d\'acte, localisation, personnes citées, champs custom). Échoue si aucune correction '
|
|
194
|
-
+ 'n\'existe pour cette pièce — utiliser write_chronology pour en créer une.',
|
|
195
|
-
inputSchema: CHRONOLOGY_CORRECTION_SCHEMA,
|
|
196
|
-
}, async ({ dossier, ...params }) => {
|
|
197
|
-
const resolved = resolveDossier(dossier);
|
|
198
|
-
const result = await run(chronologyCorrectionArgs('edit', { dossier: resolved, ...params }), resolved);
|
|
199
|
-
return toToolResult(result);
|
|
200
|
-
});
|
|
201
|
-
|
|
202
139
|
return server;
|
|
203
140
|
}
|
|
204
141
|
|
|
@@ -21,15 +21,6 @@
|
|
|
21
21
|
}
|
|
22
22
|
]
|
|
23
23
|
},
|
|
24
|
-
{
|
|
25
|
-
"matcher": "Write|Edit",
|
|
26
|
-
"hooks": [
|
|
27
|
-
{
|
|
28
|
-
"type": "command",
|
|
29
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/commit-track.mjs\""
|
|
30
|
-
}
|
|
31
|
-
]
|
|
32
|
-
},
|
|
33
24
|
{
|
|
34
25
|
"matcher": "Write",
|
|
35
26
|
"hooks": [
|
|
@@ -48,37 +39,6 @@
|
|
|
48
39
|
}
|
|
49
40
|
]
|
|
50
41
|
}
|
|
51
|
-
],
|
|
52
|
-
"Stop": [
|
|
53
|
-
{
|
|
54
|
-
"matcher": "*",
|
|
55
|
-
"hooks": [
|
|
56
|
-
{
|
|
57
|
-
"type": "command",
|
|
58
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/billing-track.mjs\""
|
|
59
|
-
}
|
|
60
|
-
]
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
"matcher": "*",
|
|
64
|
-
"hooks": [
|
|
65
|
-
{
|
|
66
|
-
"type": "command",
|
|
67
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/session-commit.mjs\""
|
|
68
|
-
}
|
|
69
|
-
]
|
|
70
|
-
}
|
|
71
|
-
],
|
|
72
|
-
"TaskCompleted": [
|
|
73
|
-
{
|
|
74
|
-
"matcher": "*",
|
|
75
|
-
"hooks": [
|
|
76
|
-
{
|
|
77
|
-
"type": "command",
|
|
78
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/billing-track.mjs\""
|
|
79
|
-
}
|
|
80
|
-
]
|
|
81
|
-
}
|
|
82
42
|
]
|
|
83
43
|
}
|
|
84
44
|
}
|
|
@@ -72,8 +72,8 @@ async function main() {
|
|
|
72
72
|
const pdfPath = path.join(rechercheDir, `${slug}.pdf`);
|
|
73
73
|
|
|
74
74
|
// Le modèle n'a vu que des codes ; l'historique du cabinet doit rester
|
|
75
|
-
// lisible → on ré-identifie le Markdown final avant écriture disque
|
|
76
|
-
//
|
|
75
|
+
// lisible → on ré-identifie le Markdown final avant écriture disque.
|
|
76
|
+
// Idempotent.
|
|
77
77
|
let markdown = renderMarkdown(data, { caseName: located.caseName });
|
|
78
78
|
const legalCase = resolveProjectCaseMapping(located.caseRoot);
|
|
79
79
|
if (legalCase?.reverse_mapping) markdown = revertMapping(markdown, legalCase.reverse_mapping);
|
|
@@ -7,7 +7,6 @@ const CASE_FOLDER_STRUCTURE_VERSION = 1;
|
|
|
7
7
|
const CASE_FOLDER_STRUCTURE_RELATIVE = '.piecemaker/case-folder-structure.json';
|
|
8
8
|
|
|
9
9
|
const DEFAULT_CASE_FOLDER_STRUCTURE = Object.freeze({
|
|
10
|
-
administrative: '00_ADMINISTRATIF_ET_FACTURATION',
|
|
11
10
|
correspondence: '01_CORRESPONDANCE',
|
|
12
11
|
correspondenceClient: '01_Client',
|
|
13
12
|
correspondenceOpposingCounsel: '02_Avocats_adverses',
|
|
@@ -65,7 +64,6 @@ function normalizeCaseFolderStructure(value = {}) {
|
|
|
65
64
|
}
|
|
66
65
|
|
|
67
66
|
ensureUniqueSiblingNames(structure, [
|
|
68
|
-
'administrative',
|
|
69
67
|
'correspondence',
|
|
70
68
|
'dataRoom',
|
|
71
69
|
'drafts',
|
|
@@ -107,9 +105,8 @@ function readStructureManifest(caseRoot) {
|
|
|
107
105
|
}
|
|
108
106
|
|
|
109
107
|
/**
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* des dossiers déjà existants.
|
|
108
|
+
* Seuls les anciens dossiers portent encore un manifeste d'arborescence : il
|
|
109
|
+
* continue de router leur Markdown. Sans manifeste, le dossier n'est pas géré.
|
|
113
110
|
*/
|
|
114
111
|
function readCaseFolderStructure(caseRoot, config = {}) {
|
|
115
112
|
const manifest = readStructureManifest(caseRoot);
|
|
@@ -119,78 +116,6 @@ function readCaseFolderStructure(caseRoot, config = {}) {
|
|
|
119
116
|
};
|
|
120
117
|
}
|
|
121
118
|
|
|
122
|
-
function caseFolderDirectoriesFromStructure(structure) {
|
|
123
|
-
return [
|
|
124
|
-
structure.administrative,
|
|
125
|
-
structure.correspondence,
|
|
126
|
-
path.join(structure.correspondence, structure.correspondenceMarkdown),
|
|
127
|
-
path.join(structure.correspondence, structure.correspondenceClient),
|
|
128
|
-
path.join(structure.correspondence, structure.correspondenceOpposingCounsel),
|
|
129
|
-
path.join(structure.correspondence, structure.correspondenceThirdParties),
|
|
130
|
-
structure.dataRoom,
|
|
131
|
-
path.join(structure.dataRoom, structure.dataRoomMarkdown),
|
|
132
|
-
structure.drafts,
|
|
133
|
-
structure.notesAndResearch,
|
|
134
|
-
structure.procedure,
|
|
135
|
-
];
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/** Chemins relatifs, dans l'ordre lisible de l'arborescence documentée. */
|
|
139
|
-
function caseFolderDirectories(config = {}) {
|
|
140
|
-
return caseFolderDirectoriesFromStructure(configuredCaseFolderStructure(config));
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function writeStructureManifest(file, structure) {
|
|
144
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
145
|
-
const temporary = `${file}.piecemaker-${process.pid}.tmp`;
|
|
146
|
-
fs.writeFileSync(temporary, `${JSON.stringify({
|
|
147
|
-
version: CASE_FOLDER_STRUCTURE_VERSION,
|
|
148
|
-
structure,
|
|
149
|
-
}, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
150
|
-
fs.renameSync(temporary, file);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Crée uniquement les dossiers manquants. Aucun fichier ni dossier existant
|
|
155
|
-
* n'est renommé ou remplacé quand l'utilisateur modifie les noms par défaut.
|
|
156
|
-
*/
|
|
157
|
-
function ensureCaseFolderStructure(caseRoot, config = {}) {
|
|
158
|
-
const requested = String(caseRoot || '').trim();
|
|
159
|
-
if (!requested || !path.isAbsolute(requested)) throw new Error('Racine du dossier juridique invalide.');
|
|
160
|
-
const root = path.resolve(requested);
|
|
161
|
-
let rootStat;
|
|
162
|
-
try {
|
|
163
|
-
rootStat = fs.statSync(root);
|
|
164
|
-
} catch {
|
|
165
|
-
throw new Error('Racine du dossier juridique introuvable.');
|
|
166
|
-
}
|
|
167
|
-
if (!rootStat.isDirectory()) throw new Error('Racine du dossier juridique invalide.');
|
|
168
|
-
|
|
169
|
-
const created = [];
|
|
170
|
-
const manifest = readStructureManifest(root);
|
|
171
|
-
const structure = manifest.structure || configuredCaseFolderStructure(config);
|
|
172
|
-
const directories = caseFolderDirectoriesFromStructure(structure);
|
|
173
|
-
for (const relative of directories) {
|
|
174
|
-
const target = path.join(root, relative);
|
|
175
|
-
if (fs.existsSync(target)) {
|
|
176
|
-
if (!fs.statSync(target).isDirectory()) {
|
|
177
|
-
throw new Error(`L’emplacement « ${relative} » existe mais n’est pas un dossier.`);
|
|
178
|
-
}
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
fs.mkdirSync(target, { recursive: true });
|
|
182
|
-
created.push(relative.split(path.sep).join('/'));
|
|
183
|
-
}
|
|
184
|
-
if (!manifest.exists) writeStructureManifest(manifest.file, structure);
|
|
185
|
-
|
|
186
|
-
return {
|
|
187
|
-
file: manifest.file,
|
|
188
|
-
names: structure,
|
|
189
|
-
directories: directories.map((relative) => relative.split(path.sep).join('/')),
|
|
190
|
-
created,
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
|
|
194
119
|
function relativeSegments(relativePath) {
|
|
195
120
|
const value = String(relativePath || '').replaceAll('\\', '/').replace(/^\.\//, '');
|
|
196
121
|
if (!value || value.startsWith('/') || value.split('/').some((segment) => !segment || segment === '.' || segment === '..')) {
|
|
@@ -278,20 +203,7 @@ function structuredMarkdownCounterpart(absolutePath, caseRoot, config = {}) {
|
|
|
278
203
|
}
|
|
279
204
|
|
|
280
205
|
module.exports = {
|
|
281
|
-
CASE_FOLDER_KEYS,
|
|
282
|
-
CASE_FOLDER_STRUCTURE_KEY,
|
|
283
|
-
CASE_FOLDER_STRUCTURE_RELATIVE,
|
|
284
|
-
DEFAULT_CASE_FOLDER_STRUCTURE,
|
|
285
|
-
caseConversionOutputDirectory,
|
|
286
|
-
caseFolderDirectories,
|
|
287
|
-
caseFolderStructureFile,
|
|
288
|
-
classifyCaseFolderPath,
|
|
289
206
|
classifyRelativeCaseFolderPath,
|
|
290
|
-
configuredCaseFolderStructure,
|
|
291
|
-
ensureCaseFolderStructure,
|
|
292
|
-
isCaseGeneratedPath,
|
|
293
|
-
normalizeCaseFolderStructure,
|
|
294
207
|
readCaseFolderStructure,
|
|
295
208
|
structuredMarkdownCounterpart,
|
|
296
|
-
validateCaseFolderName,
|
|
297
209
|
};
|
|
@@ -4,6 +4,7 @@ const os = require('node:os');
|
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
|
|
6
6
|
const PROJECTS_FILE = 'projects.json';
|
|
7
|
+
const ANONYMIZED_PROJECTS_FILE = 'anonymized-projects.json';
|
|
7
8
|
|
|
8
9
|
function piecemakerHome() {
|
|
9
10
|
return process.env.PIECEMAKER_HOME || path.join(os.homedir(), '.piecemaker');
|
|
@@ -13,6 +14,10 @@ function projectsFile() {
|
|
|
13
14
|
return path.join(piecemakerHome(), PROJECTS_FILE);
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
function anonymizedProjectsFile() {
|
|
18
|
+
return path.join(piecemakerHome(), ANONYMIZED_PROJECTS_FILE);
|
|
19
|
+
}
|
|
20
|
+
|
|
16
21
|
function realDirectory(value) {
|
|
17
22
|
const requested = String(value || '').trim();
|
|
18
23
|
if (!requested || !path.isAbsolute(requested)) return null;
|
|
@@ -41,24 +46,31 @@ function isEligibleProjectFolder(folder) {
|
|
|
41
46
|
return !excludedRoots().some((root) => isInsideOrEqualPath(root, folder));
|
|
42
47
|
}
|
|
43
48
|
|
|
44
|
-
function
|
|
49
|
+
function readSources(file) {
|
|
45
50
|
try {
|
|
46
|
-
const parsed = JSON.parse(fs.readFileSync(
|
|
51
|
+
const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
47
52
|
return parsed?.sources && typeof parsed.sources === 'object' && !Array.isArray(parsed.sources) ? parsed.sources : {};
|
|
48
53
|
} catch {
|
|
49
54
|
return {};
|
|
50
55
|
}
|
|
51
56
|
}
|
|
52
57
|
|
|
58
|
+
function publishedFolders(file) {
|
|
59
|
+
const folders = Object.values(readSources(file)).flatMap((list) => (Array.isArray(list) ? list : []));
|
|
60
|
+
return [...new Set(folders.map(realDirectory).filter(Boolean))].filter(isEligibleProjectFolder);
|
|
61
|
+
}
|
|
62
|
+
|
|
53
63
|
/** Absolute, existing and de-duplicated project folders, across every publishing server. */
|
|
54
64
|
function registeredProjectFolders() {
|
|
55
|
-
|
|
56
|
-
|
|
65
|
+
return publishedFolders(projectsFile());
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function anonymizedProjectFolders() {
|
|
69
|
+
return publishedFolders(anonymizedProjectsFile());
|
|
57
70
|
}
|
|
58
71
|
|
|
59
|
-
function
|
|
60
|
-
const
|
|
61
|
-
const sources = readProjectSources();
|
|
72
|
+
function writeSource(file, sourceId, folders) {
|
|
73
|
+
const sources = readSources(file);
|
|
62
74
|
if (folders === null) delete sources[sourceId];
|
|
63
75
|
else sources[sourceId] = [...new Set(folders.map((folder) => path.resolve(String(folder))))].sort();
|
|
64
76
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
@@ -72,6 +84,11 @@ function publishProjectSource(sourceId, folders) {
|
|
|
72
84
|
}
|
|
73
85
|
}
|
|
74
86
|
|
|
87
|
+
function publishProjectSource(sourceId, folders, anonymizedFolders = []) {
|
|
88
|
+
writeSource(projectsFile(), sourceId, folders);
|
|
89
|
+
writeSource(anonymizedProjectsFile(), sourceId, folders === null ? null : anonymizedFolders);
|
|
90
|
+
}
|
|
91
|
+
|
|
75
92
|
function resolveTarget(target) {
|
|
76
93
|
try {
|
|
77
94
|
return fs.realpathSync(path.resolve(String(target)));
|
|
@@ -101,6 +118,7 @@ function locateProjectCase(target) {
|
|
|
101
118
|
for (const caseRoot of roots) {
|
|
102
119
|
if (!isInsideOrEqualPath(caseRoot, absolute)) continue;
|
|
103
120
|
return {
|
|
121
|
+
anonymized: anonymizedProjectFolders().includes(caseRoot),
|
|
104
122
|
casesRoot: path.dirname(caseRoot),
|
|
105
123
|
caseName: path.basename(caseRoot),
|
|
106
124
|
caseRoot,
|
|
@@ -113,6 +131,7 @@ function locateProjectCase(target) {
|
|
|
113
131
|
}
|
|
114
132
|
|
|
115
133
|
module.exports = {
|
|
134
|
+
anonymizedProjectFolders,
|
|
116
135
|
locateProjectCase,
|
|
117
136
|
publishProjectSource,
|
|
118
137
|
registeredProjectFolders,
|