본문으로 건너뛰기
cccskills
무료GitHub에 공개

cli-commands

Implement or modify GitButler’s but commands using its established structure. It covers typed arguments, input validation, execution, and human-readable, shell, or JSON output, while keeping operations usable from the terminal interface.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

원문원문 설명 보기

Use when adding or modifying CLI command (`but` commands) under `crates/but/src`.

설치 방법 보기

이럴 때 유용합니다

  • Add a new but command
  • Define arguments for branches and commits
  • Separate input validation from execution
  • Implement human-readable, shell, and JSON output

포함된 파일(1)

  • SKILL.md7.4 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Imagine we're implementing a new commit3 command. The high-level structure for that must be as follows.

Arguments

Arguments live in crates/but/src/args. For our commit command that would be crates/but/src/args/commit3.rs:

use crate::args::atoms::CliIdArg;

/// Create a commit.
///
/// More details about the command here...
#[derive(Debug, clap::Parser)]
#[cfg_attr(feature = "raw-clap-docs", clap(verbatim_doc_comment))]
#[deny(missing_docs)]
pub struct Platform {
    /// The message to use for the commit.
    #[clap(short, long, group = "commit_message")]
    pub message: Option<Vec<String>>,

    /// Place the commit on the branch `BRANCH`.
    #[clap(short, long, value_name = "BRANCH", group = "targeting")]
    pub branch: Option<Option<CliIdArg>>,

    /// One or more changes to commit.
    pub changes: Vec<CliIdArg>,
}

In crates/but/src/args/mod.rs:

#[cfg(feature = "legacy")]
#[cfg_attr(feature = "raw-clap-docs", clap(verbatim_doc_comment))]
#[clap(hide = true, name = "_commit3")]
Commit3(commit3::Platform),

Things to note:

Arguments that refer to Git objects (such as commits, branches, files, hunks, etc.) use some type from crates/but/src/args/atoms/ and not String or another loose type.

Use CliIdArg for arguments that reference existing Git objects such as branches, commits, files, etc. This allows the user to use short IDs or fully qualified names.

String should only be used for truly loose text input such as commit messages.

Platform and all of its fields must have documentation. The Subcommands variant intentionally has no doc comment because clap obtains the command documentation from Platform.

Use #[clap(group = "...")] to create mutually exclusive groups of arguments.

Commands with tricky grammar can define a pub(crate) const ERROR_EXAMPLES next to Platform and register it in args::error_examples; the block is appended after clap parse errors. At most 4 lines, each a but <cmd> ... # what it does invocation that works as written (e.g. include -m where omitting it would open an editor).

Handling the command

Add a match arm to crates/but/src/lib.rs to handle the command:

match cmd {
    Subcommands::Commit3(commit_args) => {
        use crate::utils::IntermediateChannel;

        let status_after = args.status_after;
        let mut ctx = setup::init_ctx(
            &args,
            InitCtxOptions {
                background_sync: BackgroundSync::Enabled { silent: false },
                ..Default::default()
            },
            out,
        )?;
        out.begin_status_after(status_after);

        let outcome = command::legacy::commit3::commit(
            &mut ctx,
            IntermediateChannel::new(out),
            commit_args,
        )
        .emit_metrics(metrics_ctx)?;
        out.print_cli_output(outcome)?;

        run_status_after_if_requested(status_after, &mut ctx, out);

        Ok(())
    }

    // all the other commands...
}

Things to note:

Use IntermediateChannel. Do not pass OutputChannel to commands.

Use OutputChannel::print_cli_output to print the final output from the command. This ensures we handle all supported formats. If only human format is supported, use OutputChannel::print_cli_output_human.

Implementing the command

In crates/but/src/command/legacy/commit3.rs

pub fn commit(
    ctx: &mut Context,
    out: IntermediateChannel<'_>,
    args: Platform,
) -> CliResult<CommitOutcome> {
    // get whatever dependencies we need from `Context` such as
    // `RepoExclusiveGuard`, `IdMap`, `RefInfo`, etc.

    // resolve the arguments into a `CommitOperation`
    let commit_operation = resolve(ctx, args)?;

    // Run the operation
    let outcome = run(ctx, commit_operation)?;

    // Return the outcome which will be printed by the caller
    Ok(outcome)
}

fn resolve(ctx: &mut Context, args: Platform) -> CliResult<CommitOperation> {
    let Platform { message, branch, changes } = args;
    // ...
}

fn run(ctx: &mut Context, commit_op: CommitOperation) -> anyhow::Result<CommitOutcome> {
    match commit_op {
        // ...
    }
}

#[must_use]
struct CommitOutcome {
    new_commit: ObjectId,
}

impl CliOutputHuman for CommitOutcome {
    fn on_human(self, out: &mut dyn WriteWithUtils, _theme: &Theme) -> anyhow::Result<()> {
        let Self { new_commit } = self;

        writeln!(
            out,
            "Created commit {}",
            theme::Commit(new_commit, None),
        )?;

        Ok(())
    }
}

impl CliOutput for CommitOutcome {
    fn on_shell(self, out: &mut dyn WriteWithUtils) -> anyhow::Result<()> {
        let Self { new_commit } = self;

        writeln!(out, "{}", new_commit.to_hex_with_len(7))?;

        Ok(())
    }

    fn on_json(self) -> impl serde::Serialize {
        #[derive(Serialize)]
        struct Output {
            commit: HexHash,
        }

        let Self { new_commit } = self;

        Output { commit: new_commit.into() }
    }
}

Things to note:

Commands follow a resolve then run structure.

run doesn't print its final output. It returns something that implements CliOutput / CliOutputHuman which the caller can then print.

resolve returns CliResult because it needs to reject bad user input.

run returns anyhow::Result because it can only hit internal errors. Bad user input is handled by resolve.

resolve translates CLI arguments into domain targets and validates user input. It may query repository state to disambiguate or reject input, but should not retain derived data in the operation.

run() loads current repository state and computes results. Commit counts, diffs, statistics, branch details, current tips, and workspace projections generally belong in run(). State derivable from those identifiers and the context belongs in run().

The operation should not contain types from crate::args::atoms.

A non-CLI caller such as the TUI should be able to construct an operation from domain identifiers without reproducing repository queries from resolve(). If it must compute counts, diffs, or branch metadata to build the operation, those fields probably belong in run().

The operation does not contain diff specs (usually in the form Vec<DiffSpec>). It should instead contain CliIds which run turns into Vec<DiffSpec> using DiffSpecBuilder. This makes it easier for the status TUI to call run directly.

User-input pickers and prompts are created via InputOutputChannel accessed through IntermediateChannel::prepare_for_terminal_input.

The JSON output includes both commit IDs and change IDs.

Use AllowMergedArg and MergedUpstream to verify we don't mutate merged commits and branches.

Printing of commits, branches, change IDs, etc. uses newtypes from theme such as theme::Branch and theme::Commit. This ensures consistent coloring.

Bad user input errors use bad_input(...), optionally with .arg_name(), .arg_value(), and .hint().

Avoid making the operation or outcome types implement serde::Serialize. Define the specific types needed inside fn on_json. This makes it harder to break compatibility by accident. Reusing Serialize from existing domain types is fine.

More examples

For examples of this structure in practice see

  • crates/but/src/command/legacy/commit.rs
  • crates/but/src/command/legacy/move.rs
  • crates/but/src/command/legacy/squash.rs
  • crates/but/src/command/legacy/diff.rs

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

but

무료

Manage version control through GitButler’s but CLI. Select files or hunks, meaning sections of a diff, for commits; organize dependent branches, split or combine history, resolve conflicts, and create pull requests using task-specific command workflows.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Commit selected files or sections of a diff
  • Split, combine, or reorder commits
  • Arrange branches by dependency
gitbutlerapp/gitbutler2.2만2026년 10월 18일 업데이트

Review captured agent sessions related to a GitButler branch, pull request, or change. Start with a chronological overview of previous discussions and actions, then inspect specific turns when the rationale or supporting evidence needs clarification.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Catch up on previous work on the current branch
  • Review discussions related to a pull request
  • Understand the reasons behind earlier changes
gitbutlerapp/gitbutler2.2만2026년 10월 18일 업데이트

Create, update, run, and debug shell-based performance scenarios for the but CLI. It separates repository setup from the operation being measured, uses Hyperfine for timing, and guides focused validation and inspection of test output.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Add a performance scenario for a but CLI operation
  • Update repository fixtures for an existing benchmark
  • Run a short benchmark for one scenario
gitbutlerapp/gitbutler2.2만2026년 10월 18일 업데이트

Update visible interfaces in Lite and but.dev using @gitbutler/ui-react. It guides component reuse, relevant design references, concise screen text, and visual checks in light and dark themes for forms, dialogs, notifications, and other surfaces.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Add forms or confirmation dialogs
  • Improve empty states and notifications
  • Rewrite labels and hints
gitbutlerapp/gitbutler2.2만2026년 10월 18일 업데이트

Prevent and investigate excessive rendering in Lite React screens. It guides data transformations, context use, and store subscriptions, then checks compiler output and measures rendering time before and after changes to verify their effect.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Investigate lag when selecting rows in Lite
  • Review data transformations during rendering
  • Design narrower context and store subscriptions
gitbutlerapp/gitbutler2.2만2026년 10월 18일 업데이트

Capture before and after screenshots of Lite UI changes on a local machine, compare the images, and publish changed screens to a pull request. It also guides screenshot coverage checks and updates to an existing comment.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Show before and after images in a pull request
  • Check which screens a UI change affects
  • Add a screen to the screenshot catalogue
gitbutlerapp/gitbutler2.2만2026년 10월 18일 업데이트

gitbutlerapp의 스킬 모두 보기

이 스킬의 문제 신고