@abarcenas/ai-workflow-template 1.23.0 → 1.26.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.
- package/.agents/instructions/agent-skill.instructions.md +18 -20
- package/.agents/instructions/agent.instructions.md +111 -570
- package/.agents/instructions/ai-prompt-engineering-safety.instructions.md +3 -3
- package/.agents/instructions/instructions.instructions.md +6 -7
- package/.agents/instructions/learned-knowledge.instructions.md +17 -41
- package/.agents/instructions/memory-bank.instructions.md +125 -17
- package/.agents/instructions/memory-schema.instructions.md +75 -0
- package/.agents/instructions/prompt.instructions.md +25 -30
- package/.agents/instructions/task-implementation.instructions.md +12 -5
- package/.agents/skills/architecture-blueprint-generator/SKILL.md +63 -18
- package/.agents/skills/context-map/SKILL.md +27 -4
- package/.agents/skills/conventional-commit/SKILL.md +2 -2
- package/.agents/skills/github-issues/SKILL.md +2 -2
- package/.agents/skills/graphify-framework-aware/SKILL.md +133 -0
- package/.agents/skills/graphify-framework-aware/references/framework-registry.md +95 -0
- package/.agents/skills/graphify-framework-aware/scripts/fw-exclude.py +425 -0
- package/.agents/skills/memory-search/SKILL.md +69 -0
- package/.agents/skills/microsoft-docs/SKILL.md +1 -1
- package/.agents/skills/polygot-test-agent/SKILL.md +0 -1
- package/.agents/skills/prompt-builder/SKILL.md +15 -14
- package/.husky/post-merge +10 -0
- package/.husky/pre-commit +2 -0
- package/AGENTS.md +54 -0
- package/README.md +177 -3
- package/memory-bank/.vocabulary.json +52 -0
- package/opencode.mcp.example.json +8 -1
- package/package.json +25 -5
- package/scripts/mcp-memory-server.js +243 -0
- package/scripts/memory-cli.js +110 -0
- package/scripts/memory-index.js +512 -0
- package/scripts/normalize-memory.js +349 -0
- package/scripts/sync.test.js +12 -0
- package/scripts/validate-memory-schema.js +190 -0
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""graphify-framework-aware: detect project frameworks and generate .graphifyignore patterns.
|
|
3
|
+
|
|
4
|
+
Commands:
|
|
5
|
+
detect <path> Print JSON of detected frameworks and their markers.
|
|
6
|
+
apply <path> Idempotently write/update .graphifyignore with FW patterns.
|
|
7
|
+
show <path> Print FW patterns only (dry-run, no files written).
|
|
8
|
+
|
|
9
|
+
Exit codes: 0 = success, 1 = error (bad path, invalid args), 2 = nothing to apply.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import os
|
|
16
|
+
import re
|
|
17
|
+
import subprocess
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
# ── Framework Registry ────────────────────────────────────────────────────────
|
|
22
|
+
# Adding a new framework:
|
|
23
|
+
# 1. Add detection marker(s) to FRAMEWORK_MARKERS.
|
|
24
|
+
# 2. Add exclude patterns to FRAMEWORK_EXCLUDES.
|
|
25
|
+
# Complex detection (Hugo theme) goes in _post_process_excludes().
|
|
26
|
+
|
|
27
|
+
FRAMEWORK_MARKERS: dict[str, list[str]] = {
|
|
28
|
+
"angular": ["angular.json"],
|
|
29
|
+
"nextjs": ["next.config.js", "next.config.ts", "next.config.mjs"],
|
|
30
|
+
"svelte": ["svelte.config.js"],
|
|
31
|
+
"hugo": ["hugo.toml", "hugo.yaml", "hugo.json"],
|
|
32
|
+
"jekyll": ["_config.yml"],
|
|
33
|
+
"docusaurus": ["docusaurus.config.js", "docusaurus.config.ts"],
|
|
34
|
+
"flutter": ["pubspec.yaml"],
|
|
35
|
+
"wordpress": ["wp-config.php"],
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
FRAMEWORK_EXCLUDES: dict[str, list[str]] = {
|
|
39
|
+
"angular": [
|
|
40
|
+
"**/*.spec.ts",
|
|
41
|
+
"**/*.harness.ts",
|
|
42
|
+
"e2e/",
|
|
43
|
+
"src/environments/",
|
|
44
|
+
"karma.conf.js",
|
|
45
|
+
"src/test.ts",
|
|
46
|
+
],
|
|
47
|
+
"nextjs": [
|
|
48
|
+
"next-env.d.ts",
|
|
49
|
+
"out/",
|
|
50
|
+
],
|
|
51
|
+
"svelte": [
|
|
52
|
+
"build/",
|
|
53
|
+
"**/*.test.ts",
|
|
54
|
+
],
|
|
55
|
+
"hugo": [],
|
|
56
|
+
"jekyll": [
|
|
57
|
+
"_site/",
|
|
58
|
+
".jekyll-cache/",
|
|
59
|
+
".jekyll-metadata",
|
|
60
|
+
],
|
|
61
|
+
"docusaurus": [
|
|
62
|
+
".docusaurus/",
|
|
63
|
+
"build/",
|
|
64
|
+
],
|
|
65
|
+
"flutter": [
|
|
66
|
+
".dart_tool/",
|
|
67
|
+
"build/",
|
|
68
|
+
".flutter-plugins",
|
|
69
|
+
".flutter-plugins-dependencies",
|
|
70
|
+
],
|
|
71
|
+
"wordpress": [
|
|
72
|
+
"wp-admin/",
|
|
73
|
+
"wp-includes/",
|
|
74
|
+
"wp-content/languages/",
|
|
75
|
+
"wp-content/upgrade/",
|
|
76
|
+
],
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# Marker section boundaries in .graphifyignore
|
|
80
|
+
_MARKER_START = "# ===== graphify-framework-aware (auto-generated) ====="
|
|
81
|
+
_MARKER_END = "# ===== end graphify-framework-aware ====="
|
|
82
|
+
|
|
83
|
+
# Directories graphify already skips natively — don't duplicate them.
|
|
84
|
+
_GRAPHIFY_BUILTIN_SKIP = frozenset({
|
|
85
|
+
"node_modules", "__pycache__", ".git", "dist", "build", "target", "out",
|
|
86
|
+
"site-packages", "lib64", ".pytest_cache", ".mypy_cache", ".ruff_cache",
|
|
87
|
+
".tox", ".eggs", "*.egg-info", "graphify-out",
|
|
88
|
+
"coverage", "lcov-report", "visual-tests", "visual-test",
|
|
89
|
+
"__snapshots__", "snapshots", "storybook-static", "dist-protected",
|
|
90
|
+
".next", ".nuxt", ".turbo", ".angular",
|
|
91
|
+
".idea", ".cache", ".parcel-cache", ".svelte-kit", ".terraform",
|
|
92
|
+
".serverless", ".graphify", ".worktrees",
|
|
93
|
+
"venv", ".venv", "env", ".env",
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
# Lock files graphify already skips.
|
|
97
|
+
_GRAPHIFY_BUILTIN_SKIP_FILES = frozenset({
|
|
98
|
+
"package-lock.json", "yarn.lock", "pnpm-lock.yaml",
|
|
99
|
+
"Cargo.lock", "poetry.lock", "Gemfile.lock",
|
|
100
|
+
"composer.lock", "go.sum", "go.work.sum",
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# ── Detection ─────────────────────────────────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
def _glob_marker(root: Path, pattern: str) -> bool:
|
|
107
|
+
"""Check if a glob pattern matches any file in root (non-recursive by default)."""
|
|
108
|
+
matches = list(root.glob(pattern))
|
|
109
|
+
if matches:
|
|
110
|
+
return any(m.is_file() for m in matches)
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def detect_frameworks(root: Path) -> dict:
|
|
115
|
+
"""Scan project root for framework marker files.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
{"frameworks": ["angular", "nextjs"], "markers": {"angular": ["angular.json"], ...}}
|
|
119
|
+
"""
|
|
120
|
+
root = root.resolve()
|
|
121
|
+
if not root.is_dir():
|
|
122
|
+
print(f"error: not a directory: {root}", file=sys.stderr)
|
|
123
|
+
sys.exit(1)
|
|
124
|
+
|
|
125
|
+
found: dict[str, list[str]] = {}
|
|
126
|
+
for fw_name, markers in FRAMEWORK_MARKERS.items():
|
|
127
|
+
matched = [m for m in markers if _glob_marker(root, m)]
|
|
128
|
+
if matched:
|
|
129
|
+
found[fw_name] = matched
|
|
130
|
+
|
|
131
|
+
# Flutter: pubspec.yaml is generic — verify it's a Flutter project
|
|
132
|
+
if "flutter" in found:
|
|
133
|
+
pubspec = root / "pubspec.yaml"
|
|
134
|
+
try:
|
|
135
|
+
content = pubspec.read_text(encoding="utf-8")
|
|
136
|
+
if "flutter" not in content.lower():
|
|
137
|
+
del found["flutter"]
|
|
138
|
+
except OSError:
|
|
139
|
+
del found["flutter"]
|
|
140
|
+
|
|
141
|
+
# Hugo: config.toml needs content check if not hugo.toml
|
|
142
|
+
# (config.toml is too generic — many non-Hugo projects use it)
|
|
143
|
+
if "hugo" in found:
|
|
144
|
+
hugo_specific = any("hugo" in m.lower() for m in found["hugo"])
|
|
145
|
+
if not hugo_specific:
|
|
146
|
+
# Only matched generic config.toml — verify it's actually Hugo
|
|
147
|
+
config_file = next((root / m for m in found["hugo"] if (root / m).exists()), None)
|
|
148
|
+
if config_file:
|
|
149
|
+
try:
|
|
150
|
+
content = config_file.read_text(encoding="utf-8")
|
|
151
|
+
if "baseURL" not in content and "title" not in content:
|
|
152
|
+
del found["hugo"]
|
|
153
|
+
except OSError:
|
|
154
|
+
del found["hugo"]
|
|
155
|
+
else:
|
|
156
|
+
del found["hugo"]
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
"frameworks": sorted(found.keys()),
|
|
160
|
+
"markers": found,
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
# ── Exclude pattern generation ────────────────────────────────────────────────
|
|
165
|
+
|
|
166
|
+
def _hugo_theme_is_vendored(root: Path) -> bool:
|
|
167
|
+
"""Check if Hugo themes/ directory contains git submodules or cloned themes."""
|
|
168
|
+
themes_dir = root / "themes"
|
|
169
|
+
if not themes_dir.is_dir():
|
|
170
|
+
return False
|
|
171
|
+
|
|
172
|
+
# Check for .gitmodules referencing themes
|
|
173
|
+
gitmodules = root / ".gitmodules"
|
|
174
|
+
if gitmodules.exists():
|
|
175
|
+
try:
|
|
176
|
+
content = gitmodules.read_text(encoding="utf-8")
|
|
177
|
+
if "themes/" in content:
|
|
178
|
+
return True
|
|
179
|
+
except OSError:
|
|
180
|
+
pass
|
|
181
|
+
|
|
182
|
+
# Check if individual themes are git repos (submodules)
|
|
183
|
+
for theme in themes_dir.iterdir():
|
|
184
|
+
if theme.is_dir() and (theme / ".git").exists():
|
|
185
|
+
return True
|
|
186
|
+
|
|
187
|
+
return False
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _hugo_user_has_custom_layouts(root: Path) -> bool:
|
|
191
|
+
"""Check if the user has custom layouts at project root (outside themes/)."""
|
|
192
|
+
layouts = root / "layouts"
|
|
193
|
+
if not layouts.is_dir():
|
|
194
|
+
return False
|
|
195
|
+
# Has custom layouts if more than just _default/ and partials/ exist
|
|
196
|
+
entries = list(layouts.iterdir())
|
|
197
|
+
custom_entries = [e for e in entries
|
|
198
|
+
if e.name not in ("_default", "partials", "shortcodes")]
|
|
199
|
+
return len(custom_entries) > 0
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _post_process_excludes(frameworks: dict, root: Path) -> dict[str, list[str]]:
|
|
203
|
+
"""Apply framework-specific heuristics to refine exclude patterns.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
frameworks: Raw detection result {"frameworks": [...], "markers": {...}}.
|
|
207
|
+
root: Project root path.
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
Per-framework final exclude lists (may add/remove patterns).
|
|
211
|
+
"""
|
|
212
|
+
excludes: dict[str, list[str]] = {}
|
|
213
|
+
for fw in frameworks["frameworks"]:
|
|
214
|
+
patterns = list(FRAMEWORK_EXCLUDES.get(fw, []))
|
|
215
|
+
excludes[fw] = patterns
|
|
216
|
+
|
|
217
|
+
# Hugo: refine based on theme/layout detection
|
|
218
|
+
if "hugo" in excludes:
|
|
219
|
+
patterns = excludes["hugo"]
|
|
220
|
+
patterns.append("public/")
|
|
221
|
+
patterns.append("resources/")
|
|
222
|
+
|
|
223
|
+
if _hugo_theme_is_vendored(root):
|
|
224
|
+
patterns.append("archetypes/")
|
|
225
|
+
patterns.append("themes/**/archetypes/")
|
|
226
|
+
patterns.append("themes/**/layouts/")
|
|
227
|
+
else:
|
|
228
|
+
patterns.append("archetypes/")
|
|
229
|
+
|
|
230
|
+
# If user has custom layouts, only exclude defaults
|
|
231
|
+
if not _hugo_user_has_custom_layouts(root):
|
|
232
|
+
patterns.append("layouts/")
|
|
233
|
+
else:
|
|
234
|
+
patterns.append("layouts/_default/")
|
|
235
|
+
patterns.append("layouts/partials/")
|
|
236
|
+
patterns.append("layouts/shortcodes/")
|
|
237
|
+
|
|
238
|
+
return excludes
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
# ── .graphifyignore management ────────────────────────────────────────────────
|
|
242
|
+
|
|
243
|
+
def _read_graphifyignore(root: Path) -> tuple[str, list[str]]:
|
|
244
|
+
"""Read .graphifyignore and split into preamble / our-section / postscript.
|
|
245
|
+
|
|
246
|
+
Returns: (full_raw_content, lines_matching_our_section)
|
|
247
|
+
"""
|
|
248
|
+
gf = root / ".graphifyignore"
|
|
249
|
+
if not gf.exists():
|
|
250
|
+
return ("", [])
|
|
251
|
+
try:
|
|
252
|
+
content = gf.read_text(encoding="utf-8")
|
|
253
|
+
except OSError:
|
|
254
|
+
return ("", [])
|
|
255
|
+
return (content, _extract_marker_lines(content))
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def _extract_marker_lines(content: str) -> list[str]:
|
|
259
|
+
"""Extract lines between our start/end markers (exclusive)."""
|
|
260
|
+
lines = content.splitlines()
|
|
261
|
+
in_block = False
|
|
262
|
+
block_lines: list[str] = []
|
|
263
|
+
for line in lines:
|
|
264
|
+
if line.strip() == _MARKER_START:
|
|
265
|
+
in_block = True
|
|
266
|
+
continue
|
|
267
|
+
if line.strip() == _MARKER_END:
|
|
268
|
+
in_block = False
|
|
269
|
+
continue
|
|
270
|
+
if in_block:
|
|
271
|
+
block_lines.append(line)
|
|
272
|
+
return block_lines
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _normalize_ignore_line(line: str) -> str:
|
|
276
|
+
"""Strip comments and whitespace from a single .graphifyignore line."""
|
|
277
|
+
stripped = line.strip()
|
|
278
|
+
if not stripped or stripped.startswith("#"):
|
|
279
|
+
return ""
|
|
280
|
+
return stripped
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def _build_marker_block(excludes: dict[str, list[str]]) -> list[str]:
|
|
284
|
+
"""Build the marker-delimited block of FW patterns."""
|
|
285
|
+
lines: list[str] = [_MARKER_START]
|
|
286
|
+
seen: set[str] = set()
|
|
287
|
+
|
|
288
|
+
for fw_name, patterns in excludes.items():
|
|
289
|
+
lines.append(f"# auto-detected framework: {fw_name}")
|
|
290
|
+
for pat in patterns:
|
|
291
|
+
normalized = _normalize_ignore_line(pat)
|
|
292
|
+
if not normalized or normalized in seen:
|
|
293
|
+
continue
|
|
294
|
+
# Skip patterns graphify already skips natively
|
|
295
|
+
if normalized.rstrip("/") in _GRAPHIFY_BUILTIN_SKIP:
|
|
296
|
+
continue
|
|
297
|
+
if normalized in _GRAPHIFY_BUILTIN_SKIP_FILES:
|
|
298
|
+
continue
|
|
299
|
+
seen.add(normalized)
|
|
300
|
+
lines.append(pat)
|
|
301
|
+
|
|
302
|
+
lines.append(_MARKER_END)
|
|
303
|
+
return lines
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def _write_graphifyignore(root: Path, excludes: dict[str, list[str]]) -> str:
|
|
307
|
+
"""Write or update .graphifyignore, replacing our marker section.
|
|
308
|
+
|
|
309
|
+
Returns the summary string describing what was written.
|
|
310
|
+
"""
|
|
311
|
+
gf = root / ".graphifyignore"
|
|
312
|
+
existing_content, existing_block = _read_graphifyignore(root)
|
|
313
|
+
|
|
314
|
+
new_block = _build_marker_block(excludes)
|
|
315
|
+
new_block_text = "\n".join(new_block)
|
|
316
|
+
|
|
317
|
+
# Compare ignoring comments (our own section header comments are stable)
|
|
318
|
+
old_effective = [_normalize_ignore_line(l) for l in existing_block if _normalize_ignore_line(l)]
|
|
319
|
+
new_effective = []
|
|
320
|
+
for l in new_block:
|
|
321
|
+
n = _normalize_ignore_line(l)
|
|
322
|
+
if n and not n.startswith("#"):
|
|
323
|
+
new_effective.append(n)
|
|
324
|
+
|
|
325
|
+
if sorted(old_effective) == sorted(new_effective) and _MARKER_START in existing_content:
|
|
326
|
+
return "unchanged"
|
|
327
|
+
|
|
328
|
+
# Rebuild the file: remove old marker block, insert new one
|
|
329
|
+
if _MARKER_START in existing_content:
|
|
330
|
+
lines = existing_content.splitlines()
|
|
331
|
+
out_lines: list[str] = []
|
|
332
|
+
in_block = False
|
|
333
|
+
for line in lines:
|
|
334
|
+
if line.strip() == _MARKER_START:
|
|
335
|
+
in_block = True
|
|
336
|
+
continue
|
|
337
|
+
if line.strip() == _MARKER_END:
|
|
338
|
+
in_block = False
|
|
339
|
+
continue
|
|
340
|
+
if not in_block:
|
|
341
|
+
out_lines.append(line)
|
|
342
|
+
# Remove trailing blank lines before appending
|
|
343
|
+
while out_lines and out_lines[-1].strip() == "":
|
|
344
|
+
out_lines.pop()
|
|
345
|
+
out_lines.append("")
|
|
346
|
+
out_lines.append(new_block_text)
|
|
347
|
+
result = "\n".join(out_lines)
|
|
348
|
+
if not result.endswith("\n"):
|
|
349
|
+
result += "\n"
|
|
350
|
+
else:
|
|
351
|
+
# No existing block — append
|
|
352
|
+
if existing_content.strip():
|
|
353
|
+
result = existing_content.rstrip() + "\n\n" + new_block_text + "\n"
|
|
354
|
+
else:
|
|
355
|
+
result = new_block_text + "\n"
|
|
356
|
+
|
|
357
|
+
gf.write_text(result, encoding="utf-8")
|
|
358
|
+
|
|
359
|
+
# Build summary
|
|
360
|
+
fw_names = list(excludes.keys())
|
|
361
|
+
pattern_count = len(new_effective)
|
|
362
|
+
if pattern_count == 0:
|
|
363
|
+
return f"frameworks detected ({', '.join(fw_names)}) but no additional patterns needed"
|
|
364
|
+
return (
|
|
365
|
+
f"detected {', '.join(fw_names)} — wrote {pattern_count} "
|
|
366
|
+
f"exclusion pattern{'s' if pattern_count != 1 else ''} to .graphifyignore"
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
# ── CLI ───────────────────────────────────────────────────────────────────────
|
|
371
|
+
|
|
372
|
+
def _cmd_detect(root: Path) -> None:
|
|
373
|
+
result = detect_frameworks(root)
|
|
374
|
+
print(json.dumps(result, indent=2))
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def _cmd_show(root: Path) -> None:
|
|
378
|
+
result = detect_frameworks(root)
|
|
379
|
+
if not result["frameworks"]:
|
|
380
|
+
print("No supported frameworks detected.", file=sys.stderr)
|
|
381
|
+
sys.exit(2)
|
|
382
|
+
excludes = _post_process_excludes(result, root)
|
|
383
|
+
for fw, patterns in excludes.items():
|
|
384
|
+
print(f"# {fw}")
|
|
385
|
+
for pat in patterns:
|
|
386
|
+
n = _normalize_ignore_line(pat)
|
|
387
|
+
if n and n not in _GRAPHIFY_BUILTIN_SKIP and n not in _GRAPHIFY_BUILTIN_SKIP_FILES:
|
|
388
|
+
print(pat)
|
|
389
|
+
print()
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def _cmd_apply(root: Path) -> None:
|
|
393
|
+
result = detect_frameworks(root)
|
|
394
|
+
if not result["frameworks"]:
|
|
395
|
+
print("No supported frameworks detected.", file=sys.stderr)
|
|
396
|
+
sys.exit(2)
|
|
397
|
+
excludes = _post_process_excludes(result, root)
|
|
398
|
+
summary = _write_graphifyignore(root, excludes)
|
|
399
|
+
print(f"graphify-framework-aware: {summary}")
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
_CMD_TABLE: dict[str, callable] = {
|
|
403
|
+
"detect": _cmd_detect,
|
|
404
|
+
"show": _cmd_show,
|
|
405
|
+
"apply": _cmd_apply,
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def main() -> None:
|
|
410
|
+
if len(sys.argv) < 3:
|
|
411
|
+
print(f"usage: {sys.argv[0]} <detect|show|apply> <project-path>", file=sys.stderr)
|
|
412
|
+
sys.exit(1)
|
|
413
|
+
|
|
414
|
+
cmd = sys.argv[1]
|
|
415
|
+
path = Path(sys.argv[2])
|
|
416
|
+
|
|
417
|
+
if cmd not in _CMD_TABLE:
|
|
418
|
+
print(f"error: unknown command '{cmd}' (expected: detect, show, apply)", file=sys.stderr)
|
|
419
|
+
sys.exit(1)
|
|
420
|
+
|
|
421
|
+
_CMD_TABLE[cmd](path)
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
if __name__ == "__main__":
|
|
425
|
+
main()
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Search project memory bank using semantic vector search"
|
|
3
|
+
name: "memory-search"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Memory Search Skill
|
|
7
|
+
|
|
8
|
+
Use this skill to retrieve project context from the memory bank without loading all files into context.
|
|
9
|
+
|
|
10
|
+
## When to Use
|
|
11
|
+
|
|
12
|
+
- At the start of a session: search for current project focus and recent changes
|
|
13
|
+
- Before making a decision: search for related past decisions
|
|
14
|
+
- When stuck: search for architectural patterns or known issues
|
|
15
|
+
- After writing to memory-bank: run `memory_rebuild` to update the index
|
|
16
|
+
|
|
17
|
+
## Available MCP Tools
|
|
18
|
+
|
|
19
|
+
This skill uses the `memory-bank` MCP server, configured in `opencode.json`:
|
|
20
|
+
|
|
21
|
+
| Tool | Purpose |
|
|
22
|
+
|---|---|
|
|
23
|
+
| `memory_search` | Semantic search across all memory-bank and docs files |
|
|
24
|
+
| `memory_update` | Incremental sync — only changed files (~2 seconds). Use daily. |
|
|
25
|
+
| `memory_rebuild` | Full rebuild (rare — fresh clone, corrupted index) |
|
|
26
|
+
| `memory_get` | Read a specific memory file by name |
|
|
27
|
+
| `memory_stats` | Get index statistics (vector count, file count, DB size) |
|
|
28
|
+
|
|
29
|
+
## Workflow
|
|
30
|
+
|
|
31
|
+
### Session Start
|
|
32
|
+
|
|
33
|
+
1. Run `memory_stats` to check if the index is populated
|
|
34
|
+
2. If empty, run `memory_rebuild`; otherwise, run `memory_update`
|
|
35
|
+
3. Search for current context: `memory_search({ query: "current focus recent changes next steps" })`
|
|
36
|
+
|
|
37
|
+
### During Tasks
|
|
38
|
+
|
|
39
|
+
- Before implementing: `memory_search({ query: "<topic>", topK: 5 })` for related decisions
|
|
40
|
+
- When writing to memory-bank: update files, then run `memory_update`
|
|
41
|
+
- For full file content: `memory_get({ file: "activeContext" })`
|
|
42
|
+
|
|
43
|
+
### After Git Pull
|
|
44
|
+
|
|
45
|
+
The `post-merge` hook runs `memory:update` automatically if memory-bank files changed.
|
|
46
|
+
|
|
47
|
+
### Search Tips
|
|
48
|
+
|
|
49
|
+
- **Be specific**: "TDD orchestrator bootstrap prerequisites" gets better results than "project"
|
|
50
|
+
- **Use contextual terms**: Include entity names (vitest, playwright, graphify) from the vocabulary
|
|
51
|
+
- **Filter by file**: `memory_search({ query: "...", sourceFile: "systemPatterns" })` to narrow scope
|
|
52
|
+
- **Top-K tuning**: Use `topK: 3` for focused queries, `topK: 10` for broad exploration
|
|
53
|
+
|
|
54
|
+
## Vocabulary Reference
|
|
55
|
+
|
|
56
|
+
See `memory-bank/.vocabulary.json` for the full controlled vocabulary. Key tags:
|
|
57
|
+
|
|
58
|
+
- **Agent roles**: orchestrator, coder, reviewer, tracker, architect, researcher
|
|
59
|
+
- **Workflow**: tdd, feature-pipeline, bootstrap, configuration, refactor, migration
|
|
60
|
+
- **Memory ops**: decision, discovery, status-update, task-plan, retrospective
|
|
61
|
+
- **Technologies**: typescript, vitest, playwright, graphify, mcp, sqlite-vec
|
|
62
|
+
|
|
63
|
+
## Manual CLI (for debugging)
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
node scripts/memory-cli.js search "tdd orchestrator"
|
|
67
|
+
node scripts/memory-cli.js rebuild
|
|
68
|
+
node scripts/memory-cli.js stats
|
|
69
|
+
```
|
|
@@ -64,7 +64,7 @@ GitHub docs live on **docs.github.com** and **cli.github.com**.
|
|
|
64
64
|
|
|
65
65
|
| Library ID | Use for |
|
|
66
66
|
| ---------------------- | --------------------------------------------- |
|
|
67
|
-
| `/websites/github_en` | Actions, API, repos, security, admin
|
|
67
|
+
| `/websites/github_en` | Actions, API, repos, security, admin |
|
|
68
68
|
| `/websites/cli_github` | GitHub CLI (`gh`) commands and flags |
|
|
69
69
|
|
|
70
70
|
### Agent Framework — Use Learn MCP + Context7
|
|
@@ -154,7 +154,6 @@ Add tests for the authentication module with focus on edge cases
|
|
|
154
154
|
|
|
155
155
|
- Project must have a build/test system configured
|
|
156
156
|
- Testing framework should be installed (or installable)
|
|
157
|
-
- VS Code with GitHub Copilot extension
|
|
158
157
|
|
|
159
158
|
## Troubleshooting
|
|
160
159
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: prompt-builder
|
|
3
|
-
description: "Guide users through creating high-quality
|
|
3
|
+
description: "Guide users through creating high-quality opencode prompts with proper structure, tools, and best practices."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Professional Prompt Builder
|
|
7
7
|
|
|
8
|
-
You are an expert prompt engineer specializing in
|
|
8
|
+
You are an expert prompt engineer specializing in opencode prompt development with deep knowledge of:
|
|
9
9
|
|
|
10
10
|
- Prompt engineering best practices and patterns
|
|
11
|
-
-
|
|
11
|
+
- opencode customization capabilities
|
|
12
12
|
- Effective persona design and task specification
|
|
13
13
|
- Tool integration and front matter configuration
|
|
14
14
|
- Output format optimization for AI consumption
|
|
@@ -27,7 +27,7 @@ I will ask you targeted questions to gather all necessary information. After col
|
|
|
27
27
|
|
|
28
28
|
### 2. **Persona Definition**
|
|
29
29
|
|
|
30
|
-
- What role/expertise should
|
|
30
|
+
- What role/expertise should the AI assistant embody? Be specific about:
|
|
31
31
|
- Technical expertise level (junior, senior, expert, specialist)
|
|
32
32
|
- Domain knowledge (languages, frameworks, tools)
|
|
33
33
|
- Years of experience or specific qualifications
|
|
@@ -42,15 +42,15 @@ I will ask you targeted questions to gather all necessary information. After col
|
|
|
42
42
|
|
|
43
43
|
### 4. **Context & Variable Requirements**
|
|
44
44
|
|
|
45
|
-
- Will it use
|
|
46
|
-
- Will it use
|
|
47
|
-
- Does it need input
|
|
48
|
-
- Will it reference workspace
|
|
45
|
+
- Will it use selected text from the editor?
|
|
46
|
+
- Will it use the current file path or other file references?
|
|
47
|
+
- Does it need custom input parameters or variable references?
|
|
48
|
+
- Will it reference the workspace root path or other project-level variables?
|
|
49
49
|
- Does it need to access other files or prompt files as dependencies?
|
|
50
50
|
|
|
51
51
|
### 5. **Detailed Instructions & Standards**
|
|
52
52
|
|
|
53
|
-
- What step-by-step process should
|
|
53
|
+
- What step-by-step process should the AI follow?
|
|
54
54
|
- Are there specific coding standards, frameworks, or libraries to use?
|
|
55
55
|
- What patterns or best practices should be enforced?
|
|
56
56
|
- Are there things to avoid or constraints to respect?
|
|
@@ -68,11 +68,10 @@ I will ask you targeted questions to gather all necessary information. After col
|
|
|
68
68
|
|
|
69
69
|
Which tools does this prompt need? Common options include:
|
|
70
70
|
|
|
71
|
-
- **File Operations**: `
|
|
72
|
-
- **Execution**: `
|
|
73
|
-
- **External**: `
|
|
74
|
-
- **Specialized**: `playwright
|
|
75
|
-
- **Analysis**: `changes`, `findTestFiles`, `testFailure`, `searchResults`
|
|
71
|
+
- **File Operations**: `Read`, `Write`, `Edit`, `Grep`, `Glob`
|
|
72
|
+
- **Execution**: `Bash`
|
|
73
|
+
- **External**: `WebFetch`, `WebSearch`
|
|
74
|
+
- **Specialized**: `playwright` (Playwright MCP)
|
|
76
75
|
|
|
77
76
|
### 8. **Technical Configuration**
|
|
78
77
|
|
|
@@ -111,9 +110,11 @@ After gathering all requirements, I will generate a complete `.prompt.md` file f
|
|
|
111
110
|
```markdown
|
|
112
111
|
---
|
|
113
112
|
description: "[Clear, concise description from requirements]"
|
|
113
|
+
name: "[prompt-name]"
|
|
114
114
|
agent: "[agent|ask|edit based on task type]"
|
|
115
115
|
tools: ["[appropriate tools based on functionality]"]
|
|
116
116
|
model: "[only if specific model required]"
|
|
117
|
+
argument-hint: "[hint text shown in chat input]"
|
|
117
118
|
---
|
|
118
119
|
|
|
119
120
|
# [Prompt Title]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
. "$(dirname "$0")/_/husky.sh"
|
|
3
|
+
|
|
4
|
+
# After git pull/merge, check if memory-bank files changed
|
|
5
|
+
# and update the vector index incrementally
|
|
6
|
+
|
|
7
|
+
if git diff HEAD@{1} --name-only 2>/dev/null | grep -q "^memory-bank\|^docs/"; then
|
|
8
|
+
echo "[memory-bank] Memory bank files changed. Updating vector index..."
|
|
9
|
+
node scripts/memory-cli.js update 2>/dev/null || echo "[memory-bank] Index update skipped (service not installed)"
|
|
10
|
+
fi
|
package/AGENTS.md
CHANGED
|
@@ -141,9 +141,63 @@ The agent file is at `.opencode/agents/orchestrator/feature-pipeline.agent.md`.
|
|
|
141
141
|
|
|
142
142
|
**When to use:** You want a predictable, four-step feature pipeline without dynamic agent selection. The orchestrator agent is more flexible; this is more opinionated.
|
|
143
143
|
|
|
144
|
+
### TDD Orchestrator — Test-Driven Development Pipeline
|
|
145
|
+
|
|
146
|
+
Use this when you want strict Test-Driven Development: **tests first, code second**. The pipeline enforces the RED → GREEN → REFACTOR/VERIFY cycle:
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
implementer (PLAN) → unit-tester∥ (RED) → coder∥ (GREEN) → unit-tester (VERIFY ≥90%) → reviewer → tracker
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
| Step | Phase | Agent | Role |
|
|
153
|
+
|------|-------|-------|------|
|
|
154
|
+
| 1 | PLAN | implementer | Creates plan with parallel test/code batch tables |
|
|
155
|
+
| 2 | RED | unit-tester∥ | Writes failing tests before any implementation exists |
|
|
156
|
+
| 3 | GREEN | coder∥ | Writes minimal code to pass tests — no extra features |
|
|
157
|
+
| 4 | VERIFY | unit-tester | Runs all tests, checks coverage ≥ 90% |
|
|
158
|
+
| 5 | REVIEW | reviewer | Audits tests + code for quality, security, patterns |
|
|
159
|
+
| 6 | DOCUMENT | tracker | Records results to `docs/tracker-log.md` |
|
|
160
|
+
|
|
161
|
+
The agent file is at `.opencode/agents/orchestrator/tdd-orchestrator.agent.md`.
|
|
162
|
+
|
|
163
|
+
**When to use:** Any feature, bug fix, or refactor where tests should be written first. The orchestrator automatically selects this pipeline when testable code changes are requested.
|
|
164
|
+
|
|
165
|
+
**Coverage gate:** If coverage < 90%, the pipeline loops back to RED phase (up to 3 iterations).
|
|
166
|
+
|
|
167
|
+
### Memory Bank
|
|
168
|
+
|
|
169
|
+
The memory bank at `memory-bank/` preserves project context across AI sessions. Core files:
|
|
170
|
+
|
|
171
|
+
| File | Purpose |
|
|
172
|
+
|------|---------|
|
|
173
|
+
| `projectbrief.md` | Core requirements, goals, scope |
|
|
174
|
+
| `productContext.md` | Problem solved, UX goals |
|
|
175
|
+
| `systemPatterns.md` | Architecture, design patterns |
|
|
176
|
+
| `techContext.md` | Technologies, setup, constraints |
|
|
177
|
+
| `activeContext.md` | Current focus, recent changes, next steps |
|
|
178
|
+
| `progress.md` | What works, what's left, known issues |
|
|
179
|
+
| `tasks/` | Per-task tracking with `_index.md` |
|
|
180
|
+
|
|
181
|
+
**Always read `activeContext.md` and `progress.md` at the start of every task.** Update them after completing work. See `.agents/instructions/memory-bank.instructions.md` for full details.
|
|
182
|
+
|
|
144
183
|
## Additional Resources
|
|
145
184
|
|
|
146
185
|
### Playwright MCP Configuration
|
|
147
186
|
|
|
148
187
|
For details on how Playwright MCP integrates with the headed mode debugging configuration, see:
|
|
149
188
|
- **Docs**: `docs/playwright-mcp-configuration.md`
|
|
189
|
+
|
|
190
|
+
## graphify
|
|
191
|
+
|
|
192
|
+
This project has a knowledge graph at graphify-out/ with god nodes, community structure, and cross-file relationships.
|
|
193
|
+
|
|
194
|
+
When the user types `/graphify`, invoke these skills in order before doing anything else:
|
|
195
|
+
1. `skill: "graphify-framework-aware"` — detects project frameworks and configures `.graphifyignore` to exclude boilerplate
|
|
196
|
+
2. `skill: "graphify"` — runs the knowledge graph pipeline
|
|
197
|
+
|
|
198
|
+
Rules:
|
|
199
|
+
- For codebase questions, first run `graphify query "<question>"` when graphify-out/graph.json exists. Use `graphify path "<A>" "<B>"` for relationships and `graphify explain "<concept>"` for focused concepts. These return a scoped subgraph, usually much smaller than GRAPH_REPORT.md or raw grep output.
|
|
200
|
+
- Dirty graphify-out/ files are expected after hooks or incremental updates; dirty graph files are not a reason to skip graphify. Only skip graphify if the task is about stale or incorrect graph output, or the user explicitly says not to use it.
|
|
201
|
+
- If graphify-out/wiki/index.md exists, use it for broad navigation instead of raw source browsing.
|
|
202
|
+
- Read graphify-out/GRAPH_REPORT.md only for broad architecture review or when query/path/explain do not surface enough context.
|
|
203
|
+
- After modifying code, run `graphify update .` to keep the graph current (AST-only, no API cost).
|