devworkspace 0.2.1 → 0.2.2

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 (65) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +4 -3
  3. package/dist/agents/claude/content.js +3 -2
  4. package/dist/agents/claude/content.js.map +1 -1
  5. package/dist/cli/args.d.ts +16 -0
  6. package/dist/cli/args.js +5 -0
  7. package/dist/cli/args.js.map +1 -1
  8. package/dist/cli/help.js +30 -7
  9. package/dist/cli/help.js.map +1 -1
  10. package/dist/cli/render.d.ts +7 -1
  11. package/dist/cli/render.js +64 -11
  12. package/dist/cli/render.js.map +1 -1
  13. package/dist/cli/run.js +47 -8
  14. package/dist/cli/run.js.map +1 -1
  15. package/dist/config/schemas.d.ts +5 -0
  16. package/dist/config/schemas.js +2 -0
  17. package/dist/config/schemas.js.map +1 -1
  18. package/dist/discovery/commands.js +17 -3
  19. package/dist/discovery/commands.js.map +1 -1
  20. package/dist/discovery/detect.d.ts +6 -0
  21. package/dist/discovery/detect.js +12 -6
  22. package/dist/discovery/detect.js.map +1 -1
  23. package/dist/discovery/docs.d.ts +10 -0
  24. package/dist/discovery/docs.js +52 -0
  25. package/dist/discovery/docs.js.map +1 -0
  26. package/dist/discovery/entrypoints.d.ts +20 -0
  27. package/dist/discovery/entrypoints.js +67 -0
  28. package/dist/discovery/entrypoints.js.map +1 -0
  29. package/dist/knowledge/facts.d.ts +59 -2
  30. package/dist/knowledge/facts.js +115 -17
  31. package/dist/knowledge/facts.js.map +1 -1
  32. package/dist/prompts/onboarding.js +6 -3
  33. package/dist/prompts/onboarding.js.map +1 -1
  34. package/dist/services/analysis.js +6 -0
  35. package/dist/services/analysis.js.map +1 -1
  36. package/dist/services/doctor.js +14 -3
  37. package/dist/services/doctor.js.map +1 -1
  38. package/dist/services/knowledge.d.ts +17 -0
  39. package/dist/services/knowledge.js +28 -2
  40. package/dist/services/knowledge.js.map +1 -1
  41. package/dist/services/refresh.js +5 -5
  42. package/dist/services/refresh.js.map +1 -1
  43. package/dist/services/status.d.ts +11 -2
  44. package/dist/services/status.js +11 -9
  45. package/dist/services/status.js.map +1 -1
  46. package/dist/services/tasks.d.ts +2 -0
  47. package/dist/services/tasks.js +20 -12
  48. package/dist/services/tasks.js.map +1 -1
  49. package/dist/services/verify.d.ts +47 -0
  50. package/dist/services/verify.js +172 -0
  51. package/dist/services/verify.js.map +1 -0
  52. package/dist/tasks/routing.js +8 -3
  53. package/dist/tasks/routing.js.map +1 -1
  54. package/dist/tasks/task.d.ts +49 -5
  55. package/dist/tasks/task.js +68 -11
  56. package/dist/tasks/task.js.map +1 -1
  57. package/dist/verification/safety.d.ts +38 -0
  58. package/dist/verification/safety.js +120 -0
  59. package/dist/verification/safety.js.map +1 -0
  60. package/dist/verification/scope.d.ts +19 -0
  61. package/dist/verification/scope.js +119 -0
  62. package/dist/verification/scope.js.map +1 -0
  63. package/docs/knowledge.md +2 -0
  64. package/docs/testing.md +25 -2
  65. package/package.json +1 -1
@@ -3,7 +3,7 @@ import { SCHEMA_VERSION, validateAnalysisState } from '../config/schemas.js';
3
3
  import { loadProject } from '../core/workspace.js';
4
4
  import { readVersioned, writeVersioned } from '../core/versioned.js';
5
5
  import { packageVersion } from '../core/version.js';
6
- import { applyStaleness, loadFacts, saveFacts } from '../knowledge/facts.js';
6
+ import { applyStaleness, evaluateStaleness, loadFacts, saveFacts } from '../knowledge/facts.js';
7
7
  import { buildRefreshPrompt } from '../prompts/refresh.js';
8
8
  import { pathExists, sha256Text, writeJsonIfChanged, writeTextIfChanged } from '../utils/fs.js';
9
9
  import { applyDetectorFacts, runDiscovery, writeCommands } from './analysis.js';
@@ -24,15 +24,15 @@ export async function refreshWorkspace(o) {
24
24
  const headChanged = !!previous && previous.gitHead !== run.gitHead;
25
25
  if (o.dryRun) {
26
26
  const facts = await loadFacts(ws);
27
- const probe = structuredClone(facts);
28
- const rep = await applyStaleness(ws.root, probe, now);
27
+ const view = await evaluateStaleness(ws.root, facts, now);
28
+ const valueOf = (key) => facts.facts.find((f) => f.key === key)?.value ?? '';
29
29
  return {
30
30
  dryRun: true,
31
31
  headChanged,
32
32
  autoRefreshed: [],
33
33
  retired: [],
34
- stale: rep.newlyStale.map((s) => ({ ...s, value: probe.facts.find((f) => f.key === s.key)?.value ?? '' })),
35
- needsReview: rep.needsReview,
34
+ stale: [...view.recorded, ...view.detected].map((s) => ({ ...s, value: valueOf(s.key) })),
35
+ needsReview: view.needsReview,
36
36
  prompt: null,
37
37
  };
38
38
  }
@@ -1 +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, { truncated: run.discovery.scan.truncated });\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"]}
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, evaluateStaleness, 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 view = await evaluateStaleness(ws.root, facts, now);\n const valueOf = (key: string): string => facts.facts.find((f) => f.key === key)?.value ?? '';\n return {\n dryRun: true,\n headChanged,\n autoRefreshed: [],\n retired: [],\n stale: [...view.recorded, ...view.detected].map((s) => ({ ...s, value: valueOf(s.key) })),\n needsReview: view.needsReview,\n prompt: null,\n };\n }\n\n await writeJsonIfChanged(ws.discovery, run.discovery);\n await writeCommands(ws, run.discovery.commands, { truncated: run.discovery.scan.truncated });\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"]}
@@ -30,6 +30,12 @@ export interface StatusResult {
30
30
  facts: number;
31
31
  stale: number;
32
32
  analyzedAt: string | null;
33
+ staleItems: {
34
+ key: string;
35
+ reason: string;
36
+ recorded: boolean;
37
+ }[];
38
+ needsReview: number;
33
39
  };
34
40
  onboarding: {
35
41
  instructionsInstalled: boolean;
@@ -45,6 +51,7 @@ export interface StatusResult {
45
51
  command: string | null;
46
52
  required: string;
47
53
  cwd?: string;
54
+ mutates?: boolean;
48
55
  }[];
49
56
  lastSession: {
50
57
  id: string;
@@ -55,5 +62,7 @@ export interface StatusResult {
55
62
  }
56
63
  export declare function instructionsInstalled(ws: Ws): Promise<boolean>;
57
64
  export declare function knowledgeWritten(ws: Ws): Promise<boolean>;
58
- /** Cheap overview: reads small workspace files and Git status only. Never hashes or scans source files. */
59
- export declare function getStatus(o: ServiceOptions): Promise<StatusResult>;
65
+ /** Cheap overview: small workspace files, Git status and a fingerprint check of the few files facts cite. Never scans the repository. */
66
+ export declare function getStatus(o: ServiceOptions, opts?: {
67
+ deep?: boolean;
68
+ }): Promise<StatusResult>;
@@ -5,13 +5,13 @@ import { findWorkspaceRoot, loadProject, wsPaths } from '../core/workspace.js';
5
5
  import { readVersioned } from '../core/versioned.js';
6
6
  import { getGitState } from '../git/git.js';
7
7
  import { inspectAll } from '../integrations/index.js';
8
- import { loadFacts } from '../knowledge/facts.js';
8
+ import { evaluateStaleness, loadFacts } from '../knowledge/facts.js';
9
9
  import { listSessions } from '../sessions/session.js';
10
10
  import { listTasks, nextRequiredStep } from '../tasks/task.js';
11
11
  import { pathExists } from '../utils/fs.js';
12
12
  import { SECTION_END } from '../agents/claude/content.js';
13
13
  import { SKILL_PATH } from '../agents/claude/content.js';
14
- import { managerEnv } from './common.js';
14
+ import { isoNow, managerEnv } from './common.js';
15
15
  const brief = (t) => ({ id: t.id, title: t.title, type: t.type, status: t.status, updatedAt: t.updatedAt, next: nextRequiredStep(t) });
16
16
  export async function instructionsInstalled(ws) {
17
17
  for (const rel of ['CLAUDE.md', '.claude/CLAUDE.md']) {
@@ -34,8 +34,8 @@ export async function knowledgeWritten(ws) {
34
34
  return false;
35
35
  }
36
36
  }
37
- /** Cheap overview: reads small workspace files and Git status only. Never hashes or scans source files. */
38
- export async function getStatus(o) {
37
+ /** Cheap overview: small workspace files, Git status and a fingerprint check of the few files facts cite. Never scans the repository. */
38
+ export async function getStatus(o, opts = {}) {
39
39
  const root = await findWorkspaceRoot(o.cwd);
40
40
  const git = await getGitState(root ?? o.cwd);
41
41
  const gitBrief = {
@@ -51,7 +51,7 @@ export async function getStatus(o) {
51
51
  project: null,
52
52
  workspace: 'Not initialized',
53
53
  git: gitBrief,
54
- knowledge: { state: 'Unknown', facts: 0, stale: 0, analyzedAt: null },
54
+ knowledge: { state: 'Unknown', facts: 0, stale: 0, analyzedAt: null, staleItems: [], needsReview: 0 },
55
55
  onboarding: { instructionsInstalled: false, knowledgeWritten: false, ready: false },
56
56
  integrations: [],
57
57
  activeTasks: [],
@@ -73,12 +73,14 @@ export async function getStatus(o) {
73
73
  }
74
74
  try {
75
75
  const facts = await loadFacts(ws);
76
- const stale = facts.facts.filter((f) => f.status === 'STALE').length;
76
+ const view = await evaluateStaleness(ws.root, facts, isoNow(o));
77
+ const items = [...view.recorded.map((x) => ({ ...x, recorded: true })), ...view.detected.map((x) => ({ ...x, recorded: false }))];
78
+ const stale = items.length;
77
79
  let analyzedAt = null;
78
80
  if (await pathExists(ws.analysisState)) {
79
81
  analyzedAt = (await readVersioned(ws.analysisState, 'analysis', SCHEMA_VERSION, validateAnalysisState)).data.analyzedAt;
80
82
  }
81
- base.knowledge = { facts: facts.facts.length, stale, analyzedAt, state: facts.facts.length === 0 ? 'Empty' : stale > 0 ? 'Stale' : 'Current' };
83
+ base.knowledge = { facts: facts.facts.length, stale, analyzedAt, staleItems: items, needsReview: view.needsReview.length, state: facts.facts.length === 0 ? 'Empty' : stale > 0 ? 'Stale' : 'Current' };
82
84
  }
83
85
  catch (e) {
84
86
  base.workspace = 'Needs attention';
@@ -87,7 +89,7 @@ export async function getStatus(o) {
87
89
  if (await pathExists(ws.commands)) {
88
90
  try {
89
91
  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, ...(c.cwd ? { cwd: c.cwd } : {}) }));
92
+ base.verification = commands.checks.map((c) => ({ id: c.id, available: c.available, command: c.command, required: c.required, ...(c.cwd ? { cwd: c.cwd } : {}), ...(c.mutates !== undefined ? { mutates: c.mutates } : {}) }));
91
93
  }
92
94
  catch (e) {
93
95
  base.workspace = 'Needs attention';
@@ -102,7 +104,7 @@ export async function getStatus(o) {
102
104
  const sessions = await listSessions(ws, 1);
103
105
  if (sessions[0])
104
106
  base.lastSession = { id: sessions[0].id, goal: sessions[0].goal, next: sessions[0].next };
105
- base.integrations = await inspectAll(ws, managerEnv(o, root), { deep: false });
107
+ base.integrations = await inspectAll(ws, managerEnv(o, root), { deep: !!opts.deep });
106
108
  base.onboarding.instructionsInstalled = await instructionsInstalled(ws);
107
109
  base.onboarding.knowledgeWritten = await knowledgeWritten(ws);
108
110
  base.onboarding.ready = base.onboarding.instructionsInstalled && base.onboarding.knowledgeWritten && base.knowledge.state !== 'Empty';
@@ -1 +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 { inspectAll, type IntegrationReport } 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 { managerEnv, 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 /** Shallow observation (files and PATH only; never VERIFIED). Run doctor or `integrations status` to verify. */\n integrations: IntegrationReport[];\n activeTasks: TaskBrief[];\n verification: { id: string; available: boolean; command: string | null; required: string; cwd?: 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, ...(c.cwd ? { cwd: c.cwd } : {}) }));\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 inspectAll(ws, managerEnv(o, root), { deep: false });\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"]}
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/services/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,gBAAgB,EAAqB,MAAM,sBAAsB,CAAC;AAClH,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,OAAO,EAAW,MAAM,sBAAsB,CAAC;AACxF,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAA0B,MAAM,0BAA0B,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAa,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAuB,MAAM,aAAa,CAAC;AA4BtE,MAAM,KAAK,GAAG,CAAC,CAAO,EAAa,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAExJ,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,EAAM;IAChD,KAAK,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,mBAAmB,CAAC,EAAE,CAAC;QACrD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;YAC9E,IAAI,IAAI,CAAC,QAAQ,CAAC,yBAAyB,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBAAE,OAAO,IAAI,CAAC;QAC9J,CAAC;QAAC,MAAM,CAAC;YACP,cAAc;QAChB,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,EAAM;IAC3C,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC;IAClF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,yIAAyI;AACzI,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,CAAiB,EAAE,OAA2B,EAAE;IAC9E,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC5C,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IAC7C,MAAM,QAAQ,GAAwB;QACpC,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS;QACxE,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM;KAC5B,CAAC;IACF,MAAM,IAAI,GAAiB;QACzB,WAAW,EAAE,KAAK;QAClB,IAAI;QACJ,OAAO,EAAE,IAAI;QACb,SAAS,EAAE,iBAAiB;QAC5B,GAAG,EAAE,QAAQ;QACb,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,WAAW,EAAE,CAAC,EAAE;QACrG,UAAU,EAAE,EAAE,qBAAqB,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE;QACnF,YAAY,EAAE,EAAE;QAChB,WAAW,EAAE,EAAE;QACf,YAAY,EAAE,EAAE;QAChB,WAAW,EAAE,IAAI;QACjB,YAAY,EAAE,CAAC;KAChB,CAAC;IACF,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvB,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,EAAE,CAAC,CAAC;QACtC,IAAI,CAAC,OAAO,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC;QAC7D,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,gBAAgB,EAAG,CAAW,CAAC,OAAO,EAAE,CAAC;IACtF,CAAC;IAED,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,EAAE,CAAC,CAAC;QAClC,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAChE,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;QAClI,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;QAC3B,IAAI,UAAU,GAAkB,IAAI,CAAC;QACrC,IAAI,MAAM,UAAU,CAAC,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC;YACvC,UAAU,GAAG,CAAC,MAAM,aAAa,CAAC,EAAE,CAAC,aAAa,EAAE,UAAU,EAAE,cAAc,EAAE,qBAAqB,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;QAC1H,CAAC;QACD,IAAI,CAAC,SAAS,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;IAC1M,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,SAAS,GAAG,iBAAiB,CAAC;QACnC,IAAI,CAAC,gBAAgB,GAAI,CAAW,CAAC,OAAO,CAAC;IAC/C,CAAC;IAED,IAAI,MAAM,UAAU,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,CAAC,MAAM,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC;YACvG,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAe,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC/O,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,SAAS,GAAG,iBAAiB,CAAC;YACnC,IAAI,CAAC,gBAAgB,KAAM,CAAW,CAAC,OAAO,CAAC;QACjD,CAAC;IACH,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,SAAS,CAAC,EAAE,CAAC,CAAC;IAC/C,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACpC,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IACnC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,IAAI,CAAC,SAAS,GAAG,iBAAiB,CAAC;IAC3D,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAC3C,IAAI,QAAQ,CAAC,CAAC,CAAC;QAAE,IAAI,CAAC,WAAW,GAAG,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAE3G,IAAI,CAAC,YAAY,GAAG,MAAM,UAAU,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACrF,IAAI,CAAC,UAAU,CAAC,qBAAqB,GAAG,MAAM,qBAAqB,CAAC,EAAE,CAAC,CAAC;IACxE,IAAI,CAAC,UAAU,CAAC,gBAAgB,GAAG,MAAM,gBAAgB,CAAC,EAAE,CAAC,CAAC;IAC9D,IAAI,CAAC,UAAU,CAAC,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,qBAAqB,IAAI,IAAI,CAAC,UAAU,CAAC,gBAAgB,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,KAAK,OAAO,CAAC;IACtI,OAAO,IAAI,CAAC;AACd,CAAC","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 { inspectAll, type IntegrationReport } from '../integrations/index.js';\nimport { evaluateStaleness, 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 { isoNow, managerEnv, 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; staleItems: { key: string; reason: string; recorded: boolean }[]; needsReview: number };\n onboarding: { instructionsInstalled: boolean; knowledgeWritten: boolean; ready: boolean };\n /** Shallow observation (files and PATH only; never VERIFIED). Run doctor or `integrations status` to verify. */\n integrations: IntegrationReport[];\n activeTasks: TaskBrief[];\n verification: { id: string; available: boolean; command: string | null; required: string; cwd?: string; mutates?: boolean }[];\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: small workspace files, Git status and a fingerprint check of the few files facts cite. Never scans the repository. */\nexport async function getStatus(o: ServiceOptions, opts: { deep?: boolean } = {}): 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, staleItems: [], needsReview: 0 },\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 view = await evaluateStaleness(ws.root, facts, isoNow(o));\n const items = [...view.recorded.map((x) => ({ ...x, recorded: true })), ...view.detected.map((x) => ({ ...x, recorded: false }))];\n const stale = items.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, staleItems: items, needsReview: view.needsReview.length, 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, ...(c.cwd ? { cwd: c.cwd } : {}), ...(c.mutates !== undefined ? { mutates: c.mutates } : {}) }));\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 inspectAll(ws, managerEnv(o, root), { deep: !!opts.deep });\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"]}
@@ -14,6 +14,7 @@ export declare function createTask(o: ServiceOptions, input: {
14
14
  export declare function showTask(o: ServiceOptions, id: string): Promise<{
15
15
  task: Task;
16
16
  gate: GateResult;
17
+ next: string;
17
18
  }>;
18
19
  export declare function listTasksService(o: ServiceOptions, includeCompleted?: boolean): ReturnType<typeof listTasks>;
19
20
  export interface TaskPatch {
@@ -35,6 +36,7 @@ export declare function setTaskStatus(o: ServiceOptions, id: string, status: str
35
36
  export declare function recordVerification(o: ServiceOptions, id: string, check: string, result: string, extra?: {
36
37
  reason?: string;
37
38
  detail?: string;
39
+ package?: string;
38
40
  }): Promise<Task>;
39
41
  export declare function recordTests(o: ServiceOptions, id: string, phase: 'baseline' | 'after', passed: number, failed: number): Promise<Task>;
40
42
  export declare function attestTask(o: ServiceOptions, id: string, flags: {
@@ -1,16 +1,10 @@
1
- import { SCHEMA_VERSION, validateCommands } from '../config/schemas.js';
2
1
  import { DevWorkspaceError } from '../errors/index.js';
3
2
  import { loadProject, requireWorkspace } from '../core/workspace.js';
4
- import { readVersioned } from '../core/versioned.js';
3
+ import { captureScope } from '../verification/scope.js';
4
+ import { gateContext, loadContract } from './verify.js';
5
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';
6
+ import { assertNoSecrets, createTaskObject, evaluateGate, nextRequiredStep, listTasks, loadTask, requiredChecksFor, saveTask, transitionTask, TASK_STATUSES, TASK_TYPES, VERIFY_STATUSES, } from '../tasks/task.js';
8
7
  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
8
  export async function createTask(o, input) {
15
9
  return mutate(o, async (ws) => {
16
10
  const project = await loadProject(ws);
@@ -34,6 +28,7 @@ export async function createTask(o, input) {
34
28
  verification = verification.map((v) => (skipChecks.has(v.check) ? { ...v, status: 'SKIPPED', reason: 'user-override' } : v));
35
29
  const title = (input.title ?? input.text).replace(/\s+/g, ' ').trim().slice(0, 100);
36
30
  const task = createTaskObject({ title, type, description: input.text, verification, constraints: classification.constraints }, isoNow(o));
31
+ task.scope = await captureScope(ws.root, isoNow(o));
37
32
  await saveTask(ws, task);
38
33
  return { task, classification: { ...classification, type }, workflow: WORKFLOWS[type] };
39
34
  });
@@ -41,7 +36,8 @@ export async function createTask(o, input) {
41
36
  export async function showTask(o, id) {
42
37
  const ws = await requireWorkspace(o.cwd);
43
38
  const task = await loadTask(ws, id);
44
- return { task, gate: evaluateGate(task) };
39
+ const ctx = await gateContext(ws, task);
40
+ return { task, gate: evaluateGate(task, ctx), next: nextRequiredStep(task, ctx) };
45
41
  }
46
42
  export async function listTasksService(o, includeCompleted = false) {
47
43
  const ws = await requireWorkspace(o.cwd);
@@ -62,7 +58,10 @@ export async function updateTask(o, id, patch) {
62
58
  task.blockers = [];
63
59
  task.blockers.push(...(patch.blockers ?? []));
64
60
  for (const check of patch.require ?? []) {
65
- if (!task.verification.some((v) => v.check === check))
61
+ const existing = task.verification.find((v) => v.check === check);
62
+ if (existing)
63
+ existing.required = true;
64
+ else
66
65
  task.verification.push({ check, required: true, status: 'PENDING' });
67
66
  }
68
67
  task.updatedAt = isoNow(o);
@@ -78,7 +77,7 @@ export async function setTaskStatus(o, id, status, note) {
78
77
  }
79
78
  return mutate(o, async (ws) => {
80
79
  const task = await loadTask(ws, id);
81
- const gate = transitionTask(task, up, isoNow(o), note);
80
+ const gate = transitionTask(task, up, isoNow(o), note, up === 'COMPLETED' ? await gateContext(ws, task) : undefined);
82
81
  if (up === 'BLOCKED' && note)
83
82
  task.blockers.push(note.slice(0, 300));
84
83
  await saveTask(ws, task);
@@ -100,6 +99,15 @@ export async function recordVerification(o, id, check, result, extra = {}) {
100
99
  }
101
100
  item.status = normalized;
102
101
  item.updatedAt = isoNow(o);
102
+ // A reported pass is a claim: DevWorkspace did not see the command run. It is kept as such, per package, and the
103
+ // completion gate of a scoped task only accepts it with an explicit user override.
104
+ if (normalized === 'PASS') {
105
+ const pkg = !extra.package || extra.package === '' ? '.' : extra.package.replace(/\/+$/, '');
106
+ const claim = { kind: 'claimed', at: item.updatedAt, package: pkg, ...(extra.reason === 'user-override' ? { userOverride: true } : {}), ...(extra.detail ? { note: extra.detail.slice(0, 200) } : {}) };
107
+ // Never replace something DevWorkspace observed with somebody's say-so.
108
+ if (!item.evidence?.some((e) => e.package === pkg && e.kind === 'executed'))
109
+ item.evidence = [...(item.evidence ?? []).filter((e) => e.package !== pkg), claim];
110
+ }
103
111
  if (extra.reason)
104
112
  item.reason = extra.reason;
105
113
  else
@@ -1 +1 @@
1
- {"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../src/services/tasks.ts"],"names":[],"mappings":"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","sourcesContent":["import { SCHEMA_VERSION, validateCommands, type CommandEntry } from '../config/schemas.js';\nimport { DevWorkspaceError } from '../errors/index.js';\nimport { loadProject, requireWorkspace, type Ws } from '../core/workspace.js';\nimport { readVersioned } from '../core/versioned.js';\nimport { classifyRequest, WORKFLOWS, type Classification, type WorkflowSpec } from '../tasks/routing.js';\nimport {\n assertNoSecrets,\n createTaskObject,\n evaluateGate,\n listTasks,\n loadTask,\n requiredChecksFor,\n saveTask,\n transitionTask,\n type GateResult,\n type Task,\n type TaskStatus,\n type TaskType,\n type VerifyStatus,\n TASK_STATUSES,\n TASK_TYPES,\n VERIFY_STATUSES,\n} from '../tasks/task.js';\nimport { pathExists } from '../utils/fs.js';\nimport { isoNow, mutate, type ServiceOptions } from './common.js';\n\nasync function loadContract(ws: Ws): Promise<CommandEntry[]> {\n if (!(await pathExists(ws.commands))) return [];\n return (await readVersioned(ws.commands, 'commands', SCHEMA_VERSION, validateCommands)).data.checks;\n}\n\nexport interface CreateTaskResult {\n task: Task;\n classification: Classification;\n workflow: WorkflowSpec;\n}\n\nexport async function createTask(o: ServiceOptions, input: { text: string; type?: string; title?: string }): Promise<CreateTaskResult> {\n return mutate(o, async (ws) => {\n const project = await loadProject(ws);\n const classification = classifyRequest(input.text);\n let type: TaskType = classification.type;\n if (input.type) {\n const up = input.type.toUpperCase();\n if (!(TASK_TYPES as readonly string[]).includes(up)) {\n throw new DevWorkspaceError('USAGE', `Unknown task type \"${input.type}\".`, { hint: `Use one of: ${TASK_TYPES.join(', ')}.` });\n }\n type = up as TaskType;\n }\n const contract = await loadContract(ws);\n let verification = requiredChecksFor(type, contract, project.settings.review);\n // Explicit user constraints stay visible: the check is kept but recorded as skipped at the user's request.\n const skipChecks = new Set<string>();\n if (classification.constraints.includes('no-e2e')) skipChecks.add('e2e');\n if (classification.constraints.includes('no-tests')) skipChecks.add('unit').add('integration').add('e2e');\n verification = verification.map((v) => (skipChecks.has(v.check) ? { ...v, status: 'SKIPPED' as const, reason: 'user-override' } : v));\n const title = (input.title ?? input.text).replace(/\\s+/g, ' ').trim().slice(0, 100);\n const task = createTaskObject({ title, type, description: input.text, verification, constraints: classification.constraints }, isoNow(o));\n await saveTask(ws, task);\n return { task, classification: { ...classification, type }, workflow: WORKFLOWS[type] };\n });\n}\n\nexport async function showTask(o: ServiceOptions, id: string): Promise<{ task: Task; gate: GateResult }> {\n const ws = await requireWorkspace(o.cwd);\n const task = await loadTask(ws, id);\n return { task, gate: evaluateGate(task) };\n}\n\nexport async function listTasksService(o: ServiceOptions, includeCompleted = false): ReturnType<typeof listTasks> {\n const ws = await requireWorkspace(o.cwd);\n return listTasks(ws, { includeCompleted });\n}\n\nexport interface TaskPatch {\n title?: string;\n summary?: string;\n criteria?: string[];\n plan?: string[];\n areas?: string[];\n decisions?: string[];\n blockers?: string[];\n clearBlockers?: boolean;\n require?: string[];\n}\n\nexport async function updateTask(o: ServiceOptions, id: string, patch: TaskPatch): Promise<Task> {\n return mutate(o, async (ws) => {\n const task = await loadTask(ws, id);\n if (patch.title) task.title = patch.title.trim().slice(0, 200);\n if (patch.summary !== undefined) task.completionSummary = patch.summary.trim().slice(0, 4000);\n task.acceptanceCriteria.push(...(patch.criteria ?? []));\n task.plan.push(...(patch.plan ?? []));\n task.affectedAreas.push(...(patch.areas ?? []).filter((a) => !task.affectedAreas.includes(a)));\n task.decisions.push(...(patch.decisions ?? []));\n if (patch.clearBlockers) task.blockers = [];\n task.blockers.push(...(patch.blockers ?? []));\n for (const check of patch.require ?? []) {\n if (!task.verification.some((v) => v.check === check)) task.verification.push({ check, required: true, status: 'PENDING' });\n }\n task.updatedAt = isoNow(o);\n assertNoSecrets(task);\n await saveTask(ws, task);\n return task;\n });\n}\n\nexport async function setTaskStatus(o: ServiceOptions, id: string, status: string, note?: string): Promise<{ task: Task; gate: GateResult | null }> {\n const up = status.toUpperCase();\n if (!(TASK_STATUSES as readonly string[]).includes(up)) {\n throw new DevWorkspaceError('USAGE', `Unknown task status \"${status}\".`, { hint: `Use one of: ${TASK_STATUSES.join(', ')}.` });\n }\n return mutate(o, async (ws) => {\n const task = await loadTask(ws, id);\n const gate = transitionTask(task, up as TaskStatus, isoNow(o), note);\n if (up === 'BLOCKED' && note) task.blockers.push(note.slice(0, 300));\n await saveTask(ws, task);\n return { task, gate };\n });\n}\n\nexport async function recordVerification(\n o: ServiceOptions,\n id: string,\n check: string,\n result: string,\n extra: { reason?: string; detail?: string } = {},\n): Promise<Task> {\n const status = result.toUpperCase();\n const normalized = status === 'SKIP' ? 'SKIPPED' : status;\n if (!(VERIFY_STATUSES as readonly string[]).includes(normalized)) {\n throw new DevWorkspaceError('USAGE', `Unknown result \"${result}\".`, { hint: 'Use pass, fail, skip or pending.' });\n }\n return mutate(o, async (ws) => {\n const task = await loadTask(ws, id);\n let item = task.verification.find((v) => v.check === check);\n if (!item) {\n item = { check, required: false, status: 'PENDING' };\n task.verification.push(item);\n }\n item.status = normalized as VerifyStatus;\n item.updatedAt = isoNow(o);\n if (extra.reason) item.reason = extra.reason;\n else delete item.reason;\n if (extra.detail) item.detail = extra.detail.slice(0, 500);\n task.updatedAt = item.updatedAt;\n assertNoSecrets(task);\n await saveTask(ws, task);\n return task;\n });\n}\n\nexport async function recordTests(o: ServiceOptions, id: string, phase: 'baseline' | 'after', passed: number, failed: number): Promise<Task> {\n if (!Number.isInteger(passed) || !Number.isInteger(failed) || passed < 0 || failed < 0) {\n throw new DevWorkspaceError('USAGE', '--passed and --failed must be non-negative integers.');\n }\n return mutate(o, async (ws) => {\n const task = await loadTask(ws, id);\n task.tests[phase] = { passed, failed };\n task.updatedAt = isoNow(o);\n await saveTask(ws, task);\n return task;\n });\n}\n\nexport async function attestTask(\n o: ServiceOptions,\n id: string,\n flags: { userWorkIntact?: boolean; knowledgeReviewed?: boolean; finalStateInspected?: boolean },\n): Promise<Task> {\n return mutate(o, async (ws) => {\n const task = await loadTask(ws, id);\n task.attestations = { ...task.attestations, ...Object.fromEntries(Object.entries(flags).filter(([, v]) => v !== undefined)) };\n task.updatedAt = isoNow(o);\n await saveTask(ws, task);\n return task;\n });\n}\n"]}
1
+ {"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../src/services/tasks.ts"],"names":[],"mappings":"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","sourcesContent":["import { DevWorkspaceError } from '../errors/index.js';\nimport { loadProject, requireWorkspace } from '../core/workspace.js';\nimport { captureScope } from '../verification/scope.js';\nimport { gateContext, loadContract } from './verify.js';\nimport { classifyRequest, WORKFLOWS, type Classification, type WorkflowSpec } from '../tasks/routing.js';\nimport {\n assertNoSecrets,\n createTaskObject,\n evaluateGate,\n nextRequiredStep,\n listTasks,\n loadTask,\n requiredChecksFor,\n saveTask,\n transitionTask,\n type Evidence,\n type GateResult,\n type Task,\n type TaskStatus,\n type TaskType,\n type VerifyStatus,\n TASK_STATUSES,\n TASK_TYPES,\n VERIFY_STATUSES,\n} from '../tasks/task.js';\nimport { isoNow, mutate, type ServiceOptions } from './common.js';\n\nexport interface CreateTaskResult {\n task: Task;\n classification: Classification;\n workflow: WorkflowSpec;\n}\n\nexport async function createTask(o: ServiceOptions, input: { text: string; type?: string; title?: string }): Promise<CreateTaskResult> {\n return mutate(o, async (ws) => {\n const project = await loadProject(ws);\n const classification = classifyRequest(input.text);\n let type: TaskType = classification.type;\n if (input.type) {\n const up = input.type.toUpperCase();\n if (!(TASK_TYPES as readonly string[]).includes(up)) {\n throw new DevWorkspaceError('USAGE', `Unknown task type \"${input.type}\".`, { hint: `Use one of: ${TASK_TYPES.join(', ')}.` });\n }\n type = up as TaskType;\n }\n const contract = await loadContract(ws);\n let verification = requiredChecksFor(type, contract, project.settings.review);\n // Explicit user constraints stay visible: the check is kept but recorded as skipped at the user's request.\n const skipChecks = new Set<string>();\n if (classification.constraints.includes('no-e2e')) skipChecks.add('e2e');\n if (classification.constraints.includes('no-tests')) skipChecks.add('unit').add('integration').add('e2e');\n verification = verification.map((v) => (skipChecks.has(v.check) ? { ...v, status: 'SKIPPED' as const, reason: 'user-override' } : v));\n const title = (input.title ?? input.text).replace(/\\s+/g, ' ').trim().slice(0, 100);\n const task = createTaskObject({ title, type, description: input.text, verification, constraints: classification.constraints }, isoNow(o));\n task.scope = await captureScope(ws.root, isoNow(o));\n await saveTask(ws, task);\n return { task, classification: { ...classification, type }, workflow: WORKFLOWS[type] };\n });\n}\n\nexport async function showTask(o: ServiceOptions, id: string): Promise<{ task: Task; gate: GateResult; next: string }> {\n const ws = await requireWorkspace(o.cwd);\n const task = await loadTask(ws, id);\n const ctx = await gateContext(ws, task);\n return { task, gate: evaluateGate(task, ctx), next: nextRequiredStep(task, ctx) };\n}\n\nexport async function listTasksService(o: ServiceOptions, includeCompleted = false): ReturnType<typeof listTasks> {\n const ws = await requireWorkspace(o.cwd);\n return listTasks(ws, { includeCompleted });\n}\n\nexport interface TaskPatch {\n title?: string;\n summary?: string;\n criteria?: string[];\n plan?: string[];\n areas?: string[];\n decisions?: string[];\n blockers?: string[];\n clearBlockers?: boolean;\n require?: string[];\n}\n\nexport async function updateTask(o: ServiceOptions, id: string, patch: TaskPatch): Promise<Task> {\n return mutate(o, async (ws) => {\n const task = await loadTask(ws, id);\n if (patch.title) task.title = patch.title.trim().slice(0, 200);\n if (patch.summary !== undefined) task.completionSummary = patch.summary.trim().slice(0, 4000);\n task.acceptanceCriteria.push(...(patch.criteria ?? []));\n task.plan.push(...(patch.plan ?? []));\n task.affectedAreas.push(...(patch.areas ?? []).filter((a) => !task.affectedAreas.includes(a)));\n task.decisions.push(...(patch.decisions ?? []));\n if (patch.clearBlockers) task.blockers = [];\n task.blockers.push(...(patch.blockers ?? []));\n for (const check of patch.require ?? []) {\n const existing = task.verification.find((v) => v.check === check);\n if (existing) existing.required = true;\n else task.verification.push({ check, required: true, status: 'PENDING' });\n }\n task.updatedAt = isoNow(o);\n assertNoSecrets(task);\n await saveTask(ws, task);\n return task;\n });\n}\n\nexport async function setTaskStatus(o: ServiceOptions, id: string, status: string, note?: string): Promise<{ task: Task; gate: GateResult | null }> {\n const up = status.toUpperCase();\n if (!(TASK_STATUSES as readonly string[]).includes(up)) {\n throw new DevWorkspaceError('USAGE', `Unknown task status \"${status}\".`, { hint: `Use one of: ${TASK_STATUSES.join(', ')}.` });\n }\n return mutate(o, async (ws) => {\n const task = await loadTask(ws, id);\n const gate = transitionTask(task, up as TaskStatus, isoNow(o), note, up === 'COMPLETED' ? await gateContext(ws, task) : undefined);\n if (up === 'BLOCKED' && note) task.blockers.push(note.slice(0, 300));\n await saveTask(ws, task);\n return { task, gate };\n });\n}\n\nexport async function recordVerification(\n o: ServiceOptions,\n id: string,\n check: string,\n result: string,\n extra: { reason?: string; detail?: string; package?: string } = {},\n): Promise<Task> {\n const status = result.toUpperCase();\n const normalized = status === 'SKIP' ? 'SKIPPED' : status;\n if (!(VERIFY_STATUSES as readonly string[]).includes(normalized)) {\n throw new DevWorkspaceError('USAGE', `Unknown result \"${result}\".`, { hint: 'Use pass, fail, skip or pending.' });\n }\n return mutate(o, async (ws) => {\n const task = await loadTask(ws, id);\n let item = task.verification.find((v) => v.check === check);\n if (!item) {\n item = { check, required: false, status: 'PENDING' };\n task.verification.push(item);\n }\n item.status = normalized as VerifyStatus;\n item.updatedAt = isoNow(o);\n // A reported pass is a claim: DevWorkspace did not see the command run. It is kept as such, per package, and the\n // completion gate of a scoped task only accepts it with an explicit user override.\n if (normalized === 'PASS') {\n const pkg = !extra.package || extra.package === '' ? '.' : extra.package.replace(/\\/+$/, '');\n const claim: Evidence = { kind: 'claimed', at: item.updatedAt, package: pkg, ...(extra.reason === 'user-override' ? { userOverride: true } : {}), ...(extra.detail ? { note: extra.detail.slice(0, 200) } : {}) };\n // Never replace something DevWorkspace observed with somebody's say-so.\n if (!item.evidence?.some((e) => e.package === pkg && e.kind === 'executed')) item.evidence = [...(item.evidence ?? []).filter((e) => e.package !== pkg), claim];\n }\n if (extra.reason) item.reason = extra.reason;\n else delete item.reason;\n if (extra.detail) item.detail = extra.detail.slice(0, 500);\n task.updatedAt = item.updatedAt;\n assertNoSecrets(task);\n await saveTask(ws, task);\n return task;\n });\n}\n\nexport async function recordTests(o: ServiceOptions, id: string, phase: 'baseline' | 'after', passed: number, failed: number): Promise<Task> {\n if (!Number.isInteger(passed) || !Number.isInteger(failed) || passed < 0 || failed < 0) {\n throw new DevWorkspaceError('USAGE', '--passed and --failed must be non-negative integers.');\n }\n return mutate(o, async (ws) => {\n const task = await loadTask(ws, id);\n task.tests[phase] = { passed, failed };\n task.updatedAt = isoNow(o);\n await saveTask(ws, task);\n return task;\n });\n}\n\nexport async function attestTask(\n o: ServiceOptions,\n id: string,\n flags: { userWorkIntact?: boolean; knowledgeReviewed?: boolean; finalStateInspected?: boolean },\n): Promise<Task> {\n return mutate(o, async (ws) => {\n const task = await loadTask(ws, id);\n task.attestations = { ...task.attestations, ...Object.fromEntries(Object.entries(flags).filter(([, v]) => v !== undefined)) };\n task.updatedAt = isoNow(o);\n await saveTask(ws, task);\n return task;\n });\n}\n"]}
@@ -0,0 +1,47 @@
1
+ import { type CommandEntry } from '../config/schemas.js';
2
+ import { type Ws } from '../core/workspace.js';
3
+ import { type GateContext, type Task } from '../tasks/task.js';
4
+ import { type CommandSafety, type TestEnvDeclaration } from '../verification/safety.js';
5
+ import { type ServiceOptions } from './common.js';
6
+ export declare function loadContract(ws: Ws): Promise<CommandEntry[]>;
7
+ /** Contract plus what the task has changed per package: everything the completion gate needs to judge it. */
8
+ export declare function gateContext(ws: Ws, task: Task, contract?: CommandEntry[]): Promise<GateContext>;
9
+ export type RunOutcome = 'passed' | 'failed' | 'refused' | 'not-affected';
10
+ export interface PackageRun {
11
+ package: string;
12
+ command: string | null;
13
+ outcome: RunOutcome;
14
+ exitCode?: number | null;
15
+ durationMs?: number;
16
+ /** Why DevWorkspace did not run it, or the scrubbed tail of a failure. */
17
+ reason?: string;
18
+ safety: CommandSafety['safety'];
19
+ }
20
+ export interface RunCheckResult {
21
+ task: Task;
22
+ check: string;
23
+ runs: PackageRun[];
24
+ }
25
+ export declare function splitCommand(command: string): string[] | null;
26
+ /**
27
+ * PATH lookup on Windows finds the extensionless Unix shell script (`npm`) before `npm.cmd`, and that cannot be
28
+ * spawned. Prefer the Windows launcher next to it. Elsewhere the lookup result is used as is.
29
+ */
30
+ export declare function resolveExecutable(found: string | null, name: string, platform: NodeJS.Platform, exists: (p: string) => Promise<boolean>): Promise<string>;
31
+ /**
32
+ * Run one contract check for the packages the task changed, and record what really happened. DevWorkspace only runs
33
+ * read-only, non-destructive commands; anything that may touch a database runs only after the user declared a test
34
+ * environment for that package and check. Refusals are recorded as "could not run", never as a pass.
35
+ */
36
+ export declare function runTaskCheck(o: ServiceOptions, id: string, check: string, opts?: {
37
+ package?: string;
38
+ timeoutMs?: number;
39
+ }): Promise<RunCheckResult>;
40
+ export declare function declareTestEnv(o: ServiceOptions, input: {
41
+ package: string;
42
+ checks: string[];
43
+ target: string;
44
+ yes: boolean;
45
+ }): Promise<TestEnvDeclaration>;
46
+ export declare function listTestEnv(o: ServiceOptions): Promise<TestEnvDeclaration[]>;
47
+ export declare function revokeTestEnv(o: ServiceOptions, pkg: string): Promise<boolean>;
@@ -0,0 +1,172 @@
1
+ import path from 'node:path';
2
+ import { SCHEMA_VERSION, validateCommands } from '../config/schemas.js';
3
+ import { requireWorkspace } from '../core/workspace.js';
4
+ import { readVersioned } from '../core/versioned.js';
5
+ import { DevWorkspaceError } from '../errors/index.js';
6
+ import { createRunner } from '../integrations/exec.js';
7
+ import { scrub } from '../integrations/manager.js';
8
+ import { loadTask, saveTask, assertNoSecrets } from '../tasks/task.js';
9
+ import { pathExists } from '../utils/fs.js';
10
+ import { classifyCommand, isDeclared, loadTestEnv, readManifestEvidence, assertSafeTarget, removeDeclaration, saveDeclaration } from '../verification/safety.js';
11
+ import { changedSince, scopeByPackage } from '../verification/scope.js';
12
+ import { isoNow, mutate } from './common.js';
13
+ export async function loadContract(ws) {
14
+ if (!(await pathExists(ws.commands)))
15
+ return [];
16
+ return (await readVersioned(ws.commands, 'commands', SCHEMA_VERSION, validateCommands)).data.checks;
17
+ }
18
+ /** Contract plus what the task has changed per package: everything the completion gate needs to judge it. */
19
+ export async function gateContext(ws, task, contract) {
20
+ const checks = contract ?? (await loadContract(ws));
21
+ const scope = task.scope ? scopeByPackage(await changedSince(ws.root, task.scope), checks) : null;
22
+ return { contract: checks, scope };
23
+ }
24
+ /** Anything a shell would interpret. Contract commands are run as an argument vector, never through a shell. */
25
+ const SHELL_SYNTAX = /[&|;<>$`(){}"'\\*?!%^\r\n]/;
26
+ export function splitCommand(command) {
27
+ if (SHELL_SYNTAX.test(command))
28
+ return null;
29
+ const parts = command.trim().split(/\s+/);
30
+ return parts[0] ? parts : null;
31
+ }
32
+ const DEFAULT_TIMEOUT_MS = 10 * 60_000;
33
+ /**
34
+ * PATH lookup on Windows finds the extensionless Unix shell script (`npm`) before `npm.cmd`, and that cannot be
35
+ * spawned. Prefer the Windows launcher next to it. Elsewhere the lookup result is used as is.
36
+ */
37
+ export async function resolveExecutable(found, name, platform, exists) {
38
+ if (!found)
39
+ return name;
40
+ if (platform !== 'win32' || /\.(?:exe|cmd|bat|com)$/i.test(found))
41
+ return found;
42
+ for (const ext of ['.cmd', '.exe', '.bat'])
43
+ if (await exists(found + ext))
44
+ return found + ext;
45
+ return found;
46
+ }
47
+ /**
48
+ * Run one contract check for the packages the task changed, and record what really happened. DevWorkspace only runs
49
+ * read-only, non-destructive commands; anything that may touch a database runs only after the user declared a test
50
+ * environment for that package and check. Refusals are recorded as "could not run", never as a pass.
51
+ */
52
+ export async function runTaskCheck(o, id, check, opts = {}) {
53
+ const ws = await requireWorkspace(o.cwd);
54
+ const task = await loadTask(ws, id);
55
+ const contract = await loadContract(ws);
56
+ const wanted = opts.package ? (opts.package === '.' || opts.package === '' ? '.' : opts.package.replace(/\/+$/, '')) : undefined;
57
+ const entries = contract.filter((c) => c.id === check && c.available && (wanted === undefined || (c.cwd ?? '.') === wanted));
58
+ if (entries.length === 0) {
59
+ throw new DevWorkspaceError('NOT_FOUND', `The testing contract has no "${check}" command${wanted ? ` for package "${wanted}"` : ''}.`, {
60
+ hint: 'See the contract with "devworkspace context", or add the command to .devworkspace/context/commands.json.',
61
+ });
62
+ }
63
+ const scope = (await gateContext(ws, task, contract)).scope;
64
+ const decls = await loadTestEnv(ws);
65
+ const runner = o.runner ?? createRunner(o.env ?? process.env, o.platform);
66
+ const runs = [];
67
+ const evidence = [];
68
+ for (const entry of entries) {
69
+ const pkg = entry.cwd ?? '.';
70
+ const base = { package: pkg, command: entry.command };
71
+ const fingerprint = scope === null ? 'unknown' : scope[pkg];
72
+ if (wanted === undefined && fingerprint === undefined) {
73
+ runs.push({ ...base, outcome: 'not-affected', safety: 'safe', reason: 'the task changed nothing in this package' });
74
+ continue;
75
+ }
76
+ const manifest = await readManifestEvidence(ws.root, entry);
77
+ const cls = classifyCommand(entry, manifest);
78
+ if (cls.safety === 'blocked') {
79
+ runs.push({ ...base, outcome: 'refused', safety: cls.safety, reason: `refused: ${cls.reasons.join('; ')}` });
80
+ continue;
81
+ }
82
+ if (cls.safety === 'needs-test-environment' && !isDeclared(decls, pkg, check)) {
83
+ runs.push({
84
+ ...base,
85
+ outcome: 'refused',
86
+ safety: cls.safety,
87
+ reason: `refused: ${cls.reasons.join('; ')}. Run it yourself against a test environment you control, then declare it with: devworkspace testenv declare --package ${pkg} --check ${check} --target "<description>" --yes`,
88
+ });
89
+ continue;
90
+ }
91
+ const argv = splitCommand(entry.command ?? '');
92
+ if (!argv) {
93
+ runs.push({ ...base, outcome: 'refused', safety: cls.safety, reason: 'refused: the command uses shell syntax, and DevWorkspace never runs a shell' });
94
+ continue;
95
+ }
96
+ const exe = await resolveExecutable(await runner.which(argv[0]), argv[0], o.platform ?? process.platform, pathExists);
97
+ const started = Date.now();
98
+ const r = await runner.run({ command: exe, args: argv.slice(1), cwd: pkg === '.' ? ws.root : path.join(ws.root, ...pkg.split('/')), timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS });
99
+ const durationMs = Date.now() - started;
100
+ if (r.code === null && !r.timedOut && r.error) {
101
+ // The command never started (missing executable, bad cwd): that is "could not run", not a test failure.
102
+ runs.push({ ...base, outcome: 'refused', safety: cls.safety, reason: `could not start: ${scrub(r.error)}` });
103
+ continue;
104
+ }
105
+ const passed = r.ok && r.code === 0;
106
+ const tail = passed ? undefined : scrub((r.error ?? '') || (r.timedOut ? 'timed out' : '') || r.stderr.slice(-300) || r.stdout.slice(-300) || `exit ${r.code}`);
107
+ runs.push({ ...base, outcome: passed ? 'passed' : 'failed', safety: cls.safety, exitCode: r.code, durationMs, ...(tail ? { reason: tail } : {}) });
108
+ evidence.push({ kind: 'executed', at: isoNow(o), package: pkg, command: entry.command ?? undefined, exitCode: r.code, durationMs, scope: fingerprint ?? 'unknown', ...(tail ? { note: tail } : {}) });
109
+ }
110
+ const updated = await mutate(o, async (w) => {
111
+ const t = await loadTask(w, id);
112
+ let item = t.verification.find((v) => v.check === check);
113
+ if (!item) {
114
+ item = { check, required: false, status: 'PENDING' };
115
+ t.verification.push(item);
116
+ }
117
+ for (const e of evidence)
118
+ item.evidence = [...(item.evidence ?? []).filter((x) => x.package !== e.package), e];
119
+ item.status = summarize(item, runs);
120
+ const refused = runs.filter((r) => r.outcome === 'refused');
121
+ if (item.status === 'COULD_NOT_RUN')
122
+ item.detail = refused.map((r) => `${r.package}: ${r.reason}`).join(' | ').slice(0, 500);
123
+ else
124
+ delete item.detail;
125
+ delete item.reason;
126
+ item.updatedAt = isoNow(o);
127
+ t.updatedAt = item.updatedAt;
128
+ assertNoSecrets(t);
129
+ await saveTask(w, t);
130
+ return t;
131
+ });
132
+ return { task: updated, check, runs };
133
+ }
134
+ function summarize(item, runs) {
135
+ if (runs.some((r) => r.outcome === 'failed'))
136
+ return 'FAIL';
137
+ if (runs.some((r) => r.outcome === 'refused'))
138
+ return 'COULD_NOT_RUN';
139
+ if (runs.some((r) => r.outcome === 'passed'))
140
+ return 'PASS';
141
+ return item.status;
142
+ }
143
+ // ---------- declared test environments ----------
144
+ export async function declareTestEnv(o, input) {
145
+ if (!input.yes) {
146
+ throw new DevWorkspaceError('USAGE', 'Declaring a test environment is a statement that running these checks there is safe; confirm with --yes.', {
147
+ hint: 'DevWorkspace cannot verify a database target (it never reads connection settings). This records your assertion, locally, and nothing else.',
148
+ });
149
+ }
150
+ assertSafeTarget(input.target);
151
+ const ws = await requireWorkspace(o.cwd);
152
+ const pkg = input.package === '' ? '.' : input.package.replace(/\/+$/, '');
153
+ const contract = await loadContract(ws);
154
+ if (!contract.some((c) => c.available && (c.cwd ?? '.') === pkg)) {
155
+ throw new DevWorkspaceError('NOT_FOUND', `The testing contract has no commands for package "${pkg}".`, { hint: 'Use a package directory shown by "devworkspace context".' });
156
+ }
157
+ const allowed = new Set(['unit', 'integration', 'e2e', 'security']);
158
+ const bad = input.checks.filter((c) => !allowed.has(c));
159
+ if (input.checks.length === 0 || bad.length > 0) {
160
+ throw new DevWorkspaceError('USAGE', `--check must be one or more of: ${[...allowed].join(', ')}.`, { hint: 'Migrations, seeds and resets are never run by DevWorkspace.' });
161
+ }
162
+ const decl = { package: pkg, checks: [...new Set(input.checks)].sort(), target: input.target.trim(), declaredAt: isoNow(o) };
163
+ await mutate(o, async (w) => saveDeclaration(w, decl));
164
+ return decl;
165
+ }
166
+ export async function listTestEnv(o) {
167
+ return loadTestEnv(await requireWorkspace(o.cwd));
168
+ }
169
+ export async function revokeTestEnv(o, pkg) {
170
+ return mutate(o, async (w) => removeDeclaration(w, pkg === '' ? '.' : pkg.replace(/\/+$/, '')));
171
+ }
172
+ //# sourceMappingURL=verify.js.map