arkaos 2.22.1 → 2.28.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.
- package/README.md +105 -9
- package/VERSION +1 -1
- package/arka/SKILL.md +1 -0
- package/arka/skills/bootstrap-agent/SKILL.md +76 -0
- package/arka/skills/comfyui/SKILL.md +2 -2
- package/arka/skills/comfyui/references/workflows.md +6 -6
- package/arka/skills/costs/SKILL.md +11 -0
- package/arka/skills/design-ops/SKILL.md +68 -0
- package/arka/skills/design-ops/scripts/extract-colors.py +86 -0
- package/arka/skills/design-ops/scripts/shadcn-tokens.py +59 -0
- package/arka/skills/design-ops/scripts/wcag-contrast.py +92 -0
- package/arka/skills/research/SKILL.md +117 -0
- package/config/cognition/prompts/dreaming.md +3 -3
- package/config/cognition/prompts/research.md +4 -4
- package/config/constitution.yaml +4 -0
- package/config/hooks/post-tool-use.sh +17 -0
- package/config/hooks/user-prompt-submit.sh +22 -1
- package/core/agents/__pycache__/loader.cpython-313.pyc +0 -0
- package/core/agents/__pycache__/schema.cpython-313.pyc +0 -0
- package/core/agents/loader.py +4 -1
- package/core/agents/schema.py +29 -0
- package/core/cognition/__pycache__/auto_documentor.cpython-313.pyc +0 -0
- package/core/cognition/__pycache__/retrieval.cpython-313.pyc +0 -0
- package/core/cognition/capture/__pycache__/collector.cpython-313.pyc +0 -0
- package/core/cognition/capture/collector.py +10 -3
- package/core/cognition/retrieval.py +383 -0
- package/core/cognition/scheduler/__pycache__/daemon.cpython-313.pyc +0 -0
- package/core/cognition/scheduler/daemon.py +5 -0
- package/core/jobs/__pycache__/auto_doc_worker.cpython-313.pyc +0 -0
- package/core/obsidian/__pycache__/writer.cpython-313.pyc +0 -0
- package/core/obsidian/writer.py +5 -4
- package/core/runtime/__pycache__/base.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/claude_code.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/codex_cli.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/gemini_cli.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/llm_provider.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/path_resolver.cpython-313.pyc +0 -0
- package/core/runtime/path_resolver.py +202 -0
- package/core/shared/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/shared/__pycache__/safe_session_id.cpython-313.pyc +0 -0
- package/core/specs/SPEC-installer-cross-os.md +227 -0
- package/core/specs/SPEC-paths-portability.md +209 -0
- package/core/synapse/__pycache__/kb_cache.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/layers.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/flow_enforcer.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/marker_cache.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/research_gate.cpython-313.pyc +0 -0
- package/departments/brand/agents/design-ops/design-ops-lead.yaml +78 -0
- package/departments/brand/agents/design-ops/extraction-script-writer.yaml +74 -0
- package/departments/brand/agents/design-ops/shadcn-padronizer.yaml +76 -0
- package/departments/brand/agents/design-ops/wcag-auditor.yaml +76 -0
- package/departments/dev/skills/mcp/SKILL.md +16 -0
- package/departments/dev/skills/scaffold/SKILL.md +4 -4
- package/departments/kb/skills/knowledge/SKILL.md +1 -1
- package/departments/ops/skills/operations/SKILL.md +1 -1
- package/installer/cli.js +2 -1
- package/installer/doctor.js +48 -0
- package/installer/index.js +26 -1
- package/installer/migrate-user-data.js +17 -1
- package/installer/migrations/v3_path_schema.js +109 -0
- package/installer/package-manager.js +191 -0
- package/installer/path-resolver.js +124 -0
- package/installer/system-tools.js +243 -0
- package/installer/update.js +24 -3
- package/knowledge/obsidian-config.json +1 -1
- package/package.json +1 -1
- package/pyproject.toml +1 -1
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: arka-research
|
|
3
|
+
description: >
|
|
4
|
+
Fan-out research workflow. Spawns 5 parallel subagents — Perplexity,
|
|
5
|
+
Exa AI, Context7, Firecrawl, XMCP — synthesises their findings into
|
|
6
|
+
a single report, and writes a Knowledge Base note to the Obsidian
|
|
7
|
+
vault. Inspired by the multi-source research pattern surfaced in the
|
|
8
|
+
2026-05-13 Orgo podcast (Nick Saraev): "I tell Claude Code, hey,
|
|
9
|
+
spawn five sub-agents — one for Perplexity, one for Exa, one for
|
|
10
|
+
Context7, one for Firecrawl, one for XMCP — and we get best practices."
|
|
11
|
+
allowed-tools: [Agent, Read, Write, mcp__obsidian__search_notes]
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# /arka research — one prompt, five parallel sources
|
|
15
|
+
|
|
16
|
+
> Companion to `/arka bootstrap-agent`. Where bootstrap-agent generates
|
|
17
|
+
> capability, this skill generates *knowledge*.
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
/arka research <topic>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`<topic>` is a free-form research question. Examples:
|
|
26
|
+
|
|
27
|
+
- `/arka research "Hermes agent setup with Telegram gateway"`
|
|
28
|
+
- `/arka research "Mistral Medium 3.5 cost vs Sonnet 4.6"`
|
|
29
|
+
- `/arka research "AIOX Squad de Saúde framework"`
|
|
30
|
+
|
|
31
|
+
## Sources fanned out
|
|
32
|
+
|
|
33
|
+
| Subagent | MCP / tool | Strength |
|
|
34
|
+
| --- | --- | --- |
|
|
35
|
+
| Perplexity | `mcp__perplexity__ask` | Real-time web with citations |
|
|
36
|
+
| Exa AI | `mcp__exa__search` | Semantic vector search across the web |
|
|
37
|
+
| Context7 | `mcp__context7__query-docs` | Up-to-date official library docs |
|
|
38
|
+
| Firecrawl | `mcp__firecrawl__firecrawl_scrape` | Site-specific deep scraping |
|
|
39
|
+
| XMCP | `mcp__x__search` | X / Twitter conversations and threads |
|
|
40
|
+
|
|
41
|
+
If any MCP is not registered in `.mcp.json`, that subagent is skipped
|
|
42
|
+
with a `[arka:source-skipped]` note in the report. KB-first protocol
|
|
43
|
+
still applies — Obsidian search runs before any external call.
|
|
44
|
+
|
|
45
|
+
## Workflow
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
1. KB-first: search Obsidian vault for prior notes on the topic.
|
|
49
|
+
If high-confidence matches exist, the report cites them first
|
|
50
|
+
and asks the user whether external research is still needed.
|
|
51
|
+
|
|
52
|
+
2. Fan out 5 subagents in parallel via the Agent tool. Each gets
|
|
53
|
+
the same topic, returns a focused short brief (≤500 words).
|
|
54
|
+
|
|
55
|
+
3. Critic synthesis (Tier 0 reviewer, opus): collapses overlap,
|
|
56
|
+
surfaces contradictions, ranks sources by weight (official docs >
|
|
57
|
+
recent community threads > older articles).
|
|
58
|
+
|
|
59
|
+
4. Synthesis report saved to:
|
|
60
|
+
${VAULT_PATH}/Knowledge Base/Research/<YYYY-MM-DD>-<slug>.md
|
|
61
|
+
with full citations and a "where to go next" section.
|
|
62
|
+
|
|
63
|
+
5. Returns a compact summary to the user (the full report links to
|
|
64
|
+
the Obsidian note).
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Output shape
|
|
68
|
+
|
|
69
|
+
```markdown
|
|
70
|
+
---
|
|
71
|
+
title: <topic>
|
|
72
|
+
date: <YYYY-MM-DD>
|
|
73
|
+
sources: [perplexity, exa, context7, firecrawl, xmcp]
|
|
74
|
+
status: synthesised
|
|
75
|
+
tags: [research, <topic-tag>]
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
# <Topic>
|
|
79
|
+
|
|
80
|
+
## TL;DR
|
|
81
|
+
<3-bullet summary>
|
|
82
|
+
|
|
83
|
+
## Findings by source
|
|
84
|
+
### Perplexity
|
|
85
|
+
...
|
|
86
|
+
### Exa AI
|
|
87
|
+
...
|
|
88
|
+
(etc.)
|
|
89
|
+
|
|
90
|
+
## Synthesis
|
|
91
|
+
<contradictions resolved, ranked by source weight>
|
|
92
|
+
|
|
93
|
+
## Next steps
|
|
94
|
+
<2-3 follow-up questions or actions>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Cost & budget
|
|
98
|
+
|
|
99
|
+
This is a heavy operation — 5 parallel LLM calls plus a synthesis call.
|
|
100
|
+
A typical run consumes 3-8k tokens. The `/arka costs` skill tracks the
|
|
101
|
+
spend and the `[arka:warn]` token-hygiene hook flags excessive use.
|
|
102
|
+
|
|
103
|
+
## Boundaries
|
|
104
|
+
|
|
105
|
+
- Read-only: this skill never modifies code or configuration. It only
|
|
106
|
+
writes to the Obsidian Knowledge Base.
|
|
107
|
+
- KB-first: always check the vault first. Many topics already have
|
|
108
|
+
notes from prior Dreaming / Research sessions.
|
|
109
|
+
- No external research without the user asking — agents do not
|
|
110
|
+
proactively fan out unless `/arka research` is invoked.
|
|
111
|
+
|
|
112
|
+
## Cross-references
|
|
113
|
+
|
|
114
|
+
- Source pattern: 2026-05-13 Orgo podcast
|
|
115
|
+
- Related: `/arka forge` (planning, not research)
|
|
116
|
+
- Related: `/kb research` (Clara, knowledge management)
|
|
117
|
+
- Related: `/arka bootstrap-agent` (capability-from-research)
|
|
@@ -8,7 +8,7 @@ You are ArkaOS performing your nightly Dreaming session. Your job is to review e
|
|
|
8
8
|
- Read any file from any project
|
|
9
9
|
- Read git logs and diffs
|
|
10
10
|
- Search the web (WebSearch, Firecrawl)
|
|
11
|
-
- Write to Obsidian vault at
|
|
11
|
+
- Write to Obsidian vault at ${VAULT_PATH}/Projects/ArkaOS/
|
|
12
12
|
- Write to ~/.arkaos/ (captures, insights, logs, knowledge)
|
|
13
13
|
- Use browser for research
|
|
14
14
|
- Read online documentation
|
|
@@ -81,7 +81,7 @@ from core.cognition.memory.writer import DualWriter
|
|
|
81
81
|
|
|
82
82
|
writer = DualWriter(
|
|
83
83
|
obsidian_base=os.path.expanduser(
|
|
84
|
-
"
|
|
84
|
+
"${VAULT_PATH}/Projects/ArkaOS/Knowledge Base"
|
|
85
85
|
),
|
|
86
86
|
vector_db_path=os.path.expanduser("~/.arkaos/knowledge.db"),
|
|
87
87
|
)
|
|
@@ -113,7 +113,7 @@ Use `DualWriter.write()` for each KnowledgeEntry. This automatically writes to b
|
|
|
113
113
|
## Phase 6: Report + Evolution Metrics
|
|
114
114
|
|
|
115
115
|
Write daily report to Obsidian:
|
|
116
|
-
|
|
116
|
+
`${VAULT_PATH}/Projects/ArkaOS/Dreaming/YYYY-MM-DD.md`
|
|
117
117
|
|
|
118
118
|
Format:
|
|
119
119
|
```markdown
|
|
@@ -8,7 +8,7 @@ You are ArkaOS performing your daily Research session. Your job is to stay curre
|
|
|
8
8
|
- Read any file from any project
|
|
9
9
|
- Read git logs
|
|
10
10
|
- Search the web extensively (WebSearch, Firecrawl)
|
|
11
|
-
- Write to Obsidian vault at
|
|
11
|
+
- Write to Obsidian vault at ${VAULT_PATH}/Projects/ArkaOS/
|
|
12
12
|
- Write to ~/.arkaos/ (insights, logs, knowledge, profiles)
|
|
13
13
|
- Use browser for deep research
|
|
14
14
|
- Read online documentation, blogs, changelogs, GitHub releases
|
|
@@ -94,7 +94,7 @@ from core.cognition.memory.writer import DualWriter
|
|
|
94
94
|
|
|
95
95
|
writer = DualWriter(
|
|
96
96
|
obsidian_base=os.path.expanduser(
|
|
97
|
-
"
|
|
97
|
+
"${VAULT_PATH}/Projects/ArkaOS/Knowledge Base"
|
|
98
98
|
),
|
|
99
99
|
vector_db_path=os.path.expanduser("~/.arkaos/knowledge.db"),
|
|
100
100
|
)
|
|
@@ -114,7 +114,7 @@ writer.write(entry)
|
|
|
114
114
|
## Phase 5: Cross-Reference with Dreaming
|
|
115
115
|
|
|
116
116
|
1. Read tonight's Dreaming report (if it exists):
|
|
117
|
-
|
|
117
|
+
`${VAULT_PATH}/Projects/ArkaOS/Dreaming/YYYY-MM-DD.md`
|
|
118
118
|
|
|
119
119
|
2. Read pending insights from `~/.arkaos/insights.db`:
|
|
120
120
|
```python
|
|
@@ -145,7 +145,7 @@ store.save(insight)
|
|
|
145
145
|
## Phase 6: Intelligence Briefing
|
|
146
146
|
|
|
147
147
|
Write daily briefing to Obsidian:
|
|
148
|
-
|
|
148
|
+
`${VAULT_PATH}/Projects/ArkaOS/Research/YYYY-MM-DD.md`
|
|
149
149
|
|
|
150
150
|
Format:
|
|
151
151
|
```markdown
|
package/config/constitution.yaml
CHANGED
|
@@ -135,6 +135,10 @@ enforcement_levels:
|
|
|
135
135
|
rule: "All ecosystem skills must follow the standard 7-phase workflow defined in config/standards/ecosystem-workflow.md"
|
|
136
136
|
enforcement: "Ecosystem SKILL.md files must reference or implement all 7 phases"
|
|
137
137
|
|
|
138
|
+
- id: sub-squad-hierarchy
|
|
139
|
+
rule: "Agents in a sub-squad MUST declare parent_squad (department slug) and sub_squad_role. Sub-squads inherit the parent department's authority chain; their lead escalates to the department squad lead, not directly to Tier 0."
|
|
140
|
+
enforcement: "Agent loader validates parent_squad + sub_squad_role coherence (sub_squad_role requires parent_squad). PR5 v2.27.0 introduced the pattern; AIOX Squad → Sub-Squad → Skills/Scripts is the reference architecture."
|
|
141
|
+
|
|
138
142
|
- id: forge-persistence
|
|
139
143
|
rule: "All forge plans (approved and rejected) must be persisted to Obsidian"
|
|
140
144
|
enforcement: "persistence.py exports on status change; audit detects missing exports"
|
|
@@ -391,6 +391,23 @@ if not match and all_deliverables:
|
|
|
391
391
|
fi
|
|
392
392
|
fi
|
|
393
393
|
|
|
394
|
+
# ─── Cognitive layer capture (PR4 v2.26.0 hooks-as-retrieval) ────────────
|
|
395
|
+
# Backgrounded so the hook returns immediately. The retrieval helper has
|
|
396
|
+
# its own internal time caps (ripgrep ≤ 1s, Python fallback capped at 500
|
|
397
|
+
# files) so a runaway extract can never block the next user prompt. We
|
|
398
|
+
# disown so the child cannot zombie if the hook process exits first.
|
|
399
|
+
if [ -n "$SESSION_ID_PTU" ] && [ -n "$TOOL_OUTPUT" ]; then
|
|
400
|
+
_ARKAOS_REPO=$(cat "$HOME/.arkaos/.repo-path" 2>/dev/null || echo "")
|
|
401
|
+
if [ -n "$_ARKAOS_REPO" ] && [ -d "$_ARKAOS_REPO" ]; then
|
|
402
|
+
(
|
|
403
|
+
PYTHONPATH="$_ARKAOS_REPO" python3 -m core.cognition.retrieval capture "$SESSION_ID_PTU" <<EOF >/dev/null 2>&1
|
|
404
|
+
$TOOL_OUTPUT
|
|
405
|
+
EOF
|
|
406
|
+
) &
|
|
407
|
+
disown 2>/dev/null || true
|
|
408
|
+
fi
|
|
409
|
+
fi
|
|
410
|
+
|
|
394
411
|
# ─── Log Metrics ─────────────────────────────────────────────────────────
|
|
395
412
|
_DURATION_MS=$(_hook_ms)
|
|
396
413
|
METRICS_FILE="$HOME/.arkaos/hook-metrics.json"
|
|
@@ -356,9 +356,25 @@ sequential-validation, full-visibility, arka-supremacy."
|
|
|
356
356
|
fi
|
|
357
357
|
fi
|
|
358
358
|
|
|
359
|
+
# ─── Cognitive context injection (PR4 v2.26.0 hooks-as-retrieval) ───────
|
|
360
|
+
# Read the context cache populated by the previous PostToolUse turn and
|
|
361
|
+
# inject any KB hits as an `[arka:context]` advisory. The Python helper
|
|
362
|
+
# has internal TTL filtering (10 min) and returns empty when there is no
|
|
363
|
+
# fresh cache, so this stays a no-op until the cognitive layer has data
|
|
364
|
+
# to share.
|
|
365
|
+
_ARKA_CONTEXT_HITS=""
|
|
366
|
+
if [ -n "$SESSION_ID" ]; then
|
|
367
|
+
_ARKAOS_REPO=$(cat "$HOME/.arkaos/.repo-path" 2>/dev/null || echo "")
|
|
368
|
+
if [ -n "$_ARKAOS_REPO" ] && [ -d "$_ARKAOS_REPO" ]; then
|
|
369
|
+
_ARKA_CONTEXT_HITS=$(PYTHONPATH="$_ARKAOS_REPO" python3 -m core.cognition.retrieval inject "$SESSION_ID" 2>/dev/null)
|
|
370
|
+
fi
|
|
371
|
+
fi
|
|
372
|
+
|
|
359
373
|
# ─── Output ──────────────────────────────────────────────────────────────
|
|
360
374
|
_OUT_CONTEXT="${_ARKA_GREETING:-}${_SYNC_NOTICE:-}${_ROUTE_REMINDER}${_WORKFLOW_DIRECTIVE} $python_result"
|
|
361
375
|
[ -n "$_HYGIENE" ] && _OUT_CONTEXT="$_OUT_CONTEXT $_HYGIENE"
|
|
376
|
+
[ -n "$_ARKA_CONTEXT_HITS" ] && _OUT_CONTEXT="$_OUT_CONTEXT
|
|
377
|
+
$_ARKA_CONTEXT_HITS"
|
|
362
378
|
# Escape for JSON
|
|
363
379
|
_OUT_JSON=$(python3 -c "import json,sys; print(json.dumps(sys.stdin.read()))" <<< "$_OUT_CONTEXT" 2>/dev/null)
|
|
364
380
|
if [ -n "$_OUT_JSON" ]; then
|
|
@@ -368,7 +384,12 @@ else
|
|
|
368
384
|
fi
|
|
369
385
|
|
|
370
386
|
# ─── Metrics ─────────────────────────────────────────────────────────────
|
|
387
|
+
# Count @-mentions in the prompt for at_mention_frequency telemetry. Mirrors
|
|
388
|
+
# the claude_code.at_mention OpenTelemetry event introduced in Claude Code
|
|
389
|
+
# 2.1.122, but works on any version because we count from the same input
|
|
390
|
+
# the hook already receives.
|
|
391
|
+
at_mentions=$(printf '%s' "$user_input" | grep -oE '(^|[[:space:]])@[A-Za-z0-9_./-]+' | wc -l | tr -d ' ')
|
|
371
392
|
elapsed=$(_hook_ms)
|
|
372
393
|
if [ "$elapsed" -gt 0 ] 2>/dev/null; then
|
|
373
|
-
echo "{\"hook\":\"user-prompt-submit-v2\",\"ms\":$elapsed}" >> "$CACHE_DIR/hook-metrics.jsonl" 2>/dev/null
|
|
394
|
+
echo "{\"hook\":\"user-prompt-submit-v2\",\"ms\":$elapsed,\"at_mentions\":${at_mentions:-0}}" >> "$CACHE_DIR/hook-metrics.jsonl" 2>/dev/null
|
|
374
395
|
fi
|
|
Binary file
|
|
Binary file
|
package/core/agents/loader.py
CHANGED
|
@@ -60,7 +60,10 @@ def load_all_agents(
|
|
|
60
60
|
for agents_dir in search_dirs:
|
|
61
61
|
if not agents_dir.is_dir():
|
|
62
62
|
continue
|
|
63
|
-
|
|
63
|
+
# Recursive rglob picks up sub-squad subdirectories (e.g.
|
|
64
|
+
# departments/brand/agents/design-ops/*.yaml) per the v2.27.0
|
|
65
|
+
# Sub-Squad pattern. Sorted output keeps load order stable.
|
|
66
|
+
for yaml_file in sorted(agents_dir.rglob("*.yaml")):
|
|
64
67
|
try:
|
|
65
68
|
agent = load_agent(yaml_file)
|
|
66
69
|
agents.append(agent)
|
package/core/agents/schema.py
CHANGED
|
@@ -273,6 +273,35 @@ class Agent(BaseModel):
|
|
|
273
273
|
description="Claude model override for dispatch. Falls back to tier default when None.",
|
|
274
274
|
)
|
|
275
275
|
|
|
276
|
+
parent_squad: Optional[str] = Field(
|
|
277
|
+
default=None,
|
|
278
|
+
description=(
|
|
279
|
+
"Slug of the parent squad when this agent belongs to a sub-squad. "
|
|
280
|
+
"Sub-squads are operational specialisations inside a department "
|
|
281
|
+
"(e.g. brand → design-ops). Optional and backwards-compatible: "
|
|
282
|
+
"agents without a parent_squad belong directly to their department."
|
|
283
|
+
),
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
sub_squad_role: Optional[str] = Field(
|
|
287
|
+
default=None,
|
|
288
|
+
description=(
|
|
289
|
+
"Role within the sub-squad when parent_squad is set "
|
|
290
|
+
"(e.g. 'lead', 'extraction-script-writer', 'wcag-auditor'). "
|
|
291
|
+
"Inspired by the AIOX Squad → Sub-Squad pattern (KB note "
|
|
292
|
+
"AIOX Squads, April 30 2026 live)."
|
|
293
|
+
),
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
@model_validator(mode="after")
|
|
297
|
+
def validate_sub_squad_coherence(self) -> "Agent":
|
|
298
|
+
"""sub_squad_role only makes sense when parent_squad is set."""
|
|
299
|
+
if self.sub_squad_role and not self.parent_squad:
|
|
300
|
+
raise ValueError(
|
|
301
|
+
"sub_squad_role requires parent_squad to be set"
|
|
302
|
+
)
|
|
303
|
+
return self
|
|
304
|
+
|
|
276
305
|
@model_validator(mode="after")
|
|
277
306
|
def auto_fill_memory_path(self) -> "Agent":
|
|
278
307
|
if not self.memory_path:
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -7,13 +7,20 @@ from datetime import datetime, timezone
|
|
|
7
7
|
|
|
8
8
|
from core.cognition.capture.store import CaptureStore
|
|
9
9
|
from core.cognition.memory.schemas import RawCapture
|
|
10
|
+
from core.runtime.path_resolver import ProfileMissingError, project_root_regex
|
|
11
|
+
|
|
12
|
+
_FALLBACK_REGEX = re.compile(
|
|
13
|
+
r"((?:/Users|/home)/\S+/(?:Herd|Work|AIProjects|code|repos))[/\\]([^\s/\\]+)"
|
|
14
|
+
)
|
|
10
15
|
|
|
11
16
|
|
|
12
17
|
def _detect_project(digest: str) -> tuple[str, str]:
|
|
13
18
|
"""Try to detect project name and path from digest content."""
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
try:
|
|
20
|
+
regex = project_root_regex()
|
|
21
|
+
except ProfileMissingError:
|
|
22
|
+
regex = _FALLBACK_REGEX
|
|
23
|
+
path_match = regex.search(digest)
|
|
17
24
|
if path_match:
|
|
18
25
|
return path_match.group(2).rstrip("/"), path_match.group(1).rstrip("/")
|
|
19
26
|
return "unknown", os.getcwd()
|