@zcy2nn/agent-forge 1.0.1

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.
Files changed (189) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +266 -0
  3. package/agent-forge.schema.json +675 -0
  4. package/dist/agents/council.d.ts +27 -0
  5. package/dist/agents/councillor.d.ts +2 -0
  6. package/dist/agents/implementer.d.ts +2 -0
  7. package/dist/agents/index.d.ts +30 -0
  8. package/dist/agents/orchestrator.d.ts +30 -0
  9. package/dist/agents/researcher.d.ts +2 -0
  10. package/dist/agents/reviewer.d.ts +2 -0
  11. package/dist/cli/config-io.d.ts +24 -0
  12. package/dist/cli/config-manager.d.ts +4 -0
  13. package/dist/cli/custom-skills.d.ts +29 -0
  14. package/dist/cli/doctor.d.ts +38 -0
  15. package/dist/cli/index.d.ts +2 -0
  16. package/dist/cli/index.js +1932 -0
  17. package/dist/cli/install.d.ts +2 -0
  18. package/dist/cli/migration.d.ts +46 -0
  19. package/dist/cli/model-key-normalization.d.ts +1 -0
  20. package/dist/cli/paths.d.ts +35 -0
  21. package/dist/cli/providers.d.ts +99 -0
  22. package/dist/cli/skills.d.ts +52 -0
  23. package/dist/cli/system.d.ts +6 -0
  24. package/dist/cli/types.d.ts +40 -0
  25. package/dist/config/agent-mcps.d.ts +15 -0
  26. package/dist/config/constants.d.ts +28 -0
  27. package/dist/config/council-schema.d.ts +127 -0
  28. package/dist/config/index.d.ts +5 -0
  29. package/dist/config/loader.d.ts +57 -0
  30. package/dist/config/runtime-preset.d.ts +12 -0
  31. package/dist/config/schema.d.ts +371 -0
  32. package/dist/config/utils.d.ts +15 -0
  33. package/dist/council/council-manager.d.ts +49 -0
  34. package/dist/council/index.d.ts +1 -0
  35. package/dist/divoom/council.gif +0 -0
  36. package/dist/divoom/designer.gif +0 -0
  37. package/dist/divoom/explorer.gif +0 -0
  38. package/dist/divoom/fixer.gif +0 -0
  39. package/dist/divoom/input.gif +0 -0
  40. package/dist/divoom/intro.gif +0 -0
  41. package/dist/divoom/librarian.gif +0 -0
  42. package/dist/divoom/manager.d.ts +57 -0
  43. package/dist/divoom/oracle.gif +0 -0
  44. package/dist/divoom/orchestrator.gif +0 -0
  45. package/dist/hooks/apply-patch/codec.d.ts +7 -0
  46. package/dist/hooks/apply-patch/errors.d.ts +25 -0
  47. package/dist/hooks/apply-patch/execution-context.d.ts +27 -0
  48. package/dist/hooks/apply-patch/index.d.ts +15 -0
  49. package/dist/hooks/apply-patch/matching.d.ts +26 -0
  50. package/dist/hooks/apply-patch/operations.d.ts +3 -0
  51. package/dist/hooks/apply-patch/patch.d.ts +2 -0
  52. package/dist/hooks/apply-patch/prepared-changes.d.ts +17 -0
  53. package/dist/hooks/apply-patch/resolution.d.ts +19 -0
  54. package/dist/hooks/apply-patch/rewrite.d.ts +7 -0
  55. package/dist/hooks/apply-patch/test-helpers.d.ts +6 -0
  56. package/dist/hooks/apply-patch/types.d.ts +80 -0
  57. package/dist/hooks/auto-update-checker/cache.d.ts +11 -0
  58. package/dist/hooks/auto-update-checker/checker.d.ts +32 -0
  59. package/dist/hooks/auto-update-checker/constants.d.ts +11 -0
  60. package/dist/hooks/auto-update-checker/index.d.ts +18 -0
  61. package/dist/hooks/auto-update-checker/types.d.ts +22 -0
  62. package/dist/hooks/chat-headers.d.ts +16 -0
  63. package/dist/hooks/delegate-task-retry/guidance.d.ts +2 -0
  64. package/dist/hooks/delegate-task-retry/hook.d.ts +8 -0
  65. package/dist/hooks/delegate-task-retry/index.d.ts +4 -0
  66. package/dist/hooks/delegate-task-retry/patterns.d.ts +11 -0
  67. package/dist/hooks/filter-available-skills/index.d.ts +32 -0
  68. package/dist/hooks/foreground-fallback/index.d.ts +72 -0
  69. package/dist/hooks/image-hook.d.ts +19 -0
  70. package/dist/hooks/index.d.ts +13 -0
  71. package/dist/hooks/json-error-recovery/hook.d.ts +18 -0
  72. package/dist/hooks/json-error-recovery/index.d.ts +1 -0
  73. package/dist/hooks/phase-reminder/index.d.ts +26 -0
  74. package/dist/hooks/post-file-tool-nudge/index.d.ts +19 -0
  75. package/dist/hooks/task-session-manager/index.d.ts +52 -0
  76. package/dist/hooks/todo-continuation/index.d.ts +53 -0
  77. package/dist/hooks/todo-continuation/todo-hygiene.d.ts +35 -0
  78. package/dist/index.d.ts +5 -0
  79. package/dist/index.js +32981 -0
  80. package/dist/interview/dashboard.d.ts +62 -0
  81. package/dist/interview/document.d.ts +25 -0
  82. package/dist/interview/helpers.d.ts +10 -0
  83. package/dist/interview/index.d.ts +1 -0
  84. package/dist/interview/manager.d.ts +35 -0
  85. package/dist/interview/parser.d.ts +11 -0
  86. package/dist/interview/prompts.d.ts +7 -0
  87. package/dist/interview/server.d.ts +13 -0
  88. package/dist/interview/service.d.ts +34 -0
  89. package/dist/interview/types.d.ts +96 -0
  90. package/dist/interview/ui.d.ts +12 -0
  91. package/dist/mcp/context7.d.ts +6 -0
  92. package/dist/mcp/grep-app.d.ts +6 -0
  93. package/dist/mcp/index.d.ts +8 -0
  94. package/dist/mcp/types.d.ts +12 -0
  95. package/dist/mcp/websearch.d.ts +9 -0
  96. package/dist/multiplexer/factory.d.ts +26 -0
  97. package/dist/multiplexer/index.d.ts +9 -0
  98. package/dist/multiplexer/session-manager.d.ts +53 -0
  99. package/dist/multiplexer/tmux/index.d.ts +22 -0
  100. package/dist/multiplexer/types.d.ts +54 -0
  101. package/dist/multiplexer/zellij/index.d.ts +34 -0
  102. package/dist/tools/ast-grep/cli.d.ts +15 -0
  103. package/dist/tools/ast-grep/constants.d.ts +25 -0
  104. package/dist/tools/ast-grep/downloader.d.ts +5 -0
  105. package/dist/tools/ast-grep/index.d.ts +10 -0
  106. package/dist/tools/ast-grep/tools.d.ts +3 -0
  107. package/dist/tools/ast-grep/types.d.ts +30 -0
  108. package/dist/tools/ast-grep/utils.d.ts +4 -0
  109. package/dist/tools/council.d.ts +10 -0
  110. package/dist/tools/index.d.ts +6 -0
  111. package/dist/tools/preset-manager.d.ts +27 -0
  112. package/dist/tools/skill.d.ts +9 -0
  113. package/dist/tools/smartfetch/binary.d.ts +3 -0
  114. package/dist/tools/smartfetch/cache.d.ts +6 -0
  115. package/dist/tools/smartfetch/constants.d.ts +12 -0
  116. package/dist/tools/smartfetch/index.d.ts +3 -0
  117. package/dist/tools/smartfetch/network.d.ts +38 -0
  118. package/dist/tools/smartfetch/secondary-model.d.ts +28 -0
  119. package/dist/tools/smartfetch/tool.d.ts +3 -0
  120. package/dist/tools/smartfetch/types.d.ts +122 -0
  121. package/dist/tools/smartfetch/utils.d.ts +18 -0
  122. package/dist/tui-state.d.ts +15 -0
  123. package/dist/tui.d.ts +8 -0
  124. package/dist/tui.js +248 -0
  125. package/dist/utils/agent-variant.d.ts +63 -0
  126. package/dist/utils/compat.d.ts +30 -0
  127. package/dist/utils/env.d.ts +1 -0
  128. package/dist/utils/index.d.ts +9 -0
  129. package/dist/utils/internal-initiator.d.ts +6 -0
  130. package/dist/utils/logger.d.ts +8 -0
  131. package/dist/utils/polling.d.ts +21 -0
  132. package/dist/utils/session-manager.d.ts +55 -0
  133. package/dist/utils/session.d.ts +74 -0
  134. package/dist/utils/subagent-depth.d.ts +35 -0
  135. package/dist/utils/system-collapse.d.ts +6 -0
  136. package/dist/utils/task.d.ts +4 -0
  137. package/dist/utils/zip-extractor.d.ts +1 -0
  138. package/package.json +104 -0
  139. package/src/skills/brainstorming/SKILL.md +177 -0
  140. package/src/skills/brainstorming/scripts/frame-template.html +214 -0
  141. package/src/skills/brainstorming/scripts/helper.js +88 -0
  142. package/src/skills/brainstorming/scripts/server.cjs +354 -0
  143. package/src/skills/brainstorming/scripts/start-server.sh +148 -0
  144. package/src/skills/brainstorming/scripts/stop-server.sh +56 -0
  145. package/src/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
  146. package/src/skills/brainstorming/visual-companion.md +279 -0
  147. package/src/skills/codemap/README.md +59 -0
  148. package/src/skills/codemap/SKILL.md +163 -0
  149. package/src/skills/codemap/codemap.md +36 -0
  150. package/src/skills/codemap/scripts/codemap.mjs +483 -0
  151. package/src/skills/codemap/scripts/codemap.test.ts +129 -0
  152. package/src/skills/codemap.md +40 -0
  153. package/src/skills/dispatching-parallel-agents/SKILL.md +193 -0
  154. package/src/skills/executing-plans/SKILL.md +78 -0
  155. package/src/skills/finishing-a-development-branch/SKILL.md +211 -0
  156. package/src/skills/receiving-code-review/SKILL.md +224 -0
  157. package/src/skills/requesting-code-review/SKILL.md +113 -0
  158. package/src/skills/requesting-code-review/code-reviewer.md +146 -0
  159. package/src/skills/simplify/README.md +19 -0
  160. package/src/skills/simplify/SKILL.md +138 -0
  161. package/src/skills/simplify/codemap.md +36 -0
  162. package/src/skills/subagent-driven-development/SKILL.md +288 -0
  163. package/src/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
  164. package/src/skills/subagent-driven-development/implementer-prompt.md +113 -0
  165. package/src/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
  166. package/src/skills/systematic-debugging/CREATION-LOG.md +119 -0
  167. package/src/skills/systematic-debugging/SKILL.md +308 -0
  168. package/src/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
  169. package/src/skills/systematic-debugging/condition-based-waiting.md +115 -0
  170. package/src/skills/systematic-debugging/defense-in-depth.md +122 -0
  171. package/src/skills/systematic-debugging/find-polluter.sh +63 -0
  172. package/src/skills/systematic-debugging/root-cause-tracing.md +169 -0
  173. package/src/skills/systematic-debugging/test-academic.md +14 -0
  174. package/src/skills/systematic-debugging/test-pressure-1.md +58 -0
  175. package/src/skills/systematic-debugging/test-pressure-2.md +68 -0
  176. package/src/skills/systematic-debugging/test-pressure-3.md +69 -0
  177. package/src/skills/test-driven-development/SKILL.md +383 -0
  178. package/src/skills/test-driven-development/testing-anti-patterns.md +299 -0
  179. package/src/skills/using-git-worktrees/SKILL.md +226 -0
  180. package/src/skills/verification-before-completion/SKILL.md +147 -0
  181. package/src/skills/writing-plans/SKILL.md +165 -0
  182. package/src/skills/writing-plans/plan-document-reviewer-prompt.md +49 -0
  183. package/src/skills/writing-skills/SKILL.md +666 -0
  184. package/src/skills/writing-skills/anthropic-best-practices.md +1150 -0
  185. package/src/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
  186. package/src/skills/writing-skills/graphviz-conventions.dot +172 -0
  187. package/src/skills/writing-skills/persuasion-principles.md +187 -0
  188. package/src/skills/writing-skills/render-graphs.js +168 -0
  189. package/src/skills/writing-skills/testing-skills-with-subagents.md +384 -0
@@ -0,0 +1,30 @@
1
+ export declare const CLI_LANGUAGES: readonly ["bash", "c", "cpp", "csharp", "css", "elixir", "go", "haskell", "html", "java", "javascript", "json", "kotlin", "lua", "nix", "php", "python", "ruby", "rust", "scala", "solidity", "swift", "typescript", "tsx", "yaml"];
2
+ export type CliLanguage = (typeof CLI_LANGUAGES)[number];
3
+ export interface CliMatch {
4
+ file: string;
5
+ range: {
6
+ byteOffset: {
7
+ start: number;
8
+ end: number;
9
+ };
10
+ start: {
11
+ line: number;
12
+ column: number;
13
+ };
14
+ end: {
15
+ line: number;
16
+ column: number;
17
+ };
18
+ };
19
+ lines: string;
20
+ text: string;
21
+ replacement?: string;
22
+ language: string;
23
+ }
24
+ export interface SgResult {
25
+ matches: CliMatch[];
26
+ totalMatches: number;
27
+ truncated: boolean;
28
+ truncatedReason?: 'timeout' | 'max_output_bytes' | 'max_matches';
29
+ error?: string;
30
+ }
@@ -0,0 +1,4 @@
1
+ import type { CliLanguage, SgResult } from './types';
2
+ export declare function formatSearchResult(result: SgResult): string;
3
+ export declare function formatReplaceResult(result: SgResult, isDryRun: boolean): string;
4
+ export declare function getEmptyResultHint(pattern: string, lang: CliLanguage): string | null;
@@ -0,0 +1,10 @@
1
+ import { type PluginInput, type ToolDefinition } from '@opencode-ai/plugin';
2
+ import type { CouncilManager } from '../council/council-manager';
3
+ /**
4
+ * Creates the council_session tool for multi-LLM orchestration.
5
+ *
6
+ * This tool triggers a full council session: parallel councillors →
7
+ * formatted results returned to the council agent for synthesis.
8
+ * Available to the council agent.
9
+ */
10
+ export declare function createCouncilTool(_ctx: PluginInput, councilManager: CouncilManager): Record<string, ToolDefinition>;
@@ -0,0 +1,6 @@
1
+ export { ast_grep_replace, ast_grep_search } from './ast-grep';
2
+ export { createCouncilTool } from './council';
3
+ export type { PresetManager } from './preset-manager';
4
+ export { createPresetManager } from './preset-manager';
5
+ export { createSkillTool } from './skill';
6
+ export { createWebfetchTool } from './smartfetch';
@@ -0,0 +1,27 @@
1
+ import type { PluginInput } from '@opencode-ai/plugin';
2
+ import type { PluginConfig } from '../config';
3
+ /**
4
+ * Creates a preset manager for the /preset slash command.
5
+ *
6
+ * Uses the OpenCode SDK's client.config.update() to change agent models
7
+ * and temperatures without restarting. The server invalidates its agent
8
+ * cache and re-reads config on the next prompt.
9
+ *
10
+ * Note: activePreset is tracked in-memory only and resets on plugin reload.
11
+ * If the user manually edits config or another mechanism changes agents,
12
+ * this tracker may become stale until the next /preset call.
13
+ */
14
+ export declare function createPresetManager(ctx: PluginInput, config: PluginConfig): {
15
+ handleCommandExecuteBefore: (input: {
16
+ command: string;
17
+ sessionID: string;
18
+ arguments: string;
19
+ }, output: {
20
+ parts: Array<{
21
+ type: string;
22
+ text?: string;
23
+ }>;
24
+ }) => Promise<void>;
25
+ registerCommand: (opencodeConfig: Record<string, unknown>) => void;
26
+ };
27
+ export type PresetManager = ReturnType<typeof createPresetManager>;
@@ -0,0 +1,9 @@
1
+ import { type ToolDefinition } from '@opencode-ai/plugin/tool';
2
+ import { type PluginConfig } from '../config';
3
+ /**
4
+ * Create the skill loading tool.
5
+ *
6
+ * @param config - Plugin config, used for agent → skill permission resolution.
7
+ * @returns A record containing the `skill` tool definition.
8
+ */
9
+ export declare function createSkillTool(config: PluginConfig): Record<string, ToolDefinition>;
@@ -0,0 +1,3 @@
1
+ import type { BinaryFetch } from './types';
2
+ export declare function buildBinaryResultMessage(fetchResult: BinaryFetch, savedPath?: string): string;
3
+ export declare function saveBinary(binaryDir: string, data: Uint8Array, contentType: string, filename?: string): Promise<string>;
@@ -0,0 +1,6 @@
1
+ import { LRUCache } from 'lru-cache';
2
+ import type { FetchResult } from './types';
3
+ export declare const CACHE: LRUCache<string, FetchResult, unknown>;
4
+ export declare function buildCacheKey(url: string, extractMain: boolean, preferLlmsTxt: 'auto' | 'always' | 'never', saveBinary: boolean): string;
5
+ export declare function cacheFetchResult(fetchResult: FetchResult, extractMain: boolean, preferLlmsTxt: 'auto' | 'always' | 'never', saveBinary: boolean): void;
6
+ export declare function isInvalidLlmsResult(fetchResult: FetchResult | undefined): boolean;
@@ -0,0 +1,12 @@
1
+ export declare const DOCS_HOST_SUFFIXES: string[];
2
+ export declare const DOCS_HOST_PREFIXES: string[];
3
+ export declare const MAX_REDIRECTS = 10;
4
+ export declare const MAX_RESPONSE_BYTES: number;
5
+ export declare const MAX_BINARY_DOWNLOAD_BYTES: number;
6
+ export declare const DEFAULT_TIMEOUT_SECONDS = 30;
7
+ export declare const MAX_TIMEOUT_SECONDS = 120;
8
+ export declare const MAX_LLMS_PROBE_TIMEOUT_MS = 8000;
9
+ export declare const MAX_MODEL_CONTENT_CHARS = 100000;
10
+ export declare const DEFAULT_ACCEPT_LANGUAGE = "en;q=0.8,*;q=0.5";
11
+ export declare const BINARY_PREFIXES: string[];
12
+ export declare const WEBFETCH_DESCRIPTION = "Fetch a URL with better extraction for static/docs pages. Supports llms.txt probing, content-focused HTML extraction, metadata, redirects, and an optional prompt processed by a cheap secondary model.";
@@ -0,0 +1,3 @@
1
+ export { WEBFETCH_DESCRIPTION } from './constants';
2
+ export { createWebfetchTool } from './tool';
3
+ export type { BinaryFetch, CachedFetch, FetchResult, SmartfetchOptions, } from './types';
@@ -0,0 +1,38 @@
1
+ import type { BinaryFetch, DecodedBody, FetchResult, FetchWithRedirectsResult, LlmsProbeResult } from './types';
2
+ export declare function normalizeUrl(input: string): {
3
+ url: string;
4
+ upgradedToHttps: boolean;
5
+ fallbackUrl: string | undefined;
6
+ originalUrl: string;
7
+ };
8
+ export declare function isDocsLikeUrl(url: URL): boolean;
9
+ export declare function buildPermissionPatterns(normalized: ReturnType<typeof normalizeUrl>, shouldProbeLlmsTxt: boolean): string[];
10
+ export declare function buildAllowedOrigins(patterns: string[]): Set<string>;
11
+ export declare function canUseCanonicalCacheAlias(baseUrl: string, aliasUrl: string): boolean;
12
+ export declare function isBinaryContentType(contentType: string): boolean;
13
+ export declare function getBinaryKind(contentType: string): BinaryFetch['binaryKind'];
14
+ export declare function looksLikeHtmlText(text: string): boolean;
15
+ export declare function runWithScopedTimeout<T>(parentSignal: AbortSignal, timeoutMs: number, fn: (signal: AbortSignal) => Promise<T>): Promise<T>;
16
+ export declare function readBodyLimited(response: Response, maxBytes?: number): Promise<{
17
+ data: Uint8Array<ArrayBuffer>;
18
+ truncated: boolean;
19
+ }>;
20
+ export declare function fetchWithRedirects(url: string, _timeoutMs: number, format: 'text' | 'markdown' | 'html', signal: AbortSignal, extraHeaders?: Record<string, string>, method?: 'GET' | 'HEAD', allowedOrigins?: Set<string>): Promise<FetchWithRedirectsResult>;
21
+ export declare function fetchWithUpgradeFallback(normalized: ReturnType<typeof normalizeUrl>, timeoutMs: number, format: 'text' | 'markdown' | 'html', signal: AbortSignal, extraHeaders?: Record<string, string>, method?: 'GET' | 'HEAD', allowedOrigins?: Set<string>): Promise<{
22
+ result: FetchWithRedirectsResult;
23
+ upgradedToHttps: boolean;
24
+ }>;
25
+ export declare function isHtmlLikeContentType(contentType: string): boolean;
26
+ export declare function decodeBody(data: Uint8Array, charset: string | undefined, contentType?: string): DecodedBody;
27
+ export declare function looksLikeTextBody(data: Uint8Array): boolean;
28
+ export declare function isGenericBinaryMime(contentType: string): boolean;
29
+ export declare function extractHeaderMetadata(headers: Headers, finalUrl: string): {
30
+ contentType: string | undefined;
31
+ charset: string | undefined;
32
+ etag: string | undefined;
33
+ lastModified: string | undefined;
34
+ contentLength: number | undefined;
35
+ filename: string | undefined;
36
+ };
37
+ export declare function buildConditionalHeaders(cached: FetchResult | undefined): Record<string, string> | undefined;
38
+ export declare function probeLlmsText(url: URL, timeoutMs: number, signal: AbortSignal, fallbackOrigin?: string): Promise<LlmsProbeResult>;
@@ -0,0 +1,28 @@
1
+ import type { PluginInput } from '@opencode-ai/plugin';
2
+ import type { CachedFetch, SecondaryModel } from './types';
3
+ type OpenCodeClient = PluginInput['client'];
4
+ export declare function readSecondaryModelFromConfig(directory: string): Promise<SecondaryModel[]>;
5
+ export declare function decideSecondaryModelUse(fetchResult: CachedFetch, prompt: string | undefined, secondaryModels: SecondaryModel[]): {
6
+ use: boolean;
7
+ reason: "no_prompt";
8
+ } | {
9
+ use: boolean;
10
+ reason: "no_secondary_model_configured";
11
+ } | {
12
+ use: boolean;
13
+ reason: "empty_content";
14
+ } | {
15
+ use: boolean;
16
+ reason: "content_too_short";
17
+ } | {
18
+ use: boolean;
19
+ reason: "prompt_present";
20
+ };
21
+ export declare function runSecondaryModelWithFallback(client: OpenCodeClient, directory: string, models: SecondaryModel[], prompt: string, content: string): Promise<{
22
+ model: SecondaryModel;
23
+ text: string;
24
+ inputTruncated: boolean;
25
+ inputChars: number;
26
+ sourceChars: number;
27
+ }>;
28
+ export {};
@@ -0,0 +1,3 @@
1
+ import { type PluginInput, type ToolDefinition } from '@opencode-ai/plugin';
2
+ import type { SmartfetchOptions } from './types';
3
+ export declare function createWebfetchTool(pluginCtx: PluginInput, options?: SmartfetchOptions): ToolDefinition;
@@ -0,0 +1,122 @@
1
+ export type SmartfetchOptions = {
2
+ binaryDir?: string;
3
+ };
4
+ export type SecondaryModel = {
5
+ providerID: string;
6
+ modelID: string;
7
+ };
8
+ export type RedirectStep = {
9
+ from: string;
10
+ to: string;
11
+ status: number;
12
+ };
13
+ export type CachedFetch = {
14
+ requestedUrl: string;
15
+ finalUrl: string;
16
+ statusCode: number;
17
+ contentType: string;
18
+ charset?: string;
19
+ etag?: string;
20
+ lastModified?: string;
21
+ contentLength?: number;
22
+ filename?: string;
23
+ canonicalUrl?: string;
24
+ headings?: string[];
25
+ title?: string;
26
+ rawContent: string;
27
+ markdown: string;
28
+ text: string;
29
+ html: string;
30
+ extractedMain: boolean;
31
+ usedLlmsTxt: boolean;
32
+ sourceKind: 'llms_txt' | 'html' | 'text';
33
+ upgradedToHttps: boolean;
34
+ redirectChain: RedirectStep[];
35
+ truncated: boolean;
36
+ wordCount: number;
37
+ qualitySignals?: string[];
38
+ llmsProbeError?: string;
39
+ llmsProbeTruncated?: boolean;
40
+ cacheRevalidated?: boolean;
41
+ upstreamStatusCode?: number;
42
+ cacheHit?: boolean;
43
+ decodedCharset?: string;
44
+ decodeFallback?: boolean;
45
+ decodeWarning?: string;
46
+ secondaryModelInputTruncated?: boolean;
47
+ secondaryModelInputChars?: number;
48
+ secondaryModelSourceChars?: number;
49
+ };
50
+ export type BinaryFetch = {
51
+ requestedUrl: string;
52
+ finalUrl: string;
53
+ statusCode: number;
54
+ contentType: string;
55
+ charset?: string;
56
+ etag?: string;
57
+ lastModified?: string;
58
+ contentLength?: number;
59
+ filename?: string;
60
+ canonicalUrl?: string;
61
+ redirectChain: RedirectStep[];
62
+ upgradedToHttps: boolean;
63
+ truncated: boolean;
64
+ binary: true;
65
+ binaryKind: 'image' | 'audio' | 'video' | 'pdf' | 'binary';
66
+ downloadLimitBytes?: number;
67
+ metadataOnly?: boolean;
68
+ data?: Uint8Array;
69
+ llmsProbeError?: string;
70
+ llmsProbeTruncated?: boolean;
71
+ cacheRevalidated?: boolean;
72
+ upstreamStatusCode?: number;
73
+ cacheHit?: boolean;
74
+ };
75
+ export type FetchResult = CachedFetch | BinaryFetch;
76
+ export type DecodedBody = {
77
+ text: string;
78
+ decodedCharset: string;
79
+ decodeFallback: boolean;
80
+ decodeWarning?: string;
81
+ };
82
+ export type ExtractedContent = {
83
+ title?: string;
84
+ rawContent: string;
85
+ markdown: string;
86
+ text: string;
87
+ html: string;
88
+ extractedMain: boolean;
89
+ canonicalUrl?: string;
90
+ headings?: string[];
91
+ };
92
+ export type FetchWithRedirectsResult = {
93
+ blockedRedirect: true;
94
+ redirectUrl: string;
95
+ statusCode: number;
96
+ redirectChain: RedirectStep[];
97
+ } | {
98
+ response: Response;
99
+ finalUrl: string;
100
+ redirectChain: RedirectStep[];
101
+ };
102
+ export type LlmsProbeResult = {
103
+ url: string;
104
+ statusCode: number;
105
+ redirectChain: RedirectStep[];
106
+ text: string;
107
+ headers: {
108
+ contentType?: string;
109
+ charset?: string;
110
+ etag?: string;
111
+ lastModified?: string;
112
+ contentLength?: number;
113
+ filename?: string;
114
+ };
115
+ truncated: boolean;
116
+ decodedCharset: string;
117
+ decodeFallback: boolean;
118
+ decodeWarning?: string;
119
+ upgradedToHttps: boolean;
120
+ } | {
121
+ error?: string;
122
+ };
@@ -0,0 +1,18 @@
1
+ import type { CachedFetch, ExtractedContent } from './types';
2
+ export declare function wordCount(text: string): number;
3
+ export declare function frontmatter(metadata: Record<string, unknown>): string;
4
+ export declare function trimBlankRuns(input: string): string;
5
+ export declare function cleanHeadingText(input: string): string;
6
+ export declare function cleanFetchedMarkdown(input: string): string;
7
+ export declare function cleanFetchedText(input: string): string;
8
+ export declare function escapeHtml(input: string): string;
9
+ export declare function withTruncationMarker(content: string, format: 'text' | 'markdown' | 'html', truncated: boolean): string;
10
+ export declare function joinRenderedContent(metadata: string, content: string, format: 'text' | 'markdown' | 'html'): string;
11
+ export declare function renderMessageForFormat(content: string, format: 'text' | 'markdown' | 'html'): string;
12
+ export declare function buildRedirectResultMessage(originalUrl: string, redirectUrl: string, statusCode: number): string;
13
+ export declare function buildLlmsRequiredMessage(originalUrl: string, reason?: string): string;
14
+ export declare function extractFromHtml(html: string, finalUrl: string, extractMain: boolean): Promise<ExtractedContent>;
15
+ export declare function inferCanonicalUrlFromText(content: string, finalUrl: string): string | undefined;
16
+ export declare function extractHeadingsFromMarkdown(content: string): string[] | undefined;
17
+ export declare function detectQualitySignals(fetchResult: Pick<CachedFetch, 'text' | 'markdown' | 'rawContent' | 'wordCount' | 'sourceKind' | 'extractedMain'>): string[];
18
+ export declare function pickContent(fetchResult: CachedFetch, format: 'text' | 'markdown' | 'html'): string;
@@ -0,0 +1,15 @@
1
+ export interface TuiSnapshot {
2
+ version: 1;
3
+ updatedAt: number;
4
+ agentModels: Record<string, string>;
5
+ }
6
+ export declare function getTuiStatePath(): string;
7
+ export declare function readTuiSnapshot(): TuiSnapshot;
8
+ export declare function readTuiSnapshotAsync(): Promise<TuiSnapshot>;
9
+ export declare function recordTuiAgentModels(input: {
10
+ agentModels: Record<string, string>;
11
+ }): void;
12
+ export declare function recordTuiAgentModel(input: {
13
+ agentName: string;
14
+ model: string;
15
+ }): void;
package/dist/tui.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ import type { TuiPluginModule } from '@opencode-ai/plugin/tui';
2
+ import { type TuiSnapshot } from './tui-state';
3
+ export declare function formatSidebarModelName(model: string): string;
4
+ export declare function getSidebarAgentNames(snapshot: TuiSnapshot): string[];
5
+ declare const plugin: TuiPluginModule & {
6
+ id: string;
7
+ };
8
+ export default plugin;
package/dist/tui.js ADDED
@@ -0,0 +1,248 @@
1
+ import { createRequire } from "node:module";
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
12
+ var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
20
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
21
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
+ for (let key of __getOwnPropNames(mod))
23
+ if (!__hasOwnProp.call(to, key))
24
+ __defProp(to, key, {
25
+ get: __accessProp.bind(mod, key),
26
+ enumerable: true
27
+ });
28
+ if (canCache)
29
+ cache.set(mod, to);
30
+ return to;
31
+ };
32
+ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
34
+
35
+ // src/tui.ts
36
+ import { createElement, insert, setProp } from "@opentui/solid";
37
+
38
+ // src/config/constants.ts
39
+ var SUBAGENT_NAMES = [
40
+ "researcher",
41
+ "reviewer",
42
+ "implementer",
43
+ "council",
44
+ "councillor"
45
+ ];
46
+ var ORCHESTRATOR_NAME = "orchestrator";
47
+ var ALL_AGENT_NAMES = [ORCHESTRATOR_NAME, ...SUBAGENT_NAMES];
48
+ var PROTECTED_AGENTS = new Set([
49
+ "orchestrator",
50
+ "researcher",
51
+ "reviewer",
52
+ "implementer",
53
+ "councillor"
54
+ ]);
55
+ var DEFAULT_MODELS = {
56
+ orchestrator: undefined,
57
+ reviewer: "openai/gpt-5.5",
58
+ researcher: "openai/gpt-5.4-mini",
59
+ implementer: "openai/gpt-5.4-mini",
60
+ council: "openai/gpt-5.4-mini",
61
+ councillor: "openai/gpt-5.4-mini"
62
+ };
63
+ var POLL_INTERVAL_BACKGROUND_MS = 2000;
64
+ var DEFAULT_TIMEOUT_MS = 2 * 60 * 1000;
65
+ var MAX_POLL_TIME_MS = 5 * 60 * 1000;
66
+ var DEFAULT_MAX_SUBAGENT_DEPTH = 3;
67
+ var PHASE_REMINDER_TEXT = `!IMPORTANT! Recall the workflow rules:
68
+ Understand → choose the best parallelized path based on your capabilities and agents delegation rules → recall session reuse rules → execute → verify.
69
+ If delegating, launch the specialist in the same turn you mention it !END!`;
70
+ var TMUX_SPAWN_DELAY_MS = 500;
71
+ var COUNCILLOR_STAGGER_MS = 250;
72
+ var DEFAULT_DISABLED_AGENTS = ["council"];
73
+
74
+ // src/tui-state.ts
75
+ import * as fs from "node:fs";
76
+ import * as os from "node:os";
77
+ import * as path from "node:path";
78
+ var STATE_DIR = "agent-forge";
79
+ var STATE_FILE = "tui-state.json";
80
+ function dataDir() {
81
+ return process.env.XDG_DATA_HOME ?? path.join(os.homedir(), ".local", "share");
82
+ }
83
+ function getTuiStatePath() {
84
+ return path.join(dataDir(), "opencode", "storage", STATE_DIR, STATE_FILE);
85
+ }
86
+ function emptySnapshot() {
87
+ return {
88
+ version: 1,
89
+ updatedAt: Date.now(),
90
+ agentModels: {}
91
+ };
92
+ }
93
+ function parseSnapshot(value) {
94
+ const parsed = JSON.parse(value);
95
+ if (parsed?.version !== 1)
96
+ return emptySnapshot();
97
+ return {
98
+ version: 1,
99
+ updatedAt: typeof parsed.updatedAt === "number" ? parsed.updatedAt : Date.now(),
100
+ agentModels: parsed.agentModels ?? {}
101
+ };
102
+ }
103
+ function readTuiSnapshot() {
104
+ try {
105
+ return parseSnapshot(fs.readFileSync(getTuiStatePath(), "utf8"));
106
+ } catch {
107
+ return emptySnapshot();
108
+ }
109
+ }
110
+ async function readTuiSnapshotAsync() {
111
+ try {
112
+ return parseSnapshot(await fs.promises.readFile(getTuiStatePath(), "utf8"));
113
+ } catch {
114
+ return emptySnapshot();
115
+ }
116
+ }
117
+ function writeTuiSnapshot(snapshot) {
118
+ try {
119
+ const filePath = getTuiStatePath();
120
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
121
+ fs.writeFileSync(filePath, `${JSON.stringify(snapshot)}
122
+ `);
123
+ } catch {}
124
+ }
125
+ function updateSnapshot(mutator) {
126
+ const snapshot = readTuiSnapshot();
127
+ mutator(snapshot);
128
+ snapshot.updatedAt = Date.now();
129
+ writeTuiSnapshot(snapshot);
130
+ }
131
+ function recordTuiAgentModels(input) {
132
+ updateSnapshot((snapshot) => {
133
+ snapshot.agentModels = { ...input.agentModels };
134
+ });
135
+ }
136
+ function recordTuiAgentModel(input) {
137
+ updateSnapshot((snapshot) => {
138
+ snapshot.agentModels[input.agentName] = input.model;
139
+ });
140
+ }
141
+
142
+ // src/tui.ts
143
+ var PLUGIN_NAME = "agent-forge";
144
+ var FALLBACK_SIDEBAR_AGENTS = SUBAGENT_NAMES.filter((agent) => agent !== "councillor" && agent !== "council" && !DEFAULT_DISABLED_AGENTS.includes(agent));
145
+ var BORDER = { type: "single" };
146
+ async function readPackageVersion() {
147
+ try {
148
+ const packageJson = await Bun.file(new URL("../package.json", import.meta.url)).json();
149
+ return typeof packageJson.version === "string" ? packageJson.version : undefined;
150
+ } catch {
151
+ return;
152
+ }
153
+ }
154
+ function element(tag, props, children = []) {
155
+ const node = createElement(tag);
156
+ for (const [key, value] of Object.entries(props)) {
157
+ if (value !== undefined)
158
+ setProp(node, key, value);
159
+ }
160
+ for (const child of children) {
161
+ if (child === null || child === undefined || child === false)
162
+ continue;
163
+ insert(node, child);
164
+ }
165
+ return node;
166
+ }
167
+ function text(props, children) {
168
+ return element("text", props, children);
169
+ }
170
+ function box(props, children = []) {
171
+ return element("box", props, children);
172
+ }
173
+ function truncate(value, max = 24) {
174
+ return value.length > max ? `${value.slice(0, max - 1)}…` : value;
175
+ }
176
+ function formatSidebarModelName(model) {
177
+ const lastSlash = model.lastIndexOf("/");
178
+ return lastSlash === -1 ? model : model.slice(lastSlash + 1);
179
+ }
180
+ function getSidebarAgentNames(snapshot) {
181
+ const configuredAgents = Object.keys(snapshot.agentModels);
182
+ return configuredAgents.length > 0 ? configuredAgents : FALLBACK_SIDEBAR_AGENTS;
183
+ }
184
+ function row(label, value, theme, valueColor) {
185
+ return box({ width: "100%", flexDirection: "row", justifyContent: "space-between" }, [
186
+ text({ fg: theme.textMuted }, [label]),
187
+ text({ fg: valueColor ?? theme.text }, [value])
188
+ ]);
189
+ }
190
+ function renderSidebar(snapshot, version, theme) {
191
+ return box({
192
+ width: "100%",
193
+ flexDirection: "column",
194
+ border: BORDER,
195
+ borderColor: theme.borderActive,
196
+ paddingTop: 1,
197
+ paddingBottom: 1,
198
+ paddingLeft: 1,
199
+ paddingRight: 1
200
+ }, [
201
+ box({
202
+ width: "100%",
203
+ flexDirection: "row",
204
+ justifyContent: "space-between",
205
+ alignItems: "center"
206
+ }, [
207
+ box({ paddingLeft: 1, paddingRight: 1, backgroundColor: theme.accent }, [text({ fg: theme.background }, ["OMO-Slim"])]),
208
+ text({ fg: theme.textMuted }, [`v${version}`])
209
+ ]),
210
+ box({ width: "100%", marginTop: 1 }, [
211
+ text({ fg: theme.text }, ["Agents"])
212
+ ]),
213
+ ...getSidebarAgentNames(snapshot).map((agentName) => {
214
+ const model = snapshot.agentModels[agentName] ?? "pending";
215
+ return row(agentName, truncate(formatSidebarModelName(model), 26), theme, theme.textMuted);
216
+ })
217
+ ]);
218
+ }
219
+ var plugin = {
220
+ id: `${PLUGIN_NAME}:tui`,
221
+ tui: async (api, _options, meta) => {
222
+ const version = meta.version ?? await readPackageVersion() ?? "dev";
223
+ let snapshot = readTuiSnapshot();
224
+ const renderTimer = setInterval(async () => {
225
+ try {
226
+ snapshot = await readTuiSnapshotAsync();
227
+ api.renderer.requestRender();
228
+ } catch {}
229
+ }, 1000);
230
+ api.lifecycle.onDispose(() => {
231
+ clearInterval(renderTimer);
232
+ });
233
+ api.slots.register({
234
+ order: 900,
235
+ slots: {
236
+ sidebar_content() {
237
+ return renderSidebar(snapshot, version, api.theme.current);
238
+ }
239
+ }
240
+ });
241
+ }
242
+ };
243
+ var tui_default = plugin;
244
+ export {
245
+ getSidebarAgentNames,
246
+ formatSidebarModelName,
247
+ tui_default as default
248
+ };