arkaos 2.22.1 → 2.25.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/comfyui/SKILL.md +2 -2
- package/arka/skills/comfyui/references/workflows.md +6 -6
- package/arka/skills/costs/SKILL.md +11 -0
- package/config/cognition/prompts/dreaming.md +3 -3
- package/config/cognition/prompts/research.md +4 -4
- package/config/hooks/user-prompt-submit.sh +6 -1
- package/core/cognition/__pycache__/auto_documentor.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/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/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,227 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: spec
|
|
3
|
+
status: approved
|
|
4
|
+
feature: installer-cross-os
|
|
5
|
+
project: ArkaOS
|
|
6
|
+
version: 2.24.0
|
|
7
|
+
pr: PR2 of 6 (Conclave roadmap 2026-05-13)
|
|
8
|
+
branch: feature/v2.24.0-installer-cross-os
|
|
9
|
+
date_created: 2026-05-13
|
|
10
|
+
tags: [spec, arkaos, installer, cross-os, v2.24.0, conclave-pr2]
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# SPEC: Installer Cross-OS Completo (v2.24.0)
|
|
14
|
+
|
|
15
|
+
## Overview
|
|
16
|
+
|
|
17
|
+
**Problem.** ArkaOS installer is ~65 % cross-OS complete. Obsidian is never auto-installed (only path-detected). Node.js is never validated. `install.sh` hardcodes the GitHub repo URL on line 25 and only scans macOS paths for Obsidian vaults on lines 625–640.
|
|
18
|
+
|
|
19
|
+
**Goal.** Deliver a full cross-OS install flow (macOS / Linux / Windows) that auto-installs what it can without sudo, reports what needs sudo with copy-paste-ready commands, and falls back gracefully when no package manager is available.
|
|
20
|
+
|
|
21
|
+
**Actors.** End users running `npx arkaos install` on fresh macOS / Linux / Windows machines; CI pipelines running headless.
|
|
22
|
+
|
|
23
|
+
## Scope
|
|
24
|
+
|
|
25
|
+
**In scope (PR2 / v2.24.0):**
|
|
26
|
+
|
|
27
|
+
| Item | Purpose |
|
|
28
|
+
|---|---|
|
|
29
|
+
| new `installer/package-manager.js` | brew / winget / snap / apt / choco detection + install helpers |
|
|
30
|
+
| new `installer/system-tools.js` | `ensureSystemTools()` phase: Obsidian, Node.js ≥ 20, Python ≥ 3.12 |
|
|
31
|
+
| modify `installer/index.js` | call `ensureSystemTools()` before venv creation |
|
|
32
|
+
| modify `installer/update.js` | re-run `ensureSystemTools()` on `npx arkaos update` |
|
|
33
|
+
| modify `installer/cli.js` | new `npx arkaos doctor` subcommand + `--no-system` flag |
|
|
34
|
+
| new `installer/doctor.js` | diagnose-only report (table format, exit codes 0/1/2) |
|
|
35
|
+
| modify `install.sh` line 25 | `ARKAOS_REPO_URL` env override (default canonical URL) |
|
|
36
|
+
| modify `install.sh` lines 625-640 | add Linux + Windows vault detection |
|
|
37
|
+
| modify `installer/index.js` dashboard step | `pnpm install` / `npm ci` in `dashboard/` if Node ≥ 20 |
|
|
38
|
+
| new `tests/installer/test_package_manager.mjs` | ≥ 10 Node tests |
|
|
39
|
+
| new `tests/installer/test_system_tools.mjs` | ≥ 5 Node tests |
|
|
40
|
+
|
|
41
|
+
**Out of scope:**
|
|
42
|
+
|
|
43
|
+
- Auto-update of system tools when newer versions exist (just detect + report).
|
|
44
|
+
- Flatpak / AppImage Obsidian variants on Linux (defer to PR6 cleanup if user demand).
|
|
45
|
+
- Interactive sudo prompt (André chose "print command for manual run" — headless-safe).
|
|
46
|
+
- Aggressive auto-install (André chose hybrid: install what can be installed without sudo; report rest).
|
|
47
|
+
|
|
48
|
+
## Acceptance Criteria
|
|
49
|
+
|
|
50
|
+
1. **Fresh macOS install.** Given a Mac without Obsidian, when `npx arkaos install` runs, then `brew install --cask obsidian` is invoked (or printed if brew missing) and Obsidian binary is detected on completion.
|
|
51
|
+
|
|
52
|
+
2. **Fresh Ubuntu install.** Given Ubuntu without Obsidian / Node, when `npx arkaos install` runs, then the installer prints copy-paste commands like:
|
|
53
|
+
```
|
|
54
|
+
To finish install, run:
|
|
55
|
+
sudo apt update && sudo apt install -y nodejs
|
|
56
|
+
sudo snap install obsidian --classic
|
|
57
|
+
Then re-run: npx arkaos install
|
|
58
|
+
```
|
|
59
|
+
and exits with code 0 (informational, not a failure).
|
|
60
|
+
|
|
61
|
+
3. **Fresh Windows 11.** Given Windows without Obsidian / Node, when `npx arkaos install` runs, then it invokes `winget install --id Obsidian.Obsidian --silent` (and similar for Node), or prints the command if elevation is denied.
|
|
62
|
+
|
|
63
|
+
4. **`npx arkaos doctor`.** Given any state, when `npx arkaos doctor` runs, then it prints a table with Obsidian / Node / Python rows showing installed/version/location/action, with exit code 0 (all OK), 1 (missing), or 2 (version mismatch).
|
|
64
|
+
|
|
65
|
+
5. **`install.sh` GitHub URL configurable.** Given `ARKAOS_REPO_URL=https://gitlab.example.com/x/y.git` is set, when `install.sh` runs in curl|bash mode, then it clones from the custom URL.
|
|
66
|
+
|
|
67
|
+
6. **`install.sh` Linux vault detection.** Given Ubuntu user with vault at `~/.local/share/obsidian/vault` OR `~/Documents/Obsidian`, when `install.sh` runs vault auto-detection, then both candidate paths are scanned.
|
|
68
|
+
|
|
69
|
+
7. **`install.sh` Windows vault detection.** Given Git Bash on Windows with vault at `$USERPROFILE/Documents/Obsidian`, when `install.sh` runs vault auto-detection, then the path is scanned.
|
|
70
|
+
|
|
71
|
+
8. **`--no-system` flag.** Given `npx arkaos install --no-system`, when installer runs, then `ensureSystemTools()` is bypassed (legacy install-only flow preserved for CI).
|
|
72
|
+
|
|
73
|
+
9. **Idempotent.** Given `ensureSystemTools()` has already installed everything, when it runs again, then no install commands fire, no side effects, exit success.
|
|
74
|
+
|
|
75
|
+
10. **Regression-free.** Given PR2 merges, when full pytest suite runs, then all 3049 tests still pass. New Node tests add ≥ 15 cases for package-manager + system-tools.
|
|
76
|
+
|
|
77
|
+
11. **shellcheck clean.** Given the updated `install.sh`, when `shellcheck install.sh` runs, then zero new warnings vs baseline.
|
|
78
|
+
|
|
79
|
+
## Data Model
|
|
80
|
+
|
|
81
|
+
N/A — pure installer logic, no persisted schema changes.
|
|
82
|
+
|
|
83
|
+
## API Contracts
|
|
84
|
+
|
|
85
|
+
### `installer/package-manager.js`
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
/**
|
|
89
|
+
* Detect the highest-priority package manager available on this host.
|
|
90
|
+
*
|
|
91
|
+
* Priority order:
|
|
92
|
+
* macOS: brew
|
|
93
|
+
* Linux: apt > snap (apt for Node, snap for Obsidian classic-confined)
|
|
94
|
+
* Windows: winget > choco
|
|
95
|
+
*
|
|
96
|
+
* Returns the canonical name string or null when nothing is available.
|
|
97
|
+
*/
|
|
98
|
+
export function detectPackageManager(): string | null
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Install a single package via the detected (or specified) manager.
|
|
102
|
+
* Never invokes sudo automatically. When sudo is required, returns
|
|
103
|
+
* the command for the caller to print and the user to run.
|
|
104
|
+
*/
|
|
105
|
+
export function installViaPackageManager(
|
|
106
|
+
pkg: string,
|
|
107
|
+
options?: { manager?: string, fallbackUrl?: string }
|
|
108
|
+
): {
|
|
109
|
+
success: boolean,
|
|
110
|
+
command: string, // command that ran or should run
|
|
111
|
+
needsSudo: boolean,
|
|
112
|
+
installed: boolean, // did we actually install in this call?
|
|
113
|
+
fallbackOpened?: boolean, // did we open the download URL?
|
|
114
|
+
error?: string,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Format an array of missing-package commands into copy-paste text. */
|
|
118
|
+
export function formatSudoInstructions(missing: SudoCommand[]): string
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### `installer/system-tools.js`
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
export type ToolStatus = {
|
|
125
|
+
name: "obsidian" | "node" | "python",
|
|
126
|
+
installed: boolean,
|
|
127
|
+
version?: string,
|
|
128
|
+
location?: string,
|
|
129
|
+
needsAction: "none" | "install" | "upgrade",
|
|
130
|
+
suggestedCommand?: string,
|
|
131
|
+
needsSudo?: boolean,
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Check each required system tool. Install what can be installed without
|
|
136
|
+
* sudo. Collect commands for tools that need sudo into `sudoCommands`
|
|
137
|
+
* so the caller can print them to the user.
|
|
138
|
+
*
|
|
139
|
+
* Idempotent: tools already installed at sufficient version are no-ops.
|
|
140
|
+
*/
|
|
141
|
+
export function ensureSystemTools(options?: {
|
|
142
|
+
skipSystem?: boolean,
|
|
143
|
+
dryRun?: boolean,
|
|
144
|
+
}): {
|
|
145
|
+
obsidian: ToolStatus,
|
|
146
|
+
node: ToolStatus,
|
|
147
|
+
python: ToolStatus,
|
|
148
|
+
sudoCommands: string[],
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function checkObsidian(): ToolStatus
|
|
152
|
+
export function checkNode(): ToolStatus
|
|
153
|
+
export function checkPython(): ToolStatus
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### `installer/doctor.js`
|
|
157
|
+
|
|
158
|
+
```javascript
|
|
159
|
+
/**
|
|
160
|
+
* `npx arkaos doctor` — diagnose-only report. Never installs.
|
|
161
|
+
* Returns exit code 0 (all OK), 1 (missing tool), 2 (version mismatch).
|
|
162
|
+
*/
|
|
163
|
+
export function runDoctor(): number
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### CLI integration
|
|
167
|
+
|
|
168
|
+
`installer/cli.js` gains:
|
|
169
|
+
- `npx arkaos doctor` subcommand → calls `runDoctor()`
|
|
170
|
+
- `--no-system` global flag → sets `options.skipSystem = true` for `ensureSystemTools()`
|
|
171
|
+
|
|
172
|
+
## Edge Cases
|
|
173
|
+
|
|
174
|
+
1. **brew not installed on macOS** → print Homebrew install one-liner, exit 0 with informational message.
|
|
175
|
+
2. **snap available but `obsidian` not in store** (older Ubuntu) → fall back to `https://obsidian.md/download` opened in browser, or printed for headless.
|
|
176
|
+
3. **Windows < 1809 without winget** → choco fallback, or printed download URL.
|
|
177
|
+
4. **Node installed but version < 20** → `needsAction: "upgrade"`, suggested command per OS.
|
|
178
|
+
5. **Multiple Python versions present** → prefer 3.12+, recommend `pyenv` if mixed.
|
|
179
|
+
6. **User installed Obsidian via Flatpak** → detect via `flatpak list md.obsidian` (best-effort), accept as installed.
|
|
180
|
+
7. **Concurrent installer runs** → checks read filesystem only; install commands themselves are idempotent (apt/brew/winget handle already-installed packages gracefully).
|
|
181
|
+
8. **curl|bash mode** → `install.sh` clones repo first; `ARKAOS_REPO_URL` env wins over hardcoded default; supports private fork redirection.
|
|
182
|
+
9. **Sudo denied** on Windows winget → print the command and ask user to re-run from an elevated prompt; do not loop or retry.
|
|
183
|
+
10. **Headless CI** → `--no-system` flag skips entirely; `arka doctor` exit codes drive CI decisions.
|
|
184
|
+
11. **Network failure during install** → catch, report, exit with copy-paste recovery command.
|
|
185
|
+
|
|
186
|
+
## Test Scenarios
|
|
187
|
+
|
|
188
|
+
| # | Scenario | Type | Expected |
|
|
189
|
+
|---|---|---|---|
|
|
190
|
+
| 1 | `detectPackageManager` on macOS w/ brew | Unit | `"brew"` |
|
|
191
|
+
| 2 | `detectPackageManager` on Ubuntu w/ apt+snap | Unit | `"apt"` (then `"snap"` for Obsidian-specific calls) |
|
|
192
|
+
| 3 | `detectPackageManager` on Windows w/ winget | Unit | `"winget"` |
|
|
193
|
+
| 4 | `detectPackageManager` on Linux w/ none | Unit | `null` |
|
|
194
|
+
| 5 | `checkObsidian` when binary in PATH | Unit | `installed: true, location: <path>` |
|
|
195
|
+
| 6 | `checkObsidian` when binary missing | Unit | `installed: false, needsAction: "install"` |
|
|
196
|
+
| 7 | `checkNode` when version < 20 | Unit | `needsAction: "upgrade"` |
|
|
197
|
+
| 8 | `checkNode` when version ≥ 20 | Unit | `needsAction: "none"` |
|
|
198
|
+
| 9 | `checkPython` when 3.11 only | Unit | `needsAction: "upgrade"` |
|
|
199
|
+
| 10 | `ensureSystemTools` w/ all 3 missing | Integration | `sudoCommands` array populated |
|
|
200
|
+
| 11 | `ensureSystemTools` idempotent | Integration | second call: no commands |
|
|
201
|
+
| 12 | `runDoctor` exit codes | Integration | 0 / 1 / 2 verified |
|
|
202
|
+
| 13 | `install.sh` with `ARKAOS_REPO_URL` override | Manual / shell | clones from custom URL |
|
|
203
|
+
| 14 | `install.sh` Linux vault detection | Manual / shell | finds `~/.local/share/obsidian/...` |
|
|
204
|
+
| 15 | `install.sh` Windows vault detection | Manual / shell | finds `$USERPROFILE/Documents/Obsidian` |
|
|
205
|
+
| 16 | `--no-system` flag bypass | Integration | `ensureSystemTools` not called |
|
|
206
|
+
| 17 | full pytest regression | Integration | 3049 still pass |
|
|
207
|
+
|
|
208
|
+
## Dependencies
|
|
209
|
+
|
|
210
|
+
- `installer/platform.js` (existing) — `IS_WINDOWS`, `CMD_FINDER`, `PYTHON_CMD`.
|
|
211
|
+
- `installer/path-resolver.js` (PR1) — for any path template substitution.
|
|
212
|
+
- `installer/python-resolver.js` (existing) — Python detection logic.
|
|
213
|
+
- Node.js `node:child_process` for `execSync` (already used).
|
|
214
|
+
- No new npm deps.
|
|
215
|
+
|
|
216
|
+
## Quality Gate Criteria
|
|
217
|
+
|
|
218
|
+
- **Eduardo:** all error / status messages read naturally in EN; copy-paste sudo commands are accurate and OS-specific; no AI clichés.
|
|
219
|
+
- **Francisca:** package-manager detection covers macOS/Ubuntu/Debian/Fedora/Arch/Windows 10+/11; `ensureSystemTools()` is fully idempotent; no shell injection vectors in execSync calls; shellcheck clean on install.sh.
|
|
220
|
+
- **Marta:** verified on 3 synthetic environments — macOS with brew, Ubuntu without snap, Windows with winget — each producing the right printed commands and side effects.
|
|
221
|
+
|
|
222
|
+
## References
|
|
223
|
+
|
|
224
|
+
- Plan: `~/.arkaos/plans/2026-05-13-arkaos-next-level-conclave.md` (PR2 section)
|
|
225
|
+
- Memory: [[project_next_level_conclave]]
|
|
226
|
+
- Existing: [[SPEC-paths-portability]] (PR1, dependency), [[platform.js]] (platform abstraction)
|
|
227
|
+
- HIGH items inherited from PR1 audit: `install.sh:25`, `install.sh:625-640`
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: spec
|
|
3
|
+
status: approved
|
|
4
|
+
feature: paths-portability
|
|
5
|
+
project: ArkaOS
|
|
6
|
+
version: 2.23.0
|
|
7
|
+
pr: PR1 of 6 (Conclave roadmap 2026-05-13)
|
|
8
|
+
branch: feature/v2.23.0-paths-portability
|
|
9
|
+
date_created: 2026-05-13
|
|
10
|
+
date_updated: 2026-05-13
|
|
11
|
+
tags: [spec, arkaos, paths, portability, refactor, v2.23.0, conclave-pr1]
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# SPEC: Paths Portability (v2.23.0)
|
|
15
|
+
|
|
16
|
+
## Overview
|
|
17
|
+
|
|
18
|
+
**Problem.** ArkaOS source contains hardcoded user-specific paths (`/Users/andreagroferreira/...`) across SKILL.md files, JSON configs, Python regexes, and cognitive prompts. These break for the **~20K users** currently running ArkaOS with different home directories and OS layouts.
|
|
19
|
+
|
|
20
|
+
**Goal.** Replace 7 CRITICAL hardcoded paths with profile-driven templates resolved at runtime, with **non-breaking auto-migration** for the existing user base.
|
|
21
|
+
|
|
22
|
+
**Actors.** ArkaOS core (skill loader, cognition collector, installer migration), end users (via `~/.arkaos/profile.json`).
|
|
23
|
+
|
|
24
|
+
## Scope
|
|
25
|
+
|
|
26
|
+
**In scope (PR1 / v2.23.0):**
|
|
27
|
+
|
|
28
|
+
| # | File | Fix |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| 1 | `knowledge/obsidian-config.json:6` | `vault_path` value resolved at load via `path_resolver.resolve_dict` (profile.vaultPath) |
|
|
31
|
+
| 2 | `core/cognition/capture/collector.py:15-17` | Regex generated from `profile.projectRoots`; fallback `os.getcwd()` |
|
|
32
|
+
| 3 | `departments/kb/skills/knowledge/SKILL.md:25` | `${VAULT_PATH}` template |
|
|
33
|
+
| 4 | `departments/ops/skills/operations/SKILL.md:59` | `${VAULT_PATH}` template |
|
|
34
|
+
| 5 | `arka/skills/comfyui/SKILL.md:22-23` + `references/workflows.md` | `${ARKA_OS_REPOS}/{purz-comfyui-workflows,lora_tester}` |
|
|
35
|
+
| 6 | `config/cognition/prompts/dreaming.md` | `${VAULT_PATH}` template |
|
|
36
|
+
| 7 | `config/cognition/prompts/research.md` | `${VAULT_PATH}` template |
|
|
37
|
+
| 8 | `departments/dev/skills/scaffold/SKILL.md:19` | `${GIT_HOST}` template, default `github.com` HTTPS |
|
|
38
|
+
| — | new `core/runtime/path_resolver.py` | `resolve()`, `resolve_dict()`, `load_profile()` |
|
|
39
|
+
| — | new `installer/migrations/v3_path_schema.js` | Auto-migrate `projectsDir` text → `projectRoots` list + `reposRoot` |
|
|
40
|
+
| — | skill loader extension | Substitute `${VAR}` tokens at SKILL.md load |
|
|
41
|
+
| — | new `tests/python/test_path_resolver.py` | ≥ 15 tests |
|
|
42
|
+
|
|
43
|
+
**Out of scope (deferred):**
|
|
44
|
+
|
|
45
|
+
- `install.sh:25, 631-634` (GitHub URL hardcode + Mac-only Obsidian detection) → **PR2 v2.24.0** (cross-OS installer is the natural home for OS-aware logic)
|
|
46
|
+
- 50+ docs/superpowers/plans hardcoded paths (LOW priority, examples only) → **PR6 v2.28.0** cleanup pass
|
|
47
|
+
- Vision bridge migration (test_vision_bridge.py + scripts/tools/vision/*) → file was discarded; not relevant to PR1
|
|
48
|
+
- Migration of installed user skills under `~/.claude/skills/arka/` — installer rewrites these from source on every update; fixing source covers it
|
|
49
|
+
|
|
50
|
+
## Acceptance Criteria
|
|
51
|
+
|
|
52
|
+
1. **Zero CRITICAL grep hits.** Given a fresh checkout of `feature/v2.23.0-paths-portability`, when `grep -rn "/Users/andreagroferreira" core/ arka/ installer/ config/ departments/ knowledge/ --exclude-dir=node_modules --exclude-dir=__pycache__ --exclude-dir=.git --exclude-dir=docs` runs, then it returns **0 CRITICAL hits**. Hits inside `docs/superpowers/plans/` are tracked but acceptable (PR6 cleanup).
|
|
53
|
+
|
|
54
|
+
2. **Synthetic install works end-to-end.** Given `$HOME=/tmp/testuser` and `~/.arkaos/profile.json` containing `{"version":"3","vaultPath":"/tmp/testuser/vault","reposRoot":"/tmp/testuser/repos","projectRoots":["/tmp/testuser/code"]}`, when every SKILL.md is loaded, then `${VAULT_PATH}`, `${ARKA_OS_REPOS}` and `${GIT_HOST}` resolve to the configured values with zero `${` leakage.
|
|
55
|
+
|
|
56
|
+
3. **20K-user safe migration.** Given a legacy profile.json missing `projectRoots` and `reposRoot`, when `npx arkaos@latest update` runs, then:
|
|
57
|
+
- `projectRoots` is derived from parsing `projectsDir` text (regex extracts absolute paths).
|
|
58
|
+
- If parsing finds 0 paths, sensible defaults are applied (`["~/Herd","~/Work","~/AIProjects"]`).
|
|
59
|
+
- `reposRoot` defaults to `~/AIProjects` (or first match containing `AIProjects` if any).
|
|
60
|
+
- `version` bumps to `"3"`, `migrated_at` timestamp added.
|
|
61
|
+
- A `.bak-<timestamp>` backup is written first.
|
|
62
|
+
- `[arka:migrated] profile.json schema v2 → v3` logged.
|
|
63
|
+
|
|
64
|
+
4. **Hard error on truly missing profile.** Given `~/.arkaos/profile.json` is absent or unparseable JSON, when any `path_resolver` call runs, then `ProfileMissingError("Run /arka setup to configure ArkaOS paths")` is raised. No silent fallback.
|
|
65
|
+
|
|
66
|
+
5. **Env-var override.** Given `ARKAOS_VAULT_PATH=/tmp/override` is set, when `${VAULT_PATH}` is resolved, then the env var wins over profile. Same for `ARKAOS_REPOS_ROOT`, `ARKAOS_PROJECT_ROOTS`, `ARKAOS_GIT_HOST`. Empty string is treated as unset.
|
|
67
|
+
|
|
68
|
+
6. **Regression-free.** Given PR1 is merged, when full pytest suite runs, then all existing 542+ tests pass and ≥ 15 new path_resolver tests pass. Coverage ≥ 80%.
|
|
69
|
+
|
|
70
|
+
7. **Migration idempotent.** Given migration ran once, when it runs again, then profile.json is unchanged (no version re-bump, no second backup).
|
|
71
|
+
|
|
72
|
+
## Data Model
|
|
73
|
+
|
|
74
|
+
| Field (new in profile.json v3) | Type | Default | Source |
|
|
75
|
+
|---|---|---|---|
|
|
76
|
+
| `version` | `"3"` (str) | — | bumped by migration |
|
|
77
|
+
| `projectRoots` | `list[str]` | `["~/Herd","~/Work","~/AIProjects"]` | parsed from `projectsDir` text on migration |
|
|
78
|
+
| `reposRoot` | `str` | `"~/AIProjects"` | matches first path containing `AIProjects`, else default |
|
|
79
|
+
| `migrated_at` | ISO8601 `str` | — | written at migration time |
|
|
80
|
+
|
|
81
|
+
**Backwards-compat fields kept verbatim:** `language`, `market`, `role`, `company`, `projectsDir` (kept for human reference), `vaultPath`, `created`, `updated`.
|
|
82
|
+
|
|
83
|
+
## API Contracts
|
|
84
|
+
|
|
85
|
+
### `core/runtime/path_resolver.py`
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
class ProfileMissingError(RuntimeError):
|
|
89
|
+
"""Raised when ~/.arkaos/profile.json is absent or unparseable."""
|
|
90
|
+
|
|
91
|
+
@dataclass
|
|
92
|
+
class ProfileV3:
|
|
93
|
+
version: str
|
|
94
|
+
vault_path: str # ${VAULT_PATH}
|
|
95
|
+
repos_root: str # ${ARKA_OS_REPOS}
|
|
96
|
+
project_roots: list[str] # ${PROJECT_ROOTS}
|
|
97
|
+
raw: dict # full profile.json for forward-compat
|
|
98
|
+
|
|
99
|
+
def load_profile() -> ProfileV3:
|
|
100
|
+
"""Load and cache ~/.arkaos/profile.json. Raises ProfileMissingError."""
|
|
101
|
+
|
|
102
|
+
def resolve(template: str) -> str:
|
|
103
|
+
"""Substitute known tokens. Unknown tokens pass through unchanged.
|
|
104
|
+
|
|
105
|
+
Tokens:
|
|
106
|
+
${VAULT_PATH} -> ARKAOS_VAULT_PATH env or profile.vaultPath
|
|
107
|
+
${ARKA_OS_REPOS} -> ARKAOS_REPOS_ROOT env or profile.reposRoot
|
|
108
|
+
${PROJECT_ROOTS} -> os.pathsep-joined profile.projectRoots
|
|
109
|
+
${GIT_HOST} -> ARKAOS_GIT_HOST env or "github.com"
|
|
110
|
+
${HOME} -> os.path.expanduser("~")
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
def resolve_dict(d: dict) -> dict:
|
|
114
|
+
"""Recursively resolve string values in a JSON-shaped dict."""
|
|
115
|
+
|
|
116
|
+
def project_root_regex() -> re.Pattern:
|
|
117
|
+
"""Compile regex from profile.projectRoots for collector.py."""
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Skill loader extension
|
|
121
|
+
|
|
122
|
+
Find existing skill loader (likely `core/agents/loader.py` or `arka/skills/.../loader`); extend:
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
def load_skill_markdown(path: Path) -> str:
|
|
126
|
+
return path_resolver.resolve(path.read_text())
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Migration script `installer/migrations/v3_path_schema.js`
|
|
130
|
+
|
|
131
|
+
```javascript
|
|
132
|
+
function migrateV3(profile) {
|
|
133
|
+
if (profile.version === "3" || profile.projectRoots) return profile;
|
|
134
|
+
const roots = parseProjectsDirText(profile.projectsDir);
|
|
135
|
+
const reposRoot = roots.find(r => r.includes("AIProjects")) || "~/AIProjects";
|
|
136
|
+
return { ...profile, version: "3", projectRoots: roots,
|
|
137
|
+
reposRoot, migrated_at: new Date().toISOString() };
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Wired into `installer/migrate-user-data.js` (existing framework).
|
|
142
|
+
|
|
143
|
+
## Edge Cases
|
|
144
|
+
|
|
145
|
+
1. **`projectsDir` text is empty / null** → defaults applied (no error).
|
|
146
|
+
2. **`vaultPath` empty string** → treated as missing → `ProfileMissingError`.
|
|
147
|
+
3. **SKILL.md contains literal `${SOME_BASH_VAR}` for agent instructions** → resolver only touches the 5 known tokens; unknown tokens pass through.
|
|
148
|
+
4. **Windows path separators in `${PROJECT_ROOTS}`** → uses `os.pathsep` (`;` on Windows, `:` on Unix). Internal storage is POSIX `/`.
|
|
149
|
+
5. **Digest path from foreign OS** (cross-machine Obsidian sync) → `_detect_project` falls back to `os.getcwd()` if no `projectRoots` match (existing behaviour).
|
|
150
|
+
6. **Concurrent calls during migration** → migration uses temp-file + atomic rename; safe.
|
|
151
|
+
7. **User edits profile.json manually mid-session** → load_profile is cached per-process; re-import to refresh. Documented.
|
|
152
|
+
8. **Migration corrupts profile** → `.bak-<timestamp>` rollback file always exists.
|
|
153
|
+
|
|
154
|
+
## Test Scenarios
|
|
155
|
+
|
|
156
|
+
| # | Scenario | Type | Expected |
|
|
157
|
+
|---|---|---|---|
|
|
158
|
+
| 1 | `resolve("${VAULT_PATH}/foo")` with `vaultPath="/v"` | Unit | `"/v/foo"` |
|
|
159
|
+
| 2 | `resolve("${HOME}/bar")` | Unit | `"$HOME/bar"` expanded |
|
|
160
|
+
| 3 | `resolve("${UNKNOWN}/baz")` | Unit | `"${UNKNOWN}/baz"` unchanged |
|
|
161
|
+
| 4 | `resolve` with absent profile.json | Unit | `ProfileMissingError` |
|
|
162
|
+
| 5 | `resolve` with `ARKAOS_VAULT_PATH=/x` | Unit | env wins over profile |
|
|
163
|
+
| 6 | `resolve_dict` on obsidian-config.json fixture | Unit | All string values resolved |
|
|
164
|
+
| 7 | `_detect_project` with `projectRoots=["~/Herd"]` | Unit | matches path under `~/Herd` |
|
|
165
|
+
| 8 | `_detect_project` on Windows-style path | Unit | matches with `\` separator |
|
|
166
|
+
| 9 | `load_skill_markdown` on kb SKILL.md | Integration | `${VAULT_PATH}` substituted |
|
|
167
|
+
| 10 | full pytest suite | Regression | all 542+ tests pass |
|
|
168
|
+
| 11 | `migrateV3` on legacy profile (v2 schema) | Integration | new fields added, old kept, .bak written |
|
|
169
|
+
| 12 | `migrateV3` idempotent (second run no-op) | Integration | profile unchanged, no second .bak |
|
|
170
|
+
| 13 | Grep audit (CRITICAL paths) | Audit | 0 hits in core/ arka/ installer/ config/ departments/ knowledge/ |
|
|
171
|
+
| 14 | `scaffold SKILL.md ${GIT_HOST}` | Integration | `github.com` default |
|
|
172
|
+
| 15 | `comfyui SKILL.md ${ARKA_OS_REPOS}` | Integration | substituted correctly |
|
|
173
|
+
| 16 | `migrateV3` with empty `projectsDir` | Unit | defaults applied, no crash |
|
|
174
|
+
| 17 | env vars with empty string values | Unit | treated as unset, profile wins |
|
|
175
|
+
|
|
176
|
+
## Dependencies
|
|
177
|
+
|
|
178
|
+
- No external deps. Reuses existing `installer/migrate-user-data.js` framework + `core/specs/` Living Specs engine.
|
|
179
|
+
- New Python module only depends on stdlib (`json`, `os`, `pathlib`, `re`, `dataclasses`).
|
|
180
|
+
|
|
181
|
+
## Migration Plan for 20K Users
|
|
182
|
+
|
|
183
|
+
`npx arkaos@latest update` runs `migrate-user-data.js` which includes the new `migrateV3` step. Flow:
|
|
184
|
+
|
|
185
|
+
1. Read `~/.arkaos/profile.json`. If absent → skip migration (user must run `/arka setup`).
|
|
186
|
+
2. If `version === "3"` or `projectRoots` exists → no-op (idempotent).
|
|
187
|
+
3. Otherwise:
|
|
188
|
+
- Write backup: `~/.arkaos/profile.json.bak-<unix-timestamp>`.
|
|
189
|
+
- Parse `projectsDir` text with `/((?:\/Users|\/home|[A-Z]:\\Users)\/\S+?\/(?:Herd|Work|AIProjects|code|repos))/g`.
|
|
190
|
+
- Apply defaults if parsing yields 0 results.
|
|
191
|
+
- Atomic write: temp file + `fs.rename`.
|
|
192
|
+
- Log `[arka:migrated] profile.json schema v2 → v3 ({N} roots, repos=...)` to `~/.arkaos/logs/migrate.log`.
|
|
193
|
+
|
|
194
|
+
**Rollback procedure (documented in installer):** restore `.bak-<timestamp>` over profile.json.
|
|
195
|
+
|
|
196
|
+
## Quality Gate Criteria
|
|
197
|
+
|
|
198
|
+
Marta (CQO) requires APPROVED from all three reviewers:
|
|
199
|
+
|
|
200
|
+
- **Eduardo (Copy):** All SKILL.md files read naturally after substitution — no `${` leakage in human-facing prose. Error messages clear and actionable.
|
|
201
|
+
- **Francisca (Tech):** path_resolver handles all 8 edge cases. Migration is atomic + idempotent + reversible. 17 test scenarios all green. No crash on corrupt profile.
|
|
202
|
+
- **Marta (CQO):** Migration verified non-breaking by simulating 5 different legacy profile shapes (André's current one + 4 synthetic variants representing common 20K-user variations).
|
|
203
|
+
|
|
204
|
+
## References
|
|
205
|
+
|
|
206
|
+
- Plan: `~/.arkaos/plans/2026-05-13-arkaos-next-level-conclave.md` (PR1 section)
|
|
207
|
+
- Memory: [[project_next_level_conclave]]
|
|
208
|
+
- KB: [[2026-04-29-claude-code-2-1-122-and-2-1-123]] (Claude Code features unblocked by 2.1.122)
|
|
209
|
+
- Existing Specs in this project: [[SPEC-cognitive-layer-feedback-loop]], [[SPEC-wave1-single-source-of-truth]]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -16,7 +16,7 @@ Create new projects from real git repositories with full automation: dependencie
|
|
|
16
16
|
|
|
17
17
|
| Command | Git Repository | Stack |
|
|
18
18
|
|---------|---------------|-------|
|
|
19
|
-
| `/dev scaffold laravel <name>` | `
|
|
19
|
+
| `/dev scaffold laravel <name>` | `https://${GIT_HOST}/laravel/laravel.git` (override with `ARKAOS_LARAVEL_STARTER_REPO` env) | Laravel + Herd |
|
|
20
20
|
| `/dev scaffold nuxt-saas <name>` | `https://github.com/nuxt-ui-templates/dashboard.git` | Nuxt 3 Dashboard |
|
|
21
21
|
| `/dev scaffold nuxt-landing <name>` | `https://github.com/nuxt-ui-templates/landing.git` | Nuxt 3 Landing |
|
|
22
22
|
| `/dev scaffold nuxt-docs <name>` | `https://github.com/nuxt-ui-templates/docs.git` | Nuxt 3 Docs |
|
|
@@ -136,7 +136,7 @@ cp PROJECT.md "$ARKA_OS/projects/<name>/PROJECT.md"
|
|
|
136
136
|
|
|
137
137
|
Create pages in the Obsidian vault:
|
|
138
138
|
|
|
139
|
-
**Main page:**
|
|
139
|
+
**Main page:** `${VAULT_PATH}/Projects/<name>/Home.md`
|
|
140
140
|
```markdown
|
|
141
141
|
---
|
|
142
142
|
type: project
|
|
@@ -165,7 +165,7 @@ tags:
|
|
|
165
165
|
- *Part of the [[Projects MOC]]*
|
|
166
166
|
```
|
|
167
167
|
|
|
168
|
-
**Architecture page:**
|
|
168
|
+
**Architecture page:** `${VAULT_PATH}/Projects/<name>/Architecture/decisions.md`
|
|
169
169
|
```markdown
|
|
170
170
|
---
|
|
171
171
|
type: adr-log
|
|
@@ -242,7 +242,7 @@ Both directories get their respective dependencies installed, and the full-stack
|
|
|
242
242
|
|
|
243
243
|
## Error Handling
|
|
244
244
|
|
|
245
|
-
- If `git clone` fails: check SSH keys
|
|
245
|
+
- If `git clone` fails: check SSH keys / repo access permissions for the configured `${GIT_HOST}`
|
|
246
246
|
- If `composer install` fails: check PHP version (`php -v`, need 8.3+)
|
|
247
247
|
- If `pnpm install` fails: check Node version (`node -v`, need 18+)
|
|
248
248
|
- If `herd link` fails: check Herd is installed and running
|
|
@@ -22,7 +22,7 @@ Dynamic knowledge acquisition and management system. Learn from YouTube videos,
|
|
|
22
22
|
|
|
23
23
|
## Obsidian Configuration
|
|
24
24
|
|
|
25
|
-
- **Vault:**
|
|
25
|
+
- **Vault:** `${VAULT_PATH}`
|
|
26
26
|
- **Config:** Read `knowledge/obsidian-config.json` for full path/convention details
|
|
27
27
|
- **MCP:** Use Obsidian MCP when available, fallback to direct file Write
|
|
28
28
|
- **Conventions:** YAML frontmatter, wikilinks `[[]]`, MOC references, kebab-case tags
|
package/installer/cli.js
CHANGED
|
@@ -19,6 +19,7 @@ const { values, positionals } = parseArgs({
|
|
|
19
19
|
runtime: { type: "string", short: "r" },
|
|
20
20
|
path: { type: "string", short: "p" },
|
|
21
21
|
force: { type: "boolean", short: "f" },
|
|
22
|
+
"no-system": { type: "boolean" },
|
|
22
23
|
},
|
|
23
24
|
allowPositionals: true,
|
|
24
25
|
strict: false,
|
|
@@ -74,7 +75,7 @@ async function main() {
|
|
|
74
75
|
switch (command) {
|
|
75
76
|
case "install":
|
|
76
77
|
const runtime = values.runtime || await detectRuntime();
|
|
77
|
-
await install({ runtime, path: values.path, force: values.force });
|
|
78
|
+
await install({ runtime, path: values.path, force: values.force, skipSystem: values["no-system"] });
|
|
78
79
|
break;
|
|
79
80
|
|
|
80
81
|
case "init": {
|
package/installer/doctor.js
CHANGED
|
@@ -4,6 +4,7 @@ import { homedir } from "node:os";
|
|
|
4
4
|
import { execSync } from "node:child_process";
|
|
5
5
|
import { getArkaosPython, getVenvPython, canImportCore, getRepoRoot } from "./python-resolver.js";
|
|
6
6
|
import { IS_WINDOWS, HOOK_EXT, CMD_FINDER } from "./platform.js";
|
|
7
|
+
import { checkNode, checkObsidian } from "./system-tools.js";
|
|
7
8
|
|
|
8
9
|
const INSTALL_DIR = join(homedir(), ".arkaos");
|
|
9
10
|
|
|
@@ -124,6 +125,53 @@ const checks = [
|
|
|
124
125
|
check: () => existsSync(join(INSTALL_DIR, "schedules.yaml")),
|
|
125
126
|
fix: () => "Run: npx arkaos@latest update (deploys scheduler)",
|
|
126
127
|
},
|
|
128
|
+
{
|
|
129
|
+
name: "obsidian",
|
|
130
|
+
description: "Obsidian app installed",
|
|
131
|
+
severity: "warn",
|
|
132
|
+
check: () => checkObsidian().installed,
|
|
133
|
+
fix: () => {
|
|
134
|
+
const s = checkObsidian();
|
|
135
|
+
if (s.suggestedCommand) return `Run: ${s.suggestedCommand}`;
|
|
136
|
+
return `Install Obsidian from ${s.fallbackUrl || "https://obsidian.md/download"}`;
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: "node",
|
|
141
|
+
description: "Node.js 20+ available",
|
|
142
|
+
severity: "warn",
|
|
143
|
+
check: () => checkNode().needsAction === "none",
|
|
144
|
+
fix: () => {
|
|
145
|
+
const s = checkNode();
|
|
146
|
+
if (s.suggestedCommand) return `Run: ${s.suggestedCommand}`;
|
|
147
|
+
return `Install Node.js 20+ from ${s.fallbackUrl || "https://nodejs.org/en/download"}`;
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
name: "claude-code-version",
|
|
152
|
+
description: "Claude Code 2.1.122+ (ToolSearch late-binding + hooks isolation)",
|
|
153
|
+
severity: "warn",
|
|
154
|
+
check: () => {
|
|
155
|
+
if (!commandExists("claude")) return true; // no claude binary = not applicable
|
|
156
|
+
try {
|
|
157
|
+
const out = execSync("claude --version 2>&1", {
|
|
158
|
+
stdio: "pipe",
|
|
159
|
+
}).toString().trim();
|
|
160
|
+
const m = out.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
161
|
+
if (!m) return false;
|
|
162
|
+
const [, maj, min, patch] = m.map(Number);
|
|
163
|
+
// 2.1.122 minimum
|
|
164
|
+
if (maj > 2) return true;
|
|
165
|
+
if (maj < 2) return false;
|
|
166
|
+
if (min > 1) return true;
|
|
167
|
+
if (min < 1) return false;
|
|
168
|
+
return patch >= 122;
|
|
169
|
+
} catch {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
fix: () => "Upgrade Claude Code: npm install -g @anthropic-ai/claude-code@latest",
|
|
174
|
+
},
|
|
127
175
|
];
|
|
128
176
|
|
|
129
177
|
// ─── Windows-only checks ───────────────────────────────────────────────
|
package/installer/index.js
CHANGED
|
@@ -12,7 +12,7 @@ const __dirname = dirname(__filename);
|
|
|
12
12
|
const ARKAOS_ROOT = resolve(__dirname, "..");
|
|
13
13
|
const VERSION = JSON.parse(readFileSync(join(ARKAOS_ROOT, "package.json"), "utf-8")).version;
|
|
14
14
|
|
|
15
|
-
export async function install({ runtime, path, force }) {
|
|
15
|
+
export async function install({ runtime, path, force, skipSystem }) {
|
|
16
16
|
const startTime = Date.now();
|
|
17
17
|
const config = getRuntimeConfig(runtime);
|
|
18
18
|
const isUpgrade = existsSync(join(path || join(homedir(), ".arkaos"), "install-manifest.json"));
|
|
@@ -72,6 +72,31 @@ export async function install({ runtime, path, force }) {
|
|
|
72
72
|
ok("No v1 installation found");
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
// ═══ Step 2.5: Ensure system tools (Obsidian, Node ≥ 20, Python ≥ 3.12) ═══
|
|
76
|
+
// Wrapped in try/catch so a regression in system-tools.js cannot break a
|
|
77
|
+
// fresh `npx arkaos install` for the ~20K user base. We degrade to a warn
|
|
78
|
+
// and proceed to venv creation rather than exiting non-zero.
|
|
79
|
+
if (!skipSystem) {
|
|
80
|
+
try {
|
|
81
|
+
const { ensureSystemTools } = await import("./system-tools.js");
|
|
82
|
+
const { formatSudoInstructions } = await import("./package-manager.js");
|
|
83
|
+
const sys = ensureSystemTools({ skipSystem: false });
|
|
84
|
+
if (sys.sudoCommands && sys.sudoCommands.length > 0) {
|
|
85
|
+
console.log(formatSudoInstructions(sys.sudoCommands));
|
|
86
|
+
}
|
|
87
|
+
for (const tool of [sys.obsidian, sys.node]) {
|
|
88
|
+
if (!tool) continue;
|
|
89
|
+
if (tool.justInstalled) ok(`${tool.name} installed`);
|
|
90
|
+
else if (tool.needsAction === "none") ok(`${tool.name} ready`);
|
|
91
|
+
else warn(`${tool.name} ${tool.needsAction} — see commands above`);
|
|
92
|
+
}
|
|
93
|
+
} catch (err) {
|
|
94
|
+
warn(`System tool check failed: ${err.message}. Continuing without it.`);
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
warn("System tool checks skipped (--no-system)");
|
|
98
|
+
}
|
|
99
|
+
|
|
75
100
|
// ═══ Step 3: Check Python + create venv ═══
|
|
76
101
|
step(3, 14, "Checking Python 3.11+ and creating virtual environment...");
|
|
77
102
|
const systemPython = findSystemPython();
|
|
@@ -2,6 +2,8 @@ import { existsSync, mkdirSync, readdirSync, renameSync, statSync, writeFileSync
|
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
|
|
5
|
+
import { migrateProfileSchemaV3 } from "./migrations/v3_path_schema.js";
|
|
6
|
+
|
|
5
7
|
const LEGACY_SKILLS_ROOT = join(homedir(), ".claude", "skills", "arka");
|
|
6
8
|
const USER_DATA_ROOT = join(homedir(), ".arkaos");
|
|
7
9
|
|
|
@@ -28,8 +30,22 @@ export function migrateUserData({ dryRun = false } = {}) {
|
|
|
28
30
|
const skipped = [...projectsResult.skipped, ...ecosystemsResult.skipped];
|
|
29
31
|
const conflicts = [...projectsResult.conflicts, ...ecosystemsResult.conflicts];
|
|
30
32
|
|
|
33
|
+
let profileSchemaResult = null;
|
|
34
|
+
if (!dryRun) {
|
|
35
|
+
try {
|
|
36
|
+
profileSchemaResult = migrateProfileSchemaV3();
|
|
37
|
+
if (profileSchemaResult?.migrated) {
|
|
38
|
+
moved.push(
|
|
39
|
+
`profile.json schema v2 → v3 (${profileSchemaResult.projectRoots.length} roots)`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
} catch (err) {
|
|
43
|
+
conflicts.push(`profile schema v3 migration failed: ${err.message}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
31
47
|
const logPath = writeReport({ moved, skipped, conflicts, dryRun });
|
|
32
|
-
return { moved, skipped, conflicts, logPath };
|
|
48
|
+
return { moved, skipped, conflicts, logPath, profileSchemaResult };
|
|
33
49
|
}
|
|
34
50
|
|
|
35
51
|
export function printMigrationReport(result) {
|