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.
- package/.github/agents/docs-target-catalog.agent.md +52 -0
- package/.github/agents/docs-target-queue-from-catalog.agent.md +34 -0
- package/.github/agents/source-code-to-spec-documenter.agent.md +39 -0
- package/.github/agents/source-code-to-spec-reviewer.agent.md +51 -0
- package/.github/agents/source-code-to-system-ops-overview.agent.md +39 -0
- package/.github/prompts/00-generate-target-all-spec.prompt.md +35 -0
- package/.github/prompts/01-generate-target-foundation-spec.prompt.md +35 -0
- package/.github/prompts/02-generate-target-architecture-spec.prompt.md +35 -0
- package/.github/prompts/03-generate-target-ops-spec.prompt.md +35 -0
- package/.github/prompts/04-review-target-spec.prompt.md +24 -0
- package/.github/prompts/docs-target-catalog.prompt.md +32 -0
- package/.github/prompts/docs-target-queue-from-catalog.prompt.md +28 -0
- package/.github/prompts/generate-system-ops-overview.prompt.md +62 -0
- package/.github/skills/database-query/SKILL.md +140 -0
- package/.github/skills/database-query/references/client-commands.md +189 -0
- package/.github/skills/database-query/references/query-safety.md +109 -0
- package/.github/skills/database-query/scripts/find_db_config.py +273 -0
- package/.github/skills/docs-target-catalog/SKILL.md +194 -0
- package/.github/skills/docs-target-catalog/references/docs-target-queue-conversion.md +164 -0
- package/.github/skills/docs-target-catalog/references/entrypoint-source-patterns.md +83 -0
- package/.github/skills/docs-target-catalog/references/output-templates.md +168 -0
- package/.github/skills/docs-target-queue-from-catalog/SKILL.md +255 -0
- package/.github/skills/docs-target-queue-from-catalog/references/docs-target-queue-contract.md +125 -0
- package/.github/skills/docs-target-queue-from-catalog/references/metadata-acquisition-patterns.md +149 -0
- package/.github/skills/docs-target-queue-from-catalog/scripts/write_documentation_target_queue.py +527 -0
- package/.github/skills/source-code-to-ops-spec-guidelines/SKILL.md +128 -0
- package/.github/skills/source-code-to-ops-spec-guidelines/references/01-system-overview-and-business-scenarios-guideline.md +172 -0
- package/.github/skills/source-code-to-ops-spec-guidelines/references/02-core-architecture-flow-data-logic-guideline.md +637 -0
- package/.github/skills/source-code-to-ops-spec-guidelines/references/03-error-ops-scenario-coverage-guideline.md +533 -0
- package/.github/skills/source-code-to-ops-spec-guidelines/references/supporting-output-format-diagram-and-example-reference.md +523 -0
- package/.github/skills/source-code-to-spec-documenter/SKILL.md +80 -0
- package/.github/skills/source-code-to-spec-documenter/references/generation-handoff-contract.md +155 -0
- package/.github/skills/source-code-to-spec-documenter/references/generation-workflow.md +184 -0
- package/.github/skills/source-code-to-spec-documenter/references/source-tracing-rules.md +271 -0
- package/.github/skills/source-code-to-spec-documenter/references/target-queue-contract.md +78 -0
- package/.github/skills/source-code-to-spec-documenter/scripts/spec_queue.py +222 -0
- package/.github/skills/source-code-to-spec-tools/SKILL.md +117 -0
- package/.github/skills/source-code-to-spec-tools/references/repository-artifact-contract.md +122 -0
- package/.github/skills/source-code-to-spec-tools/references/target-queue-schema-contract.md +116 -0
- package/.github/skills/source-code-to-spec-tools/references/terminology-contract.md +121 -0
- package/.github/skills/source-code-to-spec-tools/scripts/catalog_query.py +324 -0
- package/.github/skills/source-code-to-spec-tools/scripts/queue_contract.py +210 -0
- package/.github/skills/source-code-to-spec-tools/scripts/source_lookup.py +360 -0
- package/.github/skills/source-code-to-spec-tools/scripts/target_query.py +407 -0
- package/.github/skills/source-code-to-system-ops-overview/SKILL.md +82 -0
- package/.github/skills/source-code-to-system-ops-overview/references/system-operations-overview-guideline.md +332 -0
- package/README.md +116 -0
- package/ops-wiki-agent-kit.js +173 -0
- 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)
|