devcouncil 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,26 +1,64 @@
1
1
  # DevCouncil: The Gated AI Orchestrator
2
2
 
3
3
  <p align="center">
4
- <img src="https://raw.githubusercontent.com/bharathvbcr/DevCouncil/main/src/devcouncil/assets/devcouncil_logo_premium.png" alt="DevCouncil Logo" width="300">
4
+ <img src="https://raw.githubusercontent.com/bharathvbcr/DevCouncil/main/src/devcouncil/assets/devcouncil_social_preview.jpg" alt="DevCouncil gated AI orchestration" width="720">
5
5
  </p>
6
6
 
7
7
  [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
8
8
  [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
9
9
  [![uv](https://img.shields.io/badge/managed%20by-uv-purple.svg)](https://github.com/astral-sh/uv)
10
10
 
11
- **"DevCouncil should not merely generate code. It should make AI-generated work prove that it satisfied the original intent."**
11
+ ## OpenAI Build Week 2026
12
12
 
13
- DevCouncil is a high-integrity command-line orchestration platform for AI-assisted software development. It turns AI implementation from a black-box generation task into a gated engineering workflow where every change is authorized, verified, and traceable back to a requirement.
13
+ Coding agents -- including Codex and other prompt-taking CLIs -- often claim success without proving that the change satisfied the original requirements. DevCouncil turns that claim into a gated engineering workflow: every change is scoped, verified, and traceable back to a requirement. Model confidence is not the final authority; evidence is.
14
14
 
15
- DevCouncil does not replace coding agents. Its primary native path is Claude Code, followed by Codex CLI; it also works beside OpenCode, Google Antigravity CLI, Warp/Oz, Cursor, Aider, and bring-your-own prompt-taking CLIs. It owns the plan, task scope, verification loop, repair prompts, and evidence trail.
15
+ ### What judges should see
16
+
17
+ 1. **Provider-free red-to-green evidence gate** -- install once, then watch a controlled sample fail verification and pass after a real fix, with no API keys.
18
+ 2. **Self-contained interactive code graph** -- open the packed `demo.html` artifact and navigate filters, path highlighting, and neighborhoods (no blank canvas).
19
+ 3. **Codex / MCP agent-control path** -- status, diffs, and task tools stay correct under real project-sized JSON so an agent can resume from evidence instead of chat memory.
20
+
21
+ ### Judge path (package: `devcouncil@0.4.2`)
22
+
23
+ DevCouncil supports macOS, Linux, and Windows. Requires Node.js 18+, Python 3.12+, and Git. No model provider key is needed for the deterministic demos below.
24
+
25
+ ```bash
26
+ # Install the Build Week release:
27
+ npm install -g devcouncil@0.4.2
28
+ devcouncil --help
29
+
30
+ # Core demo: red verdict -> apply fix -> green verdict (no API keys)
31
+ devcouncil-build-week-demo
32
+ # Equivalent: bash "$(npm root -g)/devcouncil/scripts/build-week-demo.sh"
33
+
34
+ # Interactive graph artifact (self-contained HTML)
35
+ mkdir -p /tmp/devcouncil-judge-demo
36
+ dev graph demo --project-root /tmp/devcouncil-judge-demo --json
37
+ # Open /tmp/devcouncil-judge-demo/.devcouncil/graph/demo.html
38
+ ```
39
+
40
+ Checkout fallback: clone the repo and run `bash scripts/build-week-demo.sh` (`uv sync --group dev` if you need a local `dev`).
41
+
42
+ ### Eligible Build Week work
43
+
44
+ DevCouncil existed before OpenAI Build Week. This submission covers only meaningful extensions on or after **July 13, 2026**. Eligible history begins at commit `6f5bd73` (baseline before eligible work: `3cfd5d1`). Major themes:
45
+
46
+ - canonical SQLite code-intelligence index, multi-language grammars, incremental watching, graph queries/community detection, and a self-contained interactive code-graph artifact (including a ForceGraph compatibility fix for the packed demo);
47
+ - stronger deterministic verification: stop gates, claim checking, diff-to-evidence coverage, task leases, PDG/corpus checks, bounded repair, machine-readable next actions;
48
+ - deeper CLI, MCP, dashboard, coding-agent, and CI integration, plus the installable npm release path aimed at `0.4.2` for judges.
49
+
50
+ During Build Week, Codex and GPT-5.6 were used as an engineering partner to map paths, challenge claims, run install/browser checks, implement focused repairs, and verify behavior. The maintainer set requirements, scope, and acceptance evidence. The primary Codex `/feedback` session ID is on the Devpost submission. This does **not** claim that all eligible code was authored exclusively by Codex/GPT-5.6.
51
+
52
+ See [docs/build-week-demo.md](docs/build-week-demo.md) for the provider-free demo walkthrough.
16
53
 
17
54
  ## Documentation
18
55
 
56
+ - [Build Week demo](docs/build-week-demo.md): provider-free red-to-green judge script.
19
57
  - [Quickstart](docs/quickstart.md): shortest install-to-first-task path.
20
58
  - [Daily workflow](docs/workflow.md): manual sidecar loop, verification, repair, rollback, and `dev watch`.
21
59
  - [Coding CLI integration](docs/coding-cli-integration.md): tiers, Claude Code, Codex, OpenCode, Antigravity, Cursor, Grok Build, Aider, MCP, hooks, stop gate / claim checks, and automated executors (Gemini deprecated).
22
60
  - [CLI command reference](docs/cli-reference.md): available `dev` commands.
23
- - [Repo map & code graph](docs/code-graph.md): `dev map` / `dev graph` navigation, dead code, blast radius, PDG (opt-in), HTML visualizer.
61
+ - [Repo map & code graph](docs/code-graph.md): `dev map` / `dev graph` -- navigation, dead code, blast radius, PDG (opt-in), HTML visualizer.
24
62
  - [Corpus side index](docs/corpus.md): `dev corpus` for docs/PDFs/images and optional verify gates.
25
63
  - [Hero loop](docs/hero-loop.md): certified Claude Code + MCP closed loop, leases, rigor, and `dev check --verify` on-ramp.
26
64
  - [Architecture](docs/architecture.md): components, artifact graph, state machine, gating policy, and gated execution.
@@ -152,7 +190,11 @@ DevCouncil is an application layer around coding agents. It does not just emit p
152
190
  ### Workflow Features
153
191
 
154
192
  - **Repository onboarding:** `dev setup` initializes `.devcouncil/`, generates the repo map + `AGENTS.md`/`CLAUDE.md` guides, scaffolds applicable engineering skills, runs environment checks, offers integration setup, and prints the next useful commands. Use `--skip-map` / `--skip-skills` to opt out, or `--scaffold-ci` to also write a starter GitHub Actions workflow. **`dev boot "goal"`** chains setup, `dev integrate --apply`, optional CI scaffold flags, and `dev go` in one command (see [quickstart](docs/quickstart.md)).
155
- - **Repository mapping:** `dev map` writes `.devcouncil/repo_map.json` and a symbol-level `.devcouncil/graph/code_graph.json`, identifies important files and subsystems, filters generated/temp files, and keeps managed `AGENTS.md` / `CLAUDE.md` workspace guides synchronized. Subsystems, entry points, neighbors, and important surfaces are inferred generically for **any** repository — grouped from the directory tree and ranked by an import-graph in-degree. Freshness uses git HEAD, tracked-file hash, and a content fingerprint so plain edits mark the map stale; a **missing map is stale** (fail-closed on hard rigor). Post-tool-use hooks and `dev map --watch` refresh incrementally. Unified analyze entry: `dev graph ingest`. Query with `dev graph query|trace|dead|search|cypher|html`. Liveness lists are capped at 5000 per list and 256 dependents per file (truncation metadata when hit). The map is also generated automatically on first init.
193
+ - **Repository mapping:** `dev map` writes `.devcouncil/repo_map.json` and a symbol-level `.devcouncil/graph/code_graph.json`, identifies important files and subsystems, filters generated/temp files, and keeps managed `AGENTS.md` / `CLAUDE.md` workspace guides synchronized. Subsystems, entry points, neighbors, and important surfaces are inferred generically for **any** repository — grouped from the directory tree and ranked by an import-graph in-degree. Freshness uses git HEAD, tracked-file hash, and a content fingerprint so plain edits mark the map stale; a **missing map is stale** (fail-closed on hard rigor). Post-tool-use hooks and `dev map --watch` refresh incrementally. Unified analyze entry: `dev graph ingest`. Query with `dev graph query|trace|dead|search|cypher|html`. Liveness lists are capped at 5000 per list and 256 dependents per file (truncation metadata when hit). The map is also generated automatically on first init. (See [docs/code-graph.md](docs/code-graph.md) for details).
194
+
195
+ <p align="center">
196
+ <img src="docs/graph_preview.png" alt="Repository Code Graph Preview" width="600">
197
+ </p>
156
198
  - **Engineering skills:** `dev skills` lists the bundled skills and shows which apply to the repository; `dev skills scaffold` writes them into `.claude/skills/<name>/SKILL.md`. A merged always-on `core-engineering` skill (think-before-coding, simplicity, surgical changes, goal-driven execution, evidence-grounded communication) plus domain skills (Android, iOS, Windows, web, AI training) that brief the agent on current SDKs, deprecations, and tooling before coding. Applicable skills are also embedded into `dev prompt` output.
157
199
  - **CI scaffolding:** `dev scaffold-ci` writes a starter `.github/workflows/devcouncil.yml` derived from the configured test/lint/typecheck commands, filtered to the detected language stack; it never overwrites existing CI unless `--force`. Run `dev scaffold-ci --evidence` to generate `.github/workflows/devcouncil-evidence.yml` which automates verifying PRs and uploading evidence JSON and HTML reports.
158
200
  - **Planning council:** `dev plan` turns a goal into requirements, acceptance criteria, assumptions, critique findings, and executable tasks. When advisory gaps remain, the project stays in `AWAITING_USER_DECISIONS` until you run `dev approve` (or `dev e2e`/`dev go --force`).
@@ -0,0 +1,13 @@
1
+ # Build Week calculator sample
2
+
3
+ Tiny provider-free sample used by `scripts/build-week-demo.sh`.
4
+
5
+ | File | Role |
6
+ |---|---|
7
+ | `calc.py` | Correct calculator (green / repaired state) |
8
+ | `broken_calc.py` | Deliberate `sub` bug used for the red evidence-gate pass |
9
+ | `test_calc.py` | Regression checks proving `add` and `sub` |
10
+
11
+ The demo script copies these into an isolated git repository, runs
12
+ `dev check --verify` (no API keys), applies the repair, and reruns to a
13
+ compiled zero-gap pass. See [docs/build-week-demo.md](../../docs/build-week-demo.md) and the fixture index in [examples/README.md](../README.md).
@@ -0,0 +1,11 @@
1
+ """Deliberately buggy calculator used only for the demo's red evidence-gate pass."""
2
+
3
+
4
+ def add(a: int, b: int) -> int:
5
+ """Return the sum of ``a`` and ``b``."""
6
+ return a + b
7
+
8
+
9
+ def sub(a: int, b: int) -> int:
10
+ """Intended to return ``a`` minus ``b`` — intentionally wrong for the demo."""
11
+ return a + b
@@ -0,0 +1,11 @@
1
+ """Tiny calculator sample for the provider-free Build Week demo."""
2
+
3
+
4
+ def add(a: int, b: int) -> int:
5
+ """Return the sum of ``a`` and ``b``."""
6
+ return a + b
7
+
8
+
9
+ def sub(a: int, b: int) -> int:
10
+ """Return ``a`` minus ``b``."""
11
+ return a - b
@@ -0,0 +1,21 @@
1
+ """Regression checks for the Build Week calculator demo."""
2
+
3
+ from calc import add, sub
4
+
5
+
6
+ def test_add() -> None:
7
+ assert add(2, 3) == 5
8
+
9
+
10
+ def test_sub() -> None:
11
+ assert sub(5, 3) == 2
12
+
13
+
14
+ def main() -> None:
15
+ test_add()
16
+ test_sub()
17
+ print("ok")
18
+
19
+
20
+ if __name__ == "__main__":
21
+ main()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devcouncil",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Gated orchestrator for AI-assisted software development",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/bharathvbcr/DevCouncil#readme",
@@ -21,7 +21,8 @@
21
21
  ],
22
22
  "bin": {
23
23
  "devcouncil": "bin/devcouncil.js",
24
- "dev": "bin/devcouncil.js"
24
+ "dev": "bin/devcouncil.js",
25
+ "devcouncil-build-week-demo": "scripts/build-week-demo.sh"
25
26
  },
26
27
  "files": [
27
28
  "bin/",
@@ -35,6 +36,8 @@
35
36
  "packages/codeintel-grammars/pyproject.toml",
36
37
  "packages/codeintel-grammars/hatch_build.py",
37
38
  "packages/codeintel-grammars/src/devcouncil_codeintel_grammars/__init__.py",
39
+ "scripts/build-week-demo.sh",
40
+ "examples/build-week-demo/**",
38
41
  "pyproject.toml",
39
42
  "uv.lock",
40
43
  "README.md",
@@ -47,6 +50,8 @@
47
50
  "pack:check": "npm pack --dry-run",
48
51
  "smoke:wheel": "uv run python scripts/check-wheel-assets.py",
49
52
  "smoke:package": "node scripts/npm-runtime-smoke.mjs",
53
+ "smoke:registry": "node scripts/npm-registry-smoke.mjs",
54
+ "demo:build-week": "bash scripts/build-week-demo.sh",
50
55
  "test": "uv run pytest",
51
56
  "lint": "uv run ruff check .",
52
57
  "typecheck": "uv run mypy src",
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "devcouncil"
3
- version = "0.4.0"
3
+ version = "0.4.2"
4
4
  description = "Gated orchestrator for AI-assisted software development"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -11,7 +11,7 @@ dependencies = [
11
11
  "pyyaml>=6.0.1",
12
12
  "sqlmodel>=0.0.19",
13
13
  "httpx>=0.27.0",
14
- "gitpython>=3.1.49",
14
+ "gitpython>=3.1.53",
15
15
  "mcp>=1.27.2",
16
16
  "gepa>=0.1.1",
17
17
  "watchdog>=4.0.0",
@@ -0,0 +1,200 @@
1
+ #!/usr/bin/env bash
2
+ # Provider-free red→green evidence-gate demo for OpenAI Build Week judges.
3
+ #
4
+ # Creates an isolated calculator git repo, runs `dev check --verify` against a
5
+ # deliberately buggy change (blocking / red), applies the real repair + regression
6
+ # test, then reruns to a compiled zero-gap (green) pass. No API keys required.
7
+ #
8
+ # Usage (checkout):
9
+ # bash scripts/build-week-demo.sh
10
+ #
11
+ # From npm global install (preferred judge path):
12
+ # npm install -g devcouncil@0.4.2
13
+ # devcouncil-build-week-demo
14
+ #
15
+ # Optional:
16
+ # BUILD_WEEK_DEMO_ROOT=/tmp/my-demo bash scripts/build-week-demo.sh
17
+
18
+ set -euo pipefail
19
+
20
+ REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
21
+ SAMPLE_DIR="${REPO_ROOT}/examples/build-week-demo"
22
+ GOAL='sub returns a - b'
23
+ TEST_CMD='python test_calc.py'
24
+
25
+ if [[ ! -f "${SAMPLE_DIR}/calc.py" || ! -f "${SAMPLE_DIR}/broken_calc.py" || ! -f "${SAMPLE_DIR}/test_calc.py" ]]; then
26
+ echo "error: missing sample files under ${SAMPLE_DIR}" >&2
27
+ exit 2
28
+ fi
29
+
30
+ resolve_dev() {
31
+ if [[ -n "${DEVCOUNCIL_DEV_BIN:-}" && -x "${DEVCOUNCIL_DEV_BIN}" ]]; then
32
+ printf '%s\n' "${DEVCOUNCIL_DEV_BIN}"
33
+ return
34
+ fi
35
+ if [[ -x "${REPO_ROOT}/.venv/bin/dev" ]]; then
36
+ printf '%s\n' "${REPO_ROOT}/.venv/bin/dev"
37
+ return
38
+ fi
39
+ if command -v dev >/dev/null 2>&1; then
40
+ command -v dev
41
+ return
42
+ fi
43
+ if [[ -x "${REPO_ROOT}/.venv/bin/python" ]]; then
44
+ printf '%s\n' "${REPO_ROOT}/.venv/bin/python -m devcouncil"
45
+ return
46
+ fi
47
+ if command -v python3 >/dev/null 2>&1; then
48
+ printf '%s\n' "python3 -m devcouncil"
49
+ return
50
+ fi
51
+ echo "error: could not find a DevCouncil CLI (dev / python -m devcouncil)" >&2
52
+ exit 2
53
+ }
54
+
55
+ DEV_BIN="$(resolve_dev)"
56
+ # shellcheck disable=SC2206
57
+ DEV=( ${DEV_BIN} )
58
+
59
+ PROVIDER_FREE_ENV=(
60
+ -u OPENAI_API_KEY
61
+ -u ANTHROPIC_API_KEY
62
+ -u OPENROUTER_API_KEY
63
+ -u GEMINI_API_KEY
64
+ -u GOOGLE_API_KEY
65
+ -u AZURE_OPENAI_API_KEY
66
+ -u COHERE_API_KEY
67
+ -u MISTRAL_API_KEY
68
+ -u GROQ_API_KEY
69
+ -u DEEPSEEK_API_KEY
70
+ -u TOGETHER_API_KEY
71
+ -u FIREWORKS_API_KEY
72
+ -u XAI_API_KEY
73
+ -u DEVCOUNCIL_API_KEY
74
+ )
75
+
76
+ run_check() {
77
+ local label="$1"
78
+ echo
79
+ echo "────────────────────────────────────────────────────────────"
80
+ echo " ${label}"
81
+ echo "────────────────────────────────────────────────────────────"
82
+ # Keep set +e through return: a non-zero return under set -e aborts the script.
83
+ set +e
84
+ env "${PROVIDER_FREE_ENV[@]}" "${DEV[@]}" check --verify \
85
+ --project-root "${DEMO_ROOT}" \
86
+ --goal "${GOAL}" \
87
+ --test "${TEST_CMD}"
88
+ local rc=$?
89
+ return "${rc}"
90
+ }
91
+
92
+ if [[ -n "${BUILD_WEEK_DEMO_ROOT:-}" ]]; then
93
+ DEMO_ROOT="${BUILD_WEEK_DEMO_ROOT}"
94
+ rm -rf "${DEMO_ROOT}"
95
+ mkdir -p "${DEMO_ROOT}"
96
+ else
97
+ _tmp="${TMPDIR:-/tmp}"
98
+ DEMO_ROOT="$(mktemp -d "${_tmp%/}/devcouncil-build-week-demo.XXXXXX")"
99
+ unset _tmp
100
+ fi
101
+
102
+ echo "DevCouncil Build Week demo"
103
+ echo " CLI: ${DEV_BIN}"
104
+ echo " Sample templates: ${SAMPLE_DIR}"
105
+ echo " Generated repo: ${DEMO_ROOT}"
106
+ echo
107
+ echo "Judges: leave this path open to inspect the repaired working tree."
108
+
109
+ cd "${DEMO_ROOT}"
110
+ git init -q
111
+ git config user.email "build-week-demo@devcouncil.local"
112
+ git config user.name "DevCouncil Build Week Demo"
113
+
114
+ cat > calc.py <<'EOF'
115
+ def add(a: int, b: int) -> int:
116
+ return a + b
117
+ EOF
118
+ git add calc.py
119
+ git commit -q -m "baseline: add()"
120
+
121
+ cp "${SAMPLE_DIR}/broken_calc.py" calc.py
122
+ cp "${SAMPLE_DIR}/test_calc.py" test_calc.py
123
+
124
+ echo
125
+ echo ">>> Phase 1: RED — expect a blocking evidence-gate failure"
126
+ START_TS="${SECONDS}"
127
+ set +e
128
+ run_check "RED verdict (blocking gaps expected)"
129
+ RED_RC=$?
130
+ set -e
131
+
132
+ if [[ "${RED_RC}" -eq 0 ]]; then
133
+ echo
134
+ echo "error: expected RED (non-zero) from dev check --verify, got exit 0" >&2
135
+ echo "Generated repo left at: ${DEMO_ROOT}" >&2
136
+ exit 1
137
+ fi
138
+
139
+ echo
140
+ echo "RED confirmed (exit ${RED_RC}): blocking gaps — change is not verified."
141
+
142
+ cp "${SAMPLE_DIR}/calc.py" calc.py
143
+
144
+ echo
145
+ echo ">>> Phase 2: GREEN — apply repair + regression test, expect zero-gap pass"
146
+ set +e
147
+ run_check "GREEN verdict (compiled, zero blocking gaps expected)"
148
+ GREEN_RC=$?
149
+ set -e
150
+
151
+ if [[ "${GREEN_RC}" -ne 0 ]]; then
152
+ echo
153
+ echo "error: expected GREEN (exit 0) after repair, got exit ${GREEN_RC}" >&2
154
+ echo "Generated repo left at: ${DEMO_ROOT}" >&2
155
+ exit 1
156
+ fi
157
+
158
+ GREEN_JSON_FILE="${DEMO_ROOT}/.devcouncil-demo-green.json"
159
+ set +e
160
+ env "${PROVIDER_FREE_ENV[@]}" "${DEV[@]}" check --verify \
161
+ --project-root "${DEMO_ROOT}" \
162
+ --goal "${GOAL}" \
163
+ --test "${TEST_CMD}" \
164
+ --json > "${GREEN_JSON_FILE}"
165
+ JSON_RC=$?
166
+ set -e
167
+ if [[ "${JSON_RC}" -ne 0 ]]; then
168
+ echo "error: green JSON re-check failed with exit ${JSON_RC}" >&2
169
+ exit 1
170
+ fi
171
+
172
+ python3 - "${GREEN_JSON_FILE}" <<'PY'
173
+ import json, sys
174
+ from pathlib import Path
175
+ payload = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
176
+ if not payload.get("verified", False):
177
+ raise SystemExit("green JSON missing verified=true")
178
+ if int(payload.get("blocking_gap_count", 1)) != 0:
179
+ raise SystemExit(f"green JSON still has blocking gaps: {payload.get('blocking_gap_count')}")
180
+ if int(payload.get("gap_count", 1)) != 0:
181
+ raise SystemExit(f"green JSON still has gaps: {payload.get('gap_count')}")
182
+ mode = str(payload.get("verification_mode", ""))
183
+ if mode != "compiled":
184
+ raise SystemExit(f"expected verification_mode=compiled, got {mode!r}")
185
+ print(f"JSON confirmed: verified=true, gaps=0, mode={mode}")
186
+ PY
187
+
188
+ ELAPSED=$((SECONDS - START_TS))
189
+ echo
190
+ echo "════════════════════════════════════════════════════════════"
191
+ echo " Demo complete: RED then GREEN in ${ELAPSED}s"
192
+ echo " Generated repository (inspect me):"
193
+ echo " ${DEMO_ROOT}"
194
+ echo "════════════════════════════════════════════════════════════"
195
+
196
+ if [[ "${ELAPSED}" -gt 60 ]]; then
197
+ echo "warning: demo took ${ELAPSED}s (>60s target after package install)" >&2
198
+ fi
199
+
200
+ exit 0
@@ -210,23 +210,20 @@ def status(
210
210
 
211
211
  if payload["pending_signal_items"]:
212
212
  table = Table(title="Pending Agent Responses")
213
- table.add_column("Client", style="cyan")
213
+ table.add_column("ID", style="cyan")
214
+ table.add_column("Client")
214
215
  table.add_column("Task")
215
- table.add_column("Transcript", overflow="fold")
216
- table.add_column("Review Command", overflow="fold")
217
216
  for signal in payload["pending_signal_items"]:
218
217
  table.add_row(
218
+ signal.get("id") or "",
219
219
  signal.get("client") or "",
220
220
  signal.get("task_id") or "(unscoped)",
221
- signal.get("transcript_path") or "",
222
- signal.get("review_command") or "",
223
221
  )
224
222
  console.print(table)
225
- commands = [signal.get("review_command") for signal in payload["pending_signal_items"] if signal.get("review_command")]
226
- if commands:
227
- console.print("[cyan]Pending review commands:[/cyan]")
228
- for command in commands:
229
- console.print(command)
223
+ console.print(
224
+ "[dim]Sensitive transcript paths and review commands: "
225
+ "`dev watch signals` / `dev watch review`.[/dim]"
226
+ )
230
227
 
231
228
 
232
229
  @app.command("resolve")
@@ -47,7 +47,7 @@ def _vendor_js() -> str:
47
47
  "nodeVal:function(){return this},linkColor:function(){return this},"
48
48
  "linkDirectionalParticles:function(){return this},"
49
49
  "linkDirectionalParticleWidth:function(){return this},"
50
- "onNodeClick:function(){return this},onNodeDblClick:function(){return this},"
50
+ "onNodeClick:function(){return this},"
51
51
  "width:function(){return this},height:function(){return this},_missing:true};};"
52
52
  )
53
53
 
@@ -321,6 +321,8 @@ label {{ color:var(--muted); font-size:12px; display:block; }}
321
321
  label.inline {{ display:flex; align-items:center; gap:6px; margin:4px 0; }}
322
322
  label.inline input {{ width:auto; margin:0; }}
323
323
  #detail {{ margin-top:8px; font-size:12px; color:var(--muted); white-space:pre-wrap; }}
324
+ .interaction-hint {{ margin:8px 0; padding:8px; border:1px solid #334155; border-radius:4px; background:#0f1419; color:var(--fg); font-size:12px; line-height:1.45; }}
325
+ #counts {{ margin:8px 0 4px; font-size:12px; color:var(--accent); font-weight:600; }}
324
326
  .flag-dead {{ color:var(--dead); }}
325
327
  .badge-entry {{ color:var(--entry); font-weight:600; }}
326
328
  .list {{ list-style:none; padding:0; margin:0; font-size:12px; }}
@@ -342,6 +344,8 @@ label.inline input {{ width:auto; margin:0; }}
342
344
  </div>
343
345
  <div id="panel-graph" class="panel active">
344
346
  <h1>DevCouncil Code Graph</h1>
347
+ <div id="counts" aria-live="polite">Nodes: -- · Edges: -- · Filtered: --</div>
348
+ <div class="interaction-hint" id="interactionHint"><strong>Click</strong> a node for details. <strong>Select two nodes</strong> to highlight the shortest path. <strong>Double-click</strong> a node to expand its neighborhood.</div>
345
349
  <label>Mode</label>
346
350
  <select id="mode"><option value="file">File-level</option><option value="symbol">Symbol-level</option></select>
347
351
  <label>Search</label>
@@ -360,7 +364,7 @@ label.inline input {{ width:auto; margin:0; }}
360
364
  <label>Dead confidence</label>
361
365
  <select id="deadConf"><option value="">(any)</option><option>extracted</option><option>inferred</option><option>ambiguous</option></select>
362
366
  <div class="row"><button class="primary" id="reset">Reset view</button><button class="primary" id="clearPath">Clear path</button></div>
363
- <div id="detail">Click a node for details. Select two nodes to highlight shortest path. Double-click to expand neighborhood.</div>
367
+ <div id="detail" class="muted">Select a node to inspect callers, callees, and path state.</div>
364
368
  </div>
365
369
  <div id="panel-dead" class="panel">
366
370
  <h1>Dead code</h1>
@@ -565,7 +569,16 @@ const g = Graph(elem)
565
569
  if (pathHighlight.size && pathHighlight.has(s) && pathHighlight.has(t)) return 3;
566
570
  return 1.5;
567
571
  }})
568
- .onNodeClick(n => {{
572
+ .onNodeClick((n, event) => {{
573
+ // ForceGraph exposes click events but no double-click chain method.
574
+ // The native event detail increments for a double click, so use it to
575
+ // preserve neighbor expansion without depending on a nonexistent API.
576
+ if (event && event.detail >= 2) {{
577
+ const links = activePayload().links;
578
+ expandIds = neighborExpand(n.id, links, 1);
579
+ redraw();
580
+ return;
581
+ }}
569
582
  showDetail(n);
570
583
  if (selected.length === 1 && selected[0] === n.id) {{ selected = []; pathHighlight = new Set(); redraw(); return; }}
571
584
  if (selected.length >= 2) selected = [];
@@ -577,18 +590,31 @@ const g = Graph(elem)
577
590
  showDetail(n);
578
591
  }}
579
592
  redraw();
580
- }})
581
- .onNodeDblClick(n => {{
582
- const links = activePayload().links;
583
- expandIds = neighborExpand(n.id, links, 1);
584
- redraw();
585
593
  }});
586
594
 
595
+ function updateCounts(fd) {{
596
+ const totalNodes = (activePayload().nodes || []).length;
597
+ const totalEdges = (activePayload().links || []).length;
598
+ const shownNodes = (fd.nodes || []).length;
599
+ const shownEdges = (fd.links || []).length;
600
+ const el = document.getElementById("counts");
601
+ if (el) el.textContent = "Nodes: " + shownNodes + " / " + totalNodes + " · Edges: " + shownEdges + " / " + totalEdges + " · Filtered: " + Math.max(0, totalNodes - shownNodes) + (expandIds ? " · neighborhood focus" : "");
602
+ }}
603
+
604
+ function fitView() {{
605
+ if (g && typeof g.zoomToFit === 'function') {{
606
+ requestAnimationFrame(() => {{
607
+ try {{ g.zoomToFit(400, 40); }} catch (err) {{ /* vendor stub */ }}
608
+ }});
609
+ }}
610
+ }}
611
+
587
612
  function redraw() {{
588
- const fd = filtered();
589
- g.graphData(fd);
590
- g.nodeAutoColorBy(n => colorKey(n));
591
- g.width(elem.clientWidth).height(elem.clientHeight);
613
+ const fd = filtered();
614
+ g.graphData(fd);
615
+ g.nodeAutoColorBy(n => colorKey(n));
616
+ g.width(elem.clientWidth).height(elem.clientHeight);
617
+ updateCounts(fd);
592
618
  }}
593
619
 
594
620
  function renderDeadList() {{
@@ -720,6 +746,7 @@ document.getElementById('reset').addEventListener('click', () => {{
720
746
  selected = [];
721
747
  pathHighlight = new Set();
722
748
  redraw();
749
+ fitView();
723
750
  }});
724
751
  document.getElementById('clearPath').addEventListener('click', () => {{
725
752
  selected = [];
@@ -730,6 +757,7 @@ window.addEventListener('resize', () => g.width(elem.clientWidth).height(elem.cl
730
757
  if (!g || typeof g.zoomToFit !== 'function') document.getElementById('vendorWarn').style.display = 'block';
731
758
  refillArea();
732
759
  redraw();
760
+ fitView();
733
761
  renderDeadList();
734
762
  renderCommunities();
735
763
  renderProcesses();
@@ -38,6 +38,6 @@ async def handle_cli(root: Path, arguments: dict) -> list[TextContent]:
38
38
  if forbidden:
39
39
  return error_text("forbidden flag(s) through MCP: " + ", ".join(forbidden), code="forbidden_flags", flags=forbidden)
40
40
  try:
41
- return json_text(run_cli_command(args, root))
41
+ return json_text(run_cli_command(args, root, truncate=True))
42
42
  except Exception as exc:
43
43
  return error_text(str(exc), code="cli_execution_error")