@doingdev/opencode-claude-manager-plugin 0.1.64 → 0.1.66

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 (125) hide show
  1. package/README.md +106 -120
  2. package/dist/claude/claude-agent-sdk-adapter.js +1 -1
  3. package/dist/index.d.ts +1 -1
  4. package/dist/manager/team-orchestrator.js +1 -1
  5. package/dist/plugin/agents/common.d.ts +2 -2
  6. package/dist/plugin/agents/common.js +5 -0
  7. package/dist/plugin/claude-manager.plugin.js +104 -0
  8. package/dist/plugin/inbox-ops.d.ts +50 -0
  9. package/dist/plugin/inbox-ops.js +166 -0
  10. package/dist/types/contracts.d.ts +18 -0
  11. package/package.json +13 -13
  12. package/dist/claude/session-live-tailer.d.ts +0 -51
  13. package/dist/claude/session-live-tailer.js +0 -269
  14. package/dist/manager/session-controller.d.ts +0 -41
  15. package/dist/manager/session-controller.js +0 -97
  16. package/dist/metadata/claude-metadata.service.d.ts +0 -12
  17. package/dist/metadata/claude-metadata.service.js +0 -38
  18. package/dist/metadata/repo-claude-config-reader.d.ts +0 -7
  19. package/dist/metadata/repo-claude-config-reader.js +0 -154
  20. package/dist/plugin/orchestrator.plugin.d.ts +0 -2
  21. package/dist/plugin/orchestrator.plugin.js +0 -116
  22. package/dist/providers/claude-code-wrapper.d.ts +0 -13
  23. package/dist/providers/claude-code-wrapper.js +0 -13
  24. package/dist/safety/bash-safety.d.ts +0 -21
  25. package/dist/safety/bash-safety.js +0 -62
  26. package/dist/src/claude/claude-agent-sdk-adapter.d.ts +0 -28
  27. package/dist/src/claude/claude-agent-sdk-adapter.js +0 -559
  28. package/dist/src/claude/claude-session.service.d.ts +0 -9
  29. package/dist/src/claude/claude-session.service.js +0 -15
  30. package/dist/src/claude/session-live-tailer.d.ts +0 -51
  31. package/dist/src/claude/session-live-tailer.js +0 -269
  32. package/dist/src/claude/tool-approval-manager.d.ts +0 -30
  33. package/dist/src/claude/tool-approval-manager.js +0 -279
  34. package/dist/src/index.d.ts +0 -5
  35. package/dist/src/index.js +0 -3
  36. package/dist/src/manager/context-tracker.d.ts +0 -32
  37. package/dist/src/manager/context-tracker.js +0 -103
  38. package/dist/src/manager/git-operations.d.ts +0 -18
  39. package/dist/src/manager/git-operations.js +0 -86
  40. package/dist/src/manager/persistent-manager.d.ts +0 -39
  41. package/dist/src/manager/persistent-manager.js +0 -44
  42. package/dist/src/manager/session-controller.d.ts +0 -41
  43. package/dist/src/manager/session-controller.js +0 -97
  44. package/dist/src/manager/team-orchestrator.d.ts +0 -81
  45. package/dist/src/manager/team-orchestrator.js +0 -612
  46. package/dist/src/plugin/agent-hierarchy.d.ts +0 -1
  47. package/dist/src/plugin/agent-hierarchy.js +0 -2
  48. package/dist/src/plugin/agents/browser-qa.d.ts +0 -14
  49. package/dist/src/plugin/agents/browser-qa.js +0 -31
  50. package/dist/src/plugin/agents/common.d.ts +0 -36
  51. package/dist/src/plugin/agents/common.js +0 -59
  52. package/dist/src/plugin/agents/cto.d.ts +0 -9
  53. package/dist/src/plugin/agents/cto.js +0 -39
  54. package/dist/src/plugin/agents/engineers.d.ts +0 -9
  55. package/dist/src/plugin/agents/engineers.js +0 -11
  56. package/dist/src/plugin/agents/index.d.ts +0 -5
  57. package/dist/src/plugin/agents/index.js +0 -5
  58. package/dist/src/plugin/agents/team-planner.d.ts +0 -10
  59. package/dist/src/plugin/agents/team-planner.js +0 -23
  60. package/dist/src/plugin/claude-manager.plugin.d.ts +0 -10
  61. package/dist/src/plugin/claude-manager.plugin.js +0 -950
  62. package/dist/src/plugin/service-factory.d.ts +0 -38
  63. package/dist/src/plugin/service-factory.js +0 -101
  64. package/dist/src/prompts/registry.d.ts +0 -2
  65. package/dist/src/prompts/registry.js +0 -210
  66. package/dist/src/state/file-run-state-store.d.ts +0 -14
  67. package/dist/src/state/file-run-state-store.js +0 -85
  68. package/dist/src/state/team-state-store.d.ts +0 -14
  69. package/dist/src/state/team-state-store.js +0 -88
  70. package/dist/src/state/transcript-store.d.ts +0 -15
  71. package/dist/src/state/transcript-store.js +0 -44
  72. package/dist/src/team/roster.d.ts +0 -5
  73. package/dist/src/team/roster.js +0 -40
  74. package/dist/src/types/contracts.d.ts +0 -261
  75. package/dist/src/types/contracts.js +0 -2
  76. package/dist/src/util/fs-helpers.d.ts +0 -8
  77. package/dist/src/util/fs-helpers.js +0 -21
  78. package/dist/src/util/project-context.d.ts +0 -10
  79. package/dist/src/util/project-context.js +0 -105
  80. package/dist/src/util/transcript-append.d.ts +0 -7
  81. package/dist/src/util/transcript-append.js +0 -29
  82. package/dist/state/file-run-state-store.d.ts +0 -14
  83. package/dist/state/file-run-state-store.js +0 -85
  84. package/dist/test/claude-agent-sdk-adapter.test.d.ts +0 -1
  85. package/dist/test/claude-agent-sdk-adapter.test.js +0 -707
  86. package/dist/test/claude-manager.plugin.test.d.ts +0 -1
  87. package/dist/test/claude-manager.plugin.test.js +0 -316
  88. package/dist/test/context-tracker.test.d.ts +0 -1
  89. package/dist/test/context-tracker.test.js +0 -130
  90. package/dist/test/cto-active-team.test.d.ts +0 -1
  91. package/dist/test/cto-active-team.test.js +0 -199
  92. package/dist/test/file-run-state-store.test.d.ts +0 -1
  93. package/dist/test/file-run-state-store.test.js +0 -82
  94. package/dist/test/fs-helpers.test.d.ts +0 -1
  95. package/dist/test/fs-helpers.test.js +0 -56
  96. package/dist/test/git-operations.test.d.ts +0 -1
  97. package/dist/test/git-operations.test.js +0 -133
  98. package/dist/test/persistent-manager.test.d.ts +0 -1
  99. package/dist/test/persistent-manager.test.js +0 -48
  100. package/dist/test/project-context.test.d.ts +0 -1
  101. package/dist/test/project-context.test.js +0 -92
  102. package/dist/test/prompt-registry.test.d.ts +0 -1
  103. package/dist/test/prompt-registry.test.js +0 -117
  104. package/dist/test/report-claude-event.test.d.ts +0 -1
  105. package/dist/test/report-claude-event.test.js +0 -304
  106. package/dist/test/session-controller.test.d.ts +0 -1
  107. package/dist/test/session-controller.test.js +0 -149
  108. package/dist/test/session-live-tailer.test.d.ts +0 -1
  109. package/dist/test/session-live-tailer.test.js +0 -313
  110. package/dist/test/team-orchestrator.test.d.ts +0 -1
  111. package/dist/test/team-orchestrator.test.js +0 -583
  112. package/dist/test/team-state-store.test.d.ts +0 -1
  113. package/dist/test/team-state-store.test.js +0 -54
  114. package/dist/test/tool-approval-manager.test.d.ts +0 -1
  115. package/dist/test/tool-approval-manager.test.js +0 -260
  116. package/dist/test/transcript-append.test.d.ts +0 -1
  117. package/dist/test/transcript-append.test.js +0 -37
  118. package/dist/test/transcript-store.test.d.ts +0 -1
  119. package/dist/test/transcript-store.test.js +0 -50
  120. package/dist/test/undo-propagation.test.d.ts +0 -1
  121. package/dist/test/undo-propagation.test.js +0 -837
  122. package/dist/util/project-context.d.ts +0 -10
  123. package/dist/util/project-context.js +0 -105
  124. package/dist/vitest.config.d.ts +0 -2
  125. package/dist/vitest.config.js +0 -11
@@ -1,97 +0,0 @@
1
- export class SessionController {
2
- sdkAdapter;
3
- contextTracker;
4
- sessionPrompt;
5
- wrapperType;
6
- worktree;
7
- modePrefixes;
8
- activeSessionId = null;
9
- constructor(sdkAdapter, contextTracker, sessionPrompt, wrapperType, worktree, modePrefixes = {
10
- plan: '',
11
- free: '',
12
- }) {
13
- this.sdkAdapter = sdkAdapter;
14
- this.contextTracker = contextTracker;
15
- this.sessionPrompt = sessionPrompt;
16
- this.wrapperType = wrapperType;
17
- this.worktree = worktree;
18
- this.modePrefixes = modePrefixes;
19
- }
20
- get isActive() {
21
- return this.activeSessionId !== null;
22
- }
23
- get sessionId() {
24
- return this.activeSessionId;
25
- }
26
- /**
27
- * Send a message to the persistent session. Creates one if none exists.
28
- * Returns the session result including usage data.
29
- */
30
- async sendMessage(message, options, onEvent) {
31
- const mode = options?.mode ?? 'free';
32
- const prefix = this.modePrefixes[mode];
33
- const prompt = prefix ? `${prefix}\n\n${message}` : message;
34
- const input = {
35
- cwd: this.worktree,
36
- prompt,
37
- persistSession: true,
38
- permissionMode: mode === 'plan' ? 'plan' : 'acceptEdits',
39
- includePartialMessages: true,
40
- model: options?.model,
41
- effort: options?.effort,
42
- settingSources: ['user'],
43
- abortSignal: options?.abortSignal,
44
- };
45
- if (this.activeSessionId) {
46
- // Resume existing session
47
- input.resumeSessionId = this.activeSessionId;
48
- }
49
- else {
50
- // New session — prefer dynamically constructed prompt from wrapper, fall back to static default
51
- input.systemPrompt = options?.sessionSystemPrompt ?? this.sessionPrompt;
52
- input.model ??= 'claude-opus-4-6';
53
- input.effort ??= 'high';
54
- }
55
- const result = await this.sdkAdapter.runSession(input, onEvent);
56
- // Track the session ID
57
- if (result.sessionId) {
58
- this.activeSessionId = result.sessionId;
59
- }
60
- // Update context tracking
61
- this.contextTracker.recordResult({
62
- sessionId: result.sessionId,
63
- turns: result.turns,
64
- totalCostUsd: result.totalCostUsd,
65
- inputTokens: result.inputTokens,
66
- outputTokens: result.outputTokens,
67
- contextWindowSize: result.contextWindowSize,
68
- });
69
- return result;
70
- }
71
- /**
72
- * Send /compact to the current session to compress context.
73
- */
74
- async compactSession(onEvent) {
75
- if (!this.activeSessionId) {
76
- throw new Error('No active session to compact');
77
- }
78
- const result = await this.sendMessage('/compact', undefined, onEvent);
79
- this.contextTracker.recordCompaction();
80
- return result;
81
- }
82
- /**
83
- * Clear the current session. The next sendMessage will create a fresh one.
84
- */
85
- async clearSession() {
86
- const clearedId = this.activeSessionId;
87
- this.activeSessionId = null;
88
- this.contextTracker.reset();
89
- return clearedId;
90
- }
91
- /**
92
- * Get current context tracking snapshot.
93
- */
94
- getContextSnapshot() {
95
- return this.contextTracker.snapshot();
96
- }
97
- }
@@ -1,12 +0,0 @@
1
- import type { ClaudeMetadataSnapshot, ClaudeSettingSource } from '../types/contracts.js';
2
- import type { ClaudeAgentSdkAdapter } from '../claude/claude-agent-sdk-adapter.js';
3
- import type { RepoClaudeConfigReader } from './repo-claude-config-reader.js';
4
- export declare class ClaudeMetadataService {
5
- private readonly configReader;
6
- private readonly sdkAdapter;
7
- constructor(configReader: RepoClaudeConfigReader, sdkAdapter: ClaudeAgentSdkAdapter);
8
- collect(cwd: string, options?: {
9
- includeSdkProbe?: boolean;
10
- settingSources?: ClaudeSettingSource[];
11
- }): Promise<ClaudeMetadataSnapshot>;
12
- }
@@ -1,38 +0,0 @@
1
- export class ClaudeMetadataService {
2
- configReader;
3
- sdkAdapter;
4
- constructor(configReader, sdkAdapter) {
5
- this.configReader = configReader;
6
- this.sdkAdapter = sdkAdapter;
7
- }
8
- async collect(cwd, options = {}) {
9
- const baseSnapshot = await this.configReader.read(cwd);
10
- if (!options.includeSdkProbe) {
11
- return dedupeSnapshot(baseSnapshot);
12
- }
13
- const capabilities = await this.sdkAdapter.probeCapabilities(cwd, options.settingSources);
14
- return dedupeSnapshot({
15
- ...baseSnapshot,
16
- commands: [...baseSnapshot.commands, ...capabilities.commands],
17
- agents: capabilities.agents,
18
- });
19
- }
20
- }
21
- function dedupeSnapshot(snapshot) {
22
- return {
23
- ...snapshot,
24
- commands: dedupeByName(snapshot.commands),
25
- skills: dedupeByName(snapshot.skills),
26
- hooks: dedupeByName(snapshot.hooks),
27
- agents: dedupeByName(snapshot.agents),
28
- };
29
- }
30
- function dedupeByName(items) {
31
- const seen = new Map();
32
- for (const item of items) {
33
- if (!seen.has(item.name)) {
34
- seen.set(item.name, item);
35
- }
36
- }
37
- return [...seen.values()].sort((left, right) => left.name.localeCompare(right.name));
38
- }
@@ -1,7 +0,0 @@
1
- import type { ClaudeMetadataSnapshot } from '../types/contracts.js';
2
- export declare class RepoClaudeConfigReader {
3
- read(cwd: string): Promise<ClaudeMetadataSnapshot>;
4
- private readSkills;
5
- private readCommands;
6
- private readSettings;
7
- }
@@ -1,154 +0,0 @@
1
- import { promises as fs } from 'node:fs';
2
- import path from 'node:path';
3
- import JSON5 from 'json5';
4
- export class RepoClaudeConfigReader {
5
- async read(cwd) {
6
- const claudeDirectory = path.join(cwd, '.claude');
7
- const skillsDirectory = path.join(claudeDirectory, 'skills');
8
- const commandsDirectory = path.join(claudeDirectory, 'commands');
9
- const claudeMdCandidates = [
10
- path.join(cwd, 'CLAUDE.md'),
11
- path.join(claudeDirectory, 'CLAUDE.md'),
12
- ];
13
- const collectedAt = new Date().toISOString();
14
- const [skills, commands, settingsResult, claudeMdPath] = await Promise.all([
15
- this.readSkills(skillsDirectory),
16
- this.readCommands(commandsDirectory),
17
- this.readSettings(claudeDirectory),
18
- findFirstExistingPath(claudeMdCandidates),
19
- ]);
20
- return {
21
- collectedAt,
22
- cwd,
23
- commands: [...skillsToCommands(skills), ...commands],
24
- skills,
25
- hooks: settingsResult.hooks,
26
- agents: [],
27
- claudeMdPath: claudeMdPath ?? undefined,
28
- settingsPaths: settingsResult.settingsPaths,
29
- };
30
- }
31
- async readSkills(directory) {
32
- if (!(await pathExists(directory))) {
33
- return [];
34
- }
35
- const entries = await fs.readdir(directory, { withFileTypes: true });
36
- const skills = await Promise.all(entries
37
- .filter((entry) => entry.isDirectory())
38
- .map(async (entry) => {
39
- const skillPath = path.join(directory, entry.name, 'SKILL.md');
40
- if (!(await pathExists(skillPath))) {
41
- return null;
42
- }
43
- const content = await fs.readFile(skillPath, 'utf8');
44
- return {
45
- name: entry.name,
46
- description: extractMarkdownDescription(content),
47
- path: skillPath,
48
- source: 'skill',
49
- };
50
- }));
51
- return skills.filter((skill) => skill !== null);
52
- }
53
- async readCommands(directory) {
54
- if (!(await pathExists(directory))) {
55
- return [];
56
- }
57
- const commandFiles = await collectMarkdownFiles(directory);
58
- const commands = await Promise.all(commandFiles.map(async (commandPath) => {
59
- const content = await fs.readFile(commandPath, 'utf8');
60
- return {
61
- name: path.basename(commandPath, path.extname(commandPath)),
62
- description: extractMarkdownDescription(content),
63
- source: 'command',
64
- path: commandPath,
65
- };
66
- }));
67
- return commands.sort((left, right) => left.name.localeCompare(right.name));
68
- }
69
- async readSettings(claudeDirectory) {
70
- const candidatePaths = [
71
- path.join(claudeDirectory, 'settings.json'),
72
- path.join(claudeDirectory, 'settings.local.json'),
73
- ];
74
- const settingsPaths = [];
75
- const hooks = [];
76
- for (const candidatePath of candidatePaths) {
77
- if (!(await pathExists(candidatePath))) {
78
- continue;
79
- }
80
- settingsPaths.push(candidatePath);
81
- const content = await fs.readFile(candidatePath, 'utf8');
82
- const parsed = JSON5.parse(content);
83
- const hookEntries = Object.entries(parsed.hooks ?? {});
84
- for (const [hookName, hookValue] of hookEntries) {
85
- const hookMatchers = Array.isArray(hookValue) ? hookValue : [hookValue];
86
- for (const hookMatcher of hookMatchers) {
87
- if (!hookMatcher || typeof hookMatcher !== 'object') {
88
- continue;
89
- }
90
- const matcher = typeof hookMatcher.matcher === 'string'
91
- ? hookMatcher.matcher
92
- : undefined;
93
- const commandCount = Array.isArray(hookMatcher.hooks)
94
- ? (hookMatcher.hooks?.length ?? 0)
95
- : 0;
96
- hooks.push({
97
- name: hookName,
98
- matcher,
99
- sourcePath: candidatePath,
100
- commandCount,
101
- });
102
- }
103
- }
104
- }
105
- return {
106
- settingsPaths,
107
- hooks,
108
- };
109
- }
110
- }
111
- function extractMarkdownDescription(markdown) {
112
- const lines = markdown
113
- .split(/\r?\n/)
114
- .map((line) => line.trim())
115
- .filter(Boolean);
116
- const descriptionLine = lines.find((line) => !line.startsWith('#') && !line.startsWith('---'));
117
- return descriptionLine ?? 'No description provided.';
118
- }
119
- async function collectMarkdownFiles(directory) {
120
- const entries = await fs.readdir(directory, { withFileTypes: true });
121
- const files = await Promise.all(entries.map(async (entry) => {
122
- const resolvedPath = path.join(directory, entry.name);
123
- if (entry.isDirectory()) {
124
- return collectMarkdownFiles(resolvedPath);
125
- }
126
- return entry.name.endsWith('.md') ? [resolvedPath] : [];
127
- }));
128
- return files.flat();
129
- }
130
- async function pathExists(candidatePath) {
131
- try {
132
- await fs.access(candidatePath);
133
- return true;
134
- }
135
- catch {
136
- return false;
137
- }
138
- }
139
- async function findFirstExistingPath(candidatePaths) {
140
- for (const candidatePath of candidatePaths) {
141
- if (await pathExists(candidatePath)) {
142
- return candidatePath;
143
- }
144
- }
145
- return null;
146
- }
147
- function skillsToCommands(skills) {
148
- return skills.map((skill) => ({
149
- name: skill.name,
150
- description: skill.description,
151
- source: 'skill',
152
- path: skill.path,
153
- }));
154
- }
@@ -1,2 +0,0 @@
1
- import type { Plugin } from '@opencode-ai/plugin';
2
- export declare const OrchestratorPlugin: Plugin;
@@ -1,116 +0,0 @@
1
- import { prompts } from '../prompts/registry.js';
2
- import { evaluateBashCommand, extractBashCommand, } from '../safety/bash-safety.js';
3
- /**
4
- * Thin OpenCode orchestrator plugin with Claude Code specialist subagents.
5
- *
6
- * - Registers `claude-code` provider via a local shim over ai-sdk-provider-claude-code.
7
- * - Creates one orchestrator agent (uses the user's default OpenCode model).
8
- * - Creates 4 Claude Code subagents: planning + build × opus + sonnet.
9
- * - Enforces bash safety via the permission.ask hook.
10
- *
11
- * NOTE: Claude Code `effort` is not configurable through OpenCode provider/model
12
- * options at this time. The subagent prompts compensate by setting high-quality
13
- * expectations directly.
14
- */
15
- // Resolve the shim path at module load time so it is stable for the lifetime
16
- // of the process. The compiled output for this file sits at dist/plugin/ and
17
- // the shim at dist/providers/, so we walk up one level.
18
- const claudeCodeShimUrl = new URL('../providers/claude-code-wrapper.js', import.meta.url).href;
19
- export const OrchestratorPlugin = async () => {
20
- return {
21
- config: async (config) => {
22
- config.provider ??= {};
23
- config.agent ??= {};
24
- // ── Provider ──────────────────────────────────────────────────────
25
- // Uses a file:// shim so OpenCode's factory-finder heuristic sees only
26
- // createClaudeCode and not createAPICallError from the upstream package.
27
- config.provider['claude-code'] ??= {
28
- npm: claudeCodeShimUrl,
29
- models: {
30
- opus: {
31
- id: 'opus',
32
- name: 'Claude Code Opus 4.6',
33
- },
34
- sonnet: {
35
- id: 'sonnet',
36
- name: 'Claude Code Sonnet 4.6',
37
- },
38
- },
39
- };
40
- // ── Orchestrator (uses user's default model — no model set) ───────
41
- config.agent['opencode-orchestrator'] ??= {
42
- description: 'CTO-level orchestrator that gathers context and delegates coding to Claude Code specialists.',
43
- mode: 'primary',
44
- color: '#D97757',
45
- prompt: prompts.orchestrator,
46
- permission: {
47
- '*': 'deny',
48
- read: 'allow',
49
- grep: 'allow',
50
- glob: 'allow',
51
- list: 'allow',
52
- webfetch: 'allow',
53
- question: 'allow',
54
- todowrite: 'allow',
55
- todoread: 'allow',
56
- task: 'allow',
57
- bash: 'deny',
58
- edit: 'deny',
59
- skill: 'deny',
60
- },
61
- };
62
- // ── Planning subagents ────────────────────────────────────────────
63
- // Claude Code tools (Bash, Read, Write, Edit, …) are executed internally
64
- // by the claude CLI subprocess and streamed back with providerExecuted:true.
65
- // OpenCode's own tools must not be advertised to these agents.
66
- const claudeCodePermissions = {
67
- '*': 'deny',
68
- };
69
- config.agent['claude-code-planning-opus'] ??= {
70
- description: 'Claude Code Opus specialist for investigation, architecture, and planning.',
71
- model: 'claude-code/opus',
72
- mode: 'subagent',
73
- color: 'info',
74
- prompt: prompts.planningAgent,
75
- permission: { ...claudeCodePermissions },
76
- };
77
- config.agent['claude-code-planning-sonnet'] ??= {
78
- description: 'Claude Code Sonnet specialist for lighter investigation and planning.',
79
- model: 'claude-code/sonnet',
80
- mode: 'subagent',
81
- color: 'info',
82
- prompt: prompts.planningAgent,
83
- permission: { ...claudeCodePermissions },
84
- };
85
- // ── Build subagents ───────────────────────────────────────────────
86
- config.agent['claude-code-build-opus'] ??= {
87
- description: 'Claude Code Opus specialist for implementation and validation.',
88
- model: 'claude-code/opus',
89
- mode: 'subagent',
90
- color: 'success',
91
- prompt: prompts.buildAgent,
92
- permission: { ...claudeCodePermissions },
93
- };
94
- config.agent['claude-code-build-sonnet'] ??= {
95
- description: 'Claude Code Sonnet specialist for lighter implementation tasks.',
96
- model: 'claude-code/sonnet',
97
- mode: 'subagent',
98
- color: 'success',
99
- prompt: prompts.buildAgent,
100
- permission: { ...claudeCodePermissions },
101
- };
102
- },
103
- // ── Bash safety via permission.ask hook ────────────────────────────
104
- // Handles both v1 Permission ({ type, pattern }) and v2 PermissionRequest
105
- // ({ permission, patterns }) via runtime narrowing in extractBashCommand.
106
- 'permission.ask': async (input, output) => {
107
- const command = extractBashCommand(input);
108
- if (command === null)
109
- return;
110
- const result = evaluateBashCommand(command);
111
- if (!result.allowed) {
112
- output.status = 'deny';
113
- }
114
- },
115
- };
116
- };
@@ -1,13 +0,0 @@
1
- /**
2
- * Thin re-export shim for ai-sdk-provider-claude-code.
3
- *
4
- * OpenCode's provider loader finds the provider factory by scanning
5
- * `Object.keys(module).find(key => key.startsWith("create"))`. The upstream
6
- * package exports `createAPICallError` before `createClaudeCode`, so OpenCode
7
- * picks the wrong function and `.languageModel` ends up undefined.
8
- *
9
- * This shim re-exports only `createClaudeCode`, making it the sole "create*"
10
- * export. The plugin references this file via a `file://` URL so the upstream
11
- * package is still the actual implementation.
12
- */
13
- export { createClaudeCode } from 'ai-sdk-provider-claude-code';
@@ -1,13 +0,0 @@
1
- /**
2
- * Thin re-export shim for ai-sdk-provider-claude-code.
3
- *
4
- * OpenCode's provider loader finds the provider factory by scanning
5
- * `Object.keys(module).find(key => key.startsWith("create"))`. The upstream
6
- * package exports `createAPICallError` before `createClaudeCode`, so OpenCode
7
- * picks the wrong function and `.languageModel` ends up undefined.
8
- *
9
- * This shim re-exports only `createClaudeCode`, making it the sole "create*"
10
- * export. The plugin references this file via a `file://` URL so the upstream
11
- * package is still the actual implementation.
12
- */
13
- export { createClaudeCode } from 'ai-sdk-provider-claude-code';
@@ -1,21 +0,0 @@
1
- /**
2
- * Minimal bash command safety layer.
3
- * Denies known-dangerous patterns; allows everything else.
4
- */
5
- export type BashSafetyResult = {
6
- allowed: true;
7
- } | {
8
- allowed: false;
9
- reason: string;
10
- };
11
- export declare function evaluateBashCommand(command: string): BashSafetyResult;
12
- /**
13
- * Extract the bash command string from a permission hook input,
14
- * handling both SDK payload shapes:
15
- *
16
- * v1 Permission: { type: string, pattern?: string | string[], metadata }
17
- * v2 PermissionRequest: { permission: string, patterns: string[], metadata }
18
- *
19
- * Returns `null` when the input is not a bash permission request.
20
- */
21
- export declare function extractBashCommand(input: Record<string, unknown>): string | null;
@@ -1,62 +0,0 @@
1
- /**
2
- * Minimal bash command safety layer.
3
- * Denies known-dangerous patterns; allows everything else.
4
- */
5
- const DENY_PATTERNS = [
6
- { pattern: 'rm -rf /', reason: 'Destructive: rm -rf / is not allowed.' },
7
- {
8
- pattern: 'git push --force',
9
- reason: 'Force push is not allowed.',
10
- },
11
- {
12
- pattern: 'git reset --hard',
13
- reason: 'git reset --hard is not allowed. Use a safer alternative.',
14
- },
15
- ];
16
- export function evaluateBashCommand(command) {
17
- for (const { pattern, reason } of DENY_PATTERNS) {
18
- if (command.includes(pattern)) {
19
- return { allowed: false, reason };
20
- }
21
- }
22
- return { allowed: true };
23
- }
24
- /**
25
- * Extract the bash command string from a permission hook input,
26
- * handling both SDK payload shapes:
27
- *
28
- * v1 Permission: { type: string, pattern?: string | string[], metadata }
29
- * v2 PermissionRequest: { permission: string, patterns: string[], metadata }
30
- *
31
- * Returns `null` when the input is not a bash permission request.
32
- */
33
- export function extractBashCommand(input) {
34
- // Determine the permission kind from whichever field is present.
35
- const kind = typeof input['permission'] === 'string'
36
- ? input['permission']
37
- : typeof input['type'] === 'string'
38
- ? input['type']
39
- : null;
40
- if (kind !== 'bash')
41
- return null;
42
- // Prefer an explicit command in metadata regardless of shape.
43
- const meta = input['metadata'];
44
- if (meta !== null && typeof meta === 'object' && !Array.isArray(meta)) {
45
- const cmd = meta['command'];
46
- if (typeof cmd === 'string' && cmd.length > 0)
47
- return cmd;
48
- }
49
- // v2: patterns is always string[]
50
- const patterns = input['patterns'];
51
- if (Array.isArray(patterns) && patterns.length > 0) {
52
- return patterns.join(' ');
53
- }
54
- // v1: pattern may be string or string[]
55
- const pattern = input['pattern'];
56
- if (typeof pattern === 'string' && pattern.length > 0)
57
- return pattern;
58
- if (Array.isArray(pattern) && pattern.length > 0) {
59
- return pattern.join(' ');
60
- }
61
- return null;
62
- }
@@ -1,28 +0,0 @@
1
- import { type Options, type Query, type SDKSessionInfo, type SessionMessage } from '@anthropic-ai/claude-agent-sdk';
2
- import type { ClaudeSessionEvent, ClaudeSessionRunResult, ClaudeSessionSummary, ClaudeSessionTranscriptMessage, RunClaudeSessionInput } from '../types/contracts.js';
3
- import type { ToolApprovalManager } from './tool-approval-manager.js';
4
- export type ClaudeSessionEventHandler = (event: ClaudeSessionEvent) => void | Promise<void>;
5
- interface ClaudeAgentSdkFacade {
6
- query(params: {
7
- prompt: string;
8
- options?: Options;
9
- }): Query;
10
- listSessions(options?: {
11
- dir?: string;
12
- }): Promise<SDKSessionInfo[]>;
13
- getSessionMessages(sessionId: string, options?: {
14
- dir?: string;
15
- }): Promise<SessionMessage[]>;
16
- }
17
- export declare class ClaudeAgentSdkAdapter {
18
- private readonly sdkFacade;
19
- private readonly approvalManager?;
20
- private readonly debugLogPath?;
21
- constructor(sdkFacade?: ClaudeAgentSdkFacade, approvalManager?: ToolApprovalManager | undefined, debugLogPath?: string | undefined);
22
- runSession(input: RunClaudeSessionInput, onEvent?: ClaudeSessionEventHandler): Promise<ClaudeSessionRunResult>;
23
- listSavedSessions(cwd?: string): Promise<ClaudeSessionSummary[]>;
24
- getTranscript(sessionId: string, cwd?: string): Promise<ClaudeSessionTranscriptMessage[]>;
25
- private buildOptions;
26
- private logDeniedTool;
27
- }
28
- export {};