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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/),
and this project adheres to [Semantic Versioning](https://semver.org/).

## [0.1.5] - 2026-05-12

### Added

- `keito skill install`, `keito skill status`, and `keito skill doctor` commands for installing and verifying the Keito Agent Skill.
- Optional interactive Agent Skill install prompt after `keito auth login`.
- JSON-safe skill installer execution so child installer output does not contaminate `--json` responses.

## [0.1.4] - 2026-05-12

### Added
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "keito-cli"
version = "0.1.4"
version = "0.1.5"
edition = "2021"
description = "CLI for AI agents and humans to track billable time against the Keito platform"
license = "MIT"
Expand Down
3 changes: 3 additions & 0 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod auth;
pub mod clients;
pub mod projects;
pub mod skill;
pub mod time;

use clap::{Parser, Subcommand};
Expand Down Expand Up @@ -104,4 +105,6 @@ pub enum Command {
Time(time::TimeCommand),
/// Browse projects and tasks
Projects(projects::ProjectsCommand),
/// Install and verify the Keito agent skill
Skill(skill::SkillCommand),
}
74 changes: 74 additions & 0 deletions src/cli/skill.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use clap::{Args, Subcommand, ValueEnum};

#[derive(Args)]
#[command(after_long_help = "\
The Keito Skill is the agent UX layer on top of the Keito CLI. The skill
uses the CLI for authentication and API writes, then installs Claude Code /
Codex lifecycle hooks so local coding sessions can be logged automatically.

EXAMPLES:
keito skill install
keito skill install --agent codex
keito skill status --json
keito skill doctor")]
pub struct SkillCommand {
#[command(subcommand)]
pub command: SkillSubcommand,
}

#[derive(Subcommand)]
pub enum SkillSubcommand {
/// Install the Keito Skill and configure supported agent hooks
#[command(long_about = "\
Install the Keito Skill via the open skills CLI, then run the installed hook
installer for each selected agent.

By default this configures both Codex and Claude Code. The skill still needs
per-repository setup after installation: cd into a client repo and run
/track-time-keito to select its Keito client, project, and task.")]
Install {
/// Skill source for npx skills add
#[arg(
long,
default_value = "keito-ai/keito-skill",
env = "KEITO_SKILL_SOURCE"
)]
source: String,

/// Agent hook target to configure
#[arg(long, value_enum)]
agent: Vec<SkillAgent>,

/// Skip running npx skills add and only run hook installers if present
#[arg(long)]
skip_skills_add: bool,
},

/// Show install/auth/hook status for the Keito Skill
Status,

/// Run readiness checks and print next actions
Doctor,
}

#[derive(Debug, Clone, Copy, ValueEnum, PartialEq, Eq)]
pub enum SkillAgent {
Codex,
ClaudeCode,
}

impl SkillAgent {
pub fn skills_cli_name(self) -> &'static str {
match self {
SkillAgent::Codex => "codex",
SkillAgent::ClaudeCode => "claude-code",
}
}

pub fn display_name(self) -> &'static str {
match self {
SkillAgent::Codex => "Codex",
SkillAgent::ClaudeCode => "Claude Code",
}
}
}
25 changes: 24 additions & 1 deletion src/commands/auth.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use colored::Colorize;
use dialoguer::{Input, Password};
use dialoguer::{Confirm, Input, Password};
use std::io::IsTerminal;

use crate::api::KeitorClient;
use crate::cli::auth::{AuthCommand, AuthSubcommand};
use crate::cli::GlobalFlags;
use crate::commands::skill;
use crate::config::{AppConfig, ResolvedAuth};
use crate::error::AppError;
use crate::output::{self, OutputMode};
Expand Down Expand Up @@ -71,6 +73,27 @@ async fn login(global: &GlobalFlags, mode: OutputMode) -> Result<(), AppError> {
"Credentials saved to {}.",
AppConfig::config_path()?.display()
);
maybe_offer_skill_install(global, mode).await?;
}

Ok(())
}

async fn maybe_offer_skill_install(global: &GlobalFlags, mode: OutputMode) -> Result<(), AppError> {
if mode != OutputMode::Table || global.quiet || !std::io::stdin().is_terminal() {
return Ok(());
}

let install = Confirm::new()
.with_prompt("Install the Keito agent skill for Claude Code / Codex?")
.default(true)
.interact()
.map_err(|e| AppError::Config(format!("Input error: {e}")))?;

if install {
skill::install_defaults(global, mode).await?;
} else {
println!("You can install it later with: keito skill install");
}

Ok(())
Expand Down
1 change: 1 addition & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod auth;
pub mod clients;
pub mod projects;
pub mod skill;
pub mod time;
Loading
Loading