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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +44 -0
  3. package/dist/agents/copy-editor.d.ts +2 -0
  4. package/dist/agents/definitions.d.ts +2 -0
  5. package/dist/agents/editor-in-chief.d.ts +2 -0
  6. package/dist/agents/fact-checker.d.ts +2 -0
  7. package/dist/agents/index.d.ts +12 -0
  8. package/dist/agents/index.test.d.ts +1 -0
  9. package/dist/agents/reader-advocate.d.ts +2 -0
  10. package/dist/agents/researcher.d.ts +2 -0
  11. package/dist/agents/section-editor.d.ts +2 -0
  12. package/dist/agents/types.d.ts +11 -0
  13. package/dist/agents/writer.d.ts +2 -0
  14. package/dist/config/constants.d.ts +3 -0
  15. package/dist/config/index.d.ts +3 -0
  16. package/dist/config/loader.d.ts +49 -0
  17. package/dist/config/loader.test.d.ts +1 -0
  18. package/dist/config/schema.d.ts +22 -0
  19. package/dist/hooks/delegation-tracker.d.ts +5 -0
  20. package/dist/hooks/extractors.d.ts +3 -0
  21. package/dist/hooks/extractors.test.d.ts +1 -0
  22. package/dist/hooks/index.d.ts +4 -0
  23. package/dist/hooks/system-enhancer.d.ts +3 -0
  24. package/dist/hooks/utils.d.ts +37 -0
  25. package/dist/hooks/utils.test.d.ts +1 -0
  26. package/dist/index.d.ts +11 -0
  27. package/dist/index.js +26618 -0
  28. package/dist/state.d.ts +24 -0
  29. package/dist/tools/file-manager.d.ts +35 -0
  30. package/dist/tools/index.d.ts +1 -0
  31. package/dist/utils/errors.d.ts +5 -0
  32. package/dist/utils/index.d.ts +2 -0
  33. package/dist/utils/logger.d.ts +3 -0
  34. package/package.json +53 -0
  35. package/prompts/copy-editor.md +100 -0
  36. package/prompts/editor-in-chief.md +112 -0
  37. package/prompts/fact-checker.md +52 -0
  38. package/prompts/reader-advocate.md +64 -0
  39. package/prompts/researcher.md +49 -0
  40. package/prompts/section-editor.md +67 -0
  41. package/prompts/writer.md +59 -0
  42. package/references/quality-rubric.md +40 -0
  43. package/references/slop-dictionary.md +118 -0
  44. package/references/style-guide.md +54 -0
  45. package/templates/brief-template.md +10 -0
  46. package/templates/context-template.md +7 -0
  47. 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,2 @@
1
+ import type { AgentDefinition } from './types';
2
+ export declare function createCopyEditorAgent(model: string, prompt: string): AgentDefinition;
@@ -0,0 +1,2 @@
1
+ import type { AgentTemplate } from './types';
2
+ export declare const AGENT_TEMPLATES: AgentTemplate[];
@@ -0,0 +1,2 @@
1
+ import type { AgentDefinition } from './types';
2
+ export declare function createEditorInChiefAgent(model: string, prompt: string): AgentDefinition;
@@ -0,0 +1,2 @@
1
+ import type { AgentDefinition } from './types';
2
+ export declare function createFactCheckerAgent(model: string, prompt: string): AgentDefinition;
@@ -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,2 @@
1
+ import type { AgentDefinition } from './types';
2
+ export declare function createReaderAdvocateAgent(model: string, prompt: string): AgentDefinition;
@@ -0,0 +1,2 @@
1
+ import type { AgentDefinition } from './types';
2
+ export declare function createResearcherAgent(model: string, prompt: string): AgentDefinition;
@@ -0,0 +1,2 @@
1
+ import type { AgentDefinition } from './types';
2
+ export declare function createSectionEditorAgent(model: string, prompt: string): AgentDefinition;
@@ -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,2 @@
1
+ import type { AgentDefinition } from './types';
2
+ export declare function createWriterAgent(model: string, prompt: string): AgentDefinition;
@@ -0,0 +1,3 @@
1
+ export declare const DEFAULT_MODELS: Record<string, string>;
2
+ export declare const MAX_FILE_SIZE = 10485760;
3
+ export declare const MAX_DIRECTORY_DEPTH = 10;
@@ -0,0 +1,3 @@
1
+ export * from './constants';
2
+ export * from './loader';
3
+ export * from './schema';
@@ -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,5 @@
1
+ import type { PluginConfig } from '../config';
2
+ export declare function createDelegationTrackerHook(config: PluginConfig): (input: {
3
+ sessionID: string;
4
+ agent?: string;
5
+ }, output: Record<string, unknown>) => Promise<void>;
@@ -0,0 +1,3 @@
1
+ export declare function extractCurrentPhase(content: string): string | null;
2
+ export declare function extractIncompleteTasks(content: string): string | null;
3
+ export declare function extractDecisions(content: string, limit?: number): string | null;
@@ -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,3 @@
1
+ import type { Hooks } from '@opencode-ai/plugin';
2
+ import type { PluginConfig } from '../config';
3
+ export declare function createSystemEnhancerHook(config: PluginConfig, directory: string): Partial<Hooks>;
@@ -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 {};
@@ -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;