Skip to content

feat(cli): add init wizard, --json output, and doctor diagnostics - #659

Closed
Primex-Tech wants to merge 14 commits into
TegoLabs:mainfrom
Primex-Tech:feat/cli-init-json-doctor
Closed

Primex-Tech wants to merge 14 commits into
TegoLabs:mainfrom
Primex-Tech:feat/cli-init-json-doctor

Conversation

@Primex-Tech

@Primex-Tech Primex-Tech commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor

This PR combines three previously separate feature PRs (replaces #561, #559, #564) into one clean, minimal diff.

Rather than merging the old stale branches, this branch was rebuilt fresh from current \main\ (\1e4a880e) by cherry-picking only the real feature commits. It touches 17 files (all feature-relevant) with no unrelated churn.

Features

  1. \sorokeep init\ wizard (feat(cli): add an interactive 'sorokeep init' setup wizard #370) - interactive setup for a first contract, alert channel, and guard policy, or non-interactive via --yes. (\src/commands/init.ts, \src/core/init.ts, tests). Registered in \src/cli/program.ts.
  2. *--json\ output for \watch, \guard,
    estore, \inspect*
    (feat(cli): add --json output flag to watch, guard, restore, and inspect commands #372) - machine-readable output alongside the existing human output.
  3. \sorokeep doctor\ diagnostics - node/data-dir/schema/RPC/credential checks (\src/commands/doctor.ts, \src/core/doctor.ts, tests). Registered in \src/cli/program.ts.

Review / tooling feedback addressed

  • CodeRabbit: all findings fixed (non-forcing \process.exitCode\ in doctor, config defaults resolved separately from explicit init options, empty watch-config rejection, logger kept off JSON stdout, DB-init error handling in diagnostics, test isolation).
  • GitGuardian: clean.
  • npm audit: bumped \ ast-uri, \hono, \ip-address\ in the lockfile (only) to clear high-severity advisories (pre-existing on \main, not introduced by this PR).
  • Man page: regenerated via
    pm run build:man\ so \init/\doctor\ (and previously-missing \metrics/\�udit-log) are documented.

Verification

  • \ sc --noEmit, \eslint src/ tests/, and
    pm audit --audit-level=high\ all pass.
  • Full test suite: 1330 passed, 1 skipped.

  • pm run build\ passes.

Primex-Tech and others added 12 commits August 3, 2026 23:08
Implements TegoLabs#370 with interactive and non-interactive (--yes) modes.
Uses runInitWizard in core/init.ts for testable logic.
Writes config.yaml via existing config utils.
- core/init: reject non-integer or non-positive target TTL / threshold values (Number.isSafeInteger) and reject threshold >= target TTL before any DB writes or network calls, matching the guard command constraints
- commands/init: stop treating --target-ttl 0 / --threshold 0 as unset (falsy), and only prompt for guard enablement when neither flag is provided
- index.ts: restore LF line endings to match the repository convention
- commands/init: remove the --network "testnet" default so the saved network
  and the interactive network prompt are actually used
- commands/init: add --alert-threshold so the alert trigger is independent
  from the guard threshold; mark --guard-enabled/--guard-disabled as mutually
  exclusive via Option.conflicts
- commands/init: return after every process.exit(1), share the result/save
  handling between the interactive and non-interactive paths, and skip the
  guard value prompts when the guard is disabled
- commands/init: register built-in channels from the action instead of at
  module import time
- core/init: register built-in channels inside runInitWizard instead of at
  module import time; validate the alert threshold as a positive integer;
  wrap the alert-config and policy writes in a single transaction; make
  repeated init runs idempotent by replacing the contract's alert configs
- tests: assert the real saveConfig 0o600 permissions, the saved-network
  fallback, the --yes missing-flag failure, and the mutually exclusive guard
  flags; cover the unsupported-channel, watch-failure, idempotent re-run, and
  independent-alert-threshold branches
- Add bigint replacer to printOutput for inspect --json
- Add JSON output for key resolution failures in guard.ts and restore.ts
- Add JSON output for dry-run simulation failure in guard.ts
- Remove raw keypairSource (potential secret) from JSON error payloads in guard.ts
- Replace process.exit(1) with process.exitCode = 1 in JSON failure branches so printOutput can flush stdout (guard, restore, watch, inspect)
- Only start spinners when NOT in --json mode (guard, watch)
- Normalize optional targetTtl/threshold values before parsing (guard)
- guard: reject prefix-tolerant parseInt values (e.g. 100foo, 20.5) by validating complete strings as positive integers before storing in extension policy
- watch: include complete watchContract result (instance, wasm, wasmWarning) in batch --json output
…c command

Implements TegoLabs#371 with checks for Node version, data directory writability,
database schema, RPC reachability, and alert-channel credentials.
- core/doctor: replace broken double-quoted strings containing \ placeholders with real template literals so node version, data dir, and error details are interpolated
- core/doctor: reuse getSorokeepDir() from utils/config instead of duplicating the ~/.sorokeep path (drop now-unused os/path imports)
- index.ts: restore the description string and trailing newline to match the base file
@coderabbitai

coderabbitai Bot commented Aug 3, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added interactive and non-interactive initialization for contracts, alert channels, and guard policies.
    • Added a doctor command that checks runtime, storage, database, network, and alert configuration health.
    • Added structured JSON output support to guard, inspect, restore, and watch commands.
    • Added JSON-safe formatting for command results, including large numeric values.
  • Bug Fixes

    • Improved command error handling with consistent exit statuses and clearer machine-readable failures.
    • Strengthened validation for thresholds, TTLs, contract IDs, and configuration options.

Walkthrough

The CLI adds init and doctor commands. Initialization validates and persists contract monitoring settings. Diagnostics check runtime, storage, schema, RPC, and credentials. Existing commands gain structured --json output and exit-code handling.

Changes

Initialization and diagnostics

Layer / File(s) Summary
Initialization workflow
src/core/init.ts, tests/core/init.test.ts
The wizard validates inputs, watches contracts, persists alert and guard settings, and returns structured results.
Initialization command wiring
src/commands/init.ts, src/cli/program.ts, tests/commands/init.test.ts
The init command supports interactive and non-interactive setup, configuration defaults, validation, persistence, and CLI registration.
Diagnostic checks and reporting
src/core/doctor.ts, src/commands/doctor.ts, src/cli/program.ts, tests/core/doctor.test.ts, tests/commands/doctor.test.ts
Diagnostics check runtime, storage, schema, RPC, and alert credentials. The command reports statuses and exits when a check fails.

Structured CLI output

Layer / File(s) Summary
Guard JSON responses
src/commands/guard.ts, src/utils/formatting.ts
The guard command emits structured JSON across validation, policy, dry-run, extension, and error paths. Numeric parsing now requires positive safe integers.
Inspect and restore JSON responses
src/commands/inspect.ts, src/commands/restore.ts
Both commands add structured JSON success and failure output, suppress spinners in JSON mode, and set process exit codes for failures.
Watch JSON responses
src/commands/watch.ts
Watch results now include structured batch and single-contract responses while preserving interactive output.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant initCommand
  participant runInitWizard
  participant watchContract
  participant database
  participant saveConfig
  initCommand->>runInitWizard: InitAnswers
  runInitWizard->>watchContract: watch contract
  watchContract-->>runInitWizard: WatchResult
  runInitWizard->>database: persist alert and guard settings
  runInitWizard-->>initCommand: InitResult
  initCommand->>saveConfig: save network and RPC settings
Loading

Possibly related issues

Possibly related PRs

Suggested reviewers: abdulmalikalayande

Poem

A rabbit checks the paths at dawn,
Finds every warning, fail, and yawn.
Init plants contracts in their place,
JSON hops through each command space.
With tidy logs and guards so bright,
The burrow builds with tests tonight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the three primary changes: the init wizard, JSON output, and doctor diagnostics.
Description check ✅ Passed The description directly explains the init wizard, JSON output, doctor diagnostics, tests, and verification for this changeset.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/commands/doctor.ts`:
- Around line 17-20: Replace process.exit(1) in the doctor command’s failure
handling with process.exitCode = 1 after results are printed. In
tests/commands/doctor.test.ts, update the Vitest process stub to reset
process.exitCode and assert it becomes 1 instead of expecting process.exit to
throw.

In `@src/commands/init.ts`:
- Line 37: Update the initialization flow around runInitWizard and the option
handling at lines 37, 75-79, 100-101, and 121-131 to keep explicit options
separate from existingConfig defaults. Pass an explicitly supplied network
through unchanged so interactive prompting remains available when it is absent,
while supplying the resolved configuration defaults separately for
initialization; likewise allow the wizard to use the default RPC endpoint
without replacing existingConfig.rpcUrl used by later monitoring commands.

In `@src/commands/watch.ts`:
- Around line 81-91: Update watchContractsFileSchema in the watch configuration
validation to reject empty contract inputs, including both [] and { contracts:
[] }, so watch never produces an empty results array with success: true.
Preserve the existing JSON success calculation for non-empty results.
- Around line 206-210: Update the watch command’s error handling around the
options.json branch so logger.error("Watch command failed", ...) cannot write to
JSON stdout; route that diagnostic to stderr or suppress it when JSON mode uses
printOutput(..., true), while preserving the single JSON payload and exitCode
behavior.

In `@src/core/doctor.ts`:
- Around line 56-70: Update runDiagnostics around getDatabase and the schema
query to catch initialization or SQLite errors, append a failed schema result
with the error detail, and continue returning the diagnostic summary instead of
rejecting. Ensure credential checks are skipped or warned when no database is
available, and add coverage for getDatabase throwing.

In `@tests/commands/doctor.test.ts`:
- Around line 30-44: Update the “exits with code 1 when any check fails” test
around registerDoctorCommand and parseAsync to expect non-forcing failure
handling via process.exitCode rather than a rejected “process.exit called”
error. Assert that exitCode is 1, and reset process.exitCode after the test to
prevent state leaking into other tests.

In `@tests/core/doctor.test.ts`:
- Around line 1-34: Isolate the runDiagnostics tests from real filesystem and
process state by mocking getSorokeepDir() to return a temporary test directory
and cleaning that directory as needed. In beforeEach, capture the original
SOROKEEP_SLACK_TOKEN and SOROKEEP_TELEGRAM_BOT_TOKEN values before modifying
them, then restore both original values in afterEach instead of unconditionally
deleting them.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 76565297-a317-4534-bd3e-4ef50181474c

📥 Commits

Reviewing files that changed from the base of the PR and between 1e4a880 and 6f8cbf5.

📒 Files selected for processing (14)
  • src/cli/program.ts
  • src/commands/doctor.ts
  • src/commands/guard.ts
  • src/commands/init.ts
  • src/commands/inspect.ts
  • src/commands/restore.ts
  • src/commands/watch.ts
  • src/core/doctor.ts
  • src/core/init.ts
  • src/utils/formatting.ts
  • tests/commands/doctor.test.ts
  • tests/commands/init.test.ts
  • tests/core/doctor.test.ts
  • tests/core/init.test.ts
📜 Review details
🧰 Additional context used
🪛 ast-grep (0.45.0)
tests/commands/init.test.ts

[warning] 18-18: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.writeFileSync(configPath, JSON.stringify(config))
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename-typescript)

🔇 Additional comments (28)
src/core/doctor.ts (3)

9-24: LGTM!


26-54: LGTM!


72-120: LGTM!

src/commands/doctor.ts (1)

5-16: LGTM!

tests/commands/doctor.test.ts (2)

1-28: LGTM!


46-57: LGTM!

tests/core/doctor.test.ts (1)

36-64: LGTM!

src/commands/inspect.ts (1)

6-6: LGTM!

Also applies to: 18-20, 30-51, 96-101

src/commands/restore.ts (1)

7-7: LGTM!

Also applies to: 20-31, 41-45, 54-70, 81-112, 136-140, 149-149

src/utils/formatting.ts (2)

3-19: LGTM!


80-80: LGTM!

src/commands/guard.ts (12)

7-7: LGTM!


24-75: LGTM!


86-89: LGTM!


109-113: LGTM!


122-126: LGTM!


144-148: LGTM!


161-180: LGTM!


193-197: LGTM!


210-215: LGTM!


225-233: LGTM!


243-268: LGTM!


287-296: LGTM!

src/commands/watch.ts (5)

12-15: LGTM!


43-57: LGTM!

Also applies to: 70-70


101-110: LGTM!


121-125: LGTM!


135-154: LGTM!

Comment thread src/commands/doctor.ts Outdated
Comment thread src/commands/init.ts Outdated
Comment thread src/commands/watch.ts
Comment thread src/commands/watch.ts
Comment thread src/core/doctor.ts Outdated
Comment thread tests/commands/doctor.test.ts Outdated
Comment thread tests/core/doctor.test.ts Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant