opencode-arch 1.0.0__py3-none-any.whl
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.
- opencode_arch/__init__.py +3 -0
- opencode_arch/artifacts/__init__.py +48 -0
- opencode_arch/artifacts/context.py +451 -0
- opencode_arch/artifacts/diagrams.py +451 -0
- opencode_arch/artifacts/selector.py +331 -0
- opencode_arch/artifacts/templates.py +444 -0
- opencode_arch/cli/__init__.py +1 -0
- opencode_arch/cli/bench.py +25 -0
- opencode_arch/cli/calibrate.py +208 -0
- opencode_arch/cli/confidence.py +66 -0
- opencode_arch/cli/docs.py +333 -0
- opencode_arch/cli/docs_validator.py +295 -0
- opencode_arch/cli/export_data.py +133 -0
- opencode_arch/cli/extract.py +93 -0
- opencode_arch/cli/gap_analyzer.py +107 -0
- opencode_arch/cli/generate.py +68 -0
- opencode_arch/cli/launch.py +264 -0
- opencode_arch/cli/main.py +360 -0
- opencode_arch/cli/metrics.py +186 -0
- opencode_arch/cli/prompts.py +20 -0
- opencode_arch/cli/regen_loop.py +1028 -0
- opencode_arch/context/__init__.py +29 -0
- opencode_arch/context/formatter.py +492 -0
- opencode_arch/context/pipeline_bridge.py +201 -0
- opencode_arch/extract/__init__.py +8 -0
- opencode_arch/extract/constraint_detector.py +398 -0
- opencode_arch/extract/from_artifacts.py +837 -0
- opencode_arch/extract/from_code.py +646 -0
- opencode_arch/extract/route_detector.py +400 -0
- opencode_arch/extract/table_parser.py +177 -0
- opencode_arch/learning/__init__.py +19 -0
- opencode_arch/learning/adapter.py +157 -0
- opencode_arch/learning/assessor.py +170 -0
- opencode_arch/learning/classifier.py +144 -0
- opencode_arch/learning/lessons.py +139 -0
- opencode_arch/learning/maintainer.py +281 -0
- opencode_arch/learning/patterns.py +51 -0
- opencode_arch/mcp/__init__.py +1 -0
- opencode_arch/mcp/__main__.py +8 -0
- opencode_arch/mcp/server.py +183 -0
- opencode_arch/mcp/tools/__init__.py +1 -0
- opencode_arch/mcp/tools/check.py +159 -0
- opencode_arch/mcp/tools/extract.py +107 -0
- opencode_arch/mcp/tools/feedback.py +65 -0
- opencode_arch/mcp/tools/generate.py +104 -0
- opencode_arch/mcp/tools/group.py +62 -0
- opencode_arch/mcp/tools/ingest.py +101 -0
- opencode_arch/mcp/tools/require.py +77 -0
- opencode_arch/mcp/tools/scan.py +53 -0
- opencode_arch/mcp/tools/slice.py +235 -0
- opencode_arch/mcp/tools/validate.py +59 -0
- opencode_arch/prompts/__init__.py +1 -0
- opencode_arch/prompts/regen.py +36 -0
- opencode_arch/runner/__init__.py +5 -0
- opencode_arch/runner/base.py +21 -0
- opencode_arch/runner/opencode.py +66 -0
- opencode_arch/telemetry/__init__.py +6 -0
- opencode_arch/telemetry/collector.py +40 -0
- opencode_arch/telemetry/recorder.py +12 -0
- opencode_arch/telemetry/store.py +537 -0
- opencode_arch-1.0.0.dist-info/METADATA +247 -0
- opencode_arch-1.0.0.dist-info/RECORD +65 -0
- opencode_arch-1.0.0.dist-info/WHEEL +4 -0
- opencode_arch-1.0.0.dist-info/entry_points.txt +2 -0
- opencode_arch-1.0.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
"""Artifact template definitions.
|
|
2
|
+
|
|
3
|
+
Each template describes HOW to generate a specific SE documentation artifact:
|
|
4
|
+
- What sections it has
|
|
5
|
+
- What model/manifest data feeds each section
|
|
6
|
+
- What instructions the LLM follows for each section
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class TemplateSection:
|
|
16
|
+
"""A single section within an artifact template."""
|
|
17
|
+
|
|
18
|
+
heading: str # e.g. "## Overview", "## Endpoints"
|
|
19
|
+
source: str # which model/manifest data feeds this section
|
|
20
|
+
instructions: str # LLM instructions for generating this section content
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class ArtifactTemplate:
|
|
25
|
+
"""Full template for generating one artifact."""
|
|
26
|
+
|
|
27
|
+
artifact_id: str # matches ArtifactSpec.id from selector.py
|
|
28
|
+
filename: str # output filename, e.g. "api-reference.md"
|
|
29
|
+
sections: list[TemplateSection]
|
|
30
|
+
system_prompt: str # role/context prompt for the LLM
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# ---------------------------------------------------------------------------
|
|
34
|
+
# Template Definitions — one per artifact in ARTIFACT_REGISTRY
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
TEMPLATES: dict[str, ArtifactTemplate] = {
|
|
38
|
+
"system-overview": ArtifactTemplate(
|
|
39
|
+
artifact_id="system-overview",
|
|
40
|
+
filename="system-overview.md",
|
|
41
|
+
system_prompt=(
|
|
42
|
+
"You are a technical writer documenting a software system's architecture. "
|
|
43
|
+
"Write clear, precise documentation based on the provided architecture model data."
|
|
44
|
+
),
|
|
45
|
+
sections=[
|
|
46
|
+
TemplateSection(
|
|
47
|
+
heading="## System Purpose",
|
|
48
|
+
source="meta",
|
|
49
|
+
instructions=(
|
|
50
|
+
"Describe what this system does based on the project metadata. "
|
|
51
|
+
"State the project name and its primary purpose."
|
|
52
|
+
),
|
|
53
|
+
),
|
|
54
|
+
TemplateSection(
|
|
55
|
+
heading="## Architecture Overview",
|
|
56
|
+
source="layers",
|
|
57
|
+
instructions=(
|
|
58
|
+
"Describe the high-level architecture using the layer structure. "
|
|
59
|
+
"If no layers, describe based on component organization."
|
|
60
|
+
),
|
|
61
|
+
),
|
|
62
|
+
TemplateSection(
|
|
63
|
+
heading="## Key Components",
|
|
64
|
+
source="components",
|
|
65
|
+
instructions=(
|
|
66
|
+
"List and briefly describe each major component, its role, "
|
|
67
|
+
"and technology."
|
|
68
|
+
),
|
|
69
|
+
),
|
|
70
|
+
TemplateSection(
|
|
71
|
+
heading="## Key Relationships",
|
|
72
|
+
source="relationships",
|
|
73
|
+
instructions=(
|
|
74
|
+
"Describe how components interact, using the relationship data."
|
|
75
|
+
),
|
|
76
|
+
),
|
|
77
|
+
],
|
|
78
|
+
),
|
|
79
|
+
"component-catalog": ArtifactTemplate(
|
|
80
|
+
artifact_id="component-catalog",
|
|
81
|
+
filename="component-catalog.md",
|
|
82
|
+
system_prompt=(
|
|
83
|
+
"You are a technical writer creating a component reference catalog. "
|
|
84
|
+
"Be precise and include all components with their details."
|
|
85
|
+
),
|
|
86
|
+
sections=[
|
|
87
|
+
TemplateSection(
|
|
88
|
+
heading="## Components",
|
|
89
|
+
source="components",
|
|
90
|
+
instructions=(
|
|
91
|
+
"For each component, document: name, kind, layer, status, files, "
|
|
92
|
+
"and responsibilities. Use a consistent format."
|
|
93
|
+
),
|
|
94
|
+
),
|
|
95
|
+
TemplateSection(
|
|
96
|
+
heading="## Component Dependencies",
|
|
97
|
+
source="relationships",
|
|
98
|
+
instructions=(
|
|
99
|
+
"Document depends-on relationships between components "
|
|
100
|
+
"as a dependency list."
|
|
101
|
+
),
|
|
102
|
+
),
|
|
103
|
+
],
|
|
104
|
+
),
|
|
105
|
+
"api-reference": ArtifactTemplate(
|
|
106
|
+
artifact_id="api-reference",
|
|
107
|
+
filename="api-reference.md",
|
|
108
|
+
system_prompt=(
|
|
109
|
+
"You are a technical writer creating API documentation. "
|
|
110
|
+
"Focus on precision — exact endpoint paths, methods, data formats."
|
|
111
|
+
),
|
|
112
|
+
sections=[
|
|
113
|
+
TemplateSection(
|
|
114
|
+
heading="## Interfaces",
|
|
115
|
+
source="interfaces",
|
|
116
|
+
instructions=(
|
|
117
|
+
"For each interface, document: name, type, protocol, provider, "
|
|
118
|
+
"consumer, endpoints, and data format."
|
|
119
|
+
),
|
|
120
|
+
),
|
|
121
|
+
TemplateSection(
|
|
122
|
+
heading="## Data Contracts",
|
|
123
|
+
source="interfaces",
|
|
124
|
+
instructions=(
|
|
125
|
+
"Document the data schemas and contracts for each interface."
|
|
126
|
+
),
|
|
127
|
+
),
|
|
128
|
+
],
|
|
129
|
+
),
|
|
130
|
+
"capability-map": ArtifactTemplate(
|
|
131
|
+
artifact_id="capability-map",
|
|
132
|
+
filename="capability-map.md",
|
|
133
|
+
system_prompt=(
|
|
134
|
+
"You are a technical writer documenting system capabilities. "
|
|
135
|
+
"Focus on WHAT the system can do, not HOW."
|
|
136
|
+
),
|
|
137
|
+
sections=[
|
|
138
|
+
TemplateSection(
|
|
139
|
+
heading="## Capabilities",
|
|
140
|
+
source="capabilities",
|
|
141
|
+
instructions=(
|
|
142
|
+
"List each capability with its priority, requirements, "
|
|
143
|
+
"and which components realize it."
|
|
144
|
+
),
|
|
145
|
+
),
|
|
146
|
+
TemplateSection(
|
|
147
|
+
heading="## Capability-Component Mapping",
|
|
148
|
+
source="relationships",
|
|
149
|
+
instructions=(
|
|
150
|
+
"Show which components realize which capabilities "
|
|
151
|
+
"using relationship data."
|
|
152
|
+
),
|
|
153
|
+
),
|
|
154
|
+
],
|
|
155
|
+
),
|
|
156
|
+
"behavior-flows": ArtifactTemplate(
|
|
157
|
+
artifact_id="behavior-flows",
|
|
158
|
+
filename="behavior-flows.md",
|
|
159
|
+
system_prompt=(
|
|
160
|
+
"You are a technical writer documenting system workflows and behaviors. "
|
|
161
|
+
"Describe step-by-step flows clearly."
|
|
162
|
+
),
|
|
163
|
+
sections=[
|
|
164
|
+
TemplateSection(
|
|
165
|
+
heading="## Behaviors",
|
|
166
|
+
source="behaviors",
|
|
167
|
+
instructions=(
|
|
168
|
+
"For each behavior, document: trigger, actor, preconditions, "
|
|
169
|
+
"steps, postconditions, pattern type."
|
|
170
|
+
),
|
|
171
|
+
),
|
|
172
|
+
TemplateSection(
|
|
173
|
+
heading="## Interaction Sequences",
|
|
174
|
+
source="behaviors",
|
|
175
|
+
instructions=(
|
|
176
|
+
"Describe the sequence of interactions for key behaviors."
|
|
177
|
+
),
|
|
178
|
+
),
|
|
179
|
+
],
|
|
180
|
+
),
|
|
181
|
+
"constraint-register": ArtifactTemplate(
|
|
182
|
+
artifact_id="constraint-register",
|
|
183
|
+
filename="constraint-register.md",
|
|
184
|
+
system_prompt=(
|
|
185
|
+
"You are a technical writer documenting non-functional requirements "
|
|
186
|
+
"and design constraints."
|
|
187
|
+
),
|
|
188
|
+
sections=[
|
|
189
|
+
TemplateSection(
|
|
190
|
+
heading="## Constraints",
|
|
191
|
+
source="constraints",
|
|
192
|
+
instructions=(
|
|
193
|
+
"For each constraint, document: type, metric, threshold, rationale."
|
|
194
|
+
),
|
|
195
|
+
),
|
|
196
|
+
TemplateSection(
|
|
197
|
+
heading="## Constraint Allocation",
|
|
198
|
+
source="relationships",
|
|
199
|
+
instructions=(
|
|
200
|
+
"Show which components are constrained by which constraints."
|
|
201
|
+
),
|
|
202
|
+
),
|
|
203
|
+
],
|
|
204
|
+
),
|
|
205
|
+
"dependency-graph": ArtifactTemplate(
|
|
206
|
+
artifact_id="dependency-graph",
|
|
207
|
+
filename="dependency-graph.md",
|
|
208
|
+
system_prompt=(
|
|
209
|
+
"You are a technical writer documenting system dependencies and coupling."
|
|
210
|
+
),
|
|
211
|
+
sections=[
|
|
212
|
+
TemplateSection(
|
|
213
|
+
heading="## Direct Dependencies",
|
|
214
|
+
source="relationships",
|
|
215
|
+
instructions=(
|
|
216
|
+
"List all depends-on relationships. Group by source component."
|
|
217
|
+
),
|
|
218
|
+
),
|
|
219
|
+
TemplateSection(
|
|
220
|
+
heading="## Dependency Analysis",
|
|
221
|
+
source="relationships",
|
|
222
|
+
instructions=(
|
|
223
|
+
"Identify highly-coupled components, potential circular "
|
|
224
|
+
"dependencies, and suggest improvements."
|
|
225
|
+
),
|
|
226
|
+
),
|
|
227
|
+
],
|
|
228
|
+
),
|
|
229
|
+
"layer-architecture": ArtifactTemplate(
|
|
230
|
+
artifact_id="layer-architecture",
|
|
231
|
+
filename="layer-architecture.md",
|
|
232
|
+
system_prompt=(
|
|
233
|
+
"You are a technical writer documenting the layered architecture "
|
|
234
|
+
"of the system."
|
|
235
|
+
),
|
|
236
|
+
sections=[
|
|
237
|
+
TemplateSection(
|
|
238
|
+
heading="## Layers",
|
|
239
|
+
source="layers",
|
|
240
|
+
instructions=(
|
|
241
|
+
"For each layer, document: name, order, technology stack, "
|
|
242
|
+
"directories, and contained components."
|
|
243
|
+
),
|
|
244
|
+
),
|
|
245
|
+
TemplateSection(
|
|
246
|
+
heading="## Layer Interactions",
|
|
247
|
+
source="relationships",
|
|
248
|
+
instructions=(
|
|
249
|
+
"Describe how layers communicate. Note any violations "
|
|
250
|
+
"of layer ordering."
|
|
251
|
+
),
|
|
252
|
+
),
|
|
253
|
+
],
|
|
254
|
+
),
|
|
255
|
+
"deployment-view": ArtifactTemplate(
|
|
256
|
+
artifact_id="deployment-view",
|
|
257
|
+
filename="deployment-view.md",
|
|
258
|
+
system_prompt=(
|
|
259
|
+
"You are a technical writer documenting deployment topology "
|
|
260
|
+
"and operational concerns."
|
|
261
|
+
),
|
|
262
|
+
sections=[
|
|
263
|
+
TemplateSection(
|
|
264
|
+
heading="## Deployment Units",
|
|
265
|
+
source="components",
|
|
266
|
+
instructions=(
|
|
267
|
+
"Group components by their deployment unit (layer + kind). "
|
|
268
|
+
"Describe what gets deployed together."
|
|
269
|
+
),
|
|
270
|
+
),
|
|
271
|
+
TemplateSection(
|
|
272
|
+
heading="## Operational Requirements",
|
|
273
|
+
source="constraints",
|
|
274
|
+
instructions=(
|
|
275
|
+
"List performance, reliability, and operational constraints "
|
|
276
|
+
"relevant to deployment."
|
|
277
|
+
),
|
|
278
|
+
),
|
|
279
|
+
],
|
|
280
|
+
),
|
|
281
|
+
"integration-guide": ArtifactTemplate(
|
|
282
|
+
artifact_id="integration-guide",
|
|
283
|
+
filename="integration-guide.md",
|
|
284
|
+
system_prompt=(
|
|
285
|
+
"You are a technical writer creating integration documentation "
|
|
286
|
+
"for developers connecting to this system."
|
|
287
|
+
),
|
|
288
|
+
sections=[
|
|
289
|
+
TemplateSection(
|
|
290
|
+
heading="## Available Interfaces",
|
|
291
|
+
source="interfaces",
|
|
292
|
+
instructions=(
|
|
293
|
+
"List all interfaces available for integration with protocol "
|
|
294
|
+
"and data format details."
|
|
295
|
+
),
|
|
296
|
+
),
|
|
297
|
+
TemplateSection(
|
|
298
|
+
heading="## Integration Patterns",
|
|
299
|
+
source="components",
|
|
300
|
+
instructions=(
|
|
301
|
+
"Describe recommended patterns for integrating with "
|
|
302
|
+
"each component."
|
|
303
|
+
),
|
|
304
|
+
),
|
|
305
|
+
TemplateSection(
|
|
306
|
+
heading="## Authentication & Constraints",
|
|
307
|
+
source="constraints",
|
|
308
|
+
instructions=(
|
|
309
|
+
"Document security constraints and authentication requirements."
|
|
310
|
+
),
|
|
311
|
+
),
|
|
312
|
+
],
|
|
313
|
+
),
|
|
314
|
+
"test-strategy": ArtifactTemplate(
|
|
315
|
+
artifact_id="test-strategy",
|
|
316
|
+
filename="test-strategy.md",
|
|
317
|
+
system_prompt=(
|
|
318
|
+
"You are a technical writer documenting the testing approach "
|
|
319
|
+
"for this system."
|
|
320
|
+
),
|
|
321
|
+
sections=[
|
|
322
|
+
TemplateSection(
|
|
323
|
+
heading="## Test Inventory",
|
|
324
|
+
source="manifest.tests",
|
|
325
|
+
instructions=(
|
|
326
|
+
"Summarize the test files, count, and coverage distribution "
|
|
327
|
+
"across components."
|
|
328
|
+
),
|
|
329
|
+
),
|
|
330
|
+
TemplateSection(
|
|
331
|
+
heading="## Testing Approach",
|
|
332
|
+
source="components",
|
|
333
|
+
instructions=(
|
|
334
|
+
"For each component, describe the testing strategy based on "
|
|
335
|
+
"its kind and responsibilities."
|
|
336
|
+
),
|
|
337
|
+
),
|
|
338
|
+
],
|
|
339
|
+
),
|
|
340
|
+
"metrics-dashboard": ArtifactTemplate(
|
|
341
|
+
artifact_id="metrics-dashboard",
|
|
342
|
+
filename="metrics-dashboard.md",
|
|
343
|
+
system_prompt=(
|
|
344
|
+
"You are a technical writer summarizing code quality metrics."
|
|
345
|
+
),
|
|
346
|
+
sections=[
|
|
347
|
+
TemplateSection(
|
|
348
|
+
heading="## Code Metrics",
|
|
349
|
+
source="manifest.metrics",
|
|
350
|
+
instructions=(
|
|
351
|
+
"Present the metrics data: lines of code, complexity, "
|
|
352
|
+
"file counts, module counts."
|
|
353
|
+
),
|
|
354
|
+
),
|
|
355
|
+
TemplateSection(
|
|
356
|
+
heading="## Quality Assessment",
|
|
357
|
+
source="manifest.metrics",
|
|
358
|
+
instructions=(
|
|
359
|
+
"Assess overall code health based on the metrics. "
|
|
360
|
+
"Note any concerning areas."
|
|
361
|
+
),
|
|
362
|
+
),
|
|
363
|
+
],
|
|
364
|
+
),
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
# ---------------------------------------------------------------------------
|
|
369
|
+
# Per-Capability API Detail Template
|
|
370
|
+
# ---------------------------------------------------------------------------
|
|
371
|
+
|
|
372
|
+
API_DETAIL_TEMPLATE = ArtifactTemplate(
|
|
373
|
+
artifact_id="api-detail",
|
|
374
|
+
filename="api-detail-{cap_id}.md", # placeholder — resolved per capability
|
|
375
|
+
system_prompt=(
|
|
376
|
+
"You are a senior systems engineer writing detailed API documentation for a "
|
|
377
|
+
"single capability within a software architecture. Write precise, implementation-"
|
|
378
|
+
"grounded documentation. Include exact function signatures, parameters, return "
|
|
379
|
+
"types, algorithm steps, and behavioral sequences. Use tables and code blocks. "
|
|
380
|
+
"Do NOT hallucinate — only document what is present in the provided data."
|
|
381
|
+
),
|
|
382
|
+
sections=[
|
|
383
|
+
TemplateSection(
|
|
384
|
+
heading="## Overview",
|
|
385
|
+
source="capability_detail",
|
|
386
|
+
instructions=(
|
|
387
|
+
"Create a summary table with: ID, F-Block, Priority, Status, "
|
|
388
|
+
"Realized by (component + source path), Constraints, Actor. "
|
|
389
|
+
"Then one sentence describing the capability's purpose."
|
|
390
|
+
),
|
|
391
|
+
),
|
|
392
|
+
TemplateSection(
|
|
393
|
+
heading="## Primary API",
|
|
394
|
+
source="capability_detail",
|
|
395
|
+
instructions=(
|
|
396
|
+
"For each public function in the realizing component, document: "
|
|
397
|
+
"full signature, parameters (with types and defaults), return type, "
|
|
398
|
+
"algorithm steps (numbered), and example usage. Use the function "
|
|
399
|
+
"signatures, body_hints, and constants from the provided data."
|
|
400
|
+
),
|
|
401
|
+
),
|
|
402
|
+
TemplateSection(
|
|
403
|
+
heading="## Supporting Functions",
|
|
404
|
+
source="capability_detail",
|
|
405
|
+
instructions=(
|
|
406
|
+
"Document internal/helper functions. Show how they support the "
|
|
407
|
+
"primary API. Include signatures and brief descriptions."
|
|
408
|
+
),
|
|
409
|
+
),
|
|
410
|
+
TemplateSection(
|
|
411
|
+
heading="## Behavioral View",
|
|
412
|
+
source="capability_detail",
|
|
413
|
+
instructions=(
|
|
414
|
+
"For each related behavior, document: trigger, preconditions, "
|
|
415
|
+
"step sequence (as numbered list and ASCII sequence diagram), "
|
|
416
|
+
"postconditions, and exit codes/error handling."
|
|
417
|
+
),
|
|
418
|
+
),
|
|
419
|
+
TemplateSection(
|
|
420
|
+
heading="## Relationship Graph",
|
|
421
|
+
source="capability_detail",
|
|
422
|
+
instructions=(
|
|
423
|
+
"Show the capability's relationship neighborhood as an ASCII "
|
|
424
|
+
"diagram. Include: realizes, depends-on, exposes, constrained-by."
|
|
425
|
+
),
|
|
426
|
+
),
|
|
427
|
+
TemplateSection(
|
|
428
|
+
heading="## Data Contracts",
|
|
429
|
+
source="capability_detail",
|
|
430
|
+
instructions=(
|
|
431
|
+
"Document input/output data formats for interfaces. Include "
|
|
432
|
+
"return type fields as tables. Reference test contracts as "
|
|
433
|
+
"behavioral expectations."
|
|
434
|
+
),
|
|
435
|
+
),
|
|
436
|
+
],
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def get_template(artifact_id: str) -> ArtifactTemplate | None:
|
|
441
|
+
"""Look up template by artifact ID. Returns None if not found."""
|
|
442
|
+
if artifact_id.startswith("api-detail-"):
|
|
443
|
+
return API_DETAIL_TEMPLATE
|
|
444
|
+
return TEMPLATES.get(artifact_id)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI for opencode-arch."""
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Bench command - benchmark extraction on multiple repos."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from opencode_arch.runner.base import RunnerBackend
|
|
7
|
+
from opencode_arch.cli.extract import run_extract
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async def run_bench(
|
|
11
|
+
repos: list[str],
|
|
12
|
+
runner: RunnerBackend,
|
|
13
|
+
budget: int = 4000,
|
|
14
|
+
target_score: int = 80,
|
|
15
|
+
) -> list[dict[str, Any]]:
|
|
16
|
+
"""Run extraction benchmark on multiple repositories."""
|
|
17
|
+
results = []
|
|
18
|
+
for repo_path in repos:
|
|
19
|
+
result = await run_extract(
|
|
20
|
+
repo_path=repo_path, runner=runner,
|
|
21
|
+
budget=budget, target_score=target_score,
|
|
22
|
+
)
|
|
23
|
+
result["repo"] = repo_path
|
|
24
|
+
results.append(result)
|
|
25
|
+
return results
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"""Calibration command - spot-check confidence by attempting regeneration."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import ast
|
|
5
|
+
import math
|
|
6
|
+
import random
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from architecture_model.core.types import ArchitectureModel, Component
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class CalibrationReport:
|
|
16
|
+
"""Result of a calibration run."""
|
|
17
|
+
|
|
18
|
+
components_tested: int
|
|
19
|
+
correlation: float
|
|
20
|
+
bands: dict # confidence_band → regen_success_rate
|
|
21
|
+
threshold: float # recommended confidence for safe regeneration
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def select_calibration_targets(
|
|
25
|
+
model_or_components, *, n: int = 3, min_confidence: float = 0.7, count: int = 6
|
|
26
|
+
):
|
|
27
|
+
"""Select components for calibration.
|
|
28
|
+
|
|
29
|
+
Supports two calling conventions:
|
|
30
|
+
- Legacy: select_calibration_targets(model, n=3, min_confidence=0.7) with ArchitectureModel
|
|
31
|
+
- New: select_calibration_targets(components, count=6) with list[dict]
|
|
32
|
+
"""
|
|
33
|
+
if isinstance(model_or_components, list):
|
|
34
|
+
# New dict-based interface (Task 6)
|
|
35
|
+
components = model_or_components
|
|
36
|
+
if not components:
|
|
37
|
+
return []
|
|
38
|
+
sorted_comps = sorted(components, key=lambda c: c.get("confidence", 0))
|
|
39
|
+
num = len(sorted_comps)
|
|
40
|
+
if num <= count:
|
|
41
|
+
return sorted_comps
|
|
42
|
+
indices = [round(i * (num - 1) / (count - 1)) for i in range(count)]
|
|
43
|
+
return [sorted_comps[i] for i in indices]
|
|
44
|
+
else:
|
|
45
|
+
# Legacy ArchitectureModel interface
|
|
46
|
+
model = model_or_components
|
|
47
|
+
candidates = [c for c in model.entities.components if c.confidence >= min_confidence and c.files]
|
|
48
|
+
if not candidates:
|
|
49
|
+
candidates = [c for c in model.entities.components if c.files]
|
|
50
|
+
random.shuffle(candidates)
|
|
51
|
+
return candidates[:n]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def format_calibration_prompt(comp: Component) -> str:
|
|
55
|
+
"""Format a prompt asking the agent to regenerate a component from its model spec."""
|
|
56
|
+
sections = []
|
|
57
|
+
sections.append(f"## Regenerate: {comp.name} ({comp.id})")
|
|
58
|
+
sections.append("")
|
|
59
|
+
sections.append(f"**Contract:** {comp.contract or 'Not specified'}")
|
|
60
|
+
sections.append(f"**Pattern:** {comp.pattern or 'Not specified'}")
|
|
61
|
+
sections.append(f"**Files:** {', '.join(comp.files)}")
|
|
62
|
+
|
|
63
|
+
if comp.responsibilities:
|
|
64
|
+
sections.append(f"**Responsibilities:** {'; '.join(comp.responsibilities)}")
|
|
65
|
+
|
|
66
|
+
if comp.signatures:
|
|
67
|
+
sections.append("\n**Signatures:**")
|
|
68
|
+
for sig in comp.signatures:
|
|
69
|
+
params = ", ".join(sig.params) if sig.params else ""
|
|
70
|
+
ret = f" -> {sig.returns}" if sig.returns else ""
|
|
71
|
+
decorators = " ".join(f"@{d}" for d in sig.decorators) + " " if sig.decorators else ""
|
|
72
|
+
sections.append(f" {decorators}def {sig.name}({params}){ret}")
|
|
73
|
+
if sig.body_hint:
|
|
74
|
+
sections.append(f" # Hint: {sig.body_hint}")
|
|
75
|
+
|
|
76
|
+
if comp.symbols:
|
|
77
|
+
sections.append("\n**Classes:**")
|
|
78
|
+
for sym in comp.symbols:
|
|
79
|
+
bases = f"({', '.join(sym.supers)})" if sym.supers else ""
|
|
80
|
+
sections.append(f" class {sym.name}{bases}")
|
|
81
|
+
if sym.members:
|
|
82
|
+
for m in sym.members[:10]:
|
|
83
|
+
sections.append(f" - {m}")
|
|
84
|
+
|
|
85
|
+
if comp.constants:
|
|
86
|
+
sections.append("\n**Constants:**")
|
|
87
|
+
for const in comp.constants:
|
|
88
|
+
sections.append(f" {const.name} = {const.value}")
|
|
89
|
+
|
|
90
|
+
if comp.test_contracts:
|
|
91
|
+
sections.append("\n**Expected behavior (from tests):**")
|
|
92
|
+
for tc in comp.test_contracts[:5]:
|
|
93
|
+
sections.append(f" {tc.test_method}: {tc.assertion}")
|
|
94
|
+
|
|
95
|
+
sections.append("\n---")
|
|
96
|
+
sections.append("**Task:** Implement this component using ONLY the specification above.")
|
|
97
|
+
sections.append("Do NOT read source files. Generate the complete implementation.")
|
|
98
|
+
|
|
99
|
+
return "\n".join(sections)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def compare_regeneration(original: "str | Path", generated: str) -> dict:
|
|
103
|
+
"""Compare original and generated source code by public API coverage.
|
|
104
|
+
|
|
105
|
+
original can be a string of source code or a Path to a file.
|
|
106
|
+
"""
|
|
107
|
+
if isinstance(original, Path):
|
|
108
|
+
original = original.read_text()
|
|
109
|
+
|
|
110
|
+
def _extract_names(code: str) -> tuple[set[str], set[str]]:
|
|
111
|
+
try:
|
|
112
|
+
tree = ast.parse(code)
|
|
113
|
+
except SyntaxError:
|
|
114
|
+
return set(), set()
|
|
115
|
+
functions = {node.name for node in ast.walk(tree) if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))}
|
|
116
|
+
classes = {node.name for node in ast.walk(tree) if isinstance(node, ast.ClassDef)}
|
|
117
|
+
return functions, classes
|
|
118
|
+
|
|
119
|
+
orig_funcs, orig_classes = _extract_names(original)
|
|
120
|
+
gen_funcs, gen_classes = _extract_names(generated)
|
|
121
|
+
|
|
122
|
+
func_match = len(orig_funcs & gen_funcs) / len(orig_funcs) if orig_funcs else 1.0
|
|
123
|
+
class_match = len(orig_classes & gen_classes) / len(orig_classes) if orig_classes else 1.0
|
|
124
|
+
line_ratio = len(generated.splitlines()) / max(1, len(original.splitlines()))
|
|
125
|
+
|
|
126
|
+
covered = orig_funcs & gen_funcs
|
|
127
|
+
api_coverage = len(covered) / len(orig_funcs) if orig_funcs else 1.0
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
"function_match": func_match,
|
|
131
|
+
"class_match": class_match,
|
|
132
|
+
"line_ratio": line_ratio,
|
|
133
|
+
"original_functions": len(orig_funcs),
|
|
134
|
+
"generated_functions": len(gen_funcs),
|
|
135
|
+
"calibration_score": (func_match * 0.5 + class_match * 0.3 + min(line_ratio, 1.0) * 0.2),
|
|
136
|
+
"api_coverage": api_coverage,
|
|
137
|
+
"missing_apis": sorted(orig_funcs - gen_funcs),
|
|
138
|
+
"extra_apis": sorted(gen_funcs - orig_funcs),
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# --- Task 5: Regeneration Proof ---
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def format_regeneration_prompt(component_context: dict) -> str:
|
|
146
|
+
"""Format a prompt for regenerating a component from model data only."""
|
|
147
|
+
lines = [
|
|
148
|
+
"Regenerate this component from model data only, DO NOT read source files.",
|
|
149
|
+
"",
|
|
150
|
+
f"# Component: {component_context.get('name', 'Unknown')} ({component_context.get('id', '?')})",
|
|
151
|
+
"",
|
|
152
|
+
f"**Contract:** {component_context.get('contract', 'Not specified')}",
|
|
153
|
+
f"**Pattern:** {component_context.get('pattern', 'Not specified')}",
|
|
154
|
+
]
|
|
155
|
+
|
|
156
|
+
responsibilities = component_context.get("responsibilities", [])
|
|
157
|
+
if responsibilities:
|
|
158
|
+
lines.append("\n**Responsibilities:**")
|
|
159
|
+
for r in responsibilities:
|
|
160
|
+
lines.append(f" - {r}")
|
|
161
|
+
|
|
162
|
+
signatures = component_context.get("signatures", [])
|
|
163
|
+
if signatures:
|
|
164
|
+
lines.append("\n**Signatures:**")
|
|
165
|
+
for sig in signatures:
|
|
166
|
+
params = ", ".join(sig.get("params", []))
|
|
167
|
+
ret = f" -> {sig['returns']}" if sig.get("returns") else ""
|
|
168
|
+
lines.append(f" def {sig['name']}({params}){ret}")
|
|
169
|
+
|
|
170
|
+
symbols = component_context.get("symbols", [])
|
|
171
|
+
if symbols:
|
|
172
|
+
lines.append("\n**Symbols:**")
|
|
173
|
+
for sym in symbols:
|
|
174
|
+
members = ", ".join(sym.get("members", []))
|
|
175
|
+
lines.append(f" {sym.get('kind', 'class')} {sym['name']}: [{members}]")
|
|
176
|
+
|
|
177
|
+
constants = component_context.get("constants", [])
|
|
178
|
+
if constants:
|
|
179
|
+
lines.append("\n**Constants:**")
|
|
180
|
+
for c in constants:
|
|
181
|
+
lines.append(f" {c['name']} = {c['value']}")
|
|
182
|
+
|
|
183
|
+
lines.append("\n---")
|
|
184
|
+
lines.append("Provide the complete Python module output.")
|
|
185
|
+
|
|
186
|
+
return "\n".join(lines)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
# --- Task 6: Calibration Suite ---
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def compute_correlation(data: list[dict]) -> float:
|
|
195
|
+
"""Pearson correlation between confidence and regen_quality fields."""
|
|
196
|
+
if len(data) < 2:
|
|
197
|
+
return 0.0
|
|
198
|
+
xs = [d["confidence"] for d in data]
|
|
199
|
+
ys = [d["regen_quality"] for d in data]
|
|
200
|
+
n = len(xs)
|
|
201
|
+
mean_x = sum(xs) / n
|
|
202
|
+
mean_y = sum(ys) / n
|
|
203
|
+
num = sum((x - mean_x) * (y - mean_y) for x, y in zip(xs, ys))
|
|
204
|
+
den_x = math.sqrt(sum((x - mean_x) ** 2 for x in xs))
|
|
205
|
+
den_y = math.sqrt(sum((y - mean_y) ** 2 for y in ys))
|
|
206
|
+
if den_x == 0 or den_y == 0:
|
|
207
|
+
return 0.0
|
|
208
|
+
return num / (den_x * den_y)
|