poe-code 4.0.6 → 4.0.7

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poe-code",
3
- "version": "4.0.6",
3
+ "version": "4.0.7",
4
4
  "description": "CLI tool to configure Poe API for developer workflows.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -2,12 +2,14 @@ import { promises as nodeFs } from "node:fs";
2
2
  import path from "node:path";
3
3
  import { parse } from "yaml";
4
4
  export const GASLIGHT_CONFIG_EXAMPLE = [
5
+ "setup: Prepare the workspace",
5
6
  "prompt: Implement",
6
7
  "archive: false",
7
8
  "followups:",
8
9
  " - Is this best you can do?",
9
10
  " - Did you test it well? Like real end to end test?",
10
- " - Did you forget something?"
11
+ " - Did you forget something?",
12
+ "teardown: Clean up the workspace"
11
13
  ].join("\n");
12
14
  function isMissingFile(error) {
13
15
  return (typeof error === "object" &&
@@ -35,7 +37,11 @@ function validateConfig(value, configPath, options) {
35
37
  }
36
38
  const config = value;
37
39
  if (options.rejectExtraKeys) {
38
- const extraKey = objectKeys(config).find((key) => key !== "prompt" && key !== "followups" && key !== "archive");
40
+ const extraKey = objectKeys(config).find((key) => key !== "setup" &&
41
+ key !== "prompt" &&
42
+ key !== "followups" &&
43
+ key !== "teardown" &&
44
+ key !== "archive");
39
45
  if (extraKey) {
40
46
  throw new Error(`Invalid gaslight config at ${configPath}: unexpected key "${extraKey}".`);
41
47
  }
@@ -51,9 +57,16 @@ function validateConfig(value, configPath, options) {
51
57
  if (config.archive !== undefined && typeof config.archive !== "boolean") {
52
58
  throw new Error(`Invalid gaslight config at ${configPath}: archive must be a boolean.`);
53
59
  }
60
+ for (const key of ["setup", "teardown"]) {
61
+ if (config[key] !== undefined && (typeof config[key] !== "string" || !config[key].trim())) {
62
+ throw new Error(`Invalid gaslight config at ${configPath}: ${key} must be a non-empty string.`);
63
+ }
64
+ }
54
65
  return {
66
+ ...(typeof config.setup === "string" ? { setup: config.setup.trim() } : {}),
55
67
  prompt: config.prompt.trim(),
56
68
  followups: config.followups.map((followup) => followup.trim()),
69
+ ...(typeof config.teardown === "string" ? { teardown: config.teardown.trim() } : {}),
57
70
  ...(config.archive !== undefined ? { archive: config.archive } : {})
58
71
  };
59
72
  }
@@ -82,6 +82,12 @@ function resolveModel(value) {
82
82
  }
83
83
  return trimmed;
84
84
  }
85
+ function resolveOptionalPrompt(value, label) {
86
+ if (value === undefined) {
87
+ return undefined;
88
+ }
89
+ return requireNonEmptyString(value, label);
90
+ }
85
91
  function resolvePlanPaths(options, cwd, homeDir) {
86
92
  if (options.planPaths.length === 0) {
87
93
  throw new Error("Provide at least one plan path.");
@@ -110,20 +116,32 @@ export async function runGaslight(options) {
110
116
  const spawn = options.spawn ?? defaultSpawn;
111
117
  const agent = requireNonEmptyString(options.agent, "agent");
112
118
  const model = resolveModel(options.model);
119
+ const inlineSetup = resolveOptionalPrompt(options.setup, "setup");
120
+ const inlineTeardown = resolveOptionalPrompt(options.teardown, "teardown");
113
121
  validateInlineConfig(options.prompt, options.followups);
114
122
  const planPaths = resolvePlanPaths(options, cwd, homeDir);
115
123
  for (const planPath of planPaths) {
116
124
  await requirePlan(fs, resolvePlanPath(cwd, homeDir, planPath), planPath);
117
125
  }
118
126
  const config = options.prompt !== undefined && options.followups !== undefined
119
- ? { prompt: options.prompt.trim(), followups: options.followups.map((value) => value.trim()) }
127
+ ? {
128
+ ...(inlineSetup ? { setup: inlineSetup } : {}),
129
+ prompt: options.prompt.trim(),
130
+ followups: options.followups.map((value) => value.trim()),
131
+ ...(inlineTeardown ? { teardown: inlineTeardown } : {})
132
+ }
120
133
  : await loadGaslightConfig(cwd, homeDir, fs, options.configPath);
121
134
  const shouldArchive = options.archive ?? config.archive ?? false;
122
135
  const rounds = [];
123
136
  const plans = [];
124
137
  let usage;
125
138
  for (const [planIndex, planPath] of planPaths.entries()) {
126
- const prompts = [`${config.prompt} ${planPath}`, ...config.followups];
139
+ const prompts = [
140
+ ...(config.setup ? [config.setup] : []),
141
+ `${config.prompt} ${planPath}`,
142
+ ...config.followups,
143
+ ...(config.teardown ? [config.teardown] : [])
144
+ ];
127
145
  const planRounds = [];
128
146
  let planUsage;
129
147
  let resumeThreadId;
@@ -51,16 +51,20 @@ export interface GaslightOptions {
51
51
  cwd?: string;
52
52
  homeDir?: string;
53
53
  configPath?: string;
54
+ setup?: string;
54
55
  prompt?: string;
55
56
  followups?: string[];
57
+ teardown?: string;
56
58
  onEvent?: (event: GaslightEvent) => void;
57
59
  signal?: AbortSignal;
58
60
  fs?: GaslightFileSystem;
59
61
  spawn?: GaslightSpawn;
60
62
  }
61
63
  export interface GaslightConfig {
64
+ setup?: string;
62
65
  prompt: string;
63
66
  followups: string[];
67
+ teardown?: string;
64
68
  archive?: boolean;
65
69
  path: string;
66
70
  }