spacehub

surshar / claude-code

Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.

Built by Anthropic · github.com/anthropics

claude-code / mods / agents-md / tests / register.test.ts
210 lines · 5.6 KBRaw
1import { describe, expect, mock, test, tier } from 'claude-code/testing'
2
3import Hooks from '../hooks'
4import Fixtures from './fixtures'
5
6tier('builtin')
7
8/**
9 * The blocks the engine hands `prompt.context`.
10 */
11const BLOCKS = [
12 { name: 'claudeMd', text: 'x' },
13 { name: 'currentDate', text: 'today' },
14]
15
16describe('register', () => {
17 test('by default a project with AGENTS.md alone gets it', async ($, on) => {
18 const started = Fixtures.projectOf(
19 on,
20 [Fixtures.ancestorOf('/repo', 'AGENTS.md', '# top\n')],
21 [],
22 )
23
24 on('prompt.context', ($, e) => ({
25 blocks: e.blocks,
26 instructionFiles: e.instructionFiles,
27 }))
28
29 const context = await $.prompt.context({
30 blocks: BLOCKS,
31 instructionFiles: [],
32 })
33
34 expect(context.instructionFiles).toEqual([
35 { path: '/repo/AGENTS.md', kind: 'project', content: '# top\n' },
36 ])
37 expect(context.blocks?.[0]?.text).toContain(
38 'Contents of /repo/AGENTS.md (project instructions, checked into the ' +
39 'codebase):\n\n# top',
40 )
41
42 await started.clock.settle()
43
44 expect(started.lines).toEqual([
45 'no CLAUDE.md found; AGENTS.md loaded: /repo/AGENTS.md',
46 ])
47 })
48
49 test('by default a project with a CLAUDE.md is left to the engine', async ($, on) => {
50 const started = Fixtures.projectOf(
51 on,
52 [Fixtures.ancestorOf('/repo', 'AGENTS.md', '# top\n')],
53 [Fixtures.ancestorOf('/repo/a/b', 'CLAUDE.md', '# mine\n')],
54 )
55 const handed = [
56 { path: '/repo/a/b/CLAUDE.md', kind: 'project' as const, content: '#' },
57 ]
58
59 on('prompt.context', ($, e) => ({
60 blocks: e.blocks,
61 instructionFiles: e.instructionFiles,
62 }))
63
64 expect(
65 await $.prompt.context({ blocks: BLOCKS, instructionFiles: handed }),
66 ).toEqual({ blocks: BLOCKS, instructionFiles: handed })
67
68 await started.clock.settle()
69
70 expect(started.walks).toEqual([])
71 expect(started.lines).toEqual([])
72 })
73
74 test('a failed walk leaves the context as handed', async ($, on) => {
75 Fixtures.unreadableProjectOf(on)
76
77 on('prompt.context', ($, e) => ({
78 blocks: e.blocks,
79 instructionFiles: e.instructionFiles,
80 }))
81
82 expect(
83 await $.prompt.context({ blocks: BLOCKS, instructionFiles: [] }),
84 ).toEqual({ blocks: BLOCKS, instructionFiles: [] })
85 })
86
87 for (const [name, args, file, attaches] of [
88 [
89 'an auto-paginated read',
90 {},
91 { content: '# head\nfollow', truncatedByTokenCap: true },
92 true,
93 ],
94 ['an incomplete line range', {}, { content: '# head', numLines: 1 }, true],
95 ['a complete read', {}, {}, false],
96 [
97 'an explicit limit',
98 { limit: 1 },
99 { content: '# head', numLines: 1 },
100 true,
101 ],
102 [
103 'an explicit offset',
104 { offset: 2 },
105 { content: 'follow the tail rule', numLines: 1, startLine: 2 },
106 true,
107 ],
108 ['an unstructured result', {}, undefined, false],
109 ] as const) {
110 test(`a nested AGENTS.md after ${name}`, async ($, on) => {
111 const dir = `${Fixtures.SESSION.cwd}/nested`
112 const path = `${dir}/AGENTS.md`
113 const content = '# head\nfollow the tail rule'
114 const started = Fixtures.projectOf(
115 on,
116 [Fixtures.ancestorOf(dir, 'AGENTS.md', content)],
117 [],
118 )
119
120 mock.env(on, {})
121 on('session.cwd', () => ({ value: Fixtures.SESSION.cwd }))
122 on('tool.call', ($, e) => ({
123 result:
124 e.tool === 'Read' && file !== undefined
125 ? {
126 type: 'text',
127 file: Object.assign(
128 {
129 filePath: e.file_path,
130 content,
131 numLines: 2,
132 startLine: 1,
133 totalLines: 2,
134 },
135 file,
136 ),
137 }
138 : 'read',
139 }))
140
141 const direct = await $.tool.call({
142 tool: 'Read',
143 file_path: path,
144 ...args,
145 })
146 const source = { tool: 'Read', file_path: `${dir}/source.ts` } as const
147 const after = await $.tool.call(source)
148 const repeated = await $.tool.call(source)
149
150 expect(direct.context).toBeUndefined()
151 expect(after.context).toEqual(
152 attaches ? [`Contents of ${path}:\n\n${content}`] : undefined,
153 )
154 expect(repeated.context).toBeUndefined()
155
156 await started.clock.settle()
157 })
158 }
159
160 test(
161 'the start sends the mode row alone: no walk, no toast',
162 { plugins: [Fixtures.RECORDING] },
163 async ($, on) => {
164 const started = Fixtures.projectOf(
165 on,
166 [Fixtures.ancestorOf('/repo', 'AGENTS.md', '# top\n')],
167 [],
168 )
169
170 expect(await $.session.start(Fixtures.SESSION)).toEqual({
171 cwd: '/repo/a/b',
172 })
173
174 await started.clock.settle()
175
176 expect(Fixtures.rowsOf(started)).toEqual([
177 [
178 'log',
179 {
180 event: Hooks.MODE_EVENT,
181 props: {
182 mode: { value: 'claude-md-or-agents-md', of: [...Hooks.MODES] },
183 is_interactive: true,
184 },
185 },
186 ],
187 ])
188 expect(Hooks.MODE_EVENT).toBe('agents_md_mode')
189 expect(started.walks).toEqual([])
190 expect(started.toasts).toEqual([])
191 },
192 )
193
194 test('with no telemetry noun the start goes on untouched', async ($, on) => {
195 const started = Fixtures.projectOf(
196 on,
197 [Fixtures.ancestorOf('/repo', 'AGENTS.md', '# top\n')],
198 [],
199 )
200
201 expect(
202 await $.session.start({ ...Fixtures.SESSION, isInteractive: false }),
203 ).toEqual({ cwd: '/repo/a/b' })
204
205 await started.clock.settle()
206
207 expect(Fixtures.rowsOf(started)).toEqual([])
208 expect(started.toasts).toEqual([])
209 })
210})