abelworkflow 1.0.0-rc.1 → 1.1.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 (31) hide show
  1. package/README.md +7 -8
  2. package/extensions/pi-gpt-responses-compat/index.ts +1 -1
  3. package/lib/cli/args.mjs +2 -2
  4. package/lib/cli/main.mjs +39 -41
  5. package/lib/cli/prompts.mjs +16 -29
  6. package/lib/installer/assets.mjs +13 -15
  7. package/lib/installer/install.mjs +18 -32
  8. package/lib/installer/state.mjs +0 -19
  9. package/lib/providers/claude.mjs +23 -214
  10. package/lib/providers/codex.mjs +14 -22
  11. package/lib/providers/pi.mjs +273 -58
  12. package/lib/providers/skills.mjs +39 -88
  13. package/lib/templates/workflow/AGENTS.md +10 -10
  14. package/lib/templates/workflow/commands/abel-design.md +175 -0
  15. package/lib/templates/workflow/commands/abel-implement.md +64 -46
  16. package/lib/templates/workflow/commands/abel-init.md +2 -4
  17. package/package.json +1 -7
  18. package/skills/grok-search/.env.example +3 -3
  19. package/skills/grok-search/SKILL.md +1 -1
  20. package/skills/grok-search/defaults.json +1 -1
  21. package/skills/grok-search/scripts/groksearch_cli.py +42 -15
  22. package/skills/prompt-enhancer/SKILL.md +2 -27
  23. package/lib/installer/render.mjs +0 -46
  24. package/lib/templates/workflow/commands/abel-plan.md +0 -82
  25. package/lib/templates/workflow/commands/abel-research.md +0 -126
  26. package/skills/prompt-enhancer/.env.example +0 -12
  27. package/skills/prompt-enhancer/ADVANCED.md +0 -103
  28. package/skills/prompt-enhancer/requirements.txt +0 -1
  29. package/skills/prompt-enhancer/scripts/_dotenv.py +0 -32
  30. package/skills/prompt-enhancer/scripts/enhance.py +0 -144
  31. package/skills/prompt-enhancer/scripts/prompt_enhancer_entry.py +0 -238
@@ -1,144 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Prompt Enhancer CLI for OpenAI-compatible endpoints."""
3
-
4
- import argparse
5
- import os
6
- import sys
7
- from typing import Tuple
8
-
9
- from _dotenv import load_dotenv
10
-
11
- load_dotenv()
12
-
13
-
14
- SYSTEM_PROMPT = """
15
- You are an expert Prompt Engineer for Coding Agents (Claude Code, Codex, Gemini CLI).
16
- Your goal is to rewrite the user's raw input into a structured, high-context prompt that maximizes the agent's effectiveness.
17
-
18
- Guidelines:
19
- 1. Structure: Use a clear Markdown structure with headers.
20
- 2. Chain of Thought: Explicitly ask the agent to "Think step-by-step" or "Analyze the file structure first".
21
- 3. Context: If the user's prompt is vague, add placeholders like "[Insert relevant file(s)]" or "[Specify tech stack]" in the rewritten prompt, or simply infer them if obvious.
22
- 4. Format:
23
- - Context: What is the current state? What files are involved?
24
- - Objective: What exactly should be done?
25
- - Constraints: specific libraries, coding styles, or "no placeholders".
26
- - Response Format: e.g., "Return only the code block" or "Explain step-by-step".
27
-
28
- Output Template:
29
-
30
- # Context
31
- [Refined context description]
32
-
33
- # Objective
34
- [Precise task definition]
35
-
36
- # Step-by-Step Instructions
37
- 1. [Step 1]
38
- 2. [Step 2]
39
- ...
40
-
41
- # Constraints
42
- - [Constraint 1]
43
- - [Constraint 2]
44
- """
45
-
46
-
47
- def parse_args() -> argparse.Namespace:
48
- parser = argparse.ArgumentParser(
49
- prog="prompt_enhancer_entry.py",
50
- description="Enhance a prompt through a third-party OpenAI-compatible API.",
51
- )
52
- parser.add_argument("--url", dest="api_url", help="Third-party OpenAI-compatible base URL.")
53
- parser.add_argument("--api-key", dest="api_key", help="Third-party OpenAI-compatible API key.")
54
- parser.add_argument("--model", help="Model name on the third-party endpoint.")
55
- parser.add_argument("--prompt", help="Prompt text to enhance.")
56
- parser.add_argument("prompt_parts", nargs="*", help=argparse.SUPPRESS)
57
- return parser.parse_args()
58
-
59
-
60
- def debug_enabled() -> bool:
61
- value = os.environ.get("PE_DEBUG", "")
62
- return value.strip().lower() in {"1", "true", "yes", "on"}
63
-
64
-
65
- def resolve_prompt(args: argparse.Namespace) -> str:
66
- prompt = args.prompt.strip() if args.prompt else ""
67
- if prompt:
68
- return prompt
69
- joined = " ".join(args.prompt_parts).strip()
70
- if joined:
71
- return joined
72
- raise ValueError("Missing prompt. Pass --prompt \"...\" or a positional prompt.")
73
-
74
-
75
- def resolve_config(args: argparse.Namespace) -> Tuple[str, str, str]:
76
- api_url = (args.api_url or os.environ.get("PE_API_URL", "")).strip()
77
- api_key = (args.api_key or os.environ.get("PE_API_KEY", "")).strip()
78
- model = (args.model or os.environ.get("PE_MODEL", "")).strip()
79
-
80
- missing = []
81
- if not api_url:
82
- missing.append("url")
83
- if not api_key:
84
- missing.append("apiKey")
85
- if not model:
86
- missing.append("model")
87
- if missing:
88
- raise ValueError(
89
- "Missing third-party OpenAI-compatible config: "
90
- + ", ".join(missing)
91
- + ". If these fields are unavailable, use the current agent directly."
92
- )
93
- return api_url, api_key, model
94
-
95
-
96
- def _extract_response_text(response) -> str:
97
- content = response.choices[0].message.content
98
- if isinstance(content, str):
99
- return content
100
- if content is None:
101
- raise RuntimeError("The provider returned an empty response.")
102
- return str(content)
103
-
104
-
105
- def enhance_with_openai(prompt: str, api_key: str, model: str, api_url: str = "") -> str:
106
- try:
107
- from openai import OpenAI
108
- except ImportError:
109
- raise RuntimeError("Missing dependency: openai. Install dependencies for the configured provider.") from None
110
-
111
- kwargs = {"api_key": api_key}
112
- if api_url:
113
- kwargs["base_url"] = api_url
114
- client = OpenAI(**kwargs)
115
- response = client.chat.completions.create(
116
- model=model,
117
- messages=[
118
- {"role": "system", "content": SYSTEM_PROMPT},
119
- {"role": "user", "content": prompt},
120
- ],
121
- )
122
- return _extract_response_text(response)
123
-
124
-
125
- def main() -> None:
126
- try:
127
- args = parse_args()
128
- prompt = resolve_prompt(args)
129
- api_url, api_key, model = resolve_config(args)
130
- print(enhance_with_openai(prompt, api_key, model, api_url))
131
- except Exception as exc:
132
- if debug_enabled():
133
- print(f"Error: {exc}", file=sys.stderr)
134
- else:
135
- msg = str(exc)
136
- if "Missing third-party OpenAI-compatible config" in msg:
137
- print("Missing API configuration. Use the current agent directly instead, or set PE_API_URL, PE_API_KEY, PE_MODEL.", file=sys.stderr)
138
- else:
139
- print(msg, file=sys.stderr)
140
- sys.exit(1)
141
-
142
-
143
- if __name__ == "__main__":
144
- main()
@@ -1,238 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Cross-platform bootstrap entrypoint for Prompt Enhancer."""
3
-
4
- import os
5
- import re
6
- import shutil
7
- import subprocess
8
- import sys
9
- from pathlib import Path
10
- from typing import List, Optional
11
-
12
- from _dotenv import load_dotenv
13
-
14
-
15
- ROOT_DIR = Path(__file__).resolve().parent.parent
16
- REQ_FILE = ROOT_DIR / "requirements.txt"
17
- CLI_PY = ROOT_DIR / "scripts" / "enhance.py"
18
-
19
-
20
- def debug_enabled() -> bool:
21
- value = os.environ.get("PE_DEBUG", "")
22
- return value.strip().lower() in {"1", "true", "yes", "on"}
23
-
24
-
25
- def venv_dir() -> Path:
26
- configured = os.environ.get("PROMPT_ENHANCER_VENV_DIR")
27
- if not configured:
28
- return ROOT_DIR / ".venv"
29
- dir_path = Path(configured).expanduser()
30
- if dir_path.is_absolute():
31
- return dir_path
32
- return ROOT_DIR / dir_path
33
-
34
-
35
- def venv_python() -> Optional[Path]:
36
- dir_path = venv_dir()
37
- candidates = []
38
- if sys.platform == "win32":
39
- candidates.extend(
40
- [
41
- dir_path / "Scripts" / "python.exe",
42
- dir_path / "Scripts" / "python",
43
- ]
44
- )
45
- candidates.append(dir_path / "bin" / "python")
46
- for candidate in candidates:
47
- if candidate.is_file():
48
- return candidate
49
- return None
50
-
51
-
52
- def python_spec() -> Optional[str]:
53
- for name in ("PROMPT_ENHANCER_PYTHON", "AGENTS_SKILLS_PYTHON"):
54
- value = os.environ.get(name)
55
- if value:
56
- return value
57
- return None
58
-
59
-
60
- def has_uv() -> bool:
61
- return shutil.which("uv") is not None
62
-
63
-
64
- def find_system_python() -> Optional[str]:
65
- env_python = os.environ.get("AGENTS_SKILLS_PYTHON")
66
- if env_python and Path(env_python).is_file():
67
- return env_python
68
- if sys.executable and Path(sys.executable).is_file():
69
- return sys.executable
70
- for command in ("python3", "python"):
71
- found = shutil.which(command)
72
- if found:
73
- return found
74
- return None
75
-
76
-
77
- def create_venv() -> None:
78
- dir_path = venv_dir()
79
- if has_uv():
80
- command = ["uv", "venv"]
81
- spec = python_spec()
82
- if spec:
83
- command.extend(["--python", spec])
84
- command.append(str(dir_path))
85
- subprocess.run(command, check=True)
86
- return
87
- python_bin = find_system_python()
88
- if not python_bin:
89
- print("Error: No usable uv or python found. Cannot create virtual environment.", file=sys.stderr)
90
- sys.exit(1)
91
- subprocess.run([python_bin, "-m", "venv", str(dir_path)], check=True)
92
-
93
-
94
- def first_non_empty(*values: str) -> str:
95
- for value in values:
96
- stripped = value.strip()
97
- if stripped:
98
- return stripped
99
- return ""
100
-
101
-
102
- def argv_value(flag: str, argv: Optional[List[str]] = None) -> str:
103
- argv = sys.argv[1:] if argv is None else argv
104
- for index, token in enumerate(argv):
105
- if token == flag:
106
- if index + 1 < len(argv):
107
- return argv[index + 1].strip()
108
- return ""
109
- if token.startswith(f"{flag}="):
110
- return token.split("=", 1)[1].strip()
111
- return ""
112
-
113
-
114
- def resolved_provider(argv: Optional[List[str]] = None) -> Optional[str]:
115
- argv = sys.argv[1:] if argv is None else argv
116
- api_url = first_non_empty(argv_value("--url", argv), os.environ.get("PE_API_URL", ""))
117
- api_key = first_non_empty(argv_value("--api-key", argv), os.environ.get("PE_API_KEY", ""))
118
- model = first_non_empty(argv_value("--model", argv), os.environ.get("PE_MODEL", ""))
119
-
120
- if api_url and api_key and model:
121
- return "openai"
122
- return None
123
-
124
-
125
- def required_modules() -> List[str]:
126
- return ["openai"] if resolved_provider() == "openai" else []
127
-
128
-
129
- def install_targets(modules: List[str]) -> List[str]:
130
- if not REQ_FILE.is_file():
131
- return modules
132
-
133
- requirements = {}
134
- for raw_line in REQ_FILE.read_text(encoding="utf-8").splitlines():
135
- line = raw_line.split("#", 1)[0].strip()
136
- if not line:
137
- continue
138
- package = re.split(r"[<>=!~\\[; ]", line, maxsplit=1)[0].strip().lower()
139
- if package:
140
- requirements[package] = line
141
-
142
- return [requirements.get(module.lower(), module) for module in modules]
143
-
144
-
145
- def has_required_modules(python_bin: str, modules: List[str]) -> bool:
146
- if not modules:
147
- return True
148
- check = subprocess.run(
149
- [python_bin, "-c", "; ".join(f"import {module}" for module in modules)],
150
- stdout=subprocess.DEVNULL,
151
- stderr=subprocess.DEVNULL,
152
- )
153
- return check.returncode == 0
154
-
155
-
156
- def install_deps(python_bin: Path, modules: Optional[List[str]] = None) -> None:
157
- modules = required_modules() if modules is None else modules
158
- if not modules:
159
- return
160
- targets = install_targets(modules)
161
- if has_required_modules(str(python_bin), modules):
162
- return
163
- try:
164
- kwargs = {
165
- "check": True,
166
- "stdout": subprocess.DEVNULL,
167
- "stderr": subprocess.PIPE,
168
- "text": True,
169
- }
170
- if has_uv():
171
- subprocess.run(["uv", "pip", "install", "--python", str(python_bin), *targets], **kwargs)
172
- return
173
- subprocess.run([str(python_bin), "-m", "pip", "install", *targets], **kwargs)
174
- except subprocess.CalledProcessError as exc:
175
- if debug_enabled():
176
- details = (exc.stderr or "").strip()
177
- if details:
178
- print(details, file=sys.stderr)
179
- print(
180
- "Warning: Failed to install prompt enhancer dependencies; continuing with existing environment.",
181
- file=sys.stderr,
182
- )
183
-
184
-
185
- def validate_venv_dir() -> None:
186
- dir_path = venv_dir()
187
- if dir_path.exists() and not dir_path.is_dir():
188
- print(f"Error: {dir_path} exists but is not a directory.", file=sys.stderr)
189
- sys.exit(1)
190
- if dir_path.is_dir() and not (dir_path / "pyvenv.cfg").exists() and venv_python() is None:
191
- print(f"Error: {dir_path} exists but is not a valid venv.", file=sys.stderr)
192
- sys.exit(1)
193
-
194
-
195
- def should_passthrough_cli() -> bool:
196
- return len(sys.argv) < 2
197
-
198
-
199
- def passthrough_python() -> Optional[str]:
200
- if sys.executable and Path(sys.executable).is_file():
201
- return sys.executable
202
- return find_system_python()
203
-
204
-
205
- def runtime_python() -> str:
206
- modules = required_modules()
207
- current_python = passthrough_python()
208
- if current_python and has_required_modules(current_python, modules):
209
- return current_python
210
-
211
- validate_venv_dir()
212
- python_bin = venv_python()
213
- if python_bin is None:
214
- create_venv()
215
- python_bin = venv_python()
216
- if python_bin is None:
217
- print("Error: Failed to locate python in venv after creation.", file=sys.stderr)
218
- sys.exit(1)
219
- install_deps(python_bin, modules)
220
- return str(python_bin)
221
-
222
-
223
- def main() -> None:
224
- if should_passthrough_cli():
225
- python_bin = passthrough_python()
226
- if not python_bin:
227
- print("Error: No usable python found for CLI usage output.", file=sys.stderr)
228
- sys.exit(1)
229
- result = subprocess.run([python_bin, str(CLI_PY)] + sys.argv[1:])
230
- sys.exit(result.returncode)
231
- load_dotenv()
232
- python_bin = runtime_python()
233
- result = subprocess.run([python_bin, str(CLI_PY)] + sys.argv[1:])
234
- sys.exit(result.returncode)
235
-
236
-
237
- if __name__ == "__main__":
238
- main()