docs: document OpenCode 2 support, output scrubbing, and releasing
This commit is contained in:
@@ -1,36 +1,40 @@
|
||||
# AGENTS.md — envsitter-guard
|
||||
|
||||
This repository is a minimal TypeScript plugin meant to run under OpenCode (`@opencode-ai/plugin`).
|
||||
This repository is a minimal TypeScript plugin for OpenCode 1 (`@opencode-ai/plugin`, 1.18.29+) and OpenCode 2 (`@opencode/plugin`, 2.0.32+), served from one default export.
|
||||
|
||||
## Repo layout
|
||||
|
||||
- `index.ts`: main plugin implementation (`EnvSitterGuard`).
|
||||
- `tsconfig.json`: TypeScript config (strict, `noEmit`).
|
||||
- `index.ts`: package entry. Exports only `EnvSitterGuard` = `{ id, setup, server }` (also the default export).
|
||||
- `lib/tools.ts`: the 15 `envsitter_*` tools, defined once (zod args, JSON Schema for V2, `run(input, env)`).
|
||||
- `lib/policy.ts`: pure blocking logic (protected paths, patch paths, shell scan, grep redaction, permission denial).
|
||||
- `lib/scrub.ts`: finds the project's sensitive env files and scrubs secret values from tool output with envsitter's redactor.
|
||||
- `lib/v1.ts` / `lib/v2.ts`: thin adapters (V1 hooks object; V2 `ctx.tool.transform`/`hook`, `ctx.permission.hook`).
|
||||
- `lib/help.ts`: `envsitter_help` text.
|
||||
- `test/`: `*.hook.test.ts` (V1 hooks), `*.tools.test.ts` (tools via V1), `*.v2.test.ts` (V2 setup with a fake ctx), `policy.test.ts`, `scrub.test.ts`.
|
||||
- `scripts/e2e-opencode.mjs`: `npm run test:e2e`, real OpenCode 1 (`opencode`) and 2 (`opencode2`) CLIs vs a scripted model, isolated `HOME`.
|
||||
- `.github/workflows/`: `ci.yml` (unit tests + pinned OpenCode e2e), `e2e-latest.yml` (weekly e2e vs newest OpenCode), `release.yml` (tag `vX.Y.Z` -> CI -> npm trusted publishing -> GitHub Release). Keep the `release.yml` name: npm's trusted-publisher config pins it. `origin` is Gitea (`git.boxpositron.dev`), mirrored to GitHub; jobs carry `if: github.server_url == 'https://github.com'` so Gitea's runner skips them.
|
||||
- `tsconfig.json`: strict, `noEmit` (typecheck). `tsconfig.build.json` emits `dist/` (publish); `tsconfig.test.json` emits `dist-test/`.
|
||||
- `.opencode/`: OpenCode packaging/runtime wrapper.
|
||||
- `.opencode/plugin/envsitter-guard.ts` re-exports the plugin from `index.ts`.
|
||||
- `.opencode/bun.lock` indicates Bun-managed deps inside `.opencode/`.
|
||||
- `node_modules/`: vendored deps (present locally; do not edit).
|
||||
- `.opencode/plugin/envsitter-guard.ts` re-exports the plugin from `index.ts` (the only tracked file here).
|
||||
- `.opencode/package.json` and `.opencode/bun.lock` are local-only (ignored by `.opencode/.gitignore`).
|
||||
- `node_modules/`: npm-installed deps (do not edit); `envsitter` comes from npm, not the sibling checkout.
|
||||
|
||||
## Commands (build / lint / test)
|
||||
|
||||
### Install
|
||||
|
||||
- Root install (lockfile: `package-lock.json`):
|
||||
- `npm ci`
|
||||
- `npm install` (ok for local dev; `npm ci` preferred for reproducibility)
|
||||
- Root install (lockfile: `package-lock.json`): `npm ci` (preferred) or `npm install`
|
||||
|
||||
- `.opencode/` install (Bun lockfile present):
|
||||
- `.opencode/` install (local-only Bun lockfile, untracked):
|
||||
- If you need to refresh `.opencode/node_modules`, use Bun from `.opencode/`:
|
||||
- `cd .opencode && bun install`
|
||||
- If Bun is unavailable, do not guess; ask before changing `.opencode/` dependency management.
|
||||
|
||||
### Build / typecheck
|
||||
|
||||
This repo does not emit JS as part of its normal workflow.
|
||||
Day-to-day work only typechecks. `npm run build` (`tsconfig.build.json` -> `dist/`) produces the published package and runs on `prepack`; `prepublishOnly` runs typecheck, test, then build.
|
||||
|
||||
- Typecheck (canonical):
|
||||
- `npm run typecheck`
|
||||
- Equivalent: `npx tsc -p tsconfig.json`
|
||||
- Typecheck (canonical): `npm run typecheck` (= `npx tsc -p tsconfig.json`)
|
||||
|
||||
### Lint / format
|
||||
|
||||
@@ -42,12 +46,12 @@ This repo does not emit JS as part of its normal workflow.
|
||||
|
||||
Tests use Node’s built-in test runner, compiled via `tsc` into `dist-test/`.
|
||||
|
||||
- Run all tests (builds then runs):
|
||||
- `npm test`
|
||||
- Build test output only:
|
||||
- `npm run build:test`
|
||||
- Run all tests (builds then runs): `npm test`
|
||||
- Build test output only: `npm run build:test`
|
||||
- Cross-version e2e (needs the CLIs installed; not part of `npm test`): `npm run test:e2e`
|
||||
- Run a single test file:
|
||||
- `npm run build:test && node --test dist-test/test/envsitter-guard.hook.test.js`
|
||||
- `npm run build:test && node --test dist-test/test/envsitter-guard.v2.test.js`
|
||||
- Until `envsitter` 0.1.0 is published, tests need a local build: `npm pack` in `../envsitter`, then `npm install --no-save <tarball>` here.
|
||||
|
||||
Pre-commit checks are enforced via Husky:
|
||||
- `npm install` installs the git hook.
|
||||
@@ -66,13 +70,13 @@ Pre-commit checks are enforced via Husky:
|
||||
- Quotes: double quotes.
|
||||
- Semicolons: required.
|
||||
- Trailing commas: used in multiline objects/args.
|
||||
- Keep functions small and single-purpose (see path validation helpers in `index.ts`).
|
||||
- Keep functions small and single-purpose (see `lib/policy.ts`).
|
||||
|
||||
## Naming conventions
|
||||
|
||||
- `camelCase`: functions, locals, parameters.
|
||||
- `PascalCase`: exported plugin symbol (`EnvSitterGuard`).
|
||||
- Booleans use `is*` / `has*` prefixes (e.g. `isSensitiveDotEnvPath`).
|
||||
- Booleans use `is*` / `has*` prefixes (e.g. `isProtectedPath`).
|
||||
|
||||
## Error handling & validation
|
||||
|
||||
@@ -87,42 +91,38 @@ This plugin is security-sensitive. Follow these patterns:
|
||||
|
||||
- Do not read or print `.env` values.
|
||||
- Treat `.env.secure` as sensitive; never commit secrets.
|
||||
- This repo’s plugin is designed to block sensitive `.env*` and `.envsitter/pepper` access via tooling and to guide users toward safe inspection:
|
||||
- `envsitter_keys` (lists keys only)
|
||||
- `envsitter_fingerprint` (hash/fingerprint only)
|
||||
|
||||
## Working with `.opencode/`
|
||||
|
||||
- `.opencode/` is a packaging/runtime layer for OpenCode plugins; treat it as part of the deployment surface.
|
||||
- Keep `.opencode/plugin/envsitter-guard.ts` as a thin re-export (do not add logic there).
|
||||
- Avoid editing `.opencode/node_modules` directly.
|
||||
- The plugin blocks protected env files and the pepper, and points agents to EnvSitter tools (keys, fingerprints, booleans; never values).
|
||||
|
||||
## Change discipline
|
||||
|
||||
- Prefer minimal diffs; avoid refactors during bugfixes.
|
||||
- Keep behavior consistent with `index.ts` patterns:
|
||||
- normalize paths before regex matching
|
||||
- ensure file paths stay within `worktree`
|
||||
- throttle UI toasts (`lastToastAt` pattern)
|
||||
- Keep behavior consistent with `lib/` patterns:
|
||||
- normalize paths before matching; decide "protected" only via envsitter's `isSensitiveEnvFile` / `isEnvSitterPepperFile`
|
||||
- ensure file paths stay within the project root (V1 `worktree`, V2 `ctx.location.project.directory`)
|
||||
- block silently: throw an actionable `Error`, never show a toast (hook test asserts zero toasts)
|
||||
- Never add non-plugin exports to `index.ts`: OpenCode 1 throws when a plugin module exports anything else.
|
||||
- Add or change a tool once in `lib/tools.ts`; both adapters pick it up. Update `lib/help.ts` with it.
|
||||
|
||||
## Plugin behavior notes
|
||||
|
||||
- OpenCode entrypoint is `EnvSitterGuard` exported from `index.ts`.
|
||||
- Plugin registration file `.opencode/plugin/envsitter-guard.ts` must remain a thin re-export.
|
||||
- Tool surface area intentionally small:
|
||||
- `envsitter_keys`: lists keys only (no values)
|
||||
- `envsitter_fingerprint`: deterministic fingerprint of a single key (no value)
|
||||
- Blocking behavior is enforced in `"tool.execute.before"`:
|
||||
- Blocks reads of `.env*` (except `.env.example`).
|
||||
- Blocks edits/writes/patching of `.env*` and `.envsitter/pepper`.
|
||||
- Throttles UI toasts via `lastToastAt`.
|
||||
- Tool surface (15 tools; every file-taking tool resolves paths via `resolveEnvFilePath` in `lib/tools.ts`):
|
||||
- Read-only: `envsitter_keys`, `envsitter_fingerprint`, `envsitter_match`, `envsitter_match_by_key`, `envsitter_scan`, `envsitter_validate`.
|
||||
- Mutating, dry-run unless `write: true`: `envsitter_copy`, `envsitter_format`, `envsitter_reorder`, `envsitter_annotate`, `envsitter_add`, `envsitter_set`, `envsitter_unset`, `envsitter_delete`.
|
||||
- `envsitter_help`: agent-facing guide; update its topic text whenever a tool changes.
|
||||
- V2 registers them with `codemode: false` so agents call them by name.
|
||||
- Blocking (`findBlockedAccess`, used by V1 `tool.execute.before` and V2 `execute.before`):
|
||||
- Any non-`envsitter_*` tool whose `filePath` / `path` / `file_path` / `paths` args hit a protected file.
|
||||
- `patchText` envelopes (`patch`, V1.19 `apply_patch`) and unified diffs touching protected files.
|
||||
- `command` strings naming protected files (best-effort shell scan; envsitter CLI segments allowed).
|
||||
- `grep` output is redacted after execution and every non-envsitter tool's output is scrubbed of secret values (`lib/scrub.ts`); V2 also denies protected `read`/`edit`/`shell` permission checks.
|
||||
- Protected = envsitter `isSensitiveEnvFile` (examples/templates stay readable) or `isEnvSitterPepperFile`.
|
||||
|
||||
## Path handling rules
|
||||
|
||||
- Treat all incoming tool args as untrusted.
|
||||
- Normalize path separators before matching (Windows `\\` → `/`).
|
||||
- Validate allowed paths before resolving to absolute paths.
|
||||
- Ensure resolved paths stay within `worktree` using `path.relative()` checks.
|
||||
- Ensure resolved paths stay within the project root using `path.relative()` checks.
|
||||
|
||||
## Output conventions
|
||||
|
||||
@@ -143,11 +143,7 @@ This plugin is security-sensitive. Follow these patterns:
|
||||
|
||||
## Verification checklist
|
||||
|
||||
- `npm run typecheck` passes.
|
||||
- `npm run typecheck` and `npm test` pass (V1 hook/tool tests and V2 tests).
|
||||
- No tool path allows reading raw `.env*` values.
|
||||
- `.opencode/plugin/envsitter-guard.ts` remains a re-export only.
|
||||
- Error messages remain clear and do not leak file contents.
|
||||
|
||||
## Cursor / Copilot rules
|
||||
|
||||
- No `.cursor/rules/`, `.cursorrules`, or `.github/copilot-instructions.md` were found in this repository.
|
||||
|
||||
Reference in New Issue
Block a user