ostacky 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +38 -26
- package/assets/agents/ostacky.md +65 -594
- package/assets/commands/install-stack.md +53 -19
- package/assets/commands/opsx-sync.md +25 -0
- package/assets/skills/brainstorming/SKILL.md +164 -0
- package/assets/skills/brainstorming/SKILL.md.placeholder +36 -0
- package/assets/skills/dispatching-parallel-agents/SKILL.md +182 -0
- package/assets/skills/dispatching-parallel-agents/SKILL.md.placeholder +36 -0
- package/assets/skills/openspec-apply-change/SKILL.md +156 -0
- package/assets/skills/openspec-apply-change/SKILL.md.placeholder +36 -0
- package/assets/skills/openspec-archive-change/SKILL.md +114 -0
- package/assets/skills/openspec-archive-change/SKILL.md.placeholder +36 -0
- package/assets/skills/openspec-explore/SKILL.md +288 -0
- package/assets/skills/openspec-explore/SKILL.md.placeholder +36 -0
- package/assets/skills/openspec-propose/SKILL.md +110 -0
- package/assets/skills/openspec-propose/SKILL.md.placeholder +36 -0
- package/assets/skills/review/SKILL.md +214 -0
- package/assets/skills/review/SKILL.md.placeholder +36 -0
- package/assets/skills/subagent-driven-development/SKILL.md +279 -0
- package/assets/skills/subagent-driven-development/SKILL.md.placeholder +36 -0
- package/assets/skills/tdd/SKILL.md +371 -0
- package/assets/skills/tdd/SKILL.md.placeholder +36 -0
- package/assets/skills/writing-plans/SKILL.md +152 -0
- package/assets/skills/writing-plans/SKILL.md.placeholder +36 -0
- package/dist/cli.js +389 -47
- package/manifest.json +101 -22
- package/package.json +39 -38
package/dist/cli.js
CHANGED
|
@@ -161,7 +161,7 @@ var require_picocolors = __commonJS((exports, module) => {
|
|
|
161
161
|
// package.json
|
|
162
162
|
var package_default = {
|
|
163
163
|
name: "ostacky",
|
|
164
|
-
version: "0.0.
|
|
164
|
+
version: "0.0.3",
|
|
165
165
|
description: "Instalador interactivo de agentes y comandos para OpenCode",
|
|
166
166
|
type: "module",
|
|
167
167
|
bin: {
|
|
@@ -177,7 +177,8 @@ var package_default = {
|
|
|
177
177
|
files: [
|
|
178
178
|
"dist/",
|
|
179
179
|
"assets/",
|
|
180
|
-
"manifest.json"
|
|
180
|
+
"manifest.json",
|
|
181
|
+
"LICENSE"
|
|
181
182
|
],
|
|
182
183
|
keywords: [
|
|
183
184
|
"opencode",
|
|
@@ -873,18 +874,18 @@ var L2 = () => {
|
|
|
873
874
|
};
|
|
874
875
|
|
|
875
876
|
// src/prompts.ts
|
|
876
|
-
import { join as
|
|
877
|
+
import { join as join5 } from "path";
|
|
877
878
|
// manifest.json
|
|
878
879
|
var manifest_default = {
|
|
879
|
-
version: "0.0.
|
|
880
|
+
version: "0.0.3",
|
|
880
881
|
repo: "JaimeHoracio/Ostacky",
|
|
881
|
-
tag: "v0.0.
|
|
882
|
+
tag: "v0.0.3",
|
|
882
883
|
agents: [
|
|
883
884
|
{
|
|
884
885
|
name: "ostacky",
|
|
885
886
|
file: "assets/agents/ostacky.md",
|
|
886
887
|
description: "Agente principal de desarrollo con OpenCode",
|
|
887
|
-
version: "0.0.
|
|
888
|
+
version: "0.0.3",
|
|
888
889
|
sha256: null
|
|
889
890
|
}
|
|
890
891
|
],
|
|
@@ -893,12 +894,95 @@ var manifest_default = {
|
|
|
893
894
|
name: "install-stack",
|
|
894
895
|
file: "assets/commands/install-stack.md",
|
|
895
896
|
description: "Instala el stack tecnológico del proyecto",
|
|
896
|
-
version: "0.0.
|
|
897
|
+
version: "0.0.3",
|
|
898
|
+
sha256: null
|
|
899
|
+
},
|
|
900
|
+
{
|
|
901
|
+
name: "opsx-sync",
|
|
902
|
+
file: "assets/commands/opsx-sync.md",
|
|
903
|
+
description: "Sincroniza delta specs del change activo (wrapper de openspec update)",
|
|
904
|
+
version: "0.0.3",
|
|
897
905
|
sha256: null
|
|
898
906
|
}
|
|
907
|
+
],
|
|
908
|
+
skills: [
|
|
909
|
+
{
|
|
910
|
+
name: "brainstorming",
|
|
911
|
+
file: "assets/skills/brainstorming/SKILL.md",
|
|
912
|
+
description: "Skill de descubrimiento y exploración (Superpowers)",
|
|
913
|
+
version: "0.0.3",
|
|
914
|
+
sha256: "22be76380b086c4ac6d38b8b124f6f7df47c32a67237a6ceaf8c7fd19aa524b0"
|
|
915
|
+
},
|
|
916
|
+
{
|
|
917
|
+
name: "writing-plans",
|
|
918
|
+
file: "assets/skills/writing-plans/SKILL.md",
|
|
919
|
+
description: "Skill de planificación de implementación (Superpowers)",
|
|
920
|
+
version: "0.0.3",
|
|
921
|
+
sha256: "4d56ce833af79c628e72cd95b06d13597131a09f9d98aae0a42c6a98a8ee1b95"
|
|
922
|
+
},
|
|
923
|
+
{
|
|
924
|
+
name: "tdd",
|
|
925
|
+
file: "assets/skills/tdd/SKILL.md",
|
|
926
|
+
description: "Skill de test-driven development (Superpowers)",
|
|
927
|
+
version: "0.0.3",
|
|
928
|
+
sha256: "b5cd98b3c122162b5a5eac3c78858a64ba165bf4e8c251d8a804a593c0c72312"
|
|
929
|
+
},
|
|
930
|
+
{
|
|
931
|
+
name: "subagent-driven-development",
|
|
932
|
+
file: "assets/skills/subagent-driven-development/SKILL.md",
|
|
933
|
+
description: "Skill de ejecución con subagentes (Superpowers)",
|
|
934
|
+
version: "0.0.3",
|
|
935
|
+
sha256: "7534b60788e05644831347194b1f99bcb41e2eda130994cfddafee3146ba63bd"
|
|
936
|
+
},
|
|
937
|
+
{
|
|
938
|
+
name: "dispatching-parallel-agents",
|
|
939
|
+
file: "assets/skills/dispatching-parallel-agents/SKILL.md",
|
|
940
|
+
description: "Skill de dispatch paralelo de agentes (Superpowers)",
|
|
941
|
+
version: "0.0.3",
|
|
942
|
+
sha256: "b7e99d62c5d2ade21a3b6cdc94a7f90772e23fe3294ca600386f192f6181f6b2"
|
|
943
|
+
},
|
|
944
|
+
{
|
|
945
|
+
name: "review",
|
|
946
|
+
file: "assets/skills/review/SKILL.md",
|
|
947
|
+
description: "Skill de revisión de código (Superpowers)",
|
|
948
|
+
version: "0.0.3",
|
|
949
|
+
sha256: "af8fd392835967111e865f170d90d3dd9b8266b517ffb7d551dffda704500a90"
|
|
950
|
+
},
|
|
951
|
+
{
|
|
952
|
+
name: "openspec-explore",
|
|
953
|
+
file: "assets/skills/openspec-explore/SKILL.md",
|
|
954
|
+
description: "Skill de exploración pre-spec (OpenSpec)",
|
|
955
|
+
version: "0.0.3",
|
|
956
|
+
sha256: "c3eaff7d64b7e5e757b120dd231d2e6b56e9f440055dc9ab5431b466d6244bd7"
|
|
957
|
+
},
|
|
958
|
+
{
|
|
959
|
+
name: "openspec-propose",
|
|
960
|
+
file: "assets/skills/openspec-propose/SKILL.md",
|
|
961
|
+
description: "Skill de generación de proposal (OpenSpec)",
|
|
962
|
+
version: "0.0.3",
|
|
963
|
+
sha256: "28c59bdce6e9af0ab07102b2e6d0b47a0f0dc2f1bfb8321576d47f6c240e778b"
|
|
964
|
+
},
|
|
965
|
+
{
|
|
966
|
+
name: "openspec-apply-change",
|
|
967
|
+
file: "assets/skills/openspec-apply-change/SKILL.md",
|
|
968
|
+
description: "Skill de aplicación de change (OpenSpec)",
|
|
969
|
+
version: "0.0.3",
|
|
970
|
+
sha256: "8fa8182e40fd92cefbbcc1e303d9aa9c02e2b7f3f2c0625cb61fb916e4fc5760"
|
|
971
|
+
},
|
|
972
|
+
{
|
|
973
|
+
name: "openspec-archive-change",
|
|
974
|
+
file: "assets/skills/openspec-archive-change/SKILL.md",
|
|
975
|
+
description: "Skill de archivo de change (OpenSpec)",
|
|
976
|
+
version: "0.0.3",
|
|
977
|
+
sha256: "564b8dca28084c75ca192137113e9186c4fb31d2de0e26c081fd67c37f7d4a56"
|
|
978
|
+
}
|
|
899
979
|
]
|
|
900
980
|
};
|
|
901
981
|
|
|
982
|
+
// src/github.ts
|
|
983
|
+
import { fileURLToPath } from "url";
|
|
984
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
985
|
+
|
|
902
986
|
// src/cache.ts
|
|
903
987
|
import { homedir } from "os";
|
|
904
988
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -955,6 +1039,14 @@ function putCache(repo, tag, filePath, content) {
|
|
|
955
1039
|
// src/github.ts
|
|
956
1040
|
var GITHUB_RAW = "https://raw.githubusercontent.com";
|
|
957
1041
|
var GITHUB_API = "https://api.github.com";
|
|
1042
|
+
var __filename2 = fileURLToPath(import.meta.url);
|
|
1043
|
+
var __dirname2 = dirname2(__filename2);
|
|
1044
|
+
var PACKAGE_ROOT = join2(__dirname2, "..");
|
|
1045
|
+
var BUNDLED_SKILLS_DIR = join2(PACKAGE_ROOT, "assets", "skills");
|
|
1046
|
+
function getBundledSkillPath(name) {
|
|
1047
|
+
validateFilePath(name);
|
|
1048
|
+
return join2(BUNDLED_SKILLS_DIR, name);
|
|
1049
|
+
}
|
|
958
1050
|
function getRawUrl(repo, tag, path) {
|
|
959
1051
|
return `${GITHUB_RAW}/${repo}/${tag}/${path}`;
|
|
960
1052
|
}
|
|
@@ -996,7 +1088,7 @@ async function fetchLatestManifest() {
|
|
|
996
1088
|
}
|
|
997
1089
|
async function downloadFile(manifest, filePath) {
|
|
998
1090
|
validateFilePath(filePath);
|
|
999
|
-
const item = [...manifest.agents, ...manifest.commands].find((i) => i.file === filePath);
|
|
1091
|
+
const item = [...manifest.agents, ...manifest.commands, ...manifest.skills ?? []].find((i) => i.file === filePath);
|
|
1000
1092
|
const expectedHash = item?.sha256 ?? null;
|
|
1001
1093
|
const cached = getCached(manifest.repo, manifest.tag, filePath, expectedHash);
|
|
1002
1094
|
if (cached !== null)
|
|
@@ -1015,15 +1107,26 @@ async function downloadFile(manifest, filePath) {
|
|
|
1015
1107
|
}
|
|
1016
1108
|
|
|
1017
1109
|
// src/installer.ts
|
|
1018
|
-
import {
|
|
1019
|
-
|
|
1110
|
+
import {
|
|
1111
|
+
existsSync as existsSync3,
|
|
1112
|
+
mkdirSync as mkdirSync2,
|
|
1113
|
+
writeFileSync as writeFileSync3,
|
|
1114
|
+
unlinkSync,
|
|
1115
|
+
readdirSync,
|
|
1116
|
+
statSync,
|
|
1117
|
+
copyFileSync,
|
|
1118
|
+
readFileSync as readFileSync3,
|
|
1119
|
+
rmSync
|
|
1120
|
+
} from "fs";
|
|
1121
|
+
import { createHash as createHash2 } from "crypto";
|
|
1122
|
+
import { join as join4, resolve, dirname as dirname3, relative } from "path";
|
|
1020
1123
|
|
|
1021
1124
|
// src/lockfile.ts
|
|
1022
1125
|
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1023
|
-
import { join as
|
|
1126
|
+
import { join as join3 } from "path";
|
|
1024
1127
|
var LOCKFILE_NAME = "ostacky-lock.json";
|
|
1025
1128
|
function getLockfilePath(opencodeRoot) {
|
|
1026
|
-
return
|
|
1129
|
+
return join3(opencodeRoot, LOCKFILE_NAME);
|
|
1027
1130
|
}
|
|
1028
1131
|
function readLockfile(opencodeRoot) {
|
|
1029
1132
|
const lockPath = getLockfilePath(opencodeRoot);
|
|
@@ -1045,6 +1148,8 @@ function removeFromLockfile(opencodeRoot, type, name) {
|
|
|
1045
1148
|
const lockfile = readLockfile(opencodeRoot);
|
|
1046
1149
|
if (!lockfile)
|
|
1047
1150
|
return;
|
|
1151
|
+
if (!lockfile[type])
|
|
1152
|
+
return;
|
|
1048
1153
|
if (!(name in lockfile[type]))
|
|
1049
1154
|
return;
|
|
1050
1155
|
delete lockfile[type][name];
|
|
@@ -1054,17 +1159,19 @@ function clearLockfile(opencodeRoot) {
|
|
|
1054
1159
|
const lockfile = readLockfile(opencodeRoot);
|
|
1055
1160
|
if (!lockfile) {
|
|
1056
1161
|
writeLockfile(opencodeRoot, {
|
|
1057
|
-
version: "0.0.
|
|
1162
|
+
version: "0.0.3",
|
|
1058
1163
|
lockedAt: new Date().toISOString(),
|
|
1059
1164
|
repo: "",
|
|
1060
1165
|
tag: "",
|
|
1061
1166
|
agents: {},
|
|
1062
|
-
commands: {}
|
|
1167
|
+
commands: {},
|
|
1168
|
+
skills: {}
|
|
1063
1169
|
});
|
|
1064
1170
|
return;
|
|
1065
1171
|
}
|
|
1066
1172
|
lockfile.agents = {};
|
|
1067
1173
|
lockfile.commands = {};
|
|
1174
|
+
lockfile.skills = {};
|
|
1068
1175
|
lockfile.lockedAt = new Date().toISOString();
|
|
1069
1176
|
writeLockfile(opencodeRoot, lockfile);
|
|
1070
1177
|
}
|
|
@@ -1073,12 +1180,12 @@ function clearLockfile(opencodeRoot) {
|
|
|
1073
1180
|
function findOpenCodeDir(startDir = process.cwd()) {
|
|
1074
1181
|
let current = resolve(startDir);
|
|
1075
1182
|
while (true) {
|
|
1076
|
-
const opencodeDir =
|
|
1183
|
+
const opencodeDir = join4(current, ".opencode");
|
|
1077
1184
|
if (existsSync3(opencodeDir))
|
|
1078
1185
|
return opencodeDir;
|
|
1079
|
-
if (existsSync3(
|
|
1186
|
+
if (existsSync3(join4(current, ".git")))
|
|
1080
1187
|
return null;
|
|
1081
|
-
const parent =
|
|
1188
|
+
const parent = dirname3(current);
|
|
1082
1189
|
if (parent === current)
|
|
1083
1190
|
return null;
|
|
1084
1191
|
current = parent;
|
|
@@ -1087,10 +1194,10 @@ function findOpenCodeDir(startDir = process.cwd()) {
|
|
|
1087
1194
|
function findProjectRoot(startDir = process.cwd()) {
|
|
1088
1195
|
let current = resolve(startDir);
|
|
1089
1196
|
while (true) {
|
|
1090
|
-
if (existsSync3(
|
|
1197
|
+
if (existsSync3(join4(current, ".opencode")) || existsSync3(join4(current, ".git"))) {
|
|
1091
1198
|
return current;
|
|
1092
1199
|
}
|
|
1093
|
-
const parent =
|
|
1200
|
+
const parent = dirname3(current);
|
|
1094
1201
|
if (parent === current)
|
|
1095
1202
|
return resolve(startDir);
|
|
1096
1203
|
current = parent;
|
|
@@ -1099,17 +1206,52 @@ function findProjectRoot(startDir = process.cwd()) {
|
|
|
1099
1206
|
function ensureOpenCodePaths(opencodeDir) {
|
|
1100
1207
|
const paths = {
|
|
1101
1208
|
root: opencodeDir,
|
|
1102
|
-
agents:
|
|
1103
|
-
commands:
|
|
1209
|
+
agents: join4(opencodeDir, "agents"),
|
|
1210
|
+
commands: join4(opencodeDir, "commands"),
|
|
1211
|
+
skills: join4(opencodeDir, "skills")
|
|
1104
1212
|
};
|
|
1105
|
-
for (const dir of [paths.root, paths.agents, paths.commands]) {
|
|
1213
|
+
for (const dir of [paths.root, paths.agents, paths.commands, paths.skills]) {
|
|
1106
1214
|
if (!existsSync3(dir))
|
|
1107
1215
|
mkdirSync2(dir, { recursive: true });
|
|
1108
1216
|
}
|
|
1109
1217
|
return paths;
|
|
1110
1218
|
}
|
|
1111
1219
|
function createOpenCodeDir(baseDir) {
|
|
1112
|
-
return ensureOpenCodePaths(
|
|
1220
|
+
return ensureOpenCodePaths(join4(baseDir, ".opencode"));
|
|
1221
|
+
}
|
|
1222
|
+
function copyDirRecursive(src, dest) {
|
|
1223
|
+
if (!existsSync3(dest))
|
|
1224
|
+
mkdirSync2(dest, { recursive: true });
|
|
1225
|
+
for (const entry of readdirSync(src)) {
|
|
1226
|
+
const srcPath = join4(src, entry);
|
|
1227
|
+
const destPath = join4(dest, entry);
|
|
1228
|
+
const stat = statSync(srcPath);
|
|
1229
|
+
if (stat.isDirectory()) {
|
|
1230
|
+
copyDirRecursive(srcPath, destPath);
|
|
1231
|
+
} else {
|
|
1232
|
+
copyFileSync(srcPath, destPath);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
function computeTreeHash(dir) {
|
|
1237
|
+
const lines = [];
|
|
1238
|
+
walkForHash(dir, dir, lines);
|
|
1239
|
+
const combined = lines.sort().join(`
|
|
1240
|
+
`);
|
|
1241
|
+
return createHash2("sha256").update(combined, "utf-8").digest("hex");
|
|
1242
|
+
}
|
|
1243
|
+
function walkForHash(root, current, lines) {
|
|
1244
|
+
for (const entry of readdirSync(current)) {
|
|
1245
|
+
const full = join4(current, entry);
|
|
1246
|
+
const stat = statSync(full);
|
|
1247
|
+
if (stat.isDirectory()) {
|
|
1248
|
+
walkForHash(root, full, lines);
|
|
1249
|
+
} else {
|
|
1250
|
+
const rel = relative(root, full);
|
|
1251
|
+
const content = readFileSync3(full, "utf-8");
|
|
1252
|
+
lines.push(`${rel}:${sha256(content)}`);
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1113
1255
|
}
|
|
1114
1256
|
function upsertLockfile(paths, type, item, manifest, contentHash) {
|
|
1115
1257
|
const existing = readLockfile(paths.root) ?? {
|
|
@@ -1118,8 +1260,11 @@ function upsertLockfile(paths, type, item, manifest, contentHash) {
|
|
|
1118
1260
|
repo: manifest.repo,
|
|
1119
1261
|
tag: manifest.tag,
|
|
1120
1262
|
agents: {},
|
|
1121
|
-
commands: {}
|
|
1263
|
+
commands: {},
|
|
1264
|
+
skills: {}
|
|
1122
1265
|
};
|
|
1266
|
+
if (!existing.skills)
|
|
1267
|
+
existing.skills = {};
|
|
1123
1268
|
existing[type][item.name] = {
|
|
1124
1269
|
version: item.version,
|
|
1125
1270
|
installedAt: new Date().toISOString(),
|
|
@@ -1132,22 +1277,43 @@ function upsertLockfile(paths, type, item, manifest, contentHash) {
|
|
|
1132
1277
|
}
|
|
1133
1278
|
async function installAgent(item, manifest, paths) {
|
|
1134
1279
|
const content = await downloadFile(manifest, item.file);
|
|
1135
|
-
writeFileSync3(
|
|
1280
|
+
writeFileSync3(join4(paths.agents, `${item.name}.md`), content, "utf-8");
|
|
1136
1281
|
upsertLockfile(paths, "agents", item, manifest, sha256(content));
|
|
1137
1282
|
}
|
|
1138
1283
|
async function installCommand(item, manifest, paths) {
|
|
1139
1284
|
const content = await downloadFile(manifest, item.file);
|
|
1140
|
-
writeFileSync3(
|
|
1285
|
+
writeFileSync3(join4(paths.commands, `${item.name}.md`), content, "utf-8");
|
|
1141
1286
|
upsertLockfile(paths, "commands", item, manifest, sha256(content));
|
|
1142
1287
|
}
|
|
1288
|
+
async function installSkill(item, manifest, paths) {
|
|
1289
|
+
const src = getBundledSkillPath(item.name);
|
|
1290
|
+
if (!existsSync3(src)) {
|
|
1291
|
+
throw new Error(`Skill bundleada no encontrada: ${src}. ¿Falta el directorio assets/skills/${item.name}/?`);
|
|
1292
|
+
}
|
|
1293
|
+
const treeHash = computeTreeHash(src);
|
|
1294
|
+
if (item.sha256 && treeHash !== item.sha256) {
|
|
1295
|
+
throw new Error(`Tree hash inválido para skill "${item.name}"
|
|
1296
|
+
` + ` esperado: ${item.sha256}
|
|
1297
|
+
` + ` recibido: ${treeHash}`);
|
|
1298
|
+
}
|
|
1299
|
+
const dest = join4(paths.skills, item.name);
|
|
1300
|
+
if (existsSync3(dest)) {
|
|
1301
|
+
rmSync(dest, { recursive: true, force: true });
|
|
1302
|
+
}
|
|
1303
|
+
copyDirRecursive(src, dest);
|
|
1304
|
+
upsertLockfile(paths, "skills", item, manifest, treeHash);
|
|
1305
|
+
}
|
|
1143
1306
|
function isAgentInstalled(name, paths) {
|
|
1144
|
-
return existsSync3(
|
|
1307
|
+
return existsSync3(join4(paths.agents, `${name}.md`));
|
|
1145
1308
|
}
|
|
1146
1309
|
function isCommandInstalled(name, paths) {
|
|
1147
|
-
return existsSync3(
|
|
1310
|
+
return existsSync3(join4(paths.commands, `${name}.md`));
|
|
1311
|
+
}
|
|
1312
|
+
function isSkillInstalled(name, paths) {
|
|
1313
|
+
return existsSync3(join4(paths.skills, name, "SKILL.md"));
|
|
1148
1314
|
}
|
|
1149
1315
|
function uninstallAgent(name, paths) {
|
|
1150
|
-
const filePath =
|
|
1316
|
+
const filePath = join4(paths.agents, `${name}.md`);
|
|
1151
1317
|
try {
|
|
1152
1318
|
if (existsSync3(filePath)) {
|
|
1153
1319
|
unlinkSync(filePath);
|
|
@@ -1159,7 +1325,7 @@ function uninstallAgent(name, paths) {
|
|
|
1159
1325
|
return true;
|
|
1160
1326
|
}
|
|
1161
1327
|
function uninstallCommand(name, paths) {
|
|
1162
|
-
const filePath =
|
|
1328
|
+
const filePath = join4(paths.commands, `${name}.md`);
|
|
1163
1329
|
try {
|
|
1164
1330
|
if (existsSync3(filePath)) {
|
|
1165
1331
|
unlinkSync(filePath);
|
|
@@ -1170,6 +1336,18 @@ function uninstallCommand(name, paths) {
|
|
|
1170
1336
|
removeFromLockfile(paths.root, "commands", name);
|
|
1171
1337
|
return true;
|
|
1172
1338
|
}
|
|
1339
|
+
function uninstallSkill(name, paths) {
|
|
1340
|
+
const dirPath = join4(paths.skills, name);
|
|
1341
|
+
try {
|
|
1342
|
+
if (existsSync3(dirPath)) {
|
|
1343
|
+
rmSync(dirPath, { recursive: true, force: true });
|
|
1344
|
+
}
|
|
1345
|
+
} catch {
|
|
1346
|
+
return false;
|
|
1347
|
+
}
|
|
1348
|
+
removeFromLockfile(paths.root, "skills", name);
|
|
1349
|
+
return true;
|
|
1350
|
+
}
|
|
1173
1351
|
function uninstallAll(paths) {
|
|
1174
1352
|
const lockfile = readLockfile(paths.root);
|
|
1175
1353
|
if (!lockfile)
|
|
@@ -1180,6 +1358,9 @@ function uninstallAll(paths) {
|
|
|
1180
1358
|
for (const name of Object.keys(lockfile.commands)) {
|
|
1181
1359
|
uninstallCommand(name, paths);
|
|
1182
1360
|
}
|
|
1361
|
+
for (const name of Object.keys(lockfile.skills ?? {})) {
|
|
1362
|
+
uninstallSkill(name, paths);
|
|
1363
|
+
}
|
|
1183
1364
|
clearLockfile(paths.root);
|
|
1184
1365
|
}
|
|
1185
1366
|
|
|
@@ -1213,7 +1394,8 @@ function printPostInstallSteps() {
|
|
|
1213
1394
|
"Recargá OpenCode",
|
|
1214
1395
|
"Ejecutá /install-stack para instalar el stack",
|
|
1215
1396
|
"Recargá OpenCode nuevamente",
|
|
1216
|
-
"Usá @Ostacky para invocar al agente"
|
|
1397
|
+
"Usá @Ostacky para invocar al agente",
|
|
1398
|
+
"Las skills bundleadas viven en .opencode/skills/ — revisá cuáles activás"
|
|
1217
1399
|
].join(`
|
|
1218
1400
|
→ `), "Próximos pasos");
|
|
1219
1401
|
}
|
|
@@ -1256,11 +1438,29 @@ function getUpdateCandidates(manifest, paths) {
|
|
|
1256
1438
|
candidates.push({ type: "commands", item, installedVersion: installed });
|
|
1257
1439
|
}
|
|
1258
1440
|
}
|
|
1441
|
+
for (const item of manifest.skills ?? []) {
|
|
1442
|
+
if (!isSkillInstalled(item.name, paths))
|
|
1443
|
+
continue;
|
|
1444
|
+
const installed = getInstalledVersion(lockfile, "skills", item.name);
|
|
1445
|
+
if (installed !== item.version) {
|
|
1446
|
+
candidates.push({ type: "skills", item, installedVersion: installed });
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1259
1449
|
return candidates;
|
|
1260
1450
|
}
|
|
1261
1451
|
function formatVersionDiff(from, to) {
|
|
1262
1452
|
return from ? `${from} → ${to}` : `(sin versión) → ${to}`;
|
|
1263
1453
|
}
|
|
1454
|
+
function kindLabel(type) {
|
|
1455
|
+
switch (type) {
|
|
1456
|
+
case "agents":
|
|
1457
|
+
return "agente";
|
|
1458
|
+
case "commands":
|
|
1459
|
+
return "command";
|
|
1460
|
+
case "skills":
|
|
1461
|
+
return "skill";
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1264
1464
|
async function doInstallAll(manifest, paths) {
|
|
1265
1465
|
const spin = L2();
|
|
1266
1466
|
let errors = 0;
|
|
@@ -1284,6 +1484,16 @@ async function doInstallAll(manifest, paths) {
|
|
|
1284
1484
|
errors++;
|
|
1285
1485
|
}
|
|
1286
1486
|
}
|
|
1487
|
+
for (const skill of manifest.skills ?? []) {
|
|
1488
|
+
spin.start(`Instalando skill: ${skill.name} (${skill.version})`);
|
|
1489
|
+
try {
|
|
1490
|
+
await installSkill(skill, manifest, paths);
|
|
1491
|
+
spin.stop(`Skill instalada: ${skill.name} (${skill.version})`);
|
|
1492
|
+
} catch (e2) {
|
|
1493
|
+
spin.stop(`Error en ${skill.name}: ${e2.message}`);
|
|
1494
|
+
errors++;
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1287
1497
|
if (errors === 0) {
|
|
1288
1498
|
v2.success("Todo instalado correctamente.");
|
|
1289
1499
|
} else {
|
|
@@ -1340,6 +1550,35 @@ async function doAddCommand(manifest, paths) {
|
|
|
1340
1550
|
}
|
|
1341
1551
|
}
|
|
1342
1552
|
}
|
|
1553
|
+
async function doAddSkill(manifest, paths) {
|
|
1554
|
+
const lockfile = readLockfile(paths.root);
|
|
1555
|
+
const options = (manifest.skills ?? []).map((s) => {
|
|
1556
|
+
const installed = getInstalledVersion(lockfile, "skills", s.name);
|
|
1557
|
+
const hint = installed ? `v${installed} instalado — ${s.description}` : s.description;
|
|
1558
|
+
return { value: s.name, label: `${s.name} (v${s.version})`, hint };
|
|
1559
|
+
});
|
|
1560
|
+
if (options.length === 0) {
|
|
1561
|
+
v2.info("No hay skills disponibles en el manifest.");
|
|
1562
|
+
return;
|
|
1563
|
+
}
|
|
1564
|
+
const selected = await pe({
|
|
1565
|
+
message: "¿Qué skills deseas instalar?",
|
|
1566
|
+
options,
|
|
1567
|
+
required: true
|
|
1568
|
+
});
|
|
1569
|
+
onCancel(selected);
|
|
1570
|
+
const spin = L2();
|
|
1571
|
+
for (const name of selected) {
|
|
1572
|
+
const item = manifest.skills.find((s) => s.name === name);
|
|
1573
|
+
spin.start(`Instalando skill: ${name} (${item.version})`);
|
|
1574
|
+
try {
|
|
1575
|
+
await installSkill(item, manifest, paths);
|
|
1576
|
+
spin.stop(`Skill instalada: ${name} (${item.version})`);
|
|
1577
|
+
} catch (e2) {
|
|
1578
|
+
spin.stop(`Error: ${e2.message}`);
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1343
1582
|
async function doUpdate(manifest, paths) {
|
|
1344
1583
|
const candidates = getUpdateCandidates(manifest, paths);
|
|
1345
1584
|
if (candidates.length === 0) {
|
|
@@ -1347,8 +1586,8 @@ async function doUpdate(manifest, paths) {
|
|
|
1347
1586
|
return;
|
|
1348
1587
|
}
|
|
1349
1588
|
const diffLines = candidates.map(({ type, item, installedVersion }) => {
|
|
1350
|
-
const kind = type
|
|
1351
|
-
return ` ${kind.padEnd(8)} ${item.name.padEnd(
|
|
1589
|
+
const kind = kindLabel(type);
|
|
1590
|
+
return ` ${kind.padEnd(8)} ${item.name.padEnd(24)} ${formatVersionDiff(installedVersion, item.version)}`;
|
|
1352
1591
|
});
|
|
1353
1592
|
ye(diffLines.join(`
|
|
1354
1593
|
`), "Actualizaciones disponibles");
|
|
@@ -1367,8 +1606,10 @@ async function doUpdate(manifest, paths) {
|
|
|
1367
1606
|
try {
|
|
1368
1607
|
if (type === "agents") {
|
|
1369
1608
|
await installAgent(item, manifest, paths);
|
|
1370
|
-
} else {
|
|
1609
|
+
} else if (type === "commands") {
|
|
1371
1610
|
await installCommand(item, manifest, paths);
|
|
1611
|
+
} else {
|
|
1612
|
+
await installSkill(item, manifest, paths);
|
|
1372
1613
|
}
|
|
1373
1614
|
spin.stop(`Actualizado: ${item.name} (${item.version})`);
|
|
1374
1615
|
updated++;
|
|
@@ -1392,6 +1633,7 @@ async function runInteractiveMenu() {
|
|
|
1392
1633
|
{ value: "all", label: "Instalar todo" },
|
|
1393
1634
|
{ value: "agent", label: "Instalar agente" },
|
|
1394
1635
|
{ value: "command", label: "Instalar command" },
|
|
1636
|
+
{ value: "skill", label: "Instalar skill" },
|
|
1395
1637
|
{ value: "update", label: "Actualizar instalación" },
|
|
1396
1638
|
{ value: "uninstall", label: "Desinstalar" },
|
|
1397
1639
|
{ value: "exit", label: "Salir" }
|
|
@@ -1414,6 +1656,11 @@ async function runInteractiveMenu() {
|
|
|
1414
1656
|
printPostInstallSteps();
|
|
1415
1657
|
fe("Listo.");
|
|
1416
1658
|
break;
|
|
1659
|
+
case "skill":
|
|
1660
|
+
await doAddSkill(manifest, paths);
|
|
1661
|
+
printPostInstallSteps();
|
|
1662
|
+
fe("Listo.");
|
|
1663
|
+
break;
|
|
1417
1664
|
case "update": {
|
|
1418
1665
|
const latestManifest = await loadLatestManifest();
|
|
1419
1666
|
await doUpdate(latestManifest, paths);
|
|
@@ -1465,6 +1712,18 @@ async function runAddCommandCommand() {
|
|
|
1465
1712
|
printPostInstallSteps();
|
|
1466
1713
|
fe("Listo.");
|
|
1467
1714
|
}
|
|
1715
|
+
async function runAddSkillCommand() {
|
|
1716
|
+
we(" OpenCode Installer ");
|
|
1717
|
+
const manifest = await loadManifest();
|
|
1718
|
+
const paths = await resolveOpenCodePaths();
|
|
1719
|
+
if (!paths) {
|
|
1720
|
+
fe("Cancelado.");
|
|
1721
|
+
return;
|
|
1722
|
+
}
|
|
1723
|
+
await doAddSkill(manifest, paths);
|
|
1724
|
+
printPostInstallSteps();
|
|
1725
|
+
fe("Listo.");
|
|
1726
|
+
}
|
|
1468
1727
|
async function runUpdateCommand() {
|
|
1469
1728
|
we(" OpenCode Installer ");
|
|
1470
1729
|
const manifest = await loadLatestManifest();
|
|
@@ -1478,21 +1737,24 @@ async function runUpdateCommand() {
|
|
|
1478
1737
|
}
|
|
1479
1738
|
async function doUninstallTotal(paths) {
|
|
1480
1739
|
const lockfile = readLockfile(paths.root);
|
|
1481
|
-
if (!lockfile || Object.keys(lockfile.agents).length === 0 && Object.keys(lockfile.commands).length === 0) {
|
|
1740
|
+
if (!lockfile || Object.keys(lockfile.agents).length === 0 && Object.keys(lockfile.commands).length === 0 && Object.keys(lockfile.skills ?? {}).length === 0) {
|
|
1482
1741
|
v2.info("No hay nada instalado.");
|
|
1483
1742
|
return;
|
|
1484
1743
|
}
|
|
1485
1744
|
const pathsToDelete = [];
|
|
1486
1745
|
for (const name of Object.keys(lockfile.agents)) {
|
|
1487
|
-
pathsToDelete.push(
|
|
1746
|
+
pathsToDelete.push(join5(paths.agents, `${name}.md`));
|
|
1488
1747
|
}
|
|
1489
1748
|
for (const name of Object.keys(lockfile.commands)) {
|
|
1490
|
-
pathsToDelete.push(
|
|
1749
|
+
pathsToDelete.push(join5(paths.commands, `${name}.md`));
|
|
1750
|
+
}
|
|
1751
|
+
for (const name of Object.keys(lockfile.skills ?? {})) {
|
|
1752
|
+
pathsToDelete.push(join5(paths.skills, name));
|
|
1491
1753
|
}
|
|
1492
1754
|
ye(pathsToDelete.join(`
|
|
1493
|
-
`), `Se eliminarán ${pathsToDelete.length} archivo(s)`);
|
|
1755
|
+
`), `Se eliminarán ${pathsToDelete.length} archivo(s) / directorio(s)`);
|
|
1494
1756
|
const confirm = await me({
|
|
1495
|
-
message: `¿Confirmar desinstalación de ${pathsToDelete.length}
|
|
1757
|
+
message: `¿Confirmar desinstalación de ${pathsToDelete.length} item(s)?`
|
|
1496
1758
|
});
|
|
1497
1759
|
onCancel(confirm);
|
|
1498
1760
|
if (!confirm) {
|
|
@@ -1500,7 +1762,7 @@ async function doUninstallTotal(paths) {
|
|
|
1500
1762
|
return;
|
|
1501
1763
|
}
|
|
1502
1764
|
uninstallAll(paths);
|
|
1503
|
-
v2.success(`${pathsToDelete.length}
|
|
1765
|
+
v2.success(`${pathsToDelete.length} item(s) eliminado(s).`);
|
|
1504
1766
|
}
|
|
1505
1767
|
async function doUninstallAgent(paths) {
|
|
1506
1768
|
const lockfile = readLockfile(paths.root);
|
|
@@ -1564,13 +1826,45 @@ async function doUninstallCommand(paths) {
|
|
|
1564
1826
|
}
|
|
1565
1827
|
}
|
|
1566
1828
|
}
|
|
1829
|
+
async function doUninstallSkill(paths) {
|
|
1830
|
+
const lockfile = readLockfile(paths.root);
|
|
1831
|
+
if (!lockfile) {
|
|
1832
|
+
v2.warn("No hay instalación registrada.");
|
|
1833
|
+
return;
|
|
1834
|
+
}
|
|
1835
|
+
const installed = Object.keys(lockfile.skills ?? {});
|
|
1836
|
+
if (installed.length === 0) {
|
|
1837
|
+
v2.info("No hay skills instaladas.");
|
|
1838
|
+
return;
|
|
1839
|
+
}
|
|
1840
|
+
const options = installed.map((name) => ({
|
|
1841
|
+
value: name,
|
|
1842
|
+
label: name,
|
|
1843
|
+
hint: `v${lockfile.skills[name].version}`
|
|
1844
|
+
}));
|
|
1845
|
+
const selected = await pe({
|
|
1846
|
+
message: "¿Qué skills querés desinstalar?",
|
|
1847
|
+
options,
|
|
1848
|
+
required: true
|
|
1849
|
+
});
|
|
1850
|
+
onCancel(selected);
|
|
1851
|
+
for (const name of selected) {
|
|
1852
|
+
const ok = uninstallSkill(name, paths);
|
|
1853
|
+
if (ok) {
|
|
1854
|
+
v2.success(`Skill eliminada: ${name}`);
|
|
1855
|
+
} else {
|
|
1856
|
+
v2.warn(`No se pudo eliminar la skill: ${name}`);
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1567
1860
|
async function doUninstall(paths) {
|
|
1568
1861
|
const scope = await de({
|
|
1569
1862
|
message: "¿Qué querés desinstalar?",
|
|
1570
1863
|
options: [
|
|
1571
|
-
{ value: "all", label: "Todo (agentes + commands)" },
|
|
1864
|
+
{ value: "all", label: "Todo (agentes + commands + skills)" },
|
|
1572
1865
|
{ value: "agent", label: "Solo un agente" },
|
|
1573
|
-
{ value: "command", label: "Solo un command" }
|
|
1866
|
+
{ value: "command", label: "Solo un command" },
|
|
1867
|
+
{ value: "skill", label: "Solo una skill" }
|
|
1574
1868
|
]
|
|
1575
1869
|
});
|
|
1576
1870
|
onCancel(scope);
|
|
@@ -1584,6 +1878,9 @@ async function doUninstall(paths) {
|
|
|
1584
1878
|
case "command":
|
|
1585
1879
|
await doUninstallCommand(paths);
|
|
1586
1880
|
break;
|
|
1881
|
+
case "skill":
|
|
1882
|
+
await doUninstallSkill(paths);
|
|
1883
|
+
break;
|
|
1587
1884
|
}
|
|
1588
1885
|
}
|
|
1589
1886
|
async function runUninstallCommand() {
|
|
@@ -1615,7 +1912,7 @@ async function runUninstallAgentCommand(name) {
|
|
|
1615
1912
|
v2.warn(`${name} no está instalado.`);
|
|
1616
1913
|
return;
|
|
1617
1914
|
}
|
|
1618
|
-
const filePath =
|
|
1915
|
+
const filePath = join5(paths.agents, `${name}.md`);
|
|
1619
1916
|
ye(filePath, `Se eliminará`);
|
|
1620
1917
|
const confirm = await me({ message: `¿Borrar ${name}.md?` });
|
|
1621
1918
|
onCancel(confirm);
|
|
@@ -1653,7 +1950,7 @@ async function runUninstallCommandCommand(name) {
|
|
|
1653
1950
|
v2.warn(`${name} no está instalado.`);
|
|
1654
1951
|
return;
|
|
1655
1952
|
}
|
|
1656
|
-
const filePath =
|
|
1953
|
+
const filePath = join5(paths.commands, `${name}.md`);
|
|
1657
1954
|
ye(filePath, `Se eliminará`);
|
|
1658
1955
|
const confirm = await me({ message: `¿Borrar ${name}.md?` });
|
|
1659
1956
|
onCancel(confirm);
|
|
@@ -1672,20 +1969,60 @@ async function runUninstallCommandCommand(name) {
|
|
|
1672
1969
|
}
|
|
1673
1970
|
fe("Listo.");
|
|
1674
1971
|
}
|
|
1972
|
+
async function runUninstallSkillCommand(name) {
|
|
1973
|
+
we(" OpenCode Installer ");
|
|
1974
|
+
const paths = await resolveOpenCodePaths();
|
|
1975
|
+
if (!paths) {
|
|
1976
|
+
fe("Cancelado.");
|
|
1977
|
+
return;
|
|
1978
|
+
}
|
|
1979
|
+
if (name) {
|
|
1980
|
+
try {
|
|
1981
|
+
validateFilePath(name);
|
|
1982
|
+
} catch (e2) {
|
|
1983
|
+
v2.warn(`Nombre inválido: ${e2.message}`);
|
|
1984
|
+
return;
|
|
1985
|
+
}
|
|
1986
|
+
const lockfile = readLockfile(paths.root);
|
|
1987
|
+
if (!lockfile || !lockfile.skills || !(name in lockfile.skills)) {
|
|
1988
|
+
v2.warn(`${name} no está instalado.`);
|
|
1989
|
+
return;
|
|
1990
|
+
}
|
|
1991
|
+
const dirPath = join5(paths.skills, name);
|
|
1992
|
+
ye(dirPath, `Se eliminará`);
|
|
1993
|
+
const confirm = await me({ message: `¿Borrar la skill "${name}"?` });
|
|
1994
|
+
onCancel(confirm);
|
|
1995
|
+
if (!confirm) {
|
|
1996
|
+
v2.info("Cancelado.");
|
|
1997
|
+
return;
|
|
1998
|
+
}
|
|
1999
|
+
const ok = uninstallSkill(name, paths);
|
|
2000
|
+
if (ok) {
|
|
2001
|
+
v2.success(`Skill eliminada: ${name}`);
|
|
2002
|
+
} else {
|
|
2003
|
+
v2.warn(`No se pudo eliminar la skill: ${name}`);
|
|
2004
|
+
}
|
|
2005
|
+
} else {
|
|
2006
|
+
await doUninstallSkill(paths);
|
|
2007
|
+
}
|
|
2008
|
+
fe("Listo.");
|
|
2009
|
+
}
|
|
1675
2010
|
|
|
1676
2011
|
// src/cli.ts
|
|
1677
2012
|
var HELP = `
|
|
1678
|
-
ostacky — Instalador de agentes y
|
|
2013
|
+
ostacky — Instalador de agentes, comandos y skills para OpenCode
|
|
1679
2014
|
|
|
1680
2015
|
Uso:
|
|
1681
2016
|
npx ostacky Menú interactivo
|
|
1682
2017
|
npx ostacky install Instalar todo
|
|
1683
2018
|
npx ostacky add agent Agregar agente(s)
|
|
1684
2019
|
npx ostacky add command Agregar command(s)
|
|
2020
|
+
npx ostacky add skill Agregar skill(s)
|
|
1685
2021
|
npx ostacky update Actualizar instalación
|
|
1686
2022
|
npx ostacky uninstall Desinstalar todo
|
|
1687
2023
|
npx ostacky uninstall agent Desinstalar agente(s)
|
|
1688
2024
|
npx ostacky uninstall command Desinstalar command(s)
|
|
2025
|
+
npx ostacky uninstall skill Desinstalar skill(s)
|
|
1689
2026
|
npx ostacky --help Mostrar esta ayuda
|
|
1690
2027
|
npx ostacky --version Mostrar versión
|
|
1691
2028
|
`.trim();
|
|
@@ -1700,8 +2037,10 @@ async function main() {
|
|
|
1700
2037
|
await runAddAgentCommand();
|
|
1701
2038
|
} else if (subcmd === "command") {
|
|
1702
2039
|
await runAddCommandCommand();
|
|
2040
|
+
} else if (subcmd === "skill") {
|
|
2041
|
+
await runAddSkillCommand();
|
|
1703
2042
|
} else {
|
|
1704
|
-
console.error(`Tipo desconocido: "${subcmd}". Usa 'agent' o '
|
|
2043
|
+
console.error(`Tipo desconocido: "${subcmd}". Usa 'agent', 'command' o 'skill'.`);
|
|
1705
2044
|
process.exit(1);
|
|
1706
2045
|
}
|
|
1707
2046
|
break;
|
|
@@ -1715,10 +2054,13 @@ async function main() {
|
|
|
1715
2054
|
} else if (subcmd === "command") {
|
|
1716
2055
|
const name = process.argv[4];
|
|
1717
2056
|
await runUninstallCommandCommand(name);
|
|
2057
|
+
} else if (subcmd === "skill") {
|
|
2058
|
+
const name = process.argv[4];
|
|
2059
|
+
await runUninstallSkillCommand(name);
|
|
1718
2060
|
} else if (subcmd === undefined) {
|
|
1719
2061
|
await runUninstallCommand();
|
|
1720
2062
|
} else {
|
|
1721
|
-
console.error(`Subcomando desconocido: "${subcmd}". Usa 'agent', 'command' o nada.`);
|
|
2063
|
+
console.error(`Subcomando desconocido: "${subcmd}". Usa 'agent', 'command', 'skill' o nada.`);
|
|
1722
2064
|
process.exit(1);
|
|
1723
2065
|
}
|
|
1724
2066
|
break;
|