core-cli 1.0.1__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.
Files changed (58) hide show
  1. {core_cli-1.0.1 → core_cli-1.0.2}/PKG-INFO +2 -2
  2. {core_cli-1.0.1 → core_cli-1.0.2}/README.md +1 -1
  3. {core_cli-1.0.1 → core_cli-1.0.2}/pyproject.toml +1 -1
  4. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/project/onboard.py +11 -2
  5. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/project/scout.py +6 -1
  6. core_cli-1.0.2/tests/test_project.py +98 -0
  7. {core_cli-1.0.1 → core_cli-1.0.2}/.github/workflows/ci.yml +0 -0
  8. {core_cli-1.0.1 → core_cli-1.0.2}/.gitignore +0 -0
  9. {core_cli-1.0.1 → core_cli-1.0.2}/LICENSE +0 -0
  10. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/__init__.py +0 -0
  11. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/main.py +0 -0
  12. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/__init__.py +0 -0
  13. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/code/__init__.py +0 -0
  14. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/code/actions.py +0 -0
  15. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/code/audit_duplicates.py +0 -0
  16. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/code/bridges.py +0 -0
  17. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/code/check_imports.py +0 -0
  18. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/code/check_ui.py +0 -0
  19. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/code/docstrings.py +0 -0
  20. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/code/fix_atomic.py +0 -0
  21. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/code/format.py +0 -0
  22. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/code/hub.py +0 -0
  23. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/code/integrity.py +0 -0
  24. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/code/lint.py +0 -0
  25. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/code/logging.py +0 -0
  26. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/code/test.py +0 -0
  27. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/lane/__init__.py +0 -0
  28. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/lane/claim.py +0 -0
  29. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/lane/list.py +0 -0
  30. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/lane/next.py +0 -0
  31. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/lane/propose.py +0 -0
  32. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/project/__init__.py +0 -0
  33. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/project/docs.py +0 -0
  34. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/proposals/__init__.py +0 -0
  35. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/proposals/create.py +0 -0
  36. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/proposals/integrate.py +0 -0
  37. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/proposals/list.py +0 -0
  38. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/proposals/manage.py +0 -0
  39. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/secrets/__init__.py +0 -0
  40. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/secrets/hub.py +0 -0
  41. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/secrets/manage.py +0 -0
  42. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/symbols/__init__.py +0 -0
  43. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/symbols/audit.py +0 -0
  44. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/symbols/fix_ids.py +0 -0
  45. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/symbols/hub.py +0 -0
  46. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/symbols/resolve_duplicates.py +0 -0
  47. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/symbols/sync.py +0 -0
  48. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/vectors/__init__.py +0 -0
  49. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/vectors/hub.py +0 -0
  50. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/vectors/query.py +0 -0
  51. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/vectors/rebuild.py +0 -0
  52. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/vectors/status.py +0 -0
  53. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/vectors/sync.py +0 -0
  54. {core_cli-1.0.1 → core_cli-1.0.2}/src/core_cli/resources/vectors/sync_code.py +0 -0
  55. {core_cli-1.0.1 → core_cli-1.0.2}/tests/__init__.py +0 -0
  56. {core_cli-1.0.1 → core_cli-1.0.2}/tests/test_code.py +0 -0
  57. {core_cli-1.0.1 → core_cli-1.0.2}/tests/test_lane.py +0 -0
  58. {core_cli-1.0.1 → 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.1
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
@@ -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.1"
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"}
@@ -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-admin project onboard promote {path}[/bold]"
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] {Path(path).resolve() / '.intent'}")
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 = Path(path).resolve() / ".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