poe-code 3.0.340 → 3.0.341

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.340",
3
+ "version": "3.0.341",
4
4
  "description": "CLI tool to configure Poe API for developer workflows.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -2,9 +2,7 @@ import path from "node:path";
2
2
  import { spawnSync as nodeSpawnSync } from "node:child_process";
3
3
  import { hasOwnErrorCode } from "./error-codes.js";
4
4
  export function resolveEditor(env = process.env) {
5
- const editor = getOwnEnvValue(env, "EDITOR")?.trim() ||
6
- getOwnEnvValue(env, "VISUAL")?.trim() ||
7
- "vi";
5
+ const editor = getOwnEnvValue(env, "VISUAL")?.trim() || getOwnEnvValue(env, "EDITOR")?.trim() || "vi";
8
6
  return editor.length > 0 ? editor : "vi";
9
7
  }
10
8
  export function editFile(absolutePath, options = {}) {
@@ -66,12 +64,60 @@ function getOwnEnvValue(env, key) {
66
64
  function hasErrorCode(error, code) {
67
65
  return hasOwnErrorCode(error, code);
68
66
  }
67
+ function isCommandWhitespace(character) {
68
+ return character === " " || character === "\t" || character === "\n" || character === "\r";
69
+ }
69
70
  function parseEditorCommand(command) {
70
- const parts = command.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) ?? [];
71
- return parts.map((part) => {
72
- const quote = part[0];
73
- return (quote === '"' || quote === "'") && part.at(-1) === quote ? part.slice(1, -1) : part;
74
- });
71
+ const parts = [];
72
+ let current = "";
73
+ let quote;
74
+ let escaping = false;
75
+ let hasToken = false;
76
+ for (const character of command) {
77
+ if (escaping) {
78
+ current += character;
79
+ escaping = false;
80
+ hasToken = true;
81
+ continue;
82
+ }
83
+ if (character === "\\") {
84
+ escaping = true;
85
+ hasToken = true;
86
+ continue;
87
+ }
88
+ if (quote !== undefined) {
89
+ if (character === quote) {
90
+ quote = undefined;
91
+ }
92
+ else {
93
+ current += character;
94
+ hasToken = true;
95
+ }
96
+ continue;
97
+ }
98
+ if (character === "'" || character === '"') {
99
+ quote = character;
100
+ hasToken = true;
101
+ continue;
102
+ }
103
+ if (isCommandWhitespace(character)) {
104
+ if (hasToken) {
105
+ parts.push(current);
106
+ current = "";
107
+ hasToken = false;
108
+ }
109
+ continue;
110
+ }
111
+ current += character;
112
+ hasToken = true;
113
+ }
114
+ if (escaping) {
115
+ current += "\\";
116
+ }
117
+ if (hasToken) {
118
+ parts.push(current);
119
+ }
120
+ return parts;
75
121
  }
76
122
  export { archiveSelectedPlan as archivePlan };
77
123
  export async function deletePlan(entry, fs) {
@@ -1,5 +1,6 @@
1
1
  import path from "node:path";
2
2
  import * as fsPromises from "node:fs/promises";
3
+ import { parsePlan } from "@poe-code/pipeline";
3
4
  import { planConfigScope, readMergedDocumentReadonly, resolveScope } from "@poe-code/poe-code-config";
4
5
  import { hasOwnErrorCode } from "./error-codes.js";
5
6
  import { readPlanMetadata, splitFrontmatter } from "./format.js";
@@ -42,8 +43,13 @@ function resolveAbsoluteDirectory(dir, cwd, homeDir) {
42
43
  }
43
44
  return path.isAbsolute(dir) ? dir : path.resolve(cwd, dir);
44
45
  }
45
- function isMarkdownFile(name) {
46
- return name.toLowerCase().endsWith(".md");
46
+ function isSupportedPlanFile(name) {
47
+ const lowerName = name.toLowerCase();
48
+ return lowerName.endsWith(".md") || lowerName.endsWith(".yaml") || lowerName.endsWith(".yml");
49
+ }
50
+ function isYamlPlanFile(name) {
51
+ const lowerName = name.toLowerCase();
52
+ return lowerName.endsWith(".yaml") || lowerName.endsWith(".yml");
47
53
  }
48
54
  function getPlanTypeLabel(kind) {
49
55
  switch (kind) {
@@ -92,6 +98,10 @@ function toPlanKind(value, filePath) {
92
98
  throw new Error(`${filePath}: unsupported frontmatter kind ${JSON.stringify(value)}`);
93
99
  }
94
100
  function classifyPlanKind(content, filePath) {
101
+ if (isYamlPlanFile(filePath)) {
102
+ parsePlan(content);
103
+ return "pipeline";
104
+ }
95
105
  const { data } = splitFrontmatter(content, filePath);
96
106
  if (data === undefined) {
97
107
  return "plan";
@@ -128,15 +138,31 @@ async function discoverSharedPlans(options) {
128
138
  }
129
139
  const plans = [];
130
140
  for (const name of entries) {
131
- if (!isMarkdownFile(name)) {
141
+ if (!isSupportedPlanFile(name)) {
132
142
  continue;
133
143
  }
134
144
  const absolutePath = path.join(absoluteDir, name);
135
- const canonicalPath = await options.fs.realpath(absolutePath);
145
+ const canonicalPath = await options.fs.realpath(absolutePath).catch((error) => {
146
+ if (isNotFound(error)) {
147
+ return undefined;
148
+ }
149
+ throw error;
150
+ });
151
+ if (canonicalPath === undefined) {
152
+ continue;
153
+ }
136
154
  if (canonicalPath !== path.resolve(absolutePath)) {
137
155
  throw new Error(`Plan file must not be a symbolic link: ${path.join(displayDir, name)}`);
138
156
  }
139
- const stat = await options.fs.stat(absolutePath);
157
+ const stat = await options.fs.stat(absolutePath).catch((error) => {
158
+ if (isNotFound(error)) {
159
+ return undefined;
160
+ }
161
+ throw error;
162
+ });
163
+ if (stat === undefined) {
164
+ continue;
165
+ }
140
166
  if (!stat.isFile()) {
141
167
  continue;
142
168
  }