Skip to content

feat(cli): add workspace initialization and safe startup - #15

Merged
yohnark merged 6 commits into
mainfrom
feat/14-0-1-1-init-cli
Aug 5, 2026
Merged

yohnark merged 6 commits into
mainfrom
feat/14-0-1-1-init-cli

Conversation

@yohnark

@yohnark yohnark commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Implement the 0.1.1 first-run onboarding contract from Issue #14. New users can initialize a workspace without hand-writing mottainai.config.json, while the bare executable remains an MCP stdio server entry point.

Linked issue

Closes #14

Scope

Included

  • Add mottainai init with workspace detection, personal/project scope, upstream import/discovery, client registration, non-interactive options, dry-run, JSON output, force/backup, and atomic configuration writes.
  • Keep missing-configuration diagnostics out of MCP stdout.
  • Run doctor and a built-package stdio initialize/tools/list handshake after initialization.
  • Update package version, README Quick Start, and changelog for 0.1.1.

Excluded

  • Full graphical or TUI installer.
  • Automatic package installation, advanced profile editing, marketplace/plugin management, and deletion of direct client registrations.

Implementation

  • src/init.ts owns initialization parsing, workspace/Git detection, config generation, safe JSON client import, official Claude Code/Codex registration commands, atomic writes, backups, diagnostics, and handshake verification.
  • Personal scope updates .git/info/exclude and never edits .gitignore.
  • Imported literal environment values, credential-bearing URLs, sensitive arguments, and unsafe header values are omitted; environment-variable names and OAuth profiles remain supported.
  • Client registration defaults to mottainai@0.1.1; --latest is explicit opt-in.

Behavioral changes

  • npx -y mottainai init starts workspace setup.
  • npx -y mottainai starts only the MCP server. Without configuration, TTY users receive an init hint and non-TTY users receive the same diagnostic on stderr with empty stdout.
  • --yes provides safe non-interactive defaults; --dry-run writes no configuration or Git exclusion changes; --json emits one JSON document.
  • Existing configurations are preserved unless --force is supplied, and forced replacement creates a backup.

Validation

  • Typecheck — pnpm run typecheck
  • Tests — pnpm test (467/467), focused init/CLI tests (16/16)
  • Build — pnpm run build
  • Package check — npm_config_cache=/tmp/mottainai-npm-cache npm pack --dry-run --json (mottainai@0.1.1)
  • Governance tests — node --test scripts/governance.test.mjs (18/18)
  • Built-package handshake — initialize and tools/list succeeded with 19 tools

Risks

  • Client registration depends on the selected official client command being installed. Existing mottainai registrations are detected and left unchanged.
  • Client list output that is not machine-readable is reported and not copied into the generated configuration.

Breaking changes

No. Existing valid configurations remain readable. The missing-configuration path now exits with a guided diagnostic instead of exposing only a raw filesystem error.

Migration / compatibility

No migration is required. Existing configurations continue through the existing loader. New workspaces use version 2 with gateway.workspaceRoot set to a portable relative path. Run mottainai init only for a workspace without a configuration, or use --force when replacement is intentional.

Security impact

Initialization does not copy literal tokens, authorization headers, cookies, passwords, or credential-bearing URLs into mottainai.config.json. Remote authentication uses environment-variable names or OAuth profiles, and client configuration changes use official CLI commands rather than direct file edits.

Review focus

  • Verify the stdout/stderr boundary for a missing configuration and the built-package handshake lifecycle.
  • Review idempotency and atomic replacement behavior for init --force and personal Git exclusions.
  • Review import sanitization and pinned versus --latest client registration commands.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e6d38618-ec57-4727-a854-3a6f5c664b2b

📥 Commits

Reviewing files that changed from the base of the PR and between 42df6f9 and c12d719.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • src/init.test.ts
  • src/upstream.test.ts
  • src/upstream.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/upstream.test.ts
  • src/upstream.ts
  • src/init.test.ts
  • CHANGELOG.md

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added mottainai init for interactive or non-interactive workspace setup.
    • Added dry runs, JSON output, backups, imports, client registration, validation, and credential filtering.
    • Added safe startup guidance when configuration is missing without writing to MCP stdout.
  • Bug Fixes

    • Prevented upstream HTTP requests from following redirects unexpectedly.
    • Rejected credentialed non-HTTPS upstream connections.
  • Documentation

    • Updated quick-start guidance, CI options, version pinning, credentials handling, and validation steps.
  • Tests

    • Expanded coverage for initialization, imports, backups, platform detection, JSON output, and upstream security.

Walkthrough

The pull request adds mottainai init for v2 workspace configuration, upstream MCP import, client registration, atomic writes, backups, diagnostics, and handshake checks. It improves missing-configuration startup messages, redirect-safe upstream transport, onboarding documentation, version metadata, and test coverage.

Changes

Init command and safe startup

Layer / File(s) Summary
Version bump and documentation updates
README.md, CHANGELOG.md, package.json
Documents the init-first workflow and options, records version 0.1.1 features, and updates the package version.
CLI command wiring for init
src/cli.ts
Adds the init command, option handling, JSON output, human-readable output, and JSON error handling.
Safe stdio startup error handling
src/index.ts, src/mcp-cli.test.ts
Reports missing configuration and the init command on stderr while preserving empty stdout.
Init contracts, validation, and path resolution
src/init.ts
Defines init result contracts, validates options, discovers clients and commands, and resolves workspace and configuration paths.
Credential sanitization and upstream import
src/init.ts
Imports Claude and Codex MCP registrations, filters credential-bearing values, preserves safe settings, and reports failures and timeouts.
Persistence, registration, and verification
src/init.ts
Implements Git exclusions, dry-run reporting, atomic writes, backups, client registration, interactive prompts, doctor checks, MCP handshakes, orchestration, and result formatting.
Redirect-safe upstream transport
src/upstream.ts, src/upstream.test.ts
Rejects HTTP redirects for streamable upstream requests, rejects credentialed non-HTTPS endpoints, and preserves configured request headers.
Initialization and integration validation
src/init.test.ts, src/mcp-cli.test.ts, src/upstream.test.ts
Tests initialization, path handling, discovery, persistence, sanitization, timeouts, startup behavior, CLI output, and redirect rejection.

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

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant runInit
  participant FileSystem
  participant MCPClient
  participant MCPServer

  CLI->>runInit: invoke initialization
  runInit->>FileSystem: resolve workspace and configuration
  runInit->>MCPClient: import registrations
  runInit->>FileSystem: write configuration atomically
  runInit->>MCPClient: register mottainai
  runInit->>MCPServer: run doctor and MCP handshake
  runInit-->>CLI: return initialization summary
  CLI-->>CLI: print JSON or human-readable output
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: workspace initialization and safe MCP startup behavior.
Description check ✅ Passed The description directly explains the initialization workflow, protocol-safe startup, security behavior, documentation updates, and validation.
Linked Issues check ✅ Passed The changes address Issue #14 requirements for init, workspace detection, safe writes, secret filtering, client registration, diagnostics, non-TTY use, and documentation.
Out of Scope Changes check ✅ Passed The changes are related to the initialization contract, upstream safety, package onboarding, and MCP startup behavior described in the issue.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/14-0-1-1-init-cli

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (2)
README.md (1)

128-135: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Hardcoded version pin in two places.

mottainai@0.1.1 is hardcoded in both the claude mcp add and codex mcp add examples. Each future version bump needs a manual edit here in addition to package.json and CHANGELOG.md. Consider adding a short maintenance note (e.g., in CHANGELOG.md or a release checklist) to keep these three references in sync.

🤖 Prompt for 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.

In `@README.md` around lines 128 - 135, Add a maintenance note in CHANGELOG.md or
create a release checklist documentation to remind developers that the mottainai
version must be synchronized across three locations during version bumps: the
package.json version field, and both the claude mcp add and codex mcp add
command examples in README.md. This prevents the hardcoded version pins from
drifting out of sync.
CHANGELOG.md (1)

16-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a [0.1.1] link reference definition.

The file defines a link reference for [0.1.0] at the bottom (line 78 context). No matching [0.1.1] reference definition appears for the new ## [0.1.1] - in development heading. Add a [0.1.1]: ... reference line to keep the version heading consistent with the existing convention.

📝 Proposed addition near the bottom of the file
 [Unreleased]: https://github.com/yohn-jp/mottainai/compare/main...HEAD
+[0.1.1]: https://github.com/yohn-jp/mottainai/commits/main
 [0.1.0]: https://github.com/yohn-jp/mottainai/commits/main
🤖 Prompt for 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.

In `@CHANGELOG.md` at line 16, Add a link reference definition for the new version
heading at the bottom of the CHANGELOG.md file to match the existing convention.
Locate the existing [0.1.0] link reference definition near the end of the file
and add a corresponding [0.1.1] reference definition on a new line using the
same format and URL pattern. This ensures the markdown link reference for the
new [0.1.1] heading is properly defined, consistent with how [0.1.0] is already
handled in the file.
🤖 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/init.ts`:
- Around line 491-493: Update the validation error thrown in the init
argument-checking branch to begin with lowercase “interactive” while preserving
the rest of the message and behavior.
- Around line 178-180: Update resolveConfiguration to use resolveConfigPath so
explicit paths and the MOTTAINAI_CONFIG environment variable are resolved
consistently, while retaining the default workspace/mottainai.config.json
fallback when neither is provided.
- Line 607: Update the next-step command in the summary generation around
summary.dry_run to preserve the selected configuration context: append --config
with summary.configuration when provided, or direct users to change into
summary.workspace before running doctor. Ensure the displayed command inspects
the configuration generated by the current run.
- Around line 304-312: Add bounded timeouts to the subprocess options in
runClientList and registerClient, and enforce a single timeout covering the
complete MCP handshake around client.connect and client.listTools. When any
operation times out, surface the failure through warnings and the corresponding
InitHandshakeResult while preserving cleanup in the finally block.
- Around line 147-149: Update the comment in the catch block of the
initialization flow to Japanese, preserving its explanation that PATH entries
are user input and may disappear during initialization; keep the catch behavior
unchanged and ensure the comment describes only why the empty catch is safe.
- Around line 140-152: Update commandPath to resolve Windows executables by
trying the command itself plus extensions from the environment’s PATHEXT when
process.platform is "win32", including .exe, .cmd, and .bat candidates. Preserve
the existing PATH traversal, file validation, and non-Windows behavior.
- Around line 228-280: Update importedRegistration and its URL validation so
remote credential references in auth and headersFromEnv are preserved only for
HTTPS URLs. Reject non-HTTPS URLs before constructing the imported registration,
while retaining the existing safeRemoteUrl checks for credentials and sensitive
query parameters.

In `@src/mcp-cli.test.ts`:
- Around line 251-253: Update the TTY startup branch in the relevant startup
logic of src/index.ts to write the missing-configuration guidance to stderr
instead of stdout, while preserving the existing message. Extend the tests in
the TTY startup path around the existing server.stdout/server.stderr assertions
to exercise TTY stdin and stdout, verify stdout remains empty, and assert both
guidance messages appear on stderr.

---

Nitpick comments:
In `@CHANGELOG.md`:
- Line 16: Add a link reference definition for the new version heading at the
bottom of the CHANGELOG.md file to match the existing convention. Locate the
existing [0.1.0] link reference definition near the end of the file and add a
corresponding [0.1.1] reference definition on a new line using the same format
and URL pattern. This ensures the markdown link reference for the new [0.1.1]
heading is properly defined, consistent with how [0.1.0] is already handled in
the file.

In `@README.md`:
- Around line 128-135: Add a maintenance note in CHANGELOG.md or create a
release checklist documentation to remind developers that the mottainai version
must be synchronized across three locations during version bumps: the
package.json version field, and both the claude mcp add and codex mcp add
command examples in README.md. This prevents the hardcoded version pins from
drifting out of sync.
🪄 Autofix

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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 821529f8-92e9-42ba-a612-0f2b89e88990

📥 Commits

Reviewing files that changed from the base of the PR and between 80b7606 and a12bc93.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • README.md
  • package.json
  • src/cli.ts
  • src/index.ts
  • src/init.test.ts
  • src/init.ts
  • src/mcp-cli.test.ts

Comment thread src/init.ts
Comment thread src/init.ts Outdated
Comment thread src/init.ts
Comment thread src/init.ts
Comment thread src/init.ts Outdated
Comment thread src/init.ts
Comment thread src/init.ts Outdated
Comment thread src/mcp-cli.test.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/init.ts (1)

281-300: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Sensitive Data Exposure (CWE-319): Cleartext Transmission of Sensitive Information

Reachability: Internal · Exploitability: Moderate

Reject unsafe redirects for imported credential headers. headersFromEnv is retained for HTTPS URLs and passed to StreamableHTTPClientTransport as request headers. Its default fetch handling can forward custom secret headers across redirects, including HTTP or another authority. Use a redirect-rejecting fetch wrapper or transport policy. The OAuth profile is not forwarded as a token on this path.

🤖 Prompt for 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.

In `@src/init.ts` around lines 281 - 300, The HTTPS import path around
headersFromEnv must prevent credential headers from being forwarded through
redirects to HTTP or another authority. Update the transport setup that consumes
imported.headersFromEnv, using a redirect-rejecting fetch wrapper or equivalent
transport policy, while preserving the existing OAuth profile handling and
header validation.
🧹 Nitpick comments (1)
src/init.test.ts (1)

74-93: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use complete variable names.

bin and previousPathExt add new abbreviations.

  • src/init.test.ts#L74-L93: Rename bin to binaryDirectory and previousPathExt to previousPathExtensions.
  • src/init.test.ts#L206-L236: Rename bin to binaryDirectory.

As per coding guidelines, use complete words for names and do not add abbreviations such as cfg, impl, or res.

🤖 Prompt for 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.

In `@src/init.test.ts` around lines 74 - 93, Use complete variable names in
src/init.test.ts: rename bin to binaryDirectory and previousPathExt to
previousPathExtensions in lines 74-93, and rename bin to binaryDirectory in
lines 206-236, updating all references at each site.

Source: Coding guidelines

🤖 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/init.test.ts`:
- Around line 170-175: Update the test fixture’s httpCredentials configuration
to preserve the OAuth profile reference while still exercising sanitization at
the referenced credential field near the later assertion. Ensure the init
contract retains auth type “oauth” and profile “http” so imported servers can
authenticate, rather than removing or replacing the profile-only reference.

In `@src/init.ts`:
- Around line 323-334: Update src/init.ts lines 323-334 in runClientList to
return an explicit successful-list indicator, distinguishing a zero-result
successful listing from non-zero or otherwise failed commands. Update
src/init.ts lines 424-460 to abort registration when listing failed or timed
out, and only register when a successful list conclusively confirms that
mottainai is absent.
- Around line 143-160: Update src/init.ts:143-160, src/init.ts:323-328, and
src/init.ts:449-453 so Windows .cmd and .bat results from commandPath are
executed through one shared ComSpec launcher with fixed arguments, passing
command paths and user-controlled values only as arguments. Apply this launcher
in both runClientList and registerClient, and ensure a failed runClientList
stops registration instead of continuing.

---

Outside diff comments:
In `@src/init.ts`:
- Around line 281-300: The HTTPS import path around headersFromEnv must prevent
credential headers from being forwarded through redirects to HTTP or another
authority. Update the transport setup that consumes imported.headersFromEnv,
using a redirect-rejecting fetch wrapper or equivalent transport policy, while
preserving the existing OAuth profile handling and header validation.

---

Nitpick comments:
In `@src/init.test.ts`:
- Around line 74-93: Use complete variable names in src/init.test.ts: rename bin
to binaryDirectory and previousPathExt to previousPathExtensions in lines 74-93,
and rename bin to binaryDirectory in lines 206-236, updating all references at
each site.
🪄 Autofix

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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f06c388-c98f-4a6b-89ed-6834a8957f04

📥 Commits

Reviewing files that changed from the base of the PR and between a12bc93 and 6a5e4fa.

📒 Files selected for processing (3)
  • src/index.ts
  • src/init.test.ts
  • src/init.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/index.ts

Comment thread src/init.test.ts
Comment thread src/init.ts
Comment thread src/init.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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 `@CHANGELOG.md`:
- Line 81: Update the [0.1.1] changelog reference so it no longer points to the
generic commits/main URL; remove the link until a 0.1.1 tag exists, or replace
it with a version-specific tag or comparison URL.

In `@src/upstream.test.ts`:
- Around line 32-48: Update the test around fetchWithoutRedirects to assert that
the Authorization header is forwarded in requestInit. To verify redirect
rejection as the test name claims, make the mocked fetch return a redirect
response and assert the call rejects; otherwise rename the test to describe
request-option propagation and retain the non-redirect response.

In `@src/upstream.ts`:
- Around line 195-197: Update the transport creation flow around
StreamableHTTPClientTransport to reject credentialed upstreams when
headersFromEnv is defined unless endpoint.protocol is "https:". Keep the
existing loopback HTTP broker path separate, and ensure remote credentials are
never passed to an HTTP endpoint; preserve the current transport configuration
for allowed cases.
🪄 Autofix

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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b68cd629-76b5-49b2-9524-4b969a66eca6

📥 Commits

Reviewing files that changed from the base of the PR and between 6a5e4fa and 42df6f9.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • src/init.test.ts
  • src/init.ts
  • src/upstream.test.ts
  • src/upstream.ts

Comment thread CHANGELOG.md Outdated
Comment thread src/upstream.test.ts Outdated
Comment thread src/upstream.ts
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.

feat(cli): redesign first-run setup and initialization flow

1 participant