From 385e8a97b02a0818a5fb5250499aaa9924c70bd8 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sun, 28 Dec 2025 17:30:27 +0900 Subject: [PATCH] Add builtin-commands feature with init-deep command and disabled_commands config option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New src/features/builtin-commands/ module with CommandDefinition loader - Implements init-deep command for hierarchical AGENTS.md knowledge base generation - Adds BuiltinCommandName and BuiltinCommandNameSchema to config - Integrates builtin commands loader into main plugin with proper config merging - Supports disabling specific builtin commands via disabled_commands config array 🤖 Generated with assistance of https://github.com/code-yeongyu/oh-my-opencode --- src/config/index.ts | 2 + src/config/schema.ts | 6 + src/features/builtin-commands/commands.ts | 35 ++ src/features/builtin-commands/index.ts | 2 + .../builtin-commands/templates/init-deep.ts | 299 ++++++++++++++++++ src/features/builtin-commands/types.ts | 9 + src/index.ts | 10 + 7 files changed, 363 insertions(+) create mode 100644 src/features/builtin-commands/commands.ts create mode 100644 src/features/builtin-commands/index.ts create mode 100644 src/features/builtin-commands/templates/init-deep.ts create mode 100644 src/features/builtin-commands/types.ts diff --git a/src/config/index.ts b/src/config/index.ts index 8a3d5de..5d82d19 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -5,6 +5,7 @@ export { McpNameSchema, AgentNameSchema, HookNameSchema, + BuiltinCommandNameSchema, SisyphusAgentConfigSchema, ExperimentalConfigSchema, } from "./schema" @@ -16,6 +17,7 @@ export type { McpName, AgentName, HookName, + BuiltinCommandName, SisyphusAgentConfig, ExperimentalConfig, DynamicContextPruningConfig, diff --git a/src/config/schema.ts b/src/config/schema.ts index b6fd60b..888794a 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -67,6 +67,10 @@ export const HookNameSchema = z.enum([ "thinking-block-validator", ]) +export const BuiltinCommandNameSchema = z.enum([ + "init-deep", +]) + export const AgentOverrideConfigSchema = z.object({ model: z.string().optional(), temperature: z.number().min(0).max(2).optional(), @@ -176,6 +180,7 @@ export const OhMyOpenCodeConfigSchema = z.object({ disabled_mcps: z.array(McpNameSchema).optional(), disabled_agents: z.array(BuiltinAgentNameSchema).optional(), disabled_hooks: z.array(HookNameSchema).optional(), + disabled_commands: z.array(BuiltinCommandNameSchema).optional(), agents: AgentOverridesSchema.optional(), claude_code: ClaudeCodeConfigSchema.optional(), google_auth: z.boolean().optional(), @@ -190,6 +195,7 @@ export type AgentOverrideConfig = z.infer export type AgentOverrides = z.infer export type AgentName = z.infer export type HookName = z.infer +export type BuiltinCommandName = z.infer export type SisyphusAgentConfig = z.infer export type CommentCheckerConfig = z.infer export type ExperimentalConfig = z.infer diff --git a/src/features/builtin-commands/commands.ts b/src/features/builtin-commands/commands.ts new file mode 100644 index 0000000..53a0ff3 --- /dev/null +++ b/src/features/builtin-commands/commands.ts @@ -0,0 +1,35 @@ +import type { CommandDefinition } from "../claude-code-command-loader" +import type { BuiltinCommandName, BuiltinCommands } from "./types" +import { INIT_DEEP_TEMPLATE } from "./templates/init-deep" + +const BUILTIN_COMMAND_DEFINITIONS: Record> = { + "init-deep": { + description: "(builtin) Initialize hierarchical AGENTS.md knowledge base", + template: ` +${INIT_DEEP_TEMPLATE} + + + +$ARGUMENTS +`, + argumentHint: "[--create-new] [--max-depth=N]", + }, +} + +export function loadBuiltinCommands( + disabledCommands?: BuiltinCommandName[] +): BuiltinCommands { + const disabled = new Set(disabledCommands ?? []) + const commands: BuiltinCommands = {} + + for (const [name, definition] of Object.entries(BUILTIN_COMMAND_DEFINITIONS)) { + if (!disabled.has(name as BuiltinCommandName)) { + commands[name] = { + name, + ...definition, + } + } + } + + return commands +} diff --git a/src/features/builtin-commands/index.ts b/src/features/builtin-commands/index.ts new file mode 100644 index 0000000..2a3a239 --- /dev/null +++ b/src/features/builtin-commands/index.ts @@ -0,0 +1,2 @@ +export * from "./types" +export * from "./commands" diff --git a/src/features/builtin-commands/templates/init-deep.ts b/src/features/builtin-commands/templates/init-deep.ts new file mode 100644 index 0000000..72ac907 --- /dev/null +++ b/src/features/builtin-commands/templates/init-deep.ts @@ -0,0 +1,299 @@ +export const INIT_DEEP_TEMPLATE = `# Initialize Deep Knowledge Base + +Generate comprehensive AGENTS.md files across project hierarchy. Combines root-level project knowledge (gen-knowledge) with complexity-based subdirectory documentation (gen-knowledge-deep). + +## Usage + +\`\`\` +/init-deep # Analyze and generate hierarchical AGENTS.md +/init-deep --create-new # Force create from scratch (ignore existing) +/init-deep --max-depth=2 # Limit to N directory levels (default: 3) +\`\`\` + +--- + +## Core Principles + +- **Telegraphic Style**: Sacrifice grammar for concision ("Project uses React" → "React 18") +- **Predict-then-Compare**: Predict standard → find actual → document ONLY deviations +- **Hierarchy Aware**: Parent covers general, children cover specific +- **No Redundancy**: Child AGENTS.md NEVER repeats parent content + +--- + +## Process + + +**MANDATORY: TodoWrite for ALL phases. Mark in_progress → completed in real-time.** + + +### Phase 0: Initialize + +\`\`\` +TodoWrite([ + { id: "p1-analysis", content: "Parallel project structure & complexity analysis", status: "pending", priority: "high" }, + { id: "p2-scoring", content: "Score directories, determine AGENTS.md locations", status: "pending", priority: "high" }, + { id: "p3-root", content: "Generate root AGENTS.md with Predict-then-Compare", status: "pending", priority: "high" }, + { id: "p4-subdirs", content: "Generate subdirectory AGENTS.md files in parallel", status: "pending", priority: "high" }, + { id: "p5-review", content: "Review, deduplicate, validate all files", status: "pending", priority: "medium" } +]) +\`\`\` + +--- + +## Phase 1: Parallel Project Analysis + +**Mark "p1-analysis" as in_progress.** + +Launch **ALL tasks simultaneously**: + + + +### Structural Analysis (bash - run in parallel) +\`\`\`bash +# Task A: Directory depth analysis +find . -type d -not -path '*/\\.*' -not -path '*/node_modules/*' -not -path '*/venv/*' -not -path '*/__pycache__/*' -not -path '*/dist/*' -not -path '*/build/*' | awk -F/ '{print NF-1}' | sort -n | uniq -c + +# Task B: File count per directory +find . -type f -not -path '*/\\.*' -not -path '*/node_modules/*' -not -path '*/venv/*' -not -path '*/__pycache__/*' | sed 's|/[^/]*$||' | sort | uniq -c | sort -rn | head -30 + +# Task C: Code concentration +find . -type f \\( -name "*.py" -o -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" -o -name "*.go" -o -name "*.rs" -o -name "*.java" \\) -not -path '*/node_modules/*' -not -path '*/venv/*' | sed 's|/[^/]*$||' | sort | uniq -c | sort -rn | head -20 + +# Task D: Existing knowledge files +find . -type f \\( -name "AGENTS.md" -o -name "CLAUDE.md" \\) -not -path '*/node_modules/*' 2>/dev/null +\`\`\` + +### Context Gathering (Explore agents - background_task in parallel) + +\`\`\` +background_task(agent="explore", prompt="Project structure: PREDICT standard {lang} patterns → FIND package.json/pyproject.toml/go.mod → REPORT deviations only") + +background_task(agent="explore", prompt="Entry points: PREDICT typical (main.py, index.ts) → FIND actual → REPORT non-standard organization") + +background_task(agent="explore", prompt="Conventions: FIND .cursor/rules, .cursorrules, eslintrc, pyproject.toml → REPORT project-specific rules DIFFERENT from defaults") + +background_task(agent="explore", prompt="Anti-patterns: FIND comments with 'DO NOT', 'NEVER', 'ALWAYS', 'LEGACY', 'DEPRECATED' → REPORT forbidden patterns") + +background_task(agent="explore", prompt="Build/CI: FIND .github/workflows, Makefile, justfile → REPORT non-standard build/deploy patterns") + +background_task(agent="explore", prompt="Test patterns: FIND pytest.ini, jest.config, test structure → REPORT unique testing conventions") +\`\`\` + + + +**Collect all results. Mark "p1-analysis" as completed.** + +--- + +## Phase 2: Complexity Scoring & Location Decision + +**Mark "p2-scoring" as in_progress.** + +### Scoring Matrix + +| Factor | Weight | Threshold | +|--------|--------|-----------| +| File count | 3x | >20 files = high | +| Subdirectory count | 2x | >5 subdirs = high | +| Code file ratio | 2x | >70% code = high | +| Unique patterns | 1x | Has own config | +| Module boundary | 2x | Has __init__.py/index.ts | + +### Decision Rules + +| Score | Action | +|-------|--------| +| **Root (.)** | ALWAYS create AGENTS.md | +| **High (>15)** | Create dedicated AGENTS.md | +| **Medium (8-15)** | Create if distinct domain | +| **Low (<8)** | Skip, parent sufficient | + +### Output Format + +\`\`\` +AGENTS_LOCATIONS = [ + { path: ".", type: "root" }, + { path: "src/api", score: 18, reason: "high complexity, 45 files" }, + { path: "src/hooks", score: 12, reason: "distinct domain, unique patterns" }, +] +\`\`\` + +**Mark "p2-scoring" as completed.** + +--- + +## Phase 3: Generate Root AGENTS.md + +**Mark "p3-root" as in_progress.** + +Root AGENTS.md gets **full treatment** with Predict-then-Compare synthesis. + +### Required Sections + +\`\`\`markdown +# PROJECT KNOWLEDGE BASE + +**Generated:** {TIMESTAMP} +**Commit:** {SHORT_SHA} +**Branch:** {BRANCH} + +## OVERVIEW + +{1-2 sentences: what project does, core tech stack} + +## STRUCTURE + +\\\`\\\`\\\` +{project-root}/ +├── {dir}/ # {non-obvious purpose only} +└── {entry} # entry point +\\\`\\\`\\\` + +## WHERE TO LOOK + +| Task | Location | Notes | +|------|----------|-------| +| Add feature X | \\\`src/x/\\\` | {pattern hint} | + +## CONVENTIONS + +{ONLY deviations from standard - skip generic advice} + +- **{rule}**: {specific detail} + +## ANTI-PATTERNS (THIS PROJECT) + +{Things explicitly forbidden HERE} + +- **{pattern}**: {why} → {alternative} + +## UNIQUE STYLES + +{Project-specific coding styles} + +- **{style}**: {how different} + +## COMMANDS + +\\\`\\\`\\\`bash +{dev-command} +{test-command} +{build-command} +\\\`\\\`\\\` + +## NOTES + +{Gotchas, non-obvious info} +\`\`\` + +### Quality Gates + +- [ ] Size: 50-150 lines +- [ ] No generic advice ("write clean code") +- [ ] No obvious info ("tests/ has tests") +- [ ] Every item is project-specific + +**Mark "p3-root" as completed.** + +--- + +## Phase 4: Generate Subdirectory AGENTS.md + +**Mark "p4-subdirs" as in_progress.** + +For each location in AGENTS_LOCATIONS (except root), launch **parallel document-writer agents**: + +\`\`\`typescript +for (const loc of AGENTS_LOCATIONS.filter(l => l.path !== ".")) { + background_task({ + agent: "document-writer", + prompt: \\\` + Generate AGENTS.md for: \${loc.path} + + CONTEXT: + - Complexity reason: \${loc.reason} + - Parent AGENTS.md: ./AGENTS.md (already covers project overview) + + CRITICAL RULES: + 1. Focus ONLY on this directory's specific context + 2. NEVER repeat parent AGENTS.md content + 3. Shorter is better - 30-80 lines max + 4. Telegraphic style - sacrifice grammar + + REQUIRED SECTIONS: + - OVERVIEW (1 line: what this directory does) + - STRUCTURE (only if >5 subdirs) + - WHERE TO LOOK (directory-specific tasks) + - CONVENTIONS (only if DIFFERENT from root) + - ANTI-PATTERNS (directory-specific only) + + OUTPUT: Write to \${loc.path}/AGENTS.md + \\\` + }) +} +\`\`\` + +**Wait for all agents. Mark "p4-subdirs" as completed.** + +--- + +## Phase 5: Review & Deduplicate + +**Mark "p5-review" as in_progress.** + +### Validation Checklist + +For EACH generated AGENTS.md: + +| Check | Action if Fail | +|-------|----------------| +| Contains generic advice | REMOVE the line | +| Repeats parent content | REMOVE the line | +| Missing required section | ADD it | +| Over 150 lines (root) / 80 lines (subdir) | TRIM | +| Verbose explanations | REWRITE telegraphic | + +### Cross-Reference Validation + +\`\`\` +For each child AGENTS.md: + For each line in child: + If similar line exists in parent: + REMOVE from child (parent already covers) +\`\`\` + +**Mark "p5-review" as completed.** + +--- + +## Final Report + +\`\`\` +=== init-deep Complete === + +Files Generated: + ✓ ./AGENTS.md (root, {N} lines) + ✓ ./src/hooks/AGENTS.md ({N} lines) + ✓ ./src/tools/AGENTS.md ({N} lines) + +Directories Analyzed: {N} +AGENTS.md Created: {N} +Total Lines: {N} + +Hierarchy: + ./AGENTS.md + ├── src/hooks/AGENTS.md + └── src/tools/AGENTS.md +\`\`\` + +--- + +## Anti-Patterns for THIS Command + +- **Over-documenting**: Not every directory needs AGENTS.md +- **Redundancy**: Child must NOT repeat parent +- **Generic content**: Remove anything that applies to ALL projects +- **Sequential execution**: MUST use parallel agents +- **Deep nesting**: Rarely need AGENTS.md at depth 4+ +- **Verbose style**: "This directory contains..." → just list it` diff --git a/src/features/builtin-commands/types.ts b/src/features/builtin-commands/types.ts new file mode 100644 index 0000000..42a5b43 --- /dev/null +++ b/src/features/builtin-commands/types.ts @@ -0,0 +1,9 @@ +import type { CommandDefinition } from "../claude-code-command-loader" + +export type BuiltinCommandName = "init-deep" + +export interface BuiltinCommandConfig { + disabled_commands?: BuiltinCommandName[] +} + +export type BuiltinCommands = Record diff --git a/src/index.ts b/src/index.ts index 98a904a..eec80e6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,6 +32,7 @@ import { loadOpencodeGlobalCommands, loadOpencodeProjectCommands, } from "./features/claude-code-command-loader"; +import { loadBuiltinCommands } from "./features/builtin-commands"; import { loadUserAgents, @@ -169,6 +170,12 @@ function mergeConfigs( ...(override.disabled_hooks ?? []), ]), ], + disabled_commands: [ + ...new Set([ + ...(base.disabled_commands ?? []), + ...(override.disabled_commands ?? []), + ]), + ], claude_code: deepMerge(base.claude_code, override.claude_code), }; } @@ -510,12 +517,14 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => { ...pluginComponents.mcpServers, }; + const builtinCommands = loadBuiltinCommands(pluginConfig.disabled_commands); const userCommands = (pluginConfig.claude_code?.commands ?? true) ? loadUserCommands() : {}; const opencodeGlobalCommands = loadOpencodeGlobalCommands(); const systemCommands = config.command ?? {}; const projectCommands = (pluginConfig.claude_code?.commands ?? true) ? loadProjectCommands() : {}; const opencodeProjectCommands = loadOpencodeProjectCommands(); config.command = { + ...builtinCommands, ...userCommands, ...opencodeGlobalCommands, ...systemCommands, @@ -632,6 +641,7 @@ export type { AgentOverrides, McpName, HookName, + BuiltinCommandName, } from "./config"; // NOTE: Do NOT export functions from main index.ts!