Notes for anyone (human or agent) making changes to the @insforge/cli repo.
Read this before touching source files.
The codebase has well-established conventions — stay consistent with them rather than inventing new structures.
- Command layout. Every command lives under
src/commands/<group>/<cmd>.tsand exports aregisterXxxCommand(parent: Command)function that attaches itself to the parent Commander instance. The top-level registration happens insrc/index.ts. New commands must follow the same registration pattern. - API clients. Platform API calls go through
src/lib/api/platform.ts(platformFetch), OSS/self-hosted calls throughsrc/lib/api/oss.ts(ossFetch). Do not callfetchdirectly from command files — add a typed wrapper to the appropriate client module. - Config. Use helpers from
src/lib/config.ts(getProjectConfig,getPlatformApiUrl, etc.). Never read config files directly. - Errors. Throw
CLIError/AuthError/ProjectNotLinkedErrorfromsrc/lib/errors.ts. Always let the top-levelhandleError(err, json)in the command action render the error — don't print errors yourself. - Output. Use
outputJson/outputTable/outputSuccessfromsrc/lib/output.ts. Respect the--jsonflag viagetRootOpts(cmd). - Interactive prompts. Use
@clack/prompts(already imported in most command files). Never prompt when--jsonis set. - Imports. TypeScript ESM — imports must use
.jsextensions (e.g.from '../lib/config.js'), even for.tssource files.
When in doubt, find the closest existing command and copy its structure.
The CLI uses PostHog (wired up in src/lib/analytics.ts) to track which
commands and features are actually being used. This is the single source of
truth for product telemetry — do not add alternative analytics systems.
- Entry point. Import
captureEvent(or a typed helper liketrackDiagnose) fromsrc/lib/analytics.ts. Add a new helper there if several commands will share an event shape. - Distinct ID. Use
project_id(ororg_idwhere a project isn't yet linked, e.g. increate) as the distinct ID so events can be grouped per project/org. - Event names. Use the
cli_<feature>_<action>convention (e.g.cli_diagnose_invoked). Keep event names stable — renaming breaks dashboards. - Properties. Include only non-sensitive metadata:
project_id,project_name,org_id,region,subcommand, feature flags. Never send SQL, file contents, credentials, or user-entered free text. - Flush. Always call
await shutdownAnalytics()in afinallyblock so events are flushed before the process exits. PostHog Node SDK batches by default and will drop events on a hard exit. - Build-time key.
POSTHOG_API_KEYis injected at build time bytsup.config.tsviadefine. Local builds without the env var become a no-op automatically — the CLI itself stays functional.
Do not use reportCliUsage for new commands — that legacy OSS telemetry
path has been removed from create, link, and docs. PostHog is the path
going forward.
The CLI auto-installs agent skills via installSkills() in
src/lib/skills.ts. Those skills live in a separate repo
(InsForge/agent-skills) and teach AI coding agents how to use the InsForge
SDK and CLI.
When you add, rename, or change the behavior of a user-facing CLI command,
you almost certainly need to update the insforge-cli skill in the
agent-skills repo as well — otherwise the skill will be stale and agents
will generate wrong commands.
Checklist when changing a command:
- Does the
insforge-cliskill document this command? If yes, open a PR inInsForge/agent-skillsto update it in the same change set. - Did flags, defaults, or output shape change? Update the skill examples.
- Did a command get renamed or removed? Remove or rename in the skill.
- Did you add a new command? Decide whether agents should know about it and add it to the skill if so.
Mention the skill repo update in the CLI PR description so reviewers can cross-reference both PRs.
npm run build produces a bundled ESM entry at dist/index.js and TypeScript
declarations at dist/index.d.ts. Local builds (outside CI) also emit
dist/index.js.map for debugging; CI/release builds skip source map generation.
Source maps are intentionally excluded from npm releases because they increase
package size and are not required for normal CLI usage. The files field in
package.json whitelists only dist/index.js and dist/index.d.ts, so
dist/index.js.map is never published. If you add new build outputs under
dist/, update package.json files accordingly.
Inspect what would be published with:
npm run build
npm pack --dry-runReleases are fully automated via GitHub Actions
(.github/workflows/publish.yml).
The CLI is published to npm as @insforge/cli whenever a GitHub Release is
published.
Steps to ship a release:
- Bump the version in
package.json(e.g.0.1.41→0.1.42) on your feature branch or onmain. Follow semver — patch for fixes, minor for additive features, major for breaking changes. - Commit the bump with a message like
chore: bump version to X.Y.Z. - Merge to
mainvia PR as usual. - Create and publish a GitHub Release for the version tag on
main:git checkout main && git pull gh release create vX.Y.Z --title vX.Y.Z --generate-notesgh release createcreates the tag and the release in one step. Note that pushing a bare tag (git push origin vX.Y.Z) does not trigger the workflow — it only fires when a GitHub Release is published for the tag. - The
publishworkflow triggers on the release being published and:- Runs
npm run lint, thennpm run buildwithPOSTHOG_API_KEYinjected from repo secrets. - Runs
npm publish --access publicwithNPM_TOKENfrom repo secrets.
- Runs
- Verify the new version appears on
npmjs.com/package/@insforge/cli
and that
npx @insforge/cli@latest --versionreturns the expected version.
Do not publish manually from a local machine — that bypasses the CI build (and therefore the PostHog key injection) and leads to analytics being silently disabled in published artifacts.
If a release goes wrong, deprecate the bad version with npm deprecate rather
than unpublishing — unpublishing breaks downstream users who already installed
that version.
The insforge feedback command submits reports to a dedicated InsForge
project via a public edge function — no login required, spam contained
server-side (RLS-locked table, allow-list validation, per-IP rate limit,
duplicate folding). The endpoint URL and anon key are deliberately
hardcoded in src/lib/api/feedback.ts (public client credentials
protected by RLS + function-side rate limiting), overridable via
INSFORGE_FEEDBACK_URL / INSFORGE_FEEDBACK_ANON_KEY for testing and
rotation.
The backend itself (the feedback table, the submit-feedback edge
function, and its secrets) lives in the feedback project — it is not
versioned in this repo. To inspect or maintain it, link a scratch
directory to that project with the admin key, then use
insforge functions code submit-feedback, db query, and
functions deploy as usual; the project's agent memory
(insforge memory list) records the design decisions and triage
workflow. If you change the CLI's feedback payload shape, update the
function's allow-list and the table schema in the same change set.