arkaos 4.33.0 → 4.35.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/THE-ARKAOS-GUIDE.md +1 -1
- package/VERSION +1 -1
- package/bin/arka-menubar.py +407 -0
- package/config/hooks/session-start.sh +1 -1
- package/config/install-profiles.json +75 -0
- package/config/statusline.sh +6 -1
- package/core/hooks/session_start.py +69 -28
- package/core/runtime/opencode.py +113 -0
- package/core/runtime/registry.py +6 -1
- package/harness/codex/AGENTS.md +1 -1
- package/harness/copilot/copilot-instructions.md +1 -1
- package/harness/cursor/rules/arkaos.mdc +2 -2
- package/harness/gemini/GEMINI.md +1 -1
- package/harness/opencode/AGENTS.md +1 -1
- package/harness/opencode/agents/arka-architect-gabriel.md +12 -0
- package/harness/opencode/agents/arka-brand-director-valentina.md +12 -0
- package/harness/opencode/agents/arka-cfo-helena.md +12 -0
- package/harness/opencode/agents/arka-chief-of-staff-afonso.md +12 -0
- package/harness/opencode/agents/arka-community-strategist-beatriz.md +12 -0
- package/harness/opencode/agents/arka-content-strategist-rafael.md +12 -0
- package/harness/opencode/agents/arka-conversion-strategist-ines.md +12 -0
- package/harness/opencode/agents/arka-coo-sofia.md +12 -0
- package/harness/opencode/agents/arka-copy-director-eduardo.md +12 -0
- package/harness/opencode/agents/arka-cqo-marta.md +12 -0
- package/harness/opencode/agents/arka-cto-marco.md +12 -0
- package/harness/opencode/agents/arka-design-ops-lead-iris.md +12 -0
- package/harness/opencode/agents/arka-ecom-director-ricardo.md +12 -0
- package/harness/opencode/agents/arka-knowledge-director-clara.md +12 -0
- package/harness/opencode/agents/arka-leadership-director-rodrigo.md +12 -0
- package/harness/opencode/agents/arka-marketing-director-luna.md +12 -0
- package/harness/opencode/agents/arka-ops-lead-daniel.md +12 -0
- package/harness/opencode/agents/arka-pm-director-carolina.md +12 -0
- package/harness/opencode/agents/arka-revops-lead-vicente.md +12 -0
- package/harness/opencode/agents/arka-saas-strategist-tiago.md +12 -0
- package/harness/opencode/agents/arka-sales-director-miguel.md +12 -0
- package/harness/opencode/agents/arka-strategy-director-tomas.md +12 -0
- package/harness/opencode/agents/arka-tech-director-francisca.md +12 -0
- package/harness/opencode/agents/arka-tech-lead-paulo.md +12 -0
- package/harness/opencode/agents/arka-video-producer-simao.md +12 -0
- package/harness/opencode/commands/arka-brand.md +9 -0
- package/harness/opencode/commands/arka-community.md +9 -0
- package/harness/opencode/commands/arka-content.md +9 -0
- package/harness/opencode/commands/arka-dev.md +9 -0
- package/harness/opencode/commands/arka-ecom.md +9 -0
- package/harness/opencode/commands/arka-fin.md +9 -0
- package/harness/opencode/commands/arka-kb.md +9 -0
- package/harness/opencode/commands/arka-landing.md +9 -0
- package/harness/opencode/commands/arka-lead.md +9 -0
- package/harness/opencode/commands/arka-mkt.md +9 -0
- package/harness/opencode/commands/arka-ops.md +9 -0
- package/harness/opencode/commands/arka-org.md +9 -0
- package/harness/opencode/commands/arka-pm.md +9 -0
- package/harness/opencode/commands/arka-saas.md +9 -0
- package/harness/opencode/commands/arka-sales.md +9 -0
- package/harness/opencode/commands/arka-strat.md +9 -0
- package/harness/opencode/opencode.json +16 -0
- package/harness/zed/.rules +1 -1
- package/installer/adapters/opencode.js +118 -0
- package/installer/autoupdate.js +249 -0
- package/installer/cli.js +38 -1
- package/installer/core-snapshot.js +5 -1
- package/installer/detect-runtime.js +18 -0
- package/installer/doctor.js +213 -2
- package/installer/index.js +182 -85
- package/installer/menubar.js +262 -0
- package/installer/product-stats.js +93 -0
- package/installer/profile.js +75 -0
- package/installer/prompts.js +303 -41
- package/installer/services.js +487 -0
- package/installer/ui.js +190 -0
- package/installer/update.js +219 -91
- package/knowledge/skills-manifest.json +1 -1
- package/package.json +6 -1
- package/pyproject.toml +1 -1
- package/scripts/auto-update.sh +188 -0
- package/scripts/harness_gen.py +98 -1
package/installer/doctor.js
CHANGED
|
@@ -6,9 +6,41 @@ import { getArkaosPython, getVenvPython, canImportCore, getRepoRoot, diagnoseVen
|
|
|
6
6
|
import { IS_WINDOWS, HOOK_EXT, CMD_FINDER } from "./platform.js";
|
|
7
7
|
import { checkNode, checkObsidian, checkOllama } from "./system-tools.js";
|
|
8
8
|
import { graphifyDoctor } from "./graphify.js";
|
|
9
|
+
import { status as autoupdateStatus } from "./autoupdate.js";
|
|
10
|
+
import { normalizeProfileFlag, profileIncludes } from "./profile.js";
|
|
11
|
+
import {
|
|
12
|
+
loadProfilesManifest,
|
|
13
|
+
parseExecutionModel,
|
|
14
|
+
resolveServicesForProfile,
|
|
15
|
+
} from "./services.js";
|
|
16
|
+
import { menubarHealthy } from "./menubar.js";
|
|
9
17
|
|
|
10
18
|
const INSTALL_DIR = join(homedir(), ".arkaos");
|
|
11
19
|
|
|
20
|
+
// ─── Install-profile awareness (Foundation PR-4) ────────────────────────
|
|
21
|
+
// Checks may declare `minProfile`; when the machine's persisted profile
|
|
22
|
+
// sits below it on the ladder (essential ⊂ complete ⊂ local-ai) the
|
|
23
|
+
// check reports "skipped (not in <profile> profile)" instead of a
|
|
24
|
+
// misleading warn — an essential machine without Ollama is healthy.
|
|
25
|
+
|
|
26
|
+
export function currentInstallProfile(
|
|
27
|
+
profilePath = join(INSTALL_DIR, "profile.json")
|
|
28
|
+
) {
|
|
29
|
+
try {
|
|
30
|
+
const profile = JSON.parse(readFileSync(profilePath, "utf-8"));
|
|
31
|
+
return normalizeProfileFlag(profile.installProfile) || "essential";
|
|
32
|
+
} catch {
|
|
33
|
+
return "essential";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Null when the check applies; otherwise the human-readable skip reason. */
|
|
38
|
+
export function checkSkipReason(check, activeProfile) {
|
|
39
|
+
if (!check.minProfile) return null;
|
|
40
|
+
if (profileIncludes(activeProfile, check.minProfile)) return null;
|
|
41
|
+
return `not in ${activeProfile} profile`;
|
|
42
|
+
}
|
|
43
|
+
|
|
12
44
|
// Resolve a single command via the platform-native locator. Returns true
|
|
13
45
|
// when the command is discoverable on PATH, false otherwise. stderr is
|
|
14
46
|
// suppressed through Node's stdio option so the probe does not print
|
|
@@ -413,6 +445,7 @@ export const checks = [
|
|
|
413
445
|
name: "ollama",
|
|
414
446
|
description: "Ollama present (optional — cognitive layer LLM runtime)",
|
|
415
447
|
severity: "warn",
|
|
448
|
+
minProfile: "local-ai",
|
|
416
449
|
check: () => checkOllama().installed,
|
|
417
450
|
fix: () => {
|
|
418
451
|
const s = checkOllama();
|
|
@@ -499,6 +532,7 @@ export const checks = [
|
|
|
499
532
|
name: "ffmpeg",
|
|
500
533
|
description: "FFmpeg present (video encode/cut for Hyperframes + transcription workflows)",
|
|
501
534
|
severity: "warn",
|
|
535
|
+
minProfile: "complete",
|
|
502
536
|
check: () => commandExists("ffmpeg"),
|
|
503
537
|
fix: () => "Install FFmpeg: brew install ffmpeg (macOS) / apt install ffmpeg (Linux) / winget install Gyan.FFmpeg (Windows)",
|
|
504
538
|
},
|
|
@@ -563,6 +597,16 @@ export const checks = [
|
|
|
563
597
|
check: () => statuslineConfigured(),
|
|
564
598
|
fix: () => "Run: npx arkaos install --force (redeploys and wires the statusline)",
|
|
565
599
|
},
|
|
600
|
+
{
|
|
601
|
+
name: "autoupdate",
|
|
602
|
+
description: "Auto-update daemon installed (or explicit user opt-out)",
|
|
603
|
+
severity: "warn",
|
|
604
|
+
check: () => {
|
|
605
|
+
const s = autoupdateStatus();
|
|
606
|
+
return !s.supported || s.optout || s.installed;
|
|
607
|
+
},
|
|
608
|
+
fix: () => "Run: npx arkaos autoupdate enable",
|
|
609
|
+
},
|
|
566
610
|
{
|
|
567
611
|
name: "hooks-wired",
|
|
568
612
|
description: "Hook chain referenced by ~/.claude/settings.json (governance live)",
|
|
@@ -614,6 +658,120 @@ export const checks = [
|
|
|
614
658
|
check: () => companionPluginsInstalled(),
|
|
615
659
|
fix: () => "claude plugin marketplace add obra/superpowers-marketplace && claude plugin install superpowers@superpowers-marketplace; claude plugin marketplace add thedotmack/claude-mem && claude plugin install claude-mem@thedotmack",
|
|
616
660
|
},
|
|
661
|
+
// ─── Install-profile checks (Foundation PR-4) — all warn-only ─────────
|
|
662
|
+
{
|
|
663
|
+
name: "install-profile",
|
|
664
|
+
description: "Install profile valid (profile.json + install-profiles manifest)",
|
|
665
|
+
severity: "warn",
|
|
666
|
+
check: () => {
|
|
667
|
+
// A profile.json that predates PR-3 (no installProfile key) is
|
|
668
|
+
// valid — it means essential. An explicitly invalid value or an
|
|
669
|
+
// unloadable/unresolvable manifest is the failure.
|
|
670
|
+
const profilePath = join(INSTALL_DIR, "profile.json");
|
|
671
|
+
if (existsSync(profilePath)) {
|
|
672
|
+
try {
|
|
673
|
+
const profile = JSON.parse(readFileSync(profilePath, "utf-8"));
|
|
674
|
+
if (
|
|
675
|
+
profile.installProfile !== undefined &&
|
|
676
|
+
!normalizeProfileFlag(profile.installProfile)
|
|
677
|
+
) {
|
|
678
|
+
return false;
|
|
679
|
+
}
|
|
680
|
+
} catch {
|
|
681
|
+
return false;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
const repoRoot = getRepoRoot();
|
|
685
|
+
if (!repoRoot) return true; // no repo reference — not a profile problem
|
|
686
|
+
try {
|
|
687
|
+
const manifest = loadProfilesManifest(repoRoot);
|
|
688
|
+
resolveServicesForProfile(currentInstallProfile(), manifest);
|
|
689
|
+
return true;
|
|
690
|
+
} catch {
|
|
691
|
+
return false;
|
|
692
|
+
}
|
|
693
|
+
},
|
|
694
|
+
fix: () =>
|
|
695
|
+
"profile.json installProfile must be one of essential|complete|local-ai; refresh the manifest via: npx arkaos@latest update",
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
name: "litellm-proxy",
|
|
699
|
+
description: "LiteLLM proxy installed (gateway prerequisite — complete profile)",
|
|
700
|
+
severity: "warn",
|
|
701
|
+
minProfile: "complete",
|
|
702
|
+
check: () => {
|
|
703
|
+
const py = getArkaosPython();
|
|
704
|
+
if (!py) return false;
|
|
705
|
+
try {
|
|
706
|
+
execSync(`"${py}" -c "import litellm"`, { stdio: "ignore", timeout: 30000 });
|
|
707
|
+
return true;
|
|
708
|
+
} catch {
|
|
709
|
+
return false;
|
|
710
|
+
}
|
|
711
|
+
},
|
|
712
|
+
fix: () =>
|
|
713
|
+
"Run: ~/.arkaos/venv/bin/pip install 'litellm[proxy]' (or: npx arkaos doctor --fix)",
|
|
714
|
+
},
|
|
715
|
+
{
|
|
716
|
+
name: "whisper",
|
|
717
|
+
description: "Whisper transcription installed (faster-whisper — complete profile)",
|
|
718
|
+
severity: "warn",
|
|
719
|
+
minProfile: "complete",
|
|
720
|
+
check: () => {
|
|
721
|
+
const py = getArkaosPython();
|
|
722
|
+
if (!py) return false;
|
|
723
|
+
try {
|
|
724
|
+
execSync(`"${py}" -c "import faster_whisper"`, { stdio: "ignore", timeout: 30000 });
|
|
725
|
+
return true;
|
|
726
|
+
} catch {
|
|
727
|
+
return false;
|
|
728
|
+
}
|
|
729
|
+
},
|
|
730
|
+
fix: () =>
|
|
731
|
+
"Run: ~/.arkaos/venv/bin/pip install faster-whisper (or: npx arkaos doctor --fix)",
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
name: "ollama-execution-model",
|
|
735
|
+
description: "Local execution model pulled (Model Fabric — local-ai profile)",
|
|
736
|
+
severity: "warn",
|
|
737
|
+
minProfile: "local-ai",
|
|
738
|
+
check: () => {
|
|
739
|
+
const resolved = parseExecutionModel();
|
|
740
|
+
// No ollama execution role in models.yaml — nothing to verify.
|
|
741
|
+
if (!resolved) return true;
|
|
742
|
+
try {
|
|
743
|
+
const out = execSync("ollama list", {
|
|
744
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
745
|
+
timeout: 10000,
|
|
746
|
+
}).toString();
|
|
747
|
+
return out.split(/\r?\n/).some((line) => {
|
|
748
|
+
const token = line.trim().split(/\s+/)[0];
|
|
749
|
+
return (
|
|
750
|
+
token === resolved.model ||
|
|
751
|
+
(!resolved.model.includes(":") && token.split(":")[0] === resolved.model)
|
|
752
|
+
);
|
|
753
|
+
});
|
|
754
|
+
} catch {
|
|
755
|
+
return false;
|
|
756
|
+
}
|
|
757
|
+
},
|
|
758
|
+
fix: () => {
|
|
759
|
+
const resolved = parseExecutionModel();
|
|
760
|
+
return resolved
|
|
761
|
+
? `Run: ollama pull ${resolved.model} (or: npx arkaos doctor --fix)`
|
|
762
|
+
: "Configure: npx arkaos models set execution ollama/<model>";
|
|
763
|
+
},
|
|
764
|
+
},
|
|
765
|
+
{
|
|
766
|
+
name: "menubar",
|
|
767
|
+
description: "Menu bar launcher running (macOS — ▲ quick actions)",
|
|
768
|
+
severity: "warn",
|
|
769
|
+
// Live probe, not file decoration: menubarHealthy composes file
|
|
770
|
+
// presence + opt-out semantics + `launchctl list` (QG M2). Every
|
|
771
|
+
// branch is unit-tested via the injectable exec in menubar.test.js.
|
|
772
|
+
check: () => menubarHealthy(),
|
|
773
|
+
fix: () => "Run: npx arkaos menubar enable (opt-out permanently: npx arkaos menubar disable)",
|
|
774
|
+
},
|
|
617
775
|
];
|
|
618
776
|
|
|
619
777
|
// ─── Windows-only checks ───────────────────────────────────────────────
|
|
@@ -698,14 +856,50 @@ export async function doctor(options = {}) {
|
|
|
698
856
|
console.log(` ✗ Venv repair failed (${result.reason})`);
|
|
699
857
|
}
|
|
700
858
|
}
|
|
859
|
+
|
|
860
|
+
// ─── --fix: reconcile the install profile's services (PR-4) ──────
|
|
861
|
+
// Non-interactive: consent-free services (pip into our venv, model
|
|
862
|
+
// pull) install; consent-gated ones print the exact command. Never
|
|
863
|
+
// blocks — a reconcile error degrades to a single warning line.
|
|
864
|
+
try {
|
|
865
|
+
const repoRoot = getRepoRoot();
|
|
866
|
+
if (repoRoot) {
|
|
867
|
+
const activeProfile = currentInstallProfile();
|
|
868
|
+
console.log(` → Reconciling ${activeProfile} profile services`);
|
|
869
|
+
const { reconcileServices } = await import("./services.js");
|
|
870
|
+
const results = await reconcileServices({
|
|
871
|
+
profile: activeProfile,
|
|
872
|
+
repoRoot,
|
|
873
|
+
interactive: false,
|
|
874
|
+
log: (msg) => console.log(" " + String(msg).trim()),
|
|
875
|
+
});
|
|
876
|
+
for (const r of results) {
|
|
877
|
+
if (r.status === "installed") console.log(` ✓ ${r.label} installed`);
|
|
878
|
+
else if (r.status === "failed") console.log(` ✗ ${r.label} failed${r.hint ? ` — ${r.hint}` : ""}`);
|
|
879
|
+
else if (r.status === "skipped") console.log(` · ${r.label} skipped${r.hint ? ` — ${r.hint}` : ""}`);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
} catch (err) {
|
|
883
|
+
console.log(` ⚠ Service reconciliation skipped (${err.message})`);
|
|
884
|
+
}
|
|
701
885
|
console.log("");
|
|
702
886
|
}
|
|
703
887
|
|
|
888
|
+
const activeProfile = currentInstallProfile();
|
|
704
889
|
let passed = 0;
|
|
705
890
|
let warned = 0;
|
|
706
891
|
let failed = 0;
|
|
892
|
+
let skipped = 0;
|
|
707
893
|
|
|
708
894
|
for (const check of checks) {
|
|
895
|
+
// Profile gate (PR-4): below-profile checks are informational
|
|
896
|
+
// skips, not warnings — see checkSkipReason.
|
|
897
|
+
const skipReason = checkSkipReason(check, activeProfile);
|
|
898
|
+
if (skipReason) {
|
|
899
|
+
console.log(` \x1b[90m-\x1b[0m ${check.description} — skipped (${skipReason})`);
|
|
900
|
+
skipped++;
|
|
901
|
+
continue;
|
|
902
|
+
}
|
|
709
903
|
// A single check that throws must not crash the rest of the doctor.
|
|
710
904
|
// Treat the exception as "check failed" and record a short hint so
|
|
711
905
|
// the user can see what blew up. Also keep any stack-trace noise
|
|
@@ -741,7 +935,8 @@ export async function doctor(options = {}) {
|
|
|
741
935
|
}
|
|
742
936
|
}
|
|
743
937
|
|
|
744
|
-
|
|
938
|
+
const skippedSuffix = skipped > 0 ? `, ${skipped} skipped` : "";
|
|
939
|
+
console.log(`\n Results: ${passed} passed, ${warned} warnings, ${failed} failures${skippedSuffix}\n`);
|
|
745
940
|
await securityAdvisory();
|
|
746
941
|
if (failed > 0) process.exit(1);
|
|
747
942
|
}
|
|
@@ -752,10 +947,26 @@ export async function doctor(options = {}) {
|
|
|
752
947
|
// (core.governance.harness_scanner_cli).
|
|
753
948
|
function doctorJson() {
|
|
754
949
|
const results = [];
|
|
950
|
+
const activeProfile = currentInstallProfile();
|
|
755
951
|
let passed = 0;
|
|
756
952
|
let warned = 0;
|
|
757
953
|
let failed = 0;
|
|
954
|
+
let skipped = 0;
|
|
758
955
|
for (const check of checks) {
|
|
956
|
+
// Profile gate (PR-4) — same skip semantics as the human run.
|
|
957
|
+
const skipReason = checkSkipReason(check, activeProfile);
|
|
958
|
+
if (skipReason) {
|
|
959
|
+
skipped++;
|
|
960
|
+
results.push({
|
|
961
|
+
name: check.name,
|
|
962
|
+
status: "skipped",
|
|
963
|
+
severity: check.severity,
|
|
964
|
+
description: check.description,
|
|
965
|
+
fix: "",
|
|
966
|
+
skipReason,
|
|
967
|
+
});
|
|
968
|
+
continue;
|
|
969
|
+
}
|
|
759
970
|
let ok = false;
|
|
760
971
|
let error = null;
|
|
761
972
|
try {
|
|
@@ -781,7 +992,7 @@ function doctorJson() {
|
|
|
781
992
|
}
|
|
782
993
|
console.log(JSON.stringify({
|
|
783
994
|
checks: results,
|
|
784
|
-
summary: { passed, warned, failed, total: results.length },
|
|
995
|
+
summary: { passed, warned, failed, skipped, total: results.length },
|
|
785
996
|
}));
|
|
786
997
|
if (failed > 0) process.exit(1);
|
|
787
998
|
}
|