codeforerunner 0.4.4__tar.gz → 0.4.5__tar.gz
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.
- {codeforerunner-0.4.4/src/codeforerunner.egg-info → codeforerunner-0.4.5}/PKG-INFO +16 -7
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/README.md +15 -6
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/pyproject.toml +2 -2
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/cli.py +32 -26
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/config.py +1 -1
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/doctor.py +1 -1
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/installer.py +4 -17
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/mcp_server.py +25 -26
- codeforerunner-0.4.5/src/codeforerunner/prompt_session.py +53 -0
- codeforerunner-0.4.5/src/codeforerunner/prompts/tasks/arch-review.md +121 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/prompts/tasks/init-agent-onboarding.md +14 -1
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/prompts/tasks/scan.md +2 -0
- codeforerunner-0.4.5/src/codeforerunner/tasks.json +30 -0
- codeforerunner-0.4.5/src/codeforerunner/tasks.py +59 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5/src/codeforerunner.egg-info}/PKG-INFO +16 -7
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner.egg-info/SOURCES.txt +7 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/tests/test_check.py +3 -2
- codeforerunner-0.4.5/tests/test_check_versions.py +126 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/tests/test_cli.py +57 -4
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/tests/test_doctor.py +1 -1
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/tests/test_installer.py +39 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/tests/test_mcp_server.py +88 -26
- codeforerunner-0.4.5/tests/test_prompt_session.py +50 -0
- codeforerunner-0.4.5/tests/test_tasks.py +85 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/LICENSE.md +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/setup.cfg +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/__init__.py +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/bundle.py +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/check.py +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/prompts/partials/context-format.md +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/prompts/partials/output-rules.md +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/prompts/partials/stack-hints.md +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/prompts/system/base.md +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/prompts/tasks/api-docs.md +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/prompts/tasks/audit.md +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/prompts/tasks/changelog.md +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/prompts/tasks/check.md +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/prompts/tasks/diagrams.md +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/prompts/tasks/flows.md +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/prompts/tasks/readme.md +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/prompts/tasks/refresh.md +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/prompts/tasks/review.md +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/prompts/tasks/stack-docs.md +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner/prompts/tasks/version-audit.md +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner.egg-info/dependency_links.txt +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner.egg-info/entry_points.txt +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner.egg-info/requires.txt +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/src/codeforerunner.egg-info/top_level.txt +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/tests/test_bundle.py +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/tests/test_check_config_integration.py +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/tests/test_config.py +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/tests/test_examples.py +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/tests/test_hooks_manifest.py +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/tests/test_validate_codex_marketplace.py +0 -0
- {codeforerunner-0.4.4 → codeforerunner-0.4.5}/tests/test_workflows_yaml.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codeforerunner
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.5
|
|
4
4
|
Summary: Model-agnostic repository documentation tooling (prompt-first; thin CLI).
|
|
5
5
|
Author: Derek Palmer
|
|
6
6
|
License-Expression: LicenseRef-Codeforerunner-SAL-0.1
|
|
@@ -29,7 +29,7 @@ Dynamic: license-file
|
|
|
29
29
|
|
|
30
30
|
# codeForerunner
|
|
31
31
|
|
|
32
|
-
[](https://socket.dev/npm/package/codeforerunner)
|
|
33
33
|
|
|
34
34
|
Model-agnostic repository documentation tooling. Ships a prompt pack for codebase analysis and doc generation, a thin Python CLI, an MCP server, drift-detection rules that keep docs honest — and native slash-command skills for Claude Code, Codex, Gemini CLI, and other agent CLIs.
|
|
35
35
|
|
|
@@ -44,10 +44,15 @@ Install forerunner's prompt pack as skills into your agent CLI. Each documentati
|
|
|
44
44
|
# One-liner (auto-detects Claude Code, Codex, Gemini CLI)
|
|
45
45
|
curl -fsSL https://raw.githubusercontent.com/derek-palmer/codeforerunner/main/install.sh | bash
|
|
46
46
|
|
|
47
|
+
# npm
|
|
48
|
+
npx -y codeforerunner
|
|
49
|
+
npm install -g codeforerunner
|
|
50
|
+
|
|
47
51
|
# Windows
|
|
48
52
|
irm https://raw.githubusercontent.com/derek-palmer/codeforerunner/main/install.ps1 | iex
|
|
49
53
|
|
|
50
|
-
# Via
|
|
54
|
+
# Via Python CLI
|
|
55
|
+
pip install codeforerunner
|
|
51
56
|
forerunner install --all claude
|
|
52
57
|
forerunner install --all codex
|
|
53
58
|
```
|
|
@@ -70,6 +75,7 @@ Then in your agent:
|
|
|
70
75
|
| `/forerunner-api-docs` | `api-docs` | Generate API reference docs |
|
|
71
76
|
| `/forerunner-diagrams` | `diagrams` | Generate Mermaid architecture diagrams |
|
|
72
77
|
| `/forerunner-flows` | `flows` | Document system flows |
|
|
78
|
+
| `/forerunner-arch-review` | `arch-review` | Rank architecture improvement candidates, inspired by Matt Pocock's [`/improve-codebase-architecture`](https://github.com/mattpocock/skills/tree/main/skills/engineering/improve-codebase-architecture) |
|
|
73
79
|
| `/forerunner-stack-docs` | `stack-docs` | Stack-specific developer docs |
|
|
74
80
|
| `/forerunner-version-audit` | `version-audit` | Audit pinned versions vs EOL |
|
|
75
81
|
| `/forerunner-check` | `check` | Check docs for staleness |
|
|
@@ -118,6 +124,9 @@ pip install codeforerunner
|
|
|
118
124
|
# Install skills into Claude Code
|
|
119
125
|
curl -fsSL https://raw.githubusercontent.com/derek-palmer/codeforerunner/main/install.sh | bash
|
|
120
126
|
|
|
127
|
+
# Or via npm
|
|
128
|
+
# npx -y codeforerunner
|
|
129
|
+
|
|
121
130
|
# In Claude Code:
|
|
122
131
|
# /forerunner-scan → scans your repo
|
|
123
132
|
# /forerunner-readme → generates README.md
|
|
@@ -139,7 +148,7 @@ jobs:
|
|
|
139
148
|
runs-on: ubuntu-latest
|
|
140
149
|
steps:
|
|
141
150
|
- uses: actions/checkout@v6.0.2
|
|
142
|
-
- uses: derek-palmer/codeforerunner@v0.4.
|
|
151
|
+
- uses: derek-palmer/codeforerunner@v0.4.5
|
|
143
152
|
with:
|
|
144
153
|
fail-on-drift: "true" # set "false" to warn-only
|
|
145
154
|
```
|
|
@@ -224,13 +233,13 @@ prompts/
|
|
|
224
233
|
├── scan.md api-docs.md audit.md
|
|
225
234
|
├── readme.md diagrams.md changelog.md
|
|
226
235
|
├── check.md flows.md version-audit.md
|
|
227
|
-
├── review.md stack-docs.md
|
|
236
|
+
├── review.md stack-docs.md arch-review.md
|
|
237
|
+
├── refresh.md
|
|
228
238
|
└── init-agent-onboarding.md
|
|
229
239
|
```
|
|
230
240
|
|
|
231
|
-
## Docs
|
|
241
|
+
## Docs
|
|
232
242
|
|
|
233
|
-
- `SPEC.md` — canonical phase/task tracker
|
|
234
243
|
- `docs/getting-started.md` — manual prompt use
|
|
235
244
|
- `docs/prompt-guide.md` — how system, partial, and task prompts compose
|
|
236
245
|
- `docs/editor-agent-setup.md` — adapting prompts to local agents
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# codeForerunner
|
|
4
4
|
|
|
5
|
-
[](https://socket.dev/npm/package/codeforerunner)
|
|
6
6
|
|
|
7
7
|
Model-agnostic repository documentation tooling. Ships a prompt pack for codebase analysis and doc generation, a thin Python CLI, an MCP server, drift-detection rules that keep docs honest — and native slash-command skills for Claude Code, Codex, Gemini CLI, and other agent CLIs.
|
|
8
8
|
|
|
@@ -17,10 +17,15 @@ Install forerunner's prompt pack as skills into your agent CLI. Each documentati
|
|
|
17
17
|
# One-liner (auto-detects Claude Code, Codex, Gemini CLI)
|
|
18
18
|
curl -fsSL https://raw.githubusercontent.com/derek-palmer/codeforerunner/main/install.sh | bash
|
|
19
19
|
|
|
20
|
+
# npm
|
|
21
|
+
npx -y codeforerunner
|
|
22
|
+
npm install -g codeforerunner
|
|
23
|
+
|
|
20
24
|
# Windows
|
|
21
25
|
irm https://raw.githubusercontent.com/derek-palmer/codeforerunner/main/install.ps1 | iex
|
|
22
26
|
|
|
23
|
-
# Via
|
|
27
|
+
# Via Python CLI
|
|
28
|
+
pip install codeforerunner
|
|
24
29
|
forerunner install --all claude
|
|
25
30
|
forerunner install --all codex
|
|
26
31
|
```
|
|
@@ -43,6 +48,7 @@ Then in your agent:
|
|
|
43
48
|
| `/forerunner-api-docs` | `api-docs` | Generate API reference docs |
|
|
44
49
|
| `/forerunner-diagrams` | `diagrams` | Generate Mermaid architecture diagrams |
|
|
45
50
|
| `/forerunner-flows` | `flows` | Document system flows |
|
|
51
|
+
| `/forerunner-arch-review` | `arch-review` | Rank architecture improvement candidates, inspired by Matt Pocock's [`/improve-codebase-architecture`](https://github.com/mattpocock/skills/tree/main/skills/engineering/improve-codebase-architecture) |
|
|
46
52
|
| `/forerunner-stack-docs` | `stack-docs` | Stack-specific developer docs |
|
|
47
53
|
| `/forerunner-version-audit` | `version-audit` | Audit pinned versions vs EOL |
|
|
48
54
|
| `/forerunner-check` | `check` | Check docs for staleness |
|
|
@@ -91,6 +97,9 @@ pip install codeforerunner
|
|
|
91
97
|
# Install skills into Claude Code
|
|
92
98
|
curl -fsSL https://raw.githubusercontent.com/derek-palmer/codeforerunner/main/install.sh | bash
|
|
93
99
|
|
|
100
|
+
# Or via npm
|
|
101
|
+
# npx -y codeforerunner
|
|
102
|
+
|
|
94
103
|
# In Claude Code:
|
|
95
104
|
# /forerunner-scan → scans your repo
|
|
96
105
|
# /forerunner-readme → generates README.md
|
|
@@ -112,7 +121,7 @@ jobs:
|
|
|
112
121
|
runs-on: ubuntu-latest
|
|
113
122
|
steps:
|
|
114
123
|
- uses: actions/checkout@v6.0.2
|
|
115
|
-
- uses: derek-palmer/codeforerunner@v0.4.
|
|
124
|
+
- uses: derek-palmer/codeforerunner@v0.4.5
|
|
116
125
|
with:
|
|
117
126
|
fail-on-drift: "true" # set "false" to warn-only
|
|
118
127
|
```
|
|
@@ -197,13 +206,13 @@ prompts/
|
|
|
197
206
|
├── scan.md api-docs.md audit.md
|
|
198
207
|
├── readme.md diagrams.md changelog.md
|
|
199
208
|
├── check.md flows.md version-audit.md
|
|
200
|
-
├── review.md stack-docs.md
|
|
209
|
+
├── review.md stack-docs.md arch-review.md
|
|
210
|
+
├── refresh.md
|
|
201
211
|
└── init-agent-onboarding.md
|
|
202
212
|
```
|
|
203
213
|
|
|
204
|
-
## Docs
|
|
214
|
+
## Docs
|
|
205
215
|
|
|
206
|
-
- `SPEC.md` — canonical phase/task tracker
|
|
207
216
|
- `docs/getting-started.md` — manual prompt use
|
|
208
217
|
- `docs/prompt-guide.md` — how system, partial, and task prompts compose
|
|
209
218
|
- `docs/editor-agent-setup.md` — adapting prompts to local agents
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "codeforerunner"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.5"
|
|
8
8
|
description = "Model-agnostic repository documentation tooling (prompt-first; thin CLI)."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -50,7 +50,7 @@ forerunner = "codeforerunner.cli:main"
|
|
|
50
50
|
where = ["src"]
|
|
51
51
|
|
|
52
52
|
[tool.setuptools.package-data]
|
|
53
|
-
codeforerunner = ["py.typed", "prompts/**/*.md"]
|
|
53
|
+
codeforerunner = ["py.typed", "prompts/**/*.md", "tasks.json"]
|
|
54
54
|
|
|
55
55
|
[tool.pytest.ini_options]
|
|
56
56
|
testpaths = ["tests"]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Thin CLI orchestration. Product logic lives in prompts/.
|
|
1
|
+
"""Thin CLI orchestration. Product logic lives in prompts/."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -8,12 +8,21 @@ import sys
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import Sequence
|
|
10
10
|
|
|
11
|
-
from codeforerunner.bundle import find_prompts_root
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
from codeforerunner.bundle import find_prompts_root
|
|
12
|
+
from codeforerunner.prompt_session import Denial, PromptSession
|
|
13
|
+
from codeforerunner.tasks import refresh_tasks as _refresh_tasks
|
|
14
14
|
SCAN_DONE_ENV = "FORERUNNER_SCAN_DONE"
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
def _scan_satisfied(repo_root: Path) -> bool:
|
|
18
|
+
"""CLI scan-first signal: scan artifact present, env override set, or no config to gate."""
|
|
19
|
+
return (
|
|
20
|
+
(repo_root / ".forerunner" / "scan.md").is_file()
|
|
21
|
+
or bool(os.environ.get(SCAN_DONE_ENV))
|
|
22
|
+
or not (repo_root / "forerunner.config.yaml").is_file()
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
17
26
|
def _get_bundle(args: argparse.Namespace) -> tuple[str, int]:
|
|
18
27
|
"""Resolve bundle for args.task. Returns (bundle_text, exit_code). exit_code != 0 on error."""
|
|
19
28
|
try:
|
|
@@ -22,25 +31,22 @@ def _get_bundle(args: argparse.Namespace) -> tuple[str, int]:
|
|
|
22
31
|
print(f"error: {e}", file=sys.stderr)
|
|
23
32
|
return "", 2
|
|
24
33
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
args.task not in SCAN_EXEMPT_TASKS
|
|
33
|
-
and (repo_root / "forerunner.config.yaml").is_file()
|
|
34
|
-
and not os.environ.get(SCAN_DONE_ENV)
|
|
35
|
-
):
|
|
34
|
+
repo_root = Path(args.repo).resolve() if args.repo else Path.cwd()
|
|
35
|
+
session = PromptSession(prompts_root, _scan_satisfied(repo_root))
|
|
36
|
+
decision = session.can_run(args.task)
|
|
37
|
+
if not decision.allowed:
|
|
38
|
+
if decision.reason is Denial.UNKNOWN_TASK:
|
|
39
|
+
print(f"error: unknown task '{args.task}'", file=sys.stderr)
|
|
40
|
+
return "", 2
|
|
36
41
|
print(
|
|
37
|
-
f"
|
|
38
|
-
f"
|
|
42
|
+
f"error: scan-first required — run `forerunner scan` first "
|
|
43
|
+
f"(writes .forerunner/scan.md). Set {SCAN_DONE_ENV}=1 to skip.",
|
|
39
44
|
file=sys.stderr,
|
|
40
45
|
)
|
|
46
|
+
return "", 1
|
|
41
47
|
|
|
42
48
|
try:
|
|
43
|
-
return
|
|
49
|
+
return session.bundle_for(args.task), 0
|
|
44
50
|
except FileNotFoundError as e:
|
|
45
51
|
print(f"error: {e}", file=sys.stderr)
|
|
46
52
|
return "", 2
|
|
@@ -73,12 +79,12 @@ def cmd_init(args: argparse.Namespace) -> int:
|
|
|
73
79
|
|
|
74
80
|
|
|
75
81
|
def cmd_scan(args: argparse.Namespace) -> int:
|
|
76
|
-
"""Emit the scan prompt bundle and hint about
|
|
82
|
+
"""Emit the scan prompt bundle and hint about scan artifact."""
|
|
77
83
|
rc = _doc_for(args, "scan")
|
|
78
84
|
if rc == 0:
|
|
79
85
|
print(
|
|
80
|
-
|
|
81
|
-
"scan-first
|
|
86
|
+
"hint: write the scan result to .forerunner/scan.md to satisfy the "
|
|
87
|
+
f"scan-first gate on follow-up calls. Or set {SCAN_DONE_ENV}=1 to skip.",
|
|
82
88
|
file=sys.stderr,
|
|
83
89
|
)
|
|
84
90
|
return rc
|
|
@@ -111,19 +117,19 @@ def cmd_mcp_server(args: argparse.Namespace) -> int:
|
|
|
111
117
|
except FileNotFoundError as e:
|
|
112
118
|
print(f"mcp_server: {e}", file=sys.stderr)
|
|
113
119
|
return 2
|
|
114
|
-
|
|
120
|
+
repo_root = Path(args.repo).resolve() if args.repo else Path.cwd()
|
|
121
|
+
return mcp_server.serve(prompts_root, repo_root=repo_root)
|
|
115
122
|
|
|
116
123
|
|
|
117
124
|
def cmd_refresh(args: argparse.Namespace) -> int:
|
|
118
125
|
"""Emit scan + check + all doc-task bundles to stdout for a full doc refresh."""
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
for i, task in enumerate(tasks):
|
|
126
|
+
task_names = [t.name for t in _refresh_tasks()]
|
|
127
|
+
for i, task in enumerate(task_names):
|
|
122
128
|
ns = argparse.Namespace(repo=getattr(args, "repo", None), task=task)
|
|
123
129
|
rc = cmd_doc(ns)
|
|
124
130
|
if rc != 0:
|
|
125
131
|
return rc
|
|
126
|
-
if i < len(
|
|
132
|
+
if i < len(task_names) - 1:
|
|
127
133
|
sys.stdout.write("\n---\n\n")
|
|
128
134
|
return 0
|
|
129
135
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Idempotent skill installer.
|
|
1
|
+
"""Idempotent skill installer. Re-run safe (V12); body-parity enforced (V10)."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -10,6 +10,8 @@ from dataclasses import dataclass
|
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
from typing import Iterable, Literal
|
|
12
12
|
|
|
13
|
+
from codeforerunner.tasks import installable_slugs as _installable_slugs
|
|
14
|
+
|
|
13
15
|
MARKER_BEGIN = "<!-- forerunner:begin managed=codeforerunner.skill -->"
|
|
14
16
|
MARKER_END = "<!-- forerunner:end -->"
|
|
15
17
|
|
|
@@ -21,22 +23,7 @@ EXIT_USAGE = 2
|
|
|
21
23
|
EXIT_BODY_MISMATCH = 3
|
|
22
24
|
EXIT_UNMANAGED_DEST = 4
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
TASK_SKILL_SLUGS: tuple[str, ...] = (
|
|
26
|
-
"codeforerunner",
|
|
27
|
-
"forerunner-scan",
|
|
28
|
-
"forerunner-readme",
|
|
29
|
-
"forerunner-api-docs",
|
|
30
|
-
"forerunner-audit",
|
|
31
|
-
"forerunner-changelog",
|
|
32
|
-
"forerunner-check",
|
|
33
|
-
"forerunner-diagrams",
|
|
34
|
-
"forerunner-flows",
|
|
35
|
-
"forerunner-init",
|
|
36
|
-
"forerunner-review",
|
|
37
|
-
"forerunner-stack-docs",
|
|
38
|
-
"forerunner-version-audit",
|
|
39
|
-
)
|
|
26
|
+
TASK_SKILL_SLUGS: tuple[str, ...] = _installable_slugs()
|
|
40
27
|
|
|
41
28
|
|
|
42
29
|
@dataclass(frozen=True)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Minimal stdio MCP server exposing prompt bundles as tools.
|
|
1
|
+
"""Minimal stdio MCP server exposing prompt bundles as tools.
|
|
2
2
|
|
|
3
3
|
Hand-rolled JSON-RPC 2.0 over line-delimited stdio. Stdlib only.
|
|
4
4
|
"""
|
|
@@ -11,21 +11,15 @@ from pathlib import Path
|
|
|
11
11
|
from typing import Any, Iterable
|
|
12
12
|
|
|
13
13
|
from codeforerunner import __version__ as _pkg_version
|
|
14
|
-
from codeforerunner.bundle import find_prompts_root
|
|
14
|
+
from codeforerunner.bundle import find_prompts_root
|
|
15
|
+
from codeforerunner.prompt_session import Denial, PromptSession
|
|
16
|
+
from codeforerunner.tasks import all_tasks as _all_tasks
|
|
15
17
|
|
|
16
18
|
PROTOCOL_VERSION = "2025-03-26"
|
|
17
19
|
SERVER_NAME = "codeforerunner"
|
|
18
20
|
SERVER_VERSION = _pkg_version
|
|
19
21
|
|
|
20
22
|
|
|
21
|
-
def _list_tasks(prompts_root: Path) -> list[Path]:
|
|
22
|
-
"""Return sorted list of task *.md paths under prompts_root/tasks/."""
|
|
23
|
-
tasks_dir = prompts_root / "tasks"
|
|
24
|
-
if not tasks_dir.is_dir():
|
|
25
|
-
return []
|
|
26
|
-
return sorted(tasks_dir.glob("*.md"))
|
|
27
|
-
|
|
28
|
-
|
|
29
23
|
def _description_for(task_path: Path) -> str:
|
|
30
24
|
"""First non-empty markdown line, stripped of leading '#' and whitespace."""
|
|
31
25
|
with task_path.open(encoding="utf-8") as f:
|
|
@@ -38,14 +32,14 @@ def _description_for(task_path: Path) -> str:
|
|
|
38
32
|
|
|
39
33
|
|
|
40
34
|
def _tools(prompts_root: Path) -> list[dict[str, Any]]:
|
|
41
|
-
"""Build MCP tools/list payload from
|
|
35
|
+
"""Build MCP tools/list payload from registered tasks."""
|
|
42
36
|
return [
|
|
43
37
|
{
|
|
44
|
-
"name":
|
|
45
|
-
"description": _description_for(
|
|
38
|
+
"name": task.name,
|
|
39
|
+
"description": _description_for(prompts_root / "tasks" / f"{task.name}.md"),
|
|
46
40
|
"inputSchema": {"type": "object", "properties": {}, "required": []},
|
|
47
41
|
}
|
|
48
|
-
for
|
|
42
|
+
for task in _all_tasks()
|
|
49
43
|
]
|
|
50
44
|
|
|
51
45
|
|
|
@@ -59,9 +53,6 @@ def _err(req_id: Any, code: int, message: str) -> dict[str, Any]:
|
|
|
59
53
|
return {"jsonrpc": "2.0", "id": req_id, "error": {"code": code, "message": message}}
|
|
60
54
|
|
|
61
55
|
|
|
62
|
-
SCAN_EXEMPT_TOOLS = frozenset({"init-agent-onboarding", "scan"})
|
|
63
|
-
|
|
64
|
-
|
|
65
56
|
def _handle(prompts_root: Path, msg: dict[str, Any], state: dict[str, Any]) -> dict[str, Any] | None:
|
|
66
57
|
"""Dispatch a single JSON-RPC message; return response dict or None for notifications."""
|
|
67
58
|
method = msg.get("method")
|
|
@@ -94,11 +85,11 @@ def _handle(prompts_root: Path, msg: dict[str, Any], state: dict[str, Any]) -> d
|
|
|
94
85
|
name = params.get("name")
|
|
95
86
|
if not isinstance(name, str) or "/" in name or "\\" in name or ".." in name:
|
|
96
87
|
return _err(req_id, -32602, f"invalid tool name: {name!r}")
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if not
|
|
100
|
-
|
|
101
|
-
|
|
88
|
+
session = PromptSession(prompts_root, scan_satisfied=bool(state.get("scan_called")))
|
|
89
|
+
decision = session.can_run(name)
|
|
90
|
+
if not decision.allowed:
|
|
91
|
+
if decision.reason is Denial.UNKNOWN_TASK:
|
|
92
|
+
return _err(req_id, -32602, f"unknown tool: {name!r}")
|
|
102
93
|
return _err(
|
|
103
94
|
req_id,
|
|
104
95
|
-32000,
|
|
@@ -107,7 +98,7 @@ def _handle(prompts_root: Path, msg: dict[str, Any], state: dict[str, Any]) -> d
|
|
|
107
98
|
if name == "scan":
|
|
108
99
|
state["scan_called"] = True
|
|
109
100
|
try:
|
|
110
|
-
text =
|
|
101
|
+
text = session.bundle_for(name)
|
|
111
102
|
except Exception as e: # pragma: no cover - defensive
|
|
112
103
|
return _err(req_id, -32603, f"internal error: {e}")
|
|
113
104
|
return _ok(
|
|
@@ -118,9 +109,17 @@ def _handle(prompts_root: Path, msg: dict[str, Any], state: dict[str, Any]) -> d
|
|
|
118
109
|
return _err(req_id, -32601, f"method not found: {method!r}")
|
|
119
110
|
|
|
120
111
|
|
|
121
|
-
def serve(
|
|
112
|
+
def serve(
|
|
113
|
+
prompts_root: Path,
|
|
114
|
+
repo_root: Path | None = None,
|
|
115
|
+
stdin: Iterable[str] = sys.stdin,
|
|
116
|
+
stdout=sys.stdout,
|
|
117
|
+
stderr=sys.stderr,
|
|
118
|
+
) -> int:
|
|
122
119
|
"""Run the JSON-RPC 2.0 MCP server loop over *stdin*/*stdout* until EOF."""
|
|
123
|
-
|
|
120
|
+
root = repo_root if repo_root is not None else Path.cwd()
|
|
121
|
+
scan_artifact = root / ".forerunner" / "scan.md"
|
|
122
|
+
state: dict[str, Any] = {"scan_called": scan_artifact.is_file(), "initialized": False}
|
|
124
123
|
for raw in stdin:
|
|
125
124
|
line = raw.strip()
|
|
126
125
|
if not line:
|
|
@@ -153,7 +152,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
153
152
|
except FileNotFoundError as e:
|
|
154
153
|
print(f"mcp_server: {e}", file=sys.stderr)
|
|
155
154
|
return 2
|
|
156
|
-
return serve(prompts_root)
|
|
155
|
+
return serve(prompts_root, repo_root=Path.cwd().resolve())
|
|
157
156
|
|
|
158
157
|
|
|
159
158
|
if __name__ == "__main__": # pragma: no cover
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Prompt Session — run-scoped owner of task lookup and scan-first enforcement.
|
|
2
|
+
|
|
3
|
+
CLI and MCP are thin adapters: they construct a session, ask whether a task
|
|
4
|
+
can run, and translate the structured Decision into their own surface
|
|
5
|
+
(stdout/exit codes for CLI, JSON-RPC for MCP). The session unifies the
|
|
6
|
+
scan-first *rule*; each adapter still computes its own scan-satisfied signal
|
|
7
|
+
and injects it (see docs/adr/0001).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from enum import Enum, auto
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from codeforerunner import tasks
|
|
17
|
+
from codeforerunner.bundle import resolve_bundle
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Denial(Enum):
|
|
21
|
+
UNKNOWN_TASK = auto()
|
|
22
|
+
SCAN_REQUIRED = auto()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class Decision:
|
|
27
|
+
allowed: bool
|
|
28
|
+
task: tasks.Task | None = None
|
|
29
|
+
reason: Denial | None = None
|
|
30
|
+
message: str | None = None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class PromptSession:
|
|
34
|
+
def __init__(self, prompts_root: Path, scan_satisfied: bool) -> None:
|
|
35
|
+
self._prompts_root = prompts_root
|
|
36
|
+
self._scan_done = scan_satisfied
|
|
37
|
+
|
|
38
|
+
def mark_scan_done(self) -> None:
|
|
39
|
+
"""Record that scan ran in this session (MCP adapter, after scan tool)."""
|
|
40
|
+
self._scan_done = True
|
|
41
|
+
|
|
42
|
+
def can_run(self, name: str) -> Decision:
|
|
43
|
+
try:
|
|
44
|
+
task = tasks.get(name)
|
|
45
|
+
except KeyError:
|
|
46
|
+
return Decision(False, reason=Denial.UNKNOWN_TASK, message=f"unknown task: {name!r}")
|
|
47
|
+
if task.scan_exempt or self._scan_done:
|
|
48
|
+
return Decision(True, task=task)
|
|
49
|
+
return Decision(False, task=task, reason=Denial.SCAN_REQUIRED, message="scan-first required")
|
|
50
|
+
|
|
51
|
+
def bundle_for(self, name: str) -> str:
|
|
52
|
+
"""Resolve the prompt bundle text for *name*. Call only after can_run allows."""
|
|
53
|
+
return resolve_bundle(self._prompts_root, name)
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Task: Architecture Review
|
|
2
|
+
|
|
3
|
+
Inspired by Matt Pocock's `/improve-codebase-architecture` skill:
|
|
4
|
+
https://github.com/mattpocock/skills/tree/main/skills/engineering/improve-codebase-architecture
|
|
5
|
+
|
|
6
|
+
Ranks repo-grounded Deepening Opportunities: architecture improvements that hide more behavior behind smaller interfaces, increasing leverage for callers and locality for maintainers.
|
|
7
|
+
Requires scan result as input.
|
|
8
|
+
|
|
9
|
+
## Input
|
|
10
|
+
|
|
11
|
+
- Scan result from `prompts/tasks/scan.md`
|
|
12
|
+
- File tree
|
|
13
|
+
- Key module/package files relevant to the scan result
|
|
14
|
+
- Existing tests for the modules under review
|
|
15
|
+
- `CONTEXT.md` or `CONTEXT-MAP.md` if present
|
|
16
|
+
- Relevant `docs/adr/*.md` files if present
|
|
17
|
+
- Existing architecture docs only when they clarify current design
|
|
18
|
+
|
|
19
|
+
## Review Focus
|
|
20
|
+
|
|
21
|
+
Look for architecture friction, not documentation drift:
|
|
22
|
+
|
|
23
|
+
1. Modules that are shallow: interface complexity nearly matches implementation complexity
|
|
24
|
+
2. Concepts that require bouncing across many files to understand
|
|
25
|
+
3. Seams that leak implementation details into callers
|
|
26
|
+
4. Pure helpers extracted for testability while real behavior remains hard to test
|
|
27
|
+
5. Adapter seams with more abstraction than real variation
|
|
28
|
+
6. Missing or weak tests caused by poor locality
|
|
29
|
+
|
|
30
|
+
Apply the deletion test to suspected shallow modules: if deleting the module makes complexity disappear, it was probably a pass-through; if complexity reappears across many callers, it was earning its keep.
|
|
31
|
+
|
|
32
|
+
## Vocabulary
|
|
33
|
+
|
|
34
|
+
Use these architecture terms consistently:
|
|
35
|
+
|
|
36
|
+
- Module
|
|
37
|
+
- Interface
|
|
38
|
+
- Implementation
|
|
39
|
+
- Deep
|
|
40
|
+
- Shallow
|
|
41
|
+
- Seam
|
|
42
|
+
- Adapter
|
|
43
|
+
- Leverage
|
|
44
|
+
- Locality
|
|
45
|
+
- Deepening Opportunity
|
|
46
|
+
- Deletion test
|
|
47
|
+
|
|
48
|
+
Use repo vocabulary from `CONTEXT.md` when present. If the repo lacks `CONTEXT.md` or the vocabulary is incomplete, infer temporary terms from evidence and list them under `## Suggested Glossary Additions`; do not write or rewrite `CONTEXT.md`.
|
|
49
|
+
|
|
50
|
+
## Candidate Format
|
|
51
|
+
|
|
52
|
+
For each Deepening Opportunity, include:
|
|
53
|
+
|
|
54
|
+
- **Files/modules**: concrete files or modules involved
|
|
55
|
+
- **Problem**: architecture friction observed
|
|
56
|
+
- **Evidence**: repo evidence supporting the finding
|
|
57
|
+
- **Proposed direction**: plain-English direction only
|
|
58
|
+
- **Benefits**: locality and leverage improvements
|
|
59
|
+
- **Testing impact**: what becomes easier to verify, without final test code
|
|
60
|
+
- **Risk / blast radius**: likely scope and migration risk
|
|
61
|
+
- **Recommendation strength**: `Strong`, `Worth exploring`, or `Speculative`
|
|
62
|
+
|
|
63
|
+
## Rules
|
|
64
|
+
|
|
65
|
+
- Claims must derive from provided files. If evidence is absent, omit or document in `## Gaps`.
|
|
66
|
+
- Do not report stale README/API/diagram/doc drift; use `check`, `review`, `diagrams`, or `flows` for that.
|
|
67
|
+
- Do not propose final function signatures, dataclass fields, schema shapes, or file-by-file implementation plans.
|
|
68
|
+
- Do not mutate `CONTEXT.md` or create ADRs.
|
|
69
|
+
- Do not imply Matt Pocock endorses codeforerunner.
|
|
70
|
+
- Keep the highest-signal 3-7 candidates. Fewer is acceptable when evidence is thin.
|
|
71
|
+
|
|
72
|
+
## Output Format
|
|
73
|
+
|
|
74
|
+
<!-- output: .forerunner/arch-review.md -->
|
|
75
|
+
|
|
76
|
+
# Architecture Review
|
|
77
|
+
|
|
78
|
+
> Inspired by Matt Pocock's `/improve-codebase-architecture` skill:
|
|
79
|
+
> https://github.com/mattpocock/skills/tree/main/skills/engineering/improve-codebase-architecture
|
|
80
|
+
|
|
81
|
+
## Summary
|
|
82
|
+
|
|
83
|
+
One paragraph describing the main architecture pressure observed.
|
|
84
|
+
|
|
85
|
+
## Top Recommendation
|
|
86
|
+
|
|
87
|
+
Name the highest-value Deepening Opportunity and why it should be first.
|
|
88
|
+
|
|
89
|
+
## Deepening Opportunities
|
|
90
|
+
|
|
91
|
+
### 1. [Candidate Name]
|
|
92
|
+
|
|
93
|
+
**Recommendation strength:** Strong | Worth exploring | Speculative
|
|
94
|
+
|
|
95
|
+
**Files/modules:** ...
|
|
96
|
+
|
|
97
|
+
**Problem:** ...
|
|
98
|
+
|
|
99
|
+
**Evidence:** ...
|
|
100
|
+
|
|
101
|
+
**Proposed direction:** ...
|
|
102
|
+
|
|
103
|
+
**Benefits:** ...
|
|
104
|
+
|
|
105
|
+
**Testing impact:** ...
|
|
106
|
+
|
|
107
|
+
**Risk / blast radius:** ...
|
|
108
|
+
|
|
109
|
+
## Suggested Glossary Additions
|
|
110
|
+
|
|
111
|
+
Only include if `CONTEXT.md` is missing or incomplete. Suggest terms; do not write them.
|
|
112
|
+
|
|
113
|
+
## Not Yet Decided
|
|
114
|
+
|
|
115
|
+
- Final interface shapes
|
|
116
|
+
- Migration sequence
|
|
117
|
+
- Exact tests
|
|
118
|
+
|
|
119
|
+
## Gaps
|
|
120
|
+
|
|
121
|
+
List missing evidence that could materially change the review.
|
|
@@ -10,7 +10,11 @@ Prompt-first workflow for manual use today; future wrappers may orchestrate it.
|
|
|
10
10
|
- Existing instruction files if present:
|
|
11
11
|
- `AGENTS.md`
|
|
12
12
|
- `CLAUDE.md`, `.cursor/rules/*`, `.cursorrules`, `.github/copilot-instructions.md`, `opencode.json`
|
|
13
|
-
-
|
|
13
|
+
- Existing domain vocabulary files if present:
|
|
14
|
+
- `CONTEXT.md`
|
|
15
|
+
- `CONTEXT-MAP.md`
|
|
16
|
+
- relevant `docs/adr/*.md`
|
|
17
|
+
- Key docs describing current state (`README.md`, `AGENTS.md`, roadmap docs)
|
|
14
18
|
- Optional prior scan result from `prompts/tasks/scan.md`
|
|
15
19
|
|
|
16
20
|
## Objectives
|
|
@@ -18,6 +22,7 @@ Prompt-first workflow for manual use today; future wrappers may orchestrate it.
|
|
|
18
22
|
1. Build or refine `AGENTS.md` so future agent sessions ramp quickly.
|
|
19
23
|
2. Keep only high-signal, repo-specific guidance likely to prevent mistakes.
|
|
20
24
|
3. Reconcile instruction drift across files without deleting valid custom constraints.
|
|
25
|
+
4. Create or refine `CONTEXT.md` when stable repo-specific domain terms are evident.
|
|
21
26
|
|
|
22
27
|
## Method
|
|
23
28
|
|
|
@@ -26,6 +31,7 @@ Prompt-first workflow for manual use today; future wrappers may orchestrate it.
|
|
|
26
31
|
3. Keep sections short and scannable.
|
|
27
32
|
4. Remove stale or generic guidance.
|
|
28
33
|
5. Preserve useful constraints that remain true.
|
|
34
|
+
6. For `CONTEXT.md`, include only stable repo-specific vocabulary. Do not add generic technology terms unless this repo gives them product-specific meaning.
|
|
29
35
|
|
|
30
36
|
## Required Output Sections
|
|
31
37
|
|
|
@@ -39,6 +45,9 @@ Prompt-first workflow for manual use today; future wrappers may orchestrate it.
|
|
|
39
45
|
|
|
40
46
|
- Never invent runnable surfaces (CLI, CI, hooks, Docker, package publish) if files do not exist.
|
|
41
47
|
- Never copy broad best-practice text that is not repo-specific.
|
|
48
|
+
- Create or update `CONTEXT.md` only when stable repo-specific terms exist in tracked files or established conversation context.
|
|
49
|
+
- If only generic technical words are available, do not create `CONTEXT.md`; mention the absence under `## Gaps` if relevant.
|
|
50
|
+
- Keep `CONTEXT.md` as a glossary only: no implementation plan, scratch notes, or file-by-file design decisions.
|
|
42
51
|
- Claims must derive from provided files. If evidence is absent, omit or document in `## Gaps`.
|
|
43
52
|
- If uncertain, omit.
|
|
44
53
|
- Keep naming consistent with repo conventions.
|
|
@@ -46,3 +55,7 @@ Prompt-first workflow for manual use today; future wrappers may orchestrate it.
|
|
|
46
55
|
## Output
|
|
47
56
|
|
|
48
57
|
<!-- output: AGENTS.md -->
|
|
58
|
+
|
|
59
|
+
When stable domain vocabulary exists, also output:
|
|
60
|
+
|
|
61
|
+
<!-- output: CONTEXT.md -->
|