@imenam/mcp-documentor 1.0.66 → 1.0.67
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 +18 -11
- package/dist/shared/schemas/doc.schema.d.ts +15 -3
- package/dist/shared/schemas/doc.schema.js +35 -6
- 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 +46 -7
- package/dist/src/index.js +126 -41
- 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
|
@@ -60,6 +60,7 @@ Add to your MCP client config (e.g. Cursor `mcp.json`):
|
|
|
60
60
|
| `OPENAI_API_KEY` | No | Enables semantic search via OpenAI embeddings (`search_docs`) |
|
|
61
61
|
| `MCP_DOCUMENTOR_OPENAI_MODEL` | No | Embedding model (default: `text-embedding-3-small`) |
|
|
62
62
|
| `PROXY_URL` | No | URL of the proxy server to register and launch the GUI |
|
|
63
|
+
| `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
64
|
| `MCP_DATA_DIR` | No | Overrides the default data directory (see Data storage) |
|
|
64
65
|
| `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
66
|
| `MCP_DOCUMENTOR_CODE_ROOTS` | No | JSON map of per-project code-root overrides, e.g. `{"my-proj":"/srv/my-proj"}` |
|
|
@@ -73,25 +74,31 @@ Optional `.env` at the package root (directory containing `package.json` discove
|
|
|
73
74
|
|
|
74
75
|
## Available Tools
|
|
75
76
|
|
|
77
|
+
Every tool that targets a specific documentation entry takes a **mandatory `project`**. It is never
|
|
78
|
+
guessed and never falls back to the active project: resolving a doc path across projects is what
|
|
79
|
+
made operations silently hit the wrong project when the same path existed in several. Tools that
|
|
80
|
+
work on a whole project (`list_docs`, `get_doc_tree`, `check_links`, `check_freshness` without a
|
|
81
|
+
`path`, `reindex_embeddings`) keep `project` optional, since omitting it means "all projects".
|
|
82
|
+
|
|
76
83
|
| Tool | Description |
|
|
77
84
|
|---|---|
|
|
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 |
|
|
85
|
+
| `create_doc` | Create a new documentation entry linked to source files (`project` required) |
|
|
86
|
+
| `get_doc` | Retrieve a documentation entry with full content (`project` required) |
|
|
87
|
+
| `get_doc_metadata` | Get metadata and freshness status without loading content (`project` required) |
|
|
81
88
|
| `list_docs` | List docs with staleness indicators (optionally filter by project) |
|
|
82
89
|
| `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 |
|
|
90
|
+
| `update_doc` | Fully replace content and/or metadata (`project` required; prefer `patch_doc` for small edits) |
|
|
91
|
+
| `patch_doc` | Search-and-replace a fragment of a doc’s content (`project` required) |
|
|
92
|
+
| `move_doc` | Change a doc’s path (folder/name) within its project (`project` required) |
|
|
93
|
+
| `delete_doc` | Permanently delete a documentation entry (`project` required) |
|
|
87
94
|
| `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 |
|
|
95
|
+
| `check_freshness` | Staleness for one doc (`project` required) or a full project scan |
|
|
96
|
+
| `refresh_file_snapshot` | Mark tracked files as up-to-date without editing the doc (`project` required) |
|
|
97
|
+
| `git_diff_doc` | Git diff for tracked files vs snapshot baseline (`project` required) |
|
|
91
98
|
| `check_links` | Validate `related_docs`, `doc://` links, and `#heading` anchors |
|
|
92
99
|
| `list_projects` | List all documentation projects |
|
|
93
100
|
| `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 |
|
|
101
|
+
| `upload_image` | Upload an image from a local file path to a project's assets (`project` required); returns the markdown link to insert |
|
|
95
102
|
| `sync_status` | Show branch, ahead/behind counts, conflicted files, and uncommitted changes |
|
|
96
103
|
| `pull_docs` | Pull updates from the remote (defensive fast-forward; reindexes new docs) |
|
|
97
104
|
| `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,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
|
@@ -347,6 +347,9 @@ app.post("/api/doc", async (c) => {
|
|
|
347
347
|
if (!body.title || !body.content) {
|
|
348
348
|
return c.json({ error: "title and content are required" }, 400);
|
|
349
349
|
}
|
|
350
|
+
if (!body.project) {
|
|
351
|
+
return c.json({ error: "project is required" }, 400);
|
|
352
|
+
}
|
|
350
353
|
const result = await ipcRequest("CREATE_DOC", {
|
|
351
354
|
title: body.title,
|
|
352
355
|
content: body.content,
|
|
@@ -364,7 +367,10 @@ app.post("/api/doc", async (c) => {
|
|
|
364
367
|
});
|
|
365
368
|
app.get("/api/doc/*", async (c) => {
|
|
366
369
|
const id = decodeURIComponent(c.req.path.slice("/api/doc/".length));
|
|
367
|
-
const
|
|
370
|
+
const project = c.req.query("project");
|
|
371
|
+
if (!project)
|
|
372
|
+
return c.json({ error: "project query param is required" }, 400);
|
|
373
|
+
const result = await ipcRequest("GET_DOC", { id, project });
|
|
368
374
|
if (result.error)
|
|
369
375
|
return c.json({ error: result.error }, 400);
|
|
370
376
|
return c.json(result.data);
|
|
@@ -372,11 +378,29 @@ app.get("/api/doc/*", async (c) => {
|
|
|
372
378
|
app.put("/api/doc/*", async (c) => {
|
|
373
379
|
try {
|
|
374
380
|
const id = decodeURIComponent(c.req.path.slice("/api/doc/".length));
|
|
381
|
+
const project = c.req.query("project");
|
|
382
|
+
if (!project)
|
|
383
|
+
return c.json({ error: "project query param is required" }, 400);
|
|
375
384
|
const body = await c.req.json();
|
|
376
385
|
if (body.content === undefined) {
|
|
377
386
|
return c.json({ error: "content is required" }, 400);
|
|
378
387
|
}
|
|
379
|
-
const result = await ipcRequest("UPDATE_DOC", { id, content: body.content, title: body.title }, 15000);
|
|
388
|
+
const result = await ipcRequest("UPDATE_DOC", { id, project, content: body.content, title: body.title }, 15000);
|
|
389
|
+
if (result.error)
|
|
390
|
+
return c.json({ error: result.error }, 500);
|
|
391
|
+
return c.json(result.data ?? { success: true });
|
|
392
|
+
}
|
|
393
|
+
catch (err) {
|
|
394
|
+
return c.json({ error: err.message }, 500);
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
app.delete("/api/doc/*", async (c) => {
|
|
398
|
+
try {
|
|
399
|
+
const id = decodeURIComponent(c.req.path.slice("/api/doc/".length));
|
|
400
|
+
const project = c.req.query("project");
|
|
401
|
+
if (!project)
|
|
402
|
+
return c.json({ error: "project query param is required" }, 400);
|
|
403
|
+
const result = await ipcRequest("DELETE_DOC", { id, project }, 15000);
|
|
380
404
|
if (result.error)
|
|
381
405
|
return c.json({ error: result.error }, 500);
|
|
382
406
|
return c.json(result.data ?? { success: true });
|
|
@@ -387,21 +411,30 @@ app.put("/api/doc/*", async (c) => {
|
|
|
387
411
|
});
|
|
388
412
|
app.get("/api/freshness/*", async (c) => {
|
|
389
413
|
const id = decodeURIComponent(c.req.path.slice("/api/freshness/".length));
|
|
390
|
-
const
|
|
414
|
+
const project = c.req.query("project");
|
|
415
|
+
if (!project)
|
|
416
|
+
return c.json({ error: "project query param is required" }, 400);
|
|
417
|
+
const result = await ipcRequest("CHECK_FRESHNESS", { id, project });
|
|
391
418
|
if (result.error)
|
|
392
419
|
return c.json({ error: result.error }, 400);
|
|
393
420
|
return c.json(result.data);
|
|
394
421
|
});
|
|
395
422
|
app.get("/api/git-diff/*", async (c) => {
|
|
396
423
|
const id = decodeURIComponent(c.req.path.slice("/api/git-diff/".length));
|
|
397
|
-
const
|
|
424
|
+
const project = c.req.query("project");
|
|
425
|
+
if (!project)
|
|
426
|
+
return c.json({ error: "project query param is required" }, 400);
|
|
427
|
+
const result = await ipcRequest("GIT_DIFF", { id, project }, 15000);
|
|
398
428
|
if (result.error)
|
|
399
429
|
return c.json({ error: result.error }, 400);
|
|
400
430
|
return c.json(result.data);
|
|
401
431
|
});
|
|
402
432
|
app.get("/api/history/*", async (c) => {
|
|
403
433
|
const id = decodeURIComponent(c.req.path.slice("/api/history/".length));
|
|
404
|
-
const
|
|
434
|
+
const project = c.req.query("project");
|
|
435
|
+
if (!project)
|
|
436
|
+
return c.json({ error: "project query param is required" }, 400);
|
|
437
|
+
const result = await ipcRequest("GET_DOC_HISTORY", { id, project });
|
|
405
438
|
if (result.error)
|
|
406
439
|
return c.json({ error: result.error }, 400);
|
|
407
440
|
return c.json(result.data);
|
|
@@ -411,7 +444,10 @@ app.get("/api/history-version/*", async (c) => {
|
|
|
411
444
|
const lastSlash = rest.lastIndexOf("/");
|
|
412
445
|
const id = rest.slice(0, lastSlash);
|
|
413
446
|
const version = parseInt(rest.slice(lastSlash + 1), 10);
|
|
414
|
-
const
|
|
447
|
+
const project = c.req.query("project");
|
|
448
|
+
if (!project)
|
|
449
|
+
return c.json({ error: "project query param is required" }, 400);
|
|
450
|
+
const result = await ipcRequest("GET_DOC_VERSION", { id, version, project });
|
|
415
451
|
if (result.error)
|
|
416
452
|
return c.json({ error: result.error }, 400);
|
|
417
453
|
return c.json(result.data);
|
|
@@ -429,8 +465,11 @@ app.get("/api/file-section/*", async (c) => {
|
|
|
429
465
|
const docId = c.req.query("docId");
|
|
430
466
|
if (!docId)
|
|
431
467
|
return c.json({ error: "docId query param required" }, 400);
|
|
468
|
+
const project = c.req.query("project");
|
|
469
|
+
if (!project)
|
|
470
|
+
return c.json({ error: "project query param is required" }, 400);
|
|
432
471
|
const filePath = decodeURIComponent(c.req.path.slice("/api/file-section/".length));
|
|
433
|
-
const result = await ipcRequest("GET_FILE_SECTION", { docId, filePath }, 8000);
|
|
472
|
+
const result = await ipcRequest("GET_FILE_SECTION", { docId, project, filePath }, 8000);
|
|
434
473
|
if (result.error)
|
|
435
474
|
return c.json({ error: result.error }, 400);
|
|
436
475
|
return c.json(result.data);
|