@doquflow/cli 0.2.0 → 0.4.0

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.
@@ -0,0 +1,346 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.runInteractive = runInteractive;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const promises_1 = __importDefault(require("node:fs/promises"));
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const node_readline_1 = __importDefault(require("node:readline"));
11
+ const init_1 = require("./init");
12
+ async function prompt(question) {
13
+ const rl = node_readline_1.default.createInterface({
14
+ input: process.stdin,
15
+ output: process.stdout,
16
+ });
17
+ return new Promise((resolve) => {
18
+ rl.question(question, (answer) => {
19
+ rl.close();
20
+ resolve(answer);
21
+ });
22
+ });
23
+ }
24
+ async function selectDomain() {
25
+ console.log("\nšŸ“š What domain is your wiki for?");
26
+ console.log(" 1) Code & Architecture");
27
+ console.log(" 2) Research & Analysis");
28
+ console.log(" 3) Business & Markets");
29
+ console.log(" 4) Personal Knowledge");
30
+ let selection = "";
31
+ while (!["1", "2", "3", "4"].includes(selection)) {
32
+ selection = await prompt("Select (1-4): ");
33
+ }
34
+ const domains = {
35
+ "1": "code",
36
+ "2": "research",
37
+ "3": "business",
38
+ "4": "personal",
39
+ };
40
+ return domains[selection];
41
+ }
42
+ async function getProjectInfo(domain) {
43
+ console.log(`\nšŸ“ Tell me about your ${domain} wiki`);
44
+ const name = await prompt("Project name (e.g., 'MyProject', 'Research2026'): ");
45
+ const description = await prompt("Brief description (what will this wiki track?): ");
46
+ return { name, description };
47
+ }
48
+ function getSchemaForDomain(domain) {
49
+ const schemas = {
50
+ code: `# Docuflow Wiki Schema - Code/Architecture
51
+
52
+ ## Domain
53
+ Architecture and codebase documentation.
54
+
55
+ ## Wiki Structure
56
+
57
+ ### Entities (entities/)
58
+ - Services: key microservices and components
59
+ - APIs: public interfaces and endpoints
60
+ - Databases: data models and schemas
61
+ - Frameworks: libraries and tools used
62
+
63
+ ### Concepts (concepts/)
64
+ - Design Patterns: architectural and coding patterns
65
+ - Principles: design principles and guidelines
66
+ - Integrations: how components work together
67
+ - Configuration: important settings and options
68
+
69
+ ### Syntheses (syntheses/)
70
+ - Architecture Overview: system design
71
+ - Decision Records: design decisions
72
+ - Deployment Guide: how to deploy
73
+ - API Reference: complete API docs
74
+
75
+ ### Timelines (timelines/)
76
+ - Version History: evolution of the system
77
+ - Roadmap: planned changes
78
+
79
+ ## Cross-Reference Patterns
80
+ - "integrates with" - components/services that work together
81
+ - "implemented by" - which entities implement a concept
82
+ - "uses pattern" - which architecture patterns apply
83
+ - "depends on" - dependencies between components
84
+
85
+ ## Metadata
86
+ Each page should include:
87
+ - created_at: when first documented
88
+ - updated_at: last update date
89
+ - tech_stack: relevant technologies
90
+ - contributors: who wrote/contributed
91
+ `,
92
+ research: `# Docuflow Wiki Schema - Research
93
+
94
+ ## Domain
95
+ Research findings and analysis.
96
+
97
+ ## Wiki Structure
98
+
99
+ ### Entities (entities/)
100
+ - Papers: academic papers and articles
101
+ - Researchers: key people in the field
102
+ - Conferences: important venues
103
+ - Datasets: data sources used
104
+
105
+ ### Concepts (concepts/)
106
+ - Methodologies: research methods
107
+ - Theories: key theories and frameworks
108
+ - Open Problems: unsolved questions
109
+ - Keywords: important terms
110
+
111
+ ### Syntheses (syntheses/)
112
+ - Literature Review: synthesis of papers
113
+ - Findings: key discoveries
114
+ - Future Work: research directions
115
+ - Contradictions: areas of disagreement
116
+
117
+ ### Timelines (timelines/)
118
+ - Research Evolution: how field evolved
119
+ - Timeline of Discoveries: key milestones
120
+
121
+ ## Cross-Reference Patterns
122
+ - "cites" - references between papers
123
+ - "extends" - builds on prior work
124
+ - "contradicts" - disagreement between sources
125
+ - "validates" - experimental confirmation
126
+ - "relates to" - topical connection
127
+
128
+ ## Metadata
129
+ Each page should include:
130
+ - created_at: when added
131
+ - updated_at: last refresh
132
+ - sources: which papers/sources
133
+ - citations: number of times cited
134
+ - confidence: how confident in findings
135
+ `,
136
+ business: `# Docuflow Wiki Schema - Business
137
+
138
+ ## Domain
139
+ Business and competitive analysis.
140
+
141
+ ## Wiki Structure
142
+
143
+ ### Entities (entities/)
144
+ - Companies: competitors and partners
145
+ - Products: key offerings
146
+ - Markets: market segments
147
+ - Customers: customer types
148
+
149
+ ### Concepts (concepts/)
150
+ - Business Models: how companies make money
151
+ - Market Segments: addressable markets
152
+ - Capabilities: key competitive advantages
153
+ - Trends: market trends
154
+
155
+ ### Syntheses (syntheses/)
156
+ - Competitive Analysis: comparison matrix
157
+ - Market Overview: market positioning
158
+ - Opportunities: growth opportunities
159
+ - Risks: market risks
160
+
161
+ ### Timelines (timelines/)
162
+ - Market Evolution: how market changed
163
+ - Competitive Timeline: competitor moves
164
+
165
+ ## Cross-Reference Patterns
166
+ - "competes with" - direct competitors
167
+ - "targets" - goes after customer/market
168
+ - "partners with" - partnerships
169
+ - "disrupts" - disruption threat
170
+ - "complements" - complementary products
171
+
172
+ ## Metadata
173
+ Each page should include:
174
+ - created_at: when first documented
175
+ - updated_at: last update
176
+ - market_size: TAM/SAM/SOM if known
177
+ - growth_rate: annual growth
178
+ - sources: where info came from
179
+ `,
180
+ personal: `# Docuflow Wiki Schema - Personal
181
+
182
+ ## Domain
183
+ Personal knowledge, learning, and goals.
184
+
185
+ ## Wiki Structure
186
+
187
+ ### Entities (entities/)
188
+ - Topics: areas of interest/expertise
189
+ - Resources: books, courses, websites
190
+ - People: mentors, collaborators
191
+ - Projects: personal projects
192
+
193
+ ### Concepts (concepts/)
194
+ - Learning Goals: what to learn
195
+ - Skills: competencies to develop
196
+ - Insights: key personal learnings
197
+ - Practices: habits and routines
198
+
199
+ ### Syntheses (syntheses/)
200
+ - Reflections: deeper thinking
201
+ - Progress Updates: tracking learning
202
+ - Connections: how ideas relate
203
+ - Action Plans: what to do next
204
+
205
+ ### Timelines (timelines/)
206
+ - Learning Journey: personal evolution
207
+ - Milestones: key achievements
208
+
209
+ ## Cross-Reference Patterns
210
+ - "builds on" - prerequisite knowledge
211
+ - "connects to" - related topics
212
+ - "informs" - influences thinking
213
+ - "inspires" - inspiration source
214
+ - "applies to" - practical application
215
+
216
+ ## Metadata
217
+ Each page should include:
218
+ - created_at: when started learning
219
+ - updated_at: last review
220
+ - relevance: importance (high/medium/low)
221
+ - mastery_level: expertise level (beginner/intermediate/expert)
222
+ - time_invested: hours spent
223
+ `,
224
+ };
225
+ return schemas[domain];
226
+ }
227
+ function getPlanningTemplate(domain, projectName) {
228
+ return `# ${projectName} Wiki Plan
229
+
230
+ ## Goal
231
+ Document and organize knowledge for ${projectName}.
232
+
233
+ ## Initial Sources to Add
234
+ 1. [ ] README or main overview
235
+ 2. [ ] Key source file 1
236
+ 3. [ ] Key source file 2
237
+
238
+ ## Key Entities to Define
239
+ - Entity 1: [description]
240
+ - Entity 2: [description]
241
+
242
+ ## Key Concepts to Extract
243
+ - Concept 1: [description]
244
+ - Concept 2: [description]
245
+
246
+ ## First Questions to Answer
247
+ 1. [What do you want to understand first?]
248
+ 2. [What relationships are important?]
249
+
250
+ ## Success Criteria
251
+ - [ ] Successfully ingested first 3 sources
252
+ - [ ] Created 10+ wiki pages
253
+ - [ ] Can answer key questions
254
+ - [ ] Wiki is at 80%+ health score
255
+
256
+ ## Next Review Date
257
+ [Date for first maintenance check]
258
+ `;
259
+ }
260
+ async function runInteractive() {
261
+ console.log("\n🌟 Welcome to Docuflow Wiki Setup!\n");
262
+ console.log("I'll help you set up a wiki for your project.");
263
+ console.log("This should take about 2 minutes. You can customize later.\n");
264
+ // Get domain
265
+ const domain = await selectDomain();
266
+ console.log(`āœ“ Selected: ${domain}`);
267
+ // Get project info
268
+ const { name: projectName, description } = await getProjectInfo(domain);
269
+ console.log(`āœ“ Project: "${projectName}" - ${description}`);
270
+ // Confirm setup
271
+ console.log("\n✨ I'll create a wiki structure for you with recommended schema.");
272
+ const confirm = await prompt("Ready to proceed? (y/n): ");
273
+ if (confirm.toLowerCase() !== "y") {
274
+ console.log("\nšŸ‘‹ Setup cancelled.");
275
+ return;
276
+ }
277
+ // Get schema for domain
278
+ const domainSchema = getSchemaForDomain(domain);
279
+ const planTemplate = getPlanningTemplate(domain, projectName);
280
+ console.log("\nšŸ“ Creating wiki structure...");
281
+ const docuDir = node_path_1.default.join(process.cwd(), ".docuflow");
282
+ const wikiDir = node_path_1.default.join(docuDir, "wiki");
283
+ // Create directories
284
+ await promises_1.default.mkdir(node_path_1.default.join(wikiDir, "entities"), { recursive: true });
285
+ await promises_1.default.mkdir(node_path_1.default.join(wikiDir, "concepts"), { recursive: true });
286
+ await promises_1.default.mkdir(node_path_1.default.join(wikiDir, "syntheses"), { recursive: true });
287
+ await promises_1.default.mkdir(node_path_1.default.join(wikiDir, "timelines"), { recursive: true });
288
+ await promises_1.default.mkdir(node_path_1.default.join(docuDir, "sources"), { recursive: true });
289
+ await promises_1.default.mkdir(node_path_1.default.join(docuDir, "specs"), { recursive: true });
290
+ console.log(" āœ“ Created wiki directories");
291
+ // Write schema with domain-specific content
292
+ await promises_1.default.writeFile(node_path_1.default.join(docuDir, "schema.md"), domainSchema, "utf8");
293
+ console.log(" āœ“ Created schema.md with domain-specific structure");
294
+ // Write index and log
295
+ const indexContent = `# Wiki Index
296
+
297
+ Auto-maintained catalog of all wiki pages.
298
+
299
+ ## Generated: ${new Date().toISOString()}
300
+
301
+ (Index is automatically updated as you ingest sources)
302
+ `;
303
+ await promises_1.default.writeFile(node_path_1.default.join(docuDir, "index.md"), indexContent, "utf8");
304
+ console.log(" āœ“ Created index.md");
305
+ const logContent = `# Operation Log
306
+
307
+ Record of all wiki operations.
308
+
309
+ ## [${new Date().toISOString()}] init | Wiki initialized
310
+ - Domain: ${domain}
311
+ - Project: ${projectName}
312
+ - Description: ${description}
313
+ `;
314
+ await promises_1.default.writeFile(node_path_1.default.join(docuDir, "log.md"), logContent, "utf8");
315
+ console.log(" āœ“ Created log.md");
316
+ // Write planning template
317
+ const planPath = node_path_1.default.join(docuDir, "PLAN.md");
318
+ await promises_1.default.writeFile(planPath, planTemplate, "utf8");
319
+ console.log(" āœ“ Created PLAN.md (interactive planning guide)");
320
+ // Generate CLAUDE.md so Claude Code picks up DocuFlow automatically
321
+ const claudeMdContent = (0, init_1.buildClaudeMd)(process.cwd());
322
+ const claudeMdPath = node_path_1.default.join(process.cwd(), "CLAUDE.md");
323
+ if (node_fs_1.default.existsSync(claudeMdPath)) {
324
+ const existing = await promises_1.default.readFile(claudeMdPath, "utf8");
325
+ if (existing.includes("DocuFlow")) {
326
+ const withoutDocuflow = existing.replace(/\n?# DocuFlow[\s\S]*/, "").trimEnd();
327
+ await promises_1.default.writeFile(claudeMdPath, withoutDocuflow + "\n\n" + claudeMdContent, "utf8");
328
+ }
329
+ else {
330
+ await promises_1.default.appendFile(claudeMdPath, "\n\n" + claudeMdContent, "utf8");
331
+ }
332
+ }
333
+ else {
334
+ await promises_1.default.writeFile(claudeMdPath, claudeMdContent, "utf8");
335
+ }
336
+ console.log(" āœ“ Created CLAUDE.md (Claude Code will read DocuFlow tool instructions automatically)");
337
+ console.log("\nāœ… Wiki successfully initialized!\n");
338
+ // Print summary and next steps
339
+ console.log("šŸ“‹ Next Steps:");
340
+ console.log(" 1. Review your schema: .docuflow/schema.md");
341
+ console.log(" 2. Review your plan: .docuflow/PLAN.md");
342
+ console.log(" 3. Add first source: copy to .docuflow/sources/");
343
+ console.log(" 4. Ask Claude: 'Ingest README.md into my wiki'");
344
+ console.log(" 5. Ask Claude: 'What should my wiki contain?'");
345
+ console.log("\nšŸ’” Tip: Claude Code will automatically read CLAUDE.md for DocuFlow instructions.\n");
346
+ }
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.buildClaudeMd = buildClaudeMd;
6
7
  exports.run = run;
7
8
  const node_fs_1 = __importDefault(require("node:fs"));
8
9
  const promises_1 = __importDefault(require("node:fs/promises"));
@@ -28,6 +29,125 @@ function resolveServerBin() {
28
29
  return node_path_1.default.resolve(__dirname, "..", "..", "server", "dist", "index.js");
29
30
  }
30
31
  }
32
+ async function copyTemplateFile(templateName, destPath) {
33
+ try {
34
+ // Try package-installed location first
35
+ const templatePath = require.resolve(`@doquflow/cli/templates/${templateName}`);
36
+ const content = await promises_1.default.readFile(templatePath, "utf8");
37
+ await promises_1.default.writeFile(destPath, content, "utf8");
38
+ }
39
+ catch {
40
+ try {
41
+ // Fallback: monorepo sibling path (dev environment)
42
+ const templatePath = node_path_1.default.resolve(__dirname, "..", "..", "templates", templateName);
43
+ const content = await promises_1.default.readFile(templatePath, "utf8");
44
+ await promises_1.default.writeFile(destPath, content, "utf8");
45
+ }
46
+ catch (err) {
47
+ // If template not found, create a minimal version
48
+ console.warn(` ⚠ Could not find template for ${templateName}, creating minimal version`);
49
+ if (templateName === "schema.md") {
50
+ await promises_1.default.writeFile(destPath, "# Docuflow Wiki Schema\n\n## Domain\n[Edit this file to customize your wiki]\n", "utf8");
51
+ }
52
+ else if (templateName === "index.md") {
53
+ await promises_1.default.writeFile(destPath, "# Wiki Index\n\nAuto-maintained catalog of pages.\n", "utf8");
54
+ }
55
+ else if (templateName === "log.md") {
56
+ await promises_1.default.writeFile(destPath, "# Operation Log\n\nRecord of wiki operations.\n", "utf8");
57
+ }
58
+ }
59
+ }
60
+ }
61
+ function buildClaudeMd(projectDir) {
62
+ return `# DocuFlow — AI Documentation Assistant
63
+
64
+ DocuFlow is an MCP server that gives you structured access to this codebase and maintains a living wiki.
65
+ It is registered in your Claude Desktop config and available as MCP tools in every session.
66
+
67
+ ## Codebase Scanner Tools
68
+
69
+ - **read_module** — Analyse a single source file. Returns language, classes, functions, dependencies, DB tables, endpoints, config refs, and raw content (first 8 KB).
70
+ - Example: \`read_module({ path: "src/UserService.cs" })\`
71
+ - **list_modules** — Walk a directory and extract facts for every non-binary file. Use this to understand the full project in one call.
72
+ - Example: \`list_modules({ path: "${projectDir}" })\`
73
+ - **write_spec** — Persist a markdown spec to \`.docuflow/specs/<filename>.md\` and update the index.
74
+ - Example: \`write_spec({ project_path: "${projectDir}", filename: "UserService", content: "# UserService\\n..." })\`
75
+ - **read_specs** — Read previously written specs, optionally filtered by name.
76
+ - Example: \`read_specs({ project_path: "${projectDir}" })\`
77
+
78
+ ## Wiki Pipeline Tools
79
+
80
+ - **ingest_source** — Ingest a markdown file from \`.docuflow/sources/\` and generate wiki pages (entities, concepts).
81
+ - **update_index** — Rebuild \`.docuflow/index.md\` from all wiki pages.
82
+ - **list_wiki** — List all wiki pages, optionally filtered by category (entity/concept/timeline/synthesis).
83
+ - **wiki_search** — BM25 search across all wiki pages. Returns ranked results with previews.
84
+ - **query_wiki** — One-stop Q&A: searches wiki, synthesises an answer, returns source citations.
85
+ - **synthesize_answer** — Generate a markdown synthesis from a list of specific wiki page IDs.
86
+ - **save_answer_as_page** — Persist a synthesised answer back into the wiki (knowledge compounding).
87
+
88
+ ## Health & Guidance Tools
89
+
90
+ - **lint_wiki** — Health check: orphan pages, broken refs, stale content, metadata gaps. Returns a 0–100 health score.
91
+ - **get_schema_guidance** — Analyse what wiki pages should exist based on the schema and current state.
92
+ - **preview_generation** — Preview what a tool will do before running it.
93
+
94
+ ## Common Workflows
95
+
96
+ ### First time — understand the codebase
97
+ \`\`\`
98
+ list_modules({ path: "${projectDir}" })
99
+ → read the language breakdown and dependency map
100
+ → write_spec each important module
101
+ \`\`\`
102
+
103
+ ### Ongoing — answer a question
104
+ \`\`\`
105
+ query_wiki({ project_path: "${projectDir}", question: "How does authentication work?" })
106
+ → save_answer_as_page if the answer is worth keeping
107
+ \`\`\`
108
+
109
+ ### Maintenance — check wiki health
110
+ \`\`\`
111
+ lint_wiki({ project_path: "${projectDir}" })
112
+ → fix orphans and broken refs
113
+ \`\`\`
114
+
115
+ ## Storage Layout
116
+
117
+ \`\`\`
118
+ .docuflow/
119
+ ā”œā”€ā”€ specs/ Legacy spec files written by write_spec
120
+ ā”œā”€ā”€ wiki/ LLM-generated wiki pages
121
+ │ ā”œā”€ā”€ entities/ Named things (services, APIs, databases)
122
+ │ ā”œā”€ā”€ concepts/ Design patterns, principles, integrations
123
+ │ ā”œā”€ā”€ timelines/ Chronological pages
124
+ │ └── syntheses/ Cross-cutting synthesis pages
125
+ ā”œā”€ā”€ sources/ Raw input files for ingest_source
126
+ ā”œā”€ā”€ schema.md Wiki configuration (edit to customise)
127
+ ā”œā”€ā”€ index.md Auto-maintained catalog
128
+ └── log.md Operation log
129
+ \`\`\`
130
+ `;
131
+ }
132
+ async function writeClaudeMd(projectDir) {
133
+ const claudeMdPath = node_path_1.default.join(projectDir, "CLAUDE.md");
134
+ const newSection = buildClaudeMd(projectDir);
135
+ if (node_fs_1.default.existsSync(claudeMdPath)) {
136
+ const existing = await promises_1.default.readFile(claudeMdPath, "utf8");
137
+ if (existing.includes("DocuFlow")) {
138
+ // Already has DocuFlow section — replace it
139
+ const withoutDocuflow = existing.replace(/\n?# DocuFlow[\s\S]*/, "").trimEnd();
140
+ await promises_1.default.writeFile(claudeMdPath, withoutDocuflow + "\n\n" + newSection, "utf8");
141
+ }
142
+ else {
143
+ // Append to existing CLAUDE.md
144
+ await promises_1.default.appendFile(claudeMdPath, "\n\n" + newSection, "utf8");
145
+ }
146
+ }
147
+ else {
148
+ await promises_1.default.writeFile(claudeMdPath, newSection, "utf8");
149
+ }
150
+ }
31
151
  async function run() {
32
152
  const configPath = getClaudeDesktopConfigPath();
33
153
  const serverBin = resolveServerBin();
@@ -46,9 +166,28 @@ async function run() {
46
166
  config.mcpServers.docuflow = { command: nodeBin, args: [serverBin] };
47
167
  await promises_1.default.mkdir(node_path_1.default.dirname(configPath), { recursive: true });
48
168
  await promises_1.default.writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf8");
49
- // Create .docuflow/specs/ in cwd
50
- const specsDir = node_path_1.default.join(process.cwd(), ".docuflow", "specs");
169
+ // Create .docuflow/ directory structure
170
+ const projectDir = process.cwd();
171
+ const docuflowDir = node_path_1.default.join(projectDir, ".docuflow");
172
+ const specsDir = node_path_1.default.join(docuflowDir, "specs");
173
+ const wikiDir = node_path_1.default.join(docuflowDir, "wiki");
174
+ const sourcesDir = node_path_1.default.join(docuflowDir, "sources");
175
+ const entitiesDir = node_path_1.default.join(wikiDir, "entities");
176
+ const conceptsDir = node_path_1.default.join(wikiDir, "concepts");
177
+ const timelinesDir = node_path_1.default.join(wikiDir, "timelines");
178
+ const synthesesDir = node_path_1.default.join(wikiDir, "syntheses");
51
179
  await promises_1.default.mkdir(specsDir, { recursive: true });
180
+ await promises_1.default.mkdir(entitiesDir, { recursive: true });
181
+ await promises_1.default.mkdir(conceptsDir, { recursive: true });
182
+ await promises_1.default.mkdir(timelinesDir, { recursive: true });
183
+ await promises_1.default.mkdir(synthesesDir, { recursive: true });
184
+ await promises_1.default.mkdir(sourcesDir, { recursive: true });
185
+ // Copy or create template files
186
+ await copyTemplateFile("schema.md", node_path_1.default.join(docuflowDir, "schema.md"));
187
+ await copyTemplateFile("index.md", node_path_1.default.join(docuflowDir, "index.md"));
188
+ await copyTemplateFile("log.md", node_path_1.default.join(docuflowDir, "log.md"));
189
+ // Generate CLAUDE.md so Claude Code picks up DocuFlow automatically
190
+ await writeClaudeMd(projectDir);
52
191
  // Add .docuflow/ to .gitignore if present and not already listed
53
192
  const gitignorePath = node_path_1.default.join(process.cwd(), ".gitignore");
54
193
  if (node_fs_1.default.existsSync(gitignorePath)) {
@@ -57,11 +196,32 @@ async function run() {
57
196
  await promises_1.default.appendFile(gitignorePath, "\n# Docuflow\n.docuflow/\n");
58
197
  }
59
198
  }
60
- console.log("Docuflow initialised successfully.");
199
+ console.log("āœ“ Docuflow initialised successfully.");
200
+ console.log("");
201
+ console.log("šŸ“ Structure created:");
202
+ console.log(` ${docuflowDir}/`);
203
+ console.log(` ā”œā”€ā”€ specs/ (for legacy specs)`);
204
+ console.log(` ā”œā”€ā”€ wiki/ (LLM-generated pages)`);
205
+ console.log(` │ ā”œā”€ā”€ entities/`);
206
+ console.log(` │ ā”œā”€ā”€ concepts/`);
207
+ console.log(` │ ā”œā”€ā”€ timelines/`);
208
+ console.log(` │ └── syntheses/`);
209
+ console.log(` ā”œā”€ā”€ sources/ (immutable raw files)`);
210
+ console.log(` ā”œā”€ā”€ schema.md (wiki configuration)`);
211
+ console.log(` ā”œā”€ā”€ index.md (auto-maintained catalog)`);
212
+ console.log(` └── log.md (operation log)`);
213
+ console.log("");
214
+ console.log("šŸ“ CLAUDE.md:");
215
+ console.log(` Generated at: ${node_path_1.default.join(projectDir, "CLAUDE.md")}`);
216
+ console.log(` Claude Code will automatically read DocuFlow tool instructions.`);
61
217
  console.log("");
218
+ console.log("šŸ”§ MCP Configuration:");
62
219
  console.log(` MCP key: mcpServers.docuflow`);
63
220
  console.log(` Config file: ${configPath}`);
64
- console.log(` Specs dir: ${specsDir}`);
65
221
  console.log("");
66
- console.log("Restart Claude Desktop to activate.");
222
+ console.log("šŸ“– Next steps:");
223
+ console.log(" 1. Edit .docuflow/schema.md to customize your wiki");
224
+ console.log(" 2. Add source files to .docuflow/sources/");
225
+ console.log(" 3. Use LLM Wiki tools to ingest, query, and maintain wiki");
226
+ console.log(" 4. Restart Claude Desktop to activate");
67
227
  }
@@ -7,6 +7,7 @@ exports.run = run;
7
7
  const promises_1 = __importDefault(require("node:fs/promises"));
8
8
  const node_path_1 = __importDefault(require("node:path"));
9
9
  const node_os_1 = __importDefault(require("node:os"));
10
+ const node_fs_1 = __importDefault(require("node:fs"));
10
11
  function getClaudeDesktopConfigPath() {
11
12
  const platform = process.platform;
12
13
  if (platform === "darwin") {
@@ -17,8 +18,33 @@ function getClaudeDesktopConfigPath() {
17
18
  }
18
19
  return node_path_1.default.join(node_os_1.default.homedir(), ".config", "Claude", "claude_desktop_config.json");
19
20
  }
21
+ async function readLastIngestDate(docuDir) {
22
+ try {
23
+ const log = await promises_1.default.readFile(node_path_1.default.join(docuDir, "log.md"), "utf8");
24
+ const match = log.match(/\[(\d{4}-\d{2}-\d{2}[^\]]*)\]\s+ingest/);
25
+ return match ? match[1] : "never";
26
+ }
27
+ catch {
28
+ return "never";
29
+ }
30
+ }
31
+ async function countWikiPages(docuDir) {
32
+ const counts = { entities: 0, concepts: 0, timelines: 0, syntheses: 0 };
33
+ for (const cat of Object.keys(counts)) {
34
+ try {
35
+ const files = await promises_1.default.readdir(node_path_1.default.join(docuDir, "wiki", cat));
36
+ counts[cat] = files.filter((f) => f.endsWith(".md")).length;
37
+ }
38
+ catch {
39
+ // Directory may not exist
40
+ }
41
+ }
42
+ return counts;
43
+ }
20
44
  async function run() {
21
45
  const configPath = getClaudeDesktopConfigPath();
46
+ const projectDir = process.cwd();
47
+ const docuDir = node_path_1.default.join(projectDir, ".docuflow");
22
48
  // Check MCP registration
23
49
  let registered = false;
24
50
  try {
@@ -31,21 +57,73 @@ async function run() {
31
57
  }
32
58
  // Count spec files
33
59
  let specCount = 0;
34
- const specsDir = node_path_1.default.join(process.cwd(), ".docuflow", "specs");
35
60
  try {
36
- const entries = await promises_1.default.readdir(specsDir);
37
- specCount = entries.filter(e => e.endsWith(".md")).length;
61
+ const entries = await promises_1.default.readdir(node_path_1.default.join(docuDir, "specs"));
62
+ specCount = entries.filter((e) => e.endsWith(".md")).length;
38
63
  }
39
64
  catch {
40
65
  // .docuflow/specs doesn't exist
41
66
  }
42
- console.log("Docuflow status");
43
- console.log("───────────────────────────────");
44
- console.log(` MCP registered: ${registered ? "yes" : "no"}`);
45
- console.log(` Specs written: ${specCount}`);
46
- console.log(` Config file: ${configPath}`);
67
+ // Count sources
68
+ let sourceCount = 0;
69
+ try {
70
+ const entries = await promises_1.default.readdir(node_path_1.default.join(docuDir, "sources"));
71
+ sourceCount = entries.filter((e) => e.endsWith(".md") || e.endsWith(".txt")).length;
72
+ }
73
+ catch {
74
+ // .docuflow/sources doesn't exist
75
+ }
76
+ // Check for CLAUDE.md
77
+ const claudeMdExists = node_fs_1.default.existsSync(node_path_1.default.join(projectDir, "CLAUDE.md"));
78
+ // Count wiki pages per category
79
+ const wikiCounts = await countWikiPages(docuDir);
80
+ const totalWikiPages = Object.values(wikiCounts).reduce((a, b) => a + b, 0);
81
+ // Last ingest date
82
+ const lastIngest = await readLastIngestDate(docuDir);
83
+ // Get CLI version from package.json
84
+ let version = "unknown";
85
+ try {
86
+ const pkgPath = require.resolve("@doquflow/cli/package.json");
87
+ const pkg = JSON.parse(await promises_1.default.readFile(pkgPath, "utf8"));
88
+ version = pkg.version;
89
+ }
90
+ catch {
91
+ try {
92
+ const pkgPath = node_path_1.default.resolve(__dirname, "..", "..", "package.json");
93
+ const pkg = JSON.parse(await promises_1.default.readFile(pkgPath, "utf8"));
94
+ version = pkg.version;
95
+ }
96
+ catch {
97
+ // Can't resolve version
98
+ }
99
+ }
100
+ console.log("\nDocuflow status");
101
+ console.log("───────────────────────────────────────────");
102
+ console.log(` Version: ${version}`);
103
+ console.log(` MCP registered: ${registered ? "āœ“ yes" : "āœ— no"}`);
104
+ console.log(` CLAUDE.md: ${claudeMdExists ? "āœ“ present" : "āœ— missing"}`);
105
+ console.log(` Config file: ${configPath}`);
106
+ console.log("");
107
+ console.log(" Specs written: " + specCount);
108
+ console.log(" Sources: " + sourceCount);
109
+ console.log(" Last ingest: " + lastIngest);
110
+ console.log("");
111
+ console.log(" Wiki pages: " + totalWikiPages + " total");
112
+ if (totalWikiPages > 0) {
113
+ console.log(` entities: ${wikiCounts.entities}`);
114
+ console.log(` concepts: ${wikiCounts.concepts}`);
115
+ console.log(` syntheses: ${wikiCounts.syntheses}`);
116
+ console.log(` timelines: ${wikiCounts.timelines}`);
117
+ }
118
+ console.log("───────────────────────────────────────────");
47
119
  if (!registered) {
48
- console.log("");
49
- console.log(' Run "docuflow init" to register.');
120
+ console.log('\n ⚠ Run "docuflow init" to register MCP server.');
121
+ }
122
+ if (!claudeMdExists) {
123
+ console.log('\n ⚠ Run "docuflow init" to generate CLAUDE.md.');
124
+ }
125
+ if (totalWikiPages === 0 && sourceCount === 0) {
126
+ console.log('\n šŸ’” Run "docuflow suggest" to see what to document first.');
50
127
  }
128
+ console.log();
51
129
  }
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.run = run;
7
+ const promises_1 = __importDefault(require("node:fs/promises"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const node_fs_1 = __importDefault(require("node:fs"));
10
+ const DOMAIN_SUGGESTIONS = {
11
+ "Code/Architecture": [
12
+ { priority: 1, type: "wiki_page", title: "System Architecture Overview", reason: "High-level view of how all components fit together", prompt: "Ingest the README or architecture doc, then ask: 'Create a System Architecture Overview page in the wiki'" },
13
+ { priority: 2, type: "wiki_page", title: "Core Architectural Patterns", reason: "Design patterns used throughout the codebase", prompt: "Ask Claude: 'Scan the codebase with list_modules and identify the core architectural patterns'" },
14
+ { priority: 3, type: "wiki_page", title: "Module Dependencies", reason: "Which modules depend on which — risky to change without knowing", prompt: "Ask Claude: 'Use list_modules to build a module dependency map and write it as a wiki page'" },
15
+ { priority: 4, type: "wiki_page", title: "Database Schema Overview", reason: "Tables, relationships, and ownership by module", prompt: "Ask Claude: 'Scan the project for DB tables using list_modules and create a Database Schema wiki page'" },
16
+ { priority: 5, type: "starter_question", title: "What modules are most connected?", reason: "Identifies the highest-risk code to change", prompt: "Ask Claude: 'Use list_modules on this project and tell me which files have the most dependencies'" },
17
+ ],
18
+ Research: [
19
+ { priority: 1, type: "wiki_page", title: "Research Domain Overview", reason: "Big picture of the research area", prompt: "Ingest your main survey paper, then ask: 'Create a Research Domain Overview page'" },
20
+ { priority: 2, type: "wiki_page", title: "Key Findings & Synthesis", reason: "Major discoveries and insights", prompt: "Ask Claude: 'After ingesting my sources, synthesize the key findings into a wiki page'" },
21
+ { priority: 3, type: "wiki_page", title: "Areas of Contradiction", reason: "Where researchers disagree — critical to track", prompt: "Ask Claude: 'Run lint_wiki and identify any contradictions, then create an Areas of Contradiction page'" },
22
+ { priority: 4, type: "wiki_page", title: "Open Research Questions", reason: "What's still unknown in this domain", prompt: "Ask Claude: 'Based on ingested sources, what questions remain unanswered?'" },
23
+ { priority: 5, type: "starter_question", title: "What are the most cited entities?", reason: "Shows which concepts are most important", prompt: "Ask Claude: 'Which entities appear most often across all ingested sources?'" },
24
+ ],
25
+ Business: [
26
+ { priority: 1, type: "wiki_page", title: "Market Overview", reason: "Big picture of the market and competitive landscape", prompt: "Ingest market research docs, then ask: 'Create a Market Overview wiki page'" },
27
+ { priority: 2, type: "wiki_page", title: "Competitive Analysis", reason: "How competitors differ — essential strategic context", prompt: "Ask Claude: 'After ingesting competitor info, create a Competitive Analysis synthesis page'" },
28
+ { priority: 3, type: "wiki_page", title: "Market Opportunities", reason: "Gaps and growth areas to track", prompt: "Ask Claude: 'Identify market opportunities from ingested sources and create a wiki page'" },
29
+ { priority: 4, type: "wiki_page", title: "Key Players", reason: "Entities (companies, people) to track over time", prompt: "Ask Claude: 'Create an entity page for each key competitor found in sources'" },
30
+ { priority: 5, type: "starter_question", title: "Who are the key players in this market?", reason: "First question to anchor the wiki", prompt: "Ask Claude: 'List the key companies and people from ingested market data'" },
31
+ ],
32
+ Personal: [
33
+ { priority: 1, type: "wiki_page", title: "Learning Goals", reason: "Anchors everything else in the wiki", prompt: "Ask Claude: 'Create a Learning Goals page from my notes in .docuflow/sources/'" },
34
+ { priority: 2, type: "wiki_page", title: "Key Personal Insights", reason: "Preserve insights before they fade", prompt: "Ask Claude: 'Ingest my notes and create a Key Insights synthesis page'" },
35
+ { priority: 3, type: "wiki_page", title: "Key Resources", reason: "Books, courses, links to revisit", prompt: "Ask Claude: 'Extract and create an entity page for each key resource mentioned in my notes'" },
36
+ { priority: 4, type: "wiki_page", title: "Action Items & Next Steps", reason: "Turns knowledge into action", prompt: "Ask Claude: 'What action items can you find across my wiki pages? Create a synthesis page'" },
37
+ { priority: 5, type: "starter_question", title: "What do I already know about this topic?", reason: "Baseline before adding more sources", prompt: "Ask Claude: 'Query the wiki for what I know about [your topic]'" },
38
+ ],
39
+ General: [
40
+ { priority: 1, type: "action", title: "Add your first source file", reason: "Nothing can be ingested until you put a file in .docuflow/sources/", prompt: "Copy your README, main doc, or any markdown file into .docuflow/sources/" },
41
+ { priority: 2, type: "wiki_page", title: "Overview / Summary page", reason: "A starting page that links to everything else", prompt: "Ask Claude: 'Ingest my first source and create an overview synthesis page'" },
42
+ { priority: 3, type: "starter_question", title: "What are the key entities in this domain?", reason: "Gets the wiki started with real content", prompt: "Ask Claude: 'After ingesting my first source, what are the key entities?'" },
43
+ { priority: 4, type: "wiki_page", title: "Key Concepts", reason: "Important ideas worth preserving", prompt: "Ask Claude: 'Create concept pages for the top 5 ideas from ingested sources'" },
44
+ { priority: 5, type: "action", title: "Run get_schema_guidance", reason: "DocuFlow will tell you what's missing based on your domain", prompt: "Ask Claude: 'Run get_schema_guidance on my project and show what pages are recommended'" },
45
+ ],
46
+ };
47
+ function detectDomain(schemaContent) {
48
+ if (/Architecture|Code|codebase|microservice|API/i.test(schemaContent))
49
+ return "Code/Architecture";
50
+ if (/Research|paper|findings|literature/i.test(schemaContent))
51
+ return "Research";
52
+ if (/Business|market|competitor|revenue/i.test(schemaContent))
53
+ return "Business";
54
+ if (/Personal|learning|goal|habit/i.test(schemaContent))
55
+ return "Personal";
56
+ return "General";
57
+ }
58
+ async function countWikiPages(docuDir) {
59
+ const counts = { entities: 0, concepts: 0, timelines: 0, syntheses: 0 };
60
+ for (const cat of Object.keys(counts)) {
61
+ try {
62
+ const files = await promises_1.default.readdir(node_path_1.default.join(docuDir, "wiki", cat));
63
+ counts[cat] = files.filter((f) => f.endsWith(".md")).length;
64
+ }
65
+ catch {
66
+ // Directory may not exist
67
+ }
68
+ }
69
+ return counts;
70
+ }
71
+ async function countSources(docuDir) {
72
+ try {
73
+ const files = await promises_1.default.readdir(node_path_1.default.join(docuDir, "sources"));
74
+ return files.filter((f) => f.endsWith(".md") || f.endsWith(".txt")).length;
75
+ }
76
+ catch {
77
+ return 0;
78
+ }
79
+ }
80
+ async function run() {
81
+ const projectDir = process.cwd();
82
+ const docuDir = node_path_1.default.join(projectDir, ".docuflow");
83
+ if (!node_fs_1.default.existsSync(docuDir)) {
84
+ console.log("⚠ DocuFlow not initialised in this directory.");
85
+ console.log(' Run "docuflow init" first.\n');
86
+ return;
87
+ }
88
+ // Detect domain from schema
89
+ let domain = "General";
90
+ try {
91
+ const schema = await promises_1.default.readFile(node_path_1.default.join(docuDir, "schema.md"), "utf8");
92
+ domain = detectDomain(schema);
93
+ }
94
+ catch {
95
+ // No schema yet — fall back to General
96
+ }
97
+ const [pageCounts, sourceCount] = await Promise.all([
98
+ countWikiPages(docuDir),
99
+ countSources(docuDir),
100
+ ]);
101
+ const totalPages = Object.values(pageCounts).reduce((a, b) => a + b, 0);
102
+ console.log(`\nšŸ’” DocuFlow Suggestions — ${domain} domain\n`);
103
+ console.log(` Current wiki: ${totalPages} pages | ${sourceCount} source(s) ingested\n`);
104
+ if (totalPages === 0 && sourceCount === 0) {
105
+ console.log("🌱 Your wiki is empty. Here's where to start:\n");
106
+ }
107
+ else if (totalPages < 10) {
108
+ console.log("šŸ“š Wiki is growing. Recommended next pages:\n");
109
+ }
110
+ else {
111
+ console.log("āœ… Good coverage. Here's what could be stronger:\n");
112
+ }
113
+ const suggestions = DOMAIN_SUGGESTIONS[domain] ?? DOMAIN_SUGGESTIONS.General;
114
+ for (const s of suggestions) {
115
+ const icon = s.type === "action" ? "ā–¶" : s.type === "starter_question" ? "ā“" : "šŸ“„";
116
+ console.log(` ${s.priority}. ${icon} ${s.title}`);
117
+ console.log(` ${s.reason}`);
118
+ if (s.prompt) {
119
+ console.log(` → ${s.prompt}`);
120
+ }
121
+ console.log();
122
+ }
123
+ console.log("─────────────────────────────────────────────────────");
124
+ console.log("Quick start prompts for Claude:\n");
125
+ console.log(` • "Scan the project at ${projectDir} with list_modules"`);
126
+ console.log(` • "Show me get_schema_guidance for ${projectDir}"`);
127
+ console.log(` • "What wiki pages exist? Run list_wiki on ${projectDir}"`);
128
+ console.log(` • "What should I document first in this project?"`);
129
+ console.log();
130
+ }
package/dist/index.js CHANGED
@@ -33,16 +33,26 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  return result;
34
34
  };
35
35
  })();
36
- const [, , cmd] = process.argv;
36
+ const [, , cmd, flag] = process.argv;
37
37
  if (cmd === 'init') {
38
- Promise.resolve().then(() => __importStar(require('./commands/init'))).then(m => m.run());
38
+ if (flag === '--interactive' || flag === '-i') {
39
+ Promise.resolve().then(() => __importStar(require('./commands/init-interactive'))).then(m => m.runInteractive());
40
+ }
41
+ else {
42
+ Promise.resolve().then(() => __importStar(require('./commands/init'))).then(m => m.run());
43
+ }
39
44
  }
40
45
  else if (cmd === 'status') {
41
46
  Promise.resolve().then(() => __importStar(require('./commands/status'))).then(m => m.run());
42
47
  }
48
+ else if (cmd === 'suggest') {
49
+ Promise.resolve().then(() => __importStar(require('./commands/suggest'))).then(m => m.run());
50
+ }
43
51
  else {
44
- console.log('Usage: npx @doquflow/cli <init|status>');
52
+ console.log('Usage: npx @doquflow/cli <command>');
45
53
  console.log('');
46
- console.log(' init Register Docuflow MCP server in Claude Desktop config');
47
- console.log(' status Show spec count and MCP registration status');
54
+ console.log(' init Register Docuflow MCP and generate CLAUDE.md');
55
+ console.log(' init --interactive Interactive setup wizard (choose domain, project name)');
56
+ console.log(' status Show wiki health, page counts, and MCP status');
57
+ console.log(' suggest Show what to document first (domain-specific guidance)');
48
58
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doquflow/cli",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
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.2.0"
33
+ "@doquflow/server": "0.4.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/node": "^22.0.0",