Skip to content
Closed
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
2 changes: 2 additions & 0 deletions Cargo.lock

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

3 changes: 3 additions & 0 deletions crates/chat-cli/.amazonq/mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"mcpServers": {}
}
2 changes: 2 additions & 0 deletions crates/chat-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ amzn-qdeveloper-streaming-client = { path = "../amzn-qdeveloper-streaming-client
amzn-toolkit-telemetry-client = { path = "../amzn-toolkit-telemetry-client" }
anstream = "0.6.13"
arboard = { version = "3.5.0", default-features = false }
once_cell = "1.19.0"
semantic_search_client = { path = "../semantic_search_client" }
async-trait = "0.1.87"
aws-config = "1.0.3"
aws-credential-types = "1.0.3"
Expand Down
163 changes: 163 additions & 0 deletions crates/chat-cli/src/cli/chat/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ pub enum Command {
Context {
subcommand: ContextSubcommand,
},
Knowledge {
subcommand: KnowledgeSubcommand,
},
PromptEditor {
initial_text: Option<String>,
},
Expand Down Expand Up @@ -182,6 +185,16 @@ pub enum ContextSubcommand {
Help,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum KnowledgeSubcommand {
Show,
Add { path: String },
Remove { path: String },
Update { path: String },
Clear,
Help,
}

impl ContextSubcommand {
const ADD_USAGE: &str = "/context add [--global] [--force] <path1> [path2...]";
const AVAILABLE_COMMANDS: &str = color_print::cstr! {"<cyan!>Available commands</cyan!>
Expand Down Expand Up @@ -447,6 +460,113 @@ impl Command {
return Ok(match parts[0].to_lowercase().as_str() {
"clear" => Self::Clear,
"help" => Self::Help,
"knowledge" => {
if parts.len() < 2 {
return Ok(Self::Knowledge {
subcommand: KnowledgeSubcommand::Help,
});
}

match parts[1].to_lowercase().as_str() {
"show" => Self::Knowledge {
subcommand: KnowledgeSubcommand::Show,
},
"add" => {
// Parse add command with path
let mut path = None;

let args = match shlex::split(&parts[2..].join(" ")) {
Some(args) => args,
None => return Err("Failed to parse quoted arguments".to_string()),
};

for arg in &args {
if path.is_none() {
path = Some(arg.to_string());
} else {
return Err(format!("Only a single path is allowed. Found extra path: {}", arg));
}
}

let path = path.ok_or_else(|| {
format!(
"Invalid /knowledge arguments.\n\nUsage:\n {}",
KnowledgeSubcommand::ADD_USAGE
)
})?;

Self::Knowledge {
subcommand: KnowledgeSubcommand::Add { path },
}
},
"update" => {
// Parse update command with path
let mut path = None;

let args = match shlex::split(&parts[2..].join(" ")) {
Some(args) => args,
None => return Err("Failed to parse quoted arguments".to_string()),
};

for arg in &args {
if path.is_none() {
path = Some(arg.to_string());
} else {
return Err(format!("Only a single path is allowed. Found extra path: {}", arg));
}
}

let path = path.ok_or_else(|| {
format!(
"Invalid /knowledge arguments.\n\nUsage:\n {}",
KnowledgeSubcommand::UPDATE_USAGE
)
})?;

Self::Knowledge {
subcommand: KnowledgeSubcommand::Update { path },
}
},
"rm" => {
// Parse rm command with path
let mut path = None;
let args = match shlex::split(&parts[2..].join(" ")) {
Some(args) => args,
None => return Err("Failed to parse quoted arguments".to_string()),
};

for arg in &args {
if path.is_none() {
path = Some(arg.to_string());
} else {
return Err(format!("Only a single path is allowed. Found extra path: {}", arg));
}
}

let path = path.ok_or_else(|| {
format!(
"Invalid /knowledge arguments.\n\nUsage:\n {}",
KnowledgeSubcommand::REMOVE_USAGE
)
})?;
Self::Knowledge {
subcommand: KnowledgeSubcommand::Remove { path },
}
},
"clear" => Self::Knowledge {
subcommand: KnowledgeSubcommand::Clear,
},
"help" => Self::Knowledge {
subcommand: KnowledgeSubcommand::Help,
},
other => {
return Err(KnowledgeSubcommand::usage_msg(format!(
"Unknown subcommand '{}'.",
other
)));
},
}
},
"compact" => {
let mut prompt = None;
let show_summary = true;
Expand Down Expand Up @@ -1133,3 +1253,46 @@ mod tests {
}
}
}
impl KnowledgeSubcommand {
const ADD_USAGE: &str = "/knowledge add <path>";
const AVAILABLE_COMMANDS: &str = color_print::cstr! {"<cyan!>Available commands</cyan!>
<em>help</em> <black!>Show an explanation for the knowledge command</black!>
<em>show</em> <black!>Display the knowledge base contents</black!>
<em>add <<path>></em> <black!>Add a file or directory to knowledge base</black!>
<em>update <<path>></em> <black!>Update a file or directory in knowledge base</black!>
<em>rm <<path>></em> <black!>Remove specified knowledge context by path</black!>
<em>clear</em> <black!>Remove all knowledge contexts</black!>"};
const BASE_COMMAND: &str = color_print::cstr! {"<cyan!>Usage: /knowledge [SUBCOMMAND]</cyan!>

<cyan!>Description</cyan!>
Manage knowledge base for semantic search and retrieval.
Knowledge base is used to store and search information across chat sessions."};
const REMOVE_USAGE: &str = "/knowledge rm <path>";
const UPDATE_USAGE: &str = "/knowledge update <path>";

fn usage_msg(header: impl AsRef<str>) -> String {
format!(
"{}\n\n{}\n\n{}",
header.as_ref(),
Self::BASE_COMMAND,
Self::AVAILABLE_COMMANDS
)
}

pub fn help_text() -> String {
color_print::cformat!(
r#"
<magenta,em>(Beta) Knowledge Base Management</magenta,em>

Knowledge base allows you to store and search information across chat sessions.
Files and directories added to the knowledge base are indexed for semantic search,
enabling more relevant and contextual responses.

{}

{}"#,
Self::BASE_COMMAND,
Self::AVAILABLE_COMMANDS
)
}
}
Loading
Loading