ostacky 0.8.2 → 0.8.4
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 +20 -25
- package/assets/agents/ostacky.md +18 -3
- package/assets/commands/install-stack.md +2 -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 +5 -3
- package/dist/cli.js +503 -720
- package/manifest.json +43 -43
- 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.4",
|
|
748
625
|
repo: "JaimeHoracio/Ostacky",
|
|
749
|
-
tag: "v0.8.
|
|
626
|
+
tag: "v0.8.4",
|
|
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.4: prune de skills obsoletas, lastHandoff, getAvailableTransitions, consecutiveFailures real, mem_session_summary automático al cierre.",
|
|
632
|
+
version: "0.8.4",
|
|
633
|
+
sha256: "3e08cce52941622ff7897ee71020b26d165e473597a4f85723cf92447b17faaa"
|
|
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.4: controller plugin como alma hard-gate, Context7 removido del stack.",
|
|
641
|
+
version: "0.8.4",
|
|
642
|
+
sha256: "2527559c18ac21187af6333d8bf9f6899812a5268604a8cfb76e2586aacc8367"
|
|
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.4",
|
|
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.4: 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.4",
|
|
658
|
+
sha256: "c242f7e4cdad43269134868364b3a1544d192d6362030c7000e7eee589d7e26d"
|
|
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.4",
|
|
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.4",
|
|
674
|
+
sha256: "1e8ad4dc54ae4da0fc581b3968e9e255d43a07d2911f974f953c438a8244b728"
|
|
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.
|
|
804
|
-
sha256: "
|
|
680
|
+
version: "0.8.4",
|
|
681
|
+
sha256: "b121879987c34185285754b56c2ad0a74b4f31c5fe9e2849f7f99130b8f7f20f"
|
|
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.
|
|
811
|
-
sha256: "
|
|
687
|
+
version: "0.8.4",
|
|
688
|
+
sha256: "2932bb3c38b2e41305ec8aa375a7800259eda1bba6bbecfa5b6c4de850b2be43"
|
|
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.
|
|
818
|
-
sha256: "
|
|
694
|
+
version: "0.8.4",
|
|
695
|
+
sha256: "bea7788e897cfae9aeb457eab6cda2ec8822f253b23fd868dc481ddec45cd627"
|
|
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.
|
|
825
|
-
sha256: "
|
|
701
|
+
version: "0.8.4",
|
|
702
|
+
sha256: "57e3829cc1217e46e693049eaf910f8fad58c27f6d1ec3e37ab7534993123ab6"
|
|
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.
|
|
832
|
-
sha256: "
|
|
708
|
+
version: "0.8.4",
|
|
709
|
+
sha256: "e6928f7e5fe2dc7db98f16531d47aa739bb282c4d8ef05ca867d27ab6a8cdb95"
|
|
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.
|
|
839
|
-
sha256: "
|
|
715
|
+
version: "0.8.4",
|
|
716
|
+
sha256: "8cccf2b6234be4dfbfd426d0ba4b3e26d3ed8f416adaf1d78e8e3b8ef39067df"
|
|
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.
|
|
846
|
-
sha256: "
|
|
722
|
+
version: "0.8.4",
|
|
723
|
+
sha256: "36f5d39ad42360259516dfd6ba60673fd62a5ce54b69ae4d159816b73b4543f6"
|
|
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.
|
|
853
|
-
sha256: "
|
|
729
|
+
version: "0.8.4",
|
|
730
|
+
sha256: "936c66a3e13a78375eab0d979e8d7d813b9fb070f337034128ce40f9c3f10766"
|
|
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.
|
|
860
|
-
sha256: "
|
|
736
|
+
version: "0.8.4",
|
|
737
|
+
sha256: "0e566b7cd752784a58ef96bec28b0a85c933d72a8c93a5601ca1cd01669a5620"
|
|
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.
|
|
867
|
-
sha256: "
|
|
743
|
+
version: "0.8.4",
|
|
744
|
+
sha256: "52746b6c51d5ed5026774391d55edac6558f488d68a5b84f9c7064a10e2333bb"
|
|
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.
|
|
874
|
-
sha256: "
|
|
750
|
+
version: "0.8.4",
|
|
751
|
+
sha256: "00cec01d6b58cd394abff74ed7fb554adb72c34c4d041e85aa703e72932175f4"
|
|
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.
|
|
881
|
-
sha256: "
|
|
757
|
+
version: "0.8.4",
|
|
758
|
+
sha256: "f66cb61561a7fa23df40d9450459f4109ffc2f7a6f07efa74a8c1fcb96f105bd"
|
|
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.
|
|
888
|
-
sha256: "
|
|
764
|
+
version: "0.8.4",
|
|
765
|
+
sha256: "397725e389ce4a80ef6864ec793d1b6f1ae158d1e33db56b8f5ff5167b3938fa"
|
|
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.4",
|
|
772
|
+
sha256: "6631253dd6a99208df1884d1609ba13c65702e5c7da327df3cd16c2be9357d91"
|
|
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) {
|
|
@@ -1271,19 +1094,6 @@ var init_config = __esm(() => {
|
|
|
1271
1094
|
});
|
|
1272
1095
|
|
|
1273
1096
|
// src/stack.ts
|
|
1274
|
-
var exports_stack = {};
|
|
1275
|
-
__export(exports_stack, {
|
|
1276
|
-
OPENSPEC_NPM_PACKAGE: () => OPENSPEC_NPM_PACKAGE,
|
|
1277
|
-
buildEngramDownloadUrl: () => buildEngramDownloadUrl,
|
|
1278
|
-
buildLocalMcpCommand: () => buildLocalMcpCommand,
|
|
1279
|
-
installCodeGraph: () => installCodeGraph,
|
|
1280
|
-
installEngram: () => installEngram,
|
|
1281
|
-
installStack: () => installStack,
|
|
1282
|
-
setupOpenSpec: () => setupOpenSpec,
|
|
1283
|
-
uninstallEngramConfig: () => uninstallEngramConfig,
|
|
1284
|
-
uninstallStackConfig: () => uninstallStackConfig,
|
|
1285
|
-
verifyMcpServers: () => verifyMcpServers
|
|
1286
|
-
});
|
|
1287
1097
|
import { chmodSync, copyFileSync as copyFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync4, rmSync as rmSync4, unlinkSync as unlinkSync3 } from "fs";
|
|
1288
1098
|
import { basename as basename2, dirname as dirname5, join as join7, resolve as resolve2 } from "path";
|
|
1289
1099
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
@@ -1344,12 +1154,15 @@ function copyEngramPlugin(projectRoot) {
|
|
|
1344
1154
|
}
|
|
1345
1155
|
function copyOstackyControllerPlugin(projectRoot) {
|
|
1346
1156
|
const pluginSource = join7(PACKAGE_ROOT, "assets", "plugins", "ostacky-plugin.ts");
|
|
1157
|
+
const coreSource = join7(PACKAGE_ROOT, "assets", "plugins", "controller-core.ts");
|
|
1347
1158
|
const pluginsDir = join7(projectRoot, ".opencode", "plugins");
|
|
1348
1159
|
if (!existsSync6(pluginSource)) {
|
|
1349
1160
|
throw new Error(`Plugin bundleado de OstackyController no encontrado: ${pluginSource}`);
|
|
1350
1161
|
}
|
|
1351
1162
|
mkdirSync4(pluginsDir, { recursive: true });
|
|
1352
1163
|
copyFileSync3(pluginSource, join7(pluginsDir, "ostacky-plugin.ts"));
|
|
1164
|
+
if (existsSync6(coreSource))
|
|
1165
|
+
copyFileSync3(coreSource, join7(pluginsDir, "controller-core.ts"));
|
|
1353
1166
|
}
|
|
1354
1167
|
function buildEngramDownloadUrl(tag, platform = process.platform, arch = process.arch) {
|
|
1355
1168
|
const target = getEngramReleaseTarget(platform, arch);
|
|
@@ -1400,11 +1213,11 @@ async function installCodeGraph(toolsDir) {
|
|
|
1400
1213
|
const ext = process.platform === "win32" ? "zip" : "tar.gz";
|
|
1401
1214
|
const url = `https://github.com/colbymchenry/codegraph/releases/download/${tag}/codegraph-${target}.${ext}`;
|
|
1402
1215
|
try {
|
|
1403
|
-
archivePromotion = await downloadAndExtractWithRetry(url, cgToolDir,
|
|
1404
|
-
} catch (
|
|
1216
|
+
archivePromotion = await downloadAndExtractWithRetry(url, cgToolDir, 1, 180000, 2);
|
|
1217
|
+
} catch (e) {
|
|
1405
1218
|
return {
|
|
1406
1219
|
success: false,
|
|
1407
|
-
message: `Error descargando CodeGraph ${tag}: ${
|
|
1220
|
+
message: `Error descargando CodeGraph ${tag}: ${e.message}`
|
|
1408
1221
|
};
|
|
1409
1222
|
}
|
|
1410
1223
|
const expectedBin = join7(cgToolDir, "bin", getExecutableName("codegraph"));
|
|
@@ -1567,10 +1380,10 @@ async function installEngram(toolsDir) {
|
|
|
1567
1380
|
}
|
|
1568
1381
|
try {
|
|
1569
1382
|
archivePromotion = await downloadAndExtractWithRetry(url, engramToolDir, 0, 120000, 2);
|
|
1570
|
-
} catch (
|
|
1383
|
+
} catch (e) {
|
|
1571
1384
|
return {
|
|
1572
1385
|
success: false,
|
|
1573
|
-
message: `Error descargando Engram ${tag}: ${
|
|
1386
|
+
message: `Error descargando Engram ${tag}: ${e.message}`
|
|
1574
1387
|
};
|
|
1575
1388
|
}
|
|
1576
1389
|
mkdirSync4(engramBinDir, { recursive: true });
|
|
@@ -1601,18 +1414,6 @@ async function installEngram(toolsDir) {
|
|
|
1601
1414
|
archivePromotion?.commit();
|
|
1602
1415
|
return { success: true, message: "Engram instalado localmente y configurado para OpenCode (MCP + plugin)" };
|
|
1603
1416
|
}
|
|
1604
|
-
async function installStack(toolsDir) {
|
|
1605
|
-
const { projectRoot } = resolveToolInstallLocation(toolsDir);
|
|
1606
|
-
try {
|
|
1607
|
-
copyOstackyControllerPlugin(projectRoot);
|
|
1608
|
-
} catch {}
|
|
1609
|
-
return {
|
|
1610
|
-
codegraph: await installCodeGraph(toolsDir),
|
|
1611
|
-
openspec: setupOpenSpec(projectRoot),
|
|
1612
|
-
engram: await installEngram(toolsDir),
|
|
1613
|
-
config: patchOpenCodeConfig(projectRoot)
|
|
1614
|
-
};
|
|
1615
|
-
}
|
|
1616
1417
|
function uninstallEngramConfig() {
|
|
1617
1418
|
const projectRoot = findProjectRoot();
|
|
1618
1419
|
const configPath = findOpenCodeConfig(projectRoot);
|
|
@@ -1682,29 +1483,6 @@ function uninstallStackConfig(paths) {
|
|
|
1682
1483
|
message: `Removido: ${removed.join(", ")}. Los binarios globales (codegraph, engram) no se tocaron.`
|
|
1683
1484
|
};
|
|
1684
1485
|
}
|
|
1685
|
-
function verifyMcpServers(projectRoot) {
|
|
1686
|
-
const opencodeDir = findOpenCodeDir(projectRoot);
|
|
1687
|
-
const mcpBase = opencodeDir ? join7(opencodeDir, "mcp") : join7(projectRoot, ".opencode", "mcp");
|
|
1688
|
-
const controllerPath = join7(mcpBase, "ostacky-controller", "index.js");
|
|
1689
|
-
const openspecPath = join7(mcpBase, "openspec", "index.js");
|
|
1690
|
-
const result = {
|
|
1691
|
-
ostackyController: {
|
|
1692
|
-
exists: existsSync6(controllerPath),
|
|
1693
|
-
path: controllerPath
|
|
1694
|
-
},
|
|
1695
|
-
openspec: {
|
|
1696
|
-
exists: existsSync6(openspecPath),
|
|
1697
|
-
path: openspecPath
|
|
1698
|
-
}
|
|
1699
|
-
};
|
|
1700
|
-
if (!result.ostackyController.exists) {
|
|
1701
|
-
result.ostackyController.error = "ostacky-controller MCP server not found";
|
|
1702
|
-
}
|
|
1703
|
-
if (!result.openspec.exists) {
|
|
1704
|
-
result.openspec.error = "openspec MCP server not found";
|
|
1705
|
-
}
|
|
1706
|
-
return result;
|
|
1707
|
-
}
|
|
1708
1486
|
var OPENSPEC_NPM_PACKAGE = "@fission-ai/openspec";
|
|
1709
1487
|
var init_stack = __esm(() => {
|
|
1710
1488
|
init_fs();
|
|
@@ -1714,7 +1492,7 @@ var init_stack = __esm(() => {
|
|
|
1714
1492
|
// package.json
|
|
1715
1493
|
var package_default = {
|
|
1716
1494
|
name: "ostacky",
|
|
1717
|
-
version: "0.8.
|
|
1495
|
+
version: "0.8.4",
|
|
1718
1496
|
description: "Instalador interactivo de agentes y comandos para OpenCode",
|
|
1719
1497
|
type: "module",
|
|
1720
1498
|
bin: {
|
|
@@ -2379,13 +2157,13 @@ var y2 = (s) => {
|
|
|
2379
2157
|
}
|
|
2380
2158
|
};
|
|
2381
2159
|
var k2 = (s) => {
|
|
2382
|
-
const { cursor: n, options: t, style: i } = s,
|
|
2383
|
-
let
|
|
2384
|
-
n >=
|
|
2385
|
-
const $
|
|
2386
|
-
return t.slice(
|
|
2387
|
-
const
|
|
2388
|
-
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);
|
|
2389
2167
|
});
|
|
2390
2168
|
};
|
|
2391
2169
|
var ue = (s) => new PD({ validate: s.validate, placeholder: s.placeholder, defaultValue: s.defaultValue, initialValue: s.initialValue, render() {
|
|
@@ -2435,12 +2213,12 @@ var me = (s) => {
|
|
|
2435
2213
|
return new fD({ active: n, inactive: t, initialValue: s.initialValue ?? true, render() {
|
|
2436
2214
|
const i = `${import_picocolors2.default.gray(a)}
|
|
2437
2215
|
${y2(this.state)} ${s.message}
|
|
2438
|
-
`,
|
|
2216
|
+
`, r = this.value ? n : t;
|
|
2439
2217
|
switch (this.state) {
|
|
2440
2218
|
case "submit":
|
|
2441
|
-
return `${i}${import_picocolors2.default.gray(a)} ${import_picocolors2.default.dim(
|
|
2219
|
+
return `${i}${import_picocolors2.default.gray(a)} ${import_picocolors2.default.dim(r)}`;
|
|
2442
2220
|
case "cancel":
|
|
2443
|
-
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))}
|
|
2444
2222
|
${import_picocolors2.default.gray(a)}`;
|
|
2445
2223
|
default:
|
|
2446
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}`}
|
|
@@ -2451,16 +2229,16 @@ ${import_picocolors2.default.cyan(m2)}
|
|
|
2451
2229
|
};
|
|
2452
2230
|
var de = (s) => {
|
|
2453
2231
|
const n = (t, i) => {
|
|
2454
|
-
const
|
|
2232
|
+
const r = t.label ?? String(t.value);
|
|
2455
2233
|
switch (i) {
|
|
2456
2234
|
case "selected":
|
|
2457
|
-
return `${import_picocolors2.default.dim(
|
|
2235
|
+
return `${import_picocolors2.default.dim(r)}`;
|
|
2458
2236
|
case "active":
|
|
2459
|
-
return `${import_picocolors2.default.green(j2)} ${
|
|
2237
|
+
return `${import_picocolors2.default.green(j2)} ${r} ${t.hint ? import_picocolors2.default.dim(`(${t.hint})`) : ""}`;
|
|
2460
2238
|
case "cancelled":
|
|
2461
|
-
return `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(
|
|
2239
|
+
return `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(r))}`;
|
|
2462
2240
|
default:
|
|
2463
|
-
return `${import_picocolors2.default.dim(R2)} ${import_picocolors2.default.dim(
|
|
2241
|
+
return `${import_picocolors2.default.dim(R2)} ${import_picocolors2.default.dim(r)}`;
|
|
2464
2242
|
}
|
|
2465
2243
|
};
|
|
2466
2244
|
return new jD({ options: s.options, initialValue: s.initialValue, render() {
|
|
@@ -2474,7 +2252,7 @@ ${y2(this.state)} ${s.message}
|
|
|
2474
2252
|
return `${t}${import_picocolors2.default.gray(a)} ${n(this.options[this.cursor], "cancelled")}
|
|
2475
2253
|
${import_picocolors2.default.gray(a)}`;
|
|
2476
2254
|
default:
|
|
2477
|
-
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(`
|
|
2478
2256
|
${import_picocolors2.default.cyan(a)} `)}
|
|
2479
2257
|
${import_picocolors2.default.cyan(m2)}
|
|
2480
2258
|
`;
|
|
@@ -2483,8 +2261,8 @@ ${import_picocolors2.default.cyan(m2)}
|
|
|
2483
2261
|
};
|
|
2484
2262
|
var he = (s) => {
|
|
2485
2263
|
const n = (t, i = "inactive") => {
|
|
2486
|
-
const
|
|
2487
|
-
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})`) : ""}`;
|
|
2488
2266
|
};
|
|
2489
2267
|
return new OD({ options: s.options, initialValue: s.initialValue, render() {
|
|
2490
2268
|
const t = `${import_picocolors2.default.gray(a)}
|
|
@@ -2497,7 +2275,7 @@ ${y2(this.state)} ${s.message}
|
|
|
2497
2275
|
return `${t}${import_picocolors2.default.gray(a)} ${n(this.options[0], "cancelled")}
|
|
2498
2276
|
${import_picocolors2.default.gray(a)}`;
|
|
2499
2277
|
default:
|
|
2500
|
-
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(`
|
|
2501
2279
|
${import_picocolors2.default.cyan(a)} `)}
|
|
2502
2280
|
${import_picocolors2.default.cyan(m2)}
|
|
2503
2281
|
`;
|
|
@@ -2506,8 +2284,8 @@ ${import_picocolors2.default.cyan(m2)}
|
|
|
2506
2284
|
};
|
|
2507
2285
|
var pe = (s) => {
|
|
2508
2286
|
const n = (t, i) => {
|
|
2509
|
-
const
|
|
2510
|
-
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)}`;
|
|
2511
2289
|
};
|
|
2512
2290
|
return new wD({ options: s.options, initialValues: s.initialValues, required: s.required ?? true, cursorAt: s.cursorAt, validate(t) {
|
|
2513
2291
|
if (this.required && t.length === 0)
|
|
@@ -2516,25 +2294,25 @@ ${import_picocolors2.default.reset(import_picocolors2.default.dim(`Press ${impor
|
|
|
2516
2294
|
}, render() {
|
|
2517
2295
|
const t = `${import_picocolors2.default.gray(a)}
|
|
2518
2296
|
${y2(this.state)} ${s.message}
|
|
2519
|
-
`, i = (
|
|
2520
|
-
const o = this.value.includes(
|
|
2521
|
-
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");
|
|
2522
2300
|
};
|
|
2523
2301
|
switch (this.state) {
|
|
2524
2302
|
case "submit":
|
|
2525
|
-
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")}`;
|
|
2526
2304
|
case "cancel": {
|
|
2527
|
-
const
|
|
2528
|
-
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}
|
|
2529
2307
|
${import_picocolors2.default.gray(a)}` : ""}`;
|
|
2530
2308
|
}
|
|
2531
2309
|
case "error": {
|
|
2532
|
-
const
|
|
2533
|
-
`).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(`
|
|
2534
2312
|
`);
|
|
2535
2313
|
return `${t + import_picocolors2.default.yellow(a)} ${k2({ options: this.options, cursor: this.cursor, maxItems: s.maxItems, style: i }).join(`
|
|
2536
2314
|
${import_picocolors2.default.yellow(a)} `)}
|
|
2537
|
-
${
|
|
2315
|
+
${r}
|
|
2538
2316
|
`;
|
|
2539
2317
|
}
|
|
2540
2318
|
default:
|
|
@@ -2546,9 +2324,9 @@ ${import_picocolors2.default.cyan(m2)}
|
|
|
2546
2324
|
} }).prompt();
|
|
2547
2325
|
};
|
|
2548
2326
|
var ge = (s) => {
|
|
2549
|
-
const n = (t, i,
|
|
2550
|
-
const
|
|
2551
|
-
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)}`;
|
|
2552
2330
|
};
|
|
2553
2331
|
return new dD({ options: s.options, initialValues: s.initialValues, required: s.required ?? true, cursorAt: s.cursorAt, validate(t) {
|
|
2554
2332
|
if (this.required && t.length === 0)
|
|
@@ -2562,26 +2340,26 @@ ${y2(this.state)} ${s.message}
|
|
|
2562
2340
|
case "submit":
|
|
2563
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(", "))}`;
|
|
2564
2342
|
case "cancel": {
|
|
2565
|
-
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(", "));
|
|
2566
2344
|
return `${t}${import_picocolors2.default.gray(a)} ${i.trim() ? `${i}
|
|
2567
2345
|
${import_picocolors2.default.gray(a)}` : ""}`;
|
|
2568
2346
|
}
|
|
2569
2347
|
case "error": {
|
|
2570
2348
|
const i = this.error.split(`
|
|
2571
|
-
`).map((
|
|
2349
|
+
`).map((r, c) => c === 0 ? `${import_picocolors2.default.yellow(m2)} ${import_picocolors2.default.yellow(r)}` : ` ${r}`).join(`
|
|
2572
2350
|
`);
|
|
2573
|
-
return `${t}${import_picocolors2.default.yellow(a)} ${this.options.map((
|
|
2574
|
-
const
|
|
2575
|
-
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);
|
|
2576
2354
|
}).join(`
|
|
2577
2355
|
${import_picocolors2.default.yellow(a)} `)}
|
|
2578
2356
|
${i}
|
|
2579
2357
|
`;
|
|
2580
2358
|
}
|
|
2581
2359
|
default:
|
|
2582
|
-
return `${t}${import_picocolors2.default.cyan(a)} ${this.options.map((i,
|
|
2583
|
-
const o = this.value.includes(i.value) || i.group === true && this.isGroupSelected(`${i.value}`),
|
|
2584
|
-
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);
|
|
2585
2363
|
}).join(`
|
|
2586
2364
|
${import_picocolors2.default.cyan(a)} `)}
|
|
2587
2365
|
${import_picocolors2.default.cyan(m2)}
|
|
@@ -2593,15 +2371,15 @@ var ye = (s = "", n = "") => {
|
|
|
2593
2371
|
const t = `
|
|
2594
2372
|
${s}
|
|
2595
2373
|
`.split(`
|
|
2596
|
-
`), i = T2(n).length,
|
|
2597
|
-
const $
|
|
2598
|
-
return
|
|
2599
|
-
}, 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(`
|
|
2600
2378
|
`);
|
|
2601
2379
|
process.stdout.write(`${import_picocolors2.default.gray(a)}
|
|
2602
|
-
${import_picocolors2.default.green(S2)} ${import_picocolors2.default.reset(n)} ${import_picocolors2.default.gray(N2.repeat(Math.max(
|
|
2603
|
-
${
|
|
2604
|
-
${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)}
|
|
2605
2383
|
`);
|
|
2606
2384
|
};
|
|
2607
2385
|
var ve = (s = "") => {
|
|
@@ -2622,9 +2400,9 @@ ${import_picocolors2.default.gray(m2)} ${s}
|
|
|
2622
2400
|
var v2 = { message: (s = "", { symbol: n = import_picocolors2.default.gray(a) } = {}) => {
|
|
2623
2401
|
const t = [`${import_picocolors2.default.gray(a)}`];
|
|
2624
2402
|
if (s) {
|
|
2625
|
-
const [i, ...
|
|
2403
|
+
const [i, ...r] = s.split(`
|
|
2626
2404
|
`);
|
|
2627
|
-
t.push(`${n} ${i}`, ...
|
|
2405
|
+
t.push(`${n} ${i}`, ...r.map((c) => `${import_picocolors2.default.gray(a)} ${c}`));
|
|
2628
2406
|
}
|
|
2629
2407
|
process.stdout.write(`${t.join(`
|
|
2630
2408
|
`)}
|
|
@@ -2644,54 +2422,54 @@ var v2 = { message: (s = "", { symbol: n = import_picocolors2.default.gray(a) }
|
|
|
2644
2422
|
} };
|
|
2645
2423
|
var L2 = () => {
|
|
2646
2424
|
const s = E ? ["◒", "◐", "◓", "◑"] : ["•", "o", "O", "0"], n = E ? 80 : 120, t = process.env.CI === "true";
|
|
2647
|
-
let i,
|
|
2648
|
-
const $
|
|
2649
|
-
const
|
|
2650
|
-
|
|
2651
|
-
},
|
|
2652
|
-
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", $);
|
|
2653
2431
|
}, C = () => {
|
|
2654
|
-
process.removeListener("uncaughtExceptionMonitor",
|
|
2655
|
-
},
|
|
2656
|
-
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)
|
|
2657
2435
|
return;
|
|
2658
2436
|
t && process.stdout.write(`
|
|
2659
2437
|
`);
|
|
2660
|
-
const
|
|
2438
|
+
const h = l.split(`
|
|
2661
2439
|
`);
|
|
2662
|
-
process.stdout.write(import_sisteransi2.cursor.move(-999,
|
|
2663
|
-
},
|
|
2664
|
-
|
|
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)}
|
|
2665
2443
|
`);
|
|
2666
|
-
let
|
|
2667
|
-
|
|
2668
|
-
if (t && o ===
|
|
2444
|
+
let g = 0, f = 0;
|
|
2445
|
+
b(), r = setInterval(() => {
|
|
2446
|
+
if (t && o === l)
|
|
2669
2447
|
return;
|
|
2670
|
-
|
|
2671
|
-
const
|
|
2672
|
-
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;
|
|
2673
2451
|
}, n);
|
|
2674
|
-
},
|
|
2675
|
-
|
|
2676
|
-
const
|
|
2677
|
-
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}
|
|
2678
2456
|
`), C(), i();
|
|
2679
2457
|
};
|
|
2680
|
-
return { start:
|
|
2681
|
-
o =
|
|
2458
|
+
return { start: O, stop: P, message: (h = "") => {
|
|
2459
|
+
o = x(h ?? o);
|
|
2682
2460
|
} };
|
|
2683
2461
|
};
|
|
2684
2462
|
var be = async (s, n) => {
|
|
2685
2463
|
const t = {}, i = Object.keys(s);
|
|
2686
|
-
for (const
|
|
2687
|
-
const
|
|
2688
|
-
throw
|
|
2464
|
+
for (const r of i) {
|
|
2465
|
+
const c = s[r], o = await c({ results: t })?.catch((l) => {
|
|
2466
|
+
throw l;
|
|
2689
2467
|
});
|
|
2690
2468
|
if (typeof n?.onCancel == "function" && BD(o)) {
|
|
2691
|
-
t[
|
|
2469
|
+
t[r] = "canceled", n.onCancel({ results: t });
|
|
2692
2470
|
continue;
|
|
2693
2471
|
}
|
|
2694
|
-
t[
|
|
2472
|
+
t[r] = o;
|
|
2695
2473
|
}
|
|
2696
2474
|
return t;
|
|
2697
2475
|
};
|
|
@@ -2778,7 +2556,7 @@ async function probeMcpServer(nodeExecutable, serverPath, cwd, statePath, option
|
|
|
2778
2556
|
const requiredTools = options.requiredTools ?? DEFAULT_PROBE_OPTIONS.requiredTools;
|
|
2779
2557
|
const exerciseWrite = options.exerciseWrite ?? requiredTools.includes("start_request");
|
|
2780
2558
|
try {
|
|
2781
|
-
await new Promise((
|
|
2559
|
+
await new Promise((resolve, reject) => {
|
|
2782
2560
|
const child = spawn(nodeExecutable, [serverPath], {
|
|
2783
2561
|
cwd,
|
|
2784
2562
|
env: {
|
|
@@ -2805,13 +2583,13 @@ async function probeMcpServer(nodeExecutable, serverPath, cwd, statePath, option
|
|
|
2805
2583
|
const onExit = () => {
|
|
2806
2584
|
if (exitTimeout)
|
|
2807
2585
|
clearTimeout(exitTimeout);
|
|
2808
|
-
error ? reject(error) :
|
|
2586
|
+
error ? reject(error) : resolve();
|
|
2809
2587
|
};
|
|
2810
2588
|
child.once("exit", onExit);
|
|
2811
2589
|
child.kill();
|
|
2812
2590
|
exitTimeout = setTimeout(onExit, 500);
|
|
2813
2591
|
} else {
|
|
2814
|
-
error ? reject(error) :
|
|
2592
|
+
error ? reject(error) : resolve();
|
|
2815
2593
|
}
|
|
2816
2594
|
};
|
|
2817
2595
|
const fail = (message) => finish(new Error(`${message}${stderr ? `: ${stderr.trim()}` : ""}`));
|
|
@@ -2889,14 +2667,14 @@ async function probeMcpServer(nodeExecutable, serverPath, cwd, statePath, option
|
|
|
2889
2667
|
params: {
|
|
2890
2668
|
protocolVersion: "2025-03-26",
|
|
2891
2669
|
capabilities: {},
|
|
2892
|
-
clientInfo: { name: "ostacky-installer", version: "0.8.
|
|
2670
|
+
clientInfo: { name: "ostacky-installer", version: "0.8.4" }
|
|
2893
2671
|
}
|
|
2894
2672
|
});
|
|
2895
2673
|
});
|
|
2896
2674
|
} finally {
|
|
2897
|
-
await new Promise((
|
|
2675
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
2898
2676
|
if (statePath) {
|
|
2899
|
-
for (const
|
|
2677
|
+
for (const p of [
|
|
2900
2678
|
statePath,
|
|
2901
2679
|
statePath + ".backup",
|
|
2902
2680
|
statePath + ".lock.pid",
|
|
@@ -2904,13 +2682,13 @@ async function probeMcpServer(nodeExecutable, serverPath, cwd, statePath, option
|
|
|
2904
2682
|
statePath + ".tmp." + process.pid
|
|
2905
2683
|
]) {
|
|
2906
2684
|
try {
|
|
2907
|
-
if (existsSync5(
|
|
2908
|
-
rmSync3(
|
|
2685
|
+
if (existsSync5(p))
|
|
2686
|
+
rmSync3(p, { force: true });
|
|
2909
2687
|
} catch {}
|
|
2910
2688
|
}
|
|
2911
2689
|
try {
|
|
2912
|
-
const { dirname
|
|
2913
|
-
const handoff =
|
|
2690
|
+
const { dirname, join } = await import("path");
|
|
2691
|
+
const handoff = join(dirname(statePath), ".ostacky-handoff-compaction.json");
|
|
2914
2692
|
if (existsSync5(handoff)) {
|
|
2915
2693
|
try {
|
|
2916
2694
|
rmSync3(handoff, { force: true });
|
|
@@ -3247,75 +3025,28 @@ function printPostInstallSteps() {
|
|
|
3247
3025
|
`), "Próximos pasos");
|
|
3248
3026
|
}
|
|
3249
3027
|
async function resolveOpenCodePaths(scope) {
|
|
3250
|
-
if (scope === "local" || scope === "global" || scope === "auto") {
|
|
3251
|
-
const dir2 = getOpenCodeDirForScope(scope);
|
|
3252
|
-
const isGlobalDir = dir2.replace(/\\/g, "/") === getGlobalOpenCodeDir().replace(/\\/g, "/");
|
|
3253
|
-
try {
|
|
3254
|
-
const paths = ensureOpenCodePaths(dir2);
|
|
3255
|
-
if (scope === "global")
|
|
3256
|
-
ye(dir2, "Instalación global");
|
|
3257
|
-
else if (scope === "auto")
|
|
3258
|
-
ye(dir2, `Scope auto → ${isGlobalDir ? "global" : "local"}`);
|
|
3259
|
-
else
|
|
3260
|
-
ye(dir2, "Instalación local");
|
|
3261
|
-
return paths;
|
|
3262
|
-
} catch (e2) {
|
|
3263
|
-
const msg = e2.message ?? "";
|
|
3264
|
-
if ((scope === "global" || scope === "auto" && isGlobalDir) && (msg.includes("EACCES") || msg.toLowerCase().includes("permission"))) {
|
|
3265
|
-
v2.warn(`No se pudo escribir en global (${dir2}): ${msg}. ¿Instalar local?`);
|
|
3266
|
-
const retry = await me({ message: "¿Reintentar como instalación local?" });
|
|
3267
|
-
onCancel(retry);
|
|
3268
|
-
if (retry) {
|
|
3269
|
-
const localDir2 = getOpenCodeDirForScope("local");
|
|
3270
|
-
return ensureOpenCodePaths(localDir2);
|
|
3271
|
-
}
|
|
3272
|
-
}
|
|
3273
|
-
throw e2;
|
|
3274
|
-
}
|
|
3275
|
-
}
|
|
3276
3028
|
const cwd = process.cwd();
|
|
3277
|
-
const
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
message: `¿Instalar en proyecto local (${localDir}) o global (${globalDir})?`,
|
|
3282
|
-
options: [
|
|
3283
|
-
{ value: "local", label: "Local", hint: `${localDir} (recomendado)` },
|
|
3284
|
-
{ value: "global", label: "Global", hint: globalDir }
|
|
3285
|
-
],
|
|
3286
|
-
initialValue: "local"
|
|
3287
|
-
});
|
|
3288
|
-
onCancel(scopeChoice);
|
|
3289
|
-
const chosen = scopeChoice;
|
|
3290
|
-
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
|
+
}
|
|
3291
3033
|
try {
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
const retry = await me({ message: "¿Instalar local en su lugar?" });
|
|
3298
|
-
onCancel(retry);
|
|
3299
|
-
if (retry)
|
|
3300
|
-
return ensureOpenCodePaths(getOpenCodeDirForScope("local", cwd));
|
|
3301
|
-
}
|
|
3302
|
-
throw e2;
|
|
3034
|
+
const paths = ensureOpenCodePaths(dir);
|
|
3035
|
+
ye(dir, "Instalación local");
|
|
3036
|
+
return paths;
|
|
3037
|
+
} catch (e) {
|
|
3038
|
+
throw e;
|
|
3303
3039
|
}
|
|
3304
3040
|
}
|
|
3305
|
-
function isGlobalScope(paths) {
|
|
3306
|
-
const globalDir = getGlobalOpenCodeDir().replace(/\\/g, "/");
|
|
3307
|
-
const root = paths.root.replace(/\\/g, "/");
|
|
3308
|
-
return root === globalDir || root.startsWith(globalDir + "/");
|
|
3309
|
-
}
|
|
3310
3041
|
function getOrphanedItems(manifest, paths) {
|
|
3311
3042
|
const lockfile = readLockfile(paths.root);
|
|
3312
3043
|
if (!lockfile)
|
|
3313
3044
|
return [];
|
|
3314
3045
|
const manifestNames = {
|
|
3315
|
-
agents: new Set(manifest.agents.map((
|
|
3316
|
-
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)),
|
|
3317
3048
|
skills: new Set((manifest.skills ?? []).map((s) => s.name)),
|
|
3318
|
-
mcpServers: new Set((manifest.mcpServers ?? []).map((
|
|
3049
|
+
mcpServers: new Set((manifest.mcpServers ?? []).map((m) => m.name))
|
|
3319
3050
|
};
|
|
3320
3051
|
const orphans = [];
|
|
3321
3052
|
for (const type of ["agents", "commands", "skills", "mcpServers"]) {
|
|
@@ -3392,8 +3123,8 @@ async function doInstallStack(toolsDir, projectRoot) {
|
|
|
3392
3123
|
const resolvedToolsDir = toolsDir ?? join8(resolvedProjectRoot, ".opencode", "tools");
|
|
3393
3124
|
v2.info(`Stack → projectRoot: ${resolvedProjectRoot} | toolsDir: ${resolvedToolsDir}`);
|
|
3394
3125
|
try {
|
|
3395
|
-
const { homedir
|
|
3396
|
-
const home =
|
|
3126
|
+
const { homedir } = await import("os");
|
|
3127
|
+
const home = homedir();
|
|
3397
3128
|
const inHome = resolvedProjectRoot.replace(/\\/g, "/") === home.replace(/\\/g, "/");
|
|
3398
3129
|
if (inHome && !existsSync7(join8(home, ".git"))) {
|
|
3399
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.`);
|
|
@@ -3415,34 +3146,35 @@ async function doInstallStack(toolsDir, projectRoot) {
|
|
|
3415
3146
|
if (!eng.success)
|
|
3416
3147
|
allOk = false;
|
|
3417
3148
|
spin.start("Verificando configuración...");
|
|
3418
|
-
|
|
3419
|
-
const cfg =
|
|
3149
|
+
await Promise.resolve().then(() => init_config());
|
|
3150
|
+
const cfg = patchOpenCodeConfig(resolvedProjectRoot);
|
|
3420
3151
|
spin.stop(cfg.success ? `✓ ${cfg.message}` : `✗ ${cfg.message}`);
|
|
3421
3152
|
if (!cfg.success)
|
|
3422
3153
|
allOk = false;
|
|
3423
3154
|
if (!allOk) {
|
|
3424
3155
|
v2.warn("Algunos componentes requieren atención. Revisá los mensajes de error arriba.");
|
|
3425
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 {}
|
|
3426
3164
|
return allOk;
|
|
3427
3165
|
}
|
|
3428
3166
|
async function doInstallAll(manifest, paths) {
|
|
3429
3167
|
const spin = L2();
|
|
3430
3168
|
let errors = 0;
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
if (!isGlobal) {
|
|
3434
|
-
ensureToolDirs(paths.tools, ["codegraph", "engram"]);
|
|
3435
|
-
} else {
|
|
3436
|
-
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.");
|
|
3437
|
-
v2.info("Para instalar el stack, ejecutá 'npx ostacky install-stack --scope local' dentro de cada proyecto.");
|
|
3438
|
-
}
|
|
3169
|
+
v2.info(`Scope → local | opencodeDir: ${paths.root} | tools: ${paths.tools}`);
|
|
3170
|
+
ensureToolDirs(paths.tools, ["codegraph", "engram"]);
|
|
3439
3171
|
for (const agent of manifest.agents) {
|
|
3440
3172
|
spin.start(`Descargando agente: ${agent.name} (${agent.version})`);
|
|
3441
3173
|
try {
|
|
3442
3174
|
await installAgent(agent, manifest, paths);
|
|
3443
3175
|
spin.stop(`Agente instalado: ${agent.name} (${agent.version})`);
|
|
3444
|
-
} catch (
|
|
3445
|
-
spin.stop(`Error en ${agent.name}: ${
|
|
3176
|
+
} catch (e) {
|
|
3177
|
+
spin.stop(`Error en ${agent.name}: ${e.message}`);
|
|
3446
3178
|
errors++;
|
|
3447
3179
|
}
|
|
3448
3180
|
}
|
|
@@ -3451,8 +3183,8 @@ async function doInstallAll(manifest, paths) {
|
|
|
3451
3183
|
try {
|
|
3452
3184
|
await installCommand(cmd, manifest, paths);
|
|
3453
3185
|
spin.stop(`Command instalado: ${cmd.name} (${cmd.version})`);
|
|
3454
|
-
} catch (
|
|
3455
|
-
spin.stop(`Error en ${cmd.name}: ${
|
|
3186
|
+
} catch (e) {
|
|
3187
|
+
spin.stop(`Error en ${cmd.name}: ${e.message}`);
|
|
3456
3188
|
errors++;
|
|
3457
3189
|
}
|
|
3458
3190
|
}
|
|
@@ -3461,8 +3193,8 @@ async function doInstallAll(manifest, paths) {
|
|
|
3461
3193
|
try {
|
|
3462
3194
|
await installSkill(skill, manifest, paths);
|
|
3463
3195
|
spin.stop(`Skill instalada: ${skill.name} (${skill.version})`);
|
|
3464
|
-
} catch (
|
|
3465
|
-
spin.stop(`Error en ${skill.name}: ${
|
|
3196
|
+
} catch (e) {
|
|
3197
|
+
spin.stop(`Error en ${skill.name}: ${e.message}`);
|
|
3466
3198
|
errors++;
|
|
3467
3199
|
}
|
|
3468
3200
|
}
|
|
@@ -3475,70 +3207,48 @@ async function doInstallAll(manifest, paths) {
|
|
|
3475
3207
|
try {
|
|
3476
3208
|
await installMcpServer(mcp, manifest, paths);
|
|
3477
3209
|
spin.stop(`MCP server instalado: ${mcp.name} (${mcp.version})`);
|
|
3478
|
-
} catch (
|
|
3479
|
-
spin.stop(`Error en ${mcp.name}: ${
|
|
3210
|
+
} catch (e) {
|
|
3211
|
+
spin.stop(`Error en ${mcp.name}: ${e.message}`);
|
|
3480
3212
|
errors++;
|
|
3481
3213
|
}
|
|
3482
3214
|
}
|
|
3483
3215
|
try {
|
|
3484
|
-
const { copyFileSync
|
|
3485
|
-
const { join
|
|
3486
|
-
|
|
3487
|
-
const { findProjectRoot
|
|
3488
|
-
const src =
|
|
3489
|
-
const dest =
|
|
3490
|
-
if (
|
|
3491
|
-
|
|
3492
|
-
|
|
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);
|
|
3493
3225
|
}
|
|
3494
3226
|
for (const legacy of ["ostacky-guard.ts", "ostacky-controller.ts"]) {
|
|
3495
|
-
const lp =
|
|
3496
|
-
if (
|
|
3227
|
+
const lp = join(paths.plugins, legacy);
|
|
3228
|
+
if (existsSync(lp))
|
|
3497
3229
|
try {
|
|
3498
|
-
|
|
3230
|
+
rmSync(lp, { force: true });
|
|
3499
3231
|
} catch {}
|
|
3500
3232
|
}
|
|
3501
|
-
const srcEng =
|
|
3502
|
-
const destEng =
|
|
3503
|
-
if (
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
}
|
|
3507
|
-
if (isGlobal) {
|
|
3508
|
-
try {
|
|
3509
|
-
const projRoot = findProjectRoot3();
|
|
3510
|
-
const localPlugins = join9(projRoot, ".opencode", "plugins");
|
|
3511
|
-
mkdirSync5(localPlugins, { recursive: true });
|
|
3512
|
-
if (existsSync8(src))
|
|
3513
|
-
copyFileSync4(src, join9(localPlugins, "ostacky-plugin.ts"));
|
|
3514
|
-
if (existsSync8(srcEng))
|
|
3515
|
-
copyFileSync4(srcEng, join9(localPlugins, "engram.ts"));
|
|
3516
|
-
for (const legacy of ["ostacky-guard.ts", "ostacky-controller.ts"]) {
|
|
3517
|
-
const lp = join9(localPlugins, legacy);
|
|
3518
|
-
if (existsSync8(lp))
|
|
3519
|
-
try {
|
|
3520
|
-
rmSync5(lp, { force: true });
|
|
3521
|
-
} catch {}
|
|
3522
|
-
}
|
|
3523
|
-
} 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);
|
|
3524
3238
|
}
|
|
3525
3239
|
} catch {}
|
|
3526
3240
|
let stackOk = true;
|
|
3527
3241
|
let missingTools = [];
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
missingTools.push("CodeGraph");
|
|
3539
|
-
if (!existsSync7(engramDir) || !findBinaryInDir(engramDir, "engram"))
|
|
3540
|
-
missingTools.push("Engram");
|
|
3541
|
-
}
|
|
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");
|
|
3542
3252
|
if (missingTools.length > 0) {
|
|
3543
3253
|
v2.warn(`Faltan herramientas del stack: ${missingTools.join(", ")}.
|
|
3544
3254
|
` + "Ejecutá `/install-stack` desde el agente para instalarlas manualmente.");
|
|
@@ -3548,6 +3258,14 @@ async function doInstallAll(manifest, paths) {
|
|
|
3548
3258
|
} else {
|
|
3549
3259
|
v2.warn(`Instalación parcial: ${errors} componente(s) requieren atención.`);
|
|
3550
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 {}
|
|
3551
3269
|
return errors === 0 && stackOk && missingTools.length === 0;
|
|
3552
3270
|
}
|
|
3553
3271
|
|
|
@@ -3555,10 +3273,10 @@ async function doInstallAll(manifest, paths) {
|
|
|
3555
3273
|
init_lockfile();
|
|
3556
3274
|
async function doAddAgent(manifest, paths) {
|
|
3557
3275
|
const lockfile = readLockfile(paths.root);
|
|
3558
|
-
const options = manifest.agents.map((
|
|
3559
|
-
const installed = getInstalledVersion(lockfile, "agents",
|
|
3560
|
-
const hint = installed ? `v${installed} instalado — ${
|
|
3561
|
-
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 };
|
|
3562
3280
|
});
|
|
3563
3281
|
const selected = await pe({
|
|
3564
3282
|
message: "¿Qué agentes deseas instalar?",
|
|
@@ -3568,22 +3286,22 @@ async function doAddAgent(manifest, paths) {
|
|
|
3568
3286
|
onCancel(selected);
|
|
3569
3287
|
const spin = L2();
|
|
3570
3288
|
for (const name of selected) {
|
|
3571
|
-
const item = manifest.agents.find((
|
|
3289
|
+
const item = manifest.agents.find((a) => a.name === name);
|
|
3572
3290
|
spin.start(`Descargando agente: ${name} (${item.version})`);
|
|
3573
3291
|
try {
|
|
3574
3292
|
await installAgent(item, manifest, paths);
|
|
3575
3293
|
spin.stop(`Agente instalado: ${name} (${item.version})`);
|
|
3576
|
-
} catch (
|
|
3577
|
-
spin.stop(`Error: ${
|
|
3294
|
+
} catch (e) {
|
|
3295
|
+
spin.stop(`Error: ${e.message}`);
|
|
3578
3296
|
}
|
|
3579
3297
|
}
|
|
3580
3298
|
}
|
|
3581
3299
|
async function doAddCommand(manifest, paths) {
|
|
3582
3300
|
const lockfile = readLockfile(paths.root);
|
|
3583
|
-
const options = manifest.commands.map((
|
|
3584
|
-
const installed = getInstalledVersion(lockfile, "commands",
|
|
3585
|
-
const hint = installed ? `v${installed} instalado — ${
|
|
3586
|
-
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 };
|
|
3587
3305
|
});
|
|
3588
3306
|
const selected = await pe({
|
|
3589
3307
|
message: "¿Qué commands deseas instalar?",
|
|
@@ -3593,13 +3311,13 @@ async function doAddCommand(manifest, paths) {
|
|
|
3593
3311
|
onCancel(selected);
|
|
3594
3312
|
const spin = L2();
|
|
3595
3313
|
for (const name of selected) {
|
|
3596
|
-
const item = manifest.commands.find((
|
|
3314
|
+
const item = manifest.commands.find((c) => c.name === name);
|
|
3597
3315
|
spin.start(`Descargando command: ${name} (${item.version})`);
|
|
3598
3316
|
try {
|
|
3599
3317
|
await installCommand(item, manifest, paths);
|
|
3600
3318
|
spin.stop(`Command instalado: ${name} (${item.version})`);
|
|
3601
|
-
} catch (
|
|
3602
|
-
spin.stop(`Error: ${
|
|
3319
|
+
} catch (e) {
|
|
3320
|
+
spin.stop(`Error: ${e.message}`);
|
|
3603
3321
|
}
|
|
3604
3322
|
}
|
|
3605
3323
|
}
|
|
@@ -3627,17 +3345,17 @@ async function doAddSkill(manifest, paths) {
|
|
|
3627
3345
|
try {
|
|
3628
3346
|
await installSkill(item, manifest, paths);
|
|
3629
3347
|
spin.stop(`Skill instalada: ${name} (${item.version})`);
|
|
3630
|
-
} catch (
|
|
3631
|
-
spin.stop(`Error: ${
|
|
3348
|
+
} catch (e) {
|
|
3349
|
+
spin.stop(`Error: ${e.message}`);
|
|
3632
3350
|
}
|
|
3633
3351
|
}
|
|
3634
3352
|
}
|
|
3635
3353
|
async function doAddMcp(manifest, paths) {
|
|
3636
3354
|
const lockfile = readLockfile(paths.root);
|
|
3637
|
-
const options = (manifest.mcpServers ?? []).map((
|
|
3638
|
-
const installed = getInstalledVersion(lockfile, "mcpServers",
|
|
3639
|
-
const hint = installed ? `v${installed} instalado — ${
|
|
3640
|
-
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 };
|
|
3641
3359
|
});
|
|
3642
3360
|
if (options.length === 0) {
|
|
3643
3361
|
v2.info("No hay MCP servers disponibles en el manifest.");
|
|
@@ -3651,15 +3369,15 @@ async function doAddMcp(manifest, paths) {
|
|
|
3651
3369
|
onCancel(selected);
|
|
3652
3370
|
const spin = L2();
|
|
3653
3371
|
for (const name of selected) {
|
|
3654
|
-
const item = manifest.mcpServers?.find((
|
|
3372
|
+
const item = manifest.mcpServers?.find((m) => m.name === name);
|
|
3655
3373
|
if (!item)
|
|
3656
3374
|
continue;
|
|
3657
3375
|
spin.start(`Instalando MCP server: ${name} (${item.version})`);
|
|
3658
3376
|
try {
|
|
3659
3377
|
await installMcpServer(item, manifest, paths);
|
|
3660
3378
|
spin.stop(`MCP server instalado: ${name} (${item.version})`);
|
|
3661
|
-
} catch (
|
|
3662
|
-
spin.stop(`Error: ${
|
|
3379
|
+
} catch (e) {
|
|
3380
|
+
spin.stop(`Error: ${e.message}`);
|
|
3663
3381
|
}
|
|
3664
3382
|
}
|
|
3665
3383
|
}
|
|
@@ -3726,8 +3444,8 @@ async function doUpdate(manifest, paths) {
|
|
|
3726
3444
|
}
|
|
3727
3445
|
spin.stop(`Actualizado: ${item.name} (${item.version})`);
|
|
3728
3446
|
updated++;
|
|
3729
|
-
} catch (
|
|
3730
|
-
spin.stop(`Error en ${item.name}: ${
|
|
3447
|
+
} catch (e) {
|
|
3448
|
+
spin.stop(`Error en ${item.name}: ${e.message}`);
|
|
3731
3449
|
}
|
|
3732
3450
|
}
|
|
3733
3451
|
v2.success(`${updated} recurso(s) actualizado(s).`);
|
|
@@ -3941,10 +3659,10 @@ async function doUninstall(paths) {
|
|
|
3941
3659
|
}
|
|
3942
3660
|
async function doUninstallAgentByName(name, paths) {
|
|
3943
3661
|
try {
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
} catch (
|
|
3947
|
-
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}`);
|
|
3948
3666
|
return;
|
|
3949
3667
|
}
|
|
3950
3668
|
const lockfile = readLockfile(paths.root);
|
|
@@ -3969,10 +3687,10 @@ async function doUninstallAgentByName(name, paths) {
|
|
|
3969
3687
|
}
|
|
3970
3688
|
async function doUninstallCommandByName(name, paths) {
|
|
3971
3689
|
try {
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
} catch (
|
|
3975
|
-
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}`);
|
|
3976
3694
|
return;
|
|
3977
3695
|
}
|
|
3978
3696
|
const lockfile = readLockfile(paths.root);
|
|
@@ -3997,10 +3715,10 @@ async function doUninstallCommandByName(name, paths) {
|
|
|
3997
3715
|
}
|
|
3998
3716
|
async function doUninstallSkillByName(name, paths) {
|
|
3999
3717
|
try {
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
} catch (
|
|
4003
|
-
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}`);
|
|
4004
3722
|
return;
|
|
4005
3723
|
}
|
|
4006
3724
|
const lockfile = readLockfile(paths.root);
|
|
@@ -4025,10 +3743,10 @@ async function doUninstallSkillByName(name, paths) {
|
|
|
4025
3743
|
}
|
|
4026
3744
|
async function doUninstallMcpByName(name, paths) {
|
|
4027
3745
|
try {
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
} catch (
|
|
4031
|
-
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}`);
|
|
4032
3750
|
return;
|
|
4033
3751
|
}
|
|
4034
3752
|
const lockfile = readLockfile(paths.root);
|
|
@@ -4121,9 +3839,9 @@ ${info.note}` : ""}`, `Instalación requerida (${platform})`);
|
|
|
4121
3839
|
}
|
|
4122
3840
|
}
|
|
4123
3841
|
spin.stop("Instalación ejecutada, verificando...");
|
|
4124
|
-
} catch (
|
|
3842
|
+
} catch (e) {
|
|
4125
3843
|
spin.stop("Fallo la instalación automática.");
|
|
4126
|
-
const msg =
|
|
3844
|
+
const msg = e.message ?? String(e);
|
|
4127
3845
|
v2.error(`No se pudo instalar OpenCode automáticamente: ${msg}`);
|
|
4128
3846
|
if (platform === "win32") {
|
|
4129
3847
|
ye([
|
|
@@ -4217,12 +3935,6 @@ async function runInteractiveMenu(scope) {
|
|
|
4217
3935
|
fe("Listo.");
|
|
4218
3936
|
break;
|
|
4219
3937
|
case "stack": {
|
|
4220
|
-
if (isGlobalScope(paths)) {
|
|
4221
|
-
v2.error("install-stack requiere scope local; el stack vive en <proyecto>/.opencode/tools");
|
|
4222
|
-
v2.info(`Elegiste global (${paths.root}) — el stack debe instalarse por proyecto local.`);
|
|
4223
|
-
fe("Cancelado.");
|
|
4224
|
-
break;
|
|
4225
|
-
}
|
|
4226
3938
|
ensureToolDirs(paths.tools, ["codegraph", "engram"]);
|
|
4227
3939
|
const stackOk = await doInstallStack(paths.tools, dirname7(paths.root));
|
|
4228
3940
|
if (!stackOk)
|
|
@@ -4313,25 +4025,12 @@ async function runAddMcpCommand(scope) {
|
|
|
4313
4025
|
}
|
|
4314
4026
|
async function runInstallStackCommand(scope) {
|
|
4315
4027
|
await ensureOpencodeInstalled();
|
|
4316
|
-
if (scope === "global") {
|
|
4317
|
-
v2.error("install-stack requiere scope local; el stack vive en <proyecto>/.opencode/tools");
|
|
4318
|
-
fe("Usá: npx ostacky install-stack --scope local");
|
|
4319
|
-
process.exitCode = 1;
|
|
4320
|
-
return;
|
|
4321
|
-
}
|
|
4322
4028
|
we(" OpenCode Installer — Stack ");
|
|
4323
4029
|
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
4324
4030
|
if (!paths) {
|
|
4325
4031
|
fe("Cancelado.");
|
|
4326
4032
|
return;
|
|
4327
4033
|
}
|
|
4328
|
-
if (isGlobalScope(paths)) {
|
|
4329
|
-
v2.error("install-stack requiere scope local; el stack vive en <proyecto>/.opencode/tools");
|
|
4330
|
-
v2.info(`Scope resuelto a global (${paths.root}) — el stack debe instalarse por proyecto local.`);
|
|
4331
|
-
fe("Cancelado.");
|
|
4332
|
-
process.exitCode = 1;
|
|
4333
|
-
return;
|
|
4334
|
-
}
|
|
4335
4034
|
ensureToolDirs(paths.tools, ["codegraph", "engram"]);
|
|
4336
4035
|
const stackOk = await doInstallStack(paths.tools, dirname7(paths.root));
|
|
4337
4036
|
if (!stackOk)
|
|
@@ -4353,8 +4052,8 @@ async function runUninstallStackCommand(scope) {
|
|
|
4353
4052
|
fe("Cancelado.");
|
|
4354
4053
|
return;
|
|
4355
4054
|
}
|
|
4356
|
-
|
|
4357
|
-
const result =
|
|
4055
|
+
await Promise.resolve().then(() => init_stack());
|
|
4056
|
+
const result = uninstallStackConfig(paths);
|
|
4358
4057
|
if (result.success) {
|
|
4359
4058
|
v2.success(result.message);
|
|
4360
4059
|
} else {
|
|
@@ -4394,8 +4093,8 @@ async function runUninstallAgentCommand(name, scope) {
|
|
|
4394
4093
|
if (name) {
|
|
4395
4094
|
await doUninstallAgentByName(name, paths);
|
|
4396
4095
|
} else {
|
|
4397
|
-
|
|
4398
|
-
const lockfile =
|
|
4096
|
+
await Promise.resolve().then(() => init_lockfile());
|
|
4097
|
+
const lockfile = readLockfile(paths.root);
|
|
4399
4098
|
if (lockfile && Object.keys(lockfile.agents).length > 0) {
|
|
4400
4099
|
const prompts = exports_dist;
|
|
4401
4100
|
const installed = Object.keys(lockfile.agents);
|
|
@@ -4429,8 +4128,8 @@ async function runUninstallCommandCommand(name, scope) {
|
|
|
4429
4128
|
if (name) {
|
|
4430
4129
|
await doUninstallCommandByName(name, paths);
|
|
4431
4130
|
} else {
|
|
4432
|
-
|
|
4433
|
-
const lockfile =
|
|
4131
|
+
await Promise.resolve().then(() => init_lockfile());
|
|
4132
|
+
const lockfile = readLockfile(paths.root);
|
|
4434
4133
|
if (lockfile && Object.keys(lockfile.commands).length > 0) {
|
|
4435
4134
|
const prompts = exports_dist;
|
|
4436
4135
|
const installed = Object.keys(lockfile.commands);
|
|
@@ -4464,8 +4163,8 @@ async function runUninstallSkillCommand(name, scope) {
|
|
|
4464
4163
|
if (name) {
|
|
4465
4164
|
await doUninstallSkillByName(name, paths);
|
|
4466
4165
|
} else {
|
|
4467
|
-
|
|
4468
|
-
const lockfile =
|
|
4166
|
+
await Promise.resolve().then(() => init_lockfile());
|
|
4167
|
+
const lockfile = readLockfile(paths.root);
|
|
4469
4168
|
if (lockfile && lockfile.skills && Object.keys(lockfile.skills).length > 0) {
|
|
4470
4169
|
const prompts = exports_dist;
|
|
4471
4170
|
const installed = Object.keys(lockfile.skills);
|
|
@@ -4499,8 +4198,8 @@ async function runUninstallMcpCommand(name, scope) {
|
|
|
4499
4198
|
if (name) {
|
|
4500
4199
|
await doUninstallMcpByName(name, paths);
|
|
4501
4200
|
} else {
|
|
4502
|
-
|
|
4503
|
-
const lockfile =
|
|
4201
|
+
await Promise.resolve().then(() => init_lockfile());
|
|
4202
|
+
const lockfile = readLockfile(paths.root);
|
|
4504
4203
|
if (lockfile && lockfile.mcpServers && Object.keys(lockfile.mcpServers).length > 0) {
|
|
4505
4204
|
const prompts = exports_dist;
|
|
4506
4205
|
const installed = Object.keys(lockfile.mcpServers);
|
|
@@ -4533,43 +4232,49 @@ var HELP = `
|
|
|
4533
4232
|
ostacky — Instalador de agentes, comandos, skills y MCPs para OpenCode
|
|
4534
4233
|
|
|
4535
4234
|
Uso:
|
|
4536
|
-
npx ostacky [--scope local
|
|
4537
|
-
npx ostacky install [--scope local
|
|
4538
|
-
npx ostacky add agent [--scope local
|
|
4539
|
-
npx ostacky add command [--scope
|
|
4540
|
-
npx ostacky add skill [--scope
|
|
4541
|
-
npx ostacky add mcp [--scope
|
|
4542
|
-
npx ostacky install-stack [--scope local
|
|
4543
|
-
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
|
|
4544
4243
|
npx ostacky doctor Diagnostica locks, tools, state health
|
|
4545
4244
|
npx ostacky status [--json] Muestra estado del controller sin MCP
|
|
4546
|
-
npx ostacky update [--scope
|
|
4547
|
-
npx ostacky uninstall [--scope
|
|
4548
|
-
npx ostacky uninstall agent [--scope
|
|
4549
|
-
npx ostacky uninstall command [--scope
|
|
4550
|
-
npx ostacky uninstall skill [--scope
|
|
4551
|
-
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)
|
|
4552
4251
|
npx ostacky --help Mostrar esta ayuda
|
|
4553
4252
|
npx ostacky --version Mostrar versión
|
|
4554
4253
|
|
|
4555
4254
|
Scope:
|
|
4556
|
-
--scope local Escribe en <proyecto>/.opencode (
|
|
4557
|
-
|
|
4558
|
-
--scope auto Elige local si existe .opencode o .git, si no global
|
|
4559
|
-
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)
|
|
4560
4257
|
`.trim();
|
|
4561
|
-
function
|
|
4258
|
+
function parseScopeArg(argv = process.argv) {
|
|
4562
4259
|
for (let i = 0;i < argv.length; i++) {
|
|
4563
4260
|
const arg = argv[i];
|
|
4564
4261
|
if (arg === "--scope" && i + 1 < argv.length) {
|
|
4565
|
-
const
|
|
4566
|
-
if (
|
|
4567
|
-
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
|
+
}
|
|
4568
4269
|
}
|
|
4569
4270
|
if (arg.startsWith("--scope=")) {
|
|
4570
|
-
const
|
|
4571
|
-
if (
|
|
4572
|
-
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
|
+
}
|
|
4573
4278
|
}
|
|
4574
4279
|
}
|
|
4575
4280
|
return null;
|
|
@@ -4588,7 +4293,7 @@ function withoutScopeArgs(argv) {
|
|
|
4588
4293
|
}
|
|
4589
4294
|
return out;
|
|
4590
4295
|
}
|
|
4591
|
-
var scope =
|
|
4296
|
+
var scope = parseScopeArg();
|
|
4592
4297
|
var argvNoScope = withoutScopeArgs(process.argv);
|
|
4593
4298
|
var [, , cmd, subcmd] = argvNoScope;
|
|
4594
4299
|
async function runDoctorCommand() {
|
|
@@ -4616,7 +4321,7 @@ async function runDoctorCommand() {
|
|
|
4616
4321
|
join10(opencodeDir, "plugins", "ostacky-controller.ts"),
|
|
4617
4322
|
join10(cwd, ".opencode", "plugins", "ostacky-controller.ts")
|
|
4618
4323
|
];
|
|
4619
|
-
const pluginActive = pluginPaths.some((
|
|
4324
|
+
const pluginActive = pluginPaths.some((p) => existsSync8(p));
|
|
4620
4325
|
try {
|
|
4621
4326
|
if (!existsSync8(statePath)) {
|
|
4622
4327
|
if (pluginActive)
|
|
@@ -4683,8 +4388,8 @@ async function runDoctorCommand() {
|
|
|
4683
4388
|
}
|
|
4684
4389
|
}
|
|
4685
4390
|
}
|
|
4686
|
-
} catch (
|
|
4687
|
-
check(`controller: ${
|
|
4391
|
+
} catch (e) {
|
|
4392
|
+
check(`controller: ${e.message}`, false);
|
|
4688
4393
|
}
|
|
4689
4394
|
try {
|
|
4690
4395
|
const lockPid = join10(opencodeDir, "ostacky-state.json.lock.pid");
|
|
@@ -4713,13 +4418,13 @@ async function runDoctorCommand() {
|
|
|
4713
4418
|
}
|
|
4714
4419
|
const tools = ["codegraph", "engram"];
|
|
4715
4420
|
for (const t of tools) {
|
|
4716
|
-
const
|
|
4717
|
-
const pExe =
|
|
4718
|
-
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);
|
|
4719
4424
|
}
|
|
4720
4425
|
try {
|
|
4721
4426
|
const manifest = JSON.parse(readFileSync6(join10(cwd, "manifest.json"), "utf-8"));
|
|
4722
|
-
const expected = manifest.mcpServers?.find((
|
|
4427
|
+
const expected = manifest.mcpServers?.find((x) => x.name === "ostacky-controller")?.sha256;
|
|
4723
4428
|
if (expected) {
|
|
4724
4429
|
const actual = computeTreeHash(join10(cwd, "assets", "mcp", "ostacky-controller"));
|
|
4725
4430
|
check(`manifest hash: ${expected.slice(0, 8)} vs actual ${actual.slice(0, 8)}`, expected === actual);
|
|
@@ -4742,9 +4447,9 @@ async function runDoctorCommand() {
|
|
|
4742
4447
|
} else {
|
|
4743
4448
|
const files = readdirSync3(cacheDir);
|
|
4744
4449
|
let total = 0;
|
|
4745
|
-
for (const
|
|
4450
|
+
for (const f of files) {
|
|
4746
4451
|
try {
|
|
4747
|
-
total += statSync3(join10(cacheDir,
|
|
4452
|
+
total += statSync3(join10(cacheDir, f)).size;
|
|
4748
4453
|
} catch {}
|
|
4749
4454
|
}
|
|
4750
4455
|
const totalMB = (total / 1048576).toFixed(2);
|
|
@@ -4761,9 +4466,86 @@ async function runDoctorCommand() {
|
|
|
4761
4466
|
}
|
|
4762
4467
|
} catch {}
|
|
4763
4468
|
}
|
|
4764
|
-
} catch (
|
|
4765
|
-
console.log(`⚠️ cache: check failed ${
|
|
4469
|
+
} catch (e) {
|
|
4470
|
+
console.log(`⚠️ cache: check failed ${e.message}`);
|
|
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}`);
|
|
4766
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 {}
|
|
4767
4549
|
try {
|
|
4768
4550
|
const secPath = join10(cwd, "src", "security.ts");
|
|
4769
4551
|
if (!existsSync8(secPath)) {
|
|
@@ -4810,14 +4592,20 @@ async function runStatusCommand(args) {
|
|
|
4810
4592
|
const degraded = parsed.degraded ? " degraded" : "";
|
|
4811
4593
|
const lastHandoff = parsed.lastHandoff ? ` lastHandoff: ${parsed.lastHandoff.summary?.slice(0, 60)}` : "";
|
|
4812
4594
|
if (isJson) {
|
|
4813
|
-
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));
|
|
4814
4602
|
} else {
|
|
4815
4603
|
console.log(`${parsed.state} rev ${parsed.revision}${degraded} tasks ${completed}/${expected}${lastHandoff}`);
|
|
4816
4604
|
if (parsed.lastProposal)
|
|
4817
4605
|
console.log(`lastProposal: ${parsed.lastProposal.summary} shownToUser=${parsed.lastProposal.shownToUser}`);
|
|
4818
4606
|
}
|
|
4819
|
-
} catch (
|
|
4820
|
-
console.log(`Error reading state: ${
|
|
4607
|
+
} catch (e) {
|
|
4608
|
+
console.log(`Error reading state: ${e.message}`);
|
|
4821
4609
|
}
|
|
4822
4610
|
}
|
|
4823
4611
|
async function main() {
|
|
@@ -4830,11 +4618,6 @@ async function main() {
|
|
|
4830
4618
|
await runInstallCommand(scope);
|
|
4831
4619
|
break;
|
|
4832
4620
|
case "install-stack":
|
|
4833
|
-
if (scope === "global") {
|
|
4834
|
-
console.error("Error: install-stack requiere scope local; el stack vive en <proyecto>/.opencode/tools");
|
|
4835
|
-
console.error("Sugerencia: ejecutá 'npx ostacky install-stack --scope local' dentro de cada proyecto.");
|
|
4836
|
-
process.exit(1);
|
|
4837
|
-
}
|
|
4838
4621
|
await runInstallStackCommand(scope);
|
|
4839
4622
|
break;
|
|
4840
4623
|
case "uninstall-stack":
|
|
@@ -4899,7 +4682,7 @@ async function main() {
|
|
|
4899
4682
|
await runInteractiveMenu(scope);
|
|
4900
4683
|
}
|
|
4901
4684
|
}
|
|
4902
|
-
main().catch((
|
|
4903
|
-
console.error("Error:",
|
|
4685
|
+
main().catch((e) => {
|
|
4686
|
+
console.error("Error:", e.message);
|
|
4904
4687
|
process.exit(1);
|
|
4905
4688
|
});
|