@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
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ Add `@agimon-ai/po-git` to a selected layer in `.po/modes.yaml`, then run `po sy
|
|
|
15
15
|
|
|
16
16
|
## Use
|
|
17
17
|
|
|
18
|
-
Use the browser worktree controls or the
|
|
18
|
+
Use the browser worktree controls, or an agent session. In code mode the `worktrees` namespace (`list`, `status`, `spawn`, `close`, `merge`, `prune`, `send`, `messages`) returns structured records and the `run_worktree` tool is withdrawn from the model for that session; without code mode `run_worktree` stays. The `po-use-git` skill carries the usage rules.
|
|
19
19
|
|
|
20
20
|
### New-session dialog
|
|
21
21
|
|
|
@@ -11,6 +11,7 @@ let node_os = require("node:os");
|
|
|
11
11
|
node_os = require_worktreeOperations.__toESM(node_os, 1);
|
|
12
12
|
let _agimon_ai_po_core_serverPlugin = require("@agimon-ai/po-core/serverPlugin");
|
|
13
13
|
let _agimon_ai_po_core_extensionFile = require("@agimon-ai/po-core/extensionFile");
|
|
14
|
+
let _agimon_ai_po_core_codemode = require("@agimon-ai/po-core/codemode");
|
|
14
15
|
let _agimon_ai_po_session = require("@agimon-ai/po-session");
|
|
15
16
|
let typebox = require("typebox");
|
|
16
17
|
//#region src/schemas/runWorktreeTool.ts
|
|
@@ -254,7 +255,8 @@ async function executeRunWorktreeTool(params, operations, context, options = {})
|
|
|
254
255
|
content: text(describe(record, context.sessionId), `Session: ${record.sessionId}`, dirtyFiles.length === 0 ? "Clean." : `${String(dirtyFiles.length)} uncommitted file(s): ${dirtyFiles.slice(0, 20).join(", ")}`),
|
|
255
256
|
details: {
|
|
256
257
|
action: params.action,
|
|
257
|
-
records: [record]
|
|
258
|
+
records: [record],
|
|
259
|
+
dirtyFiles
|
|
258
260
|
}
|
|
259
261
|
};
|
|
260
262
|
}
|
|
@@ -278,7 +280,10 @@ async function executeRunWorktreeTool(params, operations, context, options = {})
|
|
|
278
280
|
const waiting = await operations.messages(context, params.id);
|
|
279
281
|
return {
|
|
280
282
|
content: waiting.length === 0 ? text("No messages.") : text(`${String(waiting.length)} message(s):`, ...waiting.map((entry) => `[${entry.sentAt}] ${entry.from}: ${entry.text}`)),
|
|
281
|
-
details: {
|
|
283
|
+
details: {
|
|
284
|
+
action: params.action,
|
|
285
|
+
messages: waiting
|
|
286
|
+
}
|
|
282
287
|
};
|
|
283
288
|
}
|
|
284
289
|
case WORKTREE_ACTIONS.prune: {
|
|
@@ -293,12 +298,172 @@ async function executeRunWorktreeTool(params, operations, context, options = {})
|
|
|
293
298
|
`Untracked directories, left alone: ${plan.untracked.length === 0 ? "none" : plan.untracked.join(", ")}`,
|
|
294
299
|
...plan.keptBranches.length === 0 ? [] : [`Branches kept, they hold commits: ${plan.keptBranches.join(", ")}`]
|
|
295
300
|
]),
|
|
296
|
-
details: {
|
|
301
|
+
details: {
|
|
302
|
+
action: params.action,
|
|
303
|
+
plan
|
|
304
|
+
}
|
|
297
305
|
};
|
|
298
306
|
}
|
|
299
307
|
}
|
|
300
308
|
}
|
|
301
309
|
//#endregion
|
|
310
|
+
//#region src/services/worktreeNamespace/index.ts
|
|
311
|
+
const DECLARATIONS = `type WorktreeStatus = 'spawning' | 'running' | 'closing' | 'orphaned';
|
|
312
|
+
|
|
313
|
+
interface Worktree {
|
|
314
|
+
id: string;
|
|
315
|
+
branch: string;
|
|
316
|
+
baseRef: string;
|
|
317
|
+
/** Absolute path of the checkout, under ~/.po/git/worktrees. */
|
|
318
|
+
path: string;
|
|
319
|
+
repositoryRoot: string;
|
|
320
|
+
/** The session running inside the worktree. */
|
|
321
|
+
sessionId: string;
|
|
322
|
+
parentSessionId?: string;
|
|
323
|
+
status: WorktreeStatus;
|
|
324
|
+
createdAt: string;
|
|
325
|
+
/** How your session relates to it: 'parent' spawned it, 'child' runs inside it, 'other' is neither. */
|
|
326
|
+
relationship: 'parent' | 'child' | 'other';
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
interface WorktreeMessage {
|
|
330
|
+
worktreeId: string;
|
|
331
|
+
fromSessionId: string;
|
|
332
|
+
from: 'parent' | 'child';
|
|
333
|
+
text: string;
|
|
334
|
+
sentAt: string;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
interface PrunePlan {
|
|
338
|
+
remove: Worktree[];
|
|
339
|
+
forget: Worktree[];
|
|
340
|
+
/** Kept because they hold uncommitted work. */
|
|
341
|
+
keptDirty: Worktree[];
|
|
342
|
+
untracked: string[];
|
|
343
|
+
/** Branches kept because they hold commits. */
|
|
344
|
+
keptBranches: string[];
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
declare const worktrees: {
|
|
348
|
+
/** Every worktree for this repository. */
|
|
349
|
+
list(): Promise<Worktree[]>;
|
|
350
|
+
/** One worktree and its uncommitted files. */
|
|
351
|
+
status(id: string): Promise<{ worktree: Worktree; dirtyFiles: string[] }>;
|
|
352
|
+
/**
|
|
353
|
+
* Create a worktree on a new branch and start a Po session in it, nested under this one. baseRef defaults to
|
|
354
|
+
* the current branch's remote-tracking ref, then the remote default branch; name labels it in the rail; task is
|
|
355
|
+
* the new session's first message. Setup can be slow, so give exec a larger timeoutMs.
|
|
356
|
+
*/
|
|
357
|
+
spawn(request: { branch: string; baseRef?: string; name?: string; task?: string }): Promise<Worktree>;
|
|
358
|
+
/** Stop the worktree's session and delete its checkout. Refused on a dirty tree unless force is true. */
|
|
359
|
+
close(id: string, options?: { force?: boolean }): Promise<Worktree>;
|
|
360
|
+
/** Merge the worktree's branch into this checkout. Refused when this checkout is dirty. */
|
|
361
|
+
merge(id: string, options?: { message?: string }): Promise<Worktree>;
|
|
362
|
+
/** Remove orphaned worktrees. With dryRun, only return the plan. */
|
|
363
|
+
prune(options?: { dryRun?: boolean }): Promise<PrunePlan>;
|
|
364
|
+
/**
|
|
365
|
+
* Message the other side of a worktree. Use the record with relationship 'child' to report to your parent,
|
|
366
|
+
* and 'parent' to reach your own children. 'other' records are not communication edges.
|
|
367
|
+
*/
|
|
368
|
+
send(id: string, message: string): Promise<void>;
|
|
369
|
+
/** Messages waiting for you from a worktree. */
|
|
370
|
+
messages(id: string): Promise<WorktreeMessage[]>;
|
|
371
|
+
};`;
|
|
372
|
+
const json = (value) => JSON.parse(JSON.stringify(value));
|
|
373
|
+
/** Git worktrees for code mode: the actions of `run_worktree` as methods, returning records instead of text. */
|
|
374
|
+
function createWorktreeNamespace(options) {
|
|
375
|
+
return {
|
|
376
|
+
name: "worktrees",
|
|
377
|
+
summary: "Git worktrees, each with its own Po session nested under this one: worktrees.list, status, spawn, close, merge, prune, send and messages.",
|
|
378
|
+
declarations: DECLARATIONS,
|
|
379
|
+
bind(run) {
|
|
380
|
+
if (run.sessionId !== options.execution.sessionId) throw new Error("Worktrees namespace belongs to another session.");
|
|
381
|
+
let disposed = false;
|
|
382
|
+
run.onDispose(() => {
|
|
383
|
+
disposed = true;
|
|
384
|
+
});
|
|
385
|
+
const view = (record) => ({
|
|
386
|
+
...record,
|
|
387
|
+
relationship: record.parentSessionId === options.execution.sessionId ? "parent" : record.sessionId === options.execution.sessionId ? "child" : "other"
|
|
388
|
+
});
|
|
389
|
+
/** One `run_worktree` action through the tool's own code, so the tool and the namespace cannot drift. */
|
|
390
|
+
const act = async (name, action, input) => {
|
|
391
|
+
run.signal.throwIfAborted();
|
|
392
|
+
if (disposed) throw new Error("Worktrees namespace run is disposed.");
|
|
393
|
+
options.assertAvailable();
|
|
394
|
+
const params = validateParams({
|
|
395
|
+
action,
|
|
396
|
+
...typeof input === "object" && input !== null ? input : {}
|
|
397
|
+
});
|
|
398
|
+
const context = {
|
|
399
|
+
cwd: options.execution.cwd,
|
|
400
|
+
sessionId: options.execution.sessionId
|
|
401
|
+
};
|
|
402
|
+
const started = Date.now();
|
|
403
|
+
try {
|
|
404
|
+
const result = await executeRunWorktreeTool(params, options.operations, context, { signal: run.signal });
|
|
405
|
+
options.assertAvailable();
|
|
406
|
+
run.record?.({
|
|
407
|
+
name: `worktrees.${name}`,
|
|
408
|
+
tool: RUN_WORKTREE_TOOL_NAME,
|
|
409
|
+
args: params,
|
|
410
|
+
ok: true,
|
|
411
|
+
ms: Date.now() - started,
|
|
412
|
+
result
|
|
413
|
+
});
|
|
414
|
+
return result;
|
|
415
|
+
} catch (error) {
|
|
416
|
+
run.record?.({
|
|
417
|
+
name: `worktrees.${name}`,
|
|
418
|
+
tool: RUN_WORKTREE_TOOL_NAME,
|
|
419
|
+
args: params,
|
|
420
|
+
ok: false,
|
|
421
|
+
ms: Date.now() - started,
|
|
422
|
+
error: error instanceof Error ? error.message : String(error)
|
|
423
|
+
});
|
|
424
|
+
throw error;
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
const record = (result) => view(result.details.records[0]);
|
|
428
|
+
const members = Object.create(null);
|
|
429
|
+
members.list = async () => json((await act("list", "list", {})).details.records.map(view));
|
|
430
|
+
members.status = async (id) => {
|
|
431
|
+
const { details } = await act("status", "status", { id });
|
|
432
|
+
return json({
|
|
433
|
+
worktree: view(details.records[0]),
|
|
434
|
+
dirtyFiles: details.dirtyFiles ?? []
|
|
435
|
+
});
|
|
436
|
+
};
|
|
437
|
+
members.spawn = async (request) => json(record(await act("spawn", "spawn_worktree", request)));
|
|
438
|
+
members.close = async (id, settings) => json(record(await act("close", "close_worktree", {
|
|
439
|
+
...settings,
|
|
440
|
+
id
|
|
441
|
+
})));
|
|
442
|
+
members.merge = async (id, settings) => json(record(await act("merge", "merge", {
|
|
443
|
+
...settings,
|
|
444
|
+
id
|
|
445
|
+
})));
|
|
446
|
+
members.prune = async (settings) => {
|
|
447
|
+
const { plan } = (await act("prune", "prune", settings)).details;
|
|
448
|
+
return json({
|
|
449
|
+
...plan,
|
|
450
|
+
remove: plan.remove.map(view),
|
|
451
|
+
forget: plan.forget.map(view),
|
|
452
|
+
keptDirty: plan.keptDirty.map(view)
|
|
453
|
+
});
|
|
454
|
+
};
|
|
455
|
+
members.send = async (id, message) => {
|
|
456
|
+
await act("send", "send", {
|
|
457
|
+
id,
|
|
458
|
+
message
|
|
459
|
+
});
|
|
460
|
+
};
|
|
461
|
+
members.messages = async (id) => json((await act("messages", "messages", { id })).details.messages);
|
|
462
|
+
return members;
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
//#endregion
|
|
302
467
|
//#region src/extensions/workspaces/sessions/(backend)/_lib/root.server.ts
|
|
303
468
|
function progressResult(action, label) {
|
|
304
469
|
return {
|
|
@@ -309,7 +474,7 @@ function progressResult(action, label) {
|
|
|
309
474
|
details: { action }
|
|
310
475
|
};
|
|
311
476
|
}
|
|
312
|
-
function createGitSession({ context, host: serverHost }) {
|
|
477
|
+
function createGitSession({ context, host: serverHost, agent, signal }) {
|
|
313
478
|
if (serverHost.context.directEvents === void 0) throw new Error("Git server plugin requires the session direct event bus.");
|
|
314
479
|
if (serverHost.context.sessionId === void 0) throw new Error("Git server plugin requires a session identity.");
|
|
315
480
|
const sessionId = serverHost.context.sessionId;
|
|
@@ -322,17 +487,6 @@ function createGitSession({ context, host: serverHost }) {
|
|
|
322
487
|
sessionDelivery: () => (0, _agimon_ai_po_session.readPoSessionDelivery)(context)
|
|
323
488
|
});
|
|
324
489
|
const resources = [{
|
|
325
|
-
when: {
|
|
326
|
-
state: { "minor-mode": "help" },
|
|
327
|
-
attribution: {
|
|
328
|
-
kind: "minor",
|
|
329
|
-
mode: "help"
|
|
330
|
-
}
|
|
331
|
-
},
|
|
332
|
-
name: "po-git",
|
|
333
|
-
kind: "context",
|
|
334
|
-
read: () => (0, _agimon_ai_po_core_serverPlugin.readPackageResource)(require("url").pathToFileURL(__filename).href, "llms.txt")
|
|
335
|
-
}, {
|
|
336
490
|
name: "po-use-git",
|
|
337
491
|
kind: "skill",
|
|
338
492
|
read: () => (0, _agimon_ai_po_core_serverPlugin.readPackageResource)(require("url").pathToFileURL(__filename).href, "src/prompts/po-use-git/SKILL.md")
|
|
@@ -355,17 +509,37 @@ function createGitSession({ context, host: serverHost }) {
|
|
|
355
509
|
const changed = (_event, execution) => {
|
|
356
510
|
directEvents.publish(require_worktreeOperations.GIT_SESSION_CHANGED_EVENT, execution.sessionId, { version: 1 });
|
|
357
511
|
};
|
|
512
|
+
const hooks = [{
|
|
513
|
+
event: "tool_execution_end",
|
|
514
|
+
handle: changed
|
|
515
|
+
}, {
|
|
516
|
+
event: "agent_settled",
|
|
517
|
+
handle: changed
|
|
518
|
+
}];
|
|
519
|
+
if (agent) {
|
|
520
|
+
const namespace = createWorktreeNamespace({
|
|
521
|
+
operations,
|
|
522
|
+
execution: agent.context,
|
|
523
|
+
assertAvailable: () => {
|
|
524
|
+
if (signal?.aborted) throw new Error("Git session is unavailable.");
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
context.inject([_agimon_ai_po_core_codemode.PO_CODEMODE_SERVICE], (scoped) => {
|
|
528
|
+
const codemode = (0, _agimon_ai_po_core_codemode.readPoCodemode)(scoped);
|
|
529
|
+
if (!codemode) return void 0;
|
|
530
|
+
const registration = codemode.register("@agimon-ai/po-git", namespace);
|
|
531
|
+
const restriction = agent.registerToolRestriction({ excludedTools: [RUN_WORKTREE_TOOL_NAME] });
|
|
532
|
+
return () => {
|
|
533
|
+
restriction.dispose();
|
|
534
|
+
registration.dispose();
|
|
535
|
+
};
|
|
536
|
+
});
|
|
537
|
+
}
|
|
358
538
|
return {
|
|
359
539
|
value: {
|
|
360
540
|
resources,
|
|
361
541
|
tool,
|
|
362
|
-
hooks
|
|
363
|
-
event: "tool_execution_end",
|
|
364
|
-
handle: changed
|
|
365
|
-
}, {
|
|
366
|
-
event: "agent_settled",
|
|
367
|
-
handle: changed
|
|
368
|
-
}]
|
|
542
|
+
hooks
|
|
369
543
|
},
|
|
370
544
|
onDispose: () => messageInbox.close()
|
|
371
545
|
};
|
|
@@ -2039,20 +2213,8 @@ var agent_settled_server_default = (0, _agimon_ai_po_core_extensionFile.defineHo
|
|
|
2039
2213
|
//#region src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.ts
|
|
2040
2214
|
var tool_execution_end_server_default = (0, _agimon_ai_po_core_extensionFile.defineHook)((context) => context.root.hooks[0]);
|
|
2041
2215
|
//#endregion
|
|
2042
|
-
//#region src/extensions/workspaces/sessions/(backend)/resource/_lib/poGit.server.ts
|
|
2043
|
-
var poGit_server_default$1 = (context) => context.root.resources[0];
|
|
2044
|
-
//#endregion
|
|
2045
|
-
//#region src/extensions/workspaces/sessions/(backend)/resource/poGit.server.ts
|
|
2046
|
-
var poGit_server_default = (0, _agimon_ai_po_core_extensionFile.defineResource)(poGit_server_default$1);
|
|
2047
|
-
//#endregion
|
|
2048
|
-
//#region src/extensions/workspaces/sessions/(backend)/resource/_lib/poGitReadme.server.ts
|
|
2049
|
-
var poGitReadme_server_default$1 = (context) => context.root.resources[2];
|
|
2050
|
-
//#endregion
|
|
2051
|
-
//#region src/extensions/workspaces/sessions/(backend)/resource/poGitReadme.server.ts
|
|
2052
|
-
var poGitReadme_server_default = (0, _agimon_ai_po_core_extensionFile.defineResource)(poGitReadme_server_default$1);
|
|
2053
|
-
//#endregion
|
|
2054
2216
|
//#region src/extensions/workspaces/sessions/(backend)/resource/_lib/poUseGit.server.ts
|
|
2055
|
-
var poUseGit_server_default$1 = (context) => context.root.resources[
|
|
2217
|
+
var poUseGit_server_default$1 = (context) => context.root.resources[0];
|
|
2056
2218
|
//#endregion
|
|
2057
2219
|
//#region src/extensions/workspaces/sessions/(backend)/resource/poUseGit.server.ts
|
|
2058
2220
|
var poUseGit_server_default = (0, _agimon_ai_po_core_extensionFile.defineResource)(poUseGit_server_default$1);
|
|
@@ -2077,7 +2239,7 @@ const serverPlugin = (0, _agimon_ai_po_core_serverPlugin.defineServerPlugin)({
|
|
|
2077
2239
|
return [at(route_server_default, context)];
|
|
2078
2240
|
},
|
|
2079
2241
|
get channels() {
|
|
2080
|
-
return [() => via({ frameType: "git_changes" }, git_changes_server_default
|
|
2242
|
+
return [() => via({ frameType: "git_changes" }, at(git_changes_server_default, context)), () => via({ frameType: "git_worktrees" }, at(git_worktrees_server_default, context))];
|
|
2081
2243
|
}
|
|
2082
2244
|
}),
|
|
2083
2245
|
workspace: (context) => ({
|
|
@@ -2085,7 +2247,7 @@ const serverPlugin = (0, _agimon_ai_po_core_serverPlugin.defineServerPlugin)({
|
|
|
2085
2247
|
return [at(route_server_default, context)];
|
|
2086
2248
|
},
|
|
2087
2249
|
get channels() {
|
|
2088
|
-
return defined([context.hosted("global") ? void 0 : () => via({ frameType: "git_changes" }, git_changes_server_default
|
|
2250
|
+
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))]);
|
|
2089
2251
|
}
|
|
2090
2252
|
}),
|
|
2091
2253
|
session: async (context) => {
|
|
@@ -2100,17 +2262,13 @@ const serverPlugin = (0, _agimon_ai_po_core_serverPlugin.defineServerPlugin)({
|
|
|
2100
2262
|
return [at(route_server_default, context)];
|
|
2101
2263
|
},
|
|
2102
2264
|
get channels() {
|
|
2103
|
-
return defined([context.hosted("global") ? void 0 : () => via({ frameType: "git_changes" }, git_changes_server_default
|
|
2265
|
+
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))]);
|
|
2104
2266
|
},
|
|
2105
2267
|
get hooks() {
|
|
2106
2268
|
return [via({ event: "agent_settled" }, at(agent_settled_server_default, contextSession)), via({ event: "tool_execution_end" }, at(tool_execution_end_server_default, contextSession))];
|
|
2107
2269
|
},
|
|
2108
2270
|
get resources() {
|
|
2109
|
-
return [
|
|
2110
|
-
at(poGit_server_default, contextSession),
|
|
2111
|
-
at(poGitReadme_server_default, contextSession),
|
|
2112
|
-
at(poUseGit_server_default, contextSession)
|
|
2113
|
-
];
|
|
2271
|
+
return [at(poUseGit_server_default, contextSession)];
|
|
2114
2272
|
},
|
|
2115
2273
|
get tools() {
|
|
2116
2274
|
return [via({ name: "run_worktree" }, at(runWorktree_server_default, contextSession))];
|