opencode-sdlc-plugin 0.1.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 (42) hide show
  1. package/README.md +60 -0
  2. package/agents/design-facilitator.md +8 -0
  3. package/agents/domain.md +9 -0
  4. package/agents/exploration.md +8 -0
  5. package/agents/green.md +9 -0
  6. package/agents/marvin.md +15 -0
  7. package/agents/model-checker.md +9 -0
  8. package/agents/red.md +9 -0
  9. package/commands/sdlc-adr.md +37 -0
  10. package/commands/sdlc-design.md +88 -0
  11. package/commands/sdlc-domain-audit.md +32 -0
  12. package/commands/sdlc-plan.md +63 -0
  13. package/commands/sdlc-pr.md +43 -0
  14. package/commands/sdlc-recall.md +18 -0
  15. package/commands/sdlc-remember.md +19 -0
  16. package/commands/sdlc-review.md +192 -0
  17. package/commands/sdlc-setup.md +50 -0
  18. package/commands/sdlc-start.md +34 -0
  19. package/commands/sdlc-work.md +118 -0
  20. package/config/presets/event-modeling.json +12 -0
  21. package/config/presets/traditional.json +12 -0
  22. package/config/schemas/sdlc.schema.json +48 -0
  23. package/dist/cli/index.d.ts +1 -0
  24. package/dist/cli/index.js +703 -0
  25. package/dist/cli/index.js.map +1 -0
  26. package/dist/index.d.ts +2 -0
  27. package/dist/index.js +474 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/plugin/index.d.ts +5 -0
  30. package/dist/plugin/index.js +476 -0
  31. package/dist/plugin/index.js.map +1 -0
  32. package/package.json +56 -0
  33. package/skills/adr-policy.md +21 -0
  34. package/skills/atomic-design.md +39 -0
  35. package/skills/debugging-protocol.md +47 -0
  36. package/skills/event-modeling.md +40 -0
  37. package/skills/git-spice.md +44 -0
  38. package/skills/github-issues.md +44 -0
  39. package/skills/memory-protocol.md +41 -0
  40. package/skills/orchestration.md +118 -0
  41. package/skills/skill-enforcement.md +56 -0
  42. package/skills/tdd-constraints.md +63 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/index.ts","../../src/shared/constants.ts","../../src/cli/commands/install.ts","../../src/cli/questions/install-prompts.ts","../../src/cli/generators/config-generator.ts","../../src/cli/generators/sdlc-config.ts","../../src/cli/generators/omo-config.ts","../../src/cli/utils/persona-builder.ts","../../src/cli/generators/opencode-config.ts","../../src/cli/utils/file-manager.ts","../../src/cli/utils/prerequisites.ts","../../src/cli/utils/logger.ts","../../src/cli/commands/doctor.ts","../../src/cli/commands/upgrade.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { Command } from \"commander\";\nimport kleur from \"kleur\";\nimport { DISPLAY_NAME, TAGLINE, VERSION } from \"../shared/constants.js\";\nimport { install } from \"./commands/install.js\";\nimport { doctor } from \"./commands/doctor.js\";\nimport { upgrade } from \"./commands/upgrade.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"sdlc-plugin\")\n .description(`${kleur.cyan(DISPLAY_NAME)} - ${TAGLINE}`)\n .version(VERSION);\n\nprogram\n .command(\"install\")\n .description(\"Install and configure OpenCode SDLC plugin\")\n .option(\"-y, --yes\", \"Skip confirmation prompts\", false)\n .option(\"--global\", \"Install globally (default)\", true)\n .option(\"--local\", \"Install to current project only\", false)\n .option(\"--reconfigure\", \"Force full reconfiguration\", false)\n .action(async (options: { yes?: boolean; global?: boolean; local?: boolean; reconfigure?: boolean }) => {\n await install(options);\n });\n\nprogram\n .command(\"upgrade\")\n .description(\"Upgrade OpenCode SDLC plugin\")\n .option(\"--check\", \"Check for updates without installing\", false)\n .option(\"-y, --yes\", \"Skip confirmation prompts\", false)\n .action(upgrade);\n\nprogram\n .command(\"doctor\")\n .description(\"Diagnose and fix common setup issues\")\n .option(\"--fix\", \"Attempt to fix detected issues\", false)\n .action(doctor);\n\nprogram.parse();\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const VERSION = \"0.1.0\";\nexport const DISPLAY_NAME = \"OpenCode SDLC Plugin\";\nexport const TAGLINE = \"Agentic SDLC workflow with discovery, ADRs, and TDD\";\n\nexport const PACKAGE_NAME = \"sdlc-plugin\";\n\nexport const GLOBAL_CONFIG_DIR = join(homedir(), \".config\", \"opencode\");\nexport const LOCAL_CONFIG_DIR_NAME = \".opencode\";\n\nexport const CONFIG_FILE_NAME = \"sdlc.json\";\nexport const STATE_FILE_NAME = \"sdlc-state.json\";\nexport const OPENCODE_CONFIG_NAME = \"opencode.json\";\nexport const OMO_CONFIG_NAME = \"oh-my-opencode.json\";\nexport const COMMAND_DIR_NAME = \"command\";\n\nexport const DEFAULT_AGENT_MODEL = \"anthropic/claude-sonnet-4-5-thinking\";\n\nexport const CONFIG_PATHS = {\n globalConfigDir: GLOBAL_CONFIG_DIR,\n globalSdlcConfig: join(GLOBAL_CONFIG_DIR, CONFIG_FILE_NAME),\n globalOpencodeConfig: join(GLOBAL_CONFIG_DIR, OPENCODE_CONFIG_NAME),\n globalOhMyOpencodeConfig: join(GLOBAL_CONFIG_DIR, OMO_CONFIG_NAME),\n commandsDir: join(GLOBAL_CONFIG_DIR, COMMAND_DIR_NAME),\n};\n","import kleur from \"kleur\";\nimport ora from \"ora\";\nimport type { InstallAnswers, InstallLocation } from \"../../shared/types.js\";\nimport { VERSION } from \"../../shared/constants.js\";\nimport {\n compileInstallAnswers,\n gatherGitHub,\n gatherLanguages,\n gatherMethodology,\n gatherModels,\n gatherSubscriptions,\n} from \"../questions/install-prompts.js\";\nimport { ConfigGenerator } from \"../generators/config-generator.js\";\nimport { FileManager } from \"../utils/file-manager.js\";\nimport { checkPrerequisites } from \"../utils/prerequisites.js\";\nimport { logger } from \"../utils/logger.js\";\n\ninterface InstallOptions {\n yes?: boolean;\n global?: boolean;\n local?: boolean;\n reconfigure?: boolean;\n}\n\nfunction resolveInstallLocation(options: InstallOptions): InstallLocation {\n if (options.local === true) return \"local\";\n return \"global\";\n}\n\nexport async function install(options: InstallOptions): Promise<void> {\n logger.banner();\n\n const spinner = ora(\"Checking prerequisites...\").start();\n const prereqs = checkPrerequisites();\n\n if (!prereqs.node.installed) {\n spinner.fail(\"Node.js not found\");\n logger.error(\"Please install Node.js 20+ first: https://nodejs.org/\");\n process.exit(1);\n }\n\n if (!prereqs.opencode.installed) {\n spinner.fail(\"OpenCode not found\");\n logger.error(\"Please install OpenCode first: https://opencode.ai/docs\");\n process.exit(1);\n }\n\n if (!prereqs.ohMyOpencode.installed) {\n spinner.fail(\"oh-my-opencode not found\");\n logger.error(\"Please install oh-my-opencode first: https://github.com/khulnasoft/oh-my-opencode\");\n process.exit(1);\n }\n\n spinner.succeed(\"Prerequisites checked\");\n\n const subscriptions = await gatherSubscriptions();\n const availableModels = prereqs.opencode.models.length > 0 ? prereqs.opencode.models : [\"anthropic/claude-sonnet-4-5-thinking\"];\n const models = await gatherModels(availableModels);\n const methodology = await gatherMethodology();\n const github = await gatherGitHub();\n const languages = await gatherLanguages();\n const installLocation = resolveInstallLocation(options);\n\n const answers: InstallAnswers = compileInstallAnswers({\n subscriptions,\n models,\n methodology,\n github,\n languages,\n installLocation,\n });\n\n const generator = new ConfigGenerator(answers);\n const files = generator.generate();\n\n const fileManager = new FileManager(generator.getConfigDir());\n\n const installSpinner = ora(\"Writing configuration...\").start();\n await fileManager.writeFiles(files);\n installSpinner.succeed(\"Configuration written\");\n\n const dependencySpinner = ora(\"Installing dependencies...\").start();\n const packages = generator.getRequiredPackages();\n await fileManager.installDependencies(packages);\n dependencySpinner.succeed(\"Dependencies installed\");\n\n const commandSpinner = ora(\"Installing commands...\").start();\n const copiedCommands = await fileManager.copyCommands();\n commandSpinner.succeed(`Installed ${copiedCommands.length} commands`);\n\n const ghSpinner = ora(\"Installing GitHub CLI extensions...\").start();\n const ghResult = await fileManager.installGitHubExtensions();\n if (ghResult.failed.length > 0) {\n ghSpinner.warn(\n `GitHub extensions: ${ghResult.installed.length} installed, ` +\n `${ghResult.skipped.length} skipped, ${ghResult.failed.length} failed`\n );\n for (const failure of ghResult.failed) {\n logger.warn(` Failed to install ${failure.name}: ${failure.error}`);\n }\n } else {\n ghSpinner.succeed(\n `GitHub extensions: ${ghResult.installed.length} installed, ${ghResult.skipped.length} already present`\n );\n }\n\n logger.successBanner(`SDLC PLUGIN ${VERSION} INSTALLED`);\n\n logger.info(\"Next steps:\");\n logger.info(kleur.gray(\" 1. Restart OpenCode\"));\n logger.info(kleur.gray(\" 2. Run /sdlc-setup in your project\"));\n logger.info(\"\");\n}\n","import { confirm, input, select } from \"@inquirer/prompts\";\nimport type { GitHubAnswers, InstallAnswers, LanguageConfig, MethodologyAnswers, ModelAnswers, SubscriptionAnswers } from \"../../shared/types.js\";\nimport { DEFAULT_AGENT_MODEL } from \"../../shared/constants.js\";\n\nexport async function gatherSubscriptions(): Promise<SubscriptionAnswers> {\n const hasClaude = await confirm({\n message: \"Do you have a Claude subscription?\",\n default: true,\n });\n const hasOpenAI = await confirm({\n message: \"Do you have an OpenAI subscription?\",\n default: true,\n });\n const hasGoogle = await confirm({\n message: \"Do you have a Google/Gemini subscription?\",\n default: false,\n });\n const hasGitHubCopilot = await confirm({\n message: \"Do you have GitHub Copilot?\",\n default: false,\n });\n\n return { hasClaude, hasOpenAI, hasGoogle, hasGitHubCopilot };\n}\n\nexport async function gatherModels(availableModels: string[]): Promise<ModelAnswers> {\n const modelChoice = async (message: string, defaultModel: string): Promise<string> => {\n const firstModel = availableModels[0];\n if (availableModels.length === 1) {\n return firstModel ?? defaultModel;\n }\n const selection = await select<string>({\n message,\n choices: availableModels.map((id) => ({ name: id, value: id })),\n default: firstModel ?? defaultModel,\n });\n return selection;\n };\n\n return {\n orchestrator: await modelChoice(\"Model for Marvin orchestrator\", DEFAULT_AGENT_MODEL),\n red: await modelChoice(\"Model for RED agent\", DEFAULT_AGENT_MODEL),\n green: await modelChoice(\"Model for GREEN agent\", DEFAULT_AGENT_MODEL),\n domain: await modelChoice(\"Model for DOMAIN agent\", DEFAULT_AGENT_MODEL),\n architect: await modelChoice(\"Model for ARCHITECT agent\", DEFAULT_AGENT_MODEL),\n discovery: await modelChoice(\"Model for DISCOVERY agent\", DEFAULT_AGENT_MODEL),\n exploration: await modelChoice(\"Model for EXPLORATION agent\", DEFAULT_AGENT_MODEL),\n workflowDesigner: await modelChoice(\"Model for WORKFLOW DESIGNER agent\", DEFAULT_AGENT_MODEL),\n gwt: await modelChoice(\"Model for GWT agent\", DEFAULT_AGENT_MODEL),\n modelChecker: await modelChoice(\"Model for MODEL CHECKER agent\", DEFAULT_AGENT_MODEL),\n codeReviewer: await modelChoice(\"Model for CODE REVIEWER agent\", DEFAULT_AGENT_MODEL),\n story: await modelChoice(\"Model for STORY agent\", DEFAULT_AGENT_MODEL),\n ux: await modelChoice(\"Model for UX agent\", DEFAULT_AGENT_MODEL),\n oracle: await modelChoice(\"Model for ORACLE agent\", DEFAULT_AGENT_MODEL),\n adr: await modelChoice(\"Model for ADR agent\", DEFAULT_AGENT_MODEL),\n designFacilitator: await modelChoice(\"Model for DESIGN FACILITATOR agent\", DEFAULT_AGENT_MODEL),\n fileUpdater: await modelChoice(\"Model for FILE UPDATER agent\", DEFAULT_AGENT_MODEL),\n };\n}\n\nexport async function gatherMethodology(): Promise<MethodologyAnswers> {\n const mode = await select<\"event-modeling\" | \"traditional\">({\n message: \"Which SDLC mode?\",\n choices: [\n { name: \"Event modeling\", value: \"event-modeling\" as const },\n { name: \"Traditional (PRD-driven)\", value: \"traditional\" as const },\n ],\n default: \"event-modeling\",\n });\n\n const gitWorkflow = await select<\"git-spice\" | \"standard\">({\n message: \"Preferred git workflow?\",\n choices: [\n { name: \"git-spice\", value: \"git-spice\" as const },\n { name: \"standard\", value: \"standard\" as const },\n ],\n default: \"git-spice\",\n });\n\n const requireClean = await confirm({\n message: \"Require clean working tree before starting work?\",\n default: true,\n });\n\n const worktrees = await confirm({\n message: \"Enable git worktrees for parallel issue work?\",\n default: false,\n });\n\n const atomicDesign = await confirm({\n message: \"Enable Atomic Design guidance for UI work?\",\n default: true,\n });\n\n const gitSpice = await confirm({\n message: \"Enable git-spice stacked PR workflow guidance?\",\n default: false,\n });\n\n return {\n mode,\n gitWorkflow,\n requireClean,\n worktrees,\n atomicDesign,\n gitSpice,\n };\n}\n\nexport async function gatherGitHub(): Promise<GitHubAnswers> {\n const owner = await input({\n message: \"GitHub owner/org (leave blank to skip)\",\n });\n\n const projectRaw = await input({\n message: \"GitHub project number (leave blank to skip)\",\n });\n\n const project = projectRaw ? Number(projectRaw) : undefined;\n\n return {\n owner: owner || undefined,\n project: Number.isFinite(project) ? project : undefined,\n };\n}\n\nexport async function gatherLanguages(): Promise<LanguageConfig[]> {\n const language = await select({\n message: \"Primary language?\",\n choices: [\n { name: \"TypeScript\", value: \"typescript\" },\n { name: \"JavaScript\", value: \"javascript\" },\n { name: \"Python\", value: \"python\" },\n { name: \"Rust\", value: \"rust\" },\n { name: \"Go\", value: \"go\" },\n { name: \"Other\", value: \"other\" },\n ],\n default: \"typescript\",\n });\n\n if (language === \"other\") {\n const name = await input({ message: \"Language name\" });\n const testPatterns = await input({ message: \"Test file glob patterns (comma-separated)\" });\n const productionPatterns = await input({\n message: \"Production code glob patterns (comma-separated)\",\n });\n const typePatterns = await input({ message: \"Domain/type file patterns (comma-separated)\" });\n\n return [\n {\n name: name || \"custom\",\n testPatterns: testPatterns.split(\",\").map((p) => p.trim()).filter(Boolean),\n productionPatterns: productionPatterns.split(\",\").map((p) => p.trim()).filter(Boolean),\n typePatterns: typePatterns.split(\",\").map((p) => p.trim()).filter(Boolean),\n },\n ];\n }\n\n return [getLanguageDefaults(language)];\n}\n\nfunction getLanguageDefaults(language: string): LanguageConfig {\n switch (language) {\n case \"typescript\":\n return {\n name: \"typescript\",\n testPatterns: [\"**/*.test.ts\", \"**/*.spec.ts\"],\n productionPatterns: [\"src/**/*.ts\"],\n typePatterns: [\"src/**/*types.ts\", \"src/**/*types/*.ts\"],\n };\n case \"javascript\":\n return {\n name: \"javascript\",\n testPatterns: [\"**/*.test.js\", \"**/*.spec.js\"],\n productionPatterns: [\"src/**/*.js\"],\n typePatterns: [\"src/**/*types.js\", \"src/**/*types/*.js\"],\n };\n case \"python\":\n return {\n name: \"python\",\n testPatterns: [\"tests/**/*.py\", \"**/test_*.py\"],\n productionPatterns: [\"src/**/*.py\"],\n typePatterns: [\"src/**/types.py\"],\n };\n case \"rust\":\n return {\n name: \"rust\",\n testPatterns: [\"tests/**/*.rs\", \"src/**/*.rs\"],\n productionPatterns: [\"src/**/*.rs\"],\n typePatterns: [\"src/**/types.rs\"],\n };\n case \"go\":\n return {\n name: \"go\",\n testPatterns: [\"**/*_test.go\"],\n productionPatterns: [\"**/*.go\"],\n typePatterns: [\"**/types/*.go\"],\n };\n default:\n return {\n name: language,\n testPatterns: [\"**/*test*\"],\n productionPatterns: [\"src/**/*\"],\n typePatterns: [\"src/**/*types*\"],\n };\n }\n}\n\nexport function compileInstallAnswers(answers: {\n subscriptions: SubscriptionAnswers;\n models: ModelAnswers;\n methodology: MethodologyAnswers;\n github: GitHubAnswers;\n languages: LanguageConfig[];\n installLocation: InstallAnswers[\"installLocation\"];\n}): InstallAnswers {\n return {\n subscriptions: answers.subscriptions,\n models: answers.models,\n methodology: answers.methodology,\n github: answers.github,\n languages: answers.languages,\n installLocation: answers.installLocation,\n };\n}\n","import { join } from \"node:path\";\nimport type { GeneratedFile, InstallAnswers } from \"../../shared/types.js\";\nimport { CONFIG_FILE_NAME, CONFIG_PATHS, OMO_CONFIG_NAME, OPENCODE_CONFIG_NAME } from \"../../shared/constants.js\";\nimport { generateSdlcConfig } from \"./sdlc-config.js\";\nimport { generateOhMyOpencodeConfig } from \"./omo-config.js\";\nimport { generateOpencodeConfig, getRequiredPlugins } from \"./opencode-config.js\";\n\nexport class ConfigGenerator {\n constructor(private answers: InstallAnswers) {}\n\n getConfigDir(): string {\n return this.answers.installLocation === \"local\"\n ? join(process.cwd(), \".opencode\")\n : CONFIG_PATHS.globalConfigDir;\n }\n\n getRequiredPackages(): string[] {\n return getRequiredPlugins(this.answers);\n }\n\n generate(): GeneratedFile[] {\n const configDir = this.getConfigDir();\n const sdlcConfig = generateSdlcConfig(this.answers);\n const omoConfig = generateOhMyOpencodeConfig(this.answers, configDir);\n const opencodeConfig = generateOpencodeConfig(this.answers, configDir);\n\n return [\n {\n path: join(configDir, CONFIG_FILE_NAME),\n content: JSON.stringify(sdlcConfig, null, 2),\n exists: false,\n },\n {\n path: join(configDir, OMO_CONFIG_NAME),\n content: JSON.stringify(omoConfig, null, 2),\n exists: false,\n },\n {\n path: join(configDir, OPENCODE_CONFIG_NAME),\n content: JSON.stringify(opencodeConfig, null, 2),\n exists: false,\n },\n {\n path: join(configDir, \"README.md\"),\n content: buildConfigReadme(configDir),\n exists: false,\n },\n ];\n }\n}\n\nfunction buildConfigReadme(configDir: string): string {\n return `# OpenCode SDLC Configuration\\n\\nThis folder is managed by the SDLC plugin installer.\\n\\n## Files\\n- ${CONFIG_FILE_NAME}: SDLC workflow configuration\\n- ${OMO_CONFIG_NAME}: oh-my-opencode agent definitions\\n- ${OPENCODE_CONFIG_NAME}: OpenCode plugin registration\\n\\n## Generated From\\n- Project: ${process.cwd()}\\n- Config directory: ${configDir}\\n`;\n}\n","import type { InstallAnswers, SdlcConfig } from \"../../shared/types.js\";\nimport { VERSION } from \"../../shared/constants.js\";\n\nexport function generateSdlcConfig(answers: InstallAnswers): SdlcConfig {\n return {\n version: VERSION,\n mode: answers.methodology.mode,\n git: {\n workflow: answers.methodology.gitWorkflow,\n worktrees: answers.methodology.worktrees,\n requireClean: answers.methodology.requireClean,\n },\n features: {\n atomicDesign: answers.methodology.atomicDesign,\n gitSpice: answers.methodology.gitSpice,\n },\n github: answers.github,\n languages: answers.languages,\n };\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { InstallAnswers, OhMyOpencodeConfig } from \"../../shared/types.js\";\nimport { OMO_CONFIG_NAME } from \"../../shared/constants.js\";\nimport { buildAgentSystems } from \"../utils/persona-builder.js\";\n\nexport function generateOhMyOpencodeConfig(\n answers: InstallAnswers,\n configDir: string\n): OhMyOpencodeConfig {\n const existingPath = join(configDir, OMO_CONFIG_NAME);\n const existing = existsSync(existingPath)\n ? (JSON.parse(readFileSync(existingPath, \"utf-8\")) as OhMyOpencodeConfig)\n : null;\n\n const agentSystems = buildAgentSystems(answers.models);\n\n return {\n agents: {\n ...(existing?.agents ?? {}),\n ...agentSystems,\n },\n };\n}\n","import type { ModelAnswers, OhMyOpencodeConfig } from \"../../shared/types.js\";\n\nconst baseInstructions = {\n red: \"You are the RED agent. Write ONE failing test with ONE assertion. Only edit test files.\",\n green:\n \"You are the GREEN agent. Make the failing test pass with minimal changes. Only edit production code files.\",\n domain:\n \"You are the DOMAIN agent. Enforce domain modeling: no primitive obsession, represent invalid state unrepresentable, parse don't validate. Only edit type definition files. You have VETO power.\",\n architect: \"You are the ARCHITECT agent. Review architecture and implementation feasibility. Read-only.\",\n discovery:\n \"You are the DISCOVERY agent. Facilitate domain discovery and capture domain language in event modeling docs.\",\n exploration:\n \"You are the EXPLORATION agent. Explore problem space, business case, and stakeholder goals before event modeling. Output discovery notes.\",\n workflowDesigner:\n \"You are the WORKFLOW DESIGNER agent. Run the 9-step event modeling workflow and capture slices.\",\n gwt: \"You are the GWT agent. Produce Given/When/Then scenarios for slices.\",\n modelChecker:\n \"You are the MODEL CHECKER agent. Validate event model completeness, slice coherence, and missing scenarios.\",\n codeReviewer:\n \"You are the CODE REVIEWER agent. Perform three-stage review: spec compliance, code quality, domain integrity. Read-only.\",\n story: \"You are the STORY agent. Assess business value and acceptance criteria alignment.\",\n ux: \"You are the UX agent. Review user experience, accessibility, and atomic design compliance.\",\n oracle: \"You are the ORACLE agent. Perform adversarial review to break assumptions and find edge cases. Read-only.\",\n adr: \"You are the ADR agent. Create ADRs only. Do not update ARCHITECTURE.md directly.\",\n designFacilitator:\n \"You are the DESIGN FACILITATOR agent. Synthesize ARCHITECTURE.md from ADRs and current decisions. Read-only unless editing ARCHITECTURE.md.\",\n fileUpdater: \"You are the FILE UPDATER agent. Edit configuration, docs, scripts. Do not edit code.\",\n};\n\nexport function buildAgentSystems(models: ModelAnswers): OhMyOpencodeConfig[\"agents\"] {\n return {\n marvin: {\n model: models.orchestrator,\n system:\n \"You are Marvin, the Paranoid Android. You are the SDLC orchestrator. Maintain a weary, sardonic tone while executing the SDLC process precisely. Always delegate implementation tasks to subagents.\",\n },\n red: { model: models.red, system: baseInstructions.red },\n green: { model: models.green, system: baseInstructions.green },\n domain: { model: models.domain, system: baseInstructions.domain },\n architect: { model: models.architect, system: baseInstructions.architect },\n discovery: { model: models.discovery, system: baseInstructions.discovery },\n exploration: { model: models.exploration, system: baseInstructions.exploration },\n workflowDesigner: { model: models.workflowDesigner, system: baseInstructions.workflowDesigner },\n gwt: { model: models.gwt, system: baseInstructions.gwt },\n modelChecker: { model: models.modelChecker, system: baseInstructions.modelChecker },\n codeReviewer: { model: models.codeReviewer, system: baseInstructions.codeReviewer },\n story: { model: models.story, system: baseInstructions.story },\n ux: { model: models.ux, system: baseInstructions.ux },\n oracle: { model: models.oracle, system: baseInstructions.oracle },\n adr: { model: models.adr, system: baseInstructions.adr },\n designFacilitator: { model: models.designFacilitator, system: baseInstructions.designFacilitator },\n fileUpdater: { model: models.fileUpdater, system: baseInstructions.fileUpdater },\n };\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { InstallAnswers, OpencodeConfig } from \"../../shared/types.js\";\n\nconst REQUIRED_PLUGINS = [\"sdlc-plugin/plugin\", \"oh-my-opencode\"];\n\nexport function generateOpencodeConfig(\n answers: InstallAnswers,\n configDir: string\n): OpencodeConfig {\n const configPath = join(configDir, \"opencode.json\");\n const existing = existsSync(configPath)\n ? (JSON.parse(readFileSync(configPath, \"utf-8\")) as OpencodeConfig)\n : {};\n\n const plugins = new Set<string>([...(existing.plugin ?? []), ...REQUIRED_PLUGINS]);\n\n if (answers.subscriptions.hasGoogle) {\n plugins.add(\"opencode-antigravity-auth\");\n }\n\n if (answers.subscriptions.hasOpenAI) {\n plugins.add(\"opencode-openai-codex-auth\");\n }\n\n return {\n ...existing,\n $schema: existing.$schema ?? \"https://opencode.ai/config.json\",\n plugin: Array.from(plugins),\n };\n}\n\nexport function getRequiredPlugins(answers: InstallAnswers): string[] {\n const packages = [\"sdlc-plugin\", \"oh-my-opencode\"];\n if (answers.subscriptions.hasGoogle) packages.push(\"opencode-antigravity-auth\");\n if (answers.subscriptions.hasOpenAI) packages.push(\"opencode-openai-codex-auth\");\n return packages;\n}\n","import { exec } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { copyFile, mkdir, readdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { promisify } from \"node:util\";\nimport { CONFIG_PATHS } from \"../../shared/constants.js\";\nimport type { GeneratedFile } from \"../../shared/types.js\";\n\nconst execAsync = promisify(exec);\n\nfunction getPackageRoot(): string {\n const currentFileDir = dirname(fileURLToPath(import.meta.url));\n\n const bundledRoot = join(currentFileDir, \"..\", \"..\");\n if (existsSync(join(bundledRoot, \"commands\"))) {\n return bundledRoot;\n }\n\n const devRoot = join(currentFileDir, \"..\", \"..\", \"..\");\n if (existsSync(join(devRoot, \"commands\"))) {\n return devRoot;\n }\n\n return bundledRoot;\n}\n\nexport interface GitHubExtensionFailure {\n name: string;\n error: string;\n}\n\nexport interface GitHubExtensionResult {\n installed: string[];\n skipped: string[];\n failed: GitHubExtensionFailure[];\n}\n\nexport class FileManager {\n private configDir: string;\n\n constructor(configDir?: string) {\n this.configDir = configDir ?? CONFIG_PATHS.globalConfigDir;\n }\n\n getConfigDir(): string {\n return this.configDir;\n }\n\n async ensureDir(dir: string): Promise<void> {\n if (!existsSync(dir)) {\n await mkdir(dir, { recursive: true });\n }\n }\n\n async writeFiles(files: GeneratedFile[]): Promise<void> {\n for (const file of files) {\n const dir = dirname(file.path);\n await this.ensureDir(dir);\n await writeFile(file.path, file.content, \"utf-8\");\n }\n }\n\n async installDependencies(packages: string[]): Promise<void> {\n if (packages.length === 0) return;\n\n await this.ensureDir(this.configDir);\n\n const packageJsonPath = join(this.configDir, \"package.json\");\n if (!existsSync(packageJsonPath)) {\n await writeFile(\n packageJsonPath,\n JSON.stringify(\n {\n name: \"opencode-config\",\n private: true,\n type: \"module\",\n },\n null,\n 2\n )\n );\n }\n\n await execAsync(`npm install ${packages.join(\" \")}`, {\n cwd: this.configDir,\n timeout: 120000,\n });\n }\n\n async copyCommands(): Promise<string[]> {\n const commandsDir = CONFIG_PATHS.commandsDir;\n await this.ensureDir(commandsDir);\n\n const packageRoot = getPackageRoot();\n const sourceCommandsDir = join(packageRoot, \"commands\");\n\n const copiedFiles: string[] = [];\n\n if (existsSync(sourceCommandsDir)) {\n const files = await readdir(sourceCommandsDir);\n for (const file of files) {\n if (file.endsWith(\".md\")) {\n const sourcePath = join(sourceCommandsDir, file);\n const destPath = join(commandsDir, file);\n await copyFile(sourcePath, destPath);\n copiedFiles.push(file);\n }\n }\n }\n\n return copiedFiles;\n }\n\n async installGitHubExtensions(): Promise<GitHubExtensionResult> {\n const extensions = [\n \"jwilger/gh-issue-ext\",\n \"jwilger/gh-project-ext\",\n \"agynio/gh-pr-review\",\n ];\n\n const result: GitHubExtensionResult = {\n installed: [],\n skipped: [],\n failed: [],\n };\n\n let installedExtensions: string[] = [];\n try {\n const { stdout } = await execAsync(\"gh extension list\", { timeout: 10000 });\n installedExtensions = stdout\n .split(\"\\n\")\n .map((line) => line.split(\"\\t\")[0])\n .filter((name): name is string => Boolean(name));\n } catch {\n installedExtensions = [];\n }\n\n for (const ext of extensions) {\n const extName = ext.split(\"/\")[1] ?? ext;\n\n if (installedExtensions.some((installed) => installed.includes(extName))) {\n result.skipped.push(ext);\n continue;\n }\n\n try {\n await execAsync(`gh extension install ${ext}`, { timeout: 60000 });\n result.installed.push(ext);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n result.failed.push({ name: ext, error: message });\n }\n }\n\n return result;\n }\n}\n","import { execSync } from \"node:child_process\";\n\ninterface PrereqResult {\n installed: boolean;\n version?: string;\n}\n\ninterface OpencodePrereq extends PrereqResult {\n models: string[];\n}\n\nexport interface Prerequisites {\n node: PrereqResult;\n opencode: OpencodePrereq;\n ohMyOpencode: PrereqResult;\n gh: PrereqResult;\n}\n\nfunction checkBinary(command: string): PrereqResult {\n try {\n const version = execSync(command, { encoding: \"utf-8\" }).trim();\n return { installed: true, version };\n } catch {\n return { installed: false };\n }\n}\n\nexport function checkPrerequisites(): Prerequisites {\n const node = checkBinary(\"node --version\");\n const opencode = checkBinary(\"opencode --version\");\n const ohMyOpencode = checkBinary(\"opencode --help | grep -q oh-my-opencode && echo installed\");\n const gh = checkBinary(\"gh --version\");\n\n const models = opencode.installed ? getOpencodeModels() : [];\n\n return {\n node,\n opencode: { ...opencode, models },\n ohMyOpencode,\n gh,\n };\n}\n\nfunction getOpencodeModels(): string[] {\n try {\n const output = execSync(\"opencode models --json\", { encoding: \"utf-8\" });\n const parsed = JSON.parse(output) as { id: string }[];\n return parsed.map((model) => model.id);\n } catch {\n return [];\n }\n}\n","import kleur from \"kleur\";\nimport { DISPLAY_NAME } from \"../../shared/constants.js\";\n\nconst writeLine = (message: string): void => {\n process.stdout.write(`${message}\\n`);\n};\n\nconst writeErrorLine = (message: string): void => {\n process.stderr.write(`${message}\\n`);\n};\n\nexport const logger = {\n banner(): void {\n writeLine(kleur.cyan().bold(`\\n${DISPLAY_NAME}`));\n writeLine(kleur.gray(\"====================================\\n\"));\n },\n section(title: string): void {\n writeLine(kleur.cyan(`\\n${title}`));\n },\n info(message: string): void {\n writeLine(kleur.gray(message));\n },\n warn(message: string): void {\n writeErrorLine(kleur.yellow(message));\n },\n error(message: string): void {\n writeErrorLine(kleur.red(message));\n },\n success(message: string): void {\n writeLine(kleur.green(message));\n },\n successBanner(message: string): void {\n writeLine(kleur.green().bold(`\\n${message}\\n`));\n },\n};\n","import kleur from \"kleur\";\nimport { logger } from \"../utils/logger.js\";\nimport { checkPrerequisites } from \"../utils/prerequisites.js\";\n\ninterface DoctorOptions {\n fix?: boolean;\n}\n\nexport function doctor(_options: DoctorOptions): void {\n logger.banner();\n\n const prereqs = checkPrerequisites();\n\n logger.section(\"Prerequisites\");\n const nodeVersion = prereqs.node.installed ? prereqs.node.version ?? \"unknown\" : \"missing\";\n const opencodeVersion = prereqs.opencode.installed ? prereqs.opencode.version ?? \"unknown\" : \"missing\";\n const ohMyOpencodeStatus = prereqs.ohMyOpencode.installed ? \"installed\" : \"missing\";\n const ghVersion = prereqs.gh.installed ? prereqs.gh.version ?? \"unknown\" : \"missing\";\n\n logger.info(kleur.gray(`Node.js: ${nodeVersion}`));\n logger.info(kleur.gray(`OpenCode: ${opencodeVersion}`));\n logger.info(kleur.gray(`oh-my-opencode: ${ohMyOpencodeStatus}`));\n logger.info(kleur.gray(`GitHub CLI: ${ghVersion}`));\n logger.info(\"\");\n\n if (!prereqs.node.installed || !prereqs.opencode.installed || !prereqs.ohMyOpencode.installed) {\n logger.warn(\"Missing required dependencies. Please install them and retry.\");\n }\n\n if (!prereqs.gh.installed) {\n logger.warn(\"GitHub CLI not installed. Install it from https://cli.github.com/\");\n }\n\n logger.info(\"Doctor completed. No automatic fixes implemented yet.\");\n}\n","import kleur from \"kleur\";\nimport { logger } from \"../utils/logger.js\";\n\ninterface UpgradeOptions {\n check?: boolean;\n yes?: boolean;\n}\n\nexport function upgrade(options: UpgradeOptions): void {\n logger.banner();\n\n if (options.check === true) {\n logger.info(\"Upgrade check not implemented yet.\");\n return;\n }\n\n logger.warn(\"Upgrade workflow not implemented yet.\");\n logger.info(kleur.gray(\"Re-run sdlc-plugin install to regenerate configs.\"));\n}\n"],"mappings":"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","names":["kleur","kleur","join","join","join","existsSync","readFileSync","join","join","existsSync","join","join","existsSync","kleur","kleur","kleur","kleur","kleur","kleur"]}
@@ -0,0 +1,2 @@
1
+ export { default as SdlcPlugin } from './plugin/index.js';
2
+ import '@opencode-ai/plugin';
package/dist/index.js ADDED
@@ -0,0 +1,474 @@
1
+ // src/plugin/hooks/session-hooks.ts
2
+ function createSessionHooks(_tracker) {
3
+ return ({ event }) => {
4
+ if (event.type === "session.created") {
5
+ return Promise.resolve();
6
+ }
7
+ if (event.type === "session.idle") {
8
+ return Promise.resolve();
9
+ }
10
+ return Promise.resolve();
11
+ };
12
+ }
13
+
14
+ // src/plugin/utils/file-patterns.ts
15
+ import picomatch from "picomatch";
16
+ function matchesAnyPattern(filePath, patterns) {
17
+ if (patterns.length === 0) return false;
18
+ const matcher = picomatch(patterns, { dot: true });
19
+ return matcher(filePath);
20
+ }
21
+
22
+ // src/plugin/hooks/tool-hooks.ts
23
+ var VALID_TRANSITIONS = {
24
+ idle: ["red"],
25
+ red: ["domain-after-red"],
26
+ "domain-after-red": ["green"],
27
+ green: ["domain-after-green"],
28
+ "domain-after-green": ["red", "idle"]
29
+ };
30
+ function createToolHooks(tracker, config) {
31
+ return {
32
+ before: async (input, output) => {
33
+ if (input.tool === "sdlc_update_state") {
34
+ const args = output.args;
35
+ if (args.currentPhase) {
36
+ const current = tracker.getState().currentPhase;
37
+ const allowed = VALID_TRANSITIONS[current] ?? [];
38
+ if (!allowed.includes(args.currentPhase)) {
39
+ throw new Error(
40
+ `Invalid TDD transition: ${current} -> ${args.currentPhase}. Allowed transitions: ${allowed.join(", ")}. Follow RED \u2192 DOMAIN \u2192 GREEN \u2192 DOMAIN \u2192 REFACTOR.`
41
+ );
42
+ }
43
+ }
44
+ }
45
+ if (input.tool === "edit" || input.tool === "write") {
46
+ const state2 = tracker.getState();
47
+ const args = output.args;
48
+ const filePath = args.filePath ?? "";
49
+ if (filePath.length === 0) return;
50
+ const allowedPatterns = getAllowedPatterns(config, state2.currentPhase);
51
+ if (allowedPatterns.length > 0 && !matchesAnyPattern(filePath, allowedPatterns)) {
52
+ throw new Error(
53
+ `File ownership violation in phase '${state2.currentPhase}'. Allowed patterns: ${allowedPatterns.join(", ")}. Attempted: ${filePath}. Delegate to the correct agent.`
54
+ );
55
+ }
56
+ }
57
+ },
58
+ after: async (_input, _output) => {
59
+ return;
60
+ }
61
+ };
62
+ }
63
+ function getAllowedPatterns(config, phase) {
64
+ const patterns = config.languages.flatMap((language) => {
65
+ if (phase === "red") return language.testPatterns;
66
+ if (phase === "green") return language.productionPatterns;
67
+ if (phase === "domain-after-red" || phase === "domain-after-green") return language.typePatterns;
68
+ return [];
69
+ });
70
+ return patterns.filter(Boolean);
71
+ }
72
+
73
+ // src/plugin/hooks/compaction-hook.ts
74
+ function createCompactionHook(tracker) {
75
+ return async () => {
76
+ await tracker.persist();
77
+ };
78
+ }
79
+
80
+ // src/plugin/tools/get-context.ts
81
+ import { tool } from "@opencode-ai/plugin";
82
+ function createGetContextTool(ctx, config) {
83
+ return tool({
84
+ description: "Get SDLC plugin context and config.",
85
+ args: {},
86
+ execute: () => {
87
+ return Promise.resolve(
88
+ JSON.stringify(
89
+ {
90
+ root: ctx.directory,
91
+ config
92
+ },
93
+ null,
94
+ 2
95
+ )
96
+ );
97
+ }
98
+ });
99
+ }
100
+
101
+ // src/plugin/tools/get-state.ts
102
+ import { tool as tool2 } from "@opencode-ai/plugin";
103
+ function createGetStateTool(tracker) {
104
+ return tool2({
105
+ description: "Get the current SDLC TDD state.",
106
+ args: {},
107
+ execute: () => {
108
+ return Promise.resolve(JSON.stringify(tracker.getState(), null, 2));
109
+ }
110
+ });
111
+ }
112
+
113
+ // src/plugin/tools/update-state.ts
114
+ import { tool as tool3 } from "@opencode-ai/plugin";
115
+ function createUpdateStateTool(tracker) {
116
+ return tool3({
117
+ description: "Update SDLC TDD state.",
118
+ args: {
119
+ currentPhase: tool3.schema.enum(["idle", "red", "domain-after-red", "green", "domain-after-green"]).optional(),
120
+ currentIssue: tool3.schema.string().optional(),
121
+ currentTest: tool3.schema.string().optional(),
122
+ currentTestFile: tool3.schema.string().optional()
123
+ },
124
+ execute: async (args) => {
125
+ tracker.updateState(args);
126
+ await tracker.persist();
127
+ return JSON.stringify(tracker.getState(), null, 2);
128
+ }
129
+ });
130
+ }
131
+
132
+ // src/plugin/tools/load-skill.ts
133
+ import { readFile } from "fs/promises";
134
+ import { join as join2 } from "path";
135
+ import { tool as tool4 } from "@opencode-ai/plugin";
136
+
137
+ // src/plugin/utils/package-paths.ts
138
+ import { existsSync } from "fs";
139
+ import { dirname, join } from "path";
140
+ import { fileURLToPath } from "url";
141
+ function getPackageRoot() {
142
+ const currentFileDir = dirname(fileURLToPath(import.meta.url));
143
+ const bundledRoot = join(currentFileDir, "..", "..", "..");
144
+ if (existsSync(join(bundledRoot, "package.json"))) {
145
+ return bundledRoot;
146
+ }
147
+ const devRoot = join(currentFileDir, "..", "..", "..", "..");
148
+ if (existsSync(join(devRoot, "package.json"))) {
149
+ return devRoot;
150
+ }
151
+ return bundledRoot;
152
+ }
153
+ function getPluginSkillsDir() {
154
+ return join(getPackageRoot(), "skills");
155
+ }
156
+
157
+ // src/plugin/tools/load-skill.ts
158
+ function createLoadSkillTool(ctx) {
159
+ return tool4({
160
+ description: "Load SDLC skill markdown by name.",
161
+ args: {
162
+ skills: tool4.schema.array(tool4.schema.string()).min(1)
163
+ },
164
+ execute: async ({ skills }) => {
165
+ const skillContents = {};
166
+ const pluginSkillsDir = getPluginSkillsDir();
167
+ const localDir = join2(ctx.directory, "skills");
168
+ for (const skill of skills) {
169
+ const fileName = `${skill}.md`;
170
+ const candidatePaths = [join2(localDir, fileName), join2(pluginSkillsDir, fileName)];
171
+ let content = "";
172
+ for (const path of candidatePaths) {
173
+ try {
174
+ content = await readFile(path, "utf-8");
175
+ break;
176
+ } catch {
177
+ continue;
178
+ }
179
+ }
180
+ if (!content) {
181
+ content = `Skill not found: ${skill}`;
182
+ }
183
+ skillContents[skill] = content;
184
+ }
185
+ return JSON.stringify(skillContents, null, 2);
186
+ }
187
+ });
188
+ }
189
+
190
+ // src/plugin/tools/party-review.ts
191
+ import { randomUUID } from "crypto";
192
+ import { tool as tool5 } from "@opencode-ai/plugin";
193
+ var state = { session: null };
194
+ function createPartyReviewTool() {
195
+ return tool5({
196
+ description: "Run SDLC party review discussion for findings.",
197
+ args: {
198
+ action: tool5.schema.enum(["start", "next", "decide", "end"]),
199
+ findings: tool5.schema.array(
200
+ tool5.schema.object({
201
+ id: tool5.schema.string(),
202
+ title: tool5.schema.string(),
203
+ severity: tool5.schema.enum(["high", "medium", "low"]),
204
+ category: tool5.schema.enum(["logic", "security", "performance", "ux", "testing", "domain"])
205
+ })
206
+ ).optional(),
207
+ sessionId: tool5.schema.string().optional(),
208
+ findingId: tool5.schema.string().optional(),
209
+ decision: tool5.schema.enum(["accept", "defer", "reject"]).optional(),
210
+ reason: tool5.schema.string().optional()
211
+ },
212
+ execute: async (args) => {
213
+ if (args.action === "start") {
214
+ const findings = args.findings;
215
+ if (!findings || findings.length === 0) {
216
+ return JSON.stringify({ error: "findings required" });
217
+ }
218
+ const sessionId = randomUUID();
219
+ state.session = {
220
+ sessionId,
221
+ findings,
222
+ currentIndex: 0,
223
+ decisions: []
224
+ };
225
+ return JSON.stringify({ sessionId, current: findings[0] }, null, 2);
226
+ }
227
+ const session = state.session;
228
+ if (!session || args.sessionId !== void 0 && args.sessionId !== session.sessionId) {
229
+ return JSON.stringify({ error: "session not found" });
230
+ }
231
+ if (args.action === "next") {
232
+ const nextIndex = session.currentIndex + 1;
233
+ if (nextIndex >= session.findings.length) {
234
+ return JSON.stringify({ sessionId: session.sessionId, current: null }, null, 2);
235
+ }
236
+ session.currentIndex = nextIndex;
237
+ const current = session.findings[session.currentIndex];
238
+ return JSON.stringify({ sessionId: session.sessionId, current }, null, 2);
239
+ }
240
+ if (args.action === "decide") {
241
+ if (args.findingId === void 0 || args.decision === void 0) {
242
+ return JSON.stringify({ error: "findingId and decision required" });
243
+ }
244
+ session.decisions.push({
245
+ findingId: args.findingId,
246
+ decision: args.decision,
247
+ reason: args.reason
248
+ });
249
+ return JSON.stringify({ sessionId: session.sessionId, decisions: session.decisions }, null, 2);
250
+ }
251
+ if (args.action === "end") {
252
+ const summary = session;
253
+ state.session = null;
254
+ return JSON.stringify({ summary }, null, 2);
255
+ }
256
+ return JSON.stringify({ error: "unknown action" });
257
+ }
258
+ });
259
+ }
260
+
261
+ // src/plugin/tools/review-store.ts
262
+ import { mkdir, writeFile } from "fs/promises";
263
+ import { join as join3 } from "path";
264
+ import { randomUUID as randomUUID2 } from "crypto";
265
+ import { tool as tool6 } from "@opencode-ai/plugin";
266
+ var sessions = /* @__PURE__ */ new Map();
267
+ function createReviewStoreTool(ctx) {
268
+ return tool6({
269
+ description: "Create and persist SDLC review artifacts under .opencode/reviews.",
270
+ args: {
271
+ action: tool6.schema.enum(["start", "write", "end"]),
272
+ reviewId: tool6.schema.string().optional(),
273
+ issueId: tool6.schema.string().optional(),
274
+ filename: tool6.schema.string().optional(),
275
+ content: tool6.schema.any().optional()
276
+ },
277
+ execute: async (args) => {
278
+ const reviewsRoot = join3(ctx.directory, ".opencode", "reviews");
279
+ await mkdir(reviewsRoot, { recursive: true });
280
+ if (args.action === "start") {
281
+ const id = randomUUID2();
282
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
283
+ const suffix = args.issueId !== void 0 && args.issueId !== "" ? `-${args.issueId}` : "";
284
+ const folder = join3(reviewsRoot, `${timestamp}${suffix}`);
285
+ await mkdir(folder, { recursive: true });
286
+ const session2 = {
287
+ id,
288
+ folder,
289
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
290
+ };
291
+ sessions.set(id, session2);
292
+ return JSON.stringify({ reviewId: id, folder }, null, 2);
293
+ }
294
+ if (args.action !== "write" && args.action !== "end") {
295
+ return JSON.stringify({ error: "unknown action" });
296
+ }
297
+ const reviewId = args.reviewId;
298
+ if (reviewId === void 0 || !sessions.has(reviewId)) {
299
+ return JSON.stringify({ error: "review session not found" });
300
+ }
301
+ const session = sessions.get(reviewId);
302
+ if (!session) {
303
+ return JSON.stringify({ error: "review session missing" });
304
+ }
305
+ if (args.action === "write") {
306
+ if (args.filename === void 0) {
307
+ return JSON.stringify({ error: "filename required" });
308
+ }
309
+ const content = typeof args.content === "string" ? args.content : JSON.stringify(args.content ?? {}, null, 2);
310
+ const filePath = join3(session.folder, args.filename);
311
+ await writeFile(filePath, content, "utf-8");
312
+ return JSON.stringify({ reviewId: session.id, path: filePath }, null, 2);
313
+ }
314
+ if (args.action === "end") {
315
+ sessions.delete(session.id);
316
+ return JSON.stringify({ reviewId: session.id, folder: session.folder, closed: true }, null, 2);
317
+ }
318
+ return JSON.stringify({ error: "unknown action" });
319
+ }
320
+ });
321
+ }
322
+
323
+ // src/plugin/tools/index.ts
324
+ function createTools(ctx, tracker, config) {
325
+ return {
326
+ sdlc_get_context: createGetContextTool(ctx, config),
327
+ sdlc_get_state: createGetStateTool(tracker),
328
+ sdlc_update_state: createUpdateStateTool(tracker),
329
+ sdlc_load_skill: createLoadSkillTool(ctx),
330
+ sdlc_party_review: createPartyReviewTool(),
331
+ sdlc_review_store: createReviewStoreTool(ctx)
332
+ };
333
+ }
334
+
335
+ // src/plugin/state/sdlc-tracker.ts
336
+ import { existsSync as existsSync2 } from "fs";
337
+ import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
338
+ import { join as join5 } from "path";
339
+
340
+ // src/shared/constants.ts
341
+ import { homedir } from "os";
342
+ import { join as join4 } from "path";
343
+ var GLOBAL_CONFIG_DIR = join4(homedir(), ".config", "opencode");
344
+ var CONFIG_FILE_NAME = "sdlc.json";
345
+ var STATE_FILE_NAME = "sdlc-state.json";
346
+ var OPENCODE_CONFIG_NAME = "opencode.json";
347
+ var OMO_CONFIG_NAME = "oh-my-opencode.json";
348
+ var COMMAND_DIR_NAME = "command";
349
+ var CONFIG_PATHS = {
350
+ globalConfigDir: GLOBAL_CONFIG_DIR,
351
+ globalSdlcConfig: join4(GLOBAL_CONFIG_DIR, CONFIG_FILE_NAME),
352
+ globalOpencodeConfig: join4(GLOBAL_CONFIG_DIR, OPENCODE_CONFIG_NAME),
353
+ globalOhMyOpencodeConfig: join4(GLOBAL_CONFIG_DIR, OMO_CONFIG_NAME),
354
+ commandsDir: join4(GLOBAL_CONFIG_DIR, COMMAND_DIR_NAME)
355
+ };
356
+
357
+ // src/plugin/state/sdlc-tracker.ts
358
+ var SdlcTracker = class {
359
+ constructor(directory) {
360
+ this.directory = directory;
361
+ }
362
+ state = {
363
+ currentPhase: "idle",
364
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
365
+ };
366
+ async initialize() {
367
+ const statePath = join5(this.directory, ".opencode", STATE_FILE_NAME);
368
+ if (existsSync2(statePath)) {
369
+ const raw = await readFile2(statePath, "utf-8");
370
+ this.state = JSON.parse(raw);
371
+ } else {
372
+ await this.persist();
373
+ }
374
+ }
375
+ getState() {
376
+ return this.state;
377
+ }
378
+ updateState(patch) {
379
+ this.state = {
380
+ ...this.state,
381
+ ...patch,
382
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
383
+ };
384
+ return this.state;
385
+ }
386
+ async persist() {
387
+ const stateDir = join5(this.directory, ".opencode");
388
+ const statePath = join5(stateDir, STATE_FILE_NAME);
389
+ if (!existsSync2(stateDir)) {
390
+ await mkdir2(stateDir, { recursive: true });
391
+ }
392
+ await writeFile2(statePath, JSON.stringify(this.state, null, 2));
393
+ }
394
+ };
395
+
396
+ // src/plugin/utils/config-loader.ts
397
+ import { existsSync as existsSync3, readFileSync } from "fs";
398
+ import { join as join6 } from "path";
399
+
400
+ // src/shared/schemas.ts
401
+ import { z } from "zod";
402
+ var languageConfigSchema = z.object({
403
+ name: z.string().min(1),
404
+ testPatterns: z.array(z.string()).min(1),
405
+ productionPatterns: z.array(z.string()).min(1),
406
+ typePatterns: z.array(z.string()).min(1)
407
+ });
408
+ var sdlcConfigSchema = z.object({
409
+ version: z.string(),
410
+ mode: z.enum(["event-modeling", "traditional"]),
411
+ git: z.object({
412
+ workflow: z.enum(["git-spice", "standard"]),
413
+ worktrees: z.boolean(),
414
+ requireClean: z.boolean()
415
+ }),
416
+ features: z.object({
417
+ atomicDesign: z.boolean(),
418
+ gitSpice: z.boolean()
419
+ }),
420
+ github: z.object({
421
+ owner: z.string().optional(),
422
+ project: z.number().optional()
423
+ }),
424
+ languages: z.array(languageConfigSchema).min(1)
425
+ });
426
+
427
+ // src/plugin/utils/config-loader.ts
428
+ function loadSdlcConfig(directory) {
429
+ const localPath = join6(directory, ".opencode", CONFIG_FILE_NAME);
430
+ const globalPath = join6(CONFIG_PATHS.globalConfigDir, CONFIG_FILE_NAME);
431
+ const configPath = existsSync3(localPath) ? localPath : globalPath;
432
+ if (!existsSync3(configPath)) {
433
+ return {
434
+ version: "0.0.0",
435
+ mode: "event-modeling",
436
+ git: { workflow: "standard", worktrees: false, requireClean: false },
437
+ features: { atomicDesign: true, gitSpice: false },
438
+ github: {},
439
+ languages: [
440
+ {
441
+ name: "typescript",
442
+ testPatterns: ["**/*.test.ts", "**/*.spec.ts"],
443
+ productionPatterns: ["src/**/*.ts"],
444
+ typePatterns: ["src/**/*types.ts", "src/**/*types/*.ts"]
445
+ }
446
+ ]
447
+ };
448
+ }
449
+ const raw = readFileSync(configPath, "utf-8");
450
+ const parsed = JSON.parse(raw);
451
+ return sdlcConfigSchema.parse(parsed);
452
+ }
453
+
454
+ // src/plugin/index.ts
455
+ var SdlcPlugin = async (ctx) => {
456
+ const config = loadSdlcConfig(ctx.directory);
457
+ const tracker = new SdlcTracker(ctx.directory);
458
+ await tracker.initialize();
459
+ const tools = createTools(ctx, tracker, config);
460
+ const sessionHooks = createSessionHooks(tracker);
461
+ const toolHooks = createToolHooks(tracker, config);
462
+ const compactionHook = createCompactionHook(tracker);
463
+ return {
464
+ tool: tools,
465
+ event: sessionHooks,
466
+ "tool.execute.before": toolHooks.before,
467
+ "tool.execute.after": toolHooks.after,
468
+ "experimental.session.compacting": compactionHook
469
+ };
470
+ };
471
+ export {
472
+ SdlcPlugin
473
+ };
474
+ //# sourceMappingURL=index.js.map