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:
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* @file Keeps files AgentHub writes into a lane's directory out of git, through
|
||||
* the one `info/exclude` git actually reads for that lane. That is the COMMON
|
||||
* git dir's, not the lane's own: a linked worktree — every managed lane — has a
|
||||
* private `.git/worktrees/<name>/`, and git never reads an exclude file there.
|
||||
* A lane that is a subdirectory of its repo has no `.git` of its own at all.
|
||||
*
|
||||
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
||||
*/
|
||||
|
||||
const { execFileSync } = require("node:child_process");
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
|
||||
// Hook variables that would point git at ANOTHER repo (the one whose hook, or
|
||||
// shell, started us) — the same set `worktree.js`'s git() scrubs. With
|
||||
// GIT_DIR set, `rev-parse` would name that repo and the entry land there.
|
||||
const REPO_VARS = [
|
||||
"GIT_DIR",
|
||||
"GIT_WORK_TREE",
|
||||
"GIT_INDEX_FILE",
|
||||
"GIT_COMMON_DIR",
|
||||
"GIT_OBJECT_DIRECTORY",
|
||||
"GIT_ALTERNATE_OBJECT_DIRECTORIES",
|
||||
"GIT_PREFIX",
|
||||
"GIT_NAMESPACE",
|
||||
];
|
||||
|
||||
/**
|
||||
* Idempotently exclude `relPaths` (relative to `laneDir`), each anchored to the
|
||||
* lane's own path so the entry hides nothing outside it. Best effort: a lane
|
||||
* that is not a repo, or a git dir that cannot be written, is left alone —
|
||||
* never a reason to fail the write that needed the exclude.
|
||||
*
|
||||
* @param {string} laneDir
|
||||
* @param {string[]} relPaths e.g. [".mcp.json", ".claude/.cc-config-backups/"]
|
||||
*/
|
||||
function excludeFromGit(laneDir, relPaths) {
|
||||
try {
|
||||
const env = { ...process.env };
|
||||
for (const name of REPO_VARS) delete env[name];
|
||||
const [commonDir, prefix = ""] = execFileSync(
|
||||
"git",
|
||||
["rev-parse", "--git-common-dir", "--show-prefix"],
|
||||
{ cwd: laneDir, env, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
|
||||
).split("\n");
|
||||
const lines = relPaths.map((rel) => `/${prefix}${rel}`);
|
||||
const excludePath = path.join(path.resolve(laneDir, commonDir), "info", "exclude");
|
||||
fs.mkdirSync(path.dirname(excludePath), { recursive: true });
|
||||
let existing = fs.existsSync(excludePath) ? fs.readFileSync(excludePath, "utf8") : "";
|
||||
const present = new Set(existing.split("\n"));
|
||||
const missing = lines.filter((line) => !present.has(line));
|
||||
if (missing.length === 0) return;
|
||||
if (existing && !existing.endsWith("\n")) existing += "\n";
|
||||
fs.writeFileSync(excludePath, `${existing}${missing.join("\n")}\n`);
|
||||
} catch {
|
||||
/* not a repo, or unwritable — see above */
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { excludeFromGit };
|
||||
Reference in New Issue
Block a user