arkaos 4.3.1 → 4.3.2

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.
package/README.md CHANGED
@@ -491,7 +491,7 @@ npx arkaos uninstall # Remove ArkaOS
491
491
  **Release pipeline (operator-side, headless):**
492
492
 
493
493
  ```
494
- python -m core.release.preflight_cli --expected-npm-user <user>
494
+ ~/.arkaos/bin/arka-py -m core.release.preflight_cli --expected-npm-user <user>
495
495
  # Step 0 NON-NEGOTIABLE before any tag/push/publish (v2.43.0+).
496
496
  # Six checks: version-alignment / npm-auth / npm-publish-capability /
497
497
  # gh-auth / git-remote / git-clean. Plus no-client-name-leaks
package/VERSION CHANGED
@@ -1 +1 @@
1
- 4.3.1
1
+ 4.3.2
package/arka/SKILL.md CHANGED
@@ -180,10 +180,10 @@ violation (squad-routing, arka-supremacy, spec-driven, mandatory-qa).
180
180
  | Command | Description |
181
181
  |---------|-------------|
182
182
  | `/arka status` | System status (version, departments, agents, active projects). Includes **LLM costs (24h)** section: top-line cost + cache hit rate + call count from `core.runtime.llm_cost_telemetry.summarise(period="today")`. Also includes **Enforcement (24h)** section: total calls, block rate, top blocked tools/reasons from `core.governance.enforcement_telemetry.summarise(period="today")` (PR19 v2.41.0). Plus **Reorganization (today)** section: today's proposal path + artifact count from `core.cognition.reorganizer_scheduler.status_summary()` (PR24 v2.46.0). |
183
- | `/arka costs [period]` | LLM cost visibility — aggregates telemetry by day/week/month/all, with top expensive sessions. See `arka/skills/costs/SKILL.md`. Shells out to `python -m core.runtime.llm_cost_telemetry_cli <period>`. |
184
- | `/arka enforcement [period]` | Enforcement compliance — aggregates flow-marker enforcement telemetry by day/week/month/all. Shows block rate, top blocked tools, top reasons. Shells out to `python -m core.governance.enforcement_telemetry_cli <period>`. |
185
- | `/arka compliance [period]` | Behavior compliance summary (PR29 v2.48.0) — aggregates stop-hook telemetry by day/week/month/all. Shows rates for the four contracts: closing marker, `[arka:meta]` tag, KB citation pass, sycophancy clean. Shells out to `python -m core.governance.compliance_telemetry_cli <period>`. |
186
- | `/arka reorganize [--since-days N]` | Dreaming → Agent reorganizer. Aggregates recent KB pattern/anti-pattern/lesson artifacts (default last 7 days) into a markdown proposal at `~/.arkaos/reorganize-proposals/<date>.md`. **Propose-only** — never modifies agent YAMLs. Sanitizes client identifiers from titles and body excerpts; drops `tags:` field entirely to prevent project-name leaks. **Auto-fires on session start when today's proposal is missing** (PR24 v2.46.0 stale-aware trigger, 30s timeout, background). Shells out to `python -m core.cognition.reorganizer_cli [--since-days N] [--dry-run]`. |
183
+ | `/arka costs [period]` | LLM cost visibility — aggregates telemetry by day/week/month/all, with top expensive sessions. See `arka/skills/costs/SKILL.md`. Shells out to `~/.arkaos/bin/arka-py -m core.runtime.llm_cost_telemetry_cli <period>`. |
184
+ | `/arka enforcement [period]` | Enforcement compliance — aggregates flow-marker enforcement telemetry by day/week/month/all. Shows block rate, top blocked tools, top reasons. Shells out to `~/.arkaos/bin/arka-py -m core.governance.enforcement_telemetry_cli <period>`. |
185
+ | `/arka compliance [period]` | Behavior compliance summary (PR29 v2.48.0) — aggregates stop-hook telemetry by day/week/month/all. Shows rates for the four contracts: closing marker, `[arka:meta]` tag, KB citation pass, sycophancy clean. Shells out to `~/.arkaos/bin/arka-py -m core.governance.compliance_telemetry_cli <period>`. |
186
+ | `/arka reorganize [--since-days N]` | Dreaming → Agent reorganizer. Aggregates recent KB pattern/anti-pattern/lesson artifacts (default last 7 days) into a markdown proposal at `~/.arkaos/reorganize-proposals/<date>.md`. **Propose-only** — never modifies agent YAMLs. Sanitizes client identifiers from titles and body excerpts; drops `tags:` field entirely to prevent project-name leaks. **Auto-fires on session start when today's proposal is missing** (PR24 v2.46.0 stale-aware trigger, 30s timeout, background). Shells out to `~/.arkaos/bin/arka-py -m core.cognition.reorganizer_cli [--since-days N] [--dry-run]`. |
187
187
  | `/arka standup` | Daily standup (projects, priorities, blockers, updates) |
188
188
  | `/arka monitor` | System health monitoring |
189
189
  | `/arka onboard <path>` | Onboard an existing project into ArkaOS |
@@ -42,7 +42,7 @@ surfaces usage and emits soft advisories. It never blocks a call.
42
42
  This skill shells out to the Python CLI:
43
43
 
44
44
  ```bash
45
- python -m core.runtime.llm_cost_telemetry_cli <period>
45
+ ~/.arkaos/bin/arka-py -m core.runtime.llm_cost_telemetry_cli <period>
46
46
  ```
47
47
 
48
48
  Source:
@@ -24,9 +24,9 @@ mechanical work economises.
24
24
  Run and read all three:
25
25
 
26
26
  ```bash
27
- python -m core.runtime.model_router_cli discover # Ollama + local models
28
- python -m core.runtime.model_router_cli --json # current routing
29
- python -m core.runtime.model_router_cli usage --period week --json
27
+ ~/.arkaos/bin/arka-py -m core.runtime.model_router_cli discover # Ollama + local models
28
+ ~/.arkaos/bin/arka-py -m core.runtime.model_router_cli --json # current routing
29
+ ~/.arkaos/bin/arka-py -m core.runtime.model_router_cli usage --period week --json
30
30
  ```
31
31
 
32
32
  Also check key presence (never print values): `OPENROUTER_API_KEY` /
@@ -23,7 +23,7 @@ evidence, not from model size.
23
23
  ## Review Rubric (evidence interpretation, not role-play)
24
24
 
25
25
  1. Run the engine first — no verdict without a report:
26
- `python -m core.governance.evidence_checks <project_dir> [--changed-files ...] [--test-command '...'] --json`
26
+ `~/.arkaos/bin/arka-py -m core.governance.evidence_checks <project_dir> [--changed-files ...] [--test-command '...'] --json`
27
27
  2. Dispatch Eduardo (spellcheck + changed copy) and Francisca
28
28
  (lint/typecheck/tests/coverage/security-grep) with the report and the
29
29
  structured output schema `QG_VERDICT_JSON_SCHEMA` from
@@ -24,7 +24,7 @@ not cut corners to look fast.
24
24
  dispatch with `[arka:dispatch] paulo -> <specialist>`.
25
25
  3. Evidence over narration: a task is done when its tests RAN and exited 0
26
26
  on record — never when a subagent says it is done. Run
27
- `python -m core.governance.evidence_checks <project_dir> --json` before
27
+ `~/.arkaos/bin/arka-py -m core.governance.evidence_checks <project_dir> --json` before
28
28
  claiming completion.
29
29
  4. Submit to the Quality Gate (marta-cqo) with the evidence report; expect
30
30
  a structured `QGVerdict` back. REJECTED means loop, not negotiate.
@@ -142,7 +142,7 @@ enforcement_levels:
142
142
  scope: "All technical: code quality, tests, UX/UI, data integrity, security, performance"
143
143
  veto_power: true
144
144
  process:
145
- - "Evidence engine runs first: python -m core.governance.evidence_checks — the verdict derives from its report"
145
+ - "Evidence engine runs first: ~/.arkaos/bin/arka-py -m core.governance.evidence_checks — the verdict derives from its report"
146
146
  - "Marta receives all output from execution phase plus the evidence report"
147
147
  - "Marta dispatches Eduardo (text) and Francisca (technical) in parallel, each in an INDEPENDENT subagent context (clean context, no stake in the work being approved)"
148
148
  - "Each reviewer returns a structured QGVerdict (APPROVED or REJECTED) with specific issues; the verdict MUST name the benchmark used (reference_companies.application) and enumerate the concrete rejections a top-tier lead would raise; evidence overall=fail forces REJECTED"
@@ -197,7 +197,7 @@ enforcement_levels:
197
197
  # ─── Rule added in PR3 Squad Intelligence Upgrade (2026-05-28) ───────
198
198
  - id: agent-experience-persistence
199
199
  rule: "Quality Gate REJECTED verdicts MUST result in an Experience record for the failing agent, written to ~/.arkaos/agents/<agent_id>/experiences.jsonl via core.governance.cqo_experience_recorder. Next dispatch of that agent loads the experiences via the Synapse L2.6 layer (core.synapse.agent_experiences_layer) so the lesson carries across sessions. Without this, REJECTED reports evaporate as conversation turns and the same structural mistakes recur."
200
- enforcement: "PR3 v3.74.0 ships the storage, parser, and Synapse layer. v3.74.1 will wire a PostToolUse hook on the Agent tool that auto-records on REJECTED verdicts. Until then orchestrators MUST call cqo_experience_recorder.record_from_verdict() after every CQO dispatch. Audit via python -m core.governance.agent_experiences_cli list <agent_id>."
200
+ enforcement: "PR3 v3.74.0 ships the storage, parser, and Synapse layer. v3.74.1 will wire a PostToolUse hook on the Agent tool that auto-records on REJECTED verdicts. Until then orchestrators MUST call cqo_experience_recorder.record_from_verdict() after every CQO dispatch. Audit via ~/.arkaos/bin/arka-py -m core.governance.agent_experiences_cli list <agent_id>."
201
201
 
202
202
  should:
203
203
  description: "Best practices. Encouraged but not enforced."
@@ -221,17 +221,17 @@ enforcement_levels:
221
221
  # ─── Rule added in PR6 Squad Intelligence Upgrade (2026-05-28) ───────
222
222
  - id: design-system-locked
223
223
  rule: "Each project SHOULD declare a `design-system.yaml` at its root listing tokens (colors, spacing, fonts), allowed_components, file_globs to scan, and forbidden_patterns with suggestions. The per-project linter (core.governance.design_system_lint) detects UI/UX drift — hex literals outside the palette, inline style attributes, raw HTML where a Nuxt UI component exists, etc. Adoption is opt-in per project (no YAML at project root → no violations). v3.77.0 is advisory-only; pre-commit hook integration lands in v3.77.x once the rule sets stabilise across the operator's projects."
224
- enforcement: "Run via `python -m core.governance.design_system_lint_cli <project_path>` (text or JSON output, optional --exit-on-violations). Example template at `docs/examples/design-system-example.yaml`."
224
+ enforcement: "Run via `~/.arkaos/bin/arka-py -m core.governance.design_system_lint_cli <project_path>` (text or JSON output, optional --exit-on-violations). Example template at `docs/examples/design-system-example.yaml`."
225
225
 
226
226
  # ─── Rule added in PR5 Squad Intelligence Upgrade (2026-05-28) ───────
227
227
  - id: dna-fidelity-warn
228
228
  rule: "Agent outputs are compared against the `signature_markers` block in each agent's YAML at the end of every turn. Forbidden patterns (avoid_patterns) and missing opening phrases (opening_phrases) generate FidelityViolation records in ~/.arkaos/telemetry/dna-fidelity.jsonl. v3.76.0 is soft-warn — violations are recorded for telemetry and operator review, not blocked. Hard-block mode lands later once the marker set is calibrated against real production usage."
229
- enforcement: "Stop hook (config/hooks/stop.sh) calls core.governance.dna_fidelity.check_fidelity at turn end. Telemetry inspection: python -m core.governance.dna_fidelity_cli list|summary. Agents in scope for v3.76.0: tech-lead-paulo, cqo-marta, copy-director-eduardo, tech-director-francisca. Remaining 61 agent YAMLs get signature_markers in v3.76.x curation work."
229
+ enforcement: "Stop hook (config/hooks/stop.sh) calls core.governance.dna_fidelity.check_fidelity at turn end. Telemetry inspection: ~/.arkaos/bin/arka-py -m core.governance.dna_fidelity_cli list|summary. Agents in scope for v3.76.0: tech-lead-paulo, cqo-marta, copy-director-eduardo, tech-director-francisca. Remaining 61 agent YAMLs get signature_markers in v3.76.x curation work."
230
230
 
231
231
  # ─── Rule added in PR4 Squad Intelligence Upgrade (2026-05-28) ───────
232
232
  - id: pattern-library-first
233
233
  rule: "Before designing any new feature, consult the Pattern Library (core.knowledge.pattern_cards). If a similar pattern exists, reuse it or explicitly document why divergence is justified in the spec. New patterns SHOULD be registered with record_pattern() after Quality Gate APPROVED so future feature work inherits the prior art."
234
- enforcement: "Synapse layer L7.5 (core.synapse.pattern_library_layer) auto-injects top matching cards as context. Audit via python -m core.knowledge.pattern_cards_cli list. SHOULD level — promoted from advisory once telemetry shows consistent consultation."
234
+ enforcement: "Synapse layer L7.5 (core.synapse.pattern_library_layer) auto-injects top matching cards as context. Audit via ~/.arkaos/bin/arka-py -m core.knowledge.pattern_cards_cli list. SHOULD level — promoted from advisory once telemetry shows consistent consultation."
235
235
 
236
236
  tier_hierarchy:
237
237
  description: "Agent authority levels inspired by SpaceX/Google/Anthropic org structures"
@@ -0,0 +1,43 @@
1
+ # ArkaOS shared Python resolver (PowerShell side) — single source of truth.
2
+ #
3
+ # Windows mirror of config/hooks/_lib/arka_python.sh and
4
+ # installer/python-resolver.js. Prefers the ~/.arkaos venv (has pyyaml/
5
+ # pydantic); never silently falls back to a system python that lacks ArkaOS
6
+ # deps. Dot-source this file: it sets $env:ARKA_PY.
7
+ #
8
+ # Resolution order:
9
+ # 1. $env:ARKAOS_PYTHON (explicit override), if it exists.
10
+ # 2. The ArkaOS venv — Scripts\python.exe (Windows) then bin/python.
11
+ # 3. Any python3/python/py on PATH that can `import yaml`.
12
+ # 4. Bare `python` as a last resort.
13
+
14
+ function Resolve-ArkaPython {
15
+ if ($env:ARKAOS_PYTHON -and (Test-Path -LiteralPath $env:ARKAOS_PYTHON)) {
16
+ return $env:ARKAOS_PYTHON
17
+ }
18
+
19
+ $base = if ($env:USERPROFILE) { $env:USERPROFILE } elseif ($HOME) { $HOME } else { "" }
20
+ if ($base) {
21
+ $candidates = @(
22
+ (Join-Path $base ".arkaos\venv\Scripts\python.exe"),
23
+ (Join-Path $base ".arkaos/venv/bin/python"),
24
+ (Join-Path $base ".arkaos\.venv\Scripts\python.exe"),
25
+ (Join-Path $base ".arkaos/.venv/bin/python")
26
+ )
27
+ foreach ($c in $candidates) {
28
+ if (Test-Path -LiteralPath $c) { return $c }
29
+ }
30
+ }
31
+
32
+ foreach ($name in @("python3", "python", "py")) {
33
+ $cmd = Get-Command $name -ErrorAction SilentlyContinue
34
+ if ($cmd) {
35
+ & $cmd.Source -c "import yaml" 2>$null
36
+ if ($LASTEXITCODE -eq 0) { return $cmd.Source }
37
+ }
38
+ }
39
+
40
+ return "python"
41
+ }
42
+
43
+ $env:ARKA_PY = Resolve-ArkaPython
@@ -0,0 +1,63 @@
1
+ # shellcheck shell=bash
2
+ # ArkaOS shared Python resolver (shell side) — single source of truth.
3
+ #
4
+ # Mirrors installer/python-resolver.js (getArkaosPython). Every hook, the
5
+ # state reader, the agent-provision helper, and the `arka-py` shim source
6
+ # THIS file instead of probing for `python3` on their own. Rationale: an
7
+ # unrelated project venv first on PATH, or a system python without pyyaml/
8
+ # pydantic, silently degrades every gate. The ArkaOS venv is authoritative.
9
+ #
10
+ # Contract of arka_resolve_python():
11
+ # Echoes the interpreter path and returns 0 when a provisioned ArkaOS
12
+ # interpreter is found; echoes a best-effort fallback and returns 1 when
13
+ # only a bare `python3` remains (caller degrades gracefully).
14
+ #
15
+ # Resolution order:
16
+ # 1. $ARKAOS_PYTHON (explicit operator override), if executable.
17
+ # 2. The ArkaOS venv — ~/.arkaos/venv then ~/.arkaos/.venv, python then
18
+ # python3. `[ -x ]` follows symlinks, so a Homebrew-rotated broken
19
+ # symlink is skipped automatically.
20
+ # 3. Any python3 on PATH / known locations that can `import yaml`.
21
+ # 4. Bare `python3` as a last resort (return 1).
22
+
23
+ arka_resolve_python() {
24
+ local cand
25
+
26
+ if [ -n "${ARKAOS_PYTHON:-}" ] && [ -x "${ARKAOS_PYTHON}" ]; then
27
+ printf '%s\n' "${ARKAOS_PYTHON}"
28
+ return 0
29
+ fi
30
+
31
+ for cand in \
32
+ "$HOME/.arkaos/venv/bin/python" \
33
+ "$HOME/.arkaos/venv/bin/python3" \
34
+ "$HOME/.arkaos/.venv/bin/python" \
35
+ "$HOME/.arkaos/.venv/bin/python3"; do
36
+ if [ -x "$cand" ]; then
37
+ printf '%s\n' "$cand"
38
+ return 0
39
+ fi
40
+ done
41
+
42
+ for cand in python3 /opt/homebrew/bin/python3 /usr/local/bin/python3 /usr/bin/python3; do
43
+ if command -v "$cand" >/dev/null 2>&1 && "$cand" -c "import yaml" >/dev/null 2>&1; then
44
+ printf '%s\n' "$cand"
45
+ return 0
46
+ fi
47
+ done
48
+
49
+ printf '%s\n' "python3"
50
+ return 1
51
+ }
52
+
53
+ # Sourcing this file exports ARKA_PY once, so hooks can `exec "$ARKA_PY" ...`
54
+ # without each re-running the probe. Callers that need the return code call
55
+ # arka_resolve_python directly.
56
+ #
57
+ # `|| true` is REQUIRED: arka_resolve_python returns 1 on the last-resort
58
+ # fallback, and under `set -e` a failing command substitution in an
59
+ # assignment aborts the sourcing file mid-way (reproduced on bash 3.2.57).
60
+ # We still want ARKA_PY set to the fallback value, so swallow the status and
61
+ # keep the export unconditional.
62
+ ARKA_PY="$(arka_resolve_python)" || true
63
+ export ARKA_PY
@@ -7,6 +7,13 @@
7
7
 
8
8
  set -euo pipefail
9
9
 
10
+ # ─── Shared Python resolver (exports ARKA_PY) ──────────────────────────
11
+ # The resolver guards its own ARKA_PY assignment with `|| true`, so sourcing
12
+ # is set -e-safe even on the last-resort fallback. The trailing `|| true`
13
+ # here is belt-and-suspenders for a partially-updated lib.
14
+ _ARKA_LIB="$(dirname "${BASH_SOURCE[0]:-$0}")/_lib/arka_python.sh"
15
+ if [ -f "$_ARKA_LIB" ]; then . "$_ARKA_LIB" || true; else ARKA_PY="python3"; fi
16
+
10
17
  # Hook contract: stdin is a JSON payload with fields tool_name + tool_input.
11
18
  payload="$(cat)"
12
19
 
@@ -44,7 +51,7 @@ fi
44
51
  if [ -d "$core_root/departments" ]; then
45
52
  mkdir -p "$project_agents_dir"
46
53
  set +e
47
- python3 - "$core_root" "$subagent_type" "$target" <<'PY'
54
+ "$ARKA_PY" - "$core_root" "$subagent_type" "$target" <<'PY'
48
55
  import os, re, sys
49
56
  from pathlib import Path
50
57
 
@@ -8,6 +8,10 @@
8
8
  input=$(cat)
9
9
  NEW_CWD=$(echo "$input" | jq -r '.cwd // ""' 2>/dev/null)
10
10
 
11
+ # ─── Shared Python resolver (exports ARKA_PY) ──────────────────────────
12
+ _ARKA_LIB="$(dirname "${BASH_SOURCE[0]:-$0}")/_lib/arka_python.sh"
13
+ if [ -f "$_ARKA_LIB" ]; then . "$_ARKA_LIB"; else ARKA_PY="python3"; fi
14
+
11
15
  if [ -z "$NEW_CWD" ] || [ ! -d "$NEW_CWD" ]; then
12
16
  exit 0
13
17
  fi
@@ -23,8 +27,8 @@ fi
23
27
  ECOSYSTEM=""
24
28
  ECOSYSTEM_NAME=""
25
29
 
26
- if [ -f "$ECOSYSTEMS_FILE" ] && command -v python3 &>/dev/null; then
27
- eval "$(python3 -c "
30
+ if [ -f "$ECOSYSTEMS_FILE" ] && command -v "$ARKA_PY" >/dev/null 2>&1; then
31
+ eval "$("$ARKA_PY" -c "
28
32
  import json, os, sys
29
33
 
30
34
  cwd = '$NEW_CWD'
@@ -7,6 +7,10 @@
7
7
 
8
8
  input=$(cat)
9
9
 
10
+ # ─── Shared Python resolver (exports ARKA_PY) ──────────────────────────
11
+ _ARKA_LIB="$(dirname "${BASH_SOURCE[0]:-$0}")/_lib/arka_python.sh"
12
+ if [ -f "$_ARKA_LIB" ]; then . "$_ARKA_LIB"; else ARKA_PY="python3"; fi
13
+
10
14
  ARKAOS_HOME="${HOME}/.arkaos"
11
15
  GOTCHAS_FILE="$ARKAOS_HOME/gotchas.json"
12
16
 
@@ -74,8 +78,8 @@ project=$(basename "$cwd")
74
78
  echo "[]" > "$GOTCHAS_FILE"
75
79
  fi
76
80
 
77
- if command -v python3 &>/dev/null; then
78
- python3 -c "
81
+ if command -v "$ARKA_PY" >/dev/null 2>&1; then
82
+ "$ARKA_PY" -c "
79
83
  import json, sys
80
84
  try:
81
85
  with open('$GOTCHAS_FILE') as f:
@@ -31,7 +31,11 @@ if [ -z "${ARKAOS_ROOT:-}" ]; then
31
31
  fi
32
32
  export ARKAOS_ROOT
33
33
 
34
- if ! command -v python3 &>/dev/null; then
34
+ # ─── Shared Python resolver (exports ARKA_PY) ──────────────────────────
35
+ _ARKA_LIB="$(dirname "${BASH_SOURCE[0]:-$0}")/_lib/arka_python.sh"
36
+ if [ -f "$_ARKA_LIB" ]; then . "$_ARKA_LIB"; else ARKA_PY="python3"; fi
37
+
38
+ if ! command -v "$ARKA_PY" >/dev/null 2>&1; then
35
39
  echo '{}'
36
40
  exit 0
37
41
  fi
@@ -49,20 +53,7 @@ if [ ! -f "$ARKAOS_ROOT/core/hooks/post_tool_use.py" ]; then
49
53
  fi
50
54
  fi
51
55
 
52
- # Interpreter resolution: prefer the ArkaOS venv (has pyyaml/pydantic);
53
- # otherwise probe an unrelated project venv first on PATH would give a
54
- # python3 without yaml and silently degrade every gate.
55
- _PY=""
56
- for _cand in "$HOME/.arkaos/venv/bin/python3" "$HOME/.arkaos/.venv/bin/python3"; do
57
- if [ -x "$_cand" ]; then _PY="$_cand"; break; fi
58
- done
59
- if [ -z "$_PY" ]; then
60
- _PY="python3"
61
- if ! "$_PY" -c "import yaml" 2>/dev/null; then
62
- for _cand in /opt/homebrew/bin/python3 /usr/local/bin/python3 /usr/bin/python3; do
63
- if [ -x "$_cand" ] && "$_cand" -c "import yaml" 2>/dev/null; then _PY="$_cand"; break; fi
64
- done
65
- fi
66
- fi
56
+ # Interpreter resolution handled by the shared resolver (ARKA_PY): prefers
57
+ # the ArkaOS venv (has pyyaml/pydantic), falls back to a yaml-capable python3.
67
58
  PYTHONPATH="$ARKAOS_ROOT${PYTHONPATH:+:$PYTHONPATH}" \
68
- exec "$_PY" -m core.hooks.post_tool_use
59
+ exec "$ARKA_PY" -m core.hooks.post_tool_use
@@ -39,9 +39,11 @@ if ([string]::IsNullOrWhiteSpace($env:ARKAOS_ROOT)) {
39
39
  }
40
40
  }
41
41
 
42
- $python = Get-Command python3 -ErrorAction SilentlyContinue
43
- if (-not $python) { $python = Get-Command python -ErrorAction SilentlyContinue }
44
- if (-not $python) { exit 0 }
42
+ # Shared resolver (venv-first, yaml-verified). Mirrors _lib/arka_python.sh.
43
+ $arkaLib = Join-Path $PSScriptRoot "_lib\arka_python.ps1"
44
+ if (Test-Path -LiteralPath $arkaLib) { . $arkaLib }
45
+ $pythonExe = $env:ARKA_PY
46
+ if (-not $pythonExe) { exit 0 }
45
47
 
46
48
  # --- KB-first gate (Task #6, runs before flow-marker gate) ---
47
49
  $queryHint = ""
@@ -91,7 +93,7 @@ print(json.dumps({
91
93
  }))
92
94
  '@
93
95
 
94
- $kbDecisionJson = $kbScript | & $python.Source -
96
+ $kbDecisionJson = $kbScript | & $pythonExe -
95
97
  if (-not [string]::IsNullOrWhiteSpace($kbDecisionJson)) {
96
98
  try {
97
99
  $kbDecision = $kbDecisionJson | ConvertFrom-Json
@@ -172,7 +174,7 @@ print(json.dumps({
172
174
  }))
173
175
  '@
174
176
 
175
- $spDecisionJson = $spScript | & $python.Source -
177
+ $spDecisionJson = $spScript | & $pythonExe -
176
178
  if (-not [string]::IsNullOrWhiteSpace($spDecisionJson)) {
177
179
  try {
178
180
  $spDecision = $spDecisionJson | ConvertFrom-Json
@@ -241,7 +243,7 @@ print(json.dumps({
241
243
  }))
242
244
  '@
243
245
 
244
- $decisionJson = $pyScript | & $python.Source -
246
+ $decisionJson = $pyScript | & $pythonExe -
245
247
  if ([string]::IsNullOrWhiteSpace($decisionJson)) { exit 0 }
246
248
 
247
249
  try {
@@ -34,8 +34,12 @@ if [ -z "${ARKAOS_ROOT:-}" ]; then
34
34
  fi
35
35
  export ARKAOS_ROOT
36
36
 
37
+ # ─── Shared Python resolver (exports ARKA_PY) ──────────────────────────
38
+ _ARKA_LIB="$(dirname "${BASH_SOURCE[0]:-$0}")/_lib/arka_python.sh"
39
+ if [ -f "$_ARKA_LIB" ]; then . "$_ARKA_LIB"; else ARKA_PY="python3"; fi
40
+
37
41
  # ─── Degrade gracefully (fail open, same as before) ─────────────────────
38
- if ! command -v python3 &>/dev/null; then
42
+ if ! command -v "$ARKA_PY" >/dev/null 2>&1; then
39
43
  exit 0
40
44
  fi
41
45
  # Self-root fallback: the wrapper ships next to its python entrypoint, so
@@ -52,20 +56,7 @@ if [ ! -f "$ARKAOS_ROOT/core/hooks/pre_tool_use.py" ]; then
52
56
  fi
53
57
 
54
58
  # ─── Single python process; stdin/stdout/stderr/exit-code pass through ──
55
- # Interpreter resolution: prefer the ArkaOS venv (has pyyaml/pydantic);
56
- # otherwise probe an unrelated project venv first on PATH would give a
57
- # python3 without yaml and silently degrade every gate.
58
- _PY=""
59
- for _cand in "$HOME/.arkaos/venv/bin/python3" "$HOME/.arkaos/.venv/bin/python3"; do
60
- if [ -x "$_cand" ]; then _PY="$_cand"; break; fi
61
- done
62
- if [ -z "$_PY" ]; then
63
- _PY="python3"
64
- if ! "$_PY" -c "import yaml" 2>/dev/null; then
65
- for _cand in /opt/homebrew/bin/python3 /usr/local/bin/python3 /usr/bin/python3; do
66
- if [ -x "$_cand" ] && "$_cand" -c "import yaml" 2>/dev/null; then _PY="$_cand"; break; fi
67
- done
68
- fi
69
- fi
59
+ # Interpreter resolution handled by the shared resolver (ARKA_PY): prefers
60
+ # the ArkaOS venv (has pyyaml/pydantic), falls back to a yaml-capable python3.
70
61
  PYTHONPATH="$ARKAOS_ROOT${PYTHONPATH:+:$PYTHONPATH}" \
71
- exec "$_PY" -m core.hooks.pre_tool_use
62
+ exec "$ARKA_PY" -m core.hooks.pre_tool_use
@@ -9,9 +9,13 @@ NAME="founder"
9
9
  COMPANY="WizardingCode"
10
10
  VERSION="2.x"
11
11
 
12
- if [ -f "$HOME/.arkaos/profile.json" ] && command -v python3 &>/dev/null; then
13
- NAME=$(python3 -c "import json; p=json.load(open('$HOME/.arkaos/profile.json')); print(p.get('name', p.get('role', 'founder')))" 2>/dev/null || echo "founder")
14
- COMPANY=$(python3 -c "import json; print(json.load(open('$HOME/.arkaos/profile.json')).get('company', 'WizardingCode'))" 2>/dev/null || echo "WizardingCode")
12
+ # ─── Shared Python resolver (exports ARKA_PY) ──────────────────────────
13
+ _ARKA_LIB="$(dirname "${BASH_SOURCE[0]:-$0}")/_lib/arka_python.sh"
14
+ if [ -f "$_ARKA_LIB" ]; then . "$_ARKA_LIB"; else ARKA_PY="python3"; fi
15
+
16
+ if [ -f "$HOME/.arkaos/profile.json" ] && command -v "$ARKA_PY" >/dev/null 2>&1; then
17
+ NAME=$("$ARKA_PY" -c "import json; p=json.load(open('$HOME/.arkaos/profile.json')); print(p.get('name', p.get('role', 'founder')))" 2>/dev/null || echo "founder")
18
+ COMPANY=$("$ARKA_PY" -c "import json; print(json.load(open('$HOME/.arkaos/profile.json')).get('company', 'WizardingCode'))" 2>/dev/null || echo "WizardingCode")
15
19
  fi
16
20
 
17
21
  if [ -f "$HOME/.arkaos/.repo-path" ]; then
@@ -29,7 +33,7 @@ SYNC_STATE="$HOME/.arkaos/sync-state.json"
29
33
  DRIFT=""
30
34
 
31
35
  if [ -f "$SYNC_STATE" ]; then
32
- SYNCED=$(python3 -c "import json; print(json.load(open('$SYNC_STATE'))['version'])" 2>/dev/null || echo "none")
36
+ SYNCED=$("$ARKA_PY" -c "import json; print(json.load(open('$SYNC_STATE'))['version'])" 2>/dev/null || echo "none")
33
37
  if [ "$SYNCED" != "$VERSION" ]; then
34
38
  DRIFT="\\n[arka:update-available] Core v${VERSION} != synced v${SYNCED}. Run /arka update."
35
39
  fi
@@ -71,11 +75,11 @@ _FORGE_LINE=""
71
75
  if [ -f "$_FORGE_ACTIVE" ]; then
72
76
  _FORGE_ID=$(cat "$_FORGE_ACTIVE" 2>/dev/null)
73
77
  _FORGE_FILE="$_FORGE_PLANS/${_FORGE_ID}.yaml"
74
- if [ -f "$_FORGE_FILE" ] && command -v python3 &>/dev/null; then
75
- _FORGE_NAME=$(FORGE_FILE="$_FORGE_FILE" python3 -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(d.get('name',''))" 2>/dev/null)
76
- _FORGE_STATUS=$(FORGE_FILE="$_FORGE_FILE" python3 -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(d.get('status',''))" 2>/dev/null)
77
- _FORGE_PHASES=$(FORGE_FILE="$_FORGE_FILE" python3 -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(len(d.get('plan_phases',[])))" 2>/dev/null)
78
- _FORGE_BRANCH=$(FORGE_FILE="$_FORGE_FILE" python3 -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(d.get('governance',{}).get('branch_strategy',''))" 2>/dev/null)
78
+ if [ -f "$_FORGE_FILE" ] && command -v "$ARKA_PY" >/dev/null 2>&1; then
79
+ _FORGE_NAME=$(FORGE_FILE="$_FORGE_FILE" "$ARKA_PY" -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(d.get('name',''))" 2>/dev/null)
80
+ _FORGE_STATUS=$(FORGE_FILE="$_FORGE_FILE" "$ARKA_PY" -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(d.get('status',''))" 2>/dev/null)
81
+ _FORGE_PHASES=$(FORGE_FILE="$_FORGE_FILE" "$ARKA_PY" -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(len(d.get('plan_phases',[])))" 2>/dev/null)
82
+ _FORGE_BRANCH=$(FORGE_FILE="$_FORGE_FILE" "$ARKA_PY" -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(d.get('governance',{}).get('branch_strategy',''))" 2>/dev/null)
79
83
 
80
84
  if [ "$_FORGE_STATUS" = "approved" ]; then
81
85
  _FORGE_LINE=" ⚒ Forge plan pending: ${_FORGE_NAME} | Phases: ${_FORGE_PHASES} | /forge resume"
@@ -110,12 +114,10 @@ MSG+="\\nAbsence is measured by the Stop hook (warn-only in v2.34.0) before prom
110
114
  # dashboard/CLI model choices actually govern agent dispatch. Cheap
111
115
  # (single python read); skipped silently if the config can't be resolved.
112
116
  if [ -n "$REPO" ]; then
113
- # Prefer the ArkaOS venv python (has pydantic + yaml); the ambient
114
- # python3 usually lacks them and routing_directive() would return "".
115
- _MF_PY="python3"
116
- [ -x "$HOME/.arkaos/venv/bin/python3" ] && _MF_PY="$HOME/.arkaos/venv/bin/python3"
117
- if command -v "$_MF_PY" &>/dev/null || [ -x "$_MF_PY" ]; then
118
- _MF_DIRECTIVE=$(cd "$REPO" && PYTHONPATH="$REPO" "$_MF_PY" -c "
117
+ # ARKA_PY already resolves to the venv python (has pydantic + yaml); the
118
+ # ambient python3 usually lacks them and routing_directive() would return "".
119
+ if command -v "$ARKA_PY" >/dev/null 2>&1 || [ -x "$ARKA_PY" ]; then
120
+ _MF_DIRECTIVE=$(cd "$REPO" && PYTHONPATH="$REPO" "$ARKA_PY" -c "
119
121
  try:
120
122
  from core.runtime.model_routing_context import routing_directive
121
123
  print(routing_directive())
@@ -132,12 +134,12 @@ fi
132
134
  # If today's proposal file is missing, fire the reorganizer in the
133
135
  # background with a 30s timeout. Best-effort, never blocks session
134
136
  # start. Multiple sessions per day no-op because the file now exists.
135
- if [ -n "$REPO" ] && command -v python3 &>/dev/null; then
137
+ if [ -n "$REPO" ] && command -v "$ARKA_PY" >/dev/null 2>&1; then
136
138
  _PROPOSAL_DIR="$HOME/.arkaos/reorganize-proposals"
137
139
  _TODAY="$(date -u +%Y-%m-%d).md"
138
140
  if [ ! -f "$_PROPOSAL_DIR/$_TODAY" ]; then
139
141
  (
140
- cd "$REPO" && timeout 30s python3 -m core.cognition.reorganizer_cli >/dev/null 2>&1
142
+ cd "$REPO" && timeout 30s "$ARKA_PY" -m core.cognition.reorganizer_cli >/dev/null 2>&1
141
143
  ) &
142
144
  disown 2>/dev/null || true
143
145
  fi
@@ -149,7 +151,7 @@ fi
149
151
  # starts API+UI. Background + disown so it never touches the 5s budget.
150
152
  # Toggle off: ~/.arkaos/config.json -> {"dashboard": {"ensure_on_session": false}}
151
153
  if [ -n "$REPO" ] && [ -f "$REPO/scripts/start-dashboard.sh" ]; then
152
- _DASH_ENSURE=$(python3 -c "import json; print(json.load(open('$HOME/.arkaos/config.json')).get('dashboard',{}).get('ensure_on_session', True))" 2>/dev/null || echo "True")
154
+ _DASH_ENSURE=$("$ARKA_PY" -c "import json; print(json.load(open('$HOME/.arkaos/config.json')).get('dashboard',{}).get('ensure_on_session', True))" 2>/dev/null || echo "True")
153
155
  if [ "$_DASH_ENSURE" = "True" ] || [ "$_DASH_ENSURE" = "true" ]; then
154
156
  mkdir -p "$HOME/.arkaos/logs"
155
157
  (
@@ -166,8 +168,8 @@ if [ -n "$REPO" ] && [ -f "$REPO/scripts/start-dashboard.sh" ]; then
166
168
  fi
167
169
 
168
170
  # --- Session Memory Resume Context ---
169
- if command -v python3 &>/dev/null && [ -n "$REPO" ]; then
170
- _SESSION_CTX=$(cd "$REPO" && python3 -c "
171
+ if command -v "$ARKA_PY" >/dev/null 2>&1 && [ -n "$REPO" ]; then
172
+ _SESSION_CTX=$(cd "$REPO" && "$ARKA_PY" -c "
171
173
  import sys
172
174
  sys.path.insert(0, '$REPO')
173
175
  try:
@@ -182,7 +184,7 @@ except Exception:
182
184
  fi
183
185
 
184
186
  # ─── Output as systemMessage (same protocol as claude-mem) ─────────────
185
- python3 -c "
187
+ "$ARKA_PY" -c "
186
188
  import json
187
189
  msg = '''$(echo -e "$MSG")'''
188
190
  print(json.dumps({'systemMessage': msg}))
@@ -43,9 +43,11 @@ if ([string]::IsNullOrWhiteSpace($env:ARKAOS_ROOT)) {
43
43
  $enforcerPy = Join-Path $env:ARKAOS_ROOT "core/workflow/flow_enforcer.py"
44
44
  if (-not (Test-Path $enforcerPy)) { exit 0 }
45
45
 
46
- $python = Get-Command python3 -ErrorAction SilentlyContinue
47
- if (-not $python) { $python = Get-Command python -ErrorAction SilentlyContinue }
48
- if (-not $python) { exit 0 }
46
+ # Shared resolver (venv-first, yaml-verified). Mirrors _lib/arka_python.sh.
47
+ $arkaLib = Join-Path $PSScriptRoot "_lib\arka_python.ps1"
48
+ if (Test-Path -LiteralPath $arkaLib) { . $arkaLib }
49
+ $pythonExe = $env:ARKA_PY
50
+ if (-not $pythonExe) { exit 0 }
49
51
 
50
52
  $env:SESSION_ID_VAL = $sessionId
51
53
  $env:TRANSCRIPT_PATH_VAL = $transcriptPath
@@ -102,7 +104,7 @@ except Exception:
102
104
  pass
103
105
  '@
104
106
 
105
- $pyScript | & $python.Source - | Out-Null
107
+ $pyScript | & $pythonExe - | Out-Null
106
108
 
107
109
  # ─── Auto-documentor enqueue (fire-and-forget) ─────────────────────────
108
110
  # Queues a background job when classifier flagged flow, QG approved, and
@@ -178,7 +180,7 @@ except Exception:
178
180
  '@
179
181
 
180
182
  try {
181
- $autoDocScript | & $python.Source - | Out-Null
183
+ $autoDocScript | & $pythonExe - | Out-Null
182
184
  } catch {
183
185
  # Swallow — enqueue is fire-and-forget.
184
186
  }
@@ -33,7 +33,11 @@ if [ -z "${ARKAOS_ROOT:-}" ]; then
33
33
  fi
34
34
  export ARKAOS_ROOT
35
35
 
36
- if ! command -v python3 &>/dev/null; then
36
+ # ─── Shared Python resolver (exports ARKA_PY) ──────────────────────────
37
+ _ARKA_LIB="$(dirname "${BASH_SOURCE[0]:-$0}")/_lib/arka_python.sh"
38
+ if [ -f "$_ARKA_LIB" ]; then . "$_ARKA_LIB"; else ARKA_PY="python3"; fi
39
+
40
+ if ! command -v "$ARKA_PY" >/dev/null 2>&1; then
37
41
  exit 0
38
42
  fi
39
43
  # Self-root fallback: the wrapper ships next to its python entrypoint, so
@@ -49,21 +53,8 @@ if [ ! -f "$ARKAOS_ROOT/core/hooks/stop.py" ]; then
49
53
  fi
50
54
  fi
51
55
 
52
- # Interpreter resolution: prefer the ArkaOS venv (has pyyaml/pydantic);
53
- # otherwise probe an unrelated project venv first on PATH would give a
54
- # python3 without yaml and silently degrade every gate.
55
- _PY=""
56
- for _cand in "$HOME/.arkaos/venv/bin/python3" "$HOME/.arkaos/.venv/bin/python3"; do
57
- if [ -x "$_cand" ]; then _PY="$_cand"; break; fi
58
- done
59
- if [ -z "$_PY" ]; then
60
- _PY="python3"
61
- if ! "$_PY" -c "import yaml" 2>/dev/null; then
62
- for _cand in /opt/homebrew/bin/python3 /usr/local/bin/python3 /usr/bin/python3; do
63
- if [ -x "$_cand" ] && "$_cand" -c "import yaml" 2>/dev/null; then _PY="$_cand"; break; fi
64
- done
65
- fi
66
- fi
56
+ # Interpreter resolution handled by the shared resolver (ARKA_PY): prefers
57
+ # the ArkaOS venv (has pyyaml/pydantic), falls back to a yaml-capable python3.
67
58
  PYTHONPATH="$ARKAOS_ROOT${PYTHONPATH:+:$PYTHONPATH}" \
68
- "$_PY" -m core.hooks.stop
59
+ "$ARKA_PY" -m core.hooks.stop
69
60
  exit 0
@@ -13,6 +13,10 @@
13
13
 
14
14
  set +e # never fail
15
15
 
16
+ # ─── Shared Python resolver (exports ARKA_PY) ──────────────────────────
17
+ _ARKA_LIB="$(dirname "${BASH_SOURCE[0]:-$0}")/_lib/arka_python.sh"
18
+ if [ -f "$_ARKA_LIB" ]; then . "$_ARKA_LIB"; else ARKA_PY="python3"; fi
19
+
16
20
  _prompt="${ARKA_PROMPT:-}"
17
21
  _transcript="${ARKA_TRANSCRIPT_PATH:-}"
18
22
  _suggestions=""
@@ -64,7 +68,7 @@ if [ -n "$_transcript" ] && [ -f "$_transcript" ] && [ -n "$_prompt" ]; then
64
68
  _cur_kw=$(_kw "$_prompt" | head -20)
65
69
  # pull last 3 user messages from transcript jsonl
66
70
  _prior=$(tail -n 200 "$_transcript" 2>/dev/null \
67
- | python3 -c "
71
+ | "$ARKA_PY" -c "
68
72
  import sys, json
69
73
  msgs=[]
70
74
  for line in sys.stdin:
@@ -7,6 +7,10 @@
7
7
 
8
8
  input=$(cat)
9
9
 
10
+ # ─── Shared Python resolver (exports ARKA_PY) ──────────────────────────
11
+ _ARKA_LIB="$(dirname "${BASH_SOURCE[0]:-$0}")/_lib/arka_python.sh"
12
+ if [ -f "$_ARKA_LIB" ]; then . "$_ARKA_LIB"; else ARKA_PY="python3"; fi
13
+
10
14
  # ─── V1 Migration Detection ─────────────────────────────────────────────
11
15
  V1_PATHS=("$HOME/.claude/skills/arka-os" "$HOME/.claude/skills/arkaos")
12
16
  MIGRATION_MARKER="$HOME/.arkaos/migrated-from-v1"
@@ -27,12 +31,12 @@ if [ -f "$ARKAOS_VERSION_FILE" ]; then
27
31
  if [ -f "$_REPO_PATH/VERSION" ]; then
28
32
  _CURRENT_VERSION=$(cat "$_REPO_PATH/VERSION")
29
33
  elif [ -f "$_REPO_PATH/package.json" ]; then
30
- _CURRENT_VERSION=$(python3 -c "import json; print(json.load(open('$_REPO_PATH/package.json'))['version'])" 2>/dev/null || echo "")
34
+ _CURRENT_VERSION=$("$ARKA_PY" -c "import json; print(json.load(open('$_REPO_PATH/package.json'))['version'])" 2>/dev/null || echo "")
31
35
  fi
32
36
 
33
37
  if [ -n "${_CURRENT_VERSION:-}" ]; then
34
38
  if [ -f "$SYNC_STATE" ]; then
35
- _SYNCED_VERSION=$(python3 -c "import json; print(json.load(open('$SYNC_STATE'))['version'])" 2>/dev/null || echo "none")
39
+ _SYNCED_VERSION=$("$ARKA_PY" -c "import json; print(json.load(open('$SYNC_STATE'))['version'])" 2>/dev/null || echo "none")
36
40
  else
37
41
  _SYNCED_VERSION="none"
38
42
  fi
@@ -53,9 +57,9 @@ if [ ! -f "$_SESSION_MARKER" ] && [ ! -f "/tmp/arkaos-greeted-today" ]; then
53
57
  _GREETING_COMPANY=""
54
58
  _GREETING_VERSION=""
55
59
 
56
- if [ -f "$HOME/.arkaos/profile.json" ] && command -v python3 &>/dev/null; then
57
- _GREETING_NAME=$(python3 -c "import json; p=json.load(open('$HOME/.arkaos/profile.json')); print(p.get('name', p.get('role', 'founder')))" 2>/dev/null)
58
- _GREETING_COMPANY=$(python3 -c "import json; print(json.load(open('$HOME/.arkaos/profile.json')).get('company', ''))" 2>/dev/null)
60
+ if [ -f "$HOME/.arkaos/profile.json" ] && command -v "$ARKA_PY" >/dev/null 2>&1; then
61
+ _GREETING_NAME=$("$ARKA_PY" -c "import json; p=json.load(open('$HOME/.arkaos/profile.json')); print(p.get('name', p.get('role', 'founder')))" 2>/dev/null)
62
+ _GREETING_COMPANY=$("$ARKA_PY" -c "import json; print(json.load(open('$HOME/.arkaos/profile.json')).get('company', ''))" 2>/dev/null)
59
63
  fi
60
64
 
61
65
  if [ -f "$HOME/.arkaos/.repo-path" ]; then
@@ -111,12 +115,12 @@ fi
111
115
  python_result=""
112
116
  BRIDGE_SCRIPT="${ARKAOS_ROOT}/scripts/synapse-bridge.py"
113
117
 
114
- if command -v python3 &>/dev/null && [ -f "$BRIDGE_SCRIPT" ]; then
115
- bridge_output=$(echo "{\"user_input\":$(echo "$user_input" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))" 2>/dev/null || echo '""')}" \
116
- | ARKAOS_ROOT="$ARKAOS_ROOT" python3 "$BRIDGE_SCRIPT" --root "$ARKAOS_ROOT" 2>/dev/null)
118
+ if command -v "$ARKA_PY" >/dev/null 2>&1 && [ -f "$BRIDGE_SCRIPT" ]; then
119
+ bridge_output=$(echo "{\"user_input\":$(echo "$user_input" | "$ARKA_PY" -c "import sys,json; print(json.dumps(sys.stdin.read()))" 2>/dev/null || echo '""')}" \
120
+ | ARKAOS_ROOT="$ARKAOS_ROOT" "$ARKA_PY" "$BRIDGE_SCRIPT" --root "$ARKAOS_ROOT" 2>/dev/null)
117
121
 
118
122
  if [ -n "$bridge_output" ]; then
119
- python_result=$(echo "$bridge_output" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('context_string',''))" 2>/dev/null)
123
+ python_result=$(echo "$bridge_output" | "$ARKA_PY" -c "import sys,json; print(json.loads(sys.stdin.read()).get('context_string',''))" 2>/dev/null)
120
124
  fi
121
125
  fi
122
126
 
@@ -34,9 +34,13 @@ if [ -z "${ARKAOS_ROOT:-}" ]; then
34
34
  fi
35
35
  export ARKAOS_ROOT
36
36
 
37
+ # ─── Shared Python resolver (exports ARKA_PY) ──────────────────────────
38
+ _ARKA_LIB="$(dirname "${BASH_SOURCE[0]:-$0}")/_lib/arka_python.sh"
39
+ if [ -f "$_ARKA_LIB" ]; then . "$_ARKA_LIB"; else ARKA_PY="python3"; fi
40
+
37
41
  _ARKA_L0_FALLBACK='{"additionalContext": "[Constitution] NON-NEGOTIABLE: branch-isolation, obsidian-output, authority-boundaries, security-gate, context-first, solid-clean-code, spec-driven, human-writing, squad-routing, full-visibility, sequential-validation, mandatory-qa, arka-supremacy | QUALITY-GATE: marta-cqo, eduardo-copy, francisca-tech-ux | MUST: conventional-commits, test-coverage, pattern-matching, actionable-output, memory-persistence"}'
38
42
 
39
- if ! command -v python3 &>/dev/null; then
43
+ if ! command -v "$ARKA_PY" >/dev/null 2>&1; then
40
44
  echo "$_ARKA_L0_FALLBACK"
41
45
  exit 0
42
46
  fi
@@ -54,20 +58,7 @@ if [ ! -f "$ARKAOS_ROOT/core/hooks/user_prompt_submit.py" ]; then
54
58
  fi
55
59
  fi
56
60
 
57
- # Interpreter resolution: prefer the ArkaOS venv (has pyyaml/pydantic);
58
- # otherwise probe an unrelated project venv first on PATH would give a
59
- # python3 without yaml and silently degrade every gate.
60
- _PY=""
61
- for _cand in "$HOME/.arkaos/venv/bin/python3" "$HOME/.arkaos/.venv/bin/python3"; do
62
- if [ -x "$_cand" ]; then _PY="$_cand"; break; fi
63
- done
64
- if [ -z "$_PY" ]; then
65
- _PY="python3"
66
- if ! "$_PY" -c "import yaml" 2>/dev/null; then
67
- for _cand in /opt/homebrew/bin/python3 /usr/local/bin/python3 /usr/bin/python3; do
68
- if [ -x "$_cand" ] && "$_cand" -c "import yaml" 2>/dev/null; then _PY="$_cand"; break; fi
69
- done
70
- fi
71
- fi
61
+ # Interpreter resolution handled by the shared resolver (ARKA_PY): prefers
62
+ # the ArkaOS venv (has pyyaml/pydantic), falls back to a yaml-capable python3.
72
63
  PYTHONPATH="$ARKAOS_ROOT${PYTHONPATH:+:$PYTHONPATH}" \
73
- exec "$_PY" -m core.hooks.user_prompt_submit
64
+ exec "$ARKA_PY" -m core.hooks.user_prompt_submit
@@ -5,6 +5,13 @@
5
5
  # Dependencies: jq (required)
6
6
  # ============================================================================
7
7
 
8
+ # ─── Shared Python resolver (exports ARKA_PY) ──────────────────────────
9
+ # The lib is not a sibling here; try the in-repo path then the installed tree.
10
+ for _l in "$(dirname "${BASH_SOURCE[0]:-$0}")/../../config/hooks/_lib/arka_python.sh" "$HOME/.arkaos/config/hooks/_lib/arka_python.sh"; do
11
+ if [ -f "$_l" ]; then . "$_l"; break; fi
12
+ done
13
+ : "${ARKA_PY:=python3}"
14
+
8
15
  STATE_FILE="$HOME/.arkaos/workflow-state.json"
9
16
 
10
17
  if [ ! -f "$STATE_FILE" ]; then
@@ -73,11 +80,11 @@ case "$CMD" in
73
80
  echo "none"
74
81
  exit 0
75
82
  fi
76
- if command -v python3 &>/dev/null; then
77
- _F_NAME=$(FORGE_FILE="$_FORGE_FILE" python3 -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(d.get('name',''))" 2>/dev/null)
78
- _F_STATUS=$(FORGE_FILE="$_FORGE_FILE" python3 -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(d.get('status',''))" 2>/dev/null)
79
- _F_PHASES=$(FORGE_FILE="$_FORGE_FILE" python3 -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(len(d.get('plan_phases',[])))" 2>/dev/null)
80
- _F_BRANCH=$(FORGE_FILE="$_FORGE_FILE" python3 -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(d.get('governance',{}).get('branch_strategy',''))" 2>/dev/null)
83
+ if command -v "$ARKA_PY" >/dev/null 2>&1; then
84
+ _F_NAME=$(FORGE_FILE="$_FORGE_FILE" "$ARKA_PY" -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(d.get('name',''))" 2>/dev/null)
85
+ _F_STATUS=$(FORGE_FILE="$_FORGE_FILE" "$ARKA_PY" -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(d.get('status',''))" 2>/dev/null)
86
+ _F_PHASES=$(FORGE_FILE="$_FORGE_FILE" "$ARKA_PY" -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(len(d.get('plan_phases',[])))" 2>/dev/null)
87
+ _F_BRANCH=$(FORGE_FILE="$_FORGE_FILE" "$ARKA_PY" -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(d.get('governance',{}).get('branch_strategy',''))" 2>/dev/null)
81
88
  echo "${_FORGE_ID}|${_F_NAME}|${_F_STATUS}|${_F_PHASES}|${_F_BRANCH}"
82
89
  else
83
90
  echo "${_FORGE_ID}|||0|"
@@ -23,7 +23,7 @@ Hybrid sync engine: Python handles deterministic operations (MCPs, settings, des
23
23
 
24
24
  1. **One-stop: npm refresh + engine (PR61 v2.78.0 orchestrator).**
25
25
  ```bash
26
- cd $ARKAOS_ROOT && python -m core.sync.update_orchestrator --home ~/.arkaos --skills ~/.claude/skills --output json
26
+ cd $ARKAOS_ROOT && ~/.arkaos/bin/arka-py -m core.sync.update_orchestrator --home ~/.arkaos --skills ~/.claude/skills --output json
27
27
  ```
28
28
  The orchestrator detects whether the running ArkaOS is behind npm
29
29
  latest. When stale, it shells out to `npx arkaos@latest update`
@@ -38,7 +38,7 @@ Hybrid sync engine: Python handles deterministic operations (MCPs, settings, des
38
38
  silently skips the npm step and falls through to the engine.
39
39
 
40
40
  Fallback (no orchestrator): the underlying engine still runs the
41
- same way via `python -m core.sync.engine ...` for callers that
41
+ same way via `~/.arkaos/bin/arka-py -m core.sync.engine ...` for callers that
42
42
  don't need the version-drift gate.
43
43
 
44
44
  2. **Phase 4 (intelligent, AI subagent):** After the engine completes, dispatch ONE subagent with the engine's JSON report + the feature registry (`core/sync/features/*.yaml`). The subagent injects/removes feature sections in each `~/.claude/skills/arka-{ecosystem}/SKILL.md` while preserving all custom content.
@@ -7,7 +7,7 @@ Referenced from SKILL.md. Read only when needed.
7
7
  Run the sync engine from the ArkaOS repo root:
8
8
 
9
9
  ```bash
10
- cd $ARKAOS_ROOT && python -m core.sync.engine --home ~/.arkaos --skills ~/.claude/skills --output json
10
+ cd $ARKAOS_ROOT && ~/.arkaos/bin/arka-py -m core.sync.engine --home ~/.arkaos --skills ~/.claude/skills --output json
11
11
  ```
12
12
 
13
13
  The engine handles deterministic operations and returns a JSON report.
@@ -24,7 +24,7 @@ Any Department Workflow:
24
24
  ...
25
25
  Phase N-1: QUALITY GATE
26
26
  1. Run the evidence engine over the project/diff:
27
- python -m core.governance.evidence_checks <project_dir> \
27
+ ~/.arkaos/bin/arka-py -m core.governance.evidence_checks <project_dir> \
28
28
  [--changed-files f1,f2] [--test-command '...'] --json
29
29
  2. Marta dispatches Eduardo + Francisca to INTERPRET the report:
30
30
  - Eduardo: spellcheck section + prose review of changed copy
@@ -165,6 +165,12 @@ export async function install({ runtime, path, force, skipSystem, withOllama })
165
165
  copyFileSync(cmdSrc, join(binDir, "arka-claude.cmd"));
166
166
  installed = true;
167
167
  }
168
+ // arka-py — the ArkaOS Python entrypoint SKILL.md commands invoke.
169
+ const pyPsSrc = join(ARKAOS_ROOT, "bin", "arka-py.ps1");
170
+ const pyCmdSrc = join(ARKAOS_ROOT, "bin", "arka-py.cmd");
171
+ if (existsSync(pyPsSrc)) copyFileSync(pyPsSrc, join(binDir, "arka-py.ps1"));
172
+ if (existsSync(pyCmdSrc)) copyFileSync(pyCmdSrc, join(binDir, "arka-py.cmd"));
173
+ if (existsSync(pyPsSrc) || existsSync(pyCmdSrc)) ok("arka-py interpreter shim installed (.cmd + .ps1)");
168
174
  if (installed) {
169
175
  ok("arka-claude wrapper installed (.cmd + .ps1)");
170
176
  console.log(` Add to PATH: setx PATH "%PATH%;%USERPROFILE%\\.arkaos\\bin"`);
@@ -179,6 +185,14 @@ export async function install({ runtime, path, force, skipSystem, withOllama })
179
185
  console.log(` Add to PATH: export PATH="$HOME/.arkaos/bin:$PATH"`);
180
186
  console.log(` Optional alias: alias claude="arka-claude"`);
181
187
  }
188
+ // arka-py — the ArkaOS Python entrypoint SKILL.md commands invoke so the
189
+ // agent never runs a bare `python` that lacks ArkaOS deps (pyyaml, ...).
190
+ const arkaPySrc = join(ARKAOS_ROOT, "bin", "arka-py");
191
+ if (existsSync(arkaPySrc)) {
192
+ copyFileSync(arkaPySrc, join(binDir, "arka-py"));
193
+ try { chmodSync(join(binDir, "arka-py"), 0o755); } catch {}
194
+ ok("arka-py interpreter shim installed");
195
+ }
182
196
  }
183
197
  const claudeMdSrc = join(ARKAOS_ROOT, "config", "user-claude.md");
184
198
  const userClaudeMd = join(homedir(), ".claude", "CLAUDE.md");
@@ -248,6 +248,13 @@ export async function update() {
248
248
  if (existsSync(psSrc) || existsSync(cmdSrc)) {
249
249
  console.log(" ✓ arka-claude wrapper updated (.cmd + .ps1)");
250
250
  }
251
+ const pyPsSrc = join(ARKAOS_ROOT, "bin", "arka-py.ps1");
252
+ const pyCmdSrc = join(ARKAOS_ROOT, "bin", "arka-py.cmd");
253
+ if (existsSync(pyPsSrc)) copyFileSync(pyPsSrc, join(binDir, "arka-py.ps1"));
254
+ if (existsSync(pyCmdSrc)) copyFileSync(pyCmdSrc, join(binDir, "arka-py.cmd"));
255
+ if (existsSync(pyPsSrc) || existsSync(pyCmdSrc)) {
256
+ console.log(" ✓ arka-py interpreter shim updated (.cmd + .ps1)");
257
+ }
251
258
  } else {
252
259
  const wrapperSrc = join(ARKAOS_ROOT, "bin", "arka-claude");
253
260
  if (existsSync(wrapperSrc)) {
@@ -255,6 +262,12 @@ export async function update() {
255
262
  try { chmodSync(join(binDir, "arka-claude"), 0o755); } catch {}
256
263
  console.log(" ✓ arka-claude wrapper updated");
257
264
  }
265
+ const arkaPySrc = join(ARKAOS_ROOT, "bin", "arka-py");
266
+ if (existsSync(arkaPySrc)) {
267
+ copyFileSync(arkaPySrc, join(binDir, "arka-py"));
268
+ try { chmodSync(join(binDir, "arka-py"), 0o755); } catch {}
269
+ console.log(" ✓ arka-py interpreter shim updated");
270
+ }
258
271
  }
259
272
  const userClaudeMd = join(homedir(), ".claude", "CLAUDE.md");
260
273
  const claudeMdSrc = join(ARKAOS_ROOT, "config", "user-claude.md");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkaos",
3
- "version": "4.3.1",
3
+ "version": "4.3.2",
4
4
  "description": "The Operating System for AI Agent Teams",
5
5
  "type": "module",
6
6
  "bin": {
@@ -58,5 +58,8 @@
58
58
  "CONSTITUTION.md",
59
59
  "README.md",
60
60
  "LICENSE"
61
- ]
61
+ ],
62
+ "devDependencies": {
63
+ "eslint": "^8.57.1"
64
+ }
62
65
  }
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "arkaos-core"
3
- version = "4.3.1"
3
+ version = "4.3.2"
4
4
  description = "Core engine for ArkaOS — The Operating System for AI Agent Teams"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
@@ -175,7 +175,7 @@ def main() -> int:
175
175
  for card in _INITIAL_PATTERNS:
176
176
  record_pattern(card)
177
177
  print(f"Seeded {len(_INITIAL_PATTERNS)} pattern cards.")
178
- print("Inspect: python -m core.knowledge.pattern_cards_cli list")
178
+ print("Inspect: ~/.arkaos/bin/arka-py -m core.knowledge.pattern_cards_cli list")
179
179
  return 0
180
180
 
181
181