Skip to content
Open
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
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,18 @@ echo 'source "$HOME/.config/code-intel/env.sh"' >> ~/.zshrc
重开 shell 后第一次运行:

```bash
code-intel status ~/src/your-repo
code-intel ~/src/your-repo
code-intel query ~/src/your-repo --kind evidence --json
```

`code-intel run <repo>` 是第一行主入口的等价命名形式。`query` 只接收仓库路径,
`status` 是低摩擦的项目入口:它把仓库身份、最近一次 committed run 的新鲜度和
下一步动作放在一起。未分析时返回 `needs_run`,证据与当前 checkout 一致时返回
`ready`,不一致时返回 `stale`;Agent 可用 `--json` 读取同一个
`code-intel-project-status.v1` envelope。它只组合现有 authority/freshness 契约,
不会把未提交或过期证据伪装成当前事实。

`code-intel run <repo>` 是分析主入口的等价命名形式。`query` 只接收仓库路径,
由 ProjectContext 统一解析仓库键和 artifact root;日常调用不需要再传
`--artifact-root`、`--repo`、run id 或 manifest。低层发布/排障仍可使用
`run execute` 和 `artifact query` 的显式参数接口。
Expand Down Expand Up @@ -587,6 +594,18 @@ code-intel audit --operation scope --repo C:\path\to\repo --since <git-ref>

## Agent 工作流

### 先看状态,再沿下一步工作

```powershell
code-intel status <path>
code-intel status <path> --json
```

状态输出给出仓库绑定、committed run、新鲜度和一组有界的 `nextActions`:首次分析、
上下文查询、证据查询、MCP 追踪或 advisory 影响分析。动作清单是可迭代的产品提示,
不改变 artifact、provider 或 gate 的 authority;研究判断和后续 UX 次序仍可通过
decision/research 文档与 issue 修订。

### 先接查询面,全量扫描是深检模式

Agent 平时问单点问题走 MCP,不必为一个问题跑一整轮 pipeline:
Expand Down
1 change: 1 addition & 0 deletions crates/code-intel-cli/src/cli/command_catalog/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub(super) enum CommandAuthority {
#[allow(dead_code)]
pub(super) enum AuthorityCondition {
CommittedOrStaleAdvisory,
CommittedWhenPresent,
}

/// Mirrors the closed `effect` vocabulary in
Expand Down
80 changes: 64 additions & 16 deletions crates/code-intel-cli/src/cli/command_catalog/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ use super::legacy::{
use super::primary::{
execute_primary, matches_primary_pattern, parse_primary_args, parse_run_alias_args, PrimaryArgs,
};
use super::project_query::{execute_project_query, parse_project_query_args, ProjectQueryArgs};
use super::project_query::{
execute_project_query, execute_project_status, parse_project_query_args,
parse_project_status_args, render_project_status, ProjectQueryArgs, ProjectStatusArgs,
};

mod contract;
mod routes;
Expand All @@ -37,6 +40,7 @@ use routes::{resolve_command_route, resolve_legacy_route, CommandRoute};
pub(super) enum Command {
Version(VersionCommand),
Primary(PrimaryArgs),
ProjectStatus(ProjectStatusArgs),
ProjectQuery(ProjectQueryArgs),
Compatibility(CompatibilityCommand),
Legacy(LegacyCommand),
Expand Down Expand Up @@ -142,6 +146,7 @@ pub(super) enum CommandError {
message: String,
},
Project {
json: bool,
kind: &'static str,
exit_code: i32,
message: String,
Expand Down Expand Up @@ -187,11 +192,29 @@ pub(super) fn parse_command(raw: &[String]) -> std::result::Result<Command, Comm
exit_code: 64,
message,
}),
Some(CommandRoute::ProjectStatus(_)) => parse_project_status_args(&raw[1..])
.map(Command::ProjectStatus)
.map_err(|message| {
if raw.iter().any(|argument| argument == "--json") {
CommandError::Project {
json: true,
kind: "usage",
exit_code: 64,
message,
}
} else {
CommandError::Usage {
message,
exit_code: 64,
}
}
}),
Some(CommandRoute::ProjectQuery(_)) => parse_project_query_args(&raw[1..])
.map(Command::ProjectQuery)
.map_err(|message| {
if raw.iter().any(|argument| argument == "--json") {
CommandError::Project {
json: true,
kind: "usage",
exit_code: 64,
message,
Expand Down Expand Up @@ -269,6 +292,20 @@ pub(super) fn execute_command(
String::new(),
)),
Err(error) => Err(CommandError::Project {
json: true,
kind: error.kind(),
exit_code: error.exit_code(),
message: error.message().to_string(),
}),
},
Command::ProjectStatus(arguments) => match execute_project_status(&arguments) {
Ok(output) => Ok(buffered_outcome(
0,
render_project_status(&arguments, &output),
String::new(),
)),
Err(error) => Err(CommandError::Project {
json: arguments.json,
kind: error.kind(),
exit_code: error.exit_code(),
message: error.message().to_string(),
Expand Down Expand Up @@ -321,24 +358,35 @@ pub(super) fn render_outcome(
exit_code: 1,
},
Err(CommandError::Project {
json,
kind,
exit_code,
message,
}) => RenderedOutcome {
stdout: format!(
"{}\n",
serde_json::to_string(&json!({
"schema": "code-intel-project-error.v1",
"outcome": "error",
"kind": kind,
"exitCode": exit_code,
"diagnostic": message,
}))
.expect("project error serializes")
),
stderr: String::new(),
exit_code,
},
}) => {
if json {
RenderedOutcome {
stdout: format!(
"{}\n",
serde_json::to_string(&json!({
"schema": "code-intel-project-error.v1",
"outcome": "error",
"kind": kind,
"exitCode": exit_code,
"diagnostic": message,
}))
.expect("project error serializes")
),
stderr: String::new(),
exit_code,
}
} else {
RenderedOutcome {
stdout: String::new(),
stderr: format!("Code Intel status error: {message}\n"),
exit_code,
}
}
}
Err(CommandError::Usage { message, exit_code }) => RenderedOutcome {
stdout: String::new(),
stderr: format!("{message}\n"),
Expand Down
4 changes: 3 additions & 1 deletion crates/code-intel-cli/src/cli/command_catalog/routes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -749,7 +749,7 @@ pub(super) const COMMAND_ROUTES: &[CommandRoute] = &[
Internal,
Internal,
&[],
stdout!("text-format:help-quick.v1", "text-format:help-full.v3"),
stdout!("text-format:help-quick.v1", "text-format:help-full.v4"),
exits!(0, 1),
"retain while this major CLI contract is supported"
),
Expand All @@ -769,6 +769,7 @@ pub(super) const COMMAND_ROUTES: &[CommandRoute] = &[
),
},
project_routes::RUN_ALIAS,
project_routes::STATUS,
project_routes::QUERY,
project_routes::PRIMARY,
];
Expand All @@ -779,6 +780,7 @@ pub(super) fn resolve_command_route(raw: &[String]) -> Option<&'static CommandRo
route.command == command || route.aliases.iter().any(|alias| alias == command)
}),
CommandRoute::RunAlias(_) => raw.first().is_some_and(|command| command == "run"),
CommandRoute::ProjectStatus(_) => raw.first().is_some_and(|command| command == "status"),
CommandRoute::ProjectQuery(_) => raw.first().is_some_and(|command| command == "query"),
CommandRoute::Primary(_) => matches_primary_pattern(raw),
CommandRoute::Raw(route) => raw.first().is_some_and(|command| {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,28 @@ pub(super) const RUN_ALIAS: super::CommandRoute =
retirement_condition: "retain as the named alias of the default production entry",
});

pub(super) const STATUS: super::CommandRoute =
super::CommandRoute::ProjectStatus(super::CommandContract {
stability: super::CommandStability::Public,
controller: super::ControllerOwnership::CommittedEvidence,
authority: super::CommandAuthority::Conditional(
super::AuthorityCondition::CommittedWhenPresent,
),
effects: &[
super::CommandEffect::RepoRead,
super::CommandEffect::ProcessSpawn,
],
output_contract: super::OutputContract::Stdout {
identities: &[
"code-intel-project-status.v1",
"code-intel-project-error.v1",
"text-format:project-status.v1",
],
},
exit_contract: super::ExitContract::Exact(&[0, 64, 65, 74]),
retirement_condition: "retain as the project readiness and next-action entry",
});

pub(super) const QUERY: super::CommandRoute =
super::CommandRoute::ProjectQuery(super::CommandContract {
stability: super::CommandStability::Public,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub(in crate::cli::command_catalog) struct VersionRoute {
pub(in crate::cli::command_catalog) enum CommandRoute {
Version(VersionRoute),
RunAlias(CommandContract),
ProjectStatus(CommandContract),
ProjectQuery(CommandContract),
Primary(CommandContract),
Raw(RawRoute),
Expand Down
35 changes: 32 additions & 3 deletions crates/code-intel-cli/src/cli/command_catalog/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ fn parser_exposes_typed_commands_and_hides_route_slicing() {
])
.unwrap();
assert!(matches!(project_query, Command::ProjectQuery(_)));

let project_status = parse_command(&["status".into(), "--json".into()]).unwrap();
assert!(matches!(project_status, Command::ProjectStatus(_)));
}

#[test]
Expand Down Expand Up @@ -197,6 +200,15 @@ fn command_inventory_is_complete_unique_and_dispatchable() {
Some(CommandRoute::RunAlias(_))
));
}
CommandRoute::ProjectStatus(contract) => {
contract.assert_complete("status");
assert!(!spellings.contains(&"status".to_string()));
spellings.push("status".into());
assert!(matches!(
resolve_command_route(&["status".into()]),
Some(CommandRoute::ProjectStatus(_))
));
}
CommandRoute::ProjectQuery(contract) => {
contract.assert_complete("query");
assert!(!spellings.contains(&"query".to_string()));
Expand Down Expand Up @@ -248,7 +260,9 @@ fn command_contracts_use_concrete_output_and_exit_types() {
for route in COMMAND_ROUTES {
let contract = match route {
CommandRoute::Version(route) => &route.contract,
CommandRoute::RunAlias(contract) | CommandRoute::ProjectQuery(contract) => contract,
CommandRoute::RunAlias(contract)
| CommandRoute::ProjectStatus(contract)
| CommandRoute::ProjectQuery(contract) => contract,
CommandRoute::Primary(contract) => contract,
CommandRoute::Raw(route) => &route.contract,
CommandRoute::Legacy(route) => &route.contract,
Expand All @@ -269,6 +283,9 @@ fn unified_route_inventory_owns_version_primary_raw_and_legacy_dispatch() {
assert!(COMMAND_ROUTES
.iter()
.any(|route| matches!(route, CommandRoute::RunAlias(_))));
assert!(COMMAND_ROUTES
.iter()
.any(|route| matches!(route, CommandRoute::ProjectStatus(_))));
assert!(COMMAND_ROUTES
.iter()
.any(|route| matches!(route, CommandRoute::ProjectQuery(_))));
Expand Down Expand Up @@ -308,6 +325,17 @@ fn command_authority_and_effect_contracts_cover_conditional_and_mutating_routes(
raw("change", Some("impact")).contract.authority,
CommandAuthority::Conditional(AuthorityCondition::CommittedOrStaleAdvisory)
);
let status = COMMAND_ROUTES
.iter()
.find_map(|route| match route {
CommandRoute::ProjectStatus(contract) => Some(contract),
_ => None,
})
.expect("registered status command");
assert_eq!(
status.authority,
CommandAuthority::Conditional(AuthorityCondition::CommittedWhenPresent)
);
assert_eq!(
raw("artifact", Some("index")).contract.effects,
&[CommandEffect::RepoRead, CommandEffect::LocalWrite]
Expand Down Expand Up @@ -527,7 +555,8 @@ fn full_help_documents_every_registered_route_alias() {
}

#[test]
fn full_help_alias_discoverability_has_a_v3_output_contract() {
fn full_help_alias_discoverability_has_a_v4_output_contract() {
assert!(FULL_HELP_TEXT.contains(" status [<repo>] [--json]"));
let help = COMMAND_ROUTES
.iter()
.find_map(|route| match route {
Expand All @@ -539,7 +568,7 @@ fn full_help_alias_discoverability_has_a_v3_output_contract() {
assert_eq!(
help.contract.output_contract,
OutputContract::Stdout {
identities: &["text-format:help-quick.v1", "text-format:help-full.v3"]
identities: &["text-format:help-quick.v1", "text-format:help-full.v4"]
}
);
}
Expand Down
1 change: 1 addition & 0 deletions crates/code-intel-cli/src/cli/legacy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1125,6 +1125,7 @@ Commands:
--version|-V [--json]
help|--help|-h [--all]
run [<repo>] [--mode lite|normal|full] [--json]
status [<repo>] [--json]
query [<repo>] --kind evidence [--artifact-schema <schema>] [--type <artifact-type>] [--contains <text>] [--limit <1..100>] --json
report --repo <path> [--artifact-root <path>] [--json]
resume --repo <path> [--artifact-root <path>] [--json]
Expand Down
Loading
Loading