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
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ jobs:
- name: Test strand-core
run: cargo test -p strand-core

- name: Test strand-tauri
run: cargo test -p strand-tauri

- name: Clippy (deny warnings)
run: cargo clippy -p strand-core -p strand-tauri -- -D warnings

Expand Down
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.

11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@ keyboard alone, and the mouse stays first-class.
for the active repository, with the active PR for your checked-out branch
opening and being followed automatically even before the PR view is opened.
Create a PR or draft for the checked-out branch from the toolbar or command
palette, choosing its title, description, and target branch without an
implicit push. Optionally draft the editable title and description from the
palette, choosing its title, description, and target branch; Strand publishes
the checked-out branch first when it is not on the repository remote yet.
Optionally draft the editable title and description from the
committed branch delta using the configured Codex or Claude Code subscription.
Persistent Follow controls and native desktop notifications surface new
comments, review decisions, failed checks, pushes, and merged/closed state.
Expand Down Expand Up @@ -129,7 +130,11 @@ keyboard alone, and the mouse stays first-class.
unstaged changes when nothing is staged) via
your ChatGPT subscription (Codex CLI, `gpt-5.6-luna`) or Claude Code CLI
(`claude-sonnet-5`); Settings → AI for sign-in, provider choice, and CLI
health checks.
health checks. Generation is cancellable, scans conservative sensitive-file
signals before provider launch, reports partial-context coverage, preserves
the replaced draft for one-step undo, and can retry explicitly with the other
provider without changing your default. Repository-family writing profiles
keep terminology and style consistent across worktrees.
- **Fast by design** — reads go through [gix](https://github.com/GitoxideLabs/gitoxide),
writes through git2 and your system `git`. Performance targets live in
[`PRD.md`](./PRD.md) §8 and are measured in
Expand Down
20 changes: 18 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -1632,8 +1632,9 @@ command palette now open a keyboard-operable creation dialog for the checked-out
branch. GitHub and Azure DevOps creation share one typed IPC path and support a
title, Markdown description, target branch, and draft state through the
provider CLI. A successful PR opens immediately and joins the follow monitor;
creation deliberately requires the source branch to exist remotely instead of
hiding an implicit push. The dialog can also fill or replace its editable title
when the checked-out source branch is absent from the detected remote, creation
first publishes current `HEAD` with a non-force push and preserves any existing
upstream. The dialog can also fill or replace its editable title
and Markdown description through the configured Codex or Claude Code CLI. That
read-only suggestion uses committed changes from the target branch's merge base
to local `HEAD`; it never includes unstaged drafts or creates the PR itself.
Expand Down Expand Up @@ -1664,6 +1665,9 @@ is preflighted before Strand reports that the browser or CLI flow has started.
- ☑ Commit message suggestions from staged diffs, or all unstaged changes
when nothing is staged (Codex / Claude Code CLIs)
- ☑ Pull-request title/description suggestions from committed branch diffs
- ☑ Isolated, cancellable provider execution with sensitive-file confirmation,
deterministic bounded context, per-repository writing profiles, coverage,
undo, and explicit alternate-provider retry
- ◐ Built-in PR review surface — moved into 1.0 with the GitHub/Azure read-only
foundation; GitLab/Bitbucket adapters and review/merge parity continue here.

Expand All @@ -1674,6 +1678,18 @@ Codex CLI (`codex login` / `codex exec`) for ChatGPT Plus, Claude Code CLI
AI for sign-in; CommitBar sparkle button; palette + ⌘⇧M. Verified:
`cargo test -p strand-tauri`, `pnpm --filter ./ui exec tsc --noEmit`.

**AI writing hardening shipped (2026-07-14):** Codex now runs from an empty
temporary directory with repository access and web search disabled; both vendor
CLIs use bounded output, process-tree cancellation, and a shared untrusted-data
prompt contract. Commit and PR generation scan conservative sensitive signals
before launch, require a fingerprinted include/exclude decision, rank bounded
large-change context deterministically, report coverage/provider identity, and
support repository-family writing profiles, Cancel, Undo, alternate-provider
retry, and an AI-first PR palette action. CI now exercises the Tauri suite and
AI subprocess lifecycle in the normal Linux Rust gate; the dedicated Windows
job was removed because its full Tauri build took roughly five minutes for a
one-second platform-specific test suite.

---

## Cross-cutting tracks (run in parallel with all milestones)
Expand Down
26 changes: 23 additions & 3 deletions TASKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1290,10 +1290,15 @@ tree: watch the agent work, review fast, accept or reject safely.
(`repo_pull_request_create`, `PullRequestCreateDialog`): the PR toolbar and
command palette create GitHub or Azure DevOps PRs with title, description,
target branch, and draft state through the signed-in provider CLI, then
open and automatically follow the result. Strand never silently pushes the
source branch as part of PR creation. **Fill with Codex/Claude Code** uses
open and automatically follow the result. When the checked-out source branch
is missing from the detected remote, Strand pushes current `HEAD` first and
sets upstream only when none exists (`push_current_to_remote`). **Fill with Codex/Claude Code** uses
the configured AI subscription to draft editable title/description text
from the committed merge-base diff (`repo_suggest_pull_request`).
from the committed merge-base diff (`repo_suggest_pull_request`). The
creation shell is viewport-bounded with a scrolling body and pinned footer,
so resizing a long description cannot hide Cancel/Create PR. GitHub's
missing-head/base GraphQL failures are translated into actionable guidance
instead of exposing raw SHA errors.
- ☑ Seamless stale-while-revalidate refresh (`PullRequests.tsx`): populated
list/detail/tabs/drafts/scroll stay mounted during updates and failures;
lightweight activity gates rich-detail reloads, patches reload only for a
Expand Down Expand Up @@ -1774,6 +1779,21 @@ extraction above as prerequisite. **Do not start before 1.0 ships**
- ☑ Broken vendor-CLI installs stay distinct from signed-out sessions
(`AiProviderStatus.error`, auth-failure classification, and `--version`
login preflight prevent false “browser opened” messages)
- ☑ AI provider execution hardened and cancellable (`AiProviderAdapter`,
canonical CLI resolution, isolated read-only Codex cwd/argv, bounded
stdout/stderr, process-group/Windows Job Object teardown, and shared
network/AI `repo_cancel_op` registry)
- ☑ Sensitive-input confirmation contract (`AiGenerationOutcome` scan →
exclude/include fingerprint retry; classifications contain paths and kinds
only, and changed diffs invalidate confirmation)
- ☑ Deterministic large-change context and writing UX (`AiInputCoverage`,
200-file/4 KB manifest, ranked 8-patch/12 KB context with 3 KB per-file
caps, recent HEAD subjects, `common_dir` repository writing profiles,
coverage/provider labels, Cancel, one-step undo, cross-provider retry, and
“Draft pull request with AI…” palette action)
- ☑ AI subprocess lifecycle tests run in the normal Linux Tauri gate; the
dedicated Linux/Windows matrix was removed after its Windows full-crate build
added roughly five minutes to exercise a one-second scoped suite
- ☐ Rebase reword suggestions (share CommitBar generator)
- ☐ Conflict-resolution hints — PRD Q3 follow-up

Expand Down
45 changes: 45 additions & 0 deletions crates/strand-core/src/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,26 @@ impl Repo {
run_git_streaming(&self.path, &args, on_progress, cancel)
}

/// Push the current branch to an explicit remote. This is used by hosted
/// PR creation when the source branch does not exist on that remote yet.
/// Existing upstream configuration is preserved unless `set_upstream` is
/// requested explicitly.
pub fn push_current_to_remote(
&self,
remote: &str,
set_upstream: bool,
on_progress: impl FnMut(Progress),
cancel: Option<&CancelHandle>,
) -> Result<NetworkOutcome> {
validate_remote_arg(remote, "remote")?;
let mut args = vec!["push", "--progress"];
if set_upstream {
args.push("--set-upstream");
}
args.extend(["--", remote, "HEAD"]);
run_git_streaming(&self.path, &args, on_progress, cancel)
}

/// A freshly-created local branch has no push destination under Git's
/// default `push.default=simple`. Establish `origin/<branch>` on its first
/// push, but leave every configured push route (upstream, pushRemote, or
Expand Down Expand Up @@ -704,4 +724,29 @@ mod tests {
drop(repo);
let _ = std::fs::remove_dir_all(base);
}

#[test]
fn explicit_remote_push_preserves_an_existing_upstream() {
let (repo, local, base) = push_fixture();
let other = base.join("other.git");
git(&base, &["init", "--bare", other.to_str().unwrap()]);
git(&local, &["remote", "add", "other", other.to_str().unwrap()]);
git(&local, &["config", "branch.topic.remote", "other"]);
git(&local, &["config", "branch.topic.merge", "refs/heads/topic"]);

repo.push_current_to_remote("origin", false, |_| {}, None)
.unwrap();

assert_eq!(
git(&local, &["config", "--get", "branch.topic.remote"]),
"other"
);
assert_eq!(
git(&local, &["rev-parse", "HEAD"]),
git(&local, &["rev-parse", "refs/remotes/origin/topic"])
);

drop(repo);
let _ = std::fs::remove_dir_all(base);
}
}
10 changes: 10 additions & 0 deletions crates/strand-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ tracing.workspace = true
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tempfile = "3"

[target.'cfg(unix)'.dependencies]
libc = "0.2"

[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.61", features = [
"Win32_Foundation",
"Win32_System_JobObjects",
"Win32_System_Threading",
] }

[features]
# Enables Tauri's custom-protocol production build (set by `tauri build`).
custom-protocol = ["tauri/custom-protocol"]
Loading
Loading