stealthos-cli 0.1.0-alpha.4 → 0.1.0-alpha.5

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.
@@ -1,170 +1,215 @@
1
- # init-ai-os.ps1
2
- # Inicializa ou verifica o AI Operating System em um projeto.
3
- # Uso:
4
- # .\scripts\init-ai-os.ps1 # interativo
5
- # .\scripts\init-ai-os.ps1 -Mode new # forca novo
6
- # .\scripts\init-ai-os.ps1 -Mode existing # forca existente
7
- # .\scripts\init-ai-os.ps1 -Verify # so verifica integridade
8
- # .\scripts\init-ai-os.ps1 -TargetPath C:\path\to\project
9
-
10
- param(
11
- [ValidateSet("auto", "new", "existing", "verify")]
12
- [string]$Mode = "auto",
13
-
14
- [string]$TargetPath = (Get-Location).Path,
15
-
16
- [string]$TemplateRoot = (Split-Path (Split-Path $PSScriptRoot -Parent) -Parent),
17
-
18
- [switch]$Verify
19
- )
20
-
21
- if ($Verify) { $Mode = "verify" }
22
-
23
- $ErrorActionPreference = "Stop"
24
-
25
- function Write-Section($text) {
26
- Write-Host ""
27
- Write-Host "=== $text ===" -ForegroundColor Cyan
28
- }
29
-
30
- function Write-Ok($text) { Write-Host "[OK] $text" -ForegroundColor Green }
31
- function Write-Warn($text) { Write-Host "[WARN] $text" -ForegroundColor Yellow }
32
- function Write-Err($text) { Write-Host "[ERR] $text" -ForegroundColor Red }
33
-
34
- # ----- Detect mode if auto -----
35
- function Get-ProjectMode {
36
- param([string]$Path)
37
-
38
- $aiExists = Test-Path (Join-Path $Path ".ai")
39
- $hasCode = @(
40
- "package.json", "pyproject.toml", "requirements.txt", "Cargo.toml",
41
- "go.mod", "pom.xml", "build.gradle", "composer.json", "Gemfile",
42
- "mix.exs", "pubspec.yaml"
43
- ) | Where-Object { Test-Path (Join-Path $Path $_) }
44
-
45
- if ($aiExists) { return "verify" }
46
- if ($hasCode.Count -gt 0) { return "existing" }
47
- return "new"
48
- }
49
-
50
- if ($Mode -eq "auto") {
51
- $Mode = Get-ProjectMode -Path $TargetPath
52
- Write-Host "Modo detectado: $Mode"
53
- }
54
-
55
- # ----- VERIFY MODE -----
56
- if ($Mode -eq "verify") {
57
- Write-Section "Verificando integridade do AI OS em $TargetPath"
58
-
59
- $manifestPath = Join-Path $TargetPath ".ai\manifest.json"
60
- if (-not (Test-Path $manifestPath)) {
61
- Write-Err "manifest.json nao encontrado em .ai/. OS pode estar quebrado."
62
- exit 1
63
- }
64
-
65
- $manifest = Get-Content $manifestPath -Raw | ConvertFrom-Json
66
- $missing = @()
67
- foreach ($f in $manifest.required_files) {
68
- $full = Join-Path $TargetPath ($f -replace '/', '\')
69
- if (-not (Test-Path $full)) {
70
- $missing += $f
71
- }
72
- }
73
-
74
- if ($missing.Count -eq 0) {
75
- Write-Ok "Todos os $($manifest.required_files.Count) arquivos obrigatorios presentes."
76
- } else {
77
- Write-Err "$($missing.Count) arquivos faltando:"
78
- $missing | ForEach-Object { Write-Host " - $_" }
79
- exit 1
80
- }
81
-
82
- # Link lint
83
- Write-Section "Validando links cruzados"
84
- & (Join-Path $PSScriptRoot "lint-os.ps1") -TargetPath $TargetPath
85
- exit $LASTEXITCODE
86
- }
87
-
88
- # ----- NEW MODE -----
89
- if ($Mode -eq "new") {
90
- Write-Section "Bootstrap: projeto NOVO em $TargetPath"
91
-
92
- if (Test-Path (Join-Path $TargetPath ".ai")) {
93
- Write-Warn ".ai/ ja existe. Use -Mode verify para checar integridade ou -Mode existing para retrofit."
94
- exit 1
95
- }
96
-
97
- # Copia template
98
- $templateAi = Join-Path $TemplateRoot ".ai"
99
- $templateClaude = Join-Path $TemplateRoot ".claude"
100
- $templateEntries = @("CLAUDE.md", "GEMINI.md", "GPT.md")
101
-
102
- if (-not (Test-Path $templateAi)) {
103
- Write-Err "Template .ai/ nao encontrado em $TemplateRoot. Rode este script a partir do diretorio raiz do template."
104
- exit 1
105
- }
106
-
107
- Copy-Item $templateAi (Join-Path $TargetPath ".ai") -Recurse
108
- Write-Ok "Estrutura .ai/ copiada."
109
-
110
- if (Test-Path $templateClaude) {
111
- Copy-Item $templateClaude (Join-Path $TargetPath ".claude") -Recurse
112
- Write-Ok "Hooks .claude/ copiados."
113
- }
114
-
115
- foreach ($entry in $templateEntries) {
116
- $src = Join-Path $TemplateRoot $entry
117
- $dst = Join-Path $TargetPath $entry
118
- if ((Test-Path $src) -and -not (Test-Path $dst)) {
119
- Copy-Item $src $dst
120
- Write-Ok "Entry point $entry copiado."
121
- }
122
- }
123
-
124
- Write-Host ""
125
- Write-Host "Proximo passo: abra Claude Code / Gemini / GPT neste diretorio."
126
- Write-Host "O agente seguira o fluxo de bootstrap/new-project.md automaticamente."
127
- exit 0
128
- }
129
-
130
- # ----- EXISTING MODE -----
131
- if ($Mode -eq "existing") {
132
- Write-Section "Bootstrap: projeto EXISTENTE em $TargetPath"
133
-
134
- if (-not (Test-Path (Join-Path $TargetPath ".ai"))) {
135
- # Copia o template
136
- $templateAi = Join-Path $TemplateRoot ".ai"
137
- if (-not (Test-Path $templateAi)) {
138
- Write-Err "Template .ai/ nao encontrado em $TemplateRoot."
139
- exit 1
140
- }
141
- Copy-Item $templateAi (Join-Path $TargetPath ".ai") -Recurse
142
- Write-Ok "Estrutura .ai/ enxertada no projeto."
143
-
144
- $templateClaude = Join-Path $TemplateRoot ".claude"
145
- if (Test-Path $templateClaude) {
146
- Copy-Item $templateClaude (Join-Path $TargetPath ".claude") -Recurse
147
- Write-Ok "Hooks .claude/ enxertados."
148
- }
149
-
150
- @("CLAUDE.md", "GEMINI.md", "GPT.md") | ForEach-Object {
151
- $src = Join-Path $TemplateRoot $_
152
- $dst = Join-Path $TargetPath $_
153
- if ((Test-Path $src) -and -not (Test-Path $dst)) {
154
- Copy-Item $src $dst
155
- Write-Ok "Entry point $_ enxertado."
156
- }
157
- }
158
- } else {
159
- Write-Ok ".ai/ ja existe pulando copia."
160
- }
161
-
162
- # Detectar stack e gerar pre-fill de project-context.md
163
- Write-Section "Detectando stack do projeto"
164
- & (Join-Path $PSScriptRoot "detect-stack.ps1") -TargetPath $TargetPath
165
- Write-Host ""
166
- Write-Host "Proximo passo: abra Claude Code / Gemini / GPT neste diretorio."
167
- Write-Host "O agente seguira o fluxo de bootstrap/existing-project.md."
168
- Write-Host "Ele vai validar a deteccao com voce e completar o que falta via discovery-questions."
169
- exit 0
170
- }
1
+ # init-ai-os.ps1
2
+ # Inicializa ou verifica o AI Operating System em um projeto.
3
+ # Uso:
4
+ # .\scripts\init-ai-os.ps1 # interativo
5
+ # .\scripts\init-ai-os.ps1 -Mode new # forca novo
6
+ # .\scripts\init-ai-os.ps1 -Mode existing # forca existente
7
+ # .\scripts\init-ai-os.ps1 -Verify # so verifica integridade
8
+ # .\scripts\init-ai-os.ps1 -TargetPath C:\path\to\project
9
+
10
+ param(
11
+ [ValidateSet("auto", "new", "existing", "verify")]
12
+ [string]$Mode = "auto",
13
+
14
+ [string]$TargetPath = (Get-Location).Path,
15
+
16
+ [string]$TemplateRoot = $null,
17
+
18
+ [switch]$Verify
19
+ )
20
+
21
+ if ($Verify) { $Mode = "verify" }
22
+
23
+ $ErrorActionPreference = "Stop"
24
+
25
+ # ----- Auto-detect template layout -----
26
+ # Layout A (monorepo): <repo>/.ai/scripts/init-ai-os.ps1 -> template at <repo>/.ai
27
+ # Layout B (installed): <home>/scripts/init-ai-os.ps1 -> template at <home> itself
28
+ if (-not $TemplateRoot) {
29
+ $grandparent = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
30
+ $parent = Split-Path $PSScriptRoot -Parent
31
+ if (Test-Path (Join-Path $grandparent ".ai\manifest.json")) {
32
+ $TemplateRoot = $grandparent
33
+ } elseif (Test-Path (Join-Path $parent "manifest.json")) {
34
+ # Installed layout: parent IS the content root; use it as a virtual .ai/
35
+ $TemplateRoot = $parent
36
+ $script:InstalledLayout = $true
37
+ } else {
38
+ $TemplateRoot = $grandparent
39
+ }
40
+ }
41
+
42
+ # Files/folders never copied to user projects (runtime/snapshots/outputs)
43
+ $script:ExcludeNames = @(
44
+ "node_modules", ".runtime", "runtime", "artifacts",
45
+ "context", "snapshots", "projects", "archive", "outputs"
46
+ )
47
+
48
+ function Copy-Tree-Filtered {
49
+ param([string]$Src, [string]$Dst)
50
+ New-Item -ItemType Directory -Force -Path $Dst | Out-Null
51
+ Get-ChildItem -LiteralPath $Src -Force | ForEach-Object {
52
+ if ($script:ExcludeNames -contains $_.Name) { return }
53
+ $target = Join-Path $Dst $_.Name
54
+ if ($_.PSIsContainer) {
55
+ Copy-Tree-Filtered -Src $_.FullName -Dst $target
56
+ } else {
57
+ Copy-Item -LiteralPath $_.FullName -Destination $target -Force
58
+ }
59
+ }
60
+ }
61
+
62
+ function Write-Section($text) {
63
+ Write-Host ""
64
+ Write-Host "=== $text ===" -ForegroundColor Cyan
65
+ }
66
+
67
+ function Write-Ok($text) { Write-Host "[OK] $text" -ForegroundColor Green }
68
+ function Write-Warn($text) { Write-Host "[WARN] $text" -ForegroundColor Yellow }
69
+ function Write-Err($text) { Write-Host "[ERR] $text" -ForegroundColor Red }
70
+
71
+ # ----- Detect mode if auto -----
72
+ function Get-ProjectMode {
73
+ param([string]$Path)
74
+
75
+ $aiExists = Test-Path (Join-Path $Path ".ai")
76
+ $hasCode = @(
77
+ "package.json", "pyproject.toml", "requirements.txt", "Cargo.toml",
78
+ "go.mod", "pom.xml", "build.gradle", "composer.json", "Gemfile",
79
+ "mix.exs", "pubspec.yaml"
80
+ ) | Where-Object { Test-Path (Join-Path $Path $_) }
81
+
82
+ if ($aiExists) { return "verify" }
83
+ if ($hasCode.Count -gt 0) { return "existing" }
84
+ return "new"
85
+ }
86
+
87
+ if ($Mode -eq "auto") {
88
+ $Mode = Get-ProjectMode -Path $TargetPath
89
+ Write-Host "Modo detectado: $Mode"
90
+ }
91
+
92
+ # ----- VERIFY MODE -----
93
+ if ($Mode -eq "verify") {
94
+ Write-Section "Verificando integridade do AI OS em $TargetPath"
95
+
96
+ $manifestPath = Join-Path $TargetPath ".ai\manifest.json"
97
+ if (-not (Test-Path $manifestPath)) {
98
+ Write-Err "manifest.json nao encontrado em .ai/. OS pode estar quebrado."
99
+ exit 1
100
+ }
101
+
102
+ $manifest = Get-Content $manifestPath -Raw | ConvertFrom-Json
103
+ $missing = @()
104
+ foreach ($f in $manifest.required_files) {
105
+ $full = Join-Path $TargetPath ($f -replace '/', '\')
106
+ if (-not (Test-Path $full)) {
107
+ $missing += $f
108
+ }
109
+ }
110
+
111
+ if ($missing.Count -eq 0) {
112
+ Write-Ok "Todos os $($manifest.required_files.Count) arquivos obrigatorios presentes."
113
+ } else {
114
+ Write-Err "$($missing.Count) arquivos faltando:"
115
+ $missing | ForEach-Object { Write-Host " - $_" }
116
+ exit 1
117
+ }
118
+
119
+ # Link lint
120
+ Write-Section "Validando links cruzados"
121
+ & (Join-Path $PSScriptRoot "lint-os.ps1") -TargetPath $TargetPath
122
+ exit $LASTEXITCODE
123
+ }
124
+
125
+ # ----- NEW MODE -----
126
+ if ($Mode -eq "new") {
127
+ Write-Section "Bootstrap: projeto NOVO em $TargetPath"
128
+
129
+ if (Test-Path (Join-Path $TargetPath ".ai")) {
130
+ Write-Warn ".ai/ ja existe. Use -Mode verify para checar integridade ou -Mode existing para retrofit."
131
+ exit 1
132
+ }
133
+
134
+ # Copia template (suporta layout monorepo e installed)
135
+ if ($script:InstalledLayout) {
136
+ $templateAi = $TemplateRoot
137
+ } else {
138
+ $templateAi = Join-Path $TemplateRoot ".ai"
139
+ }
140
+ $templateClaude = Join-Path $TemplateRoot ".claude"
141
+ $templateEntries = @("CLAUDE.md", "GEMINI.md", "GPT.md")
142
+
143
+ if (-not (Test-Path $templateAi)) {
144
+ Write-Err "Template .ai/ nao encontrado em $templateAi. Rode este script a partir do diretorio raiz do template."
145
+ exit 1
146
+ }
147
+
148
+ Copy-Tree-Filtered -Src $templateAi -Dst (Join-Path $TargetPath ".ai")
149
+ Write-Ok "Estrutura .ai/ copiada."
150
+
151
+ if (Test-Path $templateClaude) {
152
+ Copy-Item $templateClaude (Join-Path $TargetPath ".claude") -Recurse
153
+ Write-Ok "Hooks .claude/ copiados."
154
+ }
155
+
156
+ foreach ($entry in $templateEntries) {
157
+ $src = Join-Path $TemplateRoot $entry
158
+ $dst = Join-Path $TargetPath $entry
159
+ if ((Test-Path $src) -and -not (Test-Path $dst)) {
160
+ Copy-Item $src $dst
161
+ Write-Ok "Entry point $entry copiado."
162
+ }
163
+ }
164
+
165
+ Write-Host ""
166
+ Write-Host "Proximo passo: abra Claude Code / Gemini / GPT neste diretorio."
167
+ Write-Host "O agente seguira o fluxo de bootstrap/new-project.md automaticamente."
168
+ exit 0
169
+ }
170
+
171
+ # ----- EXISTING MODE -----
172
+ if ($Mode -eq "existing") {
173
+ Write-Section "Bootstrap: projeto EXISTENTE em $TargetPath"
174
+
175
+ if (-not (Test-Path (Join-Path $TargetPath ".ai"))) {
176
+ # Copia o template (suporta layout monorepo e installed)
177
+ if ($script:InstalledLayout) {
178
+ $templateAi = $TemplateRoot
179
+ } else {
180
+ $templateAi = Join-Path $TemplateRoot ".ai"
181
+ }
182
+ if (-not (Test-Path $templateAi)) {
183
+ Write-Err "Template .ai/ nao encontrado em $templateAi."
184
+ exit 1
185
+ }
186
+ Copy-Tree-Filtered -Src $templateAi -Dst (Join-Path $TargetPath ".ai")
187
+ Write-Ok "Estrutura .ai/ enxertada no projeto."
188
+
189
+ $templateClaude = Join-Path $TemplateRoot ".claude"
190
+ if (Test-Path $templateClaude) {
191
+ Copy-Item $templateClaude (Join-Path $TargetPath ".claude") -Recurse
192
+ Write-Ok "Hooks .claude/ enxertados."
193
+ }
194
+
195
+ @("CLAUDE.md", "GEMINI.md", "GPT.md") | ForEach-Object {
196
+ $src = Join-Path $TemplateRoot $_
197
+ $dst = Join-Path $TargetPath $_
198
+ if ((Test-Path $src) -and -not (Test-Path $dst)) {
199
+ Copy-Item $src $dst
200
+ Write-Ok "Entry point $_ enxertado."
201
+ }
202
+ }
203
+ } else {
204
+ Write-Ok ".ai/ ja existe - pulando copia."
205
+ }
206
+
207
+ # Detectar stack e gerar pre-fill de project-context.md
208
+ Write-Section "Detectando stack do projeto"
209
+ & (Join-Path $PSScriptRoot "detect-stack.ps1") -TargetPath $TargetPath
210
+ Write-Host ""
211
+ Write-Host "Proximo passo: abra Claude Code / Gemini / GPT neste diretorio."
212
+ Write-Host "O agente seguira o fluxo de bootstrap/existing-project.md."
213
+ Write-Host "Ele vai validar a deteccao com voce e completar o que falta via discovery-questions."
214
+ exit 0
215
+ }
package/package.json CHANGED
@@ -1,42 +1,42 @@
1
- {
2
- "name": "stealthos-cli",
3
- "version": "0.1.0-alpha.4",
4
- "scripts": {
5
- "bundle-ai": "node scripts/bundle-ai.mjs",
6
- "prepublishOnly": "node scripts/bundle-ai.mjs"
7
- },
8
- "description": "StealthOS CLI — install/manage the StealthOS knowledge base in ~/.stealthos/. Subcommands: install, status, version.",
9
- "type": "module",
10
- "bin": {
11
- "stealthos": "./bin.cjs"
12
- },
13
- "files": [
14
- "bin.cjs",
15
- "bin.mjs",
16
- "src/",
17
- "scripts/",
18
- "ai/",
19
- "README.md"
20
- ],
21
- "engines": {
22
- "node": ">=18.0.0"
23
- },
24
- "keywords": [
25
- "stealthos",
26
- "mcp",
27
- "ai",
28
- "cli",
29
- "claude-code"
30
- ],
31
- "author": "Igor Kadu (https://github.com/IgorKadu)",
32
- "license": "MIT",
33
- "repository": {
34
- "type": "git",
35
- "url": "git+https://github.com/IgorKadu/stealthos.git",
36
- "directory": "packages/stealthos-cli"
37
- },
38
- "bugs": {
39
- "url": "https://github.com/IgorKadu/stealthos/issues"
40
- },
41
- "homepage": "https://github.com/IgorKadu/stealthos/tree/main/packages/stealthos-cli#readme"
42
- }
1
+ {
2
+ "name": "stealthos-cli",
3
+ "version": "0.1.0-alpha.5",
4
+ "scripts": {
5
+ "bundle-ai": "node scripts/bundle-ai.mjs",
6
+ "prepublishOnly": "node scripts/bundle-ai.mjs"
7
+ },
8
+ "description": "StealthOS CLI — install/manage the StealthOS knowledge base in ~/.stealthos/. Subcommands: install, status, version.",
9
+ "type": "module",
10
+ "bin": {
11
+ "stealthos": "./bin.cjs"
12
+ },
13
+ "files": [
14
+ "bin.cjs",
15
+ "bin.mjs",
16
+ "src/",
17
+ "scripts/",
18
+ "ai/",
19
+ "README.md"
20
+ ],
21
+ "engines": {
22
+ "node": ">=18.0.0"
23
+ },
24
+ "keywords": [
25
+ "stealthos",
26
+ "mcp",
27
+ "ai",
28
+ "cli",
29
+ "claude-code"
30
+ ],
31
+ "author": "Igor Kadu (https://github.com/IgorKadu)",
32
+ "license": "MIT",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/IgorKadu/stealthos.git",
36
+ "directory": "packages/stealthos-cli"
37
+ },
38
+ "bugs": {
39
+ "url": "https://github.com/IgorKadu/stealthos/issues"
40
+ },
41
+ "homepage": "https://github.com/IgorKadu/stealthos/tree/main/packages/stealthos-cli#readme"
42
+ }
package/src/cli.mjs CHANGED
@@ -1,79 +1,83 @@
1
- // stealthos CLI router. Subcommand dispatcher.
2
-
3
- import { installCommand } from "./commands/install.mjs";
4
- import { statusCommand } from "./commands/status.mjs";
5
-
6
- const VERSION = "0.1.0-alpha.4";
7
-
8
- function usage() {
9
- process.stdout.write(`
10
- stealthos v${VERSION}
11
-
12
- USAGE
13
- stealthos <command> [options]
14
-
15
- COMMANDS
16
- install Install StealthOS knowledge base into ~/.stealthos/
17
- Options:
18
- --source <path> Source .ai/ tree (default: auto-detect in monorepo)
19
- --home <path> Target home (default: ~/.stealthos)
20
- --force Overwrite existing files
21
- status Show ~/.stealthos/ install status (version, paths, project count)
22
- version Print stealthos CLI version
23
- help Show this message
24
-
25
- ENVIRONMENT
26
- STEALTHOS_HOME Override target home (default: ~/.stealthos)
27
- STEALTHOS_SOURCE Override source .ai/ path (default: auto-detect)
28
- STEALTHOS_DEBUG=1 Print stack traces on errors
29
-
30
- EXAMPLES
31
- stealthos install
32
- stealthos install --home /opt/stealthos
33
- stealthos status
34
- `);
35
- }
36
-
37
- export async function runCli(argv) {
38
- const [cmd = "help", ...rest] = argv;
39
- switch (cmd) {
40
- case "install":
41
- return installCommand(parseFlags(rest));
42
- case "status":
43
- return statusCommand(parseFlags(rest));
44
- case "version":
45
- case "-v":
46
- case "--version":
47
- process.stdout.write(`${VERSION}\n`);
48
- return;
49
- case "help":
50
- case "-h":
51
- case "--help":
52
- usage();
53
- return;
54
- default:
55
- process.stderr.write(`Unknown command: ${cmd}\n`);
56
- usage();
57
- process.exit(2);
58
- }
59
- }
60
-
61
- function parseFlags(args) {
62
- const out = { _positional: [] };
63
- for (let i = 0; i < args.length; i++) {
64
- const a = args[i];
65
- if (a.startsWith("--")) {
66
- const key = a.slice(2);
67
- const next = args[i + 1];
68
- if (next && !next.startsWith("--")) {
69
- out[key] = next;
70
- i++;
71
- } else {
72
- out[key] = true;
73
- }
74
- } else {
75
- out._positional.push(a);
76
- }
77
- }
78
- return out;
79
- }
1
+ // stealthos CLI router. Subcommand dispatcher.
2
+
3
+ import { installCommand } from "./commands/install.mjs";
4
+ import { statusCommand } from "./commands/status.mjs";
5
+ import { runCommand } from "./commands/run.mjs";
6
+
7
+ const VERSION = "0.1.0-alpha.5";
8
+
9
+ function usage() {
10
+ process.stdout.write(`
11
+ stealthos v${VERSION}
12
+
13
+ USAGE
14
+ stealthos <command> [options]
15
+
16
+ COMMANDS
17
+ install Install StealthOS knowledge base into ~/.stealthos/
18
+ Options:
19
+ --source <path> Source .ai/ tree (default: auto-detect in monorepo)
20
+ --home <path> Target home (default: ~/.stealthos)
21
+ --force Overwrite existing files
22
+ status Show ~/.stealthos/ install status (version, paths, project count)
23
+ run <wf> Run a workflow via the local MCP server (init|sync|work [intent...])
24
+ version Print stealthos CLI version
25
+ help Show this message
26
+
27
+ ENVIRONMENT
28
+ STEALTHOS_HOME Override target home (default: ~/.stealthos)
29
+ STEALTHOS_SOURCE Override source .ai/ path (default: auto-detect)
30
+ STEALTHOS_DEBUG=1 Print stack traces on errors
31
+
32
+ EXAMPLES
33
+ stealthos install
34
+ stealthos install --home /opt/stealthos
35
+ stealthos status
36
+ `);
37
+ }
38
+
39
+ export async function runCli(argv) {
40
+ const [cmd = "help", ...rest] = argv;
41
+ switch (cmd) {
42
+ case "install":
43
+ return installCommand(parseFlags(rest));
44
+ case "status":
45
+ return statusCommand(parseFlags(rest));
46
+ case "run":
47
+ return runCommand(parseFlags(rest));
48
+ case "version":
49
+ case "-v":
50
+ case "--version":
51
+ process.stdout.write(`${VERSION}\n`);
52
+ return;
53
+ case "help":
54
+ case "-h":
55
+ case "--help":
56
+ usage();
57
+ return;
58
+ default:
59
+ process.stderr.write(`Unknown command: ${cmd}\n`);
60
+ usage();
61
+ process.exit(2);
62
+ }
63
+ }
64
+
65
+ function parseFlags(args) {
66
+ const out = { _positional: [] };
67
+ for (let i = 0; i < args.length; i++) {
68
+ const a = args[i];
69
+ if (a.startsWith("--")) {
70
+ const key = a.slice(2);
71
+ const next = args[i + 1];
72
+ if (next && !next.startsWith("--")) {
73
+ out[key] = next;
74
+ i++;
75
+ } else {
76
+ out[key] = true;
77
+ }
78
+ } else {
79
+ out._positional.push(a);
80
+ }
81
+ }
82
+ return out;
83
+ }
@@ -0,0 +1,117 @@
1
+ // `stealthos run <workflow> [intent...]`
2
+ // Spawns aios-server.mjs as an MCP stdio process and sends a single
3
+ // tools/call for aios_run_workflow. Streams stdout/stderr from the server
4
+ // and prints the parsed tool result.
5
+ //
6
+ // Used by VSCode tasks (and as a generic CLI escape hatch) so users on IDEs
7
+ // without MCP can still trigger workflows directly.
8
+
9
+ import { spawn } from "node:child_process";
10
+ import { existsSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import { homedir } from "node:os";
13
+
14
+ function resolveServerPath() {
15
+ const home = process.env.STEALTHOS_HOME || join(homedir(), ".stealthos");
16
+ const candidate = join(home, "server", "aios-server.mjs");
17
+ if (!existsSync(candidate)) {
18
+ throw new Error(
19
+ `aios-server.mjs nao encontrado em ${candidate}. ` +
20
+ `Rode 'stealthos install' primeiro.`,
21
+ );
22
+ }
23
+ return { home, server: candidate };
24
+ }
25
+
26
+ function sendRpc(child, payload) {
27
+ return new Promise((resolve, reject) => {
28
+ let buf = "";
29
+ const onData = (chunk) => {
30
+ buf += chunk.toString("utf8");
31
+ // MCP framing: each message terminated by newline
32
+ const lines = buf.split("\n");
33
+ buf = lines.pop();
34
+ for (const line of lines) {
35
+ if (!line.trim()) continue;
36
+ try {
37
+ const msg = JSON.parse(line);
38
+ if (msg.id === payload.id) {
39
+ child.stdout.off("data", onData);
40
+ resolve(msg);
41
+ return;
42
+ }
43
+ } catch {
44
+ // ignore non-JSON lines (server logs)
45
+ }
46
+ }
47
+ };
48
+ child.stdout.on("data", onData);
49
+ child.stdin.write(JSON.stringify(payload) + "\n");
50
+ setTimeout(() => {
51
+ child.stdout.off("data", onData);
52
+ reject(new Error("timeout aguardando resposta do MCP server"));
53
+ }, 60000);
54
+ });
55
+ }
56
+
57
+ export async function runCommand(opts) {
58
+ const positional = opts._positional || [];
59
+ const workflow = positional[0];
60
+ if (!workflow) {
61
+ throw new Error("Uso: stealthos run <workflow> [intent...]");
62
+ }
63
+ const intent = positional.slice(1).join(" ");
64
+
65
+ const { home, server } = resolveServerPath();
66
+ process.stdout.write(`▶ stealthos run ${workflow}${intent ? ` (intent: ${intent})` : ""}\n`);
67
+ process.stdout.write(` home: ${home}\n server: ${server}\n\n`);
68
+
69
+ const child = spawn(process.execPath, [server], {
70
+ stdio: ["pipe", "pipe", "inherit"],
71
+ env: {
72
+ ...process.env,
73
+ STEALTHOS_MODE: "hybrid",
74
+ STEALTHOS_HOME: home,
75
+ STEALTHOS_PROJECT_DIR: process.cwd(),
76
+ },
77
+ });
78
+
79
+ let exitCode = 0;
80
+ try {
81
+ // 1. initialize
82
+ await sendRpc(child, {
83
+ jsonrpc: "2.0",
84
+ id: 1,
85
+ method: "initialize",
86
+ params: { protocolVersion: "2024-11-05", capabilities: {}, clientInfo: { name: "stealthos-cli", version: "0.1" } },
87
+ });
88
+
89
+ // 2. tools/call aios_run_workflow
90
+ const resp = await sendRpc(child, {
91
+ jsonrpc: "2.0",
92
+ id: 2,
93
+ method: "tools/call",
94
+ params: {
95
+ name: "aios_run_workflow",
96
+ arguments: { name: workflow, input: { intent } },
97
+ },
98
+ });
99
+
100
+ if (resp.error) {
101
+ process.stderr.write(`Erro do MCP: ${JSON.stringify(resp.error, null, 2)}\n`);
102
+ exitCode = 1;
103
+ } else {
104
+ const content = resp.result?.content || [];
105
+ for (const part of content) {
106
+ if (part.type === "text") process.stdout.write(part.text + "\n");
107
+ }
108
+ }
109
+ } catch (err) {
110
+ process.stderr.write(`Erro: ${err.message}\n`);
111
+ exitCode = 1;
112
+ } finally {
113
+ child.stdin.end();
114
+ child.kill();
115
+ }
116
+ process.exit(exitCode);
117
+ }