poe-code 3.0.361 → 3.0.362

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": "3.0.361",
3
+ "version": "3.0.362",
4
4
  "description": "CLI tool to configure Poe API for developer workflows.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -230,7 +230,7 @@ export async function runDocumentWorkflow(options) {
230
230
  return;
231
231
  }
232
232
  let shouldStop = false;
233
- for (let iteration = 0; iteration < initialWorkflow.maxIterations; iteration += 1) {
233
+ for (let iteration = 0;; iteration += 1) {
234
234
  throwIfAborted(options.signal);
235
235
  currentWorkflow = iteration === 0 ? initialWorkflow : await readWorkflow();
236
236
  if (iteration >= currentWorkflow.maxIterations) {
@@ -266,6 +266,9 @@ export async function runDocumentWorkflow(options) {
266
266
  if (shouldStop) {
267
267
  break;
268
268
  }
269
+ if (iteration + 1 >= currentWorkflow.maxIterations) {
270
+ break;
271
+ }
269
272
  }
270
273
  }
271
274
  catch (error) {
@@ -15,9 +15,20 @@ export const noPublishedToPrivateDep = {
15
15
  continue;
16
16
  for (const edge of dependencyEdges(consumer)) {
17
17
  const dep = model.byName.get(edge.name);
18
- if (!dep || !dep.private)
18
+ if (!dep)
19
19
  continue;
20
20
  if (edge.field !== "peerDependencies" && consumer.bundledDependencies.includes(dep.name)) {
21
+ violations.push(...collectBundledPrivateDependencyViolations({
22
+ consumer,
23
+ bundledRoot: dep,
24
+ modelByName: model.byName,
25
+ consumerBundled: new Set(consumer.bundledDependencies),
26
+ via: [dep.name]
27
+ }));
28
+ if (dep.private)
29
+ continue;
30
+ }
31
+ if (!dep.private) {
21
32
  continue;
22
33
  }
23
34
  violations.push({
@@ -34,3 +45,45 @@ export const noPublishedToPrivateDep = {
34
45
  return violations;
35
46
  }
36
47
  };
48
+ function collectBundledPrivateDependencyViolations(options) {
49
+ const violations = [];
50
+ const visited = new Set();
51
+ const pending = [
52
+ { pkg: options.bundledRoot, via: options.via }
53
+ ];
54
+ while (pending.length > 0) {
55
+ const current = pending.shift();
56
+ if (visited.has(current.pkg.name)) {
57
+ continue;
58
+ }
59
+ visited.add(current.pkg.name);
60
+ for (const edge of dependencyEdges(current.pkg)) {
61
+ const dep = options.modelByName.get(edge.name);
62
+ if (!dep || !dep.private) {
63
+ continue;
64
+ }
65
+ if (isBundledRuntimeEdge(current.pkg, edge, dep, options.consumerBundled)) {
66
+ pending.push({ pkg: dep, via: [...current.via, dep.name] });
67
+ continue;
68
+ }
69
+ violations.push({
70
+ rule: id,
71
+ package: options.consumer.name,
72
+ severity: "error",
73
+ via: edge.field,
74
+ detail: {
75
+ dependency: dep.name,
76
+ field: edge.field,
77
+ bundledVia: current.via
78
+ },
79
+ message: `published package bundles ${current.via.join(" -> ")}, whose ${edge.field} requires private workspace package ${dep.name}`,
80
+ fix: `Bundle ${dep.name} into ${current.pkg.name}, publish ${dep.name}, or remove the ${edge.field} edge from the bundled package manifest.`
81
+ });
82
+ }
83
+ }
84
+ return violations;
85
+ }
86
+ function isBundledRuntimeEdge(pkg, edge, dep, consumerBundled) {
87
+ return (edge.field !== "peerDependencies" &&
88
+ (pkg.bundledDependencies.includes(dep.name) || consumerBundled.has(dep.name)));
89
+ }
@@ -1,3 +1,4 @@
1
+ import semver from "semver";
1
2
  import { allPackages, dependencyEdges, isPublishedNpm } from "../model.js";
2
3
  const id = "published-dep-needs-version-range";
3
4
  /** A `*` / `workspace:*`-style range that does not pin to a bounded set of versions. */
@@ -36,13 +37,30 @@ export const publishedDepNeedsVersionRange = {
36
37
  const dep = model.byName.get(edge.name);
37
38
  if (!dep || !isPublishedNpm(dep))
38
39
  continue;
39
- if (!isLooseRange(edge.spec))
40
- continue;
41
40
  if (edge.field !== "peerDependencies" && consumer.bundledDependencies.includes(dep.name)) {
42
41
  continue;
43
42
  }
43
+ if (!isLooseRange(edge.spec) && !semver.satisfies(dep.version, edge.spec)) {
44
+ violations.push({
45
+ rule: id,
46
+ package: consumer.name,
47
+ severity: "error",
48
+ via: edge.field,
49
+ detail: {
50
+ dependency: dep.name,
51
+ field: edge.field,
52
+ range: edge.spec,
53
+ version: dep.version
54
+ },
55
+ message: `published dependency ${dep.name}@${edge.spec} does not include workspace version ${dep.version}`,
56
+ fix: `Replace "${edge.spec}" with a concrete range that includes ${dep.version}, such as "^${dep.version}".`
57
+ });
58
+ continue;
59
+ }
44
60
  if (isDeclaredLockstepDependency(model, consumer.dir, dep.dir))
45
61
  continue;
62
+ if (!isLooseRange(edge.spec))
63
+ continue;
46
64
  violations.push({
47
65
  rule: id,
48
66
  package: consumer.name,
@@ -146,17 +146,18 @@ export function parseFrontmatterData(value) {
146
146
  throw new Error('Invalid Ralph frontmatter: "kind" must be "ralph".');
147
147
  }
148
148
  }
149
- const statusValue = parsed ? getOwnEntry(parsed, "status") : undefined;
150
- const parsedStatus = isRecord(statusValue) ? statusValue : undefined;
151
- const state = parsePlanStatus(parsedStatus ? getOwnEntry(parsedStatus, "state") : undefined) ??
152
- parseLegacyStatus(statusValue) ??
153
- defaults.status.state;
154
- const iteration = parseNonNegativeInteger(parsedStatus ? getOwnEntry(parsedStatus, "iteration") : undefined) ??
155
- parseNonNegativeInteger(parsed ? getOwnEntry(parsed, "iteration") : undefined) ??
156
- defaults.status.iteration;
157
- const agent = parseAgent(parsed ? getOwnEntry(parsed, "agent") : undefined);
149
+ const { state, iteration } = parseStatusFields(parsed, defaults.status);
150
+ const agentValue = parsed ? getOwnEntry(parsed, "agent") : undefined;
151
+ const agent = parseAgent(agentValue);
152
+ if (parsed !== undefined && hasOwnEntry(parsed, "agent") && agent === undefined) {
153
+ throw new Error('Invalid Ralph frontmatter: "agent" must be a non-empty string or non-empty string array.');
154
+ }
158
155
  const extendsValue = parseBoolean(parsed ? getOwnEntry(parsed, "extends") : undefined);
159
- const iterations = parsePositiveInteger(parsed ? getOwnEntry(parsed, "iterations") : undefined);
156
+ const iterationsValue = parsed ? getOwnEntry(parsed, "iterations") : undefined;
157
+ const iterations = parsePositiveInteger(iterationsValue);
158
+ if (parsed !== undefined && hasOwnEntry(parsed, "iterations") && iterations === undefined) {
159
+ throw new Error('Invalid Ralph frontmatter: "iterations" must be a positive integer.');
160
+ }
160
161
  const skills = parseSkills(parsed ? getOwnEntry(parsed, "skills") : undefined);
161
162
  const hooks = parseHooks(parsed ? getOwnEntry(parsed, "hooks") : undefined);
162
163
  return {
@@ -171,6 +172,44 @@ export function parseFrontmatterData(value) {
171
172
  }
172
173
  };
173
174
  }
175
+ function parseStatusFields(parsed, defaults) {
176
+ if (parsed === undefined) {
177
+ return defaults;
178
+ }
179
+ const statusValue = getOwnEntry(parsed, "status");
180
+ const legacyIterationValue = getOwnEntry(parsed, "iteration");
181
+ const hasStatus = hasOwnEntry(parsed, "status");
182
+ const hasLegacyIteration = hasOwnEntry(parsed, "iteration");
183
+ const parsedStatus = isRecord(statusValue) ? statusValue : undefined;
184
+ if (hasLegacyIteration && parseNonNegativeInteger(legacyIterationValue) === undefined) {
185
+ throw new Error('Invalid Ralph frontmatter: "iteration" must be a non-negative integer.');
186
+ }
187
+ if (parsedStatus !== undefined) {
188
+ rejectUnknownKeys(parsedStatus, ["state", "iteration"], "status");
189
+ const statusStateValue = getOwnEntry(parsedStatus, "state");
190
+ const statusIterationValue = getOwnEntry(parsedStatus, "iteration");
191
+ const state = parsePlanStatus(statusStateValue);
192
+ const iteration = parseNonNegativeInteger(statusIterationValue);
193
+ if (hasOwnEntry(parsedStatus, "state") && state === undefined) {
194
+ throw new Error('Invalid Ralph frontmatter: "status.state" must be "open", "in_progress", "completed", or "failed".');
195
+ }
196
+ if (hasOwnEntry(parsedStatus, "iteration") && iteration === undefined) {
197
+ throw new Error('Invalid Ralph frontmatter: "status.iteration" must be a non-negative integer.');
198
+ }
199
+ return {
200
+ state: state ?? defaults.state,
201
+ iteration: iteration ?? parseNonNegativeInteger(legacyIterationValue) ?? defaults.iteration
202
+ };
203
+ }
204
+ const legacyState = parseLegacyStatus(statusValue);
205
+ if (hasStatus && legacyState === undefined) {
206
+ throw new Error('Invalid Ralph frontmatter: "status" must be "open", "pending", "cancelled", "overbake_abort", "in_progress", or "completed".');
207
+ }
208
+ return {
209
+ state: legacyState ?? defaults.state,
210
+ iteration: parseNonNegativeInteger(legacyIterationValue) ?? defaults.iteration
211
+ };
212
+ }
174
213
  function parseAgent(value) {
175
214
  if (typeof value === "string") {
176
215
  const trimmed = value.trim();
@@ -180,7 +219,7 @@ function parseAgent(value) {
180
219
  return undefined;
181
220
  }
182
221
  if (value.length === 0) {
183
- return [];
222
+ return undefined;
184
223
  }
185
224
  const agents = [];
186
225
  for (const item of value) {
@@ -281,6 +320,9 @@ function parseBoolean(value) {
281
320
  function isRecord(value) {
282
321
  return typeof value === "object" && value !== null && !Array.isArray(value);
283
322
  }
323
+ function hasOwnEntry(record, key) {
324
+ return Object.prototype.hasOwnProperty.call(record, key);
325
+ }
284
326
  function getOwnEntry(record, key) {
285
- return Object.prototype.hasOwnProperty.call(record, key) ? record[key] : undefined;
327
+ return hasOwnEntry(record, key) ? record[key] : undefined;
286
328
  }
@@ -143,7 +143,7 @@ export async function runRalph(options) {
143
143
  await updateFrontmatter(fs, absoluteDocPath, "open", iterationsCompleted);
144
144
  return;
145
145
  }
146
- if (iterationNumber === config.maxIterations) {
146
+ if (iterationNumber === currentConfig.maxIterations) {
147
147
  await updateFrontmatter(fs, absoluteDocPath, "completed", iterationsCompleted);
148
148
  stopReason = "max_iterations";
149
149
  return;