ops-wiki-agent-kit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/.github/agents/docs-target-catalog.agent.md +52 -0
  2. package/.github/agents/docs-target-queue-from-catalog.agent.md +34 -0
  3. package/.github/agents/source-code-to-spec-documenter.agent.md +39 -0
  4. package/.github/agents/source-code-to-spec-reviewer.agent.md +51 -0
  5. package/.github/agents/source-code-to-system-ops-overview.agent.md +39 -0
  6. package/.github/prompts/00-generate-target-all-spec.prompt.md +35 -0
  7. package/.github/prompts/01-generate-target-foundation-spec.prompt.md +35 -0
  8. package/.github/prompts/02-generate-target-architecture-spec.prompt.md +35 -0
  9. package/.github/prompts/03-generate-target-ops-spec.prompt.md +35 -0
  10. package/.github/prompts/04-review-target-spec.prompt.md +24 -0
  11. package/.github/prompts/docs-target-catalog.prompt.md +32 -0
  12. package/.github/prompts/docs-target-queue-from-catalog.prompt.md +28 -0
  13. package/.github/prompts/generate-system-ops-overview.prompt.md +62 -0
  14. package/.github/skills/database-query/SKILL.md +140 -0
  15. package/.github/skills/database-query/references/client-commands.md +189 -0
  16. package/.github/skills/database-query/references/query-safety.md +109 -0
  17. package/.github/skills/database-query/scripts/find_db_config.py +273 -0
  18. package/.github/skills/docs-target-catalog/SKILL.md +194 -0
  19. package/.github/skills/docs-target-catalog/references/docs-target-queue-conversion.md +164 -0
  20. package/.github/skills/docs-target-catalog/references/entrypoint-source-patterns.md +83 -0
  21. package/.github/skills/docs-target-catalog/references/output-templates.md +168 -0
  22. package/.github/skills/docs-target-queue-from-catalog/SKILL.md +255 -0
  23. package/.github/skills/docs-target-queue-from-catalog/references/docs-target-queue-contract.md +125 -0
  24. package/.github/skills/docs-target-queue-from-catalog/references/metadata-acquisition-patterns.md +149 -0
  25. package/.github/skills/docs-target-queue-from-catalog/scripts/write_documentation_target_queue.py +527 -0
  26. package/.github/skills/source-code-to-ops-spec-guidelines/SKILL.md +128 -0
  27. package/.github/skills/source-code-to-ops-spec-guidelines/references/01-system-overview-and-business-scenarios-guideline.md +172 -0
  28. package/.github/skills/source-code-to-ops-spec-guidelines/references/02-core-architecture-flow-data-logic-guideline.md +637 -0
  29. package/.github/skills/source-code-to-ops-spec-guidelines/references/03-error-ops-scenario-coverage-guideline.md +533 -0
  30. package/.github/skills/source-code-to-ops-spec-guidelines/references/supporting-output-format-diagram-and-example-reference.md +523 -0
  31. package/.github/skills/source-code-to-spec-documenter/SKILL.md +80 -0
  32. package/.github/skills/source-code-to-spec-documenter/references/generation-handoff-contract.md +155 -0
  33. package/.github/skills/source-code-to-spec-documenter/references/generation-workflow.md +184 -0
  34. package/.github/skills/source-code-to-spec-documenter/references/source-tracing-rules.md +271 -0
  35. package/.github/skills/source-code-to-spec-documenter/references/target-queue-contract.md +78 -0
  36. package/.github/skills/source-code-to-spec-documenter/scripts/spec_queue.py +222 -0
  37. package/.github/skills/source-code-to-spec-tools/SKILL.md +117 -0
  38. package/.github/skills/source-code-to-spec-tools/references/repository-artifact-contract.md +122 -0
  39. package/.github/skills/source-code-to-spec-tools/references/target-queue-schema-contract.md +116 -0
  40. package/.github/skills/source-code-to-spec-tools/references/terminology-contract.md +121 -0
  41. package/.github/skills/source-code-to-spec-tools/scripts/catalog_query.py +324 -0
  42. package/.github/skills/source-code-to-spec-tools/scripts/queue_contract.py +210 -0
  43. package/.github/skills/source-code-to-spec-tools/scripts/source_lookup.py +360 -0
  44. package/.github/skills/source-code-to-spec-tools/scripts/target_query.py +407 -0
  45. package/.github/skills/source-code-to-system-ops-overview/SKILL.md +82 -0
  46. package/.github/skills/source-code-to-system-ops-overview/references/system-operations-overview-guideline.md +332 -0
  47. package/README.md +116 -0
  48. package/ops-wiki-agent-kit.js +173 -0
  49. package/package.json +22 -0
@@ -0,0 +1,360 @@
1
+ #!/usr/bin/env python3
2
+ import sys
3
+
4
+ sys.dont_write_bytecode = True
5
+
6
+ import argparse
7
+ import fnmatch
8
+ import json
9
+ import os
10
+ import re
11
+ from pathlib import Path
12
+
13
+
14
+ def configure_stdio():
15
+ for stream_name in ("stdout", "stderr"):
16
+ stream = getattr(sys, stream_name, None)
17
+ reconfigure = getattr(stream, "reconfigure", None)
18
+ if callable(reconfigure):
19
+ reconfigure(encoding="utf-8")
20
+
21
+
22
+ DEFAULT_EXCLUDE_DIRS = {
23
+ ".git",
24
+ ".hg",
25
+ ".svn",
26
+ ".idea",
27
+ ".vscode",
28
+ "__pycache__",
29
+ "bin",
30
+ "build",
31
+ "coverage",
32
+ "dist",
33
+ "node_modules",
34
+ "target",
35
+ }
36
+
37
+ DEFAULT_TEXT_EXTENSIONS = {
38
+ ".aspx",
39
+ ".bat",
40
+ ".c",
41
+ ".cfg",
42
+ ".conf",
43
+ ".cpp",
44
+ ".cs",
45
+ ".css",
46
+ ".csv",
47
+ ".env",
48
+ ".gradle",
49
+ ".groovy",
50
+ ".h",
51
+ ".html",
52
+ ".ini",
53
+ ".java",
54
+ ".js",
55
+ ".json",
56
+ ".jsp",
57
+ ".jsx",
58
+ ".kt",
59
+ ".md",
60
+ ".php",
61
+ ".properties",
62
+ ".ps1",
63
+ ".py",
64
+ ".rb",
65
+ ".razor",
66
+ ".sql",
67
+ ".ts",
68
+ ".tsx",
69
+ ".txt",
70
+ ".vb",
71
+ ".vue",
72
+ ".xml",
73
+ ".yaml",
74
+ ".yml",
75
+ }
76
+
77
+
78
+ def normalize_text(value):
79
+ return "" if value is None else str(value)
80
+
81
+
82
+ def should_skip_dir(path, exclude_dirs):
83
+ return any(part in exclude_dirs for part in path.parts)
84
+
85
+
86
+ def matches_any_glob(relative_path, patterns):
87
+ if not patterns:
88
+ return True
89
+ value = relative_path.as_posix()
90
+ return any(fnmatch.fnmatch(value, pattern) for pattern in patterns)
91
+
92
+
93
+ def is_probably_text(path):
94
+ if path.suffix.lower() in DEFAULT_TEXT_EXTENSIONS:
95
+ return True
96
+ try:
97
+ with path.open("rb") as handle:
98
+ chunk = handle.read(2048)
99
+ except OSError:
100
+ return False
101
+ return b"\x00" not in chunk
102
+
103
+
104
+ def read_text(path):
105
+ encodings = ("utf-8-sig", "utf-8", "cp950", "latin-1")
106
+ last_error = None
107
+ for encoding in encodings:
108
+ try:
109
+ return path.read_text(encoding=encoding), encoding
110
+ except UnicodeDecodeError as exc:
111
+ last_error = exc
112
+ except OSError as exc:
113
+ raise exc
114
+ raise last_error
115
+
116
+
117
+ def iter_candidate_files(root, include_patterns, exclude_patterns, exclude_dirs):
118
+ for current_root, dirnames, filenames in os.walk(root):
119
+ dirnames[:] = [dirname for dirname in dirnames if dirname not in exclude_dirs]
120
+ current_path = Path(current_root)
121
+ for filename in filenames:
122
+ path = current_path / filename
123
+ if not path.is_file():
124
+ continue
125
+ relative_path = path.relative_to(root)
126
+ if not matches_any_glob(relative_path, include_patterns):
127
+ continue
128
+ if exclude_patterns and matches_any_glob(relative_path, exclude_patterns):
129
+ continue
130
+ if is_probably_text(path):
131
+ yield path, relative_path
132
+
133
+
134
+ def compile_matchers(handles, regex_mode, ignore_case):
135
+ flags = re.IGNORECASE if ignore_case else 0
136
+ matchers = []
137
+ for handle in handles:
138
+ if regex_mode:
139
+ pattern = re.compile(handle, flags)
140
+ matchers.append((handle, pattern))
141
+ else:
142
+ pattern = re.compile(re.escape(handle), flags)
143
+ matchers.append((handle, pattern))
144
+ return matchers
145
+
146
+
147
+ def file_has_all_handles(text, matchers):
148
+ return all(pattern.search(text) for _, pattern in matchers)
149
+
150
+
151
+ def format_relative_path(relative_path):
152
+ return relative_path.as_posix()
153
+
154
+
155
+ def build_evidence_ref(relative_path, line, matched_handles, kind, symbol):
156
+ symbol_value = symbol or ", ".join(matched_handles)
157
+ return {
158
+ "path": format_relative_path(relative_path),
159
+ "line": line,
160
+ "symbol": symbol_value,
161
+ "kind": kind,
162
+ }
163
+
164
+
165
+ def search_file(path, relative_path, matchers, context, mode, evidence_options):
166
+ try:
167
+ text, encoding = read_text(path)
168
+ except OSError as exc:
169
+ return [], {"path": format_relative_path(relative_path), "error": str(exc)}
170
+
171
+ if mode == "all" and not file_has_all_handles(text, matchers):
172
+ return [], None
173
+
174
+ lines = text.splitlines()
175
+ matches = []
176
+ for line_index, line in enumerate(lines, 1):
177
+ matched_handles = [label for label, pattern in matchers if pattern.search(line)]
178
+ if not matched_handles:
179
+ continue
180
+ start = max(1, line_index - context)
181
+ end = min(len(lines), line_index + context)
182
+ evidence_ref = build_evidence_ref(
183
+ relative_path,
184
+ line_index,
185
+ matched_handles,
186
+ evidence_options["kind"],
187
+ evidence_options["symbol"],
188
+ )
189
+ matches.append(
190
+ {
191
+ "path": format_relative_path(relative_path),
192
+ "line": line_index,
193
+ "handles": matched_handles,
194
+ "evidence_ref": evidence_ref,
195
+ "text": line.strip(),
196
+ "context": [
197
+ {
198
+ "line": number,
199
+ "text": lines[number - 1].rstrip(),
200
+ }
201
+ for number in range(start, end + 1)
202
+ ],
203
+ "encoding": encoding,
204
+ }
205
+ )
206
+ return matches, None
207
+
208
+
209
+ def search(args):
210
+ root = Path(args.root).resolve()
211
+ if not root.exists():
212
+ raise SystemExit(f"Root not found: {root}")
213
+ if not args.handle:
214
+ raise SystemExit("At least one --handle is required.")
215
+
216
+ include_patterns = args.include or []
217
+ exclude_patterns = args.exclude or []
218
+ exclude_dirs = set(DEFAULT_EXCLUDE_DIRS)
219
+ exclude_dirs.update(args.exclude_dir or [])
220
+ matchers = compile_matchers(args.handle, args.regex, args.ignore_case)
221
+ evidence_options = {
222
+ "kind": args.evidence_kind,
223
+ "symbol": args.evidence_symbol,
224
+ }
225
+
226
+ matches = []
227
+ errors = []
228
+ files_scanned = 0
229
+ files_with_matches = set()
230
+ for path, relative_path in iter_candidate_files(root, include_patterns, exclude_patterns, exclude_dirs):
231
+ files_scanned += 1
232
+ file_matches, error = search_file(path, relative_path, matchers, args.context, args.mode, evidence_options)
233
+ if error:
234
+ errors.append(error)
235
+ continue
236
+ if file_matches:
237
+ files_with_matches.add(format_relative_path(relative_path))
238
+ matches.extend(file_matches)
239
+ if len(matches) >= args.max_results:
240
+ matches = matches[: args.max_results]
241
+ break
242
+
243
+ return {
244
+ "root": str(root),
245
+ "handles": args.handle,
246
+ "mode": args.mode,
247
+ "regex": args.regex,
248
+ "ignore_case": args.ignore_case,
249
+ "files_scanned": files_scanned,
250
+ "files_with_matches": len(files_with_matches),
251
+ "match_count": len(matches),
252
+ "matches": matches,
253
+ "evidence_refs": [item["evidence_ref"] for item in matches],
254
+ "errors": errors,
255
+ }
256
+
257
+
258
+ def markdown_escape(value):
259
+ return normalize_text(value).replace("|", "\\|")
260
+
261
+
262
+ def render_markdown(payload):
263
+ rows = []
264
+ for item in payload["matches"]:
265
+ rows.append(
266
+ {
267
+ "path": item["path"],
268
+ "line": str(item["line"]),
269
+ "handles": ", ".join(item["handles"]),
270
+ "text": item["text"],
271
+ }
272
+ )
273
+ if not rows:
274
+ return "(no matches)"
275
+ columns = ["path", "line", "handles", "text"]
276
+ lines = []
277
+ lines.append("| " + " | ".join(columns) + " |")
278
+ lines.append("| " + " | ".join("---" for _ in columns) + " |")
279
+ for row in rows:
280
+ lines.append("| " + " | ".join(markdown_escape(row[column]) for column in columns) + " |")
281
+ return "\n".join(lines)
282
+
283
+
284
+ def build_parser():
285
+ parser = argparse.ArgumentParser(
286
+ description="Search source code handles and return reusable evidence refs with path and line numbers."
287
+ )
288
+ parser.add_argument("--root", default=".", help="Source root to scan.")
289
+ parser.add_argument("--handle", action="append", help="Literal string or regex to search. Repeatable.")
290
+ parser.add_argument("--regex", action="store_true", help="Treat --handle values as regular expressions.")
291
+ parser.add_argument("--ignore-case", action="store_true")
292
+ parser.add_argument(
293
+ "--mode",
294
+ choices=("any", "all"),
295
+ default="any",
296
+ help="any returns lines matching any handle; all only returns matches from files containing every handle.",
297
+ )
298
+ parser.add_argument("--include", action="append", help="Glob relative to root, e.g. **/*.java. Repeatable.")
299
+ parser.add_argument("--exclude", action="append", help="File glob relative to root. Repeatable.")
300
+ parser.add_argument("--exclude-dir", action="append", help="Directory name to skip. Repeatable.")
301
+ parser.add_argument("--context", type=int, default=0, help="Context lines before and after each match.")
302
+ parser.add_argument("--max-results", type=int, default=100)
303
+ parser.add_argument("--output", choices=("json", "markdown"), default="json")
304
+ parser.add_argument(
305
+ "--evidence-kind",
306
+ choices=(
307
+ "entrypoint",
308
+ "route",
309
+ "handler",
310
+ "class",
311
+ "method",
312
+ "SQL",
313
+ "table",
314
+ "config",
315
+ "job",
316
+ "procedure",
317
+ "routine",
318
+ "file",
319
+ "test",
320
+ "runtime_metadata",
321
+ "generated_doc",
322
+ "external_contract",
323
+ "other",
324
+ ),
325
+ default="other",
326
+ help="EvidenceRef kind assigned to matches.",
327
+ )
328
+ parser.add_argument(
329
+ "--evidence-confidence",
330
+ choices=("high", "medium", "low"),
331
+ default=None,
332
+ help=argparse.SUPPRESS,
333
+ )
334
+ parser.add_argument(
335
+ "--evidence-reason",
336
+ default=None,
337
+ help=argparse.SUPPRESS,
338
+ )
339
+ parser.add_argument(
340
+ "--evidence-symbol",
341
+ help="EvidenceRef symbol override. Defaults to the matched handle list.",
342
+ )
343
+ return parser
344
+
345
+
346
+ def main():
347
+ configure_stdio()
348
+ args = build_parser().parse_args()
349
+ payload = search(args)
350
+ if args.output == "json":
351
+ print(json.dumps(payload, ensure_ascii=False, indent=2))
352
+ else:
353
+ print(render_markdown(payload))
354
+
355
+
356
+ if __name__ == "__main__":
357
+ try:
358
+ main()
359
+ except BrokenPipeError:
360
+ sys.exit(1)