Skip to content

Commit fbba812

Browse files
committed
fix(agent): keep legacy agent files read-only
1 parent 6cda068 commit fbba812

2 files changed

Lines changed: 97 additions & 8 deletions

File tree

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { afterEach, beforeEach, describe, expect, test } from 'bun:test'
2+
import {
3+
existsSync,
4+
mkdirSync,
5+
mkdtempSync,
6+
readFileSync,
7+
rmSync,
8+
writeFileSync,
9+
} from 'fs'
10+
import { join } from 'path'
11+
import { tmpdir } from 'os'
12+
13+
import type { AgentConfig } from '#core/utils/agentLoader'
14+
import { getCwd, setCwd } from '#core/utils/state'
15+
16+
import { deleteAgent, updateAgent } from './storage'
17+
18+
describe('agents/storage Kode-first writes', () => {
19+
let originalCwd: string
20+
let projectDir: string
21+
22+
beforeEach(async () => {
23+
originalCwd = getCwd()
24+
projectDir = mkdtempSync(join(tmpdir(), 'kode-agent-storage-'))
25+
await setCwd(projectDir)
26+
})
27+
28+
afterEach(async () => {
29+
await setCwd(originalCwd)
30+
rmSync(projectDir, { recursive: true, force: true })
31+
})
32+
33+
function projectAgent(agentType: string): AgentConfig {
34+
return {
35+
agentType,
36+
whenToUse: 'Use this agent when testing storage behavior.',
37+
tools: '*',
38+
systemPrompt: 'You are a storage behavior test agent.',
39+
source: 'projectSettings',
40+
location: 'project',
41+
}
42+
}
43+
44+
test('updateAgent writes a primary .kode override instead of modifying legacy .claude', async () => {
45+
const legacyDir = join(projectDir, '.claude', 'agents')
46+
const legacyPath = join(legacyDir, 'legacy-agent.md')
47+
const primaryPath = join(projectDir, '.kode', 'agents', 'legacy-agent.md')
48+
mkdirSync(legacyDir, { recursive: true })
49+
writeFileSync(
50+
legacyPath,
51+
'---\nname: legacy-agent\ndescription: "legacy"\n---\n\nLegacy prompt\n',
52+
)
53+
54+
await updateAgent(
55+
projectAgent('legacy-agent'),
56+
'Use this agent when editing legacy agents.',
57+
['Read'],
58+
'You are an updated primary agent prompt.',
59+
)
60+
61+
expect(existsSync(primaryPath)).toBe(true)
62+
expect(readFileSync(primaryPath, 'utf8')).toContain('\ntools: Read')
63+
expect(readFileSync(legacyPath, 'utf8')).toContain('Legacy prompt')
64+
})
65+
66+
test('deleteAgent removes only primary files and rejects legacy-only deletes', async () => {
67+
const primaryDir = join(projectDir, '.kode', 'agents')
68+
const legacyDir = join(projectDir, '.claude', 'agents')
69+
const primaryPath = join(primaryDir, 'demo-agent.md')
70+
const legacyPath = join(legacyDir, 'demo-agent.md')
71+
mkdirSync(primaryDir, { recursive: true })
72+
mkdirSync(legacyDir, { recursive: true })
73+
writeFileSync(
74+
primaryPath,
75+
'---\nname: demo-agent\ndescription: "primary"\n---\n\nPrimary prompt\n',
76+
)
77+
writeFileSync(
78+
legacyPath,
79+
'---\nname: demo-agent\ndescription: "legacy"\n---\n\nLegacy prompt\n',
80+
)
81+
82+
await deleteAgent(projectAgent('demo-agent'))
83+
expect(existsSync(primaryPath)).toBe(false)
84+
expect(existsSync(legacyPath)).toBe(true)
85+
86+
await expect(deleteAgent(projectAgent('demo-agent'))).rejects.toThrow(
87+
'Cannot delete legacy agent "demo-agent" from .claude',
88+
)
89+
expect(existsSync(legacyPath)).toBe(true)
90+
})
91+
})

apps/cli/src/commands/agent/agents/storage.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -160,15 +160,9 @@ export async function updateAgent(
160160

161161
const location = agent.location as AgentLocation
162162
const primaryPath = getPrimaryAgentFilePath(location, agent.agentType)
163-
const legacyPath = getLegacyAgentFilePath(location, agent.agentType)
164-
const filePath = existsSync(primaryPath)
165-
? primaryPath
166-
: existsSync(legacyPath)
167-
? legacyPath
168-
: primaryPath
169163

170164
ensureDirectoryExists(location)
171-
writeFileSync(filePath, content, { encoding: 'utf-8', flag: 'w' })
165+
writeFileSync(primaryPath, content, { encoding: 'utf-8', flag: 'w' })
172166
}
173167

174168
export async function deleteAgent(agent: AgentConfig): Promise<void> {
@@ -182,8 +176,12 @@ export async function deleteAgent(agent: AgentConfig): Promise<void> {
182176

183177
if (existsSync(primaryPath)) {
184178
unlinkSync(primaryPath)
179+
return
185180
}
181+
186182
if (existsSync(legacyPath)) {
187-
unlinkSync(legacyPath)
183+
throw new Error(
184+
`Cannot delete legacy agent "${agent.agentType}" from .claude. Legacy agents are read-only; remove it manually or create a .kode override.`,
185+
)
188186
}
189187
}

0 commit comments

Comments
 (0)