Skip to content

Commit 133b69b

Browse files
committed
feat: Improve CLAUADE.md
1 parent eff4b4c commit 133b69b

1 file changed

Lines changed: 43 additions & 1 deletion

File tree

CLAUDE.md

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,10 +198,11 @@ Zod is preferred because types are automatically inferred from the schema, preve
198198
- Uses `PluginTester.fullTest()` from `@codifycli/plugin-test`
199199
- Tests create → modify → destroy flow
200200
- Includes validation callbacks
201+
- **Always use `testSpawn` from `@codifycli/plugin-test` for shell commands in validation callbacks.** `testSpawn` sources the user's shell RC (`.zshrc`, `.bashrc`) before running the command, so PATH and shell aliases are available — just like a real terminal session. Never use `execSync` in integration tests.
201202

202203
**Integration Test Pattern:**
203204
```typescript
204-
import { PluginTester } from '@codifycli/plugin-test'
205+
import { PluginTester, testSpawn } from '@codifycli/plugin-test'
205206

206207
await PluginTester.fullTest(pluginPath, [
207208
{ type: 'alias', alias: 'my-alias', value: 'ls -l' }
@@ -247,6 +248,16 @@ const { data } = await $.spawn('command', {
247248
})
248249
```
249250

251+
**Shell RC sourcing differs by lifecycle method.** During `refresh`, the framework uses a `BackgroundPty` that automatically sources the user's shell RC, so PATH and shell functions are available without any extra options. During all other lifecycle methods (`create`, `modify`, `destroy`), the RC is **not** sourced automatically — pass `{ interactive: true }` when the command needs PATH entries or shell aliases that come from the RC file (e.g. a tool that was just installed by adding itself to `.zshrc`).
252+
253+
```typescript
254+
// refresh — shell RC sourced automatically, no option needed
255+
const result = await $.spawnSafe('my-tool --version')
256+
257+
// create/modify/destroy — must opt in to get sourced shell
258+
await $.spawn('my-tool configure', { interactive: true })
259+
```
260+
250261
**Never use `sudo` inside `$.spawn` or `$.spawnSafe`.** Use `{ requiresRoot: true }` in the options instead. The framework handles privilege escalation through the parent process.
251262

252263
```typescript
@@ -412,6 +423,37 @@ parameterSettings: {
412423
}
413424
```
414425

426+
### Stateful Parameters for State-Bearing Parameters
427+
428+
If a parameter has its own independent state on the system (e.g. a list of installed packages, a JSON settings file, a set of config keys), implement it as a `StatefulParameter` rather than handling it inline in `create`/`modify`/`destroy`. This keeps the main resource class clean and gives the framework full visibility into the parameter's lifecycle.
429+
430+
**Rule of thumb:** if you find yourself reading current state, diffing, and writing back inside `modify()` on the resource, it should be a `StatefulParameter` instead.
431+
432+
```typescript
433+
export class MyParameter extends StatefulParameter<MyConfig, ValueType> {
434+
getSettings(): ParameterSetting { ... }
435+
async refresh(desired, config): Promise<ValueType | null> { /* read current state */ }
436+
async add(value, plan): Promise<void> { /* apply from scratch */ }
437+
async modify(newValue, previousValue, plan): Promise<void> { /* diff and update */ }
438+
async remove(value, plan): Promise<void> { /* clean up */ }
439+
}
440+
```
441+
442+
```typescript
443+
// Wrong — inline state management clutters the resource
444+
async modify(pc, plan) {
445+
if (pc.name === 'settings') {
446+
const current = JSON.parse(await fs.readFile(settingsPath, 'utf8'));
447+
await fs.writeFile(settingsPath, JSON.stringify({ ...current, ...pc.newValue }));
448+
}
449+
}
450+
451+
// Correct — delegate to a StatefulParameter
452+
parameterSettings: {
453+
settings: { type: 'stateful', definition: new SettingsParameter(), order: 1 }
454+
}
455+
```
456+
415457
### defaultConfig and exampleConfigs
416458

417459
Every resource should have a `defaultConfig` and `exampleConfigs`. These are surfaced in the Codify Editor to help users get started quickly.

0 commit comments

Comments
 (0)