devworkspace 0.0.0-stage → 0.1.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 (190) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/LICENSE +21 -0
  3. package/README.md +128 -3
  4. package/dist/agents/claude/adapter.d.ts +15 -0
  5. package/dist/agents/claude/adapter.js +107 -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 +197 -0
  14. package/dist/cli/args.js +94 -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 +181 -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 +41 -0
  23. package/dist/cli/render.js +220 -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 +339 -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/reset.d.ts +23 -0
  143. package/dist/services/reset.js +96 -0
  144. package/dist/services/reset.js.map +1 -0
  145. package/dist/services/status.d.ts +57 -0
  146. package/dist/services/status.js +111 -0
  147. package/dist/services/status.js.map +1 -0
  148. package/dist/services/tasks.d.ts +44 -0
  149. package/dist/services/tasks.js +136 -0
  150. package/dist/services/tasks.js.map +1 -0
  151. package/dist/sessions/session.d.ts +19 -0
  152. package/dist/sessions/session.js +53 -0
  153. package/dist/sessions/session.js.map +1 -0
  154. package/dist/tasks/routing.d.ts +27 -0
  155. package/dist/tasks/routing.js +206 -0
  156. package/dist/tasks/routing.js.map +1 -0
  157. package/dist/tasks/task.d.ts +90 -0
  158. package/dist/tasks/task.js +275 -0
  159. package/dist/tasks/task.js.map +1 -0
  160. package/dist/utils/cleanup.d.ts +3 -0
  161. package/dist/utils/cleanup.js +18 -0
  162. package/dist/utils/cleanup.js.map +1 -0
  163. package/dist/utils/fs.d.ts +17 -0
  164. package/dist/utils/fs.js +164 -0
  165. package/dist/utils/fs.js.map +1 -0
  166. package/dist/utils/lock.d.ts +6 -0
  167. package/dist/utils/lock.js +77 -0
  168. package/dist/utils/lock.js.map +1 -0
  169. package/dist/utils/logger.d.ts +11 -0
  170. package/dist/utils/logger.js +18 -0
  171. package/dist/utils/logger.js.map +1 -0
  172. package/dist/utils/paths.d.ts +18 -0
  173. package/dist/utils/paths.js +95 -0
  174. package/dist/utils/paths.js.map +1 -0
  175. package/dist/utils/proc.d.ts +22 -0
  176. package/dist/utils/proc.js +74 -0
  177. package/dist/utils/proc.js.map +1 -0
  178. package/docs/architecture.md +53 -0
  179. package/docs/benchmarking.md +33 -0
  180. package/docs/claude.md +46 -0
  181. package/docs/contributing.md +64 -0
  182. package/docs/getting-started.md +79 -0
  183. package/docs/integrations.md +51 -0
  184. package/docs/knowledge.md +66 -0
  185. package/docs/portability.md +40 -0
  186. package/docs/routing.md +51 -0
  187. package/docs/security.md +44 -0
  188. package/docs/testing.md +58 -0
  189. package/docs/troubleshooting.md +27 -0
  190. package/package.json +68 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"records.js","sourceRoot":"","sources":["../../src/services/records.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAqC,MAAM,0BAA0B,CAAC;AACzG,OAAO,EAAE,YAAY,EAAE,WAAW,EAAmC,MAAM,wBAAwB,CAAC;AACpG,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAuB,MAAM,aAAa,CAAC;AAElE,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,CAAiB,EAAE,KAAoB;IAC1E,OAAO,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,CAAiB;IAClD,MAAM,EAAE,GAAG,MAAM,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACzC,OAAO,CAAC,MAAM,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,CAAiB,EAAE,MAAc;IACjE,MAAM,CAAC,GAAG,CAAC,MAAM,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;IACnE,IAAI,CAAC,CAAC;QAAE,MAAM,IAAI,iBAAiB,CAAC,WAAW,EAAE,sBAAsB,MAAM,GAAG,CAAC,CAAC;IAClF,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,CAAiB,EAAE,KAAmB;IACxE,OAAO,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,CAAiB,EAAE,KAAK,GAAG,EAAE;IAC7D,MAAM,EAAE,GAAG,MAAM,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACzC,OAAO,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;AACjC,CAAC","sourcesContent":["import { requireWorkspace } from '../core/workspace.js';\nimport { addDecision, listDecisions, type Decision, type DecisionInput } from '../decisions/decision.js';\nimport { listSessions, saveSession, type Session, type SessionInput } from '../sessions/session.js';\nimport { DevWorkspaceError } from '../errors/index.js';\nimport { isoNow, mutate, type ServiceOptions } from './common.js';\n\nexport async function recordDecision(o: ServiceOptions, input: DecisionInput): Promise<Decision> {\n return mutate(o, (ws) => addDecision(ws, input, isoNow(o)));\n}\n\nexport async function getDecisions(o: ServiceOptions): Promise<Decision[]> {\n const ws = await requireWorkspace(o.cwd);\n return (await listDecisions(ws)).decisions;\n}\n\nexport async function getDecision(o: ServiceOptions, number: number): Promise<Decision> {\n const d = (await getDecisions(o)).find((x) => x.number === number);\n if (!d) throw new DevWorkspaceError('NOT_FOUND', `No decision number ${number}.`);\n return d;\n}\n\nexport async function recordSession(o: ServiceOptions, input: SessionInput): Promise<Session> {\n return mutate(o, (ws) => saveSession(ws, input, isoNow(o)));\n}\n\nexport async function getSessions(o: ServiceOptions, limit = 10): Promise<Session[]> {\n const ws = await requireWorkspace(o.cwd);\n return listSessions(ws, limit);\n}\n"]}
@@ -0,0 +1,22 @@
1
+ import { type ServiceOptions } from './common.js';
2
+ export interface RefreshResult {
3
+ dryRun: boolean;
4
+ headChanged: boolean;
5
+ autoRefreshed: string[];
6
+ retired: string[];
7
+ stale: {
8
+ key: string;
9
+ value: string;
10
+ reason: string;
11
+ }[];
12
+ needsReview: {
13
+ key: string;
14
+ reason: string;
15
+ }[];
16
+ prompt: string | null;
17
+ }
18
+ /**
19
+ * Incremental refresh. Deterministic facts are re-derived on the spot; everything else is re-hashed against its cited
20
+ * sources and only the facts whose evidence changed become STALE and enter the (optional) agent refresh prompt.
21
+ */
22
+ export declare function refreshWorkspace(o: ServiceOptions): Promise<RefreshResult>;
@@ -0,0 +1,64 @@
1
+ import path from 'node:path';
2
+ import { SCHEMA_VERSION, validateAnalysisState } from '../config/schemas.js';
3
+ import { loadProject } from '../core/workspace.js';
4
+ import { readVersioned, writeVersioned } from '../core/versioned.js';
5
+ import { packageVersion } from '../core/version.js';
6
+ import { applyStaleness, loadFacts, saveFacts } from '../knowledge/facts.js';
7
+ import { buildRefreshPrompt } from '../prompts/refresh.js';
8
+ import { pathExists, sha256Text, writeJsonIfChanged, writeTextIfChanged } from '../utils/fs.js';
9
+ import { applyDetectorFacts, runDiscovery, writeCommands } from './analysis.js';
10
+ import { isoNow, mutate } from './common.js';
11
+ /**
12
+ * Incremental refresh. Deterministic facts are re-derived on the spot; everything else is re-hashed against its cited
13
+ * sources and only the facts whose evidence changed become STALE and enter the (optional) agent refresh prompt.
14
+ */
15
+ export async function refreshWorkspace(o) {
16
+ return mutate(o, async (ws) => {
17
+ const project = await loadProject(ws);
18
+ const now = isoNow(o);
19
+ const run = await runDiscovery(ws, project.name, project.settings.maxFiles);
20
+ let previous = null;
21
+ if (await pathExists(ws.analysisState)) {
22
+ previous = (await readVersioned(ws.analysisState, 'analysis', SCHEMA_VERSION, validateAnalysisState)).data;
23
+ }
24
+ const headChanged = !!previous && previous.gitHead !== run.gitHead;
25
+ if (o.dryRun) {
26
+ const facts = await loadFacts(ws);
27
+ const probe = structuredClone(facts);
28
+ const rep = await applyStaleness(ws.root, probe, now);
29
+ return {
30
+ dryRun: true,
31
+ headChanged,
32
+ autoRefreshed: [],
33
+ retired: [],
34
+ stale: rep.newlyStale.map((s) => ({ ...s, value: probe.facts.find((f) => f.key === s.key)?.value ?? '' })),
35
+ needsReview: rep.needsReview,
36
+ prompt: null,
37
+ };
38
+ }
39
+ await writeJsonIfChanged(ws.discovery, run.discovery);
40
+ await writeCommands(ws, run.discovery.commands);
41
+ const det = await applyDetectorFacts(ws, run.discovery, now);
42
+ const facts = await loadFacts(ws);
43
+ const rep = await applyStaleness(ws.root, facts, now);
44
+ await saveFacts(ws, facts);
45
+ const stale = facts.facts.filter((f) => f.status === 'STALE' && f.sourceType !== 'detector').map((f) => ({ key: f.key, value: f.value, reason: f.staleReason ?? 'evidence changed' }));
46
+ let prompt = null;
47
+ if (stale.length > 0 || rep.needsReview.length > 0) {
48
+ await writeTextIfChanged(ws.refreshPrompt, buildRefreshPrompt({ projectName: project.name, stale, needsReview: rep.needsReview, autoRefreshed: det.written, cliVersion: packageVersion() }));
49
+ prompt = path.relative(ws.root, ws.refreshPrompt).replace(/\\/g, '/');
50
+ }
51
+ const state = {
52
+ schemaVersion: SCHEMA_VERSION,
53
+ analyzedAt: previous?.analyzedAt ?? now,
54
+ refreshedAt: now,
55
+ gitHead: run.gitHead,
56
+ fileCount: run.discovery.scan.fileCount,
57
+ truncated: run.discovery.scan.truncated,
58
+ discoveryHash: sha256Text(JSON.stringify(run.discovery)),
59
+ };
60
+ await writeVersioned(ws, ws.analysisState, state);
61
+ return { dryRun: false, headChanged, autoRefreshed: det.written, retired: det.retired, stale, needsReview: rep.needsReview, prompt };
62
+ });
63
+ }
64
+ //# sourceMappingURL=refresh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh.js","sourceRoot":"","sources":["../../src/services/refresh.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path';\nimport { SCHEMA_VERSION, validateAnalysisState, type AnalysisState } from '../config/schemas.js';\nimport { loadProject } from '../core/workspace.js';\nimport { readVersioned, writeVersioned } from '../core/versioned.js';\nimport { packageVersion } from '../core/version.js';\nimport { applyStaleness, loadFacts, saveFacts } from '../knowledge/facts.js';\nimport { buildRefreshPrompt } from '../prompts/refresh.js';\nimport { pathExists, sha256Text, writeJsonIfChanged, writeTextIfChanged } from '../utils/fs.js';\nimport { applyDetectorFacts, runDiscovery, writeCommands } from './analysis.js';\nimport { isoNow, mutate, type ServiceOptions } from './common.js';\n\nexport interface RefreshResult {\n dryRun: boolean;\n headChanged: boolean;\n autoRefreshed: string[];\n retired: string[];\n stale: { key: string; value: string; reason: string }[];\n needsReview: { key: string; reason: string }[];\n prompt: string | null;\n}\n\n/**\n * Incremental refresh. Deterministic facts are re-derived on the spot; everything else is re-hashed against its cited\n * sources and only the facts whose evidence changed become STALE and enter the (optional) agent refresh prompt.\n */\nexport async function refreshWorkspace(o: ServiceOptions): Promise<RefreshResult> {\n return mutate(o, async (ws) => {\n const project = await loadProject(ws);\n const now = isoNow(o);\n const run = await runDiscovery(ws, project.name, project.settings.maxFiles);\n let previous: AnalysisState | null = null;\n if (await pathExists(ws.analysisState)) {\n previous = (await readVersioned(ws.analysisState, 'analysis', SCHEMA_VERSION, validateAnalysisState)).data;\n }\n const headChanged = !!previous && previous.gitHead !== run.gitHead;\n\n if (o.dryRun) {\n const facts = await loadFacts(ws);\n const probe = structuredClone(facts);\n const rep = await applyStaleness(ws.root, probe, now);\n return {\n dryRun: true,\n headChanged,\n autoRefreshed: [],\n retired: [],\n stale: rep.newlyStale.map((s) => ({ ...s, value: probe.facts.find((f) => f.key === s.key)?.value ?? '' })),\n needsReview: rep.needsReview,\n prompt: null,\n };\n }\n\n await writeJsonIfChanged(ws.discovery, run.discovery);\n await writeCommands(ws, run.discovery.commands);\n const det = await applyDetectorFacts(ws, run.discovery, now);\n\n const facts = await loadFacts(ws);\n const rep = await applyStaleness(ws.root, facts, now);\n await saveFacts(ws, facts);\n\n const stale = facts.facts.filter((f) => f.status === 'STALE' && f.sourceType !== 'detector').map((f) => ({ key: f.key, value: f.value, reason: f.staleReason ?? 'evidence changed' }));\n let prompt: string | null = null;\n if (stale.length > 0 || rep.needsReview.length > 0) {\n await writeTextIfChanged(\n ws.refreshPrompt,\n buildRefreshPrompt({ projectName: project.name, stale, needsReview: rep.needsReview, autoRefreshed: det.written, cliVersion: packageVersion() }),\n );\n prompt = path.relative(ws.root, ws.refreshPrompt).replace(/\\\\/g, '/');\n }\n\n const state: AnalysisState = {\n schemaVersion: SCHEMA_VERSION,\n analyzedAt: previous?.analyzedAt ?? now,\n refreshedAt: now,\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, headChanged, autoRefreshed: det.written, retired: det.retired, stale, needsReview: rep.needsReview, prompt };\n });\n}\n"]}
@@ -0,0 +1,23 @@
1
+ import type { ServiceOptions } from './common.js';
2
+ export interface ResetOptions extends ServiceOptions {
3
+ /** Actually delete. Without it, reset only reports what it would remove. */
4
+ confirm?: boolean;
5
+ /** Also remove the DevWorkspace block from CLAUDE.md and the managed devworkspace skill. */
6
+ includeAgentFiles?: boolean;
7
+ }
8
+ export interface ResetAction {
9
+ path: string;
10
+ action: 'delete-folder' | 'remove-managed-section' | 'delete-file' | 'skip';
11
+ note: string;
12
+ }
13
+ export interface ResetResult {
14
+ root: string;
15
+ executed: boolean;
16
+ actions: ResetAction[];
17
+ fileCount: number;
18
+ }
19
+ /**
20
+ * Remove .devworkspace/ so the user can start again with `init`. Only DevWorkspace's own folder is ever deleted;
21
+ * application files are never touched, and with includeAgentFiles only the managed block / managed skill are removed.
22
+ */
23
+ export declare function resetWorkspace(o: ResetOptions): Promise<ResetResult>;
@@ -0,0 +1,96 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { removeManagedSection } from '../agents/claude/adapter.js';
4
+ import { SKILL_MARKER, SKILL_PATH } from '../agents/claude/content.js';
5
+ import { DevWorkspaceError } from '../errors/index.js';
6
+ import { findWorkspaceRoot, WORKSPACE_DIR, wsPaths } from '../core/workspace.js';
7
+ import { atomicWriteFile, pathExists } from '../utils/fs.js';
8
+ import { isInside } from '../utils/paths.js';
9
+ import { withLock } from '../utils/lock.js';
10
+ async function countFiles(dir) {
11
+ let n = 0;
12
+ for (const e of await fs.readdir(dir, { withFileTypes: true })) {
13
+ if (e.isDirectory())
14
+ n += await countFiles(path.join(dir, e.name));
15
+ else
16
+ n++;
17
+ }
18
+ return n;
19
+ }
20
+ /**
21
+ * Remove .devworkspace/ so the user can start again with `init`. Only DevWorkspace's own folder is ever deleted;
22
+ * application files are never touched, and with includeAgentFiles only the managed block / managed skill are removed.
23
+ */
24
+ export async function resetWorkspace(o) {
25
+ let root = await findWorkspaceRoot(o.cwd);
26
+ if (!root && (await pathExists(path.join(o.cwd, WORKSPACE_DIR))))
27
+ root = path.resolve(o.cwd); // half-initialised workspace
28
+ if (!root) {
29
+ throw new DevWorkspaceError('NOT_FOUND', 'No .devworkspace folder found in this directory or any parent.', { hint: 'Nothing to reset.' });
30
+ }
31
+ const ws = wsPaths(root);
32
+ const st = await fs.lstat(ws.dir);
33
+ if (st.isSymbolicLink() || !st.isDirectory()) {
34
+ throw new DevWorkspaceError('UNSAFE_PATH', '.devworkspace is a symlink or not a directory; refusing to delete it.', {
35
+ hint: 'Remove it by hand if you are sure.',
36
+ });
37
+ }
38
+ const real = await fs.realpath(ws.dir);
39
+ if (!isInside(await fs.realpath(root), real) || path.basename(real) !== WORKSPACE_DIR) {
40
+ throw new DevWorkspaceError('UNSAFE_PATH', 'Resolved workspace path is outside the project; refusing to delete it.');
41
+ }
42
+ const fileCount = await countFiles(ws.dir);
43
+ const actions = [
44
+ { path: `${WORKSPACE_DIR}/`, action: 'delete-folder', note: `${fileCount} file(s): knowledge, decisions, tasks, sessions, analysis and local state` },
45
+ ];
46
+ const pending = [];
47
+ if (o.includeAgentFiles) {
48
+ for (const rel of ['CLAUDE.md', '.claude/CLAUDE.md']) {
49
+ const full = path.join(root, ...rel.split('/'));
50
+ const text = await fs.readFile(full, 'utf8').catch(() => null);
51
+ if (text === null)
52
+ continue;
53
+ const removed = removeManagedSection(text);
54
+ if (!removed) {
55
+ if (text.includes('<!-- devworkspace:start') || text.includes('<!-- devworkspace:end')) {
56
+ actions.push({ path: rel, action: 'skip', note: 'managed markers are unbalanced; edit by hand' });
57
+ }
58
+ continue;
59
+ }
60
+ if (removed.empty) {
61
+ actions.push({ path: rel, action: 'delete-file', note: 'contained only the DevWorkspace block' });
62
+ pending.push(() => fs.rm(full, { force: true }));
63
+ }
64
+ else {
65
+ actions.push({ path: rel, action: 'remove-managed-section', note: 'your own content is kept' });
66
+ pending.push(() => atomicWriteFile(full, removed.content));
67
+ }
68
+ }
69
+ const skillFile = path.join(root, ...SKILL_PATH.split('/'));
70
+ const skill = await fs.readFile(skillFile, 'utf8').catch(() => null);
71
+ if (skill !== null) {
72
+ if (skill.includes(SKILL_MARKER)) {
73
+ const dir = path.dirname(skillFile);
74
+ const others = (await fs.readdir(dir)).filter((n) => n !== 'SKILL.md');
75
+ actions.push({ path: SKILL_PATH, action: 'delete-file', note: 'DevWorkspace-managed skill' });
76
+ pending.push(async () => {
77
+ await fs.rm(skillFile, { force: true });
78
+ if (others.length === 0)
79
+ await fs.rmdir(dir).catch(() => undefined);
80
+ });
81
+ }
82
+ else {
83
+ actions.push({ path: SKILL_PATH, action: 'skip', note: 'not managed by DevWorkspace (no marker); left alone' });
84
+ }
85
+ }
86
+ }
87
+ if (!o.confirm || o.dryRun)
88
+ return { root, executed: false, actions, fileCount };
89
+ await withLock(ws.dir, async () => {
90
+ for (const w of pending)
91
+ await w();
92
+ });
93
+ await fs.rm(ws.dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
94
+ return { root, executed: true, actions, fileCount };
95
+ }
96
+ //# sourceMappingURL=reset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reset.js","sourceRoot":"","sources":["../../src/services/reset.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { removeManagedSection } from '../agents/claude/adapter.js';\nimport { SKILL_MARKER, SKILL_PATH } from '../agents/claude/content.js';\nimport { DevWorkspaceError } from '../errors/index.js';\nimport { findWorkspaceRoot, WORKSPACE_DIR, wsPaths } from '../core/workspace.js';\nimport { atomicWriteFile, pathExists } from '../utils/fs.js';\nimport { isInside } from '../utils/paths.js';\nimport { withLock } from '../utils/lock.js';\nimport type { ServiceOptions } from './common.js';\n\nexport interface ResetOptions extends ServiceOptions {\n /** Actually delete. Without it, reset only reports what it would remove. */\n confirm?: boolean;\n /** Also remove the DevWorkspace block from CLAUDE.md and the managed devworkspace skill. */\n includeAgentFiles?: boolean;\n}\n\nexport interface ResetAction {\n path: string;\n action: 'delete-folder' | 'remove-managed-section' | 'delete-file' | 'skip';\n note: string;\n}\n\nexport interface ResetResult {\n root: string;\n executed: boolean;\n actions: ResetAction[];\n fileCount: number;\n}\n\nasync function countFiles(dir: string): Promise<number> {\n let n = 0;\n for (const e of await fs.readdir(dir, { withFileTypes: true })) {\n if (e.isDirectory()) n += await countFiles(path.join(dir, e.name));\n else n++;\n }\n return n;\n}\n\n/**\n * Remove .devworkspace/ so the user can start again with `init`. Only DevWorkspace's own folder is ever deleted;\n * application files are never touched, and with includeAgentFiles only the managed block / managed skill are removed.\n */\nexport async function resetWorkspace(o: ResetOptions): Promise<ResetResult> {\n let root = await findWorkspaceRoot(o.cwd);\n if (!root && (await pathExists(path.join(o.cwd, WORKSPACE_DIR)))) root = path.resolve(o.cwd); // half-initialised workspace\n if (!root) {\n throw new DevWorkspaceError('NOT_FOUND', 'No .devworkspace folder found in this directory or any parent.', { hint: 'Nothing to reset.' });\n }\n const ws = wsPaths(root);\n const st = await fs.lstat(ws.dir);\n if (st.isSymbolicLink() || !st.isDirectory()) {\n throw new DevWorkspaceError('UNSAFE_PATH', '.devworkspace is a symlink or not a directory; refusing to delete it.', {\n hint: 'Remove it by hand if you are sure.',\n });\n }\n const real = await fs.realpath(ws.dir);\n if (!isInside(await fs.realpath(root), real) || path.basename(real) !== WORKSPACE_DIR) {\n throw new DevWorkspaceError('UNSAFE_PATH', 'Resolved workspace path is outside the project; refusing to delete it.');\n }\n\n const fileCount = await countFiles(ws.dir);\n const actions: ResetAction[] = [\n { path: `${WORKSPACE_DIR}/`, action: 'delete-folder', note: `${fileCount} file(s): knowledge, decisions, tasks, sessions, analysis and local state` },\n ];\n const pending: (() => Promise<void>)[] = [];\n\n if (o.includeAgentFiles) {\n for (const rel of ['CLAUDE.md', '.claude/CLAUDE.md']) {\n const full = path.join(root, ...rel.split('/'));\n const text = await fs.readFile(full, 'utf8').catch(() => null);\n if (text === null) continue;\n const removed = removeManagedSection(text);\n if (!removed) {\n if (text.includes('<!-- devworkspace:start') || text.includes('<!-- devworkspace:end')) {\n actions.push({ path: rel, action: 'skip', note: 'managed markers are unbalanced; edit by hand' });\n }\n continue;\n }\n if (removed.empty) {\n actions.push({ path: rel, action: 'delete-file', note: 'contained only the DevWorkspace block' });\n pending.push(() => fs.rm(full, { force: true }));\n } else {\n actions.push({ path: rel, action: 'remove-managed-section', note: 'your own content is kept' });\n pending.push(() => atomicWriteFile(full, removed.content));\n }\n }\n const skillFile = path.join(root, ...SKILL_PATH.split('/'));\n const skill = await fs.readFile(skillFile, 'utf8').catch(() => null);\n if (skill !== null) {\n if (skill.includes(SKILL_MARKER)) {\n const dir = path.dirname(skillFile);\n const others = (await fs.readdir(dir)).filter((n) => n !== 'SKILL.md');\n actions.push({ path: SKILL_PATH, action: 'delete-file', note: 'DevWorkspace-managed skill' });\n pending.push(async () => {\n await fs.rm(skillFile, { force: true });\n if (others.length === 0) await fs.rmdir(dir).catch(() => undefined);\n });\n } else {\n actions.push({ path: SKILL_PATH, action: 'skip', note: 'not managed by DevWorkspace (no marker); left alone' });\n }\n }\n }\n\n if (!o.confirm || o.dryRun) return { root, executed: false, actions, fileCount };\n\n await withLock(ws.dir, async () => {\n for (const w of pending) await w();\n });\n await fs.rm(ws.dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });\n return { root, executed: true, actions, fileCount };\n}\n"]}
@@ -0,0 +1,57 @@
1
+ import { type Ws } from '../core/workspace.js';
2
+ import { type IntegrationStatus } from '../integrations/index.js';
3
+ import { type ServiceOptions } from './common.js';
4
+ export interface TaskBrief {
5
+ id: string;
6
+ title: string;
7
+ type: string;
8
+ status: string;
9
+ updatedAt: string;
10
+ next: string;
11
+ }
12
+ export interface StatusResult {
13
+ initialized: boolean;
14
+ root: string | null;
15
+ project: {
16
+ name: string;
17
+ id: string;
18
+ } | null;
19
+ workspace: 'Healthy' | 'Needs attention' | 'Not initialized' | 'Unreadable';
20
+ workspaceProblem?: string;
21
+ git: {
22
+ available: boolean;
23
+ isRepo: boolean;
24
+ branch: string | null;
25
+ workingTree: 'Clean' | 'Modified' | 'Unknown';
26
+ changes: number;
27
+ };
28
+ knowledge: {
29
+ state: 'Current' | 'Stale' | 'Empty' | 'Unknown';
30
+ facts: number;
31
+ stale: number;
32
+ analyzedAt: string | null;
33
+ };
34
+ onboarding: {
35
+ instructionsInstalled: boolean;
36
+ knowledgeWritten: boolean;
37
+ ready: boolean;
38
+ };
39
+ integrations: IntegrationStatus[];
40
+ activeTasks: TaskBrief[];
41
+ verification: {
42
+ id: string;
43
+ available: boolean;
44
+ command: string | null;
45
+ required: string;
46
+ }[];
47
+ lastSession: {
48
+ id: string;
49
+ goal: string;
50
+ next: string;
51
+ } | null;
52
+ corruptTasks: number;
53
+ }
54
+ export declare function instructionsInstalled(ws: Ws): Promise<boolean>;
55
+ export declare function knowledgeWritten(ws: Ws): Promise<boolean>;
56
+ /** Cheap overview: reads small workspace files and Git status only. Never hashes or scans source files. */
57
+ export declare function getStatus(o: ServiceOptions): Promise<StatusResult>;
@@ -0,0 +1,111 @@
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, loadProject, wsPaths } from '../core/workspace.js';
5
+ import { readVersioned } from '../core/versioned.js';
6
+ import { getGitState } from '../git/git.js';
7
+ import { detectIntegrations } from '../integrations/index.js';
8
+ import { loadFacts } from '../knowledge/facts.js';
9
+ import { listSessions } from '../sessions/session.js';
10
+ import { listTasks, nextRequiredStep } from '../tasks/task.js';
11
+ import { pathExists } from '../utils/fs.js';
12
+ import { SECTION_END } from '../agents/claude/content.js';
13
+ import { SKILL_PATH } from '../agents/claude/content.js';
14
+ import { integrationCtx } from './common.js';
15
+ const brief = (t) => ({ id: t.id, title: t.title, type: t.type, status: t.status, updatedAt: t.updatedAt, next: nextRequiredStep(t) });
16
+ export async function instructionsInstalled(ws) {
17
+ for (const rel of ['CLAUDE.md', '.claude/CLAUDE.md']) {
18
+ try {
19
+ const text = await fs.readFile(path.join(ws.root, ...rel.split('/')), 'utf8');
20
+ if (text.includes('<!-- devworkspace:start') && text.includes(SECTION_END) && (await pathExists(path.join(ws.root, ...SKILL_PATH.split('/')))))
21
+ return true;
22
+ }
23
+ catch {
24
+ /* try next */
25
+ }
26
+ }
27
+ return false;
28
+ }
29
+ export async function knowledgeWritten(ws) {
30
+ try {
31
+ return (await fs.stat(path.join(ws.contextDir, 'architecture.md'))).size >= 200;
32
+ }
33
+ catch {
34
+ return false;
35
+ }
36
+ }
37
+ /** Cheap overview: reads small workspace files and Git status only. Never hashes or scans source files. */
38
+ export async function getStatus(o) {
39
+ const root = await findWorkspaceRoot(o.cwd);
40
+ const git = await getGitState(root ?? o.cwd);
41
+ const gitBrief = {
42
+ available: git.available,
43
+ isRepo: git.isRepo,
44
+ branch: git.branch,
45
+ workingTree: git.isRepo ? (git.dirty ? 'Modified' : 'Clean') : 'Unknown',
46
+ changes: git.entries.length,
47
+ };
48
+ const base = {
49
+ initialized: false,
50
+ root,
51
+ project: null,
52
+ workspace: 'Not initialized',
53
+ git: gitBrief,
54
+ knowledge: { state: 'Unknown', facts: 0, stale: 0, analyzedAt: null },
55
+ onboarding: { instructionsInstalled: false, knowledgeWritten: false, ready: false },
56
+ integrations: [],
57
+ activeTasks: [],
58
+ verification: [],
59
+ lastSession: null,
60
+ corruptTasks: 0,
61
+ };
62
+ if (!root)
63
+ return base;
64
+ const ws = wsPaths(root);
65
+ base.initialized = true;
66
+ try {
67
+ const project = await loadProject(ws);
68
+ base.project = { name: project.name, id: project.projectId };
69
+ base.workspace = 'Healthy';
70
+ }
71
+ catch (e) {
72
+ return { ...base, workspace: 'Unreadable', workspaceProblem: e.message };
73
+ }
74
+ try {
75
+ const facts = await loadFacts(ws);
76
+ const stale = facts.facts.filter((f) => f.status === 'STALE').length;
77
+ let analyzedAt = null;
78
+ if (await pathExists(ws.analysisState)) {
79
+ analyzedAt = (await readVersioned(ws.analysisState, 'analysis', SCHEMA_VERSION, validateAnalysisState)).data.analyzedAt;
80
+ }
81
+ base.knowledge = { facts: facts.facts.length, stale, analyzedAt, state: facts.facts.length === 0 ? 'Empty' : stale > 0 ? 'Stale' : 'Current' };
82
+ }
83
+ catch (e) {
84
+ base.workspace = 'Needs attention';
85
+ base.workspaceProblem = e.message;
86
+ }
87
+ if (await pathExists(ws.commands)) {
88
+ try {
89
+ const commands = (await readVersioned(ws.commands, 'commands', SCHEMA_VERSION, validateCommands)).data;
90
+ base.verification = commands.checks.map((c) => ({ id: c.id, available: c.available, command: c.command, required: c.required }));
91
+ }
92
+ catch (e) {
93
+ base.workspace = 'Needs attention';
94
+ base.workspaceProblem ??= e.message;
95
+ }
96
+ }
97
+ const { tasks, corrupt } = await listTasks(ws);
98
+ base.activeTasks = tasks.map(brief);
99
+ base.corruptTasks = corrupt.length;
100
+ if (corrupt.length > 0)
101
+ base.workspace = 'Needs attention';
102
+ const sessions = await listSessions(ws, 1);
103
+ if (sessions[0])
104
+ base.lastSession = { id: sessions[0].id, goal: sessions[0].goal, next: sessions[0].next };
105
+ base.integrations = await detectIntegrations(integrationCtx(o, root));
106
+ base.onboarding.instructionsInstalled = await instructionsInstalled(ws);
107
+ base.onboarding.knowledgeWritten = await knowledgeWritten(ws);
108
+ base.onboarding.ready = base.onboarding.instructionsInstalled && base.onboarding.knowledgeWritten && base.knowledge.state !== 'Empty';
109
+ return base;
110
+ }
111
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/services/status.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, type CommandEntry } from '../config/schemas.js';\nimport { findWorkspaceRoot, loadProject, wsPaths, type Ws } from '../core/workspace.js';\nimport { readVersioned } from '../core/versioned.js';\nimport { getGitState } from '../git/git.js';\nimport { detectIntegrations, type IntegrationStatus } from '../integrations/index.js';\nimport { loadFacts } from '../knowledge/facts.js';\nimport { listSessions } from '../sessions/session.js';\nimport { listTasks, nextRequiredStep, type Task } from '../tasks/task.js';\nimport { pathExists } from '../utils/fs.js';\nimport { SECTION_END } from '../agents/claude/content.js';\nimport { SKILL_PATH } from '../agents/claude/content.js';\nimport { integrationCtx, type ServiceOptions } from './common.js';\n\nexport interface TaskBrief {\n id: string;\n title: string;\n type: string;\n status: string;\n updatedAt: string;\n next: string;\n}\n\nexport interface StatusResult {\n initialized: boolean;\n root: string | null;\n project: { name: string; id: string } | null;\n workspace: 'Healthy' | 'Needs attention' | 'Not initialized' | 'Unreadable';\n workspaceProblem?: string;\n git: { available: boolean; isRepo: boolean; branch: string | null; workingTree: 'Clean' | 'Modified' | 'Unknown'; changes: number };\n knowledge: { state: 'Current' | 'Stale' | 'Empty' | 'Unknown'; facts: number; stale: number; analyzedAt: string | null };\n onboarding: { instructionsInstalled: boolean; knowledgeWritten: boolean; ready: boolean };\n integrations: IntegrationStatus[];\n activeTasks: TaskBrief[];\n verification: { id: string; available: boolean; command: string | null; required: string }[];\n lastSession: { id: string; goal: string; next: string } | null;\n corruptTasks: number;\n}\n\nconst brief = (t: Task): TaskBrief => ({ id: t.id, title: t.title, type: t.type, status: t.status, updatedAt: t.updatedAt, next: nextRequiredStep(t) });\n\nexport async function instructionsInstalled(ws: Ws): Promise<boolean> {\n for (const rel of ['CLAUDE.md', '.claude/CLAUDE.md']) {\n try {\n const text = await fs.readFile(path.join(ws.root, ...rel.split('/')), 'utf8');\n if (text.includes('<!-- devworkspace:start') && text.includes(SECTION_END) && (await pathExists(path.join(ws.root, ...SKILL_PATH.split('/'))))) return true;\n } catch {\n /* try next */\n }\n }\n return false;\n}\n\nexport async function knowledgeWritten(ws: Ws): Promise<boolean> {\n try {\n return (await fs.stat(path.join(ws.contextDir, 'architecture.md'))).size >= 200;\n } catch {\n return false;\n }\n}\n\n/** Cheap overview: reads small workspace files and Git status only. Never hashes or scans source files. */\nexport async function getStatus(o: ServiceOptions): Promise<StatusResult> {\n const root = await findWorkspaceRoot(o.cwd);\n const git = await getGitState(root ?? o.cwd);\n const gitBrief: StatusResult['git'] = {\n available: git.available,\n isRepo: git.isRepo,\n branch: git.branch,\n workingTree: git.isRepo ? (git.dirty ? 'Modified' : 'Clean') : 'Unknown',\n changes: git.entries.length,\n };\n const base: StatusResult = {\n initialized: false,\n root,\n project: null,\n workspace: 'Not initialized',\n git: gitBrief,\n knowledge: { state: 'Unknown', facts: 0, stale: 0, analyzedAt: null },\n onboarding: { instructionsInstalled: false, knowledgeWritten: false, ready: false },\n integrations: [],\n activeTasks: [],\n verification: [],\n lastSession: null,\n corruptTasks: 0,\n };\n if (!root) return base;\n\n const ws = wsPaths(root);\n base.initialized = true;\n try {\n const project = await loadProject(ws);\n base.project = { name: project.name, id: project.projectId };\n base.workspace = 'Healthy';\n } catch (e) {\n return { ...base, workspace: 'Unreadable', workspaceProblem: (e as Error).message };\n }\n\n try {\n const facts = await loadFacts(ws);\n const stale = facts.facts.filter((f) => f.status === 'STALE').length;\n let analyzedAt: string | null = null;\n if (await pathExists(ws.analysisState)) {\n analyzedAt = (await readVersioned(ws.analysisState, 'analysis', SCHEMA_VERSION, validateAnalysisState)).data.analyzedAt;\n }\n base.knowledge = { facts: facts.facts.length, stale, analyzedAt, state: facts.facts.length === 0 ? 'Empty' : stale > 0 ? 'Stale' : 'Current' };\n } catch (e) {\n base.workspace = 'Needs attention';\n base.workspaceProblem = (e as Error).message;\n }\n\n if (await pathExists(ws.commands)) {\n try {\n const commands = (await readVersioned(ws.commands, 'commands', SCHEMA_VERSION, validateCommands)).data;\n base.verification = commands.checks.map((c: CommandEntry) => ({ id: c.id, available: c.available, command: c.command, required: c.required }));\n } catch (e) {\n base.workspace = 'Needs attention';\n base.workspaceProblem ??= (e as Error).message;\n }\n }\n\n const { tasks, corrupt } = await listTasks(ws);\n base.activeTasks = tasks.map(brief);\n base.corruptTasks = corrupt.length;\n if (corrupt.length > 0) base.workspace = 'Needs attention';\n const sessions = await listSessions(ws, 1);\n if (sessions[0]) base.lastSession = { id: sessions[0].id, goal: sessions[0].goal, next: sessions[0].next };\n\n base.integrations = await detectIntegrations(integrationCtx(o, root));\n base.onboarding.instructionsInstalled = await instructionsInstalled(ws);\n base.onboarding.knowledgeWritten = await knowledgeWritten(ws);\n base.onboarding.ready = base.onboarding.instructionsInstalled && base.onboarding.knowledgeWritten && base.knowledge.state !== 'Empty';\n return base;\n}\n"]}
@@ -0,0 +1,44 @@
1
+ import { type Classification, type WorkflowSpec } from '../tasks/routing.js';
2
+ import { listTasks, type GateResult, type Task } from '../tasks/task.js';
3
+ import { type ServiceOptions } from './common.js';
4
+ export interface CreateTaskResult {
5
+ task: Task;
6
+ classification: Classification;
7
+ workflow: WorkflowSpec;
8
+ }
9
+ export declare function createTask(o: ServiceOptions, input: {
10
+ text: string;
11
+ type?: string;
12
+ title?: string;
13
+ }): Promise<CreateTaskResult>;
14
+ export declare function showTask(o: ServiceOptions, id: string): Promise<{
15
+ task: Task;
16
+ gate: GateResult;
17
+ }>;
18
+ export declare function listTasksService(o: ServiceOptions, includeCompleted?: boolean): ReturnType<typeof listTasks>;
19
+ export interface TaskPatch {
20
+ title?: string;
21
+ summary?: string;
22
+ criteria?: string[];
23
+ plan?: string[];
24
+ areas?: string[];
25
+ decisions?: string[];
26
+ blockers?: string[];
27
+ clearBlockers?: boolean;
28
+ require?: string[];
29
+ }
30
+ export declare function updateTask(o: ServiceOptions, id: string, patch: TaskPatch): Promise<Task>;
31
+ export declare function setTaskStatus(o: ServiceOptions, id: string, status: string, note?: string): Promise<{
32
+ task: Task;
33
+ gate: GateResult | null;
34
+ }>;
35
+ export declare function recordVerification(o: ServiceOptions, id: string, check: string, result: string, extra?: {
36
+ reason?: string;
37
+ detail?: string;
38
+ }): Promise<Task>;
39
+ export declare function recordTests(o: ServiceOptions, id: string, phase: 'baseline' | 'after', passed: number, failed: number): Promise<Task>;
40
+ export declare function attestTask(o: ServiceOptions, id: string, flags: {
41
+ userWorkIntact?: boolean;
42
+ knowledgeReviewed?: boolean;
43
+ finalStateInspected?: boolean;
44
+ }): Promise<Task>;
@@ -0,0 +1,136 @@
1
+ import { SCHEMA_VERSION, validateCommands } from '../config/schemas.js';
2
+ import { DevWorkspaceError } from '../errors/index.js';
3
+ import { loadProject, requireWorkspace } from '../core/workspace.js';
4
+ import { readVersioned } from '../core/versioned.js';
5
+ import { classifyRequest, WORKFLOWS } from '../tasks/routing.js';
6
+ import { assertNoSecrets, createTaskObject, evaluateGate, listTasks, loadTask, requiredChecksFor, saveTask, transitionTask, TASK_STATUSES, TASK_TYPES, VERIFY_STATUSES, } from '../tasks/task.js';
7
+ import { pathExists } from '../utils/fs.js';
8
+ import { isoNow, mutate } from './common.js';
9
+ async function loadContract(ws) {
10
+ if (!(await pathExists(ws.commands)))
11
+ return [];
12
+ return (await readVersioned(ws.commands, 'commands', SCHEMA_VERSION, validateCommands)).data.checks;
13
+ }
14
+ export async function createTask(o, input) {
15
+ return mutate(o, async (ws) => {
16
+ const project = await loadProject(ws);
17
+ const classification = classifyRequest(input.text);
18
+ let type = classification.type;
19
+ if (input.type) {
20
+ const up = input.type.toUpperCase();
21
+ if (!TASK_TYPES.includes(up)) {
22
+ throw new DevWorkspaceError('USAGE', `Unknown task type "${input.type}".`, { hint: `Use one of: ${TASK_TYPES.join(', ')}.` });
23
+ }
24
+ type = up;
25
+ }
26
+ const contract = await loadContract(ws);
27
+ let verification = requiredChecksFor(type, contract, project.settings.review);
28
+ // Explicit user constraints stay visible: the check is kept but recorded as skipped at the user's request.
29
+ const skipChecks = new Set();
30
+ if (classification.constraints.includes('no-e2e'))
31
+ skipChecks.add('e2e');
32
+ if (classification.constraints.includes('no-tests'))
33
+ skipChecks.add('unit').add('integration').add('e2e');
34
+ verification = verification.map((v) => (skipChecks.has(v.check) ? { ...v, status: 'SKIPPED', reason: 'user-override' } : v));
35
+ const title = (input.title ?? input.text).replace(/\s+/g, ' ').trim().slice(0, 100);
36
+ const task = createTaskObject({ title, type, description: input.text, verification, constraints: classification.constraints }, isoNow(o));
37
+ await saveTask(ws, task);
38
+ return { task, classification: { ...classification, type }, workflow: WORKFLOWS[type] };
39
+ });
40
+ }
41
+ export async function showTask(o, id) {
42
+ const ws = await requireWorkspace(o.cwd);
43
+ const task = await loadTask(ws, id);
44
+ return { task, gate: evaluateGate(task) };
45
+ }
46
+ export async function listTasksService(o, includeCompleted = false) {
47
+ const ws = await requireWorkspace(o.cwd);
48
+ return listTasks(ws, { includeCompleted });
49
+ }
50
+ export async function updateTask(o, id, patch) {
51
+ return mutate(o, async (ws) => {
52
+ const task = await loadTask(ws, id);
53
+ if (patch.title)
54
+ task.title = patch.title.trim().slice(0, 200);
55
+ if (patch.summary !== undefined)
56
+ task.completionSummary = patch.summary.trim().slice(0, 4000);
57
+ task.acceptanceCriteria.push(...(patch.criteria ?? []));
58
+ task.plan.push(...(patch.plan ?? []));
59
+ task.affectedAreas.push(...(patch.areas ?? []).filter((a) => !task.affectedAreas.includes(a)));
60
+ task.decisions.push(...(patch.decisions ?? []));
61
+ if (patch.clearBlockers)
62
+ task.blockers = [];
63
+ task.blockers.push(...(patch.blockers ?? []));
64
+ for (const check of patch.require ?? []) {
65
+ if (!task.verification.some((v) => v.check === check))
66
+ task.verification.push({ check, required: true, status: 'PENDING' });
67
+ }
68
+ task.updatedAt = isoNow(o);
69
+ assertNoSecrets(task);
70
+ await saveTask(ws, task);
71
+ return task;
72
+ });
73
+ }
74
+ export async function setTaskStatus(o, id, status, note) {
75
+ const up = status.toUpperCase();
76
+ if (!TASK_STATUSES.includes(up)) {
77
+ throw new DevWorkspaceError('USAGE', `Unknown task status "${status}".`, { hint: `Use one of: ${TASK_STATUSES.join(', ')}.` });
78
+ }
79
+ return mutate(o, async (ws) => {
80
+ const task = await loadTask(ws, id);
81
+ const gate = transitionTask(task, up, isoNow(o), note);
82
+ if (up === 'BLOCKED' && note)
83
+ task.blockers.push(note.slice(0, 300));
84
+ await saveTask(ws, task);
85
+ return { task, gate };
86
+ });
87
+ }
88
+ export async function recordVerification(o, id, check, result, extra = {}) {
89
+ const status = result.toUpperCase();
90
+ const normalized = status === 'SKIP' ? 'SKIPPED' : status;
91
+ if (!VERIFY_STATUSES.includes(normalized)) {
92
+ throw new DevWorkspaceError('USAGE', `Unknown result "${result}".`, { hint: 'Use pass, fail, skip or pending.' });
93
+ }
94
+ return mutate(o, async (ws) => {
95
+ const task = await loadTask(ws, id);
96
+ let item = task.verification.find((v) => v.check === check);
97
+ if (!item) {
98
+ item = { check, required: false, status: 'PENDING' };
99
+ task.verification.push(item);
100
+ }
101
+ item.status = normalized;
102
+ item.updatedAt = isoNow(o);
103
+ if (extra.reason)
104
+ item.reason = extra.reason;
105
+ else
106
+ delete item.reason;
107
+ if (extra.detail)
108
+ item.detail = extra.detail.slice(0, 500);
109
+ task.updatedAt = item.updatedAt;
110
+ assertNoSecrets(task);
111
+ await saveTask(ws, task);
112
+ return task;
113
+ });
114
+ }
115
+ export async function recordTests(o, id, phase, passed, failed) {
116
+ if (!Number.isInteger(passed) || !Number.isInteger(failed) || passed < 0 || failed < 0) {
117
+ throw new DevWorkspaceError('USAGE', '--passed and --failed must be non-negative integers.');
118
+ }
119
+ return mutate(o, async (ws) => {
120
+ const task = await loadTask(ws, id);
121
+ task.tests[phase] = { passed, failed };
122
+ task.updatedAt = isoNow(o);
123
+ await saveTask(ws, task);
124
+ return task;
125
+ });
126
+ }
127
+ export async function attestTask(o, id, flags) {
128
+ return mutate(o, async (ws) => {
129
+ const task = await loadTask(ws, id);
130
+ task.attestations = { ...task.attestations, ...Object.fromEntries(Object.entries(flags).filter(([, v]) => v !== undefined)) };
131
+ task.updatedAt = isoNow(o);
132
+ await saveTask(ws, task);
133
+ return task;
134
+ });
135
+ }
136
+ //# sourceMappingURL=tasks.js.map