ostacky 0.5.8 → 0.5.10

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.
@@ -0,0 +1,227 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { readFileSync } from 'node:fs';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { dirname, join } from 'node:path';
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
9
+ const ASSETS_DIR = join(__dirname, '..');
10
+ const AGENT_FILE = join(ASSETS_DIR, 'agents', 'ostacky.md');
11
+ const CONTROLLER_FILE = join(ASSETS_DIR, 'mcp', 'ostacky-controller', 'index.js');
12
+
13
+ const agentContent = readFileSync(AGENT_FILE, 'utf8');
14
+ const controllerContent = readFileSync(CONTROLLER_FILE, 'utf8');
15
+
16
+ // Tools registered in the MCP controller
17
+ const MCP_TOOLS = [
18
+ 'start_request', 'request_clarification', 'record_clarification',
19
+ 'record_discovery', 'consume_route_decision', 'spec_complete',
20
+ 'record_execution_analysis', 'consume_execution_decision',
21
+ 'implementation_complete', 'sync_complete', 'block', 'replan',
22
+ 'get_state', 'get_tasks', 'validate_edit', 'complete_task',
23
+ ];
24
+
25
+ // CodeGraph tools referenced in agent instructions
26
+ const CODEGRAPH_TOOLS = [
27
+ 'codegraph_explore', 'codegraph_node', 'codegraph_search',
28
+ 'codegraph_callers', 'codegraph_callees', 'codegraph_impact',
29
+ 'codegraph_files', 'codegraph_status',
30
+ ];
31
+
32
+ // Native OpenCode tools
33
+ const NATIVE_TOOLS = ['question', 'Read', 'Edit', 'Bash', 'Grep', 'Glob'];
34
+
35
+ const ALL_VALID_TOOLS = [...MCP_TOOLS, ...CODEGRAPH_TOOLS, ...NATIVE_TOOLS];
36
+
37
+ describe('Agent contract — ostacky.md', () => {
38
+ describe('Core Instructions section', () => {
39
+ it('has "Core Instructions" heading', () => {
40
+ assert.ok(agentContent.includes('## Core Instructions'),
41
+ 'Missing "## Core Instructions" section');
42
+ });
43
+
44
+ it('has SINGLE SOURCE OF VERDAD marker', () => {
45
+ assert.ok(agentContent.includes('SINGLE SOURCE OF VERDAD'),
46
+ 'Missing "SINGLE SOURCE OF VERDAD" marker');
47
+ });
48
+ });
49
+
50
+ describe('CodeGraph section', () => {
51
+ it('has CodeGraph — búsqueda de código subsection', () => {
52
+ assert.ok(agentContent.includes('### CodeGraph — búsqueda de código'),
53
+ 'Missing "### CodeGraph — búsqueda de código" section');
54
+ });
55
+
56
+ it('has tool table with codegraph_explore', () => {
57
+ assert.ok(agentContent.includes('codegraph_explore'),
58
+ 'Missing codegraph_explore in tool table');
59
+ });
60
+ });
61
+
62
+ describe('Engram section', () => {
63
+ it('has Engram — memoria persistente subsection', () => {
64
+ assert.ok(agentContent.includes('### Engram — memoria persistente'),
65
+ 'Missing "### Engram — memoria persistente" section');
66
+ });
67
+
68
+ it('has engram_mem_context in Discovery flow', () => {
69
+ assert.ok(agentContent.includes('engram_mem_context'),
70
+ 'Missing engram_mem_context — agent should consult memory at start of request');
71
+ });
72
+
73
+ it('has engram_mem_search reference', () => {
74
+ assert.ok(agentContent.includes('engram_mem_search'),
75
+ 'Missing engram_mem_search — agent should search before deciding');
76
+ });
77
+
78
+ it('has engram_mem_save reference', () => {
79
+ assert.ok(agentContent.includes('engram_mem_save'),
80
+ 'Missing engram_mem_save — agent should save after significant work');
81
+ });
82
+ });
83
+
84
+ describe('HARD-STOP rules', () => {
85
+ it('has at least 2 HARD-STOP blocks (HTML tags or inline markers)', () => {
86
+ const htmlBlocks = (agentContent.match(/<HARD-STOP>/g) || []).length;
87
+ const inlineMarkers = (agentContent.match(/\*\*HARD-STOP\*\*/g) || []).length;
88
+ const total = htmlBlocks + inlineMarkers;
89
+ assert.ok(total >= 2,
90
+ `Expected at least 2 HARD-STOP blocks (HTML or inline), found ${total} (${htmlBlocks} HTML, ${inlineMarkers} inline)`);
91
+ });
92
+
93
+ it('has closing HARD-STOP tags matching opening tags', () => {
94
+ const opens = (agentContent.match(/<HARD-STOP>/g) || []).length;
95
+ const closes = (agentContent.match(/<\/HARD-STOP>/g) || []).length;
96
+ assert.equal(opens, closes,
97
+ `Mismatched HARD-STOP tags: ${opens} opens, ${closes} closes`);
98
+ });
99
+
100
+ it('has HARD-STOP in Regla de oro (global question stop)', () => {
101
+ const reglaDeOro = agentContent.substring(
102
+ agentContent.indexOf('## Regla de oro'),
103
+ agentContent.indexOf('## Flujo')
104
+ );
105
+ assert.ok(reglaDeOro.includes('<HARD-STOP>'),
106
+ 'Missing HARD-STOP in Regla de Oro section — agent can continue after questions');
107
+ });
108
+ });
109
+
110
+ describe('Tool name references — no stale names', () => {
111
+ it('does NOT reference "ask_user" (non-existent tool)', () => {
112
+ const refs = agentContent.match(/ask_user/g);
113
+ const count = refs ? refs.length : 0;
114
+ assert.equal(count, 0,
115
+ `Found ${count} reference(s) to "ask_user" — this tool does not exist. Use "question" instead.`);
116
+ });
117
+
118
+ it('does NOT reference "codegraph_context" (stale name)', () => {
119
+ // Allow it in comments or descriptions, but not as an active tool call
120
+ const lines = agentContent.split('\n');
121
+ const offenders = lines.filter(line =>
122
+ line.includes('codegraph_context') &&
123
+ !line.startsWith('|') &&
124
+ !line.includes('NUNCA') &&
125
+ !line.includes('Prohibido')
126
+ );
127
+ assert.equal(offenders.length, 0,
128
+ `Found stale "codegraph_context" references (use codegraph_explore): ${offenders.join('; ')}`);
129
+ });
130
+
131
+ it('references valid MCP tools in flow instructions', () => {
132
+ // Extract tool names used in flow steps (lines with backtick-quoted tool names)
133
+ const toolRefs = agentContent.match(/`([a-z_]+)`/g) || [];
134
+ const uniqueTools = [...new Set(toolRefs.map(t => t.replace(/`/g, '')))];
135
+
136
+ const flowTools = uniqueTools.filter(t =>
137
+ t.includes('request') || t.includes('record') || t.includes('consume') ||
138
+ t.includes('spec_') || t.includes('implementation') || t.includes('sync') ||
139
+ t.includes('validate') || t.includes('complete') || t.includes('block') ||
140
+ t.includes('replan') || t.includes('start_') || t.includes('get_')
141
+ );
142
+
143
+ const invalidTools = flowTools.filter(t => !MCP_TOOLS.includes(t));
144
+ assert.equal(invalidTools.length, 0,
145
+ `References non-existent MCP tools: ${invalidTools.join(', ')}`);
146
+ });
147
+ });
148
+
149
+ describe('validate_edit instructions', () => {
150
+ it('mentions content as required parameter', () => {
151
+ assert.ok(
152
+ agentContent.includes('content') &&
153
+ (agentContent.includes('content es OBLIGATORIO') ||
154
+ agentContent.includes('`content` es OBLIGATORIO')),
155
+ 'Missing explicit instruction that content is required for validate_edit'
156
+ );
157
+ });
158
+
159
+ it('instructs to Read file before validate_edit', () => {
160
+ const executionSection = agentContent.substring(
161
+ agentContent.indexOf('### 4. Execution'),
162
+ agentContent.indexOf('### 5. Sync')
163
+ );
164
+ assert.ok(executionSection.includes('Read'),
165
+ 'Execution section should instruct to Read file before validate_edit');
166
+ });
167
+ });
168
+
169
+ describe('Classification rules', () => {
170
+ it('has Nivel 0, 0+1, and 1+', () => {
171
+ assert.ok(agentContent.includes('Nivel 0'), 'Missing Nivel 0');
172
+ assert.ok(agentContent.includes('Nivel 0+1'), 'Missing Nivel 0+1');
173
+ assert.ok(agentContent.includes('Nivel 1+'), 'Missing Nivel 1+');
174
+ });
175
+
176
+ it('has defaultChoice mapping', () => {
177
+ assert.ok(agentContent.includes('defaultChoice'), 'Missing defaultChoice mapping');
178
+ assert.ok(agentContent.includes('"DIRECT"'), 'Missing DIRECT default');
179
+ assert.ok(agentContent.includes('"SPEC"'), 'Missing SPEC default');
180
+ });
181
+ });
182
+
183
+ describe('Guardrails', () => {
184
+ it('has Eficiencia de tokens section', () => {
185
+ assert.ok(agentContent.includes('### Eficiencia de tokens'),
186
+ 'Missing "### Eficiencia de tokens" guardrail section');
187
+ });
188
+
189
+ it('has Decisiones y estado section', () => {
190
+ assert.ok(agentContent.includes('### Decisiones y estado'),
191
+ 'Missing "### Decisiones y estado" guardrail section');
192
+ });
193
+ });
194
+ });
195
+
196
+ describe('Controller contract — index.js', () => {
197
+ it('exports OstackyController', () => {
198
+ assert.ok(controllerContent.includes('export { OstackyController }'),
199
+ 'Missing OstackyController export');
200
+ });
201
+
202
+ it('has validate_edit tool registered', () => {
203
+ assert.ok(controllerContent.includes("server.registerTool(\n 'validate_edit'") ||
204
+ controllerContent.includes("server.registerTool('validate_edit'"),
205
+ 'validate_edit not registered as MCP tool');
206
+ });
207
+
208
+ it('validate_edit schema has content as required (z.string())', () => {
209
+ // Find the validate_edit schema section
210
+ const validateSection = controllerContent.substring(
211
+ controllerContent.indexOf("'validate_edit'"),
212
+ controllerContent.indexOf("'complete_task'")
213
+ );
214
+ assert.ok(validateSection.includes('content: z.string()') &&
215
+ !validateSection.includes('content: z.string().optional()'),
216
+ 'validate_edit content param should be z.string() required — prevents silent state corruption');
217
+ });
218
+
219
+ it('validate_edit handler validates content type', () => {
220
+ const validateSection = controllerContent.substring(
221
+ controllerContent.indexOf("'validate_edit'"),
222
+ controllerContent.indexOf("'complete_task'")
223
+ );
224
+ assert.ok(validateSection.includes("typeof content !== 'string'"),
225
+ 'validate_edit handler should check typeof content');
226
+ });
227
+ });
@@ -0,0 +1,187 @@
1
+ import { describe, it, beforeEach } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { OstackyController } from '../mcp/ostacky-controller/index.js';
4
+
5
+ function createController(initialState = {}) {
6
+ return new OstackyController({
7
+ statePath: null,
8
+ initialState: {
9
+ state: 'INTERPRETATION_PENDING',
10
+ revision: 0,
11
+ requestId: 'test',
12
+ changeId: null,
13
+ routeDecisionId: null,
14
+ routeChoice: null,
15
+ executionDecisionId: null,
16
+ executionMode: null,
17
+ snapshots: { codegraph: null, execution: null },
18
+ tasks: {},
19
+ fileFingerprints: {},
20
+ error: null,
21
+ ...initialState,
22
+ },
23
+ });
24
+ }
25
+
26
+ describe('OstackyController', () => {
27
+ describe('startRequest', () => {
28
+ it('transitions to INTERPRETATION_PENDING', async () => {
29
+ const ctrl = createController({ state: 'DONE' });
30
+ const result = await ctrl.startRequest({ requestId: 'req-1' });
31
+ assert.equal(result.state, 'INTERPRETATION_PENDING');
32
+ assert.equal(result.requestId, 'req-1');
33
+ });
34
+
35
+ it('generates requestId when not provided', async () => {
36
+ const ctrl = createController({ state: 'DONE' });
37
+ const result = await ctrl.startRequest();
38
+ assert.ok(result.requestId.startsWith('req-'));
39
+ });
40
+ });
41
+
42
+ describe('validateEdit', () => {
43
+ it('returns CONFLICT when content is undefined (the bug we fixed)', async () => {
44
+ const ctrl = createController({ state: 'EXECUTING_INLINE' });
45
+ const result = await ctrl.validateEdit({
46
+ oldString: 'foo',
47
+ newString: 'bar',
48
+ content: undefined,
49
+ });
50
+ assert.equal(result.outcome, 'CONFLICT');
51
+ assert.ok(result.reason.includes('Missing required fields'));
52
+ });
53
+
54
+ it('returns CONFLICT when content is not a string', async () => {
55
+ const ctrl = createController({ state: 'EXECUTING_INLINE' });
56
+ const result = await ctrl.validateEdit({
57
+ oldString: 'foo',
58
+ newString: 'bar',
59
+ content: 123,
60
+ });
61
+ assert.equal(result.outcome, 'CONFLICT');
62
+ assert.ok(result.reason.includes('Missing required fields'));
63
+ });
64
+
65
+ it('returns EDITABLE when oldString found exactly once', async () => {
66
+ const ctrl = createController({ state: 'EXECUTING_INLINE' });
67
+ const result = await ctrl.validateEdit({
68
+ oldString: 'hello world',
69
+ newString: 'hello universe',
70
+ content: 'say hello world to everyone',
71
+ });
72
+ assert.equal(result.outcome, 'EDITABLE');
73
+ });
74
+
75
+ it('returns ALREADY_APPLIED when oldString equals newString', async () => {
76
+ const ctrl = createController({ state: 'EXECUTING_INLINE' });
77
+ const result = await ctrl.validateEdit({
78
+ oldString: 'hello',
79
+ newString: 'hello',
80
+ content: 'hello world',
81
+ });
82
+ assert.equal(result.outcome, 'ALREADY_APPLIED');
83
+ });
84
+
85
+ it('returns ALREADY_APPLIED when newString already present and oldString not found', async () => {
86
+ const ctrl = createController({ state: 'EXECUTING_INLINE' });
87
+ const result = await ctrl.validateEdit({
88
+ oldString: 'old text',
89
+ newString: 'new text',
90
+ content: 'already has new text here',
91
+ });
92
+ assert.equal(result.outcome, 'ALREADY_APPLIED');
93
+ });
94
+
95
+ it('returns CONFLICT when oldString not found and newString not present', async () => {
96
+ const ctrl = createController({ state: 'EXECUTING_INLINE' });
97
+ const result = await ctrl.validateEdit({
98
+ oldString: 'nonexistent',
99
+ newString: 'replacement',
100
+ content: 'some content without the old string',
101
+ });
102
+ assert.equal(result.outcome, 'CONFLICT');
103
+ assert.ok(result.reason.includes('not found'));
104
+ });
105
+
106
+ it('returns CONFLICT when oldString found multiple times', async () => {
107
+ const ctrl = createController({ state: 'EXECUTING_INLINE' });
108
+ const result = await ctrl.validateEdit({
109
+ oldString: 'dup',
110
+ newString: 'replaced',
111
+ content: 'dup and dup again',
112
+ });
113
+ assert.equal(result.outcome, 'CONFLICT');
114
+ assert.ok(result.reason.includes('2 times'));
115
+ });
116
+
117
+ it('returns CONFLICT when not in executing state', async () => {
118
+ const ctrl = createController();
119
+ const result = await ctrl.validateEdit({
120
+ oldString: 'foo',
121
+ newString: 'bar',
122
+ content: 'foo bar baz',
123
+ });
124
+ assert.equal(result.outcome, 'CONFLICT');
125
+ assert.ok(result.reason.includes('Cannot validate edit from state'));
126
+ });
127
+ });
128
+
129
+ describe('state transitions', () => {
130
+ it('full flow: start → discovery → route → execution → sync → done', async () => {
131
+ const ctrl = createController({ state: 'DONE' });
132
+ let r = await ctrl.startRequest({ requestId: 'test' });
133
+ assert.equal(r.state, 'INTERPRETATION_PENDING');
134
+
135
+ r = await ctrl.recordDiscovery({ level: '0', routeDecisionId: 'rd-1' });
136
+ assert.equal(r.state, 'ROUTE_DECISION_PENDING');
137
+ assert.equal(r.defaultChoice, 'DIRECT');
138
+
139
+ r = await ctrl.consumeRouteDecision({ decisionId: 'rd-1', choice: 'DIRECT' });
140
+ assert.equal(r.state, 'EXECUTION_ANALYSIS');
141
+
142
+ r = await ctrl.recordExecutionAnalysis({ executionDecisionId: 'ed-1' });
143
+ assert.equal(r.state, 'EXECUTION_DECISION_PENDING');
144
+
145
+ r = await ctrl.consumeExecutionDecision({ decisionId: 'ed-1', mode: 'INLINE' });
146
+ assert.equal(r.state, 'EXECUTING_INLINE');
147
+
148
+ r = await ctrl.implementationComplete();
149
+ assert.equal(r.state, 'SYNC');
150
+
151
+ r = await ctrl.syncComplete();
152
+ assert.equal(r.state, 'DONE');
153
+ });
154
+
155
+ it('rejects invalid transition: start from EXECUTING_INLINE', async () => {
156
+ const ctrl = createController({ state: 'EXECUTING_INLINE', requestId: 'original' });
157
+ const r = await ctrl.startRequest({ requestId: 'nope' });
158
+ assert.equal(r.state, 'EXECUTING_INLINE');
159
+ assert.equal(r.requestId, 'original');
160
+ });
161
+
162
+ it('level 1+ defaults to SPEC', async () => {
163
+ const ctrl = createController();
164
+ const r = await ctrl.recordDiscovery({ level: '1+', routeDecisionId: 'rd-2' });
165
+ assert.equal(r.defaultChoice, 'SPEC');
166
+ });
167
+ });
168
+
169
+ describe('completeTask', () => {
170
+ it('marks task as completed', async () => {
171
+ const ctrl = createController({ state: 'EXECUTING_INLINE' });
172
+ const r = await ctrl.completeTask({
173
+ taskId: 'task-1',
174
+ filePath: 'src/foo.ts',
175
+ fileHash: 'abc123',
176
+ });
177
+ assert.equal(r.status, 'COMPLETED');
178
+ assert.equal(r.totalCompleted, 1);
179
+ });
180
+
181
+ it('rejects completeTask from wrong state', async () => {
182
+ const ctrl = createController();
183
+ const r = await ctrl.completeTask({ taskId: 'task-1' });
184
+ assert.ok(r.error.includes('Cannot complete task from state'));
185
+ });
186
+ });
187
+ });
@@ -236,6 +236,24 @@ else
236
236
  fi
237
237
  echo ""
238
238
 
239
+ # ─── 14. Runtime tests (node:test) ──────────────────────────────────────────
240
+ echo "14. Runtime tests"
241
+ if command -v node &>/dev/null; then
242
+ TEST_OUTPUT=$(cd "$ASSETS_DIR" && node --test tests/controller.test.js tests/agent-contract.test.js 2>&1)
243
+ TEST_EXIT=$?
244
+ TEST_PASS=$(echo "$TEST_OUTPUT" | grep "^# pass" | awk '{print $3}')
245
+ TEST_FAIL=$(echo "$TEST_OUTPUT" | grep "^# fail" | awk '{print $3}')
246
+ if [ "$TEST_EXIT" -eq 0 ]; then
247
+ pass "runtime tests: ${TEST_PASS}/${TEST_PASS} passed"
248
+ else
249
+ fail "runtime tests: ${TEST_FAIL} failed out of $((TEST_PASS + TEST_FAIL))"
250
+ echo "$TEST_OUTPUT" | grep "not ok" | head -5
251
+ fi
252
+ else
253
+ warn "node not found — skipping runtime tests"
254
+ fi
255
+ echo ""
256
+
239
257
  # ─── Summary ──────────────────────────────────────────────────────────────────
240
258
  echo "═══════════════════════════════════════════════════════════"
241
259
  echo " Summary"
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.5.8",
164
+ version: "0.5.10",
165
165
  description: "Instalador interactivo de agentes y comandos para OpenCode",
166
166
  type: "module",
167
167
  bin: {
@@ -879,16 +879,16 @@ import { join as join5 } from "path";
879
879
  import { existsSync as existsSync4 } from "fs";
880
880
  // manifest.json
881
881
  var manifest_default = {
882
- version: "0.5.8",
882
+ version: "0.5.10",
883
883
  repo: "JaimeHoracio/Ostacky",
884
- tag: "v0.5.8",
884
+ tag: "v0.5.10",
885
885
  agents: [
886
886
  {
887
887
  name: "ostacky",
888
888
  file: "assets/agents/ostacky.md",
889
889
  description: "Orquestador principal con ruteo por nivel de impacto, máquina de estados persistida (controller MCP), edición segura con 3 outcomes, y delegación en OpenSpec + Superpowers",
890
- version: "0.5.8",
891
- sha256: "eb3ab4cfcdedee63dd4aae05d748d34d9cfc3323bdc5f53f06848cfe3efd439f"
890
+ version: "0.5.10",
891
+ sha256: "c624b9163a97fe0aa17a4688f59e2bcfa958c64fbde71c05c0c071911f62b7ad"
892
892
  }
893
893
  ],
894
894
  commands: [
@@ -896,14 +896,14 @@ var manifest_default = {
896
896
  name: "install-stack",
897
897
  file: "assets/commands/install-stack.md",
898
898
  description: "Instala el stack tecnológico del proyecto (CodeGraph, skills, OpenSpec, Engram, Context7, controller MCP)",
899
- version: "0.5.8",
900
- sha256: "16538e1ec81887738716dc8f97a3de4f26fc8d218489a6cd20029a8981bdbd95"
899
+ version: "0.5.10",
900
+ sha256: "61f18cb616bffe4a982d874ee9426011c203ab9c17efb88d44592e202c699330"
901
901
  },
902
902
  {
903
903
  name: "opsx-sync",
904
904
  file: "assets/commands/opsx-sync.md",
905
905
  description: "Sincroniza delta specs del change activo sin inicializar CodeGraph si ya existe índice",
906
- version: "0.5.8",
906
+ version: "0.5.10",
907
907
  sha256: "ed9948f1910743b672e1dfad496bc96972a224a98b063232be39290d43068c50"
908
908
  }
909
909
  ],
@@ -912,8 +912,8 @@ var manifest_default = {
912
912
  name: "ostacky-controller",
913
913
  file: "assets/mcp/ostacky-controller/",
914
914
  description: "Máquina de estados persistida para Ostacky: 13 tools MCP para ciclo completo de request (start_request → discovery → route → execution → sync → done), edición segura con validación de transiciones y persistencia atómica",
915
- version: "0.5.8",
916
- sha256: "d78ceb0d761c40212a0fe39db8872f58c78de235e1b0833be4accb8adcc45462"
915
+ version: "0.5.10",
916
+ sha256: "4469b9e26910a5ba016a0fe7a2d77507d40715d95ce4cbb31c498bf7001b4d0d"
917
917
  }
918
918
  ],
919
919
  skills: [
@@ -921,98 +921,98 @@ var manifest_default = {
921
921
  name: "thinking",
922
922
  file: "assets/skills/thinking/SKILL.md",
923
923
  description: "Skill unificado de pensamiento con dos modos: creative-design (producción de diseño → transición a writing-plans o openspec-propose) y open-exploration (exploración libre)",
924
- version: "0.5.8",
924
+ version: "0.5.10",
925
925
  sha256: "6a1fdac86357f89e132013e56cb84c1920880c1c035a8dcf53f69e01824724bb"
926
926
  },
927
927
  {
928
928
  name: "execution-mode-evaluation",
929
929
  file: "assets/skills/execution-mode-evaluation/SKILL.md",
930
930
  description: "Skill de análisis de modo de ejecución — output reconciliado con controller snapshot contract (recommendation field)",
931
- version: "0.5.8",
931
+ version: "0.5.10",
932
932
  sha256: "048925aec576dff014eed0b44e2a553c635ae405f5a245773f480be9b84d18a7"
933
933
  },
934
934
  {
935
935
  name: "writing-plans",
936
936
  file: "assets/skills/writing-plans/SKILL.md",
937
937
  description: "Skill de planificación de implementación (Superpowers) — execution handoff removido, Ostacky decide modo de ejecución",
938
- version: "0.5.8",
938
+ version: "0.5.10",
939
939
  sha256: "13f6e43522567505da90793934d414d290d56f4ea587ac696f66416af9e7633f"
940
940
  },
941
941
  {
942
942
  name: "tdd",
943
943
  file: "assets/skills/tdd/SKILL.md",
944
944
  description: "Skill de test-driven development (Superpowers)",
945
- version: "0.5.8",
945
+ version: "0.5.10",
946
946
  sha256: "7eacd8ee81dc5c0b85065c0392e37ee10314661f7edb59dd8e70a9e0dae8f371"
947
947
  },
948
948
  {
949
949
  name: "subagent-driven-development",
950
950
  file: "assets/skills/subagent-driven-development/SKILL.md",
951
951
  description: "Skill de ejecución con subagentes (Superpowers) — ejecuta solo después de confirmación del coordinador Ostacky",
952
- version: "0.5.8",
952
+ version: "0.5.10",
953
953
  sha256: "cdacae2e5b86f6a642ad0316172cee7dc8d19e909ef4f9ab7fcf7e84a248dda3"
954
954
  },
955
955
  {
956
956
  name: "dispatching-parallel-agents",
957
957
  file: "assets/skills/dispatching-parallel-agents/SKILL.md",
958
958
  description: "Skill de dispatch paralelo de agentes (Superpowers)",
959
- version: "0.5.8",
959
+ version: "0.5.10",
960
960
  sha256: "281edf0c38f358497c7e2066fa8217a2ba3e2a39b4205c4af0c41d328fc035a1"
961
961
  },
962
962
  {
963
963
  name: "review",
964
964
  file: "assets/skills/review/SKILL.md",
965
965
  description: "Skill de revisión de código (Superpowers)",
966
- version: "0.5.8",
966
+ version: "0.5.10",
967
967
  sha256: "14831d9ef3746ef6e2e6047c868fe7aa296d01f0644227ce776fe99436357b1a"
968
968
  },
969
969
  {
970
970
  name: "receiving-code-review",
971
971
  file: "assets/skills/receiving-code-review/SKILL.md",
972
972
  description: "Skill de recibir y procesar feedback de code review",
973
- version: "0.5.8",
973
+ version: "0.5.10",
974
974
  sha256: "0a5780e8a41539d15428114b7c46f36670acc16aba0db3348d36be1175433872"
975
975
  },
976
976
  {
977
977
  name: "openspec-propose",
978
978
  file: "assets/skills/openspec-propose/SKILL.md",
979
979
  description: "Skill de generación de proposal (OpenSpec)",
980
- version: "0.5.8",
980
+ version: "0.5.10",
981
981
  sha256: "3306b21ba9cb8c1611e8ba335126982f3767f7d317b5ed506c57a14c5f88b263"
982
982
  },
983
983
  {
984
984
  name: "openspec-apply-change",
985
985
  file: "assets/skills/openspec-apply-change/SKILL.md",
986
986
  description: "Skill de aplicación de change (OpenSpec)",
987
- version: "0.5.8",
987
+ version: "0.5.10",
988
988
  sha256: "31c1313c3de4616a07efd89306c4c058175d018f42d4381cc7da7030fd03ba30"
989
989
  },
990
990
  {
991
991
  name: "openspec-archive-change",
992
992
  file: "assets/skills/openspec-archive-change/SKILL.md",
993
993
  description: "Skill de archivo de change (OpenSpec)",
994
- version: "0.5.8",
994
+ version: "0.5.10",
995
995
  sha256: "5bf65d1848457bd57be1d21707e55fe6746cdee33bce2f32f12d87661b6aa3a4"
996
996
  },
997
997
  {
998
998
  name: "using-git-worktrees",
999
999
  file: "assets/skills/using-git-worktrees/SKILL.md",
1000
1000
  description: "Skill de uso de git worktrees para aislamiento de trabajo",
1001
- version: "0.5.8",
1001
+ version: "0.5.10",
1002
1002
  sha256: "33433c24f753d566cde16c0931f70d746b6b0cb4d1f3daaa619e928b009e4fc9"
1003
1003
  },
1004
1004
  {
1005
1005
  name: "using-superpowers",
1006
1006
  file: "assets/skills/using-superpowers/SKILL.md",
1007
1007
  description: "Skill de orquestación de Superpowers skills",
1008
- version: "0.5.8",
1008
+ version: "0.5.10",
1009
1009
  sha256: "fba542003aa788b5edb4ae2d1135773a1dde8111d1dd60378e2f61b6695d46fc"
1010
1010
  },
1011
1011
  {
1012
1012
  name: "writing-skills",
1013
1013
  file: "assets/skills/writing-skills/SKILL.md",
1014
1014
  description: "Skill de creación y edición de skills",
1015
- version: "0.5.8",
1015
+ version: "0.5.10",
1016
1016
  sha256: "7f74ffe049283803640d78f8c9ac1ea5f5e626dedb885ecff29a87c5b5d41598"
1017
1017
  }
1018
1018
  ]
@@ -1204,7 +1204,7 @@ function clearLockfile(opencodeRoot) {
1204
1204
  const lockfile = readLockfile(opencodeRoot);
1205
1205
  if (!lockfile) {
1206
1206
  writeLockfile(opencodeRoot, {
1207
- version: "0.5.8",
1207
+ version: "0.5.10",
1208
1208
  lockedAt: new Date().toISOString(),
1209
1209
  repo: "",
1210
1210
  tag: "",
@@ -1514,9 +1514,8 @@ async function installMcpServer(item, manifest, paths) {
1514
1514
  if (output)
1515
1515
  console.error(output);
1516
1516
  } catch (e2) {
1517
- throw new Error(`MCP server "${item.name}" copiado pero ${installCmd} falló: ${e2.message}.
1518
- ` + `Resolvé las dependencias manualmente:
1519
- ` + ` cd .opencode/mcp/${item.name} && bun install`);
1517
+ console.error(`[WARN] MCP server "${item.name}" copiado pero ${installCmd} falló: ${e2.message}.
1518
+ ` + ` Resolvé las dependencias manualmente: cd .opencode/mcp/${item.name} && bun install`);
1520
1519
  }
1521
1520
  }
1522
1521
  upsertLockfile(paths, "mcpServers", item, manifest, treeHash);