arkaos 2.20.1 → 2.22.0

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 (268) hide show
  1. package/VERSION +1 -1
  2. package/arka/SKILL.md +16 -1
  3. package/arka/skills/costs/SKILL.md +62 -0
  4. package/arka/skills/flow/SKILL.md +14 -0
  5. package/arka/skills/forge/SKILL.md +14 -0
  6. package/config/hooks/post-tool-use.ps1 +80 -0
  7. package/config/hooks/post-tool-use.sh +47 -0
  8. package/config/hooks/pre-tool-use.ps1 +81 -8
  9. package/config/hooks/pre-tool-use.sh +81 -7
  10. package/config/hooks/stop.ps1 +79 -0
  11. package/config/hooks/stop.sh +88 -0
  12. package/config/hooks/user-prompt-submit.ps1 +50 -0
  13. package/config/hooks/user-prompt-submit.sh +24 -0
  14. package/core/cognition/__pycache__/auto_documentor.cpython-313.pyc +0 -0
  15. package/core/cognition/auto_documentor.py +433 -0
  16. package/core/jobs/__init__.py +14 -3
  17. package/core/jobs/__pycache__/__init__.cpython-313.pyc +0 -0
  18. package/core/jobs/__pycache__/auto_doc_worker.cpython-313.pyc +0 -0
  19. package/core/jobs/auto_doc_worker.py +257 -0
  20. package/core/obsidian/__init__.py +30 -3
  21. package/core/obsidian/__pycache__/__init__.cpython-313.pyc +0 -0
  22. package/core/obsidian/__pycache__/cataloger.cpython-313.pyc +0 -0
  23. package/core/obsidian/__pycache__/relator.cpython-313.pyc +0 -0
  24. package/core/obsidian/__pycache__/taxonomy.cpython-313.pyc +0 -0
  25. package/core/obsidian/cataloger.py +251 -0
  26. package/core/obsidian/relator.py +241 -0
  27. package/core/obsidian/taxonomy.py +100 -0
  28. package/core/runtime/__init__.py +22 -1
  29. package/core/runtime/__pycache__/__init__.cpython-313.pyc +0 -0
  30. package/core/runtime/__pycache__/base.cpython-313.pyc +0 -0
  31. package/core/runtime/__pycache__/claude_code.cpython-313.pyc +0 -0
  32. package/core/runtime/__pycache__/codex_cli.cpython-313.pyc +0 -0
  33. package/core/runtime/__pycache__/cursor.cpython-313.pyc +0 -0
  34. package/core/runtime/__pycache__/gemini_cli.cpython-313.pyc +0 -0
  35. package/core/runtime/__pycache__/llm_cost_telemetry.cpython-313.pyc +0 -0
  36. package/core/runtime/__pycache__/llm_cost_telemetry_cli.cpython-313.pyc +0 -0
  37. package/core/runtime/__pycache__/llm_provider.cpython-313.pyc +0 -0
  38. package/core/runtime/__pycache__/pricing.cpython-313.pyc +0 -0
  39. package/core/runtime/base.py +30 -1
  40. package/core/runtime/claude_code.py +68 -0
  41. package/core/runtime/codex_cli.py +33 -0
  42. package/core/runtime/cursor.py +19 -0
  43. package/core/runtime/gemini_cli.py +33 -0
  44. package/core/runtime/llm_cost_telemetry.py +306 -0
  45. package/core/runtime/llm_cost_telemetry_cli.py +138 -0
  46. package/core/runtime/llm_provider.py +382 -0
  47. package/core/runtime/pricing.py +85 -0
  48. package/core/synapse/__init__.py +8 -1
  49. package/core/synapse/__pycache__/__init__.cpython-313.pyc +0 -0
  50. package/core/synapse/__pycache__/engine.cpython-313.pyc +0 -0
  51. package/core/synapse/__pycache__/kb_cache.cpython-313.pyc +0 -0
  52. package/core/synapse/__pycache__/layers.cpython-313.pyc +0 -0
  53. package/core/synapse/engine.py +15 -1
  54. package/core/synapse/kb_cache.py +125 -6
  55. package/core/synapse/layers.py +296 -0
  56. package/core/workflow/__pycache__/flow_enforcer.cpython-313.pyc +0 -0
  57. package/core/workflow/__pycache__/kb_first_decider.cpython-313.pyc +0 -0
  58. package/core/workflow/__pycache__/marker_cache.cpython-313.pyc +0 -0
  59. package/core/workflow/__pycache__/research_gate.cpython-313.pyc +0 -0
  60. package/core/workflow/flow_enforcer.py +14 -3
  61. package/core/workflow/kb_first_decider.py +63 -0
  62. package/core/workflow/marker_cache.py +147 -0
  63. package/core/workflow/research_gate.py +301 -0
  64. package/departments/brand/SKILL.md +14 -0
  65. package/departments/brand/skills/archetype-finder/SKILL.md +14 -0
  66. package/departments/brand/skills/colors/SKILL.md +14 -0
  67. package/departments/brand/skills/design-system/SKILL.md +14 -0
  68. package/departments/brand/skills/identity-system/SKILL.md +14 -0
  69. package/departments/brand/skills/logo-brief/SKILL.md +14 -0
  70. package/departments/brand/skills/mockup-generate/SKILL.md +14 -0
  71. package/departments/brand/skills/naming-evaluate/SKILL.md +14 -0
  72. package/departments/brand/skills/positioning-statement/SKILL.md +14 -0
  73. package/departments/brand/skills/primal-audit/SKILL.md +14 -0
  74. package/departments/brand/skills/ux-audit/SKILL.md +14 -0
  75. package/departments/brand/skills/voice-guide/SKILL.md +14 -0
  76. package/departments/brand/skills/wireframe/SKILL.md +14 -0
  77. package/departments/community/SKILL.md +14 -0
  78. package/departments/community/skills/ai-community/SKILL.md +14 -0
  79. package/departments/community/skills/betting-setup/SKILL.md +14 -0
  80. package/departments/community/skills/business-model/SKILL.md +14 -0
  81. package/departments/community/skills/content-calendar/SKILL.md +14 -0
  82. package/departments/community/skills/events-plan/SKILL.md +14 -0
  83. package/departments/community/skills/gamification-design/SKILL.md +14 -0
  84. package/departments/community/skills/growth-plan/SKILL.md +14 -0
  85. package/departments/community/skills/metrics-track/SKILL.md +14 -0
  86. package/departments/community/skills/moderation/SKILL.md +14 -0
  87. package/departments/community/skills/monetize-plan/SKILL.md +14 -0
  88. package/departments/community/skills/niche-setup/SKILL.md +14 -0
  89. package/departments/community/skills/onboarding-flow/SKILL.md +14 -0
  90. package/departments/community/skills/platform-select/SKILL.md +14 -0
  91. package/departments/content/SKILL.md +14 -0
  92. package/departments/content/skills/ai-workflow/SKILL.md +14 -0
  93. package/departments/content/skills/analytics/SKILL.md +14 -0
  94. package/departments/content/skills/calendar/SKILL.md +14 -0
  95. package/departments/content/skills/content-system/SKILL.md +14 -0
  96. package/departments/content/skills/monetization-plan/SKILL.md +14 -0
  97. package/departments/content/skills/newsletter-write/SKILL.md +14 -0
  98. package/departments/content/skills/platform-optimize/SKILL.md +14 -0
  99. package/departments/content/skills/repurpose-plan/SKILL.md +14 -0
  100. package/departments/content/skills/script-structure/SKILL.md +14 -0
  101. package/departments/content/skills/short-form/SKILL.md +14 -0
  102. package/departments/content/skills/thumbnail-package/SKILL.md +14 -0
  103. package/departments/content/skills/viral-design/SKILL.md +14 -0
  104. package/departments/content/skills/youtube-strategy/SKILL.md +14 -0
  105. package/departments/dev/SKILL.md +14 -0
  106. package/departments/dev/skills/ai-assisted-dev/SKILL.md +14 -0
  107. package/departments/dev/skills/architecture-design/SKILL.md +14 -0
  108. package/departments/dev/skills/code-review/SKILL.md +14 -0
  109. package/departments/dev/skills/db-design/SKILL.md +14 -0
  110. package/departments/dev/skills/ddd-model/SKILL.md +14 -0
  111. package/departments/dev/skills/demo-gif/SKILL.md +14 -0
  112. package/departments/dev/skills/deploy/SKILL.md +14 -0
  113. package/departments/dev/skills/devops-pipeline/SKILL.md +14 -0
  114. package/departments/dev/skills/docs/SKILL.md +14 -0
  115. package/departments/dev/skills/mcp/SKILL.md +14 -0
  116. package/departments/dev/skills/performance-audit/SKILL.md +14 -0
  117. package/departments/dev/skills/refactor-plan/SKILL.md +14 -0
  118. package/departments/dev/skills/research/SKILL.md +14 -0
  119. package/departments/dev/skills/security-compliance/SKILL.md +14 -0
  120. package/departments/dev/skills/stack-check/SKILL.md +14 -0
  121. package/departments/ecom/SKILL.md +14 -0
  122. package/departments/ecom/skills/analytics/SKILL.md +14 -0
  123. package/departments/ecom/skills/browse-competitor/SKILL.md +14 -0
  124. package/departments/ecom/skills/cart-recovery/SKILL.md +14 -0
  125. package/departments/ecom/skills/cro-optimize/SKILL.md +14 -0
  126. package/departments/ecom/skills/customer-journey/SKILL.md +14 -0
  127. package/departments/ecom/skills/fulfillment-plan/SKILL.md +14 -0
  128. package/departments/ecom/skills/marketplace-manage/SKILL.md +14 -0
  129. package/departments/ecom/skills/pricing-strategy/SKILL.md +14 -0
  130. package/departments/ecom/skills/product-launch/SKILL.md +14 -0
  131. package/departments/ecom/skills/rfm-segment/SKILL.md +14 -0
  132. package/departments/ecom/skills/social-commerce/SKILL.md +14 -0
  133. package/departments/ecom/skills/store-audit/SKILL.md +14 -0
  134. package/departments/ecom/skills/subscription-model/SKILL.md +14 -0
  135. package/departments/finance/SKILL.md +14 -0
  136. package/departments/finance/skills/budget-plan/SKILL.md +14 -0
  137. package/departments/finance/skills/cashflow-forecast/SKILL.md +14 -0
  138. package/departments/finance/skills/ciso-advisor/SKILL.md +14 -0
  139. package/departments/finance/skills/financial-model/SKILL.md +14 -0
  140. package/departments/finance/skills/pitch-deck/SKILL.md +14 -0
  141. package/departments/finance/skills/scenario-analysis/SKILL.md +14 -0
  142. package/departments/finance/skills/unit-economics/SKILL.md +14 -0
  143. package/departments/finance/skills/valuation-model/SKILL.md +14 -0
  144. package/departments/kb/SKILL.md +14 -0
  145. package/departments/kb/skills/ai-research/SKILL.md +14 -0
  146. package/departments/kb/skills/competitive-intel/SKILL.md +14 -0
  147. package/departments/kb/skills/knowledge-review/SKILL.md +14 -0
  148. package/departments/kb/skills/learn-content/SKILL.md +14 -0
  149. package/departments/kb/skills/moc-create/SKILL.md +14 -0
  150. package/departments/kb/skills/persona-build/SKILL.md +14 -0
  151. package/departments/kb/skills/research-plan/SKILL.md +14 -0
  152. package/departments/kb/skills/search-kb/SKILL.md +14 -0
  153. package/departments/kb/skills/source-evaluate/SKILL.md +14 -0
  154. package/departments/kb/skills/taxonomy-manage/SKILL.md +14 -0
  155. package/departments/kb/skills/write-as-persona/SKILL.md +14 -0
  156. package/departments/landing/SKILL.md +14 -0
  157. package/departments/landing/skills/ab-test/SKILL.md +14 -0
  158. package/departments/landing/skills/affiliate-bridge/SKILL.md +14 -0
  159. package/departments/landing/skills/awareness-diagnose/SKILL.md +14 -0
  160. package/departments/landing/skills/email-sequence/SKILL.md +14 -0
  161. package/departments/landing/skills/funnel-metrics/SKILL.md +14 -0
  162. package/departments/landing/skills/headline-write/SKILL.md +14 -0
  163. package/departments/landing/skills/launch-sequence/SKILL.md +14 -0
  164. package/departments/landing/skills/offer-create/SKILL.md +14 -0
  165. package/departments/landing/skills/optimize-page/SKILL.md +14 -0
  166. package/departments/landing/skills/page-architect/SKILL.md +14 -0
  167. package/departments/landing/skills/persuasion-apply/SKILL.md +14 -0
  168. package/departments/landing/skills/webinar-funnel/SKILL.md +14 -0
  169. package/departments/leadership/SKILL.md +14 -0
  170. package/departments/leadership/skills/change-manage/SKILL.md +14 -0
  171. package/departments/leadership/skills/conflict-resolve/SKILL.md +14 -0
  172. package/departments/leadership/skills/culture-audit/SKILL.md +14 -0
  173. package/departments/leadership/skills/delegation-matrix/SKILL.md +14 -0
  174. package/departments/leadership/skills/disc-assess/SKILL.md +14 -0
  175. package/departments/leadership/skills/feedback-give/SKILL.md +14 -0
  176. package/departments/leadership/skills/hiring-plan/SKILL.md +14 -0
  177. package/departments/leadership/skills/performance-review/SKILL.md +14 -0
  178. package/departments/marketing/SKILL.md +14 -0
  179. package/departments/marketing/skills/ab-test/SKILL.md +14 -0
  180. package/departments/marketing/skills/analytics-report/SKILL.md +14 -0
  181. package/departments/marketing/skills/audience-segment/SKILL.md +14 -0
  182. package/departments/marketing/skills/calendar-plan/SKILL.md +14 -0
  183. package/departments/marketing/skills/competitor-analysis/SKILL.md +14 -0
  184. package/departments/marketing/skills/content-audit/SKILL.md +14 -0
  185. package/departments/marketing/skills/email-sequence/SKILL.md +14 -0
  186. package/departments/marketing/skills/growth-loop/SKILL.md +14 -0
  187. package/departments/marketing/skills/marketing-automation/SKILL.md +14 -0
  188. package/departments/marketing/skills/paid-campaign/SKILL.md +14 -0
  189. package/departments/marketing/skills/programmatic-seo/SKILL.md +14 -0
  190. package/departments/marketing/skills/seo-audit/SKILL.md +14 -0
  191. package/departments/marketing/skills/social-strategy/SKILL.md +14 -0
  192. package/departments/ops/SKILL.md +14 -0
  193. package/departments/ops/skills/bottleneck-find/SKILL.md +14 -0
  194. package/departments/ops/skills/dashboard-build/SKILL.md +14 -0
  195. package/departments/ops/skills/gdpr-compliance/SKILL.md +14 -0
  196. package/departments/ops/skills/gtd-setup/SKILL.md +14 -0
  197. package/departments/ops/skills/integration-design/SKILL.md +14 -0
  198. package/departments/ops/skills/iso27001/SKILL.md +14 -0
  199. package/departments/ops/skills/lean-audit/SKILL.md +14 -0
  200. package/departments/ops/skills/metrics-dashboard/SKILL.md +14 -0
  201. package/departments/ops/skills/n8n-flow/SKILL.md +14 -0
  202. package/departments/ops/skills/quality-management/SKILL.md +14 -0
  203. package/departments/ops/skills/risk-management/SKILL.md +14 -0
  204. package/departments/ops/skills/soc2-compliance/SKILL.md +14 -0
  205. package/departments/ops/skills/sop-create/SKILL.md +14 -0
  206. package/departments/ops/skills/workflow-automate/SKILL.md +14 -0
  207. package/departments/ops/skills/zapier-flow/SKILL.md +14 -0
  208. package/departments/org/SKILL.md +14 -0
  209. package/departments/org/skills/compensation-plan/SKILL.md +14 -0
  210. package/departments/org/skills/culture-define/SKILL.md +14 -0
  211. package/departments/org/skills/decision-framework/SKILL.md +14 -0
  212. package/departments/org/skills/hiring-plan/SKILL.md +14 -0
  213. package/departments/org/skills/meeting-optimize/SKILL.md +14 -0
  214. package/departments/org/skills/onboarding-design/SKILL.md +14 -0
  215. package/departments/org/skills/org-design/SKILL.md +14 -0
  216. package/departments/org/skills/remote-setup/SKILL.md +14 -0
  217. package/departments/org/skills/sop-process/SKILL.md +14 -0
  218. package/departments/org/skills/team-assess/SKILL.md +14 -0
  219. package/departments/pm/SKILL.md +14 -0
  220. package/departments/pm/skills/backlog-groom/SKILL.md +14 -0
  221. package/departments/pm/skills/discovery-plan/SKILL.md +14 -0
  222. package/departments/pm/skills/estimate-forecast/SKILL.md +14 -0
  223. package/departments/pm/skills/impact-map/SKILL.md +14 -0
  224. package/departments/pm/skills/kanban-setup/SKILL.md +14 -0
  225. package/departments/pm/skills/risk-register/SKILL.md +14 -0
  226. package/departments/pm/skills/roadmap-build/SKILL.md +14 -0
  227. package/departments/pm/skills/sprint-plan/SKILL.md +14 -0
  228. package/departments/pm/skills/stakeholder-map/SKILL.md +14 -0
  229. package/departments/pm/skills/standup-run/SKILL.md +14 -0
  230. package/departments/pm/skills/story-write/SKILL.md +14 -0
  231. package/departments/saas/SKILL.md +14 -0
  232. package/departments/saas/skills/benchmark-compare/SKILL.md +14 -0
  233. package/departments/saas/skills/churn-analysis/SKILL.md +14 -0
  234. package/departments/saas/skills/customer-success/SKILL.md +14 -0
  235. package/departments/saas/skills/growth-plan/SKILL.md +14 -0
  236. package/departments/saas/skills/gtm-strategy/SKILL.md +14 -0
  237. package/departments/saas/skills/launch-execute/SKILL.md +14 -0
  238. package/departments/saas/skills/metrics-dashboard/SKILL.md +14 -0
  239. package/departments/saas/skills/micro-saas-stack/SKILL.md +14 -0
  240. package/departments/saas/skills/mvp-build/SKILL.md +14 -0
  241. package/departments/saas/skills/niche-evaluate/SKILL.md +14 -0
  242. package/departments/saas/skills/onboarding-optimize/SKILL.md +14 -0
  243. package/departments/saas/skills/plg-setup/SKILL.md +14 -0
  244. package/departments/saas/skills/pricing-strategy/SKILL.md +14 -0
  245. package/departments/saas/skills/validate-idea/SKILL.md +14 -0
  246. package/departments/sales/SKILL.md +14 -0
  247. package/departments/sales/skills/challenger-sell/SKILL.md +14 -0
  248. package/departments/sales/skills/deal-qualify/SKILL.md +14 -0
  249. package/departments/sales/skills/discovery-call/SKILL.md +14 -0
  250. package/departments/sales/skills/forecast-revenue/SKILL.md +14 -0
  251. package/departments/sales/skills/negotiate-plan/SKILL.md +14 -0
  252. package/departments/sales/skills/objection-handle/SKILL.md +14 -0
  253. package/departments/sales/skills/pipeline-manage/SKILL.md +14 -0
  254. package/departments/sales/skills/pricing-negotiate/SKILL.md +14 -0
  255. package/departments/strategy/SKILL.md +14 -0
  256. package/departments/strategy/skills/blue-ocean/SKILL.md +14 -0
  257. package/departments/strategy/skills/bmc/SKILL.md +14 -0
  258. package/departments/strategy/skills/board-advisor/SKILL.md +14 -0
  259. package/departments/strategy/skills/cto-advisor/SKILL.md +14 -0
  260. package/departments/strategy/skills/extract-data/SKILL.md +14 -0
  261. package/departments/strategy/skills/five-forces/SKILL.md +14 -0
  262. package/departments/strategy/skills/growth-strategy/SKILL.md +14 -0
  263. package/departments/strategy/skills/moat-analysis/SKILL.md +14 -0
  264. package/departments/strategy/skills/position/SKILL.md +14 -0
  265. package/departments/strategy/skills/scenario-plan/SKILL.md +14 -0
  266. package/package.json +3 -3
  267. package/pyproject.toml +1 -1
  268. package/scripts/migrate_skills_kb_first.py +137 -0
package/VERSION CHANGED
@@ -1 +1 @@
1
- 2.20.1
1
+ 2.22.0
package/arka/SKILL.md CHANGED
@@ -7,6 +7,20 @@ description: >
7
7
  allowed-tools: [Read, Write, Edit, Bash, Grep, Glob, Agent, WebFetch, WebSearch]
8
8
  ---
9
9
 
10
+ <!-- arka:kb-first-prefix begin -->
11
+ ## KB-First Research (non-negotiable)
12
+
13
+ Before any external research (Context7, WebSearch, WebFetch, Firecrawl):
14
+
15
+ 1. Call `mcp__obsidian__search_notes` on the query first.
16
+ 2. Cite relevant hits with `[[wikilinks]]` or explicitly declare a KB gap.
17
+ 3. Only after (1) and (2) may external tools run.
18
+
19
+ The Synapse L2.5 layer pre-injects top KB matches on every user prompt;
20
+ treat them as your default source. External research supplements, it
21
+ does not replace the vault.
22
+ <!-- arka:kb-first-prefix end -->
23
+
10
24
  # ArkaOS v2 — Main Orchestrator
11
25
 
12
26
  > **The Operating System for AI Agent Teams**
@@ -95,7 +109,8 @@ violation (squad-routing, arka-supremacy, spec-driven, mandatory-qa).
95
109
 
96
110
  | Command | Description |
97
111
  |---------|-------------|
98
- | `/arka status` | System status (version, departments, agents, active projects) |
112
+ | `/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")`. |
113
+ | `/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>`. |
99
114
  | `/arka standup` | Daily standup (projects, priorities, blockers, updates) |
100
115
  | `/arka monitor` | System health monitoring |
101
116
  | `/arka onboard <path>` | Onboard an existing project into ArkaOS |
@@ -0,0 +1,62 @@
1
+ ---
2
+ name: arka-costs
3
+ description: >
4
+ LLM cost visibility — aggregates `~/.arkaos/telemetry/llm-cost.jsonl` by
5
+ day/week/month/all, breaks down by provider/model/session, surfaces top
6
+ expensive sessions and cache hit rate. Visibility-only per ADR-011;
7
+ never imposes hard caps.
8
+ allowed-tools: [Bash, Read]
9
+ ---
10
+
11
+ # /arka costs — LLM cost visibility
12
+
13
+ Aggregates runtime-agnostic LLM call telemetry written by
14
+ `core/runtime/llm_cost_telemetry.record_cost`. Per ADR-011, token
15
+ budgets are **informational, not restrictive** — this command only
16
+ surfaces usage and emits soft advisories. It never blocks a call.
17
+
18
+ ## Usage
19
+
20
+ | Command | What it shows |
21
+ | --- | --- |
22
+ | `/arka costs` | Today (UTC midnight → now) |
23
+ | `/arka costs today` | Same as above |
24
+ | `/arka costs week` | Rolling last 7 days |
25
+ | `/arka costs month` | Rolling last 30 days |
26
+ | `/arka costs all` | Entire history in the JSONL |
27
+ | `/arka costs sessions` | Top 10 most expensive sessions (all time) |
28
+
29
+ ## Output
30
+
31
+ - Total cost (USD, `n/a` when all entries are unpriced models)
32
+ - Total tokens in / out, plus cached tokens
33
+ - Cache hit rate (`cached / tokens_in`)
34
+ - Breakdown by provider
35
+ - Breakdown by model (`<unknown>` bucket for calls with no model)
36
+ - Top 10 sessions sorted by cost
37
+ - Advisories — a soft line per session that crossed the
38
+ `advisory_threshold_usd` (default $5 per session)
39
+
40
+ ## Implementation
41
+
42
+ This skill shells out to the Python CLI:
43
+
44
+ ```bash
45
+ python -m core.runtime.llm_cost_telemetry_cli <period>
46
+ ```
47
+
48
+ Source:
49
+ - `core/runtime/llm_cost_telemetry.py` — `summarise`, `list_expensive_sessions`
50
+ - `core/runtime/llm_cost_telemetry_cli.py` — markdown renderer
51
+
52
+ ## Data source
53
+
54
+ `~/.arkaos/telemetry/llm-cost.jsonl` (override with `ARKA_LLM_COST_PATH`).
55
+ One JSONL line per LLM call, written by every provider adapter.
56
+ Malformed lines are skipped and counted, never raised.
57
+
58
+ ## Non-negotiables
59
+
60
+ 1. Read-only. This skill never edits state.
61
+ 2. No hard budget caps — advisories are strings, not errors.
62
+ 3. No external dependencies; stdlib only.
@@ -7,6 +7,20 @@ description: >
7
7
  allowed-tools: [Read, Write, Edit, Bash, Grep, Glob, Agent, WebFetch, WebSearch]
8
8
  ---
9
9
 
10
+ <!-- arka:kb-first-prefix begin -->
11
+ ## KB-First Research (non-negotiable)
12
+
13
+ Before any external research (Context7, WebSearch, WebFetch, Firecrawl):
14
+
15
+ 1. Call `mcp__obsidian__search_notes` on the query first.
16
+ 2. Cite relevant hits with `[[wikilinks]]` or explicitly declare a KB gap.
17
+ 3. Only after (1) and (2) may external tools run.
18
+
19
+ The Synapse L2.5 layer pre-injects top KB matches on every user prompt;
20
+ treat them as your default source. External research supplements, it
21
+ does not replace the vault.
22
+ <!-- arka:kb-first-prefix end -->
23
+
10
24
  # ArkaOS — Mandatory Workflow
11
25
 
12
26
  > This flow runs on **every** user request inside an ArkaOS-managed context.
@@ -8,6 +8,20 @@ description: >
8
8
  allowed-tools: [Read, Write, Edit, Bash, Grep, Glob, Agent, WebFetch, WebSearch]
9
9
  ---
10
10
 
11
+ <!-- arka:kb-first-prefix begin -->
12
+ ## KB-First Research (non-negotiable)
13
+
14
+ Before any external research (Context7, WebSearch, WebFetch, Firecrawl):
15
+
16
+ 1. Call `mcp__obsidian__search_notes` on the query first.
17
+ 2. Cite relevant hits with `[[wikilinks]]` or explicitly declare a KB gap.
18
+ 3. Only after (1) and (2) may external tools run.
19
+
20
+ The Synapse L2.5 layer pre-injects top KB matches on every user prompt;
21
+ treat them as your default source. External research supplements, it
22
+ does not replace the vault.
23
+ <!-- arka:kb-first-prefix end -->
24
+
11
25
  # The Forge — ArkaOS Intelligent Planning Engine
12
26
 
13
27
  > **Engine:** `core/forge/` | **Plans stored:** `~/.arkaos/plans/` | **Obsidian:** `ArkaOS/Forge/`
@@ -120,6 +120,86 @@ if ($shouldProcessGotchas -and $null -ne $payload) {
120
120
  $exitCode = if ($null -ne $payload.exit_code) { [string]$payload.exit_code } else { '0' }
121
121
  $cwd = if ($null -ne $payload.cwd) { [string]$payload.cwd } else { '' }
122
122
 
123
+ # --- Flow marker cache write (v2 ALLOW accelerator) ------------------
124
+ # Mirror of the bash hook: detect [arka:routing] or [arka:trivial] in
125
+ # $payload.assistant_message and persist via core.workflow.marker_cache.
126
+ # Non-blocking — any failure is swallowed.
127
+ try {
128
+ $sessionIdPtu = if ($null -ne $payload.session_id) { [string]$payload.session_id } else { '' }
129
+ $assistantMsg = if ($null -ne $payload.assistant_message) { [string]$payload.assistant_message } else { '' }
130
+ if ($sessionIdPtu -and $assistantMsg) {
131
+ $routingRe = [regex]'(?i)\[arka:routing\]\s*([A-Za-z_-]+)\s*->\s*([A-Za-z_-]+)'
132
+ $trivialRe = [regex]'(?i)\[arka:trivial\]\s*\S+'
133
+ $markerKind = ''
134
+ $markerDept = ''
135
+ $markerLead = ''
136
+ $routingMatch = $routingRe.Match($assistantMsg)
137
+ if ($routingMatch.Success) {
138
+ $markerKind = 'routing'
139
+ $markerDept = $routingMatch.Groups[1].Value
140
+ $markerLead = $routingMatch.Groups[2].Value
141
+ } elseif ($trivialRe.IsMatch($assistantMsg)) {
142
+ $markerKind = 'trivial'
143
+ }
144
+ if ($markerKind) {
145
+ $arkaosRootPtu = $env:ARKAOS_ROOT
146
+ if (-not $arkaosRootPtu) {
147
+ $repoPathFile = Join-Path $env:USERPROFILE '.arkaos\.repo-path'
148
+ if (Test-Path -LiteralPath $repoPathFile) {
149
+ try {
150
+ $arkaosRootPtu = (Get-Content -Raw -LiteralPath $repoPathFile -Encoding UTF8).Trim()
151
+ } catch { }
152
+ }
153
+ }
154
+ if (-not $arkaosRootPtu) {
155
+ $arkaosRootPtu = Join-Path $env:USERPROFILE '.arkaos'
156
+ }
157
+ # Locate Python (venv-first, then system).
158
+ $pythonForMarker = $null
159
+ $venvPy = Join-Path $env:USERPROFILE '.arkaos\venv\Scripts\python.exe'
160
+ if (Test-Path -LiteralPath $venvPy) {
161
+ $pythonForMarker = $venvPy
162
+ } else {
163
+ foreach ($cmd in 'python3','python','py') {
164
+ $resolved = Get-Command $cmd -ErrorAction SilentlyContinue
165
+ if ($resolved) { $pythonForMarker = $resolved.Source; break }
166
+ }
167
+ }
168
+ if ($pythonForMarker) {
169
+ $pyCode = @"
170
+ import os
171
+ try:
172
+ from core.workflow.marker_cache import write_marker
173
+ write_marker(
174
+ os.environ.get('SESSION_ID_PTU', ''),
175
+ os.environ.get('MARKER_KIND', ''),
176
+ os.environ.get('MARKER_DEPT', ''),
177
+ os.environ.get('MARKER_LEAD', ''),
178
+ )
179
+ except Exception:
180
+ pass
181
+ "@
182
+ $psi = New-Object System.Diagnostics.ProcessStartInfo
183
+ $psi.FileName = $pythonForMarker
184
+ $psi.Arguments = "-c `"$($pyCode -replace '"','\"' -replace "`r?`n",'; ')`""
185
+ $psi.UseShellExecute = $false
186
+ $psi.CreateNoWindow = $true
187
+ $psi.RedirectStandardOutput = $true
188
+ $psi.RedirectStandardError = $true
189
+ [void]$psi.EnvironmentVariables.Add('SESSION_ID_PTU', $sessionIdPtu)
190
+ [void]$psi.EnvironmentVariables.Add('MARKER_KIND', $markerKind)
191
+ [void]$psi.EnvironmentVariables.Add('MARKER_DEPT', $markerDept)
192
+ [void]$psi.EnvironmentVariables.Add('MARKER_LEAD', $markerLead)
193
+ [void]$psi.EnvironmentVariables.Add('PYTHONPATH', $arkaosRootPtu)
194
+ try {
195
+ $proc = [System.Diagnostics.Process]::Start($psi)
196
+ if (-not $proc.WaitForExit(1500)) { try { $proc.Kill() } catch { } }
197
+ } catch { }
198
+ }
199
+ }
200
+ }
201
+ } catch { }
202
+
123
203
  # ─── Only process when there is actually an error ─────────────────
124
204
  $errorPattern = '(?i)(error:|fatal:|exception:|failed|ENOENT|EACCES|EPERM|panic:)'
125
205
  if ($exitCode -eq '0' -or [string]::IsNullOrEmpty($exitCode)) {
@@ -24,6 +24,53 @@ TOOL_NAME=$(echo "$input" | jq -r '.tool_name // ""' 2>/dev/null)
24
24
  TOOL_OUTPUT=$(echo "$input" | jq -r '.tool_output // ""' 2>/dev/null)
25
25
  EXIT_CODE=$(echo "$input" | jq -r '.exit_code // "0"' 2>/dev/null)
26
26
  CWD=$(echo "$input" | jq -r '.cwd // ""' 2>/dev/null)
27
+ SESSION_ID_PTU=$(echo "$input" | jq -r '.session_id // ""' 2>/dev/null)
28
+ ASSISTANT_MSG=$(echo "$input" | jq -r '.assistant_message // ""' 2>/dev/null)
29
+
30
+ # ─── Flow marker cache write (v2 — turn-scoped ALLOW accelerator) ───────
31
+ # Detect [arka:routing] or [arka:trivial] in the assistant message that
32
+ # accompanied this tool call, and persist it so the PreToolUse enforcer
33
+ # can short-circuit the transcript scan for the rest of the turn.
34
+ # Never blocks the hook — all failures are swallowed.
35
+ if [ -n "$SESSION_ID_PTU" ] && [ -n "$ASSISTANT_MSG" ] && command -v python3 &>/dev/null; then
36
+ _MARKER_KIND=""
37
+ _MARKER_DEPT=""
38
+ _MARKER_LEAD=""
39
+ if printf '%s' "$ASSISTANT_MSG" | grep -qiE '\[arka:routing\][[:space:]]*[A-Za-z_-]+[[:space:]]*->[[:space:]]*[A-Za-z_-]+'; then
40
+ _MARKER_KIND="routing"
41
+ _ROUTE_LINE=$(printf '%s' "$ASSISTANT_MSG" | grep -iEo '\[arka:routing\][[:space:]]*[A-Za-z_-]+[[:space:]]*->[[:space:]]*[A-Za-z_-]+' | head -1)
42
+ _MARKER_DEPT=$(printf '%s' "$_ROUTE_LINE" | sed -E 's/.*\[arka:routing\][[:space:]]*([A-Za-z_-]+).*/\1/')
43
+ _MARKER_LEAD=$(printf '%s' "$_ROUTE_LINE" | sed -E 's/.*->[[:space:]]*([A-Za-z_-]+).*/\1/')
44
+ elif printf '%s' "$ASSISTANT_MSG" | grep -qiE '\[arka:trivial\][[:space:]]*\S+'; then
45
+ _MARKER_KIND="trivial"
46
+ fi
47
+
48
+ if [ -n "$_MARKER_KIND" ]; then
49
+ _MARKER_ROOT="${ARKAOS_ROOT:-}"
50
+ if [ -z "$_MARKER_ROOT" ] && [ -f "$HOME/.arkaos/.repo-path" ]; then
51
+ _MARKER_ROOT=$(cat "$HOME/.arkaos/.repo-path" 2>/dev/null)
52
+ fi
53
+ [ -z "$_MARKER_ROOT" ] && _MARKER_ROOT="$HOME/.arkaos"
54
+ SESSION_ID_PTU="$SESSION_ID_PTU" \
55
+ MARKER_KIND="$_MARKER_KIND" \
56
+ MARKER_DEPT="$_MARKER_DEPT" \
57
+ MARKER_LEAD="$_MARKER_LEAD" \
58
+ PYTHONPATH="$_MARKER_ROOT" \
59
+ python3 -c "
60
+ import os, sys
61
+ try:
62
+ from core.workflow.marker_cache import write_marker
63
+ write_marker(
64
+ os.environ.get('SESSION_ID_PTU', ''),
65
+ os.environ.get('MARKER_KIND', ''),
66
+ os.environ.get('MARKER_DEPT', ''),
67
+ os.environ.get('MARKER_LEAD', ''),
68
+ )
69
+ except Exception:
70
+ pass
71
+ " 2>/dev/null || true
72
+ fi
73
+ fi
27
74
 
28
75
  # Only process if there was an error
29
76
  if [ "$EXIT_CODE" = "0" ] || [ -z "$EXIT_CODE" ]; then
@@ -27,11 +27,6 @@ $transcriptPath = [string]$inp.transcript_path
27
27
  $sessionId = [string]$inp.session_id
28
28
  $cwd = [string]$inp.cwd
29
29
 
30
- # --- Fast allow: not a gated tool ---
31
- if ($toolName -ne "Write" -and $toolName -ne "Edit" -and $toolName -ne "MultiEdit") {
32
- exit 0
33
- }
34
-
35
30
  # --- Resolve ARKAOS_ROOT ---
36
31
  if ([string]::IsNullOrWhiteSpace($env:ARKAOS_ROOT)) {
37
32
  $repoPathFile = Join-Path $HOME ".arkaos/.repo-path"
@@ -44,13 +39,91 @@ if ([string]::IsNullOrWhiteSpace($env:ARKAOS_ROOT)) {
44
39
  }
45
40
  }
46
41
 
47
- $enforcerPy = Join-Path $env:ARKAOS_ROOT "core/workflow/flow_enforcer.py"
48
- if (-not (Test-Path $enforcerPy)) { exit 0 }
49
-
50
42
  $python = Get-Command python3 -ErrorAction SilentlyContinue
51
43
  if (-not $python) { $python = Get-Command python -ErrorAction SilentlyContinue }
52
44
  if (-not $python) { exit 0 }
53
45
 
46
+ # --- KB-first gate (Task #6, runs before flow-marker gate) ---
47
+ $queryHint = ""
48
+ if ($inp.tool_input) {
49
+ if ($inp.tool_input.query) { $queryHint = [string]$inp.tool_input.query }
50
+ elseif ($inp.tool_input.prompt) { $queryHint = [string]$inp.tool_input.prompt }
51
+ elseif ($inp.tool_input.url) { $queryHint = [string]$inp.tool_input.url }
52
+ if ($queryHint.Length -gt 500) { $queryHint = $queryHint.Substring(0, 500) }
53
+ }
54
+
55
+ $researchGatePy = Join-Path $env:ARKAOS_ROOT "core/workflow/research_gate.py"
56
+ if (Test-Path $researchGatePy) {
57
+ $env:TOOL_NAME = $toolName
58
+ $env:SESSION_ID = $sessionId
59
+ $env:QUERY_HINT = $queryHint
60
+
61
+ $kbScript = @'
62
+ import json
63
+ import os
64
+ import sys
65
+
66
+ sys.path.insert(0, os.environ["ARKAOS_ROOT"])
67
+ try:
68
+ from core.workflow.research_gate import evaluate_research_gate, record_telemetry
69
+ except Exception:
70
+ print(json.dumps({"allow": True, "nudge": False, "reason": "kb-gate-import-failed"}))
71
+ sys.exit(0)
72
+
73
+ decision = evaluate_research_gate(
74
+ tool_name=os.environ.get("TOOL_NAME", ""),
75
+ session_id=os.environ.get("SESSION_ID", ""),
76
+ query=os.environ.get("QUERY_HINT", ""),
77
+ )
78
+ try:
79
+ record_telemetry(
80
+ session_id=os.environ.get("SESSION_ID", ""),
81
+ tool=os.environ.get("TOOL_NAME", ""),
82
+ decision=decision,
83
+ )
84
+ except Exception:
85
+ pass
86
+ print(json.dumps({
87
+ "allow": decision.allow,
88
+ "nudge": decision.nudge,
89
+ "reason": decision.reason,
90
+ "stderr_msg": decision.to_stderr_message(),
91
+ }))
92
+ '@
93
+
94
+ $kbDecisionJson = $kbScript | & $python.Source -
95
+ if (-not [string]::IsNullOrWhiteSpace($kbDecisionJson)) {
96
+ try {
97
+ $kbDecision = $kbDecisionJson | ConvertFrom-Json
98
+ } catch { $kbDecision = $null }
99
+
100
+ if ($kbDecision -and -not $kbDecision.allow) {
101
+ [Console]::Error.WriteLine($kbDecision.stderr_msg)
102
+ $denyOut = @{
103
+ hookSpecificOutput = @{
104
+ hookEventName = "PreToolUse"
105
+ permissionDecision = "deny"
106
+ permissionDecisionReason = $kbDecision.stderr_msg
107
+ }
108
+ } | ConvertTo-Json -Compress -Depth 5
109
+ Write-Output $denyOut
110
+ exit 2
111
+ }
112
+
113
+ if ($kbDecision -and $kbDecision.nudge -and -not [string]::IsNullOrWhiteSpace($kbDecision.stderr_msg)) {
114
+ [Console]::Error.WriteLine($kbDecision.stderr_msg)
115
+ }
116
+ }
117
+ }
118
+
119
+ # --- Fast allow: not a flow-gated tool ---
120
+ if ($toolName -ne "Write" -and $toolName -ne "Edit" -and $toolName -ne "MultiEdit") {
121
+ exit 0
122
+ }
123
+
124
+ $enforcerPy = Join-Path $env:ARKAOS_ROOT "core/workflow/flow_enforcer.py"
125
+ if (-not (Test-Path $enforcerPy)) { exit 0 }
126
+
54
127
  # --- Delegate to Python enforcer ---
55
128
  $env:TOOL_NAME = $toolName
56
129
  $env:TRANSCRIPT_PATH = $transcriptPath
@@ -31,12 +31,6 @@ if command -v jq &>/dev/null; then
31
31
  CWD=$(echo "$input" | jq -r '.cwd // ""' 2>/dev/null)
32
32
  fi
33
33
 
34
- # ─── Fast allow: not a gated tool
35
- case "$TOOL_NAME" in
36
- Write|Edit|MultiEdit) ;;
37
- *) exit 0 ;;
38
- esac
39
-
40
34
  # ─── Resolve ARKAOS_ROOT (same rules as user-prompt-submit.sh) ──────────
41
35
  if [ -z "${ARKAOS_ROOT:-}" ]; then
42
36
  if [ -f "$HOME/.arkaos/.repo-path" ]; then
@@ -48,10 +42,90 @@ if [ -z "${ARKAOS_ROOT:-}" ]; then
48
42
  fi
49
43
  fi
50
44
 
51
- # ─── Degrade gracefully if Python or module is unavailable ──────────────
45
+ # ─── Degrade gracefully if Python is unavailable ────────────────────────
52
46
  if ! command -v python3 &>/dev/null; then
53
47
  exit 0
54
48
  fi
49
+
50
+ # ─── KB-first gate (Task #6, independent from flow_enforcer) ────────────
51
+ # Runs BEFORE the flow-marker gate so that external research tools are
52
+ # always KB-checked regardless of whether the tool is Write/Edit/MultiEdit.
53
+ # Extract the user query (if present) to feed the nudge generator.
54
+ QUERY_HINT=""
55
+ if command -v jq &>/dev/null; then
56
+ QUERY_HINT=$(echo "$input" | jq -r '.tool_input.query // .tool_input.prompt // .tool_input.url // ""' 2>/dev/null | head -c 500)
57
+ fi
58
+
59
+ if [ -f "$ARKAOS_ROOT/core/workflow/research_gate.py" ]; then
60
+ KB_DECISION_JSON=$(TOOL_NAME="$TOOL_NAME" \
61
+ SESSION_ID="$SESSION_ID" \
62
+ QUERY_HINT="$QUERY_HINT" \
63
+ ARKAOS_ROOT="$ARKAOS_ROOT" \
64
+ python3 - <<'PY' 2>/dev/null
65
+ import json
66
+ import os
67
+ import sys
68
+
69
+ sys.path.insert(0, os.environ["ARKAOS_ROOT"])
70
+ try:
71
+ from core.workflow.research_gate import evaluate_research_gate, record_telemetry
72
+ except Exception:
73
+ print(json.dumps({"allow": True, "nudge": False, "reason": "kb-gate-import-failed"}))
74
+ sys.exit(0)
75
+
76
+ decision = evaluate_research_gate(
77
+ tool_name=os.environ.get("TOOL_NAME", ""),
78
+ session_id=os.environ.get("SESSION_ID", ""),
79
+ query=os.environ.get("QUERY_HINT", ""),
80
+ )
81
+ try:
82
+ record_telemetry(
83
+ session_id=os.environ.get("SESSION_ID", ""),
84
+ tool=os.environ.get("TOOL_NAME", ""),
85
+ decision=decision,
86
+ )
87
+ except Exception:
88
+ pass
89
+ print(json.dumps({
90
+ "allow": decision.allow,
91
+ "nudge": decision.nudge,
92
+ "reason": decision.reason,
93
+ "stderr_msg": decision.to_stderr_message(),
94
+ }))
95
+ PY
96
+ )
97
+
98
+ if [ -n "$KB_DECISION_JSON" ]; then
99
+ KB_ALLOW=$(echo "$KB_DECISION_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('allow', True))" 2>/dev/null)
100
+ KB_NUDGE=$(echo "$KB_DECISION_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('nudge', False))" 2>/dev/null)
101
+ KB_STDERR=$(echo "$KB_DECISION_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('stderr_msg',''))" 2>/dev/null)
102
+
103
+ if [ "$KB_ALLOW" != "True" ] && [ "$KB_ALLOW" != "true" ]; then
104
+ echo "$KB_STDERR" >&2
105
+ STDERR_MSG="$KB_STDERR" python3 - <<'PY'
106
+ import json, os
107
+ print(json.dumps({"hookSpecificOutput": {
108
+ "hookEventName": "PreToolUse",
109
+ "permissionDecision": "deny",
110
+ "permissionDecisionReason": os.environ.get("STDERR_MSG", ""),
111
+ }}))
112
+ PY
113
+ exit 2
114
+ fi
115
+
116
+ # Nudge path: emit advisory to stderr, continue to flow-marker gate.
117
+ if [ "$KB_NUDGE" = "True" ] || [ "$KB_NUDGE" = "true" ]; then
118
+ [ -n "$KB_STDERR" ] && echo "$KB_STDERR" >&2
119
+ fi
120
+ fi
121
+ fi
122
+
123
+ # ─── Fast allow: not a flow-gated tool (Write/Edit/MultiEdit) ───────────
124
+ case "$TOOL_NAME" in
125
+ Write|Edit|MultiEdit) ;;
126
+ *) exit 0 ;;
127
+ esac
128
+
55
129
  if [ ! -f "$ARKAOS_ROOT/core/workflow/flow_enforcer.py" ]; then
56
130
  exit 0
57
131
  fi
@@ -104,6 +104,85 @@ except Exception:
104
104
 
105
105
  $pyScript | & $python.Source - | Out-Null
106
106
 
107
+ # ─── Auto-documentor enqueue (fire-and-forget) ─────────────────────────
108
+ # Queues a background job when classifier flagged flow, QG approved, and
109
+ # external research happened. Never blocks the Stop hook.
110
+ $env:SESSION_ID_VAL = $sessionId
111
+ $env:TRANSCRIPT_PATH_VAL = $transcriptPath
112
+ $env:CWD_VAL = $cwd
113
+ $env:ARKAOS_ROOT = $env:ARKAOS_ROOT
114
+
115
+ $autoDocScript = @'
116
+ import json
117
+ import os
118
+ import re
119
+ import sys
120
+ from pathlib import Path
121
+
122
+ sys.path.insert(0, os.environ.get("ARKAOS_ROOT", ""))
123
+ try:
124
+ from core.jobs.auto_doc_worker import enqueue_job
125
+ from core.workflow.flow_enforcer import _load_last_assistant_messages
126
+ except Exception:
127
+ sys.exit(0)
128
+
129
+ session_id = os.environ.get("SESSION_ID_VAL", "")
130
+ transcript_path = os.environ.get("TRANSCRIPT_PATH_VAL", "")
131
+ if not session_id or not transcript_path:
132
+ sys.exit(0)
133
+
134
+ last = ""
135
+ try:
136
+ msgs = _load_last_assistant_messages(transcript_path, n=1)
137
+ last = msgs[-1] if msgs else ""
138
+ except Exception:
139
+ last = ""
140
+
141
+ qg_approved = bool(re.search(r"\[arka:qg:approved\]", last, re.IGNORECASE))
142
+ if not qg_approved:
143
+ qg_log = Path.home() / ".arkaos" / "telemetry" / "qg.jsonl"
144
+ if qg_log.exists():
145
+ try:
146
+ for line in reversed(qg_log.read_text(encoding="utf-8").splitlines()):
147
+ if not line.strip():
148
+ continue
149
+ try:
150
+ rec = json.loads(line)
151
+ except Exception:
152
+ continue
153
+ if rec.get("session_id") == session_id:
154
+ qg_approved = rec.get("verdict", "").upper() == "APPROVED"
155
+ break
156
+ except Exception:
157
+ pass
158
+ if not qg_approved:
159
+ sys.exit(0)
160
+
161
+ external_markers = (
162
+ "WebFetch", "WebSearch", "mcp__context7", "mcp__firecrawl",
163
+ "http://", "https://",
164
+ )
165
+ has_external = False
166
+ try:
167
+ data = Path(transcript_path).read_text(encoding="utf-8", errors="replace")
168
+ has_external = any(marker in data for marker in external_markers)
169
+ except Exception:
170
+ has_external = False
171
+ if not has_external:
172
+ sys.exit(0)
173
+
174
+ try:
175
+ enqueue_job(session_id, transcript_path, "APPROVED")
176
+ except Exception:
177
+ pass
178
+ '@
179
+
180
+ try {
181
+ $autoDocScript | & $python.Source - | Out-Null
182
+ } catch {
183
+ # Swallow — enqueue is fire-and-forget.
184
+ }
185
+
107
186
  # Belt-and-braces marker cleanup (safe even if the Python block crashed).
108
187
  if ($sessionId -match '^[A-Za-z0-9._-]{1,128}$') {
109
188
  Remove-Item -LiteralPath $wfMarker -ErrorAction SilentlyContinue
@@ -115,6 +115,94 @@ except Exception:
115
115
  pass
116
116
  PY
117
117
 
118
+ # ─── Auto-documentor enqueue (fire-and-forget) ──────────────────────────
119
+ # Queues a background job when:
120
+ # (a) the session was flagged flow-required (classifier match above),
121
+ # (b) Quality Gate approved — last assistant message carries
122
+ # `[arka:qg:approved]`, OR the most recent entry for this session
123
+ # in ~/.arkaos/telemetry/qg.jsonl has verdict APPROVED, AND
124
+ # (c) at least one external research tool was invoked this session
125
+ # (URL, WebFetch/WebSearch, Context7, Firecrawl in the transcript).
126
+ # The actual documentation runs async via `core/jobs/auto_doc_worker.py`;
127
+ # this block never blocks Stop — 2s Python budget, errors swallowed.
128
+ if command -v timeout &>/dev/null; then
129
+ _ARKA_AUTO_DOC_RUNNER=(timeout 2s python3 -)
130
+ elif command -v gtimeout &>/dev/null; then
131
+ _ARKA_AUTO_DOC_RUNNER=(gtimeout 2s python3 -)
132
+ else
133
+ _ARKA_AUTO_DOC_RUNNER=(python3 -)
134
+ fi
135
+
136
+ SESSION_ID_VAL="$SESSION_ID" \
137
+ TRANSCRIPT_PATH_VAL="$TRANSCRIPT_PATH" \
138
+ CWD_VAL="$CWD" \
139
+ ARKAOS_ROOT_VAL="$ARKAOS_ROOT" \
140
+ "${_ARKA_AUTO_DOC_RUNNER[@]}" <<'PY' 2>/dev/null || true
141
+ import json
142
+ import os
143
+ import re
144
+ import sys
145
+ from pathlib import Path
146
+
147
+ sys.path.insert(0, os.environ["ARKAOS_ROOT_VAL"])
148
+ try:
149
+ from core.jobs.auto_doc_worker import enqueue_job
150
+ from core.workflow.flow_enforcer import _load_last_assistant_messages
151
+ except Exception:
152
+ sys.exit(0)
153
+
154
+ session_id = os.environ.get("SESSION_ID_VAL", "")
155
+ transcript_path = os.environ.get("TRANSCRIPT_PATH_VAL", "")
156
+ if not session_id or not transcript_path:
157
+ sys.exit(0)
158
+
159
+ last = ""
160
+ try:
161
+ msgs = _load_last_assistant_messages(transcript_path, n=1)
162
+ last = msgs[-1] if msgs else ""
163
+ except Exception:
164
+ last = ""
165
+
166
+ qg_approved = bool(re.search(r"\[arka:qg:approved\]", last, re.IGNORECASE))
167
+ if not qg_approved:
168
+ qg_log = Path.home() / ".arkaos" / "telemetry" / "qg.jsonl"
169
+ if qg_log.exists():
170
+ try:
171
+ for line in reversed(qg_log.read_text(encoding="utf-8").splitlines()):
172
+ if not line.strip():
173
+ continue
174
+ try:
175
+ rec = json.loads(line)
176
+ except Exception:
177
+ continue
178
+ if rec.get("session_id") == session_id:
179
+ qg_approved = rec.get("verdict", "").upper() == "APPROVED"
180
+ break
181
+ except Exception:
182
+ pass
183
+ if not qg_approved:
184
+ sys.exit(0)
185
+
186
+ # Heuristic check that some external research happened this session.
187
+ external_markers = (
188
+ "WebFetch", "WebSearch", "mcp__context7", "mcp__firecrawl",
189
+ "http://", "https://",
190
+ )
191
+ has_external = False
192
+ try:
193
+ data = Path(transcript_path).read_text(encoding="utf-8", errors="replace")
194
+ has_external = any(marker in data for marker in external_markers)
195
+ except Exception:
196
+ has_external = False
197
+ if not has_external:
198
+ sys.exit(0)
199
+
200
+ try:
201
+ enqueue_job(session_id, transcript_path, "APPROVED")
202
+ except Exception:
203
+ pass
204
+ PY
205
+
118
206
  # Belt-and-braces: remove the marker at shell level in case the Python
119
207
  # block above crashed before reaching clear_flow_required(). Session_id
120
208
  # is already validated by the Python helper; this shell remove is scoped