arkaos 4.13.2 → 4.14.1

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 (151) hide show
  1. package/VERSION +1 -1
  2. package/arka/SKILL.md +16 -0
  3. package/bin/arka-doctor +20 -1
  4. package/config/cognition/schedules.yaml +16 -0
  5. package/config/hooks/_lib/fastpath/engine.cjs +337 -0
  6. package/config/hooks/gate-manifest.json +741 -0
  7. package/config/hooks/post-tool-use.cjs +113 -0
  8. package/config/hooks/pre-tool-use.cjs +109 -0
  9. package/config/hooks/session-end.ps1 +40 -0
  10. package/config/hooks/session-end.sh +55 -0
  11. package/config/hooks/session-start.sh +25 -189
  12. package/config/hooks/subagent-stop.ps1 +40 -0
  13. package/config/hooks/subagent-stop.sh +56 -0
  14. package/config/mcp-policy.yaml +8 -0
  15. package/config/skills-curated.yaml +39 -0
  16. package/config/statusline.sh +57 -1
  17. package/core/cognition/capture/__pycache__/store.cpython-313.pyc +0 -0
  18. package/core/cognition/capture/store.py +7 -2
  19. package/core/evals/__pycache__/__init__.cpython-314.pyc +0 -0
  20. package/core/evals/__pycache__/sanitizer.cpython-314.pyc +0 -0
  21. package/core/evals/__pycache__/schema.cpython-314.pyc +0 -0
  22. package/core/evals/__pycache__/verdict_labels.cpython-314.pyc +0 -0
  23. package/core/forge/__pycache__/complexity.cpython-314.pyc +0 -0
  24. package/core/governance/__pycache__/evidence_checks.cpython-313.pyc +0 -0
  25. package/core/governance/__pycache__/judge.cpython-314.pyc +0 -0
  26. package/core/governance/__pycache__/leak_scanner.cpython-314.pyc +0 -0
  27. package/core/governance/__pycache__/qg_verdict.cpython-314.pyc +0 -0
  28. package/core/governance/__pycache__/routing_feedback.cpython-313.pyc +0 -0
  29. package/core/governance/__pycache__/routing_feedback.cpython-314.pyc +0 -0
  30. package/core/governance/__pycache__/routing_feedback_cli.cpython-313.pyc +0 -0
  31. package/core/governance/evidence_checks.py +42 -7
  32. package/core/governance/routing_feedback.py +229 -0
  33. package/core/governance/routing_feedback_cli.py +49 -0
  34. package/core/hooks/__pycache__/_shared.cpython-312.pyc +0 -0
  35. package/core/hooks/__pycache__/gate_manifest.cpython-313.pyc +0 -0
  36. package/core/hooks/__pycache__/post_tool_use.cpython-312.pyc +0 -0
  37. package/core/hooks/__pycache__/post_tool_use.cpython-313.pyc +0 -0
  38. package/core/hooks/__pycache__/pre_tool_use.cpython-312.pyc +0 -0
  39. package/core/hooks/__pycache__/session_end.cpython-313.pyc +0 -0
  40. package/core/hooks/__pycache__/session_start.cpython-312.pyc +0 -0
  41. package/core/hooks/__pycache__/session_start.cpython-313.pyc +0 -0
  42. package/core/hooks/__pycache__/stop.cpython-313.pyc +0 -0
  43. package/core/hooks/__pycache__/stop.cpython-314.pyc +0 -0
  44. package/core/hooks/__pycache__/subagent_stop.cpython-313.pyc +0 -0
  45. package/core/hooks/__pycache__/subagent_stop.cpython-314.pyc +0 -0
  46. package/core/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
  47. package/core/hooks/__pycache__/user_prompt_submit.cpython-314.pyc +0 -0
  48. package/core/hooks/gate_manifest.py +366 -0
  49. package/core/hooks/post_tool_use.py +5 -2
  50. package/core/hooks/session_end.py +127 -0
  51. package/core/hooks/session_start.py +322 -0
  52. package/core/hooks/stop.py +69 -0
  53. package/core/hooks/subagent_stop.py +186 -0
  54. package/core/hooks/user_prompt_submit.py +10 -6
  55. package/core/knowledge/__pycache__/embedding_backends.cpython-313.pyc +0 -0
  56. package/core/knowledge/__pycache__/embedding_backends.cpython-314.pyc +0 -0
  57. package/core/knowledge/__pycache__/pattern_cards.cpython-313.pyc +0 -0
  58. package/core/knowledge/__pycache__/pattern_cards.cpython-314.pyc +0 -0
  59. package/core/knowledge/__pycache__/recipes.cpython-314.pyc +0 -0
  60. package/core/knowledge/__pycache__/vector_store.cpython-313.pyc +0 -0
  61. package/core/knowledge/__pycache__/vector_store.cpython-314.pyc +0 -0
  62. package/core/knowledge/embedding_backends.py +278 -0
  63. package/core/knowledge/pattern_cards.py +49 -1
  64. package/core/knowledge/vector_store.py +14 -2
  65. package/core/memory/__pycache__/semantic_store.cpython-312.pyc +0 -0
  66. package/core/memory/__pycache__/semantic_store.cpython-313.pyc +0 -0
  67. package/core/memory/__pycache__/semantic_store.cpython-314.pyc +0 -0
  68. package/core/memory/__pycache__/turn_capture.cpython-313.pyc +0 -0
  69. package/core/memory/__pycache__/turn_capture.cpython-314.pyc +0 -0
  70. package/core/memory/semantic_store.py +301 -0
  71. package/core/memory/turn_capture.py +277 -0
  72. package/core/registry/__pycache__/generator.cpython-312.pyc +0 -0
  73. package/core/runtime/__pycache__/claude_code.cpython-312.pyc +0 -0
  74. package/core/runtime/__pycache__/codex_cli.cpython-312.pyc +0 -0
  75. package/core/runtime/__pycache__/cost_governor.cpython-314.pyc +0 -0
  76. package/core/runtime/__pycache__/gemini_cli.cpython-312.pyc +0 -0
  77. package/core/runtime/__pycache__/llm_cost_telemetry.cpython-312.pyc +0 -0
  78. package/core/runtime/__pycache__/mcp_telemetry.cpython-312.pyc +0 -0
  79. package/core/shared/__pycache__/decay.cpython-313.pyc +0 -0
  80. package/core/shared/__pycache__/decay.cpython-314.pyc +0 -0
  81. package/core/shared/__pycache__/sqlite_recovery.cpython-312.pyc +0 -0
  82. package/core/shared/__pycache__/sqlite_recovery.cpython-313.pyc +0 -0
  83. package/core/shared/__pycache__/sqlite_recovery.cpython-314.pyc +0 -0
  84. package/core/shared/decay.py +94 -0
  85. package/core/shared/sqlite_recovery.py +266 -0
  86. package/core/synapse/__pycache__/agent_experiences_layer.cpython-313.pyc +0 -0
  87. package/core/synapse/__pycache__/agent_experiences_layer.cpython-314.pyc +0 -0
  88. package/core/synapse/__pycache__/engine.cpython-312.pyc +0 -0
  89. package/core/synapse/__pycache__/engine.cpython-313.pyc +0 -0
  90. package/core/synapse/__pycache__/engine.cpython-314.pyc +0 -0
  91. package/core/synapse/__pycache__/layers.cpython-312.pyc +0 -0
  92. package/core/synapse/__pycache__/pattern_library_layer.cpython-314.pyc +0 -0
  93. package/core/synapse/__pycache__/recipe_layer.cpython-313.pyc +0 -0
  94. package/core/synapse/__pycache__/recipe_layer.cpython-314.pyc +0 -0
  95. package/core/synapse/__pycache__/routing_feedback_layer.cpython-313.pyc +0 -0
  96. package/core/synapse/__pycache__/routing_feedback_layer.cpython-314.pyc +0 -0
  97. package/core/synapse/__pycache__/session_memory_layer.cpython-312.pyc +0 -0
  98. package/core/synapse/__pycache__/session_memory_layer.cpython-313.pyc +0 -0
  99. package/core/synapse/__pycache__/session_memory_layer.cpython-314.pyc +0 -0
  100. package/core/synapse/agent_experiences_layer.py +42 -3
  101. package/core/synapse/engine.py +13 -0
  102. package/core/synapse/recipe_layer.py +27 -4
  103. package/core/synapse/routing_feedback_layer.py +132 -0
  104. package/core/synapse/session_memory_layer.py +152 -0
  105. package/core/workflow/__pycache__/__init__.cpython-312.pyc +0 -0
  106. package/core/workflow/__pycache__/design_authorization.cpython-314.pyc +0 -0
  107. package/core/workflow/__pycache__/frontend_gate.cpython-314.pyc +0 -0
  108. package/core/workflow/__pycache__/specialist_enforcer.cpython-314.pyc +0 -0
  109. package/installer/adapters/claude-code.js +37 -0
  110. package/installer/claude-plugins.js +6 -0
  111. package/installer/cli.js +23 -2
  112. package/installer/config-seed.js +25 -16
  113. package/installer/doctor.js +116 -1
  114. package/installer/hook-lib.js +28 -0
  115. package/installer/index.js +48 -153
  116. package/installer/mcp-runner.js +108 -0
  117. package/installer/skill-deploy.js +189 -0
  118. package/installer/skills-mode.js +64 -0
  119. package/installer/update.js +55 -79
  120. package/knowledge/commands-registry.json.bak +171 -4
  121. package/knowledge/skills-manifest.json +2337 -0
  122. package/mcps/arka-prompts/commands.py +451 -0
  123. package/mcps/arka-prompts/pyproject.toml +15 -0
  124. package/mcps/arka-prompts/server.py +156 -0
  125. package/mcps/arka-tools/__pycache__/server.cpython-313.pyc +0 -0
  126. package/mcps/arka-tools/pyproject.toml +10 -0
  127. package/mcps/arka-tools/server.py +319 -0
  128. package/mcps/profiles/base.json +19 -0
  129. package/mcps/profiles/brand.json +19 -0
  130. package/mcps/profiles/comms.json +13 -0
  131. package/mcps/profiles/content.json +20 -0
  132. package/mcps/profiles/ecommerce.json +13 -0
  133. package/mcps/profiles/full-stack.json +15 -0
  134. package/mcps/profiles/laravel.json +12 -0
  135. package/mcps/profiles/nextjs.json +13 -0
  136. package/mcps/profiles/nuxt.json +13 -0
  137. package/mcps/profiles/react.json +12 -0
  138. package/mcps/profiles/vue.json +12 -0
  139. package/mcps/registry.json +423 -0
  140. package/mcps/scripts/apply-mcps.sh +282 -0
  141. package/mcps/stacks/laravel-packages.json +49 -0
  142. package/mcps/stacks/react-packages.json +58 -0
  143. package/package.json +2 -1
  144. package/pyproject.toml +9 -1
  145. package/scripts/__pycache__/marketplace_gen.cpython-313.pyc +0 -0
  146. package/scripts/__pycache__/synapse-bridge.cpython-314.pyc +0 -0
  147. package/scripts/marketplace_gen.py +310 -0
  148. package/scripts/tools/__pycache__/docs_stats.cpython-313.pyc +0 -0
  149. package/scripts/tools/__pycache__/skill_budget.cpython-313.pyc +0 -0
  150. package/scripts/tools/docs_stats.py +6 -4
  151. package/scripts/tools/skill_budget.py +209 -0
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * ArkaOS — PostToolUse fast-path shim (F2-6).
5
+ *
6
+ * Fast-exits ("{}", exit 0) only when the manifest proves the Python
7
+ * chain would do exactly that with no pending decision-bearing write:
8
+ * benign exit code, no error-trigger in the output, tool outside the
9
+ * stateful set (ExitPlanMode/Task/Agent), and the flow-auth confirm
10
+ * rescan provably redundant (enforcement off, or confirmed auth fresh).
11
+ * MCP usage telemetry — the one write on this path — is replicated
12
+ * inline. Everything else delegates to the sibling post-tool-use.sh.
13
+ *
14
+ * Kill switch: ARKA_HOOK_FASTPATH=0. Fail-open on internal error: try
15
+ * to delegate; if even that fails, print "{}" and exit 0 — the exact
16
+ * degraded form of post-tool-use.sh (NOTE: differs from the PreToolUse
17
+ * fail-open, which is empty stdout).
18
+ */
19
+
20
+ const fs = require("node:fs");
21
+ const path = require("node:path");
22
+
23
+ function readStdin() {
24
+ try {
25
+ return fs.readFileSync(0, "utf8");
26
+ } catch {
27
+ return "";
28
+ }
29
+ }
30
+
31
+ function failOpen() {
32
+ process.stdout.write("{}\n");
33
+ process.exit(0);
34
+ }
35
+
36
+ function delegate(rawStdin) {
37
+ const sibling = path.join(__dirname, "post-tool-use.sh");
38
+ if (!fs.existsSync(sibling)) {
39
+ // Missing sibling → bash would exit 127; the contract is fail-open
40
+ // with the Post degraded form ("{}").
41
+ failOpen();
42
+ }
43
+ // Lazy require: the fast path never pays for child_process.
44
+ const { spawnSync } = require("node:child_process");
45
+ const result = spawnSync("bash", [sibling], {
46
+ input: rawStdin,
47
+ stdio: ["pipe", "inherit", "inherit"],
48
+ env: { ...process.env, ARKA_HOOK_FASTPATH: "0" },
49
+ });
50
+ if (result.error || result.status === null) {
51
+ failOpen();
52
+ }
53
+ process.exit(result.status);
54
+ }
55
+
56
+ function main() {
57
+ const rawStdin = readStdin();
58
+ if ((process.env.ARKA_HOOK_FASTPATH || "").trim() === "0") {
59
+ delegate(rawStdin);
60
+ }
61
+ // QG B3 (redo 1): the engine require lives INSIDE the fail-open
62
+ // boundary — a split deploy (.cjs present, _lib/fastpath absent)
63
+ // must delegate to the sibling .sh, not crash with a stack trace.
64
+ let engine;
65
+ try {
66
+ engine = require(path.join(__dirname, "_lib", "fastpath", "engine.cjs"));
67
+ } catch {
68
+ delegate(rawStdin);
69
+ }
70
+
71
+ let payload;
72
+ try {
73
+ payload = JSON.parse(rawStdin);
74
+ } catch {
75
+ payload = null;
76
+ }
77
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
78
+ delegate(rawStdin);
79
+ }
80
+
81
+ const manifest = engine.readJsonFile(
82
+ fs, path.join(__dirname, "gate-manifest.json")
83
+ ).data;
84
+ const homeDir = process.env.HOME || require("node:os").homedir();
85
+ const ctx = {
86
+ homeDir,
87
+ env: process.env,
88
+ fs,
89
+ config: engine.readJsonFile(fs, path.join(homeDir, ".arkaos", "config.json")),
90
+ };
91
+
92
+ const decision = engine.decidePost(payload, manifest, ctx);
93
+ if (decision.action !== "fast-exit") {
94
+ delegate(rawStdin);
95
+ }
96
+
97
+ for (const write of decision.writes) {
98
+ try {
99
+ fs.mkdirSync(path.dirname(write.path), { recursive: true });
100
+ fs.appendFileSync(write.path, write.line, { flag: "a" });
101
+ } catch {
102
+ // Telemetry must never break the hook.
103
+ }
104
+ }
105
+ process.stdout.write(decision.stdout + "\n");
106
+ process.exit(0);
107
+ }
108
+
109
+ try {
110
+ main();
111
+ } catch {
112
+ failOpen();
113
+ }
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * ArkaOS — PreToolUse fast-path shim (F2-6).
5
+ *
6
+ * An 18ms-p50 Node process (measured; ~10ms of it is bare Node startup)
7
+ * replaces the 82ms-p50 bash->Python chain for the
8
+ * decisions the manifest proves trivial: non-flow-gated tools and
9
+ * discovery Bash with no active budget cap fast-allow here (with the
10
+ * same telemetry appends the Python chain would make); EVERYTHING else
11
+ * delegates to the sibling pre-tool-use.sh, byte-for-byte the current
12
+ * behavior. This shim has no deny path — see engine.cjs invariant #0.
13
+ *
14
+ * Kill switch: ARKA_HOOK_FASTPATH=0 delegates unconditionally (and is
15
+ * re-exported to the child as a recursion guard). Fail-open contract on
16
+ * internal error: try to delegate; if even that fails, exit 0 with
17
+ * empty stdout — identical to pre-tool-use.sh degraded mode.
18
+ */
19
+
20
+ const fs = require("node:fs");
21
+ const path = require("node:path");
22
+
23
+ const FAIL_OPEN_EXIT = 0;
24
+
25
+ function readStdin() {
26
+ try {
27
+ return fs.readFileSync(0, "utf8");
28
+ } catch {
29
+ return "";
30
+ }
31
+ }
32
+
33
+ function delegate(rawStdin) {
34
+ const sibling = path.join(__dirname, "pre-tool-use.sh");
35
+ if (!fs.existsSync(sibling)) {
36
+ // Missing sibling → bash would exit 127; the contract is fail-open.
37
+ process.exit(FAIL_OPEN_EXIT);
38
+ }
39
+ // Lazy require: the fast path never pays for child_process.
40
+ const { spawnSync } = require("node:child_process");
41
+ const result = spawnSync("bash", [sibling], {
42
+ input: rawStdin,
43
+ stdio: ["pipe", "inherit", "inherit"],
44
+ env: { ...process.env, ARKA_HOOK_FASTPATH: "0" },
45
+ });
46
+ if (result.error || result.status === null) {
47
+ process.exit(FAIL_OPEN_EXIT);
48
+ }
49
+ process.exit(result.status);
50
+ }
51
+
52
+ function main() {
53
+ const rawStdin = readStdin();
54
+ if ((process.env.ARKA_HOOK_FASTPATH || "").trim() === "0") {
55
+ delegate(rawStdin);
56
+ }
57
+ // QG B3 (redo 1): the engine require lives INSIDE the fail-open
58
+ // boundary — a split deploy (.cjs present, _lib/fastpath absent)
59
+ // must delegate to the sibling .sh, not crash with a stack trace.
60
+ let engine;
61
+ try {
62
+ engine = require(path.join(__dirname, "_lib", "fastpath", "engine.cjs"));
63
+ } catch {
64
+ delegate(rawStdin);
65
+ }
66
+
67
+ let payload;
68
+ try {
69
+ payload = JSON.parse(rawStdin);
70
+ } catch {
71
+ payload = null;
72
+ }
73
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
74
+ delegate(rawStdin);
75
+ }
76
+
77
+ const manifest = engine.readJsonFile(
78
+ fs, path.join(__dirname, "gate-manifest.json")
79
+ ).data;
80
+ const homeDir = process.env.HOME || require("node:os").homedir();
81
+ const ctx = {
82
+ homeDir,
83
+ env: process.env,
84
+ fs,
85
+ config: engine.readJsonFile(fs, path.join(homeDir, ".arkaos", "config.json")),
86
+ };
87
+
88
+ const decision = engine.decidePre(payload, manifest, ctx);
89
+ if (decision.action !== "fast-allow") {
90
+ delegate(rawStdin);
91
+ }
92
+
93
+ for (const write of decision.writes) {
94
+ try {
95
+ fs.mkdirSync(path.dirname(write.path), { recursive: true });
96
+ fs.appendFileSync(write.path, write.line, { flag: "a" });
97
+ } catch {
98
+ // Telemetry must never break the hook (same contract as the
99
+ // Python try/except pass around record_telemetry).
100
+ }
101
+ }
102
+ process.exit(0); // allow: empty stdout, exit 0
103
+ }
104
+
105
+ try {
106
+ main();
107
+ } catch {
108
+ process.exit(FAIL_OPEN_EXIT);
109
+ }
@@ -0,0 +1,40 @@
1
+ # ============================================================================
2
+ # ArkaOS — SessionEnd Hook (Windows / PowerShell 5.1+)
3
+ #
4
+ # Parity with config/hooks/session-end.sh. Thin wrapper: reads the stdin JSON
5
+ # payload and pipes it to ONE python process (core.hooks.session_end), which
6
+ # owns the entire behaviour. Fail-open: no usable interpreter => exit 0.
7
+ # ============================================================================
8
+
9
+ $ErrorActionPreference = 'SilentlyContinue'
10
+ $stdinText = [Console]::In.ReadToEnd()
11
+
12
+ # Shared resolver (venv-first, yaml-verified). Mirrors _lib/arka_python.sh.
13
+ $arkaLib = Join-Path $PSScriptRoot "_lib\arka_python.ps1"
14
+ if (Test-Path -LiteralPath $arkaLib) { . $arkaLib }
15
+
16
+ if (Get-Command Resolve-ArkaRoot -ErrorAction SilentlyContinue) {
17
+ $env:ARKAOS_ROOT = Resolve-ArkaRoot
18
+ } elseif ([string]::IsNullOrWhiteSpace($env:ARKAOS_ROOT)) {
19
+ $repoPathFile = Join-Path $HOME ".arkaos/.repo-path"
20
+ if (Test-Path $repoPathFile) {
21
+ $env:ARKAOS_ROOT = (Get-Content $repoPathFile -Raw).Trim()
22
+ } elseif (Test-Path (Join-Path $HOME ".arkaos")) {
23
+ $env:ARKAOS_ROOT = (Join-Path $HOME ".arkaos")
24
+ } else {
25
+ $env:ARKAOS_ROOT = if ($env:ARKA_OS) { $env:ARKA_OS } else { Join-Path $HOME ".claude/skills/arkaos" }
26
+ }
27
+ }
28
+
29
+ $pythonExe = $env:ARKA_PY
30
+ if (-not $pythonExe) { exit 0 }
31
+ if (-not (Test-Path -LiteralPath (Join-Path $env:ARKAOS_ROOT "core/hooks/session_end.py"))) {
32
+ $selfRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).Path
33
+ if (Test-Path -LiteralPath (Join-Path $selfRoot "core/hooks/session_end.py")) {
34
+ $env:ARKAOS_ROOT = $selfRoot
35
+ } else { exit 0 }
36
+ }
37
+
38
+ $env:PYTHONPATH = "$($env:ARKAOS_ROOT)$([IO.Path]::PathSeparator)$($env:PYTHONPATH)"
39
+ $stdinText | & $pythonExe -m core.hooks.session_end
40
+ exit 0
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # ArkaOS v2 — session-end Hook (thin wrapper, F2-4)
4
+ #
5
+ # Delegates the ENTIRE event to ONE python process:
6
+ # python3 -m core.hooks.session_end
7
+ #
8
+ # Writes a final session digest immediately (PreCompact only fires on
9
+ # compaction) and marks the session ended. Best-effort, never blocks.
10
+ # Fail-open: no usable interpreter => exit 0. Timeout: 15s.
11
+ # ============================================================================
12
+
13
+ # ─── Shared Python resolver (exports ARKA_PY) ──────────────────────────
14
+ _ARKA_LIB="$(dirname "${BASH_SOURCE[0]:-$0}")/_lib/arka_python.sh"
15
+ if [ -f "$_ARKA_LIB" ]; then . "$_ARKA_LIB"; else ARKA_PY="python3"; fi
16
+
17
+ # ─── Resolve ARKAOS_ROOT (validated — see arka_resolve_root in _lib) ────
18
+ # .repo-path can point at a purged npx cache; the shared resolver falls
19
+ # through to the ~/.arkaos/lib snapshot instead of exporting a dead root.
20
+ if command -v arka_resolve_root >/dev/null 2>&1; then
21
+ ARKAOS_ROOT="$(arka_resolve_root)"
22
+ elif [ -z "${ARKAOS_ROOT:-}" ]; then
23
+ # Legacy chain (pre-snapshot _lib deployment)
24
+ if [ -f "$HOME/.arkaos/.repo-path" ]; then
25
+ ARKAOS_ROOT=$(cat "$HOME/.arkaos/.repo-path")
26
+ elif [ -d "$HOME/.arkaos" ]; then
27
+ ARKAOS_ROOT="$HOME/.arkaos"
28
+ else
29
+ ARKAOS_ROOT="${ARKA_OS:-$HOME/.claude/skills/arkaos}"
30
+ fi
31
+ fi
32
+ export ARKAOS_ROOT
33
+
34
+ # ─── Degrade gracefully (fail open, same as before) ─────────────────────
35
+ if ! command -v "$ARKA_PY" >/dev/null 2>&1; then
36
+ exit 0
37
+ fi
38
+ # Self-root fallback: the wrapper ships next to its python entrypoint, so
39
+ # when ARKAOS_ROOT points at a pre-PR-6 install without core/hooks/, use
40
+ # the root this script lives in.
41
+ if [ ! -f "$ARKAOS_ROOT/core/hooks/session_end.py" ]; then
42
+ _SELF_ROOT="$(cd "$(dirname "$0")/../.." 2>/dev/null && pwd)"
43
+ if [ -n "$_SELF_ROOT" ] && [ -f "$_SELF_ROOT/core/hooks/session_end.py" ]; then
44
+ ARKAOS_ROOT="$_SELF_ROOT"
45
+ export ARKAOS_ROOT
46
+ else
47
+ exit 0
48
+ fi
49
+ fi
50
+
51
+ # ─── Single python process; stdin/stdout/stderr/exit-code pass through ──
52
+ # Interpreter resolution handled by the shared resolver (ARKA_PY): prefers
53
+ # the ArkaOS venv (has pyyaml/pydantic), falls back to a yaml-capable python3.
54
+ PYTHONPATH="$ARKAOS_ROOT${PYTHONPATH:+:$PYTHONPATH}" \
55
+ exec "$ARKA_PY" -m core.hooks.session_end
@@ -1,201 +1,37 @@
1
1
  #!/usr/bin/env bash
2
2
  # ============================================================================
3
- # ArkaOS — SessionStart Hook
4
- # Uses systemMessage (same protocol as claude-mem) for guaranteed display.
3
+ # ArkaOS — SessionStart Hook (thin wrapper, F2-2 hook-hygiene completion)
4
+ #
5
+ # ONE python process (core.hooks.session_start) builds the entire
6
+ # systemMessage — the 13 inline python spawns of the previous version
7
+ # live there now (baseline 251ms p50 before consolidation). This file
8
+ # only resolves the interpreter and execs; with no usable interpreter
9
+ # it emits a static banner and exits 0 (fail-open, stdlib-free).
5
10
  # ============================================================================
6
11
 
7
- # ─── Profile ───────────────────────────────────────────────────────────
8
- NAME="founder"
9
- COMPANY="WizardingCode"
10
- VERSION="2.x"
12
+ # _HOOK_CWD captured BEFORE any cd — inside a compound command $PWD
13
+ # would expand after the cd (QG blocker, F1-A3: cross-project leak).
14
+ _HOOK_CWD="$PWD"
15
+ export ARKA_HOOK_CWD="$_HOOK_CWD"
11
16
 
12
17
  # ─── Shared Python resolver (exports ARKA_PY) ──────────────────────────
13
18
  _ARKA_LIB="$(dirname "${BASH_SOURCE[0]:-$0}")/_lib/arka_python.sh"
14
19
  if [ -f "$_ARKA_LIB" ]; then . "$_ARKA_LIB"; else ARKA_PY="python3"; fi
15
20
 
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")
19
- fi
20
-
21
- if [ -f "$HOME/.arkaos/.repo-path" ]; then
22
- REPO=$(cat "$HOME/.arkaos/.repo-path")
23
- [ -f "$REPO/VERSION" ] && VERSION=$(cat "$REPO/VERSION" | tr -d '[:space:]')
24
- fi
25
- # .repo-path may point at a purged npx cache — the installer's snapshot
26
- # keeps the drift banner alive ("2.x" is the untouched default above).
27
- if [ "$VERSION" = "2.x" ] && [ -f "$HOME/.arkaos/lib/VERSION" ]; then
28
- VERSION=$(tr -d '[:space:]' < "$HOME/.arkaos/lib/VERSION")
29
- fi
30
-
31
- # ─── Static greeting (cache-friendly) ──────────────────────────────────
32
- # Time-of-day branching removed: it invalidated prompt cache 3x/day without
33
- # meaningful signal. Static greeting keeps SessionStart output stable.
34
- GREETING="Olá"
35
-
36
- # ─── Version drift ─────────────────────────────────────────────────────
37
- SYNC_STATE="$HOME/.arkaos/sync-state.json"
38
- DRIFT=""
39
-
40
- if [ -f "$SYNC_STATE" ]; then
41
- SYNCED=$("$ARKA_PY" -c "import json; print(json.load(open('$SYNC_STATE'))['version'])" 2>/dev/null || echo "none")
42
- if [ "$SYNCED" != "$VERSION" ]; then
43
- DRIFT="\\n[arka:update-available] Core v${VERSION} != synced v${SYNCED}. Run /arka update."
44
- fi
45
- else
46
- DRIFT="\\n[arka:update-available] Never synced. Run /arka update."
47
- fi
48
-
49
- # ─── Build message ─────────────────────────────────────────────────────
50
- MSG="\\n╔══════════════════════════════════════════════╗\\n"
51
- MSG+="║ ║\\n"
52
- MSG+="║ A R K A O S ║\\n"
53
- MSG+="║ ║\\n"
54
- MSG+="║ The Operating System for AI Teams ║\\n"
55
- MSG+="║ by WizardingCode ║\\n"
56
- MSG+="║ ║\\n"
57
- MSG+="╚══════════════════════════════════════════════╝\\n"
58
- MSG+="\\n"
59
- MSG+="${GREETING}, ${NAME} (${COMPANY})\\n"
60
- # ─── Active Workflow ──────────────────────────────────────────────────
61
- STATE_READER="$REPO/core/workflow/state_reader.sh"
62
- if [ -n "$REPO" ] && [ -f "$STATE_READER" ] && bash "$STATE_READER" active 2>/dev/null; then
63
- WF_SUMMARY=$(bash "$STATE_READER" summary 2>/dev/null)
64
- WF_NAME=$(echo "$WF_SUMMARY" | cut -d'|' -f1)
65
- WF_PHASE=$(echo "$WF_SUMMARY" | cut -d'|' -f2)
66
- WF_PROGRESS=$(echo "$WF_SUMMARY" | cut -d'|' -f3)
67
- WF_BRANCH=$(echo "$WF_SUMMARY" | cut -d'|' -f4)
68
- WF_VIOLATIONS=$(echo "$WF_SUMMARY" | cut -d'|' -f5)
69
- MSG+="\\nWorkflow: ${WF_NAME} (${WF_PROGRESS})"
70
- [ -n "$WF_BRANCH" ] && MSG+=" branch:${WF_BRANCH}"
71
- [ "$WF_VIOLATIONS" != "0" ] && MSG+=" VIOLATIONS:${WF_VIOLATIONS}"
72
- MSG+="\\n"
73
- fi
74
-
75
- # --- Forge Plan Display ---
76
- _FORGE_PLANS="$HOME/.arkaos/plans"
77
- _FORGE_ACTIVE="$_FORGE_PLANS/active.yaml"
78
- _FORGE_LINE=""
79
-
80
- if [ -f "$_FORGE_ACTIVE" ]; then
81
- _FORGE_ID=$(cat "$_FORGE_ACTIVE" 2>/dev/null)
82
- _FORGE_FILE="$_FORGE_PLANS/${_FORGE_ID}.yaml"
83
- if [ -f "$_FORGE_FILE" ] && command -v "$ARKA_PY" >/dev/null 2>&1; then
84
- _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)
85
- _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)
86
- _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)
87
- _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)
88
-
89
- if [ "$_FORGE_STATUS" = "approved" ]; then
90
- _FORGE_LINE=" ⚒ Forge plan pending: ${_FORGE_NAME} | Phases: ${_FORGE_PHASES} | /forge resume"
91
- elif [ "$_FORGE_STATUS" = "executing" ]; then
92
- _FORGE_LINE=" ⚒ Forge executing: ${_FORGE_NAME} | Phases: ${_FORGE_PHASES} | Branch: ${_FORGE_BRANCH}"
93
- fi
94
- fi
95
- fi
96
-
97
- # Counts intentionally omitted (prompt-surface P0 2026-07-08): hand-typed
98
- # numbers drift from docs_stats.py truth and change no model behavior.
99
- MSG+="ArkaOS v${VERSION}"
100
- [ -n "$_FORGE_LINE" ] && MSG+="\\n${_FORGE_LINE}"
101
- MSG+="${DRIFT}"
102
-
103
- # ─── Evidence Flow Contract (top-of-session, highest priority) ──────────
104
- MSG+="\\n\\n[ARKA:EVIDENCE-FLOW] NON-NEGOTIABLE. Every non-trivial request runs the 4-gate evidence flow (constitution rule evidence-flow; source arka/skills/flow/SKILL.md):"
105
- MSG+="\\n G1 CONTEXT ([arka:routing] <dept> -> <lead> + KB/graph grounding, cite or declare gap)"
106
- MSG+="\\n G2 PLAN (short plan -> EXPLICIT user approval; silence != approval)"
107
- MSG+="\\n G3 EXECUTE (closes only with real test run on record: command + exit 0)"
108
- MSG+="\\n G4 REVIEW (executable checks: lint/type/coverage/security/spell -> honest summary)"
109
- MSG+="\\nEmit [arka:gate:N] at each gate start. Gates pass on evidence, never on narration."
110
- MSG+="\\nBypass ONLY via [arka:trivial] <reason> for single-file edits under 10 lines."
111
-
112
- # ─── Transparency tag contract (PR12 v2.34.0) ───────────────────────────
113
- MSG+="\\n\\n[ARKA:META-TAG] Every substantive response ends with a single line:"
114
- MSG+="\\n [arka:meta] kb=N research=X persona=Y gap=Z critic=W"
115
- MSG+="\\nFields: kb=N (Obsidian/KB notes consulted), research=X (MCPs invoked: perplexity,exa,context7,firecrawl,xmcp or 'none'), persona=Y (advisor name or 'orchestrator'), gap=Z (KB gap topic or 'none'), critic=W (passed|failed|skipped)."
116
- MSG+="\\nMandatory after: EFFECT tool calls, plan/recommendation outputs, QG verdicts. Optional for pure read-only status replies."
117
- MSG+="\\nAbsence is measured by the Stop hook (warn-only in v2.34.0) before promotion to hard enforcement."
118
-
119
- # ─── Model Fabric routing (consumption layer) ───────────────────────────
120
- # Feed the operator's ~/.arkaos/models.yaml back to the orchestrator so
121
- # dashboard/CLI model choices actually govern agent dispatch. Cheap
122
- # (single python read); skipped silently if the config can't be resolved.
123
- if [ -n "$REPO" ]; then
124
- # ARKA_PY already resolves to the venv python (has pydantic + yaml); the
125
- # ambient python3 usually lacks them and routing_directive() would return "".
126
- if command -v "$ARKA_PY" >/dev/null 2>&1 || [ -x "$ARKA_PY" ]; then
127
- _MF_DIRECTIVE=$(cd "$REPO" && PYTHONPATH="$REPO" "$ARKA_PY" -c "
128
- try:
129
- from core.runtime.model_routing_context import routing_directive
130
- print(routing_directive())
131
- except Exception:
132
- pass
133
- " 2>/dev/null)
134
- if [ -n "$_MF_DIRECTIVE" ]; then
135
- MSG+="\\n\\n${_MF_DIRECTIVE}"
136
- fi
137
- fi
138
- fi
139
-
140
- # ─── Stale-aware reorganizer trigger (PR24 v2.46.0) ─────────────────────
141
- # If today's proposal file is missing, fire the reorganizer in the
142
- # background with a 30s timeout. Best-effort, never blocks session
143
- # start. Multiple sessions per day no-op because the file now exists.
144
- if [ -n "$REPO" ] && command -v "$ARKA_PY" >/dev/null 2>&1; then
145
- _PROPOSAL_DIR="$HOME/.arkaos/reorganize-proposals"
146
- _TODAY="$(date -u +%Y-%m-%d).md"
147
- if [ ! -f "$_PROPOSAL_DIR/$_TODAY" ]; then
148
- (
149
- cd "$REPO" && timeout 30s "$ARKA_PY" -m core.cognition.reorganizer_cli >/dev/null 2>&1
150
- ) &
151
- disown 2>/dev/null || true
152
- fi
153
- fi
154
-
155
- # ─── Dashboard ensure (v4.1.2) ──────────────────────────────────────────
156
- # Safety net for the login autostart (`npx arkaos autostart enable`): if the
157
- # dashboard is healthy this is two curl health checks (~50ms); if not, it
158
- # starts API+UI. Background + disown so it never touches the 5s budget.
159
- # Toggle off: ~/.arkaos/config.json -> {"dashboard": {"ensure_on_session": false}}
160
- if [ -n "$REPO" ] && [ -f "$REPO/scripts/start-dashboard.sh" ]; then
161
- _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")
162
- if [ "$_DASH_ENSURE" = "True" ] || [ "$_DASH_ENSURE" = "true" ]; then
163
- mkdir -p "$HOME/.arkaos/logs"
164
- (
165
- # GNU `timeout` is absent on stock macOS — degrade to an unbounded
166
- # run; the launcher itself is bounded (health wait + start).
167
- if command -v timeout >/dev/null 2>&1; then
168
- ARKAOS_NO_BROWSER=1 timeout 90s bash "$REPO/scripts/start-dashboard.sh" ensure >> "$HOME/.arkaos/logs/dashboard-ensure.log" 2>&1
169
- else
170
- ARKAOS_NO_BROWSER=1 bash "$REPO/scripts/start-dashboard.sh" ensure >> "$HOME/.arkaos/logs/dashboard-ensure.log" 2>&1
171
- fi
172
- ) &
173
- disown 2>/dev/null || true
174
- fi
175
- fi
21
+ REPO=""
22
+ [ -f "$HOME/.arkaos/.repo-path" ] && REPO=$(cat "$HOME/.arkaos/.repo-path")
176
23
 
177
- # --- Session Memory Resume Context ---
178
- if command -v "$ARKA_PY" >/dev/null 2>&1 && [ -n "$REPO" ]; then
179
- _SESSION_CTX=$(cd "$REPO" && "$ARKA_PY" -c "
180
- import sys
181
- sys.path.insert(0, '$REPO')
182
- try:
183
- from core.memory.rehydrator import build_resume_context
184
- ctx = build_resume_context()
185
- if ctx:
186
- print('\\n[SESSION] ' + ctx.replace('\\n', '\\n[SESSION] '))
187
- except Exception:
188
- pass
189
- " 2>/dev/null)
190
- [ -n "$_SESSION_CTX" ] && MSG+="\\n${_SESSION_CTX}"
24
+ # The MODULE file is the guard, not just core/: an older installed
25
+ # snapshot without it would exec into "No module named ..." (exit 1,
26
+ # empty output a broken SessionStart instead of a degraded banner).
27
+ if [ -n "$REPO" ] && [ -f "$REPO/core/hooks/session_start.py" ] \
28
+ && command -v "$ARKA_PY" >/dev/null 2>&1; then
29
+ cd "$REPO" 2>/dev/null || true
30
+ PYTHONPATH="$REPO" exec "$ARKA_PY" -m core.hooks.session_start
191
31
  fi
192
32
 
193
- # ─── Output as systemMessage (same protocol as claude-mem) ─────────────
194
- # OWASP A03: MSG carries profile-derived NAME/COMPANY. Pass it through the
195
- # environment (with \n already expanded) rather than interpolating it into
196
- # the Python source, so a name containing quotes or ''' cannot break the
197
- # script or the JSON envelope.
198
- ARKA_MSG="$(echo -e "$MSG")" "$ARKA_PY" -c "
199
- import json, os
200
- print(json.dumps({'systemMessage': os.environ['ARKA_MSG']}))
201
- "
33
+ # ─── Degraded fallback: static banner, valid JSON, exit 0 ──────────────
34
+ cat <<'EOF'
35
+ {"systemMessage": "\n╔══════════════════════════════════════════════╗\n║ A R K A O S ║\n║ The Operating System for AI Teams ║\n╚══════════════════════════════════════════════╝\nOlá, founder (WizardingCode)\nArkaOS (degraded: no usable interpreter — run npx arkaos doctor)"}
36
+ EOF
37
+ exit 0
@@ -0,0 +1,40 @@
1
+ # ============================================================================
2
+ # ArkaOS — SubagentStop Hook (Windows / PowerShell 5.1+)
3
+ #
4
+ # Parity with config/hooks/subagent-stop.sh. Thin wrapper: reads the stdin JSON
5
+ # payload and pipes it to ONE python process (core.hooks.subagent_stop), which
6
+ # owns the entire behaviour. Fail-open: no usable interpreter => exit 0.
7
+ # ============================================================================
8
+
9
+ $ErrorActionPreference = 'SilentlyContinue'
10
+ $stdinText = [Console]::In.ReadToEnd()
11
+
12
+ # Shared resolver (venv-first, yaml-verified). Mirrors _lib/arka_python.sh.
13
+ $arkaLib = Join-Path $PSScriptRoot "_lib\arka_python.ps1"
14
+ if (Test-Path -LiteralPath $arkaLib) { . $arkaLib }
15
+
16
+ if (Get-Command Resolve-ArkaRoot -ErrorAction SilentlyContinue) {
17
+ $env:ARKAOS_ROOT = Resolve-ArkaRoot
18
+ } elseif ([string]::IsNullOrWhiteSpace($env:ARKAOS_ROOT)) {
19
+ $repoPathFile = Join-Path $HOME ".arkaos/.repo-path"
20
+ if (Test-Path $repoPathFile) {
21
+ $env:ARKAOS_ROOT = (Get-Content $repoPathFile -Raw).Trim()
22
+ } elseif (Test-Path (Join-Path $HOME ".arkaos")) {
23
+ $env:ARKAOS_ROOT = (Join-Path $HOME ".arkaos")
24
+ } else {
25
+ $env:ARKAOS_ROOT = if ($env:ARKA_OS) { $env:ARKA_OS } else { Join-Path $HOME ".claude/skills/arkaos" }
26
+ }
27
+ }
28
+
29
+ $pythonExe = $env:ARKA_PY
30
+ if (-not $pythonExe) { exit 0 }
31
+ if (-not (Test-Path -LiteralPath (Join-Path $env:ARKAOS_ROOT "core/hooks/subagent_stop.py"))) {
32
+ $selfRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).Path
33
+ if (Test-Path -LiteralPath (Join-Path $selfRoot "core/hooks/subagent_stop.py")) {
34
+ $env:ARKAOS_ROOT = $selfRoot
35
+ } else { exit 0 }
36
+ }
37
+
38
+ $env:PYTHONPATH = "$($env:ARKAOS_ROOT)$([IO.Path]::PathSeparator)$($env:PYTHONPATH)"
39
+ $stdinText | & $pythonExe -m core.hooks.subagent_stop
40
+ exit 0
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # ArkaOS v2 — subagent-stop Hook (thin wrapper, F2-4)
4
+ #
5
+ # Delegates the ENTIRE event to ONE python process:
6
+ # python3 -m core.hooks.subagent_stop
7
+ #
8
+ # Persists each dispatched subagent's output + runs warn-only honesty
9
+ # checks (phantom-action, meta-tag), nudging deliverable-shaped output
10
+ # toward the Quality Gate. Never blocks (the subagent already ran).
11
+ # Fail-open: no usable interpreter => exit 0. Timeout: 10s.
12
+ # ============================================================================
13
+
14
+ # ─── Shared Python resolver (exports ARKA_PY) ──────────────────────────
15
+ _ARKA_LIB="$(dirname "${BASH_SOURCE[0]:-$0}")/_lib/arka_python.sh"
16
+ if [ -f "$_ARKA_LIB" ]; then . "$_ARKA_LIB"; else ARKA_PY="python3"; fi
17
+
18
+ # ─── Resolve ARKAOS_ROOT (validated — see arka_resolve_root in _lib) ────
19
+ # .repo-path can point at a purged npx cache; the shared resolver falls
20
+ # through to the ~/.arkaos/lib snapshot instead of exporting a dead root.
21
+ if command -v arka_resolve_root >/dev/null 2>&1; then
22
+ ARKAOS_ROOT="$(arka_resolve_root)"
23
+ elif [ -z "${ARKAOS_ROOT:-}" ]; then
24
+ # Legacy chain (pre-snapshot _lib deployment)
25
+ if [ -f "$HOME/.arkaos/.repo-path" ]; then
26
+ ARKAOS_ROOT=$(cat "$HOME/.arkaos/.repo-path")
27
+ elif [ -d "$HOME/.arkaos" ]; then
28
+ ARKAOS_ROOT="$HOME/.arkaos"
29
+ else
30
+ ARKAOS_ROOT="${ARKA_OS:-$HOME/.claude/skills/arkaos}"
31
+ fi
32
+ fi
33
+ export ARKAOS_ROOT
34
+
35
+ # ─── Degrade gracefully (fail open, same as before) ─────────────────────
36
+ if ! command -v "$ARKA_PY" >/dev/null 2>&1; then
37
+ exit 0
38
+ fi
39
+ # Self-root fallback: the wrapper ships next to its python entrypoint, so
40
+ # when ARKAOS_ROOT points at a pre-PR-6 install without core/hooks/, use
41
+ # the root this script lives in.
42
+ if [ ! -f "$ARKAOS_ROOT/core/hooks/subagent_stop.py" ]; then
43
+ _SELF_ROOT="$(cd "$(dirname "$0")/../.." 2>/dev/null && pwd)"
44
+ if [ -n "$_SELF_ROOT" ] && [ -f "$_SELF_ROOT/core/hooks/subagent_stop.py" ]; then
45
+ ARKAOS_ROOT="$_SELF_ROOT"
46
+ export ARKAOS_ROOT
47
+ else
48
+ exit 0
49
+ fi
50
+ fi
51
+
52
+ # ─── Single python process; stdin/stdout/stderr/exit-code pass through ──
53
+ # Interpreter resolution handled by the shared resolver (ARKA_PY): prefers
54
+ # the ArkaOS venv (has pyyaml/pydantic), falls back to a yaml-capable python3.
55
+ PYTHONPATH="$ARKAOS_ROOT${PYTHONPATH:+:$PYTHONPATH}" \
56
+ exec "$ARKA_PY" -m core.hooks.subagent_stop