devworkspace 0.2.2 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/README.md +5 -4
- package/dist/agents/claude/adapter.d.ts +3 -14
- package/dist/agents/claude/adapter.js +46 -49
- package/dist/agents/claude/adapter.js.map +1 -1
- package/dist/agents/claude/content.js +1 -1
- package/dist/agents/claude/content.js.map +1 -1
- package/dist/agents/codex/adapter.d.ts +2 -0
- package/dist/agents/codex/adapter.js +61 -0
- package/dist/agents/codex/adapter.js.map +1 -0
- package/dist/agents/copilot/adapter.d.ts +2 -0
- package/dist/agents/copilot/adapter.js +62 -0
- package/dist/agents/copilot/adapter.js.map +1 -0
- package/dist/agents/cursor/adapter.d.ts +2 -0
- package/dist/agents/cursor/adapter.js +127 -0
- package/dist/agents/cursor/adapter.js.map +1 -0
- package/dist/agents/index.d.ts +15 -0
- package/dist/agents/index.js +54 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/shared.d.ts +20 -0
- package/dist/agents/shared.js +76 -0
- package/dist/agents/shared.js.map +1 -0
- package/dist/agents/types.d.ts +16 -1
- package/dist/agents/types.js.map +1 -1
- package/dist/cli/args.d.ts +3 -0
- package/dist/cli/args.js +1 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/help.js +15 -8
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/render.d.ts +4 -1
- package/dist/cli/render.js +46 -0
- package/dist/cli/render.js.map +1 -1
- package/dist/cli/run.js +24 -6
- package/dist/cli/run.js.map +1 -1
- package/dist/discovery/cache.d.ts +19 -0
- package/dist/discovery/cache.js +50 -0
- package/dist/discovery/cache.js.map +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/adapters/claudePlugin.d.ts +12 -0
- package/dist/integrations/adapters/claudePlugin.js +48 -10
- package/dist/integrations/adapters/claudePlugin.js.map +1 -1
- package/dist/integrations/adapters/pluginSkill.js +5 -0
- package/dist/integrations/adapters/pluginSkill.js.map +1 -1
- package/dist/security/files.js +10 -2
- package/dist/security/files.js.map +1 -1
- package/dist/security/redact.js +1 -1
- package/dist/security/redact.js.map +1 -1
- package/dist/services/agent.d.ts +12 -2
- package/dist/services/agent.js +96 -14
- package/dist/services/agent.js.map +1 -1
- package/dist/services/analysis.d.ts +7 -1
- package/dist/services/analysis.js +14 -7
- package/dist/services/analysis.js.map +1 -1
- package/dist/services/doctor.js +31 -14
- package/dist/services/doctor.js.map +1 -1
- package/dist/services/refresh.js +1 -1
- package/dist/services/refresh.js.map +1 -1
- package/dist/services/reset.js +45 -5
- package/dist/services/reset.js.map +1 -1
- package/dist/services/status.d.ts +2 -0
- package/dist/services/status.js +62 -10
- package/dist/services/status.js.map +1 -1
- package/dist/services/taskContext.d.ts +59 -0
- package/dist/services/taskContext.js +164 -0
- package/dist/services/taskContext.js.map +1 -0
- package/dist/services/tasks.js +6 -1
- package/dist/services/tasks.js.map +1 -1
- package/dist/tasks/routing.js +16 -10
- package/dist/tasks/routing.js.map +1 -1
- package/dist/tasks/task.js +6 -2
- package/dist/tasks/task.js.map +1 -1
- package/dist/verification/scope.js +1 -1
- package/dist/verification/scope.js.map +1 -1
- package/docs/agents.md +60 -0
- package/docs/architecture.md +2 -2
- package/docs/benchmarking.md +19 -0
- package/docs/knowledge.md +17 -0
- package/package.json +1 -1
package/dist/services/refresh.js
CHANGED
|
@@ -16,7 +16,7 @@ export async function refreshWorkspace(o) {
|
|
|
16
16
|
return mutate(o, async (ws) => {
|
|
17
17
|
const project = await loadProject(ws);
|
|
18
18
|
const now = isoNow(o);
|
|
19
|
-
const run = await runDiscovery(ws, project.name, project.settings.maxFiles);
|
|
19
|
+
const run = await runDiscovery(ws, project.name, project.settings.maxFiles, { writeCache: !o.dryRun });
|
|
20
20
|
let previous = null;
|
|
21
21
|
if (await pathExists(ws.analysisState)) {
|
|
22
22
|
previous = (await readVersioned(ws.analysisState, 'analysis', SCHEMA_VERSION, validateAnalysisState)).data;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refresh.js","sourceRoot":"","sources":["../../src/services/refresh.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAsB,MAAM,sBAAsB,CAAC;AACjG,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAChG,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAChG,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,MAAM,EAAE,MAAM,EAAuB,MAAM,aAAa,CAAC;AAYlE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,CAAiB;IACtD,OAAO,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE;QAC5B,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,EAAE,CAAC,CAAC;QACtC,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACtB,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;
|
|
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, { writeCache: !o.dryRun });\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"]}
|
package/dist/services/reset.js
CHANGED
|
@@ -3,7 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { removeManagedSection } from '../agents/claude/adapter.js';
|
|
4
4
|
import { SKILL_MARKER, SKILL_PATH } from '../agents/claude/content.js';
|
|
5
5
|
import { DevWorkspaceError } from '../errors/index.js';
|
|
6
|
-
import { findWorkspaceRoot, WORKSPACE_DIR, wsPaths } from '../core/workspace.js';
|
|
6
|
+
import { findWorkspaceRoot, loadProject, WORKSPACE_DIR, wsPaths } from '../core/workspace.js';
|
|
7
7
|
import { atomicWriteFile, pathExists } from '../utils/fs.js';
|
|
8
8
|
import { isInside } from '../utils/paths.js';
|
|
9
9
|
import { withLock } from '../utils/lock.js';
|
|
@@ -45,7 +45,27 @@ export async function resetWorkspace(o) {
|
|
|
45
45
|
];
|
|
46
46
|
const pending = [];
|
|
47
47
|
if (o.includeAgentFiles) {
|
|
48
|
-
|
|
48
|
+
const candidateFiles = [
|
|
49
|
+
'CLAUDE.md',
|
|
50
|
+
'.claude/CLAUDE.md',
|
|
51
|
+
'AGENTS.md',
|
|
52
|
+
'.cursorrules',
|
|
53
|
+
'.cursor/rules/devworkspace.mdc',
|
|
54
|
+
'.github/copilot-instructions.md',
|
|
55
|
+
];
|
|
56
|
+
try {
|
|
57
|
+
const p = await loadProject(ws);
|
|
58
|
+
for (const m of p.managedFiles) {
|
|
59
|
+
if (!candidateFiles.includes(m.path))
|
|
60
|
+
candidateFiles.push(m.path);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
/* project.json might be missing */
|
|
65
|
+
}
|
|
66
|
+
for (const rel of candidateFiles) {
|
|
67
|
+
if (rel === SKILL_PATH)
|
|
68
|
+
continue;
|
|
49
69
|
const full = path.join(root, ...rel.split('/'));
|
|
50
70
|
const text = await fs.readFile(full, 'utf8').catch(() => null);
|
|
51
71
|
if (text === null)
|
|
@@ -57,9 +77,29 @@ export async function resetWorkspace(o) {
|
|
|
57
77
|
}
|
|
58
78
|
continue;
|
|
59
79
|
}
|
|
60
|
-
if
|
|
80
|
+
// Check if remaining content is empty or contains only cursor rule frontmatter
|
|
81
|
+
const cleanContent = removed.content
|
|
82
|
+
.replace(/^---\r?\ndescription:[^\r\n]*\r?\nalwaysApply:[^\r\n]*\r?\n---\r?\n?/m, '')
|
|
83
|
+
.trim();
|
|
84
|
+
const isEmpty = removed.empty || cleanContent.length === 0;
|
|
85
|
+
if (isEmpty) {
|
|
61
86
|
actions.push({ path: rel, action: 'delete-file', note: 'contained only the DevWorkspace block' });
|
|
62
|
-
pending.push(() =>
|
|
87
|
+
pending.push(async () => {
|
|
88
|
+
await fs.rm(full, { force: true });
|
|
89
|
+
const parent = path.dirname(full);
|
|
90
|
+
if (parent !== root) {
|
|
91
|
+
const siblings = await fs.readdir(parent).catch(() => []);
|
|
92
|
+
if (siblings.length === 0) {
|
|
93
|
+
await fs.rmdir(parent).catch(() => undefined);
|
|
94
|
+
const grand = path.dirname(parent);
|
|
95
|
+
if (grand !== root) {
|
|
96
|
+
const grandSiblings = await fs.readdir(grand).catch(() => []);
|
|
97
|
+
if (grandSiblings.length === 0)
|
|
98
|
+
await fs.rmdir(grand).catch(() => undefined);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
});
|
|
63
103
|
}
|
|
64
104
|
else {
|
|
65
105
|
actions.push({ path: rel, action: 'remove-managed-section', note: 'your own content is kept' });
|
|
@@ -71,7 +111,7 @@ export async function resetWorkspace(o) {
|
|
|
71
111
|
if (skill !== null) {
|
|
72
112
|
if (skill.includes(SKILL_MARKER)) {
|
|
73
113
|
const dir = path.dirname(skillFile);
|
|
74
|
-
const others = (await fs.readdir(dir)).filter((n) => n !== 'SKILL.md');
|
|
114
|
+
const others = (await fs.readdir(dir).catch(() => [])).filter((n) => n !== 'SKILL.md');
|
|
75
115
|
actions.push({ path: SKILL_PATH, action: 'delete-file', note: 'DevWorkspace-managed skill' });
|
|
76
116
|
pending.push(async () => {
|
|
77
117
|
await fs.rm(skillFile, { force: true });
|
|
@@ -1 +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"]}
|
|
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, loadProject, 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 const candidateFiles = [\n 'CLAUDE.md',\n '.claude/CLAUDE.md',\n 'AGENTS.md',\n '.cursorrules',\n '.cursor/rules/devworkspace.mdc',\n '.github/copilot-instructions.md',\n ];\n try {\n const p = await loadProject(ws);\n for (const m of p.managedFiles) {\n if (!candidateFiles.includes(m.path)) candidateFiles.push(m.path);\n }\n } catch {\n /* project.json might be missing */\n }\n\n for (const rel of candidateFiles) {\n if (rel === SKILL_PATH) continue;\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 // Check if remaining content is empty or contains only cursor rule frontmatter\n const cleanContent = removed.content\n .replace(/^---\\r?\\ndescription:[^\\r\\n]*\\r?\\nalwaysApply:[^\\r\\n]*\\r?\\n---\\r?\\n?/m, '')\n .trim();\n const isEmpty = removed.empty || cleanContent.length === 0;\n\n if (isEmpty) {\n actions.push({ path: rel, action: 'delete-file', note: 'contained only the DevWorkspace block' });\n pending.push(async () => {\n await fs.rm(full, { force: true });\n const parent = path.dirname(full);\n if (parent !== root) {\n const siblings = await fs.readdir(parent).catch(() => []);\n if (siblings.length === 0) {\n await fs.rmdir(parent).catch(() => undefined);\n const grand = path.dirname(parent);\n if (grand !== root) {\n const grandSiblings = await fs.readdir(grand).catch(() => []);\n if (grandSiblings.length === 0) await fs.rmdir(grand).catch(() => undefined);\n }\n }\n }\n });\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).catch(() => [])).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"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Ws } from '../core/workspace.js';
|
|
2
2
|
import { type IntegrationReport } from '../integrations/index.js';
|
|
3
|
+
import type { AgentAdapter } from '../agents/types.js';
|
|
3
4
|
import { type ServiceOptions } from './common.js';
|
|
4
5
|
export interface TaskBrief {
|
|
5
6
|
id: string;
|
|
@@ -60,6 +61,7 @@ export interface StatusResult {
|
|
|
60
61
|
} | null;
|
|
61
62
|
corruptTasks: number;
|
|
62
63
|
}
|
|
64
|
+
export declare function detectInstalledAgents(ws: Ws): Promise<AgentAdapter[]>;
|
|
63
65
|
export declare function instructionsInstalled(ws: Ws): Promise<boolean>;
|
|
64
66
|
export declare function knowledgeWritten(ws: Ws): Promise<boolean>;
|
|
65
67
|
/** Cheap overview: small workspace files, Git status and a fingerprint check of the few files facts cite. Never scans the repository. */
|
package/dist/services/status.js
CHANGED
|
@@ -9,22 +9,74 @@ 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
|
-
import { SECTION_END } from '../agents/
|
|
12
|
+
import { SECTION_START, SECTION_END } from '../agents/shared.js';
|
|
13
13
|
import { SKILL_PATH } from '../agents/claude/content.js';
|
|
14
|
+
import { listAgentAdapters } from '../agents/index.js';
|
|
14
15
|
import { isoNow, managerEnv } from './common.js';
|
|
15
16
|
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
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
export async function detectInstalledAgents(ws) {
|
|
18
|
+
const installed = [];
|
|
19
|
+
for (const adapter of listAgentAdapters()) {
|
|
20
|
+
if (adapter.id === 'claude') {
|
|
21
|
+
let claudeFound = false;
|
|
22
|
+
for (const rel of ['CLAUDE.md', '.claude/CLAUDE.md']) {
|
|
23
|
+
try {
|
|
24
|
+
const text = await fs.readFile(path.join(ws.root, ...rel.split('/')), 'utf8');
|
|
25
|
+
if (text.includes(SECTION_START) && text.includes(SECTION_END) && (await pathExists(path.join(ws.root, ...SKILL_PATH.split('/'))))) {
|
|
26
|
+
claudeFound = true;
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
/* try next */
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (claudeFound)
|
|
35
|
+
installed.push(adapter);
|
|
36
|
+
}
|
|
37
|
+
else if (adapter.id === 'codex') {
|
|
38
|
+
try {
|
|
39
|
+
const text = await fs.readFile(path.join(ws.root, 'AGENTS.md'), 'utf8');
|
|
40
|
+
if (text.includes(SECTION_START) && text.includes(SECTION_END))
|
|
41
|
+
installed.push(adapter);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
/* not found */
|
|
45
|
+
}
|
|
22
46
|
}
|
|
23
|
-
|
|
24
|
-
|
|
47
|
+
else if (adapter.id === 'cursor') {
|
|
48
|
+
let cursorFound = false;
|
|
49
|
+
for (const rel of ['.cursor/rules/devworkspace.mdc', '.cursorrules']) {
|
|
50
|
+
try {
|
|
51
|
+
const text = await fs.readFile(path.join(ws.root, ...rel.split('/')), 'utf8');
|
|
52
|
+
if (text.includes(SECTION_START) && text.includes(SECTION_END)) {
|
|
53
|
+
cursorFound = true;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
/* try next */
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (cursorFound)
|
|
62
|
+
installed.push(adapter);
|
|
63
|
+
}
|
|
64
|
+
else if (adapter.id === 'copilot') {
|
|
65
|
+
try {
|
|
66
|
+
const text = await fs.readFile(path.join(ws.root, '.github', 'copilot-instructions.md'), 'utf8');
|
|
67
|
+
if (text.includes(SECTION_START) && text.includes(SECTION_END))
|
|
68
|
+
installed.push(adapter);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
/* not found */
|
|
72
|
+
}
|
|
25
73
|
}
|
|
26
74
|
}
|
|
27
|
-
return
|
|
75
|
+
return installed;
|
|
76
|
+
}
|
|
77
|
+
export async function instructionsInstalled(ws) {
|
|
78
|
+
const installed = await detectInstalledAgents(ws);
|
|
79
|
+
return installed.length > 0;
|
|
28
80
|
}
|
|
29
81
|
export async function knowledgeWritten(ws) {
|
|
30
82
|
try {
|
|
@@ -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 { 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"]}
|
|
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 { 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_START, SECTION_END } from '../agents/shared.js';\nimport { SKILL_PATH } from '../agents/claude/content.js';\nimport { listAgentAdapters } from '../agents/index.js';\nimport type { AgentAdapter } from '../agents/types.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 detectInstalledAgents(ws: Ws): Promise<AgentAdapter[]> {\n const installed: AgentAdapter[] = [];\n for (const adapter of listAgentAdapters()) {\n if (adapter.id === 'claude') {\n let claudeFound = false;\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(SECTION_START) && text.includes(SECTION_END) && (await pathExists(path.join(ws.root, ...SKILL_PATH.split('/'))))) {\n claudeFound = true;\n break;\n }\n } catch {\n /* try next */\n }\n }\n if (claudeFound) installed.push(adapter);\n } else if (adapter.id === 'codex') {\n try {\n const text = await fs.readFile(path.join(ws.root, 'AGENTS.md'), 'utf8');\n if (text.includes(SECTION_START) && text.includes(SECTION_END)) installed.push(adapter);\n } catch {\n /* not found */\n }\n } else if (adapter.id === 'cursor') {\n let cursorFound = false;\n for (const rel of ['.cursor/rules/devworkspace.mdc', '.cursorrules']) {\n try {\n const text = await fs.readFile(path.join(ws.root, ...rel.split('/')), 'utf8');\n if (text.includes(SECTION_START) && text.includes(SECTION_END)) {\n cursorFound = true;\n break;\n }\n } catch {\n /* try next */\n }\n }\n if (cursorFound) installed.push(adapter);\n } else if (adapter.id === 'copilot') {\n try {\n const text = await fs.readFile(path.join(ws.root, '.github', 'copilot-instructions.md'), 'utf8');\n if (text.includes(SECTION_START) && text.includes(SECTION_END)) installed.push(adapter);\n } catch {\n /* not found */\n }\n }\n }\n return installed;\n}\n\nexport async function instructionsInstalled(ws: Ws): Promise<boolean> {\n const installed = await detectInstalledAgents(ws);\n return installed.length > 0;\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"]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type CommandEntry, type Fact } from '../config/schemas.js';
|
|
2
|
+
import type { TaskType } from '../tasks/task.js';
|
|
3
|
+
import { type ContextResult } from './knowledge.js';
|
|
4
|
+
import type { ServiceOptions } from './common.js';
|
|
5
|
+
export interface ContextItem {
|
|
6
|
+
kind: 'fact' | 'check' | 'decision' | 'document' | 'rule';
|
|
7
|
+
id: string;
|
|
8
|
+
/** Why this item is in the context; every item carries one. */
|
|
9
|
+
reason: string;
|
|
10
|
+
}
|
|
11
|
+
export interface TaskContextResult {
|
|
12
|
+
project: {
|
|
13
|
+
name: string;
|
|
14
|
+
id: string;
|
|
15
|
+
};
|
|
16
|
+
request: string;
|
|
17
|
+
type: TaskType;
|
|
18
|
+
confidence: 'high' | 'low';
|
|
19
|
+
readOnly: boolean;
|
|
20
|
+
/** `clipped` = characters left out of a long detector-derived value; the full value is in facts.json. */
|
|
21
|
+
facts: (Fact & {
|
|
22
|
+
clipped?: number;
|
|
23
|
+
})[];
|
|
24
|
+
/** Detector facts folded into another fact with the identical value; nothing is deleted from facts.json. */
|
|
25
|
+
aliases: {
|
|
26
|
+
key: string;
|
|
27
|
+
sameAs: string;
|
|
28
|
+
}[];
|
|
29
|
+
checks: CommandEntry[];
|
|
30
|
+
safety: ContextResult['safety'];
|
|
31
|
+
rules: string[];
|
|
32
|
+
decisions: {
|
|
33
|
+
number: number;
|
|
34
|
+
title: string;
|
|
35
|
+
status: string;
|
|
36
|
+
file: string;
|
|
37
|
+
}[];
|
|
38
|
+
/** Detailed documents are referenced by name, never embedded. */
|
|
39
|
+
documents: {
|
|
40
|
+
name: string;
|
|
41
|
+
bytes: number;
|
|
42
|
+
}[];
|
|
43
|
+
packages: string[];
|
|
44
|
+
included: ContextItem[];
|
|
45
|
+
counts: {
|
|
46
|
+
facts: [number, number];
|
|
47
|
+
checks: [number, number];
|
|
48
|
+
decisions: [number, number];
|
|
49
|
+
documents: [number, number];
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Task-aware context. Selection is deterministic (sorted inputs, no clocks, no randomness) and rule-based: the request is
|
|
54
|
+
* classified, its keywords select facts/decisions/packages, and the type decides which broader groups come along.
|
|
55
|
+
* Safety rules are included for every type; the per-package "not run automatically" list whenever checks are included.
|
|
56
|
+
*/
|
|
57
|
+
export declare function getTaskContext(o: ServiceOptions, opts: {
|
|
58
|
+
request: string;
|
|
59
|
+
}): Promise<TaskContextResult>;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { SCHEMA_VERSION, validateCommands } from '../config/schemas.js';
|
|
4
|
+
import { loadProject, requireWorkspace } from '../core/workspace.js';
|
|
5
|
+
import { readVersioned } from '../core/versioned.js';
|
|
6
|
+
import { listDecisions } from '../decisions/decision.js';
|
|
7
|
+
import { loadFacts } from '../knowledge/facts.js';
|
|
8
|
+
import { classifyRequest } from '../tasks/routing.js';
|
|
9
|
+
import { pathExists } from '../utils/fs.js';
|
|
10
|
+
import { classifyCommand, readManifestEvidence } from '../verification/safety.js';
|
|
11
|
+
import { dirtyFiles, packageDirs } from '../verification/scope.js';
|
|
12
|
+
import { SAFETY_RULES } from './knowledge.js';
|
|
13
|
+
const STOP = new Set(['the', 'and', 'for', 'with', 'that', 'this', 'from', 'into', 'does', 'how', 'what', 'where', 'why', 'when', 'which', 'fix', 'add', 'bug', 'make', 'new', 'can', 'are', 'not', 'our', 'all', 'use', 'get', 'set']);
|
|
14
|
+
const CLIP = 400;
|
|
15
|
+
const SECURITY_WORDS = /auth|tenant|permission|rbac|security|isolation|role|session|ownership/i;
|
|
16
|
+
const ISSUE_KEY = /^(?:issue|limit|known|bug|risk)/i;
|
|
17
|
+
/** Facts that describe the stack and layout; implementation-style tasks need them, a lookup question does not. */
|
|
18
|
+
const STACK_KEY = /^(?:stack\.|repo\.packages|repo\.monorepo|repo\.entry-points|convention|style|arch|pattern|testing)/i;
|
|
19
|
+
const BROAD_TYPES = ['FEATURE', 'REFACTOR', 'ARCHITECTURE', 'TESTING', 'PERFORMANCE', 'MAINTENANCE', 'RELEASE'];
|
|
20
|
+
const words = (text) => [...new Set((text.toLowerCase().match(/[a-z0-9_][a-z0-9_.-]{2,}/g) ?? []).filter((w) => !STOP.has(w)))].sort();
|
|
21
|
+
const norm = (s) => s.toLowerCase().replace(/\s+/g, ' ').trim();
|
|
22
|
+
/** Which context documents are worth pointing at for a task type. */
|
|
23
|
+
const DOCS_BY_TYPE = {
|
|
24
|
+
ARCHITECTURE: ['architecture.md', 'integrations.md'],
|
|
25
|
+
REFACTOR: ['architecture.md', 'conventions.md'],
|
|
26
|
+
FEATURE: ['architecture.md', 'conventions.md'],
|
|
27
|
+
BUG: ['testing.md'],
|
|
28
|
+
TESTING: ['testing.md'],
|
|
29
|
+
SECURITY: ['testing.md', 'architecture.md'],
|
|
30
|
+
PERFORMANCE: ['architecture.md'],
|
|
31
|
+
RELEASE: ['testing.md'],
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Task-aware context. Selection is deterministic (sorted inputs, no clocks, no randomness) and rule-based: the request is
|
|
35
|
+
* classified, its keywords select facts/decisions/packages, and the type decides which broader groups come along.
|
|
36
|
+
* Safety rules are included for every type; the per-package "not run automatically" list whenever checks are included.
|
|
37
|
+
*/
|
|
38
|
+
export async function getTaskContext(o, opts) {
|
|
39
|
+
const ws = await requireWorkspace(o.cwd);
|
|
40
|
+
const project = await loadProject(ws);
|
|
41
|
+
const cls = classifyRequest(opts.request);
|
|
42
|
+
const type = cls.type;
|
|
43
|
+
const kw = words(opts.request);
|
|
44
|
+
const included = [];
|
|
45
|
+
const hit = (text) => kw.filter((w) => text.includes(w));
|
|
46
|
+
// Facts
|
|
47
|
+
const all = (await loadFacts(ws)).facts;
|
|
48
|
+
const picked = new Map();
|
|
49
|
+
for (const f of all) {
|
|
50
|
+
const m = hit(`${f.key} ${f.value} ${f.source}`.toLowerCase());
|
|
51
|
+
if (m.length > 0)
|
|
52
|
+
picked.set(f.key, `matches "${m.slice(0, 3).join('", "')}"`);
|
|
53
|
+
}
|
|
54
|
+
const group = (re, why, also) => {
|
|
55
|
+
for (const f of all)
|
|
56
|
+
if (!picked.has(f.key) && (re.test(f.key) || also?.(f)))
|
|
57
|
+
picked.set(f.key, why);
|
|
58
|
+
};
|
|
59
|
+
if (type !== 'QUESTION' && type !== 'DOCUMENTATION')
|
|
60
|
+
group(/^repo\.(?:packages|entry-points)$/, `package layout (${type})`);
|
|
61
|
+
if (type === 'BUG' || type === 'SECURITY')
|
|
62
|
+
group(ISSUE_KEY, 'known issue / limitation', (f) => f.status === 'STALE' && f.sourceType !== 'detector');
|
|
63
|
+
if (BROAD_TYPES.includes(type))
|
|
64
|
+
group(STACK_KEY, `stack and conventions (${type})`);
|
|
65
|
+
if (type === 'SECURITY')
|
|
66
|
+
group(/^$/, 'security / authorization rule', (f) => SECURITY_WORDS.test(`${f.key} ${f.value}`));
|
|
67
|
+
if (type === 'DOCUMENTATION')
|
|
68
|
+
group(/^repo\.docs$/, 'documentation index');
|
|
69
|
+
const selected = all.filter((f) => picked.has(f.key));
|
|
70
|
+
// Compaction: a detector fact whose value equals another selected fact's is folded into it. Human-authored facts are never folded or clipped.
|
|
71
|
+
const aliases = [];
|
|
72
|
+
const owners = new Map();
|
|
73
|
+
for (const f of [...selected].sort((a, b) => Number(a.sourceType === 'detector') - Number(b.sourceType === 'detector') || a.key.localeCompare(b.key))) {
|
|
74
|
+
if (!owners.has(norm(f.value)))
|
|
75
|
+
owners.set(norm(f.value), f.key);
|
|
76
|
+
}
|
|
77
|
+
const facts = [];
|
|
78
|
+
for (const f of [...selected].sort((a, b) => a.key.localeCompare(b.key))) {
|
|
79
|
+
const owner = owners.get(norm(f.value));
|
|
80
|
+
if (owner !== f.key && f.sourceType === 'detector') {
|
|
81
|
+
aliases.push({ key: f.key, sameAs: owner });
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const clip = f.sourceType === 'detector' && f.value.length > CLIP;
|
|
85
|
+
facts.push(clip ? { ...f, value: f.value.slice(0, CLIP), clipped: f.value.length - CLIP } : f);
|
|
86
|
+
included.push({ kind: 'fact', id: f.key, reason: picked.get(f.key) });
|
|
87
|
+
}
|
|
88
|
+
// Packages and the testing contract
|
|
89
|
+
let allChecks = [];
|
|
90
|
+
if (await pathExists(ws.commands))
|
|
91
|
+
allChecks = (await readVersioned(ws.commands, 'commands', SCHEMA_VERSION, validateCommands)).data.checks;
|
|
92
|
+
const pkgReason = new Map();
|
|
93
|
+
for (const d of packageDirs(allChecks)) {
|
|
94
|
+
const m = hit(d.toLowerCase());
|
|
95
|
+
if (m.length > 0)
|
|
96
|
+
pkgReason.set(d, `request names "${m[0]}"`);
|
|
97
|
+
}
|
|
98
|
+
// dirtyFiles already leaves out documentation and tool-managed files, so a docs-only change selects no package.
|
|
99
|
+
// Only worth the Git calls when the request did not already pick a package and there is more than one to choose from.
|
|
100
|
+
const dirty = type !== 'QUESTION' && type !== 'DOCUMENTATION' && pkgReason.size === 0 && packageDirs(allChecks).length > 1 ? await dirtyFiles(ws.root) : null;
|
|
101
|
+
for (const f of Object.keys(dirty?.files ?? {})) {
|
|
102
|
+
const d = packageDirs(allChecks).find((x) => f === x || f.startsWith(`${x}/`));
|
|
103
|
+
if (d && !pkgReason.has(d))
|
|
104
|
+
pkgReason.set(d, 'has uncommitted code changes');
|
|
105
|
+
}
|
|
106
|
+
const usable = allChecks.filter((c) => c.available);
|
|
107
|
+
const scoped = pkgReason.size > 0 ? usable.filter((c) => !c.cwd || c.cwd === '.' || pkgReason.has(c.cwd)) : usable;
|
|
108
|
+
const checks = type === 'QUESTION' || type === 'DOCUMENTATION' ? [] : scoped;
|
|
109
|
+
for (const c of checks)
|
|
110
|
+
included.push({ kind: 'check', id: `${c.cwd ?? '.'}:${c.id}`, reason: c.cwd && pkgReason.has(c.cwd) ? `package: ${pkgReason.get(c.cwd)}` : `testing contract (${type})` });
|
|
111
|
+
// Commands that must not run unguarded stay visible whenever their package's checks are in play.
|
|
112
|
+
const safety = [];
|
|
113
|
+
for (const c of checks) {
|
|
114
|
+
const s = classifyCommand(c, await readManifestEvidence(ws.root, c));
|
|
115
|
+
if (s.safety !== 'safe')
|
|
116
|
+
safety.push({ package: c.cwd ?? '.', check: c.id, command: c.command, safety: s.safety, reasons: s.reasons });
|
|
117
|
+
}
|
|
118
|
+
SAFETY_RULES.forEach((_, i) => included.push({ kind: 'rule', id: `safety-${i + 1}`, reason: 'safety rule, included in every context' }));
|
|
119
|
+
// Decisions: titles and file references only
|
|
120
|
+
const decs = (await listDecisions(ws)).decisions;
|
|
121
|
+
const wantAll = type === 'ARCHITECTURE' || type === 'SECURITY';
|
|
122
|
+
const decisions = [];
|
|
123
|
+
for (const d of decs) {
|
|
124
|
+
const m = hit(`${d.title} ${d.decision} ${d.context}`.toLowerCase());
|
|
125
|
+
if (d.status === 'superseded' && m.length === 0)
|
|
126
|
+
continue;
|
|
127
|
+
if (m.length > 0 || wantAll) {
|
|
128
|
+
decisions.push({ number: d.number, title: d.title, status: d.status, file: d.file });
|
|
129
|
+
included.push({ kind: 'decision', id: String(d.number).padStart(4, '0'), reason: m.length > 0 ? `matches "${m[0]}"` : `${type} tasks review all decisions` });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Documents: referenced, never embedded
|
|
133
|
+
const documents = [];
|
|
134
|
+
let docCount = 0;
|
|
135
|
+
if (await pathExists(ws.contextDir)) {
|
|
136
|
+
for (const name of (await fs.readdir(ws.contextDir)).filter((n) => n.endsWith('.md')).sort()) {
|
|
137
|
+
docCount++;
|
|
138
|
+
const byType = (DOCS_BY_TYPE[type] ?? []).includes(name);
|
|
139
|
+
const m = hit(name.toLowerCase());
|
|
140
|
+
if (byType || m.length > 0) {
|
|
141
|
+
documents.push({ name, bytes: (await fs.stat(path.join(ws.contextDir, name))).size });
|
|
142
|
+
included.push({ kind: 'document', id: name, reason: byType ? `reference for ${type}` : `request names "${m[0]}"` });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
project: { name: project.name, id: project.projectId },
|
|
148
|
+
request: opts.request,
|
|
149
|
+
type,
|
|
150
|
+
confidence: cls.confidence,
|
|
151
|
+
readOnly: cls.readOnly,
|
|
152
|
+
facts,
|
|
153
|
+
aliases,
|
|
154
|
+
checks,
|
|
155
|
+
safety,
|
|
156
|
+
rules: SAFETY_RULES,
|
|
157
|
+
decisions,
|
|
158
|
+
documents,
|
|
159
|
+
packages: [...pkgReason.keys()].sort(),
|
|
160
|
+
included,
|
|
161
|
+
counts: { facts: [facts.length, all.length], checks: [checks.length, usable.length], decisions: [decisions.length, decs.length], documents: [documents.length, docCount] },
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=taskContext.js.map
|