devworkspace 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/README.md +4 -3
- package/dist/agents/claude/content.js +4 -3
- package/dist/agents/claude/content.js.map +1 -1
- package/dist/cli/args.d.ts +19 -0
- package/dist/cli/args.js +6 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/help.js +32 -9
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/render.d.ts +9 -1
- package/dist/cli/render.js +89 -11
- package/dist/cli/render.js.map +1 -1
- package/dist/cli/run.js +54 -8
- package/dist/cli/run.js.map +1 -1
- package/dist/config/schemas.d.ts +5 -0
- package/dist/config/schemas.js +2 -0
- package/dist/config/schemas.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/discovery/commands.js +17 -3
- package/dist/discovery/commands.js.map +1 -1
- package/dist/discovery/detect.d.ts +6 -0
- package/dist/discovery/detect.js +12 -6
- package/dist/discovery/detect.js.map +1 -1
- package/dist/discovery/docs.d.ts +10 -0
- package/dist/discovery/docs.js +52 -0
- package/dist/discovery/docs.js.map +1 -0
- package/dist/discovery/entrypoints.d.ts +20 -0
- package/dist/discovery/entrypoints.js +67 -0
- package/dist/discovery/entrypoints.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/integrations/adapters/claudePlugin.js +12 -3
- package/dist/integrations/adapters/claudePlugin.js.map +1 -1
- package/dist/knowledge/facts.d.ts +59 -2
- package/dist/knowledge/facts.js +115 -17
- package/dist/knowledge/facts.js.map +1 -1
- package/dist/prompts/onboarding.js +6 -3
- package/dist/prompts/onboarding.js.map +1 -1
- package/dist/services/analysis.d.ts +7 -1
- package/dist/services/analysis.js +17 -4
- package/dist/services/analysis.js.map +1 -1
- package/dist/services/doctor.js +14 -3
- package/dist/services/doctor.js.map +1 -1
- package/dist/services/knowledge.d.ts +17 -0
- package/dist/services/knowledge.js +28 -2
- package/dist/services/knowledge.js.map +1 -1
- package/dist/services/refresh.js +6 -6
- package/dist/services/refresh.js.map +1 -1
- package/dist/services/status.d.ts +11 -2
- package/dist/services/status.js +11 -9
- 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.d.ts +2 -0
- package/dist/services/tasks.js +20 -12
- package/dist/services/tasks.js.map +1 -1
- package/dist/services/verify.d.ts +47 -0
- package/dist/services/verify.js +172 -0
- package/dist/services/verify.js.map +1 -0
- package/dist/tasks/routing.js +10 -3
- package/dist/tasks/routing.js.map +1 -1
- package/dist/tasks/task.d.ts +49 -5
- package/dist/tasks/task.js +68 -11
- package/dist/tasks/task.js.map +1 -1
- package/dist/verification/safety.d.ts +38 -0
- package/dist/verification/safety.js +120 -0
- package/dist/verification/safety.js.map +1 -0
- package/dist/verification/scope.d.ts +19 -0
- package/dist/verification/scope.js +119 -0
- package/dist/verification/scope.js.map +1 -0
- package/docs/benchmarking.md +19 -0
- package/docs/knowledge.md +19 -0
- package/docs/testing.md +25 -2
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.js","sourceRoot":"","sources":["../../src/verification/scope.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport type { CommandEntry } from '../config/schemas.js';\nimport { gitAvailable, parsePorcelainZ } from '../git/git.js';\nimport { isSensitiveFile } from '../security/files.js';\nimport type { TaskScope } from '../tasks/task.js';\nimport { sha256File, sha256Text } from '../utils/fs.js';\nimport { run } from '../utils/proc.js';\n\nconst MAX_TRACKED_FILES = 2000;\nconst MAX_HASH_BYTES = 8 * 1024 * 1024;\n/** Documentation and tool-managed folders never decide which package's checks apply. */\nconst NOT_CODE = /^(?:\\.devworkspace|\\.claude|graphify-out)\\/|\\.(?:md|mdx|rst|txt)$/i;\n\nexport type ChangeMap = Record<string, string>;\n\nasync function git(root: string, args: string[]): Promise<string | null> {\n const r = await run('git', args, { cwd: root, timeoutMs: 30_000 });\n return r.ok ? r.stdout : null;\n}\n\nasync function fileFingerprint(root: string, rel: string): Promise<string> {\n const full = path.join(root, ...rel.split('/'));\n try {\n const st = await fs.lstat(full);\n if (!st.isFile()) return 'not-a-file';\n if (st.size > MAX_HASH_BYTES) return `large:${st.size}:${Math.floor(st.mtimeMs)}`;\n return await sha256File(full);\n } catch {\n return 'deleted';\n }\n}\n\n/** Files that differ from HEAD right now (modified, staged or untracked), project-relative, with a content hash. */\nexport async function dirtyFiles(root: string): Promise<{ head: string | null; files: ChangeMap } | null> {\n if (!(await gitAvailable())) return null;\n if ((await git(root, ['rev-parse', '--is-inside-work-tree']))?.trim() !== 'true') return null;\n const prefix = ((await git(root, ['rev-parse', '--show-prefix'])) ?? '').trim();\n const head = ((await git(root, ['rev-parse', 'HEAD'])) ?? '').trim() || null;\n const status = await git(root, ['status', '--porcelain=v1', '-z', '--untracked-files=all']);\n if (status === null) return null;\n const files: ChangeMap = {};\n for (const e of parsePorcelainZ(status)) {\n if (e.xy === '!!' || !e.path.startsWith(prefix)) continue;\n const rel = e.path.slice(prefix.length);\n if (NOT_CODE.test(rel) || isSensitiveFile(rel)) continue;\n if (Object.keys(files).length >= MAX_TRACKED_FILES) break;\n files[rel] = await fileFingerprint(root, rel);\n }\n return { head, files };\n}\n\n/** Snapshot taken when a task is created: what is already modified is not the task's doing. */\nexport async function captureScope(root: string, now: string): Promise<TaskScope> {\n const d = await dirtyFiles(root);\n return { capturedAt: now, head: d?.head ?? null, baseline: d?.files ?? {} };\n}\n\n/** Project-relative files committed since `head` (so committing during a task does not hide its changes). */\nasync function committedSince(root: string, head: string): Promise<string[] | null> {\n const prefix = ((await git(root, ['rev-parse', '--show-prefix'])) ?? '').trim();\n const out = await git(root, ['diff', '--name-only', '-z', head, 'HEAD']);\n if (out === null) return null;\n return out.split('\\0').filter((f) => f && f.startsWith(prefix)).map((f) => f.slice(prefix.length));\n}\n\n/** Files that changed since the task started, with their current hashes. Null when that cannot be determined. */\nexport async function changedSince(root: string, scope: TaskScope): Promise<ChangeMap | null> {\n const now = await dirtyFiles(root);\n if (!now) return null;\n const changed: ChangeMap = {};\n for (const [rel, hash] of Object.entries(now.files)) if (scope.baseline[rel] !== hash) changed[rel] = hash;\n // A file that was modified at the start and has since been cleaned up (committed or reverted) also changed.\n for (const rel of Object.keys(scope.baseline)) if (!(rel in now.files)) changed[rel] = await fileFingerprint(root, rel);\n if (scope.head && now.head && scope.head !== now.head) {\n const committed = await committedSince(root, scope.head);\n if (committed === null) return null;\n for (const rel of committed) if (!NOT_CODE.test(rel) && !isSensitiveFile(rel) && !(rel in changed)) changed[rel] = await fileFingerprint(root, rel);\n }\n return changed;\n}\n\n/** Directories (relative to the project root) that have their own checks in the contract, longest first. */\nexport function packageDirs(contract: CommandEntry[]): string[] {\n const dirs = new Set<string>();\n for (const c of contract) if (c.available && c.cwd && c.cwd !== '.') dirs.add(c.cwd);\n return [...dirs].sort((a, b) => b.length - a.length || a.localeCompare(b));\n}\n\n/**\n * Fingerprint of the changes inside each package. A change under a package belongs to it; a change outside every package\n * (root config, shared tooling) belongs to all of them. \".\" covers everything. Returns null when nothing is known.\n */\nexport function scopeByPackage(changes: ChangeMap | null, contract: CommandEntry[]): Record<string, string> | null {\n if (!changes) return null;\n const files = Object.keys(changes).filter((f) => !NOT_CODE.test(f)).sort();\n if (files.length === 0) return null;\n const dirs = packageDirs(contract);\n const owner = (f: string): string | null => dirs.find((d) => f === d || f.startsWith(`${d}/`)) ?? null;\n const shared = files.filter((f) => owner(f) === null);\n const line = (f: string): string => `${f}:${changes[f]}`;\n const out: Record<string, string> = { '.': sha256Text(files.map(line).join('\\n')) };\n for (const d of dirs) {\n const mine = files.filter((f) => owner(f) === d);\n if (mine.length > 0 || shared.length > 0) out[d] = sha256Text([...mine, ...shared].sort().map(line).join('\\n'));\n }\n return out;\n}\n"]}
|
package/docs/benchmarking.md
CHANGED
|
@@ -31,3 +31,22 @@ Report medians and ranges, not single runs. Include the onboarding cost in arm B
|
|
|
31
31
|
- Provider token counts are often unavailable inside IDE integrations; use tool-call counts as a proxy and say so.
|
|
32
32
|
- Savings depend heavily on repository size, task type and knowledge quality. A one-file repo will show none.
|
|
33
33
|
- Do not extrapolate to percentages without data from your own runs.
|
|
34
|
+
|
|
35
|
+
## Measuring DevWorkspace itself (speed and context size)
|
|
36
|
+
|
|
37
|
+
The section above is about agent behaviour. This one measures the tool, deterministically and offline.
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
npm run build
|
|
41
|
+
node scripts/bench.mjs --files 3000 --runs 9 --json result.json
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`scripts/bench.mjs` generates an ERP-shaped monorepo (NestJS-style `backend/` with DB-dependent scripts, React `frontend/`, docs, a `.env` that is never read) in a temp directory, commits it, and times the `dist/` build in-process: first and repeated `analyze`, dry-run `analyze`/`refresh`, `refresh`, and context generation (legacy, `--topic`, and `--for` for five request types). It prints medians, min/max and every sample.
|
|
45
|
+
|
|
46
|
+
To compare against a baseline, build the baseline commit in a separate `git worktree`, copy `scripts/bench.mjs` into it, and run the script in both. Alternate the runs (A, B, A, B) to cancel machine drift.
|
|
47
|
+
|
|
48
|
+
**Token counts are estimates**: rendered CLI output bytes / 4. No tokenizer or model is called. Use them to compare two outputs of the same tool, not as billing numbers. The generated fixture's facts are synthetic; real repositories have more and longer facts, so absolute sizes will differ.
|
|
49
|
+
|
|
50
|
+
Reported per context: bytes, estimated tokens, facts included/available, checks included/available, decisions and documents included/available, and how many safety rules and unsafe-command warnings were kept. Cache behaviour (`hit` / `miss` / `invalidated`) is asserted in `tests/integration/progressive-context.test.ts`; correctness of discovery is covered by the existing discovery suites.
|
|
51
|
+
|
|
52
|
+
What the numbers do not show: the scan (`readdir` of every directory) and the Git query still run on every analyze, because the cache may not decide that nothing changed. Both are the remaining bottlenecks.
|
package/docs/knowledge.md
CHANGED
|
@@ -44,6 +44,8 @@ Uncertainty is first-class: record `INFERRED`/`LOW` or `UNKNOWN` rather than gue
|
|
|
44
44
|
|
|
45
45
|
Each fact lists the repository files that evidence it and stores their SHA-256 at the time of recording. `devworkspace refresh` re-hashes those sources: a changed or removed source marks the fact `STALE` with a reason. Only stale facts are handed to the agent in the refresh prompt (incremental, not a full re-analysis). The agent re-checks and runs `devworkspace fact verify <key>` (re-hash, mark verified), corrects the fact, or removes it. Facts without sources cannot be proven stale, so cite files.
|
|
46
46
|
|
|
47
|
+
Generated files are not evidence by themselves: re-running `analyze` rewrites `.devworkspace/analysis/*`, which must not invalidate unrelated facts. A fact may cite a section of the discovery report as `.devworkspace/analysis/discovery.json#ci` (fingerprint of that section only), and a bare `discovery.json` citation is narrowed to the section named by the fact's key (`ci.none` depends on `ci`). Any other generated file is ignored. `status`, `refresh --dry-run` and `doctor` all use the same read-only check, so they agree, and each stale fact says which evidence changed. A fact recorded before a dependency was tracked adopts its current fingerprint instead of going stale.
|
|
48
|
+
|
|
47
49
|
Content hashing is deliberately conservative: *any* byte change to a cited file triggers re-validation. It over-reports rather than silently trusting old context.
|
|
48
50
|
|
|
49
51
|
Deterministic (`detector`) facts are simply re-derived on every `analyze`/`refresh`; ones that no longer apply are removed (they are derived data and come back if the evidence returns), so they never produce a warning nobody can clear.
|
|
@@ -64,3 +66,20 @@ Never store: source files, `node_modules`/generated output, chat transcripts, te
|
|
|
64
66
|
## Progressive loading
|
|
65
67
|
|
|
66
68
|
The generated instructions tell the agent to load context in levels and stop at the shallowest one that answers the request: (1) instructions → (2) `devworkspace context --topic …` → (3) relevant decisions → (4) Graphify → (5) specific source files → (6) tests/implementation. "Where is customer uniqueness checked?" stops at level 4–5; "Refactor the customer validation architecture" goes to level 6.
|
|
69
|
+
|
|
70
|
+
### Task-aware context (`context --for`)
|
|
71
|
+
|
|
72
|
+
`devworkspace context --for "<request>"` classifies the request (same classifier as `task new`) and prints only what it needs, each item with the reason it is included:
|
|
73
|
+
|
|
74
|
+
- facts whose key, value or source match the request's keywords, plus type groups (stack and conventions for implementation tasks; known issues for bugs; authorization and tenant rules for security);
|
|
75
|
+
- checks of the package the request names, or of the packages with uncommitted code changes, otherwise all checks; none for questions and documentation;
|
|
76
|
+
- decisions by title and file (never their text) and context documents by name (never embedded);
|
|
77
|
+
- the safety rules, always, and the list of commands DevWorkspace will not run by itself whenever their checks are included.
|
|
78
|
+
|
|
79
|
+
Detector-derived facts whose value equals another selected fact's are folded into it in the output, and very long detector values are clipped with a pointer to `context --topic <key>`. Human- and AI-authored facts are never folded or clipped, and nothing is removed from `facts.json`. Output is deterministic. Plain `context` is unchanged.
|
|
80
|
+
|
|
81
|
+
Routing note (0.3.0): requests naming cross-tenant access, tenant isolation, CSRF/XSS, SQL injection, IDOR, CVEs or privilege escalation are classified SECURITY even when they begin with "fix" (they were BUG), so the exploit-test and review gates apply.
|
|
82
|
+
|
|
83
|
+
### Discovery cache
|
|
84
|
+
|
|
85
|
+
`analyze` and `refresh` reuse the previous detection result from `.devworkspace/local/cache/discovery.json` (git-ignored, never exported) only when the scan result, the content of every manifest detection reads, the project name, the scan limit and the DevWorkspace version are identical. The directory scan and the Git query always run; the cache is a shortcut for recomputation, never evidence. `--dry-run`, `status`, `doctor` and `context` never write it. Delete the `local/cache` folder at any time.
|
package/docs/testing.md
CHANGED
|
@@ -21,9 +21,27 @@ Re-running `analyze` keeps any command with `origin: "user"` (confirmed by a per
|
|
|
21
21
|
|
|
22
22
|
Sources: `package.json` scripts (matched by common names), plus conventions for Python (pytest/ruff/mypy/black/alembic), Go, Rust, Maven and Gradle. npm's default `"no test specified"` placeholder is ignored.
|
|
23
23
|
|
|
24
|
+
Formatters are listed too. A `format` script that rewrites files is recorded with `"mutates": true` and `required: "no"`; a `format:check` (or a `format` that only checks) is read-only. DevWorkspace never runs a command marked `mutates`.
|
|
25
|
+
|
|
24
26
|
### Commands are untrusted input
|
|
25
27
|
|
|
26
|
-
Everything marked `origin: "repository-script"` comes from files in the repository.
|
|
28
|
+
Everything marked `origin: "repository-script"` comes from files in the repository. `analyze`, `status`, `doctor` and `refresh` never run any of them. Only `devworkspace task run` executes a contract command, as an argument vector (never through a shell), and only after the safety rules below. The generated instructions still tell the agent to read what an unfamiliar script does first.
|
|
29
|
+
|
|
30
|
+
### What `task run` will and will not run
|
|
31
|
+
|
|
32
|
+
| Command | Runs? |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| typecheck, lint, build, unit tests, read-only format checks | yes |
|
|
35
|
+
| migrations; anything whose definition mentions drop, truncate, reset, seed, migrate, flush; any command marked `mutates` | **never** |
|
|
36
|
+
| integration, security and e2e checks; a catch-all `test` script in a package that depends on a database or Redis | only after you declare a test environment for that package and check |
|
|
37
|
+
|
|
38
|
+
DevWorkspace does not read `.env` files or connection settings, so it **cannot verify which database a test would use**, and it does not guess from names (a database called `test` is not treated as safe). The only way to allow such a check is an explicit, local declaration by a person:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
devworkspace testenv declare --package backend --check integration --target "disposable docker compose stack" --yes
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
This records your assertion in `.devworkspace/local/test-environment.json` (git-ignored). Targets that look like connection strings or credentials are refused. A refused check is recorded as `COULD_NOT_RUN`, never as a pass.
|
|
27
45
|
|
|
28
46
|
## Task-specific verification
|
|
29
47
|
|
|
@@ -32,9 +50,14 @@ Questions need none; docs need only documentation checks; bugs, features and ref
|
|
|
32
50
|
```bash
|
|
33
51
|
devworkspace task tests <id> --phase baseline --passed 214 --failed 3 # before changing code
|
|
34
52
|
devworkspace task tests <id> --phase after --passed 218 --failed 3
|
|
35
|
-
devworkspace task
|
|
53
|
+
devworkspace task run <id> typecheck # executes the contract command for every package the task changed
|
|
54
|
+
devworkspace task verify <id> review pass # a report; recorded as a claim, not as an executed result
|
|
36
55
|
```
|
|
37
56
|
|
|
57
|
+
### Package-aware completion
|
|
58
|
+
|
|
59
|
+
A task records, when it is created, which files were already modified. When you complete it, DevWorkspace works out which files changed since, assigns them to packages (the `cwd` directories of the contract; a change outside every package counts for all of them) and requires **executed** evidence for each applicable check **in each changed package**, taken after the last change. A backend-only build is not evidence for frontend changes. A pass that was only reported with `task verify` is a claim and does not satisfy the gate unless you add `--reason user-override`. Tasks created by older versions keep the original rules.
|
|
60
|
+
|
|
38
61
|
Pre-existing failures are recorded, not blamed on new work. Regressions block completion.
|
|
39
62
|
|
|
40
63
|
## Failure reporting
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devworkspace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Project-local AI engineering workspace: deterministic repository discovery, portable project knowledge, tasks, decisions and verification gates for AI coding agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|