core-cli 1.0.0__tar.gz → 1.0.2__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.
- {core_cli-1.0.0 → core_cli-1.0.2}/PKG-INFO +3 -3
- {core_cli-1.0.0 → core_cli-1.0.2}/README.md +1 -1
- {core_cli-1.0.0 → core_cli-1.0.2}/pyproject.toml +2 -2
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/project/onboard.py +11 -2
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/project/scout.py +6 -1
- core_cli-1.0.2/tests/test_project.py +98 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/.github/workflows/ci.yml +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/.gitignore +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/LICENSE +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/__init__.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/main.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/__init__.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/code/__init__.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/code/actions.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/code/audit_duplicates.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/code/bridges.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/code/check_imports.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/code/check_ui.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/code/docstrings.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/code/fix_atomic.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/code/format.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/code/hub.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/code/integrity.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/code/lint.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/code/logging.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/code/test.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/lane/__init__.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/lane/claim.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/lane/list.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/lane/next.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/lane/propose.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/project/__init__.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/project/docs.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/proposals/__init__.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/proposals/create.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/proposals/integrate.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/proposals/list.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/proposals/manage.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/secrets/__init__.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/secrets/hub.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/secrets/manage.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/symbols/__init__.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/symbols/audit.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/symbols/fix_ids.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/symbols/hub.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/symbols/resolve_duplicates.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/symbols/sync.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/vectors/__init__.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/vectors/hub.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/vectors/query.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/vectors/rebuild.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/vectors/status.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/vectors/sync.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/src/core_cli/resources/vectors/sync_code.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/tests/__init__.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/tests/test_code.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/tests/test_lane.py +0 -0
- {core_cli-1.0.0 → core_cli-1.0.2}/tests/test_proposals.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: core-cli
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: Consumer governance CLI for CORE — pure HTTP client for governing repositories against CORE constitutional rules.
|
|
5
5
|
Project-URL: Homepage, https://github.com/DariuszNewecki/CORE
|
|
6
6
|
Project-URL: Repository, https://github.com/DariuszNewecki/core-cli
|
|
@@ -18,7 +18,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
18
18
|
Classifier: Topic :: Software Development
|
|
19
19
|
Classifier: Topic :: Software Development :: Quality Assurance
|
|
20
20
|
Requires-Python: >=3.12
|
|
21
|
-
Requires-Dist: core-runtime>=2.
|
|
21
|
+
Requires-Dist: core-runtime>=2.9.0
|
|
22
22
|
Provides-Extra: dev
|
|
23
23
|
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
24
24
|
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
@@ -61,7 +61,7 @@ export CORE_API_URL=http://localhost:8000
|
|
|
61
61
|
| `code` | `actions`, `audit_duplicates`, `bridges`, `check_imports`, `check_ui`, `docstrings`, `fix_atomic`, `format`, `integrity`, `lint`, `logging`, `test` |
|
|
62
62
|
| `symbols` | `audit`, `fix_ids`, `resolve_duplicates`, `sync` |
|
|
63
63
|
| `vectors` | `query`, `rebuild`, `status`, `sync`, `sync_code` |
|
|
64
|
-
| `project` | `docs`, `onboard`, `scout` |
|
|
64
|
+
| `project` | `docs`, `onboard`, `promote`, `scout` |
|
|
65
65
|
|
|
66
66
|
## Architecture
|
|
67
67
|
|
|
@@ -35,7 +35,7 @@ export CORE_API_URL=http://localhost:8000
|
|
|
35
35
|
| `code` | `actions`, `audit_duplicates`, `bridges`, `check_imports`, `check_ui`, `docstrings`, `fix_atomic`, `format`, `integrity`, `lint`, `logging`, `test` |
|
|
36
36
|
| `symbols` | `audit`, `fix_ids`, `resolve_duplicates`, `sync` |
|
|
37
37
|
| `vectors` | `query`, `rebuild`, `status`, `sync`, `sync_code` |
|
|
38
|
-
| `project` | `docs`, `onboard`, `scout` |
|
|
38
|
+
| `project` | `docs`, `onboard`, `promote`, `scout` |
|
|
39
39
|
|
|
40
40
|
## Architecture
|
|
41
41
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "core-cli"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.2"
|
|
8
8
|
description = "Consumer governance CLI for CORE — pure HTTP client for governing repositories against CORE constitutional rules."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -24,7 +24,7 @@ classifiers = [
|
|
|
24
24
|
"Development Status :: 4 - Beta",
|
|
25
25
|
]
|
|
26
26
|
dependencies = [
|
|
27
|
-
"core-runtime>=2.
|
|
27
|
+
"core-runtime>=2.9.0",
|
|
28
28
|
]
|
|
29
29
|
|
|
30
30
|
[project.optional-dependencies]
|
|
@@ -46,6 +46,12 @@ async def onboard_project(
|
|
|
46
46
|
Pass --write --stage to write to work/staged/<name>/ first, inspect the
|
|
47
47
|
files, then run `project onboard promote <path>` to deliver to the target.
|
|
48
48
|
"""
|
|
49
|
+
# `path` is sent as a plain string over HTTP and resolved by the CORE API
|
|
50
|
+
# process, not this CLI — a relative path (e.g. ".") would silently
|
|
51
|
+
# resolve against the *server's* cwd, not the caller's. Resolve to
|
|
52
|
+
# absolute here so it means the same thing on both sides.
|
|
53
|
+
path = path.resolve()
|
|
54
|
+
|
|
49
55
|
if stage and not write:
|
|
50
56
|
console.print(
|
|
51
57
|
"[yellow]--stage has no effect without --write "
|
|
@@ -77,7 +83,7 @@ async def onboard_project(
|
|
|
77
83
|
console.print(
|
|
78
84
|
f"\n[bold green]Staged to[/bold green] {stage_dir}/.intent\n"
|
|
79
85
|
f"[dim]Inspect, then run:[/dim] "
|
|
80
|
-
f"[bold]core
|
|
86
|
+
f"[bold]core project promote {path}[/bold]"
|
|
81
87
|
)
|
|
82
88
|
elif mode == "dry-run":
|
|
83
89
|
console.print("[yellow]DRY-RUN complete. Pass --write to apply.[/yellow]")
|
|
@@ -100,6 +106,9 @@ async def promote_onboard(
|
|
|
100
106
|
Reads from work/staged/<name>/.intent/ (within CORE's repo) and writes to
|
|
101
107
|
<path>/.intent/. Run `project onboard promote --help` for full lifecycle details.
|
|
102
108
|
"""
|
|
109
|
+
# Same reasoning as onboard_project: resolve before sending over HTTP.
|
|
110
|
+
path = path.resolve()
|
|
111
|
+
|
|
103
112
|
console.print(f"[bold cyan]Promoting staged onboard to:[/bold cyan] {path}")
|
|
104
113
|
client = CoreApiClient()
|
|
105
114
|
try:
|
|
@@ -107,4 +116,4 @@ async def promote_onboard(
|
|
|
107
116
|
except Exception as exc:
|
|
108
117
|
console.print(f"[red]Promote failed: {exc}[/red]")
|
|
109
118
|
raise typer.Exit(1) from exc
|
|
110
|
-
console.print(f"[bold green]Promoted to[/bold green] {
|
|
119
|
+
console.print(f"[bold green]Promoted to[/bold green] {path / '.intent'}")
|
|
@@ -62,6 +62,11 @@ async def scout_project(
|
|
|
62
62
|
(ADR-119 D5 — no --accept-all). Requires Phase A first:
|
|
63
63
|
run `project onboard <target> --write` before this command.
|
|
64
64
|
"""
|
|
65
|
+
# path is sent as a plain string over HTTP for server-side signal
|
|
66
|
+
# extraction — a relative path would resolve against the CORE API
|
|
67
|
+
# process's cwd, not the caller's, and silently analyze the wrong repo.
|
|
68
|
+
path = path.resolve()
|
|
69
|
+
|
|
65
70
|
console.print(Rule("[bold cyan]Scout — Phase B: Rule Induction[/bold cyan]"))
|
|
66
71
|
console.print(f"[bold cyan]Target:[/bold cyan] {path}")
|
|
67
72
|
|
|
@@ -101,7 +106,7 @@ async def scout_project(
|
|
|
101
106
|
return
|
|
102
107
|
|
|
103
108
|
# ── Write ─────────────────────────────────────────────────────────────────
|
|
104
|
-
target_intent =
|
|
109
|
+
target_intent = path / ".intent"
|
|
105
110
|
rules_json = _build_rules_document(confirmed)
|
|
106
111
|
mappings_yaml = _build_mappings_document(confirmed)
|
|
107
112
|
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# tests/test_project.py
|
|
2
|
+
|
|
3
|
+
"""Unit tests for project onboard/promote/scout — stub the HTTP client layer.
|
|
4
|
+
|
|
5
|
+
Covers the relative-path resolution fix: `path` is sent as a plain string
|
|
6
|
+
over HTTP and resolved by the CORE API process, not this CLI. A relative
|
|
7
|
+
path must be resolved to absolute here before it's ever sent, or it would
|
|
8
|
+
silently mean a different location depending on which process resolves it.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from unittest.mock import AsyncMock, MagicMock, patch
|
|
15
|
+
|
|
16
|
+
from core_cli.resources.project.onboard import onboard_project, promote_onboard
|
|
17
|
+
from core_cli.resources.project.scout import scout_project
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
_ONBOARD_CLIENT = "core_cli.resources.project.onboard.CoreApiClient"
|
|
21
|
+
_SCOUT_CLIENT = "core_cli.resources.project.scout.CoreApiClient"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _make_project_client(**overrides) -> AsyncMock:
|
|
25
|
+
project = AsyncMock()
|
|
26
|
+
project.onboard.return_value = {"mode": "write", "stage_dir": None}
|
|
27
|
+
project.promote.return_value = {"promoted": True}
|
|
28
|
+
project.scout.return_value = {"candidates": [], "candidate_count": 0, "matched": 0}
|
|
29
|
+
for key, value in overrides.items():
|
|
30
|
+
setattr(project, key, value)
|
|
31
|
+
|
|
32
|
+
client = AsyncMock()
|
|
33
|
+
client.project = project
|
|
34
|
+
return client
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# ---------------------------------------------------------------------------
|
|
38
|
+
# onboard_project
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
async def test_onboard_resolves_relative_path(tmp_path: Path, monkeypatch) -> None:
|
|
43
|
+
monkeypatch.chdir(tmp_path)
|
|
44
|
+
mock_client = _make_project_client()
|
|
45
|
+
|
|
46
|
+
with patch(_ONBOARD_CLIENT, return_value=mock_client):
|
|
47
|
+
await onboard_project.__wrapped__(
|
|
48
|
+
ctx=MagicMock(), path=Path("."), write=True, stage=False
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
mock_client.project.onboard.assert_called_once_with(
|
|
52
|
+
str(tmp_path), write=True, stage=False
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
async def test_onboard_absolute_path_unchanged(tmp_path: Path) -> None:
|
|
57
|
+
mock_client = _make_project_client()
|
|
58
|
+
|
|
59
|
+
with patch(_ONBOARD_CLIENT, return_value=mock_client):
|
|
60
|
+
await onboard_project.__wrapped__(
|
|
61
|
+
ctx=MagicMock(), path=tmp_path, write=True, stage=False
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
mock_client.project.onboard.assert_called_once_with(
|
|
65
|
+
str(tmp_path), write=True, stage=False
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# ---------------------------------------------------------------------------
|
|
70
|
+
# promote_onboard
|
|
71
|
+
# ---------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
async def test_promote_resolves_relative_path(tmp_path: Path, monkeypatch) -> None:
|
|
75
|
+
monkeypatch.chdir(tmp_path)
|
|
76
|
+
mock_client = _make_project_client()
|
|
77
|
+
|
|
78
|
+
with patch(_ONBOARD_CLIENT, return_value=mock_client):
|
|
79
|
+
await promote_onboard.__wrapped__(ctx=MagicMock(), path=Path("."))
|
|
80
|
+
|
|
81
|
+
mock_client.project.promote.assert_called_once_with(str(tmp_path))
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# ---------------------------------------------------------------------------
|
|
85
|
+
# scout_project
|
|
86
|
+
# ---------------------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
async def test_scout_resolves_relative_path(tmp_path: Path, monkeypatch) -> None:
|
|
90
|
+
monkeypatch.chdir(tmp_path)
|
|
91
|
+
mock_client = _make_project_client()
|
|
92
|
+
|
|
93
|
+
with patch(_SCOUT_CLIENT, return_value=mock_client):
|
|
94
|
+
await scout_project.__wrapped__(
|
|
95
|
+
ctx=MagicMock(), path=Path("."), write=False, reset=False
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
mock_client.project.scout.assert_called_once_with(str(tmp_path), reset=False)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|