wize-dev-kit 0.2.5 → 0.3.1

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.
Files changed (31) hide show
  1. package/AGENTS.md +21 -0
  2. package/ARCH.md +40 -4
  3. package/CHANGELOG.md +62 -1
  4. package/README.md +4 -1
  5. package/package.json +3 -2
  6. package/src/method-skills/1-analysis/wize-document-project/documentation-requirements.csv +12 -0
  7. package/src/method-skills/1-analysis/wize-document-project/templates/api-contracts-template.md +38 -0
  8. package/src/method-skills/1-analysis/wize-document-project/templates/architecture-template.md +54 -0
  9. package/src/method-skills/1-analysis/wize-document-project/templates/component-inventory-template.md +40 -0
  10. package/src/method-skills/1-analysis/wize-document-project/templates/contribution-guide-template.md +34 -0
  11. package/src/method-skills/1-analysis/wize-document-project/templates/data-models-template.md +36 -0
  12. package/src/method-skills/1-analysis/wize-document-project/templates/deep-dive-template.md +312 -0
  13. package/src/method-skills/1-analysis/wize-document-project/templates/deployment-guide-template.md +42 -0
  14. package/src/method-skills/1-analysis/wize-document-project/templates/development-guide-template.md +61 -0
  15. package/src/method-skills/1-analysis/wize-document-project/templates/index-template.md +185 -0
  16. package/src/method-skills/1-analysis/wize-document-project/templates/project-overview-template.md +110 -0
  17. package/src/method-skills/1-analysis/wize-document-project/templates/project-scan-report-schema.json +159 -0
  18. package/src/method-skills/1-analysis/wize-document-project/templates/source-tree-template.md +142 -0
  19. package/src/method-skills/1-analysis/wize-document-project/workflow.md +23 -0
  20. package/src/tea-skills/wize-tea-risk/workflow.md +11 -0
  21. package/tools/installer/commands/doctor.js +319 -0
  22. package/tools/installer/commands/document-project.js +93 -0
  23. package/tools/installer/document-project/batch-scanner.js +93 -0
  24. package/tools/installer/document-project/classify.js +170 -0
  25. package/tools/installer/document-project/modes/deep-dive.js +196 -0
  26. package/tools/installer/document-project/modes/full-rescan.js +15 -0
  27. package/tools/installer/document-project/modes/initial-scan.js +100 -0
  28. package/tools/installer/document-project/modes/quick.js +211 -0
  29. package/tools/installer/document-project/render-index.js +101 -0
  30. package/tools/installer/document-project/state.js +110 -0
  31. package/tools/installer/wize-cli.js +49 -30
@@ -0,0 +1,211 @@
1
+ // Quick baseline mode for `wize-dev-kit document-project`.
2
+ //
3
+ // Produces the 6 lightweight baseline files. Does not read source files.
4
+
5
+ 'use strict';
6
+
7
+ const fs = require('node:fs');
8
+ const path = require('node:path');
9
+
10
+ const BASELINE_FILES = {
11
+ 'overview.md': overviewMarkdown,
12
+ 'architecture-snapshot.md': architectureMarkdown,
13
+ 'conventions.md': conventionsMarkdown,
14
+ 'dependencies.md': dependenciesMarkdown,
15
+ 'risk-spots.md': riskMarkdown,
16
+ 'open-questions.md': questionsMarkdown
17
+ };
18
+
19
+ function now() {
20
+ return new Date().toISOString().slice(0, 10);
21
+ }
22
+
23
+ function readPackage(root) {
24
+ try {
25
+ return JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf-8'));
26
+ } catch {
27
+ return {};
28
+ }
29
+ }
30
+
31
+ function countLines(root) {
32
+ let total = 0;
33
+ const dirs = ['src', 'tools', 'adapters', 'schemas', 'test'];
34
+ for (const dir of dirs) {
35
+ const full = path.join(root, dir);
36
+ if (!fs.existsSync(full)) continue;
37
+ total += walkLines(full);
38
+ }
39
+ return total;
40
+ }
41
+
42
+ function walkLines(dir) {
43
+ let total = 0;
44
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
45
+ for (const e of entries) {
46
+ const full = path.join(dir, e.name);
47
+ if (e.isDirectory()) total += walkLines(full);
48
+ else if (e.isFile() && /\.(js|md|yaml|json)$/.test(e.name)) {
49
+ const content = fs.readFileSync(full, 'utf-8');
50
+ total += content.split('\n').length;
51
+ }
52
+ }
53
+ return total;
54
+ }
55
+
56
+ function recentCommitCount(root) {
57
+ // Avoid shelling out; return 0 if not a git repo or git unavailable.
58
+ // Caller can overlay a real count from CLI.
59
+ return 0;
60
+ }
61
+
62
+ function overviewMarkdown(root, pkg) {
63
+ return `---
64
+ status: baseline
65
+ owner: Pepper Potts + Peggy Carter
66
+ created: ${now()}
67
+ last_refreshed: ${now()}
68
+ ---
69
+
70
+ # Overview
71
+
72
+ **Project:** ${pkg.name || path.basename(root)}
73
+ **What it is:** ${pkg.description || 'Project documented by Wize Dev Kit.'}
74
+ **Current version:** ${pkg.version || 'unknown'}
75
+
76
+ ## Size
77
+
78
+ - Source lines of code: ~${countLines(root)} LOC.
79
+ - Runtime dependencies: ${Object.keys(pkg.dependencies || {}).length}.
80
+ - Dev dependencies: ${Object.keys(pkg.devDependencies || {}).length}.
81
+
82
+ ## What it ships
83
+
84
+ - See architecture-snapshot.md for components and entry points.
85
+ - See conventions.md for coding patterns.
86
+ - See dependencies.md for runtime and dev dependency roles.
87
+ `;
88
+ }
89
+
90
+ function architectureMarkdown() {
91
+ return `---
92
+ status: baseline
93
+ owner: Pepper Potts + Tony Stark
94
+ created: ${now()}
95
+ last_refreshed: ${now()}
96
+ ---
97
+
98
+ # Architecture Snapshot
99
+
100
+ ## Entry points
101
+
102
+ - Documented after a deeper scan (initial_scan mode).
103
+
104
+ ## Components
105
+
106
+ - Run \`wize-dev-kit document-project initial_scan deep\` for component-level detail.
107
+
108
+ ## Integration surface
109
+
110
+ - See dependencies.md for external integrations.
111
+ `;
112
+ }
113
+
114
+ function conventionsMarkdown() {
115
+ return `---
116
+ status: baseline
117
+ owner: Peggy Carter
118
+ created: ${now()}
119
+ last_refreshed: ${now()}
120
+ sampled: "pending deeper scan"
121
+ ---
122
+
123
+ # Conventions (observed, not prescribed)
124
+
125
+ ## Quick notes
126
+
127
+ - Run \`wize-dev-kit document-project initial_scan deep\` to sample files and fill this doc.
128
+ `;
129
+ }
130
+
131
+ function dependenciesMarkdown(root, pkg) {
132
+ const runtime = Object.entries(pkg.dependencies || {})
133
+ .map(([name, version]) => `| ${name} | ${version} | | |`).join('\n');
134
+ const dev = Object.entries(pkg.devDependencies || {})
135
+ .map(([name, version]) => `| ${name} | ${version} | | |`).join('\n');
136
+
137
+ return `---
138
+ status: baseline
139
+ owner: Pepper Potts
140
+ created: ${now()}
141
+ last_refreshed: ${now()}
142
+ ---
143
+
144
+ # Dependencies
145
+
146
+ ## Runtime
147
+
148
+ | Name | Version | Role in this repo | Load-bearing? |
149
+ |---|---|---|---|
150
+ ${runtime || '| — | — | — | — |'}
151
+
152
+ ## Dev / bundled
153
+
154
+ | Name | Version | Role in this repo | Load-bearing? |
155
+ |---|---|---|---|
156
+ ${dev || '| — | — | — | — |'}
157
+ `;
158
+ }
159
+
160
+ function riskMarkdown() {
161
+ return `---
162
+ status: baseline
163
+ owner: Pepper Potts + Tony Stark
164
+ created: ${now()}
165
+ last_refreshed: ${now()}
166
+ ---
167
+
168
+ # Risk Spots
169
+
170
+ | Area | Symptom | Likely cause | Confidence |
171
+ |---|---|---|---|
172
+ | | | | |
173
+
174
+ Run \`wize-dev-kit document-project initial_scan deep\` to populate risk spots.
175
+ `;
176
+ }
177
+
178
+ function questionsMarkdown() {
179
+ return `---
180
+ status: baseline
181
+ owner: Pepper Potts + Peggy Carter
182
+ created: ${now()}
183
+ last_refreshed: ${now()}
184
+ ---
185
+
186
+ # Open Questions
187
+
188
+ | Question | Why it matters | Owner to ask |
189
+ |---|---|---|
190
+ | | | |
191
+ `;
192
+ }
193
+
194
+ function runQuick(projectRoot, opts = {}) {
195
+ const pkg = readPackage(projectRoot);
196
+ const dir = path.join(projectRoot, '.wize', 'knowledge', 'document-project');
197
+ fs.mkdirSync(dir, { recursive: true });
198
+
199
+ const written = [];
200
+ for (const [name, factory] of Object.entries(BASELINE_FILES)) {
201
+ const file = path.join(dir, name);
202
+ const content = factory(projectRoot, pkg);
203
+ fs.writeFileSync(file, content, 'utf-8');
204
+ written.push(name);
205
+ }
206
+
207
+ if (opts.log) opts.log(`Quick baseline written to ${dir}`);
208
+ return { changed: true, mode: 'quick', written };
209
+ }
210
+
211
+ module.exports = { runQuick, BASELINE_FILES, countLines };
@@ -0,0 +1,101 @@
1
+ // Master index renderer for `wize-dev-kit document-project`.
2
+ //
3
+ // Generates `.wize/knowledge/document-project/index.md` linking all docs.
4
+
5
+ 'use strict';
6
+
7
+ const fs = require('node:fs');
8
+ const path = require('node:path');
9
+
10
+ function link(name, file) {
11
+ return `- [${name}](./${file})`;
12
+ }
13
+
14
+ function linkWithMarker(name, file, exists) {
15
+ const base = link(name, file);
16
+ return exists ? base : `${base} _(To be generated)_`;
17
+ }
18
+
19
+ function renderIndex(projectRoot, { projectTypes = [], parts = [], generated = [], existing = [], deepDiveFiles = [] } = {}) {
20
+ const dir = path.join(projectRoot, '.wize', 'knowledge', 'document-project');
21
+ fs.mkdirSync(dir, { recursive: true });
22
+
23
+ const generatedSet = new Set(generated.map(g => path.basename(g)));
24
+ const existingSet = new Set(existing.map(e => path.basename(e)));
25
+ const has = name => generatedSet.has(name) || existingSet.has(name);
26
+
27
+ const isMultiPart = parts.length >= 2;
28
+ const primaryType = projectTypes[0] || 'unknown';
29
+
30
+ const lines = [
31
+ '---',
32
+ 'status: baseline',
33
+ 'owner: Pepper Potts + Peggy Carter',
34
+ `created: ${new Date().toISOString().slice(0, 10)}`,
35
+ `last_refreshed: ${new Date().toISOString().slice(0, 10)}`,
36
+ '---',
37
+ '',
38
+ '# Project Documentation Index',
39
+ '',
40
+ `**Type:** ${isMultiPart ? 'multi-part' : primaryType}`,
41
+ `**Primary project type(s):** ${projectTypes.join(', ') || 'unknown'}`,
42
+ '',
43
+ '## Generated Documentation',
44
+ '',
45
+ link('Project Overview', 'project-overview.md'),
46
+ linkWithMarker('Source Tree Analysis', 'source-tree-analysis.md', has('source-tree-analysis.md')),
47
+ ];
48
+
49
+ if (isMultiPart) {
50
+ for (const part of parts) {
51
+ const suffix = part.part_id === 'root' ? '' : `-${part.part_id}`;
52
+ lines.push(linkWithMarker(`Architecture — ${part.part_id || 'root'}`, `architecture${suffix}.md`, has(`architecture${suffix}.md`)));
53
+ lines.push(linkWithMarker(`Development Guide — ${part.part_id || 'root'}`, `development-guide${suffix}.md`, has(`development-guide${suffix}.md`)));
54
+ lines.push(linkWithMarker(`Component Inventory — ${part.part_id || 'root'}`, `component-inventory${suffix}.md`, has(`component-inventory${suffix}.md`)));
55
+ lines.push(linkWithMarker(`API Contracts — ${part.part_id || 'root'}`, `api-contracts${suffix}.md`, has(`api-contracts${suffix}.md`)));
56
+ lines.push(linkWithMarker(`Data Models — ${part.part_id || 'root'}`, `data-models${suffix}.md`, has(`data-models${suffix}.md`)));
57
+ }
58
+ lines.push(linkWithMarker('Integration Architecture', 'integration-architecture.md', has('integration-architecture.md')));
59
+ } else {
60
+ lines.push(linkWithMarker('Architecture', 'architecture.md', has('architecture.md')));
61
+ lines.push(linkWithMarker('Development Guide', 'development-guide.md', has('development-guide.md')));
62
+ lines.push(linkWithMarker('Component Inventory', 'component-inventory.md', has('component-inventory.md')));
63
+ lines.push(linkWithMarker('API Contracts', 'api-contracts.md', has('api-contracts.md')));
64
+ lines.push(linkWithMarker('Data Models', 'data-models.md', has('data-models.md')));
65
+ }
66
+
67
+ lines.push(linkWithMarker('Deployment Guide', 'deployment-guide.md', has('deployment-guide.md')));
68
+ lines.push(linkWithMarker('Contribution Guide', 'contribution-guide.md', has('contribution-guide.md')));
69
+ if (deepDiveFiles.length) {
70
+ lines.push('', '## Deep-Dive Docs', '');
71
+ for (const f of deepDiveFiles) {
72
+ lines.push(link(f.replace(/^deep-dive-/, '').replace(/\.md$/, ''), f));
73
+ }
74
+ } else {
75
+ lines.push(linkWithMarker('Deep-Dive Docs', 'deep-dive/', false)); // directory marker
76
+ }
77
+
78
+ if (existing.length) {
79
+ lines.push('', '## Existing Documentation', '');
80
+ for (const doc of existing) {
81
+ const rel = path.relative(dir, doc);
82
+ lines.push(`- [${path.basename(doc)}](./${rel})`);
83
+ }
84
+ }
85
+
86
+ lines.push('', '## Brownfield Baseline', '');
87
+ lines.push(link('Overview', 'overview.md'));
88
+ lines.push(link('Architecture Snapshot', 'architecture-snapshot.md'));
89
+ lines.push(link('Conventions', 'conventions.md'));
90
+ lines.push(link('Dependencies', 'dependencies.md'));
91
+ lines.push(link('Risk Spots', 'risk-spots.md'));
92
+ lines.push(link('Open Questions', 'open-questions.md'));
93
+
94
+ lines.push('', '---', '', '_Generated by `wize-document-project`._', '');
95
+
96
+ const file = path.join(dir, 'index.md');
97
+ fs.writeFileSync(file, lines.join('\n'), 'utf-8');
98
+ return { changed: true, written: ['index.md'] };
99
+ }
100
+
101
+ module.exports = { renderIndex, link, linkWithMarker };
@@ -0,0 +1,110 @@
1
+ // State file management for `wize-dev-kit document-project`.
2
+ //
3
+ // Tracks scan progress so long-running deep/exhaustive scans can resume.
4
+
5
+ 'use strict';
6
+
7
+ const fs = require('node:fs');
8
+ const path = require('node:path');
9
+
10
+ const WORKFLOW_VERSION = '0.3.1';
11
+ const STATE_FILE = 'project-scan-report.json';
12
+ const ARCHIVE_DIR = '_archive';
13
+
14
+ function nowIso() {
15
+ return new Date().toISOString();
16
+ }
17
+
18
+ function statePath(projectRoot) {
19
+ return path.join(projectRoot, '.wize', 'knowledge', 'document-project', STATE_FILE);
20
+ }
21
+
22
+ function archivePath(projectRoot, timestamp) {
23
+ const dir = path.join(projectRoot, '.wize', 'knowledge', 'document-project', ARCHIVE_DIR);
24
+ fs.mkdirSync(dir, { recursive: true });
25
+ const safe = timestamp.replace(/[:.]/g, '-');
26
+ return path.join(dir, `project-scan-report-${safe}.json`);
27
+ }
28
+
29
+ function initState(projectRoot, mode, scanLevel) {
30
+ const state = {
31
+ workflow_version: WORKFLOW_VERSION,
32
+ timestamps: { started: nowIso(), last_updated: nowIso() },
33
+ mode,
34
+ scan_level: scanLevel,
35
+ project_root: projectRoot,
36
+ project_knowledge: path.join(projectRoot, '.wize', 'knowledge', 'document-project'),
37
+ completed_steps: [],
38
+ current_step: 'step_1',
39
+ findings: {},
40
+ outputs_generated: [STATE_FILE],
41
+ resume_instructions: `Starting ${mode} (${scanLevel}) from step_1`
42
+ };
43
+ writeState(projectRoot, state);
44
+ return state;
45
+ }
46
+
47
+ function loadState(projectRoot) {
48
+ const p = statePath(projectRoot);
49
+ if (!fs.existsSync(p)) return null;
50
+ try {
51
+ const content = fs.readFileSync(p, 'utf-8');
52
+ const state = JSON.parse(content);
53
+ if (!state.workflow_version || !state.timestamps || !state.mode || !state.scan_level) {
54
+ return null;
55
+ }
56
+ return state;
57
+ } catch {
58
+ return null;
59
+ }
60
+ }
61
+
62
+ function writeState(projectRoot, state) {
63
+ const p = statePath(projectRoot);
64
+ fs.mkdirSync(path.dirname(p), { recursive: true });
65
+ state.timestamps.last_updated = nowIso();
66
+ fs.writeFileSync(p, JSON.stringify(state, null, 2), 'utf-8');
67
+ }
68
+
69
+ function updateState(projectRoot, patch) {
70
+ const state = loadState(projectRoot) || initState(projectRoot, 'quick', 'quick');
71
+ for (const key of Object.keys(patch)) {
72
+ const val = patch[key];
73
+ if (Array.isArray(val) && Array.isArray(state[key])) {
74
+ state[key].push(...val);
75
+ } else if (typeof val === 'object' && val !== null && !Array.isArray(val)) {
76
+ state[key] = { ...(state[key] || {}), ...val };
77
+ } else {
78
+ state[key] = val;
79
+ }
80
+ }
81
+ writeState(projectRoot, state);
82
+ return state;
83
+ }
84
+
85
+ function archiveOldState(projectRoot) {
86
+ const p = statePath(projectRoot);
87
+ if (!fs.existsSync(p)) return false;
88
+ const content = fs.readFileSync(p, 'utf-8');
89
+ const archived = archivePath(projectRoot, nowIso());
90
+ fs.writeFileSync(archived, content, 'utf-8');
91
+ fs.rmSync(p);
92
+ return true;
93
+ }
94
+
95
+ function stateAgeDays(state) {
96
+ if (!state || !state.timestamps || !state.timestamps.last_updated) return null;
97
+ const then = Date.parse(state.timestamps.last_updated);
98
+ if (Number.isNaN(then)) return null;
99
+ return Math.floor((Date.now() - then) / (24 * 3600 * 1000));
100
+ }
101
+
102
+ module.exports = {
103
+ statePath,
104
+ initState,
105
+ loadState,
106
+ updateState,
107
+ archiveOldState,
108
+ stateAgeDays,
109
+ WORKFLOW_VERSION
110
+ };
@@ -16,10 +16,11 @@ const readline = require('node:readline');
16
16
  const prompts = require('prompts');
17
17
  const { applyGitignore, generateUserToml } = require('./setup-helpers.js');
18
18
  const { cmdUpdate } = require('./commands/update.js');
19
- const { detectHarnessCli, runHeadlessBaseline, manualInstructions, defaultPrompt } = require('./baseline.js');
20
19
  const { printUpdateHintIfAny } = require('./version-check.js');
21
20
  const { cmdSync: cmdSyncReal } = require('./commands/sync.js');
22
21
  const { cmdAgentList, cmdAgentCreate, cmdAgentEdit } = require('./commands/agent.js');
22
+ const { cmdDoctor } = require('./commands/doctor.js');
23
+ const { cmdDocumentProject } = require('./commands/document-project.js');
23
24
 
24
25
  const INTERACTIVE = process.stdout.isTTY && process.stdin.isTTY;
25
26
 
@@ -72,6 +73,8 @@ Commands:
72
73
  agent <create|list> Manage agents (built-in or custom).
73
74
  workflow <create|list> Manage workflows.
74
75
  validate Run schema + lint + dry-run validators.
76
+ doctor Diagnose the kit + project state, suggest fixes.
77
+ document-project Document the current repo (quick | initial_scan | full_rescan | deep_dive).
75
78
  help Show this message.
76
79
 
77
80
  Documentation:
@@ -135,6 +138,23 @@ async function confirm(question, defaultYes = true) {
135
138
  return ans.startsWith('y');
136
139
  }
137
140
 
141
+ async function select(label, choices, defaultValue) {
142
+ if (INTERACTIVE) {
143
+ const initial = choices.findIndex(c => c.value === defaultValue);
144
+ const { picked } = await prompts({
145
+ type: 'select',
146
+ name: 'picked',
147
+ message: label,
148
+ choices: choices.map(c => ({ title: c.title, value: c.value, description: c.description })),
149
+ initial: initial === -1 ? 0 : initial
150
+ });
151
+ if (picked === undefined) process.exit(130);
152
+ return picked;
153
+ }
154
+ // Non-TTY fallback: return default.
155
+ return defaultValue;
156
+ }
157
+
138
158
  async function selectLanguage(label, defaultCode = 'en') {
139
159
  if (INTERACTIVE) {
140
160
  const choices = LANGUAGES.map(l => ({
@@ -417,35 +437,26 @@ async function cmdInstall(args) {
417
437
  }
418
438
 
419
439
  if (detection.brownfield) {
420
- const baseline = await confirm('\nRun `wize-document-project` to baseline the existing repo now?', true);
421
- if (baseline) {
422
- const preferIde = targets.map(t => t.code);
423
- const harnesses = detectHarnessCli({ preferIde });
424
- if (process.env.WIZE_SKIP_BASELINE === '1') {
425
- console.log('\nWIZE_SKIP_BASELINE=1 not running the baseline.');
426
- console.log(manualInstructions(harnesses[0]));
427
- } else if (harnesses.length === 0) {
428
- console.log(manualInstructions(null));
440
+ if (process.env.WIZE_SKIP_BASELINE === '1') {
441
+ console.log('\nWIZE_SKIP_BASELINE=1 — not running the baseline.');
442
+ } else if (!INTERACTIVE) {
443
+ console.log('\nNon-interactive install detected; running quick baseline...');
444
+ cmdDocumentProject({ kitRoot: KIT_ROOT, projectRoot: cwd, args: ['quick'], opts: { log: console.log } });
445
+ } else {
446
+ const mode = await select(
447
+ 'Document the existing repo now?',
448
+ [
449
+ { title: 'Quick baseline (default, 6 files)', value: 'quick' },
450
+ { title: 'Initial scan (pattern + conditional docs)', value: 'initial_scan' },
451
+ { title: 'Full rescan (archive old state, re-run)', value: 'full_rescan' },
452
+ { title: 'Skip documentation for now', value: 'skip' }
453
+ ],
454
+ 'quick'
455
+ );
456
+ if (mode !== 'skip') {
457
+ cmdDocumentProject({ kitRoot: KIT_ROOT, projectRoot: cwd, args: [mode], opts: { log: console.log } });
429
458
  } else {
430
- const chosen = harnesses[0];
431
- console.log(`\nDetected harness: ${chosen.binary} (${chosen.path}).`);
432
- const confirmRun = await confirm(
433
- `Run /wize-document-project via ${chosen.binary} now?`,
434
- true
435
- );
436
- if (confirmRun) {
437
- const r = runHeadlessBaseline({
438
- harness: chosen,
439
- projectRoot: cwd,
440
- prompt: defaultPrompt()
441
- });
442
- if (!r.ok && !r.skipped) {
443
- console.log(`\n${chosen.binary} exited with code ${r.exitCode}. You can re-run later with:`);
444
- console.log(manualInstructions(chosen));
445
- }
446
- } else {
447
- console.log(manualInstructions(chosen));
448
- }
459
+ console.log('\nSkipped documentation. Run `wize-dev-kit document-project` later.');
449
460
  }
450
461
  }
451
462
  }
@@ -540,7 +551,7 @@ function cmdValidate() {
540
551
  // for `update` (already updating), `install` (already setting up), `uninstall`
541
552
  // (already leaving), `validate` (developer-tool), and `version` (the user is
542
553
  // already asking about versions).
543
- const HINT_COMMANDS = new Set(['list', 'sync', 'agent', 'workflow', 'help']);
554
+ const HINT_COMMANDS = new Set(['list', 'sync', 'agent', 'workflow', 'help', 'doctor', 'document-project']);
544
555
 
545
556
  async function main() {
546
557
  const [cmd, ...rest] = process.argv.slice(2);
@@ -561,6 +572,14 @@ async function main() {
561
572
  case 'agent': return cmdAgent(rest);
562
573
  case 'workflow': return cmdWorkflow(rest);
563
574
  case 'validate': return cmdValidate();
575
+ case 'doctor': return cmdDoctor({ kitRoot: KIT_ROOT, projectRoot: process.cwd() });
576
+ case 'document-project':
577
+ return cmdDocumentProject({
578
+ kitRoot: KIT_ROOT,
579
+ projectRoot: process.cwd(),
580
+ args: rest,
581
+ opts: { log: console.log }
582
+ });
564
583
  case 'version':
565
584
  case '--version':
566
585
  case '-v':