ocpipe 0.5.10 → 0.5.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ocpipe",
3
- "version": "0.5.10",
3
+ "version": "0.5.12",
4
4
  "description": "SDK for LLM pipelines with OpenCode and Zod",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -4,6 +4,10 @@
4
4
  * Uses the Claude Agent SDK v2 for running LLM agents with session management.
5
5
  */
6
6
 
7
+ import { execSync } from 'child_process'
8
+ import { existsSync, readFileSync } from 'fs'
9
+ import { join } from 'path'
10
+ import { homedir } from 'os'
7
11
  import {
8
12
  unstable_v2_createSession,
9
13
  unstable_v2_resumeSession,
@@ -51,6 +55,27 @@ function printToolEvent(color: string, type: string, title: string): void {
51
55
  console.error(line)
52
56
  }
53
57
 
58
+ /** Resolve the Claude Code executable path.
59
+ * Priority: 1) explicit option, 2) `which claude` from PATH, 3) common default paths. */
60
+ function resolveClaudeCodePath(explicit?: string): string | undefined {
61
+ if (explicit) return explicit
62
+
63
+ try {
64
+ const found = execSync('which claude', { encoding: 'utf8', timeout: 3000 }).trim()
65
+ if (found) return found
66
+ } catch {}
67
+
68
+ const defaults = [
69
+ join(homedir(), '.local', 'bin', 'claude'),
70
+ '/usr/local/bin/claude',
71
+ ]
72
+ for (const p of defaults) {
73
+ if (existsSync(p)) return p
74
+ }
75
+
76
+ return undefined
77
+ }
78
+
54
79
  /** Normalize model ID to Claude Code format (opus, sonnet, haiku). */
55
80
  function normalizeModelId(modelId: string): string {
56
81
  const lower = modelId.toLowerCase()
@@ -60,6 +85,21 @@ function normalizeModelId(modelId: string): string {
60
85
  return modelId
61
86
  }
62
87
 
88
+ /** loadAgentDefinition loads an OpenCode agent definition file and extracts the
89
+ * markdown body (stripping the YAML frontmatter) for use as a system prompt. */
90
+ function loadAgentDefinition(agent: string, workdir?: string): string | undefined {
91
+ if (!agent || !workdir) return undefined
92
+
93
+ const agentPath = join(workdir, '.opencode', 'agents', `${agent}.md`)
94
+ if (!existsSync(agentPath)) return undefined
95
+
96
+ const content = readFileSync(agentPath, 'utf8')
97
+
98
+ // Strip YAML frontmatter (--- delimited block at the start).
99
+ const stripped = content.replace(/^---\n[\s\S]*?\n---\n*/, '')
100
+ return stripped.trim() || undefined
101
+ }
102
+
63
103
  /** Extract text from assistant messages. */
64
104
  function getAssistantText(msg: SDKMessage): string | null {
65
105
  if (msg.type !== 'assistant') return null
@@ -102,9 +142,11 @@ export async function runClaudeCodeAgent(
102
142
  ): Promise<RunAgentResult> {
103
143
  const {
104
144
  prompt,
145
+ agent,
105
146
  model,
106
147
  sessionId,
107
148
  timeoutSec = 600,
149
+ workdir,
108
150
  claudeCode,
109
151
  signal,
110
152
  } = options
@@ -121,9 +163,20 @@ export async function runClaudeCodeAgent(
121
163
 
122
164
  // Build session options with configurable permission mode (default: acceptEdits)
123
165
  const permissionMode = claudeCode?.permissionMode ?? 'acceptEdits'
166
+
167
+ // Resolve system prompt: explicit option > agent definition file > none
168
+ const systemPrompt = claudeCode?.systemPrompt ?? loadAgentDefinition(agent, workdir)
169
+
124
170
  const sessionOptions: SDKSessionOptions = {
125
171
  model: modelStr,
126
172
  permissionMode,
173
+ ...(workdir && { cwd: workdir }),
174
+ ...(systemPrompt && { systemPrompt }),
175
+ // Enable session persistence so close+resume works.
176
+ // The v2 SDK defaults persistSession to false, unlike v1 which defaults to true.
177
+ // Without this, session.close() destroys the session and resumeSession() fails
178
+ // with "No conversation found with session ID".
179
+ ...({ persistSession: true }),
127
180
  hooks: {
128
181
  PreToolUse: [{ hooks: [logToolCall] }],
129
182
  },
@@ -132,10 +185,11 @@ export async function runClaudeCodeAgent(
132
185
  claudeCode?.dangerouslySkipPermissions && {
133
186
  allowDangerouslySkipPermissions: true,
134
187
  }),
135
- // Pass through custom executable path if provided
136
- ...(claudeCode?.pathToClaudeCodeExecutable && {
137
- pathToClaudeCodeExecutable: claudeCode.pathToClaudeCodeExecutable,
138
- }),
188
+ // Resolve executable path: explicit option > PATH lookup > default locations
189
+ ...(() => {
190
+ const resolved = resolveClaudeCodePath(claudeCode?.pathToClaudeCodeExecutable)
191
+ return resolved ? { pathToClaudeCodeExecutable: resolved } : {}
192
+ })(),
139
193
  }
140
194
 
141
195
  console.error(
package/src/types.ts CHANGED
@@ -26,6 +26,11 @@ export interface ClaudeCodeOptions {
26
26
  dangerouslySkipPermissions?: boolean
27
27
  /** Path to Claude Code executable (default: auto-detected). */
28
28
  pathToClaudeCodeExecutable?: string
29
+ /**
30
+ * System prompt for the Claude Code session.
31
+ * Can be a full string or use the preset format with append.
32
+ */
33
+ systemPrompt?: string | { type: 'preset'; preset: 'claude_code'; append: string }
29
34
  }
30
35
 
31
36
  /** Model configuration for LLM backends. */