@diologue/local-agent 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +162 -2
- package/dist/cli.mjs.map +4 -4
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -726,9 +726,9 @@ var getDiff = async (cwd) => {
|
|
|
726
726
|
return [trackedDiff, ...untrackedDiffs].filter((part) => part.trim().length > 0).join("\n");
|
|
727
727
|
};
|
|
728
728
|
var runGitWithStdin = async (cwd, args, input) => {
|
|
729
|
-
const { execFile:
|
|
729
|
+
const { execFile: execFile4 } = await import("node:child_process");
|
|
730
730
|
return new Promise((resolve, reject) => {
|
|
731
|
-
const child =
|
|
731
|
+
const child = execFile4(
|
|
732
732
|
"git",
|
|
733
733
|
args,
|
|
734
734
|
{
|
|
@@ -787,6 +787,40 @@ var parseDiffPaths = (unified) => {
|
|
|
787
787
|
}
|
|
788
788
|
return paths;
|
|
789
789
|
};
|
|
790
|
+
var getRemoteUrl = async (cwd) => {
|
|
791
|
+
try {
|
|
792
|
+
return (await runGit(cwd, ["remote", "get-url", "origin"])).trim() || null;
|
|
793
|
+
} catch (err) {
|
|
794
|
+
if (err instanceof GitCommandError) return null;
|
|
795
|
+
throw err;
|
|
796
|
+
}
|
|
797
|
+
};
|
|
798
|
+
var getDefaultBranch = async (cwd) => {
|
|
799
|
+
try {
|
|
800
|
+
const ref = (await runGit(cwd, ["rev-parse", "--abbrev-ref", "origin/HEAD"])).trim();
|
|
801
|
+
const name = ref.replace(/^origin\//, "");
|
|
802
|
+
return name || "main";
|
|
803
|
+
} catch (err) {
|
|
804
|
+
if (err instanceof GitCommandError) return "main";
|
|
805
|
+
throw err;
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
var createBranch = async (cwd, name) => {
|
|
809
|
+
await runGit(cwd, ["checkout", "-b", name]);
|
|
810
|
+
};
|
|
811
|
+
var commitAll = async (cwd, message) => {
|
|
812
|
+
await runGit(cwd, ["add", "-A"]);
|
|
813
|
+
await runGit(cwd, ["commit", "-m", message]);
|
|
814
|
+
return (await runGit(cwd, ["rev-parse", "--short", "HEAD"])).trim();
|
|
815
|
+
};
|
|
816
|
+
var pushBranch = async (cwd, branch) => {
|
|
817
|
+
await runGit(cwd, ["push", "-u", "origin", branch]);
|
|
818
|
+
};
|
|
819
|
+
var branchNameForTitle = (title) => {
|
|
820
|
+
const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40).replace(/-+$/g, "");
|
|
821
|
+
const rand = Math.random().toString(36).slice(2, 7);
|
|
822
|
+
return `diologue/${slug || "change"}-${rand}`;
|
|
823
|
+
};
|
|
790
824
|
|
|
791
825
|
// src/lib/paths.ts
|
|
792
826
|
import { access, lstat, realpath, stat } from "node:fs/promises";
|
|
@@ -915,6 +949,71 @@ var pickDirectory = async () => {
|
|
|
915
949
|
return result;
|
|
916
950
|
};
|
|
917
951
|
|
|
952
|
+
// src/lib/github.ts
|
|
953
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
954
|
+
var GhError = class extends Error {
|
|
955
|
+
constructor(message, code, stderr) {
|
|
956
|
+
super(message);
|
|
957
|
+
this.code = code;
|
|
958
|
+
this.stderr = stderr;
|
|
959
|
+
this.name = "GhError";
|
|
960
|
+
}
|
|
961
|
+
};
|
|
962
|
+
var run2 = (args, cwd) => new Promise((resolve, reject) => {
|
|
963
|
+
execFile3(
|
|
964
|
+
"gh",
|
|
965
|
+
args,
|
|
966
|
+
{ cwd, timeout: 6e4, windowsHide: true },
|
|
967
|
+
(err, stdout, stderr) => {
|
|
968
|
+
if (err) {
|
|
969
|
+
const code = err.code;
|
|
970
|
+
if (code === "ENOENT") {
|
|
971
|
+
reject(
|
|
972
|
+
new GhError(
|
|
973
|
+
"The GitHub CLI (gh) isn't installed on this machine.",
|
|
974
|
+
"gh_unavailable"
|
|
975
|
+
)
|
|
976
|
+
);
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
979
|
+
reject(new GhError(stderr || err.message, "gh_failed", stderr));
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
resolve({ stdout, stderr });
|
|
983
|
+
}
|
|
984
|
+
);
|
|
985
|
+
});
|
|
986
|
+
var ghReady = async () => {
|
|
987
|
+
try {
|
|
988
|
+
await run2(["auth", "status"]);
|
|
989
|
+
return true;
|
|
990
|
+
} catch {
|
|
991
|
+
return false;
|
|
992
|
+
}
|
|
993
|
+
};
|
|
994
|
+
var createPullRequest = async (opts) => {
|
|
995
|
+
const { stdout } = await run2(
|
|
996
|
+
[
|
|
997
|
+
"pr",
|
|
998
|
+
"create",
|
|
999
|
+
"--base",
|
|
1000
|
+
opts.base,
|
|
1001
|
+
"--head",
|
|
1002
|
+
opts.head,
|
|
1003
|
+
"--title",
|
|
1004
|
+
opts.title,
|
|
1005
|
+
"--body",
|
|
1006
|
+
opts.body
|
|
1007
|
+
],
|
|
1008
|
+
opts.cwd
|
|
1009
|
+
);
|
|
1010
|
+
const url = stdout.split("\n").map((l) => l.trim()).filter(Boolean).reverse().find((l) => l.startsWith("http"));
|
|
1011
|
+
if (!url) {
|
|
1012
|
+
throw new GhError("gh pr create didn't return a PR URL.", "gh_failed", stdout);
|
|
1013
|
+
}
|
|
1014
|
+
return url;
|
|
1015
|
+
};
|
|
1016
|
+
|
|
918
1017
|
// src/routes/repo.ts
|
|
919
1018
|
var selectRepoSchema = z.object({
|
|
920
1019
|
path: z.string().min(1)
|
|
@@ -926,6 +1025,10 @@ var applyPatchSchema = z.object({
|
|
|
926
1025
|
var revertPatchSchema = z.object({
|
|
927
1026
|
unified: z.string().min(1).max(8 * 1024 * 1024)
|
|
928
1027
|
});
|
|
1028
|
+
var createPrSchema = z.object({
|
|
1029
|
+
title: z.string().min(1).max(200),
|
|
1030
|
+
body: z.string().max(2e4).optional()
|
|
1031
|
+
});
|
|
929
1032
|
var buildRepoStatus = async (resolvedPath) => {
|
|
930
1033
|
const [branch, head, dirty] = await Promise.all([
|
|
931
1034
|
getBranch(resolvedPath),
|
|
@@ -1001,6 +1104,63 @@ var createRepoRouter = (state) => {
|
|
|
1001
1104
|
message: "No desktop folder dialog is available on this machine. Type the repo path instead."
|
|
1002
1105
|
});
|
|
1003
1106
|
});
|
|
1107
|
+
router.post("/create-pr", async (req, res) => {
|
|
1108
|
+
const repo = requireSelectedRepo(state, res);
|
|
1109
|
+
if (!repo) return;
|
|
1110
|
+
const parsed = createPrSchema.safeParse(req.body);
|
|
1111
|
+
if (!parsed.success) {
|
|
1112
|
+
res.status(400).json({ error: "invalid_body", issues: parsed.error.issues });
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
const { title } = parsed.data;
|
|
1116
|
+
const body = parsed.data.body ?? "Opened from the Diologue coding agent.";
|
|
1117
|
+
const cwd = repo.path;
|
|
1118
|
+
try {
|
|
1119
|
+
if (!await ghReady()) {
|
|
1120
|
+
res.status(501).json({
|
|
1121
|
+
error: "gh_unavailable",
|
|
1122
|
+
message: "GitHub CLI (gh) isn't installed or authenticated on this machine. Run `gh auth login`, or push the branch and open the PR manually."
|
|
1123
|
+
});
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
if (!await getRemoteUrl(cwd)) {
|
|
1127
|
+
res.status(400).json({
|
|
1128
|
+
error: "no_remote",
|
|
1129
|
+
message: "This repo has no `origin` remote. Add one (git remote add origin \u2026) before creating a PR."
|
|
1130
|
+
});
|
|
1131
|
+
return;
|
|
1132
|
+
}
|
|
1133
|
+
if (!await isDirty(cwd)) {
|
|
1134
|
+
res.status(409).json({
|
|
1135
|
+
error: "nothing_to_commit",
|
|
1136
|
+
message: "There are no changes in the working tree to open a PR for."
|
|
1137
|
+
});
|
|
1138
|
+
return;
|
|
1139
|
+
}
|
|
1140
|
+
const current = await getBranch(cwd) ?? "";
|
|
1141
|
+
const onAgentBranch = current.startsWith("diologue/");
|
|
1142
|
+
const base = onAgentBranch ? await getDefaultBranch(cwd) : current || "main";
|
|
1143
|
+
const branch = onAgentBranch ? current : branchNameForTitle(title);
|
|
1144
|
+
if (!onAgentBranch) {
|
|
1145
|
+
await createBranch(cwd, branch);
|
|
1146
|
+
}
|
|
1147
|
+
await commitAll(cwd, title);
|
|
1148
|
+
await pushBranch(cwd, branch);
|
|
1149
|
+
const url = await createPullRequest({ cwd, base, head: branch, title, body });
|
|
1150
|
+
const payload = { url, branch, base };
|
|
1151
|
+
res.json(payload);
|
|
1152
|
+
} catch (err) {
|
|
1153
|
+
if (err instanceof GhError) {
|
|
1154
|
+
res.status(502).json({ error: err.code, message: err.message });
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
if (err instanceof GitCommandError) {
|
|
1158
|
+
sendGitError(res, err);
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
throw err;
|
|
1162
|
+
}
|
|
1163
|
+
});
|
|
1004
1164
|
router.get("/status", async (_req, res) => {
|
|
1005
1165
|
const repo = state.getSelectedRepo();
|
|
1006
1166
|
if (!repo) {
|