Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
162 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

g1t coordinates the work and the person's own sessions do it: Ask g1t reads g1t with the same tools any agent gets, answers in one conversation shared with the @g1t direct message in Chat, and hands work to sessions in the person's own harness, whose subagents g1t records, ties to issues and pull requests, and lets see each other.1//! Starting work on an issue in a session of its own, on the person's own
2//! Claude (the integrations service, `src/agent_sessions.rs`).
3//!
4//! g1t runs no model. Work on an issue happens in someone's own harness:
5//! their Claude Code with the g1t plugin, or a Claude Managed Agents
6//! session that g1t starts with the workspace's own Anthropic key. Either
7//! way the session is recorded on g1t (`work::Session`) and tied to the
8//! issue and the pull requests it makes. `start_agent_session` is what
9//! Ask, chat and the issue page call to hand work off.
10
11use serde::{Deserialize, Serialize};
12
13use crate::repos::RepoPath;
14use crate::work::Session;
15use crate::{User, Viewer};
16
17/// What kind of work a session is asked for. A hint in its prompt.
18#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
19#[serde(rename_all = "snake_case")]
20pub enum AgentMode {
21 /// Work out how, and say so on the issue; change nothing.
22 Plan,
23 /// Make the change and open a pull request.
24 #[default]
25 Implement,
26 /// Review the issue's pull request.
27 Review,
28}
29
30impl AgentMode {
31 pub fn as_str(self) -> &'static str {
32 match self {
33 AgentMode::Plan => "plan",
34 AgentMode::Implement => "implement",
35 AgentMode::Review => "review",
36 }
37 }
38
39 pub fn parse(name: &str) -> Option<AgentMode> {
40 match name.trim().to_ascii_lowercase().as_str() {
41 "plan" => Some(AgentMode::Plan),
42 "implement" => Some(AgentMode::Implement),
43 "review" => Some(AgentMode::Review),
44 _ => None,
45 }
46 }
47}
48
49/// The most characters of an instruction kept.
50pub const MAX_INSTRUCTION_CHARS: usize = 4_000;
51
52/// `start_agent_session`: work on `issue` (or a new issue from `title` and
53/// `body`), as `actor`. Returns `Outcome<AgentSessionStarted>`.
54#[derive(Debug, Serialize, Deserialize)]
55pub struct StartAgentSessionArgs {
56 pub actor: User,
57 pub repo: RepoPath,
58 #[serde(default)]
59 pub issue: Option<u32>,
60 /// Without `issue`: a new issue's title, opened first.
61 #[serde(default)]
62 pub title: Option<String>,
63 #[serde(default)]
64 pub body: Option<String>,
65 /// What to do beyond what the issue says.
66 #[serde(default)]
67 pub instruction: Option<String>,
68 #[serde(default)]
69 pub mode: Option<AgentMode>,
70 /// `true`: a hand-off to run in Claude Code, never a session started
71 /// here. `false`: a session here or a refusal. None: a session here
72 /// when the workspace has an Anthropic connection, else the hand-off.
73 #[serde(default)]
74 pub handoff: Option<bool>,
75}
76
77/// What `start_agent_session` started, or the hand-off to run.
78#[derive(Clone, Debug, Default, Serialize, Deserialize)]
79#[serde(rename_all = "camelCase")]
80pub struct AgentSessionStarted {
81 /// The issue it works on.
82 pub issue: u32,
83 /// The session on g1t, when one was started here.
84 #[serde(default)]
85 pub session: Option<Session>,
86 /// Its page on g1t.
87 #[serde(default)]
88 pub session_url: Option<String>,
89 /// Who runs it: `anthropic`, or none for a hand-off.
90 #[serde(default)]
91 pub provider: Option<String>,
92 /// Its own id with the provider (`sesn_…`).
93 #[serde(default)]
94 pub provider_session_id: Option<String>,
95 /// Where the provider shows it, when it says.
96 #[serde(default)]
97 pub provider_session_url: Option<String>,
98 /// Whether this is a hand-off: nothing was started.
99 pub handoff: bool,
100 /// For a hand-off: the Claude Code command line that starts it.
101 #[serde(default)]
102 pub command: Option<String>,
103 /// What the session is asked: the prompt sent, or to paste.
104 pub prompt: String,
105}
106
107/// `agent_sessions_setup`: whether `viewer` can start sessions on the
108/// workspace's Anthropic key. Returns `Outcome<AgentSessionsSetup>`.
109#[derive(Debug, Serialize, Deserialize)]
110pub struct AgentSessionsSetupArgs {
111 pub viewer: Viewer,
112 pub workspace: String,
113}
114
115#[derive(Clone, Debug, Default, Serialize, Deserialize)]
116#[serde(rename_all = "camelCase")]
117pub struct AgentSessionsSetup {
118 /// A session can be started with the workspace's Anthropic key.
119 pub available: bool,
120 /// The Anthropic connection used.
121 #[serde(default)]
122 pub connection_name: Option<String>,
123 /// The model sessions run on.
124 #[serde(default)]
125 pub model: Option<String>,
126 /// Why not, when not.
127 #[serde(default)]
128 pub problem: Option<String>,
129}
130
131/// `sync_agent_session`: brings a Managed Agents session's record on g1t
132/// up to date now (it is otherwise followed every minute). Returns
133/// `Outcome<bool>`: whether it is still going.
134#[derive(Debug, Serialize, Deserialize)]
135pub struct SyncAgentSessionArgs {
136 pub viewer: Viewer,
137 pub repo: RepoPath,
138 /// The g1t session (`ses_…`).
139 pub id: String,
140}
141
142/// `stop_agent_session`: interrupts a Managed Agents session and ends its
143/// record. Only whoever started it. Returns `Outcome<bool>`.
144#[derive(Debug, Serialize, Deserialize)]
145pub struct StopAgentSessionArgs {
146 pub actor: User,
147 pub repo: RepoPath,
148 pub id: String,
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn modes_are_named_one_way() {
157 assert_eq!(AgentMode::parse("Review"), Some(AgentMode::Review));
158 assert_eq!(AgentMode::parse("nap"), None);
159 assert_eq!(serde_json::to_value(AgentMode::Plan).unwrap(), "plan");
160 assert_eq!(AgentMode::default(), AgentMode::Implement);
161 }
162}