You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CLAUDE.md
+43-1Lines changed: 43 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -198,10 +198,11 @@ Zod is preferred because types are automatically inferred from the schema, preve
198
198
- Uses `PluginTester.fullTest()` from `@codifycli/plugin-test`
199
199
- Tests create → modify → destroy flow
200
200
- 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.
**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
**Never use `sudo` inside `$.spawn` or `$.spawnSafe`.** Use `{ requiresRoot: true }` in the options instead. The framework handles privilege escalation through the parent process.
251
262
252
263
```typescript
@@ -412,6 +423,37 @@ parameterSettings: {
412
423
}
413
424
```
414
425
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.
0 commit comments