@kl-c/matrixos 0.1.7 → 0.1.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.
- package/.agents/command/adopt.md +50 -0
- package/.agents/command/features.md +35 -0
- package/.opencode/command/adopt.md +50 -0
- package/.opencode/command/features.md +35 -0
- package/dist/cli/config-manager/write-gateway-env.d.ts +7 -0
- package/dist/cli/index.js +942 -789
- package/dist/cli/install-validators.d.ts +1 -0
- package/dist/cli/skills/debugging/references/methodology/02-investigate.md +1 -1
- package/dist/cli/skills/refactor/SKILL.md +1 -1
- package/dist/cli/skills/superpowers-brainstorming/SKILL.md +162 -0
- package/dist/cli/skills/superpowers-brainstorming/SUPERPOWERS_LICENSE +21 -0
- package/dist/cli/skills/superpowers-brainstorming/scripts/frame-template.html +213 -0
- package/dist/cli/skills/superpowers-brainstorming/scripts/helper.js +167 -0
- package/dist/cli/skills/superpowers-brainstorming/scripts/server.cjs +723 -0
- package/dist/cli/skills/superpowers-brainstorming/scripts/start-server.sh +209 -0
- package/dist/cli/skills/superpowers-brainstorming/scripts/stop-server.sh +120 -0
- package/dist/cli/skills/superpowers-brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/dist/cli/skills/superpowers-brainstorming/visual-companion.md +291 -0
- package/dist/cli/skills/superpowers-receiving-code-review/SKILL.md +216 -0
- package/dist/cli/skills/superpowers-receiving-code-review/SUPERPOWERS_LICENSE +21 -0
- package/dist/cli/skills/superpowers-systematic-debugging/CREATION-LOG.md +119 -0
- package/dist/cli/skills/superpowers-systematic-debugging/SKILL.md +299 -0
- package/dist/cli/skills/superpowers-systematic-debugging/SUPERPOWERS_LICENSE +21 -0
- package/dist/cli/skills/superpowers-systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/dist/cli/skills/superpowers-systematic-debugging/condition-based-waiting.md +115 -0
- package/dist/cli/skills/superpowers-systematic-debugging/defense-in-depth.md +122 -0
- package/dist/cli/skills/superpowers-systematic-debugging/find-polluter.sh +63 -0
- package/dist/cli/skills/superpowers-systematic-debugging/root-cause-tracing.md +169 -0
- package/dist/cli/skills/superpowers-systematic-debugging/test-academic.md +14 -0
- package/dist/cli/skills/superpowers-systematic-debugging/test-pressure-1.md +58 -0
- package/dist/cli/skills/superpowers-systematic-debugging/test-pressure-2.md +68 -0
- package/dist/cli/skills/superpowers-systematic-debugging/test-pressure-3.md +69 -0
- package/dist/cli/skills/superpowers-writing-skills/SKILL.md +692 -0
- package/dist/cli/skills/superpowers-writing-skills/SUPERPOWERS_LICENSE +21 -0
- package/dist/cli/skills/superpowers-writing-skills/anthropic-best-practices.md +1150 -0
- package/dist/cli/skills/superpowers-writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/dist/cli/skills/superpowers-writing-skills/graphviz-conventions.dot +172 -0
- package/dist/cli/skills/superpowers-writing-skills/persuasion-principles.md +187 -0
- package/dist/cli/skills/superpowers-writing-skills/render-graphs.js +168 -0
- package/dist/cli/skills/superpowers-writing-skills/testing-skills-with-subagents.md +384 -0
- package/dist/cli/types.d.ts +4 -0
- package/dist/cli-node/index.js +942 -789
- package/dist/config/schema/oh-my-opencode-config.d.ts +6 -0
- package/dist/config/schema/watchdog.d.ts +6 -0
- package/dist/features/anti-loop/background-registry.d.ts +10 -0
- package/dist/features/dashboard/frontend/css/style.css +211 -0
- package/dist/features/dashboard/frontend/index.html +173 -0
- package/dist/features/dashboard/frontend/js/api.js +2 -0
- package/dist/features/dashboard/frontend/js/app.js +349 -0
- package/dist/features/matrix-inventory/features.d.ts +45 -0
- package/dist/hooks/agent-anti-loop/hook.d.ts +16 -0
- package/dist/index.js +220 -45
- package/dist/{oh-my-opencode.schema.json → matrixos.schema.json} +43 -1
- package/dist/plugin/agent-anti-loop.d.ts +6 -0
- package/dist/skills/debugging/references/methodology/02-investigate.md +1 -1
- package/dist/skills/refactor/SKILL.md +1 -1
- package/dist/skills/superpowers-brainstorming/SKILL.md +162 -0
- package/dist/skills/superpowers-brainstorming/SUPERPOWERS_LICENSE +21 -0
- package/dist/skills/superpowers-brainstorming/scripts/frame-template.html +213 -0
- package/dist/skills/superpowers-brainstorming/scripts/helper.js +167 -0
- package/dist/skills/superpowers-brainstorming/scripts/server.cjs +723 -0
- package/dist/skills/superpowers-brainstorming/scripts/start-server.sh +209 -0
- package/dist/skills/superpowers-brainstorming/scripts/stop-server.sh +120 -0
- package/dist/skills/superpowers-brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/dist/skills/superpowers-brainstorming/visual-companion.md +291 -0
- package/dist/skills/superpowers-receiving-code-review/SKILL.md +216 -0
- package/dist/skills/superpowers-receiving-code-review/SUPERPOWERS_LICENSE +21 -0
- package/dist/skills/superpowers-systematic-debugging/CREATION-LOG.md +119 -0
- package/dist/skills/superpowers-systematic-debugging/SKILL.md +299 -0
- package/dist/skills/superpowers-systematic-debugging/SUPERPOWERS_LICENSE +21 -0
- package/dist/skills/superpowers-systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/dist/skills/superpowers-systematic-debugging/condition-based-waiting.md +115 -0
- package/dist/skills/superpowers-systematic-debugging/defense-in-depth.md +122 -0
- package/dist/skills/superpowers-systematic-debugging/find-polluter.sh +63 -0
- package/dist/skills/superpowers-systematic-debugging/root-cause-tracing.md +169 -0
- package/dist/skills/superpowers-systematic-debugging/test-academic.md +14 -0
- package/dist/skills/superpowers-systematic-debugging/test-pressure-1.md +58 -0
- package/dist/skills/superpowers-systematic-debugging/test-pressure-2.md +68 -0
- package/dist/skills/superpowers-systematic-debugging/test-pressure-3.md +69 -0
- package/dist/skills/superpowers-writing-skills/SKILL.md +692 -0
- package/dist/skills/superpowers-writing-skills/SUPERPOWERS_LICENSE +21 -0
- package/dist/skills/superpowers-writing-skills/anthropic-best-practices.md +1150 -0
- package/dist/skills/superpowers-writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/dist/skills/superpowers-writing-skills/graphviz-conventions.dot +172 -0
- package/dist/skills/superpowers-writing-skills/persuasion-principles.md +187 -0
- package/dist/skills/superpowers-writing-skills/render-graphs.js +168 -0
- package/dist/skills/superpowers-writing-skills/testing-skills-with-subagents.md +384 -0
- package/dist/testing/create-gateway-relay.d.ts +26 -0
- package/dist/tui.js +13 -0
- package/package.json +2 -1
- package/packages/shared-skills/skills/debugging/references/methodology/02-investigate.md +1 -1
- package/packages/shared-skills/skills/refactor/SKILL.md +1 -1
- package/packages/shared-skills/skills/superpowers-brainstorming/SKILL.md +162 -0
- package/packages/shared-skills/skills/superpowers-brainstorming/SUPERPOWERS_LICENSE +21 -0
- package/packages/shared-skills/skills/superpowers-brainstorming/scripts/frame-template.html +213 -0
- package/packages/shared-skills/skills/superpowers-brainstorming/scripts/helper.js +167 -0
- package/packages/shared-skills/skills/superpowers-brainstorming/scripts/server.cjs +723 -0
- package/packages/shared-skills/skills/superpowers-brainstorming/scripts/start-server.sh +209 -0
- package/packages/shared-skills/skills/superpowers-brainstorming/scripts/stop-server.sh +120 -0
- package/packages/shared-skills/skills/superpowers-brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/packages/shared-skills/skills/superpowers-brainstorming/visual-companion.md +291 -0
- package/packages/shared-skills/skills/superpowers-receiving-code-review/SKILL.md +216 -0
- package/packages/shared-skills/skills/superpowers-receiving-code-review/SUPERPOWERS_LICENSE +21 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/CREATION-LOG.md +119 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/SKILL.md +299 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/SUPERPOWERS_LICENSE +21 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/condition-based-waiting.md +115 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/defense-in-depth.md +122 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/find-polluter.sh +63 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/root-cause-tracing.md +169 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/test-academic.md +14 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/test-pressure-1.md +58 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/test-pressure-2.md +68 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/test-pressure-3.md +69 -0
- package/packages/shared-skills/skills/superpowers-writing-skills/SKILL.md +692 -0
- package/packages/shared-skills/skills/superpowers-writing-skills/SUPERPOWERS_LICENSE +21 -0
- package/packages/shared-skills/skills/superpowers-writing-skills/anthropic-best-practices.md +1150 -0
- package/packages/shared-skills/skills/superpowers-writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/packages/shared-skills/skills/superpowers-writing-skills/graphviz-conventions.dot +172 -0
- package/packages/shared-skills/skills/superpowers-writing-skills/persuasion-principles.md +187 -0
- package/packages/shared-skills/skills/superpowers-writing-skills/render-graphs.js +168 -0
- package/packages/shared-skills/skills/superpowers-writing-skills/testing-skills-with-subagents.md +384 -0
package/dist/index.js
CHANGED
|
@@ -263536,10 +263536,10 @@ var require_on_exit_leak_free = __commonJS((exports, module) => {
|
|
|
263536
263536
|
exit: onExit,
|
|
263537
263537
|
beforeExit: onBeforeExit
|
|
263538
263538
|
};
|
|
263539
|
-
var
|
|
263539
|
+
var registry3;
|
|
263540
263540
|
function ensureRegistry() {
|
|
263541
|
-
if (
|
|
263542
|
-
|
|
263541
|
+
if (registry3 === undefined) {
|
|
263542
|
+
registry3 = new FinalizationRegistry(clear);
|
|
263543
263543
|
}
|
|
263544
263544
|
}
|
|
263545
263545
|
function install(event) {
|
|
@@ -263554,7 +263554,7 @@ var require_on_exit_leak_free = __commonJS((exports, module) => {
|
|
|
263554
263554
|
}
|
|
263555
263555
|
process.removeListener(event, functions[event]);
|
|
263556
263556
|
if (refs.exit.length === 0 && refs.beforeExit.length === 0) {
|
|
263557
|
-
|
|
263557
|
+
registry3 = undefined;
|
|
263558
263558
|
}
|
|
263559
263559
|
}
|
|
263560
263560
|
function onExit() {
|
|
@@ -263588,7 +263588,7 @@ var require_on_exit_leak_free = __commonJS((exports, module) => {
|
|
|
263588
263588
|
const ref = new WeakRef(obj);
|
|
263589
263589
|
ref.fn = fn;
|
|
263590
263590
|
ensureRegistry();
|
|
263591
|
-
|
|
263591
|
+
registry3.register(obj, ref);
|
|
263592
263592
|
refs[event].push(ref);
|
|
263593
263593
|
}
|
|
263594
263594
|
function register(obj, fn) {
|
|
@@ -263598,10 +263598,10 @@ var require_on_exit_leak_free = __commonJS((exports, module) => {
|
|
|
263598
263598
|
_register("beforeExit", obj, fn);
|
|
263599
263599
|
}
|
|
263600
263600
|
function unregister(obj) {
|
|
263601
|
-
if (
|
|
263601
|
+
if (registry3 === undefined) {
|
|
263602
263602
|
return;
|
|
263603
263603
|
}
|
|
263604
|
-
|
|
263604
|
+
registry3.unregister(obj);
|
|
263605
263605
|
for (const event of ["exit", "beforeExit"]) {
|
|
263606
263606
|
refs[event] = refs[event].filter((ref) => {
|
|
263607
263607
|
const _obj = ref.deref();
|
|
@@ -263787,7 +263787,7 @@ var require_thread_stream = __commonJS((exports, module) => {
|
|
|
263787
263787
|
}
|
|
263788
263788
|
var FinalizationRegistry2 = process.env.NODE_V8_COVERAGE ? FakeFinalizationRegistry : global.FinalizationRegistry || FakeFinalizationRegistry;
|
|
263789
263789
|
var WeakRef2 = process.env.NODE_V8_COVERAGE ? FakeWeakRef : global.WeakRef || FakeWeakRef;
|
|
263790
|
-
var
|
|
263790
|
+
var registry3 = new FinalizationRegistry2((worker) => {
|
|
263791
263791
|
if (worker.exited) {
|
|
263792
263792
|
return;
|
|
263793
263793
|
}
|
|
@@ -263815,7 +263815,7 @@ var require_thread_stream = __commonJS((exports, module) => {
|
|
|
263815
263815
|
worker.stream = new FakeWeakRef(stream3);
|
|
263816
263816
|
worker.on("message", onWorkerMessage);
|
|
263817
263817
|
worker.on("exit", onWorkerExit);
|
|
263818
|
-
|
|
263818
|
+
registry3.register(stream3, worker);
|
|
263819
263819
|
return worker;
|
|
263820
263820
|
}
|
|
263821
263821
|
function drain(stream3) {
|
|
@@ -263918,7 +263918,7 @@ var require_thread_stream = __commonJS((exports, module) => {
|
|
|
263918
263918
|
if (stream3 === undefined) {
|
|
263919
263919
|
return;
|
|
263920
263920
|
}
|
|
263921
|
-
|
|
263921
|
+
registry3.unregister(stream3);
|
|
263922
263922
|
stream3.worker.exited = true;
|
|
263923
263923
|
stream3.worker.off("exit", onWorkerExit);
|
|
263924
263924
|
destroy(stream3, code !== 0 ? new Error("the worker thread exited") : null);
|
|
@@ -297388,10 +297388,10 @@ var init_loader2 = __esm(() => {
|
|
|
297388
297388
|
|
|
297389
297389
|
// packages/omo-config-core/src/loader/profile-resolution.ts
|
|
297390
297390
|
function registryLookup(config3, name) {
|
|
297391
|
-
const
|
|
297392
|
-
if (!
|
|
297391
|
+
const registry3 = config3.profileRegistry;
|
|
297392
|
+
if (!registry3)
|
|
297393
297393
|
return;
|
|
297394
|
-
const found =
|
|
297394
|
+
const found = registry3.profiles[name];
|
|
297395
297395
|
if (!found)
|
|
297396
297396
|
return;
|
|
297397
297397
|
const parsed = ProfileSchema.safeParse(found);
|
|
@@ -367932,7 +367932,7 @@ function getCachedVersion(options = {}) {
|
|
|
367932
367932
|
// package.json
|
|
367933
367933
|
var package_default = {
|
|
367934
367934
|
name: "@kl-c/matrixos",
|
|
367935
|
-
version: "0.1.
|
|
367935
|
+
version: "0.1.10",
|
|
367936
367936
|
description: "MaTrixOS \u2014 Agentic OS for OpenCode. Personalizable, communicating, self-improving, resilient.",
|
|
367937
367937
|
main: "./dist/index.js",
|
|
367938
367938
|
types: "dist/index.d.ts",
|
|
@@ -368012,6 +368012,7 @@ var package_default = {
|
|
|
368012
368012
|
scripts: {
|
|
368013
368013
|
build: "bun run script/build.ts",
|
|
368014
368014
|
"build:cli-node": "bun run script/build-cli-node.ts",
|
|
368015
|
+
"build:dashboard-frontend": "rm -rf dist/features/dashboard/frontend && mkdir -p dist/features/dashboard/frontend && cp -R packages/omo-opencode/src/features/dashboard/frontend/. dist/features/dashboard/frontend/",
|
|
368015
368016
|
"build:shared-skills-assets": "rm -rf dist/skills && cp -R packages/shared-skills/skills dist/skills && mkdir -p dist/cli && rm -rf dist/cli/skills && cp -R packages/shared-skills/skills dist/cli/skills",
|
|
368016
368017
|
"build:lsp-tools-mcp": "npm --prefix packages/lsp-tools-mcp ci && npm --prefix packages/lsp-tools-mcp run build",
|
|
368017
368018
|
"build:lsp-daemon": "npm --prefix packages/lsp-daemon ci && npm --prefix packages/lsp-daemon run build",
|
|
@@ -441333,7 +441334,11 @@ function createAgentAntiLoopHooks(config) {
|
|
|
441333
441334
|
nudged: false,
|
|
441334
441335
|
fallbackTriggered: false,
|
|
441335
441336
|
avoidanceCount: 0,
|
|
441336
|
-
avoidanceEscalated: false
|
|
441337
|
+
avoidanceEscalated: false,
|
|
441338
|
+
hookExecutionTimestamps: [],
|
|
441339
|
+
hookRecursionBreachedAt: 0,
|
|
441340
|
+
contextTokens: 0,
|
|
441341
|
+
contextBloatBreachedAt: 0
|
|
441337
441342
|
};
|
|
441338
441343
|
function makeSignature(tool3, args) {
|
|
441339
441344
|
try {
|
|
@@ -441386,6 +441391,25 @@ function createAgentAntiLoopHooks(config) {
|
|
|
441386
441391
|
const elapsed = now - state3.lastMeaningfulToolAt;
|
|
441387
441392
|
return { stuck: elapsed >= config.stuckTimeoutSec * 1000, secondsSinceLastMeaningful: Math.floor(elapsed / 1000) };
|
|
441388
441393
|
},
|
|
441394
|
+
onHookExecute() {
|
|
441395
|
+
const now = Date.now();
|
|
441396
|
+
state3.hookExecutionTimestamps.push(now);
|
|
441397
|
+
const windowStart = now - config.hookRecursionWindowMs;
|
|
441398
|
+
state3.hookExecutionTimestamps = state3.hookExecutionTimestamps.filter((t2) => t2 >= windowStart);
|
|
441399
|
+
},
|
|
441400
|
+
isHookRecursion() {
|
|
441401
|
+
return state3.hookExecutionTimestamps.length >= config.hookRecursionThreshold;
|
|
441402
|
+
},
|
|
441403
|
+
onContextUpdate(tokens) {
|
|
441404
|
+
state3.contextTokens = tokens;
|
|
441405
|
+
},
|
|
441406
|
+
isContextBloat() {
|
|
441407
|
+
const now = Date.now();
|
|
441408
|
+
const elapsedSinceMeaningful = now - state3.lastMeaningfulToolAt;
|
|
441409
|
+
const bloated = state3.contextTokens >= config.contextBloatMaxTokens;
|
|
441410
|
+
const stale = elapsedSinceMeaningful >= config.contextBloatStuckSec * 1000;
|
|
441411
|
+
return bloated && stale;
|
|
441412
|
+
},
|
|
441389
441413
|
reset() {
|
|
441390
441414
|
state3.consecutiveBySignature.clear();
|
|
441391
441415
|
state3.recentSignatures = [];
|
|
@@ -441394,6 +441418,10 @@ function createAgentAntiLoopHooks(config) {
|
|
|
441394
441418
|
state3.fallbackTriggered = false;
|
|
441395
441419
|
state3.avoidanceCount = 0;
|
|
441396
441420
|
state3.avoidanceEscalated = false;
|
|
441421
|
+
state3.hookExecutionTimestamps = [];
|
|
441422
|
+
state3.hookRecursionBreachedAt = 0;
|
|
441423
|
+
state3.contextTokens = 0;
|
|
441424
|
+
state3.contextBloatBreachedAt = 0;
|
|
441397
441425
|
},
|
|
441398
441426
|
getState() {
|
|
441399
441427
|
return { ...state3, consecutiveBySignature: new Map(state3.consecutiveBySignature), recentSignatures: [...state3.recentSignatures] };
|
|
@@ -441424,6 +441452,12 @@ function createAntiLoopLogger(logsDir) {
|
|
|
441424
441452
|
};
|
|
441425
441453
|
}
|
|
441426
441454
|
|
|
441455
|
+
// packages/omo-opencode/src/features/anti-loop/background-registry.ts
|
|
441456
|
+
var registry2 = new Map;
|
|
441457
|
+
function getRunningTasks() {
|
|
441458
|
+
return [...registry2.values()].filter((r) => r.status === "running");
|
|
441459
|
+
}
|
|
441460
|
+
|
|
441427
441461
|
// packages/omo-opencode/src/plugin/agent-anti-loop.ts
|
|
441428
441462
|
import { join as join135 } from "path";
|
|
441429
441463
|
function createAgentAntiLoopWiring(config, deps) {
|
|
@@ -441470,9 +441504,62 @@ function createAgentAntiLoopWiring(config, deps) {
|
|
|
441470
441504
|
if (config.recoveryStrategy === "nudge" && deps.injectNudge) {
|
|
441471
441505
|
deps.injectNudge(`Watchdog: no progress for ${secondsSinceLastMeaningful}s. Try a different approach.`);
|
|
441472
441506
|
}
|
|
441507
|
+
const running = getRunningTasks();
|
|
441508
|
+
if (running.length > 0) {
|
|
441509
|
+
const ids = running.map((r) => r.taskId).join(", ");
|
|
441510
|
+
deps.log("[agent-anti-loop] zombie session detected", { runningTasks: ids });
|
|
441511
|
+
antiLoopLog.log({ sessionId: process.env.OMO_SESSION_ID, pattern: "zombie_session", detail: `Session idle but ${running.length} background task(s) running: ${ids}`, actionTaken: config.recoveryStrategy });
|
|
441512
|
+
if (deps.injectNudge) {
|
|
441513
|
+
deps.injectNudge(`Watchdog: session idle but background task(s) still running (${ids}). Check for orphaned work.`);
|
|
441514
|
+
}
|
|
441515
|
+
}
|
|
441473
441516
|
},
|
|
441474
441517
|
sessionCreated() {
|
|
441475
441518
|
hooks.reset();
|
|
441519
|
+
},
|
|
441520
|
+
onHookExecute() {
|
|
441521
|
+
hooks.onHookExecute();
|
|
441522
|
+
if (hooks.isHookRecursion()) {
|
|
441523
|
+
const now = Date.now();
|
|
441524
|
+
const state3 = hooks.getState();
|
|
441525
|
+
if (now - state3.hookRecursionBreachedAt > config.hookRecursionWindowMs) {
|
|
441526
|
+
state3.hookRecursionBreachedAt = now;
|
|
441527
|
+
deps.log("[agent-anti-loop] hook recursion detected", { count: state3.hookExecutionTimestamps.length, windowMs: config.hookRecursionWindowMs });
|
|
441528
|
+
antiLoopLog.log({ sessionId: process.env.OMO_SESSION_ID, pattern: "hook_recursion", detail: `${state3.hookExecutionTimestamps.length} hook executions within ${config.hookRecursionWindowMs}ms`, actionTaken: config.recoveryStrategy });
|
|
441529
|
+
if (deps.injectNudge) {
|
|
441530
|
+
deps.injectNudge("Watchdog: hook recursion detected. A hook is re-triggering itself in a loop.");
|
|
441531
|
+
}
|
|
441532
|
+
}
|
|
441533
|
+
}
|
|
441534
|
+
},
|
|
441535
|
+
onContextUpdate(tokens) {
|
|
441536
|
+
hooks.onContextUpdate(tokens);
|
|
441537
|
+
if (hooks.isContextBloat()) {
|
|
441538
|
+
const now = Date.now();
|
|
441539
|
+
const state3 = hooks.getState();
|
|
441540
|
+
if (now - state3.contextBloatBreachedAt > config.contextBloatStuckSec * 1000) {
|
|
441541
|
+
state3.contextBloatBreachedAt = now;
|
|
441542
|
+
deps.log("[agent-anti-loop] context bloat detected", { tokens, threshold: config.contextBloatMaxTokens });
|
|
441543
|
+
antiLoopLog.log({ sessionId: process.env.OMO_SESSION_ID, pattern: "context_bloat", detail: `Context ${tokens} tokens >= ${config.contextBloatMaxTokens} with no progress for ${config.contextBloatStuckSec}s`, actionTaken: config.recoveryStrategy });
|
|
441544
|
+
if (deps.injectNudge) {
|
|
441545
|
+
deps.injectNudge(`Watchdog: context bloat (${tokens} tokens) without progress. Consider compacting or clearing context.`);
|
|
441546
|
+
}
|
|
441547
|
+
}
|
|
441548
|
+
}
|
|
441549
|
+
},
|
|
441550
|
+
backgroundTaskCheck() {
|
|
441551
|
+
const running = getRunningTasks();
|
|
441552
|
+
const now = Date.now();
|
|
441553
|
+
for (const task of running) {
|
|
441554
|
+
const ageMs = now - task.startedAt;
|
|
441555
|
+
if (ageMs >= config.runawayTaskTimeoutSec * 1000) {
|
|
441556
|
+
deps.log("[agent-anti-loop] runaway task detected", { taskId: task.taskId, ageSec: Math.floor(ageMs / 1000) });
|
|
441557
|
+
antiLoopLog.log({ sessionId: process.env.OMO_SESSION_ID, pattern: "runaway_task", toolName: task.taskId, detail: `Background task running ${Math.floor(ageMs / 1000)}s >= ${config.runawayTaskTimeoutSec}s`, actionTaken: config.recoveryStrategy });
|
|
441558
|
+
if (deps.injectNudge) {
|
|
441559
|
+
deps.injectNudge(`Watchdog: background task ${task.taskId} has been running ${Math.floor(ageMs / 1000)}s without completing.`);
|
|
441560
|
+
}
|
|
441561
|
+
}
|
|
441562
|
+
}
|
|
441476
441563
|
}
|
|
441477
441564
|
};
|
|
441478
441565
|
}
|
|
@@ -442765,6 +442852,13 @@ class MatrixGateway {
|
|
|
442765
442852
|
async stop() {
|
|
442766
442853
|
await Promise.all(Array.from(this.adapters.values()).map((a) => a.disconnect()));
|
|
442767
442854
|
}
|
|
442855
|
+
async send(env2) {
|
|
442856
|
+
const adapter = this.adapters.get(env2.channel);
|
|
442857
|
+
if (!adapter) {
|
|
442858
|
+
throw new Error(`no adapter registered for channel "${env2.channel}"`);
|
|
442859
|
+
}
|
|
442860
|
+
return adapter.send(env2);
|
|
442861
|
+
}
|
|
442768
442862
|
async handleInbound(env2) {
|
|
442769
442863
|
if (this.seenIds.has(env2.id)) {
|
|
442770
442864
|
return;
|
|
@@ -443466,6 +443560,12 @@ var WatchdogConfigSchema = z54.object({
|
|
|
443466
443560
|
stuckTimeoutSec: z54.number().int().positive().max(600).default(90),
|
|
443467
443561
|
auditLogPath: z54.string().optional(),
|
|
443468
443562
|
recoveryStrategy: z54.enum(["nudge", "fallback", "restart", "notify", "safe"]).default("nudge"),
|
|
443563
|
+
hookRecursionWindowMs: z54.number().int().positive().max(60000).default(5000),
|
|
443564
|
+
hookRecursionThreshold: z54.number().int().positive().max(100).default(10),
|
|
443565
|
+
contextBloatMaxTokens: z54.number().int().positive().default(200000),
|
|
443566
|
+
contextBloatStuckSec: z54.number().int().positive().max(600).default(120),
|
|
443567
|
+
zombieSessionIdleSec: z54.number().int().positive().max(600).default(90),
|
|
443568
|
+
runawayTaskTimeoutSec: z54.number().int().positive().max(86400).default(600),
|
|
443469
443569
|
heartbeatIntervalSec: z54.number().positive().max(300).default(30),
|
|
443470
443570
|
heartbeatFilePath: z54.string().default(".matrixos/tmp/heartbeat"),
|
|
443471
443571
|
fatalMarkerPath: z54.string().default(".matrixos/tmp/watchdog-fatal.json"),
|
|
@@ -451039,6 +451139,78 @@ function recordPluginTelemetry(input2) {
|
|
|
451039
451139
|
posthog.shutdown();
|
|
451040
451140
|
}
|
|
451041
451141
|
|
|
451142
|
+
// packages/omo-opencode/src/testing/create-gateway-relay.ts
|
|
451143
|
+
function createGatewayRelayHandler(deps) {
|
|
451144
|
+
const { input: input2, gateway, log: log8 } = deps;
|
|
451145
|
+
return async (env2) => {
|
|
451146
|
+
const inboundText = env2.content.text;
|
|
451147
|
+
if (!inboundText) {
|
|
451148
|
+
log8("[gateway] ignoring non-text inbound message", { channel: env2.channel });
|
|
451149
|
+
return;
|
|
451150
|
+
}
|
|
451151
|
+
log8("[gateway] inbound message \u2192 relaying to Morpheus", {
|
|
451152
|
+
channel: env2.channel,
|
|
451153
|
+
from: env2.sender.id,
|
|
451154
|
+
text: inboundText.slice(0, 200)
|
|
451155
|
+
});
|
|
451156
|
+
try {
|
|
451157
|
+
const createResult = await input2.client.session.create({
|
|
451158
|
+
body: {
|
|
451159
|
+
title: `Gateway relay (${env2.channel})`,
|
|
451160
|
+
parentID: undefined
|
|
451161
|
+
}
|
|
451162
|
+
});
|
|
451163
|
+
const sessionID = createResult.data?.id;
|
|
451164
|
+
if (!sessionID)
|
|
451165
|
+
throw new Error("session create returned no id");
|
|
451166
|
+
await input2.client.session.prompt({
|
|
451167
|
+
path: { id: sessionID },
|
|
451168
|
+
body: {
|
|
451169
|
+
agent: "morpheus",
|
|
451170
|
+
parts: [{ type: "text", text: inboundText }]
|
|
451171
|
+
},
|
|
451172
|
+
query: { directory: input2.directory }
|
|
451173
|
+
});
|
|
451174
|
+
await waitForCompletion(sessionID, { sessionID, messageID: "", agent: "morpheus", abort: new AbortController().signal }, input2);
|
|
451175
|
+
const reply = await processMessages(sessionID, input2);
|
|
451176
|
+
await gateway.send({
|
|
451177
|
+
id: makeEnvelopeId(env2.channel, env2.metadata.providerMessageId),
|
|
451178
|
+
channel: env2.channel,
|
|
451179
|
+
direction: "outbound",
|
|
451180
|
+
timestamp: new Date().toISOString(),
|
|
451181
|
+
sender: env2.sender,
|
|
451182
|
+
recipient: { id: env2.sender.id },
|
|
451183
|
+
content: { type: "text", text: reply },
|
|
451184
|
+
metadata: {
|
|
451185
|
+
providerMessageId: env2.metadata.providerMessageId,
|
|
451186
|
+
raw: null,
|
|
451187
|
+
isGroup: env2.metadata.isGroup
|
|
451188
|
+
}
|
|
451189
|
+
});
|
|
451190
|
+
deps.log("[gateway] reply sent", { channel: env2.channel, to: env2.sender.id });
|
|
451191
|
+
} catch (relayError) {
|
|
451192
|
+
const detail = relayError instanceof Error ? relayError.message : String(relayError);
|
|
451193
|
+
log8("[gateway] relay failed", { detail });
|
|
451194
|
+
try {
|
|
451195
|
+
await gateway.send({
|
|
451196
|
+
id: makeEnvelopeId(env2.channel, env2.metadata.providerMessageId),
|
|
451197
|
+
channel: env2.channel,
|
|
451198
|
+
direction: "outbound",
|
|
451199
|
+
timestamp: new Date().toISOString(),
|
|
451200
|
+
sender: env2.sender,
|
|
451201
|
+
recipient: { id: env2.sender.id },
|
|
451202
|
+
content: { type: "text", text: `\u26A0\uFE0F Erreur de traitement: ${detail}` },
|
|
451203
|
+
metadata: {
|
|
451204
|
+
providerMessageId: env2.metadata.providerMessageId,
|
|
451205
|
+
raw: null,
|
|
451206
|
+
isGroup: env2.metadata.isGroup
|
|
451207
|
+
}
|
|
451208
|
+
});
|
|
451209
|
+
} catch {}
|
|
451210
|
+
}
|
|
451211
|
+
};
|
|
451212
|
+
}
|
|
451213
|
+
|
|
451042
451214
|
// packages/daily-brief-core/src/schema.ts
|
|
451043
451215
|
import * as z64 from "zod";
|
|
451044
451216
|
var DailyBriefConfigSchema = z64.object({
|
|
@@ -451704,36 +451876,6 @@ function createPluginModule(overrides2 = {}) {
|
|
|
451704
451876
|
}
|
|
451705
451877
|
return out;
|
|
451706
451878
|
}
|
|
451707
|
-
if (pluginConfig.gateway?.enabled) {
|
|
451708
|
-
try {
|
|
451709
|
-
const gwConfig = pluginConfig.gateway;
|
|
451710
|
-
gateway = new MatrixGateway(gwConfig, {
|
|
451711
|
-
handler: async (env2) => {
|
|
451712
|
-
deps.log("[gateway] inbound message", {
|
|
451713
|
-
channel: env2.channel,
|
|
451714
|
-
from: env2.sender.id,
|
|
451715
|
-
text: env2.content.text
|
|
451716
|
-
});
|
|
451717
|
-
}
|
|
451718
|
-
});
|
|
451719
|
-
for (const ch of gwConfig.channels) {
|
|
451720
|
-
if (ch.type === "telegram") {
|
|
451721
|
-
const voice = buildTelegramVoice(ch);
|
|
451722
|
-
gateway.registerAdapter(new TelegramAdapter(ch, undefined, voice));
|
|
451723
|
-
} else if (ch.type === "discord") {
|
|
451724
|
-
gateway.registerAdapter(new DiscordAdapter(ch));
|
|
451725
|
-
} else if (ch.type === "whatsapp") {
|
|
451726
|
-
gateway.registerAdapter(new WhatsAppAdapter(ch, undefined, { enabled: true }));
|
|
451727
|
-
}
|
|
451728
|
-
}
|
|
451729
|
-
await gateway.start();
|
|
451730
|
-
deps.log("[gateway] started", { channels: gwConfig.channels.map((c2) => c2.type) });
|
|
451731
|
-
} catch (error) {
|
|
451732
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
451733
|
-
console.warn(`[gateway] failed to start: ${detail}`);
|
|
451734
|
-
gateway = undefined;
|
|
451735
|
-
}
|
|
451736
|
-
}
|
|
451737
451879
|
if (pluginConfig.cron?.enabled) {
|
|
451738
451880
|
try {
|
|
451739
451881
|
const cronStore = createCronStore();
|
|
@@ -451886,6 +452028,39 @@ function createPluginModule(overrides2 = {}) {
|
|
|
451886
452028
|
mergedSkills: toolsResult.mergedSkills,
|
|
451887
452029
|
availableSkills: toolsResult.availableSkills
|
|
451888
452030
|
});
|
|
452031
|
+
if (pluginConfig.gateway?.enabled) {
|
|
452032
|
+
try {
|
|
452033
|
+
const gwConfig = pluginConfig.gateway;
|
|
452034
|
+
let gwInstance;
|
|
452035
|
+
gwInstance = new MatrixGateway(gwConfig, {
|
|
452036
|
+
handler: createGatewayRelayHandler({
|
|
452037
|
+
input: input2,
|
|
452038
|
+
gateway: gwInstance,
|
|
452039
|
+
log: deps.log
|
|
452040
|
+
})
|
|
452041
|
+
});
|
|
452042
|
+
gateway = gwInstance;
|
|
452043
|
+
for (const ch of gwConfig.channels) {
|
|
452044
|
+
if (ch.type === "telegram") {
|
|
452045
|
+
const voice = buildTelegramVoice(ch);
|
|
452046
|
+
gateway.registerAdapter(new TelegramAdapter(ch, undefined, voice));
|
|
452047
|
+
} else if (ch.type === "discord") {
|
|
452048
|
+
gateway.registerAdapter(new DiscordAdapter(ch));
|
|
452049
|
+
} else if (ch.type === "whatsapp") {
|
|
452050
|
+
gateway.registerAdapter(new WhatsAppAdapter(ch, undefined, { enabled: true }));
|
|
452051
|
+
}
|
|
452052
|
+
}
|
|
452053
|
+
await gateway.start();
|
|
452054
|
+
const gw = gateway;
|
|
452055
|
+
deps.log("[gateway] started \u2014 Telegram/Discord/WhatsApp relay active", {
|
|
452056
|
+
channels: gwConfig.channels.map((c2) => c2.type)
|
|
452057
|
+
});
|
|
452058
|
+
} catch (error) {
|
|
452059
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
452060
|
+
console.warn(`[gateway] failed to start: ${detail}`);
|
|
452061
|
+
gateway = undefined;
|
|
452062
|
+
}
|
|
452063
|
+
}
|
|
451889
452064
|
let dreamerTrigger;
|
|
451890
452065
|
const dreamerConfig = pluginConfig.dreamer;
|
|
451891
452066
|
if (dreamerConfig?.enabled) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"$id": "https://raw.githubusercontent.com/
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/shirofx/MaTrixOS/dev/assets/matrixos.schema.json",
|
|
4
4
|
"title": "Oh My OpenCode Configuration",
|
|
5
5
|
"description": "Configuration schema for oh-my-opencode plugin",
|
|
6
6
|
"type": "object",
|
|
@@ -7117,6 +7117,42 @@
|
|
|
7117
7117
|
"safe"
|
|
7118
7118
|
]
|
|
7119
7119
|
},
|
|
7120
|
+
"hookRecursionWindowMs": {
|
|
7121
|
+
"default": 5000,
|
|
7122
|
+
"type": "integer",
|
|
7123
|
+
"exclusiveMinimum": 0,
|
|
7124
|
+
"maximum": 60000
|
|
7125
|
+
},
|
|
7126
|
+
"hookRecursionThreshold": {
|
|
7127
|
+
"default": 10,
|
|
7128
|
+
"type": "integer",
|
|
7129
|
+
"exclusiveMinimum": 0,
|
|
7130
|
+
"maximum": 100
|
|
7131
|
+
},
|
|
7132
|
+
"contextBloatMaxTokens": {
|
|
7133
|
+
"default": 200000,
|
|
7134
|
+
"type": "integer",
|
|
7135
|
+
"exclusiveMinimum": 0,
|
|
7136
|
+
"maximum": 9007199254740991
|
|
7137
|
+
},
|
|
7138
|
+
"contextBloatStuckSec": {
|
|
7139
|
+
"default": 120,
|
|
7140
|
+
"type": "integer",
|
|
7141
|
+
"exclusiveMinimum": 0,
|
|
7142
|
+
"maximum": 600
|
|
7143
|
+
},
|
|
7144
|
+
"zombieSessionIdleSec": {
|
|
7145
|
+
"default": 90,
|
|
7146
|
+
"type": "integer",
|
|
7147
|
+
"exclusiveMinimum": 0,
|
|
7148
|
+
"maximum": 600
|
|
7149
|
+
},
|
|
7150
|
+
"runawayTaskTimeoutSec": {
|
|
7151
|
+
"default": 600,
|
|
7152
|
+
"type": "integer",
|
|
7153
|
+
"exclusiveMinimum": 0,
|
|
7154
|
+
"maximum": 86400
|
|
7155
|
+
},
|
|
7120
7156
|
"heartbeatIntervalSec": {
|
|
7121
7157
|
"default": 30,
|
|
7122
7158
|
"type": "number",
|
|
@@ -7186,6 +7222,12 @@
|
|
|
7186
7222
|
"softDoomLoopThreshold",
|
|
7187
7223
|
"stuckTimeoutSec",
|
|
7188
7224
|
"recoveryStrategy",
|
|
7225
|
+
"hookRecursionWindowMs",
|
|
7226
|
+
"hookRecursionThreshold",
|
|
7227
|
+
"contextBloatMaxTokens",
|
|
7228
|
+
"contextBloatStuckSec",
|
|
7229
|
+
"zombieSessionIdleSec",
|
|
7230
|
+
"runawayTaskTimeoutSec",
|
|
7189
7231
|
"heartbeatIntervalSec",
|
|
7190
7232
|
"heartbeatFilePath",
|
|
7191
7233
|
"fatalMarkerPath",
|
|
@@ -13,5 +13,11 @@ export interface AgentAntiLoopWiring {
|
|
|
13
13
|
sessionIdle: () => Promise<void>;
|
|
14
14
|
sessionCreated: () => void;
|
|
15
15
|
onAgentMessage: (content: string) => void;
|
|
16
|
+
/** Record a hook execution for recursion detection (§4.12 0.1.8). */
|
|
17
|
+
onHookExecute: () => void;
|
|
18
|
+
/** Update the estimated context size in tokens (§4.12 0.1.8). */
|
|
19
|
+
onContextUpdate: (tokens: number) => void;
|
|
20
|
+
/** Scan for zombie_session + runaway_task among running background tasks (§4.12 0.1.8). */
|
|
21
|
+
backgroundTaskCheck: () => void;
|
|
16
22
|
}
|
|
17
23
|
export declare function createAgentAntiLoopWiring(config: WatchdogConfig, deps: AgentAntiLoopDeps): AgentAntiLoopWiring;
|
|
@@ -50,7 +50,7 @@ When the `team_*` tools are present, create a **debug-squad** team and split inv
|
|
|
50
50
|
```json
|
|
51
51
|
{
|
|
52
52
|
"name": "debug-squad",
|
|
53
|
-
"lead": { "kind": "subagent_type", "subagent_type": "
|
|
53
|
+
"lead": { "kind": "subagent_type", "subagent_type": "morpheus" },
|
|
54
54
|
"members": [
|
|
55
55
|
{
|
|
56
56
|
"kind": "category",
|
|
@@ -664,7 +664,7 @@ Record the chosen path in the TodoWrite list.
|
|
|
664
664
|
\`\`\`json
|
|
665
665
|
{
|
|
666
666
|
"name": "refactor-squad",
|
|
667
|
-
"lead": { "kind": "subagent_type", "subagent_type": "
|
|
667
|
+
"lead": { "kind": "subagent_type", "subagent_type": "morpheus" },
|
|
668
668
|
"members": [
|
|
669
669
|
{
|
|
670
670
|
"kind": "category",
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: brainstorming
|
|
3
|
+
description: "You MUST use this before any creative work - creating features, building components, adding functionality, or modifying behavior. Explores user intent, requirements and design before implementation."
|
|
4
|
+
---
|
|
5
|
+
> **Forked from [obra/superpowers](https://github.com/obra/superpowers) — MIT License, Copyright (c) 2025 Jesse Vincent.**
|
|
6
|
+
> Vendored as a native MaTrixOS skill. The original MIT license text is in `SUPERPOWERS_LICENSE` in this folder.
|
|
7
|
+
> Routing rule: MaTrixOS owns plan/execute/verify (ulw-plan, start-work, review-work). This skill is a complementary aid, invoked on demand.
|
|
8
|
+
|
|
9
|
+
# Brainstorming Ideas Into Designs
|
|
10
|
+
|
|
11
|
+
Help turn ideas into fully formed designs and specs through natural collaborative dialogue.
|
|
12
|
+
|
|
13
|
+
Start by understanding the current project context, then ask questions one at a time to refine the idea. Once you understand what you're building, present the design and get user approval.
|
|
14
|
+
|
|
15
|
+
<HARD-GATE>
|
|
16
|
+
Do NOT invoke any implementation skill, write any code, scaffold any project, or take any implementation action until you have presented a design and the user has approved it. This applies to EVERY project regardless of perceived simplicity.
|
|
17
|
+
</HARD-GATE>
|
|
18
|
+
|
|
19
|
+
## Anti-Pattern: "This Is Too Simple To Need A Design"
|
|
20
|
+
|
|
21
|
+
Every project goes through this process. A todo list, a single-function utility, a config change — all of them. "Simple" projects are where unexamined assumptions cause the most wasted work. The design can be short (a few sentences for truly simple projects), but you MUST present it and get approval.
|
|
22
|
+
|
|
23
|
+
## Checklist
|
|
24
|
+
|
|
25
|
+
You MUST create a task for each of these items and complete them in order:
|
|
26
|
+
|
|
27
|
+
1. **Explore project context** — check files, docs, recent commits
|
|
28
|
+
2. **Offer the visual companion just-in-time** — NOT upfront. The first time a question would genuinely be clearer shown than described, offer it then (its own message); on approval its browser tab opens for you. If no visual question ever arises, never offer it. See the Visual Companion section below.
|
|
29
|
+
3. **Ask clarifying questions** — one at a time, understand purpose/constraints/success criteria
|
|
30
|
+
4. **Propose 2-3 approaches** — with trade-offs and your recommendation
|
|
31
|
+
5. **Present design** — in sections scaled to their complexity, get user approval after each section
|
|
32
|
+
6. **Write design doc** — save to `docs/superpowers/specs/YYYY-MM-DD-<topic>-design.md` and commit
|
|
33
|
+
7. **Spec self-review** — quick inline check for placeholders, contradictions, ambiguity, scope (see below)
|
|
34
|
+
8. **User reviews written spec** — ask user to review the spec file before proceeding
|
|
35
|
+
9. **Transition to implementation** — invoke writing-plans skill to create implementation plan
|
|
36
|
+
|
|
37
|
+
## Process Flow
|
|
38
|
+
|
|
39
|
+
```dot
|
|
40
|
+
digraph brainstorming {
|
|
41
|
+
"Explore project context" [shape=box];
|
|
42
|
+
"Ask clarifying questions" [shape=box];
|
|
43
|
+
"Propose 2-3 approaches" [shape=box];
|
|
44
|
+
"Present design sections" [shape=box];
|
|
45
|
+
"User approves design?" [shape=diamond];
|
|
46
|
+
"Write design doc" [shape=box];
|
|
47
|
+
"Spec self-review\n(fix inline)" [shape=box];
|
|
48
|
+
"User reviews spec?" [shape=diamond];
|
|
49
|
+
"Invoke writing-plans skill" [shape=doublecircle];
|
|
50
|
+
|
|
51
|
+
"Explore project context" -> "Ask clarifying questions";
|
|
52
|
+
"Ask clarifying questions" -> "Propose 2-3 approaches";
|
|
53
|
+
"Propose 2-3 approaches" -> "Present design sections";
|
|
54
|
+
"Present design sections" -> "User approves design?";
|
|
55
|
+
"User approves design?" -> "Present design sections" [label="no, revise"];
|
|
56
|
+
"User approves design?" -> "Write design doc" [label="yes"];
|
|
57
|
+
"Write design doc" -> "Spec self-review\n(fix inline)";
|
|
58
|
+
"Spec self-review\n(fix inline)" -> "User reviews spec?";
|
|
59
|
+
"User reviews spec?" -> "Write design doc" [label="changes requested"];
|
|
60
|
+
"User reviews spec?" -> "Invoke writing-plans skill" [label="approved"];
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**The terminal state is invoking writing-plans.** Do NOT invoke frontend-design, mcp-builder, or any other implementation skill. The ONLY skill you invoke after brainstorming is writing-plans.
|
|
65
|
+
|
|
66
|
+
## The Process
|
|
67
|
+
|
|
68
|
+
**Understanding the idea:**
|
|
69
|
+
|
|
70
|
+
- Check out the current project state first (files, docs, recent commits)
|
|
71
|
+
- Before asking detailed questions, assess scope: if the request describes multiple independent subsystems (e.g., "build a platform with chat, file storage, billing, and analytics"), flag this immediately. Don't spend questions refining details of a project that needs to be decomposed first.
|
|
72
|
+
- If the project is too large for a single spec, help the user decompose into sub-projects: what are the independent pieces, how do they relate, what order should they be built? Then brainstorm the first sub-project through the normal design flow. Each sub-project gets its own spec → plan → implementation cycle.
|
|
73
|
+
- For appropriately-scoped projects, ask questions one at a time to refine the idea
|
|
74
|
+
- Prefer multiple choice questions when possible, but open-ended is fine too
|
|
75
|
+
- Only one question per message - if a topic needs more exploration, break it into multiple questions
|
|
76
|
+
- Focus on understanding: purpose, constraints, success criteria
|
|
77
|
+
|
|
78
|
+
**Exploring approaches:**
|
|
79
|
+
|
|
80
|
+
- Propose 2-3 different approaches with trade-offs
|
|
81
|
+
- Present options conversationally with your recommendation and reasoning
|
|
82
|
+
- Lead with your recommended option and explain why
|
|
83
|
+
|
|
84
|
+
**Presenting the design:**
|
|
85
|
+
|
|
86
|
+
- Once you believe you understand what you're building, present the design
|
|
87
|
+
- Scale each section to its complexity: a few sentences if straightforward, up to 200-300 words if nuanced
|
|
88
|
+
- Ask after each section whether it looks right so far
|
|
89
|
+
- Cover: architecture, components, data flow, error handling, testing
|
|
90
|
+
- Be ready to go back and clarify if something doesn't make sense
|
|
91
|
+
|
|
92
|
+
**Design for isolation and clarity:**
|
|
93
|
+
|
|
94
|
+
- Break the system into smaller units that each have one clear purpose, communicate through well-defined interfaces, and can be understood and tested independently
|
|
95
|
+
- For each unit, you should be able to answer: what does it do, how do you use it, and what does it depend on?
|
|
96
|
+
- Can someone understand what a unit does without reading its internals? Can you change the internals without breaking consumers? If not, the boundaries need work.
|
|
97
|
+
- Smaller, well-bounded units are also easier for you to work with - you reason better about code you can hold in context at once, and your edits are more reliable when files are focused. When a file grows large, that's often a signal that it's doing too much.
|
|
98
|
+
|
|
99
|
+
**Working in existing codebases:**
|
|
100
|
+
|
|
101
|
+
- Explore the current structure before proposing changes. Follow existing patterns.
|
|
102
|
+
- Where existing code has problems that affect the work (e.g., a file that's grown too large, unclear boundaries, tangled responsibilities), include targeted improvements as part of the design - the way a good developer improves code they're working in.
|
|
103
|
+
- Don't propose unrelated refactoring. Stay focused on what serves the current goal.
|
|
104
|
+
|
|
105
|
+
## After the Design
|
|
106
|
+
|
|
107
|
+
**Documentation:**
|
|
108
|
+
|
|
109
|
+
- Write the validated design (spec) to `docs/superpowers/specs/YYYY-MM-DD-<topic>-design.md`
|
|
110
|
+
- (User preferences for spec location override this default)
|
|
111
|
+
- Use elements-of-style:writing-clearly-and-concisely skill if available
|
|
112
|
+
- Commit the design document to git
|
|
113
|
+
|
|
114
|
+
**Spec Self-Review:**
|
|
115
|
+
After writing the spec document, look at it with fresh eyes:
|
|
116
|
+
|
|
117
|
+
1. **Placeholder scan:** Any "TBD", "TODO", incomplete sections, or vague requirements? Fix them.
|
|
118
|
+
2. **Internal consistency:** Do any sections contradict each other? Does the architecture match the feature descriptions?
|
|
119
|
+
3. **Scope check:** Is this focused enough for a single implementation plan, or does it need decomposition?
|
|
120
|
+
4. **Ambiguity check:** Could any requirement be interpreted two different ways? If so, pick one and make it explicit.
|
|
121
|
+
|
|
122
|
+
Fix any issues inline. No need to re-review — just fix and move on.
|
|
123
|
+
|
|
124
|
+
**User Review Gate:**
|
|
125
|
+
After the spec review loop passes, ask the user to review the written spec before proceeding:
|
|
126
|
+
|
|
127
|
+
> "Spec written and committed to `<path>`. Please review it and let me know if you want to make any changes before we start writing out the implementation plan."
|
|
128
|
+
|
|
129
|
+
Wait for the user's response. If they request changes, make them and re-run the spec review loop. Only proceed once the user approves.
|
|
130
|
+
|
|
131
|
+
**Implementation:**
|
|
132
|
+
|
|
133
|
+
- Invoke the writing-plans skill to create a detailed implementation plan
|
|
134
|
+
- Do NOT invoke any other skill. writing-plans is the next step.
|
|
135
|
+
|
|
136
|
+
## Key Principles
|
|
137
|
+
|
|
138
|
+
- **One question at a time** - Don't overwhelm with multiple questions
|
|
139
|
+
- **Multiple choice preferred** - Easier to answer than open-ended when possible
|
|
140
|
+
- **YAGNI ruthlessly** - Remove unnecessary features from all designs
|
|
141
|
+
- **Explore alternatives** - Always propose 2-3 approaches before settling
|
|
142
|
+
- **Incremental validation** - Present design, get approval before moving on
|
|
143
|
+
- **Be flexible** - Go back and clarify when something doesn't make sense
|
|
144
|
+
|
|
145
|
+
## Visual Companion
|
|
146
|
+
|
|
147
|
+
A browser-based companion for showing mockups, diagrams, and visual options during brainstorming. Available as a tool — not a mode. Accepting the companion means it's available for questions that benefit from visual treatment; it does NOT mean every question goes through the browser.
|
|
148
|
+
|
|
149
|
+
**Offering the companion (just-in-time):** Do NOT offer it upfront. Wait until a question would genuinely be clearer shown than told — a real mockup / layout / diagram question, not merely a UI *topic*. The first time that happens, offer it then, as its own message:
|
|
150
|
+
> "This next part might be easier if I show you — I can put together mockups, diagrams, and comparisons in a browser tab as we go. It's still new and can be token-intensive. Want me to? I'll open it for you."
|
|
151
|
+
|
|
152
|
+
**This offer MUST be its own message.** Only the offer — no clarifying question, summary, or other content. Wait for the user's response. If they accept, start the server with `--open` so their browser opens to the first screen automatically. If they decline, continue text-only and don't offer again unless they raise it.
|
|
153
|
+
|
|
154
|
+
**Per-question decision:** Even after the user accepts, decide FOR EACH QUESTION whether to use the browser or the terminal. The test: **would the user understand this better by seeing it than reading it?**
|
|
155
|
+
|
|
156
|
+
- **Use the browser** for content that IS visual — mockups, wireframes, layout comparisons, architecture diagrams, side-by-side visual designs
|
|
157
|
+
- **Use the terminal** for content that is text — requirements questions, conceptual choices, tradeoff lists, A/B/C/D text options, scope decisions
|
|
158
|
+
|
|
159
|
+
A question about a UI topic is not automatically a visual question. "What does personality mean in this context?" is a conceptual question — use the terminal. "Which wizard layout works better?" is a visual question — use the browser.
|
|
160
|
+
|
|
161
|
+
If they agree to the companion, read the detailed guide before proceeding:
|
|
162
|
+
`skills/brainstorming/visual-companion.md`
|