arkaos 4.34.0 → 4.36.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 (70) hide show
  1. package/THE-ARKAOS-GUIDE.md +1 -1
  2. package/VERSION +1 -1
  3. package/bin/arka-menubar.py +460 -0
  4. package/config/install-profiles.json +75 -0
  5. package/core/runtime/opencode.py +252 -0
  6. package/core/runtime/registry.py +6 -1
  7. package/harness/codex/AGENTS.md +1 -1
  8. package/harness/copilot/copilot-instructions.md +1 -1
  9. package/harness/cursor/rules/arkaos.mdc +2 -2
  10. package/harness/gemini/GEMINI.md +1 -1
  11. package/harness/opencode/AGENTS.md +1 -1
  12. package/harness/opencode/agents/arka-architect-gabriel.md +12 -0
  13. package/harness/opencode/agents/arka-brand-director-valentina.md +12 -0
  14. package/harness/opencode/agents/arka-cfo-helena.md +12 -0
  15. package/harness/opencode/agents/arka-chief-of-staff-afonso.md +12 -0
  16. package/harness/opencode/agents/arka-community-strategist-beatriz.md +12 -0
  17. package/harness/opencode/agents/arka-content-strategist-rafael.md +12 -0
  18. package/harness/opencode/agents/arka-conversion-strategist-ines.md +12 -0
  19. package/harness/opencode/agents/arka-coo-sofia.md +12 -0
  20. package/harness/opencode/agents/arka-copy-director-eduardo.md +12 -0
  21. package/harness/opencode/agents/arka-cqo-marta.md +12 -0
  22. package/harness/opencode/agents/arka-cto-marco.md +12 -0
  23. package/harness/opencode/agents/arka-design-ops-lead-iris.md +12 -0
  24. package/harness/opencode/agents/arka-ecom-director-ricardo.md +12 -0
  25. package/harness/opencode/agents/arka-knowledge-director-clara.md +12 -0
  26. package/harness/opencode/agents/arka-leadership-director-rodrigo.md +12 -0
  27. package/harness/opencode/agents/arka-marketing-director-luna.md +12 -0
  28. package/harness/opencode/agents/arka-ops-lead-daniel.md +12 -0
  29. package/harness/opencode/agents/arka-pm-director-carolina.md +12 -0
  30. package/harness/opencode/agents/arka-revops-lead-vicente.md +12 -0
  31. package/harness/opencode/agents/arka-saas-strategist-tiago.md +12 -0
  32. package/harness/opencode/agents/arka-sales-director-miguel.md +12 -0
  33. package/harness/opencode/agents/arka-strategy-director-tomas.md +12 -0
  34. package/harness/opencode/agents/arka-tech-director-francisca.md +12 -0
  35. package/harness/opencode/agents/arka-tech-lead-paulo.md +12 -0
  36. package/harness/opencode/agents/arka-video-producer-simao.md +12 -0
  37. package/harness/opencode/commands/arka-brand.md +9 -0
  38. package/harness/opencode/commands/arka-community.md +9 -0
  39. package/harness/opencode/commands/arka-content.md +9 -0
  40. package/harness/opencode/commands/arka-dev.md +9 -0
  41. package/harness/opencode/commands/arka-ecom.md +9 -0
  42. package/harness/opencode/commands/arka-fin.md +9 -0
  43. package/harness/opencode/commands/arka-kb.md +9 -0
  44. package/harness/opencode/commands/arka-landing.md +9 -0
  45. package/harness/opencode/commands/arka-lead.md +9 -0
  46. package/harness/opencode/commands/arka-mkt.md +9 -0
  47. package/harness/opencode/commands/arka-ops.md +9 -0
  48. package/harness/opencode/commands/arka-org.md +9 -0
  49. package/harness/opencode/commands/arka-pm.md +9 -0
  50. package/harness/opencode/commands/arka-saas.md +9 -0
  51. package/harness/opencode/commands/arka-sales.md +9 -0
  52. package/harness/opencode/commands/arka-strat.md +9 -0
  53. package/harness/opencode/opencode.json +16 -0
  54. package/harness/zed/.rules +1 -1
  55. package/installer/adapters/opencode.js +118 -0
  56. package/installer/cli.js +31 -1
  57. package/installer/detect-runtime.js +18 -0
  58. package/installer/doctor.js +202 -2
  59. package/installer/index.js +174 -88
  60. package/installer/menubar.js +262 -0
  61. package/installer/product-stats.js +93 -0
  62. package/installer/profile.js +75 -0
  63. package/installer/prompts.js +303 -41
  64. package/installer/services.js +487 -0
  65. package/installer/ui.js +190 -0
  66. package/installer/update.js +212 -95
  67. package/knowledge/skills-manifest.json +1 -1
  68. package/package.json +6 -1
  69. package/pyproject.toml +1 -1
  70. package/scripts/harness_gen.py +98 -1
@@ -7,9 +7,40 @@ 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
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";
10
17
 
11
18
  const INSTALL_DIR = join(homedir(), ".arkaos");
12
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
+
13
44
  // Resolve a single command via the platform-native locator. Returns true
14
45
  // when the command is discoverable on PATH, false otherwise. stderr is
15
46
  // suppressed through Node's stdio option so the probe does not print
@@ -414,6 +445,7 @@ export const checks = [
414
445
  name: "ollama",
415
446
  description: "Ollama present (optional — cognitive layer LLM runtime)",
416
447
  severity: "warn",
448
+ minProfile: "local-ai",
417
449
  check: () => checkOllama().installed,
418
450
  fix: () => {
419
451
  const s = checkOllama();
@@ -500,6 +532,7 @@ export const checks = [
500
532
  name: "ffmpeg",
501
533
  description: "FFmpeg present (video encode/cut for Hyperframes + transcription workflows)",
502
534
  severity: "warn",
535
+ minProfile: "complete",
503
536
  check: () => commandExists("ffmpeg"),
504
537
  fix: () => "Install FFmpeg: brew install ffmpeg (macOS) / apt install ffmpeg (Linux) / winget install Gyan.FFmpeg (Windows)",
505
538
  },
@@ -625,6 +658,120 @@ export const checks = [
625
658
  check: () => companionPluginsInstalled(),
626
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",
627
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
+ },
628
775
  ];
629
776
 
630
777
  // ─── Windows-only checks ───────────────────────────────────────────────
@@ -709,14 +856,50 @@ export async function doctor(options = {}) {
709
856
  console.log(` ✗ Venv repair failed (${result.reason})`);
710
857
  }
711
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
+ }
712
885
  console.log("");
713
886
  }
714
887
 
888
+ const activeProfile = currentInstallProfile();
715
889
  let passed = 0;
716
890
  let warned = 0;
717
891
  let failed = 0;
892
+ let skipped = 0;
718
893
 
719
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
+ }
720
903
  // A single check that throws must not crash the rest of the doctor.
721
904
  // Treat the exception as "check failed" and record a short hint so
722
905
  // the user can see what blew up. Also keep any stack-trace noise
@@ -752,7 +935,8 @@ export async function doctor(options = {}) {
752
935
  }
753
936
  }
754
937
 
755
- console.log(`\n Results: ${passed} passed, ${warned} warnings, ${failed} failures\n`);
938
+ const skippedSuffix = skipped > 0 ? `, ${skipped} skipped` : "";
939
+ console.log(`\n Results: ${passed} passed, ${warned} warnings, ${failed} failures${skippedSuffix}\n`);
756
940
  await securityAdvisory();
757
941
  if (failed > 0) process.exit(1);
758
942
  }
@@ -763,10 +947,26 @@ export async function doctor(options = {}) {
763
947
  // (core.governance.harness_scanner_cli).
764
948
  function doctorJson() {
765
949
  const results = [];
950
+ const activeProfile = currentInstallProfile();
766
951
  let passed = 0;
767
952
  let warned = 0;
768
953
  let failed = 0;
954
+ let skipped = 0;
769
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
+ }
770
970
  let ok = false;
771
971
  let error = null;
772
972
  try {
@@ -792,7 +992,7 @@ function doctorJson() {
792
992
  }
793
993
  console.log(JSON.stringify({
794
994
  checks: results,
795
- summary: { passed, warned, failed, total: results.length },
995
+ summary: { passed, warned, failed, skipped, total: results.length },
796
996
  }));
797
997
  if (failed > 0) process.exit(1);
798
998
  }