opencode-planpilot 0.2.0 → 0.2.2
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/package.json +1 -1
- package/src/command.ts +14 -1
- package/src/index.ts +157 -75
- package/src/prompt.ts +104 -0
package/package.json
CHANGED
package/src/command.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { AppError, invalidInput } from "./lib/errors"
|
|
14
14
|
import { ensureNonEmpty, projectMatchesPath, resolveMaybeRealpath } from "./lib/util"
|
|
15
15
|
import { formatGoalDetail, formatPlanDetail, formatPlanMarkdown, formatStepDetail } from "./lib/format"
|
|
16
|
+
import { PLANPILOT_HELP_TEXT } from "./prompt"
|
|
16
17
|
|
|
17
18
|
const DEFAULT_PAGE = 1
|
|
18
19
|
const DEFAULT_LIMIT = 20
|
|
@@ -72,6 +73,14 @@ export async function runCommand(argv: string[], context: CommandContext, io: Co
|
|
|
72
73
|
let shouldSync = false
|
|
73
74
|
|
|
74
75
|
switch (section) {
|
|
76
|
+
case "help": {
|
|
77
|
+
if (subcommand !== undefined || args.length) {
|
|
78
|
+
const rest = [subcommand, ...args].filter((x) => x !== undefined)
|
|
79
|
+
throw invalidInput(`help unexpected argument: ${rest.join(" ")}`)
|
|
80
|
+
}
|
|
81
|
+
log(PLANPILOT_HELP_TEXT)
|
|
82
|
+
return
|
|
83
|
+
}
|
|
75
84
|
case "plan": {
|
|
76
85
|
const result = await handlePlan(app, subcommand, args, { cwd: context.cwd })
|
|
77
86
|
planIds = result.planIds
|
|
@@ -234,6 +243,11 @@ function handlePlanAddTree(app: PlanpilotApp, args: string[]): number[] {
|
|
|
234
243
|
log(`Created plan ID: ${result.plan.id}: ${result.plan.title} (steps: ${result.stepCount}, goals: ${result.goalCount})`)
|
|
235
244
|
app.setActivePlan(result.plan.id, false)
|
|
236
245
|
log(`Active plan set to ${result.plan.id}: ${result.plan.title}`)
|
|
246
|
+
|
|
247
|
+
// Print full detail so the AI can reference plan/step/goal IDs immediately.
|
|
248
|
+
const detail = app.getPlanDetail(result.plan.id)
|
|
249
|
+
log("")
|
|
250
|
+
log(formatPlanDetail(detail.plan, detail.steps, detail.goals))
|
|
237
251
|
return [result.plan.id]
|
|
238
252
|
}
|
|
239
253
|
|
|
@@ -1464,4 +1478,3 @@ function syncPlanMarkdown(app: PlanpilotApp, planIds: number[]) {
|
|
|
1464
1478
|
fs.writeFileSync(mdPath, markdown, "utf8")
|
|
1465
1479
|
})
|
|
1466
1480
|
}
|
|
1467
|
-
|
package/src/index.ts
CHANGED
|
@@ -5,62 +5,14 @@ import { openDatabase } from "./lib/db"
|
|
|
5
5
|
import { invalidInput } from "./lib/errors"
|
|
6
6
|
import { formatStepDetail } from "./lib/format"
|
|
7
7
|
import { parseWaitFromComment } from "./lib/util"
|
|
8
|
-
|
|
9
|
-
const PLANPILOT_TOOL_DESCRIPTION = [
|
|
10
|
-
"Planpilot planner for plan workflows.",
|
|
11
|
-
"Hints: 1. Model is plan/step/goal with ai/human executors and status auto-propagation upward (goals -> steps -> plan). 2. Keep comments short and decision-focused. 3. Add human steps only when AI cannot act. 4. Use `step wait` when ending a reply while waiting on external tasks.",
|
|
12
|
-
"",
|
|
13
|
-
"Usage:",
|
|
14
|
-
"- argv is tokenized: [section, subcommand, ...args]",
|
|
15
|
-
"- section: plan | step | goal",
|
|
16
|
-
"",
|
|
17
|
-
"Plan commands:",
|
|
18
|
-
"- plan add <title> <content>",
|
|
19
|
-
"- plan add-tree <title> <content> --step <content> [--executor ai|human] [--goal <content>]... [--step ...]...",
|
|
20
|
-
"- plan list [--scope project|all] [--status todo|done|all] [--limit N] [--page N] [--order id|title|created|updated] [--desc]",
|
|
21
|
-
"- plan count [--scope project|all] [--status todo|done|all]",
|
|
22
|
-
"- plan search --search <term> [--search <term> ...] [--search-mode any|all] [--search-field plan|title|content|comment|steps|goals|all] [--match-case] [--scope project|all] [--status todo|done|all] [--limit N] [--page N] [--order id|title|created|updated] [--desc]",
|
|
23
|
-
"- plan show <id>",
|
|
24
|
-
"- plan export <id> <path>",
|
|
25
|
-
"- plan comment <id> <comment> [<id> <comment> ...]",
|
|
26
|
-
"- plan update <id> [--title <title>] [--content <content>] [--status todo|done] [--comment <comment>]",
|
|
27
|
-
"- plan done <id>",
|
|
28
|
-
"- plan remove <id>",
|
|
29
|
-
"- plan activate <id> [--force]",
|
|
30
|
-
"- plan show-active",
|
|
31
|
-
"- plan deactivate",
|
|
32
|
-
"",
|
|
33
|
-
"Step commands:",
|
|
34
|
-
"- step add <plan_id> <content...> [--executor ai|human] [--at <pos>]",
|
|
35
|
-
"- step add-tree <plan_id> <content> [--executor ai|human] [--goal <content> ...]",
|
|
36
|
-
"- step list <plan_id> [--status todo|done|all] [--executor ai|human] [--limit N] [--page N]",
|
|
37
|
-
"- step count <plan_id> [--status todo|done|all] [--executor ai|human]",
|
|
38
|
-
"- step show <id>",
|
|
39
|
-
"- step show-next",
|
|
40
|
-
"- step wait <id> --delay <ms> [--reason <text>]",
|
|
41
|
-
"- step wait <id> --clear",
|
|
42
|
-
"- step comment <id> <comment> [<id> <comment> ...]",
|
|
43
|
-
"- step update <id> [--content <content>] [--status todo|done] [--executor ai|human] [--comment <comment>]",
|
|
44
|
-
"- step done <id> [--all-goals]",
|
|
45
|
-
"- step move <id> --to <pos>",
|
|
46
|
-
"- step remove <id...>",
|
|
47
|
-
"",
|
|
48
|
-
"Goal commands:",
|
|
49
|
-
"- goal add <step_id> <content...>",
|
|
50
|
-
"- goal list <step_id> [--status todo|done|all] [--limit N] [--page N]",
|
|
51
|
-
"- goal count <step_id> [--status todo|done|all]",
|
|
52
|
-
"- goal show <id>",
|
|
53
|
-
"- goal comment <id> <comment> [<id> <comment> ...]",
|
|
54
|
-
"- goal update <id> [--content <content>] [--status todo|done] [--comment <comment>]",
|
|
55
|
-
"- goal done <id...>",
|
|
56
|
-
"- goal remove <id...>",
|
|
57
|
-
].join("\n")
|
|
8
|
+
import { PLANPILOT_SYSTEM_INJECTION, PLANPILOT_TOOL_DESCRIPTION, formatPlanpilotAutoContinueMessage } from "./prompt"
|
|
58
9
|
|
|
59
10
|
export const PlanpilotPlugin: Plugin = async (ctx) => {
|
|
60
11
|
const inFlight = new Set<string>()
|
|
61
12
|
const skipNextAuto = new Set<string>()
|
|
62
13
|
const lastIdleAt = new Map<string, number>()
|
|
63
14
|
const waitTimers = new Map<string, ReturnType<typeof setTimeout>>()
|
|
15
|
+
const runSeq = new Map<string, number>()
|
|
64
16
|
|
|
65
17
|
const clearWaitTimer = (sessionID: string) => {
|
|
66
18
|
const existing = waitTimers.get(sessionID)
|
|
@@ -85,6 +37,16 @@ export const PlanpilotPlugin: Plugin = async (ctx) => {
|
|
|
85
37
|
}
|
|
86
38
|
}
|
|
87
39
|
|
|
40
|
+
const logDebug = async (message: string, extra?: Record<string, any>) => {
|
|
41
|
+
await log("debug", message, extra)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const nextRun = (sessionID: string) => {
|
|
45
|
+
const next = (runSeq.get(sessionID) ?? 0) + 1
|
|
46
|
+
runSeq.set(sessionID, next)
|
|
47
|
+
return next
|
|
48
|
+
}
|
|
49
|
+
|
|
88
50
|
type SessionMessage = {
|
|
89
51
|
info?: {
|
|
90
52
|
role?: string
|
|
@@ -162,32 +124,76 @@ export const PlanpilotPlugin: Plugin = async (ctx) => {
|
|
|
162
124
|
aborted,
|
|
163
125
|
ready,
|
|
164
126
|
missingUser: false,
|
|
127
|
+
assistantFinish: finish,
|
|
128
|
+
assistantErrorName: typeof error === "object" && error ? (error as any).name : undefined,
|
|
165
129
|
}
|
|
166
130
|
}
|
|
167
131
|
|
|
168
|
-
const handleSessionIdle = async (sessionID: string) => {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
132
|
+
const handleSessionIdle = async (sessionID: string, source: string) => {
|
|
133
|
+
const now = Date.now()
|
|
134
|
+
const run = nextRun(sessionID)
|
|
135
|
+
|
|
136
|
+
if (inFlight.has(sessionID)) {
|
|
137
|
+
await logDebug("auto-continue skipped: already in-flight", { sessionID, source, run })
|
|
172
138
|
return
|
|
173
139
|
}
|
|
174
|
-
const lastIdle = lastIdleAt.get(sessionID)
|
|
175
|
-
const now = Date.now()
|
|
176
|
-
if (lastIdle && now - lastIdle < 1000) return
|
|
177
|
-
lastIdleAt.set(sessionID, now)
|
|
178
140
|
inFlight.add(sessionID)
|
|
179
141
|
try {
|
|
142
|
+
if (skipNextAuto.has(sessionID)) {
|
|
143
|
+
skipNextAuto.delete(sessionID)
|
|
144
|
+
await logDebug("auto-continue skipped: skipNextAuto", { sessionID, source, run })
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const lastIdle = lastIdleAt.get(sessionID)
|
|
149
|
+
if (lastIdle && now - lastIdle < 1000) {
|
|
150
|
+
await logDebug("auto-continue skipped: idle debounce", {
|
|
151
|
+
sessionID,
|
|
152
|
+
source,
|
|
153
|
+
run,
|
|
154
|
+
lastIdle,
|
|
155
|
+
now,
|
|
156
|
+
deltaMs: now - lastIdle,
|
|
157
|
+
})
|
|
158
|
+
return
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
lastIdleAt.set(sessionID, now)
|
|
162
|
+
|
|
180
163
|
const app = new PlanpilotApp(openDatabase(), sessionID)
|
|
181
164
|
const active = app.getActivePlan()
|
|
182
|
-
if (!active)
|
|
165
|
+
if (!active) {
|
|
166
|
+
clearWaitTimer(sessionID)
|
|
167
|
+
await logDebug("auto-continue skipped: no active plan", { sessionID, source, run })
|
|
168
|
+
return
|
|
169
|
+
}
|
|
183
170
|
const next = app.nextStep(active.plan_id)
|
|
184
|
-
if (!next)
|
|
185
|
-
|
|
171
|
+
if (!next) {
|
|
172
|
+
clearWaitTimer(sessionID)
|
|
173
|
+
await logDebug("auto-continue skipped: no pending step", { sessionID, source, run, planId: active.plan_id })
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
if (next.executor !== "ai") {
|
|
177
|
+
clearWaitTimer(sessionID)
|
|
178
|
+
await logDebug("auto-continue skipped: next executor is not ai", {
|
|
179
|
+
sessionID,
|
|
180
|
+
source,
|
|
181
|
+
run,
|
|
182
|
+
planId: active.plan_id,
|
|
183
|
+
stepId: next.id,
|
|
184
|
+
executor: next.executor,
|
|
185
|
+
})
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
|
|
186
189
|
const wait = parseWaitFromComment(next.comment)
|
|
187
190
|
if (wait && wait.until > now) {
|
|
188
191
|
clearWaitTimer(sessionID)
|
|
189
192
|
await log("info", "auto-continue delayed by step wait", {
|
|
190
193
|
sessionID,
|
|
194
|
+
source,
|
|
195
|
+
run,
|
|
196
|
+
planId: active.plan_id,
|
|
191
197
|
stepId: next.id,
|
|
192
198
|
until: wait.until,
|
|
193
199
|
reason: wait.reason,
|
|
@@ -195,7 +201,7 @@ export const PlanpilotPlugin: Plugin = async (ctx) => {
|
|
|
195
201
|
const msUntil = Math.max(0, wait.until - now)
|
|
196
202
|
const timer = setTimeout(() => {
|
|
197
203
|
waitTimers.delete(sessionID)
|
|
198
|
-
handleSessionIdle(sessionID).catch((err) => {
|
|
204
|
+
handleSessionIdle(sessionID, "wait_timer").catch((err) => {
|
|
199
205
|
void log("warn", "auto-continue retry failed", {
|
|
200
206
|
sessionID,
|
|
201
207
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -211,22 +217,61 @@ export const PlanpilotPlugin: Plugin = async (ctx) => {
|
|
|
211
217
|
|
|
212
218
|
const goals = app.goalsForStep(next.id)
|
|
213
219
|
const detail = formatStepDetail(next, goals)
|
|
214
|
-
if (!detail.trim())
|
|
220
|
+
if (!detail.trim()) {
|
|
221
|
+
await log("warn", "auto-continue stopped: empty step detail", {
|
|
222
|
+
sessionID,
|
|
223
|
+
source,
|
|
224
|
+
run,
|
|
225
|
+
planId: active.plan_id,
|
|
226
|
+
stepId: next.id,
|
|
227
|
+
})
|
|
228
|
+
return
|
|
229
|
+
}
|
|
215
230
|
|
|
216
231
|
const autoContext = await resolveAutoContext(sessionID)
|
|
217
232
|
if (autoContext?.missingUser) {
|
|
218
233
|
await log("warn", "auto-continue stopped: missing user context", { sessionID })
|
|
219
234
|
return
|
|
220
235
|
}
|
|
221
|
-
if (autoContext
|
|
236
|
+
if (!autoContext) {
|
|
237
|
+
await logDebug("auto-continue stopped: missing autoContext (no recent messages?)", {
|
|
238
|
+
sessionID,
|
|
239
|
+
source,
|
|
240
|
+
run,
|
|
241
|
+
planId: active.plan_id,
|
|
242
|
+
stepId: next.id,
|
|
243
|
+
})
|
|
244
|
+
return
|
|
245
|
+
}
|
|
246
|
+
if (autoContext.aborted) {
|
|
247
|
+
await logDebug("auto-continue skipped: last assistant aborted", {
|
|
248
|
+
sessionID,
|
|
249
|
+
source,
|
|
250
|
+
run,
|
|
251
|
+
planId: active.plan_id,
|
|
252
|
+
stepId: next.id,
|
|
253
|
+
assistantErrorName: autoContext.assistantErrorName,
|
|
254
|
+
assistantFinish: autoContext.assistantFinish,
|
|
255
|
+
})
|
|
256
|
+
return
|
|
257
|
+
}
|
|
258
|
+
if (autoContext.ready === false) {
|
|
259
|
+
await logDebug("auto-continue skipped: last assistant not ready", {
|
|
260
|
+
sessionID,
|
|
261
|
+
source,
|
|
262
|
+
run,
|
|
263
|
+
planId: active.plan_id,
|
|
264
|
+
stepId: next.id,
|
|
265
|
+
assistantFinish: autoContext.assistantFinish,
|
|
266
|
+
})
|
|
267
|
+
return
|
|
268
|
+
}
|
|
222
269
|
|
|
223
270
|
const timestamp = new Date().toISOString()
|
|
224
|
-
const message =
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
Next step details:
|
|
229
|
-
${detail.trimEnd()}`
|
|
271
|
+
const message = formatPlanpilotAutoContinueMessage({
|
|
272
|
+
timestamp,
|
|
273
|
+
stepDetail: detail,
|
|
274
|
+
})
|
|
230
275
|
|
|
231
276
|
const promptBody: any = {
|
|
232
277
|
agent: autoContext?.agent ?? undefined,
|
|
@@ -237,18 +282,51 @@ ${detail.trimEnd()}`
|
|
|
237
282
|
promptBody.variant = autoContext.variant
|
|
238
283
|
}
|
|
239
284
|
|
|
285
|
+
await logDebug("auto-continue sending prompt_async", {
|
|
286
|
+
sessionID,
|
|
287
|
+
source,
|
|
288
|
+
run,
|
|
289
|
+
planId: active.plan_id,
|
|
290
|
+
stepId: next.id,
|
|
291
|
+
agent: promptBody.agent,
|
|
292
|
+
model: promptBody.model,
|
|
293
|
+
variant: promptBody.variant,
|
|
294
|
+
messageChars: message.length,
|
|
295
|
+
})
|
|
296
|
+
|
|
240
297
|
await ctx.client.session.promptAsync({
|
|
241
298
|
path: { id: sessionID },
|
|
242
299
|
body: promptBody,
|
|
243
300
|
})
|
|
301
|
+
|
|
302
|
+
await log("info", "auto-continue prompt_async accepted", {
|
|
303
|
+
sessionID,
|
|
304
|
+
source,
|
|
305
|
+
run,
|
|
306
|
+
planId: active.plan_id,
|
|
307
|
+
stepId: next.id,
|
|
308
|
+
})
|
|
244
309
|
} catch (err) {
|
|
245
|
-
await log("warn", "failed to auto-continue plan", {
|
|
310
|
+
await log("warn", "failed to auto-continue plan", {
|
|
311
|
+
sessionID,
|
|
312
|
+
source,
|
|
313
|
+
error: err instanceof Error ? err.message : String(err),
|
|
314
|
+
stack: err instanceof Error ? err.stack : undefined,
|
|
315
|
+
})
|
|
246
316
|
} finally {
|
|
247
317
|
inFlight.delete(sessionID)
|
|
248
318
|
}
|
|
249
319
|
}
|
|
250
320
|
|
|
321
|
+
await log("info", "planpilot plugin initialized", {
|
|
322
|
+
directory: ctx.directory,
|
|
323
|
+
worktree: ctx.worktree,
|
|
324
|
+
})
|
|
325
|
+
|
|
251
326
|
return {
|
|
327
|
+
"experimental.chat.system.transform": async (_input, output) => {
|
|
328
|
+
output.system.push(PLANPILOT_SYSTEM_INJECTION)
|
|
329
|
+
},
|
|
252
330
|
tool: {
|
|
253
331
|
planpilot: tool({
|
|
254
332
|
description: PLANPILOT_TOOL_DESCRIPTION,
|
|
@@ -285,17 +363,22 @@ ${detail.trimEnd()}`
|
|
|
285
363
|
},
|
|
286
364
|
}),
|
|
287
365
|
},
|
|
288
|
-
"experimental.session.compacting": async ({ sessionID }) => {
|
|
366
|
+
"experimental.session.compacting": async ({ sessionID }, output) => {
|
|
289
367
|
skipNextAuto.add(sessionID)
|
|
290
368
|
lastIdleAt.set(sessionID, Date.now())
|
|
369
|
+
|
|
370
|
+
await logDebug("compaction hook: skip next auto-continue", { sessionID })
|
|
371
|
+
|
|
372
|
+
// Compaction runs with tools disabled; inject Planpilot guidance into the continuation summary.
|
|
373
|
+
output.context.push(PLANPILOT_TOOL_DESCRIPTION)
|
|
291
374
|
},
|
|
292
375
|
event: async ({ event }) => {
|
|
293
376
|
if (event.type === "session.idle") {
|
|
294
|
-
await handleSessionIdle(event.properties.sessionID)
|
|
377
|
+
await handleSessionIdle(event.properties.sessionID, "session.idle")
|
|
295
378
|
return
|
|
296
379
|
}
|
|
297
380
|
if (event.type === "session.status" && event.properties.status.type === "idle") {
|
|
298
|
-
await handleSessionIdle(event.properties.sessionID)
|
|
381
|
+
await handleSessionIdle(event.properties.sessionID, "session.status")
|
|
299
382
|
}
|
|
300
383
|
},
|
|
301
384
|
}
|
|
@@ -310,4 +393,3 @@ function containsForbiddenFlags(argv: string[]): boolean {
|
|
|
310
393
|
return false
|
|
311
394
|
})
|
|
312
395
|
}
|
|
313
|
-
|
package/src/prompt.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
export const PLANPILOT_HELP_TEXT = [
|
|
2
|
+
"Planpilot - Plan/Step/Goal auto-continue workflow.",
|
|
3
|
+
"",
|
|
4
|
+
"Model:",
|
|
5
|
+
"- plan -> step -> goal",
|
|
6
|
+
"- step.executor: ai | human",
|
|
7
|
+
"- status rolls up: goals -> steps -> plan",
|
|
8
|
+
"",
|
|
9
|
+
"Rules (important):",
|
|
10
|
+
"- Prefer assigning steps to ai. Use human steps only for actions that require human approval/credentials",
|
|
11
|
+
" or elevated permissions / destructive operations (e.g. GitHub web UI, sudo, deleting data/files).",
|
|
12
|
+
"- One step = one executor. Do NOT mix ai/human work within the same step.",
|
|
13
|
+
" If a person must do something, create a separate human step.",
|
|
14
|
+
"- Keep statuses up to date. Mark goals/steps done as soon as they are completed so roll-up and auto-continue stay correct.",
|
|
15
|
+
"- `step wait` is ONLY for asynchronous, non-blocking external work that is already in progress",
|
|
16
|
+
" (build/test/job/CI/network/remote service).",
|
|
17
|
+
" If you can run something now, do it instead of waiting.",
|
|
18
|
+
" Do NOT use `step wait` to wait for human action.",
|
|
19
|
+
"- Keep comments short and decision-focused.",
|
|
20
|
+
"",
|
|
21
|
+
"Status propagation:",
|
|
22
|
+
"- Step with goals: done iff ALL goals are done; else todo.",
|
|
23
|
+
"- Plan with steps: done iff ALL steps are done; else todo.",
|
|
24
|
+
"- Step with 0 goals: manual status (`step update` / `step done`).",
|
|
25
|
+
"- Plan with 0 steps: manual status (`plan update` / `plan done`).",
|
|
26
|
+
"- When a plan becomes done, it is removed from active plan.",
|
|
27
|
+
"",
|
|
28
|
+
"Auto-continue:",
|
|
29
|
+
"- When the session is idle and an active plan exists:",
|
|
30
|
+
" - if next pending step.executor is ai: Planpilot auto-sends the next step + goals.",
|
|
31
|
+
" - if next pending step.executor is human: no auto-continue.",
|
|
32
|
+
"- Pause while waiting on external systems: `step wait`.",
|
|
33
|
+
"- Stop auto-continue:",
|
|
34
|
+
" - `plan deactivate`, OR",
|
|
35
|
+
" - insert a human step BEFORE the next pending ai step (so the next executor becomes human).",
|
|
36
|
+
"",
|
|
37
|
+
"Invocation:",
|
|
38
|
+
"- argv is tokenized: [section, subcommand, ...args]",
|
|
39
|
+
"- section: help | plan | step | goal",
|
|
40
|
+
"",
|
|
41
|
+
"Commands:",
|
|
42
|
+
"- help",
|
|
43
|
+
"",
|
|
44
|
+
"Plan:",
|
|
45
|
+
"- plan add <title> <content>",
|
|
46
|
+
"- plan add-tree <title> <content> --step <content> [--executor ai|human] [--goal <content>]... [--step ...]...",
|
|
47
|
+
"- plan list [--scope project|all] [--status todo|done|all] [--limit N] [--page N] [--order id|title|created|updated] [--desc]",
|
|
48
|
+
"- plan count [--scope project|all] [--status todo|done|all]",
|
|
49
|
+
"- plan search --search <term> [--search <term> ...] [--search-mode any|all] [--search-field plan|title|content|comment|steps|goals|all] [--match-case] [--scope project|all] [--status todo|done|all] [--limit N] [--page N] [--order id|title|created|updated] [--desc]",
|
|
50
|
+
"- plan show <id>",
|
|
51
|
+
"- plan export <id> <path>",
|
|
52
|
+
"- plan comment <id> <comment> [<id> <comment> ...]",
|
|
53
|
+
"- plan update <id> [--title <title>] [--content <content>] [--status todo|done] [--comment <comment>]",
|
|
54
|
+
"- plan done <id>",
|
|
55
|
+
"- plan remove <id>",
|
|
56
|
+
"- plan activate <id> [--force]",
|
|
57
|
+
"- plan show-active",
|
|
58
|
+
"- plan deactivate",
|
|
59
|
+
"",
|
|
60
|
+
"Step:",
|
|
61
|
+
"- step add <plan_id> <content...> [--executor ai|human] [--at <pos>]",
|
|
62
|
+
"- step add-tree <plan_id> <content> [--executor ai|human] [--goal <content> ...]",
|
|
63
|
+
"- step list <plan_id> [--status todo|done|all] [--executor ai|human] [--limit N] [--page N]",
|
|
64
|
+
"- step count <plan_id> [--status todo|done|all] [--executor ai|human]",
|
|
65
|
+
"- step show <id>",
|
|
66
|
+
"- step show-next",
|
|
67
|
+
"- step wait <id> --delay <ms> [--reason <text>]",
|
|
68
|
+
"- step wait <id> --clear",
|
|
69
|
+
"- step comment <id> <comment> [<id> <comment> ...]",
|
|
70
|
+
"- step update <id> [--content <content>] [--status todo|done] [--executor ai|human] [--comment <comment>]",
|
|
71
|
+
"- step done <id> [--all-goals]",
|
|
72
|
+
"- step move <id> --to <pos>",
|
|
73
|
+
"- step remove <id...>",
|
|
74
|
+
"",
|
|
75
|
+
"Goal:",
|
|
76
|
+
"- goal add <step_id> <content...>",
|
|
77
|
+
"- goal list <step_id> [--status todo|done|all] [--limit N] [--page N]",
|
|
78
|
+
"- goal count <step_id> [--status todo|done|all]",
|
|
79
|
+
"- goal show <id>",
|
|
80
|
+
"- goal comment <id> <comment> [<id> <comment> ...]",
|
|
81
|
+
"- goal update <id> [--content <content>] [--status todo|done] [--comment <comment>]",
|
|
82
|
+
"- goal done <id...>",
|
|
83
|
+
"- goal remove <id...>",
|
|
84
|
+
].join("\n")
|
|
85
|
+
|
|
86
|
+
export const PLANPILOT_TOOL_DESCRIPTION = [
|
|
87
|
+
"Planpilot planner for auto-continue plan workflows.",
|
|
88
|
+
"For multi-step and complex tasks, use Planpilot to structure work into plans/steps/goals.",
|
|
89
|
+
"Run `planpilot help` for full usage + rules.",
|
|
90
|
+
].join("\n")
|
|
91
|
+
|
|
92
|
+
export const PLANPILOT_SYSTEM_INJECTION =
|
|
93
|
+
"If the task is multi-step or complex, must use the `planpilot` plan tool. For full usage + rules, run: planpilot help."
|
|
94
|
+
|
|
95
|
+
export function formatPlanpilotAutoContinueMessage(input: { timestamp: string; stepDetail: string }): string {
|
|
96
|
+
const detail = (input.stepDetail ?? "").trimEnd()
|
|
97
|
+
return [
|
|
98
|
+
`Planpilot @ ${input.timestamp}`,
|
|
99
|
+
"This message was automatically sent by the Planpilot tool because the next pending step executor is ai.",
|
|
100
|
+
"For full usage + rules, run: planpilot help",
|
|
101
|
+
"Next step details:",
|
|
102
|
+
detail,
|
|
103
|
+
].join("\n")
|
|
104
|
+
}
|