devworkspace 0.0.0-stage → 0.1.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.
Files changed (187) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/LICENSE +21 -0
  3. package/README.md +127 -3
  4. package/dist/agents/claude/adapter.d.ts +10 -0
  5. package/dist/agents/claude/adapter.js +97 -0
  6. package/dist/agents/claude/adapter.js.map +1 -0
  7. package/dist/agents/claude/content.d.ts +7 -0
  8. package/dist/agents/claude/content.js +136 -0
  9. package/dist/agents/claude/content.js.map +1 -0
  10. package/dist/agents/types.d.ts +26 -0
  11. package/dist/agents/types.js +2 -0
  12. package/dist/agents/types.js.map +1 -0
  13. package/dist/cli/args.d.ts +194 -0
  14. package/dist/cli/args.js +93 -0
  15. package/dist/cli/args.js.map +1 -0
  16. package/dist/cli/help.d.ts +10 -0
  17. package/dist/cli/help.js +169 -0
  18. package/dist/cli/help.js.map +1 -0
  19. package/dist/cli/main.d.ts +2 -0
  20. package/dist/cli/main.js +19 -0
  21. package/dist/cli/main.js.map +1 -0
  22. package/dist/cli/render.d.ts +39 -0
  23. package/dist/cli/render.js +211 -0
  24. package/dist/cli/render.js.map +1 -0
  25. package/dist/cli/run.d.ts +10 -0
  26. package/dist/cli/run.js +329 -0
  27. package/dist/cli/run.js.map +1 -0
  28. package/dist/config/schemas.d.ts +76 -0
  29. package/dist/config/schemas.js +83 -0
  30. package/dist/config/schemas.js.map +1 -0
  31. package/dist/config/validate.d.ts +22 -0
  32. package/dist/config/validate.js +76 -0
  33. package/dist/config/validate.js.map +1 -0
  34. package/dist/core/migrations.d.ts +15 -0
  35. package/dist/core/migrations.js +34 -0
  36. package/dist/core/migrations.js.map +1 -0
  37. package/dist/core/version.d.ts +2 -0
  38. package/dist/core/version.js +12 -0
  39. package/dist/core/version.js.map +1 -0
  40. package/dist/core/versioned.d.ts +13 -0
  41. package/dist/core/versioned.js +16 -0
  42. package/dist/core/versioned.js.map +1 -0
  43. package/dist/core/workspace.d.ts +34 -0
  44. package/dist/core/workspace.js +92 -0
  45. package/dist/core/workspace.js.map +1 -0
  46. package/dist/decisions/decision.d.ts +31 -0
  47. package/dist/decisions/decision.js +123 -0
  48. package/dist/decisions/decision.js.map +1 -0
  49. package/dist/discovery/commands.d.ts +12 -0
  50. package/dist/discovery/commands.js +92 -0
  51. package/dist/discovery/commands.js.map +1 -0
  52. package/dist/discovery/detect.d.ts +72 -0
  53. package/dist/discovery/detect.js +421 -0
  54. package/dist/discovery/detect.js.map +1 -0
  55. package/dist/discovery/ignore.d.ts +6 -0
  56. package/dist/discovery/ignore.js +76 -0
  57. package/dist/discovery/ignore.js.map +1 -0
  58. package/dist/discovery/scan.d.ts +24 -0
  59. package/dist/discovery/scan.js +132 -0
  60. package/dist/discovery/scan.js.map +1 -0
  61. package/dist/errors/index.d.ts +25 -0
  62. package/dist/errors/index.js +39 -0
  63. package/dist/errors/index.js.map +1 -0
  64. package/dist/git/git.d.ts +27 -0
  65. package/dist/git/git.js +97 -0
  66. package/dist/git/git.js.map +1 -0
  67. package/dist/index.d.ts +26 -0
  68. package/dist/index.js +21 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/integrations/agent-skills.d.ts +10 -0
  71. package/dist/integrations/agent-skills.js +45 -0
  72. package/dist/integrations/agent-skills.js.map +1 -0
  73. package/dist/integrations/graphify.d.ts +7 -0
  74. package/dist/integrations/graphify.js +54 -0
  75. package/dist/integrations/graphify.js.map +1 -0
  76. package/dist/integrations/index.d.ts +6 -0
  77. package/dist/integrations/index.js +30 -0
  78. package/dist/integrations/index.js.map +1 -0
  79. package/dist/integrations/omniroute.d.ts +7 -0
  80. package/dist/integrations/omniroute.js +24 -0
  81. package/dist/integrations/omniroute.js.map +1 -0
  82. package/dist/integrations/ponytail.d.ts +10 -0
  83. package/dist/integrations/ponytail.js +58 -0
  84. package/dist/integrations/ponytail.js.map +1 -0
  85. package/dist/integrations/probe.d.ts +6 -0
  86. package/dist/integrations/probe.js +41 -0
  87. package/dist/integrations/probe.js.map +1 -0
  88. package/dist/integrations/types.d.ts +25 -0
  89. package/dist/integrations/types.js +2 -0
  90. package/dist/integrations/types.js.map +1 -0
  91. package/dist/knowledge/facts.d.ts +43 -0
  92. package/dist/knowledge/facts.js +195 -0
  93. package/dist/knowledge/facts.js.map +1 -0
  94. package/dist/portability/bundle.d.ts +22 -0
  95. package/dist/portability/bundle.js +102 -0
  96. package/dist/portability/bundle.js.map +1 -0
  97. package/dist/prompts/onboarding.d.ts +19 -0
  98. package/dist/prompts/onboarding.js +53 -0
  99. package/dist/prompts/onboarding.js.map +1 -0
  100. package/dist/prompts/refresh.d.ts +15 -0
  101. package/dist/prompts/refresh.js +33 -0
  102. package/dist/prompts/refresh.js.map +1 -0
  103. package/dist/prompts/requirements.d.ts +9 -0
  104. package/dist/prompts/requirements.js +34 -0
  105. package/dist/prompts/requirements.js.map +1 -0
  106. package/dist/security/files.d.ts +5 -0
  107. package/dist/security/files.js +44 -0
  108. package/dist/security/files.js.map +1 -0
  109. package/dist/security/redact.d.ts +13 -0
  110. package/dist/security/redact.js +43 -0
  111. package/dist/security/redact.js.map +1 -0
  112. package/dist/services/agent.d.ts +11 -0
  113. package/dist/services/agent.js +42 -0
  114. package/dist/services/agent.js.map +1 -0
  115. package/dist/services/analysis.d.ts +45 -0
  116. package/dist/services/analysis.js +151 -0
  117. package/dist/services/analysis.js.map +1 -0
  118. package/dist/services/common.d.ts +18 -0
  119. package/dist/services/common.js +18 -0
  120. package/dist/services/common.js.map +1 -0
  121. package/dist/services/doctor.d.ts +27 -0
  122. package/dist/services/doctor.js +223 -0
  123. package/dist/services/doctor.js.map +1 -0
  124. package/dist/services/init.d.ts +18 -0
  125. package/dist/services/init.js +135 -0
  126. package/dist/services/init.js.map +1 -0
  127. package/dist/services/knowledge.d.ts +40 -0
  128. package/dist/services/knowledge.js +72 -0
  129. package/dist/services/knowledge.js.map +1 -0
  130. package/dist/services/migrate.d.ts +16 -0
  131. package/dist/services/migrate.js +54 -0
  132. package/dist/services/migrate.js.map +1 -0
  133. package/dist/services/portability.d.ts +27 -0
  134. package/dist/services/portability.js +155 -0
  135. package/dist/services/portability.js.map +1 -0
  136. package/dist/services/records.d.ts +8 -0
  137. package/dist/services/records.js +26 -0
  138. package/dist/services/records.js.map +1 -0
  139. package/dist/services/refresh.d.ts +22 -0
  140. package/dist/services/refresh.js +64 -0
  141. package/dist/services/refresh.js.map +1 -0
  142. package/dist/services/status.d.ts +57 -0
  143. package/dist/services/status.js +111 -0
  144. package/dist/services/status.js.map +1 -0
  145. package/dist/services/tasks.d.ts +44 -0
  146. package/dist/services/tasks.js +136 -0
  147. package/dist/services/tasks.js.map +1 -0
  148. package/dist/sessions/session.d.ts +19 -0
  149. package/dist/sessions/session.js +53 -0
  150. package/dist/sessions/session.js.map +1 -0
  151. package/dist/tasks/routing.d.ts +27 -0
  152. package/dist/tasks/routing.js +206 -0
  153. package/dist/tasks/routing.js.map +1 -0
  154. package/dist/tasks/task.d.ts +90 -0
  155. package/dist/tasks/task.js +275 -0
  156. package/dist/tasks/task.js.map +1 -0
  157. package/dist/utils/cleanup.d.ts +3 -0
  158. package/dist/utils/cleanup.js +18 -0
  159. package/dist/utils/cleanup.js.map +1 -0
  160. package/dist/utils/fs.d.ts +17 -0
  161. package/dist/utils/fs.js +164 -0
  162. package/dist/utils/fs.js.map +1 -0
  163. package/dist/utils/lock.d.ts +6 -0
  164. package/dist/utils/lock.js +77 -0
  165. package/dist/utils/lock.js.map +1 -0
  166. package/dist/utils/logger.d.ts +11 -0
  167. package/dist/utils/logger.js +18 -0
  168. package/dist/utils/logger.js.map +1 -0
  169. package/dist/utils/paths.d.ts +18 -0
  170. package/dist/utils/paths.js +95 -0
  171. package/dist/utils/paths.js.map +1 -0
  172. package/dist/utils/proc.d.ts +22 -0
  173. package/dist/utils/proc.js +74 -0
  174. package/dist/utils/proc.js.map +1 -0
  175. package/docs/architecture.md +53 -0
  176. package/docs/benchmarking.md +33 -0
  177. package/docs/claude.md +46 -0
  178. package/docs/contributing.md +64 -0
  179. package/docs/getting-started.md +68 -0
  180. package/docs/integrations.md +51 -0
  181. package/docs/knowledge.md +66 -0
  182. package/docs/portability.md +40 -0
  183. package/docs/routing.md +51 -0
  184. package/docs/security.md +44 -0
  185. package/docs/testing.md +58 -0
  186. package/docs/troubleshooting.md +26 -0
  187. package/package.json +68 -4
@@ -0,0 +1,151 @@
1
+ import path from 'node:path';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { SCHEMA_VERSION, validateCommands } from '../config/schemas.js';
4
+ import { TRUST_NOTE } from '../discovery/commands.js';
5
+ import { detectProject } from '../discovery/detect.js';
6
+ import { scanProject } from '../discovery/scan.js';
7
+ import { getGitState } from '../git/git.js';
8
+ import { detectIntegrations } from '../integrations/index.js';
9
+ import { buildFact, loadFacts, saveFacts, upsertFact } from '../knowledge/facts.js';
10
+ import { loadProject } from '../core/workspace.js';
11
+ import { readVersioned, writeVersioned } from '../core/versioned.js';
12
+ import { packageVersion } from '../core/version.js';
13
+ import { pathExists, sha256Text, writeJsonIfChanged, writeTextIfChanged, atomicWriteJson } from '../utils/fs.js';
14
+ import { isSensitiveFile } from '../security/files.js';
15
+ import { buildOnboardingPrompt } from '../prompts/onboarding.js';
16
+ import { claudeAdapter } from '../agents/claude/adapter.js';
17
+ import { integrationCtx, isoNow } from './common.js';
18
+ export async function runDiscovery(ws, projectName, maxFiles) {
19
+ const scan = await scanProject(ws.root, maxFiles ? { maxFiles } : {});
20
+ const [discovery, git] = await Promise.all([detectProject({ root: ws.root, projectName, scan }), getGitState(ws.root)]);
21
+ return {
22
+ discovery,
23
+ gitHead: git.head,
24
+ git: { isRepo: git.isRepo, branch: git.branch, dirty: git.dirty, changedFiles: git.entries.length },
25
+ };
26
+ }
27
+ function existingFiles(files, max = 10) {
28
+ return files.filter((f) => !isSensitiveFile(f)).slice(0, max);
29
+ }
30
+ /** Facts derived purely from deterministic detection. They are re-derived on every analyze/refresh. */
31
+ export function detectorFacts(d) {
32
+ const out = [];
33
+ const add = (key, value, source, sources = []) => {
34
+ out.push({ key, value, source, sourceType: 'detector', sources, confidence: 'HIGH', status: 'DETECTED' });
35
+ };
36
+ const manifests = ['package.json', 'pyproject.toml', 'requirements.txt', 'go.mod', 'Cargo.toml', 'pom.xml', 'composer.json'];
37
+ const present = (f) => d.frameworks.some((x) => x.evidence === f) || d.commands.some((c) => c.source?.startsWith(f));
38
+ if (d.ecosystems.length > 0) {
39
+ add('stack.ecosystems', d.ecosystems.join(', '), 'repository manifests', manifests.filter(present));
40
+ }
41
+ if (d.languages.length > 0) {
42
+ add('stack.languages', d.languages.slice(0, 6).map((l) => `${l.name} (${l.files} files)`).join(', '), 'file extension counts');
43
+ }
44
+ if (d.packageManager)
45
+ add('stack.package-manager', d.packageManager, 'lockfiles / package manifests');
46
+ if (d.frameworks.length > 0) {
47
+ add('stack.frameworks', d.frameworks.map((f) => f.name).join(', '), 'dependency manifests', [...new Set(d.frameworks.map((f) => f.evidence))].slice(0, 10));
48
+ }
49
+ if (d.monorepo.tool) {
50
+ add('repo.monorepo', `${d.monorepo.tool}: ${d.monorepo.packages.map((p) => p.path).slice(0, 30).join(', ') || 'packages not enumerated'}`, 'workspace configuration');
51
+ }
52
+ if (d.ci.length > 0)
53
+ add('repo.ci', d.ci.join(', '), 'CI configuration', existingFiles(d.ci));
54
+ if (d.docker.length > 0)
55
+ add('repo.docker', d.docker.join(', '), 'container files', existingFiles(d.docker));
56
+ if (d.deployment.length > 0)
57
+ add('repo.deployment-hints', d.deployment.join(', '), 'deployment configuration', existingFiles(d.deployment));
58
+ if (d.testConfigs.length > 0)
59
+ add('repo.test-configs', d.testConfigs.join(', '), 'test configuration files', existingFiles(d.testConfigs));
60
+ if (d.aiInstructions.length > 0)
61
+ add('repo.ai-instructions', d.aiInstructions.join(', '), 'existing agent instruction files', existingFiles(d.aiInstructions));
62
+ return out;
63
+ }
64
+ export async function applyDetectorFacts(ws, d, now) {
65
+ const facts = await loadFacts(ws);
66
+ const written = [];
67
+ const skipped = [];
68
+ const keys = new Set();
69
+ for (const input of detectorFacts(d)) {
70
+ keys.add(input.key);
71
+ const existing = facts.facts.find((f) => f.key === input.key);
72
+ try {
73
+ const fact = await buildFact(ws.root, input, now, existing);
74
+ const action = upsertFact(facts, fact);
75
+ if (action !== 'unchanged')
76
+ written.push(input.key);
77
+ }
78
+ catch {
79
+ skipped.push(input.key);
80
+ }
81
+ }
82
+ // Detector facts that no longer apply are dropped: they are derived data, recreated if the evidence returns,
83
+ // and leaving them as STALE would produce a warning that no human or agent action can clear.
84
+ const retired = facts.facts.filter((f) => f.sourceType === 'detector' && !keys.has(f.key)).map((f) => f.key);
85
+ if (retired.length > 0)
86
+ facts.facts = facts.facts.filter((f) => !retired.includes(f.key));
87
+ await saveFacts(ws, facts);
88
+ return { written, skipped, retired };
89
+ }
90
+ export async function writeCommands(ws, detected) {
91
+ let previous = null;
92
+ if (await pathExists(ws.commands)) {
93
+ try {
94
+ previous = (await readVersioned(ws.commands, 'commands', SCHEMA_VERSION, validateCommands)).data;
95
+ }
96
+ catch {
97
+ previous = null;
98
+ }
99
+ }
100
+ // Entries a human or the agent edited (origin "user") are never overwritten by detection.
101
+ const merged = detected.map((d) => previous?.checks.find((p) => p.id === d.id && p.origin === 'user') ?? d);
102
+ const file = { schemaVersion: SCHEMA_VERSION, trustNote: TRUST_NOTE, checks: merged };
103
+ validateCommands(file);
104
+ await writeVersioned(ws, ws.commands, file);
105
+ return true;
106
+ }
107
+ export async function analyzeWorkspace(ws, o) {
108
+ const project = await loadProject(ws);
109
+ const now = isoNow(o);
110
+ const run = await runDiscovery(ws, project.name, project.settings.maxFiles);
111
+ const integrations = await detectIntegrations(integrationCtx(o, ws.root));
112
+ const existingAgentFiles = await claudeAdapter.detectExisting(ws.root);
113
+ const prompt = buildOnboardingPrompt({
114
+ projectName: project.name,
115
+ discovery: run.discovery,
116
+ integrations,
117
+ git: run.git,
118
+ existingAgentFiles,
119
+ cliVersion: packageVersion(),
120
+ });
121
+ const rel = (p) => path.relative(ws.root, p).replace(/\\/g, '/');
122
+ const files = { discovery: rel(ws.discovery), onboardingPrompt: rel(ws.onboardingPrompt), commands: rel(ws.commands) };
123
+ const nextSteps = [
124
+ `Open ${files.onboardingPrompt}`,
125
+ 'Paste it into Claude Code (or Claude in VS Code) in this project and let it finish onboarding.',
126
+ 'Then run: devworkspace doctor',
127
+ ];
128
+ if (o.dryRun) {
129
+ return { dryRun: true, projectName: project.name, files, discovery: run.discovery, facts: { written: [], skipped: [], retired: [] }, integrations, promptBytes: prompt.length, nextSteps };
130
+ }
131
+ await writeJsonIfChanged(ws.discovery, run.discovery);
132
+ await writeCommands(ws, run.discovery.commands);
133
+ const facts = await applyDetectorFacts(ws, run.discovery, now);
134
+ await writeTextIfChanged(ws.onboardingPrompt, prompt);
135
+ await atomicWriteJson(ws.integrationsState, { schemaVersion: SCHEMA_VERSION, detectedAt: now, integrations });
136
+ const state = {
137
+ schemaVersion: SCHEMA_VERSION,
138
+ analyzedAt: now,
139
+ refreshedAt: null,
140
+ gitHead: run.gitHead,
141
+ fileCount: run.discovery.scan.fileCount,
142
+ truncated: run.discovery.scan.truncated,
143
+ discoveryHash: sha256Text(JSON.stringify(run.discovery)),
144
+ };
145
+ await writeVersioned(ws, ws.analysisState, state);
146
+ return { dryRun: false, projectName: project.name, files, discovery: run.discovery, facts, integrations, promptBytes: prompt.length, nextSteps };
147
+ }
148
+ export function newProjectId() {
149
+ return randomUUID();
150
+ }
151
+ //# sourceMappingURL=analysis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analysis.js","sourceRoot":"","sources":["../../src/services/analysis.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport { SCHEMA_VERSION, validateCommands, type AnalysisState, type CommandEntry, type CommandsFile } from '../config/schemas.js';\nimport { TRUST_NOTE } from '../discovery/commands.js';\nimport { detectProject, type DiscoveryReport } from '../discovery/detect.js';\nimport { scanProject } from '../discovery/scan.js';\nimport { getGitState } from '../git/git.js';\nimport { detectIntegrations } from '../integrations/index.js';\nimport { buildFact, loadFacts, saveFacts, upsertFact, type FactInput } from '../knowledge/facts.js';\nimport { loadProject } from '../core/workspace.js';\nimport { readVersioned, writeVersioned } from '../core/versioned.js';\nimport { packageVersion } from '../core/version.js';\nimport { pathExists, sha256Text, writeJsonIfChanged, writeTextIfChanged, atomicWriteJson } from '../utils/fs.js';\nimport { isSensitiveFile } from '../security/files.js';\nimport { buildOnboardingPrompt } from '../prompts/onboarding.js';\nimport { claudeAdapter } from '../agents/claude/adapter.js';\nimport type { Ws } from '../core/workspace.js';\nimport { integrationCtx, isoNow, type ServiceOptions } from './common.js';\n\nexport interface DiscoveryRun {\n discovery: DiscoveryReport;\n gitHead: string | null;\n git: { isRepo: boolean; branch: string | null; dirty: boolean; changedFiles: number };\n}\n\nexport async function runDiscovery(ws: Ws, projectName: string, maxFiles?: number): Promise<DiscoveryRun> {\n const scan = await scanProject(ws.root, maxFiles ? { maxFiles } : {});\n const [discovery, git] = await Promise.all([detectProject({ root: ws.root, projectName, scan }), getGitState(ws.root)]);\n return {\n discovery,\n gitHead: git.head,\n git: { isRepo: git.isRepo, branch: git.branch, dirty: git.dirty, changedFiles: git.entries.length },\n };\n}\n\nfunction existingFiles(files: string[], max = 10): string[] {\n return files.filter((f) => !isSensitiveFile(f)).slice(0, max);\n}\n\n/** Facts derived purely from deterministic detection. They are re-derived on every analyze/refresh. */\nexport function detectorFacts(d: DiscoveryReport): FactInput[] {\n const out: FactInput[] = [];\n const add = (key: string, value: string, source: string, sources: string[] = []): void => {\n out.push({ key, value, source, sourceType: 'detector', sources, confidence: 'HIGH', status: 'DETECTED' });\n };\n const manifests = ['package.json', 'pyproject.toml', 'requirements.txt', 'go.mod', 'Cargo.toml', 'pom.xml', 'composer.json'];\n const present = (f: string): boolean => d.frameworks.some((x) => x.evidence === f) || d.commands.some((c) => c.source?.startsWith(f));\n if (d.ecosystems.length > 0) {\n add('stack.ecosystems', d.ecosystems.join(', '), 'repository manifests', manifests.filter(present));\n }\n if (d.languages.length > 0) {\n add('stack.languages', d.languages.slice(0, 6).map((l) => `${l.name} (${l.files} files)`).join(', '), 'file extension counts');\n }\n if (d.packageManager) add('stack.package-manager', d.packageManager, 'lockfiles / package manifests');\n if (d.frameworks.length > 0) {\n add('stack.frameworks', d.frameworks.map((f) => f.name).join(', '), 'dependency manifests', [...new Set(d.frameworks.map((f) => f.evidence))].slice(0, 10));\n }\n if (d.monorepo.tool) {\n add('repo.monorepo', `${d.monorepo.tool}: ${d.monorepo.packages.map((p) => p.path).slice(0, 30).join(', ') || 'packages not enumerated'}`, 'workspace configuration');\n }\n if (d.ci.length > 0) add('repo.ci', d.ci.join(', '), 'CI configuration', existingFiles(d.ci));\n if (d.docker.length > 0) add('repo.docker', d.docker.join(', '), 'container files', existingFiles(d.docker));\n if (d.deployment.length > 0) add('repo.deployment-hints', d.deployment.join(', '), 'deployment configuration', existingFiles(d.deployment));\n if (d.testConfigs.length > 0) add('repo.test-configs', d.testConfigs.join(', '), 'test configuration files', existingFiles(d.testConfigs));\n if (d.aiInstructions.length > 0) add('repo.ai-instructions', d.aiInstructions.join(', '), 'existing agent instruction files', existingFiles(d.aiInstructions));\n return out;\n}\n\nexport async function applyDetectorFacts(ws: Ws, d: DiscoveryReport, now: string): Promise<{ written: string[]; skipped: string[]; retired: string[] }> {\n const facts = await loadFacts(ws);\n const written: string[] = [];\n const skipped: string[] = [];\n const keys = new Set<string>();\n for (const input of detectorFacts(d)) {\n keys.add(input.key);\n const existing = facts.facts.find((f) => f.key === input.key);\n try {\n const fact = await buildFact(ws.root, input, now, existing);\n const action = upsertFact(facts, fact);\n if (action !== 'unchanged') written.push(input.key);\n } catch {\n skipped.push(input.key);\n }\n }\n // Detector facts that no longer apply are dropped: they are derived data, recreated if the evidence returns,\n // and leaving them as STALE would produce a warning that no human or agent action can clear.\n const retired = facts.facts.filter((f) => f.sourceType === 'detector' && !keys.has(f.key)).map((f) => f.key);\n if (retired.length > 0) facts.facts = facts.facts.filter((f) => !retired.includes(f.key));\n await saveFacts(ws, facts);\n return { written, skipped, retired };\n}\n\nexport async function writeCommands(ws: Ws, detected: CommandEntry[]): Promise<boolean> {\n let previous: CommandsFile | null = null;\n if (await pathExists(ws.commands)) {\n try {\n previous = (await readVersioned(ws.commands, 'commands', SCHEMA_VERSION, validateCommands)).data;\n } catch {\n previous = null;\n }\n }\n // Entries a human or the agent edited (origin \"user\") are never overwritten by detection.\n const merged = detected.map((d) => previous?.checks.find((p) => p.id === d.id && p.origin === 'user') ?? d);\n const file: CommandsFile = { schemaVersion: SCHEMA_VERSION, trustNote: TRUST_NOTE, checks: merged };\n validateCommands(file);\n await writeVersioned(ws, ws.commands, file);\n return true;\n}\n\nexport interface AnalyzeResult {\n dryRun: boolean;\n projectName: string;\n files: { discovery: string; onboardingPrompt: string; commands: string };\n discovery: DiscoveryReport;\n facts: { written: string[]; skipped: string[]; retired: string[] };\n integrations: Awaited<ReturnType<typeof detectIntegrations>>;\n promptBytes: number;\n nextSteps: string[];\n}\n\nexport async function analyzeWorkspace(ws: Ws, o: ServiceOptions): Promise<AnalyzeResult> {\n const project = await loadProject(ws);\n const now = isoNow(o);\n const run = await runDiscovery(ws, project.name, project.settings.maxFiles);\n const integrations = await detectIntegrations(integrationCtx(o, ws.root));\n const existingAgentFiles = await claudeAdapter.detectExisting(ws.root);\n const prompt = buildOnboardingPrompt({\n projectName: project.name,\n discovery: run.discovery,\n integrations,\n git: run.git,\n existingAgentFiles,\n cliVersion: packageVersion(),\n });\n const rel = (p: string): string => path.relative(ws.root, p).replace(/\\\\/g, '/');\n const files = { discovery: rel(ws.discovery), onboardingPrompt: rel(ws.onboardingPrompt), commands: rel(ws.commands) };\n const nextSteps = [\n `Open ${files.onboardingPrompt}`,\n 'Paste it into Claude Code (or Claude in VS Code) in this project and let it finish onboarding.',\n 'Then run: devworkspace doctor',\n ];\n if (o.dryRun) {\n return { dryRun: true, projectName: project.name, files, discovery: run.discovery, facts: { written: [], skipped: [], retired: [] }, integrations, promptBytes: prompt.length, nextSteps };\n }\n\n await writeJsonIfChanged(ws.discovery, run.discovery);\n await writeCommands(ws, run.discovery.commands);\n const facts = await applyDetectorFacts(ws, run.discovery, now);\n await writeTextIfChanged(ws.onboardingPrompt, prompt);\n await atomicWriteJson(ws.integrationsState, { schemaVersion: SCHEMA_VERSION, detectedAt: now, integrations });\n const state: AnalysisState = {\n schemaVersion: SCHEMA_VERSION,\n analyzedAt: now,\n refreshedAt: null,\n gitHead: run.gitHead,\n fileCount: run.discovery.scan.fileCount,\n truncated: run.discovery.scan.truncated,\n discoveryHash: sha256Text(JSON.stringify(run.discovery)),\n };\n await writeVersioned(ws, ws.analysisState, state);\n return { dryRun: false, projectName: project.name, files, discovery: run.discovery, facts, integrations, promptBytes: prompt.length, nextSteps };\n}\n\nexport function newProjectId(): string {\n return randomUUID();\n}\n"]}
@@ -0,0 +1,18 @@
1
+ import type { IntegrationContext } from '../integrations/types.js';
2
+ import { type Ws } from '../core/workspace.js';
3
+ export interface ServiceOptions {
4
+ /** Directory the command was started in. */
5
+ cwd: string;
6
+ dryRun?: boolean;
7
+ /** Injectable clock for deterministic tests. */
8
+ now?: () => Date;
9
+ /** Injectable integration context (home directory, PATH lookup) for tests. */
10
+ integrationContext?: (root: string) => IntegrationContext;
11
+ homeDir?: string;
12
+ /** Environment used for PATH lookups (defaults to process.env). */
13
+ env?: NodeJS.ProcessEnv;
14
+ }
15
+ export declare function isoNow(o: ServiceOptions): string;
16
+ export declare function integrationCtx(o: ServiceOptions, root: string): IntegrationContext;
17
+ /** Resolve the workspace and run a mutation under the workspace lock. */
18
+ export declare function mutate<T>(o: ServiceOptions, fn: (ws: Ws) => Promise<T>): Promise<T>;
@@ -0,0 +1,18 @@
1
+ import os from 'node:os';
2
+ import { defaultIntegrationContext } from '../integrations/index.js';
3
+ import { requireWorkspace } from '../core/workspace.js';
4
+ import { withLock } from '../utils/lock.js';
5
+ export function isoNow(o) {
6
+ return (o.now ? o.now() : new Date()).toISOString();
7
+ }
8
+ export function integrationCtx(o, root) {
9
+ if (o.integrationContext)
10
+ return o.integrationContext(root);
11
+ return defaultIntegrationContext(root, o.env ?? process.env, o.homeDir ?? os.homedir());
12
+ }
13
+ /** Resolve the workspace and run a mutation under the workspace lock. */
14
+ export async function mutate(o, fn) {
15
+ const ws = await requireWorkspace(o.cwd);
16
+ return withLock(ws.dir, () => fn(ws));
17
+ }
18
+ //# sourceMappingURL=common.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"common.js","sourceRoot":"","sources":["../../src/services/common.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAErE,OAAO,EAAE,gBAAgB,EAAW,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAe5C,MAAM,UAAU,MAAM,CAAC,CAAiB;IACtC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,CAAiB,EAAE,IAAY;IAC5D,IAAI,CAAC,CAAC,kBAAkB;QAAE,OAAO,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAC5D,OAAO,yBAAyB,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;AAC1F,CAAC;AAED,yEAAyE;AACzE,MAAM,CAAC,KAAK,UAAU,MAAM,CAAI,CAAiB,EAAE,EAA0B;IAC3E,MAAM,EAAE,GAAG,MAAM,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACzC,OAAO,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;AACxC,CAAC","sourcesContent":["import os from 'node:os';\nimport { defaultIntegrationContext } from '../integrations/index.js';\nimport type { IntegrationContext } from '../integrations/types.js';\nimport { requireWorkspace, type Ws } from '../core/workspace.js';\nimport { withLock } from '../utils/lock.js';\n\nexport interface ServiceOptions {\n /** Directory the command was started in. */\n cwd: string;\n dryRun?: boolean;\n /** Injectable clock for deterministic tests. */\n now?: () => Date;\n /** Injectable integration context (home directory, PATH lookup) for tests. */\n integrationContext?: (root: string) => IntegrationContext;\n homeDir?: string;\n /** Environment used for PATH lookups (defaults to process.env). */\n env?: NodeJS.ProcessEnv;\n}\n\nexport function isoNow(o: ServiceOptions): string {\n return (o.now ? o.now() : new Date()).toISOString();\n}\n\nexport function integrationCtx(o: ServiceOptions, root: string): IntegrationContext {\n if (o.integrationContext) return o.integrationContext(root);\n return defaultIntegrationContext(root, o.env ?? process.env, o.homeDir ?? os.homedir());\n}\n\n/** Resolve the workspace and run a mutation under the workspace lock. */\nexport async function mutate<T>(o: ServiceOptions, fn: (ws: Ws) => Promise<T>): Promise<T> {\n const ws = await requireWorkspace(o.cwd);\n return withLock(ws.dir, () => fn(ws));\n}\n"]}
@@ -0,0 +1,27 @@
1
+ import { type ServiceOptions } from './common.js';
2
+ export type CheckStatus = 'pass' | 'warn' | 'fail' | 'info';
3
+ export interface DoctorCheck {
4
+ id: string;
5
+ status: CheckStatus;
6
+ message: string;
7
+ remediation?: string;
8
+ }
9
+ export interface DoctorResult {
10
+ root: string | null;
11
+ checks: DoctorCheck[];
12
+ counts: {
13
+ pass: number;
14
+ warn: number;
15
+ fail: number;
16
+ info: number;
17
+ };
18
+ healthy: boolean;
19
+ ready: boolean;
20
+ fixed: string[];
21
+ }
22
+ export interface DoctorOptions extends ServiceOptions {
23
+ fix?: boolean;
24
+ minNodeMajor?: number;
25
+ }
26
+ export declare const MIN_NODE_MAJOR = 20;
27
+ export declare function runDoctor(o: DoctorOptions): Promise<DoctorResult>;
@@ -0,0 +1,223 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { SCHEMA_VERSION, validateAnalysisState, validateCommands } from '../config/schemas.js';
4
+ import { findWorkspaceRoot, LOCAL_GITIGNORE, loadProject, wsPaths } from '../core/workspace.js';
5
+ import { readVersioned } from '../core/versioned.js';
6
+ import { getGitState, gitAvailable, gitTrackedFiles } from '../git/git.js';
7
+ import { detectIntegrations } from '../integrations/index.js';
8
+ import { loadFacts } from '../knowledge/facts.js';
9
+ import { ONBOARDING_REQUIREMENTS, validatePrompt } from '../prompts/requirements.js';
10
+ import { isSensitiveFile } from '../security/files.js';
11
+ import { findSecrets } from '../security/redact.js';
12
+ import { listDecisions } from '../decisions/decision.js';
13
+ import { listTasks } from '../tasks/task.js';
14
+ import { ensureDir, listFilesRecursive, pathExists, readTextBounded, atomicWriteFile } from '../utils/fs.js';
15
+ import { findAbsolutePaths } from '../utils/paths.js';
16
+ import { instructionsInstalled, knowledgeWritten } from './status.js';
17
+ import { integrationCtx } from './common.js';
18
+ import { withLock } from '../utils/lock.js';
19
+ export const MIN_NODE_MAJOR = 20;
20
+ export async function runDoctor(o) {
21
+ const checks = [];
22
+ const fixed = [];
23
+ const add = (id, status, message, remediation) => {
24
+ checks.push({ id, status, message, ...(remediation ? { remediation } : {}) });
25
+ };
26
+ const major = Number(process.versions.node.split('.')[0]);
27
+ const minNode = o.minNodeMajor ?? MIN_NODE_MAJOR;
28
+ if (major >= minNode)
29
+ add('node', 'pass', `Node.js ${process.versions.node}`);
30
+ else
31
+ add('node', 'fail', `Node.js ${process.versions.node} is older than the required ${minNode}`, `Install Node.js ${minNode} or newer (https://nodejs.org).`);
32
+ const hasGit = await gitAvailable();
33
+ if (hasGit)
34
+ add('git', 'pass', 'Git is available');
35
+ else
36
+ add('git', 'warn', 'Git was not found on PATH', 'Install Git; DevWorkspace works without it but cannot protect uncommitted changes.');
37
+ const root = await findWorkspaceRoot(o.cwd);
38
+ if (!root) {
39
+ if (await pathExists(path.join(o.cwd, '.devworkspace'))) {
40
+ add('workspace', 'fail', 'Partial initialization: .devworkspace/ exists but project.json is missing', 'Restore project.json from Git, or run "devworkspace init" to complete the setup (existing files are kept).');
41
+ }
42
+ else {
43
+ add('workspace', 'fail', 'No DevWorkspace found in this directory or any parent', 'Run: devworkspace init');
44
+ }
45
+ return finish(null, checks, fixed);
46
+ }
47
+ const ws = wsPaths(root);
48
+ add('workspace', 'pass', 'Workspace found');
49
+ // Project config and schema compatibility.
50
+ let projectOk = false;
51
+ try {
52
+ const p = await loadProject(ws);
53
+ projectOk = true;
54
+ add('project', 'pass', `project.json valid (schema v${p.schemaVersion}, id ${p.projectId.slice(0, 8)})`);
55
+ }
56
+ catch (e) {
57
+ const code = e.code;
58
+ add('project', 'fail', e.message.split('\n')[0], code === 'SCHEMA_TOO_NEW' ? 'Upgrade DevWorkspace: npm install -g devworkspace@latest' : 'Restore .devworkspace/project.json from Git, or re-run "devworkspace init" after moving the broken file away.');
59
+ }
60
+ // Layout.
61
+ const dirs = [ws.contextDir, ws.decisionsDir, ws.tasksActive, ws.tasksCompleted, ws.sessionsDir, ws.analysisDir, ws.stateDir];
62
+ const missingDirs = [];
63
+ for (const d of dirs)
64
+ if (!(await pathExists(d)))
65
+ missingDirs.push(d);
66
+ if (missingDirs.length === 0)
67
+ add('layout', 'pass', 'Workspace folders present');
68
+ else if (o.fix && projectOk) {
69
+ await withLock(ws.dir, async () => {
70
+ for (const d of missingDirs)
71
+ await ensureDir(d);
72
+ });
73
+ fixed.push(`created ${missingDirs.length} missing folder(s)`);
74
+ add('layout', 'pass', `Recreated ${missingDirs.length} missing folder(s)`);
75
+ }
76
+ else {
77
+ add('layout', 'warn', `Missing folders: ${missingDirs.map((d) => path.basename(d)).join(', ')}`, 'Run: devworkspace doctor --fix');
78
+ }
79
+ // Ignore rules.
80
+ const giOk = await fs.readFile(ws.gitignore, 'utf8').then((gi) => gi.split(/\r?\n/).some((l) => l.trim() === 'local/'), () => false);
81
+ if (giOk)
82
+ add('ignore-local', 'pass', 'Machine-local state is git-ignored');
83
+ else if (o.fix && projectOk) {
84
+ await atomicWriteFile(ws.gitignore, LOCAL_GITIGNORE);
85
+ fixed.push('wrote .devworkspace/.gitignore');
86
+ add('ignore-local', 'pass', 'Wrote .devworkspace/.gitignore');
87
+ }
88
+ else
89
+ add('ignore-local', 'fail', '.devworkspace/.gitignore does not ignore local/', 'Run: devworkspace doctor --fix');
90
+ try {
91
+ const rootGi = await fs.readFile(path.join(root, '.gitignore'), 'utf8');
92
+ if (rootGi.split(/\r?\n/).some((l) => /^\/?\.devworkspace\/?\*?$/.test(l.trim()))) {
93
+ add('ignore-root', 'warn', 'The root .gitignore ignores all of .devworkspace/, so project knowledge will not be shared', 'Remove that rule; .devworkspace/.gitignore already excludes machine-local files.');
94
+ }
95
+ }
96
+ catch {
97
+ /* no root .gitignore */
98
+ }
99
+ // Knowledge and contract.
100
+ try {
101
+ const facts = await loadFacts(ws);
102
+ const stale = facts.facts.filter((f) => f.status === 'STALE');
103
+ add('facts', 'pass', `${facts.facts.length} fact(s) stored`);
104
+ if (stale.length > 0)
105
+ add('staleness', 'warn', `${stale.length} stale fact(s): ${stale.slice(0, 5).map((f) => f.key).join(', ')}`, 'Run: devworkspace refresh');
106
+ else if (facts.facts.length > 0)
107
+ add('staleness', 'pass', 'No stale facts recorded');
108
+ }
109
+ catch (e) {
110
+ add('facts', 'fail', e.message.split('\n')[0], 'Restore .devworkspace/context/facts.json from Git or a backup under .devworkspace/local/backups.');
111
+ }
112
+ if (await pathExists(ws.commands)) {
113
+ try {
114
+ const c = (await readVersioned(ws.commands, 'commands', SCHEMA_VERSION, validateCommands)).data;
115
+ add('contract', 'pass', 'Testing contract present');
116
+ const unit = c.checks.find((x) => x.id === 'unit');
117
+ if (!unit?.available)
118
+ add('tests-available', 'warn', 'No unit-test command detected', 'Add a test script or edit .devworkspace/context/commands.json; tasks will not be able to demand tests.');
119
+ if (!c.checks.some((x) => x.id === 'build' && x.available))
120
+ add('build-available', 'info', 'No build command detected');
121
+ }
122
+ catch (e) {
123
+ add('contract', 'fail', e.message.split('\n')[0], 'Fix commands.json or re-run: devworkspace analyze');
124
+ }
125
+ }
126
+ else
127
+ add('contract', 'warn', 'Testing contract not generated yet', 'Run: devworkspace analyze');
128
+ if (await pathExists(ws.analysisState)) {
129
+ try {
130
+ const a = (await readVersioned(ws.analysisState, 'analysis', SCHEMA_VERSION, validateAnalysisState)).data;
131
+ add('analysis', 'pass', `Last analysis ${a.analyzedAt}${a.truncated ? ' (partial: size limits hit)' : ''}`);
132
+ }
133
+ catch (e) {
134
+ add('analysis', 'fail', e.message.split('\n')[0], 'Re-run: devworkspace analyze');
135
+ }
136
+ }
137
+ else
138
+ add('analysis', 'warn', 'Repository has not been analyzed', 'Run: devworkspace analyze');
139
+ // Prompt quality (when present).
140
+ if (await pathExists(ws.onboardingPrompt)) {
141
+ const text = (await readTextBounded(ws.onboardingPrompt, 1024 * 1024)) ?? '';
142
+ const missing = validatePrompt(text, ONBOARDING_REQUIREMENTS);
143
+ if (missing.length === 0)
144
+ add('onboarding-prompt', 'pass', 'Onboarding prompt satisfies all behavioural requirements');
145
+ else
146
+ add('onboarding-prompt', 'fail', `Onboarding prompt is missing: ${missing.join(', ')}`, 'Re-run: devworkspace analyze');
147
+ }
148
+ // Agent wiring.
149
+ if (await instructionsInstalled(ws))
150
+ add('agent-instructions', 'pass', 'Claude instructions and skill installed');
151
+ else
152
+ add('agent-instructions', 'warn', 'Claude instructions are not installed (or the managed section is missing)', 'Run: devworkspace agent install');
153
+ if (await knowledgeWritten(ws))
154
+ add('onboarding', 'pass', 'Architecture knowledge written');
155
+ else
156
+ add('onboarding', 'warn', 'Onboarding is not complete: .devworkspace/context/architecture.md is missing or empty', 'Paste .devworkspace/analysis/onboarding-prompt.md into Claude Code.');
157
+ // Integrations.
158
+ for (const i of await detectIntegrations(integrationCtx(o, root))) {
159
+ if (i.state === 'READY')
160
+ add(`integration-${i.id}`, 'pass', `${i.name}: ready`);
161
+ else if (i.optional)
162
+ add(`integration-${i.id}`, 'info', `${i.name}: ${i.state.toLowerCase().replace('_', ' ')} (optional)`);
163
+ else
164
+ add(`integration-${i.id}`, 'warn', `${i.name}: ${i.state.toLowerCase().replace('_', ' ')}${i.details[0] ? ` - ${i.details[0]}` : ''}`, i.remediation[0]);
165
+ }
166
+ // Security and portability.
167
+ const wsFiles = (await listFilesRecursive(ws.dir, 3000)).filter((f) => !f.startsWith(ws.localDir) && !/\.tmp$/.test(f));
168
+ const secretHits = [];
169
+ const absHits = [];
170
+ for (const f of wsFiles) {
171
+ if (!/\.(json|md)$/.test(f))
172
+ continue;
173
+ const rel = path.relative(ws.dir, f).replace(/\\/g, '/');
174
+ const text = await readTextBounded(f, 1024 * 1024);
175
+ if (!text)
176
+ continue;
177
+ if (findSecrets(text).length > 0)
178
+ secretHits.push(rel);
179
+ if (rel !== 'analysis/onboarding-prompt.md' && rel !== 'analysis/refresh-prompt.md' && findAbsolutePaths(text).length > 0)
180
+ absHits.push(rel);
181
+ }
182
+ if (secretHits.length === 0)
183
+ add('secrets', 'pass', 'No secret-like values in .devworkspace');
184
+ else
185
+ add('secrets', 'fail', `Secret-like values found in: ${secretHits.join(', ')}`, 'Remove the values, rotate the credential if it was real, and re-run doctor.');
186
+ if (absHits.length === 0)
187
+ add('portability', 'pass', 'No machine-specific absolute paths in portable knowledge');
188
+ else
189
+ add('portability', 'warn', `Absolute paths found in: ${absHits.join(', ')}`, 'Replace them with project-relative paths so the workspace works after the project moves.');
190
+ const git = await getGitState(root);
191
+ if (git.isRepo) {
192
+ const tracked = await gitTrackedFiles(root);
193
+ const bad = (tracked ?? []).filter((f) => isSensitiveFile(f));
194
+ if (bad.length > 0)
195
+ add('tracked-secrets', 'warn', `Credential-like files are tracked by Git: ${bad.slice(0, 5).join(', ')}`, 'Remove them from Git history if they hold real secrets (git rm --cached, then rotate).');
196
+ else
197
+ add('tracked-secrets', 'pass', 'No credential-like files tracked by Git');
198
+ }
199
+ const leftovers = wsFiles.filter((f) => f.endsWith('.tmp'));
200
+ const allTmp = (await listFilesRecursive(ws.dir, 3000)).filter((f) => f.endsWith('.tmp'));
201
+ if (allTmp.length > 0 || leftovers.length > 0)
202
+ add('tmp-files', 'warn', `${allTmp.length} leftover temporary file(s) from an interrupted write`, 'They are safe to delete: ' + allTmp.slice(0, 3).map((f) => path.relative(root, f).replace(/\\/g, '/')).join(', '));
203
+ // Tasks and decisions.
204
+ const { tasks, corrupt } = await listTasks(ws, { includeCompleted: true });
205
+ if (corrupt.length > 0)
206
+ add('tasks', 'fail', `${corrupt.length} unreadable task file(s): ${corrupt.map((c) => c.file).join(', ')}`, 'Restore them from Git or delete the broken file.');
207
+ else
208
+ add('tasks', 'pass', `${tasks.length} task(s) readable`);
209
+ const dec = await listDecisions(ws);
210
+ if (dec.unreadable.length > 0)
211
+ add('decisions', 'warn', `Unreadable decision file(s): ${dec.unreadable.join(', ')}`, 'Check their front matter.');
212
+ return finish(root, checks, fixed, ws);
213
+ }
214
+ async function finish(root, checks, fixed, ws) {
215
+ const counts = { pass: 0, warn: 0, fail: 0, info: 0 };
216
+ for (const c of checks)
217
+ counts[c.status]++;
218
+ let ready = false;
219
+ if (ws)
220
+ ready = counts.fail === 0 && (await instructionsInstalled(ws)) && (await knowledgeWritten(ws));
221
+ return { root, checks, counts, healthy: counts.fail === 0, ready, fixed };
222
+ }
223
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/services/doctor.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { SCHEMA_VERSION, validateAnalysisState, validateCommands } from '../config/schemas.js';\nimport { findWorkspaceRoot, LOCAL_GITIGNORE, loadProject, wsPaths, type Ws } from '../core/workspace.js';\nimport { readVersioned } from '../core/versioned.js';\nimport { getGitState, gitAvailable, gitTrackedFiles } from '../git/git.js';\nimport { detectIntegrations } from '../integrations/index.js';\nimport { loadFacts } from '../knowledge/facts.js';\nimport { ONBOARDING_REQUIREMENTS, validatePrompt } from '../prompts/requirements.js';\nimport { isSensitiveFile } from '../security/files.js';\nimport { findSecrets } from '../security/redact.js';\nimport { listDecisions } from '../decisions/decision.js';\nimport { listTasks } from '../tasks/task.js';\nimport { ensureDir, listFilesRecursive, pathExists, readTextBounded, atomicWriteFile } from '../utils/fs.js';\nimport { findAbsolutePaths } from '../utils/paths.js';\nimport { instructionsInstalled, knowledgeWritten } from './status.js';\nimport { integrationCtx, type ServiceOptions } from './common.js';\nimport { withLock } from '../utils/lock.js';\n\nexport type CheckStatus = 'pass' | 'warn' | 'fail' | 'info';\n\nexport interface DoctorCheck {\n id: string;\n status: CheckStatus;\n message: string;\n remediation?: string;\n}\n\nexport interface DoctorResult {\n root: string | null;\n checks: DoctorCheck[];\n counts: { pass: number; warn: number; fail: number; info: number };\n healthy: boolean;\n ready: boolean;\n fixed: string[];\n}\n\nexport interface DoctorOptions extends ServiceOptions {\n fix?: boolean;\n minNodeMajor?: number;\n}\n\nexport const MIN_NODE_MAJOR = 20;\n\nexport async function runDoctor(o: DoctorOptions): Promise<DoctorResult> {\n const checks: DoctorCheck[] = [];\n const fixed: string[] = [];\n const add = (id: string, status: CheckStatus, message: string, remediation?: string): void => {\n checks.push({ id, status, message, ...(remediation ? { remediation } : {}) });\n };\n\n const major = Number(process.versions.node.split('.')[0]);\n const minNode = o.minNodeMajor ?? MIN_NODE_MAJOR;\n if (major >= minNode) add('node', 'pass', `Node.js ${process.versions.node}`);\n else add('node', 'fail', `Node.js ${process.versions.node} is older than the required ${minNode}`, `Install Node.js ${minNode} or newer (https://nodejs.org).`);\n\n const hasGit = await gitAvailable();\n if (hasGit) add('git', 'pass', 'Git is available');\n else add('git', 'warn', 'Git was not found on PATH', 'Install Git; DevWorkspace works without it but cannot protect uncommitted changes.');\n\n const root = await findWorkspaceRoot(o.cwd);\n if (!root) {\n if (await pathExists(path.join(o.cwd, '.devworkspace'))) {\n add('workspace', 'fail', 'Partial initialization: .devworkspace/ exists but project.json is missing', 'Restore project.json from Git, or run \"devworkspace init\" to complete the setup (existing files are kept).');\n } else {\n add('workspace', 'fail', 'No DevWorkspace found in this directory or any parent', 'Run: devworkspace init');\n }\n return finish(null, checks, fixed);\n }\n const ws = wsPaths(root);\n add('workspace', 'pass', 'Workspace found');\n\n // Project config and schema compatibility.\n let projectOk = false;\n try {\n const p = await loadProject(ws);\n projectOk = true;\n add('project', 'pass', `project.json valid (schema v${p.schemaVersion}, id ${p.projectId.slice(0, 8)})`);\n } catch (e) {\n const code = (e as { code?: string }).code;\n add('project', 'fail', (e as Error).message.split('\\n')[0]!, code === 'SCHEMA_TOO_NEW' ? 'Upgrade DevWorkspace: npm install -g devworkspace@latest' : 'Restore .devworkspace/project.json from Git, or re-run \"devworkspace init\" after moving the broken file away.');\n }\n\n // Layout.\n const dirs = [ws.contextDir, ws.decisionsDir, ws.tasksActive, ws.tasksCompleted, ws.sessionsDir, ws.analysisDir, ws.stateDir];\n const missingDirs: string[] = [];\n for (const d of dirs) if (!(await pathExists(d))) missingDirs.push(d);\n if (missingDirs.length === 0) add('layout', 'pass', 'Workspace folders present');\n else if (o.fix && projectOk) {\n await withLock(ws.dir, async () => {\n for (const d of missingDirs) await ensureDir(d);\n });\n fixed.push(`created ${missingDirs.length} missing folder(s)`);\n add('layout', 'pass', `Recreated ${missingDirs.length} missing folder(s)`);\n } else {\n add('layout', 'warn', `Missing folders: ${missingDirs.map((d) => path.basename(d)).join(', ')}`, 'Run: devworkspace doctor --fix');\n }\n\n // Ignore rules.\n const giOk = await fs.readFile(ws.gitignore, 'utf8').then(\n (gi) => gi.split(/\\r?\\n/).some((l) => l.trim() === 'local/'),\n () => false,\n );\n if (giOk) add('ignore-local', 'pass', 'Machine-local state is git-ignored');\n else if (o.fix && projectOk) {\n await atomicWriteFile(ws.gitignore, LOCAL_GITIGNORE);\n fixed.push('wrote .devworkspace/.gitignore');\n add('ignore-local', 'pass', 'Wrote .devworkspace/.gitignore');\n } else add('ignore-local', 'fail', '.devworkspace/.gitignore does not ignore local/', 'Run: devworkspace doctor --fix');\n\n try {\n const rootGi = await fs.readFile(path.join(root, '.gitignore'), 'utf8');\n if (rootGi.split(/\\r?\\n/).some((l) => /^\\/?\\.devworkspace\\/?\\*?$/.test(l.trim()))) {\n add('ignore-root', 'warn', 'The root .gitignore ignores all of .devworkspace/, so project knowledge will not be shared', 'Remove that rule; .devworkspace/.gitignore already excludes machine-local files.');\n }\n } catch {\n /* no root .gitignore */\n }\n\n // Knowledge and contract.\n try {\n const facts = await loadFacts(ws);\n const stale = facts.facts.filter((f) => f.status === 'STALE');\n add('facts', 'pass', `${facts.facts.length} fact(s) stored`);\n if (stale.length > 0) add('staleness', 'warn', `${stale.length} stale fact(s): ${stale.slice(0, 5).map((f) => f.key).join(', ')}`, 'Run: devworkspace refresh');\n else if (facts.facts.length > 0) add('staleness', 'pass', 'No stale facts recorded');\n } catch (e) {\n add('facts', 'fail', (e as Error).message.split('\\n')[0]!, 'Restore .devworkspace/context/facts.json from Git or a backup under .devworkspace/local/backups.');\n }\n\n if (await pathExists(ws.commands)) {\n try {\n const c = (await readVersioned(ws.commands, 'commands', SCHEMA_VERSION, validateCommands)).data;\n add('contract', 'pass', 'Testing contract present');\n const unit = c.checks.find((x) => x.id === 'unit');\n if (!unit?.available) add('tests-available', 'warn', 'No unit-test command detected', 'Add a test script or edit .devworkspace/context/commands.json; tasks will not be able to demand tests.');\n if (!c.checks.some((x) => x.id === 'build' && x.available)) add('build-available', 'info', 'No build command detected');\n } catch (e) {\n add('contract', 'fail', (e as Error).message.split('\\n')[0]!, 'Fix commands.json or re-run: devworkspace analyze');\n }\n } else add('contract', 'warn', 'Testing contract not generated yet', 'Run: devworkspace analyze');\n\n if (await pathExists(ws.analysisState)) {\n try {\n const a = (await readVersioned(ws.analysisState, 'analysis', SCHEMA_VERSION, validateAnalysisState)).data;\n add('analysis', 'pass', `Last analysis ${a.analyzedAt}${a.truncated ? ' (partial: size limits hit)' : ''}`);\n } catch (e) {\n add('analysis', 'fail', (e as Error).message.split('\\n')[0]!, 'Re-run: devworkspace analyze');\n }\n } else add('analysis', 'warn', 'Repository has not been analyzed', 'Run: devworkspace analyze');\n\n // Prompt quality (when present).\n if (await pathExists(ws.onboardingPrompt)) {\n const text = (await readTextBounded(ws.onboardingPrompt, 1024 * 1024)) ?? '';\n const missing = validatePrompt(text, ONBOARDING_REQUIREMENTS);\n if (missing.length === 0) add('onboarding-prompt', 'pass', 'Onboarding prompt satisfies all behavioural requirements');\n else add('onboarding-prompt', 'fail', `Onboarding prompt is missing: ${missing.join(', ')}`, 'Re-run: devworkspace analyze');\n }\n\n // Agent wiring.\n if (await instructionsInstalled(ws)) add('agent-instructions', 'pass', 'Claude instructions and skill installed');\n else add('agent-instructions', 'warn', 'Claude instructions are not installed (or the managed section is missing)', 'Run: devworkspace agent install');\n if (await knowledgeWritten(ws)) add('onboarding', 'pass', 'Architecture knowledge written');\n else add('onboarding', 'warn', 'Onboarding is not complete: .devworkspace/context/architecture.md is missing or empty', 'Paste .devworkspace/analysis/onboarding-prompt.md into Claude Code.');\n\n // Integrations.\n for (const i of await detectIntegrations(integrationCtx(o, root))) {\n if (i.state === 'READY') add(`integration-${i.id}`, 'pass', `${i.name}: ready`);\n else if (i.optional) add(`integration-${i.id}`, 'info', `${i.name}: ${i.state.toLowerCase().replace('_', ' ')} (optional)`);\n else add(`integration-${i.id}`, 'warn', `${i.name}: ${i.state.toLowerCase().replace('_', ' ')}${i.details[0] ? ` - ${i.details[0]}` : ''}`, i.remediation[0]);\n }\n\n // Security and portability.\n const wsFiles = (await listFilesRecursive(ws.dir, 3000)).filter((f) => !f.startsWith(ws.localDir) && !/\\.tmp$/.test(f));\n const secretHits: string[] = [];\n const absHits: string[] = [];\n for (const f of wsFiles) {\n if (!/\\.(json|md)$/.test(f)) continue;\n const rel = path.relative(ws.dir, f).replace(/\\\\/g, '/');\n const text = await readTextBounded(f, 1024 * 1024);\n if (!text) continue;\n if (findSecrets(text).length > 0) secretHits.push(rel);\n if (rel !== 'analysis/onboarding-prompt.md' && rel !== 'analysis/refresh-prompt.md' && findAbsolutePaths(text).length > 0) absHits.push(rel);\n }\n if (secretHits.length === 0) add('secrets', 'pass', 'No secret-like values in .devworkspace');\n else add('secrets', 'fail', `Secret-like values found in: ${secretHits.join(', ')}`, 'Remove the values, rotate the credential if it was real, and re-run doctor.');\n if (absHits.length === 0) add('portability', 'pass', 'No machine-specific absolute paths in portable knowledge');\n else add('portability', 'warn', `Absolute paths found in: ${absHits.join(', ')}`, 'Replace them with project-relative paths so the workspace works after the project moves.');\n\n const git = await getGitState(root);\n if (git.isRepo) {\n const tracked = await gitTrackedFiles(root);\n const bad = (tracked ?? []).filter((f) => isSensitiveFile(f));\n if (bad.length > 0) add('tracked-secrets', 'warn', `Credential-like files are tracked by Git: ${bad.slice(0, 5).join(', ')}`, 'Remove them from Git history if they hold real secrets (git rm --cached, then rotate).');\n else add('tracked-secrets', 'pass', 'No credential-like files tracked by Git');\n }\n\n const leftovers = wsFiles.filter((f) => f.endsWith('.tmp'));\n const allTmp = (await listFilesRecursive(ws.dir, 3000)).filter((f) => f.endsWith('.tmp'));\n if (allTmp.length > 0 || leftovers.length > 0) add('tmp-files', 'warn', `${allTmp.length} leftover temporary file(s) from an interrupted write`, 'They are safe to delete: ' + allTmp.slice(0, 3).map((f) => path.relative(root, f).replace(/\\\\/g, '/')).join(', '));\n\n // Tasks and decisions.\n const { tasks, corrupt } = await listTasks(ws, { includeCompleted: true });\n if (corrupt.length > 0) add('tasks', 'fail', `${corrupt.length} unreadable task file(s): ${corrupt.map((c) => c.file).join(', ')}`, 'Restore them from Git or delete the broken file.');\n else add('tasks', 'pass', `${tasks.length} task(s) readable`);\n const dec = await listDecisions(ws);\n if (dec.unreadable.length > 0) add('decisions', 'warn', `Unreadable decision file(s): ${dec.unreadable.join(', ')}`, 'Check their front matter.');\n\n return finish(root, checks, fixed, ws);\n}\n\nasync function finish(root: string | null, checks: DoctorCheck[], fixed: string[], ws?: Ws): Promise<DoctorResult> {\n const counts = { pass: 0, warn: 0, fail: 0, info: 0 };\n for (const c of checks) counts[c.status]++;\n let ready = false;\n if (ws) ready = counts.fail === 0 && (await instructionsInstalled(ws)) && (await knowledgeWritten(ws));\n return { root, checks, counts, healthy: counts.fail === 0, ready, fixed };\n}\n"]}
@@ -0,0 +1,18 @@
1
+ import { detectIntegrations } from '../integrations/index.js';
2
+ import { type ServiceOptions } from './common.js';
3
+ export interface InitOptions extends ServiceOptions {
4
+ /** Explicit project root; defaults to the Git top-level or the current directory. */
5
+ root?: string;
6
+ }
7
+ export interface InitResult {
8
+ root: string;
9
+ dryRun: boolean;
10
+ alreadyInitialized: boolean;
11
+ created: string[];
12
+ unchanged: string[];
13
+ gitignoreUpdated: boolean;
14
+ notes: string[];
15
+ integrations: Awaited<ReturnType<typeof detectIntegrations>>;
16
+ nextSteps: string[];
17
+ }
18
+ export declare function initWorkspace(o: InitOptions): Promise<InitResult>;