opencode-planpilot 0.2.0 → 0.2.1
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 +12 -58
- 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,56 +5,7 @@ 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>()
|
|
@@ -221,12 +172,10 @@ export const PlanpilotPlugin: Plugin = async (ctx) => {
|
|
|
221
172
|
if (autoContext?.aborted || autoContext?.ready === false) return
|
|
222
173
|
|
|
223
174
|
const timestamp = new Date().toISOString()
|
|
224
|
-
const message =
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
Next step details:
|
|
229
|
-
${detail.trimEnd()}`
|
|
175
|
+
const message = formatPlanpilotAutoContinueMessage({
|
|
176
|
+
timestamp,
|
|
177
|
+
stepDetail: detail,
|
|
178
|
+
})
|
|
230
179
|
|
|
231
180
|
const promptBody: any = {
|
|
232
181
|
agent: autoContext?.agent ?? undefined,
|
|
@@ -249,6 +198,9 @@ ${detail.trimEnd()}`
|
|
|
249
198
|
}
|
|
250
199
|
|
|
251
200
|
return {
|
|
201
|
+
"experimental.chat.system.transform": async (_input, output) => {
|
|
202
|
+
output.system.push(PLANPILOT_SYSTEM_INJECTION)
|
|
203
|
+
},
|
|
252
204
|
tool: {
|
|
253
205
|
planpilot: tool({
|
|
254
206
|
description: PLANPILOT_TOOL_DESCRIPTION,
|
|
@@ -285,9 +237,12 @@ ${detail.trimEnd()}`
|
|
|
285
237
|
},
|
|
286
238
|
}),
|
|
287
239
|
},
|
|
288
|
-
"experimental.session.compacting": async ({ sessionID }) => {
|
|
240
|
+
"experimental.session.compacting": async ({ sessionID }, output) => {
|
|
289
241
|
skipNextAuto.add(sessionID)
|
|
290
242
|
lastIdleAt.set(sessionID, Date.now())
|
|
243
|
+
|
|
244
|
+
// Compaction runs with tools disabled; inject Planpilot guidance into the continuation summary.
|
|
245
|
+
output.context.push(PLANPILOT_TOOL_DESCRIPTION)
|
|
291
246
|
},
|
|
292
247
|
event: async ({ event }) => {
|
|
293
248
|
if (event.type === "session.idle") {
|
|
@@ -310,4 +265,3 @@ function containsForbiddenFlags(argv: string[]): boolean {
|
|
|
310
265
|
return false
|
|
311
266
|
})
|
|
312
267
|
}
|
|
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
|
+
}
|