feat: per-change-block hunk actions (#50), codex lanes without a pipeline, macOS LaunchAgent

- git (#50): stage-hunk/discard-hunk take an optional `block` — one run of
  consecutive +/- lines inside an @@ hunk. `onlyBlock` (server/lib/git-diff.js)
  cuts git's fresh hunk down to it, folding the other blocks into the side
  being checked; LaneDiffView hangs one review bar per block (unified + split)
  and its arrows walk blocks.
- git exclude: entries AgentHub writes for a lane go to the COMMON git dir with
  the lane's path prefix (server/lib/git-exclude.js, used by router-apply and
  lane-mcp) — a linked worktree's own info/exclude is never read by git, and a
  subdir lane has no .git. MCP sync no longer fails with ENOTDIR on a worktree
  lane or creates a stray .git in a non-repo lane.
- codex lanes carry no pipeline: codex-ingest mirrors only idle/running,
  codex-nudge is removed, agents install writes nothing for codex, the
  Workspace hides stage/pipeline/feature pickers/PipelineMap for them, and
  every server start clears leftover codex stage data and the old AGENTS.md
  stage block.
- Changes panel: a whole file opens for reading and enters the editor on a
  click on its text (caret at the clicked line), not automatically.
- macOS: LaunchAgent start-at-login (scripts/launchd-service.js, picked by
  scripts/service-manager.js).
- Adds the AgentHub introduction slides and their image-embedding script.
This commit is contained in:
2026-09-25 17:30:28 +07:00
parent 3f6e903c22
commit 952e8584f0
48 changed files with 3336 additions and 1579 deletions
+33 -18
View File
@@ -1463,16 +1463,24 @@ async function cmdStart(flags) {
}
/**
* Installs/removes a systemd `--user` service so the dashboard survives
* reboots without anything (Claude Code included) needing to start it first.
* Linux/systemd only; other platforms are told to keep using `agenthub start`.
* Installs/removes a systemd `--user` service (Linux) or a LaunchAgent
* (macOS) so the dashboard survives reboots without anything (Claude Code
* included) needing to start it first. Elsewhere, `agenthub start` by hand.
*/
async function cmdService(sub, flags) {
const svc = require(path.join(REPO_ROOT, "scripts", "systemd-service.js"));
const svc = require(path.join(REPO_ROOT, "scripts", "service-manager.js"));
if (!svc.systemctlAvailable()) {
console.error(c.red("✖ systemd --user is not available on this machine."));
console.error(
c.dim(" Auto-start on boot needs Linux + systemd. Use `agenthub start` manually instead.")
c.red(
`✖ ${svc.kind === "launchd" ? "launchd (a GUI login session)" : "systemd --user"} is not available here.`
)
);
console.error(
c.dim(
svc.kind === "launchd"
? " A LaunchAgent needs the user logged in at the Mac itself, not only over SSH. Use `agenthub start` meanwhile."
: " Auto-start on boot needs Linux + systemd. Use `agenthub start` manually instead."
)
);
process.exit(1);
}
@@ -1493,24 +1501,27 @@ async function cmdService(sub, flags) {
description: "AgentHub dashboard server",
workingDir: REPO_ROOT,
execStart: `${process.execPath} ${path.join(REPO_ROOT, "server", "index.js")}`,
argv: [process.execPath, path.join(REPO_ROOT, "server", "index.js")],
env,
});
if (result === "foreign") {
console.error(
c.red(
`✖ ~/.config/systemd/user/${name}.service already exists and wasn't written by agenthub.`
)
c.red(`✖ ${svc.unitPath(name)} already exists and wasn't written by agenthub.`)
);
console.error(c.dim(" Remove or rename it, then re-run `agenthub service install`."));
process.exit(1);
}
if (result === "failed") {
console.error(c.red("✖ Failed to write/enable the systemd unit."));
console.error(c.red(`✖ Failed to write/enable the ${svc.label}.`));
process.exit(1);
}
console.log(
`${c.green("✔")} systemd --user service '${name}' ${result === "unchanged" ? "already up to date" : "installed"} and enabled.`
`${c.green("✔")} ${svc.label} '${name}' ${result === "unchanged" ? "already up to date" : "installed"} and enabled.`
);
if (svc.kind === "launchd") {
console.log(c.dim(` It starts at every login. Server log: ${svc.logPath()}`));
return;
}
const lingered = svc.enableLinger();
if (lingered) {
console.log(
@@ -1531,20 +1542,24 @@ async function cmdService(sub, flags) {
if (sub === "uninstall") {
const result = svc.uninstallUserService(name);
if (result === "foreign") {
console.error(
c.red(
`✖ ~/.config/systemd/user/${name}.service wasn't written by agenthub — not touching it.`
)
);
console.error(c.red(`✖ ${svc.unitPath(name)} wasn't written by agenthub — not touching it.`));
process.exit(1);
}
console.log(
`${c.green("✔")} systemd service ${result === "absent" ? "was not installed" : "removed"}.`
`${c.green("✔")} ${svc.label} ${result === "absent" ? "was not installed" : "removed"}.`
);
return;
}
if (sub === "status") {
spawn("systemctl", ["--user", "status", `${name}.service`, "--no-pager"], { stdio: "inherit" });
if (svc.kind === "launchd") {
spawn("launchctl", ["print", `gui/${process.getuid()}/io.smartgift.${name}`], {
stdio: "inherit",
});
} else {
spawn("systemctl", ["--user", "status", `${name}.service`, "--no-pager"], {
stdio: "inherit",
});
}
return;
}
console.error(c.red("✖ Usage: agenthub service install|uninstall|status [--port N]"));