@imenam/mcp-documentor 1.0.66 → 1.0.68
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/README.md +34 -11
- package/dist/shared/schemas/doc.schema.d.ts +15 -3
- package/dist/shared/schemas/doc.schema.js +35 -6
- package/dist/src/cli/setup-mcp.d.ts +5 -0
- package/dist/src/cli/setup-mcp.js +94 -0
- package/dist/src/core/log-dir.d.ts +6 -0
- package/dist/src/core/log-dir.js +12 -0
- package/dist/src/core/project-scope.d.ts +24 -0
- package/dist/src/core/project-scope.js +28 -0
- package/dist/src/core/storage-engine.d.ts +0 -4
- package/dist/src/core/storage-engine.js +4 -9
- package/dist/src/gui-worker.js +48 -8
- package/dist/src/index.js +135 -43
- package/dist/src/tools/check-freshness.d.ts +2 -2
- package/dist/src/tools/check-freshness.js +8 -7
- package/dist/src/tools/create-doc.js +4 -6
- package/dist/src/tools/delete-doc.js +14 -7
- package/dist/src/tools/get-doc-metadata.d.ts +2 -2
- package/dist/src/tools/get-doc-metadata.js +6 -5
- package/dist/src/tools/get-doc-version.js +5 -2
- package/dist/src/tools/get-doc.d.ts +2 -2
- package/dist/src/tools/get-doc.js +6 -5
- package/dist/src/tools/git-diff-doc.js +8 -4
- package/dist/src/tools/list-doc-versions.js +5 -2
- package/dist/src/tools/move-doc.js +5 -8
- package/dist/src/tools/patch-doc.js +6 -5
- package/dist/src/tools/refresh-file-snapshot.js +8 -4
- package/dist/src/tools/restore-doc-version.js +5 -2
- package/dist/src/tools/update-doc.d.ts +2 -2
- package/dist/src/tools/update-doc.js +6 -5
- package/dist/src/tools/upload-image.js +12 -3
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/gui/dist/assets/{arc-sHJlsIyi.js → arc-Carsq0k3.js} +1 -1
- package/gui/dist/assets/{architectureDiagram-3BPJPVTR-BiF2hxSq.js → architectureDiagram-3BPJPVTR-caqcwdTw.js} +1 -1
- package/gui/dist/assets/{blockDiagram-GPEHLZMM-BCnnBvty.js → blockDiagram-GPEHLZMM-0wUO8k2I.js} +1 -1
- package/gui/dist/assets/{c4Diagram-AAUBKEIU-Bu3KJ-ij.js → c4Diagram-AAUBKEIU-DS6q7fMy.js} +1 -1
- package/gui/dist/assets/channel-D51HexOc.js +1 -0
- package/gui/dist/assets/{chunk-2J33WTMH-C_ekvnEC.js → chunk-2J33WTMH-CrJc-G6D.js} +1 -1
- package/gui/dist/assets/{chunk-4BX2VUAB-Dmp27nYT.js → chunk-4BX2VUAB-C5vtPFbB.js} +1 -1
- package/gui/dist/assets/{chunk-55IACEB6-Jrgqw_BR.js → chunk-55IACEB6-BchyQUFM.js} +1 -1
- package/gui/dist/assets/{chunk-727SXJPM-BYwz54rU.js → chunk-727SXJPM-BCIaWtKq.js} +1 -1
- package/gui/dist/assets/{chunk-AQP2D5EJ-CuvuX4qY.js → chunk-AQP2D5EJ-BEQJDJt5.js} +1 -1
- package/gui/dist/assets/{chunk-FMBD7UC4-CNL_nFEL.js → chunk-FMBD7UC4-IfQ3QtrC.js} +1 -1
- package/gui/dist/assets/{chunk-ND2GUHAM-DECudjWh.js → chunk-ND2GUHAM-4Du33w7t.js} +1 -1
- package/gui/dist/assets/{chunk-QZHKN3VN-BAtGKt_6.js → chunk-QZHKN3VN-HR0CP6fa.js} +1 -1
- package/gui/dist/assets/classDiagram-4FO5ZUOK-CU0dQ3zy.js +1 -0
- package/gui/dist/assets/classDiagram-v2-Q7XG4LA2-CU0dQ3zy.js +1 -0
- package/gui/dist/assets/{cose-bilkent-S5V4N54A-DQ2M_2Lo.js → cose-bilkent-S5V4N54A-C9Re3ENx.js} +1 -1
- package/gui/dist/assets/{dagre-BM42HDAG-D6vlHW8t.js → dagre-BM42HDAG-DZmcvMMp.js} +1 -1
- package/gui/dist/assets/{diagram-2AECGRRQ-s2G4QZQa.js → diagram-2AECGRRQ-EyJrAGht.js} +1 -1
- package/gui/dist/assets/{diagram-5GNKFQAL-YyU3AqQI.js → diagram-5GNKFQAL-CHd3T9_b.js} +1 -1
- package/gui/dist/assets/{diagram-KO2AKTUF-BuFjXIUa.js → diagram-KO2AKTUF-CIyFrZSR.js} +1 -1
- package/gui/dist/assets/{diagram-LMA3HP47-CjO5OS2W.js → diagram-LMA3HP47-BlkJALyz.js} +1 -1
- package/gui/dist/assets/{diagram-OG6HWLK6-DVYIxc4z.js → diagram-OG6HWLK6-P8LA7IZE.js} +1 -1
- package/gui/dist/assets/{erDiagram-TEJ5UH35-BsxhnVVZ.js → erDiagram-TEJ5UH35-BPywgWN1.js} +1 -1
- package/gui/dist/assets/{flowDiagram-I6XJVG4X-D2BV25QW.js → flowDiagram-I6XJVG4X-ByL0kGiV.js} +1 -1
- package/gui/dist/assets/{ganttDiagram-6RSMTGT7-BR95C5C6.js → ganttDiagram-6RSMTGT7-_pnaYCU4.js} +1 -1
- package/gui/dist/assets/{gitGraphDiagram-PVQCEYII-bHC_XC7V.js → gitGraphDiagram-PVQCEYII-DN67CDGs.js} +1 -1
- package/gui/dist/assets/{index-DhwbMWam.js → index-Bjc5MOe1.js} +88 -88
- package/gui/dist/assets/{index-Cbo0HWer.css → index-hjkEDRmc.css} +1 -1
- package/gui/dist/assets/{infoDiagram-5YYISTIA-DE0rWygZ.js → infoDiagram-5YYISTIA-Bm5LEpzH.js} +1 -1
- package/gui/dist/assets/{ishikawaDiagram-YF4QCWOH-C03LZBFa.js → ishikawaDiagram-YF4QCWOH-DUYSULkT.js} +1 -1
- package/gui/dist/assets/{journeyDiagram-JHISSGLW-tRPsZRYs.js → journeyDiagram-JHISSGLW-BvJ1_f0L.js} +1 -1
- package/gui/dist/assets/{kanban-definition-UN3LZRKU-BYePPZ0q.js → kanban-definition-UN3LZRKU-DDYKg6RY.js} +1 -1
- package/gui/dist/assets/{linear-Bgo5v91h.js → linear-HlpG9u6P.js} +1 -1
- package/gui/dist/assets/{mindmap-definition-RKZ34NQL-C87iCyRq.js → mindmap-definition-RKZ34NQL-CPa_wo_s.js} +1 -1
- package/gui/dist/assets/{pieDiagram-4H26LBE5-BINdJQBG.js → pieDiagram-4H26LBE5-BWo0c4DP.js} +1 -1
- package/gui/dist/assets/{quadrantDiagram-W4KKPZXB-BNcy-pgy.js → quadrantDiagram-W4KKPZXB-CLahU-6B.js} +1 -1
- package/gui/dist/assets/{requirementDiagram-4Y6WPE33-D_DCIFsd.js → requirementDiagram-4Y6WPE33-BoYbgzQ7.js} +1 -1
- package/gui/dist/assets/{sankeyDiagram-5OEKKPKP-nCf_iEpj.js → sankeyDiagram-5OEKKPKP-CGm79aMo.js} +1 -1
- package/gui/dist/assets/{sequenceDiagram-3UESZ5HK-3rvwaBlu.js → sequenceDiagram-3UESZ5HK-BkknTD52.js} +1 -1
- package/gui/dist/assets/{stateDiagram-AJRCARHV-BRPOJHIL.js → stateDiagram-AJRCARHV-DSBurgwC.js} +1 -1
- package/gui/dist/assets/stateDiagram-v2-BHNVJYJU-DfKn22XF.js +1 -0
- package/gui/dist/assets/{timeline-definition-PNZ67QCA-C75DjOcj.js → timeline-definition-PNZ67QCA-CBHyrGuz.js} +1 -1
- package/gui/dist/assets/{vennDiagram-CIIHVFJN-CLZG8u4c.js → vennDiagram-CIIHVFJN-Dn9fr_bR.js} +1 -1
- package/gui/dist/assets/{wardley-L42UT6IY-BJnZFkDe.js → wardley-L42UT6IY-nMuySeAb.js} +1 -1
- package/gui/dist/assets/{wardleyDiagram-YWT4CUSO-CBvWRjIO.js → wardleyDiagram-YWT4CUSO-Byn2STLI.js} +1 -1
- package/gui/dist/assets/{xychartDiagram-2RQKCTM6-BGMFJ2t3.js → xychartDiagram-2RQKCTM6-Bi-Y9wp5.js} +1 -1
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/gui/dist/assets/channel-fkOAmVLo.js +0 -1
- package/gui/dist/assets/classDiagram-4FO5ZUOK-D_v2HS-t.js +0 -1
- package/gui/dist/assets/classDiagram-v2-Q7XG4LA2-D_v2HS-t.js +0 -1
- package/gui/dist/assets/stateDiagram-v2-BHNVJYJU-BUd1OQnG.js +0 -1
package/README.md
CHANGED
|
@@ -37,6 +37,21 @@ Global install exposes:
|
|
|
37
37
|
|
|
38
38
|
## MCP Configuration
|
|
39
39
|
|
|
40
|
+
### Quick setup
|
|
41
|
+
|
|
42
|
+
From the repo you want to document, generate a ready-to-fill `.mcp.json` in the current directory:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npx @imenam/mcp-documentor --claude-setup-mcp
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
It creates the `mcp-documentor` entry with the usual environment variables as empty placeholders —
|
|
49
|
+
an empty variable is simply inactive, so the generated config runs as-is and you only fill in what
|
|
50
|
+
you need. An existing `.mcp.json` is merged, other servers are preserved, and an existing
|
|
51
|
+
`mcp-documentor` entry is only replaced when `--force` is passed.
|
|
52
|
+
|
|
53
|
+
### Manual setup
|
|
54
|
+
|
|
40
55
|
Add to your MCP client config (e.g. Cursor `mcp.json`):
|
|
41
56
|
|
|
42
57
|
```json
|
|
@@ -60,7 +75,9 @@ Add to your MCP client config (e.g. Cursor `mcp.json`):
|
|
|
60
75
|
| `OPENAI_API_KEY` | No | Enables semantic search via OpenAI embeddings (`search_docs`) |
|
|
61
76
|
| `MCP_DOCUMENTOR_OPENAI_MODEL` | No | Embedding model (default: `text-embedding-3-small`) |
|
|
62
77
|
| `PROXY_URL` | No | URL of the proxy server to register and launch the GUI |
|
|
78
|
+
| `APP_GROUP` | No | Groups the app under a collapsible section (folded by default) in the proxy dashboard. Apps sharing the same value appear together; if unset, the app lands in the default "ungrouped" section |
|
|
63
79
|
| `MCP_DATA_DIR` | No | Overrides the default data directory (see Data storage) |
|
|
80
|
+
| `MCP_LOG_DIR` | No | Overrides the log directory of both the server and the GUI worker (default: `<package>/.docs-data/logs`). Must be set in the MCP client config — the package `.env` is loaded after the logger starts |
|
|
64
81
|
| `MCP_DOCUMENTOR_CODE_ROOT` | No | Base directory that tracked-file paths are stored relative to, keeping docs portable across machines (see [Portable tracked-file paths](#portable-tracked-file-paths)) |
|
|
65
82
|
| `MCP_DOCUMENTOR_CODE_ROOTS` | No | JSON map of per-project code-root overrides, e.g. `{"my-proj":"/srv/my-proj"}` |
|
|
66
83
|
| `READ_ONLY` | No | When set to `1`/`true`/`yes`, disables all create/update/delete tools and GUI write actions (read-only mode) |
|
|
@@ -73,25 +90,31 @@ Optional `.env` at the package root (directory containing `package.json` discove
|
|
|
73
90
|
|
|
74
91
|
## Available Tools
|
|
75
92
|
|
|
93
|
+
Every tool that targets a specific documentation entry takes a **mandatory `project`**. It is never
|
|
94
|
+
guessed and never falls back to the active project: resolving a doc path across projects is what
|
|
95
|
+
made operations silently hit the wrong project when the same path existed in several. Tools that
|
|
96
|
+
work on a whole project (`list_docs`, `get_doc_tree`, `check_links`, `check_freshness` without a
|
|
97
|
+
`path`, `reindex_embeddings`) keep `project` optional, since omitting it means "all projects".
|
|
98
|
+
|
|
76
99
|
| Tool | Description |
|
|
77
100
|
|---|---|
|
|
78
|
-
| `create_doc` | Create a new documentation entry linked to source files |
|
|
79
|
-
| `get_doc` | Retrieve a documentation entry with full content |
|
|
80
|
-
| `get_doc_metadata` | Get metadata and freshness status without loading content |
|
|
101
|
+
| `create_doc` | Create a new documentation entry linked to source files (`project` required) |
|
|
102
|
+
| `get_doc` | Retrieve a documentation entry with full content (`project` required) |
|
|
103
|
+
| `get_doc_metadata` | Get metadata and freshness status without loading content (`project` required) |
|
|
81
104
|
| `list_docs` | List docs with staleness indicators (optionally filter by project) |
|
|
82
105
|
| `get_doc_tree` | Return folder/doc structure for a project (or all projects) |
|
|
83
|
-
| `update_doc` | Fully replace content and/or metadata (prefer `patch_doc` for small edits) |
|
|
84
|
-
| `patch_doc` | Search-and-replace a fragment of a doc’s content |
|
|
85
|
-
| `move_doc` | Change a doc’s path (folder/name)
|
|
86
|
-
| `delete_doc` | Permanently delete a documentation entry |
|
|
106
|
+
| `update_doc` | Fully replace content and/or metadata (`project` required; prefer `patch_doc` for small edits) |
|
|
107
|
+
| `patch_doc` | Search-and-replace a fragment of a doc’s content (`project` required) |
|
|
108
|
+
| `move_doc` | Change a doc’s path (folder/name) within its project (`project` required) |
|
|
109
|
+
| `delete_doc` | Permanently delete a documentation entry (`project` required) |
|
|
87
110
|
| `search_docs` | Semantic search over docs (natural language; requires OpenAI) |
|
|
88
|
-
| `check_freshness` | Staleness for one doc or a full project scan |
|
|
89
|
-
| `refresh_file_snapshot` | Mark tracked files as up-to-date without editing the doc |
|
|
90
|
-
| `git_diff_doc` | Git diff for tracked files vs snapshot baseline |
|
|
111
|
+
| `check_freshness` | Staleness for one doc (`project` required) or a full project scan |
|
|
112
|
+
| `refresh_file_snapshot` | Mark tracked files as up-to-date without editing the doc (`project` required) |
|
|
113
|
+
| `git_diff_doc` | Git diff for tracked files vs snapshot baseline (`project` required) |
|
|
91
114
|
| `check_links` | Validate `related_docs`, `doc://` links, and `#heading` anchors |
|
|
92
115
|
| `list_projects` | List all documentation projects |
|
|
93
116
|
| `set_project` | Switch the active project **for the GUI only** (not for MCP tools) |
|
|
94
|
-
| `upload_image` | Upload an image from a local file path to a project's assets; returns the markdown link to insert |
|
|
117
|
+
| `upload_image` | Upload an image from a local file path to a project's assets (`project` required); returns the markdown link to insert |
|
|
95
118
|
| `sync_status` | Show branch, ahead/behind counts, conflicted files, and uncommitted changes |
|
|
96
119
|
| `pull_docs` | Pull updates from the remote (defensive fast-forward; reindexes new docs) |
|
|
97
120
|
| `push_docs` | Commit local doc changes and push them to the remote |
|
|
@@ -94,6 +94,7 @@ export declare const FreshnessReportSchema: z.ZodObject<{
|
|
|
94
94
|
}, z.core.$strip>;
|
|
95
95
|
export type FreshnessReport = z.infer<typeof FreshnessReportSchema>;
|
|
96
96
|
export declare const CreateDocArgsSchema: z.ZodObject<{
|
|
97
|
+
project: z.ZodString;
|
|
97
98
|
title: z.ZodString;
|
|
98
99
|
content: z.ZodOptional<z.ZodString>;
|
|
99
100
|
content_file: z.ZodOptional<z.ZodString>;
|
|
@@ -110,6 +111,7 @@ export declare const CreateDocArgsSchema: z.ZodObject<{
|
|
|
110
111
|
}, z.core.$strip>;
|
|
111
112
|
export declare const UpdateDocArgsSchema: z.ZodObject<{
|
|
112
113
|
path: z.ZodString;
|
|
114
|
+
project: z.ZodString;
|
|
113
115
|
title: z.ZodOptional<z.ZodString>;
|
|
114
116
|
content: z.ZodOptional<z.ZodString>;
|
|
115
117
|
content_file: z.ZodOptional<z.ZodString>;
|
|
@@ -125,10 +127,16 @@ export declare const UpdateDocArgsSchema: z.ZodObject<{
|
|
|
125
127
|
}, z.core.$strip>;
|
|
126
128
|
export declare const GetDocArgsSchema: z.ZodObject<{
|
|
127
129
|
path: z.ZodString;
|
|
130
|
+
project: z.ZodString;
|
|
131
|
+
}, z.core.$strip>;
|
|
132
|
+
export declare const DeleteDocArgsSchema: z.ZodObject<{
|
|
133
|
+
path: z.ZodString;
|
|
134
|
+
project: z.ZodString;
|
|
128
135
|
}, z.core.$strip>;
|
|
129
136
|
export declare const MoveDocArgsSchema: z.ZodObject<{
|
|
130
137
|
path: z.ZodString;
|
|
131
138
|
new_path: z.ZodString;
|
|
139
|
+
project: z.ZodString;
|
|
132
140
|
}, z.core.$strip>;
|
|
133
141
|
export declare const CheckFreshnessArgsSchema: z.ZodObject<{
|
|
134
142
|
path: z.ZodOptional<z.ZodString>;
|
|
@@ -140,13 +148,16 @@ export declare const SearchDocsArgsSchema: z.ZodObject<{
|
|
|
140
148
|
}, z.core.$strip>;
|
|
141
149
|
export declare const RefreshFileSnapshotArgsSchema: z.ZodObject<{
|
|
142
150
|
paths: z.ZodArray<z.ZodString>;
|
|
151
|
+
project: z.ZodString;
|
|
143
152
|
file_paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
144
153
|
}, z.core.$strip>;
|
|
145
154
|
export declare const GetDocMetadataArgsSchema: z.ZodObject<{
|
|
146
155
|
path: z.ZodString;
|
|
156
|
+
project: z.ZodString;
|
|
147
157
|
}, z.core.$strip>;
|
|
148
158
|
export declare const PatchDocArgsSchema: z.ZodObject<{
|
|
149
159
|
path: z.ZodString;
|
|
160
|
+
project: z.ZodString;
|
|
150
161
|
search: z.ZodOptional<z.ZodString>;
|
|
151
162
|
search_file: z.ZodOptional<z.ZodString>;
|
|
152
163
|
replacement: z.ZodOptional<z.ZodString>;
|
|
@@ -157,6 +168,7 @@ export declare const CheckLinksArgsSchema: z.ZodObject<{
|
|
|
157
168
|
}, z.core.$strip>;
|
|
158
169
|
export declare const GitDiffDocArgsSchema: z.ZodObject<{
|
|
159
170
|
paths: z.ZodArray<z.ZodString>;
|
|
171
|
+
project: z.ZodString;
|
|
160
172
|
file_paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
161
173
|
}, z.core.$strip>;
|
|
162
174
|
export declare const SetProjectArgsSchema: z.ZodObject<{
|
|
@@ -164,17 +176,17 @@ export declare const SetProjectArgsSchema: z.ZodObject<{
|
|
|
164
176
|
}, z.core.$strip>;
|
|
165
177
|
export declare const ListDocVersionsArgsSchema: z.ZodObject<{
|
|
166
178
|
path: z.ZodString;
|
|
167
|
-
project: z.
|
|
179
|
+
project: z.ZodString;
|
|
168
180
|
}, z.core.$strip>;
|
|
169
181
|
export declare const GetDocVersionArgsSchema: z.ZodObject<{
|
|
170
182
|
path: z.ZodString;
|
|
171
183
|
version: z.ZodNumber;
|
|
172
|
-
project: z.
|
|
184
|
+
project: z.ZodString;
|
|
173
185
|
}, z.core.$strip>;
|
|
174
186
|
export declare const RestoreDocVersionArgsSchema: z.ZodObject<{
|
|
175
187
|
path: z.ZodString;
|
|
176
188
|
version: z.ZodNumber;
|
|
177
|
-
project: z.
|
|
189
|
+
project: z.ZodString;
|
|
178
190
|
}, z.core.$strip>;
|
|
179
191
|
export declare const ExportBundleSchema: z.ZodObject<{
|
|
180
192
|
id: z.ZodString;
|
|
@@ -45,7 +45,17 @@ export const FreshnessReportSchema = z.object({
|
|
|
45
45
|
broken: z.array(z.string()).default([]),
|
|
46
46
|
total: z.number(),
|
|
47
47
|
});
|
|
48
|
+
/**
|
|
49
|
+
* Every tool that targets a specific documentation entry takes this. It is mandatory
|
|
50
|
+
* and never defaulted: resolving a doc path across projects is what let operations
|
|
51
|
+
* silently hit the wrong project.
|
|
52
|
+
*/
|
|
53
|
+
const requiredProject = (action) => z
|
|
54
|
+
.string()
|
|
55
|
+
.min(1)
|
|
56
|
+
.describe(`Project the documentation entry belongs to. Mandatory — ${action} is never resolved across projects, and never falls back to the current project.`);
|
|
48
57
|
export const CreateDocArgsSchema = z.object({
|
|
58
|
+
project: requiredProject("creation").describe("Project to create the documentation entry in. Mandatory — never defaulted to the current project. The project is created automatically if it does not exist yet."),
|
|
49
59
|
title: z.string().describe("Title of the documentation entry"),
|
|
50
60
|
content: z.string().optional().describe("Documentation content in markdown"),
|
|
51
61
|
content_file: z.string().optional().describe("Absolute path to a text file whose content will be used instead of the content parameter. Use this to reuse a previously persisted draft."),
|
|
@@ -58,6 +68,7 @@ export const CreateDocArgsSchema = z.object({
|
|
|
58
68
|
});
|
|
59
69
|
export const UpdateDocArgsSchema = z.object({
|
|
60
70
|
path: z.string().describe("Path of the documentation entry to update, e.g. 'backend/auth/authentication' or 'authentication' for root docs"),
|
|
71
|
+
project: requiredProject("the update"),
|
|
61
72
|
title: z.string().optional().describe("Title of the documentation entry"),
|
|
62
73
|
content: z.string().optional().describe("New content (replaces existing)"),
|
|
63
74
|
content_file: z.string().optional().describe("Absolute path to a text file whose content will be used instead of the content parameter. Use this to reuse a previously persisted draft."),
|
|
@@ -67,14 +78,25 @@ export const UpdateDocArgsSchema = z.object({
|
|
|
67
78
|
});
|
|
68
79
|
export const GetDocArgsSchema = z.object({
|
|
69
80
|
path: z.string().describe("Path of the documentation entry, e.g. 'backend/auth/authentication' or 'authentication' for root docs"),
|
|
81
|
+
project: requiredProject("the lookup"),
|
|
82
|
+
});
|
|
83
|
+
export const DeleteDocArgsSchema = z.object({
|
|
84
|
+
path: z.string().describe("Path of the documentation entry to delete, e.g. 'backend/auth/authentication'"),
|
|
85
|
+
project: requiredProject("deletion"),
|
|
70
86
|
});
|
|
71
87
|
export const MoveDocArgsSchema = z.object({
|
|
72
88
|
path: z.string().describe("Current path of the documentation entry, e.g. 'backend/auth/authentication'"),
|
|
73
89
|
new_path: z.string().describe("New path for the documentation entry, e.g. 'backend/v2/authentication'. Changes folder and/or name."),
|
|
90
|
+
project: requiredProject("the move"),
|
|
74
91
|
});
|
|
75
|
-
export const CheckFreshnessArgsSchema = z
|
|
92
|
+
export const CheckFreshnessArgsSchema = z
|
|
93
|
+
.object({
|
|
76
94
|
path: z.string().optional().describe("Path of a single documentation entry to check, e.g. 'backend/auth/authentication'. If omitted, checks all docs in the project."),
|
|
77
|
-
project: z.string().optional().describe("Project to
|
|
95
|
+
project: z.string().min(1).optional().describe("Project to work in. Mandatory when path is provided; when path is omitted it selects the project to scan (all projects if omitted too)."),
|
|
96
|
+
})
|
|
97
|
+
.refine((d) => !d.path || !!d.project, {
|
|
98
|
+
message: "project is required when path is provided — freshness of a single entry is never resolved across projects",
|
|
99
|
+
path: ["project"],
|
|
78
100
|
});
|
|
79
101
|
export const SearchDocsArgsSchema = z.object({
|
|
80
102
|
query: z.string().describe("Search query in natural language"),
|
|
@@ -82,13 +104,16 @@ export const SearchDocsArgsSchema = z.object({
|
|
|
82
104
|
});
|
|
83
105
|
export const RefreshFileSnapshotArgsSchema = z.object({
|
|
84
106
|
paths: z.array(z.string()).min(1).describe("Paths of documentation entries to refresh, e.g. ['backend/auth/authentication', 'setup']"),
|
|
107
|
+
project: requiredProject("the refresh").describe("Project holding every entry listed in paths. Mandatory — snapshots are never refreshed across projects. Call once per project if the entries are spread across several."),
|
|
85
108
|
file_paths: z.array(z.string()).optional().describe("Specific tracked files to refresh, as absolute paths (they are matched against the stored paths, relative or absolute). Refreshes all tracked files if omitted."),
|
|
86
109
|
});
|
|
87
110
|
export const GetDocMetadataArgsSchema = z.object({
|
|
88
111
|
path: z.string().describe("Path of the documentation entry, e.g. 'backend/auth/authentication'"),
|
|
112
|
+
project: requiredProject("the lookup"),
|
|
89
113
|
});
|
|
90
114
|
export const PatchDocArgsSchema = z.object({
|
|
91
115
|
path: z.string().describe("Path of the documentation entry to patch, e.g. 'backend/auth/authentication'"),
|
|
116
|
+
project: requiredProject("the patch"),
|
|
92
117
|
search: z.string().optional().describe("Exact text to find in the current content"),
|
|
93
118
|
search_file: z.string().optional().describe("Absolute path to a text file whose content will be used as the search text. Use this to reuse a previously persisted draft."),
|
|
94
119
|
replacement: z.string().optional().describe("Text to replace it with"),
|
|
@@ -103,6 +128,7 @@ export const CheckLinksArgsSchema = z.object({
|
|
|
103
128
|
});
|
|
104
129
|
export const GitDiffDocArgsSchema = z.object({
|
|
105
130
|
paths: z.array(z.string()).min(1).describe("Paths of documentation entries to diff, e.g. ['backend/auth/authentication']"),
|
|
131
|
+
project: requiredProject("the diff").describe("Project holding every entry listed in paths. Mandatory — entries are never resolved across projects. Call once per project if they are spread across several."),
|
|
106
132
|
file_paths: z.array(z.string()).optional().describe("Specific tracked files to diff, as absolute paths (matched against the stored paths, relative or absolute). All tracked files if omitted."),
|
|
107
133
|
});
|
|
108
134
|
export const SetProjectArgsSchema = z.object({
|
|
@@ -110,17 +136,17 @@ export const SetProjectArgsSchema = z.object({
|
|
|
110
136
|
});
|
|
111
137
|
export const ListDocVersionsArgsSchema = z.object({
|
|
112
138
|
path: z.string(),
|
|
113
|
-
project:
|
|
139
|
+
project: requiredProject("history lookup"),
|
|
114
140
|
});
|
|
115
141
|
export const GetDocVersionArgsSchema = z.object({
|
|
116
142
|
path: z.string(),
|
|
117
143
|
version: z.number().int().positive(),
|
|
118
|
-
project:
|
|
144
|
+
project: requiredProject("history lookup"),
|
|
119
145
|
});
|
|
120
146
|
export const RestoreDocVersionArgsSchema = z.object({
|
|
121
147
|
path: z.string(),
|
|
122
148
|
version: z.number().int().positive(),
|
|
123
|
-
project:
|
|
149
|
+
project: requiredProject("the restore"),
|
|
124
150
|
});
|
|
125
151
|
export const ExportBundleSchema = z.object({
|
|
126
152
|
id: z.string(),
|
|
@@ -134,7 +160,10 @@ export const ExportBundleSchema = z.object({
|
|
|
134
160
|
export const PrepareExportBundleArgsSchema = z.object({
|
|
135
161
|
name: z.string().describe("Bundle name (becomes the identifier via slugify)"),
|
|
136
162
|
zip_filename: z.string().describe("ZIP filename to download, e.g. 'my-bundle.zip'"),
|
|
137
|
-
docs: z
|
|
163
|
+
docs: z
|
|
164
|
+
.array(z.string().regex(/^[^:]+:.+$/, "each doc reference must be fully qualified as 'project:doc-id'"))
|
|
165
|
+
.min(1)
|
|
166
|
+
.describe("Doc references, each fully qualified as 'project:doc-id'. The project prefix is mandatory — a bare 'doc-id' is rejected so a bundle can never pick up the wrong project's entry."),
|
|
138
167
|
description: z.string().optional().describe("Short description of the bundle"),
|
|
139
168
|
});
|
|
140
169
|
export const DeleteExportBundleArgsSchema = z.object({
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
const SERVER_NAME = "mcp-documentor";
|
|
4
|
+
const PACKAGE_NAME = "@imenam/mcp-documentor";
|
|
5
|
+
/**
|
|
6
|
+
* Valeurs volontairement vides : chaque variable est inactive tant qu'elle n'est
|
|
7
|
+
* pas remplie (PROXY_URL vide = GUI désactivée, clé vide = pas d'embeddings),
|
|
8
|
+
* donc le fichier généré est utilisable tel quel, jamais à moitié cassé.
|
|
9
|
+
*/
|
|
10
|
+
function buildServerEntry() {
|
|
11
|
+
return {
|
|
12
|
+
command: "npx",
|
|
13
|
+
args: ["-y", PACKAGE_NAME],
|
|
14
|
+
env: {
|
|
15
|
+
PROXY_URL: "",
|
|
16
|
+
APP_GROUP: "",
|
|
17
|
+
APP_PATH: "",
|
|
18
|
+
MCP_DATA_DIR: "",
|
|
19
|
+
MCP_LOG_DIR: "",
|
|
20
|
+
MCP_DOCUMENTOR_CODE_ROOT: "",
|
|
21
|
+
MCP_DOCUMENTOR_OPENAI_API_KEY: "",
|
|
22
|
+
MCP_DOCUMENTOR_OPENAI_MODEL: "text-embedding-3-small",
|
|
23
|
+
MCP_DOCUMENTOR_SYNC_REMOTE: "",
|
|
24
|
+
MCP_DOCUMENTOR_SYNC_BRANCH: "",
|
|
25
|
+
MCP_DOCUMENTOR_SYNC_TOKEN: "",
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const GENERATED_VARS = [
|
|
30
|
+
["PROXY_URL", "URL du proxy pour exposer la GUI"],
|
|
31
|
+
["APP_GROUP", "groupe d'affichage dans le dashboard du proxy"],
|
|
32
|
+
["APP_PATH", "chemin de montage de la GUI derrière le proxy"],
|
|
33
|
+
["MCP_DATA_DIR", "répertoire de données (défaut : celui du package)"],
|
|
34
|
+
["MCP_LOG_DIR", "répertoire des logs"],
|
|
35
|
+
["MCP_DOCUMENTOR_CODE_ROOT", "racine du code pour des chemins trackés portables"],
|
|
36
|
+
["MCP_DOCUMENTOR_OPENAI_API_KEY", "clé OpenAI (active search_docs)"],
|
|
37
|
+
["MCP_DOCUMENTOR_OPENAI_MODEL", "modèle d'embedding"],
|
|
38
|
+
["MCP_DOCUMENTOR_SYNC_REMOTE", "URL HTTPS du repo privé de sync GitHub"],
|
|
39
|
+
["MCP_DOCUMENTOR_SYNC_BRANCH", "branche de sync (défaut : main)"],
|
|
40
|
+
["MCP_DOCUMENTOR_SYNC_TOKEN", "PAT GitHub fine-grained pour la sync"],
|
|
41
|
+
];
|
|
42
|
+
const OPTIONAL_VARS = [
|
|
43
|
+
["MCP_DOCUMENTOR_CODE_ROOTS", 'code roots par projet, ex: {"my-proj":"/srv/my-proj"}'],
|
|
44
|
+
["READ_ONLY", "1/true pour désactiver toutes les écritures"],
|
|
45
|
+
["SYNC_PULL_ON_START", "false pour désactiver le pull au démarrage"],
|
|
46
|
+
];
|
|
47
|
+
export function runSetupMcp({ cwd, force }) {
|
|
48
|
+
const target = path.join(cwd, ".mcp.json");
|
|
49
|
+
let config = {};
|
|
50
|
+
const exists = fs.existsSync(target);
|
|
51
|
+
if (exists) {
|
|
52
|
+
const raw = fs.readFileSync(target, "utf-8").trim();
|
|
53
|
+
if (raw !== "") {
|
|
54
|
+
try {
|
|
55
|
+
config = JSON.parse(raw);
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
console.error(`Erreur : ${target} contient du JSON invalide (${err.message}).`);
|
|
59
|
+
console.error("Corrigez le fichier ou supprimez-le, puis relancez la commande.");
|
|
60
|
+
return 1;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (typeof config !== "object" || config === null || Array.isArray(config)) {
|
|
64
|
+
console.error(`Erreur : ${target} ne contient pas un objet JSON.`);
|
|
65
|
+
return 1;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (typeof config.mcpServers !== "object" || config.mcpServers === null || Array.isArray(config.mcpServers)) {
|
|
69
|
+
config.mcpServers = {};
|
|
70
|
+
}
|
|
71
|
+
const alreadyPresent = Object.prototype.hasOwnProperty.call(config.mcpServers, SERVER_NAME);
|
|
72
|
+
if (alreadyPresent && !force) {
|
|
73
|
+
console.error(`Le serveur "${SERVER_NAME}" est déjà configuré dans ${target}.`);
|
|
74
|
+
console.error("Relancez avec --force pour écraser cette entrée (les autres serveurs sont préservés).");
|
|
75
|
+
return 1;
|
|
76
|
+
}
|
|
77
|
+
config.mcpServers[SERVER_NAME] = buildServerEntry();
|
|
78
|
+
fs.writeFileSync(target, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
79
|
+
const action = alreadyPresent ? "remplacée" : exists ? "ajoutée" : "créée";
|
|
80
|
+
console.log(`Configuration ${SERVER_NAME} ${action} dans ${target}`);
|
|
81
|
+
console.log("");
|
|
82
|
+
console.log('À compléter dans le champ "env" :');
|
|
83
|
+
for (const [name, description] of GENERATED_VARS) {
|
|
84
|
+
console.log(` ${name.padEnd(30)} ${description}`);
|
|
85
|
+
}
|
|
86
|
+
console.log("");
|
|
87
|
+
console.log("Variables optionnelles à ajouter au besoin :");
|
|
88
|
+
for (const [name, description] of OPTIONAL_VARS) {
|
|
89
|
+
console.log(` ${name.padEnd(30)} ${description}`);
|
|
90
|
+
}
|
|
91
|
+
console.log("");
|
|
92
|
+
console.log("Une variable laissée vide est simplement inactive.");
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Répertoire des logs : MCP_LOG_DIR s'il est renseigné, sinon <racine>/.docs-data/logs.
|
|
3
|
+
* Lu depuis process.env uniquement (le .env du package est chargé après l'init du
|
|
4
|
+
* logger), donc la variable doit venir de la config du client MCP.
|
|
5
|
+
*/
|
|
6
|
+
export declare function resolveLogDir(rootDir: string): string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
/**
|
|
3
|
+
* Répertoire des logs : MCP_LOG_DIR s'il est renseigné, sinon <racine>/.docs-data/logs.
|
|
4
|
+
* Lu depuis process.env uniquement (le .env du package est chargé après l'init du
|
|
5
|
+
* logger), donc la variable doit venir de la config du client MCP.
|
|
6
|
+
*/
|
|
7
|
+
export function resolveLogDir(rootDir) {
|
|
8
|
+
const configured = process.env.MCP_LOG_DIR?.trim();
|
|
9
|
+
if (configured)
|
|
10
|
+
return path.resolve(configured);
|
|
11
|
+
return path.join(rootDir, ".docs-data", "logs");
|
|
12
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { StorageEngine } from "./storage-engine.js";
|
|
2
|
+
import type { DocEntry } from "../../shared/schemas/doc.schema.js";
|
|
3
|
+
/**
|
|
4
|
+
* Doc-targeted tools resolve their entry inside one explicitly named project.
|
|
5
|
+
* Cross-project resolution (storageEngine.resolveDoc) is deliberately not used
|
|
6
|
+
* here: it picked the first project holding the path, which silently sent reads
|
|
7
|
+
* and writes to the wrong project when the same path exists in several.
|
|
8
|
+
*/
|
|
9
|
+
export type ScopeError = {
|
|
10
|
+
errorText: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function isScopeError(value: unknown): value is ScopeError;
|
|
13
|
+
/** Wraps a scope error into the MCP tool result shape. */
|
|
14
|
+
export declare function scopeErrorResult(error: ScopeError): {
|
|
15
|
+
isError: true;
|
|
16
|
+
content: {
|
|
17
|
+
type: string;
|
|
18
|
+
text: string;
|
|
19
|
+
}[];
|
|
20
|
+
};
|
|
21
|
+
export declare function assertProject(storageEngine: StorageEngine, project: string): ScopeError | null;
|
|
22
|
+
export declare function resolveDocInProject(storageEngine: StorageEngine, docPath: string, project: string, action?: string): Promise<{
|
|
23
|
+
doc: DocEntry;
|
|
24
|
+
} | ScopeError>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function isScopeError(value) {
|
|
2
|
+
return typeof value === "object" && value !== null && "errorText" in value;
|
|
3
|
+
}
|
|
4
|
+
/** Wraps a scope error into the MCP tool result shape. */
|
|
5
|
+
export function scopeErrorResult(error) {
|
|
6
|
+
return { isError: true, content: [{ type: "text", text: error.errorText }] };
|
|
7
|
+
}
|
|
8
|
+
export function assertProject(storageEngine, project) {
|
|
9
|
+
const knownProjects = storageEngine.getConfig().projects;
|
|
10
|
+
if (!knownProjects.includes(project)) {
|
|
11
|
+
return {
|
|
12
|
+
errorText: `Project "${project}" does not exist. Known projects: ${knownProjects.join(", ")}.`,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
export async function resolveDocInProject(storageEngine, docPath, project, action = "operation") {
|
|
18
|
+
const projectError = assertProject(storageEngine, project);
|
|
19
|
+
if (projectError)
|
|
20
|
+
return projectError;
|
|
21
|
+
const doc = await storageEngine.getDoc(docPath, project);
|
|
22
|
+
if (!doc) {
|
|
23
|
+
return {
|
|
24
|
+
errorText: `Documentation entry "${docPath}" not found in project "${project}" — no ${action} performed. Check the project with list_projects or get_doc_tree before retrying.`,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return { doc };
|
|
28
|
+
}
|
|
@@ -44,10 +44,6 @@ export declare class StorageEngine {
|
|
|
44
44
|
saveDoc(doc: DocEntry, project?: string, changeType?: "create" | "update" | "patch"): Promise<void>;
|
|
45
45
|
getDoc(id: string, project?: string): Promise<DocEntry | null>;
|
|
46
46
|
listDocs(project?: string): Promise<DocSummary[]>;
|
|
47
|
-
resolveDoc(id: string): Promise<{
|
|
48
|
-
doc: DocEntry;
|
|
49
|
-
project: string;
|
|
50
|
-
} | null>;
|
|
51
47
|
deleteDoc(id: string, project?: string): Promise<boolean>;
|
|
52
48
|
moveDoc(oldId: string, newId: string, project?: string): Promise<boolean>;
|
|
53
49
|
getBundles(): ExportBundle[];
|
|
@@ -215,15 +215,10 @@ export class StorageEngine {
|
|
|
215
215
|
}
|
|
216
216
|
return summaries;
|
|
217
217
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
if (doc)
|
|
223
|
-
return { doc, project };
|
|
224
|
-
}
|
|
225
|
-
return null;
|
|
226
|
-
}
|
|
218
|
+
// resolveDoc() (first project whose tree contained the path) was removed on purpose:
|
|
219
|
+
// it silently sent reads and writes to the wrong project when the same doc path
|
|
220
|
+
// existed in several. Doc-targeted callers must name their project and go through
|
|
221
|
+
// getDoc(id, project) — see core/project-scope.ts.
|
|
227
222
|
async deleteDoc(id, project) {
|
|
228
223
|
const config = this.getConfig();
|
|
229
224
|
const proj = project ?? config.currentProject;
|
package/dist/src/gui-worker.js
CHANGED
|
@@ -12,6 +12,7 @@ import { promisify } from "util";
|
|
|
12
12
|
const execFileAsync = promisify(execFile);
|
|
13
13
|
import { fileURLToPath } from "url";
|
|
14
14
|
import { createLogger, setupLogging } from "@imenam/mcp-gui-interface";
|
|
15
|
+
import { resolveLogDir } from "./core/log-dir.js";
|
|
15
16
|
import matter from "gray-matter";
|
|
16
17
|
import JSZip from "jszip";
|
|
17
18
|
import puppeteer from "puppeteer-core";
|
|
@@ -31,7 +32,7 @@ const dataDir = process.env.MCP_DATA_DIR?.trim() || path.join(rootDir, ".docs-da
|
|
|
31
32
|
const staticRoot = path.join(rootDir, "gui/dist");
|
|
32
33
|
const absoluteStaticRoot = path.resolve(staticRoot);
|
|
33
34
|
const configuredAppPath = process.env.APP_PATH || "/documentor-gui";
|
|
34
|
-
setupLogging({ processLabel: "mcp-documentor:GUI", logDir:
|
|
35
|
+
setupLogging({ processLabel: "mcp-documentor:GUI", logDir: resolveLogDir(rootDir) });
|
|
35
36
|
const logger = createLogger("mcp-documentor:GUI");
|
|
36
37
|
process.on("uncaughtException", (err) => {
|
|
37
38
|
logger.error(`Uncaught Exception: ${err.message}`);
|
|
@@ -347,6 +348,9 @@ app.post("/api/doc", async (c) => {
|
|
|
347
348
|
if (!body.title || !body.content) {
|
|
348
349
|
return c.json({ error: "title and content are required" }, 400);
|
|
349
350
|
}
|
|
351
|
+
if (!body.project) {
|
|
352
|
+
return c.json({ error: "project is required" }, 400);
|
|
353
|
+
}
|
|
350
354
|
const result = await ipcRequest("CREATE_DOC", {
|
|
351
355
|
title: body.title,
|
|
352
356
|
content: body.content,
|
|
@@ -364,7 +368,10 @@ app.post("/api/doc", async (c) => {
|
|
|
364
368
|
});
|
|
365
369
|
app.get("/api/doc/*", async (c) => {
|
|
366
370
|
const id = decodeURIComponent(c.req.path.slice("/api/doc/".length));
|
|
367
|
-
const
|
|
371
|
+
const project = c.req.query("project");
|
|
372
|
+
if (!project)
|
|
373
|
+
return c.json({ error: "project query param is required" }, 400);
|
|
374
|
+
const result = await ipcRequest("GET_DOC", { id, project });
|
|
368
375
|
if (result.error)
|
|
369
376
|
return c.json({ error: result.error }, 400);
|
|
370
377
|
return c.json(result.data);
|
|
@@ -372,11 +379,29 @@ app.get("/api/doc/*", async (c) => {
|
|
|
372
379
|
app.put("/api/doc/*", async (c) => {
|
|
373
380
|
try {
|
|
374
381
|
const id = decodeURIComponent(c.req.path.slice("/api/doc/".length));
|
|
382
|
+
const project = c.req.query("project");
|
|
383
|
+
if (!project)
|
|
384
|
+
return c.json({ error: "project query param is required" }, 400);
|
|
375
385
|
const body = await c.req.json();
|
|
376
386
|
if (body.content === undefined) {
|
|
377
387
|
return c.json({ error: "content is required" }, 400);
|
|
378
388
|
}
|
|
379
|
-
const result = await ipcRequest("UPDATE_DOC", { id, content: body.content, title: body.title }, 15000);
|
|
389
|
+
const result = await ipcRequest("UPDATE_DOC", { id, project, content: body.content, title: body.title }, 15000);
|
|
390
|
+
if (result.error)
|
|
391
|
+
return c.json({ error: result.error }, 500);
|
|
392
|
+
return c.json(result.data ?? { success: true });
|
|
393
|
+
}
|
|
394
|
+
catch (err) {
|
|
395
|
+
return c.json({ error: err.message }, 500);
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
app.delete("/api/doc/*", async (c) => {
|
|
399
|
+
try {
|
|
400
|
+
const id = decodeURIComponent(c.req.path.slice("/api/doc/".length));
|
|
401
|
+
const project = c.req.query("project");
|
|
402
|
+
if (!project)
|
|
403
|
+
return c.json({ error: "project query param is required" }, 400);
|
|
404
|
+
const result = await ipcRequest("DELETE_DOC", { id, project }, 15000);
|
|
380
405
|
if (result.error)
|
|
381
406
|
return c.json({ error: result.error }, 500);
|
|
382
407
|
return c.json(result.data ?? { success: true });
|
|
@@ -387,21 +412,30 @@ app.put("/api/doc/*", async (c) => {
|
|
|
387
412
|
});
|
|
388
413
|
app.get("/api/freshness/*", async (c) => {
|
|
389
414
|
const id = decodeURIComponent(c.req.path.slice("/api/freshness/".length));
|
|
390
|
-
const
|
|
415
|
+
const project = c.req.query("project");
|
|
416
|
+
if (!project)
|
|
417
|
+
return c.json({ error: "project query param is required" }, 400);
|
|
418
|
+
const result = await ipcRequest("CHECK_FRESHNESS", { id, project });
|
|
391
419
|
if (result.error)
|
|
392
420
|
return c.json({ error: result.error }, 400);
|
|
393
421
|
return c.json(result.data);
|
|
394
422
|
});
|
|
395
423
|
app.get("/api/git-diff/*", async (c) => {
|
|
396
424
|
const id = decodeURIComponent(c.req.path.slice("/api/git-diff/".length));
|
|
397
|
-
const
|
|
425
|
+
const project = c.req.query("project");
|
|
426
|
+
if (!project)
|
|
427
|
+
return c.json({ error: "project query param is required" }, 400);
|
|
428
|
+
const result = await ipcRequest("GIT_DIFF", { id, project }, 15000);
|
|
398
429
|
if (result.error)
|
|
399
430
|
return c.json({ error: result.error }, 400);
|
|
400
431
|
return c.json(result.data);
|
|
401
432
|
});
|
|
402
433
|
app.get("/api/history/*", async (c) => {
|
|
403
434
|
const id = decodeURIComponent(c.req.path.slice("/api/history/".length));
|
|
404
|
-
const
|
|
435
|
+
const project = c.req.query("project");
|
|
436
|
+
if (!project)
|
|
437
|
+
return c.json({ error: "project query param is required" }, 400);
|
|
438
|
+
const result = await ipcRequest("GET_DOC_HISTORY", { id, project });
|
|
405
439
|
if (result.error)
|
|
406
440
|
return c.json({ error: result.error }, 400);
|
|
407
441
|
return c.json(result.data);
|
|
@@ -411,7 +445,10 @@ app.get("/api/history-version/*", async (c) => {
|
|
|
411
445
|
const lastSlash = rest.lastIndexOf("/");
|
|
412
446
|
const id = rest.slice(0, lastSlash);
|
|
413
447
|
const version = parseInt(rest.slice(lastSlash + 1), 10);
|
|
414
|
-
const
|
|
448
|
+
const project = c.req.query("project");
|
|
449
|
+
if (!project)
|
|
450
|
+
return c.json({ error: "project query param is required" }, 400);
|
|
451
|
+
const result = await ipcRequest("GET_DOC_VERSION", { id, version, project });
|
|
415
452
|
if (result.error)
|
|
416
453
|
return c.json({ error: result.error }, 400);
|
|
417
454
|
return c.json(result.data);
|
|
@@ -429,8 +466,11 @@ app.get("/api/file-section/*", async (c) => {
|
|
|
429
466
|
const docId = c.req.query("docId");
|
|
430
467
|
if (!docId)
|
|
431
468
|
return c.json({ error: "docId query param required" }, 400);
|
|
469
|
+
const project = c.req.query("project");
|
|
470
|
+
if (!project)
|
|
471
|
+
return c.json({ error: "project query param is required" }, 400);
|
|
432
472
|
const filePath = decodeURIComponent(c.req.path.slice("/api/file-section/".length));
|
|
433
|
-
const result = await ipcRequest("GET_FILE_SECTION", { docId, filePath }, 8000);
|
|
473
|
+
const result = await ipcRequest("GET_FILE_SECTION", { docId, project, filePath }, 8000);
|
|
434
474
|
if (result.error)
|
|
435
475
|
return c.json({ error: result.error }, 400);
|
|
436
476
|
return c.json(result.data);
|