ostacky 0.8.1 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -23
- package/assets/agents/ostacky.md +18 -3
- package/assets/commands/install-stack.md +15 -115
- package/assets/docs/ostacky-reference.md +0 -2
- package/assets/mcp/ostacky-controller/controller-core.js +206 -0
- package/assets/mcp/ostacky-controller/index.js +166 -164
- package/assets/mcp/ostacky-controller/package.json +1 -1
- package/assets/plugins/controller-core.ts +206 -0
- package/assets/plugins/ostacky-plugin.ts +12 -124
- package/assets/skills/brainstorming/SKILL.md +6 -4
- package/assets/skills/graceful-degradation/SKILL.md +0 -9
- package/dist/cli.js +537 -720
- package/manifest.json +151 -151
- package/package.json +1 -1
|
@@ -16,6 +16,7 @@ import { readFileSync, writeFileSync, renameSync, mkdirSync, existsSync, statSyn
|
|
|
16
16
|
import { join, dirname, basename, resolve, relative } from "node:path"
|
|
17
17
|
import { SENSITIVE_DEFAULT, BASH_SENSITIVE_RE, isSensitive, extractPathsFromBash } from "../../src/security.ts"
|
|
18
18
|
import { isTrivial } from "../../src/tiered.ts"
|
|
19
|
+
import { STATES, TRANSITIONS, DEFAULT_STATE } from "./controller-core.ts"
|
|
19
20
|
|
|
20
21
|
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
21
22
|
|
|
@@ -25,127 +26,7 @@ const IDLE_THRESHOLD_MS = 45_000
|
|
|
25
26
|
const PURPLE_TENUE = "\x1b[38;5;183m"
|
|
26
27
|
const PURPLE_RESET = "\x1b[0m"
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
INTERPRETATION_PENDING: "INTERPRETATION_PENDING",
|
|
30
|
-
CLARIFICATION_PENDING: "CLARIFICATION_PENDING",
|
|
31
|
-
DISCOVERY: "DISCOVERY",
|
|
32
|
-
ROUTE_DECISION_PENDING: "ROUTE_DECISION_PENDING",
|
|
33
|
-
SPECIFICATION: "SPECIFICATION",
|
|
34
|
-
EXECUTION_ANALYSIS: "EXECUTION_ANALYSIS",
|
|
35
|
-
EXECUTION_DECISION_PENDING: "EXECUTION_DECISION_PENDING",
|
|
36
|
-
EXECUTING_INLINE: "EXECUTING_INLINE",
|
|
37
|
-
EXECUTING_SUBAGENTS: "EXECUTING_SUBAGENTS",
|
|
38
|
-
SYNC: "SYNC",
|
|
39
|
-
DONE: "DONE",
|
|
40
|
-
BLOCKED: "BLOCKED",
|
|
41
|
-
} as const)
|
|
42
|
-
|
|
43
|
-
const TRANSITIONS: Record<string, Array<{ via: string; to: string; choice?: string; mode?: string }>> = {
|
|
44
|
-
INTERPRETATION_PENDING: [
|
|
45
|
-
{ via: "request_clarification", to: "CLARIFICATION_PENDING" },
|
|
46
|
-
{ via: "proceed_to_discovery", to: "DISCOVERY" },
|
|
47
|
-
{ via: "record_discovery", to: "ROUTE_DECISION_PENDING" },
|
|
48
|
-
{ via: "block", to: "BLOCKED" },
|
|
49
|
-
],
|
|
50
|
-
CLARIFICATION_PENDING: [
|
|
51
|
-
{ via: "record_clarification", to: "DISCOVERY" },
|
|
52
|
-
{ via: "block", to: "BLOCKED" },
|
|
53
|
-
{ via: "abandon", to: "BLOCKED" },
|
|
54
|
-
],
|
|
55
|
-
DISCOVERY: [
|
|
56
|
-
{ via: "record_discovery", to: "ROUTE_DECISION_PENDING" },
|
|
57
|
-
{ via: "block", to: "BLOCKED" },
|
|
58
|
-
{ via: "abandon", to: "BLOCKED" },
|
|
59
|
-
],
|
|
60
|
-
ROUTE_DECISION_PENDING: [
|
|
61
|
-
{ via: "consume_route_decision", to: "SPECIFICATION", choice: "SPEC" },
|
|
62
|
-
{ via: "consume_route_decision", to: "EXECUTION_ANALYSIS", choice: "DIRECT" },
|
|
63
|
-
{ via: "block", to: "BLOCKED" },
|
|
64
|
-
{ via: "abandon", to: "BLOCKED" },
|
|
65
|
-
],
|
|
66
|
-
SPECIFICATION: [
|
|
67
|
-
{ via: "spec_complete", to: "EXECUTION_ANALYSIS" },
|
|
68
|
-
{ via: "block", to: "BLOCKED" },
|
|
69
|
-
{ via: "abandon", to: "BLOCKED" },
|
|
70
|
-
],
|
|
71
|
-
EXECUTION_ANALYSIS: [
|
|
72
|
-
{ via: "record_execution_analysis", to: "EXECUTION_DECISION_PENDING" },
|
|
73
|
-
{ via: "block", to: "BLOCKED" },
|
|
74
|
-
{ via: "abandon", to: "BLOCKED" },
|
|
75
|
-
],
|
|
76
|
-
EXECUTION_DECISION_PENDING: [
|
|
77
|
-
{ via: "consume_execution_decision", to: "EXECUTING_INLINE", mode: "INLINE" },
|
|
78
|
-
{ via: "consume_execution_decision", to: "EXECUTING_SUBAGENTS", mode: "SUBAGENT_DRIVEN" },
|
|
79
|
-
{ via: "block", to: "BLOCKED" },
|
|
80
|
-
{ via: "abandon", to: "BLOCKED" },
|
|
81
|
-
],
|
|
82
|
-
EXECUTING_INLINE: [
|
|
83
|
-
{ via: "implementation_complete", to: "SYNC" },
|
|
84
|
-
{ via: "block", to: "BLOCKED" },
|
|
85
|
-
],
|
|
86
|
-
EXECUTING_SUBAGENTS: [
|
|
87
|
-
{ via: "implementation_complete", to: "SYNC" },
|
|
88
|
-
{ via: "block", to: "BLOCKED" },
|
|
89
|
-
],
|
|
90
|
-
BLOCKED: [
|
|
91
|
-
{ via: "replan", to: "INTERPRETATION_PENDING" },
|
|
92
|
-
{ via: "abandon", to: "DONE" },
|
|
93
|
-
],
|
|
94
|
-
SYNC: [
|
|
95
|
-
{ via: "sync_complete", to: "DONE" },
|
|
96
|
-
{ via: "block", to: "BLOCKED" },
|
|
97
|
-
],
|
|
98
|
-
DONE: [],
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const DEFAULT_STATE: any = {
|
|
102
|
-
state: STATES.INTERPRETATION_PENDING,
|
|
103
|
-
revision: 0,
|
|
104
|
-
requestId: null,
|
|
105
|
-
changeId: null,
|
|
106
|
-
routeDecisionId: null,
|
|
107
|
-
routeChoice: null,
|
|
108
|
-
level: null,
|
|
109
|
-
executionDecisionId: null,
|
|
110
|
-
executionMode: null,
|
|
111
|
-
snapshots: { codegraph: null, execution: null },
|
|
112
|
-
tasks: {},
|
|
113
|
-
fileFingerprints: {},
|
|
114
|
-
error: null,
|
|
115
|
-
lastHandoff: null,
|
|
116
|
-
expectedTasks: null,
|
|
117
|
-
expectedTaskCount: null,
|
|
118
|
-
auditSeq: 0,
|
|
119
|
-
degraded: false,
|
|
120
|
-
schemaVersion: 1,
|
|
121
|
-
stateOversizedCount: 0,
|
|
122
|
-
codegraphBypassCount: 0,
|
|
123
|
-
degradedEditsCount: 0,
|
|
124
|
-
cacheHitCount: 0,
|
|
125
|
-
cacheMissCount: 0,
|
|
126
|
-
tokenSavingEstimate: 0,
|
|
127
|
-
discoveryCacheHitCount: 0,
|
|
128
|
-
redundantCallCount: 0,
|
|
129
|
-
cacheMissWithoutPutCount: 0,
|
|
130
|
-
stateCheckCount: 0,
|
|
131
|
-
toolCallCount: 0,
|
|
132
|
-
lastProposal: null,
|
|
133
|
-
allowedFiles: {},
|
|
134
|
-
deniedFiles: {},
|
|
135
|
-
sensitivePatterns: SENSITIVE_DEFAULT,
|
|
136
|
-
sensitiveAccess: { allowed: 0, denied: 0, blockedAttempts: 0 },
|
|
137
|
-
staleContentAttempts: 0,
|
|
138
|
-
completeWithoutValidateCount: 0,
|
|
139
|
-
toolTimeoutCount: 0,
|
|
140
|
-
lastToolDurationMs: 0,
|
|
141
|
-
stateDurationMs: 0,
|
|
142
|
-
subagentFailedCount: 0,
|
|
143
|
-
lastValidated: null,
|
|
144
|
-
pendingFileAccess: {},
|
|
145
|
-
lastHeartbeat: 0,
|
|
146
|
-
watchdogEnabled: true,
|
|
147
|
-
ts: Date.now(),
|
|
148
|
-
}
|
|
29
|
+
// DEFAULT_STATE imported from controller-core.ts
|
|
149
30
|
|
|
150
31
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
151
32
|
|
|
@@ -558,13 +439,20 @@ export const OstackyController: Plugin = async (ctx) => {
|
|
|
558
439
|
const s = readState((input as any).ctx?.directory ?? "")
|
|
559
440
|
} catch {}
|
|
560
441
|
}
|
|
561
|
-
// Heartbeat + color purple tenue: Ostacky vs modelo (fácil)
|
|
442
|
+
// Heartbeat + color purple tenue: Ostacky vs modelo (fácil) — single-writer: plugin memo, no persist cada tool (D10)
|
|
443
|
+
let lastHeartbeatMem = 0
|
|
562
444
|
try {
|
|
563
445
|
const dir = ctx.directory
|
|
564
446
|
const s = readState(dir)
|
|
565
447
|
if (s) {
|
|
566
|
-
|
|
567
|
-
|
|
448
|
+
const now = Date.now()
|
|
449
|
+
lastHeartbeatMem = now
|
|
450
|
+
// Single-writer: solo persiste si OSTACKY_PLUGIN_PERSIST=1 o idle>30s o state cambió
|
|
451
|
+
const shouldPersist = process.env.OSTACKY_PLUGIN_PERSIST === "1" || (now - (s.lastHeartbeat || 0) > 30000)
|
|
452
|
+
if (shouldPersist) {
|
|
453
|
+
s.lastHeartbeat = now
|
|
454
|
+
try { persistState(dir, s) } catch {}
|
|
455
|
+
}
|
|
568
456
|
if (["EXECUTING_INLINE", "EXECUTING_SUBAGENTS", "SYNC"].includes(s.state) && output && typeof output.title === "string" && output.title && !output.title.includes("🟣")) {
|
|
569
457
|
output.title = `🟣 ${PURPLE_TENUE}[OSTACKY]${PURPLE_RESET} ${output.title}`
|
|
570
458
|
}
|
|
@@ -38,12 +38,14 @@ Do NOT invoke any implementation skill, write any code, or scaffold any project
|
|
|
38
38
|
|
|
39
39
|
1. **Check discovery-cache first** — `src/discovery-cache.ts` `getDiscoverySnapshot(query)` + `getEngramDedup(query, requestId)`. Si hit válido (TTL+gitDiffHash), **reusar** sin llamar tools. Solo si miss → `engram_mem_search` + `codegraph_codegraph_explore` y **SHALL `putDiscoverySnapshot`** antes de avanzar. No re-llamar si área difiere <30% del snapshot.
|
|
40
40
|
2. **Ask clarifying questions** — one at a time, understand purpose/constraints/success criteria
|
|
41
|
-
4. **Propose 2-3 approaches (hardening-v2 — SHALL)** — con tabla trade-offs (coste|riesgo|complejidad) + evidencia CodeGraph+Engram
|
|
41
|
+
4. **Propose 2-3 approaches (hardening-v2 — SHALL)** — con tabla trade-offs (coste|riesgo|complejidad) + evidencia CodeGraph+Engram sin alucinar, YAGNI, y recomendación con razón; cada approach cita symbols existentes y mem_search hits verificables
|
|
42
42
|
5. **Present design** — in sections scaled to complexity, get user approval after each section. **Gate post-brainstorming (hardening-v2):** tras presentar diseño, preguntar "¿Procedo con este diseño o querés ajustar algo?" y esperar confirmación explícita antes de `record_discovery`, `openspec-propose` o implementación directa
|
|
43
|
-
6. **Write design doc (output path condicional — router exclusivo):**
|
|
44
|
-
-
|
|
45
|
-
- Si trigger + `
|
|
43
|
+
6. **Write design doc (output path condicional — router exclusivo) — SHALL read fresco + edit no write + sync proactiva:**
|
|
44
|
+
- Antes de `write` SHALL `read` fresco de `openspec/changes/<id>/{proposal,design,tasks}` o `docs/superpowers/specs/*.md` si existen; si existen SHALL `edit` (no `write`); `write` solo si no existía. `getDiscoverySnapshot` no aplica a specs/docs. Para `open-explore` sin archivo SHALL `mem_save topic_key:brainstorm/<hash>` por iteración.
|
|
45
|
+
- Si trigger + `level 1+` no-downgradeable (`estLines>30` o `fileCount>2` o API pública) y change activo → escribir `openspec/changes/<id>/design.md` sección `## Alternatives Considered` con 2-3 approaches (tabla coste|riesgo|complejidad + evidencia CodeGraph+Engram), no `docs/`. No invocar `openspec-propose` separado — este es el diseño. Si ya existe, SHALL `edit` no `write`.
|
|
46
|
+
- Si trigger + `0/0+1` o `1+` downgradeable (`estLines<30`&&`fileCount==1`&&sin API) → solo `docs/superpowers/specs/YYYY-MM-DD-<topic>-design.md` + gate único `¿Procedo?` → DIRECT sin change. SHALL sugerir downgrade: `"Esto parece 0+1 (~X líneas, 1 archivo). ¿Lo tratamos sin spec?"` y esperar. Si `docsSpec` ya existe SHALL `read` + `edit`.
|
|
46
47
|
- Gate post-brainstorming es **único**; no hay segundo `¿Procedo con spec?`. `get_audit` `WARN:duplicate_design_generated` si ambos artefactos mismo `requestId`.
|
|
48
|
+
- **Sync proactiva SHALL:** al final de cada turno con decisiones nuevas no reflejadas en disco SHALL listar cálido `Noté que lo que acordamos (X por Z) aún no está en <archivo>: 1) ...` (max 3) y proponer en UNA sola pregunta `¿Querés que agregue [X, Y] a <archivo> e implemente <mejor propuesta> —la recomiendo por <tradeoff>—?` (respetando Regla 5). Para brainstorming SHALL indicar `¿Querés que implementemos lo último que charlamos (B, recomendada por <tradeoff>) o preferís que vaya con A?`
|
|
47
49
|
7. **Spec self-review** — check for placeholders, contradictions, ambiguity, scope
|
|
48
50
|
8. **User reviews spec** — ask user to review before proceeding
|
|
49
51
|
9. **Save to Engram** — `engram_mem_save` with the design decision and tradeoffs
|
|
@@ -20,7 +20,6 @@ When critical tools are unavailable, this skill provides a systematic approach t
|
|
|
20
20
|
| CodeGraph | MCP server | Engram → Read + Glob | No structural analysis, manual exploration |
|
|
21
21
|
| Engram | MCP server | Continue without memory | No persistence across sessions |
|
|
22
22
|
| Controller | MCP server | Inline validation + manual state | No state machine, no edit validation |
|
|
23
|
-
| Context7 | MCP server (remote) | Skip documentation | No external API docs |
|
|
24
23
|
|
|
25
24
|
## Detection
|
|
26
25
|
|
|
@@ -108,12 +107,6 @@ Incluso en degraded, el guard de credenciales sigue activo (hard gate incluso en
|
|
|
108
107
|
- No state persistence across crashes
|
|
109
108
|
- No pending state enforcement (check_pending_state unavailable)
|
|
110
109
|
|
|
111
|
-
### Without Context7
|
|
112
|
-
|
|
113
|
-
**Impact:** Minimal — only affects external API documentation lookups.
|
|
114
|
-
|
|
115
|
-
**Workflow:** Proceed normally. Use existing knowledge.
|
|
116
|
-
|
|
117
110
|
## Communication Protocol
|
|
118
111
|
|
|
119
112
|
### Status Report Format
|
|
@@ -141,7 +134,6 @@ Herramientas disponibles:
|
|
|
141
134
|
✅ Controller — ping OK
|
|
142
135
|
❌ CodeGraph — timeout 10s
|
|
143
136
|
✅ Engram — disponible
|
|
144
|
-
✅ Context7 — disponible
|
|
145
137
|
|
|
146
138
|
¿Continuar con lectura manual de archivos?
|
|
147
139
|
```
|
|
@@ -154,7 +146,6 @@ Herramientas disponibles:
|
|
|
154
146
|
❌ Controller — no responde
|
|
155
147
|
❌ CodeGraph — no instalado
|
|
156
148
|
❌ Engram — timeout 5s
|
|
157
|
-
✅ Context7 — disponible (pero sin docs relevantes)
|
|
158
149
|
|
|
159
150
|
Modo básico: sin validación de edits, sin memoria persistente, sin análisis estructural.
|
|
160
151
|
¿Continuar o cancelar?
|