opencode-planpilot 0.2.2 → 0.2.4
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 +397 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +3815 -0
- package/dist/index.js.map +1 -0
- package/dist/studio-bridge.d.ts +2 -0
- package/dist/studio-bridge.js +1985 -0
- package/dist/studio-bridge.js.map +1 -0
- package/dist/studio-web/planpilot-todo-bar.d.ts +33 -0
- package/dist/studio-web/planpilot-todo-bar.js +704 -0
- package/dist/studio-web/planpilot-todo-bar.js.map +1 -0
- package/dist/studio.manifest.json +968 -0
- package/package.json +6 -1
- package/src/command.ts +0 -13
- package/src/index.ts +587 -26
- package/src/lib/config.ts +436 -0
- package/src/prompt.ts +7 -2
- package/src/studio/bridge.ts +746 -0
- package/src/studio-web/main.ts +1030 -0
- package/src/studio-web/planpilot-todo-bar.ts +974 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
import fs from "fs"
|
|
2
|
+
import path from "path"
|
|
3
|
+
import { resolvePlanpilotDir } from "./db"
|
|
4
|
+
|
|
5
|
+
export type KeywordRule = {
|
|
6
|
+
any: string[]
|
|
7
|
+
all: string[]
|
|
8
|
+
none: string[]
|
|
9
|
+
matchCase: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type EventRule = {
|
|
13
|
+
enabled: boolean
|
|
14
|
+
force: boolean
|
|
15
|
+
keywords: KeywordRule
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type SessionErrorRule = EventRule & {
|
|
19
|
+
errorNames: string[]
|
|
20
|
+
statusCodes: number[]
|
|
21
|
+
retryableOnly: boolean
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type SessionRetryRule = EventRule & {
|
|
25
|
+
attemptAtLeast: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type SendRetryConfig = {
|
|
29
|
+
enabled: boolean
|
|
30
|
+
maxAttempts: number
|
|
31
|
+
delaysMs: number[]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type AutoContinueConfig = {
|
|
35
|
+
sendRetry: SendRetryConfig
|
|
36
|
+
onSessionError: SessionErrorRule
|
|
37
|
+
onSessionRetry: SessionRetryRule
|
|
38
|
+
onPermissionAsked: EventRule
|
|
39
|
+
onPermissionRejected: EventRule
|
|
40
|
+
onQuestionAsked: EventRule
|
|
41
|
+
onQuestionRejected: EventRule
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type RuntimeConfig = {
|
|
45
|
+
paused: boolean
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type PlanpilotConfig = {
|
|
49
|
+
autoContinue: AutoContinueConfig
|
|
50
|
+
runtime: RuntimeConfig
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type LoadedPlanpilotConfig = {
|
|
54
|
+
path: string
|
|
55
|
+
loadedFromFile: boolean
|
|
56
|
+
config: PlanpilotConfig
|
|
57
|
+
loadError?: string
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const DEFAULT_KEYWORDS: KeywordRule = {
|
|
61
|
+
any: [],
|
|
62
|
+
all: [],
|
|
63
|
+
none: [],
|
|
64
|
+
matchCase: false,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const DEFAULT_EVENT_RULE: EventRule = {
|
|
68
|
+
enabled: false,
|
|
69
|
+
force: false,
|
|
70
|
+
keywords: DEFAULT_KEYWORDS,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const DEFAULT_SESSION_ERROR_RULE: SessionErrorRule = {
|
|
74
|
+
enabled: false,
|
|
75
|
+
force: true,
|
|
76
|
+
keywords: DEFAULT_KEYWORDS,
|
|
77
|
+
errorNames: [],
|
|
78
|
+
statusCodes: [],
|
|
79
|
+
retryableOnly: false,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const DEFAULT_SESSION_RETRY_RULE: SessionRetryRule = {
|
|
83
|
+
enabled: false,
|
|
84
|
+
force: false,
|
|
85
|
+
keywords: DEFAULT_KEYWORDS,
|
|
86
|
+
attemptAtLeast: 1,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const DEFAULT_SEND_RETRY: SendRetryConfig = {
|
|
90
|
+
enabled: true,
|
|
91
|
+
maxAttempts: 3,
|
|
92
|
+
delaysMs: [1500, 5000, 15000],
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const DEFAULT_PLANPILOT_CONFIG: PlanpilotConfig = {
|
|
96
|
+
autoContinue: {
|
|
97
|
+
sendRetry: DEFAULT_SEND_RETRY,
|
|
98
|
+
onSessionError: DEFAULT_SESSION_ERROR_RULE,
|
|
99
|
+
onSessionRetry: DEFAULT_SESSION_RETRY_RULE,
|
|
100
|
+
onPermissionAsked: DEFAULT_EVENT_RULE,
|
|
101
|
+
onPermissionRejected: {
|
|
102
|
+
...DEFAULT_EVENT_RULE,
|
|
103
|
+
force: true,
|
|
104
|
+
},
|
|
105
|
+
onQuestionAsked: DEFAULT_EVENT_RULE,
|
|
106
|
+
onQuestionRejected: {
|
|
107
|
+
...DEFAULT_EVENT_RULE,
|
|
108
|
+
force: true,
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
runtime: {
|
|
112
|
+
paused: false,
|
|
113
|
+
},
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function resolvePlanpilotConfigPath(): string {
|
|
117
|
+
const override = process.env.OPENCODE_PLANPILOT_CONFIG
|
|
118
|
+
if (override && override.trim()) {
|
|
119
|
+
const value = override.trim()
|
|
120
|
+
return path.isAbsolute(value) ? value : path.resolve(value)
|
|
121
|
+
}
|
|
122
|
+
return path.join(resolvePlanpilotDir(), "config.json")
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
type RawKeywordRule = {
|
|
126
|
+
any?: unknown
|
|
127
|
+
all?: unknown
|
|
128
|
+
none?: unknown
|
|
129
|
+
matchCase?: unknown
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
type RawEventRule = {
|
|
133
|
+
enabled?: unknown
|
|
134
|
+
force?: unknown
|
|
135
|
+
keywords?: RawKeywordRule
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
type RawSessionErrorRule = RawEventRule & {
|
|
139
|
+
errorNames?: unknown
|
|
140
|
+
statusCodes?: unknown
|
|
141
|
+
retryableOnly?: unknown
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
type RawSessionRetryRule = RawEventRule & {
|
|
145
|
+
attemptAtLeast?: unknown
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
type RawSendRetryConfig = {
|
|
149
|
+
enabled?: unknown
|
|
150
|
+
maxAttempts?: unknown
|
|
151
|
+
delaysMs?: unknown
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
type RawAutoContinueConfig = {
|
|
155
|
+
sendRetry?: RawSendRetryConfig
|
|
156
|
+
onSessionError?: RawSessionErrorRule
|
|
157
|
+
onSessionRetry?: RawSessionRetryRule
|
|
158
|
+
onPermissionAsked?: RawEventRule
|
|
159
|
+
onPermissionRejected?: RawEventRule
|
|
160
|
+
onQuestionAsked?: RawEventRule
|
|
161
|
+
onQuestionRejected?: RawEventRule
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
type RawRuntimeConfig = {
|
|
165
|
+
paused?: unknown
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
type RawPlanpilotConfig = {
|
|
169
|
+
autoContinue?: RawAutoContinueConfig
|
|
170
|
+
runtime?: RawRuntimeConfig
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function cloneDefaultConfig(): PlanpilotConfig {
|
|
174
|
+
return {
|
|
175
|
+
autoContinue: {
|
|
176
|
+
sendRetry: {
|
|
177
|
+
enabled: DEFAULT_PLANPILOT_CONFIG.autoContinue.sendRetry.enabled,
|
|
178
|
+
maxAttempts: DEFAULT_PLANPILOT_CONFIG.autoContinue.sendRetry.maxAttempts,
|
|
179
|
+
delaysMs: [...DEFAULT_PLANPILOT_CONFIG.autoContinue.sendRetry.delaysMs],
|
|
180
|
+
},
|
|
181
|
+
onSessionError: {
|
|
182
|
+
enabled: DEFAULT_PLANPILOT_CONFIG.autoContinue.onSessionError.enabled,
|
|
183
|
+
force: DEFAULT_PLANPILOT_CONFIG.autoContinue.onSessionError.force,
|
|
184
|
+
keywords: {
|
|
185
|
+
any: [...DEFAULT_PLANPILOT_CONFIG.autoContinue.onSessionError.keywords.any],
|
|
186
|
+
all: [...DEFAULT_PLANPILOT_CONFIG.autoContinue.onSessionError.keywords.all],
|
|
187
|
+
none: [...DEFAULT_PLANPILOT_CONFIG.autoContinue.onSessionError.keywords.none],
|
|
188
|
+
matchCase: DEFAULT_PLANPILOT_CONFIG.autoContinue.onSessionError.keywords.matchCase,
|
|
189
|
+
},
|
|
190
|
+
errorNames: [...DEFAULT_PLANPILOT_CONFIG.autoContinue.onSessionError.errorNames],
|
|
191
|
+
statusCodes: [...DEFAULT_PLANPILOT_CONFIG.autoContinue.onSessionError.statusCodes],
|
|
192
|
+
retryableOnly: DEFAULT_PLANPILOT_CONFIG.autoContinue.onSessionError.retryableOnly,
|
|
193
|
+
},
|
|
194
|
+
onSessionRetry: {
|
|
195
|
+
enabled: DEFAULT_PLANPILOT_CONFIG.autoContinue.onSessionRetry.enabled,
|
|
196
|
+
force: DEFAULT_PLANPILOT_CONFIG.autoContinue.onSessionRetry.force,
|
|
197
|
+
keywords: {
|
|
198
|
+
any: [...DEFAULT_PLANPILOT_CONFIG.autoContinue.onSessionRetry.keywords.any],
|
|
199
|
+
all: [...DEFAULT_PLANPILOT_CONFIG.autoContinue.onSessionRetry.keywords.all],
|
|
200
|
+
none: [...DEFAULT_PLANPILOT_CONFIG.autoContinue.onSessionRetry.keywords.none],
|
|
201
|
+
matchCase: DEFAULT_PLANPILOT_CONFIG.autoContinue.onSessionRetry.keywords.matchCase,
|
|
202
|
+
},
|
|
203
|
+
attemptAtLeast: DEFAULT_PLANPILOT_CONFIG.autoContinue.onSessionRetry.attemptAtLeast,
|
|
204
|
+
},
|
|
205
|
+
onPermissionAsked: {
|
|
206
|
+
enabled: DEFAULT_PLANPILOT_CONFIG.autoContinue.onPermissionAsked.enabled,
|
|
207
|
+
force: DEFAULT_PLANPILOT_CONFIG.autoContinue.onPermissionAsked.force,
|
|
208
|
+
keywords: {
|
|
209
|
+
any: [...DEFAULT_PLANPILOT_CONFIG.autoContinue.onPermissionAsked.keywords.any],
|
|
210
|
+
all: [...DEFAULT_PLANPILOT_CONFIG.autoContinue.onPermissionAsked.keywords.all],
|
|
211
|
+
none: [...DEFAULT_PLANPILOT_CONFIG.autoContinue.onPermissionAsked.keywords.none],
|
|
212
|
+
matchCase: DEFAULT_PLANPILOT_CONFIG.autoContinue.onPermissionAsked.keywords.matchCase,
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
onPermissionRejected: {
|
|
216
|
+
enabled: DEFAULT_PLANPILOT_CONFIG.autoContinue.onPermissionRejected.enabled,
|
|
217
|
+
force: DEFAULT_PLANPILOT_CONFIG.autoContinue.onPermissionRejected.force,
|
|
218
|
+
keywords: {
|
|
219
|
+
any: [...DEFAULT_PLANPILOT_CONFIG.autoContinue.onPermissionRejected.keywords.any],
|
|
220
|
+
all: [...DEFAULT_PLANPILOT_CONFIG.autoContinue.onPermissionRejected.keywords.all],
|
|
221
|
+
none: [...DEFAULT_PLANPILOT_CONFIG.autoContinue.onPermissionRejected.keywords.none],
|
|
222
|
+
matchCase: DEFAULT_PLANPILOT_CONFIG.autoContinue.onPermissionRejected.keywords.matchCase,
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
onQuestionAsked: {
|
|
226
|
+
enabled: DEFAULT_PLANPILOT_CONFIG.autoContinue.onQuestionAsked.enabled,
|
|
227
|
+
force: DEFAULT_PLANPILOT_CONFIG.autoContinue.onQuestionAsked.force,
|
|
228
|
+
keywords: {
|
|
229
|
+
any: [...DEFAULT_PLANPILOT_CONFIG.autoContinue.onQuestionAsked.keywords.any],
|
|
230
|
+
all: [...DEFAULT_PLANPILOT_CONFIG.autoContinue.onQuestionAsked.keywords.all],
|
|
231
|
+
none: [...DEFAULT_PLANPILOT_CONFIG.autoContinue.onQuestionAsked.keywords.none],
|
|
232
|
+
matchCase: DEFAULT_PLANPILOT_CONFIG.autoContinue.onQuestionAsked.keywords.matchCase,
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
onQuestionRejected: {
|
|
236
|
+
enabled: DEFAULT_PLANPILOT_CONFIG.autoContinue.onQuestionRejected.enabled,
|
|
237
|
+
force: DEFAULT_PLANPILOT_CONFIG.autoContinue.onQuestionRejected.force,
|
|
238
|
+
keywords: {
|
|
239
|
+
any: [...DEFAULT_PLANPILOT_CONFIG.autoContinue.onQuestionRejected.keywords.any],
|
|
240
|
+
all: [...DEFAULT_PLANPILOT_CONFIG.autoContinue.onQuestionRejected.keywords.all],
|
|
241
|
+
none: [...DEFAULT_PLANPILOT_CONFIG.autoContinue.onQuestionRejected.keywords.none],
|
|
242
|
+
matchCase: DEFAULT_PLANPILOT_CONFIG.autoContinue.onQuestionRejected.keywords.matchCase,
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
runtime: {
|
|
247
|
+
paused: DEFAULT_PLANPILOT_CONFIG.runtime.paused,
|
|
248
|
+
},
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function parseBoolean(value: unknown, fallback: boolean): boolean {
|
|
253
|
+
if (typeof value === "boolean") return value
|
|
254
|
+
return fallback
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function parseStringArray(value: unknown): string[] {
|
|
258
|
+
if (!Array.isArray(value)) return []
|
|
259
|
+
const parsed = value
|
|
260
|
+
.filter((item): item is string => typeof item === "string")
|
|
261
|
+
.map((item) => item.trim())
|
|
262
|
+
.filter((item) => item.length > 0)
|
|
263
|
+
return Array.from(new Set(parsed))
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function parseNumberArray(value: unknown): number[] {
|
|
267
|
+
if (!Array.isArray(value)) return []
|
|
268
|
+
const parsed = value
|
|
269
|
+
.map((item) => (typeof item === "number" ? item : Number.NaN))
|
|
270
|
+
.filter((item) => Number.isFinite(item))
|
|
271
|
+
.map((item) => Math.trunc(item))
|
|
272
|
+
return Array.from(new Set(parsed))
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function parsePositiveInt(value: unknown, fallback: number): number {
|
|
276
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return fallback
|
|
277
|
+
const parsed = Math.trunc(value)
|
|
278
|
+
return parsed > 0 ? parsed : fallback
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function parsePositiveNumberArray(value: unknown, fallback: number[]): number[] {
|
|
282
|
+
if (!Array.isArray(value)) return fallback
|
|
283
|
+
const parsed = value
|
|
284
|
+
.map((item) => (typeof item === "number" ? item : Number.NaN))
|
|
285
|
+
.filter((item) => Number.isFinite(item))
|
|
286
|
+
.map((item) => Math.trunc(item))
|
|
287
|
+
.filter((item) => item > 0)
|
|
288
|
+
if (!parsed.length) return fallback
|
|
289
|
+
return Array.from(new Set(parsed))
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function parseKeywordRule(value: RawKeywordRule | undefined, fallback: KeywordRule): KeywordRule {
|
|
293
|
+
return {
|
|
294
|
+
any: parseStringArray(value?.any),
|
|
295
|
+
all: parseStringArray(value?.all),
|
|
296
|
+
none: parseStringArray(value?.none),
|
|
297
|
+
matchCase: parseBoolean(value?.matchCase, fallback.matchCase),
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function parseEventRule(value: RawEventRule | undefined, fallback: EventRule): EventRule {
|
|
302
|
+
return {
|
|
303
|
+
enabled: parseBoolean(value?.enabled, fallback.enabled),
|
|
304
|
+
force: parseBoolean(value?.force, fallback.force),
|
|
305
|
+
keywords: parseKeywordRule(value?.keywords, fallback.keywords),
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function parseSessionErrorRule(value: RawSessionErrorRule | undefined, fallback: SessionErrorRule): SessionErrorRule {
|
|
310
|
+
const base = parseEventRule(value, fallback)
|
|
311
|
+
return {
|
|
312
|
+
...base,
|
|
313
|
+
errorNames: parseStringArray(value?.errorNames),
|
|
314
|
+
statusCodes: parseNumberArray(value?.statusCodes),
|
|
315
|
+
retryableOnly: parseBoolean(value?.retryableOnly, fallback.retryableOnly),
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function parseSessionRetryRule(value: RawSessionRetryRule | undefined, fallback: SessionRetryRule): SessionRetryRule {
|
|
320
|
+
const base = parseEventRule(value, fallback)
|
|
321
|
+
const rawAttempt = typeof value?.attemptAtLeast === "number" ? Math.trunc(value.attemptAtLeast) : fallback.attemptAtLeast
|
|
322
|
+
return {
|
|
323
|
+
...base,
|
|
324
|
+
attemptAtLeast: rawAttempt > 0 ? rawAttempt : fallback.attemptAtLeast,
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function parseSendRetryConfig(value: RawSendRetryConfig | undefined, fallback: SendRetryConfig): SendRetryConfig {
|
|
329
|
+
return {
|
|
330
|
+
enabled: parseBoolean(value?.enabled, fallback.enabled),
|
|
331
|
+
maxAttempts: parsePositiveInt(value?.maxAttempts, fallback.maxAttempts),
|
|
332
|
+
delaysMs: parsePositiveNumberArray(value?.delaysMs, fallback.delaysMs),
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function parseConfig(raw: RawPlanpilotConfig): PlanpilotConfig {
|
|
337
|
+
return {
|
|
338
|
+
autoContinue: {
|
|
339
|
+
sendRetry: parseSendRetryConfig(raw.autoContinue?.sendRetry, DEFAULT_PLANPILOT_CONFIG.autoContinue.sendRetry),
|
|
340
|
+
onSessionError: parseSessionErrorRule(
|
|
341
|
+
raw.autoContinue?.onSessionError,
|
|
342
|
+
DEFAULT_PLANPILOT_CONFIG.autoContinue.onSessionError,
|
|
343
|
+
),
|
|
344
|
+
onSessionRetry: parseSessionRetryRule(
|
|
345
|
+
raw.autoContinue?.onSessionRetry,
|
|
346
|
+
DEFAULT_PLANPILOT_CONFIG.autoContinue.onSessionRetry,
|
|
347
|
+
),
|
|
348
|
+
onPermissionAsked: parseEventRule(
|
|
349
|
+
raw.autoContinue?.onPermissionAsked,
|
|
350
|
+
DEFAULT_PLANPILOT_CONFIG.autoContinue.onPermissionAsked,
|
|
351
|
+
),
|
|
352
|
+
onPermissionRejected: parseEventRule(
|
|
353
|
+
raw.autoContinue?.onPermissionRejected,
|
|
354
|
+
DEFAULT_PLANPILOT_CONFIG.autoContinue.onPermissionRejected,
|
|
355
|
+
),
|
|
356
|
+
onQuestionAsked: parseEventRule(
|
|
357
|
+
raw.autoContinue?.onQuestionAsked,
|
|
358
|
+
DEFAULT_PLANPILOT_CONFIG.autoContinue.onQuestionAsked,
|
|
359
|
+
),
|
|
360
|
+
onQuestionRejected: parseEventRule(
|
|
361
|
+
raw.autoContinue?.onQuestionRejected,
|
|
362
|
+
DEFAULT_PLANPILOT_CONFIG.autoContinue.onQuestionRejected,
|
|
363
|
+
),
|
|
364
|
+
},
|
|
365
|
+
runtime: {
|
|
366
|
+
paused: parseBoolean(raw.runtime?.paused, DEFAULT_PLANPILOT_CONFIG.runtime.paused),
|
|
367
|
+
},
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
export function normalizePlanpilotConfig(raw: unknown): PlanpilotConfig {
|
|
372
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
373
|
+
return cloneDefaultConfig()
|
|
374
|
+
}
|
|
375
|
+
return parseConfig(raw as RawPlanpilotConfig)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export function savePlanpilotConfig(config: PlanpilotConfig): LoadedPlanpilotConfig {
|
|
379
|
+
const filePath = resolvePlanpilotConfigPath()
|
|
380
|
+
const normalized = normalizePlanpilotConfig(config)
|
|
381
|
+
const parentDir = path.dirname(filePath)
|
|
382
|
+
fs.mkdirSync(parentDir, { recursive: true })
|
|
383
|
+
fs.writeFileSync(filePath, `${JSON.stringify(normalized, null, 2)}\n`, "utf8")
|
|
384
|
+
return {
|
|
385
|
+
path: filePath,
|
|
386
|
+
loadedFromFile: true,
|
|
387
|
+
config: normalized,
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export function loadPlanpilotConfig(): LoadedPlanpilotConfig {
|
|
392
|
+
const filePath = resolvePlanpilotConfigPath()
|
|
393
|
+
try {
|
|
394
|
+
if (!fs.existsSync(filePath)) {
|
|
395
|
+
return {
|
|
396
|
+
path: filePath,
|
|
397
|
+
loadedFromFile: false,
|
|
398
|
+
config: cloneDefaultConfig(),
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
const text = fs.readFileSync(filePath, "utf8")
|
|
402
|
+
const parsed = JSON.parse(text) as unknown
|
|
403
|
+
return {
|
|
404
|
+
path: filePath,
|
|
405
|
+
loadedFromFile: true,
|
|
406
|
+
config: normalizePlanpilotConfig(parsed),
|
|
407
|
+
}
|
|
408
|
+
} catch (error) {
|
|
409
|
+
const loadError = error instanceof Error ? error.message : String(error)
|
|
410
|
+
return {
|
|
411
|
+
path: filePath,
|
|
412
|
+
loadedFromFile: false,
|
|
413
|
+
config: cloneDefaultConfig(),
|
|
414
|
+
loadError,
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export function matchesKeywords(text: string, rule: KeywordRule): boolean {
|
|
420
|
+
const source = rule.matchCase ? text : text.toLowerCase()
|
|
421
|
+
const normalize = (value: string) => (rule.matchCase ? value : value.toLowerCase())
|
|
422
|
+
const any = rule.any.map(normalize)
|
|
423
|
+
const all = rule.all.map(normalize)
|
|
424
|
+
const none = rule.none.map(normalize)
|
|
425
|
+
|
|
426
|
+
if (any.length > 0 && !any.some((term) => source.includes(term))) {
|
|
427
|
+
return false
|
|
428
|
+
}
|
|
429
|
+
if (!all.every((term) => source.includes(term))) {
|
|
430
|
+
return false
|
|
431
|
+
}
|
|
432
|
+
if (none.some((term) => source.includes(term))) {
|
|
433
|
+
return false
|
|
434
|
+
}
|
|
435
|
+
return true
|
|
436
|
+
}
|
package/src/prompt.ts
CHANGED
|
@@ -42,7 +42,6 @@ export const PLANPILOT_HELP_TEXT = [
|
|
|
42
42
|
"- help",
|
|
43
43
|
"",
|
|
44
44
|
"Plan:",
|
|
45
|
-
"- plan add <title> <content>",
|
|
46
45
|
"- plan add-tree <title> <content> --step <content> [--executor ai|human] [--goal <content>]... [--step ...]...",
|
|
47
46
|
"- plan list [--scope project|all] [--status todo|done|all] [--limit N] [--page N] [--order id|title|created|updated] [--desc]",
|
|
48
47
|
"- plan count [--scope project|all] [--status todo|done|all]",
|
|
@@ -92,11 +91,17 @@ export const PLANPILOT_TOOL_DESCRIPTION = [
|
|
|
92
91
|
export const PLANPILOT_SYSTEM_INJECTION =
|
|
93
92
|
"If the task is multi-step or complex, must use the `planpilot` plan tool. For full usage + rules, run: planpilot help."
|
|
94
93
|
|
|
95
|
-
export function formatPlanpilotAutoContinueMessage(input: {
|
|
94
|
+
export function formatPlanpilotAutoContinueMessage(input: {
|
|
95
|
+
timestamp: string
|
|
96
|
+
stepDetail: string
|
|
97
|
+
triggerDetail?: string
|
|
98
|
+
}): string {
|
|
96
99
|
const detail = (input.stepDetail ?? "").trimEnd()
|
|
100
|
+
const trigger = (input.triggerDetail ?? "").trim()
|
|
97
101
|
return [
|
|
98
102
|
`Planpilot @ ${input.timestamp}`,
|
|
99
103
|
"This message was automatically sent by the Planpilot tool because the next pending step executor is ai.",
|
|
104
|
+
...(trigger ? [`Trigger context: ${trigger}`] : []),
|
|
100
105
|
"For full usage + rules, run: planpilot help",
|
|
101
106
|
"Next step details:",
|
|
102
107
|
detail,
|