From 08d6c3876983aebbda203b2d64057f7fa6724311 Mon Sep 17 00:00:00 2001 From: mikemikimike <13286568797@163.com> Date: Wed, 19 Aug 2026 19:54:51 +0800 Subject: [PATCH 1/3] feat(claude-code): expose settings and loaded skills --- .changeset/pink-skills-dance.md | 5 +++++ packages/ai-claude-code/src/adapters/text.ts | 7 ++++++- packages/ai-claude-code/src/stream/sdk-types.ts | 1 + packages/ai-claude-code/src/stream/translate.ts | 1 + packages/ai-claude-code/tests/text-adapter.test.ts | 3 ++- packages/ai-claude-code/tests/translate.test.ts | 13 +++++++++++++ 6 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 .changeset/pink-skills-dance.md diff --git a/.changeset/pink-skills-dance.md b/.changeset/pink-skills-dance.md new file mode 100644 index 000000000..d47546721 --- /dev/null +++ b/.changeset/pink-skills-dance.md @@ -0,0 +1,5 @@ +--- +'@tanstack/ai-claude-code': minor +--- + +Expose Claude Code setting sources and loaded skills in session metadata. diff --git a/packages/ai-claude-code/src/adapters/text.ts b/packages/ai-claude-code/src/adapters/text.ts index 3773c26ff..6e1154fae 100644 --- a/packages/ai-claude-code/src/adapters/text.ts +++ b/packages/ai-claude-code/src/adapters/text.ts @@ -62,6 +62,8 @@ export type ClaudeCodePermissionMode = | 'bypassPermissions' | 'plan' +export type ClaudeCodeSettingSource = 'user' | 'project' | 'local' + const DEFAULT_WORKDIR = '/workspace' export interface ClaudeCodeTextConfig { @@ -85,6 +87,8 @@ export interface ClaudeCodeTextConfig { addDirs?: Array /** Maximum harness-internal turns (`--max-turns`). */ maxTurns?: number + /** Claude Code filesystem settings loaded via `--setting-sources`. Defaults to `['user']`. */ + settingSources?: Array /** * How `systemPrompts` from `chat()` are applied: * - `'append'` (default): `--append-system-prompt` on top of the preset. @@ -208,6 +212,7 @@ export class ClaudeCodeTextAdapter< const config = this.adapterConfig const modelOptions = options.modelOptions const exeParts = (config.claudeExecutable ?? 'claude').split(' ') + const settingSources = config.settingSources ?? ['user'] // `--setting-sources user` before `-p`. Do not pass `--bare`: that flag // skips stored `claude login` credentials and prints @@ -216,7 +221,7 @@ export class ClaudeCodeTextAdapter< const args: Array = [ ...exeParts, '--setting-sources', - 'user', + settingSources.join(','), '-p', '--output-format', 'stream-json', diff --git a/packages/ai-claude-code/src/stream/sdk-types.ts b/packages/ai-claude-code/src/stream/sdk-types.ts index ab1ffcfc5..9ad3b11e2 100644 --- a/packages/ai-claude-code/src/stream/sdk-types.ts +++ b/packages/ai-claude-code/src/stream/sdk-types.ts @@ -14,6 +14,7 @@ export interface SdkInitMessage { session_id: string model: string tools: Array + skills?: Array cwd?: string } diff --git a/packages/ai-claude-code/src/stream/translate.ts b/packages/ai-claude-code/src/stream/translate.ts index 072cc90a8..7cfa20bc5 100644 --- a/packages/ai-claude-code/src/stream/translate.ts +++ b/packages/ai-claude-code/src/stream/translate.ts @@ -576,6 +576,7 @@ export async function* translateSdkStream( sessionId: sdkMessage.session_id, model: sdkMessage.model, tools: sdkMessage.tools, + skills: sdkMessage.skills ?? [], }, } continue diff --git a/packages/ai-claude-code/tests/text-adapter.test.ts b/packages/ai-claude-code/tests/text-adapter.test.ts index a7b300bde..587bb12c1 100644 --- a/packages/ai-claude-code/tests/text-adapter.test.ts +++ b/packages/ai-claude-code/tests/text-adapter.test.ts @@ -181,6 +181,7 @@ describe('claude-code in-sandbox adapter', () => { claudeExecutable: 'node fake-claude.mjs', streamPartials: false, emitDiff: false, + settingSources: ['project', 'local'], }) const chunks = await collect( @@ -200,7 +201,7 @@ describe('claude-code in-sandbox adapter', () => { const argv = await sbx.fs.read('/workspace/argv.txt') expect(argv).not.toContain('--bare') expect(argv).toContain('--setting-sources') - expect(argv).toContain('user') + expect(argv).toContain('project,local') expect(argv).toContain('--json-schema') expect(argv).toContain('"type":"object"') expect(argv).toContain('"summary"') diff --git a/packages/ai-claude-code/tests/translate.test.ts b/packages/ai-claude-code/tests/translate.test.ts index 5c6fe1a76..b28ed7db0 100644 --- a/packages/ai-claude-code/tests/translate.test.ts +++ b/packages/ai-claude-code/tests/translate.test.ts @@ -40,6 +40,7 @@ const init: AgentSdkMessage = { session_id: 'sess-abc', model: 'claude-opus-4-6', tools: ['Bash', 'Read'], + skills: ['repo-search'], cwd: '/tmp', } @@ -106,10 +107,22 @@ describe('translateSdkStream', () => { sessionId: 'sess-abc', model: 'claude-opus-4-6', tools: ['Bash', 'Read'], + skills: ['repo-search'], }, }) }) + it('uses an empty skill list when the SDK init message omits skills', async () => { + const { skills: _skills, ...initWithoutSkills } = init + const chunks = await collect([ + initWithoutSkills, + assistantText('hi'), + resultSuccess, + ]) + const custom = chunks.find((c) => c.type === 'CUSTOM') + expect(custom).toMatchObject({ value: { skills: [] } }) + }) + it('maps usage onto RUN_FINISHED including cache token details', async () => { const chunks = await collect([init, assistantText('hi'), resultSuccess]) const finished = chunks.find((c) => c.type === 'RUN_FINISHED') From 1fb897fbc48fa008fcd9a35990d38d26ff0df4f7 Mon Sep 17 00:00:00 2001 From: mikemikimike <13286568797@163.com> Date: Wed, 19 Aug 2026 20:11:57 +0800 Subject: [PATCH 2/3] test(claude-code): cover default setting source --- .../ai-claude-code/tests/text-adapter.test.ts | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/packages/ai-claude-code/tests/text-adapter.test.ts b/packages/ai-claude-code/tests/text-adapter.test.ts index 587bb12c1..36af915a3 100644 --- a/packages/ai-claude-code/tests/text-adapter.test.ts +++ b/packages/ai-claude-code/tests/text-adapter.test.ts @@ -221,6 +221,44 @@ describe('claude-code in-sandbox adapter', () => { await sbx.destroy() }) + it('uses user as the default setting source', async () => { + const fake = [ + `import { writeFileSync } from 'node:fs'`, + `writeFileSync('argv.txt', process.argv.slice(2).join(' '))`, + `let input = ''`, + `process.stdin.on('data', (d) => { input += d })`, + `process.stdin.on('end', () => {`, + ` const w = (o) => process.stdout.write(JSON.stringify(o) + '\\n')`, + ` w({ type: 'system', subtype: 'init', session_id: 'sess-default', model: 'haiku', tools: [] })`, + ` w({ type: 'result', subtype: 'success', result: 'ok', usage: { input_tokens: 1, output_tokens: 1 } })`, + `})`, + ].join('\n') + const sbx = await provider.create({}) + await sbx.fs.write('/workspace/fake-claude.mjs', fake) + + try { + const adapter = claudeCodeText('haiku', { + claudeExecutable: 'node fake-claude.mjs', + streamPartials: false, + emitDiff: false, + }) + await collect( + adapter.chatStream({ + model: 'haiku', + messages: [{ role: 'user', content: 'hi' }], + logger: noopLogger, + capabilities: capabilityContextWith(sbx), + }), + ) + + expect(await sbx.fs.read('/workspace/argv.txt')).toMatch( + /--setting-sources user/, + ) + } finally { + await sbx.destroy() + } + }) + it('copies ANTHROPIC_API_KEY from the host process into the CLI env', async () => { const fake = [ `import { writeFileSync } from 'node:fs'`, From 437ff527587206868eb1a389652a4b55b9aa3db8 Mon Sep 17 00:00:00 2001 From: mikemikimike <13286568797@163.com> Date: Wed, 19 Aug 2026 20:26:16 +0800 Subject: [PATCH 3/3] test(claude-code): tighten default source assertion --- packages/ai-claude-code/tests/text-adapter.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ai-claude-code/tests/text-adapter.test.ts b/packages/ai-claude-code/tests/text-adapter.test.ts index 36af915a3..1f60b294b 100644 --- a/packages/ai-claude-code/tests/text-adapter.test.ts +++ b/packages/ai-claude-code/tests/text-adapter.test.ts @@ -252,7 +252,7 @@ describe('claude-code in-sandbox adapter', () => { ) expect(await sbx.fs.read('/workspace/argv.txt')).toMatch( - /--setting-sources user/, + /--setting-sources user(?:\s|$)/, ) } finally { await sbx.destroy()