This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
QiForge CLI — a Node.js CLI tool for provisioning AI Agent oracle projects on the IXO blockchain. It creates blockchain entities, provisions Matrix accounts for data storage, and scaffolds oracle projects from the qiforge. Requires Node.js 22+. Supports two authentication modes: SignX (QR code via IXO Mobile App) or offline wallet (local mnemonic + Matrix password).
| Command | Purpose |
|---|---|
pnpm build |
Build with tsup (single ESM bundle → dist/cli.js) |
pnpm dev |
Watch mode build |
pnpm start |
Run the built CLI (node dist/cli.js) |
pnpm test |
Run tests with Jest (ts-jest) |
pnpm lint |
ESLint on src/**/*.ts |
pnpm type-check |
tsc --noEmit |
The binary name is qiforge-cli. Build produces a single dist/cli.js with shebang, targeting node22 via tsup.
All commands implement Command interface (src/commands/index.ts) and return CLIResult { success, data?, error? }. Commands are registered in CLIManager.registerCommands() in src/cli.ts.
Entry flow (src/cli.ts → CLIManager):
- Parse args:
--initruns init,--chat/chatruns chat,offline-logintriggers offline auth,--helpshows help, no args → interactive select menu handleAuthentication()checks for persisted wallet (~/.wallet.json), prompts login choice (SignX or Offline) if missingregisterCommands()instantiates and registers all commands- Route to selected command's
execute()method
Non-interactive mode: Commands support --no-interactive flag + CLI flags via parseCliFlags() (src/utils/cli-flags.ts) for CI/CD pipelines. Flags use --key value or --key=value syntax.
Adding a new command: Create src/commands/your-command.command.ts implementing Command, register it in CLIManager.registerCommands(). See docs/DEVELOPMENT.md for detailed guide.
Wallet(src/utils/wallet.ts) — Persists login state to~/.wallet.json. Supports two modes viaWalletProps.mode:'signx'(default, QR-based) and'offline'(local mnemonic). ThesignAndBroadcast(msgs)method abstracts signing — delegates to SignX QR flow or localsignAndBroadcastWithMnemonic(). All command code callswallet.signAndBroadcast()instead of touchingsignXClientdirectly.OfflineLoginCommand(src/commands/offline-login.command.ts) — Login flow for offline mode: prompts for mnemonic, display name, Matrix username/password. Derives wallet viagetSecpClient(), authenticates Matrix viamxLoginRaw(), persists withmode: 'offline'. Supports CLI flags (--mnemonic,--matrixPassword,--network,--name) for non-interactive use.RuntimeConfig(src/utils/runtime-config.ts) — Singleton for transient per-session state (projectPath, network, entityDid, registerUserResult). Useconfig.addValue(key, value)/config.getOrThrow(key).SignXClient(src/utils/signx/signx.ts) — Wraps@ixo/signx-sdk. Used only in SignX mode. Flow:transact()→displayTransactionQRCode()→pollNextTransaction()→awaitTransaction().CreateEntity(src/utils/entity.ts) — Core service for oracle entity provisioning. Orchestrates profile upload to Matrix, user registration,MsgCreateEntitybroadcast, domain card/AuthZ/fees linked resource creation.registerUserSimplified()(src/utils/account/simplifiedRegistration.ts) — Creates a complete oracle identity: generates mnemonic → secp wallet → funds account → creates IID on-chain → provisions Matrix account with E2EE → stores encrypted mnemonic as room state.parseCliFlags()(src/utils/cli-flags.ts) — Simple flag parser forprocess.argv. Supports--key value,--key=value, and--boolean-flagsyntax. Used by commands to support non-interactive mode.saveOracleConfig()/loadOracleConfig()(src/utils/oracle-config.ts) — Saves/loadsoracle.config.jsonwith oracle metadata, LLM model, prompt config, skills, and MCP servers. Written to both project root andapps/app/for Docker builds.
- Matrix as content store: JSON documents (profile, domain card, configs) are uploaded to Matrix as unencrypted media. The
mxc://URL becomes theserviceEndpointin blockchainLinkedResource.prooffield is a CID, not a signature. - Two Matrix roles: The logged-in user's Matrix account (from SignX) handles media uploads. The oracle gets its own dedicated Matrix account provisioned by
registerUserSimplified(). - Dual signing modes: In SignX mode, all user transactions require mobile QR scanning. In offline mode, transactions are signed locally with the stored mnemonic via
signAndBroadcastWithMnemonic(). TheWallet.signAndBroadcast()method abstracts this — commands are mode-agnostic. The oracle's own IID is always created withsignAndBroadcastWithMnemonic()using its newly generated mnemonic regardless of mode.
- Interactive prompts use
@clack/prompts(p.group(),p.text(),p.select(),p.spinner()) - Commands support non-interactive mode via
parseCliFlags()— checkflags['no-interactive'] === 'true'then use flag values with fallback to prompts - Input validation uses Zod via helpers in
src/utils/common.ts(checkRequiredString,checkRequiredURL,checkRequiredNumber,checkIsEntityDid,checkRequiredPin) — these returnstring | undefinedto match clack's validation API - Network URLs (RPC, Matrix, SignX, bots) are defined as constants in
src/utils/common.tskeyed byNETWORKtype (devnet | testnet | mainnet) - Error handling uses custom classes in
src/utils/errors.tswithhandleError()utility - Constructor injection for
WalletandRuntimeConfiginto commands oracle.config.jsonstores oracle metadata including AI config (model, prompts, skills, MCP servers) — validated with Zod schema insrc/utils/oracle-config.ts