projscan 3.5.0 → 3.7.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/README.md +27 -21
- package/dist/cli/commands/claim.d.ts +5 -0
- package/dist/cli/commands/claim.js +139 -0
- package/dist/cli/commands/claim.js.map +1 -0
- package/dist/cli/commands/collision.d.ts +5 -0
- package/dist/cli/commands/collision.js +62 -0
- package/dist/cli/commands/collision.js.map +1 -0
- package/dist/cli/commands/coordinate.d.ts +7 -0
- package/dist/cli/commands/coordinate.js +98 -0
- package/dist/cli/commands/coordinate.js.map +1 -0
- package/dist/cli/commands/mergeRisk.d.ts +5 -0
- package/dist/cli/commands/mergeRisk.js +58 -0
- package/dist/cli/commands/mergeRisk.js.map +1 -0
- package/dist/cli/commands/route.d.ts +5 -0
- package/dist/cli/commands/route.js +53 -0
- package/dist/cli/commands/route.js.map +1 -0
- package/dist/cli/index.js +10 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/agentBrief.js +32 -2
- package/dist/core/agentBrief.js.map +1 -1
- package/dist/core/claims.d.ts +60 -0
- package/dist/core/claims.js +139 -0
- package/dist/core/claims.js.map +1 -0
- package/dist/core/collisionDetector.d.ts +65 -0
- package/dist/core/collisionDetector.js +222 -0
- package/dist/core/collisionDetector.js.map +1 -0
- package/dist/core/coordination.d.ts +62 -0
- package/dist/core/coordination.js +121 -0
- package/dist/core/coordination.js.map +1 -0
- package/dist/core/embeddings.js +30 -17
- package/dist/core/embeddings.js.map +1 -1
- package/dist/core/intentRouter.d.ts +40 -0
- package/dist/core/intentRouter.js +213 -0
- package/dist/core/intentRouter.js.map +1 -0
- package/dist/core/mergeRisk.d.ts +42 -0
- package/dist/core/mergeRisk.js +71 -0
- package/dist/core/mergeRisk.js.map +1 -0
- package/dist/core/preflight.js +50 -0
- package/dist/core/preflight.js.map +1 -1
- package/dist/core/roadmapCatalog.js +50 -50
- package/dist/core/roadmapCatalog.js.map +1 -1
- package/dist/mcp/tools/claim.d.ts +7 -0
- package/dist/mcp/tools/claim.js +69 -0
- package/dist/mcp/tools/claim.js.map +1 -0
- package/dist/mcp/tools/collision.d.ts +7 -0
- package/dist/mcp/tools/collision.js +38 -0
- package/dist/mcp/tools/collision.js.map +1 -0
- package/dist/mcp/tools/coordinate.d.ts +7 -0
- package/dist/mcp/tools/coordinate.js +24 -0
- package/dist/mcp/tools/coordinate.js.map +1 -0
- package/dist/mcp/tools/coordinateWatch.d.ts +4 -0
- package/dist/mcp/tools/coordinateWatch.js +138 -0
- package/dist/mcp/tools/coordinateWatch.js.map +1 -0
- package/dist/mcp/tools/mergeRisk.d.ts +7 -0
- package/dist/mcp/tools/mergeRisk.js +24 -0
- package/dist/mcp/tools/mergeRisk.js.map +1 -0
- package/dist/mcp/tools/route.d.ts +7 -0
- package/dist/mcp/tools/route.js +24 -0
- package/dist/mcp/tools/route.js.map +1 -0
- package/dist/mcp/tools.js +12 -0
- package/dist/mcp/tools.js.map +1 -1
- package/dist/projscan-sbom.cdx.json +6 -6
- package/dist/tool-manifest.json +132 -3
- package/dist/types.d.ts +12 -2
- package/dist/utils/formatSupport.d.ts +9 -0
- package/dist/utils/formatSupport.js +9 -0
- package/dist/utils/formatSupport.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
|
|
10
10
|
**Agent-first code intelligence.** An MCP server that lets AI coding agents (Claude Code, Codex, Cursor, Gemini, Windsurf, Cline, Continue, Zed — any MCP-aware client) query your codebase — with a CLI for humans and a local plugin layer for team-specific policy and reporting.
|
|
11
11
|
|
|
12
|
-
[AI Agent Quick Start](#ai-agent-integration-mcp) · [CLI Quick Start](#quick-start) · [Commands](#commands) · [Full Guide](https://github.com/abhiyoheswaran1/projscan/blob/v3.
|
|
12
|
+
[AI Agent Quick Start](#ai-agent-integration-mcp) · [CLI Quick Start](#quick-start) · [Commands](#commands) · [Full Guide](https://github.com/abhiyoheswaran1/projscan/blob/v3.7.0/docs/GUIDE.md) · [Roadmap](https://github.com/abhiyoheswaran1/projscan/blob/v3.7.0/docs/ROADMAP.md)
|
|
13
13
|
|
|
14
|
-
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.
|
|
14
|
+
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.7.0/docs/projscan-reporter-plugin.png" alt="projscan reporter plugin running in a macOS-style terminal window with a team health summary" width="700">
|
|
15
15
|
|
|
16
16
|
</div>
|
|
17
17
|
|
|
@@ -33,7 +33,7 @@ The local plugin platform lets teams add project-specific findings and render `d
|
|
|
33
33
|
npx projscan
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.
|
|
36
|
+
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.7.0/docs/projscan-reporter-plugin.gif" alt="projscan doctor rendered through a local reporter plugin in a macOS-style terminal window" width="700">
|
|
37
37
|
|
|
38
38
|
Run `projscan doctor` for a focused health check:
|
|
39
39
|
|
|
@@ -41,7 +41,7 @@ Run `projscan doctor` for a focused health check:
|
|
|
41
41
|
npx projscan doctor
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.
|
|
44
|
+
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.7.0/docs/npx%20projscan%20doctor.gif" alt="npx projscan doctor" width="700">
|
|
45
45
|
|
|
46
46
|
## Install
|
|
47
47
|
|
|
@@ -94,9 +94,9 @@ npm run test:trust-smoke
|
|
|
94
94
|
|
|
95
95
|
The full command catalog is below. Most users should start with the five-command path above instead of scanning the catalog.
|
|
96
96
|
|
|
97
|
-
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.
|
|
97
|
+
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.7.0/docs/npx%20projscan%20--help.gif" alt="npx projscan --help" width="700">
|
|
98
98
|
|
|
99
|
-
For a comprehensive walkthrough, see the **[Full Guide](https://github.com/abhiyoheswaran1/projscan/blob/v3.
|
|
99
|
+
For a comprehensive walkthrough, see the **[Full Guide](https://github.com/abhiyoheswaran1/projscan/blob/v3.7.0/docs/GUIDE.md)**.
|
|
100
100
|
|
|
101
101
|
## Repo Understanding
|
|
102
102
|
|
|
@@ -177,31 +177,31 @@ projscan --help
|
|
|
177
177
|
<details>
|
|
178
178
|
<summary><strong>projscan structure</strong> - Directory tree with file counts</summary>
|
|
179
179
|
|
|
180
|
-
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.
|
|
180
|
+
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.7.0/docs/npx%20projscan%20structure.gif" alt="npx projscan structure" width="700">
|
|
181
181
|
</details>
|
|
182
182
|
|
|
183
183
|
<details>
|
|
184
184
|
<summary><strong>projscan diagram</strong> - Architecture visualization</summary>
|
|
185
185
|
|
|
186
|
-
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.
|
|
186
|
+
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.7.0/docs/npx%20projscan%20diagram.gif" alt="npx projscan diagram" width="700">
|
|
187
187
|
</details>
|
|
188
188
|
|
|
189
189
|
<details>
|
|
190
190
|
<summary><strong>projscan dependencies</strong> - Dependency analysis</summary>
|
|
191
191
|
|
|
192
|
-
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.
|
|
192
|
+
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.7.0/docs/npx%20projscan%20dependencies.gif" alt="npx projscan dependencies" width="700">
|
|
193
193
|
</details>
|
|
194
194
|
|
|
195
195
|
<details>
|
|
196
196
|
<summary><strong>projscan explain</strong> - File explanation</summary>
|
|
197
197
|
|
|
198
|
-
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.
|
|
198
|
+
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.7.0/docs/npx%20projscan%20explain.gif" alt="npx projscan explain" width="700">
|
|
199
199
|
</details>
|
|
200
200
|
|
|
201
201
|
<details>
|
|
202
202
|
<summary><strong>projscan badge</strong> - Health badge generation</summary>
|
|
203
203
|
|
|
204
|
-
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.
|
|
204
|
+
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.7.0/docs/npx%20projscan%20badge.gif" alt="npx projscan badge" width="700">
|
|
205
205
|
</details>
|
|
206
206
|
|
|
207
207
|
### Output Formats
|
|
@@ -223,7 +223,7 @@ Run `projscan help` for the generated command-by-command support matrix.
|
|
|
223
223
|
|
|
224
224
|
projscan can load local plugins from `.projscan-plugins/` when `PROJSCAN_PLUGINS_PREVIEW=1` is set. The environment flag is kept for explicit local-code opt-in. Analyzer plugins emit normal projscan issues; reporter plugins render supported CLI commands with team-specific output.
|
|
225
225
|
|
|
226
|
-
**2.0 upgrade notes:** migrating from 1.x or authoring plugins? Start with the [2.0 Migration Guide](https://github.com/abhiyoheswaran1/projscan/blob/v3.
|
|
226
|
+
**2.0 upgrade notes:** migrating from 1.x or authoring plugins? Start with the [2.0 Migration Guide](https://github.com/abhiyoheswaran1/projscan/blob/v3.7.0/docs/2.0-MIGRATION.md), then use [Plugin Authoring](https://github.com/abhiyoheswaran1/projscan/blob/v3.7.0/docs/PLUGIN-AUTHORING.md), the [Plugin Gallery](https://github.com/abhiyoheswaran1/projscan/blob/v3.7.0/docs/PLUGIN-GALLERY.md), and the [manifest schema](https://github.com/abhiyoheswaran1/projscan/blob/v3.7.0/docs/plugin.schema.json) as the stable contract.
|
|
227
227
|
|
|
228
228
|
```bash
|
|
229
229
|
projscan plugin list
|
|
@@ -234,9 +234,9 @@ PROJSCAN_PLUGINS_PREVIEW=1 projscan doctor --reporter team-radar
|
|
|
234
234
|
PROJSCAN_PLUGINS_PREVIEW=1 projscan ci --reporter team-radar --min-score 80
|
|
235
235
|
```
|
|
236
236
|
|
|
237
|
-
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.
|
|
237
|
+
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.7.0/docs/projscan-reporter-plugin.gif" alt="projscan local reporter plugin rendering a team health report" width="700">
|
|
238
238
|
|
|
239
|
-
Reporter plugins are intentionally CLI-only. MCP tools keep returning structured JSON-compatible payloads so agents can reason over stable data, while humans can get a polished local report for their team. Custom presentation, team-branded summaries, and white-label reports belong in reporter plugins rather than new core HTML theming flags. See [Plugin Authoring](https://github.com/abhiyoheswaran1/projscan/blob/v3.
|
|
239
|
+
Reporter plugins are intentionally CLI-only. MCP tools keep returning structured JSON-compatible payloads so agents can reason over stable data, while humans can get a polished local report for their team. Custom presentation, team-branded summaries, and white-label reports belong in reporter plugins rather than new core HTML theming flags. See [Plugin Authoring](https://github.com/abhiyoheswaran1/projscan/blob/v3.7.0/docs/PLUGIN-AUTHORING.md) for manifest shape, `render(context)`, validation, and the trust model.
|
|
240
240
|
|
|
241
241
|
### Options
|
|
242
242
|
|
|
@@ -401,7 +401,7 @@ If you read projscan's [Socket report](https://socket.dev/npm/package/projscan),
|
|
|
401
401
|
### Audit it yourself
|
|
402
402
|
|
|
403
403
|
- **Source is open** at [github.com/abhiyoheswaran1/projscan](https://github.com/abhiyoheswaran1/projscan). The npm tarball matches the `dist/` produced by `npm run build` at the matching tag.
|
|
404
|
-
- **Public API surface is locked** by `scripts/check-stability.mjs`, which runs in CI on every PR and fails on any rename or removal of an MCP tool, CLI command, or exit code. See [`docs/STABILITY.md`](https://github.com/abhiyoheswaran1/projscan/blob/v3.
|
|
404
|
+
- **Public API surface is locked** by `scripts/check-stability.mjs`, which runs in CI on every PR and fails on any rename or removal of an MCP tool, CLI command, or exit code. See [`docs/STABILITY.md`](https://github.com/abhiyoheswaran1/projscan/blob/v3.7.0/docs/STABILITY.md).
|
|
405
405
|
- **Run it offline:** `npm install -g projscan` followed by anything except `audit` and `--mode semantic` works without network.
|
|
406
406
|
- **Drop privilege further:** in CI, run projscan in a sandbox that disallows network egress; everything except `audit` will pass.
|
|
407
407
|
|
|
@@ -452,7 +452,7 @@ projscan ci --changed-only # Gate only on this PR's diff
|
|
|
452
452
|
projscan ci --format sarif > projscan.sarif # SARIF for Code Scanning
|
|
453
453
|
```
|
|
454
454
|
|
|
455
|
-
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.
|
|
455
|
+
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.7.0/docs/npx%20projscan%20ci%20--min-score%2070.gif" alt="npx projscan ci --min-score 70" width="700">
|
|
456
456
|
|
|
457
457
|
### GitHub Action (recommended)
|
|
458
458
|
|
|
@@ -529,7 +529,7 @@ Fields:
|
|
|
529
529
|
- `hotspots.limit` / `hotspots.since` - defaults for the `hotspots` command
|
|
530
530
|
- `monorepo.importPolicy` - cross-package import allow/deny rules in monorepos *(0.14+)*
|
|
531
531
|
|
|
532
|
-
See [`docs/GUIDE.md` → Configuration](https://github.com/abhiyoheswaran1/projscan/blob/v3.
|
|
532
|
+
See [`docs/GUIDE.md` → Configuration](https://github.com/abhiyoheswaran1/projscan/blob/v3.7.0/docs/GUIDE.md#configuration-projscanrc) for the full reference (field types, validation behavior, embedding config in `package.json`, monorepo `importPolicy` semantics).
|
|
533
533
|
|
|
534
534
|
## Tracking Health Over Time
|
|
535
535
|
|
|
@@ -542,7 +542,7 @@ projscan diff # Compare against baseline
|
|
|
542
542
|
projscan diff --format markdown # Markdown diff for PRs
|
|
543
543
|
```
|
|
544
544
|
|
|
545
|
-
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.
|
|
545
|
+
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.7.0/docs/npx%20projscan%20diff%20--save-baseline.gif" alt="npx projscan diff --save-baseline" width="700">
|
|
546
546
|
|
|
547
547
|
## Hotspots - Where to Fix First
|
|
548
548
|
|
|
@@ -631,7 +631,7 @@ Coverage is also automatically joined into `projscan hotspots` when one of those
|
|
|
631
631
|
|
|
632
632
|
**This is the primary way to use projscan.** `projscan mcp` starts an [MCP](https://modelcontextprotocol.io) server over stdio so AI coding agents can query your codebase with real structural accuracy - not regex, not grep.
|
|
633
633
|
|
|
634
|
-
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.
|
|
634
|
+
<img src="https://raw.githubusercontent.com/abhiyoheswaran1/projscan/v3.7.0/docs/projscan-agent-demo.gif" alt="projscan answering two agent questions: what breaks if I rename buildCodeGraph (impact analysis with definitions, direct callers, transitive reach), and where should I fix first (ranked hotspots with cyclomatic complexity)" width="700">
|
|
635
635
|
|
|
636
636
|
Two questions an agent asks; structural answers in milliseconds. *"What breaks if I rename `buildCodeGraph`?"* → 31 direct callers, 97 files reachable. *"Where should I fix first?"* → ranked hotspots with AST cyclomatic complexity, churn, and ownership signals.
|
|
637
637
|
|
|
@@ -777,7 +777,7 @@ Capability is advertised under `experimental.fileChanged` on `initialize` so cli
|
|
|
777
777
|
- *"How do I plan the next six product lines?"* → `projscan_release_train`
|
|
778
778
|
- *"How do I wire projscan into this MCP client?"* → `projscan_adoption { action: "mcp_config", client: "codex" }`
|
|
779
779
|
|
|
780
|
-
### The
|
|
780
|
+
### The 47 MCP tools
|
|
781
781
|
|
|
782
782
|
**Structural (0.6.0 / 0.11 / 0.13 / 0.14 / 0.15 - agent-native):**
|
|
783
783
|
- **`projscan_start`** *(3.0.4)* - first-60-seconds repo orientation. Composes setup diagnostics, `firstTenMinutes`, workflow recipes, workplan, quality scorecard, top risks, adoption gaps, next commands, and optional handoff payload.
|
|
@@ -800,6 +800,12 @@ Capability is advertised under `experimental.fileChanged` on `initialize` so cli
|
|
|
800
800
|
- **`projscan_fix_suggest`** *(0.14)* - structured action prompt for any open issue: headline, why it matters, where, one-paragraph instruction, optional suggested test. Closes the diagnose → fix loop.
|
|
801
801
|
- **`projscan_explain_issue`** *(0.14)* - deep dive on one issue: code excerpt, related issues in the same file, similar past commits via `git log --grep`, plus the structured FixSuggestion.
|
|
802
802
|
- **`projscan_impact`** *(0.15)* - transitive blast-radius for a file or symbol. BFS over reverse imports + symbol callsites. Use BEFORE renaming or deleting to see what breaks.
|
|
803
|
+
- **`projscan_collision`** *(3.6)* - detect change collisions across the repo's in-flight git worktrees (parallel agents). Flags same-file edits and dependency overlaps (one worktree edits a file another's change imports) before the branches merge. Local-first; needs ≥2 worktrees.
|
|
804
|
+
- **`projscan_claim`** *(3.6)* - advisory claims/leases over files, directories, or symbols, shared across the repo's worktrees. `add` returns contention when another agent already holds an overlapping target; `list` / `release` manage them. Local-first.
|
|
805
|
+
- **`projscan_merge_risk`** *(3.6)* - merge-risk preflight across in-flight worktrees: a safe integration order (merge the least-entangled branch first) plus conflict hotspots (files changed by 2+ worktrees). Builds on `projscan_collision`. Local-first.
|
|
806
|
+
- **`projscan_route`** *(3.6)* - map a stated goal (e.g. "what breaks if I rename X") to the right projscan tool with the exact call, or list the full capability catalog. A discovery entry point over the tool surface; deterministic, no LLM.
|
|
807
|
+
- **`projscan_coordinate`** *(3.6)* - one-call swarm coordination read: composes collisions, claims, and merge-risk into a `readiness` verdict (clear / caution / conflicted) with counts and the recommended integration order. The single entry point for the coordination arc. Local-first.
|
|
808
|
+
- **`projscan_coordinate_watch`** *(3.7)* - long-running coordination watch: polls the in-flight worktrees and emits a `notifications/projscan/coordination_changed` notification whenever the swarm state changes. Pairs with `projscan_coordinate`. `start` / `stop` / `list`.
|
|
803
809
|
|
|
804
810
|
**Analysis:**
|
|
805
811
|
- `projscan_analyze` - full project report
|
|
@@ -830,7 +836,7 @@ Capability is advertised under `experimental.fileChanged` on `initialize` so cli
|
|
|
830
836
|
- **`projscan_apply_fix`** *(1.6)* - mechanically execute the safe fix templates. Default is dry-run; pass `confirm: true` to write. Atomic writes, per-apply rollback record at `.projscan-cache/rollbacks/<id>.json`. Reverse with `action: "rollback", rollback_id: ...`. Six templates supported at this release: `unused-dependency-*`, `missing-test-framework`, `missing-eslint`, `missing-prettier`, `missing-editorconfig`, `missing-readme`.
|
|
831
837
|
- **`projscan_taint`** *(1.6)* - source-to-sink reachability over the per-function call graph. Built-in defaults cover common JS / Python sources (`process.env`, `req.body`, etc.) and sinks (`exec`, `eval`, `db.query`, etc.). Project-specific names go in `.projscanrc.json` `taint`. `projscan_review` automatically diffs taint flows between base and head and **blocks any PR that introduces a new flow**. In 3.0.2, review surfaces hardened `newDataflowRisks`, compact `graphEvidence`, and graph-readiness gates for safer handoff.
|
|
832
838
|
|
|
833
|
-
Analyzer plugins can optionally read graph/dataflow context through `check(rootPath, files, context)` while staying on manifest schema v1. The packaged `graph-context` example shows `context.getSemanticGraph()` and `context.getDataflow()` in a real analyzer. For analyzer and reporter plugin authoring, manifest validation, `--reporter <name>`, and the trust model, see [Plugin Authoring](https://github.com/abhiyoheswaran1/projscan/blob/v3.
|
|
839
|
+
Analyzer plugins can optionally read graph/dataflow context through `check(rootPath, files, context)` while staying on manifest schema v1. The packaged `graph-context` example shows `context.getSemanticGraph()` and `context.getDataflow()` in a real analyzer. For analyzer and reporter plugin authoring, manifest validation, `--reporter <name>`, and the trust model, see [Plugin Authoring](https://github.com/abhiyoheswaran1/projscan/blob/v3.7.0/docs/PLUGIN-AUTHORING.md).
|
|
834
840
|
|
|
835
841
|
### Context-window budgeting
|
|
836
842
|
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { program, getRootPath, setupLogLevel, maybeCompactBanner, assertFormatSupported } from '../_shared.js';
|
|
3
|
+
import { addClaim, listClaims, releaseClaim, pruneClaims, isClaimActive, findContendedClaims } from '../../core/claims.js';
|
|
4
|
+
/**
|
|
5
|
+
* `projscan claim` (4.x) — advisory claims/leases so parallel agents see who
|
|
6
|
+
* owns which file, directory, or symbol. Shared across the repo's worktrees.
|
|
7
|
+
*/
|
|
8
|
+
export function registerClaim() {
|
|
9
|
+
const claim = program
|
|
10
|
+
.command('claim')
|
|
11
|
+
.description('Advisory claims/leases for coordinating parallel agents')
|
|
12
|
+
.action(async () => {
|
|
13
|
+
await runList();
|
|
14
|
+
});
|
|
15
|
+
claim
|
|
16
|
+
.command('list')
|
|
17
|
+
.description('List active claims across the repo worktrees')
|
|
18
|
+
.action(async () => {
|
|
19
|
+
await runList();
|
|
20
|
+
});
|
|
21
|
+
claim
|
|
22
|
+
.command('add <target>')
|
|
23
|
+
.description('Claim a file, directory, or symbol')
|
|
24
|
+
.requiredOption('--agent <name>', 'agent holding the claim')
|
|
25
|
+
.option('--note <text>', 'optional note')
|
|
26
|
+
.option('--ttl <seconds>', 'lease duration in seconds (claim expires after)')
|
|
27
|
+
.action(async (target, cmdOpts) => {
|
|
28
|
+
setupLogLevel();
|
|
29
|
+
maybeCompactBanner();
|
|
30
|
+
const format = assertFormatSupported('claim add');
|
|
31
|
+
const rootPath = getRootPath();
|
|
32
|
+
const ttlSeconds = cmdOpts.ttl !== undefined ? Number.parseInt(cmdOpts.ttl, 10) : undefined;
|
|
33
|
+
if (ttlSeconds !== undefined && (!Number.isFinite(ttlSeconds) || ttlSeconds <= 0)) {
|
|
34
|
+
const message = '--ttl must be a positive number of seconds.';
|
|
35
|
+
if (format === 'json')
|
|
36
|
+
console.log(JSON.stringify({ ok: false, error: message }, null, 2));
|
|
37
|
+
else
|
|
38
|
+
console.error(chalk.red(message));
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
const result = await addClaim(rootPath, {
|
|
42
|
+
target,
|
|
43
|
+
agent: cmdOpts.agent,
|
|
44
|
+
...(cmdOpts.note ? { note: cmdOpts.note } : {}),
|
|
45
|
+
...(ttlSeconds !== undefined ? { ttlSeconds } : {}),
|
|
46
|
+
});
|
|
47
|
+
if (format === 'json') {
|
|
48
|
+
console.log(JSON.stringify(result, null, 2));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
console.log(`${chalk.green('✓')} claimed ${chalk.bold(result.claim.target)} for ${chalk.bold(result.claim.agent)}`);
|
|
52
|
+
if (result.contention.length > 0) {
|
|
53
|
+
console.log(chalk.yellow(` ⚠ contention: also held by ${result.contention.map((c) => `${c.agent} (${c.target})`).join(', ')}`));
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
claim
|
|
57
|
+
.command('release [id]')
|
|
58
|
+
.description('Release a claim by id, or by --target / --agent')
|
|
59
|
+
.option('--target <target>', 'release claims on this target')
|
|
60
|
+
.option('--agent <name>', 'release this agent\'s claims (or scope --target to it)')
|
|
61
|
+
.action(async (id, cmdOpts) => {
|
|
62
|
+
setupLogLevel();
|
|
63
|
+
maybeCompactBanner();
|
|
64
|
+
const format = assertFormatSupported('claim release');
|
|
65
|
+
const rootPath = getRootPath();
|
|
66
|
+
if (!id && !cmdOpts.target && !cmdOpts.agent) {
|
|
67
|
+
const message = 'release needs an <id>, --target, or --agent.';
|
|
68
|
+
if (format === 'json')
|
|
69
|
+
console.log(JSON.stringify({ ok: false, error: message }, null, 2));
|
|
70
|
+
else
|
|
71
|
+
console.error(chalk.red(message));
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
const released = await releaseClaim(rootPath, {
|
|
75
|
+
...(id ? { id } : {}),
|
|
76
|
+
...(cmdOpts.target ? { target: cmdOpts.target } : {}),
|
|
77
|
+
...(cmdOpts.agent ? { agent: cmdOpts.agent } : {}),
|
|
78
|
+
});
|
|
79
|
+
if (format === 'json') {
|
|
80
|
+
console.log(JSON.stringify({ ok: true, released }, null, 2));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
console.log(released.length > 0
|
|
84
|
+
? `${chalk.green('✓')} released ${released.length} claim(s)`
|
|
85
|
+
: chalk.dim(' no matching claims to release'));
|
|
86
|
+
});
|
|
87
|
+
claim
|
|
88
|
+
.command('prune')
|
|
89
|
+
.description('Remove expired-lease claims')
|
|
90
|
+
.action(async () => {
|
|
91
|
+
setupLogLevel();
|
|
92
|
+
maybeCompactBanner();
|
|
93
|
+
const format = assertFormatSupported('claim prune');
|
|
94
|
+
const rootPath = getRootPath();
|
|
95
|
+
const pruned = await pruneClaims(rootPath);
|
|
96
|
+
if (format === 'json') {
|
|
97
|
+
console.log(JSON.stringify({ ok: true, pruned }, null, 2));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
console.log(pruned.length > 0
|
|
101
|
+
? `${chalk.green('✓')} pruned ${pruned.length} expired claim(s)`
|
|
102
|
+
: chalk.dim(' no expired claims'));
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
async function runList() {
|
|
106
|
+
setupLogLevel();
|
|
107
|
+
maybeCompactBanner();
|
|
108
|
+
const format = assertFormatSupported('claim list');
|
|
109
|
+
const rootPath = getRootPath();
|
|
110
|
+
const claims = await listClaims(rootPath);
|
|
111
|
+
if (format === 'json') {
|
|
112
|
+
console.log(JSON.stringify({ claims }, null, 2));
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
console.log('');
|
|
116
|
+
console.log(chalk.bold('Claims'));
|
|
117
|
+
console.log(chalk.dim('────────────────────────────────────────'));
|
|
118
|
+
if (claims.length === 0) {
|
|
119
|
+
console.log(chalk.dim(' no active claims'));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const now = new Date();
|
|
123
|
+
for (const c of claims) {
|
|
124
|
+
const lease = c.expiresAt
|
|
125
|
+
? isClaimActive(c, now)
|
|
126
|
+
? chalk.dim(` ⏱ expires ${c.expiresAt}`)
|
|
127
|
+
: chalk.red(' ⏱ expired')
|
|
128
|
+
: '';
|
|
129
|
+
console.log(` ${chalk.bold(c.target)} ${chalk.dim(`— ${c.agent}`)}${c.note ? chalk.dim(` (${c.note})`) : ''}${lease}`);
|
|
130
|
+
console.log(chalk.dim(` ${c.id}`));
|
|
131
|
+
}
|
|
132
|
+
// Surface any overlapping holders so contention is visible at a glance.
|
|
133
|
+
const contendedTargets = new Set(findContendedClaims(claims).map((c) => c.target));
|
|
134
|
+
if (contendedTargets.size > 0) {
|
|
135
|
+
console.log('');
|
|
136
|
+
console.log(chalk.yellow(` ⚠ ${contendedTargets.size} target(s) claimed by more than one agent`));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=claim.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim.js","sourceRoot":"","sources":["../../../src/cli/commands/claim.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { program, getRootPath, setupLogLevel, maybeCompactBanner, assertFormatSupported } from '../_shared.js';
|
|
4
|
+
import { detectCollisions } from '../../core/collisionDetector.js';
|
|
5
|
+
/**
|
|
6
|
+
* `projscan collisions` (4.x) — surface change collisions across the repo's
|
|
7
|
+
* in-flight git worktrees so parallel agents see overlaps before merge.
|
|
8
|
+
*/
|
|
9
|
+
export function registerCollision() {
|
|
10
|
+
program
|
|
11
|
+
.command('collisions')
|
|
12
|
+
.description('Detect change collisions across in-flight git worktrees (parallel agents)')
|
|
13
|
+
.option('--base-ref <ref>', 'base ref each worktree is diffed against')
|
|
14
|
+
.option('--transitive', 'also report multi-hop dependency overlaps (less precise)')
|
|
15
|
+
.option('--max-distance <n>', 'max import hops for --transitive (default 5)')
|
|
16
|
+
.action(async (cmdOpts) => {
|
|
17
|
+
setupLogLevel();
|
|
18
|
+
maybeCompactBanner();
|
|
19
|
+
const format = assertFormatSupported('collisions');
|
|
20
|
+
const rootPath = getRootPath();
|
|
21
|
+
const maxDistance = cmdOpts.maxDistance !== undefined ? Number.parseInt(cmdOpts.maxDistance, 10) : undefined;
|
|
22
|
+
const report = await detectCollisions(rootPath, {
|
|
23
|
+
...(cmdOpts.baseRef ? { baseRef: cmdOpts.baseRef } : {}),
|
|
24
|
+
...(cmdOpts.transitive ? { transitive: true } : {}),
|
|
25
|
+
...(maxDistance !== undefined && Number.isFinite(maxDistance) && maxDistance > 0 ? { maxDistance } : {}),
|
|
26
|
+
});
|
|
27
|
+
if (format === 'json') {
|
|
28
|
+
console.log(JSON.stringify(report, null, 2));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
console.log('');
|
|
32
|
+
console.log(chalk.bold('Worktree collisions'));
|
|
33
|
+
console.log(chalk.dim('────────────────────────────────────────'));
|
|
34
|
+
if (!report.available) {
|
|
35
|
+
console.log(chalk.dim(` ${report.reason}`));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const rel = (p) => {
|
|
39
|
+
const r = path.relative(rootPath, p);
|
|
40
|
+
return r === '' ? '.' : r;
|
|
41
|
+
};
|
|
42
|
+
console.log(` ${report.worktrees.length} worktree(s): ${report.worktrees
|
|
43
|
+
.map((w) => `${chalk.bold(w.branch ?? '(detached)')} ${chalk.dim(`(${w.changedFileCount} changed)`)}`)
|
|
44
|
+
.join(', ')}`);
|
|
45
|
+
if (report.collisions.length === 0) {
|
|
46
|
+
console.log('');
|
|
47
|
+
console.log(` ${chalk.green('✓')} No collisions across in-flight worktrees.`);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const high = report.collisions.filter((c) => c.severity === 'high');
|
|
51
|
+
const medium = report.collisions.filter((c) => c.severity === 'medium');
|
|
52
|
+
console.log('');
|
|
53
|
+
console.log(` ${chalk.red(`${high.length} high`)} · ${chalk.yellow(`${medium.length} medium`)} collision(s)`);
|
|
54
|
+
console.log('');
|
|
55
|
+
for (const c of report.collisions) {
|
|
56
|
+
const tag = c.severity === 'high' ? chalk.red('● same-file') : chalk.yellow('● dependency');
|
|
57
|
+
console.log(` ${tag} ${chalk.dim(`[${rel(c.worktreeA)} ↔ ${rel(c.worktreeB)}]`)}`);
|
|
58
|
+
console.log(` ${c.reason}`);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=collision.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collision.js","sourceRoot":"","sources":["../../../src/cli/commands/collision.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `projscan coordinate` (4.x) — one-call swarm coordination read across
|
|
3
|
+
* in-flight worktrees: collisions + claims + merge-risk folded into a readiness
|
|
4
|
+
* verdict. `--watch` re-evaluates on an interval and re-emits only when the
|
|
5
|
+
* coordination state changes (polling, since the state spans all worktrees).
|
|
6
|
+
*/
|
|
7
|
+
export declare function registerCoordinate(): void;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { program, getRootPath, setupLogLevel, maybeCompactBanner, assertFormatSupported } from '../_shared.js';
|
|
3
|
+
import { computeCoordination, coordinationSignature, } from '../../core/coordination.js';
|
|
4
|
+
const VERDICT_COLOR = {
|
|
5
|
+
clear: chalk.green,
|
|
6
|
+
caution: chalk.yellow,
|
|
7
|
+
conflicted: chalk.red,
|
|
8
|
+
};
|
|
9
|
+
const WATCH_DEFAULT_SECONDS = 5;
|
|
10
|
+
const WATCH_MIN_SECONDS = 2;
|
|
11
|
+
const WATCH_MAX_SECONDS = 600;
|
|
12
|
+
/**
|
|
13
|
+
* `projscan coordinate` (4.x) — one-call swarm coordination read across
|
|
14
|
+
* in-flight worktrees: collisions + claims + merge-risk folded into a readiness
|
|
15
|
+
* verdict. `--watch` re-evaluates on an interval and re-emits only when the
|
|
16
|
+
* coordination state changes (polling, since the state spans all worktrees).
|
|
17
|
+
*/
|
|
18
|
+
export function registerCoordinate() {
|
|
19
|
+
program
|
|
20
|
+
.command('coordinate')
|
|
21
|
+
.description('One-call swarm coordination read (collisions + claims + merge-risk)')
|
|
22
|
+
.option('--base-ref <ref>', 'base ref each worktree is diffed against')
|
|
23
|
+
.option('--watch', 're-evaluate on an interval; re-emit only when coordination state changes')
|
|
24
|
+
.option('--interval <seconds>', `poll interval for --watch (default ${WATCH_DEFAULT_SECONDS})`)
|
|
25
|
+
.action(async (cmdOpts) => {
|
|
26
|
+
setupLogLevel();
|
|
27
|
+
maybeCompactBanner();
|
|
28
|
+
const format = assertFormatSupported('coordinate');
|
|
29
|
+
const rootPath = getRootPath();
|
|
30
|
+
const detectOptions = cmdOpts.baseRef ? { baseRef: cmdOpts.baseRef } : {};
|
|
31
|
+
if (!cmdOpts.watch) {
|
|
32
|
+
render(await computeCoordination(rootPath, detectOptions), format, false);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const intervalMs = resolveIntervalMs(cmdOpts.interval, format);
|
|
36
|
+
if (format === 'console') {
|
|
37
|
+
console.log(chalk.dim(`Watching swarm coordination (every ${intervalMs / 1000}s; Ctrl+C to stop)…`));
|
|
38
|
+
}
|
|
39
|
+
let lastSignature = '';
|
|
40
|
+
const tick = async () => {
|
|
41
|
+
try {
|
|
42
|
+
const report = await computeCoordination(rootPath, detectOptions);
|
|
43
|
+
const signature = coordinationSignature(report);
|
|
44
|
+
if (signature !== lastSignature) {
|
|
45
|
+
lastSignature = signature;
|
|
46
|
+
render(report, format, true);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
// Keep watching across transient errors (e.g. mid-rebase git state).
|
|
51
|
+
process.stderr.write(`[projscan] coordinate watch tick failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
await tick();
|
|
55
|
+
const timer = setInterval(() => void tick(), intervalMs);
|
|
56
|
+
const stop = () => {
|
|
57
|
+
clearInterval(timer);
|
|
58
|
+
process.exit(0);
|
|
59
|
+
};
|
|
60
|
+
process.on('SIGINT', stop);
|
|
61
|
+
process.on('SIGTERM', stop);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function resolveIntervalMs(raw, format) {
|
|
65
|
+
if (raw === undefined)
|
|
66
|
+
return WATCH_DEFAULT_SECONDS * 1000;
|
|
67
|
+
const seconds = Number.parseInt(raw, 10);
|
|
68
|
+
if (!Number.isFinite(seconds) || seconds < WATCH_MIN_SECONDS || seconds > WATCH_MAX_SECONDS) {
|
|
69
|
+
const message = `--interval must be ${WATCH_MIN_SECONDS}–${WATCH_MAX_SECONDS} seconds.`;
|
|
70
|
+
if (format === 'json')
|
|
71
|
+
console.log(JSON.stringify({ ok: false, error: message }, null, 2));
|
|
72
|
+
else
|
|
73
|
+
console.error(chalk.red(message));
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
return seconds * 1000;
|
|
77
|
+
}
|
|
78
|
+
function render(report, format, watch) {
|
|
79
|
+
if (format === 'json') {
|
|
80
|
+
// NDJSON in watch mode (one object per change), pretty once otherwise.
|
|
81
|
+
console.log(watch ? JSON.stringify(report) : JSON.stringify(report, null, 2));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
console.log('');
|
|
85
|
+
const heading = watch ? `Swarm coordination · ${new Date().toLocaleTimeString()}` : 'Swarm coordination';
|
|
86
|
+
console.log(chalk.bold(heading));
|
|
87
|
+
console.log(chalk.dim('────────────────────────────────────────'));
|
|
88
|
+
if (!report.available) {
|
|
89
|
+
console.log(chalk.dim(` ${report.reason}`));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
console.log(` Readiness: ${VERDICT_COLOR[report.readiness](report.readiness.toUpperCase())}`);
|
|
93
|
+
console.log('');
|
|
94
|
+
for (const line of report.summary) {
|
|
95
|
+
console.log(` • ${line}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=coordinate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordinate.js","sourceRoot":"","sources":["../../../src/cli/commands/coordinate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { program, getRootPath, setupLogLevel, maybeCompactBanner, assertFormatSupported } from '../_shared.js';
|
|
4
|
+
import { computeMergeRisk } from '../../core/mergeRisk.js';
|
|
5
|
+
/**
|
|
6
|
+
* `projscan merge-risk` (4.x) — safe integration order + risk hotspots across
|
|
7
|
+
* the repo's in-flight git worktrees. Builds on collision detection.
|
|
8
|
+
*/
|
|
9
|
+
export function registerMergeRisk() {
|
|
10
|
+
program
|
|
11
|
+
.command('merge-risk')
|
|
12
|
+
.description('Safe integration order + conflict hotspots across in-flight worktrees')
|
|
13
|
+
.option('--base-ref <ref>', 'base ref each worktree is diffed against')
|
|
14
|
+
.action(async (cmdOpts) => {
|
|
15
|
+
setupLogLevel();
|
|
16
|
+
maybeCompactBanner();
|
|
17
|
+
const format = assertFormatSupported('merge-risk');
|
|
18
|
+
const rootPath = getRootPath();
|
|
19
|
+
const report = await computeMergeRisk(rootPath, cmdOpts.baseRef ? { baseRef: cmdOpts.baseRef } : {});
|
|
20
|
+
if (format === 'json') {
|
|
21
|
+
console.log(JSON.stringify(report, null, 2));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const rel = (p) => {
|
|
25
|
+
const r = path.relative(rootPath, p);
|
|
26
|
+
return r === '' ? '.' : r;
|
|
27
|
+
};
|
|
28
|
+
console.log('');
|
|
29
|
+
console.log(chalk.bold('Merge risk'));
|
|
30
|
+
console.log(chalk.dim('────────────────────────────────────────'));
|
|
31
|
+
if (!report.available) {
|
|
32
|
+
console.log(chalk.dim(` ${report.reason}`));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
console.log(chalk.bold(' Integration order (merge cleanest first):'));
|
|
36
|
+
report.integrationOrder.forEach((step, i) => {
|
|
37
|
+
const risk = step.riskScore === 0
|
|
38
|
+
? chalk.green('clean')
|
|
39
|
+
: step.riskScore <= 2
|
|
40
|
+
? chalk.yellow(`risk ${step.riskScore}`)
|
|
41
|
+
: chalk.red(`risk ${step.riskScore}`);
|
|
42
|
+
console.log(` ${i + 1}. ${chalk.bold(step.branch ?? rel(step.worktree))} ${chalk.dim(`(${step.changedFileCount} changed, ${step.collisionCount} collision(s))`)} ${risk}`);
|
|
43
|
+
});
|
|
44
|
+
if (report.hotFiles.length > 0) {
|
|
45
|
+
console.log('');
|
|
46
|
+
console.log(chalk.bold(' Conflict hotspots (changed by multiple worktrees):'));
|
|
47
|
+
for (const h of report.hotFiles) {
|
|
48
|
+
const tag = h.severity === 'high' ? chalk.red('●') : chalk.yellow('●');
|
|
49
|
+
console.log(` ${tag} ${h.file} ${chalk.dim(`(${h.worktrees.length} worktrees)`)}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
console.log('');
|
|
54
|
+
console.log(` ${chalk.green('✓')} No files changed by multiple worktrees.`);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=mergeRisk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mergeRisk.js","sourceRoot":"","sources":["../../../src/cli/commands/mergeRisk.ts"],"names":[],"mappings":"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"}
|