@jetrabbits/agentic 0.0.3 → 0.0.5

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 (65) hide show
  1. package/AGENTS.md +15 -0
  2. package/Makefile +40 -0
  3. package/README.md +1 -0
  4. package/UPGRADE.md +61 -0
  5. package/agentic +948 -10
  6. package/areas/devops/ci-cd/prompts/release-pipeline.md +69 -79
  7. package/areas/devops/ci-cd/rules/supply-chain-security.md +39 -19
  8. package/areas/devops/ci-cd/skills/github-actions-patterns/SKILL.md +6 -1
  9. package/areas/devops/ci-cd/skills/pipeline-security/SKILL.md +54 -119
  10. package/areas/devops/ci-cd/workflows/release-pipeline.md +72 -62
  11. package/areas/devops/kubernetes/skills/pod-troubleshooting/SKILL.md +1 -1
  12. package/areas/devops/observability/rules/alerting-standards.md +37 -31
  13. package/areas/devops/observability/rules/golden-signals.md +29 -20
  14. package/areas/devops/observability/skills/distributed-tracing/SKILL.md +10 -1
  15. package/areas/software/backend/rules/security.md +32 -12
  16. package/areas/software/frontend/skills/component-design/SKILL.md +13 -1
  17. package/areas/software/full-stack/AGENTS.md +1 -4
  18. package/areas/software/full-stack/rules/security-guide.md +48 -12
  19. package/areas/software/full-stack/workflows/debug-issue.md +2 -2
  20. package/areas/software/security/prompts/security-scan.md +47 -55
  21. package/areas/software/security/rules/dependency-policy.md +43 -8
  22. package/areas/software/security/skills/dependency-audit/SKILL.md +46 -25
  23. package/areas/software/security/skills/threat-modeling/SKILL.md +26 -0
  24. package/docs/agentic-lifecycle.md +103 -0
  25. package/docs/agentic-token-minimization/README.md +79 -0
  26. package/docs/agentic-usage.md +145 -0
  27. package/docs/catalog.schema.json +203 -0
  28. package/docs/guidance-updates/2026-04-10-software-devops-best-practices.md +26 -0
  29. package/docs/opencode_prepare_agents.md +40 -0
  30. package/docs/opencode_setup.md +45 -0
  31. package/docs/prompt-format.md +80 -0
  32. package/docs/site/README.md +44 -0
  33. package/docs/site/app.js +127 -0
  34. package/docs/site/catalog.json +5002 -0
  35. package/docs/site/index.html +52 -0
  36. package/docs/site/styles.css +177 -0
  37. package/extensions/codex/agents/developer.toml +1 -1
  38. package/extensions/codex/agents/devops-engineer.toml +1 -1
  39. package/extensions/codex/agents/product-owner.toml +1 -1
  40. package/extensions/codex/agents/team-lead.toml +1 -1
  41. package/extensions/opencode/plugins/model-checker.json +2 -3
  42. package/extensions/opencode/plugins/model-checker.ts +23 -0
  43. package/extensions/opencode/plugins/telegram-notification.ts +33 -5
  44. package/package.json +6 -2
  45. package/scripts/assess_area_quality.py +216 -0
  46. package/scripts/build_docs_catalog.py +283 -0
  47. package/scripts/lint_prompts.py +113 -0
  48. package/areas/software/full-stack/skills/bash-pro/SKILL.md +0 -310
  49. package/areas/software/full-stack/skills/python-pro/SKILL.md +0 -158
  50. package/areas/software/full-stack/skills/skill-creator/LICENSE.txt +0 -202
  51. package/areas/software/full-stack/skills/skill-creator/SKILL.md +0 -356
  52. package/areas/software/full-stack/skills/skill-creator/references/output-patterns.md +0 -82
  53. package/areas/software/full-stack/skills/skill-creator/references/workflows.md +0 -28
  54. package/areas/software/full-stack/skills/skill-creator/scripts/init_skill.py +0 -303
  55. package/areas/software/full-stack/skills/skill-creator/scripts/package_skill.py +0 -110
  56. package/areas/software/full-stack/skills/skill-creator/scripts/quick_validate.py +0 -95
  57. package/extensions/codex/skills/babysit-pr/SKILL.md +0 -187
  58. package/extensions/codex/skills/babysit-pr/agents/openai.yaml +0 -4
  59. package/extensions/codex/skills/babysit-pr/references/github-api-notes.md +0 -72
  60. package/extensions/codex/skills/babysit-pr/references/heuristics.md +0 -58
  61. package/extensions/codex/skills/babysit-pr/scripts/gh_pr_watch.py +0 -806
  62. package/extensions/codex/skills/babysit-pr/scripts/test_gh_pr_watch.py +0 -155
  63. package/extensions/opencode/skills/code_review_expert/SKILL.md +0 -144
  64. package/extensions/opencode/skills/design_expert/SKILL.md +0 -42
  65. package/extensions/opencode/skills/qa_expert/SKILL.md +0 -116
@@ -0,0 +1,283 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import json
6
+ import re
7
+ from dataclasses import dataclass, asdict
8
+ from pathlib import Path
9
+ from typing import Dict, List, Tuple
10
+
11
+ ROOT = Path(__file__).resolve().parents[1]
12
+ AREAS_DIR = ROOT / "areas"
13
+
14
+ WORKFLOW_RE = re.compile(r"^---\n(.*?)\n---\n", re.DOTALL)
15
+ TRIGGER_RE = re.compile(r"^trigger:\s*(.+)$", re.MULTILINE)
16
+ NAME_RE = re.compile(r"^name:\s*(.+)$", re.MULTILINE)
17
+ DESC_RE = re.compile(r"^description:\s*(.+)$", re.MULTILINE)
18
+ PROMPT_FRONTMATTER_RE = re.compile(r"^---\n(.*?)\n---\n", re.DOTALL)
19
+ PROMPT_WORKFLOW_RE = re.compile(r"^workflow:\s*([a-z0-9][a-z0-9-]*)\s*$", re.MULTILINE)
20
+ PROMPT_TITLE_RE = re.compile(r"^#\s*Prompt:\s*`?([^`\n]+)`?", re.MULTILINE)
21
+ USE_WHEN_RE = re.compile(r"^Use when:\s*(.+)$", re.MULTILINE)
22
+ EXAMPLE_RE = re.compile(
23
+ r"##\s*Example\s*(\d+)\s*[—-]\s*(.*?)\n.*?\*\*EN:\*\*\s*\n```\n(.*?)\n```\s*\n\s*\*\*RU:\*\*\s*\n```\n(.*?)\n```",
24
+ re.DOTALL,
25
+ )
26
+ COMMAND_REF_RE = re.compile(r"(?m)^/([a-z0-9][a-z0-9-]*)\b")
27
+ PLACEHOLDER_STRINGS = (
28
+ "<project context>",
29
+ "<контекст проекта>",
30
+ "Objective: clearly state",
31
+ "Goal: execute workflow steps end-to-end",
32
+ "Use when: run workflow",
33
+ )
34
+
35
+
36
+ def _parse_yaml_list(frontmatter: str, key: str) -> List[str]:
37
+ m = re.search(rf"^{re.escape(key)}:\s*\n((?:\s*-\s.*\n)+)", frontmatter, re.MULTILINE)
38
+ if not m:
39
+ inline = re.search(rf"^{re.escape(key)}:\s*\[(.*?)\]\s*$", frontmatter, re.MULTILINE)
40
+ if not inline:
41
+ return []
42
+ return [x.strip().strip("'\"") for x in inline.group(1).split(",") if x.strip()]
43
+ return [line.strip()[1:].strip().strip("'\"") for line in m.group(1).splitlines() if line.strip().startswith("-")]
44
+
45
+
46
+ def _area_and_stem(path: Path) -> tuple[str, str]:
47
+ rel = path.relative_to(ROOT)
48
+ parts = rel.parts
49
+ return "/".join(parts[1:3]), path.stem
50
+
51
+
52
+ def _resolve_skill_paths(area: str, uses_skills: List[str]) -> List[dict]:
53
+ base = ROOT / "areas" / area / "skills"
54
+ resolved: List[dict] = []
55
+ for name in uses_skills:
56
+ c1 = base / name / "SKILL.md"
57
+ c2 = base / f"{name}.md"
58
+ path = c1 if c1.exists() else c2 if c2.exists() else None
59
+ resolved.append({"name": name, "path": str(path.relative_to(ROOT)) if path else None})
60
+ return resolved
61
+
62
+
63
+ @dataclass
64
+ class Workflow:
65
+ key: str
66
+ area: str
67
+ stem: str
68
+ trigger: str
69
+ name: str
70
+ description: str
71
+ path: str
72
+ inputs: List[str]
73
+ outputs: List[str]
74
+ roles: List[str]
75
+ related_rules: List[str]
76
+ uses_skills: List[str]
77
+ quality_gates: List[str]
78
+ skill_refs: List[dict]
79
+
80
+
81
+ @dataclass
82
+ class Example:
83
+ number: int
84
+ title: str
85
+ en: str
86
+ ru: str
87
+
88
+
89
+ @dataclass
90
+ class Prompt:
91
+ key: str
92
+ area: str
93
+ stem: str
94
+ workflow: str
95
+ trigger: str
96
+ path: str
97
+ use_when: str
98
+ examples: List[Example]
99
+ command_refs: List[str]
100
+
101
+
102
+ def parse_workflow(path: Path) -> Workflow:
103
+ text = path.read_text(encoding="utf-8")
104
+ m = WORKFLOW_RE.search(text)
105
+ if not m:
106
+ raise ValueError(f"No YAML frontmatter: {path}")
107
+ front = m.group(1)
108
+ trig = TRIGGER_RE.search(front)
109
+ name = NAME_RE.search(front)
110
+ desc = DESC_RE.search(front)
111
+ area, stem = _area_and_stem(path)
112
+ uses_skills = _parse_yaml_list(front, "uses-skills")
113
+
114
+ return Workflow(
115
+ key=f"{area}:{stem}",
116
+ area=area,
117
+ stem=stem,
118
+ trigger=trig.group(1).strip() if trig else "",
119
+ name=name.group(1).strip() if name else path.stem,
120
+ description=desc.group(1).strip() if desc else "",
121
+ path=str(path.relative_to(ROOT)),
122
+ inputs=_parse_yaml_list(front, "inputs"),
123
+ outputs=_parse_yaml_list(front, "outputs"),
124
+ roles=_parse_yaml_list(front, "roles"),
125
+ related_rules=_parse_yaml_list(front, "related-rules"),
126
+ uses_skills=uses_skills,
127
+ quality_gates=_parse_yaml_list(front, "quality-gates"),
128
+ skill_refs=_resolve_skill_paths(area, uses_skills),
129
+ )
130
+
131
+
132
+ def parse_prompt(path: Path) -> Prompt:
133
+ text = path.read_text(encoding="utf-8")
134
+ frontmatter = PROMPT_FRONTMATTER_RE.match(text)
135
+ workflow = ""
136
+ if frontmatter:
137
+ workflow_match = PROMPT_WORKFLOW_RE.search(frontmatter.group(1))
138
+ if workflow_match:
139
+ workflow = workflow_match.group(1).strip()
140
+ t = PROMPT_TITLE_RE.search(text)
141
+ uw = USE_WHEN_RE.search(text)
142
+ examples = [
143
+ Example(number=int(m.group(1)), title=m.group(2).strip(), en=m.group(3).strip(), ru=m.group(4).strip())
144
+ for m in EXAMPLE_RE.finditer(text)
145
+ ]
146
+ area, stem = _area_and_stem(path)
147
+ refs = sorted({m.group(1) for m in COMMAND_REF_RE.finditer(text)})
148
+ return Prompt(
149
+ key=f"{area}:{stem}",
150
+ area=area,
151
+ stem=stem,
152
+ workflow=workflow,
153
+ trigger=t.group(1).strip() if t else "",
154
+ path=str(path.relative_to(ROOT)),
155
+ use_when=uw.group(1).strip() if uw else "",
156
+ examples=examples,
157
+ command_refs=refs,
158
+ )
159
+
160
+
161
+ def _match_prompt_to_workflow(prompt: Prompt, workflows_by_area: Dict[str, Dict[str, Workflow]]) -> Workflow | None:
162
+ area_wf = workflows_by_area.get(prompt.area, {})
163
+ if prompt.workflow:
164
+ return area_wf.get(prompt.workflow)
165
+ return None
166
+
167
+
168
+ def build_catalog(validate: bool = False) -> Tuple[dict, List[str]]:
169
+ workflows_by_area: Dict[str, Dict[str, Workflow]] = {}
170
+ prompts: List[Prompt] = []
171
+ problems: List[str] = []
172
+ prompts_by_workflow: Dict[tuple[str, str], Prompt] = {}
173
+
174
+ for path in sorted(AREAS_DIR.glob("**/workflows/*.md")):
175
+ wf = parse_workflow(path)
176
+ workflows_by_area.setdefault(wf.area, {})[wf.stem] = wf
177
+ if not wf.trigger:
178
+ problems.append(f"workflow missing trigger: {wf.path}")
179
+
180
+ for path in sorted(AREAS_DIR.glob("**/prompts/*.md")):
181
+ pr = parse_prompt(path)
182
+ prompts.append(pr)
183
+ if not pr.trigger:
184
+ problems.append(f"prompt missing trigger: {pr.path}")
185
+ if not pr.workflow:
186
+ problems.append(f"prompt missing front matter workflow key: {pr.path}")
187
+ if pr.workflow and pr.stem != pr.workflow:
188
+ problems.append(f"prompt filename must match workflow stem: {pr.path}")
189
+ if pr.workflow and pr.trigger != f"/{pr.workflow}":
190
+ problems.append(f"prompt header must match workflow stem: {pr.path}")
191
+ if not pr.examples:
192
+ problems.append(f"prompt has no EN/RU examples: {pr.path}")
193
+ if len(pr.examples) < 2 or len(pr.examples) > 3:
194
+ problems.append(f"prompt example count must be 2 or 3: {pr.path}")
195
+ if "Workflow link command:" in path.read_text(encoding="utf-8"):
196
+ problems.append(f"legacy workflow link block not allowed: {pr.path}")
197
+ if any(token in path.read_text(encoding="utf-8") for token in PLACEHOLDER_STRINGS):
198
+ problems.append(f"placeholder scaffold text not allowed: {pr.path}")
199
+ if pr.workflow and pr.command_refs and set(pr.command_refs) != {pr.workflow}:
200
+ problems.append(f"prompt contains non-canonical slash commands: {pr.path}")
201
+ if pr.workflow:
202
+ key = (pr.area, pr.workflow)
203
+ if key in prompts_by_workflow:
204
+ problems.append(f"multiple prompts mapped to workflow {pr.area}/{pr.workflow}: {pr.path}")
205
+ else:
206
+ prompts_by_workflow[key] = pr
207
+
208
+ matched_prompt_keys: set[str] = set()
209
+ prompt_by_workflow_key: Dict[str, Prompt] = {}
210
+ for pr in prompts:
211
+ wf = _match_prompt_to_workflow(pr, workflows_by_area)
212
+ if wf:
213
+ prompt_by_workflow_key[wf.key] = pr
214
+ matched_prompt_keys.add(pr.key)
215
+ elif validate:
216
+ problems.append(f"prompt not matched to workflow via front matter: {pr.path}")
217
+
218
+ if validate:
219
+ all_workflows = [wf for area in workflows_by_area.values() for wf in area.values()]
220
+ for wf in all_workflows:
221
+ if wf.key not in prompt_by_workflow_key:
222
+ problems.append(f"workflow has no matched prompt: {wf.path}")
223
+
224
+ areas_out: Dict[str, dict] = {}
225
+ all_workflows = [wf for area in workflows_by_area.values() for wf in area.values()]
226
+ for wf in sorted(all_workflows, key=lambda x: (x.area, x.stem)):
227
+ pr = prompt_by_workflow_key.get(wf.key)
228
+ bucket = areas_out.setdefault(wf.area, {"area": wf.area, "workflows": []})
229
+ bucket["workflows"].append(
230
+ {
231
+ "trigger": wf.trigger,
232
+ "name": wf.name,
233
+ "description": wf.description,
234
+ "workflow_path": wf.path,
235
+ "prompt_path": pr.path if pr else None,
236
+ "prompt_command_refs": pr.command_refs if pr else [],
237
+ "use_when": pr.use_when if pr else "",
238
+ "inputs": wf.inputs,
239
+ "outputs": wf.outputs,
240
+ "roles": wf.roles,
241
+ "related_rules": wf.related_rules,
242
+ "uses_skills": wf.uses_skills,
243
+ "skill_refs": wf.skill_refs,
244
+ "quality_gates": wf.quality_gates,
245
+ "examples": {"both": [asdict(e) for e in (pr.examples if pr else [])]},
246
+ }
247
+ )
248
+
249
+ catalog = {
250
+ "version": "1.1.0",
251
+ "generated_from": "areas/**/{workflows,prompts}",
252
+ "areas": list(areas_out.values()),
253
+ "stats": {
254
+ "workflows": len(all_workflows),
255
+ "prompts": len(prompts),
256
+ "matched_prompts": len(matched_prompt_keys),
257
+ "problems": len(problems),
258
+ },
259
+ }
260
+ return catalog, problems
261
+
262
+
263
+ def main() -> int:
264
+ parser = argparse.ArgumentParser()
265
+ parser.add_argument("--output", default=str(ROOT / "docs/site/catalog.json"))
266
+ parser.add_argument("--validate", action="store_true")
267
+ args = parser.parse_args()
268
+
269
+ catalog, problems = build_catalog(validate=args.validate)
270
+ out = Path(args.output)
271
+ out.parent.mkdir(parents=True, exist_ok=True)
272
+ out.write_text(json.dumps(catalog, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
273
+
274
+ if problems:
275
+ print("Catalog validation issues:")
276
+ for issue in problems:
277
+ print(f"- {issue}")
278
+
279
+ return 1 if args.validate and problems else 0
280
+
281
+
282
+ if __name__ == "__main__":
283
+ raise SystemExit(main())
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import re
6
+ from pathlib import Path
7
+
8
+ ROOT = Path(__file__).resolve().parents[1]
9
+ AREAS = ROOT / "areas"
10
+
11
+ FRONTMATTER = re.compile(r"^---\n(.*?)\n---\n", re.DOTALL)
12
+ WORKFLOW_KEY = re.compile(r"^workflow:\s*([a-z0-9][a-z0-9-]*)\s*$", re.MULTILINE)
13
+ PROMPT_HEADER = re.compile(r"^#\s*Prompt:\s*`?([^`\n]+)`?", re.MULTILINE)
14
+ USE_WHEN = re.compile(r"^Use when:\s*.+$", re.MULTILINE)
15
+ EXAMPLE_BLOCK = re.compile(r"##\s*Example\s*\d+\s*[—-]\s*.+?", re.MULTILINE)
16
+ EN_BLOCK = re.compile(r"\*\*EN:\*\*\s*\n```\n.+?\n```", re.DOTALL)
17
+ RU_BLOCK = re.compile(r"\*\*RU:\*\*\s*\n```\n.+?\n```", re.DOTALL)
18
+ COMMAND_REF_RE = re.compile(r"(?m)^/([a-z0-9][a-z0-9-]*)\b")
19
+ PLACEHOLDER_STRINGS = (
20
+ "<project context>",
21
+ "<контекст проекта>",
22
+ "Objective: clearly state",
23
+ "Goal: execute workflow steps end-to-end",
24
+ "Use when: run workflow",
25
+ )
26
+
27
+
28
+ def workflow_stems_for_prompt(prompt_path: Path) -> set[str]:
29
+ wf_dir = prompt_path.parent.parent / "workflows"
30
+ if not wf_dir.exists():
31
+ return set()
32
+ return {p.stem for p in wf_dir.glob("*.md")}
33
+
34
+
35
+ def parse_workflow_key(text: str) -> str | None:
36
+ frontmatter = FRONTMATTER.match(text)
37
+ if not frontmatter:
38
+ return None
39
+ match = WORKFLOW_KEY.search(frontmatter.group(1))
40
+ if not match:
41
+ return None
42
+ return match.group(1)
43
+
44
+
45
+ def main() -> int:
46
+ ap = argparse.ArgumentParser()
47
+ ap.add_argument("--strict", action="store_true", help="Exit 1 on any issue")
48
+ args = ap.parse_args()
49
+
50
+ issues: list[str] = []
51
+ for path in sorted(AREAS.glob("**/prompts/*.md")):
52
+ text = path.read_text(encoding="utf-8")
53
+ rel = path.relative_to(ROOT)
54
+
55
+ workflow = parse_workflow_key(text)
56
+ if workflow is None:
57
+ issues.append(f"{rel}: missing prompt front matter with `workflow: <workflow-stem>`")
58
+
59
+ header = PROMPT_HEADER.search(text)
60
+ if not header:
61
+ issues.append(f"{rel}: missing `# Prompt:` header")
62
+ continue
63
+
64
+ header_command = header.group(1).strip()
65
+ if workflow and header_command != f"/{workflow}":
66
+ issues.append(f"{rel}: prompt header must equal `/{workflow}`")
67
+ if not USE_WHEN.search(text):
68
+ issues.append(f"{rel}: missing `Use when:` section")
69
+ example_count = len(EXAMPLE_BLOCK.findall(text))
70
+ if not example_count:
71
+ issues.append(f"{rel}: missing `## Example N — ...` block")
72
+ elif example_count < 2 or example_count > 3:
73
+ issues.append(f"{rel}: example count must be 2 or 3 (found {example_count})")
74
+ if not EN_BLOCK.search(text):
75
+ issues.append(f"{rel}: missing EN fenced block")
76
+ if not RU_BLOCK.search(text):
77
+ issues.append(f"{rel}: missing RU fenced block")
78
+ if len(EN_BLOCK.findall(text)) != example_count:
79
+ issues.append(f"{rel}: EN block count must match example count")
80
+ if len(RU_BLOCK.findall(text)) != example_count:
81
+ issues.append(f"{rel}: RU block count must match example count")
82
+ if "Workflow link command:" in text:
83
+ issues.append(f"{rel}: legacy `Workflow link command:` block is not allowed")
84
+ if any(token in text for token in PLACEHOLDER_STRINGS):
85
+ issues.append(f"{rel}: placeholder or generic scaffold text remains")
86
+ if workflow and path.stem != workflow:
87
+ issues.append(f"{rel}: prompt filename must match workflow stem `{workflow}`")
88
+
89
+ refs = {m.group(1) for m in COMMAND_REF_RE.finditer(text)}
90
+ area_stems = workflow_stems_for_prompt(path)
91
+ if workflow:
92
+ if workflow not in area_stems:
93
+ issues.append(f"{rel}: workflow `{workflow}` not found in sibling workflows/")
94
+ if not refs:
95
+ issues.append(f"{rel}: no /<workflow-file-name> command reference found")
96
+ elif refs != {workflow}:
97
+ issues.append(f"{rel}: all slash commands must be canonical `/{workflow}` (found: {', '.join(sorted(refs))})")
98
+ elif area_stems and not (refs & area_stems):
99
+ issues.append(f"{rel}: no /<workflow-file-name> command reference found")
100
+
101
+
102
+ if issues:
103
+ print("Prompt lint issues:")
104
+ for issue in issues:
105
+ print(f"- {issue}")
106
+ return 1 if args.strict else 0
107
+
108
+ print("All prompts pass format checks.")
109
+ return 0
110
+
111
+
112
+ if __name__ == "__main__":
113
+ raise SystemExit(main())