getcodexy 1.6.2__tar.gz → 1.7.0__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.
- getcodexy-1.7.0/PKG-INFO +83 -0
- getcodexy-1.7.0/README.md +73 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/pyproject.toml +4 -3
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/_version_data/uv.lock +1 -1
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component-manifest.json +15 -8
- getcodexy-1.7.0/src/codexy_runtime_tools/component_capability_observation.py +106 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_capability_probe.py +35 -95
- getcodexy-1.7.0/src/codexy_runtime_tools/component_capability_probe_process.py +250 -0
- getcodexy-1.7.0/src/codexy_runtime_tools/component_core_hooks.py +27 -0
- getcodexy-1.7.0/src/codexy_runtime_tools/component_health.py +146 -0
- getcodexy-1.6.2/src/codexy_runtime_tools/component_health.py → getcodexy-1.7.0/src/codexy_runtime_tools/component_health_support.py +11 -116
- getcodexy-1.7.0/src/codexy_runtime_tools/component_hook_activation.py +94 -0
- getcodexy-1.7.0/src/codexy_runtime_tools/component_hook_activation_expected.py +140 -0
- getcodexy-1.7.0/src/codexy_runtime_tools/component_hook_activation_host.py +240 -0
- getcodexy-1.7.0/src/codexy_runtime_tools/component_hook_activation_validation.py +156 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_inspection.py +43 -20
- getcodexy-1.7.0/src/codexy_runtime_tools/component_inspection_types.py +18 -0
- getcodexy-1.7.0/src/codexy_runtime_tools/component_interactive_probe.py +115 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_lifecycle_admission.py +1 -1
- getcodexy-1.7.0/src/codexy_runtime_tools/component_lifecycle_finish.py +72 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_lifecycle_operation.py +5 -6
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_lifecycle_recovery.py +42 -51
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_manifest.py +4 -2
- getcodexy-1.7.0/src/codexy_runtime_tools/component_mcp_probe.py +89 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_registration_health.py +55 -63
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_transition_journal.py +5 -2
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_transition_model.py +1 -1
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_transition_receipt.py +32 -7
- getcodexy-1.7.0/src/codexy_runtime_tools/component_watcher_probe.py +174 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/github_checks.py +13 -10
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/installer.py +17 -8
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/marketplace_identity.py +8 -2
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/package.py +9 -2
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/release_contract_validation.py +26 -26
- getcodexy-1.7.0/src/codexy_runtime_tools/release_provenance.py +32 -0
- getcodexy-1.7.0/src/codexy_runtime_tools/release_watcher_contract.py +34 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/runtime.py +5 -4
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/runtime_configuration.py +10 -2
- getcodexy-1.7.0/src/codexy_runtime_tools/title_policy.py +124 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/uv.lock +1 -1
- getcodexy-1.6.2/PKG-INFO +0 -8
- {getcodexy-1.6.2 → getcodexy-1.7.0}/build_backend.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/__init__.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/activation_transaction.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/cache.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_cli.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_integrity.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_inventory_classification.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_inventory_records.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_lifecycle.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_lifecycle_interlock.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_lifecycle_preflight.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_lifecycle_support.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_lifecycle_terminal.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_manifest_dependencies.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_manifest_values.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_observed_inventory.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_resolver.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_transaction_durability.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_transaction_identity.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_transaction_receipts.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_transaction_snapshot.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_transaction_state.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/component_transition_rejections.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/contract.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/github_pre_session.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/identity.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/local_marketplace.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/marketplace_repin.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/monolith_baseline.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/monolith_classifier.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/monolith_migration.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/monolith_migration_host.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/monolith_migration_plan.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/monolith_migration_state.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/package_archive.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/plugin_resolution.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/pre_session.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/source.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/updater.py +0 -0
- {getcodexy-1.6.2 → getcodexy-1.7.0}/src/codexy_runtime_tools/version_lock.py +0 -0
getcodexy-1.7.0/PKG-INFO
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: getcodexy
|
|
3
|
+
Version: 1.7.0
|
|
4
|
+
Summary: A Codex harness for coordinating agents through planning, implementation, verification, and review.
|
|
5
|
+
Author: Eunsoo Lee
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Project-URL: Repository, https://github.com/eunsoogi/codexy
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# Codexy
|
|
12
|
+
|
|
13
|
+
Codexy is a Codex harness for coordinating agents through planning,
|
|
14
|
+
implementation, verification, and review.
|
|
15
|
+
|
|
16
|
+
It helps Codex turn a broad repository request into an owned plan, divide work
|
|
17
|
+
among agents, implement changes, verify actual outcomes, and keep the next
|
|
18
|
+
handoff tied to current evidence.
|
|
19
|
+
|
|
20
|
+
## Components
|
|
21
|
+
|
|
22
|
+
Codexy is delivered as three cooperating components. `github` and `devtools`
|
|
23
|
+
each depend on `core`; the installer adds those dependencies automatically.
|
|
24
|
+
|
|
25
|
+
| Component | Plugin | What it adds |
|
|
26
|
+
| ---------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
27
|
+
| `core` | `codexy` | Orchestration, finite goals and plans, worktree ownership, specialist roles, instruction hooks, and proof-driven completion. |
|
|
28
|
+
| `github` | `codexy-github` | Issue-to-merge workflows for branches, pull requests, CI, review feedback, releases, and GitHub safety checks. |
|
|
29
|
+
| `devtools` | `codexy-devtools` | Local Codegraph and LSP servers, configuration, wrappers, and developer-tool guidance. |
|
|
30
|
+
|
|
31
|
+
The component model lets each repository install the capabilities it needs
|
|
32
|
+
without losing a single ownership and verification contract.
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
Install the `getcodexy` command, then install the complete Codexy product:
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
uv tool install getcodexy
|
|
40
|
+
uv tool update-shell
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Start a new shell so the updated tool path is available, then install Codexy:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
getcodexy install
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The default selection installs `core`, `github`, and `devtools`. To choose a
|
|
50
|
+
smaller dependency closure, name the optional components you need:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
getcodexy install core
|
|
54
|
+
getcodexy install github
|
|
55
|
+
getcodexy install devtools
|
|
56
|
+
getcodexy install github devtools
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Open a fresh Codex session after installation or update so the host can expose
|
|
60
|
+
the installed plugins, skills, agents, hooks, and MCP servers.
|
|
61
|
+
|
|
62
|
+
## Maintain and check Codexy
|
|
63
|
+
|
|
64
|
+
Keep the installer and installed component selection current, inspect health, or
|
|
65
|
+
remove an optional component:
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
uv tool upgrade getcodexy
|
|
69
|
+
getcodexy update
|
|
70
|
+
getcodexy status
|
|
71
|
+
getcodexy doctor
|
|
72
|
+
getcodexy remove github
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`status` reports the installed inventory and its consistency. `doctor` checks
|
|
76
|
+
host readiness and the health of each installed component. Update and removal
|
|
77
|
+
operations preserve the dependency closure and reject inconsistent selections.
|
|
78
|
+
|
|
79
|
+
## Project links
|
|
80
|
+
|
|
81
|
+
- [Source repository](https://github.com/eunsoogi/codexy)
|
|
82
|
+
- [Issue tracker](https://github.com/eunsoogi/codexy/issues)
|
|
83
|
+
- [License](https://github.com/eunsoogi/codexy/blob/main/LICENSE)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Codexy
|
|
2
|
+
|
|
3
|
+
Codexy is a Codex harness for coordinating agents through planning,
|
|
4
|
+
implementation, verification, and review.
|
|
5
|
+
|
|
6
|
+
It helps Codex turn a broad repository request into an owned plan, divide work
|
|
7
|
+
among agents, implement changes, verify actual outcomes, and keep the next
|
|
8
|
+
handoff tied to current evidence.
|
|
9
|
+
|
|
10
|
+
## Components
|
|
11
|
+
|
|
12
|
+
Codexy is delivered as three cooperating components. `github` and `devtools`
|
|
13
|
+
each depend on `core`; the installer adds those dependencies automatically.
|
|
14
|
+
|
|
15
|
+
| Component | Plugin | What it adds |
|
|
16
|
+
| ---------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
17
|
+
| `core` | `codexy` | Orchestration, finite goals and plans, worktree ownership, specialist roles, instruction hooks, and proof-driven completion. |
|
|
18
|
+
| `github` | `codexy-github` | Issue-to-merge workflows for branches, pull requests, CI, review feedback, releases, and GitHub safety checks. |
|
|
19
|
+
| `devtools` | `codexy-devtools` | Local Codegraph and LSP servers, configuration, wrappers, and developer-tool guidance. |
|
|
20
|
+
|
|
21
|
+
The component model lets each repository install the capabilities it needs
|
|
22
|
+
without losing a single ownership and verification contract.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
Install the `getcodexy` command, then install the complete Codexy product:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
uv tool install getcodexy
|
|
30
|
+
uv tool update-shell
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Start a new shell so the updated tool path is available, then install Codexy:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
getcodexy install
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The default selection installs `core`, `github`, and `devtools`. To choose a
|
|
40
|
+
smaller dependency closure, name the optional components you need:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
getcodexy install core
|
|
44
|
+
getcodexy install github
|
|
45
|
+
getcodexy install devtools
|
|
46
|
+
getcodexy install github devtools
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Open a fresh Codex session after installation or update so the host can expose
|
|
50
|
+
the installed plugins, skills, agents, hooks, and MCP servers.
|
|
51
|
+
|
|
52
|
+
## Maintain and check Codexy
|
|
53
|
+
|
|
54
|
+
Keep the installer and installed component selection current, inspect health, or
|
|
55
|
+
remove an optional component:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
uv tool upgrade getcodexy
|
|
59
|
+
getcodexy update
|
|
60
|
+
getcodexy status
|
|
61
|
+
getcodexy doctor
|
|
62
|
+
getcodexy remove github
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`status` reports the installed inventory and its consistency. `doctor` checks
|
|
66
|
+
host readiness and the health of each installed component. Update and removal
|
|
67
|
+
operations preserve the dependency closure and reject inconsistent selections.
|
|
68
|
+
|
|
69
|
+
## Project links
|
|
70
|
+
|
|
71
|
+
- [Source repository](https://github.com/eunsoogi/codexy)
|
|
72
|
+
- [Issue tracker](https://github.com/eunsoogi/codexy/issues)
|
|
73
|
+
- [License](https://github.com/eunsoogi/codexy/blob/main/LICENSE)
|
|
@@ -5,8 +5,9 @@ backend-path = ["."]
|
|
|
5
5
|
|
|
6
6
|
[project]
|
|
7
7
|
name = "getcodexy"
|
|
8
|
-
version = "1.
|
|
9
|
-
description = "
|
|
8
|
+
version = "1.7.0"
|
|
9
|
+
description = "A Codex harness for coordinating agents through planning, implementation, verification, and review."
|
|
10
|
+
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
11
|
requires-python = ">=3.10"
|
|
11
12
|
license = "MIT"
|
|
12
13
|
authors = [{ name = "Eunsoo Lee" }]
|
|
@@ -23,4 +24,4 @@ codexy-github-check = "codexy_runtime_tools.github_checks:main"
|
|
|
23
24
|
[tool.uv.build-backend]
|
|
24
25
|
module-name = "codexy_runtime_tools"
|
|
25
26
|
module-root = "src"
|
|
26
|
-
source-include = ["build_backend.py", "uv.lock"]
|
|
27
|
+
source-include = ["README.md", "build_backend.py", "uv.lock"]
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"conflicting-component-request": "A component request contains a duplicate or conflicting operand.",
|
|
11
11
|
"conflicting-installed-state": "An installed record does not match its official marketplace asset.",
|
|
12
12
|
"dependency-protected-removal": "A retained component still requires a requested removal.",
|
|
13
|
+
"hook-state-unavailable": "The trusted host did not return an inspectable effective Codexy hook state.",
|
|
13
14
|
"incompatible-component-selection": "A component selection is not in the manifest compatibility set.",
|
|
14
15
|
"inconsistent-installed-state": "The installed inventory violates component dependencies.",
|
|
15
16
|
"installed-state-mismatch": "The fresh installed inventory differs from the expected selection.",
|
|
@@ -18,6 +19,9 @@
|
|
|
18
19
|
"mixed-version-state": "Installed components do not share one lockstep version.",
|
|
19
20
|
"no-recorded-selection": "update requires a recorded installed component inventory.",
|
|
20
21
|
"operation-failed": "A mutation did not complete and its pre-operation selection was restored.",
|
|
22
|
+
"required-hook-disabled": "A required Codexy hook is present but disabled in the host activation state.",
|
|
23
|
+
"required-hook-trust-missing": "A required Codexy hook is not present in the host trusted activation state.",
|
|
24
|
+
"required-hook-trust-stale": "A required Codexy hook trust entry no longer matches the installed hook.",
|
|
21
25
|
"unknown-component": "A named component is not core, github, or devtools.",
|
|
22
26
|
"unknown-installed-component": "The official Codexy marketplace inventory contains an unrecognized product."
|
|
23
27
|
},
|
|
@@ -25,7 +29,7 @@
|
|
|
25
29
|
{
|
|
26
30
|
"id": "core",
|
|
27
31
|
"plugin": "codexy",
|
|
28
|
-
"version": "1.
|
|
32
|
+
"version": "1.7.0",
|
|
29
33
|
"dependencies": [],
|
|
30
34
|
"asset": {
|
|
31
35
|
"pluginId": "codexy@codexy",
|
|
@@ -33,6 +37,9 @@
|
|
|
33
37
|
"requiredPaths": [
|
|
34
38
|
".codex-plugin/plugin.json",
|
|
35
39
|
"assets/codexy-icon.png",
|
|
40
|
+
".mcp.json",
|
|
41
|
+
"mcp/codexy-mcp-watcher.sh",
|
|
42
|
+
"mcp/codexy-mcp-watcher.cmd",
|
|
36
43
|
"skills/dreaming/scripts/resumable-context-capsule.sh",
|
|
37
44
|
"skills/dreaming/scripts/resumable-context-capsule.cmd",
|
|
38
45
|
"skills/dreaming/scripts/resumable_context_capsule.py"
|
|
@@ -42,7 +49,7 @@
|
|
|
42
49
|
{
|
|
43
50
|
"id": "github",
|
|
44
51
|
"plugin": "codexy-github",
|
|
45
|
-
"version": "1.
|
|
52
|
+
"version": "1.7.0",
|
|
46
53
|
"dependencies": [
|
|
47
54
|
"core"
|
|
48
55
|
],
|
|
@@ -58,7 +65,7 @@
|
|
|
58
65
|
{
|
|
59
66
|
"id": "devtools",
|
|
60
67
|
"plugin": "codexy-devtools",
|
|
61
|
-
"version": "1.
|
|
68
|
+
"version": "1.7.0",
|
|
62
69
|
"dependencies": [
|
|
63
70
|
"core"
|
|
64
71
|
],
|
|
@@ -75,27 +82,27 @@
|
|
|
75
82
|
"compatibleCombinations": [
|
|
76
83
|
{
|
|
77
84
|
"components": [],
|
|
78
|
-
"version": "1.
|
|
85
|
+
"version": "1.7.0"
|
|
79
86
|
},
|
|
80
87
|
{
|
|
81
88
|
"components": [
|
|
82
89
|
"core"
|
|
83
90
|
],
|
|
84
|
-
"version": "1.
|
|
91
|
+
"version": "1.7.0"
|
|
85
92
|
},
|
|
86
93
|
{
|
|
87
94
|
"components": [
|
|
88
95
|
"core",
|
|
89
96
|
"github"
|
|
90
97
|
],
|
|
91
|
-
"version": "1.
|
|
98
|
+
"version": "1.7.0"
|
|
92
99
|
},
|
|
93
100
|
{
|
|
94
101
|
"components": [
|
|
95
102
|
"core",
|
|
96
103
|
"devtools"
|
|
97
104
|
],
|
|
98
|
-
"version": "1.
|
|
105
|
+
"version": "1.7.0"
|
|
99
106
|
},
|
|
100
107
|
{
|
|
101
108
|
"components": [
|
|
@@ -103,7 +110,7 @@
|
|
|
103
110
|
"github",
|
|
104
111
|
"devtools"
|
|
105
112
|
],
|
|
106
|
-
"version": "1.
|
|
113
|
+
"version": "1.7.0"
|
|
107
114
|
}
|
|
108
115
|
]
|
|
109
116
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""Additive capability observations for the read-only doctor report."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Mapping
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
UNKNOWN = "unknown"
|
|
9
|
+
_DIRECT_SOURCE = "getcodexy-direct-probe"
|
|
10
|
+
_DIRECT_SCOPE = "plugin-subprocess"
|
|
11
|
+
_REGISTRATION_SOURCE = "getcodexy-registration-check"
|
|
12
|
+
_REGISTRATION_SCOPE = "current-doctor-invocation"
|
|
13
|
+
|
|
14
|
+
CAPABILITIES = {
|
|
15
|
+
"core": (
|
|
16
|
+
"hook:codexy-thread-delivery",
|
|
17
|
+
"specialist:codexy-architect",
|
|
18
|
+
"specialist:codexy-auditor",
|
|
19
|
+
"specialist:codexy-cartographer",
|
|
20
|
+
"specialist:codexy-inspector",
|
|
21
|
+
"specialist:codexy-sentinel",
|
|
22
|
+
"specialist:codexy-shipwright",
|
|
23
|
+
"specialist:codexy-warden",
|
|
24
|
+
),
|
|
25
|
+
"github": ("hook:codexy-github-workflow-context", "specialist:codexy-weaver"),
|
|
26
|
+
"devtools": ("mcp:codegraph", "mcp:lsp"),
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def component_observations(
|
|
31
|
+
component: str,
|
|
32
|
+
configured: bool,
|
|
33
|
+
probe: Mapping[str, object] | None = None,
|
|
34
|
+
) -> dict[str, dict[str, object]]:
|
|
35
|
+
"""Return per-capability states without claiming native host execution."""
|
|
36
|
+
observations = {
|
|
37
|
+
name: _entry(configured, source=_REGISTRATION_SOURCE, scope=_REGISTRATION_SCOPE)
|
|
38
|
+
for name in CAPABILITIES.get(component, ())
|
|
39
|
+
}
|
|
40
|
+
if not probe:
|
|
41
|
+
return observations
|
|
42
|
+
direct = probe.get("_capability_probes")
|
|
43
|
+
if not isinstance(direct, dict):
|
|
44
|
+
direct = {}
|
|
45
|
+
for name in observations:
|
|
46
|
+
details = direct.get(name)
|
|
47
|
+
if isinstance(details, dict):
|
|
48
|
+
observations[name] = _entry(
|
|
49
|
+
configured,
|
|
50
|
+
started=details.get("started") is True,
|
|
51
|
+
callable=details.get("callable") is True,
|
|
52
|
+
source=_DIRECT_SOURCE,
|
|
53
|
+
scope=_DIRECT_SCOPE,
|
|
54
|
+
)
|
|
55
|
+
return observations
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def probe_observation(
|
|
59
|
+
capability: str,
|
|
60
|
+
configured: bool,
|
|
61
|
+
started: bool,
|
|
62
|
+
callable_: bool,
|
|
63
|
+
) -> dict[str, dict[str, object]]:
|
|
64
|
+
"""Describe one direct probe for health's additive observation mapping."""
|
|
65
|
+
return {
|
|
66
|
+
capability: {
|
|
67
|
+
"configured": configured,
|
|
68
|
+
"started": started,
|
|
69
|
+
"callable": callable_,
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def record_probe(
|
|
75
|
+
target: dict[str, object],
|
|
76
|
+
capability: str,
|
|
77
|
+
configured: bool,
|
|
78
|
+
started: bool,
|
|
79
|
+
callable_: bool,
|
|
80
|
+
) -> None:
|
|
81
|
+
"""Attach one current direct-probe result to an internal probe payload."""
|
|
82
|
+
probes = target.setdefault("_capability_probes", {})
|
|
83
|
+
if isinstance(probes, dict):
|
|
84
|
+
probes.update(probe_observation(capability, configured, started, callable_))
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _entry(
|
|
88
|
+
configured: bool,
|
|
89
|
+
*,
|
|
90
|
+
started: bool = False,
|
|
91
|
+
callable: bool = False,
|
|
92
|
+
source: str,
|
|
93
|
+
scope: str,
|
|
94
|
+
) -> dict[str, object]:
|
|
95
|
+
return {
|
|
96
|
+
"states": {
|
|
97
|
+
"configured": "configured" if configured else UNKNOWN,
|
|
98
|
+
"loaded": "loaded" if started else UNKNOWN,
|
|
99
|
+
"callable": "callable" if callable else UNKNOWN,
|
|
100
|
+
"verified": UNKNOWN,
|
|
101
|
+
},
|
|
102
|
+
"source": source if configured or started or callable else UNKNOWN,
|
|
103
|
+
"scope": scope if configured or started or callable else UNKNOWN,
|
|
104
|
+
"host_id": None,
|
|
105
|
+
"session_id": None,
|
|
106
|
+
}
|
|
@@ -2,10 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
5
|
-
from collections import namedtuple
|
|
6
5
|
import shlex
|
|
7
6
|
import subprocess
|
|
8
7
|
|
|
8
|
+
from .component_hook_activation import ACTIVATION_REPAIRS
|
|
9
|
+
from .component_capability_observation import record_probe
|
|
10
|
+
from .component_capability_probe_process import (
|
|
11
|
+
_RUN_OPTIONS,
|
|
12
|
+
_RunResult,
|
|
13
|
+
_probe_diagnostics,
|
|
14
|
+
_run,
|
|
15
|
+
)
|
|
16
|
+
from .component_watcher_probe import probe_watcher as _probe_watcher
|
|
9
17
|
from .version_lock import default_package_version
|
|
10
18
|
|
|
11
19
|
|
|
@@ -16,8 +24,6 @@ _START_REPAIR = f"repair the installed launcher/runtime, then {_RERUN}"
|
|
|
16
24
|
_CALL_REPAIR = f"use the reported safe component fallback and {_RERUN}"
|
|
17
25
|
_EXPOSED_REPAIR = "repair the Codexy registration, then restart Codex"
|
|
18
26
|
_IDENTITY_REPAIR = "reinstall the selected release, then restart Codex"
|
|
19
|
-
_RUN_OPTIONS = {"capture_output": True, "text": True, "timeout": 5}
|
|
20
|
-
_RunResult = namedtuple("_RunResult", "returncode stdout category")
|
|
21
27
|
FAILURES = {
|
|
22
28
|
"trusted-inventory-unavailable": (_INVENTORY_REPAIR, False),
|
|
23
29
|
"component-not-installed": ("getcodexy bootstrap", True),
|
|
@@ -27,6 +33,7 @@ FAILURES = {
|
|
|
27
33
|
"capability-call-failed": (_CALL_REPAIR, False),
|
|
28
34
|
"runtime-identity-mismatch": (_IDENTITY_REPAIR, True),
|
|
29
35
|
"artifact-authority-invalid": ("reinstall from a trusted release artifact", True),
|
|
36
|
+
**ACTIVATION_REPAIRS,
|
|
30
37
|
}
|
|
31
38
|
HOOK_SPECS = {
|
|
32
39
|
"core": ("PermissionRequest", "codexy-thread-delivery"),
|
|
@@ -53,17 +60,34 @@ def probe_component(component, plugin, record):
|
|
|
53
60
|
base = _base(record)
|
|
54
61
|
if plugin is None:
|
|
55
62
|
return base
|
|
63
|
+
if component == "core":
|
|
64
|
+
return _probe_core(plugin, base)
|
|
56
65
|
probe = _probe_hook if component in HOOK_SPECS else _probe_devtools
|
|
57
66
|
return probe(component, plugin, base)
|
|
58
67
|
|
|
59
68
|
|
|
69
|
+
def _probe_core(plugin, base):
|
|
70
|
+
hook = _probe_hook("core", plugin, base)
|
|
71
|
+
if not hook.get("started") or not hook.get("callable"):
|
|
72
|
+
return hook
|
|
73
|
+
watcher = _probe_watcher(plugin, base)
|
|
74
|
+
probes = dict(hook.get("_capability_probes", {}))
|
|
75
|
+
probes.update(watcher.get("_capability_probes", {}))
|
|
76
|
+
watcher["_capability_probes"] = probes
|
|
77
|
+
if not watcher.get("started") or not watcher.get("callable"):
|
|
78
|
+
return watcher
|
|
79
|
+
return _outcome(base, _capability_probes=probes)
|
|
80
|
+
|
|
81
|
+
|
|
60
82
|
def _probe_hook(component, plugin, base):
|
|
61
83
|
event, marker = HOOK_SPECS[component]
|
|
84
|
+
capability = f"hook:{marker}"
|
|
62
85
|
payload = {"prompt": "review GitHub issue 723"}
|
|
63
86
|
if component == "core":
|
|
64
87
|
payload = {"tool_name": "codex_app__send_message_to_thread", "tool_input": {}}
|
|
65
88
|
command = _registered_hook(plugin, event, marker)
|
|
66
89
|
if not command:
|
|
90
|
+
record_probe(base, capability, False, False, False)
|
|
67
91
|
return _failure(base, "capability-not-exposed")
|
|
68
92
|
result = _run(
|
|
69
93
|
_argv(command, plugin),
|
|
@@ -71,10 +95,13 @@ def _probe_hook(component, plugin, base):
|
|
|
71
95
|
json.dumps(payload),
|
|
72
96
|
os.environ | {"PLUGIN_ROOT": str(plugin)},
|
|
73
97
|
)
|
|
98
|
+
base["_capability_probe"] = _probe_diagnostics(result)
|
|
74
99
|
base["_category"] = result.category
|
|
75
100
|
if result.category == "missing-launcher":
|
|
101
|
+
record_probe(base, capability, True, False, False)
|
|
76
102
|
return _failure(base, "component-start-failed", started=False)
|
|
77
103
|
if result.category in {"timeout", "nonzero-exit"}:
|
|
104
|
+
record_probe(base, capability, True, True, False)
|
|
78
105
|
return _failure(base, "capability-call-failed")
|
|
79
106
|
try:
|
|
80
107
|
output = json.loads(result.stdout.strip().splitlines()[-1])[
|
|
@@ -87,6 +114,8 @@ def _probe_hook(component, plugin, base):
|
|
|
87
114
|
valid = False
|
|
88
115
|
if not valid:
|
|
89
116
|
base["_category"] = "malformed-output"
|
|
117
|
+
base["_capability_probe"]["category"] = "malformed-output"
|
|
118
|
+
record_probe(base, capability, True, True, valid)
|
|
90
119
|
reason = None if valid else "capability-not-exposed"
|
|
91
120
|
return _outcome(base, callable=valid, reason_code=reason)
|
|
92
121
|
|
|
@@ -108,71 +137,6 @@ def _registered_hook(plugin, event, marker):
|
|
|
108
137
|
return None
|
|
109
138
|
|
|
110
139
|
|
|
111
|
-
def _probe_devtools(_component, plugin, base):
|
|
112
|
-
try:
|
|
113
|
-
config = json.loads((plugin / ".mcp.json").read_text(encoding="utf-8"))
|
|
114
|
-
except (OSError, ValueError, json.JSONDecodeError):
|
|
115
|
-
return _failure(base, "capability-not-exposed")
|
|
116
|
-
probes = []
|
|
117
|
-
for server in MCP_SPECS:
|
|
118
|
-
server_config = config.get(server) if isinstance(config, dict) else None
|
|
119
|
-
result = probe_server(server, plugin, server_config)
|
|
120
|
-
if not result.get("started") or not result.get("callable"):
|
|
121
|
-
return result
|
|
122
|
-
probes.append(result)
|
|
123
|
-
return _outcome(
|
|
124
|
-
base,
|
|
125
|
-
runtime_name=",".join(str(item["runtime_name"]) for item in probes),
|
|
126
|
-
runtime_version=probes[0]["runtime_version"],
|
|
127
|
-
runtime_names=[item["runtime_name"] for item in probes],
|
|
128
|
-
runtime_versions=[item["runtime_version"] for item in probes],
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
def probe_server(server, plugin, config):
|
|
133
|
-
base = _base()
|
|
134
|
-
if not isinstance(config, dict) or not isinstance(config.get("command"), str):
|
|
135
|
-
return _failure(base, "capability-not-exposed")
|
|
136
|
-
target, extra = MCP_SPECS[server]
|
|
137
|
-
arguments = {"root": str(plugin), **extra}
|
|
138
|
-
requests = (
|
|
139
|
-
_request("initialize", 1, _INITIALIZE_PARAMS),
|
|
140
|
-
_request("notifications/initialized"),
|
|
141
|
-
_request("tools/list", 2),
|
|
142
|
-
_request("tools/call", 3, {"name": target, "arguments": arguments}),
|
|
143
|
-
)
|
|
144
|
-
returncode, responses, timed = _rpc(
|
|
145
|
-
_argv(config["command"], plugin, config.get("args", ())), plugin, requests
|
|
146
|
-
)
|
|
147
|
-
if 1 not in responses:
|
|
148
|
-
failed = bool(returncode or timed)
|
|
149
|
-
reason = "component-start-failed" if failed else "capability-not-exposed"
|
|
150
|
-
return _failure(base, reason, started=not failed)
|
|
151
|
-
initialized = responses[1].get("result", {})
|
|
152
|
-
listed = responses.get(2, {}).get("result", {})
|
|
153
|
-
info = initialized.get("serverInfo", {}) if isinstance(initialized, dict) else {}
|
|
154
|
-
tools = listed.get("tools", []) if isinstance(listed, dict) else []
|
|
155
|
-
if (
|
|
156
|
-
not isinstance(info, dict)
|
|
157
|
-
or not isinstance(tools, list)
|
|
158
|
-
or not any(
|
|
159
|
-
isinstance(tool, dict) and tool.get("name") == target for tool in tools
|
|
160
|
-
)
|
|
161
|
-
):
|
|
162
|
-
return _failure(base, "capability-not-exposed")
|
|
163
|
-
call = responses.get(3, {})
|
|
164
|
-
result = call.get("result") if isinstance(call, dict) else None
|
|
165
|
-
if (
|
|
166
|
-
not isinstance(result, dict)
|
|
167
|
-
or any((call.get("error"), call.get("isError"), result.get("isError")))
|
|
168
|
-
or not isinstance(result.get("content"), list)
|
|
169
|
-
):
|
|
170
|
-
return _failure(base, "capability-call-failed")
|
|
171
|
-
return _outcome(
|
|
172
|
-
base, runtime_name=info.get("name"), runtime_version=info.get("version")
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
|
|
176
140
|
def _base(record=None):
|
|
177
141
|
return dict(
|
|
178
142
|
started=False,
|
|
@@ -190,33 +154,6 @@ def _outcome(base, **fields):
|
|
|
190
154
|
return {**base, "started": True, "callable": True, **fields}
|
|
191
155
|
|
|
192
156
|
|
|
193
|
-
def _run(argv, cwd, input_text, env=None):
|
|
194
|
-
try:
|
|
195
|
-
result = subprocess.run(
|
|
196
|
-
argv, input=input_text, cwd=cwd, env=env, **_RUN_OPTIONS
|
|
197
|
-
)
|
|
198
|
-
except subprocess.TimeoutExpired as error:
|
|
199
|
-
return _RunResult(-1, error.stdout or "", "timeout")
|
|
200
|
-
except OSError:
|
|
201
|
-
return _RunResult(-1, "", "missing-launcher")
|
|
202
|
-
category = "success" if result.returncode == 0 else "nonzero-exit"
|
|
203
|
-
category = "missing-launcher" if result.returncode == 127 else category
|
|
204
|
-
return _RunResult(result.returncode, result.stdout, category)
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
def _rpc(argv, cwd, requests):
|
|
208
|
-
run = _run(argv, cwd, "\n".join(json.dumps(request) for request in requests) + "\n")
|
|
209
|
-
values = {}
|
|
210
|
-
for line in (run.stdout or "").splitlines():
|
|
211
|
-
try:
|
|
212
|
-
value = json.loads(line)
|
|
213
|
-
except (ValueError, json.JSONDecodeError):
|
|
214
|
-
continue
|
|
215
|
-
if isinstance(value, dict) and isinstance(value.get("id"), int):
|
|
216
|
-
values[value["id"]] = value
|
|
217
|
-
return run.returncode, values, run.category == "timeout"
|
|
218
|
-
|
|
219
|
-
|
|
220
157
|
def _argv(command, plugin, args=()):
|
|
221
158
|
values = shlex.split(command.replace("${PLUGIN_ROOT}", str(plugin))) + [
|
|
222
159
|
str(value) for value in (args if isinstance(args, list) else ())
|
|
@@ -232,6 +169,9 @@ def _argv(command, plugin, args=()):
|
|
|
232
169
|
return values
|
|
233
170
|
|
|
234
171
|
|
|
172
|
+
from .component_mcp_probe import _probe_devtools, probe_server # noqa: E402
|
|
173
|
+
|
|
174
|
+
|
|
235
175
|
def probe_reason(probe, default):
|
|
236
176
|
reason = probe.get("reason_code")
|
|
237
177
|
return reason if isinstance(reason, str) and reason in FAILURES else default
|