@ccx-agent/opencode-ccx 0.1.0 → 0.2.2

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ccx-agent
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 CHANGED
@@ -1,48 +1,66 @@
1
- # ccx — Coding Copilot, eXtended
1
+ <p align="center">
2
+ <h1 align="center">ccx</h1>
3
+ <p align="center"><strong>Coding Copilot, eXtended</strong></p>
4
+ <p align="center">
5
+ An <a href="https://opencode.ai">OpenCode</a> plugin that makes your AI coding agent disciplined, verification-driven, and risk-aware.
6
+ </p>
7
+ <p align="center">
8
+ <a href="https://www.npmjs.com/package/@ccx-agent/opencode-ccx"><img src="https://img.shields.io/npm/v/@ccx-agent/opencode-ccx?style=flat-square&color=blue" alt="npm version"></a>
9
+ <a href="https://github.com/Rejudge-F/ccx/blob/main/LICENSE"><img src="https://img.shields.io/github/license/Rejudge-F/ccx?style=flat-square" alt="license"></a>
10
+ </p>
11
+ </p>
2
12
 
3
- An [OpenCode](https://opencode.ai) plugin that turns your AI coding agent into a disciplined software engineer. ccx injects a complete behavioral framework — system prompt sections, specialized subagents, and safety hooks — so the agent writes less unnecessary code, verifies its own work, and thinks before running destructive commands.
13
+ ---
4
14
 
5
- ## Why
15
+ ## The Problem
6
16
 
7
- Out of the box, LLM coding agents tend to over-engineer, skip verification, and act before thinking. ccx fixes this by injecting a set of behavioral constraints and specialized subagents into your OpenCode session:
17
+ LLM coding agents tend to over-engineer, skip verification, and run destructive commands without thinking. They add abstractions nobody asked for, report "done" without testing, and `rm -rf` without blinking.
8
18
 
9
- - **Writes less, not more** — no speculative abstractions, no unrequested refactors, no gold-plating
10
- - **Verifies before reporting done** — an adversarial verification agent stress-tests implementations with real commands, not code reading
11
- - **Asks before destroying** — a risk guard flags `rm -rf`, `git push --force`, and other irreversible actions before they execute
12
- - **Delegates intelligently** — a coordinator agent decomposes complex tasks into research, implementation, and verification phases across parallel workers
19
+ ## The Solution
13
20
 
14
- ## What You Get
21
+ ccx injects a complete behavioral framework into your OpenCode agent — system prompt sections that enforce coding discipline, specialized subagents that verify and plan, and safety hooks that catch dangerous operations before they execute.
15
22
 
16
- | Component | What it does |
17
- |-----------|-------------|
18
- | **ccx** (primary agent) | Main agent with 7 composable system prompt sections governing coding discipline, risk awareness, tool usage, and output style |
19
- | **ccx-explore** | Read-only codebase search specialist. Fast parallel file/content search across large repos |
20
- | **ccx-plan** | Read-only software architect. Investigates code and produces step-by-step implementation plans with critical file lists |
21
- | **ccx-verification** | Adversarial verifier with VERDICT protocol (PASS/FAIL/PARTIAL). Runs builds, tests, linters, then tries to break things with boundary values, concurrency probes, and idempotency checks |
22
- | **ccx-general-purpose** | Multi-strategy task worker for anything that doesn't fit the specialists |
23
- | **ccx-coordinator** | Multi-agent orchestrator. Decomposes work into Research → Synthesis → Implementation → Verification phases across parallel workers |
24
- | **Risk Guard** | `tool.execute.before` hook that warns on destructive operations |
25
- | **Verification Reminder** | `tool.execute.after` hook that nudges verification after N file edits |
23
+ **Before ccx:** Agent writes 200 lines, adds 3 helper files, says "done."
26
24
 
27
- ## Install
25
+ **After ccx:** Agent writes 40 lines, runs the tests, spawns a verification agent that tries to break it, then reports the VERDICT.
26
+
27
+ ---
28
+
29
+ ## Quick Start
30
+
31
+ ```bash
32
+ opencode plugin @ccx-agent/opencode-ccx
33
+ ```
34
+
35
+ That's it. Restart OpenCode, press `@`, select **ccx**.
36
+
37
+ For global install (applies to all projects):
28
38
 
29
39
  ```bash
30
- bun add ccx
40
+ opencode plugin @ccx-agent/opencode-ccx -g
31
41
  ```
32
42
 
33
- Add to your OpenCode config:
43
+ ### Manual Installation
44
+
45
+ If you prefer manual setup, add to your `opencode.json`:
34
46
 
35
47
  ```jsonc
36
48
  // ~/.config/opencode/opencode.json
37
49
  {
38
50
  "plugin": [
39
- "ccx"
40
- // ... your other plugins
51
+ "@ccx-agent/opencode-ccx@0.1.0"
41
52
  ]
42
53
  }
43
54
  ```
44
55
 
45
- Or use a local build:
56
+ ### Development / Local Build
57
+
58
+ ```bash
59
+ git clone https://github.com/Rejudge-F/ccx.git
60
+ cd ccx && bun install && bun run build
61
+ ```
62
+
63
+ Then in `opencode.json`:
46
64
 
47
65
  ```jsonc
48
66
  {
@@ -52,9 +70,63 @@ Or use a local build:
52
70
  }
53
71
  ```
54
72
 
55
- Restart OpenCode. Press `@` to see `ccx` in the agent list.
73
+ ---
74
+
75
+ ## What's Inside
76
+
77
+ ### Primary Agent: `ccx`
78
+
79
+ The main agent comes with 7 composable system prompt sections:
80
+
81
+ | Section | What it enforces |
82
+ |---------|-----------------|
83
+ | **intro** | Agent identity and trust boundary for untrusted content |
84
+ | **system-rules** | Permission model, context compression, prompt injection awareness |
85
+ | **doing-tasks** | Scope control — no unrequested features, no speculative abstractions, duplicate code > premature abstraction |
86
+ | **actions** | Risk classification — reversible vs irreversible, local vs shared, ask before destroying |
87
+ | **using-tools** | Dedicated tools over Bash, parallel tool calls, structured progress tracking |
88
+ | **tone-style** | Concise output, no emojis, `file:line` references, `owner/repo#N` links |
89
+ | **output-efficiency** | Lead with the answer, skip filler, report at milestones not continuously |
90
+
91
+ ### Subagents
92
+
93
+ | Agent | Mode | Purpose |
94
+ |-------|------|---------|
95
+ | **ccx-explore** | read-only | Fast codebase search — parallel glob/grep/read across large repos |
96
+ | **ccx-plan** | read-only | Software architect — produces step-by-step plans with critical file lists |
97
+ | **ccx-verification** | read-only | Adversarial verifier — VERDICT protocol (PASS/FAIL/PARTIAL), runs real commands, catches self-rationalizations |
98
+ | **ccx-general-purpose** | read-write | Multi-strategy worker for tasks outside other specialists |
99
+ | **ccx-coordinator** | orchestrator | Decomposes complex work into Research, Synthesis, Implementation, Verification across parallel workers |
100
+
101
+ ### Safety Hooks
102
+
103
+ | Hook | Trigger | Behavior |
104
+ |------|---------|----------|
105
+ | **Risk Guard** | `tool.execute.before` | Warns on `rm -rf`, `git push --force`, `DROP TABLE`, and other irreversible commands |
106
+ | **Verification Reminder** | `tool.execute.after` | Nudges the agent to run `ccx-verification` after N file edits |
107
+
108
+ ---
109
+
110
+ ## The Verification Agent
111
+
112
+ The most opinionated part of ccx. Key design decisions:
113
+
114
+ - **Execution over reading** — every check must include a `Command run` block with real terminal output. "I reviewed the code and it looks correct" is rejected.
115
+ - **Self-rationalization awareness** — the prompt lists excuses the agent will reach for ("the code looks correct", "the tests already pass", "this would take too long") and instructs it to do the opposite.
116
+ - **Type-specific strategies** — different verification approaches for frontend, backend, CLI, infrastructure, database migrations, refactoring, mobile, data pipelines, and more.
117
+ - **Adversarial probes required** — before issuing PASS, at least one probe must be attempted: concurrency, boundary values, idempotency, or orphan operations.
118
+
119
+ Output format:
120
+
121
+ ```
122
+ VERDICT: PASS
123
+ ```
124
+
125
+ or `FAIL` with reproduction steps, or `PARTIAL` with what couldn't be verified and why.
126
+
127
+ ---
56
128
 
57
- ## Configure
129
+ ## Configuration
58
130
 
59
131
  Create `~/.config/opencode/ccx.json` (global) or `.opencode/ccx.json` (project-level):
60
132
 
@@ -73,59 +145,53 @@ Create `~/.config/opencode/ccx.json` (global) or `.opencode/ccx.json` (project-l
73
145
 
74
146
  | Field | Type | Default | Description |
75
147
  |-------|------|---------|-------------|
76
- | `enabled` | boolean | `true` | Master toggle |
77
- | `disabled_sections` | string[] | `[]` | System prompt sections to skip (e.g., `["tone-style", "output-efficiency"]`) |
78
- | `disabled_hooks` | string[] | `[]` | Hooks to disable (e.g., `["risk-guard"]`) |
79
- | `output_style` | string \| null | `null` | Custom output style name |
80
- | `verification.auto_remind` | boolean | `true` | Nudge verification after file edits |
81
- | `verification.min_file_edits` | number | `3` | Edit threshold before nudge |
82
-
83
- ## How It Works
84
-
85
- ccx hooks into OpenCode's plugin lifecycle at two points:
86
-
87
- **1. `config` hook** — Injects the `ccx` primary agent (with full system prompt) and 5 subagents into `config.agent`. The primary agent's prompt is composed from 7 independent sections + subagent orchestration guidance.
88
-
89
- **2. `tool.execute.before/after` hooks** — Risk Guard scans commands for destructive patterns pre-execution; Verification Reminder tracks file edit count post-execution.
90
-
91
- ### System Prompt Sections
92
-
93
- The primary agent's system prompt is assembled from these composable sections:
148
+ | `enabled` | `boolean` | `true` | Master toggle for the entire plugin |
149
+ | `disabled_sections` | `string[]` | `[]` | Prompt sections to skip (e.g., `["tone-style"]`) |
150
+ | `disabled_hooks` | `string[]` | `[]` | Hooks to disable (e.g., `["risk-guard"]`) |
151
+ | `output_style` | `string \| null` | `null` | Custom output style name |
152
+ | `verification.auto_remind` | `boolean` | `true` | Auto-nudge verification after edits |
153
+ | `verification.min_file_edits` | `number` | `3` | File edit threshold before nudge |
94
154
 
95
- | Section | Purpose |
96
- |---------|---------|
97
- | `intro` | Agent identity and trust boundary |
98
- | `system-rules` | Permission model, context compression, prompt injection awareness |
99
- | `doing-tasks` | Coding discipline — scope control, minimal abstraction, honest reporting |
100
- | `actions` | Risk classification for reversible vs irreversible operations |
101
- | `using-tools` | Prefer dedicated tools over Bash, parallel tool call strategy |
102
- | `tone-style` | No emojis, concise output, code reference formatting |
103
- | `output-efficiency` | Lead with answers, skip filler, milestone-based updates |
104
-
105
- ### Verification Agent
106
-
107
- The verification agent is the most opinionated component. Key behaviors:
108
-
109
- - **Runs commands, not reads code** — every check must include a `Command run` block with actual terminal output
110
- - **Catches its own rationalizations** — the prompt explicitly lists excuses the agent will reach for ("the code looks correct", "the tests already pass") and instructs it to do the opposite
111
- - **Adapts by change type** — different verification strategies for frontend, backend, CLI, infrastructure, database migrations, refactoring, etc.
112
- - **Requires adversarial probes** — at least one concurrency, boundary value, or idempotency test before issuing PASS
155
+ ---
113
156
 
114
157
  ## Project Structure
115
158
 
116
159
  ```
117
160
  ccx/
118
161
  ├── src/
119
- │ ├── index.ts # Plugin entry point
120
- │ ├── prompts/ # 7 system prompt sections + compose + environment
121
- │ ├── agents/ # 5 subagent definitions (explore, plan, verification, coordinator, general-purpose)
122
- │ ├── hooks/ # config-handler, risk-guard, verification-reminder, environment-context, system-prompt-injector
123
- │ ├── config/ # Zod schema + JSONC config loader
124
- │ └── plugin/ # OpenCode plugin interface + tool registry
162
+ │ ├── index.ts # Plugin entry — composes everything
163
+ │ ├── prompts/ # 7 system prompt sections + composer + environment detector
164
+ │ ├── agents/ # 5 subagent definitions with tailored prompts
165
+ │ ├── hooks/ # config-handler, risk-guard, verification-reminder
166
+ │ ├── config/ # Zod schema + JSONC config loader
167
+ │ └── plugin/ # OpenCode plugin interface + agent tool registry
168
+ ├── .github/workflows/
169
+ │ └── publish.yml # Auto-publish to npm on tag push
125
170
  ├── package.json
126
171
  └── tsconfig.json
127
172
  ```
128
173
 
174
+ ---
175
+
176
+ ## Contributing
177
+
178
+ ```bash
179
+ git clone https://github.com/Rejudge-F/ccx.git
180
+ cd ccx
181
+ bun install
182
+ bun run typecheck # type check
183
+ bun run build # build to dist/
184
+ ```
185
+
186
+ To publish a new version:
187
+
188
+ ```bash
189
+ # bump version in package.json, then:
190
+ git add -A && git commit -m "chore: bump to x.y.z"
191
+ git tag vx.y.z && git push origin main --tags
192
+ # GitHub Actions auto-publishes to npm
193
+ ```
194
+
129
195
  ## License
130
196
 
131
197
  MIT
@@ -0,0 +1,21 @@
1
+ import type { OhMyCCAgentConfig } from "../config/schema";
2
+ type ChatMessageInput = {
3
+ sessionID: string;
4
+ agent?: string;
5
+ model?: {
6
+ providerID: string;
7
+ modelID: string;
8
+ };
9
+ messageID?: string;
10
+ };
11
+ type ChatMessageOutput = {
12
+ message: Record<string, unknown>;
13
+ parts: Array<{
14
+ type: string;
15
+ text?: string;
16
+ [key: string]: unknown;
17
+ }>;
18
+ };
19
+ export declare function recordToolCall(sessionID: string, tool: string, file?: string): void;
20
+ export declare function createChatMessageHook(config: OhMyCCAgentConfig): (input: ChatMessageInput, output: ChatMessageOutput) => Promise<void>;
21
+ export {};
@@ -0,0 +1,18 @@
1
+ type ChatParamsInput = {
2
+ sessionID: string;
3
+ agent: string;
4
+ model: {
5
+ modelID?: string;
6
+ [key: string]: unknown;
7
+ };
8
+ provider: unknown;
9
+ message: unknown;
10
+ };
11
+ type ChatParamsOutput = {
12
+ temperature: number;
13
+ topP: number;
14
+ topK: number;
15
+ options: Record<string, unknown>;
16
+ };
17
+ export declare function createChatParamsHook(): (input: ChatParamsInput, output: ChatParamsOutput) => Promise<void>;
18
+ export {};
@@ -0,0 +1,6 @@
1
+ export declare function createCompactionHook(): (input: {
2
+ sessionID: string;
3
+ }, output: {
4
+ context: string[];
5
+ prompt?: string;
6
+ }) => Promise<void>;
@@ -0,0 +1,9 @@
1
+ import type { OhMyCCAgentConfig } from "../config/schema";
2
+ export declare function trackFileEdit(sessionID: string, filePath: string): void;
3
+ export declare function markVerificationTriggered(sessionID: string): void;
4
+ export declare function createDynamicSystemPrompt(config: OhMyCCAgentConfig, directory: string): (input: {
5
+ sessionID?: string;
6
+ model: unknown;
7
+ }, output: {
8
+ system: string[];
9
+ }) => Promise<void>;
@@ -0,0 +1,16 @@
1
+ type MessageInfo = {
2
+ info: {
3
+ role: string;
4
+ [key: string]: unknown;
5
+ };
6
+ parts: Array<{
7
+ type: string;
8
+ text?: string;
9
+ output?: string;
10
+ [key: string]: unknown;
11
+ }>;
12
+ };
13
+ export declare function createMessageTransformHook(): (_input: Record<string, unknown>, output: {
14
+ messages: MessageInfo[];
15
+ }) => Promise<void>;
16
+ export {};
@@ -0,0 +1,6 @@
1
+ export declare function createToolDefinitionHook(): (input: {
2
+ toolID: string;
3
+ }, output: {
4
+ description: string;
5
+ parameters: unknown;
6
+ }) => Promise<void>;
package/dist/index.js CHANGED
@@ -1,12 +1,16 @@
1
1
  // @bun
2
2
  var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
3
7
  var __export = (target, all) => {
4
8
  for (var name in all)
5
9
  __defProp(target, name, {
6
10
  get: all[name],
7
11
  enumerable: true,
8
12
  configurable: true,
9
- set: (newValue) => all[name] = () => newValue
13
+ set: __exportSetter.bind(all, name)
10
14
  });
11
15
  };
12
16
 
@@ -13634,6 +13638,141 @@ function loadConfig(directory) {
13634
13638
  return readConfigFile(projectConfigPath) ?? readConfigFile(userConfigPath) ?? defaults;
13635
13639
  }
13636
13640
 
13641
+ // src/hooks/dynamic-system-prompt.ts
13642
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
13643
+ import { join as join2 } from "path";
13644
+ var sessionStates = new Map;
13645
+ function getState(sessionID) {
13646
+ const existing = sessionStates.get(sessionID);
13647
+ if (existing)
13648
+ return existing;
13649
+ const state = {
13650
+ editedFiles: new Set,
13651
+ verificationTriggered: false,
13652
+ turnCount: 0
13653
+ };
13654
+ sessionStates.set(sessionID, state);
13655
+ return state;
13656
+ }
13657
+ function trackFileEdit(sessionID, filePath) {
13658
+ getState(sessionID).editedFiles.add(filePath);
13659
+ }
13660
+ function loadProjectInstructions(directory) {
13661
+ const candidates = ["CLAUDE.md", ".claude/instructions.md", "AGENTS.md"];
13662
+ for (const name of candidates) {
13663
+ const fullPath = join2(directory, name);
13664
+ if (existsSync2(fullPath)) {
13665
+ try {
13666
+ const content = readFileSync2(fullPath, "utf-8").trim();
13667
+ if (content)
13668
+ return content;
13669
+ } catch {
13670
+ continue;
13671
+ }
13672
+ }
13673
+ }
13674
+ return null;
13675
+ }
13676
+ function createDynamicSystemPrompt(config2, directory) {
13677
+ const projectInstructions = loadProjectInstructions(directory);
13678
+ return async (input, output) => {
13679
+ if (!config2.enabled)
13680
+ return;
13681
+ const sessionID = input.sessionID;
13682
+ if (projectInstructions) {
13683
+ output.system.push(`# Project Instructions
13684
+
13685
+ The following instructions were found in the project root and MUST be followed:
13686
+
13687
+ ${projectInstructions}`);
13688
+ }
13689
+ if (sessionID) {
13690
+ const state = getState(sessionID);
13691
+ state.turnCount++;
13692
+ if (state.editedFiles.size > 0) {
13693
+ const fileList = [...state.editedFiles].slice(-20).join(", ");
13694
+ output.system.push(`# Session Context
13695
+
13696
+ Files edited in this session (${state.editedFiles.size} total): ${fileList}`);
13697
+ }
13698
+ if (config2.verification.auto_remind && state.editedFiles.size >= config2.verification.min_file_edits && !state.verificationTriggered) {
13699
+ output.system.push(`# Verification Reminder
13700
+
13701
+ You have edited ${state.editedFiles.size} files in this session. You MUST run verification (via ccx-verification subagent) before declaring the task complete. This is not optional.`);
13702
+ }
13703
+ }
13704
+ };
13705
+ }
13706
+
13707
+ // src/hooks/chat-message.ts
13708
+ var EDIT_TOOLS = new Set(["edit", "write"]);
13709
+ var recentToolCalls = new Map;
13710
+ function recordToolCall(sessionID, tool, file2) {
13711
+ const calls = recentToolCalls.get(sessionID) ?? [];
13712
+ calls.push({ tool, file: file2 });
13713
+ if (calls.length > 50)
13714
+ calls.shift();
13715
+ recentToolCalls.set(sessionID, calls);
13716
+ }
13717
+ function createChatMessageHook(config2) {
13718
+ return async (input, output) => {
13719
+ if (!config2.enabled)
13720
+ return;
13721
+ const { sessionID, agent } = input;
13722
+ if (!sessionID)
13723
+ return;
13724
+ const calls = recentToolCalls.get(sessionID);
13725
+ if (calls && calls.length > 0) {
13726
+ const editedInRecent = calls.filter((c) => EDIT_TOOLS.has(c.tool) && c.file).map((c) => c.file);
13727
+ for (const file2 of editedInRecent) {
13728
+ trackFileEdit(sessionID, file2);
13729
+ }
13730
+ }
13731
+ if (agent && !agent.startsWith("ccx"))
13732
+ return;
13733
+ const reminders = [];
13734
+ if (config2.verification.auto_remind && calls && calls.filter((c) => EDIT_TOOLS.has(c.tool)).length >= config2.verification.min_file_edits) {
13735
+ reminders.push(`<system-reminder>You have made file edits. Remember the verification contract: run ccx-verification before declaring completion if you have 3+ file edits, backend/API changes, or infrastructure changes.</system-reminder>`);
13736
+ }
13737
+ if (reminders.length > 0) {
13738
+ output.parts.push({
13739
+ type: "text",
13740
+ text: reminders.join(`
13741
+ `)
13742
+ });
13743
+ }
13744
+ };
13745
+ }
13746
+
13747
+ // src/hooks/chat-params.ts
13748
+ var AGENT_PARAMS = {
13749
+ "ccx-plan": { temperature: 0.3, topP: 0.9 },
13750
+ "ccx-verification": { temperature: 0.2, topP: 0.85 },
13751
+ "ccx-explore": { temperature: 0.5, topP: 0.95 },
13752
+ "ccx-coordinator": { temperature: 0.4, topP: 0.9 },
13753
+ "ccx-general-purpose": { temperature: 0.5, topP: 0.95 }
13754
+ };
13755
+ function createChatParamsHook() {
13756
+ return async (input, output) => {
13757
+ const agentParams = AGENT_PARAMS[input.agent];
13758
+ if (!agentParams)
13759
+ return;
13760
+ if (agentParams.temperature !== undefined)
13761
+ output.temperature = agentParams.temperature;
13762
+ if (agentParams.topP !== undefined)
13763
+ output.topP = agentParams.topP;
13764
+ if (agentParams.topK !== undefined)
13765
+ output.topK = agentParams.topK;
13766
+ };
13767
+ }
13768
+
13769
+ // src/hooks/compaction.ts
13770
+ function createCompactionHook() {
13771
+ return async (input, output) => {
13772
+ output.context.push("IMPORTANT: When summarizing this conversation, you MUST preserve the following:", "1. The original user task/request and any refinements", "2. The implementation plan if one was created", "3. All file paths that were edited, created, or deleted", "4. The current verification status (PASS/FAIL/PARTIAL) if verification was run", "5. Any unresolved issues, blockers, or pending decisions", "6. Key architectural decisions and their rationale", "7. The names of any subagents that were dispatched and their outcomes", "Do NOT summarize tool outputs verbatim \u2014 capture only the conclusions and any error messages.");
13773
+ };
13774
+ }
13775
+
13637
13776
  // src/prompts/actions.ts
13638
13777
  function getActionsSection() {
13639
13778
  return `# Executing actions with care
@@ -13688,8 +13827,8 @@ function getDoingTasksSection() {
13688
13827
  }
13689
13828
 
13690
13829
  // src/prompts/environment.ts
13691
- import { existsSync as existsSync2 } from "fs";
13692
- import { join as join2 } from "path";
13830
+ import { existsSync as existsSync3 } from "fs";
13831
+ import { join as join3 } from "path";
13693
13832
  import { type as osType, release as osRelease } from "os";
13694
13833
  function normalizeShell(shell) {
13695
13834
  if (shell.includes("zsh"))
@@ -13715,7 +13854,7 @@ function getOsVersion() {
13715
13854
  }
13716
13855
  function detectGit(cwd) {
13717
13856
  try {
13718
- return existsSync2(join2(cwd, ".git"));
13857
+ return existsSync3(join3(cwd, ".git"));
13719
13858
  } catch {
13720
13859
  return false;
13721
13860
  }
@@ -14348,8 +14487,8 @@ function createConfigHook(config2, directory) {
14348
14487
  }
14349
14488
 
14350
14489
  // src/hooks/environment-context.ts
14351
- import { existsSync as existsSync3 } from "fs";
14352
- import { join as join3 } from "path";
14490
+ import { existsSync as existsSync4 } from "fs";
14491
+ import { join as join4 } from "path";
14353
14492
  var environmentBySession = new Map;
14354
14493
  function isRecord(value) {
14355
14494
  return typeof value === "object" && value !== null;
@@ -14367,7 +14506,7 @@ function createEnvironmentContext(directory) {
14367
14506
  const sessionID = typeof event.sessionID === "string" ? event.sessionID : typeof event.sessionId === "string" ? event.sessionId : undefined;
14368
14507
  const environment = {
14369
14508
  cwd: directory,
14370
- isGit: existsSync3(join3(directory, ".git")),
14509
+ isGit: existsSync4(join4(directory, ".git")),
14371
14510
  platform: process.platform,
14372
14511
  shell: process.env.SHELL ?? "unknown"
14373
14512
  };
@@ -14380,6 +14519,51 @@ function createEnvironmentContext(directory) {
14380
14519
  };
14381
14520
  }
14382
14521
 
14522
+ // src/hooks/message-transform.ts
14523
+ var MAX_TOOL_OUTPUT_LENGTH = 8000;
14524
+ var TOOL_OUTPUT_TRIM_TO = 4000;
14525
+ function trimLongToolOutputs(messages) {
14526
+ for (const msg of messages) {
14527
+ if (msg.info.role !== "assistant")
14528
+ continue;
14529
+ for (const part of msg.parts) {
14530
+ if (part.type !== "tool")
14531
+ continue;
14532
+ const output = typeof part.output === "string" ? part.output : undefined;
14533
+ if (output && output.length > MAX_TOOL_OUTPUT_LENGTH) {
14534
+ const head = output.slice(0, TOOL_OUTPUT_TRIM_TO / 2);
14535
+ const tail = output.slice(-TOOL_OUTPUT_TRIM_TO / 2);
14536
+ const trimmed = output.length - TOOL_OUTPUT_TRIM_TO;
14537
+ part.output = `${head}
14538
+
14539
+ ... [${trimmed} characters trimmed] ...
14540
+
14541
+ ${tail}`;
14542
+ }
14543
+ }
14544
+ }
14545
+ }
14546
+ function collapseConsecutiveReasoningParts(messages) {
14547
+ for (const msg of messages) {
14548
+ if (msg.info.role !== "assistant")
14549
+ continue;
14550
+ const collapsed = [];
14551
+ for (const part of msg.parts) {
14552
+ if (part.type === "reasoning" && collapsed.length > 0 && collapsed[collapsed.length - 1].type === "reasoning") {
14553
+ continue;
14554
+ }
14555
+ collapsed.push(part);
14556
+ }
14557
+ msg.parts = collapsed;
14558
+ }
14559
+ }
14560
+ function createMessageTransformHook() {
14561
+ return async (_input, output) => {
14562
+ trimLongToolOutputs(output.messages);
14563
+ collapseConsecutiveReasoningParts(output.messages);
14564
+ };
14565
+ }
14566
+
14383
14567
  // src/hooks/risk-guard.ts
14384
14568
  var RISK_PATTERNS = [
14385
14569
  /rm\s+-rf\b/i,
@@ -14427,6 +14611,35 @@ function createRiskGuard() {
14427
14611
  };
14428
14612
  }
14429
14613
 
14614
+ // src/hooks/tool-definition.ts
14615
+ var TOOL_SAFETY_HINTS = {
14616
+ bash: "SAFETY: Before executing, verify the command is non-destructive. Never run rm -rf, git push --force, git reset --hard, DROP TABLE, or TRUNCATE TABLE without explicit user approval. Prefer --dry-run flags when available. Quote all file paths containing spaces.",
14617
+ edit: "SAFETY: Always read the file first before editing. Preserve existing indentation. Do not modify files you have not examined. Never edit .env files or credentials.",
14618
+ write: "SAFETY: Prefer editing existing files over creating new ones. Never write to .env or credential files. Avoid creating files in the workspace root unless they are permanent source code."
14619
+ };
14620
+ var TOOL_CONTEXT_HINTS = {
14621
+ glob: "Use this for finding files by name pattern. Prefer this over bash find commands.",
14622
+ grep: "Use this for searching file contents by regex. Prefer this over bash grep/rg commands.",
14623
+ read: "Use this for reading file contents. Prefer this over bash cat/head/tail commands."
14624
+ };
14625
+ function createToolDefinitionHook() {
14626
+ return async (input, output) => {
14627
+ const toolName = input.toolID.toLowerCase();
14628
+ const safetyHint = TOOL_SAFETY_HINTS[toolName];
14629
+ if (safetyHint) {
14630
+ output.description = `${output.description}
14631
+
14632
+ ${safetyHint}`;
14633
+ }
14634
+ const contextHint = TOOL_CONTEXT_HINTS[toolName];
14635
+ if (contextHint) {
14636
+ output.description = `${output.description}
14637
+
14638
+ ${contextHint}`;
14639
+ }
14640
+ };
14641
+ }
14642
+
14430
14643
  // src/hooks/verification-reminder.ts
14431
14644
  var sessionStateById = new Map;
14432
14645
  var EDIT_TOOL_NAMES = new Set(["edit", "write"]);
@@ -26848,14 +27061,35 @@ function createPluginInterface(args) {
26848
27061
  const riskGuard = createRiskGuard();
26849
27062
  const verificationReminder = createVerificationReminder(config3);
26850
27063
  const environmentContext = createEnvironmentContext(ctx.directory);
27064
+ const dynamicSystemPrompt = createDynamicSystemPrompt(config3, ctx.directory);
27065
+ const chatMessageHook = createChatMessageHook(config3);
27066
+ const chatParamsHook = createChatParamsHook();
27067
+ const compactionHook = createCompactionHook();
27068
+ const toolDefinitionHook = createToolDefinitionHook();
27069
+ const messageTransformHook = createMessageTransformHook();
26851
27070
  return {
26852
27071
  config: configHook,
26853
27072
  tool: createAgentTools(),
26854
27073
  "tool.execute.before": riskGuard,
26855
- "tool.execute.after": verificationReminder,
27074
+ "tool.execute.after": async (input, output) => {
27075
+ await verificationReminder(input, output);
27076
+ const sessionID = typeof input.sessionID === "string" ? input.sessionID : undefined;
27077
+ const tool3 = typeof input.tool === "string" ? input.tool.toLowerCase() : undefined;
27078
+ if (sessionID && tool3) {
27079
+ const args2 = input.args;
27080
+ const file3 = typeof args2?.filePath === "string" ? args2.filePath : typeof args2?.path === "string" ? args2.path : undefined;
27081
+ recordToolCall(sessionID, tool3, file3);
27082
+ }
27083
+ },
26856
27084
  event: async (input) => {
26857
27085
  await environmentContext(input, {});
26858
- }
27086
+ },
27087
+ "chat.message": chatMessageHook,
27088
+ "chat.params": chatParamsHook,
27089
+ "tool.definition": toolDefinitionHook,
27090
+ "experimental.chat.system.transform": dynamicSystemPrompt,
27091
+ "experimental.chat.messages.transform": messageTransformHook,
27092
+ "experimental.session.compacting": compactionHook
26859
27093
  };
26860
27094
  }
26861
27095
 
@@ -2,7 +2,7 @@ import type { Plugin } from "@opencode-ai/plugin";
2
2
  import type { OhMyCCAgentConfig } from "../config/schema";
3
3
  type PluginContext = Parameters<Plugin>[0];
4
4
  type PluginInstance = Awaited<ReturnType<Plugin>>;
5
- export type PluginInterface = Pick<PluginInstance, "config" | "tool" | "tool.execute.before" | "tool.execute.after" | "event">;
5
+ export type PluginInterface = Pick<PluginInstance, "config" | "tool" | "tool.execute.before" | "tool.execute.after" | "event" | "chat.message" | "chat.params" | "tool.definition" | "experimental.chat.system.transform" | "experimental.chat.messages.transform" | "experimental.session.compacting">;
6
6
  export declare function createPluginInterface(args: {
7
7
  ctx: PluginContext;
8
8
  config: OhMyCCAgentConfig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ccx-agent/opencode-ccx",
3
- "version": "0.1.0",
3
+ "version": "0.2.2",
4
4
  "description": "Production-grade prompt engineering for OpenCode — disciplined coding, risk-aware actions, adversarial verification",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -20,6 +20,10 @@
20
20
  "typecheck": "tsc --noEmit",
21
21
  "prepublishOnly": "bun run clean && bun run build"
22
22
  },
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/Rejudge-F/ccx.git"
26
+ },
23
27
  "keywords": [
24
28
  "opencode",
25
29
  "plugin",