Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ User-facing setup and installation guidance belongs in `README.md`.
- `configs/` contains the files copied into the user's home directory.
- `tests/` contains Vitest coverage for setup flows and generated files.

## CLI Mode Design

`setup` and `init` are intentionally distinct public modes:

- `setup` is interactive. It asks the user to select steps, tools, apps, and configurable presets.
- `init` is non-interactive. It passes an explicit recommended-defaults option to the shared setup engine, which selects recommended steps and content without prompting.
- Package installers used by either mode should remain non-interactive once selections have been resolved.
- `init` must not launch secondary interactive tools such as `p10k configure`. Personalization that cannot be expressed through parameters belongs in a later explicit user action.

Do not turn `init` into an alias for `setup`. New recommended content should be controlled through setup-engine parameters and recommended-value lists, not through prompts in the `init` path.

## Zsh Architecture

Suitup generates a thin `~/.zshrc` that only orchestrates loading.
Expand Down
1 change: 1 addition & 0 deletions CLAUDE.md
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,23 +50,23 @@ When you run suitup locally from the repo, use a zsh session. The curl installer
curl -fsSL https://raw.githubusercontent.com/ChangeHow/suitup/main/install.sh | bash
```

The installer bootstraps missing `zsh` and Node.js/npm when possible, downloads a temporary copy of the repo, runs `npm ci`, then asks whether you want `setup` or guided `init` before launching the interactive flow inside `zsh`. Package installation commands themselves run without extra confirmation prompts. On Linux, Node.js bootstrap now uses the official Node.js 20.x binary tarball so it is not tied to a specific distro repository.
The installer bootstraps missing `zsh` and Node.js/npm when possible, downloads a temporary copy of the repo, runs `npm ci`, then asks whether you want interactive `setup` or non-interactive `init`. Package installation commands themselves run without extra confirmation prompts. On Linux, Node.js bootstrap now uses the official Node.js 20.x binary tarball so it is not tied to a specific distro repository.

You can also pass a command directly to skip the prompt:

```bash
curl -fsSL https://raw.githubusercontent.com/ChangeHow/suitup/main/install.sh | bash -s -- init
```

`init` is an interactive guided initialization path. It asks which steps, tools, and apps to install; the underlying package installation commands do not ask for confirmation again:
`init` is a non-interactive initialization path. The command passes the recommended-defaults option to the setup engine, which selects the recommended steps and content without asking questions:

- bootstrap package manager + zsh when needed
- install the layered zsh config
- install zinit + Powerlevel10k preset
- choose CLI tools and frontend tooling
- choose GUI apps on macOS
- install recommended CLI tools and frontend tooling
- install recommended GUI apps on macOS
- write shared aliases
- run `p10k configure` at the end to set up your prompt theme
- leave Powerlevel10k personalization to a later manual `p10k configure` run

You can also pass any other command:

Expand All @@ -93,7 +93,7 @@ node src/cli.js

| Command | Description |
|---------|-------------|
| `node src/cli.js init` | Interactive guided initialization |
| `node src/cli.js init` | Non-interactive initialization with recommended defaults |
| `node src/cli.js` | Full interactive setup (default) |
| `node src/cli.js setup` | Same as above |
| `node src/cli.js append` | Append configs to existing `.zshrc` |
Expand Down
12 changes: 6 additions & 6 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,23 +50,23 @@ Suitup 可以帮你初始化 Zsh 和 Homebrew,但更稳妥的路径仍然是
curl -fsSL https://raw.githubusercontent.com/ChangeHow/suitup/main/install.sh | bash
```

安装脚本会在必要时自动安装 `zsh` 和 Node.js/npm,临时下载仓库,执行 `npm ci`,然后询问你想要运行 `setup` 还是引导式 `init`,再在 `zsh` 中启动交互流程。底层软件包安装命令本身不会重复要求确认。在 Linux 上,Node.js 引导安装现在会使用官方 Node.js 20.x 二进制 tarball,不再依赖某个发行版的软件源。
安装脚本会在必要时自动安装 `zsh` 和 Node.js/npm,临时下载仓库,执行 `npm ci`,然后询问你想要运行交互式 `setup` 还是非交互式 `init`。底层软件包安装命令本身不会重复要求确认。在 Linux 上,Node.js 引导安装现在会使用官方 Node.js 20.x 二进制 tarball,不再依赖某个发行版的软件源。

你也可以直接传入命令来跳过询问:

```bash
curl -fsSL https://raw.githubusercontent.com/ChangeHow/suitup/main/install.sh | bash -s -- init
```

`init` 是交互式引导初始化路径,会询问要执行的步骤、工具和应用;底层软件包安装命令不会再次要求确认
`init` 是非交互式初始化路径。该命令会向 setup 引擎传入“推荐默认值”参数,不经询问直接选择推荐步骤和内容

- 按需安装包管理器和 zsh
- 安装分层 zsh 配置
- 安装 zinit + Powerlevel10k 预设
- 选择 CLI 工具和前端工具链
- 在 macOS 上选择 GUI 应用
- 安装推荐的 CLI 工具和前端工具链
- 在 macOS 上安装推荐的 GUI 应用
- 写入共享 aliases
- 最后运行 `p10k configure` 完成提示符主题配置
- Powerlevel10k 个性化配置留待之后手动运行 `p10k configure`

你也可以传入其他命令:

Expand All @@ -93,7 +93,7 @@ node src/cli.js

| 命令 | 说明 |
|------|------|
| `node src/cli.js init` | 交互式引导初始化 |
| `node src/cli.js init` | 使用推荐默认值的非交互式初始化 |
| `node src/cli.js` | 完整交互式安装(默认) |
| `node src/cli.js setup` | 同上 |
| `node src/cli.js append` | 追加配置到已有 `.zshrc` |
Expand Down
2 changes: 1 addition & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ elif [[ -r /dev/tty ]]; then
echo '' >&2
echo 'How would you like to run suitup?' >&2
echo ' 1) setup — interactive, choose each step yourself' >&2
echo ' 2) init — interactive guided initialization' >&2
echo ' 2) init — non-interactive, install recommended defaults' >&2
echo '' >&2
read -r -p 'Enter 1 or 2 [default: 1]: ' _suitup_choice < /dev/tty
case "${_suitup_choice}" in
Expand Down
2 changes: 1 addition & 1 deletion src/cli-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export function getHelpText(executable = "node src/cli.js") {
Run suitup from a zsh session.

Commands:
init Interactive guided initialization
init Non-interactive init with recommended defaults
setup Full interactive environment setup (default)
append Append recommended configs to existing .zshrc
verify Verify installation and config integrity
Expand Down
2 changes: 1 addition & 1 deletion src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export async function main(argv = process.argv) {
await runSetup();
break;
case "init":
await runSetup();
await runSetup({ defaults: true });
break;
case "append":
await runAppend();
Expand Down
8 changes: 1 addition & 7 deletions src/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import pc from "picocolors";
import { existsSync } from "node:fs";
import { homedir } from "node:os";
import { join } from "node:path";
import { spawnSync } from "node:child_process";
import { bootstrap } from "./steps/bootstrap.js";
import { installZinit } from "./steps/plugin-manager.js";
import { CLI_TOOLS, installCliTools } from "./steps/cli-tools.js";
Expand Down Expand Up @@ -457,12 +456,7 @@ export async function runSetup({ defaults = false } = {}) {
}

if (promptTheme === "p10k" && !existsSync(join(homedir(), ".p10k.zsh"))) {
if (defaults) {
p.log.step("Launching Powerlevel10k configuration wizard...");
spawnSync("zsh", ["-lc", "p10k configure"], { stdio: "inherit" });
} else {
p.log.info("Powerlevel10k is selected, but `~/.p10k.zsh` was not found. Suitup keeps a basic prompt until you run `p10k configure` in zsh, which avoids dropping you into an interactive wizard during setup.");
}
p.log.info("Powerlevel10k is selected, but `~/.p10k.zsh` was not found. Suitup keeps a basic prompt until you run `p10k configure` in zsh, which avoids dropping you into an interactive wizard during setup.");
}

p.outro(
Expand Down
4 changes: 2 additions & 2 deletions tests/cli-main.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ describe("cli main", () => {
expect(runSetup).toHaveBeenCalledTimes(1);
});

test("routes init to interactive setup", async () => {
test("routes init to non-interactive recommended setup", async () => {
const { main } = await import("../src/cli.js");

await main(["node", "src/cli.js", "init"]);

expect(requireZshShell).toHaveBeenCalledWith();
expect(runSetup).toHaveBeenCalledWith();
expect(runSetup).toHaveBeenCalledWith({ defaults: true });
});

test("prints help without invoking async commands", async () => {
Expand Down
2 changes: 1 addition & 1 deletion tests/cli.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ describe("cli command resolution", () => {

test("help text lists init", () => {
const helpText = getHelpText();
expect(helpText).toContain("init");
expect(helpText).toContain("init Non-interactive init with recommended defaults");
});

test("help text lists migrate-paths", () => {
Expand Down
2 changes: 1 addition & 1 deletion tests/install-script.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe("install.sh", () => {

expect(content).toContain("How would you like to run suitup?");
expect(content).toContain("interactive, choose each step yourself");
expect(content).toContain("interactive guided initialization");
expect(content).toContain("non-interactive, install recommended defaults");
});

test("passes init directly to the CLI and validates supported commands", () => {
Expand Down