@hiai-gg/hiai-opencode 0.1.8 → 0.1.9

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # hiai-opencode
2
2
 
3
- [![CI](https://github.com/HiAi-gg/hiai-opencode/actions/workflows/ci.yml/badge.svg)](https://github.com/HiAi-gg/hiai-opencode/actions/workflows/ci.yml)
3
+ [![CI](https://github.com/HiAi-gg/hiai-opencode/actions/workflows/ci.yml/badge.svg?branch=main&event=push)](https://github.com/HiAi-gg/hiai-opencode/actions/workflows/ci.yml?query=branch%3Amain)
4
4
 
5
5
  `hiai-opencode` is an OpenCode plugin that turns vanilla OpenCode into an opinionated multi-agent cockpit.
6
6
 
@@ -98,6 +98,29 @@ MCP integrations and which agents use them:
98
98
  - MCP wiring for `playwright`, `stitch`, `sequential-thinking`, `firecrawl`, `rag`, `mempalace`, `context7`, plus remote `websearch` and `grep_app`
99
99
  - LSP wiring for TypeScript, Svelte, Python, Bash, and ESLint
100
100
 
101
+ ## Continuation, Ralph-Loop, And Auto-Start
102
+
103
+ The plugin runs three layered mechanisms so a session does not give up halfway through a TODO list.
104
+
105
+ | Mechanism | Trigger | What it does |
106
+ |-----------|---------|--------------|
107
+ | **Todo continuation enforcer** | `session.idle` while open todos remain | Injects a continuation prompt after a 2s countdown. Backs off on stagnation, abort, token-limit, and pending-question. |
108
+ | **Ralph-loop** | `/ralph-loop <goal>` or `/ulw-loop <goal>` | Runs an explicit completion loop. Stops on `<promise>DONE</promise>`. Cancel with `/cancel-ralph`. |
109
+ | **Auto ralph-loop** | N+ open todos in one session | Auto-starts ralph-loop in ULTRAWORK mode so each iteration is forced to delegate to specialist agents (researcher / strategist / coder / critic). |
110
+
111
+ Tune the auto-start threshold in `hiai-opencode.json`:
112
+
113
+ ```json
114
+ {
115
+ "ralph_loop": {
116
+ "enabled": true,
117
+ "auto_start_threshold": 5
118
+ }
119
+ }
120
+ ```
121
+
122
+ `auto_start_threshold: 0` disables auto-start. The enforcer always yields to ralph-loop while it owns the session, so the two never inject duplicate prompts.
123
+
101
124
  ## Requirements
102
125
 
103
126
  Minimum:
@@ -493,6 +516,50 @@ On some Windows/OpenCode environments, local MCP process spawning can fail with
493
516
 
494
517
  This most often affects `sequential-thinking` and `mempalace`, and sometimes local `npx`-backed tools.
495
518
 
519
+ ## Troubleshooting MCP Servers
520
+
521
+ ### Firecrawl tools return "FIRECRAWL_API_KEY missing"
522
+
523
+ The `skill_mcp` env scrubber filters `process.env` before launching stdio MCP servers — secret-shaped names (`*_API_KEY`, `*_TOKEN`, etc.) and npm/pnpm config vars are stripped so they cannot leak into a malicious server. Keys you set via `hiai-opencode.json` are an explicit allowlist and pass through.
524
+
525
+ If your key only lives in `process.env`, move it into the MCP `environment` block:
526
+
527
+ ```json
528
+ {
529
+ "mcp": {
530
+ "firecrawl": {
531
+ "enabled": true,
532
+ "environment": { "FIRECRAWL_API_KEY": "fc-..." }
533
+ }
534
+ }
535
+ }
536
+ ```
537
+
538
+ (Versions ≤ 0.1.8 had a bug where customEnv was merged before filtering, so even an explicit `FIRECRAWL_API_KEY` got stripped. Fixed in 0.1.9 — explicit `environment` always wins over the filter.)
539
+
540
+ ### Playwright MCP fails to find Chromium
541
+
542
+ When `@playwright/mcp` cannot locate a system browser, point it at one explicitly:
543
+
544
+ ```json
545
+ {
546
+ "mcp": {
547
+ "playwright": {
548
+ "enabled": true,
549
+ "environment": {
550
+ "PLAYWRIGHT_MCP_EXECUTABLE_PATH": "/usr/bin/chromium"
551
+ }
552
+ }
553
+ }
554
+ }
555
+ ```
556
+
557
+ The path can be Chromium, Chrome, or Edge. Run `npx playwright install chromium` first if no browser is installed.
558
+
559
+ ### `skill_mcp(playwright)` connects to localhost:3001 and fails
560
+
561
+ If `mcp__playwright__browser_*` works but `skill_mcp(mcp_name="playwright", ...)` errors with a connection to `localhost:3001/mcp`, you have a leftover HTTP-mode MCP registration in your global or parent `opencode.json`. Either start the HTTP server, or remove that registration so the plugin's stdio registration (`npx @playwright/mcp@latest`) is the only one. As a workaround, agents (Vision, Critic) can call `mcp__playwright__browser_*` direct tools instead of going through `skill_mcp`.
562
+
496
563
  ## Diagnostics
497
564
 
498
565
  The plugin now emits startup warnings for common misconfiguration, including:
@@ -4177,6 +4177,8 @@ export declare const HiaiOpenCodeConfigSchema: z.ZodObject<{
4177
4177
  reset: "reset";
4178
4178
  continue: "continue";
4179
4179
  }>>;
4180
+ auto_start_threshold: z.ZodDefault<z.ZodNumber>;
4181
+ auto_start_with_strategist: z.ZodDefault<z.ZodBoolean>;
4180
4182
  }, z.core.$strip>>;
4181
4183
  runtime_fallback: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
4182
4184
  enabled: z.ZodOptional<z.ZodBoolean>;
@@ -7,5 +7,7 @@ export declare const RalphLoopConfigSchema: z.ZodObject<{
7
7
  reset: "reset";
8
8
  continue: "continue";
9
9
  }>>;
10
+ auto_start_threshold: z.ZodDefault<z.ZodNumber>;
11
+ auto_start_with_strategist: z.ZodDefault<z.ZodBoolean>;
10
12
  }, z.core.$strip>;
11
13
  export type RalphLoopConfig = z.infer<typeof RalphLoopConfigSchema>;
@@ -0,0 +1,23 @@
1
+ import type { Todo } from "./types";
2
+ export type AutoLoopDecision = {
3
+ kind: "skip";
4
+ reason: string;
5
+ } | {
6
+ kind: "start";
7
+ openTodos: number;
8
+ goal: string;
9
+ };
10
+ export type AutoLoopInputs = {
11
+ threshold: number | undefined;
12
+ todos: Todo[];
13
+ alreadyStarted: boolean;
14
+ hasStartCallback: boolean;
15
+ };
16
+ /**
17
+ * Pure decision: should the enforcer auto-start ralph-loop right now?
18
+ * Kept separate so it can be unit-tested without mocking the full plugin
19
+ * context. The caller is responsible for actually invoking startRalphLoop
20
+ * and updating session state.
21
+ */
22
+ export declare function decideAutoLoop(input: AutoLoopInputs): AutoLoopDecision;
23
+ export declare function buildAutoLoopGoal(openTodos: number): string;
@@ -7,6 +7,11 @@ export declare function createTodoContinuationHandler(args: {
7
7
  backgroundManager?: BackgroundManager;
8
8
  skipAgents?: string[];
9
9
  isContinuationStopped?: (sessionID: string) => boolean;
10
+ autoLoopThreshold?: number;
11
+ startRalphLoop?: (sessionID: string, prompt: string, options?: {
12
+ ultrawork?: boolean;
13
+ }) => boolean;
14
+ isRalphLoopActive?: (sessionID: string) => boolean;
10
15
  }): (input: {
11
16
  event: {
12
17
  type: string;
@@ -8,4 +8,9 @@ export declare function handleSessionIdle(args: {
8
8
  backgroundManager?: BackgroundManager;
9
9
  skipAgents?: string[];
10
10
  isContinuationStopped?: (sessionID: string) => boolean;
11
+ autoLoopThreshold?: number;
12
+ startRalphLoop?: (sessionID: string, prompt: string, options?: {
13
+ ultrawork?: boolean;
14
+ }) => boolean;
15
+ isRalphLoopActive?: (sessionID: string) => boolean;
11
16
  }): Promise<void>;
@@ -4,6 +4,23 @@ export interface TodoContinuationEnforcerOptions {
4
4
  backgroundManager?: BackgroundManager;
5
5
  skipAgents?: string[];
6
6
  isContinuationStopped?: (sessionID: string) => boolean;
7
+ /**
8
+ * If set to N >= 1, the enforcer will auto-start ralph-loop on a session that
9
+ * has N or more open todos and no active loop. 0/undefined disables.
10
+ */
11
+ autoLoopThreshold?: number;
12
+ /**
13
+ * Lazy callback to start ralph-loop. Lazy because ralph-loop is created
14
+ * after the enforcer in the plugin wiring.
15
+ */
16
+ startRalphLoop?: (sessionID: string, prompt: string, options?: {
17
+ ultrawork?: boolean;
18
+ }) => boolean;
19
+ /**
20
+ * Lazy callback returning true if ralph-loop is currently active for the
21
+ * session. Used to avoid double-injection.
22
+ */
23
+ isRalphLoopActive?: (sessionID: string) => boolean;
7
24
  }
8
25
  export interface TodoContinuationEnforcer {
9
26
  handler: (input: {
@@ -40,6 +57,7 @@ export interface SessionState {
40
57
  recentCompactionAt?: number;
41
58
  recentCompactionEpoch?: number;
42
59
  acknowledgedCompactionEpoch?: number;
60
+ autoLoopStarted?: boolean;
43
61
  }
44
62
  export interface MessageInfo {
45
63
  id?: string;