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.
- package/LICENSE +21 -0
- package/README.md +30 -29
- package/extensions/gpt-responses-compat.ts +213 -0
- package/extensions/k2think-compat.ts +40 -0
- package/lib/cli/args.mjs +5 -19
- package/lib/cli/main.mjs +105 -72
- package/lib/cli/prompts.mjs +9 -2
- package/lib/config/dotenv.mjs +1 -1
- package/lib/config/jsonc.mjs +1 -1
- package/lib/config/store.mjs +197 -45
- package/lib/config/toml.mjs +147 -453
- package/lib/installer/assets.mjs +67 -45
- package/lib/installer/install.mjs +202 -45
- package/lib/installer/links.mjs +124 -101
- package/lib/installer/lock.mjs +82 -0
- package/lib/installer/state.mjs +14 -21
- package/lib/paths.mjs +9 -9
- package/lib/providers/claude.mjs +58 -114
- package/lib/providers/codex.mjs +101 -198
- package/lib/providers/pi.mjs +332 -439
- package/lib/providers/skills.mjs +88 -68
- package/lib/providers/url.mjs +35 -0
- package/lib/templates/workflow/commands/abel-design.md +23 -161
- package/lib/templates/workflow/commands/abel-diagnose.md +30 -56
- package/lib/templates/workflow/commands/abel-implement.md +30 -150
- package/lib/templates/workflow/commands/abel-init.md +1 -1
- package/lib/tools/cli-installer.mjs +29 -190
- package/package.json +7 -9
- package/skills/context7-auto-research/context7-api.cjs +75 -17
- package/skills/dev-browser/SKILL.md +15 -1
- package/skills/dev-browser/dist/scripts/start.d.ts +0 -1
- package/skills/dev-browser/dist/scripts/start.js +8 -36
- package/skills/dev-browser/dist/src/client.d.ts +3 -4
- package/skills/dev-browser/dist/src/client.js +117 -117
- package/skills/dev-browser/dist/src/entrypoint.d.ts +1 -1
- package/skills/dev-browser/dist/src/entrypoint.js +38 -31
- package/skills/dev-browser/dist/src/index.d.ts +0 -1
- package/skills/dev-browser/dist/src/index.js +0 -1
- package/skills/dev-browser/dist/src/page-api.d.ts +0 -1
- package/skills/dev-browser/dist/src/page-api.js +0 -1
- package/skills/dev-browser/dist/src/relay.d.ts +0 -1
- package/skills/dev-browser/dist/src/relay.js +53 -7
- package/skills/dev-browser/dist/src/runtime.d.ts +3 -14
- package/skills/dev-browser/dist/src/runtime.js +5 -15
- package/skills/dev-browser/dist/src/snapshot/browser-script.d.ts +0 -1
- package/skills/dev-browser/dist/src/snapshot/browser-script.js +0 -1
- package/skills/dev-browser/dist/src/snapshot/index.d.ts +0 -1
- package/skills/dev-browser/dist/src/snapshot/index.js +0 -1
- package/skills/dev-browser/dist/src/snapshot/inject.d.ts +0 -1
- package/skills/dev-browser/dist/src/snapshot/inject.js +0 -1
- package/skills/dev-browser/dist/src/standalone.d.ts +0 -1
- package/skills/dev-browser/dist/src/standalone.js +2 -3
- package/skills/dev-browser/dist/src/startup.d.ts +3 -12
- package/skills/dev-browser/dist/src/startup.js +6 -10
- package/skills/dev-browser/dist/src/target-registry.d.ts +0 -1
- package/skills/dev-browser/dist/src/target-registry.js +0 -1
- package/skills/dev-browser/dist/src/types.d.ts +0 -1
- package/skills/dev-browser/dist/src/types.js +0 -1
- package/skills/dev-browser/package-lock.json +1 -1
- package/skills/dev-browser/package.json +2 -2
- package/skills/grok-search/.env.example +1 -1
- package/skills/grok-search/SKILL.md +13 -16
- package/skills/grok-search/scripts/_dotenv.py +1 -1
- package/skills/grok-search/scripts/groksearch_cli.py +271 -195
- package/skills/time/SKILL.md +5 -2
- package/skills/time/scripts/time_cli.py +61 -22
- package/extensions/pi-gpt-responses-compat/index.ts +0 -25
- package/lib/templates/codex/config-base.toml +0 -103
- package/skills/dev-browser/dist/scripts/start.d.ts.map +0 -1
- package/skills/dev-browser/dist/scripts/start.js.map +0 -1
- package/skills/dev-browser/dist/src/client.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/client.js.map +0 -1
- package/skills/dev-browser/dist/src/entrypoint.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/entrypoint.js.map +0 -1
- package/skills/dev-browser/dist/src/index.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/index.js.map +0 -1
- package/skills/dev-browser/dist/src/page-api.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/page-api.js.map +0 -1
- package/skills/dev-browser/dist/src/relay.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/relay.js.map +0 -1
- package/skills/dev-browser/dist/src/runtime.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/runtime.js.map +0 -1
- package/skills/dev-browser/dist/src/snapshot/browser-script.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/snapshot/browser-script.js.map +0 -1
- package/skills/dev-browser/dist/src/snapshot/index.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/snapshot/index.js.map +0 -1
- package/skills/dev-browser/dist/src/snapshot/inject.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/snapshot/inject.js.map +0 -1
- package/skills/dev-browser/dist/src/standalone.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/standalone.js.map +0 -1
- package/skills/dev-browser/dist/src/startup.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/startup.js.map +0 -1
- package/skills/dev-browser/dist/src/target-registry.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/target-registry.js.map +0 -1
- package/skills/dev-browser/dist/src/types.d.ts.map +0 -1
- package/skills/dev-browser/dist/src/types.js.map +0 -1
- package/skills/grok-search/gitignore.template +0 -4
- package/skills/grok-search/scripts/groksearch_entry.py +0 -131
|
@@ -1,170 +1,50 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: abel-implement
|
|
3
|
-
description: Implement
|
|
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
|
-
|
|
11
|
-
- Requested: `<change_name>`
|
|
10
|
+
# abel-implement
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
Implement one ready OpenSpec change with minimal, verified patches.
|
|
14
13
|
|
|
15
|
-
|
|
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
|
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
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
|
-
|
|
21
|
+
## Readiness
|
|
27
22
|
|
|
28
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
34
|
+
For each incomplete task, in dependency order:
|
|
48
35
|
|
|
49
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
-
|
|
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
|
|
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 {
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
127
|
-
|
|
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
|
|
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 (!
|
|
229
|
-
const help =
|
|
230
|
-
p.log.warn(
|
|
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}
|
|
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(
|
|
258
|
-
const
|
|
259
|
-
|
|
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
|
-
|
|
263
|
-
} catch (
|
|
264
|
-
|
|
265
|
-
throw
|
|
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
|
-
|
|
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.
|
|
4
|
-
"description": "Install AbelWorkflow into ~/.agents and create Claude
|
|
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
|
|
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",
|