opencode-writer-swarm 1.0.0
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/LICENSE +21 -0
- package/README.md +44 -0
- package/dist/agents/copy-editor.d.ts +2 -0
- package/dist/agents/definitions.d.ts +2 -0
- package/dist/agents/editor-in-chief.d.ts +2 -0
- package/dist/agents/fact-checker.d.ts +2 -0
- package/dist/agents/index.d.ts +12 -0
- package/dist/agents/index.test.d.ts +1 -0
- package/dist/agents/reader-advocate.d.ts +2 -0
- package/dist/agents/researcher.d.ts +2 -0
- package/dist/agents/section-editor.d.ts +2 -0
- package/dist/agents/types.d.ts +11 -0
- package/dist/agents/writer.d.ts +2 -0
- package/dist/config/constants.d.ts +3 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/loader.d.ts +49 -0
- package/dist/config/loader.test.d.ts +1 -0
- package/dist/config/schema.d.ts +22 -0
- package/dist/hooks/delegation-tracker.d.ts +5 -0
- package/dist/hooks/extractors.d.ts +3 -0
- package/dist/hooks/extractors.test.d.ts +1 -0
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/system-enhancer.d.ts +3 -0
- package/dist/hooks/utils.d.ts +37 -0
- package/dist/hooks/utils.test.d.ts +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +26618 -0
- package/dist/state.d.ts +24 -0
- package/dist/tools/file-manager.d.ts +35 -0
- package/dist/tools/index.d.ts +1 -0
- package/dist/utils/errors.d.ts +5 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/logger.d.ts +3 -0
- package/package.json +53 -0
- package/prompts/copy-editor.md +100 -0
- package/prompts/editor-in-chief.md +112 -0
- package/prompts/fact-checker.md +52 -0
- package/prompts/reader-advocate.md +64 -0
- package/prompts/researcher.md +49 -0
- package/prompts/section-editor.md +67 -0
- package/prompts/writer.md +59 -0
- package/references/quality-rubric.md +40 -0
- package/references/slop-dictionary.md +118 -0
- package/references/style-guide.md +54 -0
- package/templates/brief-template.md +10 -0
- package/templates/context-template.md +7 -0
- package/templates/plan-template.md +18 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 zaxbysauce
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# OpenCode Writer Swarm
|
|
2
|
+
# OpenCode-StoryForge
|
|
3
|
+
|
|
4
|
+
OpenCode-StoryForge is the editorial swarm plugin for OpenCode that orchestrates a curated team of AI agents to draft, research, revise, and polish multi-stage writing projects. Designed with GitHub and npm users in mind, it markets a catchy collaborative workflow, pro-level polishing crew, and clear configuration knobs while still linking down to the technical usage sections below.
|
|
5
|
+
|
|
6
|
+
## Agents
|
|
7
|
+
|
|
8
|
+
- **Editor-in-Chief**: Orchestrator, direction setter.
|
|
9
|
+
- **Writer**: Draft creator.
|
|
10
|
+
- **Researcher**: Fact gatherer.
|
|
11
|
+
- **Section Editor**: Structure reviewer.
|
|
12
|
+
- **Copy Editor**: Language polisher and AI slop remover.
|
|
13
|
+
- **Fact Checker**: Verification expert.
|
|
14
|
+
- **Reader Advocate**: Audience representative.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
## Editor-in-Chief Role
|
|
19
|
+
|
|
20
|
+
OpenCode surfaces `editor_in_chief` as the primary role inside the UI, so you can pick it from the agent selector just like any other OpenCode plugin. When you kick off the Editor-in-Chief, StoryForge inherits whichever model you selected for that session (the same behavior as `opencode-swarm`), and you can still override the model via `opencode-writer-swarm` config if you need a different fallback.
|
|
21
|
+
|
|
22
|
+
1. Add the plugin to your `opencode.json`.
|
|
23
|
+
2. Configure models in `~/.config/opencode/opencode-writer-swarm.json` or `.opencode/opencode-writer-swarm.json`.
|
|
24
|
+
3. Invoke `@editor_in_chief` with a writing request.
|
|
25
|
+
|
|
26
|
+
## Workflow
|
|
27
|
+
|
|
28
|
+
1. **Brief**: Editor-in-Chief creates a brief.
|
|
29
|
+
2. **Research**: Researcher gathers facts.
|
|
30
|
+
3. **Plan**: Editor-in-Chief creates a content plan.
|
|
31
|
+
4. **Draft**: Writer produces the first draft.
|
|
32
|
+
5. **Review**: Section Editor, Copy Editor, Fact Checker, and Reader Advocate review the draft.
|
|
33
|
+
6. **Polish**: Copy Editor does a final polish.
|
|
34
|
+
7. **Delivery**: Final output saved to `.writer/final/`.
|
|
35
|
+
|
|
36
|
+
## Configuration
|
|
37
|
+
|
|
38
|
+
| Setting | Description |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| `config_validation_enabled` (`CONFIG_VALIDATION_ENABLED`) | Enables the prototype-pollution guard in `deepMerge`. Set to `false` to fall back to the previous behavior. |
|
|
41
|
+
| `FILE_VALIDATION_ENABLED` | Controls symlink/size/depth validation for `.writer` files. Disable to revert to the legacy permissive reader. |
|
|
42
|
+
| `FILE_RETRY_ENABLED` / `WRITER_MAX_RETRIES` | Toggles exponential backoff retries for writer file writes and limits the number of retries. |
|
|
43
|
+
| `LOG_REDACTION_ENABLED` | When `true` (default), startup logs redact keys ending in `_KEY`, `_SECRET`, or `_TOKEN`. Setting to `false` temporarily disables redaction for debugging. |
|
|
44
|
+
| `VERBOSE_INIT` / `LOG_LEVEL=debug` | Emit detailed initialization metadata (agent count, sanitized config keys) during plugin startup. |
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AgentConfig as SDKAgentConfig } from '@opencode-ai/sdk';
|
|
2
|
+
import { type PluginConfig } from '../config';
|
|
3
|
+
import type { AgentDefinition } from './types';
|
|
4
|
+
export type { AgentDefinition } from './types';
|
|
5
|
+
/**
|
|
6
|
+
* Create all agent definitions with configuration applied
|
|
7
|
+
*/
|
|
8
|
+
export declare function createAgents(config?: PluginConfig): AgentDefinition[];
|
|
9
|
+
/**
|
|
10
|
+
* Get agent configurations formatted for the OpenCode SDK.
|
|
11
|
+
*/
|
|
12
|
+
export declare function getAgentConfigs(config?: PluginConfig): Record<string, SDKAgentConfig>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AgentConfig } from '@opencode-ai/sdk';
|
|
2
|
+
export interface AgentDefinition {
|
|
3
|
+
name: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
config: AgentConfig;
|
|
6
|
+
}
|
|
7
|
+
export interface AgentTemplate {
|
|
8
|
+
name: string;
|
|
9
|
+
description: string;
|
|
10
|
+
defaultTemperature: number;
|
|
11
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type PluginConfig } from './schema';
|
|
2
|
+
export declare const MAX_CONFIG_FILE_BYTES = 102400;
|
|
3
|
+
/**
|
|
4
|
+
* Structured error log entry for config loading failures.
|
|
5
|
+
*/
|
|
6
|
+
export interface ConfigLoadErrorLog {
|
|
7
|
+
timestamp: string;
|
|
8
|
+
filePath: string;
|
|
9
|
+
errorCode: string | null;
|
|
10
|
+
errorName: string;
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Log a structured error when config loading fails.
|
|
15
|
+
* Exported for testing purposes.
|
|
16
|
+
*/
|
|
17
|
+
export declare function logConfigLoadError(filePath: string, error: unknown): void;
|
|
18
|
+
/**
|
|
19
|
+
* Options for deepMerge function.
|
|
20
|
+
*/
|
|
21
|
+
export interface DeepMergeOptions {
|
|
22
|
+
enforceKeyFiltering?: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Deep merge two objects, with override values taking precedence.
|
|
26
|
+
* Arrays are replaced, not merged.
|
|
27
|
+
*
|
|
28
|
+
* When enforceKeyFiltering is enabled, keys like __proto__, constructor, and prototype
|
|
29
|
+
* are skipped to prevent prototype pollution attacks.
|
|
30
|
+
*/
|
|
31
|
+
export declare function deepMerge<T>(base?: T, override?: T, options?: DeepMergeOptions): T | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Load plugin configuration from user and project config files.
|
|
34
|
+
*
|
|
35
|
+
* Config locations:
|
|
36
|
+
* 1. User config: ~/.config/opencode/opencode-writer-swarm.json
|
|
37
|
+
* 2. Project config: <directory>/.opencode/opencode-writer-swarm.json
|
|
38
|
+
*
|
|
39
|
+
* Project config takes precedence.
|
|
40
|
+
*/
|
|
41
|
+
export declare function loadPluginConfig(directory: string): PluginConfig;
|
|
42
|
+
/**
|
|
43
|
+
* Load prompt file from prompts/ directory
|
|
44
|
+
*/
|
|
45
|
+
export declare function loadPrompt(name: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Load reference file from references/ directory
|
|
48
|
+
*/
|
|
49
|
+
export declare function loadReference(name: string): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const AgentOverrideConfigSchema: z.ZodObject<{
|
|
3
|
+
model: z.ZodOptional<z.ZodString>;
|
|
4
|
+
temperature: z.ZodOptional<z.ZodNumber>;
|
|
5
|
+
disabled: z.ZodOptional<z.ZodBoolean>;
|
|
6
|
+
}, z.core.$strip>;
|
|
7
|
+
export type AgentOverrideConfig = z.infer<typeof AgentOverrideConfigSchema>;
|
|
8
|
+
export declare const PluginConfigSchema: z.ZodObject<{
|
|
9
|
+
agents: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
10
|
+
model: z.ZodOptional<z.ZodString>;
|
|
11
|
+
temperature: z.ZodOptional<z.ZodNumber>;
|
|
12
|
+
disabled: z.ZodOptional<z.ZodBoolean>;
|
|
13
|
+
}, z.core.$strip>>>;
|
|
14
|
+
qa_retry_limit: z.ZodDefault<z.ZodNumber>;
|
|
15
|
+
file_retry_enabled: z.ZodDefault<z.ZodBoolean>;
|
|
16
|
+
max_file_operation_retries: z.ZodDefault<z.ZodNumber>;
|
|
17
|
+
config_validation_enabled: z.ZodDefault<z.ZodBoolean>;
|
|
18
|
+
}, z.core.$strip>;
|
|
19
|
+
export type PluginConfig = z.infer<typeof PluginConfigSchema>;
|
|
20
|
+
export declare function getFileRetryEnabled(config?: PluginConfig): boolean;
|
|
21
|
+
export declare function getMaxFileRetries(config?: PluginConfig): number;
|
|
22
|
+
export declare function getConfigValidationEnabled(config?: PluginConfig): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { safeHook, composeHandlers } from './utils';
|
|
2
|
+
import { createSystemEnhancerHook } from './system-enhancer';
|
|
3
|
+
import { createDelegationTrackerHook } from './delegation-tracker';
|
|
4
|
+
export { safeHook, composeHandlers, createSystemEnhancerHook, createDelegationTrackerHook, };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export declare function safeHook<I, O>(fn: (input: I, output: O) => Promise<void>): (input: I, output: O) => Promise<void>;
|
|
2
|
+
export declare function composeHandlers<I, O>(...fns: Array<(input: I, output: O) => Promise<void>>): (input: I, output: O) => Promise<void>;
|
|
3
|
+
declare function isFileValidationEnabled(): boolean;
|
|
4
|
+
declare function getMaxFileBytes(): number;
|
|
5
|
+
declare function getMaxScanDepth(): number;
|
|
6
|
+
export declare function validateWriterPath(directory: string, filename: string): string;
|
|
7
|
+
export declare function checkFileSizeLimit(filePath: string): void;
|
|
8
|
+
export declare function checkDirectoryDepth(currentDepth: number): void;
|
|
9
|
+
export declare function isSymlink(filePath: string): boolean;
|
|
10
|
+
export { getMaxFileBytes, getMaxScanDepth, isFileValidationEnabled };
|
|
11
|
+
export declare function readWriterFileAsync(directory: string, filename: string): Promise<string | null>;
|
|
12
|
+
/**
|
|
13
|
+
* Estimates the number of tokens in a text string.
|
|
14
|
+
*
|
|
15
|
+
* **Formula:** `tokenCount ≈ Math.ceil(characterCount × 0.33)`
|
|
16
|
+
*
|
|
17
|
+
* This is based on the general observation that one token corresponds to roughly
|
|
18
|
+
* 3 characters of English text on average. The multiplier of 0.33 (1/3) provides
|
|
19
|
+
* a conservative upper-bound estimate.
|
|
20
|
+
*
|
|
21
|
+
* **Accuracy:**
|
|
22
|
+
* - Expected variance: approximately ±40%
|
|
23
|
+
* - Actual token counts vary significantly based on:
|
|
24
|
+
* - Language (non-English text often requires more tokens per character)
|
|
25
|
+
* - Content type (code, technical terms, vs. natural language)
|
|
26
|
+
* - Tokenizer model (GPT-3/4, Claude, etc. use different tokenization schemes)
|
|
27
|
+
* - Presence of special characters, whitespace, and punctuation
|
|
28
|
+
*
|
|
29
|
+
* **Recommendation:**
|
|
30
|
+
* Use this function only for rough budget planning and preliminary size estimates.
|
|
31
|
+
* For precise token counting required for API limits or billing, use the actual
|
|
32
|
+
* tokenizer of the target model (e.g., tiktoken for OpenAI models).
|
|
33
|
+
*
|
|
34
|
+
* @param text - The input string to estimate token count for
|
|
35
|
+
* @returns The estimated number of tokens (rounded up), or 0 for empty/null input
|
|
36
|
+
*/
|
|
37
|
+
export declare function estimateTokens(text: string): number;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Plugin } from '@opencode-ai/plugin';
|
|
2
|
+
import type { AgentConfig as SDKAgentConfig, Config } from '@opencode-ai/sdk';
|
|
3
|
+
import { type PluginConfig } from './config';
|
|
4
|
+
export declare function getSafeConfigKeys(config: PluginConfig): string[];
|
|
5
|
+
export declare function formatStartupLog(agentCount: number, configKeys: string[], directory: string): string;
|
|
6
|
+
type StoryForgeConfig = Config & PluginConfig & {
|
|
7
|
+
agent?: Record<string, SDKAgentConfig>;
|
|
8
|
+
};
|
|
9
|
+
export declare function ensureAgentMap(opencodeConfig: StoryForgeConfig, agents: Record<string, SDKAgentConfig>, logger?: (message: string, data?: unknown) => void): StoryForgeConfig;
|
|
10
|
+
export declare const WriterSwarmPlugin: Plugin;
|
|
11
|
+
export default WriterSwarmPlugin;
|