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
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,13 @@ Open a pull request or issue:
gzg openclaw/openclaw#81834
gzg https://github.com/openclaw/openclaw/pull/81834
gzg https://github.com/openclaw/openclaw/issues/88499
gzg 81834
```

When you run `gzg` from inside a git checkout with a GitHub remote, a bare
number uses that repository. For example, `gzg 81834` inside a checkout of
`openclaw/openclaw` opens `openclaw/openclaw#81834`.

Run `gzg` again from the same terminal context to restore the last ghzinga
dashboard for that pane, tmux pane, Herdr pane, working tree, or named session.
Use `--new` to start a separate saved session, `--no-restore` to ignore saved
Expand Down
4 changes: 2 additions & 2 deletions captures/ghzinga-issue-88499/large/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"title": "openai-responses provider: 404 on previous_response_id when store=false (default)",
"mode": "issue",
"binary": "target/debug/gzg",
"git_commit": "d1c4c23a3cc65a3dd83871080ad6fcf19be99d14",
"git_commit": "b5b380107ac995b3a2bae8bd03037e0f8e1188bd",
"config_path": "captures/ghzinga-issue-88499/capture-empty-config.toml",
"requested_columns": 160,
"requested_rows": 50,
Expand Down Expand Up @@ -113,5 +113,5 @@
}
],
"actual_tmux_size": "160x50",
"app_tree_hash": "6ae1209dc8e8616c4c3261c38aa550b693d11b9c6052ad724339380e053c3813"
"app_tree_hash": "da9cc487fc389c235f2a0e7757d9db29532c8c3fca520e60c8da4b5f45d18cc7"
}
4 changes: 2 additions & 2 deletions captures/ghzinga-issue-88499/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"title": "openai-responses provider: 404 on previous_response_id when store=false (default)",
"mode": "issue",
"binary": "target/debug/gzg",
"git_commit": "d1c4c23a3cc65a3dd83871080ad6fcf19be99d14",
"git_commit": "b5b380107ac995b3a2bae8bd03037e0f8e1188bd",
"config_path": "captures/ghzinga-issue-88499/capture-empty-config.toml",
"offline_fixture": "fixtures/issue-88499.json",
"offline_resource_fixtures": [],
Expand All @@ -24,5 +24,5 @@
"rows": 50
}
],
"app_tree_hash": "6ae1209dc8e8616c4c3261c38aa550b693d11b9c6052ad724339380e053c3813"
"app_tree_hash": "da9cc487fc389c235f2a0e7757d9db29532c8c3fca520e60c8da4b5f45d18cc7"
}
4 changes: 2 additions & 2 deletions captures/ghzinga-issue-88499/medium/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"title": "openai-responses provider: 404 on previous_response_id when store=false (default)",
"mode": "issue",
"binary": "target/debug/gzg",
"git_commit": "d1c4c23a3cc65a3dd83871080ad6fcf19be99d14",
"git_commit": "b5b380107ac995b3a2bae8bd03037e0f8e1188bd",
"config_path": "captures/ghzinga-issue-88499/capture-empty-config.toml",
"requested_columns": 120,
"requested_rows": 36,
Expand Down Expand Up @@ -113,5 +113,5 @@
}
],
"actual_tmux_size": "120x36",
"app_tree_hash": "6ae1209dc8e8616c4c3261c38aa550b693d11b9c6052ad724339380e053c3813"
"app_tree_hash": "da9cc487fc389c235f2a0e7757d9db29532c8c3fca520e60c8da4b5f45d18cc7"
}
4 changes: 2 additions & 2 deletions captures/ghzinga-issue-88499/mouse-smoke/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"fixtures/issue-66943.json"
],
"binary": "target/debug/gzg",
"git_commit": "d1c4c23a3cc65a3dd83871080ad6fcf19be99d14",
"git_commit": "b5b380107ac995b3a2bae8bd03037e0f8e1188bd",
"config_path": "captures/ghzinga-issue-88499/mouse-smoke/capture-empty-config.toml",
"command": "cd . && TERM=xterm-256color GZG_CONFIG_PATH=./captures/ghzinga-issue-88499/mouse-smoke/capture-empty-config.toml GZG_STATE_HOME=./captures/ghzinga-issue-88499/mouse-smoke/.capture-state GZG_CACHE_HOME=./captures/ghzinga-issue-88499/mouse-smoke/.capture-cache BROWSER=./captures/ghzinga-issue-88499/mouse-smoke/capture-open-url.sh GZG_COPY_COMMAND=./captures/ghzinga-issue-88499/mouse-smoke/capture-copy-url.sh ./target/debug/gzg https://github.com/openclaw/openclaw/issues/88499 --offline-fixture ./captures/ghzinga-issue-88499/mouse-smoke/navigation-fixture.json --offline-resource-fixture ./fixtures/issue-66943.json --no-restore --refresh-seconds 0",
"actual_tmux_size": "120x36",
Expand Down Expand Up @@ -120,5 +120,5 @@
"ansi": "70_mouse_quit_confirm.ansi"
}
],
"app_tree_hash": "6ae1209dc8e8616c4c3261c38aa550b693d11b9c6052ad724339380e053c3813"
"app_tree_hash": "da9cc487fc389c235f2a0e7757d9db29532c8c3fca520e60c8da4b5f45d18cc7"
}
4 changes: 2 additions & 2 deletions captures/ghzinga-issue-88499/narrow/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"title": "openai-responses provider: 404 on previous_response_id when store=false (default)",
"mode": "issue",
"binary": "target/debug/gzg",
"git_commit": "d1c4c23a3cc65a3dd83871080ad6fcf19be99d14",
"git_commit": "b5b380107ac995b3a2bae8bd03037e0f8e1188bd",
"config_path": "captures/ghzinga-issue-88499/capture-empty-config.toml",
"requested_columns": 80,
"requested_rows": 24,
Expand Down Expand Up @@ -113,5 +113,5 @@
}
],
"actual_tmux_size": "80x24",
"app_tree_hash": "6ae1209dc8e8616c4c3261c38aa550b693d11b9c6052ad724339380e053c3813"
"app_tree_hash": "da9cc487fc389c235f2a0e7757d9db29532c8c3fca520e60c8da4b5f45d18cc7"
}
4 changes: 2 additions & 2 deletions captures/ghzinga-pr-81834/large/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"title": "feat(senseaudio): add SenseAudio TTS provider",
"mode": "pr",
"binary": "target/debug/gzg",
"git_commit": "d1c4c23a3cc65a3dd83871080ad6fcf19be99d14",
"git_commit": "b5b380107ac995b3a2bae8bd03037e0f8e1188bd",
"config_path": "captures/ghzinga-pr-81834/capture-empty-config.toml",
"requested_columns": 160,
"requested_rows": 50,
Expand Down Expand Up @@ -183,5 +183,5 @@
}
],
"actual_tmux_size": "160x50",
"app_tree_hash": "6ae1209dc8e8616c4c3261c38aa550b693d11b9c6052ad724339380e053c3813"
"app_tree_hash": "da9cc487fc389c235f2a0e7757d9db29532c8c3fca520e60c8da4b5f45d18cc7"
}
4 changes: 2 additions & 2 deletions captures/ghzinga-pr-81834/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"title": "feat(senseaudio): add SenseAudio TTS provider",
"mode": "pr",
"binary": "target/debug/gzg",
"git_commit": "d1c4c23a3cc65a3dd83871080ad6fcf19be99d14",
"git_commit": "b5b380107ac995b3a2bae8bd03037e0f8e1188bd",
"config_path": "captures/ghzinga-pr-81834/capture-empty-config.toml",
"offline_fixture": null,
"offline_resource_fixtures": [],
Expand All @@ -24,5 +24,5 @@
"rows": 50
}
],
"app_tree_hash": "6ae1209dc8e8616c4c3261c38aa550b693d11b9c6052ad724339380e053c3813"
"app_tree_hash": "da9cc487fc389c235f2a0e7757d9db29532c8c3fca520e60c8da4b5f45d18cc7"
}
4 changes: 2 additions & 2 deletions captures/ghzinga-pr-81834/medium/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"title": "feat(senseaudio): add SenseAudio TTS provider",
"mode": "pr",
"binary": "target/debug/gzg",
"git_commit": "d1c4c23a3cc65a3dd83871080ad6fcf19be99d14",
"git_commit": "b5b380107ac995b3a2bae8bd03037e0f8e1188bd",
"config_path": "captures/ghzinga-pr-81834/capture-empty-config.toml",
"requested_columns": 120,
"requested_rows": 36,
Expand Down Expand Up @@ -183,5 +183,5 @@
}
],
"actual_tmux_size": "120x36",
"app_tree_hash": "6ae1209dc8e8616c4c3261c38aa550b693d11b9c6052ad724339380e053c3813"
"app_tree_hash": "da9cc487fc389c235f2a0e7757d9db29532c8c3fca520e60c8da4b5f45d18cc7"
}
4 changes: 2 additions & 2 deletions captures/ghzinga-pr-81834/mouse-smoke/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"fixtures/issue-66943.json"
],
"binary": "target/debug/gzg",
"git_commit": "d1c4c23a3cc65a3dd83871080ad6fcf19be99d14",
"git_commit": "b5b380107ac995b3a2bae8bd03037e0f8e1188bd",
"config_path": "captures/ghzinga-pr-81834/mouse-smoke/capture-empty-config.toml",
"command": "cd . && TERM=xterm-256color GZG_CONFIG_PATH=./captures/ghzinga-pr-81834/mouse-smoke/capture-empty-config.toml GZG_STATE_HOME=./captures/ghzinga-pr-81834/mouse-smoke/.capture-state GZG_CACHE_HOME=./captures/ghzinga-pr-81834/mouse-smoke/.capture-cache BROWSER=./captures/ghzinga-pr-81834/mouse-smoke/capture-open-url.sh GZG_COPY_COMMAND=./captures/ghzinga-pr-81834/mouse-smoke/capture-copy-url.sh ./target/debug/gzg 'openclaw/openclaw#81834' --offline-fixture ./captures/ghzinga-pr-81834/mouse-smoke/navigation-fixture.json --offline-resource-fixture ./fixtures/issue-66943.json --no-restore --refresh-seconds 0",
"actual_tmux_size": "120x36",
Expand Down Expand Up @@ -240,5 +240,5 @@
],
"load_full_fixture": "captures/ghzinga-pr-81834/mouse-smoke/load-full-fixture.json",
"load_full_command": "cd . && TERM=xterm-256color GZG_CONFIG_PATH=./captures/ghzinga-pr-81834/mouse-smoke/capture-empty-config.toml GZG_STATE_HOME=./captures/ghzinga-pr-81834/mouse-smoke/.capture-state GZG_CACHE_HOME=./captures/ghzinga-pr-81834/mouse-smoke/.capture-cache ./target/debug/gzg 'openclaw/openclaw#81834' --offline-fixture ./captures/ghzinga-pr-81834/mouse-smoke/load-full-fixture.json --no-restore --refresh-seconds 0",
"app_tree_hash": "6ae1209dc8e8616c4c3261c38aa550b693d11b9c6052ad724339380e053c3813"
"app_tree_hash": "da9cc487fc389c235f2a0e7757d9db29532c8c3fca520e60c8da4b5f45d18cc7"
}
4 changes: 2 additions & 2 deletions captures/ghzinga-pr-81834/narrow/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"title": "feat(senseaudio): add SenseAudio TTS provider",
"mode": "pr",
"binary": "target/debug/gzg",
"git_commit": "d1c4c23a3cc65a3dd83871080ad6fcf19be99d14",
"git_commit": "b5b380107ac995b3a2bae8bd03037e0f8e1188bd",
"config_path": "captures/ghzinga-pr-81834/capture-empty-config.toml",
"requested_columns": 80,
"requested_rows": 24,
Expand Down Expand Up @@ -183,5 +183,5 @@
}
],
"actual_tmux_size": "80x24",
"app_tree_hash": "6ae1209dc8e8616c4c3261c38aa550b693d11b9c6052ad724339380e053c3813"
"app_tree_hash": "da9cc487fc389c235f2a0e7757d9db29532c8c3fca520e60c8da4b5f45d18cc7"
}
34 changes: 31 additions & 3 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub const DEFAULT_REFRESH_SECONDS: u64 = 300;
about = "Monitor a GitHub PR or issue in a terminal UI"
)]
pub struct Cli {
/// GitHub resource as URL, owner/repo#number, or owner/repo number.
/// GitHub resource as URL, owner/repo#number, owner/repo number, or bare number in a GitHub repo.
#[arg(value_name = "RESOURCE")]
pub resource: Vec<String>,

Expand Down Expand Up @@ -102,18 +102,33 @@ impl Cli {
}

pub fn parse_resource_id(&self) -> Result<ResourceId, ResourceIdError> {
self.parse_resource_id_with_repo_context(None)
}

pub fn parse_resource_id_with_repo_context(
&self,
repo_name_with_owner: Option<&str>,
) -> Result<ResourceId, ResourceIdError> {
match self.resource.as_slice() {
[single] => ResourceId::parse(single),
[single] => ResourceId::parse_with_repo_context(single, repo_name_with_owner),
[owner_repo, number] => ResourceId::from_owner_repo_number(owner_repo, number),
_ => Err(ResourceIdError::Invalid),
}
}

pub fn parse_optional_resource_id(&self) -> Result<Option<ResourceId>, ResourceIdError> {
self.parse_optional_resource_id_with_repo_context(None)
}

pub fn parse_optional_resource_id_with_repo_context(
&self,
repo_name_with_owner: Option<&str>,
) -> Result<Option<ResourceId>, ResourceIdError> {
if self.resource.is_empty() {
Ok(None)
} else {
self.parse_resource_id().map(Some)
self.parse_resource_id_with_repo_context(repo_name_with_owner)
.map(Some)
}
}
}
Expand Down Expand Up @@ -173,6 +188,19 @@ mod tests {
);
}

#[test]
fn parses_relative_resource_from_repo_context() {
let cli = Cli::parse_from(["ghzinga", "81834"]);

assert_eq!(
cli.parse_resource_id_with_repo_context(Some("openclaw/openclaw"))
.unwrap()
.canonical_name(),
"openclaw/openclaw#81834"
);
assert!(cli.parse_resource_id().is_err());
}

#[test]
fn parses_offline_fixture_flag() {
let cli = Cli::parse_from([
Expand Down
37 changes: 36 additions & 1 deletion src/domain/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ static OWNER_REPO_HASH_RE: LazyLock<Regex> = LazyLock::new(|| {

#[derive(Debug, Error, PartialEq, Eq)]
pub enum ResourceIdError {
#[error("expected a GitHub PR/issue URL, owner/repo#number, or owner/repo number")]
#[error("expected a GitHub PR/issue URL, owner/repo#number, owner/repo number, or a number from inside a GitHub repo")]
Invalid,
#[error("resource number must be positive")]
InvalidNumber,
Expand Down Expand Up @@ -48,6 +48,13 @@ pub struct ResourceId {

impl ResourceId {
pub fn parse(input: &str) -> Result<Self, ResourceIdError> {
Self::parse_with_repo_context(input, None)
}

pub fn parse_with_repo_context(
input: &str,
repo_name_with_owner: Option<&str>,
) -> Result<Self, ResourceIdError> {
let trimmed = input.trim();
if trimmed.is_empty() {
return Err(ResourceIdError::Invalid);
Expand All @@ -61,6 +68,13 @@ impl ResourceId {
return Ok(parsed);
}

if let Some(repo_name_with_owner) = repo_name_with_owner {
let number = trimmed.strip_prefix('#').unwrap_or(trimmed);
if !number.is_empty() && number.chars().all(|ch| ch.is_ascii_digit()) {
return Self::from_owner_repo_number(repo_name_with_owner, number);
}
}

Err(ResourceIdError::Invalid)
}

Expand Down Expand Up @@ -743,6 +757,27 @@ mod tests {
assert_eq!(id.canonical_name(), "openclaw/openclaw#81834");
}

#[test]
fn parses_relative_numbers_with_repo_context() {
let id = ResourceId::parse_with_repo_context("81834", Some("openclaw/openclaw")).unwrap();

assert_eq!(id.canonical_name(), "openclaw/openclaw#81834");
assert_eq!(id.kind_hint, None);

let hash_id =
ResourceId::parse_with_repo_context("#88499", Some("openclaw/openclaw")).unwrap();

assert_eq!(hash_id.canonical_name(), "openclaw/openclaw#88499");
}

#[test]
fn rejects_relative_numbers_without_repo_context() {
assert_eq!(
ResourceId::parse_with_repo_context("81834", None).unwrap_err(),
ResourceIdError::Invalid
);
}

#[test]
fn rejects_zero_number() {
assert_eq!(
Expand Down
Loading