diff --git a/docs/superpowers/plans/2026-08-20-thin-cli.md b/docs/superpowers/plans/2026-08-20-thin-cli.md deleted file mode 100644 index ddf8419..0000000 --- a/docs/superpowers/plans/2026-08-20-thin-cli.md +++ /dev/null @@ -1,884 +0,0 @@ -# Polaris Thin CLI Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Restore a standard-library-only `polaris` command dispatcher on the pre-CodeGraph baseline and release it as Polaris `0.1.16`. - -**Architecture:** A root-level `polaris_cli.py` resolves one of eight user-facing commands to the repository-locked Python script and launches it with `sys.executable`. Protocol behavior remains in `scripts/`; packaging exposes the dispatcher as a console entry point, and vendoring copies the dispatcher and package metadata into each target repository. - -**Tech Stack:** Python 3.10 standard library (`argparse`, `pathlib`, `subprocess`, `unittest`), setuptools package metadata, JSON protocol manifests, GitHub Actions. - -**Spec:** [`docs/superpowers/specs/2026-08-20-thin-cli-design.md`](../specs/2026-08-20-thin-cli-design.md) - -## Global Constraints - -- Set the Polaris release version to exactly `0.1.16`; keep Workflow version exactly `0.1.2`. -- Expose exactly `vendor`, `init-project`, `init-task`, `doctor`, `validate-project`, `validate-task`, `recover`, and `migrate`. -- Keep `polaris_cli.py` dependency-free beyond the Python standard library and free of protocol logic. -- Preserve each script's arguments, standard streams, and exit status; only append an inferred `--repo` when the caller did not supply one. -- Do not expose transition, artifact-building, scheduling, daemon, runtime, CodeGraph, provider, or `code-intelligence` functionality. -- Keep `transition_task.py` as the only writer of `VERIFIED` and `CLOSED` state transitions. -- Add or update tests for dispatch resolution, vendoring, package metadata, and migration; verify documentation and CI wiring mechanically. -- Preserve the user's existing untracked `.worktrees/`, `build/`, and `corona_polaris.egg-info/` directories; do not delete or rewrite them. - ---- - -## File map - -- Create `polaris_cli.py`: parse the public command name, locate source/vendored scripts, invoke the selected script, and translate dispatcher-level failures to exit codes. -- Create `pyproject.toml`: define the `corona-polaris` distribution and `polaris` console entry point without runtime dependencies. -- Modify `scripts/vendor_project.py`: copy the CLI and package metadata into `tools/polaris/` as managed files. -- Modify `tests/test_core.py`: cover the exact command surface, path resolution, argument/exit-code forwarding, vendored execution, package metadata, copied CLI files, and the new migration. -- Modify `VERSION`, `templates/project.json`, `templates/task-sources/state.json`, `templates/task/state.json`, and `workflow/migrations.json`: advance the protocol to `0.1.16` with an adjacent migration. -- Modify `AGENTS.md`, `plan.md`, `README.md`, and `docs/USAGE.md`: make the thin CLI part of the v0.1 authority and document the eight-command boundary. -- Modify `.github/workflows/ci.yml`: compile, install, and smoke-test the console entry point before running tests. -- Modify `.gitignore`: ignore Python packaging output (`build/`, `dist/`, and `*.egg-info/`). - ---- - -### Task 1: Add the thin dispatcher and package metadata - -**Files:** - -- Create: `polaris_cli.py` -- Create: `pyproject.toml` -- Modify: `.gitignore` -- Modify: `tests/test_core.py` - -**Interfaces:** - -- Consumes: the existing script entry points under `scripts/`, each of which accepts its own arguments and returns a process exit status. -- Produces: `COMMANDS: dict[str, tuple[str, str]]`, `_option_value(arguments: Sequence[str], option: str) -> str | None`, `_ancestors(path: Path) -> tuple[Path, ...]`, `_protocol_script(root: Path, script_name: str) -> tuple[Path, Path] | None`, `_resolve_script(command: str, arguments: Sequence[str], cwd: Path) -> tuple[Path, list[str]]`, `dispatch(command: str, arguments: Sequence[str], cwd: Path | None = None) -> int`, and `main(argv: Sequence[str] | None = None) -> int`. - -- [ ] **Step 1: Add the CLI imports and failing command-surface/forwarding test** - -Add `redirect_stderr` to the existing `contextlib` import, add `ROOT` to `sys.path`, import the not-yet-created module, and add this test to `PolarisCoreTests`: - -```python -from contextlib import contextmanager, redirect_stderr, redirect_stdout - -sys.path.insert(0, str(ROOT)) -sys.path.insert(0, str(SCRIPTS)) - -import polaris_cli # noqa: E402 - -def test_cli_exposes_only_user_commands_and_forwards_to_locked_scripts(self) -> None: - """统一 CLI 只暴露用户命令,并透传参数、仓库根和退出码。""" - self.assertEqual( - {command: spec[0] for command, spec in polaris_cli.COMMANDS.items()}, - { - "vendor": "vendor_project.py", - "init-project": "init_project.py", - "init-task": "init_task.py", - "doctor": "doctor_project.py", - "validate-project": "validate_project.py", - "validate-task": "validate_task.py", - "recover": "recover_task.py", - "migrate": "migrate_project.py", - }, - ) - completed = subprocess.CompletedProcess([], 7) - with mock.patch.object( - polaris_cli.subprocess, "run", return_value=completed - ) as invoked: - result = polaris_cli.dispatch("doctor", ["--json"], ROOT / "tests") - self.assertEqual(result, 7) - invoked.assert_called_once_with( - [ - sys.executable, - str(ROOT / "scripts" / "doctor_project.py"), - "--json", - "--repo", - str(ROOT), - ] - ) - - output = io.StringIO() - with redirect_stdout(output): - self.assertEqual(polaris_cli.main(["--help"]), 0) - for command in polaris_cli.COMMANDS: - self.assertIn(command, output.getvalue()) - self.assertNotIn("transition-task", output.getvalue()) - self.assertNotIn("code-intelligence", output.getvalue()) -``` - -- [ ] **Step 2: Run the focused test and confirm the expected red state** - -Run: - -```bash -python -m unittest discover -s tests -p "test_core.py" -k "test_cli_exposes_only_user_commands" -v -``` - -Expected: import failure for `polaris_cli` because `polaris_cli.py` does not exist. - -- [ ] **Step 3: Create the minimal dispatcher** - -Create `polaris_cli.py` with this complete implementation: - -```python -"""Zero-runtime-dependency command dispatcher for user-facing Polaris scripts.""" - -from __future__ import annotations - -import argparse -import subprocess -import sys -from pathlib import Path -from typing import Sequence - - -COMMANDS = { - "vendor": ("vendor_project.py", "Vendor Polaris into a target repository"), - "init-project": ("init_project.py", "Initialize project Authority"), - "init-task": ("init_task.py", "Initialize a task"), - "doctor": ("doctor_project.py", "Diagnose project health"), - "validate-project": ("validate_project.py", "Validate project Authority"), - "validate-task": ("validate_task.py", "Validate a task"), - "recover": ("recover_task.py", "Recover a task from repository Authority"), - "migrate": ("migrate_project.py", "Run the next explicit project migration"), -} - - -def _parser() -> argparse.ArgumentParser: - command_help = "\n".join( - f" {command:<18} {description}" - for command, (_, description) in COMMANDS.items() - ) - parser = argparse.ArgumentParser( - prog="polaris", - description="Dispatch user-facing commands to the repository-locked Polaris protocol.", - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=( - "commands:\n" - + command_help - + "\n\nRun 'polaris --help' for command options." - ), - ) - parser.add_argument("command", nargs="?", help=argparse.SUPPRESS) - return parser - - -def _option_value(arguments: Sequence[str], option: str) -> str | None: - for index, argument in enumerate(arguments): - if argument == option: - if index + 1 >= len(arguments): - return None - return arguments[index + 1] - prefix = option + "=" - if argument.startswith(prefix): - return argument[len(prefix) :] - return None - - -def _ancestors(path: Path) -> tuple[Path, ...]: - resolved = path.resolve() - if resolved.is_file(): - resolved = resolved.parent - return (resolved, *resolved.parents) - - -def _protocol_script(root: Path, script_name: str) -> tuple[Path, Path] | None: - vendored_script = root / "tools" / "polaris" / "scripts" / script_name - if vendored_script.is_file() and (root / "tools" / "polaris" / "VERSION").is_file(): - return vendored_script, root - source_script = root / "scripts" / script_name - if source_script.is_file() and (root / "VERSION").is_file(): - vendored_root = root.parent.name == "tools" and root.name == "polaris" - repository = root.parent.parent if vendored_root else root - return source_script, repository - return None - - -def _resolve_script( - command: str, arguments: Sequence[str], cwd: Path -) -> tuple[Path, list[str]]: - script_name = COMMANDS[command][0] - if command == "vendor": - explicit_source = _option_value(arguments, "--source") - roots = ( - (Path(explicit_source).resolve(),) - if explicit_source is not None - else _ancestors(cwd) - ) - for root in roots: - resolved = _protocol_script(root, script_name) - if resolved is not None: - return resolved[0], list(arguments) - raise FileNotFoundError( - "cannot locate a Polaris protocol source for vendor; " - "run from a Polaris source/vendored repository or pass --source" - ) - - explicit_repo = _option_value(arguments, "--repo") - roots = ( - (Path(explicit_repo).resolve(),) - if explicit_repo is not None - else _ancestors(cwd) - ) - for root in roots: - resolved = _protocol_script(root, script_name) - if resolved is None: - continue - script, repository = resolved - forwarded = list(arguments) - if explicit_repo is None: - forwarded.extend(["--repo", str(repository)]) - return script, forwarded - raise FileNotFoundError( - "cannot locate tools/polaris for this repository; " - "run inside an initialized vendored project or pass --repo" - ) - - -def dispatch(command: str, arguments: Sequence[str], cwd: Path | None = None) -> int: - if command not in COMMANDS: - raise ValueError(f"unknown Polaris command: {command}") - script, forwarded = _resolve_script(command, arguments, cwd or Path.cwd()) - completed = subprocess.run([sys.executable, str(script), *forwarded]) - return completed.returncode - - -def main(argv: Sequence[str] | None = None) -> int: - arguments = list(sys.argv[1:] if argv is None else argv) - parser = _parser() - if not arguments or arguments[0] in {"-h", "--help"}: - parser.print_help() - return 0 - command = arguments[0] - if command not in COMMANDS: - parser.error(f"unknown command: {command}") - try: - return dispatch(command, arguments[1:]) - except KeyboardInterrupt: - return 130 - except (FileNotFoundError, OSError, ValueError) as exc: - print(f"ERROR: {exc}", file=sys.stderr) - return 2 - - -if __name__ == "__main__": - sys.exit(main()) -``` - -- [ ] **Step 4: Run the focused command test and confirm it passes** - -Run: - -```bash -python -m unittest discover -s tests -p "test_core.py" -k "test_cli_exposes_only_user_commands" -v -``` - -Expected: one test passes. - -- [ ] **Step 5: Add failing tests for explicit options and dispatcher errors** - -Add this test to `PolarisCoreTests`: - -```python -def test_cli_preserves_explicit_locations_and_reports_dispatch_errors(self) -> None: - """显式 --repo/--source 不被改写,定位失败与中断使用固定退出码。""" - explicit_repo = ["--repo=" + str(ROOT), "--json"] - script, forwarded = polaris_cli._resolve_script( - "doctor", explicit_repo, self.repo - ) - self.assertEqual(script, ROOT / "scripts" / "doctor_project.py") - self.assertEqual(forwarded, explicit_repo) - - explicit_source = [str(self.repo), "--source", str(ROOT), "--force"] - script, forwarded = polaris_cli._resolve_script( - "vendor", explicit_source, self.repo - ) - self.assertEqual(script, ROOT / "scripts" / "vendor_project.py") - self.assertEqual(forwarded, explicit_source) - - errors = io.StringIO() - with tempfile.TemporaryDirectory(prefix="polaris-no-protocol-") as empty: - with redirect_stderr(errors): - self.assertEqual(polaris_cli.main(["doctor", "--repo", empty]), 2) - self.assertIn("cannot locate tools/polaris", errors.getvalue()) - - with mock.patch.object( - polaris_cli, "dispatch", side_effect=KeyboardInterrupt - ): - self.assertEqual(polaris_cli.main(["doctor"]), 130) -``` - -- [ ] **Step 6: Run both CLI behavior tests** - -Run: - -```bash -python -m unittest discover -s tests -p "test_core.py" -k "test_cli_" -v -``` - -Expected: both CLI tests pass. - -- [ ] **Step 7: Add the failing package metadata test** - -Add this test to `PolarisCoreTests`: - -```python -def test_cli_packaging_declares_no_runtime_dependencies(self) -> None: - """pip console script 使用独立分发名,且不声明运行时第三方依赖。""" - metadata = (ROOT / "pyproject.toml").read_text(encoding="utf-8") - self.assertIn('name = "corona-polaris"', metadata) - self.assertIn( - 'version = "' + (ROOT / "VERSION").read_text().strip() + '"', - metadata, - ) - self.assertIn('dependencies = []', metadata) - self.assertIn('polaris = "polaris_cli:main"', metadata) -``` - -- [ ] **Step 8: Run the package metadata test and confirm the expected red state** - -Run: - -```bash -python -m unittest discover -s tests -p "test_core.py" -k "test_cli_packaging" -v -``` - -Expected: failure because `pyproject.toml` does not exist. - -- [ ] **Step 9: Create package metadata and ignore build outputs** - -Create `pyproject.toml`: - -```toml -[build-system] -requires = ["setuptools"] -build-backend = "setuptools.build_meta" - -[project] -name = "corona-polaris" -version = "0.1.15" -description = "Repo-native AI engineering workflow command dispatcher" -requires-python = ">=3.10" -dependencies = [] - -[project.scripts] -polaris = "polaris_cli:main" - -[tool.setuptools] -py-modules = ["polaris_cli"] -``` - -The package initially matches the branch's current `VERSION`. Task 3 advances the package and protocol together to the approved final version `0.1.16`. - -Add these lines after `.coverage` in `.gitignore`: - -```gitignore -build/ -dist/ -*.egg-info/ -``` - -- [ ] **Step 10: Run all Task 1 tests and the direct help smoke test** - -Run: - -```bash -python -m unittest discover -s tests -p "test_core.py" -k "test_cli_" -v -python polaris_cli.py --help -``` - -Expected: all CLI tests pass; help lists the eight public commands and omits internal commands. - -- [ ] **Step 11: Commit the dispatcher slice** - -```bash -git add polaris_cli.py pyproject.toml .gitignore tests/test_core.py -git commit -m "feat: restore thin Polaris CLI" -``` - ---- - -### Task 2: Vendor and execute the repository-locked CLI - -**Files:** - -- Modify: `scripts/vendor_project.py` -- Modify: `tests/test_core.py` - -**Interfaces:** - -- Consumes: `polaris_cli._resolve_script(...)` from Task 1 and `_stage_install(source: Path, target: Path, stage: Path, adapters: list[dict[str, Any]], skills: tuple[str, ...]) -> dict[str, Any]`. -- Produces: vendored `tools/polaris/pyproject.toml` and `tools/polaris/polaris_cli.py`, recorded in `tools/polaris/install-manifest.json` as managed files. - -- [ ] **Step 1: Add failing assertions for the two vendored package files** - -Extend `test_vendored_target_is_self_contained` immediately after its existing `VERSION` assertion: - -```python -self.assertEqual( - (self.repo / "tools" / "polaris" / "pyproject.toml").read_bytes(), - (ROOT / "pyproject.toml").read_bytes(), -) -self.assertEqual( - (self.repo / "tools" / "polaris" / "polaris_cli.py").read_bytes(), - (ROOT / "polaris_cli.py").read_bytes(), -) -manifest = read_json(self.repo / "tools" / "polaris" / "install-manifest.json") -managed = {entry["path"] for entry in manifest["managed_files"]} -self.assertIn("tools/polaris/pyproject.toml", managed) -self.assertIn("tools/polaris/polaris_cli.py", managed) -``` - -- [ ] **Step 2: Run the vendoring test and confirm the expected red state** - -Run: - -```bash -python -m unittest discover -s tests -p "test_core.py" -k "test_vendored_target_is_self_contained" -v -``` - -Expected: failure reading `tools/polaris/pyproject.toml`. - -- [ ] **Step 3: Copy package files during transactional staging** - -In `_stage_install`, replace the one-file `VERSION` copy with: - -```python -tools_target.mkdir(parents=True) -for name in ("VERSION", "pyproject.toml", "polaris_cli.py"): - require_regular_file(source / name, f"Polaris {name} source") - shutil.copy2(source / name, tools_target / name) -for name in ("hosts", "scripts", "schemas", "skills", "templates", "workflow"): - require_regular_tree(source / name, f"Polaris {name} source") - shutil.copytree(source / name, tools_target / name, ignore=ignore_generated) -``` - -Do not add a `providers` directory: this branch intentionally has no CodeGraph/provider subsystem. The existing `tools_target.rglob("*")` manifest collection must remain unchanged so the two copied files become managed automatically. - -- [ ] **Step 4: Run the vendoring test and confirm it passes** - -Run: - -```bash -python -m unittest discover -s tests -p "test_core.py" -k "test_vendored_target_is_self_contained" -v -``` - -Expected: one test passes, including manifest ownership assertions. - -- [ ] **Step 5: Add the failing vendored execution integration test** - -Add this test to `PolarisCoreTests`: - -```python -def test_cli_runs_vendored_protocol_from_nested_and_explicit_repositories(self) -> None: - """CLI 从子目录或 --repo 定位 vendored 协议,并保持原脚本 JSON 语义。""" - vendor(ROOT, self.repo, False) - nested = self.repo / "src" / "nested path" - nested.mkdir(parents=True) - command = [ - sys.executable, - str(ROOT / "polaris_cli.py"), - "validate-project", - "--json", - ] - nested_result = subprocess.run( - command, cwd=nested, text=True, encoding="utf-8", capture_output=True - ) - self.assertEqual(nested_result.returncode, 0, nested_result.stderr) - self.assertEqual(json.loads(nested_result.stdout)["status"], "PASS") - - explicit_result = subprocess.run( - [*command, "--repo", str(self.repo)], - cwd=ROOT, - text=True, - encoding="utf-8", - capture_output=True, - ) - self.assertEqual(explicit_result.returncode, 0, explicit_result.stderr) - self.assertEqual(json.loads(explicit_result.stdout)["status"], "PASS") -``` - -- [ ] **Step 6: Run the integration test** - -Run: - -```bash -python -m unittest discover -s tests -p "test_core.py" -k "test_cli_runs_vendored_protocol" -v -``` - -Expected: the nested and explicit invocations both pass and return JSON with `status: PASS`. - -- [ ] **Step 7: Run the complete vendoring and CLI subset** - -Run: - -```bash -python -m unittest discover -s tests -p "test_core.py" -k "cli" -v -python -m unittest discover -s tests -p "test_core.py" -k "vendor" -v -``` - -Expected: all selected tests pass. - -- [ ] **Step 8: Commit repository-locked CLI vendoring** - -```bash -git add scripts/vendor_project.py tests/test_core.py -git commit -m "feat: vendor locked CLI with protocol" -``` - ---- - -### Task 3: Release protocol version 0.1.16 and add its migration - -**Files:** - -- Modify: `VERSION` -- Modify: `pyproject.toml` -- Modify: `templates/project.json` -- Modify: `templates/task-sources/state.json` -- Modify: `templates/task/state.json` -- Modify: `workflow/migrations.json` -- Modify: `tests/test_core.py` - -**Interfaces:** - -- Consumes: the existing declarative migration loader and the test helper `set_protocol_version(version: str) -> None`. -- Produces: one adjacent migration with ID `0.1.15-to-0.1.16`, project strategy `replace_version`, task strategy `append_version_event`, and unchanged Workflow version `0.1.2`. - -- [ ] **Step 1: Retarget the three existing migration behavior tests** - -In these tests: - -- `test_explicit_migration_appends_task_event_and_records_completion` -- `test_migration_resumes_after_event_append_without_duplication` -- `test_migration_reclaims_only_its_own_dead_process_lock` - -make these exact replacements without changing any other migration test data: - -```python -self.set_protocol_version("0.1.14") -# becomes -self.set_protocol_version("0.1.15") - -self.assertEqual(result["from"], "0.1.14") -self.assertEqual(result["to"], "0.1.15") -# becomes -self.assertEqual(result["from"], "0.1.15") -self.assertEqual(result["to"], "0.1.16") - -# Every source event version in these tests: -"polaris_version": "0.1.14" -# becomes: -"polaris_version": "0.1.15" - -# Every target event version in these tests: -"polaris_version": "0.1.15" -# becomes: -"polaris_version": "0.1.16" - -# Every migration ID and migration record filename stem: -"0.1.14-to-0.1.15" -# becomes: -"0.1.15-to-0.1.16" -``` - -Keep every `"workflow_version": "0.1.2"` value and the resumability, no-duplicate-event, dead-lock reclamation, and live-lock rejection assertions intact. - -- [ ] **Step 2: Run the migration tests and confirm the expected red state** - -Run: - -```bash -python -m unittest discover -s tests -p "test_core.py" -k "migration" -v -``` - -Expected: the three retargeted tests fail because `0.1.15-to-0.1.16` is not declared and the source/template version is still `0.1.15`. - -- [ ] **Step 3: Advance every protocol version source** - -Set `VERSION` to: - -```text -0.1.16 -``` - -Set the project version in `pyproject.toml` to: - -```toml -version = "0.1.16" -``` - -Set `polaris_version` to `0.1.16` in all three JSON templates: - -```json -"polaris_version": "0.1.16" -``` - -Do not change any `workflow_version` value. - -- [ ] **Step 4: Append the adjacent migration declaration** - -Append this object to `workflow/migrations.json` after the `0.1.14-to-0.1.15` step: - -```json -{ - "migration_id": "0.1.15-to-0.1.16", - "from_polaris_version": "0.1.15", - "to_polaris_version": "0.1.16", - "from_workflow_version": "0.1.2", - "to_workflow_version": "0.1.2", - "project_strategy": "replace_version", - "task_strategy": "append_version_event" -} -``` - -Keep the file as four-space-indented JSON. - -- [ ] **Step 5: Run the migration and version-sensitive tests** - -Run: - -```bash -python -m unittest discover -s tests -p "test_core.py" -k "migration" -v -python -m unittest discover -s tests -p "test_core.py" -k "protocol_version" -v -python -m unittest discover -s tests -p "test_core.py" -k "cli_packaging" -v -``` - -Expected: all selected tests pass; the metadata version test now agrees with `VERSION`. - -- [ ] **Step 6: Confirm version consistency mechanically** - -Run: - -```bash -rg -n '0\.1\.15|0\.1\.16' VERSION pyproject.toml templates workflow/migrations.json tests/test_core.py -``` - -Expected: `0.1.15` remains only as the source side of the new migration and in historical migrations; all current-version values are `0.1.16`. - -- [ ] **Step 7: Commit the release migration slice** - -```bash -git add VERSION pyproject.toml templates/project.json templates/task-sources/state.json templates/task/state.json workflow/migrations.json tests/test_core.py -git commit -m "chore: release Polaris 0.1.16" -``` - ---- - -### Task 4: Make the product authority and user docs describe the CLI boundary - -**Files:** - -- Modify: `AGENTS.md` -- Modify: `plan.md` -- Modify: `README.md` -- Modify: `docs/USAGE.md` - -**Interfaces:** - -- Consumes: the eight-command `COMMANDS` mapping and vendored-install behavior from Tasks 1–2. -- Produces: authoritative product rules and user instructions that match the implemented CLI without documenting internal scripts as public commands. - -- [ ] **Step 1: Update the repository rule for the CLI** - -Replace the current `AGENTS.md` prohibition on any CLI with: - -```markdown -- Keep the user-facing `polaris` CLI a standard-library-only thin dispatcher over the existing scripts. Do not move protocol logic into it or add a daemon, scheduler, database, Dashboard, Task DAG, or custom Agent Runtime in v0.1. -``` - -- [ ] **Step 2: Update the authoritative v0.1 plan** - -Make these exact semantic changes in `plan.md`: - -```markdown -Thin standard-library `polaris` CLI - → locates the source or repository-locked protocol - → dispatches exactly eight user-facing commands to existing scripts - → never owns workflow or state-transition logic -``` - -- Replace statements that v0.1 has no CLI with the rule that v0.1 has no independent CLI runtime or duplicated protocol logic. -- Add `pyproject.toml` and `polaris_cli.py` to the repository structure. -- Add the eight command names exactly as declared in `polaris_cli.COMMANDS`. -- Keep daemon, Dashboard, scheduler, database, Task DAG, custom Agent Runtime, and direct Agent writes to `VERIFIED`/`CLOSED` out of scope. -- Change the v0.1 completion checklist from “没有 CLI” to “CLI 仅为标准库薄分发器,且只暴露八个用户命令”. -- Record `0.1.16` as the thin-CLI release while keeping Workflow `0.1.2`. - -- [ ] **Step 3: Update README installation and command examples** - -Set the displayed version to `0.1.16`. Document installation and the public surface with these canonical snippets: - -```powershell -python -m pip install --no-deps . -polaris vendor C:\path\to\target-repo -python -m pip install --no-deps ./tools/polaris -polaris init-project my-project --repo . -polaris doctor --repo . --json -polaris validate-project --repo . -polaris init-task TASK-0001 --rigor R1 --repo . -polaris validate-task TASK-0001 --repo . -polaris recover TASK-0001 --repo . -polaris migrate --repo . -``` - -State directly beside the examples: - -```markdown -The CLI only locates and dispatches to the source or repository-locked scripts. Internal transition and artifact commands remain direct Python script entry points and are not public CLI commands. -``` - -Retain direct `python tools/polaris/scripts/transition_task.py ...` examples for internal workflow execution. Remove “not a Polaris CLI” and “do not add a CLI” claims. Describe `0.1.16` as the thin CLI release; do not add CodeGraph/provider history. - -- [ ] **Step 4: Update the usage guide from first install through migration** - -Set the displayed version to `0.1.16`, add the same `pip --no-deps` installation flow, and use `polaris` for the eight public operations. Keep these operational rules explicit: - -```markdown -- Install from `./tools/polaris` when operating a vendored repository so the command version matches the locked protocol. -- `polaris --help` delegates to the selected script and therefore shows that script's existing options. -- Exit codes remain `0` for success, `1` for protocol/rule failure, and `2` for input/environment failure; interruption returns `130` at the dispatcher. -- State transitions and artifact construction stay internal and continue to run through repository scripts. -``` - -Update the migration history with `0.1.16` as the thin CLI addition and no Workflow version change. Remove every claim that v0.1 offers no CLI, without weakening the prohibition on a daemon, UI, or custom runtime. - -- [ ] **Step 5: Search for contradictory product claims** - -Run: - -```bash -rg -n "不提供.*CLI|不实现 CLI|没有 CLI|not.*CLI|0\.1\.15|code-intelligence|CodeGraph" AGENTS.md plan.md README.md docs/USAGE.md -``` - -Expected: no current-state claim says the CLI is absent; `0.1.15` appears only in migration history; CodeGraph and `code-intelligence` do not appear in the rebuilt product documentation. - -- [ ] **Step 6: Check the eight-command documentation against code** - -Run: - -```bash -python - <<'PY' -from pathlib import Path -import polaris_cli - -text = "\n".join( - Path(path).read_text(encoding="utf-8") - for path in ("plan.md", "README.md", "docs/USAGE.md") -) -missing = [command for command in polaris_cli.COMMANDS if command not in text] -internal = [command for command in ("transition-task", "code-intelligence") if command in polaris_cli.COMMANDS] -assert not missing, missing -assert not internal, internal -print("documented public commands:", ", ".join(polaris_cli.COMMANDS)) -PY -``` - -Expected: the script prints all eight public commands and exits `0`. - -- [ ] **Step 7: Commit the authority and documentation slice** - -```bash -git add AGENTS.md plan.md README.md docs/USAGE.md -git commit -m "docs: define thin CLI product boundary" -``` - ---- - -### Task 5: Wire cross-platform CI and run final verification - -**Files:** - -- Modify: `.github/workflows/ci.yml` -- Test: `tests/test_core.py` - -**Interfaces:** - -- Consumes: installable `pyproject.toml`, `polaris_cli.py`, and the repository's `tests/run_tests.py` runner. -- Produces: Linux, Windows, and macOS CI coverage for syntax, console-entry installation, help output, and the full workflow suite. - -- [ ] **Step 1: Add the CLI to compilation and install smoke checks** - -Replace the existing compile step and insert two steps before the test suite: - -```yaml - - name: Compile Python sources - run: python -m compileall -q polaris_cli.py scripts tests - - - name: Install zero-runtime-dependency CLI - run: python -m pip install --no-deps . - - - name: Smoke test CLI entry point - run: polaris --help - - - name: Run test suite - run: python tests/run_tests.py -``` - -- [ ] **Step 2: Compile all Python sources locally** - -Run: - -```bash -python -m compileall -q polaris_cli.py scripts tests -``` - -Expected: exit `0` with no syntax errors. - -- [ ] **Step 3: Run the full scenario-logged test suite** - -Run: - -```bash -python tests/run_tests.py -``` - -Expected: exit `0`; every test records `PASS` and the final mechanical conclusion is `PASS`. - -- [ ] **Step 4: Run native unittest discovery as a second check** - -Run: - -```bash -python -m unittest discover -s tests -v -``` - -Expected: exit `0` with no failures or errors. - -- [ ] **Step 5: Exercise help and one source-tree command directly** - -Run: - -```bash -python polaris_cli.py --help -python polaris_cli.py doctor --repo . --json -``` - -Expected: help lists exactly the eight commands; Doctor emits a valid JSON report and retains its script-defined exit status. - -- [ ] **Step 6: Verify the final diff is scoped and free of CodeGraph additions** - -Run: - -```bash -git status --short -git diff --check -git diff --stat 3589fc3..HEAD -rg -n "code-intelligence|CodeGraph|codegraph" polaris_cli.py pyproject.toml scripts/vendor_project.py tests/test_core.py AGENTS.md plan.md README.md docs/USAGE.md -``` - -Expected: `git diff --check` succeeds; only planned CLI/version/docs/CI files and the two planning documents are changed; the final search returns no CodeGraph CLI or subsystem additions. The pre-existing untracked `.worktrees/`, `build/`, and `corona_polaris.egg-info/` may still appear in status and must remain untouched. - -- [ ] **Step 7: Commit CI wiring** - -```bash -git add .github/workflows/ci.yml -git commit -m "ci: install and smoke test Polaris CLI" -``` - -- [ ] **Step 8: Record final verification evidence** - -Run: - -```bash -git status --short -git log --oneline --decorate -5 -``` - -Expected: the implementation commits are present; no tracked implementation changes remain uncommitted; only the preserved pre-existing untracked directories may remain. diff --git a/docs/superpowers/specs/2026-08-20-thin-cli-design.md b/docs/superpowers/specs/2026-08-20-thin-cli-design.md deleted file mode 100644 index c788812..0000000 --- a/docs/superpowers/specs/2026-08-20-thin-cli-design.md +++ /dev/null @@ -1,63 +0,0 @@ -# Polaris Thin CLI Design - -## Context - -Polaris is being rebuilt from commit `3589fc3`, before CodeGraph support was added. The current protocol version is `0.1.15`. The rebuild intentionally omits CodeGraph and restores only the useful user-facing CLI layer. - -## Decision - -Release the CLI restoration as Polaris `0.1.16`. - -The CLI is a standard-library-only dispatcher. It does not own workflow rules, validation, migrations, state transitions, scheduling, persistence, or agent orchestration. It locates the correct source or vendored protocol script, forwards arguments unchanged apart from supplying an inferred `--repo`, and returns the child process exit status. - -The public command set is exactly: - -| Command | Script | -| --- | --- | -| `vendor` | `vendor_project.py` | -| `init-project` | `init_project.py` | -| `init-task` | `init_task.py` | -| `doctor` | `doctor_project.py` | -| `validate-project` | `validate_project.py` | -| `validate-task` | `validate_task.py` | -| `recover` | `recover_task.py` | -| `migrate` | `migrate_project.py` | - -Internal state transitions and artifact-building scripts remain script-only. There is no `code-intelligence` command and no CodeGraph/provider subsystem. - -## Resolution and dispatch - -For commands other than `vendor`, the dispatcher uses an explicit `--repo` when present. Otherwise it walks from the current directory toward the filesystem root. At each candidate repository it prefers `tools/polaris/scripts/