@prevalentware/opencode-goal-plugin 0.1.37 → 0.1.39
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 +2 -2
- package/dist/server.js +56 -14
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ The OpenCode Goal Plugin adds:
|
|
|
18
18
|
|
|
19
19
|
- `/goal <objective>` as an OpenCode command for TUI, desktop, and web.
|
|
20
20
|
- A sidebar goal indicator with status, elapsed time, and objective.
|
|
21
|
-
- Agent tools: `get_goal`, `get_goal_history`, `create_goal`, `set_goal`, `update_goal_objective`, `update_goal`, and `clear_goal`.
|
|
21
|
+
- Agent tools: `get_goal`, `get_goal_history`, `list_all_goals`, `create_goal`, `set_goal`, `update_goal_objective`, `update_goal`, and `clear_goal`.
|
|
22
22
|
- Goal close evidence: `complete` requires verified evidence, and `unmet` requires a concrete blocker.
|
|
23
23
|
- Persistent per-session goal state with history, checkpoints, budgets, and owner-only file permissions.
|
|
24
24
|
- Optional automatic continuation on `session.idle` / `session.status`, with no-progress pause and budget wrap-up safeguards.
|
|
@@ -239,7 +239,7 @@ This plugin follows Codex's native goal-mode semantics where OpenCode plugin hoo
|
|
|
239
239
|
|
|
240
240
|
```bash
|
|
241
241
|
bun install
|
|
242
|
-
bun test
|
|
242
|
+
bun run test
|
|
243
243
|
bun run lint
|
|
244
244
|
bun run typecheck
|
|
245
245
|
bun run build
|
package/dist/server.js
CHANGED
|
@@ -127,6 +127,7 @@ class StateWriteError extends Data.TaggedError("StateWriteError") {
|
|
|
127
127
|
}
|
|
128
128
|
var MAX_HISTORY_ENTRIES = 50;
|
|
129
129
|
var MAX_CHECKPOINTS = 8;
|
|
130
|
+
var MAX_LISTED_GOALS = 50;
|
|
130
131
|
var CHECKPOINT_CHAR_LIMIT = 280;
|
|
131
132
|
var DEFAULT_NO_PROGRESS_TOKEN_THRESHOLD = 50;
|
|
132
133
|
var DEFAULT_MAX_NO_PROGRESS_TURNS = 2;
|
|
@@ -241,8 +242,7 @@ function parseStateText(raw, file) {
|
|
|
241
242
|
function decodeState(value) {
|
|
242
243
|
return Schema.decodeUnknown(StateSchema)(value).pipe(Effect.map(mutableState), Effect.map(normalizeState), Effect.mapError((cause) => new StateDecodeError({ cause })));
|
|
243
244
|
}
|
|
244
|
-
function readStateEffect() {
|
|
245
|
-
const file = statePath();
|
|
245
|
+
function readStateEffect(file = statePath()) {
|
|
246
246
|
return Effect.tryPromise({
|
|
247
247
|
try: () => readFile(file, "utf8"),
|
|
248
248
|
catch: (cause) => new StateReadError({ cause })
|
|
@@ -251,10 +251,9 @@ function readStateEffect() {
|
|
|
251
251
|
catch: (cause) => new StateDecodeError({ cause })
|
|
252
252
|
})), Effect.flatMap(decodeState), Effect.catchAll((error) => error._tag === "StateReadError" && isMissingStateFile(error.cause) ? Effect.succeed(emptyState()) : Effect.fail(error)));
|
|
253
253
|
}
|
|
254
|
-
function writeStateEffect(state) {
|
|
254
|
+
function writeStateEffect(state, file = statePath()) {
|
|
255
255
|
return Effect.tryPromise({
|
|
256
256
|
try: async () => {
|
|
257
|
-
const file = statePath();
|
|
258
257
|
await mkdir(dirname2(file), { recursive: true, mode: 448 });
|
|
259
258
|
await atomicWriteFile(file, JSON.stringify(state, null, 2) + `
|
|
260
259
|
`);
|
|
@@ -276,15 +275,18 @@ function enqueueMutation(operation) {
|
|
|
276
275
|
return current;
|
|
277
276
|
}
|
|
278
277
|
async function mutate(fn) {
|
|
279
|
-
return enqueueMutation(() =>
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
278
|
+
return enqueueMutation(() => {
|
|
279
|
+
const file = statePath();
|
|
280
|
+
return Effect.runPromise(Effect.gen(function* () {
|
|
281
|
+
const state = yield* readStateEffect(file);
|
|
282
|
+
const result = yield* Effect.tryPromise({
|
|
283
|
+
try: () => Promise.resolve(fn(state)),
|
|
284
|
+
catch: (cause) => cause instanceof Error ? cause : new Error(String(cause))
|
|
285
|
+
});
|
|
286
|
+
yield* writeStateEffect(state, file);
|
|
287
|
+
return result;
|
|
288
|
+
}));
|
|
289
|
+
});
|
|
288
290
|
}
|
|
289
291
|
function validateObjective(objective) {
|
|
290
292
|
const value = objective.trim();
|
|
@@ -456,6 +458,30 @@ async function getGoal(sessionID) {
|
|
|
456
458
|
const goal = state.goals[sessionID];
|
|
457
459
|
return goal ? snapshot(goal) : null;
|
|
458
460
|
}
|
|
461
|
+
async function getAllGoals() {
|
|
462
|
+
const state = await readState();
|
|
463
|
+
const sorted = Object.values(state.goals).sort((left, right) => right.updatedAt - left.updatedAt || (left.sessionID < right.sessionID ? -1 : left.sessionID > right.sessionID ? 1 : 0));
|
|
464
|
+
const goals = sorted.slice(0, MAX_LISTED_GOALS).map(goalListItem);
|
|
465
|
+
return { goals, total: sorted.length, truncated: sorted.length > goals.length };
|
|
466
|
+
}
|
|
467
|
+
function goalListItem(goal) {
|
|
468
|
+
return {
|
|
469
|
+
sessionID: goal.sessionID,
|
|
470
|
+
objective: goal.objective,
|
|
471
|
+
status: goal.status,
|
|
472
|
+
tokenBudget: goal.tokenBudget,
|
|
473
|
+
tokensUsed: goal.tokensUsed,
|
|
474
|
+
timeUsedSeconds: goal.timeUsedSeconds,
|
|
475
|
+
createdAt: goal.createdAt,
|
|
476
|
+
updatedAt: goal.updatedAt,
|
|
477
|
+
closedAt: goal.closedAt ?? null,
|
|
478
|
+
maxAutoTurns: goal.maxAutoTurns,
|
|
479
|
+
maxDurationSeconds: goal.maxDurationSeconds,
|
|
480
|
+
autoTurns: goal.autoTurns,
|
|
481
|
+
stopReason: goal.stopReason,
|
|
482
|
+
remainingTokens: remainingTokens(goal)
|
|
483
|
+
};
|
|
484
|
+
}
|
|
459
485
|
async function getGoalInternal(sessionID) {
|
|
460
486
|
const state = await readState();
|
|
461
487
|
const goal = state.goals[sessionID];
|
|
@@ -1140,7 +1166,7 @@ var STALE_PENDING_MS = 30000;
|
|
|
1140
1166
|
var RETRY_SETTLE_MS = 25;
|
|
1141
1167
|
var TRANSPORT_ERROR_PATTERN = /\b(?:network|fetch|socket|connect|connection|timeout|timed out|ECONNRESET|ECONNREFUSED|ETIMEDOUT|EAI_AGAIN|ENOTFOUND|EPIPE|transport|stream|websocket|offline|internet|request failed|proxy)\b/i;
|
|
1142
1168
|
var NON_TRANSPORT_TERMINAL_PATTERN = /\b(?:abort(?:ed)?|interrupt(?:ed|ion)?)\b/i;
|
|
1143
|
-
var NON_PROGRESS_TOOLS = new Set(["get_goal", "get_goal_history"]);
|
|
1169
|
+
var NON_PROGRESS_TOOLS = new Set(["get_goal", "get_goal_history", "list_all_goals"]);
|
|
1144
1170
|
var TASK_TERMINAL_STATES = new Set(["completed", "error", "cancelled"]);
|
|
1145
1171
|
var PLAN_MODE_CREATE_NOTICE = 'Goal recorded while the session is in Plan mode, so execution is paused. Do not start implementation work now. Ask the user to switch to Build mode and resume the goal (for example with "/goal resume") to begin execution.';
|
|
1146
1172
|
var LIMITED_GOAL_NOTICE = "Safety limit reached. Do not start or continue substantive work for this goal. Summarize useful progress, remaining work, and blockers, then wait for the user to resume or edit the goal.";
|
|
@@ -2171,6 +2197,13 @@ var server = async ({ client }, options) => {
|
|
|
2171
2197
|
return JSON.stringify({ goal, history_report: formatGoalHistory(goal) }, null, 2);
|
|
2172
2198
|
}
|
|
2173
2199
|
},
|
|
2200
|
+
list_all_goals: {
|
|
2201
|
+
description: "List up to 50 public goal summaries across all sessions in this state file, ordered by most recently updated first. Elapsed time is the last persisted value; total and truncated report omitted older goals.",
|
|
2202
|
+
args: {},
|
|
2203
|
+
async execute() {
|
|
2204
|
+
return JSON.stringify(await getAllGoals(), null, 2);
|
|
2205
|
+
}
|
|
2206
|
+
},
|
|
2174
2207
|
create_goal: {
|
|
2175
2208
|
description: "Create a goal only when explicitly requested by the user or system/developer instructions; do not infer goals from ordinary tasks. If any non-closed goal exists, this returns the existing goal as either reused or conflicting and must not be retried. While the session is in Plan mode, the goal is recorded as paused and execution requires the user to switch to Build mode.",
|
|
2176
2209
|
args: {
|
|
@@ -3046,6 +3079,15 @@ function goalToolsV2(services) {
|
|
|
3046
3079
|
return { content: JSON.stringify({ goal, history_report: formatGoalHistory(goal) }, null, 2) };
|
|
3047
3080
|
}
|
|
3048
3081
|
},
|
|
3082
|
+
{
|
|
3083
|
+
name: "list_all_goals",
|
|
3084
|
+
description: "List up to 50 public goal summaries across all sessions in this state file, ordered by most recently updated first. Elapsed time is the last persisted value; total and truncated report omitted older goals.",
|
|
3085
|
+
input: v2ObjectSchema({}),
|
|
3086
|
+
options: { codemode: false },
|
|
3087
|
+
execute: async () => ({
|
|
3088
|
+
content: JSON.stringify(await getAllGoals(), null, 2)
|
|
3089
|
+
})
|
|
3090
|
+
},
|
|
3049
3091
|
{
|
|
3050
3092
|
name: "create_goal",
|
|
3051
3093
|
description: "Create a goal only when explicitly requested by the user or system/developer instructions; do not infer goals from ordinary tasks. If any non-closed goal exists, this returns the existing goal as either reused or conflicting and must not be retried. While the session is in Plan mode, the goal is recorded as paused and execution requires the user to switch to Build mode.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prevalentware/opencode-goal-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.39",
|
|
4
4
|
"description": "OpenCode goal plugin that adds Codex-style long-running goal mode, /goal commands, persistence, and TUI status for AI coding agents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"opencode",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"lint": "eslint .",
|
|
50
50
|
"pack:dry-run": "npm pack --dry-run",
|
|
51
51
|
"test": "bun test",
|
|
52
|
+
"test:coverage": "bun test --coverage",
|
|
52
53
|
"typecheck": "tsc --noEmit",
|
|
53
54
|
"prepublishOnly": "bun run test && bun run build"
|
|
54
55
|
},
|
|
@@ -62,6 +63,7 @@
|
|
|
62
63
|
"devDependencies": {
|
|
63
64
|
"@eslint/js": "^10.0.1",
|
|
64
65
|
"@opencode-ai/plugin-v2": "npm:@opencode-ai/plugin@0.0.0-next-17055",
|
|
66
|
+
"@opencode-ai/theme": "0.0.0-next-17055",
|
|
65
67
|
"@opentui/core": "^0.4.0",
|
|
66
68
|
"@types/bun": "^1.3.13",
|
|
67
69
|
"eslint": "^10.3.0",
|