@agimon-ai/po-git 0.0.0-alpha.28 → 0.0.0-alpha.29
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/README.md +1 -1
- package/dist/extensions/server.cjs +201 -43
- package/dist/extensions/server.cjs.map +1 -1
- package/dist/extensions/server.d.mts.map +1 -1
- package/dist/extensions/server.mjs +201 -43
- package/dist/extensions/server.mjs.map +1 -1
- package/package.json +8 -8
- package/src/prompts/po-use-git/SKILL.md +33 -30
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.mts","names":[],"sources":["../../generated/server.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"server.d.mts","names":[],"sources":["../../generated/server.ts"],"mappings":";qBAuBa,wDAAY"}
|
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import { composeServerProviderOverrides, defineServerPlugin, readPackageResource } from "@agimon-ai/po-core/serverPlugin";
|
|
6
6
|
import { composeRootHooks, composeRootInjects, defineChannel, defineHook, defineResource, defineRoot, defineRoute, defineServerTool } from "@agimon-ai/po-core/extensionFile";
|
|
7
|
+
import { PO_CODEMODE_SERVICE, readPoCodemode } from "@agimon-ai/po-core/codemode";
|
|
7
8
|
import { readPoSessionDelivery } from "@agimon-ai/po-session";
|
|
8
9
|
import { Type } from "typebox";
|
|
9
10
|
//#region src/schemas/runWorktreeTool.ts
|
|
@@ -247,7 +248,8 @@ async function executeRunWorktreeTool(params, operations, context, options = {})
|
|
|
247
248
|
content: text(describe(record, context.sessionId), `Session: ${record.sessionId}`, dirtyFiles.length === 0 ? "Clean." : `${String(dirtyFiles.length)} uncommitted file(s): ${dirtyFiles.slice(0, 20).join(", ")}`),
|
|
248
249
|
details: {
|
|
249
250
|
action: params.action,
|
|
250
|
-
records: [record]
|
|
251
|
+
records: [record],
|
|
252
|
+
dirtyFiles
|
|
251
253
|
}
|
|
252
254
|
};
|
|
253
255
|
}
|
|
@@ -271,7 +273,10 @@ async function executeRunWorktreeTool(params, operations, context, options = {})
|
|
|
271
273
|
const waiting = await operations.messages(context, params.id);
|
|
272
274
|
return {
|
|
273
275
|
content: waiting.length === 0 ? text("No messages.") : text(`${String(waiting.length)} message(s):`, ...waiting.map((entry) => `[${entry.sentAt}] ${entry.from}: ${entry.text}`)),
|
|
274
|
-
details: {
|
|
276
|
+
details: {
|
|
277
|
+
action: params.action,
|
|
278
|
+
messages: waiting
|
|
279
|
+
}
|
|
275
280
|
};
|
|
276
281
|
}
|
|
277
282
|
case WORKTREE_ACTIONS.prune: {
|
|
@@ -286,12 +291,172 @@ async function executeRunWorktreeTool(params, operations, context, options = {})
|
|
|
286
291
|
`Untracked directories, left alone: ${plan.untracked.length === 0 ? "none" : plan.untracked.join(", ")}`,
|
|
287
292
|
...plan.keptBranches.length === 0 ? [] : [`Branches kept, they hold commits: ${plan.keptBranches.join(", ")}`]
|
|
288
293
|
]),
|
|
289
|
-
details: {
|
|
294
|
+
details: {
|
|
295
|
+
action: params.action,
|
|
296
|
+
plan
|
|
297
|
+
}
|
|
290
298
|
};
|
|
291
299
|
}
|
|
292
300
|
}
|
|
293
301
|
}
|
|
294
302
|
//#endregion
|
|
303
|
+
//#region src/services/worktreeNamespace/index.ts
|
|
304
|
+
const DECLARATIONS = `type WorktreeStatus = 'spawning' | 'running' | 'closing' | 'orphaned';
|
|
305
|
+
|
|
306
|
+
interface Worktree {
|
|
307
|
+
id: string;
|
|
308
|
+
branch: string;
|
|
309
|
+
baseRef: string;
|
|
310
|
+
/** Absolute path of the checkout, under ~/.po/git/worktrees. */
|
|
311
|
+
path: string;
|
|
312
|
+
repositoryRoot: string;
|
|
313
|
+
/** The session running inside the worktree. */
|
|
314
|
+
sessionId: string;
|
|
315
|
+
parentSessionId?: string;
|
|
316
|
+
status: WorktreeStatus;
|
|
317
|
+
createdAt: string;
|
|
318
|
+
/** How your session relates to it: 'parent' spawned it, 'child' runs inside it, 'other' is neither. */
|
|
319
|
+
relationship: 'parent' | 'child' | 'other';
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
interface WorktreeMessage {
|
|
323
|
+
worktreeId: string;
|
|
324
|
+
fromSessionId: string;
|
|
325
|
+
from: 'parent' | 'child';
|
|
326
|
+
text: string;
|
|
327
|
+
sentAt: string;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
interface PrunePlan {
|
|
331
|
+
remove: Worktree[];
|
|
332
|
+
forget: Worktree[];
|
|
333
|
+
/** Kept because they hold uncommitted work. */
|
|
334
|
+
keptDirty: Worktree[];
|
|
335
|
+
untracked: string[];
|
|
336
|
+
/** Branches kept because they hold commits. */
|
|
337
|
+
keptBranches: string[];
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
declare const worktrees: {
|
|
341
|
+
/** Every worktree for this repository. */
|
|
342
|
+
list(): Promise<Worktree[]>;
|
|
343
|
+
/** One worktree and its uncommitted files. */
|
|
344
|
+
status(id: string): Promise<{ worktree: Worktree; dirtyFiles: string[] }>;
|
|
345
|
+
/**
|
|
346
|
+
* Create a worktree on a new branch and start a Po session in it, nested under this one. baseRef defaults to
|
|
347
|
+
* the current branch's remote-tracking ref, then the remote default branch; name labels it in the rail; task is
|
|
348
|
+
* the new session's first message. Setup can be slow, so give exec a larger timeoutMs.
|
|
349
|
+
*/
|
|
350
|
+
spawn(request: { branch: string; baseRef?: string; name?: string; task?: string }): Promise<Worktree>;
|
|
351
|
+
/** Stop the worktree's session and delete its checkout. Refused on a dirty tree unless force is true. */
|
|
352
|
+
close(id: string, options?: { force?: boolean }): Promise<Worktree>;
|
|
353
|
+
/** Merge the worktree's branch into this checkout. Refused when this checkout is dirty. */
|
|
354
|
+
merge(id: string, options?: { message?: string }): Promise<Worktree>;
|
|
355
|
+
/** Remove orphaned worktrees. With dryRun, only return the plan. */
|
|
356
|
+
prune(options?: { dryRun?: boolean }): Promise<PrunePlan>;
|
|
357
|
+
/**
|
|
358
|
+
* Message the other side of a worktree. Use the record with relationship 'child' to report to your parent,
|
|
359
|
+
* and 'parent' to reach your own children. 'other' records are not communication edges.
|
|
360
|
+
*/
|
|
361
|
+
send(id: string, message: string): Promise<void>;
|
|
362
|
+
/** Messages waiting for you from a worktree. */
|
|
363
|
+
messages(id: string): Promise<WorktreeMessage[]>;
|
|
364
|
+
};`;
|
|
365
|
+
const json = (value) => JSON.parse(JSON.stringify(value));
|
|
366
|
+
/** Git worktrees for code mode: the actions of `run_worktree` as methods, returning records instead of text. */
|
|
367
|
+
function createWorktreeNamespace(options) {
|
|
368
|
+
return {
|
|
369
|
+
name: "worktrees",
|
|
370
|
+
summary: "Git worktrees, each with its own Po session nested under this one: worktrees.list, status, spawn, close, merge, prune, send and messages.",
|
|
371
|
+
declarations: DECLARATIONS,
|
|
372
|
+
bind(run) {
|
|
373
|
+
if (run.sessionId !== options.execution.sessionId) throw new Error("Worktrees namespace belongs to another session.");
|
|
374
|
+
let disposed = false;
|
|
375
|
+
run.onDispose(() => {
|
|
376
|
+
disposed = true;
|
|
377
|
+
});
|
|
378
|
+
const view = (record) => ({
|
|
379
|
+
...record,
|
|
380
|
+
relationship: record.parentSessionId === options.execution.sessionId ? "parent" : record.sessionId === options.execution.sessionId ? "child" : "other"
|
|
381
|
+
});
|
|
382
|
+
/** One `run_worktree` action through the tool's own code, so the tool and the namespace cannot drift. */
|
|
383
|
+
const act = async (name, action, input) => {
|
|
384
|
+
run.signal.throwIfAborted();
|
|
385
|
+
if (disposed) throw new Error("Worktrees namespace run is disposed.");
|
|
386
|
+
options.assertAvailable();
|
|
387
|
+
const params = validateParams({
|
|
388
|
+
action,
|
|
389
|
+
...typeof input === "object" && input !== null ? input : {}
|
|
390
|
+
});
|
|
391
|
+
const context = {
|
|
392
|
+
cwd: options.execution.cwd,
|
|
393
|
+
sessionId: options.execution.sessionId
|
|
394
|
+
};
|
|
395
|
+
const started = Date.now();
|
|
396
|
+
try {
|
|
397
|
+
const result = await executeRunWorktreeTool(params, options.operations, context, { signal: run.signal });
|
|
398
|
+
options.assertAvailable();
|
|
399
|
+
run.record?.({
|
|
400
|
+
name: `worktrees.${name}`,
|
|
401
|
+
tool: RUN_WORKTREE_TOOL_NAME,
|
|
402
|
+
args: params,
|
|
403
|
+
ok: true,
|
|
404
|
+
ms: Date.now() - started,
|
|
405
|
+
result
|
|
406
|
+
});
|
|
407
|
+
return result;
|
|
408
|
+
} catch (error) {
|
|
409
|
+
run.record?.({
|
|
410
|
+
name: `worktrees.${name}`,
|
|
411
|
+
tool: RUN_WORKTREE_TOOL_NAME,
|
|
412
|
+
args: params,
|
|
413
|
+
ok: false,
|
|
414
|
+
ms: Date.now() - started,
|
|
415
|
+
error: error instanceof Error ? error.message : String(error)
|
|
416
|
+
});
|
|
417
|
+
throw error;
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
const record = (result) => view(result.details.records[0]);
|
|
421
|
+
const members = Object.create(null);
|
|
422
|
+
members.list = async () => json((await act("list", "list", {})).details.records.map(view));
|
|
423
|
+
members.status = async (id) => {
|
|
424
|
+
const { details } = await act("status", "status", { id });
|
|
425
|
+
return json({
|
|
426
|
+
worktree: view(details.records[0]),
|
|
427
|
+
dirtyFiles: details.dirtyFiles ?? []
|
|
428
|
+
});
|
|
429
|
+
};
|
|
430
|
+
members.spawn = async (request) => json(record(await act("spawn", "spawn_worktree", request)));
|
|
431
|
+
members.close = async (id, settings) => json(record(await act("close", "close_worktree", {
|
|
432
|
+
...settings,
|
|
433
|
+
id
|
|
434
|
+
})));
|
|
435
|
+
members.merge = async (id, settings) => json(record(await act("merge", "merge", {
|
|
436
|
+
...settings,
|
|
437
|
+
id
|
|
438
|
+
})));
|
|
439
|
+
members.prune = async (settings) => {
|
|
440
|
+
const { plan } = (await act("prune", "prune", settings)).details;
|
|
441
|
+
return json({
|
|
442
|
+
...plan,
|
|
443
|
+
remove: plan.remove.map(view),
|
|
444
|
+
forget: plan.forget.map(view),
|
|
445
|
+
keptDirty: plan.keptDirty.map(view)
|
|
446
|
+
});
|
|
447
|
+
};
|
|
448
|
+
members.send = async (id, message) => {
|
|
449
|
+
await act("send", "send", {
|
|
450
|
+
id,
|
|
451
|
+
message
|
|
452
|
+
});
|
|
453
|
+
};
|
|
454
|
+
members.messages = async (id) => json((await act("messages", "messages", { id })).details.messages);
|
|
455
|
+
return members;
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
//#endregion
|
|
295
460
|
//#region src/extensions/workspaces/sessions/(backend)/_lib/root.server.ts
|
|
296
461
|
function progressResult(action, label) {
|
|
297
462
|
return {
|
|
@@ -302,7 +467,7 @@ function progressResult(action, label) {
|
|
|
302
467
|
details: { action }
|
|
303
468
|
};
|
|
304
469
|
}
|
|
305
|
-
function createGitSession({ context, host: serverHost }) {
|
|
470
|
+
function createGitSession({ context, host: serverHost, agent, signal }) {
|
|
306
471
|
if (serverHost.context.directEvents === void 0) throw new Error("Git server plugin requires the session direct event bus.");
|
|
307
472
|
if (serverHost.context.sessionId === void 0) throw new Error("Git server plugin requires a session identity.");
|
|
308
473
|
const sessionId = serverHost.context.sessionId;
|
|
@@ -315,17 +480,6 @@ function createGitSession({ context, host: serverHost }) {
|
|
|
315
480
|
sessionDelivery: () => readPoSessionDelivery(context)
|
|
316
481
|
});
|
|
317
482
|
const resources = [{
|
|
318
|
-
when: {
|
|
319
|
-
state: { "minor-mode": "help" },
|
|
320
|
-
attribution: {
|
|
321
|
-
kind: "minor",
|
|
322
|
-
mode: "help"
|
|
323
|
-
}
|
|
324
|
-
},
|
|
325
|
-
name: "po-git",
|
|
326
|
-
kind: "context",
|
|
327
|
-
read: () => readPackageResource(import.meta.url, "llms.txt")
|
|
328
|
-
}, {
|
|
329
483
|
name: "po-use-git",
|
|
330
484
|
kind: "skill",
|
|
331
485
|
read: () => readPackageResource(import.meta.url, "src/prompts/po-use-git/SKILL.md")
|
|
@@ -348,17 +502,37 @@ function createGitSession({ context, host: serverHost }) {
|
|
|
348
502
|
const changed = (_event, execution) => {
|
|
349
503
|
directEvents.publish(GIT_SESSION_CHANGED_EVENT, execution.sessionId, { version: 1 });
|
|
350
504
|
};
|
|
505
|
+
const hooks = [{
|
|
506
|
+
event: "tool_execution_end",
|
|
507
|
+
handle: changed
|
|
508
|
+
}, {
|
|
509
|
+
event: "agent_settled",
|
|
510
|
+
handle: changed
|
|
511
|
+
}];
|
|
512
|
+
if (agent) {
|
|
513
|
+
const namespace = createWorktreeNamespace({
|
|
514
|
+
operations,
|
|
515
|
+
execution: agent.context,
|
|
516
|
+
assertAvailable: () => {
|
|
517
|
+
if (signal?.aborted) throw new Error("Git session is unavailable.");
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
context.inject([PO_CODEMODE_SERVICE], (scoped) => {
|
|
521
|
+
const codemode = readPoCodemode(scoped);
|
|
522
|
+
if (!codemode) return void 0;
|
|
523
|
+
const registration = codemode.register("@agimon-ai/po-git", namespace);
|
|
524
|
+
const restriction = agent.registerToolRestriction({ excludedTools: [RUN_WORKTREE_TOOL_NAME] });
|
|
525
|
+
return () => {
|
|
526
|
+
restriction.dispose();
|
|
527
|
+
registration.dispose();
|
|
528
|
+
};
|
|
529
|
+
});
|
|
530
|
+
}
|
|
351
531
|
return {
|
|
352
532
|
value: {
|
|
353
533
|
resources,
|
|
354
534
|
tool,
|
|
355
|
-
hooks
|
|
356
|
-
event: "tool_execution_end",
|
|
357
|
-
handle: changed
|
|
358
|
-
}, {
|
|
359
|
-
event: "agent_settled",
|
|
360
|
-
handle: changed
|
|
361
|
-
}]
|
|
535
|
+
hooks
|
|
362
536
|
},
|
|
363
537
|
onDispose: () => messageInbox.close()
|
|
364
538
|
};
|
|
@@ -2032,20 +2206,8 @@ var agent_settled_server_default = defineHook((context) => context.root.hooks[1]
|
|
|
2032
2206
|
//#region src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.ts
|
|
2033
2207
|
var tool_execution_end_server_default = defineHook((context) => context.root.hooks[0]);
|
|
2034
2208
|
//#endregion
|
|
2035
|
-
//#region src/extensions/workspaces/sessions/(backend)/resource/_lib/poGit.server.ts
|
|
2036
|
-
var poGit_server_default$1 = (context) => context.root.resources[0];
|
|
2037
|
-
//#endregion
|
|
2038
|
-
//#region src/extensions/workspaces/sessions/(backend)/resource/poGit.server.ts
|
|
2039
|
-
var poGit_server_default = defineResource(poGit_server_default$1);
|
|
2040
|
-
//#endregion
|
|
2041
|
-
//#region src/extensions/workspaces/sessions/(backend)/resource/_lib/poGitReadme.server.ts
|
|
2042
|
-
var poGitReadme_server_default$1 = (context) => context.root.resources[2];
|
|
2043
|
-
//#endregion
|
|
2044
|
-
//#region src/extensions/workspaces/sessions/(backend)/resource/poGitReadme.server.ts
|
|
2045
|
-
var poGitReadme_server_default = defineResource(poGitReadme_server_default$1);
|
|
2046
|
-
//#endregion
|
|
2047
2209
|
//#region src/extensions/workspaces/sessions/(backend)/resource/_lib/poUseGit.server.ts
|
|
2048
|
-
var poUseGit_server_default$1 = (context) => context.root.resources[
|
|
2210
|
+
var poUseGit_server_default$1 = (context) => context.root.resources[0];
|
|
2049
2211
|
//#endregion
|
|
2050
2212
|
//#region src/extensions/workspaces/sessions/(backend)/resource/poUseGit.server.ts
|
|
2051
2213
|
var poUseGit_server_default = defineResource(poUseGit_server_default$1);
|
|
@@ -2070,7 +2232,7 @@ const serverPlugin = defineServerPlugin({
|
|
|
2070
2232
|
return [at(route_server_default, context)];
|
|
2071
2233
|
},
|
|
2072
2234
|
get channels() {
|
|
2073
|
-
return [() => via({ frameType: "git_changes" }, git_changes_server_default
|
|
2235
|
+
return [() => via({ frameType: "git_changes" }, at(git_changes_server_default, context)), () => via({ frameType: "git_worktrees" }, at(git_worktrees_server_default, context))];
|
|
2074
2236
|
}
|
|
2075
2237
|
}),
|
|
2076
2238
|
workspace: (context) => ({
|
|
@@ -2078,7 +2240,7 @@ const serverPlugin = defineServerPlugin({
|
|
|
2078
2240
|
return [at(route_server_default, context)];
|
|
2079
2241
|
},
|
|
2080
2242
|
get channels() {
|
|
2081
|
-
return defined([context.hosted("global") ? void 0 : () => via({ frameType: "git_changes" }, git_changes_server_default
|
|
2243
|
+
return defined([context.hosted("global") ? void 0 : () => via({ frameType: "git_changes" }, at(git_changes_server_default, context)), context.hosted("global") ? void 0 : () => via({ frameType: "git_worktrees" }, at(git_worktrees_server_default, context))]);
|
|
2082
2244
|
}
|
|
2083
2245
|
}),
|
|
2084
2246
|
session: async (context) => {
|
|
@@ -2093,17 +2255,13 @@ const serverPlugin = defineServerPlugin({
|
|
|
2093
2255
|
return [at(route_server_default, context)];
|
|
2094
2256
|
},
|
|
2095
2257
|
get channels() {
|
|
2096
|
-
return defined([context.hosted("global") ? void 0 : () => via({ frameType: "git_changes" }, git_changes_server_default
|
|
2258
|
+
return defined([context.hosted("global") ? void 0 : () => via({ frameType: "git_changes" }, at(git_changes_server_default, context)), context.hosted("global") ? void 0 : () => via({ frameType: "git_worktrees" }, at(git_worktrees_server_default, context))]);
|
|
2097
2259
|
},
|
|
2098
2260
|
get hooks() {
|
|
2099
2261
|
return [via({ event: "agent_settled" }, at(agent_settled_server_default, contextSession)), via({ event: "tool_execution_end" }, at(tool_execution_end_server_default, contextSession))];
|
|
2100
2262
|
},
|
|
2101
2263
|
get resources() {
|
|
2102
|
-
return [
|
|
2103
|
-
at(poGit_server_default, contextSession),
|
|
2104
|
-
at(poGitReadme_server_default, contextSession),
|
|
2105
|
-
at(poUseGit_server_default, contextSession)
|
|
2106
|
-
];
|
|
2264
|
+
return [at(poUseGit_server_default, contextSession)];
|
|
2107
2265
|
},
|
|
2108
2266
|
get tools() {
|
|
2109
2267
|
return [via({ name: "run_worktree" }, at(runWorktree_server_default, contextSession))];
|