hyperflash 1.1.1__tar.gz → 1.1.2__tar.gz
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.
- {hyperflash-1.1.1/src/hyperflash.egg-info → hyperflash-1.1.2}/PKG-INFO +24 -3
- {hyperflash-1.1.1 → hyperflash-1.1.2}/README.md +23 -2
- {hyperflash-1.1.1 → hyperflash-1.1.2}/pyproject.toml +1 -1
- {hyperflash-1.1.1 → hyperflash-1.1.2}/src/hyperflash/__init__.py +1 -1
- {hyperflash-1.1.1 → hyperflash-1.1.2}/src/hyperflash/cli.py +17 -8
- {hyperflash-1.1.1 → hyperflash-1.1.2}/src/hyperflash/polyglot.py +127 -48
- {hyperflash-1.1.1 → hyperflash-1.1.2/src/hyperflash.egg-info}/PKG-INFO +24 -3
- {hyperflash-1.1.1 → hyperflash-1.1.2}/tests/test_cli.py +54 -1
- {hyperflash-1.1.1 → hyperflash-1.1.2}/tests/test_core.py +71 -0
- {hyperflash-1.1.1 → hyperflash-1.1.2}/LICENSE +0 -0
- {hyperflash-1.1.1 → hyperflash-1.1.2}/setup.cfg +0 -0
- {hyperflash-1.1.1 → hyperflash-1.1.2}/src/hyperflash/core.py +0 -0
- {hyperflash-1.1.1 → hyperflash-1.1.2}/src/hyperflash/mcp_server.py +0 -0
- {hyperflash-1.1.1 → hyperflash-1.1.2}/src/hyperflash.egg-info/SOURCES.txt +0 -0
- {hyperflash-1.1.1 → hyperflash-1.1.2}/src/hyperflash.egg-info/dependency_links.txt +0 -0
- {hyperflash-1.1.1 → hyperflash-1.1.2}/src/hyperflash.egg-info/entry_points.txt +0 -0
- {hyperflash-1.1.1 → hyperflash-1.1.2}/src/hyperflash.egg-info/requires.txt +0 -0
- {hyperflash-1.1.1 → hyperflash-1.1.2}/src/hyperflash.egg-info/top_level.txt +0 -0
- {hyperflash-1.1.1 → hyperflash-1.1.2}/tests/test_mcp.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hyperflash
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.2
|
|
4
4
|
Summary: Universal Engineering Reliability Runtime & Multi-Agent Verification Guardrails
|
|
5
5
|
Author: Virat Ghatge
|
|
6
6
|
License: MIT
|
|
@@ -184,7 +184,12 @@ Scaffold your preferred IDE or agent harness in 1 second:
|
|
|
184
184
|
# Configure Cursor, Claude Code, Windsurf, Git Hooks, Antigravity, and OpenCode in one command
|
|
185
185
|
hyperflash init
|
|
186
186
|
|
|
187
|
-
#
|
|
187
|
+
# Configure globally across ALL workspaces (user home directory):
|
|
188
|
+
# Sets up OpenCode (~/.config/opencode/opencode.json), Claude Code (~/.claude.json), and Antigravity globally
|
|
189
|
+
hyperflash init --global # or: hyperflash init -g
|
|
190
|
+
|
|
191
|
+
# Or target your specific harness globally or locally:
|
|
192
|
+
hyperflash init --target opencode --global
|
|
188
193
|
hyperflash init --target cursor
|
|
189
194
|
hyperflash init --target claude
|
|
190
195
|
hyperflash init --target windsurf
|
|
@@ -193,6 +198,14 @@ hyperflash init --target antigravity
|
|
|
193
198
|
hyperflash init --target opencode
|
|
194
199
|
```
|
|
195
200
|
|
|
201
|
+
> [!TIP]
|
|
202
|
+
> **Global Configuration Mode (`--global` / `-g`):**
|
|
203
|
+
> When running `hyperflash init --global` (or `-g`), HyperFlash configures your user home directory rather than the current working folder. This means:
|
|
204
|
+
> - **OpenCode:** Registers HyperFlash agent persona and MCP server in `~/.config/opencode/opencode.json` so `opencode --agent hyperflash` works anywhere on your system.
|
|
205
|
+
> - **Claude Code:** Registers the MCP server in `~/.claude.json` and slash command in `~/.claude/commands/hyperflash.md`.
|
|
206
|
+
> - **Antigravity:** Installs skills, agents, and rules globally in `~/.gemini/config/`.
|
|
207
|
+
> You only need to run this once—no per-repository initialization required!
|
|
208
|
+
|
|
196
209
|
### 🚀 Universal Agent Launcher
|
|
197
210
|
Launch your preferred coding environment with the native HyperFlash agent persona pre-activated:
|
|
198
211
|
|
|
@@ -352,9 +365,17 @@ OpenCode supports dedicated named agents natively. HyperFlash installs as a firs
|
|
|
352
365
|
|
|
353
366
|
#### Step 1: Automatic Setup via CLI
|
|
354
367
|
```bash
|
|
368
|
+
# Option A: Global setup (recommended) - works across all folders on your system:
|
|
369
|
+
hyperflash init --target opencode --global
|
|
370
|
+
|
|
371
|
+
# Option B: Local workspace setup - configures current repository:
|
|
355
372
|
hyperflash init --target opencode
|
|
356
373
|
```
|
|
357
|
-
|
|
374
|
+
|
|
375
|
+
**Global Setup (`--global`) configures:**
|
|
376
|
+
- `~/.config/opencode/opencode.json`: Declares the global `hyperflash` agent persona and registers the stdio MCP server without overwriting existing agents or servers.
|
|
377
|
+
|
|
378
|
+
**Local Setup configures:**
|
|
358
379
|
- `OPENCODE.md`: OpenCode engineering constitution.
|
|
359
380
|
- `.opencode/agents/hyperflash.json`: Dedicated agent persona definition.
|
|
360
381
|
- `opencode.json`: Declares `agent: "hyperflash"`, registers agent configuration, and wires the `hyperflash` stdio MCP server.
|
|
@@ -159,7 +159,12 @@ Scaffold your preferred IDE or agent harness in 1 second:
|
|
|
159
159
|
# Configure Cursor, Claude Code, Windsurf, Git Hooks, Antigravity, and OpenCode in one command
|
|
160
160
|
hyperflash init
|
|
161
161
|
|
|
162
|
-
#
|
|
162
|
+
# Configure globally across ALL workspaces (user home directory):
|
|
163
|
+
# Sets up OpenCode (~/.config/opencode/opencode.json), Claude Code (~/.claude.json), and Antigravity globally
|
|
164
|
+
hyperflash init --global # or: hyperflash init -g
|
|
165
|
+
|
|
166
|
+
# Or target your specific harness globally or locally:
|
|
167
|
+
hyperflash init --target opencode --global
|
|
163
168
|
hyperflash init --target cursor
|
|
164
169
|
hyperflash init --target claude
|
|
165
170
|
hyperflash init --target windsurf
|
|
@@ -168,6 +173,14 @@ hyperflash init --target antigravity
|
|
|
168
173
|
hyperflash init --target opencode
|
|
169
174
|
```
|
|
170
175
|
|
|
176
|
+
> [!TIP]
|
|
177
|
+
> **Global Configuration Mode (`--global` / `-g`):**
|
|
178
|
+
> When running `hyperflash init --global` (or `-g`), HyperFlash configures your user home directory rather than the current working folder. This means:
|
|
179
|
+
> - **OpenCode:** Registers HyperFlash agent persona and MCP server in `~/.config/opencode/opencode.json` so `opencode --agent hyperflash` works anywhere on your system.
|
|
180
|
+
> - **Claude Code:** Registers the MCP server in `~/.claude.json` and slash command in `~/.claude/commands/hyperflash.md`.
|
|
181
|
+
> - **Antigravity:** Installs skills, agents, and rules globally in `~/.gemini/config/`.
|
|
182
|
+
> You only need to run this once—no per-repository initialization required!
|
|
183
|
+
|
|
171
184
|
### 🚀 Universal Agent Launcher
|
|
172
185
|
Launch your preferred coding environment with the native HyperFlash agent persona pre-activated:
|
|
173
186
|
|
|
@@ -327,9 +340,17 @@ OpenCode supports dedicated named agents natively. HyperFlash installs as a firs
|
|
|
327
340
|
|
|
328
341
|
#### Step 1: Automatic Setup via CLI
|
|
329
342
|
```bash
|
|
343
|
+
# Option A: Global setup (recommended) - works across all folders on your system:
|
|
344
|
+
hyperflash init --target opencode --global
|
|
345
|
+
|
|
346
|
+
# Option B: Local workspace setup - configures current repository:
|
|
330
347
|
hyperflash init --target opencode
|
|
331
348
|
```
|
|
332
|
-
|
|
349
|
+
|
|
350
|
+
**Global Setup (`--global`) configures:**
|
|
351
|
+
- `~/.config/opencode/opencode.json`: Declares the global `hyperflash` agent persona and registers the stdio MCP server without overwriting existing agents or servers.
|
|
352
|
+
|
|
353
|
+
**Local Setup configures:**
|
|
333
354
|
- `OPENCODE.md`: OpenCode engineering constitution.
|
|
334
355
|
- `.opencode/agents/hyperflash.json`: Dedicated agent persona definition.
|
|
335
356
|
- `opencode.json`: Declares `agent: "hyperflash"`, registers agent configuration, and wires the `hyperflash` stdio MCP server.
|
|
@@ -36,37 +36,39 @@ def cmd_init(args: argparse.Namespace) -> int:
|
|
|
36
36
|
"""Handles 'hyperflash init' command."""
|
|
37
37
|
target: str = args.target.lower()
|
|
38
38
|
workspace: str = args.workspace
|
|
39
|
+
is_global: bool = getattr(args, "is_global", False)
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
target_desc: str = "globally in user home" if is_global else f"in '{workspace}'"
|
|
42
|
+
print(f"⚡ HyperFlash: Initializing reliability runtime for target '{target}' {target_desc}...")
|
|
41
43
|
|
|
42
44
|
if target == "all":
|
|
43
|
-
results = setup_all(workspace)
|
|
45
|
+
results = setup_all(workspace, is_global=is_global)
|
|
44
46
|
for platform, files in results.items():
|
|
45
47
|
print(f" • {platform.capitalize()}:")
|
|
46
48
|
for f in files:
|
|
47
49
|
print(f" - Configured: {f}")
|
|
48
50
|
elif target == "cursor":
|
|
49
|
-
files = setup_cursor(workspace)
|
|
51
|
+
files = setup_cursor(workspace, is_global=is_global)
|
|
50
52
|
for f in files:
|
|
51
53
|
print(f" • Configured Cursor: {f}")
|
|
52
54
|
elif target == "claude":
|
|
53
|
-
files = setup_claude(workspace)
|
|
55
|
+
files = setup_claude(workspace, is_global=is_global)
|
|
54
56
|
for f in files:
|
|
55
57
|
print(f" • Configured Claude Code: {f}")
|
|
56
58
|
elif target == "windsurf":
|
|
57
|
-
files = setup_windsurf(workspace)
|
|
59
|
+
files = setup_windsurf(workspace, is_global=is_global)
|
|
58
60
|
for f in files:
|
|
59
61
|
print(f" • Configured Windsurf: {f}")
|
|
60
62
|
elif target == "git":
|
|
61
|
-
files = setup_git_hooks(workspace)
|
|
63
|
+
files = setup_git_hooks(workspace, is_global=is_global)
|
|
62
64
|
for f in files:
|
|
63
65
|
print(f" • Configured Git pre-commit hook: {f}")
|
|
64
66
|
elif target == "antigravity":
|
|
65
|
-
files = setup_antigravity(workspace)
|
|
67
|
+
files = setup_antigravity(workspace, is_global=is_global)
|
|
66
68
|
for f in files:
|
|
67
69
|
print(f" • Configured Antigravity: {f}")
|
|
68
70
|
elif target == "opencode":
|
|
69
|
-
files = setup_opencode(workspace)
|
|
71
|
+
files = setup_opencode(workspace, is_global=is_global)
|
|
70
72
|
for f in files:
|
|
71
73
|
print(f" • Configured OpenCode: {f}")
|
|
72
74
|
else:
|
|
@@ -244,6 +246,13 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
244
246
|
default=".",
|
|
245
247
|
help="Workspace root directory (default: .)",
|
|
246
248
|
)
|
|
249
|
+
p_init.add_argument(
|
|
250
|
+
"-g",
|
|
251
|
+
"--global",
|
|
252
|
+
dest="is_global",
|
|
253
|
+
action="store_true",
|
|
254
|
+
help="Configure globally in user home directory across all workspaces",
|
|
255
|
+
)
|
|
247
256
|
|
|
248
257
|
# launch
|
|
249
258
|
p_launch = subparsers.add_parser("launch", help="Launch your preferred coding harness with HyperFlash agent active")
|
|
@@ -144,6 +144,31 @@ OPENCODE_CONFIG_DICT: dict[str, Any] = {
|
|
|
144
144
|
},
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
OPENCODE_GLOBAL_CONFIG_DICT: dict[str, Any] = {
|
|
148
|
+
"agent": {
|
|
149
|
+
"hyperflash": {
|
|
150
|
+
"name": "HyperFlash",
|
|
151
|
+
"description": "Universal Engineering Reliability & Multi-Agent Verification Runtime",
|
|
152
|
+
"system": (
|
|
153
|
+
"You are HYPERFLASH, the Principal Multi-Agent Engineering Reliability Orchestrator. "
|
|
154
|
+
"You operate strictly under the 8 HyperFlash Invariants: 1. Ground before mutating. "
|
|
155
|
+
"2. Stay in declared scope. 3. Fresh evidence required before completion. "
|
|
156
|
+
"4. Never exact-retry failed modifications. 5. Satisfy acceptance criteria. "
|
|
157
|
+
"6. Report reality only. 7. ZERO code elision. 8. Exhaustive static typing. "
|
|
158
|
+
"Always use hyperflash_safe_patch to mutate files, and run hyperflash_verify before claiming a task is done."
|
|
159
|
+
),
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
"mcp": {
|
|
163
|
+
"servers": {
|
|
164
|
+
"hyperflash": {
|
|
165
|
+
"type": "local",
|
|
166
|
+
"command": ["hyperflash", "mcp"],
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
}
|
|
171
|
+
|
|
147
172
|
GIT_PRE_COMMIT_CONTENT: str = """#!/usr/bin/env bash
|
|
148
173
|
# HyperFlash Zero-Elision & Integrity Gate
|
|
149
174
|
set -e
|
|
@@ -208,86 +233,129 @@ def merge_json_config(file_path: Path, new_data: dict[str, Any]) -> None:
|
|
|
208
233
|
else:
|
|
209
234
|
existing["agents"] = new_data["agents"]
|
|
210
235
|
|
|
236
|
+
# Merge nested 'agent' dict if both are dicts
|
|
237
|
+
if "agent" in new_data:
|
|
238
|
+
if isinstance(new_data["agent"], dict):
|
|
239
|
+
agent_dict = existing.get("agent", {})
|
|
240
|
+
if isinstance(agent_dict, dict):
|
|
241
|
+
agent_dict.update(new_data["agent"])
|
|
242
|
+
existing["agent"] = agent_dict
|
|
243
|
+
else:
|
|
244
|
+
existing["agent"] = new_data["agent"]
|
|
245
|
+
else:
|
|
246
|
+
existing["agent"] = new_data["agent"]
|
|
247
|
+
|
|
248
|
+
# Merge nested 'mcp' dict (OpenCode global config schema: {"mcp": {"servers": {...}}})
|
|
249
|
+
if "mcp" in new_data:
|
|
250
|
+
if isinstance(new_data["mcp"], dict):
|
|
251
|
+
mcp_dict = existing.get("mcp", {})
|
|
252
|
+
if isinstance(mcp_dict, dict):
|
|
253
|
+
servers = mcp_dict.get("servers", {})
|
|
254
|
+
new_servers = new_data["mcp"].get("servers", {})
|
|
255
|
+
if isinstance(servers, dict) and isinstance(new_servers, dict):
|
|
256
|
+
servers.update(new_servers)
|
|
257
|
+
mcp_dict["servers"] = servers
|
|
258
|
+
else:
|
|
259
|
+
mcp_dict.update(new_data["mcp"])
|
|
260
|
+
existing["mcp"] = mcp_dict
|
|
261
|
+
else:
|
|
262
|
+
existing["mcp"] = new_data["mcp"]
|
|
263
|
+
else:
|
|
264
|
+
existing["mcp"] = new_data["mcp"]
|
|
265
|
+
|
|
211
266
|
for k, v in new_data.items():
|
|
212
|
-
if k not in ("mcpServers", "agents"):
|
|
267
|
+
if k not in ("mcpServers", "agents", "agent", "mcp"):
|
|
213
268
|
existing[k] = v
|
|
214
269
|
|
|
215
270
|
file_path.write_text(json.dumps(existing, indent=2), encoding="utf-8")
|
|
216
271
|
|
|
217
272
|
|
|
218
|
-
def setup_cursor(workspace_dir: Path | str = ".") -> list[str]:
|
|
273
|
+
def setup_cursor(workspace_dir: Path | str = ".", is_global: bool = False) -> list[str]:
|
|
219
274
|
"""
|
|
220
275
|
Configures HyperFlash rules, agent rule mdc, and stdio MCP server for Cursor.
|
|
221
276
|
"""
|
|
222
|
-
|
|
277
|
+
target_root = Path.home() if is_global else Path(workspace_dir).resolve()
|
|
223
278
|
created_files: list[str] = []
|
|
224
279
|
|
|
225
|
-
rules_path =
|
|
280
|
+
rules_path = target_root / ".cursorrules"
|
|
226
281
|
rules_path.write_text(CURSOR_RULES_CONTENT, encoding="utf-8")
|
|
227
282
|
created_files.append(str(rules_path))
|
|
228
283
|
|
|
229
|
-
rules_dir =
|
|
284
|
+
rules_dir = target_root / ".cursor" / "rules"
|
|
230
285
|
rules_dir.mkdir(parents=True, exist_ok=True)
|
|
231
286
|
mdc_path = rules_dir / "hyperflash.mdc"
|
|
232
287
|
mdc_path.write_text(CURSOR_RULE_MDC_CONTENT, encoding="utf-8")
|
|
233
288
|
created_files.append(str(mdc_path))
|
|
234
289
|
|
|
235
|
-
mcp_path =
|
|
290
|
+
mcp_path = target_root / ".cursor" / "mcp.json"
|
|
236
291
|
merge_json_config(mcp_path, MCP_CONFIG_DICT)
|
|
237
292
|
created_files.append(str(mcp_path))
|
|
238
293
|
|
|
239
294
|
return created_files
|
|
240
295
|
|
|
241
296
|
|
|
242
|
-
def setup_claude(workspace_dir: Path | str = ".") -> list[str]:
|
|
297
|
+
def setup_claude(workspace_dir: Path | str = ".", is_global: bool = False) -> list[str]:
|
|
243
298
|
"""
|
|
244
299
|
Configures HyperFlash rules, slash command, and stdio MCP server for Claude Code.
|
|
245
300
|
"""
|
|
246
|
-
workspace = Path(workspace_dir).resolve()
|
|
247
301
|
created_files: list[str] = []
|
|
248
302
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
303
|
+
if is_global:
|
|
304
|
+
home = Path.home()
|
|
305
|
+
commands_dir = home / ".claude" / "commands"
|
|
306
|
+
commands_dir.mkdir(parents=True, exist_ok=True)
|
|
307
|
+
cmd_file = commands_dir / "hyperflash.md"
|
|
308
|
+
cmd_file.write_text(CLAUDE_COMMAND_MD_CONTENT, encoding="utf-8")
|
|
309
|
+
created_files.append(str(cmd_file))
|
|
310
|
+
|
|
311
|
+
claude_json = home / ".claude.json"
|
|
312
|
+
merge_json_config(claude_json, MCP_CONFIG_DICT)
|
|
313
|
+
created_files.append(str(claude_json))
|
|
314
|
+
else:
|
|
315
|
+
workspace = Path(workspace_dir).resolve()
|
|
316
|
+
|
|
317
|
+
claude_md = workspace / "CLAUDE.md"
|
|
318
|
+
claude_md.write_text(CLAUDE_MD_CONTENT, encoding="utf-8")
|
|
319
|
+
created_files.append(str(claude_md))
|
|
320
|
+
|
|
321
|
+
commands_dir = workspace / ".claude" / "commands"
|
|
322
|
+
commands_dir.mkdir(parents=True, exist_ok=True)
|
|
323
|
+
cmd_file = commands_dir / "hyperflash.md"
|
|
324
|
+
cmd_file.write_text(CLAUDE_COMMAND_MD_CONTENT, encoding="utf-8")
|
|
325
|
+
created_files.append(str(cmd_file))
|
|
326
|
+
|
|
327
|
+
claude_json = workspace / ".claude.json"
|
|
328
|
+
merge_json_config(claude_json, MCP_CONFIG_DICT)
|
|
329
|
+
created_files.append(str(claude_json))
|
|
262
330
|
|
|
263
331
|
return created_files
|
|
264
332
|
|
|
265
333
|
|
|
266
|
-
def setup_windsurf(workspace_dir: Path | str = ".") -> list[str]:
|
|
334
|
+
def setup_windsurf(workspace_dir: Path | str = ".", is_global: bool = False) -> list[str]:
|
|
267
335
|
"""
|
|
268
336
|
Configures HyperFlash rules, agent persona rule, and stdio MCP server for Windsurf.
|
|
269
337
|
"""
|
|
270
|
-
|
|
338
|
+
target_root = Path.home() if is_global else Path(workspace_dir).resolve()
|
|
271
339
|
created_files: list[str] = []
|
|
272
340
|
|
|
273
|
-
rules_path =
|
|
341
|
+
rules_path = target_root / ".windsurfrules"
|
|
274
342
|
rules_path.write_text(WINDSURF_RULES_CONTENT, encoding="utf-8")
|
|
275
343
|
created_files.append(str(rules_path))
|
|
276
344
|
|
|
277
|
-
rules_dir =
|
|
345
|
+
rules_dir = target_root / ".windsurf" / "rules"
|
|
278
346
|
rules_dir.mkdir(parents=True, exist_ok=True)
|
|
279
347
|
rule_md_path = rules_dir / "hyperflash.md"
|
|
280
348
|
rule_md_path.write_text(WINDSURF_RULE_MD_CONTENT, encoding="utf-8")
|
|
281
349
|
created_files.append(str(rule_md_path))
|
|
282
350
|
|
|
283
|
-
mcp_path =
|
|
351
|
+
mcp_path = target_root / ".windsurf" / "mcp.json"
|
|
284
352
|
merge_json_config(mcp_path, MCP_CONFIG_DICT)
|
|
285
353
|
created_files.append(str(mcp_path))
|
|
286
354
|
|
|
287
355
|
return created_files
|
|
288
356
|
|
|
289
357
|
|
|
290
|
-
def setup_git_hooks(workspace_dir: Path | str = ".") -> list[str]:
|
|
358
|
+
def setup_git_hooks(workspace_dir: Path | str = ".", is_global: bool = False) -> list[str]:
|
|
291
359
|
"""
|
|
292
360
|
Installs HyperFlash pre-commit hook into .git/hooks.
|
|
293
361
|
"""
|
|
@@ -295,6 +363,9 @@ def setup_git_hooks(workspace_dir: Path | str = ".") -> list[str]:
|
|
|
295
363
|
git_hooks_dir = workspace / ".git" / "hooks"
|
|
296
364
|
created_files: list[str] = []
|
|
297
365
|
|
|
366
|
+
if is_global and not (workspace / ".git").exists():
|
|
367
|
+
return created_files
|
|
368
|
+
|
|
298
369
|
git_hooks_dir.mkdir(parents=True, exist_ok=True)
|
|
299
370
|
|
|
300
371
|
hook_file = git_hooks_dir / "pre-commit"
|
|
@@ -309,7 +380,7 @@ def setup_git_hooks(workspace_dir: Path | str = ".") -> list[str]:
|
|
|
309
380
|
return created_files
|
|
310
381
|
|
|
311
382
|
|
|
312
|
-
def setup_antigravity(workspace_dir: Path | str = ".") -> list[str]:
|
|
383
|
+
def setup_antigravity(workspace_dir: Path | str = ".", is_global: bool = False) -> list[str]:
|
|
313
384
|
"""
|
|
314
385
|
Installs or updates HyperFlash skill and agents in Antigravity (~/.gemini/config/).
|
|
315
386
|
"""
|
|
@@ -349,39 +420,47 @@ def setup_antigravity(workspace_dir: Path | str = ".") -> list[str]:
|
|
|
349
420
|
return created_files
|
|
350
421
|
|
|
351
422
|
|
|
352
|
-
def setup_opencode(workspace_dir: Path | str = ".") -> list[str]:
|
|
423
|
+
def setup_opencode(workspace_dir: Path | str = ".", is_global: bool = False) -> list[str]:
|
|
353
424
|
"""
|
|
354
425
|
Configures HyperFlash rules, agent persona, and stdio MCP server for OpenCode.
|
|
426
|
+
If is_global is True, writes to user global config: ~/.config/opencode/opencode.json.
|
|
427
|
+
If not purely global, also writes local workspace files: OPENCODE.md, .opencode/agents/hyperflash.json, opencode.json.
|
|
355
428
|
"""
|
|
356
|
-
workspace = Path(workspace_dir).resolve()
|
|
357
429
|
created_files: list[str] = []
|
|
358
430
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
431
|
+
if is_global:
|
|
432
|
+
global_config_path = Path.home() / ".config" / "opencode" / "opencode.json"
|
|
433
|
+
merge_json_config(global_config_path, OPENCODE_GLOBAL_CONFIG_DICT)
|
|
434
|
+
created_files.append(str(global_config_path))
|
|
435
|
+
else:
|
|
436
|
+
workspace = Path(workspace_dir).resolve()
|
|
437
|
+
|
|
438
|
+
opencode_md = workspace / "OPENCODE.md"
|
|
439
|
+
opencode_md.write_text(OPENCODE_MD_CONTENT, encoding="utf-8")
|
|
440
|
+
created_files.append(str(opencode_md))
|
|
362
441
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
442
|
+
agent_dir = workspace / ".opencode" / "agents"
|
|
443
|
+
agent_dir.mkdir(parents=True, exist_ok=True)
|
|
444
|
+
agent_json_path = agent_dir / "hyperflash.json"
|
|
445
|
+
agent_json_path.write_text(json.dumps(OPENCODE_AGENT_DICT, indent=2), encoding="utf-8")
|
|
446
|
+
created_files.append(str(agent_json_path))
|
|
368
447
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
448
|
+
opencode_json = workspace / "opencode.json"
|
|
449
|
+
merge_json_config(opencode_json, OPENCODE_CONFIG_DICT)
|
|
450
|
+
created_files.append(str(opencode_json))
|
|
372
451
|
|
|
373
452
|
return created_files
|
|
374
453
|
|
|
375
454
|
|
|
376
|
-
def setup_all(workspace_dir: Path | str = ".") -> dict[str, list[str]]:
|
|
455
|
+
def setup_all(workspace_dir: Path | str = ".", is_global: bool = False) -> dict[str, list[str]]:
|
|
377
456
|
"""
|
|
378
457
|
Executes polyglot configuration across all supported platforms.
|
|
379
458
|
"""
|
|
380
459
|
return {
|
|
381
|
-
"cursor": setup_cursor(workspace_dir),
|
|
382
|
-
"claude": setup_claude(workspace_dir),
|
|
383
|
-
"windsurf": setup_windsurf(workspace_dir),
|
|
384
|
-
"git": setup_git_hooks(workspace_dir),
|
|
385
|
-
"antigravity": setup_antigravity(workspace_dir),
|
|
386
|
-
"opencode": setup_opencode(workspace_dir),
|
|
460
|
+
"cursor": setup_cursor(workspace_dir, is_global=is_global),
|
|
461
|
+
"claude": setup_claude(workspace_dir, is_global=is_global),
|
|
462
|
+
"windsurf": setup_windsurf(workspace_dir, is_global=is_global),
|
|
463
|
+
"git": setup_git_hooks(workspace_dir, is_global=is_global),
|
|
464
|
+
"antigravity": setup_antigravity(workspace_dir, is_global=is_global),
|
|
465
|
+
"opencode": setup_opencode(workspace_dir, is_global=is_global),
|
|
387
466
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hyperflash
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.2
|
|
4
4
|
Summary: Universal Engineering Reliability Runtime & Multi-Agent Verification Guardrails
|
|
5
5
|
Author: Virat Ghatge
|
|
6
6
|
License: MIT
|
|
@@ -184,7 +184,12 @@ Scaffold your preferred IDE or agent harness in 1 second:
|
|
|
184
184
|
# Configure Cursor, Claude Code, Windsurf, Git Hooks, Antigravity, and OpenCode in one command
|
|
185
185
|
hyperflash init
|
|
186
186
|
|
|
187
|
-
#
|
|
187
|
+
# Configure globally across ALL workspaces (user home directory):
|
|
188
|
+
# Sets up OpenCode (~/.config/opencode/opencode.json), Claude Code (~/.claude.json), and Antigravity globally
|
|
189
|
+
hyperflash init --global # or: hyperflash init -g
|
|
190
|
+
|
|
191
|
+
# Or target your specific harness globally or locally:
|
|
192
|
+
hyperflash init --target opencode --global
|
|
188
193
|
hyperflash init --target cursor
|
|
189
194
|
hyperflash init --target claude
|
|
190
195
|
hyperflash init --target windsurf
|
|
@@ -193,6 +198,14 @@ hyperflash init --target antigravity
|
|
|
193
198
|
hyperflash init --target opencode
|
|
194
199
|
```
|
|
195
200
|
|
|
201
|
+
> [!TIP]
|
|
202
|
+
> **Global Configuration Mode (`--global` / `-g`):**
|
|
203
|
+
> When running `hyperflash init --global` (or `-g`), HyperFlash configures your user home directory rather than the current working folder. This means:
|
|
204
|
+
> - **OpenCode:** Registers HyperFlash agent persona and MCP server in `~/.config/opencode/opencode.json` so `opencode --agent hyperflash` works anywhere on your system.
|
|
205
|
+
> - **Claude Code:** Registers the MCP server in `~/.claude.json` and slash command in `~/.claude/commands/hyperflash.md`.
|
|
206
|
+
> - **Antigravity:** Installs skills, agents, and rules globally in `~/.gemini/config/`.
|
|
207
|
+
> You only need to run this once—no per-repository initialization required!
|
|
208
|
+
|
|
196
209
|
### 🚀 Universal Agent Launcher
|
|
197
210
|
Launch your preferred coding environment with the native HyperFlash agent persona pre-activated:
|
|
198
211
|
|
|
@@ -352,9 +365,17 @@ OpenCode supports dedicated named agents natively. HyperFlash installs as a firs
|
|
|
352
365
|
|
|
353
366
|
#### Step 1: Automatic Setup via CLI
|
|
354
367
|
```bash
|
|
368
|
+
# Option A: Global setup (recommended) - works across all folders on your system:
|
|
369
|
+
hyperflash init --target opencode --global
|
|
370
|
+
|
|
371
|
+
# Option B: Local workspace setup - configures current repository:
|
|
355
372
|
hyperflash init --target opencode
|
|
356
373
|
```
|
|
357
|
-
|
|
374
|
+
|
|
375
|
+
**Global Setup (`--global`) configures:**
|
|
376
|
+
- `~/.config/opencode/opencode.json`: Declares the global `hyperflash` agent persona and registers the stdio MCP server without overwriting existing agents or servers.
|
|
377
|
+
|
|
378
|
+
**Local Setup configures:**
|
|
358
379
|
- `OPENCODE.md`: OpenCode engineering constitution.
|
|
359
380
|
- `.opencode/agents/hyperflash.json`: Dedicated agent persona definition.
|
|
360
381
|
- `opencode.json`: Declares `agent: "hyperflash"`, registers agent configuration, and wires the `hyperflash` stdio MCP server.
|
|
@@ -30,7 +30,7 @@ def test_cli_version_flag(capsys: pytest.CaptureFixture[str]) -> None:
|
|
|
30
30
|
parser.parse_args(["--version"])
|
|
31
31
|
assert exc_info.value.code == 0
|
|
32
32
|
captured = capsys.readouterr()
|
|
33
|
-
assert "hyperflash 1.1.
|
|
33
|
+
assert "hyperflash 1.1.2" in captured.out
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
def test_cli_decide_updates_contract(tmp_path: Path) -> None:
|
|
@@ -172,6 +172,59 @@ def test_cli_init_all(tmp_path: Path) -> None:
|
|
|
172
172
|
assert (tmp_path / "opencode.json").exists()
|
|
173
173
|
|
|
174
174
|
|
|
175
|
+
def test_cli_init_global_flag_parsing() -> None:
|
|
176
|
+
"""Verifies that -g and --global flags correctly set is_global attribute on parsed args."""
|
|
177
|
+
parser = build_parser()
|
|
178
|
+
args_short = parser.parse_args(["init", "-g"])
|
|
179
|
+
assert args_short.is_global is True
|
|
180
|
+
|
|
181
|
+
args_long = parser.parse_args(["init", "--global"])
|
|
182
|
+
assert args_long.is_global is True
|
|
183
|
+
|
|
184
|
+
args_default = parser.parse_args(["init"])
|
|
185
|
+
assert args_default.is_global is False
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def test_cli_init_opencode_global(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
189
|
+
"""Verifies that 'hyperflash init --target opencode --global' writes to user global config."""
|
|
190
|
+
fake_home = tmp_path / "fake_home"
|
|
191
|
+
fake_home.mkdir()
|
|
192
|
+
monkeypatch.setattr(Path, "home", lambda: fake_home)
|
|
193
|
+
|
|
194
|
+
parser = build_parser()
|
|
195
|
+
args = parser.parse_args(["init", "--target", "opencode", "--global"])
|
|
196
|
+
exit_code = cmd_init(args)
|
|
197
|
+
assert exit_code == 0
|
|
198
|
+
|
|
199
|
+
global_config = fake_home / ".config" / "opencode" / "opencode.json"
|
|
200
|
+
assert global_config.exists()
|
|
201
|
+
data = json.loads(global_config.read_text(encoding="utf-8"))
|
|
202
|
+
assert "agent" in data
|
|
203
|
+
assert "hyperflash" in data["agent"]
|
|
204
|
+
assert data["agent"]["hyperflash"]["name"] == "HyperFlash"
|
|
205
|
+
assert "mcp" in data
|
|
206
|
+
assert "servers" in data["mcp"]
|
|
207
|
+
assert "hyperflash" in data["mcp"]["servers"]
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def test_cli_init_all_global(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
211
|
+
"""Verifies that 'hyperflash init --global' configures harnesses in user home."""
|
|
212
|
+
fake_home = tmp_path / "fake_home"
|
|
213
|
+
fake_home.mkdir()
|
|
214
|
+
monkeypatch.setattr(Path, "home", lambda: fake_home)
|
|
215
|
+
|
|
216
|
+
parser = build_parser()
|
|
217
|
+
args = parser.parse_args(["init", "--global"])
|
|
218
|
+
exit_code = cmd_init(args)
|
|
219
|
+
assert exit_code == 0
|
|
220
|
+
|
|
221
|
+
assert (fake_home / ".config" / "opencode" / "opencode.json").exists()
|
|
222
|
+
assert (fake_home / ".claude.json").exists()
|
|
223
|
+
assert (fake_home / ".claude" / "commands" / "hyperflash.md").exists()
|
|
224
|
+
assert (fake_home / ".cursorrules").exists()
|
|
225
|
+
assert (fake_home / ".windsurfrules").exists()
|
|
226
|
+
|
|
227
|
+
|
|
175
228
|
def test_cli_launch_missing_harness(capsys: pytest.CaptureFixture[str]) -> None:
|
|
176
229
|
"""Verifies that launching a harness not found in PATH returns 1 and friendly error."""
|
|
177
230
|
parser = build_parser()
|
|
@@ -4,6 +4,7 @@ Tests elision detection, symbol graph generation, contract lifecycle, and verifi
|
|
|
4
4
|
"""
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
+
import json
|
|
7
8
|
from pathlib import Path
|
|
8
9
|
|
|
9
10
|
import pytest
|
|
@@ -185,3 +186,73 @@ def test_setup_opencode(tmp_path: Path) -> None:
|
|
|
185
186
|
assert "mcpServers" in content
|
|
186
187
|
assert "hyperflash" in content
|
|
187
188
|
assert '"agent": "hyperflash"' in content
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def test_setup_opencode_global(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
192
|
+
"""Verifies that setup_opencode(is_global=True) writes to ~/.config/opencode/opencode.json."""
|
|
193
|
+
fake_home = tmp_path / "home"
|
|
194
|
+
fake_home.mkdir()
|
|
195
|
+
monkeypatch.setattr(Path, "home", lambda: fake_home)
|
|
196
|
+
|
|
197
|
+
created = setup_opencode(is_global=True)
|
|
198
|
+
assert len(created) == 1
|
|
199
|
+
|
|
200
|
+
expected_path = fake_home / ".config" / "opencode" / "opencode.json"
|
|
201
|
+
assert str(expected_path) == created[0]
|
|
202
|
+
assert expected_path.exists()
|
|
203
|
+
|
|
204
|
+
data = json.loads(expected_path.read_text(encoding="utf-8"))
|
|
205
|
+
assert "agent" in data
|
|
206
|
+
assert "hyperflash" in data["agent"]
|
|
207
|
+
hf_agent = data["agent"]["hyperflash"]
|
|
208
|
+
assert hf_agent["name"] == "HyperFlash"
|
|
209
|
+
assert "Universal Engineering Reliability" in hf_agent["description"]
|
|
210
|
+
assert "8 HyperFlash Invariants" in hf_agent["system"]
|
|
211
|
+
|
|
212
|
+
assert "mcp" in data
|
|
213
|
+
assert "servers" in data["mcp"]
|
|
214
|
+
assert "hyperflash" in data["mcp"]["servers"]
|
|
215
|
+
hf_server = data["mcp"]["servers"]["hyperflash"]
|
|
216
|
+
assert hf_server["type"] == "local"
|
|
217
|
+
assert hf_server["command"] == ["hyperflash", "mcp"]
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def test_setup_opencode_global_merges_without_overwriting(
|
|
221
|
+
tmp_path: Path,
|
|
222
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
223
|
+
) -> None:
|
|
224
|
+
"""Verifies that global config setup safely merges without deleting existing agents or servers."""
|
|
225
|
+
fake_home = tmp_path / "home"
|
|
226
|
+
config_dir = fake_home / ".config" / "opencode"
|
|
227
|
+
config_dir.mkdir(parents=True)
|
|
228
|
+
target_file = config_dir / "opencode.json"
|
|
229
|
+
|
|
230
|
+
initial_config = {
|
|
231
|
+
"agent": {
|
|
232
|
+
"custom_coder": {
|
|
233
|
+
"name": "Custom Coder",
|
|
234
|
+
"system": "You write code."
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
"mcp": {
|
|
238
|
+
"servers": {
|
|
239
|
+
"git_mcp": {
|
|
240
|
+
"type": "local",
|
|
241
|
+
"command": ["git-mcp"]
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
"custom_setting": True
|
|
246
|
+
}
|
|
247
|
+
target_file.write_text(json.dumps(initial_config), encoding="utf-8")
|
|
248
|
+
monkeypatch.setattr(Path, "home", lambda: fake_home)
|
|
249
|
+
|
|
250
|
+
created = setup_opencode(is_global=True)
|
|
251
|
+
assert len(created) == 1
|
|
252
|
+
|
|
253
|
+
data = json.loads(target_file.read_text(encoding="utf-8"))
|
|
254
|
+
assert "custom_coder" in data["agent"]
|
|
255
|
+
assert "hyperflash" in data["agent"]
|
|
256
|
+
assert "git_mcp" in data["mcp"]["servers"]
|
|
257
|
+
assert "hyperflash" in data["mcp"]["servers"]
|
|
258
|
+
assert data["custom_setting"] is True
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|