abelworkflow 1.1.2 → 1.2.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 (98) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +30 -29
  3. package/extensions/gpt-responses-compat.ts +213 -0
  4. package/extensions/k2think-compat.ts +40 -0
  5. package/lib/cli/args.mjs +5 -19
  6. package/lib/cli/main.mjs +105 -72
  7. package/lib/cli/prompts.mjs +9 -2
  8. package/lib/config/dotenv.mjs +1 -1
  9. package/lib/config/jsonc.mjs +1 -1
  10. package/lib/config/store.mjs +197 -45
  11. package/lib/config/toml.mjs +147 -453
  12. package/lib/installer/assets.mjs +67 -45
  13. package/lib/installer/install.mjs +202 -45
  14. package/lib/installer/links.mjs +124 -101
  15. package/lib/installer/lock.mjs +82 -0
  16. package/lib/installer/state.mjs +14 -21
  17. package/lib/paths.mjs +9 -9
  18. package/lib/providers/claude.mjs +58 -114
  19. package/lib/providers/codex.mjs +101 -198
  20. package/lib/providers/pi.mjs +332 -439
  21. package/lib/providers/skills.mjs +88 -68
  22. package/lib/providers/url.mjs +35 -0
  23. package/lib/templates/workflow/commands/abel-design.md +23 -161
  24. package/lib/templates/workflow/commands/abel-diagnose.md +30 -56
  25. package/lib/templates/workflow/commands/abel-implement.md +30 -150
  26. package/lib/templates/workflow/commands/abel-init.md +1 -1
  27. package/lib/tools/cli-installer.mjs +29 -190
  28. package/package.json +7 -9
  29. package/skills/context7-auto-research/context7-api.cjs +75 -17
  30. package/skills/dev-browser/SKILL.md +15 -1
  31. package/skills/dev-browser/dist/scripts/start.d.ts +0 -1
  32. package/skills/dev-browser/dist/scripts/start.js +8 -36
  33. package/skills/dev-browser/dist/src/client.d.ts +3 -4
  34. package/skills/dev-browser/dist/src/client.js +117 -117
  35. package/skills/dev-browser/dist/src/entrypoint.d.ts +1 -1
  36. package/skills/dev-browser/dist/src/entrypoint.js +38 -31
  37. package/skills/dev-browser/dist/src/index.d.ts +0 -1
  38. package/skills/dev-browser/dist/src/index.js +0 -1
  39. package/skills/dev-browser/dist/src/page-api.d.ts +0 -1
  40. package/skills/dev-browser/dist/src/page-api.js +0 -1
  41. package/skills/dev-browser/dist/src/relay.d.ts +0 -1
  42. package/skills/dev-browser/dist/src/relay.js +53 -7
  43. package/skills/dev-browser/dist/src/runtime.d.ts +3 -14
  44. package/skills/dev-browser/dist/src/runtime.js +5 -15
  45. package/skills/dev-browser/dist/src/snapshot/browser-script.d.ts +0 -1
  46. package/skills/dev-browser/dist/src/snapshot/browser-script.js +0 -1
  47. package/skills/dev-browser/dist/src/snapshot/index.d.ts +0 -1
  48. package/skills/dev-browser/dist/src/snapshot/index.js +0 -1
  49. package/skills/dev-browser/dist/src/snapshot/inject.d.ts +0 -1
  50. package/skills/dev-browser/dist/src/snapshot/inject.js +0 -1
  51. package/skills/dev-browser/dist/src/standalone.d.ts +0 -1
  52. package/skills/dev-browser/dist/src/standalone.js +2 -3
  53. package/skills/dev-browser/dist/src/startup.d.ts +3 -12
  54. package/skills/dev-browser/dist/src/startup.js +6 -10
  55. package/skills/dev-browser/dist/src/target-registry.d.ts +0 -1
  56. package/skills/dev-browser/dist/src/target-registry.js +0 -1
  57. package/skills/dev-browser/dist/src/types.d.ts +0 -1
  58. package/skills/dev-browser/dist/src/types.js +0 -1
  59. package/skills/dev-browser/package-lock.json +1 -1
  60. package/skills/dev-browser/package.json +2 -2
  61. package/skills/grok-search/.env.example +1 -1
  62. package/skills/grok-search/SKILL.md +13 -16
  63. package/skills/grok-search/scripts/_dotenv.py +1 -1
  64. package/skills/grok-search/scripts/groksearch_cli.py +271 -195
  65. package/skills/time/SKILL.md +5 -2
  66. package/skills/time/scripts/time_cli.py +61 -22
  67. package/extensions/pi-gpt-responses-compat/index.ts +0 -25
  68. package/lib/templates/codex/config-base.toml +0 -103
  69. package/skills/dev-browser/dist/scripts/start.d.ts.map +0 -1
  70. package/skills/dev-browser/dist/scripts/start.js.map +0 -1
  71. package/skills/dev-browser/dist/src/client.d.ts.map +0 -1
  72. package/skills/dev-browser/dist/src/client.js.map +0 -1
  73. package/skills/dev-browser/dist/src/entrypoint.d.ts.map +0 -1
  74. package/skills/dev-browser/dist/src/entrypoint.js.map +0 -1
  75. package/skills/dev-browser/dist/src/index.d.ts.map +0 -1
  76. package/skills/dev-browser/dist/src/index.js.map +0 -1
  77. package/skills/dev-browser/dist/src/page-api.d.ts.map +0 -1
  78. package/skills/dev-browser/dist/src/page-api.js.map +0 -1
  79. package/skills/dev-browser/dist/src/relay.d.ts.map +0 -1
  80. package/skills/dev-browser/dist/src/relay.js.map +0 -1
  81. package/skills/dev-browser/dist/src/runtime.d.ts.map +0 -1
  82. package/skills/dev-browser/dist/src/runtime.js.map +0 -1
  83. package/skills/dev-browser/dist/src/snapshot/browser-script.d.ts.map +0 -1
  84. package/skills/dev-browser/dist/src/snapshot/browser-script.js.map +0 -1
  85. package/skills/dev-browser/dist/src/snapshot/index.d.ts.map +0 -1
  86. package/skills/dev-browser/dist/src/snapshot/index.js.map +0 -1
  87. package/skills/dev-browser/dist/src/snapshot/inject.d.ts.map +0 -1
  88. package/skills/dev-browser/dist/src/snapshot/inject.js.map +0 -1
  89. package/skills/dev-browser/dist/src/standalone.d.ts.map +0 -1
  90. package/skills/dev-browser/dist/src/standalone.js.map +0 -1
  91. package/skills/dev-browser/dist/src/startup.d.ts.map +0 -1
  92. package/skills/dev-browser/dist/src/startup.js.map +0 -1
  93. package/skills/dev-browser/dist/src/target-registry.d.ts.map +0 -1
  94. package/skills/dev-browser/dist/src/target-registry.js.map +0 -1
  95. package/skills/dev-browser/dist/src/types.d.ts.map +0 -1
  96. package/skills/dev-browser/dist/src/types.js.map +0 -1
  97. package/skills/grok-search/gitignore.template +0 -4
  98. package/skills/grok-search/scripts/groksearch_entry.py +0 -131
@@ -1,170 +1,50 @@
1
1
  ---
2
2
  name: abel-implement
3
- description: Implement approved changes with mandatory TDD.
3
+ description: Implement an OpenSpec change with test-first Red, Green, Refactor cycles.
4
4
  category: abel
5
5
  tags: [abel, implementation, TDD]
6
6
  argument-hint: [change_name]
7
7
  ---
8
8
 
9
9
  <!-- ABEL:START -->
10
- **Arguments**
11
- - Requested: `<change_name>`
10
+ # abel-implement
12
11
 
13
- **Guardrails**
12
+ Implement one ready OpenSpec change with minimal, verified patches.
14
13
 
15
- - Keep changes tightly scoped to the requested outcome; enforce side-effect review before applying any modification.
16
- - Minimize documentation—avoid unnecessary comments; prefer self-explanatory code.
17
- - Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) for additional OpenSpec conventions or clarifications.
14
+ ## Rules
18
15
 
19
- **TDD Guardrails (mandatory)**
20
- - **Red Phase**: Create or execute the task's failing executable verification ONLY; implementation code is FORBIDDEN.
21
- - **Green Phase**: Write MINIMAL code to satisfy the verification; over-engineering is FORBIDDEN.
22
- - **Refactor Phase**: Optimize code quality while keeping target and affected verification green and introducing no new full-suite failures.
23
- - **Mandatory**: Run the task's executable verification after EVERY code change; never skip verification.
24
- - **Test-First**: Each task MUST have a failing executable verification before implementation begins.
16
+ - Follow the repository instructions and the Implement column of its Stage Skill Matrix.
17
+ - Before any write, state assumptions and unknowns explicitly. If a critical unknown could change behavior, scope, architecture, or safety, stop and ask the user or return to `/abel-design --change <change-name>`.
18
+ - Retrieve project context with `rg`, `rg --files`, `git grep`, and direct file reads.
19
+ - Use unified diff patches and stay inside the task's declared scope. Do not invent missing product or architecture decisions.
25
20
 
26
- **Skill Integration**: See `Stage Skill Matrix` (Implement column)
21
+ ## Readiness
27
22
 
28
- **Readiness Preflight (before any code or test write)**
23
+ 1. Resolve `<change-name>` from the explicit argument; ask only if it is missing or ambiguous.
24
+ 2. Run `openspec status --change <change-name> --json`. Read `schemaName`, `changeRoot`, `artifactPaths`, `applyRequires`, and artifact states; every required artifact must be done.
25
+ 3. Inspect `openspec schema which <schemaName> --json`. Resolve its concrete `apply.tracks` file inside `changeRoot`; never guess a task filename.
26
+ 4. Run `openspec validate <change-name> --strict --type change` and `openspec instructions apply --change <change-name> --json`.
27
+ 5. Read the reported artifacts, tracking file, affected source, and nearby tests. Confirm that every task states scope, dependencies, Requirement/Scenario, Red failure, Green behavior, and verification commands.
28
+ 6. Run the affected tests and full suite as a baseline. Record commands, exit status, and existing failure identities; an existing failure is not the target Red.
29
29
 
30
- 1. Explicit argument takes precedence. Resolve it directly; ask only when the argument is missing or cannot be resolved uniquely. Never require unconditional `openspec view` confirmation.
31
- 2. Run `openspec status --change <change-name> --json`; read `schemaName`, `changeRoot`, `artifactPaths`, `applyRequires`, and `artifacts`.
32
- 3. Run `openspec schema which <schemaName> --json`, read the resolved `schema.yaml`, and extract `apply.tracks`. Resolve `apply.tracks` relative to `changeRoot`; it must name one concrete existing regular file inside `changeRoot`. Missing, null, non-concrete, or escaping paths fail closed; do not infer the tracking path from apply-instruction `contextFiles` or `tasks`.
33
- 4. Every artifact id in `applyRequires` must have status `done`; array presence or a general completion flag is insufficient.
34
- 5. Run `openspec validate <change-name> --strict --type change`; require zero issues.
35
- 6. Read every planning artifact reported by `artifactPaths`, then run `openspec instructions apply --change <change-name> --json` and follow the returned apply contract.
36
- 7. Verify stable Requirement and Scenario references, the Requirement → Scenario → Verification → Task chain, and every task verification contract. Rebuild the Gate A and Gate B summaries; if approval cannot be proven in the current conversation, show the summaries and require explicit user confirmation.
37
- 8. Run and record affected baseline tests and the full test suite before any write. Record each command, exit status, and normalized failure identities/reasons. Keep existing failures from the full suite separate from the target Red; an existing failure never counts as Red.
38
- 9. If any preflight item fails, STOP and return `/abel-design --change <change-name>`. Do not invent or repair product, behavior, or architecture decisions here.
30
+ If readiness is incomplete or inconsistent, stop without product writes and return the issue to design.
39
31
 
40
- **Tool Routing**:
41
- - **TDD Cycle**: Autonomous refactoring
42
- - **Final Review** (after all tasks):
43
- - Backend refactor (subagents)
44
- - Frontend refactor (subagents)
45
- - **E2E tasks** → `/dev-browser`
32
+ ## TDD Cycle
46
33
 
47
- After preflight, detect the project's verification tooling and work through the tracked tasks sequentially.
34
+ For each incomplete task, in dependency order:
48
35
 
49
- **TDD Cycle (per task)**
36
+ 1. Red: add or update the smallest executable test or static check, then run it. It must fail for the target's expected reason before implementation changes. If it passes or fails for another reason, stop and resolve the mismatch.
37
+ 2. Green: apply the minimal implementation patch and rerun the Red command until it passes.
38
+ 3. Refactor: simplify only where useful, keeping the target and affected tests green after each code change.
39
+ 4. Run the task's affected verification. Review the diff for unintended behavior, unrelated edits, security issues, and leaked runtime state.
40
+ 5. Leave the task incomplete in `apply.tracks` until final verification succeeds.
50
41
 
51
- Consume these ordinary indented bullets from the task verification contract, never Markdown checkboxes:
52
- - verification type: property | example | E2E | static
53
- - Red command and expected failure reason
54
- - Green expected behavior
55
- - affected-suite command
56
- - target scope/files
42
+ ## Final Verification
57
43
 
58
- - Red must fail because of the target defect described by the contract.
59
- - If the failure reason differs or the command is invalid, STOP and return `/abel-design --change <change-name>`; do not improvise a replacement contract.
60
- - A non-behavior-change task starts with its specified failing executable static verification.
61
- - A manual-only task is not implementation-ready; STOP and return `/abel-design --change <change-name>`.
62
-
63
- ```
64
- ┌─────────────────────────────────────────────────────────┐
65
- │ Step 1: 🔴 Red Phase - Execute Contract Verification │
66
- │ ├─ Use the contract's verification type and scope │
67
- │ ├─ Generate the required failing verification via │
68
- │ │ PROMPT: "Generate failing verification for: {task}│
69
- │ │ Context: {code_context} │
70
- │ │ Output: unified diff patch │
71
- │ │ FORBIDDEN: implementation code" │
72
- │ ├─ Apply verification code (after review) │
73
- │ ├─ Run Red command → MUST FAIL for expected reason │
74
- │ └─ If it passes or fails differently → STOP │
75
- ├─────────────────────────────────────────────────────────┤
76
- │ Step 2: 🟢 Green Phase - Minimal Implementation │
77
- │ ├─ Generate minimal implementation │
78
- │ │ PROMPT: "Generate minimal code for verification: │
79
- │ │ Verification: {verification_context} │
80
- │ │ Context: {code_context} │
81
- │ │ Output: unified diff patch │
82
- │ │ FORBIDDEN: over-engineering" │
83
- │ ├─ Apply implementation (after review & rewrite) │
84
- │ ├─ Run Red command → MUST PASS │
85
- │ └─ If fails → analyze error, fix, retry │
86
- ├─────────────────────────────────────────────────────────┤
87
- │ Step 3: 🔵 Refactor Phase │
88
- │ ├─ Analyzes code quality │
89
- │ ├─ Apply standard refactoring techniques: │
90
- │ │ ├─ Eliminate code duplication │
91
- │ │ ├─ Improve naming and structure │
92
- │ │ ├─ Enhance readability │
93
- │ │ └─ Simplify logic where possible │
94
- │ ├─ Apply refactoring changes │
95
- │ ├─ Run affected-suite command → MUST STILL PASS │
96
- │ └─ If fails → rollback refactoring │
97
- └─────────────────────────────────────────────────────────┘
98
- ```
99
-
100
- Before applying any change, perform mandatory side-effect review.
101
-
102
- After a task's TDD cycle completes, locate the task ID's checkbox in the concrete tracking file resolved from schema `apply.tracks`; require exactly one match and update only it. Zero or multiple matches must STOP and return `/abel-design --change <change-name>`. Never hardcode an artifact filename or infer the tracking path from apply instructions.
103
-
104
- **Final Review & Refactor** (after all tasks complete)
105
-
106
- 1. Require all target tests to be green, then run the affected suites.
107
- 2. Re-run the same full-suite command (the full test suite), compare normalized failure identities with the recorded full-suite baseline, and require no new failures.
108
-
109
- 3. Execute global code review via subagents:
110
-
111
- 4. Wait for background tasks to complete; review diff patches.
112
- 5. Rewrite patches into production-grade code (per rewriting principle).
113
- 6. Apply refactoring changes.
114
- 7. Re-run target, affected, and full-suite verification against the baseline.
115
- 8. If a target test fails or the full suite has a new failure, analyze the root cause and fix or roll back.
116
- 9. Perform final side-effect review.
117
- 10. Report that the change is ready for archive; do not archive until the user explicitly authorizes `/opsx:archive`.
118
-
119
- **TDD Output Format**
120
-
121
- ```
122
- ## /abel-implement (TDD Mode)
123
-
124
- ### Task 1/N: {task_description}
125
-
126
- 🔴 Red Phase
127
- ├─ Type: {verification_type}
128
- ├─ Generated: {verification_files_or_none}
129
- ├─ Run: {red_command}
130
- └─ Result: failed for {expected_failure_reason} ✓
131
-
132
- 🟢 Green Phase
133
- ├─ Generated: {implementation_files}
134
- ├─ Run: {red_command}
135
- └─ Result: {green_expected_behavior} ✓
136
-
137
- 🔵 Refactor Phase (Agent autonomous)
138
- ├─ Optimized: {description}
139
- ├─ Run: {affected_suite_command}
140
- └─ Result: target verification green ✓
141
-
142
- ✓ Task complete → Next task
143
-
144
- ---
145
-
146
- ### All Tasks Complete
147
-
148
- 🔍 Final Review & Refactor
149
-
150
- Backend Review
151
- ├─ Files: {backend_files}
152
- ├─ Status: Running in background...
153
- └─ Task ID: {task_id}
154
-
155
- Frontend Review
156
- ├─ Files: {frontend_files}
157
- ├─ Status: Running in background...
158
- └─ Task ID: {task_id}
159
-
160
- [Waiting for completion...]
161
-
162
- ✓ Reviews complete
163
- ├─ Applied: {refactoring_summary}
164
- ├─ Target tests: green ✓
165
- ├─ Full suite: no failures beyond baseline ✓
166
- └─ Ready for user-authorized archive
167
-
168
- ✓ Implementation complete
169
- ```
44
+ - Run all target tests, affected suites, and the same full-suite baseline command. Require no new failure identities.
45
+ - Rerun `openspec validate <change-name> --strict --type change` and inspect `openspec status --change <change-name> --json`.
46
+ - Only after those checks pass, mark each implemented task complete in the file resolved from `apply.tracks`; require exactly one matching task ID. Rerun validation and status after this completion-only patch.
47
+ - If any verification fails, ensure those tasks remain incomplete or restore them to incomplete so a resumed run selects them again.
48
+ - Review the complete unified diff and report changed files, Red/Green evidence, final verification, baseline exceptions, and residual risks.
49
+ - Do not archive automatically; `/opsx:archive` remains a separate user-authorized action.
170
50
  <!-- ABEL:END -->
@@ -14,7 +14,7 @@ tags: [openspec, init, setup]
14
14
  **Steps**
15
15
  1. Detect OS and adapt command style (Unix/PowerShell).
16
16
  2. Verify `openspec` availability via `openspec --version`.
17
- 3. If missing, `npm install -g @fission-ai/openspec@latest`, then re-check version.
17
+ 3. If missing, stop and report `npm install -g @fission-ai/openspec` as manual remediation; do not run it automatically.
18
18
  4. Run `openspec init` (or `openspec update` for existing projects).
19
19
  5. Verify required skills:
20
20
  - Skills: `/dev-browser` `/context7-auto-research` `/grok-search`
@@ -1,47 +1,20 @@
1
1
  import { spawn, spawnSync } from "node:child_process";
2
- import { join } from "node:path";
3
2
  import * as p from "@clack/prompts";
4
3
  import c from "picocolors";
5
- import { defaultPaths, getPlatform, isWindows } from "../paths.mjs";
6
-
7
- const { homeDir: home } = defaultPaths;
4
+ import { getPlatform, isWindows } from "../paths.mjs";
8
5
 
9
6
  function getRunCommandSpawnOptions(platform = getPlatform()) {
10
7
  return { stdio: "inherit", shell: platform === "win32" };
11
8
  }
12
9
 
13
- function getCommandPath(command) {
14
- const checker = isWindows() ? "where" : "which";
15
- const result = spawnSync(checker, [command], { encoding: "utf8" });
16
- if (result.status !== 0) {
17
- return undefined;
18
- }
19
- return result.stdout
20
- .split(/\r?\n/u)
21
- .map((line) => line.trim())
22
- .find(Boolean);
23
- }
24
-
25
10
  function commandExists(command) {
26
- return Boolean(getCommandPath(command));
27
- }
28
-
29
- function readCommandOutput(command, args) {
30
- const result = spawnSync(command, args, {
31
- encoding: "utf8",
32
- shell: isWindows(),
33
- stdio: ["ignore", "pipe", "ignore"]
34
- });
35
- if (result.status !== 0) {
36
- return undefined;
37
- }
38
- const outputValue = result.stdout.trim();
39
- return outputValue || undefined;
11
+ const checker = isWindows() ? "where" : "which";
12
+ return spawnSync(checker, [command], { stdio: "ignore" }).status === 0;
40
13
  }
41
14
 
42
15
  async function runCommand(command, args) {
43
16
  await new Promise((resolvePromise, rejectPromise) => {
44
- const child = spawn(command, args, getRunCommandSpawnOptions(getPlatform()));
17
+ const child = spawn(command, args, getRunCommandSpawnOptions());
45
18
  child.on("error", rejectPromise);
46
19
  child.on("close", (code) => {
47
20
  if (code === 0) {
@@ -53,109 +26,20 @@ async function runCommand(command, args) {
53
26
  });
54
27
  }
55
28
 
56
- function normalizeInstallPath(value) {
57
- if (!value) {
58
- return "";
59
- }
60
- return value.replace(/\\/gu, "/").replace(/\/+$/u, "").toLowerCase();
61
- }
62
-
63
- function installPathIsWithin(path, parentPath) {
64
- const normalizedPath = normalizeInstallPath(path);
65
- const normalizedParent = normalizeInstallPath(parentPath);
66
- return Boolean(normalizedPath && normalizedParent)
67
- && (normalizedPath === normalizedParent || normalizedPath.startsWith(`${normalizedParent}/`));
68
- }
69
-
70
- function getNpmGlobalBinDirs(npmGlobalPrefix, platform = getPlatform()) {
71
- if (!npmGlobalPrefix) {
72
- return [];
73
- }
74
- return platform === "win32" ? [npmGlobalPrefix] : [join(npmGlobalPrefix, "bin")];
75
- }
76
-
77
- function inferPackageManagerFromCommandPath(commandPath, {
78
- bunGlobalBinDir,
79
- npmGlobalPrefix,
80
- platform = getPlatform()
81
- } = {}) {
82
- if (installPathIsWithin(commandPath, bunGlobalBinDir)) {
83
- return "bun";
84
- }
85
- if (getNpmGlobalBinDirs(npmGlobalPrefix, platform)
86
- .some((binDir) => installPathIsWithin(commandPath, binDir))) {
87
- return "npm";
88
- }
89
- return null;
90
- }
91
-
92
- function chooseCliInstallPackageManager({
93
- commandPath,
94
- availablePackageManagers = [],
95
- bunGlobalBinDir,
96
- npmGlobalPrefix,
97
- platform = getPlatform(),
98
- nodeAvailable = true,
99
- supportedPackageManagers = ["bun", "npm"]
100
- } = {}) {
101
- const supported = new Set(supportedPackageManagers);
102
- const available = new Set(availablePackageManagers.filter((packageManager) => {
103
- if (!supported.has(packageManager)) {
104
- return false;
105
- }
106
- return packageManager !== "bun" || nodeAvailable;
107
- }));
108
- const bunBlockedByMissingNode = !nodeAvailable
109
- && supported.has("bun")
110
- && availablePackageManagers.includes("bun");
111
- const existingPackageManager = inferPackageManagerFromCommandPath(commandPath, {
112
- bunGlobalBinDir,
113
- npmGlobalPrefix,
114
- platform
115
- });
116
-
117
- if (existingPackageManager && available.has(existingPackageManager)) {
118
- return { packageManager: existingPackageManager, source: "existing" };
119
- }
120
- for (const packageManager of ["bun", "npm"]) {
121
- if (available.has(packageManager)) {
122
- return { packageManager, source: "available" };
123
- }
124
- }
29
+ function buildCliToolInstallCommand({ packageName, skipScripts = false }) {
125
30
  return {
126
- packageManager: null,
127
- source: bunBlockedByMissingNode ? "missing-node" : (availablePackageManagers.length > 0 ? "unsupported" : "missing")
31
+ command: "npm",
32
+ args: [
33
+ "install",
34
+ "-g",
35
+ ...(skipScripts ? ["--ignore-scripts"] : []),
36
+ packageName,
37
+ "--force"
38
+ ]
128
39
  };
129
40
  }
130
41
 
131
- function buildCliToolInstallCommand(packageManager, { packageName, skipScripts = false }) {
132
- if (packageManager === "npm") {
133
- return {
134
- command: "npm",
135
- args: [
136
- "install",
137
- "-g",
138
- ...(skipScripts ? ["--ignore-scripts"] : []),
139
- packageName,
140
- "--force"
141
- ]
142
- };
143
- }
144
- if (packageManager === "bun") {
145
- return {
146
- command: "bun",
147
- args: [
148
- "install",
149
- "-g",
150
- ...(skipScripts ? ["--ignore-scripts"] : []),
151
- packageName
152
- ]
153
- };
154
- }
155
- throw new Error(`Unsupported package manager: ${packageManager}`);
156
- }
157
-
158
- function getPackageManagerInstallHelp(platform = getPlatform()) {
42
+ function getNpmInstallHelp(platform = getPlatform()) {
159
43
  const platformLabel = {
160
44
  darwin: "macOS",
161
45
  win32: "Windows",
@@ -168,32 +52,12 @@ function getPackageManagerInstallHelp(platform = getPlatform()) {
168
52
  };
169
53
  }
170
54
 
171
- function getAvailablePackageManagers() {
172
- return ["bun", "npm"].filter((packageManager) => commandExists(packageManager));
173
- }
174
-
175
- function getBunGlobalBinDir(availablePackageManagers = getAvailablePackageManagers()) {
176
- if (!availablePackageManagers.includes("bun")) {
177
- return undefined;
178
- }
179
- return readCommandOutput("bun", ["pm", "bin", "-g"]) || join(home, ".bun", "bin");
180
- }
181
-
182
- function getNpmGlobalPrefix(availablePackageManagers = getAvailablePackageManagers()) {
183
- if (!availablePackageManagers.includes("npm")) {
184
- return undefined;
185
- }
186
- return readCommandOutput("npm", ["prefix", "-g"]);
187
- }
188
-
189
55
  async function installCliTool(tool, { confirmOrCancel }) {
190
56
  const toolConfig = {
191
57
  claude: {
192
58
  label: "Claude Code",
193
59
  command: "claude",
194
- packageName: "@anthropic-ai/claude-code",
195
- supportedPackageManagers: ["npm"],
196
- installRequirement: "Claude Code 安装需要执行 postinstall;Bun 默认会阻止该脚本,因此需使用 npm。"
60
+ packageName: "@anthropic-ai/claude-code"
197
61
  },
198
62
  codex: {
199
63
  label: "Codex",
@@ -208,44 +72,21 @@ async function installCliTool(tool, { confirmOrCancel }) {
208
72
  }
209
73
  }[tool];
210
74
 
211
- if (!toolConfig) {
212
- throw new Error(`Unsupported tool: ${tool}`);
213
- }
214
-
215
- const commandPath = getCommandPath(toolConfig.command);
216
- const installed = Boolean(commandPath);
217
- const availablePackageManagers = getAvailablePackageManagers();
218
- const packageManagerChoice = chooseCliInstallPackageManager({
219
- commandPath,
220
- availablePackageManagers,
221
- bunGlobalBinDir: getBunGlobalBinDir(availablePackageManagers),
222
- npmGlobalPrefix: getNpmGlobalPrefix(availablePackageManagers),
223
- platform: getPlatform(),
224
- nodeAvailable: commandExists("node"),
225
- supportedPackageManagers: toolConfig.supportedPackageManagers
226
- });
75
+ if (!toolConfig) throw new Error(`Unsupported tool: ${tool}`);
227
76
 
228
- if (!packageManagerChoice.packageManager) {
229
- const help = getPackageManagerInstallHelp();
230
- p.log.warn(`未检测到可用于安装 ${toolConfig.label} 的包管理器。`);
231
- if (packageManagerChoice.source === "missing-node") {
232
- p.log.message(`${toolConfig.label} 是 Node CLI;使用 Bun 安装前也需要先安装 Node.js。`);
233
- }
234
- if (toolConfig.installRequirement) {
235
- p.log.message(toolConfig.installRequirement);
236
- }
77
+ if (!commandExists("npm")) {
78
+ const help = getNpmInstallHelp();
79
+ p.log.warn(`未检测到 npm,无法安装 ${toolConfig.label}。`);
237
80
  p.log.message(`${help.platformLabel} 可先安装 Node.js/npm,再重新运行安装。`);
238
81
  p.log.message(`中国大陆镜像: ${help.mainlandUrl}`);
239
82
  p.log.message(`官方下载页: ${help.officialUrl}`);
240
83
  return;
241
84
  }
242
85
 
86
+ const installed = commandExists(toolConfig.command);
243
87
  if (installed) {
244
- const managerMessage = packageManagerChoice.source === "existing"
245
- ? `检测到原安装方式为 ${packageManagerChoice.packageManager}`
246
- : `未识别原安装方式,将使用 ${packageManagerChoice.packageManager}`;
247
88
  const shouldUpdate = await confirmOrCancel({
248
- message: `${toolConfig.label} 已检测到(${managerMessage}),是否继续安装/更新?`,
89
+ message: `${toolConfig.label} 已检测到,是否继续安装/更新?`,
249
90
  initialValue: false
250
91
  });
251
92
  if (!shouldUpdate) {
@@ -254,24 +95,22 @@ async function installCliTool(tool, { confirmOrCancel }) {
254
95
  }
255
96
  }
256
97
 
257
- const installCommand = buildCliToolInstallCommand(packageManagerChoice.packageManager, toolConfig);
258
- const s = p.spinner();
259
- s.start(`正在使用 ${packageManagerChoice.packageManager} 安装 ${toolConfig.label}...`);
98
+ const installCommand = buildCliToolInstallCommand(toolConfig);
99
+ const spinner = p.spinner();
100
+ spinner.start(`正在使用 npm 安装 ${toolConfig.label}...`);
260
101
  try {
261
102
  await runCommand(installCommand.command, installCommand.args);
262
- s.stop(`${toolConfig.label} 安装完成`);
263
- } catch (e) {
264
- s.cancel(c.red(`${toolConfig.label} 安装失败: ${e.message}`));
265
- throw e;
103
+ spinner.stop(`${toolConfig.label} 安装完成`);
104
+ } catch (error) {
105
+ spinner.cancel(c.red(`${toolConfig.label} 安装失败: ${error.message}`));
106
+ throw error;
266
107
  }
267
108
  }
268
109
 
269
110
  export {
270
111
  buildCliToolInstallCommand,
271
- chooseCliInstallPackageManager,
272
- getPackageManagerInstallHelp,
112
+ getNpmInstallHelp,
273
113
  getRunCommandSpawnOptions,
274
- inferPackageManagerFromCommandPath,
275
114
  installCliTool,
276
115
  commandExists
277
116
  };
package/package.json CHANGED
@@ -1,29 +1,29 @@
1
1
  {
2
2
  "name": "abelworkflow",
3
- "version": "1.1.2",
4
- "description": "Install AbelWorkflow into ~/.agents and create Claude/Codex symlinks.",
3
+ "version": "1.2.0",
4
+ "description": "Install AbelWorkflow into ~/.agents and create Claude, Codex, and Pi links.",
5
5
  "type": "module",
6
6
  "scripts": {
7
- "test": "npm run test:node",
7
+ "test": "npm run build && npm run test:node",
8
8
  "test:node": "node --test test/*.test.mjs",
9
- "test:python": "python -m unittest discover -s test",
9
+ "test:python": "python -B -m unittest discover -s test",
10
10
  "test:dev-browser": "npm test --prefix skills/dev-browser",
11
11
  "test:contracts": "node --test test/runtime-doc-contracts.test.mjs test/cli-args.test.mjs",
12
12
  "typecheck": "npm run typecheck --prefix skills/dev-browser",
13
13
  "build": "npm run build --prefix skills/dev-browser",
14
14
  "prepack": "npm run build",
15
15
  "check:docs": "node --test test/docs-contracts.test.mjs test/runtime-doc-contracts.test.mjs",
16
- "check:package": "node --test test/package-contents.test.mjs",
17
- "check": "npm run build && npm run test:node && npm run test:python && npm run test:dev-browser && npm run typecheck && npm run check:docs && npm run check:package"
16
+ "check:package": "npm run build && node --test test/package-contents.test.mjs",
17
+ "check": "npm run build && npm run test:node && npm run test:python && npm run test:dev-browser && npm run typecheck"
18
18
  },
19
19
  "bin": {
20
20
  "abelworkflow": "bin/abelworkflow.mjs"
21
21
  },
22
22
  "files": [
23
23
  "bin",
24
+ "extensions",
24
25
  "lib",
25
26
  "README.md",
26
- "extensions",
27
27
  "skills/context7-auto-research/.env.example",
28
28
  "skills/context7-auto-research/SKILL.md",
29
29
  "skills/context7-auto-research/context7-api.cjs",
@@ -36,11 +36,9 @@
36
36
  "skills/grok-search/.env.example",
37
37
  "skills/grok-search/SKILL.md",
38
38
  "skills/grok-search/defaults.json",
39
- "skills/grok-search/gitignore.template",
40
39
  "skills/grok-search/requirements.txt",
41
40
  "skills/grok-search/scripts/_dotenv.py",
42
41
  "skills/grok-search/scripts/groksearch_cli.py",
43
- "skills/grok-search/scripts/groksearch_entry.py",
44
42
  "skills/prompt-enhancer/SKILL.md",
45
43
  "skills/prompt-enhancer/TEMPLATE.md",
46
44
  "skills/time/SKILL.md",