ostacky 0.8.1 → 0.8.3
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 +21 -23
- package/assets/agents/ostacky.md +18 -3
- package/assets/commands/install-stack.md +15 -115
- package/assets/docs/ostacky-reference.md +0 -2
- package/assets/mcp/ostacky-controller/controller-core.js +206 -0
- package/assets/mcp/ostacky-controller/index.js +166 -164
- package/assets/mcp/ostacky-controller/package.json +1 -1
- package/assets/plugins/controller-core.ts +206 -0
- package/assets/plugins/ostacky-plugin.ts +12 -124
- package/assets/skills/brainstorming/SKILL.md +6 -4
- package/assets/skills/graceful-degradation/SKILL.md +0 -9
- package/dist/cli.js +537 -720
- package/manifest.json +151 -151
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { createRequire } from "node:module";
|
|
3
2
|
var __create = Object.create;
|
|
4
3
|
var __getProtoOf = Object.getPrototypeOf;
|
|
5
4
|
var __defProp = Object.defineProperty;
|
|
@@ -19,7 +18,7 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
19
18
|
return cached;
|
|
20
19
|
}
|
|
21
20
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
22
|
-
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
21
|
+
const to = isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
23
22
|
if (mod && typeof mod === "object" || typeof mod === "function") {
|
|
24
23
|
for (let key of __getOwnPropNames(mod))
|
|
25
24
|
if (!__hasOwnProp.call(to, key))
|
|
@@ -46,8 +45,17 @@ var __export = (target, all) => {
|
|
|
46
45
|
set: __exportSetter.bind(all, name)
|
|
47
46
|
});
|
|
48
47
|
};
|
|
49
|
-
var __esm = (fn, res) => () =>
|
|
50
|
-
|
|
48
|
+
var __esm = (fn, res, err) => () => {
|
|
49
|
+
if (fn)
|
|
50
|
+
try {
|
|
51
|
+
res = fn(fn = 0);
|
|
52
|
+
} catch (e) {
|
|
53
|
+
err = [e];
|
|
54
|
+
}
|
|
55
|
+
if (err)
|
|
56
|
+
throw err[0];
|
|
57
|
+
return res;
|
|
58
|
+
};
|
|
51
59
|
|
|
52
60
|
// node_modules/sisteransi/src/index.js
|
|
53
61
|
var require_src = __commonJS(function(exports, module) {
|
|
@@ -178,17 +186,6 @@ var require_picocolors = __commonJS(function(exports, module) {
|
|
|
178
186
|
});
|
|
179
187
|
|
|
180
188
|
// src/security.ts
|
|
181
|
-
var exports_security = {};
|
|
182
|
-
__export(exports_security, {
|
|
183
|
-
BASH_SENSITIVE_RE: () => BASH_SENSITIVE_RE,
|
|
184
|
-
SENSITIVE_DEFAULT: () => SENSITIVE_DEFAULT,
|
|
185
|
-
extractPathsFromBash: () => extractPathsFromBash,
|
|
186
|
-
getSensitivePatterns: () => getSensitivePatterns,
|
|
187
|
-
isSensitive: () => isSensitive,
|
|
188
|
-
sha256: () => sha256,
|
|
189
|
-
validateFilePath: () => validateFilePath,
|
|
190
|
-
verifyChecksum: () => verifyChecksum
|
|
191
|
-
});
|
|
192
189
|
import { createHash } from "crypto";
|
|
193
190
|
function validateFilePath(filePath) {
|
|
194
191
|
if (filePath.includes("..") || filePath.startsWith("/") || filePath.startsWith("\\") || /^[a-zA-Z]:/.test(filePath)) {
|
|
@@ -208,124 +205,13 @@ function verifyChecksum(content, expectedHash, label) {
|
|
|
208
205
|
recibido: ${actual}`);
|
|
209
206
|
}
|
|
210
207
|
}
|
|
211
|
-
|
|
212
|
-
if (!filePath)
|
|
213
|
-
return false;
|
|
214
|
-
const normalized = filePath.replace(/\\/g, "/");
|
|
215
|
-
const lower = normalized.toLowerCase();
|
|
216
|
-
if (lower.endsWith(".env.example") || lower.endsWith(".env.template") || lower.endsWith(".env.sample"))
|
|
217
|
-
return false;
|
|
218
|
-
const base = lower.split("/").pop() || "";
|
|
219
|
-
for (const pat of patterns) {
|
|
220
|
-
if (pat.includes(".env") && base.startsWith(".env"))
|
|
221
|
-
return true;
|
|
222
|
-
if (pat.includes(".secrets") && lower.includes(".secrets"))
|
|
223
|
-
return true;
|
|
224
|
-
if (pat.includes("*.pem") && lower.endsWith(".pem"))
|
|
225
|
-
return true;
|
|
226
|
-
if (pat.includes("*.key") && lower.endsWith(".key"))
|
|
227
|
-
return true;
|
|
228
|
-
if (pat.includes(".aws") && lower.includes(".aws"))
|
|
229
|
-
return true;
|
|
230
|
-
if (pat.includes(".ssh") && lower.includes(".ssh"))
|
|
231
|
-
return true;
|
|
232
|
-
if (pat.includes("credentials.json") && lower.endsWith("credentials.json"))
|
|
233
|
-
return true;
|
|
234
|
-
if (pat.includes(".npmrc") && lower.endsWith(".npmrc"))
|
|
235
|
-
return true;
|
|
236
|
-
}
|
|
237
|
-
if (/\.(pem|key)$/i.test(normalized))
|
|
238
|
-
return true;
|
|
239
|
-
if (base.startsWith(".env"))
|
|
240
|
-
return true;
|
|
241
|
-
return false;
|
|
242
|
-
}
|
|
243
|
-
function extractPathsFromBash(cmd) {
|
|
244
|
-
if (!cmd)
|
|
245
|
-
return [];
|
|
246
|
-
const normalized = cmd.replace(/&&/g, ";").replace(/\|\|/g, ";");
|
|
247
|
-
const segments = normalized.split(/[|;><\n]+/);
|
|
248
|
-
const paths = [];
|
|
249
|
-
for (const seg of segments) {
|
|
250
|
-
const trimmed = seg.trim();
|
|
251
|
-
if (!trimmed)
|
|
252
|
-
continue;
|
|
253
|
-
const tokens = trimmed.match(/(?:[^\s"'`\\]+|"[^"]*"|'[^']*'|`[^`]*`)+/g) || [];
|
|
254
|
-
for (let token of tokens) {
|
|
255
|
-
const stripped = token.replace(/["'`]/g, "").replace(/\\/g, "");
|
|
256
|
-
if (!stripped)
|
|
257
|
-
continue;
|
|
258
|
-
if (["cat", "grep", "ls", "echo", "awk", "sed", "cut", "head", "tail", "wc", "find", "xargs", "bash", "sh", "zsh", "env", "printenv", "node", "bun", "npm", "npx", "ls"].includes(stripped))
|
|
259
|
-
continue;
|
|
260
|
-
if (stripped.startsWith("-"))
|
|
261
|
-
continue;
|
|
262
|
-
const lower = stripped.toLowerCase();
|
|
263
|
-
if (stripped.includes("/") || stripped.includes(".") || lower.startsWith(".env") || lower.includes(".secrets") || lower.endsWith(".pem") || lower.endsWith(".key") || lower.includes(".aws") || lower.includes(".ssh") || lower.endsWith("credentials.json") || lower.endsWith(".npmrc")) {
|
|
264
|
-
const cleaned = stripped.replace(/[,:;)\]]+$/, "");
|
|
265
|
-
if (cleaned)
|
|
266
|
-
paths.push(cleaned);
|
|
267
|
-
} else if (stripped === ".env") {
|
|
268
|
-
paths.push(stripped);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
return [...new Set(paths)];
|
|
273
|
-
}
|
|
274
|
-
function getSensitivePatterns() {
|
|
275
|
-
const raw = process.env.OSTACKY_SENSITIVE_PATTERNS;
|
|
276
|
-
if (!raw)
|
|
277
|
-
return SENSITIVE_DEFAULT;
|
|
278
|
-
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
279
|
-
}
|
|
280
|
-
var SENSITIVE_DEFAULT, BASH_SENSITIVE_RE;
|
|
281
|
-
var init_security = __esm(() => {
|
|
282
|
-
SENSITIVE_DEFAULT = [
|
|
283
|
-
"**/.env*",
|
|
284
|
-
"**/.secrets/**",
|
|
285
|
-
"**/*.pem",
|
|
286
|
-
"**/*.key",
|
|
287
|
-
"**/.aws/**",
|
|
288
|
-
"**/.ssh/**",
|
|
289
|
-
"**/credentials.json",
|
|
290
|
-
"**/.npmrc"
|
|
291
|
-
];
|
|
292
|
-
BASH_SENSITIVE_RE = /(?:^|[^a-zA-Z0-9_.-])(\.env(\b|[_.-])|\.secrets\b|\.pem\b|\.key\b|credentials\.json|\.aws\b|\.ssh\b|\.npmrc\b)/i;
|
|
293
|
-
});
|
|
208
|
+
var init_security = () => {};
|
|
294
209
|
|
|
295
210
|
// src/fs.ts
|
|
296
|
-
var exports_fs = {};
|
|
297
|
-
__export(exports_fs, {
|
|
298
|
-
USER_AGENT: () => USER_AGENT,
|
|
299
|
-
checkBunAvailability: () => checkBunAvailability,
|
|
300
|
-
computeTreeHash: () => computeTreeHash,
|
|
301
|
-
copyDirRecursive: () => copyDirRecursive,
|
|
302
|
-
createOpenCodeDir: () => createOpenCodeDir,
|
|
303
|
-
detectPlatformTarget: () => detectPlatformTarget,
|
|
304
|
-
downloadAndExtract: () => downloadAndExtract,
|
|
305
|
-
downloadAndExtractWithRetry: () => downloadAndExtractWithRetry,
|
|
306
|
-
downloadToFile: () => downloadToFile,
|
|
307
|
-
downloadWithRetry: () => downloadWithRetry,
|
|
308
|
-
ensureOpenCodePaths: () => ensureOpenCodePaths,
|
|
309
|
-
ensureToolDirs: () => ensureToolDirs,
|
|
310
|
-
findBinaryInDir: () => findBinaryInDir,
|
|
311
|
-
findExecutablePath: () => findExecutablePath,
|
|
312
|
-
findOpenCodeDir: () => findOpenCodeDir,
|
|
313
|
-
findProjectRoot: () => findProjectRoot,
|
|
314
|
-
getBunInstallCommand: () => getBunInstallCommand,
|
|
315
|
-
getCommandInvocation: () => getCommandInvocation,
|
|
316
|
-
getEngramReleaseTarget: () => getEngramReleaseTarget,
|
|
317
|
-
getExecutableName: () => getExecutableName,
|
|
318
|
-
getExecutableNames: () => getExecutableNames,
|
|
319
|
-
getGlobalOpenCodeDir: () => getGlobalOpenCodeDir,
|
|
320
|
-
getOpenCodeDirForScope: () => getOpenCodeDirForScope,
|
|
321
|
-
isCommandAvailable: () => isCommandAvailable,
|
|
322
|
-
parseScopeArg: () => parseScopeArg,
|
|
323
|
-
promoteStagedDirectory: () => promoteStagedDirectory,
|
|
324
|
-
shouldRetryDownload: () => shouldRetryDownload
|
|
325
|
-
});
|
|
326
211
|
import {
|
|
327
212
|
existsSync,
|
|
328
213
|
mkdirSync,
|
|
214
|
+
writeFileSync,
|
|
329
215
|
unlinkSync,
|
|
330
216
|
readdirSync,
|
|
331
217
|
statSync,
|
|
@@ -336,9 +222,8 @@ import {
|
|
|
336
222
|
renameSync
|
|
337
223
|
} from "fs";
|
|
338
224
|
import { createHash as createHash2 } from "crypto";
|
|
339
|
-
import { join, resolve, dirname, relative, basename
|
|
225
|
+
import { join, resolve, dirname, relative, basename } from "path";
|
|
340
226
|
import { execFileSync } from "child_process";
|
|
341
|
-
import { homedir } from "os";
|
|
342
227
|
function findOpenCodeDir(startDir = process.cwd()) {
|
|
343
228
|
let current = resolve(startDir);
|
|
344
229
|
while (true) {
|
|
@@ -370,47 +255,11 @@ function findProjectRoot(startDir = process.cwd()) {
|
|
|
370
255
|
current = parent;
|
|
371
256
|
}
|
|
372
257
|
}
|
|
373
|
-
function getGlobalOpenCodeDir(platform = process.platform, home = homedir()) {
|
|
374
|
-
if (platform === "win32") {
|
|
375
|
-
const appData = process.env.APPDATA ?? win32.join(home, "AppData", "Roaming");
|
|
376
|
-
return win32.join(appData, "opencode");
|
|
377
|
-
}
|
|
378
|
-
const xdg = process.env.XDG_CONFIG_HOME ?? join(home, ".config");
|
|
379
|
-
return join(xdg, "opencode");
|
|
380
|
-
}
|
|
381
258
|
function getOpenCodeDirForScope(scope, cwd = process.cwd()) {
|
|
382
|
-
if (scope === "global")
|
|
383
|
-
return getGlobalOpenCodeDir();
|
|
384
|
-
if (scope === "local") {
|
|
385
|
-
const existing2 = findOpenCodeDir(cwd);
|
|
386
|
-
if (existing2)
|
|
387
|
-
return existing2;
|
|
388
|
-
return join(findProjectRoot(cwd), ".opencode");
|
|
389
|
-
}
|
|
390
259
|
const existing = findOpenCodeDir(cwd);
|
|
391
260
|
if (existing)
|
|
392
261
|
return existing;
|
|
393
|
-
|
|
394
|
-
if (existsSync(join(root, ".opencode")) || existsSync(join(root, ".git"))) {
|
|
395
|
-
return join(root, ".opencode");
|
|
396
|
-
}
|
|
397
|
-
return getGlobalOpenCodeDir();
|
|
398
|
-
}
|
|
399
|
-
function parseScopeArg(argv = process.argv) {
|
|
400
|
-
for (let i = 0;i < argv.length; i++) {
|
|
401
|
-
const arg = argv[i];
|
|
402
|
-
if (arg === "--scope" && i + 1 < argv.length) {
|
|
403
|
-
const v3 = argv[i + 1];
|
|
404
|
-
if (v3 === "local" || v3 === "global" || v3 === "auto")
|
|
405
|
-
return v3;
|
|
406
|
-
}
|
|
407
|
-
if (arg.startsWith("--scope=")) {
|
|
408
|
-
const v3 = arg.split("=")[1];
|
|
409
|
-
if (v3 === "local" || v3 === "global" || v3 === "auto")
|
|
410
|
-
return v3;
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
return null;
|
|
262
|
+
return join(findProjectRoot(cwd), ".opencode");
|
|
414
263
|
}
|
|
415
264
|
function ensureOpenCodePaths(opencodeDir) {
|
|
416
265
|
const paths = {
|
|
@@ -435,9 +284,6 @@ function ensureToolDirs(toolsDir, toolNames) {
|
|
|
435
284
|
mkdirSync(dir, { recursive: true });
|
|
436
285
|
}
|
|
437
286
|
}
|
|
438
|
-
function createOpenCodeDir(baseDir) {
|
|
439
|
-
return ensureOpenCodePaths(join(baseDir, ".opencode"));
|
|
440
|
-
}
|
|
441
287
|
function copyDirRecursive(src, dest, skipGenerated = false) {
|
|
442
288
|
if (!existsSync(dest))
|
|
443
289
|
mkdirSync(dest, { recursive: true });
|
|
@@ -498,44 +344,6 @@ function findExecutablePath(cmd) {
|
|
|
498
344
|
return null;
|
|
499
345
|
}
|
|
500
346
|
}
|
|
501
|
-
function getBunInstallCommand() {
|
|
502
|
-
const platform = process.platform;
|
|
503
|
-
if (platform === "darwin" || platform === "linux") {
|
|
504
|
-
return {
|
|
505
|
-
command: "curl -fsSL https://bun.com/install | bash",
|
|
506
|
-
note: platform === "linux" ? "Requires 'unzip' package (sudo apt install unzip)" : undefined
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
if (platform === "win32") {
|
|
510
|
-
return {
|
|
511
|
-
command: 'powershell -c "irm bun.sh/install.ps1|iex"',
|
|
512
|
-
note: "Requires Windows 10 v1809 or later"
|
|
513
|
-
};
|
|
514
|
-
}
|
|
515
|
-
return {
|
|
516
|
-
command: "npm install -g bun",
|
|
517
|
-
note: "Cross-platform fallback"
|
|
518
|
-
};
|
|
519
|
-
}
|
|
520
|
-
function checkBunAvailability() {
|
|
521
|
-
const bunPath = findExecutablePath("bun");
|
|
522
|
-
if (bunPath) {
|
|
523
|
-
let version;
|
|
524
|
-
try {
|
|
525
|
-
version = execFileSync(bunPath, ["--version"], {
|
|
526
|
-
encoding: "utf-8",
|
|
527
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
528
|
-
}).trim();
|
|
529
|
-
} catch {}
|
|
530
|
-
return { available: true, path: bunPath, version };
|
|
531
|
-
}
|
|
532
|
-
const { command, note } = getBunInstallCommand();
|
|
533
|
-
return {
|
|
534
|
-
available: false,
|
|
535
|
-
installCommand: command,
|
|
536
|
-
installNote: note
|
|
537
|
-
};
|
|
538
|
-
}
|
|
539
347
|
function detectPlatformTarget(platform = process.platform, arch = process.arch) {
|
|
540
348
|
let os;
|
|
541
349
|
let cpu;
|
|
@@ -620,7 +428,7 @@ function promoteStagedDirectory(stagedDir, destinationDir) {
|
|
|
620
428
|
};
|
|
621
429
|
}
|
|
622
430
|
function downloadToFile(url, dest, timeoutMs = 180000) {
|
|
623
|
-
return new Promise((
|
|
431
|
+
return new Promise((resolve, reject) => {
|
|
624
432
|
const controller = new AbortController;
|
|
625
433
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
626
434
|
fetch(url, { signal: controller.signal, headers: { "User-Agent": USER_AGENT } }).then((res) => {
|
|
@@ -636,7 +444,7 @@ function downloadToFile(url, dest, timeoutMs = 180000) {
|
|
|
636
444
|
reject(error);
|
|
637
445
|
};
|
|
638
446
|
file.once("error", fail);
|
|
639
|
-
file.once("finish",
|
|
447
|
+
file.once("finish", resolve);
|
|
640
448
|
const writable = new WritableStream({
|
|
641
449
|
write(chunk) {
|
|
642
450
|
return new Promise((ok, failWrite) => file.write(Buffer.from(chunk), (err) => err ? failWrite(err) : ok()));
|
|
@@ -657,23 +465,6 @@ function downloadToFile(url, dest, timeoutMs = 180000) {
|
|
|
657
465
|
});
|
|
658
466
|
});
|
|
659
467
|
}
|
|
660
|
-
async function downloadWithRetry(url, dest, maxRetries = 2, timeoutMs = 180000) {
|
|
661
|
-
let lastError = null;
|
|
662
|
-
for (let attempt = 0;attempt <= maxRetries; attempt++) {
|
|
663
|
-
try {
|
|
664
|
-
await downloadToFile(url, dest, timeoutMs);
|
|
665
|
-
return;
|
|
666
|
-
} catch (err) {
|
|
667
|
-
lastError = err;
|
|
668
|
-
if (attempt < maxRetries && shouldRetryDownload(lastError)) {
|
|
669
|
-
const delay = 1000 * Math.pow(2, attempt);
|
|
670
|
-
console.error(`[download] Intento ${attempt + 1} falló: ${lastError.message}. Reintentando en ${delay}ms...`);
|
|
671
|
-
await new Promise((r2) => setTimeout(r2, delay));
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
throw lastError;
|
|
676
|
-
}
|
|
677
468
|
async function downloadAndExtract(url, destDir, stripComponents = 1, timeoutMs = 180000) {
|
|
678
469
|
const tmp = join(dirname(destDir), `.${basename(destDir)}.download-${Date.now()}-${process.pid}`);
|
|
679
470
|
if (!existsSync(tmp))
|
|
@@ -714,7 +505,7 @@ async function downloadAndExtractWithRetry(url, destDir, stripComponents = 1, ti
|
|
|
714
505
|
if (attempt < maxRetries && shouldRetryDownload(lastError)) {
|
|
715
506
|
const delay = 1000 * Math.pow(2, attempt);
|
|
716
507
|
console.error(`[download] Intento ${attempt + 1} falló: ${lastError.message}. Reintentando en ${delay}ms...`);
|
|
717
|
-
await new Promise((
|
|
508
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
718
509
|
}
|
|
719
510
|
}
|
|
720
511
|
}
|
|
@@ -735,40 +526,126 @@ function findBinaryInDir(dir, name) {
|
|
|
735
526
|
}
|
|
736
527
|
return null;
|
|
737
528
|
}
|
|
738
|
-
|
|
529
|
+
function ensureGitignore(projectRoot = findProjectRoot()) {
|
|
530
|
+
const gitignorePath = join(projectRoot, ".gitignore");
|
|
531
|
+
const patternsAdded = [];
|
|
532
|
+
let created = false;
|
|
533
|
+
let updated = false;
|
|
534
|
+
let raw = "";
|
|
535
|
+
if (existsSync(gitignorePath)) {
|
|
536
|
+
raw = readFileSync(gitignorePath, "utf-8");
|
|
537
|
+
} else {
|
|
538
|
+
created = true;
|
|
539
|
+
}
|
|
540
|
+
const hasHeader = raw.includes(OSTACKY_GITIGNORE_HEADER);
|
|
541
|
+
const missingPatterns = OSTACKY_GITIGNORE_PATTERNS.filter((p) => !raw.includes(p));
|
|
542
|
+
if (missingPatterns.length === 0 && hasHeader) {
|
|
543
|
+
if (raw.length > 0 && !raw.endsWith(`
|
|
544
|
+
`)) {
|
|
545
|
+
writeFileSync(gitignorePath, raw + `
|
|
546
|
+
`, "utf-8");
|
|
547
|
+
updated = true;
|
|
548
|
+
}
|
|
549
|
+
return { created, updated, patternsAdded };
|
|
550
|
+
}
|
|
551
|
+
if (!hasHeader) {
|
|
552
|
+
const needsNewline = raw.length > 0 && !raw.endsWith(`
|
|
553
|
+
`);
|
|
554
|
+
const block = [OSTACKY_GITIGNORE_HEADER, ...OSTACKY_GITIGNORE_PATTERNS].join(`
|
|
555
|
+
`) + `
|
|
556
|
+
`;
|
|
557
|
+
const newContent = raw + (needsNewline ? `
|
|
558
|
+
` : raw.length > 0 ? "" : "") + block;
|
|
559
|
+
let final = "";
|
|
560
|
+
if (raw.length === 0) {
|
|
561
|
+
final = block;
|
|
562
|
+
} else if (raw.endsWith(`
|
|
563
|
+
`)) {
|
|
564
|
+
final = raw + block;
|
|
565
|
+
} else {
|
|
566
|
+
final = raw + `
|
|
567
|
+
` + block;
|
|
568
|
+
}
|
|
569
|
+
writeFileSync(gitignorePath, final, "utf-8");
|
|
570
|
+
patternsAdded.push(...missingPatterns.length ? missingPatterns : [...OSTACKY_GITIGNORE_PATTERNS]);
|
|
571
|
+
updated = true;
|
|
572
|
+
return { created, updated, patternsAdded };
|
|
573
|
+
}
|
|
574
|
+
const lines = raw.split(`
|
|
575
|
+
`);
|
|
576
|
+
const headerIdx = lines.findIndex((l) => l.trim() === OSTACKY_GITIGNORE_HEADER);
|
|
577
|
+
if (headerIdx !== -1) {
|
|
578
|
+
let insertIdx = headerIdx + 1;
|
|
579
|
+
while (insertIdx < lines.length && (OSTACKY_GITIGNORE_PATTERNS.some((p) => lines[insertIdx].trim() === p) || lines[insertIdx].trim() === "")) {
|
|
580
|
+
insertIdx++;
|
|
581
|
+
}
|
|
582
|
+
lines.splice(insertIdx, 0, ...missingPatterns);
|
|
583
|
+
const newContent = lines.join(`
|
|
584
|
+
`);
|
|
585
|
+
const finalContent = newContent.endsWith(`
|
|
586
|
+
`) ? newContent : newContent + `
|
|
587
|
+
`;
|
|
588
|
+
writeFileSync(gitignorePath, finalContent, "utf-8");
|
|
589
|
+
patternsAdded.push(...missingPatterns);
|
|
590
|
+
updated = true;
|
|
591
|
+
} else {
|
|
592
|
+
const newContent = raw.endsWith(`
|
|
593
|
+
`) ? raw + missingPatterns.join(`
|
|
594
|
+
`) + `
|
|
595
|
+
` : raw + `
|
|
596
|
+
` + missingPatterns.join(`
|
|
597
|
+
`) + `
|
|
598
|
+
`;
|
|
599
|
+
writeFileSync(gitignorePath, newContent, "utf-8");
|
|
600
|
+
patternsAdded.push(...missingPatterns);
|
|
601
|
+
updated = true;
|
|
602
|
+
}
|
|
603
|
+
return { created, updated, patternsAdded };
|
|
604
|
+
}
|
|
605
|
+
var USER_AGENT = "ostacky-installer", OSTACKY_GITIGNORE_PATTERNS, OSTACKY_GITIGNORE_HEADER = "# Ostacky";
|
|
739
606
|
var init_fs = __esm(() => {
|
|
740
607
|
init_security();
|
|
608
|
+
OSTACKY_GITIGNORE_PATTERNS = [
|
|
609
|
+
".opencode/tools/",
|
|
610
|
+
".opencode/cache/",
|
|
611
|
+
".opencode/ostacky-state.json",
|
|
612
|
+
".opencode/ostacky-state.json.backup*",
|
|
613
|
+
".opencode/ostacky-state.json.lock.*",
|
|
614
|
+
".opencode/ostacky-audit.jsonl",
|
|
615
|
+
".codegraph/",
|
|
616
|
+
"openspec/"
|
|
617
|
+
];
|
|
741
618
|
});
|
|
742
619
|
|
|
743
620
|
// manifest.json
|
|
744
621
|
var manifest_default;
|
|
745
622
|
var init_manifest = __esm(() => {
|
|
746
623
|
manifest_default = {
|
|
747
|
-
version: "0.8.
|
|
624
|
+
version: "0.8.3",
|
|
748
625
|
repo: "JaimeHoracio/Ostacky",
|
|
749
|
-
tag: "v0.8.
|
|
626
|
+
tag: "v0.8.3",
|
|
750
627
|
agents: [
|
|
751
628
|
{
|
|
752
629
|
name: "ostacky",
|
|
753
630
|
file: "assets/agents/ostacky.md",
|
|
754
|
-
description: "Orquestador con recuperación automática (nunca se congela), ruteo por nivel de impacto, controller MCP con SDK oficial, edición segura con fallback inline, y delegación en OpenSpec + Superpowers. v0.8.
|
|
755
|
-
version: "0.8.
|
|
756
|
-
sha256: "
|
|
631
|
+
description: "Orquestador con recuperación automática (nunca se congela), ruteo por nivel de impacto, controller MCP con SDK oficial, edición segura con fallback inline, y delegación en OpenSpec + Superpowers. v0.8.3: prune de skills obsoletas, lastHandoff, getAvailableTransitions, consecutiveFailures real, mem_session_summary automático al cierre.",
|
|
632
|
+
version: "0.8.3",
|
|
633
|
+
sha256: "b32ec792c01d1f2c4dac58c4de752e4d30f5df8315b33512f4a7efc15a7380eb"
|
|
757
634
|
}
|
|
758
635
|
],
|
|
759
636
|
commands: [
|
|
760
637
|
{
|
|
761
638
|
name: "install-stack",
|
|
762
639
|
file: "assets/commands/install-stack.md",
|
|
763
|
-
description: "Instala el stack tecnológico del proyecto (CodeGraph, skills, OpenSpec, Engram, controller plugin). v0.8.
|
|
764
|
-
version: "0.8.
|
|
765
|
-
sha256: "
|
|
640
|
+
description: "Instala el stack tecnológico del proyecto (CodeGraph, skills, OpenSpec, Engram, controller plugin). v0.8.3: controller plugin como alma hard-gate, Context7 removido del stack.",
|
|
641
|
+
version: "0.8.3",
|
|
642
|
+
sha256: "24802dc29675a584dacf4d0ac8a944dd935ee0f96a4e804a9b1c5bdb3709438c"
|
|
766
643
|
},
|
|
767
644
|
{
|
|
768
645
|
name: "opsx-sync",
|
|
769
646
|
file: "assets/commands/opsx-sync.md",
|
|
770
647
|
description: "Sincroniza delta specs del change activo sin inicializar CodeGraph si ya existe índice",
|
|
771
|
-
version: "0.8.
|
|
648
|
+
version: "0.8.3",
|
|
772
649
|
sha256: "fe0158478f2ca63b315037a85fc1632b77868532e319af6c7384285441767d64"
|
|
773
650
|
}
|
|
774
651
|
],
|
|
@@ -776,15 +653,15 @@ var init_manifest = __esm(() => {
|
|
|
776
653
|
{
|
|
777
654
|
name: "ostacky-controller",
|
|
778
655
|
file: "assets/mcp/ostacky-controller/",
|
|
779
|
-
description: "Máquina de estados persistida con @modelcontextprotocol/server SDK. v0.8.
|
|
780
|
-
version: "0.8.
|
|
781
|
-
sha256: "
|
|
656
|
+
description: "Máquina de estados persistida con @modelcontextprotocol/server SDK. v0.8.3: 22 tools (incluye set_handoff, get_handoff, clear_handoff, get_available_transitions funcional). Bugfixes: consecutiveFailures real, lastHandoff state, defaultChoice persistido. Robustez: degraded mode automático tras 3 fallos, persistence condicional para Nivel 0, prune de skills obsoletas.",
|
|
657
|
+
version: "0.8.3",
|
|
658
|
+
sha256: "da929dae61b9caa0b818ee1c69c5573075d84cb8d31da6910888fc5b6163a0d1"
|
|
782
659
|
},
|
|
783
660
|
{
|
|
784
661
|
name: "openspec",
|
|
785
662
|
file: "assets/mcp/openspec/",
|
|
786
663
|
description: "MCP server local para OpenSpec - proposal, apply, archive, sync de cambios",
|
|
787
|
-
version: "0.8.
|
|
664
|
+
version: "0.8.3",
|
|
788
665
|
sha256: "fa4be28bfc1e75db8f1a037e2580f04a60066c80e8e5934e90e1041020d04609"
|
|
789
666
|
}
|
|
790
667
|
],
|
|
@@ -793,106 +670,106 @@ var init_manifest = __esm(() => {
|
|
|
793
670
|
name: "brainstorming",
|
|
794
671
|
file: "assets/skills/brainstorming/SKILL.md",
|
|
795
672
|
description: "Skill unificado de pensamiento con dos modos: creative-design (producción de diseño → transición a implementación directa o openspec-propose) y open-exploration (exploración libre)",
|
|
796
|
-
version: "0.8.
|
|
797
|
-
sha256: "
|
|
673
|
+
version: "0.8.3",
|
|
674
|
+
sha256: "f1013a86a8f27914d85a716e23c7fd32db1786d93615e8b0834b13d184308549"
|
|
798
675
|
},
|
|
799
676
|
{
|
|
800
677
|
name: "execution-mode-evaluation",
|
|
801
678
|
file: "assets/skills/execution-mode-evaluation/SKILL.md",
|
|
802
679
|
description: "Skill de análisis de modo de ejecución — output reconciliado con controller snapshot contract (recommendation field)",
|
|
803
|
-
version: "0.8.
|
|
680
|
+
version: "0.8.3",
|
|
804
681
|
sha256: "7b89dda29927c88f1eb13ffcea541a12423384c172f66c4f3d9b61efe0c7a163"
|
|
805
682
|
},
|
|
806
683
|
{
|
|
807
684
|
name: "tdd",
|
|
808
685
|
file: "assets/skills/tdd/SKILL.md",
|
|
809
686
|
description: "Skill de test-driven development (Superpowers)",
|
|
810
|
-
version: "0.8.
|
|
687
|
+
version: "0.8.3",
|
|
811
688
|
sha256: "aa412298980b7826165c211145c1b8e9135f68f36247bb3cb96d0a0eae274486"
|
|
812
689
|
},
|
|
813
690
|
{
|
|
814
691
|
name: "subagent-driven-development",
|
|
815
692
|
file: "assets/skills/subagent-driven-development/SKILL.md",
|
|
816
693
|
description: "Skill de ejecución con subagentes (Superpowers) — ejecuta solo después de confirmación del coordinador Ostacky",
|
|
817
|
-
version: "0.8.
|
|
694
|
+
version: "0.8.3",
|
|
818
695
|
sha256: "1a42d714a9a13faf05f0bf7b580e8d837c2e77a5633839542ccce603023419e8"
|
|
819
696
|
},
|
|
820
697
|
{
|
|
821
698
|
name: "dispatching-parallel-agents",
|
|
822
699
|
file: "assets/skills/dispatching-parallel-agents/SKILL.md",
|
|
823
700
|
description: "Skill de dispatch paralelo de agentes (Superpowers)",
|
|
824
|
-
version: "0.8.
|
|
701
|
+
version: "0.8.3",
|
|
825
702
|
sha256: "3c8a66d51ae2e719e877d02c3dac32bd804722c3bb9227c258ecae78252a20ab"
|
|
826
703
|
},
|
|
827
704
|
{
|
|
828
705
|
name: "review",
|
|
829
706
|
file: "assets/skills/review/SKILL.md",
|
|
830
707
|
description: "Skill de revisión de código (Superpowers)",
|
|
831
|
-
version: "0.8.
|
|
708
|
+
version: "0.8.3",
|
|
832
709
|
sha256: "b19650ed4d1d4d9857a4dd5b7e08e91328a1a1fc9c45bcc2fb600fa9d0213279"
|
|
833
710
|
},
|
|
834
711
|
{
|
|
835
712
|
name: "receiving-code-review",
|
|
836
713
|
file: "assets/skills/receiving-code-review/SKILL.md",
|
|
837
714
|
description: "Skill de recibir y procesar feedback de code review",
|
|
838
|
-
version: "0.8.
|
|
715
|
+
version: "0.8.3",
|
|
839
716
|
sha256: "d761e884e71d8d3476ac734d287cae403a4024506a7d12e7361a448217c0a831"
|
|
840
717
|
},
|
|
841
718
|
{
|
|
842
719
|
name: "openspec-propose",
|
|
843
720
|
file: "assets/skills/openspec-propose/SKILL.md",
|
|
844
721
|
description: "Skill de generación de proposal (OpenSpec)",
|
|
845
|
-
version: "0.8.
|
|
722
|
+
version: "0.8.3",
|
|
846
723
|
sha256: "bb59100b9fd3c9f9a1ec6509fa975ec55bf7fc737df4fe73bb3e9bbc217b83f7"
|
|
847
724
|
},
|
|
848
725
|
{
|
|
849
726
|
name: "openspec-apply-change",
|
|
850
727
|
file: "assets/skills/openspec-apply-change/SKILL.md",
|
|
851
728
|
description: "Skill de aplicación de change (OpenSpec)",
|
|
852
|
-
version: "0.8.
|
|
729
|
+
version: "0.8.3",
|
|
853
730
|
sha256: "dfc823bf89fc7505e91ab6dee9c1f004be38a410bd3d88fb67b211b1b6cbb1d0"
|
|
854
731
|
},
|
|
855
732
|
{
|
|
856
733
|
name: "openspec-archive-change",
|
|
857
734
|
file: "assets/skills/openspec-archive-change/SKILL.md",
|
|
858
735
|
description: "Skill de archivo de change (OpenSpec)",
|
|
859
|
-
version: "0.8.
|
|
736
|
+
version: "0.8.3",
|
|
860
737
|
sha256: "16e4b561de7747283663fed602e506abf502452c49a8d4b86d7a5539c40f0195"
|
|
861
738
|
},
|
|
862
739
|
{
|
|
863
740
|
name: "openspec-explore",
|
|
864
741
|
file: "assets/skills/openspec-explore/SKILL.md",
|
|
865
742
|
description: "Modo explore para OpenSpec — thinking partner para explorar ideas, investigar problemas y clarificar requisitos antes/durante un cambio",
|
|
866
|
-
version: "0.8.
|
|
743
|
+
version: "0.8.3",
|
|
867
744
|
sha256: "37ae4aaf17ea71a395bab6dc4d9d61b9a49d7910f6692535ae8553244c46880b"
|
|
868
745
|
},
|
|
869
746
|
{
|
|
870
747
|
name: "using-git-worktrees",
|
|
871
748
|
file: "assets/skills/using-git-worktrees/SKILL.md",
|
|
872
749
|
description: "Skill de uso de git worktrees para aislamiento de trabajo",
|
|
873
|
-
version: "0.8.
|
|
750
|
+
version: "0.8.3",
|
|
874
751
|
sha256: "93341bc1b7c053618a8b6dc07e3615b77990d7b549a0201f5835d95fef67ce13"
|
|
875
752
|
},
|
|
876
753
|
{
|
|
877
754
|
name: "using-superpowers",
|
|
878
755
|
file: "assets/skills/using-superpowers/SKILL.md",
|
|
879
756
|
description: "Skill de orquestación de Superpowers skills",
|
|
880
|
-
version: "0.8.
|
|
757
|
+
version: "0.8.3",
|
|
881
758
|
sha256: "7e54536f96d2a379185a10bfc1e970850caa2561b6d8aca7080f0defea0381a4"
|
|
882
759
|
},
|
|
883
760
|
{
|
|
884
761
|
name: "writing-skills",
|
|
885
762
|
file: "assets/skills/writing-skills/SKILL.md",
|
|
886
763
|
description: "Skill de creación y edición de skills",
|
|
887
|
-
version: "0.8.
|
|
764
|
+
version: "0.8.3",
|
|
888
765
|
sha256: "3d76b906cee518a2b809febb70db95697a35b9f368986bb504b4120c3bfb437a"
|
|
889
766
|
},
|
|
890
767
|
{
|
|
891
768
|
name: "graceful-degradation",
|
|
892
769
|
file: "assets/skills/graceful-degradation/SKILL.md",
|
|
893
770
|
description: "Skill de degradación graceful cuando múltiples tools están indisponibles",
|
|
894
|
-
version: "0.8.
|
|
895
|
-
sha256: "
|
|
771
|
+
version: "0.8.3",
|
|
772
|
+
sha256: "79d0eeb2116382cb5db5b753277a333b7ca5682f4a541170b4a6158125c2cb50"
|
|
896
773
|
}
|
|
897
774
|
]
|
|
898
775
|
};
|
|
@@ -933,19 +810,6 @@ var init_cache = __esm(() => {
|
|
|
933
810
|
});
|
|
934
811
|
|
|
935
812
|
// src/github.ts
|
|
936
|
-
var exports_github = {};
|
|
937
|
-
__export(exports_github, {
|
|
938
|
-
BUNDLED_MCP_DIR: () => BUNDLED_MCP_DIR,
|
|
939
|
-
BUNDLED_SKILLS_DIR: () => BUNDLED_SKILLS_DIR,
|
|
940
|
-
PACKAGE_ROOT: () => PACKAGE_ROOT,
|
|
941
|
-
downloadFile: () => downloadFile,
|
|
942
|
-
fetchLatestManifest: () => fetchLatestManifest,
|
|
943
|
-
fetchLatestReleaseTag: () => fetchLatestReleaseTag,
|
|
944
|
-
fetchManifest: () => fetchManifest,
|
|
945
|
-
getBundledMcpPath: () => getBundledMcpPath,
|
|
946
|
-
getBundledSkillPath: () => getBundledSkillPath,
|
|
947
|
-
getRawUrl: () => getRawUrl
|
|
948
|
-
});
|
|
949
813
|
import { fileURLToPath } from "url";
|
|
950
814
|
import { dirname as dirname3, join as join3 } from "path";
|
|
951
815
|
function getBundledSkillPath(name) {
|
|
@@ -977,9 +841,9 @@ async function fetchLatestReleaseTag(repo) {
|
|
|
977
841
|
headers: { "User-Agent": USER_AGENT }
|
|
978
842
|
});
|
|
979
843
|
const loc = res.headers.get("location") ?? "";
|
|
980
|
-
const
|
|
981
|
-
if (
|
|
982
|
-
return
|
|
844
|
+
const m = loc.match(/releases\/tag\/(v[^/]+)$/);
|
|
845
|
+
if (m)
|
|
846
|
+
return m[1];
|
|
983
847
|
} catch {}
|
|
984
848
|
return null;
|
|
985
849
|
}
|
|
@@ -1044,15 +908,6 @@ var init_github = __esm(() => {
|
|
|
1044
908
|
});
|
|
1045
909
|
|
|
1046
910
|
// src/lockfile.ts
|
|
1047
|
-
var exports_lockfile = {};
|
|
1048
|
-
__export(exports_lockfile, {
|
|
1049
|
-
clearLockfile: () => clearLockfile,
|
|
1050
|
-
getInstalledVersion: () => getInstalledVersion,
|
|
1051
|
-
getLockfilePath: () => getLockfilePath,
|
|
1052
|
-
readLockfile: () => readLockfile,
|
|
1053
|
-
removeFromLockfile: () => removeFromLockfile,
|
|
1054
|
-
writeLockfile: () => writeLockfile
|
|
1055
|
-
});
|
|
1056
911
|
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
1057
912
|
import { join as join4 } from "path";
|
|
1058
913
|
function getLockfilePath(opencodeRoot) {
|
|
@@ -1114,19 +969,6 @@ var init_lockfile = __esm(() => {
|
|
|
1114
969
|
});
|
|
1115
970
|
|
|
1116
971
|
// src/config.ts
|
|
1117
|
-
var exports_config = {};
|
|
1118
|
-
__export(exports_config, {
|
|
1119
|
-
ensureMcpEntry: () => ensureMcpEntry,
|
|
1120
|
-
ensureMcpEntryAtProjectRoot: () => ensureMcpEntryAtProjectRoot,
|
|
1121
|
-
ensureOpenCodeConfig: () => ensureOpenCodeConfig,
|
|
1122
|
-
findOpenCodeConfig: () => findOpenCodeConfig,
|
|
1123
|
-
patchOpenCodeConfig: () => patchOpenCodeConfig,
|
|
1124
|
-
readOpenCodeConfig: () => readOpenCodeConfig,
|
|
1125
|
-
setMcpEntry: () => setMcpEntry,
|
|
1126
|
-
setMcpEntryAtProjectRoot: () => setMcpEntryAtProjectRoot,
|
|
1127
|
-
stripJsoncComments: () => stripJsoncComments,
|
|
1128
|
-
writeOpenCodeConfig: () => writeOpenCodeConfig
|
|
1129
|
-
});
|
|
1130
972
|
import { existsSync as existsSync4, readFileSync as readFileSync4, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
1131
973
|
import { join as join5 } from "path";
|
|
1132
974
|
function stripJsoncComments(text) {
|
|
@@ -1211,9 +1053,6 @@ function findOpenCodeConfig(projectRoot) {
|
|
|
1211
1053
|
}
|
|
1212
1054
|
return null;
|
|
1213
1055
|
}
|
|
1214
|
-
function setMcpEntry(name, entry) {
|
|
1215
|
-
setMcpEntryAtProjectRoot(findProjectRoot(), name, entry);
|
|
1216
|
-
}
|
|
1217
1056
|
function setMcpEntryAtProjectRoot(projectRoot, name, entry) {
|
|
1218
1057
|
const configPath = ensureOpenCodeConfig(projectRoot);
|
|
1219
1058
|
const config = readOpenCodeConfig(configPath);
|
|
@@ -1224,22 +1063,6 @@ function setMcpEntryAtProjectRoot(projectRoot, name, entry) {
|
|
|
1224
1063
|
config.mcp[name] = entry;
|
|
1225
1064
|
writeOpenCodeConfig(configPath, config);
|
|
1226
1065
|
}
|
|
1227
|
-
function ensureMcpEntry(name, entry) {
|
|
1228
|
-
ensureMcpEntryAtProjectRoot(findProjectRoot(), name, entry);
|
|
1229
|
-
}
|
|
1230
|
-
function ensureMcpEntryAtProjectRoot(projectRoot, name, entry) {
|
|
1231
|
-
const configPath = ensureOpenCodeConfig(projectRoot);
|
|
1232
|
-
const config = readOpenCodeConfig(configPath);
|
|
1233
|
-
if (!config)
|
|
1234
|
-
throw new Error(`Error parseando ${configPath}`);
|
|
1235
|
-
if (!config.mcp)
|
|
1236
|
-
config.mcp = {};
|
|
1237
|
-
const mcp = config.mcp;
|
|
1238
|
-
if (!mcp[name]) {
|
|
1239
|
-
mcp[name] = entry;
|
|
1240
|
-
writeOpenCodeConfig(configPath, config);
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
1066
|
function patchOpenCodeConfig(projectRoot = findProjectRoot()) {
|
|
1244
1067
|
const configPath = findOpenCodeConfig(projectRoot);
|
|
1245
1068
|
if (!configPath) {
|
|
@@ -1260,12 +1083,6 @@ function patchOpenCodeConfig(projectRoot = findProjectRoot()) {
|
|
|
1260
1083
|
delete config.plugin;
|
|
1261
1084
|
changed = true;
|
|
1262
1085
|
}
|
|
1263
|
-
if (config.mcp && typeof config.mcp === "object" && "context7" in config.mcp) {
|
|
1264
|
-
delete config.mcp.context7;
|
|
1265
|
-
if (Object.keys(config.mcp).length === 0)
|
|
1266
|
-
delete config.mcp;
|
|
1267
|
-
changed = true;
|
|
1268
|
-
}
|
|
1269
1086
|
if (changed) {
|
|
1270
1087
|
writeOpenCodeConfig(configPath, config);
|
|
1271
1088
|
return { success: true, message: "Config actualizada (plugin legacy eliminado)" };
|
|
@@ -1277,19 +1094,6 @@ var init_config = __esm(() => {
|
|
|
1277
1094
|
});
|
|
1278
1095
|
|
|
1279
1096
|
// src/stack.ts
|
|
1280
|
-
var exports_stack = {};
|
|
1281
|
-
__export(exports_stack, {
|
|
1282
|
-
OPENSPEC_NPM_PACKAGE: () => OPENSPEC_NPM_PACKAGE,
|
|
1283
|
-
buildEngramDownloadUrl: () => buildEngramDownloadUrl,
|
|
1284
|
-
buildLocalMcpCommand: () => buildLocalMcpCommand,
|
|
1285
|
-
installCodeGraph: () => installCodeGraph,
|
|
1286
|
-
installEngram: () => installEngram,
|
|
1287
|
-
installStack: () => installStack,
|
|
1288
|
-
setupOpenSpec: () => setupOpenSpec,
|
|
1289
|
-
uninstallEngramConfig: () => uninstallEngramConfig,
|
|
1290
|
-
uninstallStackConfig: () => uninstallStackConfig,
|
|
1291
|
-
verifyMcpServers: () => verifyMcpServers
|
|
1292
|
-
});
|
|
1293
1097
|
import { chmodSync, copyFileSync as copyFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync4, rmSync as rmSync4, unlinkSync as unlinkSync3 } from "fs";
|
|
1294
1098
|
import { basename as basename2, dirname as dirname5, join as join7, resolve as resolve2 } from "path";
|
|
1295
1099
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
@@ -1303,11 +1107,21 @@ function resolveToolInstallLocation(toolsDir) {
|
|
|
1303
1107
|
}
|
|
1304
1108
|
function runTool(binary, args, cwd, timeout = 30000) {
|
|
1305
1109
|
const invocation = getCommandInvocation(binary, args);
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1110
|
+
try {
|
|
1111
|
+
execFileSync3(invocation.command, invocation.args, {
|
|
1112
|
+
cwd,
|
|
1113
|
+
stdio: "pipe",
|
|
1114
|
+
timeout
|
|
1115
|
+
});
|
|
1116
|
+
} catch (error) {
|
|
1117
|
+
const err = error;
|
|
1118
|
+
const stdout = err.stdout ? Buffer.from(err.stdout).toString().trim() : "";
|
|
1119
|
+
const stderr = err.stderr ? Buffer.from(err.stderr).toString().trim() : "";
|
|
1120
|
+
const detail = [stderr, stdout].filter(Boolean).join(`
|
|
1121
|
+
`) || err.message;
|
|
1122
|
+
const invocationStr = `${invocation.command} ${invocation.args.join(" ")}`;
|
|
1123
|
+
throw new Error(`${detail} (invocación: ${invocationStr}, cwd: ${cwd ?? process.cwd()})`);
|
|
1124
|
+
}
|
|
1311
1125
|
}
|
|
1312
1126
|
function findToolBinary(toolDir, name) {
|
|
1313
1127
|
const binDir = join7(toolDir, "bin");
|
|
@@ -1340,12 +1154,15 @@ function copyEngramPlugin(projectRoot) {
|
|
|
1340
1154
|
}
|
|
1341
1155
|
function copyOstackyControllerPlugin(projectRoot) {
|
|
1342
1156
|
const pluginSource = join7(PACKAGE_ROOT, "assets", "plugins", "ostacky-plugin.ts");
|
|
1157
|
+
const coreSource = join7(PACKAGE_ROOT, "assets", "plugins", "controller-core.ts");
|
|
1343
1158
|
const pluginsDir = join7(projectRoot, ".opencode", "plugins");
|
|
1344
1159
|
if (!existsSync6(pluginSource)) {
|
|
1345
1160
|
throw new Error(`Plugin bundleado de OstackyController no encontrado: ${pluginSource}`);
|
|
1346
1161
|
}
|
|
1347
1162
|
mkdirSync4(pluginsDir, { recursive: true });
|
|
1348
1163
|
copyFileSync3(pluginSource, join7(pluginsDir, "ostacky-plugin.ts"));
|
|
1164
|
+
if (existsSync6(coreSource))
|
|
1165
|
+
copyFileSync3(coreSource, join7(pluginsDir, "controller-core.ts"));
|
|
1349
1166
|
}
|
|
1350
1167
|
function buildEngramDownloadUrl(tag, platform = process.platform, arch = process.arch) {
|
|
1351
1168
|
const target = getEngramReleaseTarget(platform, arch);
|
|
@@ -1396,18 +1213,36 @@ async function installCodeGraph(toolsDir) {
|
|
|
1396
1213
|
const ext = process.platform === "win32" ? "zip" : "tar.gz";
|
|
1397
1214
|
const url = `https://github.com/colbymchenry/codegraph/releases/download/${tag}/codegraph-${target}.${ext}`;
|
|
1398
1215
|
try {
|
|
1399
|
-
archivePromotion = await downloadAndExtractWithRetry(url, cgToolDir,
|
|
1400
|
-
} catch (
|
|
1216
|
+
archivePromotion = await downloadAndExtractWithRetry(url, cgToolDir, 0, 180000, 2);
|
|
1217
|
+
} catch (e) {
|
|
1401
1218
|
return {
|
|
1402
1219
|
success: false,
|
|
1403
|
-
message: `Error descargando CodeGraph ${tag}: ${
|
|
1220
|
+
message: `Error descargando CodeGraph ${tag}: ${e.message}`
|
|
1404
1221
|
};
|
|
1405
1222
|
}
|
|
1406
|
-
|
|
1407
|
-
|
|
1223
|
+
const expectedBin = join7(cgToolDir, "bin", getExecutableName("codegraph"));
|
|
1224
|
+
let found = findToolBinary(cgToolDir, "codegraph");
|
|
1225
|
+
if (!found)
|
|
1226
|
+
found = findBinaryInDir(cgToolDir, "codegraph");
|
|
1227
|
+
if (!found) {
|
|
1408
1228
|
return failAfterExtraction(`Descarga de CodeGraph ${tag} completada pero no se encontró el binario en ${join7(cgToolDir, "bin")}.`);
|
|
1409
1229
|
}
|
|
1410
|
-
|
|
1230
|
+
try {
|
|
1231
|
+
if (found !== expectedBin) {
|
|
1232
|
+
mkdirSync4(join7(cgToolDir, "bin"), { recursive: true });
|
|
1233
|
+
if (found.toLowerCase().endsWith(".cmd") && expectedBin.toLowerCase().endsWith(".exe")) {
|
|
1234
|
+
localBin = found;
|
|
1235
|
+
} else {
|
|
1236
|
+
copyFileSync3(found, expectedBin);
|
|
1237
|
+
localBin = expectedBin;
|
|
1238
|
+
}
|
|
1239
|
+
} else {
|
|
1240
|
+
localBin = found;
|
|
1241
|
+
}
|
|
1242
|
+
} catch (error) {
|
|
1243
|
+
return failAfterExtraction(`CodeGraph fue descargado pero no se pudo materializar el binario local: ${error.message}`);
|
|
1244
|
+
}
|
|
1245
|
+
if (process.platform !== "win32" && localBin) {
|
|
1411
1246
|
try {
|
|
1412
1247
|
chmodSync(localBin, 493);
|
|
1413
1248
|
} catch {}
|
|
@@ -1422,7 +1257,8 @@ async function installCodeGraph(toolsDir) {
|
|
|
1422
1257
|
try {
|
|
1423
1258
|
runTool(localBin, ["init", "-i"], projectRoot, 120000);
|
|
1424
1259
|
} catch (error) {
|
|
1425
|
-
|
|
1260
|
+
const msg = error.message;
|
|
1261
|
+
indexWarning = `El índice no se pudo inicializar todavía: ${msg}. Sugerencia: ejecutá manualmente \`${localBin} init -i\` en ${projectRoot} o \`npx ostacky install-stack --scope local\` para reintentar. Si el path del binario (${localBin}) apunta a ${cgToolDir} y esperabas otro proyecto, verificá que corriste el comando dentro del proyecto correcto (con .git) y con --scope local.`;
|
|
1426
1262
|
}
|
|
1427
1263
|
try {
|
|
1428
1264
|
configureLocalTool(projectRoot, "codegraph", buildLocalMcpCommand(localBin, ["serve", "--mcp"]));
|
|
@@ -1544,10 +1380,10 @@ async function installEngram(toolsDir) {
|
|
|
1544
1380
|
}
|
|
1545
1381
|
try {
|
|
1546
1382
|
archivePromotion = await downloadAndExtractWithRetry(url, engramToolDir, 0, 120000, 2);
|
|
1547
|
-
} catch (
|
|
1383
|
+
} catch (e) {
|
|
1548
1384
|
return {
|
|
1549
1385
|
success: false,
|
|
1550
|
-
message: `Error descargando Engram ${tag}: ${
|
|
1386
|
+
message: `Error descargando Engram ${tag}: ${e.message}`
|
|
1551
1387
|
};
|
|
1552
1388
|
}
|
|
1553
1389
|
mkdirSync4(engramBinDir, { recursive: true });
|
|
@@ -1578,18 +1414,6 @@ async function installEngram(toolsDir) {
|
|
|
1578
1414
|
archivePromotion?.commit();
|
|
1579
1415
|
return { success: true, message: "Engram instalado localmente y configurado para OpenCode (MCP + plugin)" };
|
|
1580
1416
|
}
|
|
1581
|
-
async function installStack(toolsDir) {
|
|
1582
|
-
const { projectRoot } = resolveToolInstallLocation(toolsDir);
|
|
1583
|
-
try {
|
|
1584
|
-
copyOstackyControllerPlugin(projectRoot);
|
|
1585
|
-
} catch {}
|
|
1586
|
-
return {
|
|
1587
|
-
codegraph: await installCodeGraph(toolsDir),
|
|
1588
|
-
openspec: setupOpenSpec(projectRoot),
|
|
1589
|
-
engram: await installEngram(toolsDir),
|
|
1590
|
-
config: patchOpenCodeConfig(projectRoot)
|
|
1591
|
-
};
|
|
1592
|
-
}
|
|
1593
1417
|
function uninstallEngramConfig() {
|
|
1594
1418
|
const projectRoot = findProjectRoot();
|
|
1595
1419
|
const configPath = findOpenCodeConfig(projectRoot);
|
|
@@ -1621,7 +1445,7 @@ function uninstallStackConfig(paths) {
|
|
|
1621
1445
|
const mcp = config.mcp;
|
|
1622
1446
|
let changed = false;
|
|
1623
1447
|
if (mcp) {
|
|
1624
|
-
for (const name of ["codegraph", "engram"
|
|
1448
|
+
for (const name of ["codegraph", "engram"]) {
|
|
1625
1449
|
if (name in mcp) {
|
|
1626
1450
|
delete mcp[name];
|
|
1627
1451
|
removed.push(`mcp.${name}`);
|
|
@@ -1659,29 +1483,6 @@ function uninstallStackConfig(paths) {
|
|
|
1659
1483
|
message: `Removido: ${removed.join(", ")}. Los binarios globales (codegraph, engram) no se tocaron.`
|
|
1660
1484
|
};
|
|
1661
1485
|
}
|
|
1662
|
-
function verifyMcpServers(projectRoot) {
|
|
1663
|
-
const opencodeDir = findOpenCodeDir(projectRoot);
|
|
1664
|
-
const mcpBase = opencodeDir ? join7(opencodeDir, "mcp") : join7(projectRoot, ".opencode", "mcp");
|
|
1665
|
-
const controllerPath = join7(mcpBase, "ostacky-controller", "index.js");
|
|
1666
|
-
const openspecPath = join7(mcpBase, "openspec", "index.js");
|
|
1667
|
-
const result = {
|
|
1668
|
-
ostackyController: {
|
|
1669
|
-
exists: existsSync6(controllerPath),
|
|
1670
|
-
path: controllerPath
|
|
1671
|
-
},
|
|
1672
|
-
openspec: {
|
|
1673
|
-
exists: existsSync6(openspecPath),
|
|
1674
|
-
path: openspecPath
|
|
1675
|
-
}
|
|
1676
|
-
};
|
|
1677
|
-
if (!result.ostackyController.exists) {
|
|
1678
|
-
result.ostackyController.error = "ostacky-controller MCP server not found";
|
|
1679
|
-
}
|
|
1680
|
-
if (!result.openspec.exists) {
|
|
1681
|
-
result.openspec.error = "openspec MCP server not found";
|
|
1682
|
-
}
|
|
1683
|
-
return result;
|
|
1684
|
-
}
|
|
1685
1486
|
var OPENSPEC_NPM_PACKAGE = "@fission-ai/openspec";
|
|
1686
1487
|
var init_stack = __esm(() => {
|
|
1687
1488
|
init_fs();
|
|
@@ -1691,7 +1492,7 @@ var init_stack = __esm(() => {
|
|
|
1691
1492
|
// package.json
|
|
1692
1493
|
var package_default = {
|
|
1693
1494
|
name: "ostacky",
|
|
1694
|
-
version: "0.8.
|
|
1495
|
+
version: "0.8.3",
|
|
1695
1496
|
description: "Instalador interactivo de agentes y comandos para OpenCode",
|
|
1696
1497
|
type: "module",
|
|
1697
1498
|
bin: {
|
|
@@ -2356,13 +2157,13 @@ var y2 = (s) => {
|
|
|
2356
2157
|
}
|
|
2357
2158
|
};
|
|
2358
2159
|
var k2 = (s) => {
|
|
2359
|
-
const { cursor: n, options: t, style: i } = s,
|
|
2360
|
-
let
|
|
2361
|
-
n >=
|
|
2362
|
-
const $
|
|
2363
|
-
return t.slice(
|
|
2364
|
-
const
|
|
2365
|
-
return
|
|
2160
|
+
const { cursor: n, options: t, style: i } = s, r = s.maxItems ?? Number.POSITIVE_INFINITY, c = Math.max(process.stdout.rows - 4, 0), o = Math.min(c, Math.max(r, 5));
|
|
2161
|
+
let l = 0;
|
|
2162
|
+
n >= l + o - 3 ? l = Math.max(Math.min(n - o + 3, t.length - o), 0) : n < l + 2 && (l = Math.max(n - 2, 0));
|
|
2163
|
+
const $ = o < t.length && l > 0, d = o < t.length && l + o < t.length;
|
|
2164
|
+
return t.slice(l, l + o).map((w, b, C) => {
|
|
2165
|
+
const I = b === 0 && $, x = b === C.length - 1 && d;
|
|
2166
|
+
return I || x ? import_picocolors2.default.dim("...") : i(w, b + l === n);
|
|
2366
2167
|
});
|
|
2367
2168
|
};
|
|
2368
2169
|
var ue = (s) => new PD({ validate: s.validate, placeholder: s.placeholder, defaultValue: s.defaultValue, initialValue: s.initialValue, render() {
|
|
@@ -2412,12 +2213,12 @@ var me = (s) => {
|
|
|
2412
2213
|
return new fD({ active: n, inactive: t, initialValue: s.initialValue ?? true, render() {
|
|
2413
2214
|
const i = `${import_picocolors2.default.gray(a)}
|
|
2414
2215
|
${y2(this.state)} ${s.message}
|
|
2415
|
-
`,
|
|
2216
|
+
`, r = this.value ? n : t;
|
|
2416
2217
|
switch (this.state) {
|
|
2417
2218
|
case "submit":
|
|
2418
|
-
return `${i}${import_picocolors2.default.gray(a)} ${import_picocolors2.default.dim(
|
|
2219
|
+
return `${i}${import_picocolors2.default.gray(a)} ${import_picocolors2.default.dim(r)}`;
|
|
2419
2220
|
case "cancel":
|
|
2420
|
-
return `${i}${import_picocolors2.default.gray(a)} ${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(
|
|
2221
|
+
return `${i}${import_picocolors2.default.gray(a)} ${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(r))}
|
|
2421
2222
|
${import_picocolors2.default.gray(a)}`;
|
|
2422
2223
|
default:
|
|
2423
2224
|
return `${i}${import_picocolors2.default.cyan(a)} ${this.value ? `${import_picocolors2.default.green(j2)} ${n}` : `${import_picocolors2.default.dim(R2)} ${import_picocolors2.default.dim(n)}`} ${import_picocolors2.default.dim("/")} ${this.value ? `${import_picocolors2.default.dim(R2)} ${import_picocolors2.default.dim(t)}` : `${import_picocolors2.default.green(j2)} ${t}`}
|
|
@@ -2428,16 +2229,16 @@ ${import_picocolors2.default.cyan(m2)}
|
|
|
2428
2229
|
};
|
|
2429
2230
|
var de = (s) => {
|
|
2430
2231
|
const n = (t, i) => {
|
|
2431
|
-
const
|
|
2232
|
+
const r = t.label ?? String(t.value);
|
|
2432
2233
|
switch (i) {
|
|
2433
2234
|
case "selected":
|
|
2434
|
-
return `${import_picocolors2.default.dim(
|
|
2235
|
+
return `${import_picocolors2.default.dim(r)}`;
|
|
2435
2236
|
case "active":
|
|
2436
|
-
return `${import_picocolors2.default.green(j2)} ${
|
|
2237
|
+
return `${import_picocolors2.default.green(j2)} ${r} ${t.hint ? import_picocolors2.default.dim(`(${t.hint})`) : ""}`;
|
|
2437
2238
|
case "cancelled":
|
|
2438
|
-
return `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(
|
|
2239
|
+
return `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(r))}`;
|
|
2439
2240
|
default:
|
|
2440
|
-
return `${import_picocolors2.default.dim(R2)} ${import_picocolors2.default.dim(
|
|
2241
|
+
return `${import_picocolors2.default.dim(R2)} ${import_picocolors2.default.dim(r)}`;
|
|
2441
2242
|
}
|
|
2442
2243
|
};
|
|
2443
2244
|
return new jD({ options: s.options, initialValue: s.initialValue, render() {
|
|
@@ -2451,7 +2252,7 @@ ${y2(this.state)} ${s.message}
|
|
|
2451
2252
|
return `${t}${import_picocolors2.default.gray(a)} ${n(this.options[this.cursor], "cancelled")}
|
|
2452
2253
|
${import_picocolors2.default.gray(a)}`;
|
|
2453
2254
|
default:
|
|
2454
|
-
return `${t}${import_picocolors2.default.cyan(a)} ${k2({ cursor: this.cursor, options: this.options, maxItems: s.maxItems, style: (i,
|
|
2255
|
+
return `${t}${import_picocolors2.default.cyan(a)} ${k2({ cursor: this.cursor, options: this.options, maxItems: s.maxItems, style: (i, r) => n(i, r ? "active" : "inactive") }).join(`
|
|
2455
2256
|
${import_picocolors2.default.cyan(a)} `)}
|
|
2456
2257
|
${import_picocolors2.default.cyan(m2)}
|
|
2457
2258
|
`;
|
|
@@ -2460,8 +2261,8 @@ ${import_picocolors2.default.cyan(m2)}
|
|
|
2460
2261
|
};
|
|
2461
2262
|
var he = (s) => {
|
|
2462
2263
|
const n = (t, i = "inactive") => {
|
|
2463
|
-
const
|
|
2464
|
-
return i === "selected" ? `${import_picocolors2.default.dim(
|
|
2264
|
+
const r = t.label ?? String(t.value);
|
|
2265
|
+
return i === "selected" ? `${import_picocolors2.default.dim(r)}` : i === "cancelled" ? `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(r))}` : i === "active" ? `${import_picocolors2.default.bgCyan(import_picocolors2.default.gray(` ${t.value} `))} ${r} ${t.hint ? import_picocolors2.default.dim(`(${t.hint})`) : ""}` : `${import_picocolors2.default.gray(import_picocolors2.default.bgWhite(import_picocolors2.default.inverse(` ${t.value} `)))} ${r} ${t.hint ? import_picocolors2.default.dim(`(${t.hint})`) : ""}`;
|
|
2465
2266
|
};
|
|
2466
2267
|
return new OD({ options: s.options, initialValue: s.initialValue, render() {
|
|
2467
2268
|
const t = `${import_picocolors2.default.gray(a)}
|
|
@@ -2474,7 +2275,7 @@ ${y2(this.state)} ${s.message}
|
|
|
2474
2275
|
return `${t}${import_picocolors2.default.gray(a)} ${n(this.options[0], "cancelled")}
|
|
2475
2276
|
${import_picocolors2.default.gray(a)}`;
|
|
2476
2277
|
default:
|
|
2477
|
-
return `${t}${import_picocolors2.default.cyan(a)} ${this.options.map((i,
|
|
2278
|
+
return `${t}${import_picocolors2.default.cyan(a)} ${this.options.map((i, r) => n(i, r === this.cursor ? "active" : "inactive")).join(`
|
|
2478
2279
|
${import_picocolors2.default.cyan(a)} `)}
|
|
2479
2280
|
${import_picocolors2.default.cyan(m2)}
|
|
2480
2281
|
`;
|
|
@@ -2483,8 +2284,8 @@ ${import_picocolors2.default.cyan(m2)}
|
|
|
2483
2284
|
};
|
|
2484
2285
|
var pe = (s) => {
|
|
2485
2286
|
const n = (t, i) => {
|
|
2486
|
-
const
|
|
2487
|
-
return i === "active" ? `${import_picocolors2.default.cyan(V2)} ${
|
|
2287
|
+
const r = t.label ?? String(t.value);
|
|
2288
|
+
return i === "active" ? `${import_picocolors2.default.cyan(V2)} ${r} ${t.hint ? import_picocolors2.default.dim(`(${t.hint})`) : ""}` : i === "selected" ? `${import_picocolors2.default.green(M2)} ${import_picocolors2.default.dim(r)}` : i === "cancelled" ? `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(r))}` : i === "active-selected" ? `${import_picocolors2.default.green(M2)} ${r} ${t.hint ? import_picocolors2.default.dim(`(${t.hint})`) : ""}` : i === "submitted" ? `${import_picocolors2.default.dim(r)}` : `${import_picocolors2.default.dim(G2)} ${import_picocolors2.default.dim(r)}`;
|
|
2488
2289
|
};
|
|
2489
2290
|
return new wD({ options: s.options, initialValues: s.initialValues, required: s.required ?? true, cursorAt: s.cursorAt, validate(t) {
|
|
2490
2291
|
if (this.required && t.length === 0)
|
|
@@ -2493,25 +2294,25 @@ ${import_picocolors2.default.reset(import_picocolors2.default.dim(`Press ${impor
|
|
|
2493
2294
|
}, render() {
|
|
2494
2295
|
const t = `${import_picocolors2.default.gray(a)}
|
|
2495
2296
|
${y2(this.state)} ${s.message}
|
|
2496
|
-
`, i = (
|
|
2497
|
-
const o = this.value.includes(
|
|
2498
|
-
return
|
|
2297
|
+
`, i = (r, c) => {
|
|
2298
|
+
const o = this.value.includes(r.value);
|
|
2299
|
+
return c && o ? n(r, "active-selected") : o ? n(r, "selected") : n(r, c ? "active" : "inactive");
|
|
2499
2300
|
};
|
|
2500
2301
|
switch (this.state) {
|
|
2501
2302
|
case "submit":
|
|
2502
|
-
return `${t}${import_picocolors2.default.gray(a)} ${this.options.filter(({ value:
|
|
2303
|
+
return `${t}${import_picocolors2.default.gray(a)} ${this.options.filter(({ value: r }) => this.value.includes(r)).map((r) => n(r, "submitted")).join(import_picocolors2.default.dim(", ")) || import_picocolors2.default.dim("none")}`;
|
|
2503
2304
|
case "cancel": {
|
|
2504
|
-
const
|
|
2505
|
-
return `${t}${import_picocolors2.default.gray(a)} ${
|
|
2305
|
+
const r = this.options.filter(({ value: c }) => this.value.includes(c)).map((c) => n(c, "cancelled")).join(import_picocolors2.default.dim(", "));
|
|
2306
|
+
return `${t}${import_picocolors2.default.gray(a)} ${r.trim() ? `${r}
|
|
2506
2307
|
${import_picocolors2.default.gray(a)}` : ""}`;
|
|
2507
2308
|
}
|
|
2508
2309
|
case "error": {
|
|
2509
|
-
const
|
|
2510
|
-
`).map((
|
|
2310
|
+
const r = this.error.split(`
|
|
2311
|
+
`).map((c, o) => o === 0 ? `${import_picocolors2.default.yellow(m2)} ${import_picocolors2.default.yellow(c)}` : ` ${c}`).join(`
|
|
2511
2312
|
`);
|
|
2512
2313
|
return `${t + import_picocolors2.default.yellow(a)} ${k2({ options: this.options, cursor: this.cursor, maxItems: s.maxItems, style: i }).join(`
|
|
2513
2314
|
${import_picocolors2.default.yellow(a)} `)}
|
|
2514
|
-
${
|
|
2315
|
+
${r}
|
|
2515
2316
|
`;
|
|
2516
2317
|
}
|
|
2517
2318
|
default:
|
|
@@ -2523,9 +2324,9 @@ ${import_picocolors2.default.cyan(m2)}
|
|
|
2523
2324
|
} }).prompt();
|
|
2524
2325
|
};
|
|
2525
2326
|
var ge = (s) => {
|
|
2526
|
-
const n = (t, i,
|
|
2527
|
-
const
|
|
2528
|
-
return i === "active" ? `${import_picocolors2.default.dim(
|
|
2327
|
+
const n = (t, i, r = []) => {
|
|
2328
|
+
const c = t.label ?? String(t.value), o = typeof t.group == "string", l = o && (r[r.indexOf(t) + 1] ?? { group: true }), $ = o && l.group === true, d = o ? `${$ ? m2 : a} ` : "";
|
|
2329
|
+
return i === "active" ? `${import_picocolors2.default.dim(d)}${import_picocolors2.default.cyan(V2)} ${c} ${t.hint ? import_picocolors2.default.dim(`(${t.hint})`) : ""}` : i === "group-active" ? `${d}${import_picocolors2.default.cyan(V2)} ${import_picocolors2.default.dim(c)}` : i === "group-active-selected" ? `${d}${import_picocolors2.default.green(M2)} ${import_picocolors2.default.dim(c)}` : i === "selected" ? `${import_picocolors2.default.dim(d)}${import_picocolors2.default.green(M2)} ${import_picocolors2.default.dim(c)}` : i === "cancelled" ? `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(c))}` : i === "active-selected" ? `${import_picocolors2.default.dim(d)}${import_picocolors2.default.green(M2)} ${c} ${t.hint ? import_picocolors2.default.dim(`(${t.hint})`) : ""}` : i === "submitted" ? `${import_picocolors2.default.dim(c)}` : `${import_picocolors2.default.dim(d)}${import_picocolors2.default.dim(G2)} ${import_picocolors2.default.dim(c)}`;
|
|
2529
2330
|
};
|
|
2530
2331
|
return new dD({ options: s.options, initialValues: s.initialValues, required: s.required ?? true, cursorAt: s.cursorAt, validate(t) {
|
|
2531
2332
|
if (this.required && t.length === 0)
|
|
@@ -2539,26 +2340,26 @@ ${y2(this.state)} ${s.message}
|
|
|
2539
2340
|
case "submit":
|
|
2540
2341
|
return `${t}${import_picocolors2.default.gray(a)} ${this.options.filter(({ value: i }) => this.value.includes(i)).map((i) => n(i, "submitted")).join(import_picocolors2.default.dim(", "))}`;
|
|
2541
2342
|
case "cancel": {
|
|
2542
|
-
const i = this.options.filter(({ value:
|
|
2343
|
+
const i = this.options.filter(({ value: r }) => this.value.includes(r)).map((r) => n(r, "cancelled")).join(import_picocolors2.default.dim(", "));
|
|
2543
2344
|
return `${t}${import_picocolors2.default.gray(a)} ${i.trim() ? `${i}
|
|
2544
2345
|
${import_picocolors2.default.gray(a)}` : ""}`;
|
|
2545
2346
|
}
|
|
2546
2347
|
case "error": {
|
|
2547
2348
|
const i = this.error.split(`
|
|
2548
|
-
`).map((
|
|
2349
|
+
`).map((r, c) => c === 0 ? `${import_picocolors2.default.yellow(m2)} ${import_picocolors2.default.yellow(r)}` : ` ${r}`).join(`
|
|
2549
2350
|
`);
|
|
2550
|
-
return `${t}${import_picocolors2.default.yellow(a)} ${this.options.map((
|
|
2551
|
-
const
|
|
2552
|
-
return !$
|
|
2351
|
+
return `${t}${import_picocolors2.default.yellow(a)} ${this.options.map((r, c, o) => {
|
|
2352
|
+
const l = this.value.includes(r.value) || r.group === true && this.isGroupSelected(`${r.value}`), $ = c === this.cursor;
|
|
2353
|
+
return !$ && typeof r.group == "string" && this.options[this.cursor].value === r.group ? n(r, l ? "group-active-selected" : "group-active", o) : $ && l ? n(r, "active-selected", o) : l ? n(r, "selected", o) : n(r, $ ? "active" : "inactive", o);
|
|
2553
2354
|
}).join(`
|
|
2554
2355
|
${import_picocolors2.default.yellow(a)} `)}
|
|
2555
2356
|
${i}
|
|
2556
2357
|
`;
|
|
2557
2358
|
}
|
|
2558
2359
|
default:
|
|
2559
|
-
return `${t}${import_picocolors2.default.cyan(a)} ${this.options.map((i,
|
|
2560
|
-
const o = this.value.includes(i.value) || i.group === true && this.isGroupSelected(`${i.value}`),
|
|
2561
|
-
return !
|
|
2360
|
+
return `${t}${import_picocolors2.default.cyan(a)} ${this.options.map((i, r, c) => {
|
|
2361
|
+
const o = this.value.includes(i.value) || i.group === true && this.isGroupSelected(`${i.value}`), l = r === this.cursor;
|
|
2362
|
+
return !l && typeof i.group == "string" && this.options[this.cursor].value === i.group ? n(i, o ? "group-active-selected" : "group-active", c) : l && o ? n(i, "active-selected", c) : o ? n(i, "selected", c) : n(i, l ? "active" : "inactive", c);
|
|
2562
2363
|
}).join(`
|
|
2563
2364
|
${import_picocolors2.default.cyan(a)} `)}
|
|
2564
2365
|
${import_picocolors2.default.cyan(m2)}
|
|
@@ -2570,15 +2371,15 @@ var ye = (s = "", n = "") => {
|
|
|
2570
2371
|
const t = `
|
|
2571
2372
|
${s}
|
|
2572
2373
|
`.split(`
|
|
2573
|
-
`), i = T2(n).length,
|
|
2574
|
-
const $
|
|
2575
|
-
return
|
|
2576
|
-
}, 0), i) + 2,
|
|
2374
|
+
`), i = T2(n).length, r = Math.max(t.reduce((o, l) => {
|
|
2375
|
+
const $ = T2(l);
|
|
2376
|
+
return $.length > o ? $.length : o;
|
|
2377
|
+
}, 0), i) + 2, c = t.map((o) => `${import_picocolors2.default.gray(a)} ${import_picocolors2.default.dim(o)}${" ".repeat(r - T2(o).length)}${import_picocolors2.default.gray(a)}`).join(`
|
|
2577
2378
|
`);
|
|
2578
2379
|
process.stdout.write(`${import_picocolors2.default.gray(a)}
|
|
2579
|
-
${import_picocolors2.default.green(S2)} ${import_picocolors2.default.reset(n)} ${import_picocolors2.default.gray(N2.repeat(Math.max(
|
|
2580
|
-
${
|
|
2581
|
-
${import_picocolors2.default.gray(ie + N2.repeat(
|
|
2380
|
+
${import_picocolors2.default.green(S2)} ${import_picocolors2.default.reset(n)} ${import_picocolors2.default.gray(N2.repeat(Math.max(r - i - 1, 1)) + re)}
|
|
2381
|
+
${c}
|
|
2382
|
+
${import_picocolors2.default.gray(ie + N2.repeat(r + 2) + ne)}
|
|
2582
2383
|
`);
|
|
2583
2384
|
};
|
|
2584
2385
|
var ve = (s = "") => {
|
|
@@ -2599,9 +2400,9 @@ ${import_picocolors2.default.gray(m2)} ${s}
|
|
|
2599
2400
|
var v2 = { message: (s = "", { symbol: n = import_picocolors2.default.gray(a) } = {}) => {
|
|
2600
2401
|
const t = [`${import_picocolors2.default.gray(a)}`];
|
|
2601
2402
|
if (s) {
|
|
2602
|
-
const [i, ...
|
|
2403
|
+
const [i, ...r] = s.split(`
|
|
2603
2404
|
`);
|
|
2604
|
-
t.push(`${n} ${i}`, ...
|
|
2405
|
+
t.push(`${n} ${i}`, ...r.map((c) => `${import_picocolors2.default.gray(a)} ${c}`));
|
|
2605
2406
|
}
|
|
2606
2407
|
process.stdout.write(`${t.join(`
|
|
2607
2408
|
`)}
|
|
@@ -2621,54 +2422,54 @@ var v2 = { message: (s = "", { symbol: n = import_picocolors2.default.gray(a) }
|
|
|
2621
2422
|
} };
|
|
2622
2423
|
var L2 = () => {
|
|
2623
2424
|
const s = E ? ["◒", "◐", "◓", "◑"] : ["•", "o", "O", "0"], n = E ? 80 : 120, t = process.env.CI === "true";
|
|
2624
|
-
let i,
|
|
2625
|
-
const $
|
|
2626
|
-
const
|
|
2627
|
-
|
|
2628
|
-
},
|
|
2629
|
-
process.on("uncaughtExceptionMonitor",
|
|
2425
|
+
let i, r, c = false, o = "", l;
|
|
2426
|
+
const $ = (h) => {
|
|
2427
|
+
const g = h > 1 ? "Something went wrong" : "Canceled";
|
|
2428
|
+
c && P(g, h);
|
|
2429
|
+
}, d = () => $(2), w = () => $(1), b = () => {
|
|
2430
|
+
process.on("uncaughtExceptionMonitor", d), process.on("unhandledRejection", d), process.on("SIGINT", w), process.on("SIGTERM", w), process.on("exit", $);
|
|
2630
2431
|
}, C = () => {
|
|
2631
|
-
process.removeListener("uncaughtExceptionMonitor",
|
|
2632
|
-
},
|
|
2633
|
-
if (
|
|
2432
|
+
process.removeListener("uncaughtExceptionMonitor", d), process.removeListener("unhandledRejection", d), process.removeListener("SIGINT", w), process.removeListener("SIGTERM", w), process.removeListener("exit", $);
|
|
2433
|
+
}, I = () => {
|
|
2434
|
+
if (l === undefined)
|
|
2634
2435
|
return;
|
|
2635
2436
|
t && process.stdout.write(`
|
|
2636
2437
|
`);
|
|
2637
|
-
const
|
|
2438
|
+
const h = l.split(`
|
|
2638
2439
|
`);
|
|
2639
|
-
process.stdout.write(import_sisteransi2.cursor.move(-999,
|
|
2640
|
-
},
|
|
2641
|
-
|
|
2440
|
+
process.stdout.write(import_sisteransi2.cursor.move(-999, h.length - 1)), process.stdout.write(import_sisteransi2.erase.down(h.length));
|
|
2441
|
+
}, x = (h) => h.replace(/\.+$/, ""), O = (h = "") => {
|
|
2442
|
+
c = true, i = cD(), o = x(h), process.stdout.write(`${import_picocolors2.default.gray(a)}
|
|
2642
2443
|
`);
|
|
2643
|
-
let
|
|
2644
|
-
|
|
2645
|
-
if (t && o ===
|
|
2444
|
+
let g = 0, f = 0;
|
|
2445
|
+
b(), r = setInterval(() => {
|
|
2446
|
+
if (t && o === l)
|
|
2646
2447
|
return;
|
|
2647
|
-
|
|
2648
|
-
const
|
|
2649
|
-
process.stdout.write(`${
|
|
2448
|
+
I(), l = o;
|
|
2449
|
+
const W = import_picocolors2.default.magenta(s[g]), _ = t ? "..." : ".".repeat(Math.floor(f)).slice(0, 3);
|
|
2450
|
+
process.stdout.write(`${W} ${o}${_}`), g = g + 1 < s.length ? g + 1 : 0, f = f < s.length ? f + 0.125 : 0;
|
|
2650
2451
|
}, n);
|
|
2651
|
-
},
|
|
2652
|
-
|
|
2653
|
-
const
|
|
2654
|
-
o =
|
|
2452
|
+
}, P = (h = "", g = 0) => {
|
|
2453
|
+
c = false, clearInterval(r), I();
|
|
2454
|
+
const f = g === 0 ? import_picocolors2.default.green(S2) : g === 1 ? import_picocolors2.default.red(A2) : import_picocolors2.default.red(B);
|
|
2455
|
+
o = x(h ?? o), process.stdout.write(`${f} ${o}
|
|
2655
2456
|
`), C(), i();
|
|
2656
2457
|
};
|
|
2657
|
-
return { start:
|
|
2658
|
-
o =
|
|
2458
|
+
return { start: O, stop: P, message: (h = "") => {
|
|
2459
|
+
o = x(h ?? o);
|
|
2659
2460
|
} };
|
|
2660
2461
|
};
|
|
2661
2462
|
var be = async (s, n) => {
|
|
2662
2463
|
const t = {}, i = Object.keys(s);
|
|
2663
|
-
for (const
|
|
2664
|
-
const
|
|
2665
|
-
throw
|
|
2464
|
+
for (const r of i) {
|
|
2465
|
+
const c = s[r], o = await c({ results: t })?.catch((l) => {
|
|
2466
|
+
throw l;
|
|
2666
2467
|
});
|
|
2667
2468
|
if (typeof n?.onCancel == "function" && BD(o)) {
|
|
2668
|
-
t[
|
|
2469
|
+
t[r] = "canceled", n.onCancel({ results: t });
|
|
2669
2470
|
continue;
|
|
2670
2471
|
}
|
|
2671
|
-
t[
|
|
2472
|
+
t[r] = o;
|
|
2672
2473
|
}
|
|
2673
2474
|
return t;
|
|
2674
2475
|
};
|
|
@@ -2755,7 +2556,7 @@ async function probeMcpServer(nodeExecutable, serverPath, cwd, statePath, option
|
|
|
2755
2556
|
const requiredTools = options.requiredTools ?? DEFAULT_PROBE_OPTIONS.requiredTools;
|
|
2756
2557
|
const exerciseWrite = options.exerciseWrite ?? requiredTools.includes("start_request");
|
|
2757
2558
|
try {
|
|
2758
|
-
await new Promise((
|
|
2559
|
+
await new Promise((resolve, reject) => {
|
|
2759
2560
|
const child = spawn(nodeExecutable, [serverPath], {
|
|
2760
2561
|
cwd,
|
|
2761
2562
|
env: {
|
|
@@ -2782,13 +2583,13 @@ async function probeMcpServer(nodeExecutable, serverPath, cwd, statePath, option
|
|
|
2782
2583
|
const onExit = () => {
|
|
2783
2584
|
if (exitTimeout)
|
|
2784
2585
|
clearTimeout(exitTimeout);
|
|
2785
|
-
error ? reject(error) :
|
|
2586
|
+
error ? reject(error) : resolve();
|
|
2786
2587
|
};
|
|
2787
2588
|
child.once("exit", onExit);
|
|
2788
2589
|
child.kill();
|
|
2789
2590
|
exitTimeout = setTimeout(onExit, 500);
|
|
2790
2591
|
} else {
|
|
2791
|
-
error ? reject(error) :
|
|
2592
|
+
error ? reject(error) : resolve();
|
|
2792
2593
|
}
|
|
2793
2594
|
};
|
|
2794
2595
|
const fail = (message) => finish(new Error(`${message}${stderr ? `: ${stderr.trim()}` : ""}`));
|
|
@@ -2866,14 +2667,14 @@ async function probeMcpServer(nodeExecutable, serverPath, cwd, statePath, option
|
|
|
2866
2667
|
params: {
|
|
2867
2668
|
protocolVersion: "2025-03-26",
|
|
2868
2669
|
capabilities: {},
|
|
2869
|
-
clientInfo: { name: "ostacky-installer", version: "0.8.
|
|
2670
|
+
clientInfo: { name: "ostacky-installer", version: "0.8.3" }
|
|
2870
2671
|
}
|
|
2871
2672
|
});
|
|
2872
2673
|
});
|
|
2873
2674
|
} finally {
|
|
2874
|
-
await new Promise((
|
|
2675
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
2875
2676
|
if (statePath) {
|
|
2876
|
-
for (const
|
|
2677
|
+
for (const p of [
|
|
2877
2678
|
statePath,
|
|
2878
2679
|
statePath + ".backup",
|
|
2879
2680
|
statePath + ".lock.pid",
|
|
@@ -2881,13 +2682,13 @@ async function probeMcpServer(nodeExecutable, serverPath, cwd, statePath, option
|
|
|
2881
2682
|
statePath + ".tmp." + process.pid
|
|
2882
2683
|
]) {
|
|
2883
2684
|
try {
|
|
2884
|
-
if (existsSync5(
|
|
2885
|
-
rmSync3(
|
|
2685
|
+
if (existsSync5(p))
|
|
2686
|
+
rmSync3(p, { force: true });
|
|
2886
2687
|
} catch {}
|
|
2887
2688
|
}
|
|
2888
2689
|
try {
|
|
2889
|
-
const { dirname
|
|
2890
|
-
const handoff =
|
|
2690
|
+
const { dirname, join } = await import("path");
|
|
2691
|
+
const handoff = join(dirname(statePath), ".ostacky-handoff-compaction.json");
|
|
2891
2692
|
if (existsSync5(handoff)) {
|
|
2892
2693
|
try {
|
|
2893
2694
|
rmSync3(handoff, { force: true });
|
|
@@ -3224,75 +3025,28 @@ function printPostInstallSteps() {
|
|
|
3224
3025
|
`), "Próximos pasos");
|
|
3225
3026
|
}
|
|
3226
3027
|
async function resolveOpenCodePaths(scope) {
|
|
3227
|
-
if (scope === "local" || scope === "global" || scope === "auto") {
|
|
3228
|
-
const dir2 = getOpenCodeDirForScope(scope);
|
|
3229
|
-
const isGlobalDir = dir2.replace(/\\/g, "/") === getGlobalOpenCodeDir().replace(/\\/g, "/");
|
|
3230
|
-
try {
|
|
3231
|
-
const paths = ensureOpenCodePaths(dir2);
|
|
3232
|
-
if (scope === "global")
|
|
3233
|
-
ye(dir2, "Instalación global");
|
|
3234
|
-
else if (scope === "auto")
|
|
3235
|
-
ye(dir2, `Scope auto → ${isGlobalDir ? "global" : "local"}`);
|
|
3236
|
-
else
|
|
3237
|
-
ye(dir2, "Instalación local");
|
|
3238
|
-
return paths;
|
|
3239
|
-
} catch (e2) {
|
|
3240
|
-
const msg = e2.message ?? "";
|
|
3241
|
-
if ((scope === "global" || scope === "auto" && isGlobalDir) && (msg.includes("EACCES") || msg.toLowerCase().includes("permission"))) {
|
|
3242
|
-
v2.warn(`No se pudo escribir en global (${dir2}): ${msg}. ¿Instalar local?`);
|
|
3243
|
-
const retry = await me({ message: "¿Reintentar como instalación local?" });
|
|
3244
|
-
onCancel(retry);
|
|
3245
|
-
if (retry) {
|
|
3246
|
-
const localDir2 = getOpenCodeDirForScope("local");
|
|
3247
|
-
return ensureOpenCodePaths(localDir2);
|
|
3248
|
-
}
|
|
3249
|
-
}
|
|
3250
|
-
throw e2;
|
|
3251
|
-
}
|
|
3252
|
-
}
|
|
3253
3028
|
const cwd = process.cwd();
|
|
3254
|
-
const
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
message: `¿Instalar en proyecto local (${localDir}) o global (${globalDir})?`,
|
|
3259
|
-
options: [
|
|
3260
|
-
{ value: "local", label: "Local", hint: `${localDir} (recomendado)` },
|
|
3261
|
-
{ value: "global", label: "Global", hint: globalDir }
|
|
3262
|
-
],
|
|
3263
|
-
initialValue: "local"
|
|
3264
|
-
});
|
|
3265
|
-
onCancel(scopeChoice);
|
|
3266
|
-
const chosen = scopeChoice;
|
|
3267
|
-
const dir = getOpenCodeDirForScope(chosen, cwd);
|
|
3029
|
+
const dir = scope === "local" || !scope ? getOpenCodeDirForScope("local", cwd) : getOpenCodeDirForScope("local", cwd);
|
|
3030
|
+
if (scope && scope !== "local") {
|
|
3031
|
+
v2.warn(`Scope ${scope} removido; usando local en ${dir}`);
|
|
3032
|
+
}
|
|
3268
3033
|
try {
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
const retry = await me({ message: "¿Instalar local en su lugar?" });
|
|
3275
|
-
onCancel(retry);
|
|
3276
|
-
if (retry)
|
|
3277
|
-
return ensureOpenCodePaths(getOpenCodeDirForScope("local", cwd));
|
|
3278
|
-
}
|
|
3279
|
-
throw e2;
|
|
3034
|
+
const paths = ensureOpenCodePaths(dir);
|
|
3035
|
+
ye(dir, "Instalación local");
|
|
3036
|
+
return paths;
|
|
3037
|
+
} catch (e) {
|
|
3038
|
+
throw e;
|
|
3280
3039
|
}
|
|
3281
3040
|
}
|
|
3282
|
-
function isGlobalScope(paths) {
|
|
3283
|
-
const globalDir = getGlobalOpenCodeDir().replace(/\\/g, "/");
|
|
3284
|
-
const root = paths.root.replace(/\\/g, "/");
|
|
3285
|
-
return root === globalDir || root.startsWith(globalDir + "/");
|
|
3286
|
-
}
|
|
3287
3041
|
function getOrphanedItems(manifest, paths) {
|
|
3288
3042
|
const lockfile = readLockfile(paths.root);
|
|
3289
3043
|
if (!lockfile)
|
|
3290
3044
|
return [];
|
|
3291
3045
|
const manifestNames = {
|
|
3292
|
-
agents: new Set(manifest.agents.map((
|
|
3293
|
-
commands: new Set(manifest.commands.map((
|
|
3046
|
+
agents: new Set(manifest.agents.map((a) => a.name)),
|
|
3047
|
+
commands: new Set(manifest.commands.map((c) => c.name)),
|
|
3294
3048
|
skills: new Set((manifest.skills ?? []).map((s) => s.name)),
|
|
3295
|
-
mcpServers: new Set((manifest.mcpServers ?? []).map((
|
|
3049
|
+
mcpServers: new Set((manifest.mcpServers ?? []).map((m) => m.name))
|
|
3296
3050
|
};
|
|
3297
3051
|
const orphans = [];
|
|
3298
3052
|
for (const type of ["agents", "commands", "skills", "mcpServers"]) {
|
|
@@ -3366,6 +3120,16 @@ async function doInstallStack(toolsDir, projectRoot) {
|
|
|
3366
3120
|
const spin = L2();
|
|
3367
3121
|
let allOk = true;
|
|
3368
3122
|
const resolvedProjectRoot = projectRoot ?? dirname6(dirname6(toolsDir ?? join8(process.cwd(), ".opencode", "tools")));
|
|
3123
|
+
const resolvedToolsDir = toolsDir ?? join8(resolvedProjectRoot, ".opencode", "tools");
|
|
3124
|
+
v2.info(`Stack → projectRoot: ${resolvedProjectRoot} | toolsDir: ${resolvedToolsDir}`);
|
|
3125
|
+
try {
|
|
3126
|
+
const { homedir } = await import("os");
|
|
3127
|
+
const home = homedir();
|
|
3128
|
+
const inHome = resolvedProjectRoot.replace(/\\/g, "/") === home.replace(/\\/g, "/");
|
|
3129
|
+
if (inHome && !existsSync7(join8(home, ".git"))) {
|
|
3130
|
+
v2.warn(`Estás instalando el stack en tu home (${home}). Si esperabas instalar en un proyecto, hacé cd al proyecto y usá --scope local.`);
|
|
3131
|
+
}
|
|
3132
|
+
} catch {}
|
|
3369
3133
|
spin.start("Instalando CodeGraph...");
|
|
3370
3134
|
const cg = await installCodeGraph(toolsDir);
|
|
3371
3135
|
spin.stop(cg.success ? `✓ ${cg.message}` : `✗ ${cg.message}`);
|
|
@@ -3382,33 +3146,35 @@ async function doInstallStack(toolsDir, projectRoot) {
|
|
|
3382
3146
|
if (!eng.success)
|
|
3383
3147
|
allOk = false;
|
|
3384
3148
|
spin.start("Verificando configuración...");
|
|
3385
|
-
|
|
3386
|
-
const cfg =
|
|
3149
|
+
await Promise.resolve().then(() => init_config());
|
|
3150
|
+
const cfg = patchOpenCodeConfig(resolvedProjectRoot);
|
|
3387
3151
|
spin.stop(cfg.success ? `✓ ${cfg.message}` : `✗ ${cfg.message}`);
|
|
3388
3152
|
if (!cfg.success)
|
|
3389
3153
|
allOk = false;
|
|
3390
3154
|
if (!allOk) {
|
|
3391
3155
|
v2.warn("Algunos componentes requieren atención. Revisá los mensajes de error arriba.");
|
|
3392
3156
|
}
|
|
3157
|
+
try {
|
|
3158
|
+
const gi = ensureGitignore(resolvedProjectRoot);
|
|
3159
|
+
if (gi.created)
|
|
3160
|
+
v2.info(`.gitignore creado con patrones Ostacky`);
|
|
3161
|
+
else if (gi.updated)
|
|
3162
|
+
v2.info(`.gitignore actualizado: ${gi.patternsAdded.join(", ")}`);
|
|
3163
|
+
} catch {}
|
|
3393
3164
|
return allOk;
|
|
3394
3165
|
}
|
|
3395
3166
|
async function doInstallAll(manifest, paths) {
|
|
3396
3167
|
const spin = L2();
|
|
3397
3168
|
let errors = 0;
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
ensureToolDirs(paths.tools, ["codegraph", "engram"]);
|
|
3401
|
-
} else {
|
|
3402
|
-
v2.info("Scope global detectado: el stack (CodeGraph/Engram) y plugins (ostacky-controller, engram) permanecen siempre en <proyecto>/.opencode — se omite instalación de stack global.");
|
|
3403
|
-
v2.info("Para instalar el stack, ejecutá 'npx ostacky install-stack --scope local' dentro de cada proyecto.");
|
|
3404
|
-
}
|
|
3169
|
+
v2.info(`Scope → local | opencodeDir: ${paths.root} | tools: ${paths.tools}`);
|
|
3170
|
+
ensureToolDirs(paths.tools, ["codegraph", "engram"]);
|
|
3405
3171
|
for (const agent of manifest.agents) {
|
|
3406
3172
|
spin.start(`Descargando agente: ${agent.name} (${agent.version})`);
|
|
3407
3173
|
try {
|
|
3408
3174
|
await installAgent(agent, manifest, paths);
|
|
3409
3175
|
spin.stop(`Agente instalado: ${agent.name} (${agent.version})`);
|
|
3410
|
-
} catch (
|
|
3411
|
-
spin.stop(`Error en ${agent.name}: ${
|
|
3176
|
+
} catch (e) {
|
|
3177
|
+
spin.stop(`Error en ${agent.name}: ${e.message}`);
|
|
3412
3178
|
errors++;
|
|
3413
3179
|
}
|
|
3414
3180
|
}
|
|
@@ -3417,8 +3183,8 @@ async function doInstallAll(manifest, paths) {
|
|
|
3417
3183
|
try {
|
|
3418
3184
|
await installCommand(cmd, manifest, paths);
|
|
3419
3185
|
spin.stop(`Command instalado: ${cmd.name} (${cmd.version})`);
|
|
3420
|
-
} catch (
|
|
3421
|
-
spin.stop(`Error en ${cmd.name}: ${
|
|
3186
|
+
} catch (e) {
|
|
3187
|
+
spin.stop(`Error en ${cmd.name}: ${e.message}`);
|
|
3422
3188
|
errors++;
|
|
3423
3189
|
}
|
|
3424
3190
|
}
|
|
@@ -3427,8 +3193,8 @@ async function doInstallAll(manifest, paths) {
|
|
|
3427
3193
|
try {
|
|
3428
3194
|
await installSkill(skill, manifest, paths);
|
|
3429
3195
|
spin.stop(`Skill instalada: ${skill.name} (${skill.version})`);
|
|
3430
|
-
} catch (
|
|
3431
|
-
spin.stop(`Error en ${skill.name}: ${
|
|
3196
|
+
} catch (e) {
|
|
3197
|
+
spin.stop(`Error en ${skill.name}: ${e.message}`);
|
|
3432
3198
|
errors++;
|
|
3433
3199
|
}
|
|
3434
3200
|
}
|
|
@@ -3441,70 +3207,48 @@ async function doInstallAll(manifest, paths) {
|
|
|
3441
3207
|
try {
|
|
3442
3208
|
await installMcpServer(mcp, manifest, paths);
|
|
3443
3209
|
spin.stop(`MCP server instalado: ${mcp.name} (${mcp.version})`);
|
|
3444
|
-
} catch (
|
|
3445
|
-
spin.stop(`Error en ${mcp.name}: ${
|
|
3210
|
+
} catch (e) {
|
|
3211
|
+
spin.stop(`Error en ${mcp.name}: ${e.message}`);
|
|
3446
3212
|
errors++;
|
|
3447
3213
|
}
|
|
3448
3214
|
}
|
|
3449
3215
|
try {
|
|
3450
|
-
const { copyFileSync
|
|
3451
|
-
const { join
|
|
3452
|
-
|
|
3453
|
-
const { findProjectRoot
|
|
3454
|
-
const src =
|
|
3455
|
-
const dest =
|
|
3456
|
-
if (
|
|
3457
|
-
|
|
3458
|
-
|
|
3216
|
+
const { copyFileSync, mkdirSync, existsSync, rmSync } = await import("fs");
|
|
3217
|
+
const { join } = await import("path");
|
|
3218
|
+
await Promise.resolve().then(() => init_github());
|
|
3219
|
+
const { findProjectRoot } = await Promise.resolve().then(() => (init_fs(), {}));
|
|
3220
|
+
const src = join(PACKAGE_ROOT, "assets", "plugins", "ostacky-plugin.ts");
|
|
3221
|
+
const dest = join(paths.plugins, "ostacky-plugin.ts");
|
|
3222
|
+
if (existsSync(src)) {
|
|
3223
|
+
mkdirSync(paths.plugins, { recursive: true });
|
|
3224
|
+
copyFileSync(src, dest);
|
|
3459
3225
|
}
|
|
3460
3226
|
for (const legacy of ["ostacky-guard.ts", "ostacky-controller.ts"]) {
|
|
3461
|
-
const lp =
|
|
3462
|
-
if (
|
|
3227
|
+
const lp = join(paths.plugins, legacy);
|
|
3228
|
+
if (existsSync(lp))
|
|
3463
3229
|
try {
|
|
3464
|
-
|
|
3230
|
+
rmSync(lp, { force: true });
|
|
3465
3231
|
} catch {}
|
|
3466
3232
|
}
|
|
3467
|
-
const srcEng =
|
|
3468
|
-
const destEng =
|
|
3469
|
-
if (
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
}
|
|
3473
|
-
if (isGlobal) {
|
|
3474
|
-
try {
|
|
3475
|
-
const projRoot = findProjectRoot3();
|
|
3476
|
-
const localPlugins = join9(projRoot, ".opencode", "plugins");
|
|
3477
|
-
mkdirSync5(localPlugins, { recursive: true });
|
|
3478
|
-
if (existsSync8(src))
|
|
3479
|
-
copyFileSync4(src, join9(localPlugins, "ostacky-plugin.ts"));
|
|
3480
|
-
if (existsSync8(srcEng))
|
|
3481
|
-
copyFileSync4(srcEng, join9(localPlugins, "engram.ts"));
|
|
3482
|
-
for (const legacy of ["ostacky-guard.ts", "ostacky-controller.ts"]) {
|
|
3483
|
-
const lp = join9(localPlugins, legacy);
|
|
3484
|
-
if (existsSync8(lp))
|
|
3485
|
-
try {
|
|
3486
|
-
rmSync5(lp, { force: true });
|
|
3487
|
-
} catch {}
|
|
3488
|
-
}
|
|
3489
|
-
} catch {}
|
|
3233
|
+
const srcEng = join(PACKAGE_ROOT, "assets", "plugins", "engram.ts");
|
|
3234
|
+
const destEng = join(paths.plugins, "engram.ts");
|
|
3235
|
+
if (existsSync(srcEng)) {
|
|
3236
|
+
mkdirSync(paths.plugins, { recursive: true });
|
|
3237
|
+
copyFileSync(srcEng, destEng);
|
|
3490
3238
|
}
|
|
3491
3239
|
} catch {}
|
|
3492
3240
|
let stackOk = true;
|
|
3493
3241
|
let missingTools = [];
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
missingTools.push("CodeGraph");
|
|
3505
|
-
if (!existsSync7(engramDir) || !findBinaryInDir(engramDir, "engram"))
|
|
3506
|
-
missingTools.push("Engram");
|
|
3507
|
-
}
|
|
3242
|
+
v2.info("Instalando stack de herramientas...");
|
|
3243
|
+
stackOk = await doInstallStack(paths.tools, dirname6(paths.root));
|
|
3244
|
+
if (!stackOk)
|
|
3245
|
+
errors++;
|
|
3246
|
+
const codegraphDir = join8(paths.tools, "codegraph");
|
|
3247
|
+
const engramDir = join8(paths.tools, "engram");
|
|
3248
|
+
if (!existsSync7(codegraphDir) || !findBinaryInDir(codegraphDir, "codegraph"))
|
|
3249
|
+
missingTools.push("CodeGraph");
|
|
3250
|
+
if (!existsSync7(engramDir) || !findBinaryInDir(engramDir, "engram"))
|
|
3251
|
+
missingTools.push("Engram");
|
|
3508
3252
|
if (missingTools.length > 0) {
|
|
3509
3253
|
v2.warn(`Faltan herramientas del stack: ${missingTools.join(", ")}.
|
|
3510
3254
|
` + "Ejecutá `/install-stack` desde el agente para instalarlas manualmente.");
|
|
@@ -3514,6 +3258,14 @@ async function doInstallAll(manifest, paths) {
|
|
|
3514
3258
|
} else {
|
|
3515
3259
|
v2.warn(`Instalación parcial: ${errors} componente(s) requieren atención.`);
|
|
3516
3260
|
}
|
|
3261
|
+
try {
|
|
3262
|
+
const projectRoot = dirname6(paths.root);
|
|
3263
|
+
const gi = ensureGitignore(projectRoot);
|
|
3264
|
+
if (gi.created)
|
|
3265
|
+
v2.info(`.gitignore creado con patrones Ostacky`);
|
|
3266
|
+
else if (gi.updated)
|
|
3267
|
+
v2.info(`.gitignore actualizado: ${gi.patternsAdded.join(", ")}`);
|
|
3268
|
+
} catch {}
|
|
3517
3269
|
return errors === 0 && stackOk && missingTools.length === 0;
|
|
3518
3270
|
}
|
|
3519
3271
|
|
|
@@ -3521,10 +3273,10 @@ async function doInstallAll(manifest, paths) {
|
|
|
3521
3273
|
init_lockfile();
|
|
3522
3274
|
async function doAddAgent(manifest, paths) {
|
|
3523
3275
|
const lockfile = readLockfile(paths.root);
|
|
3524
|
-
const options = manifest.agents.map((
|
|
3525
|
-
const installed = getInstalledVersion(lockfile, "agents",
|
|
3526
|
-
const hint = installed ? `v${installed} instalado — ${
|
|
3527
|
-
return { value:
|
|
3276
|
+
const options = manifest.agents.map((a) => {
|
|
3277
|
+
const installed = getInstalledVersion(lockfile, "agents", a.name);
|
|
3278
|
+
const hint = installed ? `v${installed} instalado — ${a.description}` : a.description;
|
|
3279
|
+
return { value: a.name, label: `${a.name} (v${a.version})`, hint };
|
|
3528
3280
|
});
|
|
3529
3281
|
const selected = await pe({
|
|
3530
3282
|
message: "¿Qué agentes deseas instalar?",
|
|
@@ -3534,22 +3286,22 @@ async function doAddAgent(manifest, paths) {
|
|
|
3534
3286
|
onCancel(selected);
|
|
3535
3287
|
const spin = L2();
|
|
3536
3288
|
for (const name of selected) {
|
|
3537
|
-
const item = manifest.agents.find((
|
|
3289
|
+
const item = manifest.agents.find((a) => a.name === name);
|
|
3538
3290
|
spin.start(`Descargando agente: ${name} (${item.version})`);
|
|
3539
3291
|
try {
|
|
3540
3292
|
await installAgent(item, manifest, paths);
|
|
3541
3293
|
spin.stop(`Agente instalado: ${name} (${item.version})`);
|
|
3542
|
-
} catch (
|
|
3543
|
-
spin.stop(`Error: ${
|
|
3294
|
+
} catch (e) {
|
|
3295
|
+
spin.stop(`Error: ${e.message}`);
|
|
3544
3296
|
}
|
|
3545
3297
|
}
|
|
3546
3298
|
}
|
|
3547
3299
|
async function doAddCommand(manifest, paths) {
|
|
3548
3300
|
const lockfile = readLockfile(paths.root);
|
|
3549
|
-
const options = manifest.commands.map((
|
|
3550
|
-
const installed = getInstalledVersion(lockfile, "commands",
|
|
3551
|
-
const hint = installed ? `v${installed} instalado — ${
|
|
3552
|
-
return { value:
|
|
3301
|
+
const options = manifest.commands.map((c) => {
|
|
3302
|
+
const installed = getInstalledVersion(lockfile, "commands", c.name);
|
|
3303
|
+
const hint = installed ? `v${installed} instalado — ${c.description}` : c.description;
|
|
3304
|
+
return { value: c.name, label: `${c.name} (v${c.version})`, hint };
|
|
3553
3305
|
});
|
|
3554
3306
|
const selected = await pe({
|
|
3555
3307
|
message: "¿Qué commands deseas instalar?",
|
|
@@ -3559,13 +3311,13 @@ async function doAddCommand(manifest, paths) {
|
|
|
3559
3311
|
onCancel(selected);
|
|
3560
3312
|
const spin = L2();
|
|
3561
3313
|
for (const name of selected) {
|
|
3562
|
-
const item = manifest.commands.find((
|
|
3314
|
+
const item = manifest.commands.find((c) => c.name === name);
|
|
3563
3315
|
spin.start(`Descargando command: ${name} (${item.version})`);
|
|
3564
3316
|
try {
|
|
3565
3317
|
await installCommand(item, manifest, paths);
|
|
3566
3318
|
spin.stop(`Command instalado: ${name} (${item.version})`);
|
|
3567
|
-
} catch (
|
|
3568
|
-
spin.stop(`Error: ${
|
|
3319
|
+
} catch (e) {
|
|
3320
|
+
spin.stop(`Error: ${e.message}`);
|
|
3569
3321
|
}
|
|
3570
3322
|
}
|
|
3571
3323
|
}
|
|
@@ -3593,17 +3345,17 @@ async function doAddSkill(manifest, paths) {
|
|
|
3593
3345
|
try {
|
|
3594
3346
|
await installSkill(item, manifest, paths);
|
|
3595
3347
|
spin.stop(`Skill instalada: ${name} (${item.version})`);
|
|
3596
|
-
} catch (
|
|
3597
|
-
spin.stop(`Error: ${
|
|
3348
|
+
} catch (e) {
|
|
3349
|
+
spin.stop(`Error: ${e.message}`);
|
|
3598
3350
|
}
|
|
3599
3351
|
}
|
|
3600
3352
|
}
|
|
3601
3353
|
async function doAddMcp(manifest, paths) {
|
|
3602
3354
|
const lockfile = readLockfile(paths.root);
|
|
3603
|
-
const options = (manifest.mcpServers ?? []).map((
|
|
3604
|
-
const installed = getInstalledVersion(lockfile, "mcpServers",
|
|
3605
|
-
const hint = installed ? `v${installed} instalado — ${
|
|
3606
|
-
return { value:
|
|
3355
|
+
const options = (manifest.mcpServers ?? []).map((m) => {
|
|
3356
|
+
const installed = getInstalledVersion(lockfile, "mcpServers", m.name);
|
|
3357
|
+
const hint = installed ? `v${installed} instalado — ${m.description}` : m.description;
|
|
3358
|
+
return { value: m.name, label: `${m.name} (v${m.version})`, hint };
|
|
3607
3359
|
});
|
|
3608
3360
|
if (options.length === 0) {
|
|
3609
3361
|
v2.info("No hay MCP servers disponibles en el manifest.");
|
|
@@ -3617,15 +3369,15 @@ async function doAddMcp(manifest, paths) {
|
|
|
3617
3369
|
onCancel(selected);
|
|
3618
3370
|
const spin = L2();
|
|
3619
3371
|
for (const name of selected) {
|
|
3620
|
-
const item = manifest.mcpServers?.find((
|
|
3372
|
+
const item = manifest.mcpServers?.find((m) => m.name === name);
|
|
3621
3373
|
if (!item)
|
|
3622
3374
|
continue;
|
|
3623
3375
|
spin.start(`Instalando MCP server: ${name} (${item.version})`);
|
|
3624
3376
|
try {
|
|
3625
3377
|
await installMcpServer(item, manifest, paths);
|
|
3626
3378
|
spin.stop(`MCP server instalado: ${name} (${item.version})`);
|
|
3627
|
-
} catch (
|
|
3628
|
-
spin.stop(`Error: ${
|
|
3379
|
+
} catch (e) {
|
|
3380
|
+
spin.stop(`Error: ${e.message}`);
|
|
3629
3381
|
}
|
|
3630
3382
|
}
|
|
3631
3383
|
}
|
|
@@ -3692,8 +3444,8 @@ async function doUpdate(manifest, paths) {
|
|
|
3692
3444
|
}
|
|
3693
3445
|
spin.stop(`Actualizado: ${item.name} (${item.version})`);
|
|
3694
3446
|
updated++;
|
|
3695
|
-
} catch (
|
|
3696
|
-
spin.stop(`Error en ${item.name}: ${
|
|
3447
|
+
} catch (e) {
|
|
3448
|
+
spin.stop(`Error en ${item.name}: ${e.message}`);
|
|
3697
3449
|
}
|
|
3698
3450
|
}
|
|
3699
3451
|
v2.success(`${updated} recurso(s) actualizado(s).`);
|
|
@@ -3907,10 +3659,10 @@ async function doUninstall(paths) {
|
|
|
3907
3659
|
}
|
|
3908
3660
|
async function doUninstallAgentByName(name, paths) {
|
|
3909
3661
|
try {
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
} catch (
|
|
3913
|
-
v2.warn(`Nombre inválido: ${
|
|
3662
|
+
await Promise.resolve().then(() => init_security());
|
|
3663
|
+
validateFilePath(`${name}.md`);
|
|
3664
|
+
} catch (e) {
|
|
3665
|
+
v2.warn(`Nombre inválido: ${e.message}`);
|
|
3914
3666
|
return;
|
|
3915
3667
|
}
|
|
3916
3668
|
const lockfile = readLockfile(paths.root);
|
|
@@ -3935,10 +3687,10 @@ async function doUninstallAgentByName(name, paths) {
|
|
|
3935
3687
|
}
|
|
3936
3688
|
async function doUninstallCommandByName(name, paths) {
|
|
3937
3689
|
try {
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
} catch (
|
|
3941
|
-
v2.warn(`Nombre inválido: ${
|
|
3690
|
+
await Promise.resolve().then(() => init_security());
|
|
3691
|
+
validateFilePath(`${name}.md`);
|
|
3692
|
+
} catch (e) {
|
|
3693
|
+
v2.warn(`Nombre inválido: ${e.message}`);
|
|
3942
3694
|
return;
|
|
3943
3695
|
}
|
|
3944
3696
|
const lockfile = readLockfile(paths.root);
|
|
@@ -3963,10 +3715,10 @@ async function doUninstallCommandByName(name, paths) {
|
|
|
3963
3715
|
}
|
|
3964
3716
|
async function doUninstallSkillByName(name, paths) {
|
|
3965
3717
|
try {
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
} catch (
|
|
3969
|
-
v2.warn(`Nombre inválido: ${
|
|
3718
|
+
await Promise.resolve().then(() => init_security());
|
|
3719
|
+
validateFilePath(name);
|
|
3720
|
+
} catch (e) {
|
|
3721
|
+
v2.warn(`Nombre inválido: ${e.message}`);
|
|
3970
3722
|
return;
|
|
3971
3723
|
}
|
|
3972
3724
|
const lockfile = readLockfile(paths.root);
|
|
@@ -3991,10 +3743,10 @@ async function doUninstallSkillByName(name, paths) {
|
|
|
3991
3743
|
}
|
|
3992
3744
|
async function doUninstallMcpByName(name, paths) {
|
|
3993
3745
|
try {
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
} catch (
|
|
3997
|
-
v2.warn(`Nombre inválido: ${
|
|
3746
|
+
await Promise.resolve().then(() => init_security());
|
|
3747
|
+
validateFilePath(name);
|
|
3748
|
+
} catch (e) {
|
|
3749
|
+
v2.warn(`Nombre inválido: ${e.message}`);
|
|
3998
3750
|
return;
|
|
3999
3751
|
}
|
|
4000
3752
|
const lockfile = readLockfile(paths.root);
|
|
@@ -4087,9 +3839,9 @@ ${info.note}` : ""}`, `Instalación requerida (${platform})`);
|
|
|
4087
3839
|
}
|
|
4088
3840
|
}
|
|
4089
3841
|
spin.stop("Instalación ejecutada, verificando...");
|
|
4090
|
-
} catch (
|
|
3842
|
+
} catch (e) {
|
|
4091
3843
|
spin.stop("Fallo la instalación automática.");
|
|
4092
|
-
const msg =
|
|
3844
|
+
const msg = e.message ?? String(e);
|
|
4093
3845
|
v2.error(`No se pudo instalar OpenCode automáticamente: ${msg}`);
|
|
4094
3846
|
if (platform === "win32") {
|
|
4095
3847
|
ye([
|
|
@@ -4183,12 +3935,6 @@ async function runInteractiveMenu(scope) {
|
|
|
4183
3935
|
fe("Listo.");
|
|
4184
3936
|
break;
|
|
4185
3937
|
case "stack": {
|
|
4186
|
-
if (isGlobalScope(paths)) {
|
|
4187
|
-
v2.error("install-stack requiere scope local; el stack vive en <proyecto>/.opencode/tools");
|
|
4188
|
-
v2.info(`Elegiste global (${paths.root}) — el stack debe instalarse por proyecto local.`);
|
|
4189
|
-
fe("Cancelado.");
|
|
4190
|
-
break;
|
|
4191
|
-
}
|
|
4192
3938
|
ensureToolDirs(paths.tools, ["codegraph", "engram"]);
|
|
4193
3939
|
const stackOk = await doInstallStack(paths.tools, dirname7(paths.root));
|
|
4194
3940
|
if (!stackOk)
|
|
@@ -4279,25 +4025,12 @@ async function runAddMcpCommand(scope) {
|
|
|
4279
4025
|
}
|
|
4280
4026
|
async function runInstallStackCommand(scope) {
|
|
4281
4027
|
await ensureOpencodeInstalled();
|
|
4282
|
-
if (scope === "global") {
|
|
4283
|
-
v2.error("install-stack requiere scope local; el stack vive en <proyecto>/.opencode/tools");
|
|
4284
|
-
fe("Usá: npx ostacky install-stack --scope local");
|
|
4285
|
-
process.exitCode = 1;
|
|
4286
|
-
return;
|
|
4287
|
-
}
|
|
4288
4028
|
we(" OpenCode Installer — Stack ");
|
|
4289
4029
|
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
4290
4030
|
if (!paths) {
|
|
4291
4031
|
fe("Cancelado.");
|
|
4292
4032
|
return;
|
|
4293
4033
|
}
|
|
4294
|
-
if (isGlobalScope(paths)) {
|
|
4295
|
-
v2.error("install-stack requiere scope local; el stack vive en <proyecto>/.opencode/tools");
|
|
4296
|
-
v2.info(`Scope resuelto a global (${paths.root}) — el stack debe instalarse por proyecto local.`);
|
|
4297
|
-
fe("Cancelado.");
|
|
4298
|
-
process.exitCode = 1;
|
|
4299
|
-
return;
|
|
4300
|
-
}
|
|
4301
4034
|
ensureToolDirs(paths.tools, ["codegraph", "engram"]);
|
|
4302
4035
|
const stackOk = await doInstallStack(paths.tools, dirname7(paths.root));
|
|
4303
4036
|
if (!stackOk)
|
|
@@ -4319,8 +4052,8 @@ async function runUninstallStackCommand(scope) {
|
|
|
4319
4052
|
fe("Cancelado.");
|
|
4320
4053
|
return;
|
|
4321
4054
|
}
|
|
4322
|
-
|
|
4323
|
-
const result =
|
|
4055
|
+
await Promise.resolve().then(() => init_stack());
|
|
4056
|
+
const result = uninstallStackConfig(paths);
|
|
4324
4057
|
if (result.success) {
|
|
4325
4058
|
v2.success(result.message);
|
|
4326
4059
|
} else {
|
|
@@ -4360,8 +4093,8 @@ async function runUninstallAgentCommand(name, scope) {
|
|
|
4360
4093
|
if (name) {
|
|
4361
4094
|
await doUninstallAgentByName(name, paths);
|
|
4362
4095
|
} else {
|
|
4363
|
-
|
|
4364
|
-
const lockfile =
|
|
4096
|
+
await Promise.resolve().then(() => init_lockfile());
|
|
4097
|
+
const lockfile = readLockfile(paths.root);
|
|
4365
4098
|
if (lockfile && Object.keys(lockfile.agents).length > 0) {
|
|
4366
4099
|
const prompts = exports_dist;
|
|
4367
4100
|
const installed = Object.keys(lockfile.agents);
|
|
@@ -4395,8 +4128,8 @@ async function runUninstallCommandCommand(name, scope) {
|
|
|
4395
4128
|
if (name) {
|
|
4396
4129
|
await doUninstallCommandByName(name, paths);
|
|
4397
4130
|
} else {
|
|
4398
|
-
|
|
4399
|
-
const lockfile =
|
|
4131
|
+
await Promise.resolve().then(() => init_lockfile());
|
|
4132
|
+
const lockfile = readLockfile(paths.root);
|
|
4400
4133
|
if (lockfile && Object.keys(lockfile.commands).length > 0) {
|
|
4401
4134
|
const prompts = exports_dist;
|
|
4402
4135
|
const installed = Object.keys(lockfile.commands);
|
|
@@ -4430,8 +4163,8 @@ async function runUninstallSkillCommand(name, scope) {
|
|
|
4430
4163
|
if (name) {
|
|
4431
4164
|
await doUninstallSkillByName(name, paths);
|
|
4432
4165
|
} else {
|
|
4433
|
-
|
|
4434
|
-
const lockfile =
|
|
4166
|
+
await Promise.resolve().then(() => init_lockfile());
|
|
4167
|
+
const lockfile = readLockfile(paths.root);
|
|
4435
4168
|
if (lockfile && lockfile.skills && Object.keys(lockfile.skills).length > 0) {
|
|
4436
4169
|
const prompts = exports_dist;
|
|
4437
4170
|
const installed = Object.keys(lockfile.skills);
|
|
@@ -4465,8 +4198,8 @@ async function runUninstallMcpCommand(name, scope) {
|
|
|
4465
4198
|
if (name) {
|
|
4466
4199
|
await doUninstallMcpByName(name, paths);
|
|
4467
4200
|
} else {
|
|
4468
|
-
|
|
4469
|
-
const lockfile =
|
|
4201
|
+
await Promise.resolve().then(() => init_lockfile());
|
|
4202
|
+
const lockfile = readLockfile(paths.root);
|
|
4470
4203
|
if (lockfile && lockfile.mcpServers && Object.keys(lockfile.mcpServers).length > 0) {
|
|
4471
4204
|
const prompts = exports_dist;
|
|
4472
4205
|
const installed = Object.keys(lockfile.mcpServers);
|
|
@@ -4499,43 +4232,49 @@ var HELP = `
|
|
|
4499
4232
|
ostacky — Instalador de agentes, comandos, skills y MCPs para OpenCode
|
|
4500
4233
|
|
|
4501
4234
|
Uso:
|
|
4502
|
-
npx ostacky [--scope local
|
|
4503
|
-
npx ostacky install [--scope local
|
|
4504
|
-
npx ostacky add agent [--scope local
|
|
4505
|
-
npx ostacky add command [--scope
|
|
4506
|
-
npx ostacky add skill [--scope
|
|
4507
|
-
npx ostacky add mcp [--scope
|
|
4508
|
-
npx ostacky install-stack [--scope local
|
|
4509
|
-
npx ostacky uninstall-stack [--scope local
|
|
4235
|
+
npx ostacky [--scope local] Menú interactivo (instalación completa, siempre local)
|
|
4236
|
+
npx ostacky install [--scope local] Instalar TODO (agente + skills + MCPs + CodeGraph + OpenSpec + Engram)
|
|
4237
|
+
npx ostacky add agent [--scope local] Agregar agente(s)
|
|
4238
|
+
npx ostacky add command [--scope local] Agregar command(s)
|
|
4239
|
+
npx ostacky add skill [--scope local] Agregar skill(s)
|
|
4240
|
+
npx ostacky add mcp [--scope local] Agregar MCP server(s)
|
|
4241
|
+
npx ostacky install-stack [--scope local] Instalar solo el stack de herramientas (CodeGraph, OpenSpec, Engram)
|
|
4242
|
+
npx ostacky uninstall-stack [--scope local] Remover la configuración del stack del proyecto
|
|
4510
4243
|
npx ostacky doctor Diagnostica locks, tools, state health
|
|
4511
4244
|
npx ostacky status [--json] Muestra estado del controller sin MCP
|
|
4512
|
-
npx ostacky update [--scope
|
|
4513
|
-
npx ostacky uninstall [--scope
|
|
4514
|
-
npx ostacky uninstall agent [--scope
|
|
4515
|
-
npx ostacky uninstall command [--scope
|
|
4516
|
-
npx ostacky uninstall skill [--scope
|
|
4517
|
-
npx ostacky uninstall mcp [--scope
|
|
4245
|
+
npx ostacky update [--scope local] Actualizar instalación
|
|
4246
|
+
npx ostacky uninstall [--scope local] Desinstalar todo
|
|
4247
|
+
npx ostacky uninstall agent [--scope local] Desinstalar agente(s)
|
|
4248
|
+
npx ostacky uninstall command [--scope local] Desinstalar command(s)
|
|
4249
|
+
npx ostacky uninstall skill [--scope local] Desinstalar skill(s)
|
|
4250
|
+
npx ostacky uninstall mcp [--scope local] Desinstalar MCP server(s)
|
|
4518
4251
|
npx ostacky --help Mostrar esta ayuda
|
|
4519
4252
|
npx ostacky --version Mostrar versión
|
|
4520
4253
|
|
|
4521
4254
|
Scope:
|
|
4522
|
-
--scope local Escribe en <proyecto>/.opencode (
|
|
4523
|
-
|
|
4524
|
-
--scope auto Elige local si existe .opencode o .git, si no global
|
|
4525
|
-
Sin flag Pregunta interactiva local (default) vs global
|
|
4255
|
+
--scope local Escribe en <proyecto>/.opencode (siempre local, instalador único)
|
|
4256
|
+
Sin flag Asume local implícito (no pregunta global)
|
|
4526
4257
|
`.trim();
|
|
4527
|
-
function
|
|
4258
|
+
function parseScopeArg(argv = process.argv) {
|
|
4528
4259
|
for (let i = 0;i < argv.length; i++) {
|
|
4529
4260
|
const arg = argv[i];
|
|
4530
4261
|
if (arg === "--scope" && i + 1 < argv.length) {
|
|
4531
|
-
const
|
|
4532
|
-
if (
|
|
4533
|
-
return
|
|
4262
|
+
const v = argv[i + 1];
|
|
4263
|
+
if (v === "local")
|
|
4264
|
+
return v;
|
|
4265
|
+
if (v === "global" || v === "auto") {
|
|
4266
|
+
console.error(`Error: --scope ${v} removido; Ostacky instala siempre local en <proyecto>/.opencode. Hacé cd al proyecto y re-ejecutá con --scope local.`);
|
|
4267
|
+
process.exit(1);
|
|
4268
|
+
}
|
|
4534
4269
|
}
|
|
4535
4270
|
if (arg.startsWith("--scope=")) {
|
|
4536
|
-
const
|
|
4537
|
-
if (
|
|
4538
|
-
return
|
|
4271
|
+
const v = arg.split("=")[1];
|
|
4272
|
+
if (v === "local")
|
|
4273
|
+
return v;
|
|
4274
|
+
if (v === "global" || v === "auto") {
|
|
4275
|
+
console.error(`Error: --scope ${v} removido; Ostacky instala siempre local en <proyecto>/.opencode. Hacé cd al proyecto y re-ejecutá con --scope local.`);
|
|
4276
|
+
process.exit(1);
|
|
4277
|
+
}
|
|
4539
4278
|
}
|
|
4540
4279
|
}
|
|
4541
4280
|
return null;
|
|
@@ -4554,7 +4293,7 @@ function withoutScopeArgs(argv) {
|
|
|
4554
4293
|
}
|
|
4555
4294
|
return out;
|
|
4556
4295
|
}
|
|
4557
|
-
var scope =
|
|
4296
|
+
var scope = parseScopeArg();
|
|
4558
4297
|
var argvNoScope = withoutScopeArgs(process.argv);
|
|
4559
4298
|
var [, , cmd, subcmd] = argvNoScope;
|
|
4560
4299
|
async function runDoctorCommand() {
|
|
@@ -4582,7 +4321,7 @@ async function runDoctorCommand() {
|
|
|
4582
4321
|
join10(opencodeDir, "plugins", "ostacky-controller.ts"),
|
|
4583
4322
|
join10(cwd, ".opencode", "plugins", "ostacky-controller.ts")
|
|
4584
4323
|
];
|
|
4585
|
-
const pluginActive = pluginPaths.some((
|
|
4324
|
+
const pluginActive = pluginPaths.some((p) => existsSync8(p));
|
|
4586
4325
|
try {
|
|
4587
4326
|
if (!existsSync8(statePath)) {
|
|
4588
4327
|
if (pluginActive)
|
|
@@ -4649,8 +4388,8 @@ async function runDoctorCommand() {
|
|
|
4649
4388
|
}
|
|
4650
4389
|
}
|
|
4651
4390
|
}
|
|
4652
|
-
} catch (
|
|
4653
|
-
check(`controller: ${
|
|
4391
|
+
} catch (e) {
|
|
4392
|
+
check(`controller: ${e.message}`, false);
|
|
4654
4393
|
}
|
|
4655
4394
|
try {
|
|
4656
4395
|
const lockPid = join10(opencodeDir, "ostacky-state.json.lock.pid");
|
|
@@ -4679,13 +4418,13 @@ async function runDoctorCommand() {
|
|
|
4679
4418
|
}
|
|
4680
4419
|
const tools = ["codegraph", "engram"];
|
|
4681
4420
|
for (const t of tools) {
|
|
4682
|
-
const
|
|
4683
|
-
const pExe =
|
|
4684
|
-
check(`tool ${t}: ${existsSync8(
|
|
4421
|
+
const p = join10(opencodeDir, "tools", t, "bin", t);
|
|
4422
|
+
const pExe = p + ".exe";
|
|
4423
|
+
check(`tool ${t}: ${existsSync8(p) || existsSync8(pExe) ? "found" : "missing"}`, existsSync8(p) || existsSync8(pExe), true);
|
|
4685
4424
|
}
|
|
4686
4425
|
try {
|
|
4687
4426
|
const manifest = JSON.parse(readFileSync6(join10(cwd, "manifest.json"), "utf-8"));
|
|
4688
|
-
const expected = manifest.mcpServers?.find((
|
|
4427
|
+
const expected = manifest.mcpServers?.find((x) => x.name === "ostacky-controller")?.sha256;
|
|
4689
4428
|
if (expected) {
|
|
4690
4429
|
const actual = computeTreeHash(join10(cwd, "assets", "mcp", "ostacky-controller"));
|
|
4691
4430
|
check(`manifest hash: ${expected.slice(0, 8)} vs actual ${actual.slice(0, 8)}`, expected === actual);
|
|
@@ -4708,9 +4447,9 @@ async function runDoctorCommand() {
|
|
|
4708
4447
|
} else {
|
|
4709
4448
|
const files = readdirSync3(cacheDir);
|
|
4710
4449
|
let total = 0;
|
|
4711
|
-
for (const
|
|
4450
|
+
for (const f of files) {
|
|
4712
4451
|
try {
|
|
4713
|
-
total += statSync3(join10(cacheDir,
|
|
4452
|
+
total += statSync3(join10(cacheDir, f)).size;
|
|
4714
4453
|
} catch {}
|
|
4715
4454
|
}
|
|
4716
4455
|
const totalMB = (total / 1048576).toFixed(2);
|
|
@@ -4727,9 +4466,86 @@ async function runDoctorCommand() {
|
|
|
4727
4466
|
}
|
|
4728
4467
|
} catch {}
|
|
4729
4468
|
}
|
|
4730
|
-
} catch (
|
|
4731
|
-
console.log(`⚠️ cache: check failed ${
|
|
4469
|
+
} catch (e) {
|
|
4470
|
+
console.log(`⚠️ cache: check failed ${e.message}`);
|
|
4732
4471
|
}
|
|
4472
|
+
try {
|
|
4473
|
+
const coreSrc = join10(cwd, "src", "controller-core.ts");
|
|
4474
|
+
const coreMcp = join10(cwd, "assets", "mcp", "ostacky-controller", "controller-core.js");
|
|
4475
|
+
const corePlugin = join10(cwd, "assets", "plugins", "controller-core.ts");
|
|
4476
|
+
const coreSync = existsSync8(coreSrc) && existsSync8(coreMcp) && existsSync8(corePlugin);
|
|
4477
|
+
if (coreSync) {
|
|
4478
|
+
const srcHash = readFileSync6(coreSrc, "utf-8").slice(0, 100);
|
|
4479
|
+
const mcpHash = readFileSync6(coreMcp, "utf-8").slice(0, 100);
|
|
4480
|
+
const ok = readFileSync6(coreMcp, "utf-8").includes("STATES") && readFileSync6(corePlugin, "utf-8").includes("STATES");
|
|
4481
|
+
check(`controller-core: synced (${ok ? "STATES present" : "mismatch"})`, ok);
|
|
4482
|
+
if (!ok)
|
|
4483
|
+
console.log(" Run: bun run scripts/sync-controller-core.ts (if exists) or copy src/controller-core.ts");
|
|
4484
|
+
} else {
|
|
4485
|
+
check("controller-core: synced", false, true);
|
|
4486
|
+
}
|
|
4487
|
+
const auditPath = join10(opencodeDir, "ostacky-audit.jsonl");
|
|
4488
|
+
if (existsSync8(auditPath)) {
|
|
4489
|
+
const sz = statSync3(auditPath).size;
|
|
4490
|
+
const lines = readFileSync6(auditPath, "utf-8").split(`
|
|
4491
|
+
`).filter(Boolean).length;
|
|
4492
|
+
check(`audit: jsonl ${lines} entries, ${(sz / 1024).toFixed(1)}KB`, sz < 512000);
|
|
4493
|
+
if (existsSync8(statePath)) {
|
|
4494
|
+
const s = JSON.parse(readFileSync6(statePath, "utf-8"));
|
|
4495
|
+
const stateSize = statSync3(statePath).size;
|
|
4496
|
+
check(`state size <50KB (${(stateSize / 1024).toFixed(1)}KB)`, stateSize < 51200);
|
|
4497
|
+
if (s.audit && s.audit.length > 20)
|
|
4498
|
+
console.log(`⚠️ state.audit large: ${s.audit.length} (should be tail only, full in jsonl)`);
|
|
4499
|
+
else if (s.auditTail)
|
|
4500
|
+
console.log(`✅ state.auditTail: ${s.auditTail.length} (jsonl primary)`);
|
|
4501
|
+
}
|
|
4502
|
+
} else {
|
|
4503
|
+
console.log("ℹ️ audit: jsonl not yet created (will be created on next audit)");
|
|
4504
|
+
}
|
|
4505
|
+
const ostackyMd = readFileSync6(join10(cwd, "assets", "agents", "ostacky.md"), "utf-8");
|
|
4506
|
+
const hasLevels = ostackyMd.includes("LEVEL_THRESHOLDS") || ostackyMd.includes("classifyLevel");
|
|
4507
|
+
check("levels: unified via tiered.ts", hasLevels, true);
|
|
4508
|
+
const hasHonesty = ostackyMd.includes("Principios de honestidad");
|
|
4509
|
+
check("honesty: 7 SHALL", hasHonesty);
|
|
4510
|
+
if (!hasHonesty)
|
|
4511
|
+
console.log(" Expected: ## Principios de honestidad (SHALL) in ostacky.md");
|
|
4512
|
+
const pluginPath = join10(cwd, "assets", "plugins", "ostacky-plugin.ts");
|
|
4513
|
+
if (existsSync8(pluginPath)) {
|
|
4514
|
+
const plugin = readFileSync6(pluginPath, "utf-8");
|
|
4515
|
+
const hasSpecGuard = plugin.includes("getDiscoverySnapshot") && plugin.includes("specSnapshot");
|
|
4516
|
+
const hasNoOverwrite = plugin.includes("No edites sin Read fresco") || plugin.includes("specSnapshot");
|
|
4517
|
+
check("spec: no-overwrite guard", hasNoOverwrite, true);
|
|
4518
|
+
}
|
|
4519
|
+
const hasSyncProactive = ostackyMd.includes("Noté que lo que acordamos");
|
|
4520
|
+
check("sync: proactive WARN", hasSyncProactive, true);
|
|
4521
|
+
} catch (e) {
|
|
4522
|
+
console.log(`⚠️ honesty/spec checks failed: ${e.message}`);
|
|
4523
|
+
}
|
|
4524
|
+
try {
|
|
4525
|
+
const giPath = join10(cwd, ".gitignore");
|
|
4526
|
+
if (!existsSync8(giPath)) {
|
|
4527
|
+
console.log("⚠️ .gitignore: missing (run npx ostacky install --scope local to create)");
|
|
4528
|
+
hasWarn = true;
|
|
4529
|
+
} else {
|
|
4530
|
+
const gi = readFileSync6(giPath, "utf-8");
|
|
4531
|
+
const needed = [
|
|
4532
|
+
".opencode/tools/",
|
|
4533
|
+
".opencode/cache/",
|
|
4534
|
+
".opencode/ostacky-state.json",
|
|
4535
|
+
".codegraph/",
|
|
4536
|
+
"openspec/"
|
|
4537
|
+
];
|
|
4538
|
+
const missing = needed.filter((p) => !gi.includes(p));
|
|
4539
|
+
if (missing.length > 0) {
|
|
4540
|
+
console.log(`⚠️ .gitignore: missing ${missing.join(", ")} — run npx ostacky install to regenerate`);
|
|
4541
|
+
hasWarn = true;
|
|
4542
|
+
} else {
|
|
4543
|
+
console.log("✅ .gitignore: OK (covers .opencode/tools/, cache, state, .codegraph/, openspec/)");
|
|
4544
|
+
}
|
|
4545
|
+
if (!gi.includes("# Ostacky"))
|
|
4546
|
+
console.log("ℹ️ .gitignore: missing # Ostacky header");
|
|
4547
|
+
}
|
|
4548
|
+
} catch {}
|
|
4733
4549
|
try {
|
|
4734
4550
|
const secPath = join10(cwd, "src", "security.ts");
|
|
4735
4551
|
if (!existsSync8(secPath)) {
|
|
@@ -4776,14 +4592,20 @@ async function runStatusCommand(args) {
|
|
|
4776
4592
|
const degraded = parsed.degraded ? " degraded" : "";
|
|
4777
4593
|
const lastHandoff = parsed.lastHandoff ? ` lastHandoff: ${parsed.lastHandoff.summary?.slice(0, 60)}` : "";
|
|
4778
4594
|
if (isJson) {
|
|
4779
|
-
console.log(JSON.stringify({
|
|
4595
|
+
console.log(JSON.stringify({
|
|
4596
|
+
state: parsed.state,
|
|
4597
|
+
revision: parsed.revision,
|
|
4598
|
+
degraded: !!parsed.degraded,
|
|
4599
|
+
tasks: `${completed}/${expected}`,
|
|
4600
|
+
lastHandoff: parsed.lastHandoff
|
|
4601
|
+
}, null, 2));
|
|
4780
4602
|
} else {
|
|
4781
4603
|
console.log(`${parsed.state} rev ${parsed.revision}${degraded} tasks ${completed}/${expected}${lastHandoff}`);
|
|
4782
4604
|
if (parsed.lastProposal)
|
|
4783
4605
|
console.log(`lastProposal: ${parsed.lastProposal.summary} shownToUser=${parsed.lastProposal.shownToUser}`);
|
|
4784
4606
|
}
|
|
4785
|
-
} catch (
|
|
4786
|
-
console.log(`Error reading state: ${
|
|
4607
|
+
} catch (e) {
|
|
4608
|
+
console.log(`Error reading state: ${e.message}`);
|
|
4787
4609
|
}
|
|
4788
4610
|
}
|
|
4789
4611
|
async function main() {
|
|
@@ -4796,11 +4618,6 @@ async function main() {
|
|
|
4796
4618
|
await runInstallCommand(scope);
|
|
4797
4619
|
break;
|
|
4798
4620
|
case "install-stack":
|
|
4799
|
-
if (scope === "global") {
|
|
4800
|
-
console.error("Error: install-stack requiere scope local; el stack vive en <proyecto>/.opencode/tools");
|
|
4801
|
-
console.error("Sugerencia: ejecutá 'npx ostacky install-stack --scope local' dentro de cada proyecto.");
|
|
4802
|
-
process.exit(1);
|
|
4803
|
-
}
|
|
4804
4621
|
await runInstallStackCommand(scope);
|
|
4805
4622
|
break;
|
|
4806
4623
|
case "uninstall-stack":
|
|
@@ -4865,7 +4682,7 @@ async function main() {
|
|
|
4865
4682
|
await runInteractiveMenu(scope);
|
|
4866
4683
|
}
|
|
4867
4684
|
}
|
|
4868
|
-
main().catch((
|
|
4869
|
-
console.error("Error:",
|
|
4685
|
+
main().catch((e) => {
|
|
4686
|
+
console.error("Error:", e.message);
|
|
4870
4687
|
process.exit(1);
|
|
4871
4688
|
});
|