@doquflow/cli 0.4.4 → 0.4.6
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/commands/init.js +183 -23
- package/package.json +2 -2
package/dist/commands/init.js
CHANGED
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.buildClaudeMd = buildClaudeMd;
|
|
7
|
+
exports.buildAgentsMd = buildAgentsMd;
|
|
7
8
|
exports.run = run;
|
|
8
9
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
10
|
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
@@ -29,6 +30,12 @@ function getVSCodeMcpConfigPath() {
|
|
|
29
30
|
}
|
|
30
31
|
return node_path_1.default.join(node_os_1.default.homedir(), ".config", "Code", "User", "mcp.json");
|
|
31
32
|
}
|
|
33
|
+
function getCopilotCliMcpConfigPath() {
|
|
34
|
+
return node_path_1.default.join(node_os_1.default.homedir(), ".copilot", "mcp-config.json");
|
|
35
|
+
}
|
|
36
|
+
function getCodexConfigPath() {
|
|
37
|
+
return node_path_1.default.join(node_os_1.default.homedir(), ".codex", "config.toml");
|
|
38
|
+
}
|
|
32
39
|
function resolveServerBin() {
|
|
33
40
|
// Try npm-installed package first
|
|
34
41
|
try {
|
|
@@ -158,9 +165,98 @@ async function writeClaudeMd(projectDir) {
|
|
|
158
165
|
await promises_1.default.writeFile(claudeMdPath, newSection, "utf8");
|
|
159
166
|
}
|
|
160
167
|
}
|
|
168
|
+
function buildAgentsMd(projectDir) {
|
|
169
|
+
return `# DocuFlow — AI Documentation Assistant
|
|
170
|
+
|
|
171
|
+
DocuFlow is an MCP server that provides structured access to this codebase and maintains a living wiki.
|
|
172
|
+
It is registered via \`.codex/config.toml\` and available as MCP tools in every Codex session.
|
|
173
|
+
|
|
174
|
+
## Available MCP Tools
|
|
175
|
+
|
|
176
|
+
### Codebase Scanner
|
|
177
|
+
- **read_module** — Analyse a single file: language, classes, functions, dependencies, DB tables, endpoints, config refs, raw content.
|
|
178
|
+
- \`read_module({ path: "src/UserService.cs" })\`
|
|
179
|
+
- **list_modules** — Walk a directory, extract facts for every file. One call to understand the whole project.
|
|
180
|
+
- \`list_modules({ path: "${projectDir}" })\`
|
|
181
|
+
- **write_spec** — Save a markdown spec to \`.docuflow/specs/<name>.md\`.
|
|
182
|
+
- \`write_spec({ project_path: "${projectDir}", filename: "UserService", content: "..." })\`
|
|
183
|
+
- **read_specs** — Read saved specs, optionally filtered by name.
|
|
184
|
+
- \`read_specs({ project_path: "${projectDir}" })\`
|
|
185
|
+
|
|
186
|
+
### Wiki Pipeline
|
|
187
|
+
- **ingest_source** — Ingest a markdown file from \`.docuflow/sources/\` into the wiki (entities, concepts).
|
|
188
|
+
- **update_index** — Rebuild \`.docuflow/index.md\` from all wiki pages.
|
|
189
|
+
- **list_wiki** — List all wiki pages by category (entity/concept/timeline/synthesis).
|
|
190
|
+
- **wiki_search** — BM25 search across all wiki pages.
|
|
191
|
+
- **query_wiki** — Q&A: searches wiki, synthesises an answer, returns citations.
|
|
192
|
+
- \`query_wiki({ project_path: "${projectDir}", question: "How does auth work?" })\`
|
|
193
|
+
- **synthesize_answer** — Generate a markdown synthesis from a list of page IDs.
|
|
194
|
+
- **save_answer_as_page** — Persist a synthesis as a wiki page.
|
|
195
|
+
|
|
196
|
+
### Health & Guidance
|
|
197
|
+
- **lint_wiki** — Health check: orphan pages, broken refs, stale content. Returns a 0–100 health score.
|
|
198
|
+
- **get_schema_guidance** — Recommend what wiki pages should exist based on schema + current state.
|
|
199
|
+
- **preview_generation** — Preview what a tool will generate before running it.
|
|
200
|
+
|
|
201
|
+
## Common Workflows
|
|
202
|
+
|
|
203
|
+
Start here — understand the codebase:
|
|
204
|
+
\`\`\`
|
|
205
|
+
list_modules({ path: "${projectDir}" })
|
|
206
|
+
→ write_spec for important modules
|
|
207
|
+
\`\`\`
|
|
208
|
+
|
|
209
|
+
Answer a question:
|
|
210
|
+
\`\`\`
|
|
211
|
+
query_wiki({ project_path: "${projectDir}", question: "..." })
|
|
212
|
+
\`\`\`
|
|
213
|
+
|
|
214
|
+
Maintain wiki health:
|
|
215
|
+
\`\`\`
|
|
216
|
+
lint_wiki({ project_path: "${projectDir}" })
|
|
217
|
+
\`\`\`
|
|
218
|
+
|
|
219
|
+
## Storage Layout
|
|
220
|
+
|
|
221
|
+
\`\`\`
|
|
222
|
+
.docuflow/
|
|
223
|
+
├── specs/ Code specs written by write_spec
|
|
224
|
+
├── wiki/ LLM-generated wiki pages
|
|
225
|
+
│ ├── entities/
|
|
226
|
+
│ ├── concepts/
|
|
227
|
+
│ ├── timelines/
|
|
228
|
+
│ └── syntheses/
|
|
229
|
+
├── sources/ Raw markdown docs to ingest
|
|
230
|
+
├── schema.md Wiki configuration (edit to customise)
|
|
231
|
+
├── index.md Auto-maintained catalog
|
|
232
|
+
└── log.md Operation log
|
|
233
|
+
\`\`\`
|
|
234
|
+
`;
|
|
235
|
+
}
|
|
236
|
+
async function writeAgentsMd(projectDir) {
|
|
237
|
+
const agentsMdPath = node_path_1.default.join(projectDir, "AGENTS.md");
|
|
238
|
+
const newSection = buildAgentsMd(projectDir);
|
|
239
|
+
if (node_fs_1.default.existsSync(agentsMdPath)) {
|
|
240
|
+
const existing = await promises_1.default.readFile(agentsMdPath, "utf8");
|
|
241
|
+
if (existing.includes("DocuFlow")) {
|
|
242
|
+
// Replace existing DocuFlow section
|
|
243
|
+
const withoutDocuflow = existing.replace(/\n?# DocuFlow[\s\S]*/, "").trimEnd();
|
|
244
|
+
await promises_1.default.writeFile(agentsMdPath, withoutDocuflow + "\n\n" + newSection, "utf8");
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
// Append to existing AGENTS.md
|
|
248
|
+
await promises_1.default.appendFile(agentsMdPath, "\n\n" + newSection, "utf8");
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
await promises_1.default.writeFile(agentsMdPath, newSection, "utf8");
|
|
253
|
+
}
|
|
254
|
+
}
|
|
161
255
|
async function run() {
|
|
162
256
|
const configPath = getClaudeDesktopConfigPath();
|
|
163
257
|
const vscodeConfigPath = getVSCodeMcpConfigPath();
|
|
258
|
+
const copilotCliConfigPath = getCopilotCliMcpConfigPath();
|
|
259
|
+
const codexConfigPath = getCodexConfigPath();
|
|
164
260
|
const serverBin = resolveServerBin();
|
|
165
261
|
const nodeBin = process.execPath;
|
|
166
262
|
// Register in Claude Desktop config
|
|
@@ -198,6 +294,45 @@ async function run() {
|
|
|
198
294
|
catch {
|
|
199
295
|
// VS Code not installed or config dir not writable — skip silently
|
|
200
296
|
}
|
|
297
|
+
// Register in GitHub Copilot CLI MCP config (~/.copilot/mcp-config.json)
|
|
298
|
+
let copilotCliRegistered = false;
|
|
299
|
+
let copilotCliConfig = {};
|
|
300
|
+
try {
|
|
301
|
+
const raw = await promises_1.default.readFile(copilotCliConfigPath, "utf8");
|
|
302
|
+
copilotCliConfig = JSON.parse(raw);
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
// File may not exist yet
|
|
306
|
+
}
|
|
307
|
+
if (!copilotCliConfig.mcpServers)
|
|
308
|
+
copilotCliConfig.mcpServers = {};
|
|
309
|
+
copilotCliConfig.mcpServers.docuflow = { type: "local", command: nodeBin, args: [serverBin], tools: ["*"] };
|
|
310
|
+
try {
|
|
311
|
+
await promises_1.default.mkdir(node_path_1.default.dirname(copilotCliConfigPath), { recursive: true });
|
|
312
|
+
await promises_1.default.writeFile(copilotCliConfigPath, JSON.stringify(copilotCliConfig, null, 2) + "\n", "utf8");
|
|
313
|
+
copilotCliRegistered = true;
|
|
314
|
+
}
|
|
315
|
+
catch {
|
|
316
|
+
// Copilot CLI not installed — skip silently
|
|
317
|
+
}
|
|
318
|
+
// Register in OpenAI Codex CLI (~/.codex/config.toml in TOML format)
|
|
319
|
+
let codexCliRegistered = false;
|
|
320
|
+
try {
|
|
321
|
+
await promises_1.default.mkdir(node_path_1.default.dirname(codexConfigPath), { recursive: true });
|
|
322
|
+
let tomlContent = "";
|
|
323
|
+
try {
|
|
324
|
+
tomlContent = await promises_1.default.readFile(codexConfigPath, "utf8");
|
|
325
|
+
}
|
|
326
|
+
catch { /* new file */ }
|
|
327
|
+
if (!tomlContent.includes("[mcp_servers.docuflow]")) {
|
|
328
|
+
const entry = `\n[mcp_servers.docuflow]\ncommand = "${nodeBin}"\nargs = [${JSON.stringify(serverBin)}]\n`;
|
|
329
|
+
await promises_1.default.writeFile(codexConfigPath, tomlContent + entry, "utf8");
|
|
330
|
+
}
|
|
331
|
+
codexCliRegistered = true;
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
// Codex CLI not installed — skip silently
|
|
335
|
+
}
|
|
201
336
|
// Create .docuflow/ directory structure
|
|
202
337
|
const projectDir = process.cwd();
|
|
203
338
|
const docuflowDir = node_path_1.default.join(projectDir, ".docuflow");
|
|
@@ -220,6 +355,29 @@ async function run() {
|
|
|
220
355
|
await copyTemplateFile("log.md", node_path_1.default.join(docuflowDir, "log.md"));
|
|
221
356
|
// Generate CLAUDE.md so Claude Code picks up DocuFlow automatically
|
|
222
357
|
await writeClaudeMd(projectDir);
|
|
358
|
+
// Generate AGENTS.md so OpenAI Codex picks up DocuFlow automatically
|
|
359
|
+
await writeAgentsMd(projectDir);
|
|
360
|
+
// Write .vscode/mcp.json for project-level workspace MCP config (shareable via git)
|
|
361
|
+
// Uses npx so it works on any machine — safe to commit
|
|
362
|
+
const vscodeDirPath = node_path_1.default.join(projectDir, ".vscode");
|
|
363
|
+
const vscodeWorkspaceMcpPath = node_path_1.default.join(vscodeDirPath, "mcp.json");
|
|
364
|
+
let workspaceMcpConfig = {};
|
|
365
|
+
try {
|
|
366
|
+
const raw = await promises_1.default.readFile(vscodeWorkspaceMcpPath, "utf8");
|
|
367
|
+
workspaceMcpConfig = JSON.parse(raw);
|
|
368
|
+
}
|
|
369
|
+
catch {
|
|
370
|
+
// File doesn't exist yet
|
|
371
|
+
}
|
|
372
|
+
if (!workspaceMcpConfig.servers)
|
|
373
|
+
workspaceMcpConfig.servers = {};
|
|
374
|
+
workspaceMcpConfig.servers.docuflow = {
|
|
375
|
+
command: "npx",
|
|
376
|
+
args: ["-y", "-p", "@doquflow/server", "docuflow-server"],
|
|
377
|
+
type: "stdio",
|
|
378
|
+
};
|
|
379
|
+
await promises_1.default.mkdir(vscodeDirPath, { recursive: true });
|
|
380
|
+
await promises_1.default.writeFile(vscodeWorkspaceMcpPath, JSON.stringify(workspaceMcpConfig, null, 2) + "\n", "utf8");
|
|
223
381
|
// Add .docuflow/ to .gitignore if present and not already listed
|
|
224
382
|
const gitignorePath = node_path_1.default.join(process.cwd(), ".gitignore");
|
|
225
383
|
if (node_fs_1.default.existsSync(gitignorePath)) {
|
|
@@ -228,32 +386,34 @@ async function run() {
|
|
|
228
386
|
await promises_1.default.appendFile(gitignorePath, "\n# Docuflow\n.docuflow/\n");
|
|
229
387
|
}
|
|
230
388
|
}
|
|
231
|
-
console.log("
|
|
389
|
+
console.log("\u2713 DocuFlow initialised successfully.");
|
|
232
390
|
console.log("");
|
|
233
|
-
console.log("
|
|
391
|
+
console.log("\ud83d\udcc1 Structure created:");
|
|
234
392
|
console.log(` ${docuflowDir}/`);
|
|
235
|
-
console.log(`
|
|
236
|
-
console.log(`
|
|
237
|
-
console.log(`
|
|
238
|
-
console.log(`
|
|
239
|
-
console.log(`
|
|
240
|
-
console.log(`
|
|
241
|
-
console.log(`
|
|
242
|
-
console.log(`
|
|
243
|
-
console.log(`
|
|
244
|
-
console.log(`
|
|
393
|
+
console.log(` \u251c\u2500\u2500 specs/ (code specs written by the agent)`);
|
|
394
|
+
console.log(` \u251c\u2500\u2500 wiki/ (LLM-generated wiki pages)`);
|
|
395
|
+
console.log(` \u2502 \u251c\u2500\u2500 entities/`);
|
|
396
|
+
console.log(` \u2502 \u251c\u2500\u2500 concepts/`);
|
|
397
|
+
console.log(` \u2502 \u251c\u2500\u2500 timelines/`);
|
|
398
|
+
console.log(` \u2502 \u2514\u2500\u2500 syntheses/`);
|
|
399
|
+
console.log(` \u251c\u2500\u2500 sources/ (raw markdown documents to ingest)`);
|
|
400
|
+
console.log(` \u251c\u2500\u2500 schema.md (wiki configuration)`);
|
|
401
|
+
console.log(` \u251c\u2500\u2500 index.md (auto-maintained catalog)`);
|
|
402
|
+
console.log(` \u2514\u2500\u2500 log.md (operation log)`);
|
|
245
403
|
console.log("");
|
|
246
|
-
console.log("
|
|
247
|
-
console.log(`
|
|
248
|
-
console.log(`
|
|
404
|
+
console.log("\ud83d\udcdd Instruction files:");
|
|
405
|
+
console.log(` CLAUDE.md ✓ ${node_path_1.default.join(projectDir, "CLAUDE.md")}`);
|
|
406
|
+
console.log(` AGENTS.md ✓ ${node_path_1.default.join(projectDir, "AGENTS.md")}`);
|
|
249
407
|
console.log("");
|
|
250
|
-
console.log("
|
|
251
|
-
console.log(` Claude Desktop:
|
|
252
|
-
console.log(`
|
|
408
|
+
console.log("\ud83d\udd27 MCP Registration:");
|
|
409
|
+
console.log(` Claude Desktop: \u2713 registered`);
|
|
410
|
+
console.log(` VS Code Copilot: ${vscodeRegistered ? "\u2713 registered (user-level)" : "\u2014 not detected"}`);
|
|
411
|
+
console.log(` Copilot CLI: ${copilotCliRegistered ? "\u2713 registered (~/.copilot/mcp-config.json)" : "\u2014 not detected"}`);
|
|
412
|
+
console.log(` Codex CLI: ${codexCliRegistered ? "\u2713 registered (~/.codex/config.toml)" : "\u2014 not detected"}`);
|
|
413
|
+
console.log(` Workspace: \u2713 .vscode/mcp.json written (commit to share with team)`);
|
|
253
414
|
console.log("");
|
|
254
|
-
console.log("
|
|
255
|
-
console.log(" 1. Edit .docuflow/schema.md to customize your wiki");
|
|
256
|
-
console.log(" 2. Add
|
|
257
|
-
console.log(" 3.
|
|
258
|
-
console.log(" 4. Restart Claude Desktop / reload VS Code window to activate");
|
|
415
|
+
console.log("\ud83d\udcd6 Next steps:");
|
|
416
|
+
console.log(" 1. Edit .docuflow/schema.md to customize your wiki domain");
|
|
417
|
+
console.log(" 2. Add markdown docs to .docuflow/sources/ then ingest them");
|
|
418
|
+
console.log(" 3. Restart Claude Desktop / reload VS Code / restart Copilot CLI");
|
|
259
419
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doquflow/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"description": "CLI for setting up Docuflow in your project",
|
|
5
5
|
"author": "Docuflow <hello@doquflows.dev>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"build": "tsc"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@doquflow/server": "0.4.
|
|
33
|
+
"@doquflow/server": "0.4.6"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^22.0.0",
|