abelworkflow 0.6.2 → 0.6.4

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/.gitignore CHANGED
@@ -13,3 +13,4 @@ skills/dev-browser/tmp/
13
13
  .codex/
14
14
  *pycache*/
15
15
  .codex
16
+ openspec/
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
+ ```
@@ -77,7 +77,7 @@ argument-hint: [change_name]
77
77
  - Use `openspec list --specs` to check for conflicts with existing specifications.
78
78
  - Search existing patterns with `rg -n "INVARIANT:|PROPERTY:|Constraint:" openspec/` before defining new ones.
79
79
  - For complex proposals, consider running steps 2-4 iteratively on sub-components.
80
- - Use `AskUserQuestions` for ANY ambiguity—do not assume or guess.
80
+ - Ask the user directly for ANY ambiguity—do not assume or guess.
81
81
 
82
82
  **Exit Criteria**
83
83
  A proposal is ready to exit the Plan phase only when:
@@ -26,7 +26,7 @@ Produce constraint sets that narrow the solution space, plus measurable success
26
26
 
27
27
  ## Phase 0 — Requirement Intake Gate (MANDATORY)
28
28
  - **MUST** confirm the user’s requirement exists and is clear **before** any research/action.
29
- - If missing/unclear, **MUST** use `AskUserQuestions` to collect: goal, in-scope area, top scenarios, non-goals, known constraints, success signals.
29
+ - If missing/unclear, **MUST** ask the user directly in a concise grouped message to collect: goal, in-scope area, top scenarios, non-goals, known constraints, success signals.
30
30
  - **MUST NOT** run `/opsx:new`, any codebase retrieval, spawn subagents, or generate artifacts until the user confirms a brief requirement summary.
31
31
 
32
32
  ---
@@ -97,7 +97,7 @@ All explore subagents MUST return valid JSON using this schema:
97
97
 
98
98
  ## Phase 7 — User Interaction for Ambiguity Resolution
99
99
  - Compile prioritized list of open questions from aggregated reports.
100
- - Use `AskUserQuestions` tool to present questions systematically:
100
+ - Present questions directly to the user in a concise grouped message:
101
101
  * Group related questions together.
102
102
  * Provide context for each question.
103
103
  * Suggest default answers when applicable.
@@ -122,5 +122,5 @@ All explore subagents MUST return valid JSON using this schema:
122
122
  - `openspec status --change <name>` - Check artifact completion status
123
123
  - `openspec instructions proposal --change <name>` - Get proposal instructions
124
124
  - Validate subagent outputs conform to template before aggregation.
125
- - Use `AskUserQuestions` for ANY ambiguity—do not assume or guess.
126
- <!-- OC:RESEARCH:END -->
125
+ - Ask the user directly for ANY ambiguity—do not assume or guess.
126
+ <!-- OC:RESEARCH:END -->
package/lib/cli.mjs CHANGED
@@ -1871,8 +1871,8 @@ function buildCodexConfigContent(currentContent, {
1871
1871
  content = mergeCodexTemplateDefaults(content, effectiveTemplateContent);
1872
1872
  }
1873
1873
  const lineEnding = detectLineEnding(content);
1874
- if (includeSubagentDefaults && readTopLevelTomlString(content, "approvals_reviewer") === "guardian_subagent") {
1875
- content = updateTopLevelTomlField(content, "approvals_reviewer", "reviewer");
1874
+ if (includeSubagentDefaults && readTopLevelTomlString(content, "approvals_reviewer") === "reviewer") {
1875
+ content = updateTopLevelTomlField(content, "approvals_reviewer", "guardian_subagent");
1876
1876
  }
1877
1877
  content = updateTopLevelTomlField(content, "model_provider", providerId);
1878
1878
  content = updateTopLevelTomlField(content, "preferred_auth_method", "apikey");
@@ -2,7 +2,7 @@ personality = "pragmatic"
2
2
  model_provider = "abelworkflow"
3
3
  disable_response_storage = true
4
4
  preferred_auth_method = "apikey"
5
- approvals_reviewer = "reviewer"
5
+ approvals_reviewer = "guardian_subagent"
6
6
  approval_policy = "on-request"
7
7
  sandbox_mode = "workspace-write"
8
8
  service_tier = "fast"
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "abelworkflow",
3
- "version": "0.6.2",
3
+ "version": "0.6.4",
4
4
  "description": "Install AbelWorkflow into ~/.agents and create Claude/Codex symlinks.",
5
5
  "type": "module",
6
+ "scripts": {
7
+ "test:contracts": "node --test test/runtime-doc-contracts.test.mjs"
8
+ },
6
9
  "bin": {
7
10
  "abelworkflow": "bin/abelworkflow.mjs"
8
11
  },
@@ -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"