Files
bot-agents/AGENTS.md
T
Danny Kim 32806120b3 Group the sources into aliased modules and lint with the published plugins (#23)
Groups the flat `src/` into cohesive modules and turns on the published lint plugins. No behavior change.

## Module layout

Each module is a direct `src/` subfolder with an `index.ts` barrel and a `#` alias in `package.json` `imports` (layer order, top first):

| Alias | Files |
| --- | --- |
| `#prepare` | `index.ts` (the prepare phase entry point) |
| `#run` | `index.ts` (the run phase entry point) |
| `#post` | `index.ts` (the post phase entry point), `post.ts`, `post.test.ts` |
| `#agents` | `auth.ts`, `opencode.ts` (Codex / Claude Code sign-in, OpenCode args and output) |
| `#processes` | `child.ts`, `isolation.ts` |
| `#runtime` | `runtime.ts`, `credentials.ts` |
| `#profiles` | `profiles.ts` |
| `#common` | `errors.ts`, `json.ts` |
| `#testing` | `index.ts` (unchanged) |

Every top-level entry of `src/` is an aliased module folder. Tests stay next to their code as `*.test.ts`. Each phase's `index.ts` is its entry point, and `action.yml` now runs `node src/<phase>/index.ts`. The post phase's logic moved to `src/post/post.ts` so its test can import it; `index.ts` keeps the env reading and the `import.meta.main` call. `run`'s prompt URL is now `../../prompt.md`. `scripts/protect-source.sh` now copies `src/` recursively (it used to copy `src/*.ts` only), and the workflow test checks that module files get staged too.

## Lint plugins

- New devDependencies: `eslint-plugin-explicit-types@^0.6.1` and `eslint-plugin-import-structure@^0.10.2`, both in `jsPlugins`. The lockfile comes from a real `npm install`.
- Enabled configs: `importStructure.configs.modules`, `explicitTypes.configs.concrete`, `explicitTypes.configs.absence`.
- **explicit-types `naming` is off** (the other app repos turn it on). Its `upper-snake-string-unions` and `no-upper-snake-object-keys` rules fire on wire tokens this repo doesn't choose: profile types `"claude" | "codex" | "opencode"` from `profiles.json`, failure reasons posted to Gitea, Gitea's `"new" | "old"` side, and environment variable names such as `PATH`. Satisfying them would change behavior or need opt-outs. This is logged as an open question for the user.

Lint fixes, all type-level:
- `Pick<BotProfile, "type" | "model">` became a named `ProfileIdentity` interface that `BotProfile`, `ProfileFailure`, and `RuntimeProfile` extend. `RuntimeOutcome` is now `RuntimeSuccess | RuntimeFailure`.
- Inline object types became named interfaces.
- `Record<string, string>` env maps became `NodeJS.ProcessEnv`. `JsonObject` and the verdict table became `NodeJS.Dict<…>`.
- `T | undefined` locals and returns became `T | null` with matching checks. `typeof fetch` became a `Fetcher` type.
- Claude's result fields are held in camelCase (`isError`, `apiErrorStatus`) after parsing, because `id-match` rejects snake_case interface members.
- Duplicate import sources are merged (`import-structure/no-duplicate-import-source`).

## Verification

- `vp check`: clean.
- `vp test`: 61 passed and 5 skipped as a normal user. All 66 passed as root in `node:24-bookworm`.
- Each phase (`node src/<phase>/index.ts`) run from a `protect-source.sh` copy loads its full alias graph under Node.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: #23
Reviewed-by: bot <noreply@capsulizers.com>
Co-authored-by: Danny Kim <temeddix@gmail.com>
Co-committed-by: Danny Kim <temeddix@gmail.com>
2026-10-18 15:00:16 +00:00

3.5 KiB

At the start of every new session, run damem recall in the terminal and treat its output as the context for this repository. If the damem command is not found, read INSTALL.md and follow it before reasoning or responding.

Pull requests

Land every change through a pull request. Immediately after creating one, comment @bot to request an AI-bot review. Wait for green CI and the bot's approval on the current head before merging. Never merge without the user's explicit permission.

Runtime and checks

The action's TypeScript runs directly on the job image's Node.js (24.2 or newer; package.json has no engines, the node:24-bookworm image pins it), which strips the types itself: there is no build step and no runtime dependency. Keep the sources to erasable syntax and .ts imports, which tsconfig.json enforces. Its strictness set is fixed and leaves exactOptionalPropertyTypes off (.agents/memory/ts-only-files-and-imports.md). Checks use Vite+: vp check formats, lints, and type-checks, and vp test runs Vitest. Unit tests sit next to the code as *.test.ts in src/; repository-level tests live in tests/.

A # alias in package.json imports may point anywhere and owns its target: an alias to a folder's index.ts owns that folder and everything below it; an alias to any other file owns only that file. No overlap: no file may be owned by 2 aliases, so no alias inside another alias's area and no 2 aliases with the same target. Outside an area, only the exact alias reaches it; a deeper alias specifier or a relative path into the area is a bypass. Inside an area, relative imports of siblings and subfolders are free. A ../ import is banned in every file, owned or not (import/no-relative-parent-imports); runtime file URLs such as new URL("../prompt.md", import.meta.url) are paths, not imports, and stay. Files no alias owns need no index.ts and no alias, and import ./ siblings and subfolders freely. Every top-level entry of src/ is an aliased module folder. The aliases, in layer order, are the three phases #prepare, #run, and #post, then #agents (Codex, Claude Code and OpenCode specifics), #processes (child processes and the isolated profile users), #runtime (the prepared runtime and its credentials), #profiles, #common (error and JSON checks), and #testing (test helpers), each with a src/<name>/index.ts barrel. A phase's index.ts is also its entry point, which action.yml runs as node src/<phase>/index.ts. Tests that use the test helpers import #testing, and tests/ imports src/ only through it. Local convention, stricter than the general rule above: in this repo, aliases point only at a direct subfolder of src/ ("#helpers": "./src/helpers/index.ts"), and code outside src/ uses ./ relative imports. scripts/ is flat: one file per tool, no subfolders and no index.ts, imported directly by file path (.agents/memory/imports-aliases-barrels.md).

The only config files are package.json, tsconfig.json, and vite.config.ts, plus package-lock.json. Repo-local lint rules and the rule list live in scripts/project-lint.ts. Lint disable comments are not allowed; fix the source instead.

The composite action uses npm only to install the externally distributed Claude Code, Codex, and OpenCode command-line tools; do not treat those vendor executables as repository TypeScript dependencies.