@softspark/ai-toolkit 2.4.1 → 2.6.1
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/AGENTS.md +33 -20
- package/CHANGELOG.md +57 -0
- package/README.md +29 -13
- package/app/.claude-plugin/plugin.json +3 -2
- package/app/ARCHITECTURE.md +11 -0
- package/app/agents/code-reviewer.md +6 -7
- package/app/agents/frontend-specialist.md +33 -2
- package/app/agents/seo-specialist.md +1 -1
- package/app/personas/frontend-lead.md +48 -5
- package/app/skills/a11y-validate/SKILL.md +377 -0
- package/app/skills/a11y-validate/reference/aria-patterns.md +259 -0
- package/app/skills/a11y-validate/reference/eaa-compliance.md +252 -0
- package/app/skills/a11y-validate/reference/mobile-eaa.md +329 -0
- package/app/skills/a11y-validate/reference/wcag-2-1-aa.md +285 -0
- package/app/skills/a11y-validate/reference/wcag-2-2-aa.md +221 -0
- package/app/skills/a11y-validate/scripts/a11y-scanner.py +639 -0
- package/app/skills/clean-code/reference/python.md +3 -3
- package/app/skills/design-engineering/SKILL.md +2 -5
- package/app/skills/hipaa-validate/SKILL.md +39 -23
- package/app/skills/hipaa-validate/scripts/hipaa_scan.py +64 -7
- package/app/skills/review/SKILL.md +30 -6
- package/app/skills/seo-validate/SKILL.md +460 -0
- package/app/skills/seo-validate/reference/core-web-vitals.md +445 -0
- package/app/skills/seo-validate/reference/geo-aeo-patterns.md +259 -0
- package/app/skills/seo-validate/reference/geo-guidelines.md +248 -0
- package/app/skills/seo-validate/reference/schema-types.md +465 -0
- package/app/skills/seo-validate/reference/spa-ssg-patterns.md +351 -0
- package/app/skills/seo-validate/reference/w3c-guidelines.md +289 -0
- package/app/skills/seo-validate/scripts/seo-scanner.py +549 -0
- package/bin/ai-toolkit.js +24 -9
- package/kb/reference/architecture-overview.md +1 -1
- package/kb/reference/comparison.md +1 -1
- package/kb/reference/opencode-compatibility.md +161 -0
- package/kb/reference/skills-catalog.md +3 -1
- package/llms-full.txt +177 -6
- package/llms.txt +1 -0
- package/manifest.json +3 -3
- package/package.json +6 -3
- package/scripts/config_cli.py +4 -10
- package/scripts/doctor.py +3 -3
- package/scripts/generate_opencode.py +117 -0
- package/scripts/generate_opencode_agents.py +126 -0
- package/scripts/generate_opencode_commands.py +158 -0
- package/scripts/generate_opencode_json.py +133 -0
- package/scripts/generate_opencode_plugin.py +169 -0
- package/scripts/install_steps/ai_tools.py +117 -1
- package/scripts/install_steps/install_state.py +1 -1
- package/scripts/plugin.py +1 -1
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Generate the ai-toolkit opencode plugin at ``.opencode/plugins/ai-toolkit-hooks.js``.
|
|
3
|
+
|
|
4
|
+
opencode plugins are JS/TS modules with a NAMED export that receive a
|
|
5
|
+
context (``$``, ``project``, ``client``, ``directory``, ``worktree``) and
|
|
6
|
+
return a hooks object. Docs: https://opencode.ai/docs/plugins/
|
|
7
|
+
|
|
8
|
+
This generator emits a single-file plugin that bridges ai-toolkit's
|
|
9
|
+
shared Bash hooks (``$HOME/.softspark/ai-toolkit/hooks/*.sh``) to
|
|
10
|
+
opencode lifecycle events. Coverage map:
|
|
11
|
+
|
|
12
|
+
opencode event -> ai-toolkit Bash hook(s)
|
|
13
|
+
---------------------------------------------------------------
|
|
14
|
+
session.created -> session-start.sh + session-context.sh + mcp-health.sh
|
|
15
|
+
session.compacted -> pre-compact.sh + pre-compact-save.sh
|
|
16
|
+
session.deleted -> session-end.sh + save-session.sh
|
|
17
|
+
message.updated -> user-prompt-submit.sh + track-usage.sh
|
|
18
|
+
message.part.updated -> user-prompt-submit.sh + track-usage.sh
|
|
19
|
+
tool.execute.before (bash) -> guard-destructive.sh + commit-quality.sh
|
|
20
|
+
tool.execute.after -> post-tool-use.sh
|
|
21
|
+
permission.asked -> guard-destructive.sh
|
|
22
|
+
command.executed -> post-tool-use.sh
|
|
23
|
+
|
|
24
|
+
Security: hooks are invoked via ``$`` with the script path bound to a
|
|
25
|
+
JS constant (no opencode-payload interpolation into the command). Event
|
|
26
|
+
payloads are passed on stdin as JSON. Non-zero exit codes are logged to
|
|
27
|
+
stderr; ``exit 2`` is preserved as a block signal for PreToolUse guards.
|
|
28
|
+
|
|
29
|
+
Usage:
|
|
30
|
+
python3 scripts/generate_opencode_plugin.py [target-dir] [--config-root PATH]
|
|
31
|
+
"""
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
import argparse
|
|
35
|
+
import sys
|
|
36
|
+
from pathlib import Path
|
|
37
|
+
|
|
38
|
+
PLUGIN_BODY = r"""// ai-toolkit opencode plugin — bridges shared Bash hooks to opencode events.
|
|
39
|
+
// Auto-generated by ai-toolkit. Do not edit by hand; re-run to update:
|
|
40
|
+
// ai-toolkit opencode-plugin
|
|
41
|
+
// Docs: https://opencode.ai/docs/plugins/
|
|
42
|
+
//
|
|
43
|
+
// Hooks live at $HOME/.softspark/ai-toolkit/hooks/*.sh and are shared with
|
|
44
|
+
// Claude Code and Codex CLI. This plugin invokes them via Bun's `$` shell
|
|
45
|
+
// with the script path bound as a JS constant — no string interpolation of
|
|
46
|
+
// opencode event payloads into the shell, so event data cannot inject
|
|
47
|
+
// shell metacharacters. Payloads are passed on stdin as JSON.
|
|
48
|
+
|
|
49
|
+
const HOOKS_DIR = `${process.env.HOME}/.softspark/ai-toolkit/hooks`;
|
|
50
|
+
|
|
51
|
+
/** Invoke a Bash hook with a JSON payload on stdin. */
|
|
52
|
+
async function runHook($, script, payload) {
|
|
53
|
+
const scriptPath = `${HOOKS_DIR}/${script}`;
|
|
54
|
+
try {
|
|
55
|
+
const input = JSON.stringify(payload ?? {});
|
|
56
|
+
const proc = $`bash ${scriptPath}`.env({
|
|
57
|
+
...process.env,
|
|
58
|
+
AI_TOOLKIT_EVENT: payload?.event || "unknown",
|
|
59
|
+
});
|
|
60
|
+
proc.stdin.write(input);
|
|
61
|
+
proc.stdin.end();
|
|
62
|
+
const result = await proc.quiet().nothrow();
|
|
63
|
+
if (result.exitCode !== 0 && result.exitCode !== 2) {
|
|
64
|
+
// Exit 2 is the toolkit's "block" signal — pass through to opencode as a guard.
|
|
65
|
+
process.stderr.write(
|
|
66
|
+
`[ai-toolkit] ${script} exited ${result.exitCode}\n${result.stderr.toString()}`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
return result.exitCode;
|
|
70
|
+
} catch (err) {
|
|
71
|
+
process.stderr.write(`[ai-toolkit] failed to run ${script}: ${err.message}\n`);
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const AiToolkitHooks = async ({ $, project, directory, worktree }) => ({
|
|
77
|
+
event: async ({ event }) => {
|
|
78
|
+
const payload = { event: event.type, project, directory, worktree };
|
|
79
|
+
switch (event.type) {
|
|
80
|
+
case "session.created":
|
|
81
|
+
await runHook($, "session-start.sh", payload);
|
|
82
|
+
await runHook($, "session-context.sh", payload);
|
|
83
|
+
await runHook($, "mcp-health.sh", payload);
|
|
84
|
+
break;
|
|
85
|
+
case "session.compacted":
|
|
86
|
+
await runHook($, "pre-compact.sh", payload);
|
|
87
|
+
await runHook($, "pre-compact-save.sh", payload);
|
|
88
|
+
break;
|
|
89
|
+
case "session.deleted":
|
|
90
|
+
await runHook($, "session-end.sh", payload);
|
|
91
|
+
await runHook($, "save-session.sh", payload);
|
|
92
|
+
break;
|
|
93
|
+
case "message.updated":
|
|
94
|
+
case "message.part.updated":
|
|
95
|
+
await runHook($, "user-prompt-submit.sh", payload);
|
|
96
|
+
await runHook($, "track-usage.sh", payload);
|
|
97
|
+
break;
|
|
98
|
+
case "permission.asked":
|
|
99
|
+
await runHook($, "guard-destructive.sh", payload);
|
|
100
|
+
break;
|
|
101
|
+
case "command.executed":
|
|
102
|
+
await runHook($, "post-tool-use.sh", payload);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
"tool.execute.before": async (input, output) => {
|
|
108
|
+
const payload = {
|
|
109
|
+
event: "tool.execute.before",
|
|
110
|
+
tool: input?.tool,
|
|
111
|
+
args: output?.args,
|
|
112
|
+
project,
|
|
113
|
+
};
|
|
114
|
+
if (input?.tool === "bash") {
|
|
115
|
+
await runHook($, "guard-destructive.sh", payload);
|
|
116
|
+
await runHook($, "commit-quality.sh", payload);
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
"tool.execute.after": async (input, output) => {
|
|
121
|
+
const payload = {
|
|
122
|
+
event: "tool.execute.after",
|
|
123
|
+
tool: input?.tool,
|
|
124
|
+
result: output,
|
|
125
|
+
project,
|
|
126
|
+
};
|
|
127
|
+
await runHook($, "post-tool-use.sh", payload);
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def generate(target_dir: Path, config_root: Path | None = None) -> Path:
|
|
134
|
+
"""Write the opencode plugin file and return its path.
|
|
135
|
+
|
|
136
|
+
``config_root`` lets the caller override the default project-local
|
|
137
|
+
layout. When omitted, writes to ``target_dir/.opencode/plugins/``.
|
|
138
|
+
Pass ``config_root=~/.config/opencode`` to lay down the plugin
|
|
139
|
+
globally at ``~/.config/opencode/plugins/ai-toolkit-hooks.js``.
|
|
140
|
+
"""
|
|
141
|
+
base = config_root if config_root is not None else target_dir / ".opencode"
|
|
142
|
+
plugins_dir = base / "plugins"
|
|
143
|
+
plugins_dir.mkdir(parents=True, exist_ok=True)
|
|
144
|
+
path = plugins_dir / "ai-toolkit-hooks.js"
|
|
145
|
+
path.write_text(PLUGIN_BODY, encoding="utf-8")
|
|
146
|
+
return path
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def main() -> None:
|
|
150
|
+
parser = argparse.ArgumentParser(description="Generate opencode plugin")
|
|
151
|
+
parser.add_argument("target", nargs="?", default=".", help="Target directory")
|
|
152
|
+
parser.add_argument(
|
|
153
|
+
"--config-root",
|
|
154
|
+
type=Path,
|
|
155
|
+
default=None,
|
|
156
|
+
help="Override base (e.g. ~/.config/opencode for global install).",
|
|
157
|
+
)
|
|
158
|
+
args = parser.parse_args()
|
|
159
|
+
target = Path(args.target)
|
|
160
|
+
path = generate(target, config_root=args.config_root)
|
|
161
|
+
try:
|
|
162
|
+
rel = path.relative_to(target)
|
|
163
|
+
except ValueError:
|
|
164
|
+
rel = path
|
|
165
|
+
print(f"Generated: {rel}")
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
if __name__ == "__main__":
|
|
169
|
+
main()
|
|
@@ -92,6 +92,14 @@ def install_ai_tools(target_dir: Path, rules_dir: Path,
|
|
|
92
92
|
_install_codex_global(target_dir, rules_dir)
|
|
93
93
|
installed.append("codex")
|
|
94
94
|
|
|
95
|
+
if "opencode" in eds:
|
|
96
|
+
if dry_run:
|
|
97
|
+
print(" Would inject: ~/.config/opencode/{AGENTS.md, agents/, "
|
|
98
|
+
"commands/, plugins/ai-toolkit-hooks.js, opencode.json}")
|
|
99
|
+
else:
|
|
100
|
+
_install_opencode_global(target_dir, rules_dir)
|
|
101
|
+
installed.append("opencode")
|
|
102
|
+
|
|
95
103
|
print()
|
|
96
104
|
print(f" Available: {', '.join(GLOBAL_CAPABLE_EDITORS)}")
|
|
97
105
|
print(" Note: Copilot, Cline, Roo Code, Aider, Antigravity have no global config -- use 'ai-toolkit install --local' per project")
|
|
@@ -128,6 +136,51 @@ def _install_codex_global(target_dir: Path, rules_dir: Path) -> None:
|
|
|
128
136
|
_install_codex_skills(target_dir)
|
|
129
137
|
|
|
130
138
|
|
|
139
|
+
def _install_opencode_global(target_dir: Path, rules_dir: Path) -> None:
|
|
140
|
+
"""Install opencode at the global level (~/.config/opencode/).
|
|
141
|
+
|
|
142
|
+
Creates:
|
|
143
|
+
- ~/.config/opencode/AGENTS.md (marker injection with rules)
|
|
144
|
+
- ~/.config/opencode/agents/ai-toolkit-*.md (subagents)
|
|
145
|
+
- ~/.config/opencode/commands/ai-toolkit-*.md (slash commands)
|
|
146
|
+
- ~/.config/opencode/plugins/ai-toolkit-hooks.js (hook bridge)
|
|
147
|
+
- ~/.config/opencode/opencode.json (MCP merge, preserves user keys)
|
|
148
|
+
"""
|
|
149
|
+
opencode_home = target_dir / ".config" / "opencode"
|
|
150
|
+
inject_with_rules(
|
|
151
|
+
"generate_opencode.py",
|
|
152
|
+
opencode_home / "AGENTS.md",
|
|
153
|
+
rules_dir,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
from generate_opencode_agents import generate as gen_opencode_agents
|
|
157
|
+
written, removed = gen_opencode_agents(target_dir, config_root=opencode_home)
|
|
158
|
+
msg = f" Created: ~/.config/opencode/agents/ ({written} agents"
|
|
159
|
+
if removed:
|
|
160
|
+
msg += f", {removed} stale removed"
|
|
161
|
+
msg += ")"
|
|
162
|
+
print(msg)
|
|
163
|
+
|
|
164
|
+
from generate_opencode_commands import generate as gen_opencode_commands
|
|
165
|
+
written, removed = gen_opencode_commands(target_dir, config_root=opencode_home)
|
|
166
|
+
msg = f" Created: ~/.config/opencode/commands/ ({written} commands"
|
|
167
|
+
if removed:
|
|
168
|
+
msg += f", {removed} stale removed"
|
|
169
|
+
msg += ")"
|
|
170
|
+
print(msg)
|
|
171
|
+
|
|
172
|
+
from generate_opencode_plugin import generate as gen_opencode_plugin
|
|
173
|
+
gen_opencode_plugin(target_dir, config_root=opencode_home)
|
|
174
|
+
print(" Created: ~/.config/opencode/plugins/ai-toolkit-hooks.js")
|
|
175
|
+
|
|
176
|
+
from generate_opencode_json import merge_into_opencode_json
|
|
177
|
+
_, count = merge_into_opencode_json(
|
|
178
|
+
target_dir, output_path=opencode_home / "opencode.json"
|
|
179
|
+
)
|
|
180
|
+
suffix = f" ({count} MCP server(s) merged)" if count else " (no MCP servers)"
|
|
181
|
+
print(f" Created: ~/.config/opencode/opencode.json{suffix}")
|
|
182
|
+
|
|
183
|
+
|
|
131
184
|
def inject_with_rules(
|
|
132
185
|
generator_script: str,
|
|
133
186
|
target_file: Path,
|
|
@@ -194,7 +247,7 @@ def run_script(script_name: str, *args: str, capture: bool = False) -> str:
|
|
|
194
247
|
# All known editor identifiers for --editors flag
|
|
195
248
|
ALL_EDITORS = [
|
|
196
249
|
"copilot", "cursor", "windsurf", "cline", "roo",
|
|
197
|
-
"aider", "augment", "antigravity", "codex",
|
|
250
|
+
"aider", "augment", "antigravity", "codex", "gemini", "opencode",
|
|
198
251
|
]
|
|
199
252
|
|
|
200
253
|
# Map of project files/dirs → editor names for auto-detection
|
|
@@ -213,7 +266,15 @@ _EDITOR_MARKERS: dict[str, str] = {
|
|
|
213
266
|
".agent/rules": "antigravity",
|
|
214
267
|
".agents/skills": "codex",
|
|
215
268
|
".codex": "codex",
|
|
269
|
+
# NOTE: AGENTS.md alone is ambiguous (Codex + opencode both read it);
|
|
270
|
+
# prefer the dedicated .opencode/ and opencode.json markers when
|
|
271
|
+
# disambiguating. If only AGENTS.md is present, Codex takes precedence
|
|
272
|
+
# to preserve v2.4.x behavior.
|
|
216
273
|
"AGENTS.md": "codex",
|
|
274
|
+
"opencode.json": "opencode",
|
|
275
|
+
".opencode": "opencode",
|
|
276
|
+
".opencode/agents": "opencode",
|
|
277
|
+
".opencode/commands": "opencode",
|
|
217
278
|
}
|
|
218
279
|
|
|
219
280
|
|
|
@@ -485,6 +546,7 @@ def _install_local_dry_run(reset: bool, editors: list[str] | None = None) -> Non
|
|
|
485
546
|
"aider": " Would generate: .aider.conf.yml + CONVENTIONS.md",
|
|
486
547
|
"augment": " Would generate: .augment/rules/ai-toolkit-*.md",
|
|
487
548
|
"antigravity": " Would generate: .agent/rules/ + .agent/workflows/",
|
|
549
|
+
"opencode": " Would generate: AGENTS.md + .opencode/{agents,commands,plugins}/ + opencode.json",
|
|
488
550
|
}
|
|
489
551
|
for ed, msg in _EDITOR_DRY_RUN.items():
|
|
490
552
|
if ed in eds:
|
|
@@ -511,6 +573,24 @@ def _reset_local_configs(cwd: Path) -> None:
|
|
|
511
573
|
p.unlink()
|
|
512
574
|
print(f" Removed: {rel}")
|
|
513
575
|
|
|
576
|
+
# opencode: remove only ai-toolkit-prefixed generated files so the
|
|
577
|
+
# user's own .opencode/agents/ or commands/ entries are preserved.
|
|
578
|
+
# opencode.json is left alone — it may contain user MCP servers and other
|
|
579
|
+
# project settings; reinstall re-merges our MCP entries idempotently.
|
|
580
|
+
opencode_plugin = cwd / ".opencode" / "plugins" / "ai-toolkit-hooks.js"
|
|
581
|
+
if opencode_plugin.is_file():
|
|
582
|
+
opencode_plugin.unlink()
|
|
583
|
+
print(" Removed: .opencode/plugins/ai-toolkit-hooks.js")
|
|
584
|
+
for sub in ("agents", "commands"):
|
|
585
|
+
sub_dir = cwd / ".opencode" / sub
|
|
586
|
+
if sub_dir.is_dir():
|
|
587
|
+
removed_any = False
|
|
588
|
+
for f in sorted(sub_dir.glob("ai-toolkit-*.md")):
|
|
589
|
+
f.unlink()
|
|
590
|
+
removed_any = True
|
|
591
|
+
if removed_any:
|
|
592
|
+
print(f" Removed: .opencode/{sub}/ai-toolkit-*.md")
|
|
593
|
+
|
|
514
594
|
|
|
515
595
|
def _create_local_claude_md(cwd: Path, reset: bool) -> None:
|
|
516
596
|
claude_local = cwd / "CLAUDE.md"
|
|
@@ -699,6 +779,42 @@ def _create_local_ai_tool_configs(cwd: Path, rules_dir: Path,
|
|
|
699
779
|
# .agents/skills/ — filtered symlinks (Codex-compatible skills only)
|
|
700
780
|
_install_codex_skills(cwd)
|
|
701
781
|
|
|
782
|
+
if "opencode" in eds:
|
|
783
|
+
# AGENTS.md — shared with Codex via marker injection (opencode reads same file)
|
|
784
|
+
# Use a dedicated section tag so Codex and opencode don't clobber each other.
|
|
785
|
+
inject_with_rules(
|
|
786
|
+
"generate_opencode.py",
|
|
787
|
+
cwd / "AGENTS.md",
|
|
788
|
+
rules_dir,
|
|
789
|
+
)
|
|
790
|
+
# .opencode/agents/ — native subagents
|
|
791
|
+
from generate_opencode_agents import generate as gen_opencode_agents
|
|
792
|
+
written, removed = gen_opencode_agents(cwd)
|
|
793
|
+
msg = f" Created: .opencode/agents/ ({written} agents"
|
|
794
|
+
if removed:
|
|
795
|
+
msg += f", {removed} stale removed"
|
|
796
|
+
msg += ")"
|
|
797
|
+
print(msg)
|
|
798
|
+
# .opencode/commands/ — native slash commands
|
|
799
|
+
from generate_opencode_commands import generate as gen_opencode_commands
|
|
800
|
+
written, removed = gen_opencode_commands(cwd)
|
|
801
|
+
msg = f" Created: .opencode/commands/ ({written} commands"
|
|
802
|
+
if removed:
|
|
803
|
+
msg += f", {removed} stale removed"
|
|
804
|
+
msg += ")"
|
|
805
|
+
print(msg)
|
|
806
|
+
# .opencode/plugins/ai-toolkit-hooks.js — lifecycle hook bridge
|
|
807
|
+
from generate_opencode_plugin import generate as gen_opencode_plugin
|
|
808
|
+
gen_opencode_plugin(cwd)
|
|
809
|
+
print(" Created: .opencode/plugins/ai-toolkit-hooks.js")
|
|
810
|
+
# opencode.json — merge MCP servers from .mcp.json (preserves user keys)
|
|
811
|
+
from generate_opencode_json import merge_into_opencode_json
|
|
812
|
+
_, mcp_count = merge_into_opencode_json(cwd)
|
|
813
|
+
if mcp_count:
|
|
814
|
+
print(f" Updated: opencode.json ({mcp_count} MCP server(s) from .mcp.json)")
|
|
815
|
+
else:
|
|
816
|
+
print(" Updated: opencode.json ($schema set)")
|
|
817
|
+
|
|
702
818
|
synced_paths = sync_project_mcp_to_editors(cwd, sorted(eds))
|
|
703
819
|
for path in synced_paths:
|
|
704
820
|
rel = path.relative_to(cwd)
|
|
@@ -89,7 +89,7 @@ def remove_mcp_template(name: str) -> None:
|
|
|
89
89
|
DEFAULT_GLOBAL_EDITORS: list[str] = []
|
|
90
90
|
|
|
91
91
|
# All editors that support global install (opt-in via --editors)
|
|
92
|
-
GLOBAL_CAPABLE_EDITORS = ["augment", "codex", "cursor", "gemini", "windsurf"]
|
|
92
|
+
GLOBAL_CAPABLE_EDITORS = ["augment", "codex", "cursor", "gemini", "opencode", "windsurf"]
|
|
93
93
|
|
|
94
94
|
|
|
95
95
|
def get_global_editors() -> list[str]:
|
package/scripts/plugin.py
CHANGED
|
@@ -912,7 +912,7 @@ def cmd_status(editors: list[str]) -> None:
|
|
|
912
912
|
print(f" Hooks: {', '.join(h.name for h in hooks)}")
|
|
913
913
|
elif editor == "codex":
|
|
914
914
|
rules_dir = CODEX_ROOT / ".agents" / "rules"
|
|
915
|
-
rule_files = sorted(rules_dir.glob(f"
|
|
915
|
+
rule_files = sorted(rules_dir.glob(f"plugin-{name}-*.md")) if rules_dir.is_dir() else []
|
|
916
916
|
if rule_files:
|
|
917
917
|
print(f" Rules: {', '.join(f.name for f in rule_files)}")
|
|
918
918
|
if name == "memory-pack":
|