abelworkflow 0.9.2 → 1.0.0

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.
Files changed (134) hide show
  1. package/README.md +54 -182
  2. package/bin/abelworkflow.mjs +5 -3
  3. package/extensions/pi-gpt-responses-compat/index.ts +1 -1
  4. package/lib/cli/args.mjs +76 -0
  5. package/lib/cli/main.mjs +385 -0
  6. package/lib/cli/prompts.mjs +106 -0
  7. package/lib/config/dotenv.mjs +102 -0
  8. package/lib/config/jsonc.mjs +80 -0
  9. package/lib/config/store.mjs +310 -0
  10. package/lib/config/toml.mjs +638 -0
  11. package/lib/installer/assets.mjs +324 -0
  12. package/lib/installer/install.mjs +153 -0
  13. package/lib/installer/links.mjs +427 -0
  14. package/lib/installer/state.mjs +163 -0
  15. package/lib/paths.mjs +99 -0
  16. package/lib/providers/claude.mjs +197 -0
  17. package/lib/providers/codex.mjs +433 -0
  18. package/lib/providers/pi.mjs +577 -0
  19. package/lib/providers/skills.mjs +105 -0
  20. package/lib/templates/codex/agents/default.toml +7 -0
  21. package/lib/templates/codex/agents/explorer.toml +7 -0
  22. package/lib/templates/codex/agents/planner.toml +7 -0
  23. package/lib/templates/codex/agents/reviewer.toml +7 -0
  24. package/lib/templates/codex/agents/worker.toml +7 -0
  25. package/lib/templates/codex/config-base.toml +11 -3
  26. package/lib/templates/workflow/AGENTS.md +50 -0
  27. package/lib/templates/workflow/commands/abel-design.md +175 -0
  28. package/{commands → lib/templates/workflow/commands}/abel-diagnose.md +5 -5
  29. package/lib/templates/workflow/commands/abel-implement.md +170 -0
  30. package/{commands → lib/templates/workflow/commands}/abel-init.md +3 -5
  31. package/{.gitignore → lib/templates/workflow/gitignore.template} +0 -2
  32. package/lib/tools/cli-installer.mjs +277 -0
  33. package/package.json +37 -10
  34. package/skills/context7-auto-research/SKILL.md +5 -5
  35. package/skills/context7-auto-research/{context7-api.js → context7-api.cjs} +5 -8
  36. package/skills/dev-browser/SKILL.md +53 -33
  37. package/skills/dev-browser/dist/scripts/start.d.ts +2 -0
  38. package/skills/dev-browser/dist/scripts/start.d.ts.map +1 -0
  39. package/skills/dev-browser/dist/scripts/start.js +118 -0
  40. package/skills/dev-browser/dist/scripts/start.js.map +1 -0
  41. package/skills/dev-browser/dist/src/client.d.ts +93 -0
  42. package/skills/dev-browser/dist/src/client.d.ts.map +1 -0
  43. package/skills/dev-browser/dist/src/client.js +310 -0
  44. package/skills/dev-browser/dist/src/client.js.map +1 -0
  45. package/skills/dev-browser/dist/src/entrypoint.d.ts +29 -0
  46. package/skills/dev-browser/dist/src/entrypoint.d.ts.map +1 -0
  47. package/skills/dev-browser/dist/src/entrypoint.js +106 -0
  48. package/skills/dev-browser/dist/src/entrypoint.js.map +1 -0
  49. package/skills/dev-browser/dist/src/index.d.ts +4 -0
  50. package/skills/dev-browser/dist/src/index.d.ts.map +1 -0
  51. package/skills/dev-browser/dist/src/index.js +2 -0
  52. package/skills/dev-browser/dist/src/index.js.map +1 -0
  53. package/skills/dev-browser/dist/src/page-api.d.ts +25 -0
  54. package/skills/dev-browser/dist/src/page-api.d.ts.map +1 -0
  55. package/skills/dev-browser/dist/src/page-api.js +104 -0
  56. package/skills/dev-browser/dist/src/page-api.js.map +1 -0
  57. package/skills/dev-browser/dist/src/relay.d.ts +27 -0
  58. package/skills/dev-browser/dist/src/relay.d.ts.map +1 -0
  59. package/skills/dev-browser/dist/src/relay.js +521 -0
  60. package/skills/dev-browser/dist/src/relay.js.map +1 -0
  61. package/skills/dev-browser/dist/src/runtime.d.ts +45 -0
  62. package/skills/dev-browser/dist/src/runtime.d.ts.map +1 -0
  63. package/skills/dev-browser/dist/src/runtime.js +54 -0
  64. package/skills/dev-browser/dist/src/runtime.js.map +1 -0
  65. package/skills/dev-browser/dist/src/snapshot/browser-script.d.ts +23 -0
  66. package/skills/dev-browser/dist/src/snapshot/browser-script.d.ts.map +1 -0
  67. package/skills/dev-browser/{src/snapshot/browser-script.ts → dist/src/snapshot/browser-script.js} +21 -30
  68. package/skills/dev-browser/dist/src/snapshot/browser-script.js.map +1 -0
  69. package/skills/dev-browser/dist/src/snapshot/index.d.ts +14 -0
  70. package/skills/dev-browser/dist/src/snapshot/index.d.ts.map +1 -0
  71. package/skills/dev-browser/{src/snapshot/index.ts → dist/src/snapshot/index.js} +2 -2
  72. package/skills/dev-browser/dist/src/snapshot/index.js.map +1 -0
  73. package/skills/dev-browser/dist/src/snapshot/inject.d.ts +13 -0
  74. package/skills/dev-browser/dist/src/snapshot/inject.d.ts.map +1 -0
  75. package/skills/dev-browser/{src/snapshot/inject.ts → dist/src/snapshot/inject.js} +2 -2
  76. package/skills/dev-browser/dist/src/snapshot/inject.js.map +1 -0
  77. package/skills/dev-browser/dist/src/standalone.d.ts +32 -0
  78. package/skills/dev-browser/dist/src/standalone.d.ts.map +1 -0
  79. package/skills/dev-browser/dist/src/standalone.js +174 -0
  80. package/skills/dev-browser/dist/src/standalone.js.map +1 -0
  81. package/skills/dev-browser/dist/src/startup.d.ts +55 -0
  82. package/skills/dev-browser/dist/src/startup.d.ts.map +1 -0
  83. package/skills/dev-browser/dist/src/startup.js +81 -0
  84. package/skills/dev-browser/dist/src/startup.js.map +1 -0
  85. package/skills/dev-browser/dist/src/target-registry.d.ts +29 -0
  86. package/skills/dev-browser/dist/src/target-registry.d.ts.map +1 -0
  87. package/skills/dev-browser/dist/src/target-registry.js +135 -0
  88. package/skills/dev-browser/dist/src/target-registry.js.map +1 -0
  89. package/skills/dev-browser/dist/src/types.d.ts +27 -0
  90. package/skills/dev-browser/dist/src/types.d.ts.map +1 -0
  91. package/skills/dev-browser/dist/src/types.js +2 -0
  92. package/skills/dev-browser/dist/src/types.js.map +1 -0
  93. package/skills/dev-browser/package-lock.json +67 -1510
  94. package/skills/dev-browser/package.json +18 -14
  95. package/skills/dev-browser/references/scraping.md +94 -105
  96. package/skills/grok-search/.env.example +3 -3
  97. package/skills/grok-search/SKILL.md +1 -1
  98. package/skills/grok-search/defaults.json +3 -0
  99. package/skills/grok-search/gitignore.template +4 -0
  100. package/skills/grok-search/scripts/_dotenv.py +5 -1
  101. package/skills/grok-search/scripts/groksearch_cli.py +15 -33
  102. package/skills/prompt-enhancer/SKILL.md +2 -27
  103. package/skills/prompt-enhancer/TEMPLATE.md +0 -4
  104. package/.skill-lock.json +0 -29
  105. package/AGENTS.md +0 -45
  106. package/commands/abel-implement.md +0 -157
  107. package/commands/abel-plan.md +0 -88
  108. package/commands/abel-research.md +0 -126
  109. package/lib/cli/logic.mjs +0 -213
  110. package/lib/cli.mjs +0 -3016
  111. package/skills/confidence-check/SKILL.md +0 -34
  112. package/skills/confidence-check/confidence.ts +0 -276
  113. package/skills/dev-browser/bun.lock +0 -460
  114. package/skills/dev-browser/scripts/start.ts +0 -280
  115. package/skills/dev-browser/src/client.ts +0 -474
  116. package/skills/dev-browser/src/entrypoint.ts +0 -157
  117. package/skills/dev-browser/src/index.ts +0 -289
  118. package/skills/dev-browser/src/relay.ts +0 -731
  119. package/skills/dev-browser/src/runtime.test.ts +0 -60
  120. package/skills/dev-browser/src/runtime.ts +0 -171
  121. package/skills/dev-browser/src/startup.test.ts +0 -95
  122. package/skills/dev-browser/src/startup.ts +0 -153
  123. package/skills/dev-browser/src/types.ts +0 -35
  124. package/skills/dev-browser/tsconfig.json +0 -36
  125. package/skills/dev-browser/vitest.config.ts +0 -12
  126. package/skills/prompt-enhancer/.env.example +0 -12
  127. package/skills/prompt-enhancer/ADVANCED.md +0 -103
  128. package/skills/prompt-enhancer/requirements.txt +0 -2
  129. package/skills/prompt-enhancer/scripts/_dotenv.py +0 -28
  130. package/skills/prompt-enhancer/scripts/enhance.py +0 -191
  131. package/skills/prompt-enhancer/scripts/prompt_enhancer_entry.py +0 -253
  132. package/skills/sequential-think/SKILL.md +0 -198
  133. package/skills/sequential-think/scripts/.env.example +0 -5
  134. package/skills/sequential-think/scripts/sequential_think_cli.py +0 -253
@@ -0,0 +1,310 @@
1
+ import * as fs from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ import lockfile from "proper-lockfile";
4
+ import { parseDotenv, updateDotenvContent } from "./dotenv.mjs";
5
+ import { stripJsonComments } from "./jsonc.mjs";
6
+
7
+ const newBackupMarker = ".abelworkflow.bak.";
8
+ const newBackupSuffixPattern = /^\d+-\d+-\d{10,}$/u;
9
+ const chmod = fs.chmod;
10
+ let uniqueFileIndex = 0;
11
+
12
+ function nextUniqueSuffix() {
13
+ uniqueFileIndex += 1;
14
+ return `${Date.now()}-${process.pid}-${String(uniqueFileIndex).padStart(10, "0")}`;
15
+ }
16
+
17
+ function isMissing(error) {
18
+ return error?.code === "ENOENT";
19
+ }
20
+
21
+ function isPosix() {
22
+ return process.platform !== "win32";
23
+ }
24
+
25
+ async function pathExists(path) {
26
+ try {
27
+ await fs.lstat(path);
28
+ return true;
29
+ } catch (error) {
30
+ if (isMissing(error)) return false;
31
+ throw error;
32
+ }
33
+ }
34
+
35
+ async function pathTargetExists(path) {
36
+ try {
37
+ await fs.stat(path);
38
+ return true;
39
+ } catch (error) {
40
+ if (isMissing(error)) return false;
41
+ throw error;
42
+ }
43
+ }
44
+
45
+ async function repairSensitiveMode(path) {
46
+ if (!isPosix()) return false;
47
+ const current = await fs.lstat(path);
48
+ if (!current.isFile()) {
49
+ throw new Error(`Refusing sensitive file access through non-regular path: ${path}`);
50
+ }
51
+ if ((current.mode & 0o777) === 0o600) return false;
52
+ await fs.chmod(path, 0o600);
53
+ return true;
54
+ }
55
+
56
+ async function readFileWithOptions(path, { fallback, sensitive = false, hasFallback = true } = {}) {
57
+ try {
58
+ if (sensitive) await assertSensitiveRegularIfPresent(path);
59
+ const content = await fs.readFile(path, "utf8");
60
+ if (sensitive) await repairSensitiveMode(path);
61
+ return content;
62
+ } catch (error) {
63
+ if (isMissing(error) && hasFallback) return fallback;
64
+ throw error;
65
+ }
66
+ }
67
+
68
+ async function assertSensitiveRegularIfPresent(path) {
69
+ try {
70
+ const targetStat = await fs.lstat(path);
71
+ if (!targetStat.isFile()) {
72
+ throw new Error(`Refusing sensitive file access through non-regular path: ${path}`);
73
+ }
74
+ } catch (error) {
75
+ if (!isMissing(error)) throw error;
76
+ }
77
+ }
78
+
79
+ function parseJsonAtPath(path, content, jsonc = false) {
80
+ try {
81
+ return JSON.parse(jsonc ? stripJsonComments(content) : content);
82
+ } catch (error) {
83
+ const format = jsonc ? "JSONC" : "JSON";
84
+ throw new SyntaxError(`Invalid ${format} in ${path}: ${error.message}`, { cause: error });
85
+ }
86
+ }
87
+
88
+ async function readJsonFileSafe(path, fallback = {}, options = {}) {
89
+ const content = await readFileWithOptions(path, {
90
+ fallback: null,
91
+ sensitive: options.sensitive
92
+ });
93
+ return content === null ? fallback : parseJsonAtPath(path, content);
94
+ }
95
+
96
+ async function readJsoncFileSafe(path, fallback = {}, options = {}) {
97
+ const content = await readFileWithOptions(path, {
98
+ fallback: null,
99
+ sensitive: options.sensitive
100
+ });
101
+ return content === null ? fallback : parseJsonAtPath(path, content, true);
102
+ }
103
+
104
+ async function readText(path, options = {}) {
105
+ return readFileWithOptions(path, {
106
+ fallback: options.fallback,
107
+ sensitive: options.sensitive,
108
+ hasFallback: Object.hasOwn(options, "fallback")
109
+ });
110
+ }
111
+
112
+ async function listNewBackups(targetPath) {
113
+ const directory = dirname(targetPath);
114
+ const prefix = `${targetPath.slice(directory.length + 1)}${newBackupMarker}`;
115
+ let names;
116
+ try {
117
+ names = await fs.readdir(directory);
118
+ } catch (error) {
119
+ if (isMissing(error)) return [];
120
+ throw error;
121
+ }
122
+ return names.filter((name) => (
123
+ name.startsWith(prefix) && newBackupSuffixPattern.test(name.slice(prefix.length))
124
+ )).sort();
125
+ }
126
+
127
+ async function pruneNewBackups(targetPath, backupLimit) {
128
+ if (!Number.isInteger(backupLimit) || backupLimit < 0) {
129
+ throw new TypeError("backupLimit must be a non-negative integer");
130
+ }
131
+ const names = await listNewBackups(targetPath);
132
+ const removeCount = Math.max(0, names.length - backupLimit);
133
+ await Promise.all(names.slice(0, removeCount).map((name) => fs.rm(join(dirname(targetPath), name), {
134
+ recursive: true,
135
+ force: true
136
+ })));
137
+ }
138
+
139
+ async function createBackupPath(targetPath) {
140
+ while (true) {
141
+ const backupPath = `${targetPath}${newBackupMarker}${nextUniqueSuffix()}`;
142
+ if (!(await pathExists(backupPath))) return backupPath;
143
+ }
144
+ }
145
+
146
+ async function copyBackup(targetPath, { sensitive = false, backupLimit = 3 } = {}) {
147
+ if (backupLimit === 0 || !(await pathExists(targetPath))) return null;
148
+ if (sensitive) await assertSensitiveRegularIfPresent(targetPath);
149
+ const backupPath = await createBackupPath(targetPath);
150
+ await fs.cp(targetPath, backupPath, { recursive: true, force: false, errorOnExist: true });
151
+ if (sensitive && isPosix()) await fs.chmod(backupPath, 0o600);
152
+ await pruneNewBackups(targetPath, backupLimit);
153
+ return backupPath;
154
+ }
155
+
156
+ async function writeText(path, content, options = {}) {
157
+ const {
158
+ sensitive = false,
159
+ backupLimit = 3,
160
+ mode: requestedMode,
161
+ renameFile = fs.rename
162
+ } = options;
163
+ const bytes = Buffer.isBuffer(content) ? content : Buffer.from(String(content));
164
+ let previous = null;
165
+ let previousMode;
166
+ let exists = true;
167
+ let permissionRepaired = false;
168
+
169
+ if (sensitive) await assertSensitiveRegularIfPresent(path);
170
+ try {
171
+ previous = await fs.readFile(path);
172
+ previousMode = (await fs.stat(path)).mode & 0o777;
173
+ } catch (error) {
174
+ if (!isMissing(error)) throw error;
175
+ exists = false;
176
+ }
177
+
178
+ if (exists && sensitive) {
179
+ permissionRepaired = await repairSensitiveMode(path);
180
+ previousMode = 0o600;
181
+ } else if (exists && requestedMode !== undefined && isPosix() && previousMode !== requestedMode) {
182
+ await fs.chmod(path, requestedMode);
183
+ previousMode = requestedMode;
184
+ permissionRepaired = true;
185
+ }
186
+
187
+ if (exists && previous.equals(bytes)) {
188
+ return { status: permissionRepaired ? "permission-repaired" : "unchanged", backupPath: null };
189
+ }
190
+
191
+ await fs.mkdir(dirname(path), { recursive: true, mode: sensitive ? 0o700 : 0o777 });
192
+ const tempPath = join(dirname(path), `.${path.slice(dirname(path).length + 1)}.abelworkflow.tmp.${nextUniqueSuffix()}`);
193
+ const mode = sensitive ? 0o600 : (requestedMode ?? previousMode ?? 0o644);
194
+ let backupPath = null;
195
+ try {
196
+ await fs.writeFile(tempPath, bytes, { flag: "wx", mode });
197
+ if (isPosix()) await fs.chmod(tempPath, mode);
198
+ if (exists) backupPath = await copyBackup(path, { sensitive, backupLimit });
199
+ await renameFile(tempPath, path);
200
+ return { status: exists ? "updated" : "created", backupPath };
201
+ } catch (error) {
202
+ await fs.rm(tempPath, { force: true }).catch(() => {});
203
+ throw error;
204
+ }
205
+ }
206
+
207
+ async function writeJson(path, value, options = {}) {
208
+ await readJsonFileSafe(path, {}, options);
209
+ return writeText(path, `${JSON.stringify(value, null, 2)}\n`, options);
210
+ }
211
+
212
+ async function updateLockedJson(path, updater, options = {}) {
213
+ await fs.mkdir(dirname(path), { recursive: true, mode: options.sensitive ? 0o700 : 0o777 });
214
+ const release = await lockfile.lock(path, {
215
+ realpath: false,
216
+ retries: options.retries ?? 2,
217
+ lockfilePath: `${path}.lock`
218
+ });
219
+ try {
220
+ const current = await readJsonFileSafe(path, options.defaultValue ?? {}, options);
221
+ const updated = await updater(current);
222
+ return await writeJson(path, updated, options);
223
+ } finally {
224
+ await release();
225
+ }
226
+ }
227
+
228
+ async function backupExistingPath(targetPath, options = {}) {
229
+ return copyBackup(targetPath, options);
230
+ }
231
+
232
+ async function backupPrivateFile(targetPath, content, options = {}) {
233
+ if (!(await pathExists(targetPath))) return null;
234
+ const backupLimit = options.backupLimit ?? 3;
235
+ if (backupLimit === 0) return null;
236
+ const backupPath = await createBackupPath(targetPath);
237
+ await fs.writeFile(backupPath, content, { encoding: "utf8", flag: "wx", mode: 0o600 });
238
+ if (isPosix()) await fs.chmod(backupPath, 0o600);
239
+ await pruneNewBackups(targetPath, backupLimit);
240
+ return backupPath;
241
+ }
242
+
243
+ async function backupIfNeeded(targetPath, options = {}) {
244
+ if (!(await pathExists(targetPath))) return null;
245
+ const backupLimit = options.backupLimit ?? 3;
246
+ if (backupLimit === 0) return null;
247
+ const backupPath = await createBackupPath(targetPath);
248
+ await fs.rename(targetPath, backupPath);
249
+ if (options.sensitive && isPosix()) await fs.chmod(backupPath, 0o600);
250
+ await pruneNewBackups(targetPath, backupLimit);
251
+ return backupPath;
252
+ }
253
+
254
+ async function ensurePrivateJsonFile(path) {
255
+ await fs.mkdir(dirname(path), { recursive: true, mode: 0o700 });
256
+ try {
257
+ await fs.writeFile(path, "{}\n", { encoding: "utf8", flag: "wx", mode: 0o600 });
258
+ if (isPosix()) await fs.chmod(path, 0o600);
259
+ return true;
260
+ } catch (error) {
261
+ if (error?.code !== "EEXIST") throw error;
262
+ await assertSensitiveRegularIfPresent(path);
263
+ await repairSensitiveMode(path);
264
+ return false;
265
+ }
266
+ }
267
+
268
+ async function readDotenvFile(path, options = {}) {
269
+ const content = await readFileWithOptions(path, {
270
+ fallback: "",
271
+ sensitive: options.sensitive
272
+ });
273
+ return parseDotenv(content);
274
+ }
275
+
276
+ async function updateDotenvFile(path, updates, options = {}) {
277
+ const content = await readFileWithOptions(path, {
278
+ fallback: "",
279
+ sensitive: options.sensitive
280
+ });
281
+ return writeText(path, updateDotenvContent(content, updates), options);
282
+ }
283
+
284
+ async function writeJsonFileSafe(path, data, options = {}) {
285
+ return writeJson(path, data, { ...options, backupLimit: options.backupLimit ?? 0 });
286
+ }
287
+
288
+ async function writeJsonFileWithBackup(path, data, options = {}) {
289
+ return writeJson(path, data, options);
290
+ }
291
+
292
+ export {
293
+ backupExistingPath,
294
+ backupIfNeeded,
295
+ backupPrivateFile,
296
+ chmod,
297
+ ensurePrivateJsonFile,
298
+ pathExists,
299
+ pathTargetExists,
300
+ readDotenvFile,
301
+ readJsonFileSafe,
302
+ readJsoncFileSafe,
303
+ readText,
304
+ updateDotenvFile,
305
+ updateLockedJson,
306
+ writeJson,
307
+ writeJsonFileSafe,
308
+ writeJsonFileWithBackup,
309
+ writeText
310
+ };