abelworkflow 0.6.2 → 0.6.3

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/README.md CHANGED
@@ -142,18 +142,36 @@ git -C "$HOME\.agents" pull
142
142
 
143
143
  #### 2. 执行本地初始化
144
144
 
145
+ **Linux/macOS(bash/zsh)**
146
+
145
147
  ```bash
146
148
  cd ~/.agents
147
149
  node bin/abelworkflow.mjs
148
150
  ```
149
151
 
152
+ **Windows(PowerShell)**
153
+
154
+ ```powershell
155
+ Set-Location "$HOME\.agents"
156
+ node .\bin\abelworkflow.mjs
157
+ ```
158
+
150
159
  如果只想重建链接,不进入菜单:
151
160
 
161
+ **Linux/macOS(bash/zsh)**
162
+
152
163
  ```bash
153
164
  cd ~/.agents
154
165
  node bin/abelworkflow.mjs install
155
166
  ```
156
167
 
168
+ **Windows(PowerShell)**
169
+
170
+ ```powershell
171
+ Set-Location "$HOME\.agents"
172
+ node .\bin\abelworkflow.mjs install
173
+ ```
174
+
157
175
  ### 映射关系(本仓库 → 配置目录)
158
176
 
159
177
  | 本仓库 | Claude Code | Codex | 说明 |
@@ -164,9 +182,21 @@ node bin/abelworkflow.mjs install
164
182
 
165
183
  ### 验证(可选)
166
184
 
185
+ **Linux/macOS(bash/zsh)**
186
+
167
187
  ```bash
168
188
  ls -la "$HOME/.claude/CLAUDE.md" "$HOME/.claude/commands/oc"
169
189
  ls -la "$HOME/.codex/AGENTS.md" "$HOME/.codex/prompts/"{init,research,plan,implementation,diagnose}.md
170
190
  cat "$HOME/.claude/settings.json" | head
171
191
  cat "$HOME/.codex/config.toml" | head
172
192
  ```
193
+
194
+ **Windows(PowerShell)**
195
+
196
+ ```powershell
197
+ Get-Item "$HOME\.claude\CLAUDE.md", "$HOME\.claude\commands\oc"
198
+ Get-Item "$HOME\.codex\AGENTS.md"
199
+ Get-ChildItem "$HOME\.codex\prompts" | Where-Object { $_.Name -in @("init.md", "research.md", "plan.md", "implementation.md", "diagnose.md") }
200
+ Get-Content "$HOME\.claude\settings.json" -TotalCount 10
201
+ Get-Content "$HOME\.codex\config.toml" -TotalCount 10
202
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abelworkflow",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "Install AbelWorkflow into ~/.agents and create Claude/Codex symlinks.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -43,6 +43,7 @@ async function main() {
43
43
  ensurePlaywrightChromium({
44
44
  isInstalled: () =>
45
45
  isPlaywrightChromiumInstalled({
46
+ skillDir: runtimePaths.skillDir,
46
47
  platform: process.platform,
47
48
  env: process.env,
48
49
  exists: existsSync,
@@ -0,0 +1,60 @@
1
+ import { join } from "node:path";
2
+
3
+ import { describe, expect, it } from "vitest";
4
+
5
+ import { getPlaywrightBrowserRoots, isPlaywrightChromiumInstalled } from "./runtime.js";
6
+
7
+ describe("getPlaywrightBrowserRoots", () => {
8
+ const skillDir = "/tmp/dev-browser";
9
+
10
+ it("uses the hermetic local browser directory when PLAYWRIGHT_BROWSERS_PATH is 0", () => {
11
+ expect(
12
+ getPlaywrightBrowserRoots({
13
+ skillDir,
14
+ platform: "win32",
15
+ env: { PLAYWRIGHT_BROWSERS_PATH: "0" },
16
+ })
17
+ ).toEqual([join(skillDir, "node_modules", "playwright-core", ".local-browsers")]);
18
+ });
19
+
20
+ it("prefers LOCALAPPDATA on Windows before USERPROFILE fallback", () => {
21
+ expect(
22
+ getPlaywrightBrowserRoots({
23
+ skillDir,
24
+ platform: "win32",
25
+ env: {
26
+ LOCALAPPDATA: "C:\\Users\\Alice\\AppData\\Local",
27
+ USERPROFILE: "C:\\Users\\Alice",
28
+ },
29
+ })
30
+ ).toEqual([join("C:\\Users\\Alice\\AppData\\Local", "ms-playwright")]);
31
+ });
32
+
33
+ it("falls back to USERPROFILE when LOCALAPPDATA is unavailable", () => {
34
+ expect(
35
+ getPlaywrightBrowserRoots({
36
+ skillDir,
37
+ platform: "win32",
38
+ env: { USERPROFILE: "C:\\Users\\Alice" },
39
+ })
40
+ ).toEqual([join("C:\\Users\\Alice", "AppData", "Local", "ms-playwright")]);
41
+ });
42
+ });
43
+
44
+ describe("isPlaywrightChromiumInstalled", () => {
45
+ const skillDir = "/tmp/dev-browser";
46
+
47
+ it("detects chromium in the hermetic local browser directory", () => {
48
+ const hermeticRoot = join(skillDir, "node_modules", "playwright-core", ".local-browsers");
49
+
50
+ expect(
51
+ isPlaywrightChromiumInstalled({
52
+ skillDir,
53
+ platform: "win32",
54
+ env: { PLAYWRIGHT_BROWSERS_PATH: "0" },
55
+ exists: (path) => path === hermeticRoot,
56
+ readDir: () => ["chromium-1162"],
57
+ })
58
+ ).toBe(true);
59
+ });
60
+ });
@@ -12,6 +12,7 @@ export interface PackageManager {
12
12
  }
13
13
 
14
14
  export interface ChromiumInstallCheckOptions {
15
+ skillDir: string;
15
16
  platform: NodeJS.Platform;
16
17
  env: NodeJS.ProcessEnv;
17
18
  exists: (path: string) => boolean;
@@ -66,21 +67,43 @@ export function getPlaywrightInstallCommand(manager: PackageManager): string {
66
67
  return [manager.command, ...manager.args].join(" ");
67
68
  }
68
69
 
70
+ function normalizePathForComparison(path: string): string {
71
+ return path.replaceAll("\\", "/").replace(/\/+/g, "/").toLowerCase();
72
+ }
73
+
69
74
  export function getPlaywrightBrowserRoots({
75
+ skillDir,
70
76
  platform,
71
77
  env,
72
78
  }: {
79
+ skillDir: string;
73
80
  platform: NodeJS.Platform;
74
81
  env: NodeJS.ProcessEnv;
75
82
  }): string[] {
76
83
  const explicitPath = env.PLAYWRIGHT_BROWSERS_PATH?.trim();
77
84
  if (explicitPath) {
85
+ if (explicitPath === "0") {
86
+ return [join(skillDir, "node_modules", "playwright-core", ".local-browsers")];
87
+ }
78
88
  return [explicitPath];
79
89
  }
80
90
 
81
91
  if (platform === "win32") {
82
- const userProfile = env.USERPROFILE ?? env.HOME;
83
- return userProfile ? [join(userProfile, "AppData", "Local", "ms-playwright")] : [];
92
+ const roots: string[] = [];
93
+ const localAppData = env.LOCALAPPDATA?.trim();
94
+ if (localAppData) {
95
+ roots.push(join(localAppData, "ms-playwright"));
96
+ }
97
+
98
+ const userProfile = env.USERPROFILE?.trim() ?? env.HOME?.trim();
99
+ if (userProfile) {
100
+ const fallbackRoot = join(userProfile, "AppData", "Local", "ms-playwright");
101
+ if (!roots.some((root) => normalizePathForComparison(root) === normalizePathForComparison(fallbackRoot))) {
102
+ roots.push(fallbackRoot);
103
+ }
104
+ }
105
+
106
+ return roots;
84
107
  }
85
108
 
86
109
  const home = env.HOME ?? env.USERPROFILE;
@@ -88,12 +111,13 @@ export function getPlaywrightBrowserRoots({
88
111
  }
89
112
 
90
113
  export function isPlaywrightChromiumInstalled({
114
+ skillDir,
91
115
  platform,
92
116
  env,
93
117
  exists,
94
118
  readDir,
95
119
  }: ChromiumInstallCheckOptions): boolean {
96
- for (const root of getPlaywrightBrowserRoots({ platform, env })) {
120
+ for (const root of getPlaywrightBrowserRoots({ skillDir, platform, env })) {
97
121
  if (!exists(root)) {
98
122
  continue;
99
123
  }
@@ -35,9 +35,55 @@ python "<SKILL_DIR>/scripts/groksearch_entry.py" get_config_info [--no-test]
35
35
  python "<SKILL_DIR>/scripts/groksearch_entry.py" toggle_builtin_tools --action on|off|status [--root /path/to/project]
36
36
  ```
37
37
 
38
- ## Failure Recovery
38
+ ## Tool Routing Policy
39
+ ### Forced Replacement Rules
39
40
 
40
- - Connection or auth failure: run `get_config_info` and verify `GROK_API_URL` and `GROK_API_KEY`.
41
- - `web_search` needs broader coverage: add `--extra-sources N`; if Tavily is unavailable, keep Grok results and note the warning.
42
- - `web_fetch` fails on Tavily extract: retry with `--fallback-grok`.
43
- - Hash-route/docsify pages may require the materialized markdown URL hinted by the CLI error.
41
+ | Scenario | Disabled | Use Instead |
42
+ |----------|----------|-------------|
43
+ | Web Search | `WebSearch` | CLI `web_search` via `groksearch_entry.py` |
44
+ | Web Fetch | `WebFetch` | CLI `web_fetch` via `groksearch_entry.py` |
45
+
46
+ ### Tool Capability Matrix
47
+
48
+ | Tool | Parameters | Output |
49
+ |------|------------|--------|
50
+ | `web_search` | `query`(required), `platform`/`min_results`/`max_results`/`model`/`extra_sources`(optional) | `[{title,url,description}]` |
51
+ | `web_fetch` | `url`(required), `out`/`fallback_grok`(optional) | Structured Markdown |
52
+ | `web_map` | `url`(required), `instructions`/`max_depth`/`max_breadth`/`limit`/`timeout`(optional) | JSON string |
53
+ | `get_config_info` | `no_test`(optional) | `{api_url,status,connection_test}` |
54
+ | `toggle_builtin_tools` | `action`(on/off/status), `root`(optional) | `{blocked,deny_list}` |
55
+
56
+ ## Search Workflow
57
+
58
+ ### Phase 1: Query Construction
59
+ - **Intent Recognition**: Broad search → `web_search` | Deep retrieval → `web_fetch`
60
+ - **Parameter Optimization**: Set `platform` for specific sources, adjust result counts; if you need broader source coverage, consider adding `--extra-sources 3`
61
+
62
+ ### Phase 2: Search Execution
63
+ 1. Start with `web_search` for structured summaries
64
+ 2. Use `web_fetch` on key URLs if summaries insufficient
65
+ 3. Retry with adjusted query if first round unsatisfactory
66
+
67
+ ### Phase 3: Result Synthesis
68
+ 1. Cross-reference multiple sources
69
+ 2. **Must annotate source and date** for time-sensitive info
70
+ 3. **Must include source URLs**: `Title [1](URL)`
71
+
72
+ ## Error Handling
73
+
74
+ | Error | Recovery |
75
+ |-------|----------|
76
+ | Connection Failure | Run `get_config_info`, verify API URL/Key |
77
+ | No Results | Broaden search terms |
78
+ | Fetch Timeout | Try alternative sources |
79
+ | Tavily unavailable while using `--extra-sources` | Command keeps Grok results and prints a Tavily warning to stderr |
80
+ | Tavily extract failure | Use `--fallback-grok`, or inspect the Tavily warning/error message |
81
+
82
+ ## Anti-Patterns
83
+
84
+ | Prohibited | Correct |
85
+ |------------|---------|
86
+ | No source citation | Include `Source [1](URL)` |
87
+ | Assume current repo has `skills/grok-search` | Check global skill directories under `$HOME` first, then project-level `.claude/.codex/.agents`, then call `<SKILL_DIR>/scripts/groksearch_entry.py` |
88
+ | Call `scripts/groksearch_cli.py` directly | Call `python scripts/groksearch_entry.py ...` |
89
+ | Use built-in WebSearch/WebFetch | Use GrokSearch CLI |
@@ -1,35 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Resolve the installed skill directory for dev-browser.
3
- # Outputs the resolved path to stdout. Exits 1 if not found.
4
- SKILL_NAME="dev-browser"
5
- SKILL_DIR=""
6
- CANDIDATE_SUFFIXES=".agents/skills .claude/skills .codex/skills .config/agents/skills"
7
- for base in \
8
- "${CODEX_HOME:-$HOME/.codex}/skills" \
9
- "$HOME/.agents/skills" \
10
- "$HOME/.claude/skills" \
11
- "$HOME/.config/agents/skills"
12
- do
13
- if [ -d "$base/$SKILL_NAME" ]; then
14
- SKILL_DIR="$base/$SKILL_NAME"
15
- break
16
- fi
17
- done
18
-
19
- if [ -z "$SKILL_DIR" ]; then
20
- SEARCH_DIR=$(pwd -P)
21
- while :; do
22
- for suffix in $CANDIDATE_SUFFIXES; do
23
- if [ -d "$SEARCH_DIR/$suffix/$SKILL_NAME" ]; then
24
- SKILL_DIR="$SEARCH_DIR/$suffix/$SKILL_NAME"
25
- break 2
26
- fi
27
- done
28
- [ "$SEARCH_DIR" = "/" ] && break
29
- SEARCH_DIR=$(dirname "$SEARCH_DIR")
30
- done
31
- fi
32
-
33
- [ -n "$SKILL_DIR" ] || { echo "Skill not found: $SKILL_NAME" >&2; exit 1; }
34
-
35
- echo "$SKILL_DIR"