arkaos 4.11.0 → 4.12.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 +1 -1
- package/VERSION +1 -1
- package/arka/SKILL.md +10 -1
- package/arka/skills/flow/SKILL.md +23 -1
- package/arka/skills/recipes/SKILL.md +69 -0
- package/arka/skills/refine/SKILL.md +81 -0
- package/config/constitution.yaml +8 -0
- package/config/mcp-policy.yaml +8 -0
- package/config/output-styles/arkaos.md +52 -0
- package/core/evals/__init__.py +4 -0
- package/core/evals/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/evals/__pycache__/record_cli.cpython-313.pyc +0 -0
- package/core/evals/__pycache__/verdict_labels.cpython-313.pyc +0 -0
- package/core/evals/record_cli.py +41 -8
- package/core/evals/verdict_labels.py +53 -13
- package/core/forge/__pycache__/complexity.cpython-313.pyc +0 -0
- package/core/forge/complexity.py +9 -0
- package/core/governance/__pycache__/evidence_checks.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/judge.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/phantom_action_check.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/phantom_action_check.cpython-314.pyc +0 -0
- package/core/governance/evidence_checks.py +60 -4
- package/core/governance/judge.py +95 -0
- package/core/governance/phantom_action_check.py +41 -0
- package/core/hooks/__pycache__/post_tool_use.cpython-313.pyc +0 -0
- package/core/hooks/__pycache__/stop.cpython-313.pyc +0 -0
- package/core/hooks/__pycache__/stop.cpython-314.pyc +0 -0
- package/core/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
- package/core/hooks/post_tool_use.py +13 -0
- package/core/hooks/stop.py +23 -0
- package/core/hooks/user_prompt_submit.py +69 -0
- package/core/knowledge/__pycache__/recipes.cpython-313.pyc +0 -0
- package/core/knowledge/__pycache__/recipes_cli.cpython-313.pyc +0 -0
- package/core/knowledge/recipes.py +237 -0
- package/core/knowledge/recipes_cli.py +99 -0
- package/core/shared/__pycache__/safe_session_id.cpython-313.pyc +0 -0
- package/core/shared/__pycache__/safe_session_id.cpython-314.pyc +0 -0
- package/core/shared/safe_session_id.py +11 -3
- package/core/synapse/__pycache__/engine.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/engine.cpython-314.pyc +0 -0
- package/core/synapse/__pycache__/pattern_library_layer.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/recipe_layer.cpython-313.pyc +0 -0
- package/core/synapse/engine.py +6 -0
- package/core/synapse/pattern_library_layer.py +10 -2
- package/core/synapse/recipe_layer.py +114 -0
- package/core/sync/__pycache__/mcp_optimizer.cpython-313.pyc +0 -0
- package/core/sync/__pycache__/mcp_syncer.cpython-313.pyc +0 -0
- package/core/sync/__pycache__/settings_syncer.cpython-313.pyc +0 -0
- package/core/sync/mcp_optimizer.py +4 -1
- package/core/sync/mcp_syncer.py +50 -6
- package/core/sync/settings_syncer.py +9 -2
- package/core/workflow/__pycache__/flow_enforcer.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/flow_enforcer.cpython-314.pyc +0 -0
- package/core/workflow/__pycache__/plan_approval.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/plan_approval.cpython-314.pyc +0 -0
- package/core/workflow/flow_enforcer.py +63 -0
- package/core/workflow/plan_approval.py +185 -0
- package/departments/dev/SKILL.md +10 -0
- package/departments/dev/skills/onboard/SKILL.md +6 -2
- package/departments/dev/skills/refactor-plan/SKILL.md +7 -0
- package/departments/dev/skills/scaffold/SKILL.md +6 -0
- package/departments/quality/SKILL.md +16 -0
- package/installer/doctor.js +7 -0
- package/installer/index.js +20 -0
- package/installer/output-style.js +92 -0
- package/installer/update.js +19 -0
- package/installer/worktree-baseref.js +8 -2
- package/knowledge/commands-keywords.json +4 -0
- package/knowledge/commands-registry.json +41 -3
- package/package.json +1 -1
- package/pyproject.toml +1 -1
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""Per-session plan-approval state machine (Interaction Reform PR3).
|
|
2
|
+
|
|
3
|
+
The flow contract says Gate 2 "waits for EXPLICIT user approval", but
|
|
4
|
+
nothing executable ever verified it — the enforcer only checked marker
|
|
5
|
+
PRESENCE, so a bare ``[arka:routing]`` unlocked writes. This module
|
|
6
|
+
tracks the missing signal:
|
|
7
|
+
|
|
8
|
+
none → presented → approved
|
|
9
|
+
|
|
10
|
+
- **presented** — the Stop hook observed the turn ending at Gate 2
|
|
11
|
+
(plan shown, no Gate 3 yet).
|
|
12
|
+
- **approved** — the UserPromptSubmit hook classified the NEXT user
|
|
13
|
+
message as an approval, or PostToolUse saw a successful
|
|
14
|
+
``ExitPlanMode`` (the native plan-mode approve button IS approval).
|
|
15
|
+
|
|
16
|
+
A new ``presented`` invalidates an older ``approved`` (plan B after an
|
|
17
|
+
approved plan A requires fresh approval): approval is only live when
|
|
18
|
+
``approved_ts >= presented_ts``.
|
|
19
|
+
|
|
20
|
+
Same persistence contract as ``flow_authorization``: CWE-22-safe state
|
|
21
|
+
files under ``arkaos_temp_dir`` (override ``ARKA_PLAN_APPROVAL_DIR``),
|
|
22
|
+
12h TTL, never raises — hooks must not break a turn. In PR3 the
|
|
23
|
+
enforcer only WARNS on missing approval (telemetry to measure false
|
|
24
|
+
positives across the install base); hard deny is PR4, gated on that
|
|
25
|
+
telemetry.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
import json
|
|
31
|
+
import os
|
|
32
|
+
import re
|
|
33
|
+
import time
|
|
34
|
+
from dataclasses import asdict, dataclass
|
|
35
|
+
from pathlib import Path
|
|
36
|
+
|
|
37
|
+
from core.shared.safe_session_id import safe_session_id
|
|
38
|
+
from core.shared.temp_paths import arkaos_temp_dir
|
|
39
|
+
|
|
40
|
+
DEFAULT_TTL_SECONDS = 12 * 60 * 60
|
|
41
|
+
|
|
42
|
+
_APPROVE_RE = re.compile(
|
|
43
|
+
r"\b(aprovo|aprovado|avan[çc]a(?:m|mos)?|segue em frente|for[çc]a"
|
|
44
|
+
r"|manda|bora|go ahead|approved?|proceed|ship it|lgtm)\b",
|
|
45
|
+
re.IGNORECASE,
|
|
46
|
+
)
|
|
47
|
+
_REJECT_RE = re.compile(
|
|
48
|
+
r"\b(n[ãa]o|para|espera|aguarda|cancela?|stop|reject|hold)\b",
|
|
49
|
+
re.IGNORECASE,
|
|
50
|
+
)
|
|
51
|
+
_SHORT_YES_RE = re.compile(r"\b(sim|ok|okay|yes|yep|sure|dale)\b", re.IGNORECASE)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _state_dir() -> Path | None:
|
|
55
|
+
"""Resolve the state dir, creating it. None when the mkdir fails.
|
|
56
|
+
|
|
57
|
+
The module's never-raises contract must hold in its OWN internals:
|
|
58
|
+
a read-only override dir or a full disk returns None (callers treat
|
|
59
|
+
it as "no state") instead of propagating an OSError into a hook
|
|
60
|
+
(QG 2026-07-09, PR4 prerequisite #3).
|
|
61
|
+
"""
|
|
62
|
+
override = os.environ.get("ARKA_PLAN_APPROVAL_DIR", "").strip()
|
|
63
|
+
try:
|
|
64
|
+
if override:
|
|
65
|
+
path = Path(override)
|
|
66
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
67
|
+
return path
|
|
68
|
+
return arkaos_temp_dir("arkaos-plan-approval")
|
|
69
|
+
except OSError:
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _state_file(session_id: str) -> Path | None:
|
|
74
|
+
safe = safe_session_id(session_id)
|
|
75
|
+
if safe is None:
|
|
76
|
+
return None
|
|
77
|
+
state_dir = _state_dir()
|
|
78
|
+
if state_dir is None:
|
|
79
|
+
return None
|
|
80
|
+
return state_dir / f"{safe}.json"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass
|
|
84
|
+
class ApprovalState:
|
|
85
|
+
state: str = "none" # none | presented | approved
|
|
86
|
+
presented_ts: float = 0.0
|
|
87
|
+
approved_ts: float = 0.0
|
|
88
|
+
source: str = "" # text | exit-plan-mode
|
|
89
|
+
excerpt: str = ""
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _read(session_id: str) -> ApprovalState:
|
|
93
|
+
path = _state_file(session_id)
|
|
94
|
+
if path is None or not path.exists():
|
|
95
|
+
return ApprovalState()
|
|
96
|
+
try:
|
|
97
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
98
|
+
return ApprovalState(**{
|
|
99
|
+
k: data.get(k, getattr(ApprovalState(), k))
|
|
100
|
+
for k in ApprovalState().__dict__
|
|
101
|
+
})
|
|
102
|
+
except Exception: # noqa: BLE001 — corrupt state resets, never raises
|
|
103
|
+
return ApprovalState()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _write(session_id: str, state: ApprovalState) -> None:
|
|
107
|
+
path = _state_file(session_id)
|
|
108
|
+
if path is None:
|
|
109
|
+
return
|
|
110
|
+
try:
|
|
111
|
+
path.write_text(
|
|
112
|
+
json.dumps(asdict(state)) + "\n", encoding="utf-8"
|
|
113
|
+
)
|
|
114
|
+
except OSError:
|
|
115
|
+
return
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def mark_presented(session_id: str) -> None:
|
|
119
|
+
"""The turn ended at Gate 2: a plan is on the table."""
|
|
120
|
+
state = _read(session_id)
|
|
121
|
+
state.state = "presented"
|
|
122
|
+
state.presented_ts = time.time()
|
|
123
|
+
_write(session_id, state)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def mark_approved(
|
|
127
|
+
session_id: str, source: str = "text", excerpt: str = ""
|
|
128
|
+
) -> None:
|
|
129
|
+
state = _read(session_id)
|
|
130
|
+
state.state = "approved"
|
|
131
|
+
state.approved_ts = time.time()
|
|
132
|
+
state.source = source
|
|
133
|
+
state.excerpt = excerpt[:120]
|
|
134
|
+
_write(session_id, state)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def mark_rejected(session_id: str) -> None:
|
|
138
|
+
_write(session_id, ApprovalState())
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def get_state(session_id: str) -> ApprovalState:
|
|
142
|
+
return _read(session_id)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def is_presented(session_id: str) -> bool:
|
|
146
|
+
state = _read(session_id)
|
|
147
|
+
return (
|
|
148
|
+
state.state in ("presented", "approved")
|
|
149
|
+
and time.time() - state.presented_ts < DEFAULT_TTL_SECONDS
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def is_approved(session_id: str) -> bool:
|
|
154
|
+
"""Approval is live only when it POSTdates the latest presented plan."""
|
|
155
|
+
state = _read(session_id)
|
|
156
|
+
if state.state != "approved" or not state.approved_ts:
|
|
157
|
+
return False
|
|
158
|
+
if state.approved_ts + 0.001 < state.presented_ts:
|
|
159
|
+
return False
|
|
160
|
+
return time.time() - state.approved_ts < DEFAULT_TTL_SECONDS
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def classify_reply(text: str, has_creation_verb: bool = False) -> str:
|
|
164
|
+
"""Classify a user message that follows a presented plan.
|
|
165
|
+
|
|
166
|
+
Returns "approve" | "reject" | "other". A short bare "sim/ok/yes"
|
|
167
|
+
counts as approval ONLY when unambiguous: <= 5 tokens, no creation
|
|
168
|
+
verb (a new instruction, not consent), and no question mark.
|
|
169
|
+
"""
|
|
170
|
+
stripped = (text or "").strip()
|
|
171
|
+
if not stripped:
|
|
172
|
+
return "other"
|
|
173
|
+
if _REJECT_RE.search(stripped):
|
|
174
|
+
return "reject"
|
|
175
|
+
if _APPROVE_RE.search(stripped) and not has_creation_verb:
|
|
176
|
+
return "approve"
|
|
177
|
+
tokens = stripped.split()
|
|
178
|
+
if (
|
|
179
|
+
len(tokens) <= 5
|
|
180
|
+
and _SHORT_YES_RE.search(stripped)
|
|
181
|
+
and not has_creation_verb
|
|
182
|
+
and "?" not in stripped
|
|
183
|
+
):
|
|
184
|
+
return "approve"
|
|
185
|
+
return "other"
|
package/departments/dev/SKILL.md
CHANGED
|
@@ -70,6 +70,16 @@ Phase 7: QUALITY GATE → Marta + Eduardo (copy) + Francisca (tech) → APPROVED
|
|
|
70
70
|
Phase 8: DOCUMENTATION → ADR + docs to Obsidian
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
## Codebase grounding (codebase-memory MCP)
|
|
74
|
+
|
|
75
|
+
When the `codebase-memory` MCP is active in the project, consult it
|
|
76
|
+
BEFORE touching complex code — in Phase 1 RESEARCH and in every
|
|
77
|
+
Diagnose step: `get_architecture` for the structural map,
|
|
78
|
+
`search_graph`/`get_code_snippet` to locate the real implementations,
|
|
79
|
+
`trace_path` for impact analysis before a refactor. The graph is a
|
|
80
|
+
PRIOR, not ground truth: verify with Read before editing. When the MCP
|
|
81
|
+
is absent the phases run as before (Grep/Read exploration).
|
|
82
|
+
|
|
73
83
|
## Focused Workflow (4 Phases)
|
|
74
84
|
|
|
75
85
|
For `/dev debug`, `/dev refactor`, `/dev db`, `/dev performance`, `/dev pipeline`: Diagnose → Implement with tests → Rita validates → Quality Gate (Marta).
|
|
@@ -44,8 +44,12 @@ The onboard flow is a 12-step pipeline, split across two reference files:
|
|
|
44
44
|
8. Generate `PROJECT.md` in project root
|
|
45
45
|
9. Register in `$ARKA_OS/projects/<name>/`
|
|
46
46
|
10. Apply MCP profile — generate `.mcp.json` + `.claude/settings.local.json`
|
|
47
|
-
11.
|
|
48
|
-
|
|
47
|
+
11. Index the codebase graph — when the `codebase-memory-mcp` binary
|
|
48
|
+
exists, run `codebase-memory-mcp cli index_repository
|
|
49
|
+
'{"repo_path": "<abs project path>"}'` so the consistency layer is
|
|
50
|
+
warm from day one; skip silently when the binary is absent
|
|
51
|
+
12. Create Obsidian docs — Home, Architecture, Projects MOC, Ecosystem MOC
|
|
52
|
+
13. Report — summary + next steps (mention the graph index status)
|
|
49
53
|
|
|
50
54
|
The `ecosystem` subcommands (list/create/add) and error handling also live in mcp-config.md.
|
|
51
55
|
|
|
@@ -26,6 +26,13 @@ allowed-tools: [Read, Write, Edit, Bash, Grep, Glob, Agent, WebFetch, WebSearch]
|
|
|
26
26
|
|
|
27
27
|
Plan a refactoring: identify code smells, select refactoring patterns, ensure test safety net.
|
|
28
28
|
|
|
29
|
+
When the `codebase-memory` MCP is active, ground the plan in the real
|
|
30
|
+
structure BEFORE proposing moves: `get_architecture` for the module
|
|
31
|
+
map, `trace_path` for every call-site the refactor touches (impact
|
|
32
|
+
analysis), `search_graph` to find duplicated implementations worth
|
|
33
|
+
consolidating. The graph is a prior — verify with Read before planning
|
|
34
|
+
an edit.
|
|
35
|
+
|
|
29
36
|
## Output
|
|
30
37
|
|
|
31
38
|
Refactoring plan with patterns, risk assessment, and test verification steps
|
|
@@ -109,6 +109,12 @@ Profile mapping:
|
|
|
109
109
|
|
|
110
110
|
This generates `.mcp.json` and `.claude/settings.local.json`.
|
|
111
111
|
|
|
112
|
+
After the profile is applied, warm the consistency layer: when the
|
|
113
|
+
`codebase-memory-mcp` binary exists, run
|
|
114
|
+
`codebase-memory-mcp cli index_repository '{"repo_path": "<abs path>"}'`
|
|
115
|
+
so the graph is available from the first feature. Skip silently when
|
|
116
|
+
the binary is absent (the sync defers the MCP entry until installed).
|
|
117
|
+
|
|
112
118
|
### Step 6: Generate PROJECT.md
|
|
113
119
|
|
|
114
120
|
Create `PROJECT.md` in the project root with:
|
|
@@ -23,6 +23,13 @@ override it with narrative.
|
|
|
23
23
|
Any Department Workflow:
|
|
24
24
|
...
|
|
25
25
|
Phase N-1: QUALITY GATE
|
|
26
|
+
0. Output-judge (constitution gate-judges, MEDIUM/HIGH work only):
|
|
27
|
+
one judge dispatched via the Agent tool with
|
|
28
|
+
JUDGE_VERDICT_JSON_SCHEMA (core.governance.judge), frontier
|
|
29
|
+
model, over deliverable + diff + evidence report. REVISE loops
|
|
30
|
+
the work back before the personas run (max 2); PASS findings
|
|
31
|
+
become reviewer input. Record it:
|
|
32
|
+
arka-py -m core.evals.record_cli --kind judge
|
|
26
33
|
1. Run the evidence engine over the project/diff:
|
|
27
34
|
~/.arkaos/bin/arka-py -m core.governance.evidence_checks <project_dir> \
|
|
28
35
|
[--changed-files f1,f2] [--test-command '...'] --json
|
|
@@ -46,6 +53,15 @@ Any Department Workflow:
|
|
|
46
53
|
(~/.arkaos/telemetry/qg-verdicts.jsonl). Applies to EVERY review,
|
|
47
54
|
not only eval runs; --eval-task-id only when the review judged a
|
|
48
55
|
config/evals task.
|
|
56
|
+
7. Recipe promotion (Interaction Reform PR7, APPROVED reusable
|
|
57
|
+
features only): when the deliverable is a feature worth reusing
|
|
58
|
+
across projects (auth flow, payment integration, a standard UI
|
|
59
|
+
pattern…), PROPOSE to the operator "promote this to a recipe?".
|
|
60
|
+
On confirmation, capture it —
|
|
61
|
+
`arka-py -m core.knowledge.recipes_cli capture --spec <spec.json>`
|
|
62
|
+
(spec = {recipe, narrative, files}). Capture is fail-closed:
|
|
63
|
+
every field and file is sanitized first, refused without a
|
|
64
|
+
redaction config. Never silent — always operator-confirmed.
|
|
49
65
|
Phase N: DELIVERY
|
|
50
66
|
→ Only reaches user after APPROVED from all three
|
|
51
67
|
```
|
package/installer/doctor.js
CHANGED
|
@@ -208,6 +208,13 @@ const checks = [
|
|
|
208
208
|
check: () => graphifyDoctor().installed,
|
|
209
209
|
fix: () => graphifyDoctor().hint || "Run: uv tool install graphifyy (or pipx install graphifyy)",
|
|
210
210
|
},
|
|
211
|
+
{
|
|
212
|
+
name: "codebase-memory",
|
|
213
|
+
description: "codebase-memory-mcp binary present (default consistency layer for code stacks)",
|
|
214
|
+
severity: "warn",
|
|
215
|
+
check: () => commandExists("codebase-memory-mcp"),
|
|
216
|
+
fix: () => "Install codebase-memory-mcp (see mcps/registry.json entry for the one-liner), then /arka update to activate per project",
|
|
217
|
+
},
|
|
211
218
|
{
|
|
212
219
|
name: "magic-api-key",
|
|
213
220
|
description: "Magic API key configured (frontend UI/UX — Magic MCP)",
|
package/installer/index.js
CHANGED
|
@@ -372,6 +372,26 @@ export async function install({ runtime, path, force, skipSystem, withOllama })
|
|
|
372
372
|
console.log(` Warning: could not seed worktree.baseRef (${err.message})`);
|
|
373
373
|
}
|
|
374
374
|
|
|
375
|
+
// Interaction Reform PR1 — install the ArkaOS output style and seed
|
|
376
|
+
// it as the Claude Code default. File copy is unconditional (making
|
|
377
|
+
// the style available is not a preference); the settings seed is
|
|
378
|
+
// if-absent only — an explicit operator choice is never overridden.
|
|
379
|
+
try {
|
|
380
|
+
const { installOutputStyles, seedOutputStyleDefault } = await import("./output-style.js");
|
|
381
|
+
const styleResult = installOutputStyles({
|
|
382
|
+
sourceDir: join(ARKAOS_ROOT, "config", "output-styles"),
|
|
383
|
+
});
|
|
384
|
+
if (styleResult.copied > 0) {
|
|
385
|
+
console.log(` ${styleResult.copied} output style(s) installed (~/.claude/output-styles/).`);
|
|
386
|
+
}
|
|
387
|
+
const seedResult = seedOutputStyleDefault({ runtime });
|
|
388
|
+
if (!seedResult.skipped && seedResult.action === "created") {
|
|
389
|
+
console.log(` outputStyle default set to "${seedResult.value}".`);
|
|
390
|
+
}
|
|
391
|
+
} catch (err) {
|
|
392
|
+
console.log(` Warning: could not install output style (${err.message})`);
|
|
393
|
+
}
|
|
394
|
+
|
|
375
395
|
// PR43 v2.62.0 — auto-install default Claude Code plugins. Only runs
|
|
376
396
|
// when runtime is Claude Code AND the `claude` CLI is available.
|
|
377
397
|
// Idempotent: skips plugins already in installed_plugins.json.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// ArkaOS output style install + default seed (Interaction Reform PR1).
|
|
2
|
+
//
|
|
3
|
+
// The ArkaOS output style used to exist only as a hand-placed file in
|
|
4
|
+
// ~/.claude/output-styles/ — orphaned from the installer, so fresh
|
|
5
|
+
// installs never got the branded personality and /config showed the
|
|
6
|
+
// generic default. This module makes the repo the source of truth:
|
|
7
|
+
//
|
|
8
|
+
// 1. Copies config/output-styles/*.md → ~/.claude/output-styles/
|
|
9
|
+
// (always — the file being available is not a preference).
|
|
10
|
+
// 2. Seeds `"outputStyle": "ArkaOS"` in ~/.claude/settings.json
|
|
11
|
+
// ONLY when the key is absent. An operator who explicitly chose
|
|
12
|
+
// another style (including "default") is never overridden — the
|
|
13
|
+
// same seed-if-absent contract as worktree-baseref.js and
|
|
14
|
+
// config-seed.js.
|
|
15
|
+
//
|
|
16
|
+
// Never raises — failures are non-fatal install warnings.
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
copyFileSync,
|
|
20
|
+
existsSync,
|
|
21
|
+
mkdirSync,
|
|
22
|
+
readdirSync,
|
|
23
|
+
readFileSync,
|
|
24
|
+
renameSync,
|
|
25
|
+
writeFileSync,
|
|
26
|
+
} from "node:fs";
|
|
27
|
+
import { homedir } from "node:os";
|
|
28
|
+
import { join } from "node:path";
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
export const DEFAULT_OUTPUT_STYLE = "ArkaOS";
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
export function installOutputStyles({
|
|
35
|
+
sourceDir,
|
|
36
|
+
home = homedir(),
|
|
37
|
+
} = {}) {
|
|
38
|
+
if (!sourceDir || !existsSync(sourceDir)) {
|
|
39
|
+
return { skipped: "source-not-found", copied: 0 };
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const destDir = join(home, ".claude", "output-styles");
|
|
43
|
+
mkdirSync(destDir, { recursive: true });
|
|
44
|
+
let copied = 0;
|
|
45
|
+
for (const name of readdirSync(sourceDir)) {
|
|
46
|
+
if (!name.endsWith(".md")) continue;
|
|
47
|
+
copyFileSync(join(sourceDir, name), join(destDir, name));
|
|
48
|
+
copied += 1;
|
|
49
|
+
}
|
|
50
|
+
return { skipped: null, copied };
|
|
51
|
+
} catch {
|
|
52
|
+
return { skipped: "copy-failed", copied: 0 };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
export function seedOutputStyleDefault({
|
|
58
|
+
runtime = "claude-code",
|
|
59
|
+
home = homedir(),
|
|
60
|
+
defaultValue = DEFAULT_OUTPUT_STYLE,
|
|
61
|
+
} = {}) {
|
|
62
|
+
if (runtime !== "claude-code") {
|
|
63
|
+
return { skipped: "runtime-not-claude-code", action: null };
|
|
64
|
+
}
|
|
65
|
+
const settingsPath = join(home, ".claude", "settings.json");
|
|
66
|
+
if (!existsSync(settingsPath)) {
|
|
67
|
+
return { skipped: "claude-settings-not-found", action: null };
|
|
68
|
+
}
|
|
69
|
+
let settings;
|
|
70
|
+
try {
|
|
71
|
+
settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
72
|
+
} catch {
|
|
73
|
+
return { skipped: "settings-not-parseable", action: null };
|
|
74
|
+
}
|
|
75
|
+
if (typeof settings !== "object" || settings === null) {
|
|
76
|
+
return { skipped: "settings-not-object", action: null };
|
|
77
|
+
}
|
|
78
|
+
if (typeof settings.outputStyle === "string" && settings.outputStyle) {
|
|
79
|
+
return { skipped: null, action: "noop", value: settings.outputStyle };
|
|
80
|
+
}
|
|
81
|
+
settings.outputStyle = defaultValue;
|
|
82
|
+
const tmp = `${settingsPath}.tmp-${process.pid}`;
|
|
83
|
+
try {
|
|
84
|
+
writeFileSync(tmp, JSON.stringify(settings, null, 2) + "\n");
|
|
85
|
+
renameSync(tmp, settingsPath);
|
|
86
|
+
} catch {
|
|
87
|
+
// Read-only ~/.claude, disk full… — "never raises" must be true of
|
|
88
|
+
// THIS module, not delegated to caller try/catch (QG blocker M1).
|
|
89
|
+
return { skipped: "write-failed", action: null };
|
|
90
|
+
}
|
|
91
|
+
return { skipped: null, action: "created", value: defaultValue };
|
|
92
|
+
}
|
package/installer/update.js
CHANGED
|
@@ -174,6 +174,25 @@ export async function update() {
|
|
|
174
174
|
copyFileSync(statuslineSrc, join(installDir, "config", statuslineFile));
|
|
175
175
|
console.log(" ✓ Statusline updated");
|
|
176
176
|
}
|
|
177
|
+
// Interaction Reform PR1 — refresh the ArkaOS output style file and
|
|
178
|
+
// seed the default (if-absent; explicit operator choice preserved).
|
|
179
|
+
try {
|
|
180
|
+
const { installOutputStyles, seedOutputStyleDefault } = await import("./output-style.js");
|
|
181
|
+
const styleResult = installOutputStyles({
|
|
182
|
+
sourceDir: join(ARKAOS_ROOT, "config", "output-styles"),
|
|
183
|
+
});
|
|
184
|
+
if (styleResult.copied > 0) {
|
|
185
|
+
console.log(" ✓ Output style updated");
|
|
186
|
+
}
|
|
187
|
+
const seedResult = seedOutputStyleDefault({
|
|
188
|
+
runtime: manifest.runtime || "claude-code",
|
|
189
|
+
});
|
|
190
|
+
if (!seedResult.skipped && seedResult.action === "created") {
|
|
191
|
+
console.log(` ✓ outputStyle default set to "${seedResult.value}"`);
|
|
192
|
+
}
|
|
193
|
+
} catch (err) {
|
|
194
|
+
console.log(` ⚠ Output style update failed (${err.message})`);
|
|
195
|
+
}
|
|
177
196
|
|
|
178
197
|
// ── 3. Update hooks ──
|
|
179
198
|
console.log(" [3/8] Updating hooks...");
|
|
@@ -51,8 +51,14 @@ export function seedWorktreeBaseRef({
|
|
|
51
51
|
}
|
|
52
52
|
settings.worktree = { ...(existing ?? {}), baseRef: defaultValue };
|
|
53
53
|
const tmp = `${settingsPath}.tmp-${process.pid}`;
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
try {
|
|
55
|
+
writeFileSync(tmp, JSON.stringify(settings, null, 2) + "\n");
|
|
56
|
+
renameSync(tmp, settingsPath);
|
|
57
|
+
} catch {
|
|
58
|
+
// Keeps the header's "never raises" claim true inside the module
|
|
59
|
+
// (QG blocker M1 parity, PR1 Interaction Reform).
|
|
60
|
+
return { skipped: "write-failed", action: null };
|
|
61
|
+
}
|
|
56
62
|
return {
|
|
57
63
|
skipped: null,
|
|
58
64
|
action: existing ? "merged" : "created",
|
|
@@ -462,5 +462,9 @@
|
|
|
462
462
|
"dev-spec-list": {
|
|
463
463
|
"keywords": ["list specs", "show specs", "existing specs", "all specifications", "project specs"],
|
|
464
464
|
"examples": ["show me all specs for this project", "list existing specifications", "what specs do we have"]
|
|
465
|
+
},
|
|
466
|
+
"arka-recipes": {
|
|
467
|
+
"keywords": ["recipe", "recipes", "receita", "reuse", "validated build", "apply recipe", "feature template"],
|
|
468
|
+
"examples": ["que receitas temos", "apply the laravel login recipe", "reusa a receita de auth"]
|
|
465
469
|
}
|
|
466
470
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_meta": {
|
|
3
3
|
"version": "3.0.0",
|
|
4
|
-
"generated": "2026-07-
|
|
5
|
-
"total_commands":
|
|
4
|
+
"generated": "2026-07-09T19:22:12Z",
|
|
5
|
+
"total_commands": 264,
|
|
6
6
|
"generator": "core/registry/generator.py",
|
|
7
7
|
"departments": {
|
|
8
|
-
"arka":
|
|
8
|
+
"arka": 21,
|
|
9
9
|
"brand": 12,
|
|
10
10
|
"community": 14,
|
|
11
11
|
"content": 14,
|
|
@@ -317,6 +317,44 @@
|
|
|
317
317
|
"requires_branch": false,
|
|
318
318
|
"modifies_code": false
|
|
319
319
|
},
|
|
320
|
+
{
|
|
321
|
+
"id": "arka-refine",
|
|
322
|
+
"command": "/arka refine <rough idea>",
|
|
323
|
+
"department": "arka",
|
|
324
|
+
"description": "Turn a vague or domain-unfamiliar ask into a precise English prompt by asking about the topic first (arka-refine). Auto-suggested on vague requests.",
|
|
325
|
+
"lead_agent": "arka",
|
|
326
|
+
"keywords": [],
|
|
327
|
+
"examples": [],
|
|
328
|
+
"source": "builtin",
|
|
329
|
+
"tier": 2,
|
|
330
|
+
"requires_branch": false,
|
|
331
|
+
"modifies_code": false
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
"id": "arka-recipes",
|
|
335
|
+
"command": "/arka recipes [list\\|show\\|apply] <slug>",
|
|
336
|
+
"department": "arka",
|
|
337
|
+
"description": "Reuse validated QG-approved feature builds (arka-recipes). Synapse L7.6 auto-surfaces matches.",
|
|
338
|
+
"lead_agent": "arka",
|
|
339
|
+
"keywords": [
|
|
340
|
+
"recipe",
|
|
341
|
+
"recipes",
|
|
342
|
+
"receita",
|
|
343
|
+
"reuse",
|
|
344
|
+
"validated build",
|
|
345
|
+
"apply recipe",
|
|
346
|
+
"feature template"
|
|
347
|
+
],
|
|
348
|
+
"examples": [
|
|
349
|
+
"que receitas temos",
|
|
350
|
+
"apply the laravel login recipe",
|
|
351
|
+
"reusa a receita de auth"
|
|
352
|
+
],
|
|
353
|
+
"source": "builtin",
|
|
354
|
+
"tier": 2,
|
|
355
|
+
"requires_branch": false,
|
|
356
|
+
"modifies_code": false
|
|
357
|
+
},
|
|
320
358
|
{
|
|
321
359
|
"id": "do",
|
|
322
360
|
"command": "/do <description>",
|
package/package.json
CHANGED