traceforge-toolkit 0.1.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.
- traceforge/__init__.py +72 -0
- traceforge/__main__.py +5 -0
- traceforge/_generated.py +254 -0
- traceforge/adapters/__init__.py +12 -0
- traceforge/adapters/base.py +82 -0
- traceforge/adapters/genai_otel.py +164 -0
- traceforge/adapters/mapped_json.py +297 -0
- traceforge/adapters/otel.py +220 -0
- traceforge/boundary/__init__.py +46 -0
- traceforge/boundary/data/boundary-model.joblib +0 -0
- traceforge/boundary/decode.py +87 -0
- traceforge/boundary/features.py +137 -0
- traceforge/boundary/inference.py +267 -0
- traceforge/boundary/inferencer.py +146 -0
- traceforge/classify/__init__.py +95 -0
- traceforge/classify/cmd.py +109 -0
- traceforge/classify/coding.py +213 -0
- traceforge/classify/config.py +554 -0
- traceforge/classify/core.py +266 -0
- traceforge/classify/data/binary_info.yaml +358 -0
- traceforge/classify/data/canonical_tools.yaml +251 -0
- traceforge/classify/data/effect_overrides.yaml +480 -0
- traceforge/classify/data/mcp_profiles.yaml +711 -0
- traceforge/classify/data/recommendation_rules.yaml +111 -0
- traceforge/classify/data/risk.yaml +534 -0
- traceforge/classify/data/shell_defaults.yaml +95 -0
- traceforge/classify/data/shell_rules.yaml +1192 -0
- traceforge/classify/data/tool_classifications.yaml +215 -0
- traceforge/classify/data/verb_inference.yaml +218 -0
- traceforge/classify/mcp.py +181 -0
- traceforge/classify/phases.py +75 -0
- traceforge/classify/powershell.py +93 -0
- traceforge/classify/registry.py +138 -0
- traceforge/classify/risk.py +553 -0
- traceforge/classify/rules.py +215 -0
- traceforge/classify/schema.yaml +282 -0
- traceforge/classify/shell.py +503 -0
- traceforge/classify/tools.py +91 -0
- traceforge/classify/workflow.py +32 -0
- traceforge/cli/__init__.py +42 -0
- traceforge/cli/config_cmd.py +130 -0
- traceforge/cli/detect.py +46 -0
- traceforge/cli/download_cmd.py +74 -0
- traceforge/cli/factory.py +60 -0
- traceforge/cli/gate_cmd.py +30 -0
- traceforge/cli/init_cmd.py +86 -0
- traceforge/cli/replay.py +130 -0
- traceforge/cli/runner.py +181 -0
- traceforge/cli/score.py +217 -0
- traceforge/cli/status.py +65 -0
- traceforge/cli/watch.py +297 -0
- traceforge/config/__init__.py +80 -0
- traceforge/config/defaults.py +149 -0
- traceforge/config/loader.py +239 -0
- traceforge/config/mappings.py +104 -0
- traceforge/config/models.py +579 -0
- traceforge/enricher.py +576 -0
- traceforge/formatting/__init__.py +12 -0
- traceforge/formatting/budget.py +92 -0
- traceforge/formatting/density.py +154 -0
- traceforge/gate/__init__.py +17 -0
- traceforge/gate/client.py +145 -0
- traceforge/gate/external.py +305 -0
- traceforge/gate/registry.py +108 -0
- traceforge/gate/server.py +174 -0
- traceforge/gates/__init__.py +8 -0
- traceforge/gates/pii.py +352 -0
- traceforge/gates/pii_patterns.yaml +228 -0
- traceforge/governance/__init__.py +121 -0
- traceforge/governance/assessor.py +441 -0
- traceforge/governance/budget.py +59 -0
- traceforge/governance/canonical.py +56 -0
- traceforge/governance/codec.py +414 -0
- traceforge/governance/context.py +249 -0
- traceforge/governance/drift.py +196 -0
- traceforge/governance/emitter.py +234 -0
- traceforge/governance/envelope.py +138 -0
- traceforge/governance/ifc.py +364 -0
- traceforge/governance/integrity.py +162 -0
- traceforge/governance/labeler.py +250 -0
- traceforge/governance/mcp_drift.py +328 -0
- traceforge/governance/monitor.py +539 -0
- traceforge/governance/observer.py +276 -0
- traceforge/governance/persistence.py +401 -0
- traceforge/governance/phase1.py +77 -0
- traceforge/governance/pii.py +276 -0
- traceforge/governance/pipeline.py +840 -0
- traceforge/governance/registry.py +110 -0
- traceforge/governance/results.py +183 -0
- traceforge/governance/risk_wrapper.py +113 -0
- traceforge/governance/rules.py +368 -0
- traceforge/governance/scorer.py +262 -0
- traceforge/governance/shield.py +318 -0
- traceforge/governance/state.py +466 -0
- traceforge/governance/types.py +176 -0
- traceforge/mappings/__init__.py +1 -0
- traceforge/mappings/aider.yaml +216 -0
- traceforge/mappings/aider_markdown.yaml +113 -0
- traceforge/mappings/amazonq.yaml +56 -0
- traceforge/mappings/antigravity.yaml +88 -0
- traceforge/mappings/claude.yaml +93 -0
- traceforge/mappings/cline.yaml +286 -0
- traceforge/mappings/codex.yaml +158 -0
- traceforge/mappings/continue_dev.yaml +57 -0
- traceforge/mappings/copilot.yaml +266 -0
- traceforge/mappings/copilot_markdown.yaml +72 -0
- traceforge/mappings/copilot_vscode.yaml +181 -0
- traceforge/mappings/crewai.yaml +592 -0
- traceforge/mappings/goose.yaml +128 -0
- traceforge/mappings/langgraph.yaml +165 -0
- traceforge/mappings/maf.yaml +90 -0
- traceforge/mappings/maf_transcript.yaml +99 -0
- traceforge/mappings/openai_agents.yaml +144 -0
- traceforge/mappings/opencode.yaml +473 -0
- traceforge/mappings/openhands.yaml +320 -0
- traceforge/mappings/pydantic_ai.yaml +183 -0
- traceforge/mappings/smolagents.yaml +89 -0
- traceforge/mappings/sweagent.yaml +82 -0
- traceforge/migrations/__init__.py +1 -0
- traceforge/migrations/env.py +60 -0
- traceforge/migrations/models.py +145 -0
- traceforge/migrations/runner.py +44 -0
- traceforge/migrations/script.py.mako +25 -0
- traceforge/migrations/versions/0001_initial.py +176 -0
- traceforge/migrations/versions/__init__.py +4 -0
- traceforge/models.py +29 -0
- traceforge/parsers/__init__.py +21 -0
- traceforge/parsers/aider.py +416 -0
- traceforge/parsers/base.py +226 -0
- traceforge/parsers/copilot.py +314 -0
- traceforge/phase/__init__.py +50 -0
- traceforge/phase/data/phase-model.joblib +0 -0
- traceforge/phase/data/potion-base-8M/README.md +99 -0
- traceforge/phase/data/potion-base-8M/config.json +13 -0
- traceforge/phase/data/potion-base-8M/model.safetensors +0 -0
- traceforge/phase/data/potion-base-8M/modules.json +14 -0
- traceforge/phase/data/potion-base-8M/tokenizer.json +1 -0
- traceforge/phase/event_rows.py +107 -0
- traceforge/phase/features.py +468 -0
- traceforge/phase/inference.py +279 -0
- traceforge/phase/inferencer.py +171 -0
- traceforge/phase/segmentation.py +258 -0
- traceforge/pipeline.py +891 -0
- traceforge/preprocessors/__init__.py +34 -0
- traceforge/preprocessors/amazonq.py +224 -0
- traceforge/preprocessors/antigravity.py +116 -0
- traceforge/preprocessors/claude.py +95 -0
- traceforge/preprocessors/cline.py +36 -0
- traceforge/preprocessors/codex.py +311 -0
- traceforge/preprocessors/continue_dev.py +119 -0
- traceforge/preprocessors/copilot_vscode.py +171 -0
- traceforge/preprocessors/goose.py +156 -0
- traceforge/preprocessors/maf_transcript.py +84 -0
- traceforge/preprocessors/openai_agents.py +86 -0
- traceforge/preprocessors/opencode.py +85 -0
- traceforge/preprocessors/openhands.py +36 -0
- traceforge/preprocessors/pydantic_ai.py +62 -0
- traceforge/preprocessors/registry.py +24 -0
- traceforge/preprocessors/smolagents.py +90 -0
- traceforge/py.typed +0 -0
- traceforge/sdk/__init__.py +59 -0
- traceforge/sdk/gate_policy.py +63 -0
- traceforge/sdk/gate_types.py +140 -0
- traceforge/sdk/pipeline.py +265 -0
- traceforge/sdk/verdict.py +81 -0
- traceforge/sinks/__init__.py +23 -0
- traceforge/sinks/base.py +95 -0
- traceforge/sinks/callback.py +132 -0
- traceforge/sinks/console.py +112 -0
- traceforge/sinks/factory.py +94 -0
- traceforge/sinks/jsonl.py +125 -0
- traceforge/sinks/otel_exporter.py +212 -0
- traceforge/sinks/parquet.py +260 -0
- traceforge/sinks/s3.py +206 -0
- traceforge/sinks/sqlite_output.py +234 -0
- traceforge/sinks/webhook.py +136 -0
- traceforge/sources/__init__.py +18 -0
- traceforge/sources/auto_detect.py +173 -0
- traceforge/sources/base.py +45 -0
- traceforge/sources/file_poll.py +136 -0
- traceforge/sources/file_watch.py +221 -0
- traceforge/sources/http_poll.py +141 -0
- traceforge/sources/replay.py +63 -0
- traceforge/sources/sqlite.py +187 -0
- traceforge/sources/sse.py +198 -0
- traceforge/telemetry/__init__.py +192 -0
- traceforge/title/__init__.py +23 -0
- traceforge/title/_resolve.py +79 -0
- traceforge/title/context.py +295 -0
- traceforge/title/data/boilerplate_files.json +14 -0
- traceforge/title/heuristics.py +429 -0
- traceforge/title/hygiene.py +90 -0
- traceforge/title/inference.py +314 -0
- traceforge/title/inferencer.py +477 -0
- traceforge/title/naming.py +398 -0
- traceforge/trace.py +291 -0
- traceforge/tracking/__init__.py +30 -0
- traceforge/tracking/models.py +115 -0
- traceforge/tracking/phase_tracker.py +288 -0
- traceforge/types.py +315 -0
- traceforge_toolkit-0.1.0.dist-info/METADATA +188 -0
- traceforge_toolkit-0.1.0.dist-info/RECORD +205 -0
- traceforge_toolkit-0.1.0.dist-info/WHEEL +4 -0
- traceforge_toolkit-0.1.0.dist-info/entry_points.txt +2 -0
- traceforge_toolkit-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# Native tool classifications — maps canonical tool names to Classification objects.
|
|
2
|
+
# Mechanism reflects the RESOURCE DOMAIN the tool accesses (not implementation).
|
|
3
|
+
|
|
4
|
+
tool_classifications:
|
|
5
|
+
view:
|
|
6
|
+
mechanism: filesystem
|
|
7
|
+
effect: read_only
|
|
8
|
+
scope: [artifact.source_code]
|
|
9
|
+
role: [retriever.file_browser]
|
|
10
|
+
action: [retrieve.read]
|
|
11
|
+
capability: [filesystem_read]
|
|
12
|
+
|
|
13
|
+
edit:
|
|
14
|
+
mechanism: filesystem
|
|
15
|
+
effect: mutating
|
|
16
|
+
scope: [artifact.source_code]
|
|
17
|
+
role: [modifier.file_editor]
|
|
18
|
+
action: [modify.edit]
|
|
19
|
+
capability: [filesystem_write]
|
|
20
|
+
|
|
21
|
+
create:
|
|
22
|
+
mechanism: filesystem
|
|
23
|
+
effect: mutating
|
|
24
|
+
scope: [artifact.source_code]
|
|
25
|
+
role: [modifier.file_editor]
|
|
26
|
+
action: [persist.write]
|
|
27
|
+
capability: [filesystem_write]
|
|
28
|
+
|
|
29
|
+
grep:
|
|
30
|
+
mechanism: filesystem
|
|
31
|
+
effect: read_only
|
|
32
|
+
scope: [artifact.source_code]
|
|
33
|
+
role: [retriever.search_index]
|
|
34
|
+
action: [retrieve.search]
|
|
35
|
+
capability: [filesystem_read]
|
|
36
|
+
|
|
37
|
+
glob:
|
|
38
|
+
mechanism: filesystem
|
|
39
|
+
effect: read_only
|
|
40
|
+
scope: [artifact.source_code]
|
|
41
|
+
role: [retriever.file_browser]
|
|
42
|
+
action: [retrieve.browse]
|
|
43
|
+
capability: [filesystem_read]
|
|
44
|
+
|
|
45
|
+
git_commit:
|
|
46
|
+
mechanism: process.shell
|
|
47
|
+
effect: mutating
|
|
48
|
+
scope: [artifact.repository]
|
|
49
|
+
role: [persistence.version_control]
|
|
50
|
+
action: [persist.commit]
|
|
51
|
+
capability: [filesystem_write]
|
|
52
|
+
|
|
53
|
+
git_push:
|
|
54
|
+
mechanism: process.shell
|
|
55
|
+
effect: mutating
|
|
56
|
+
scope: [artifact.repository]
|
|
57
|
+
role: [persistence.version_control]
|
|
58
|
+
action: [deliver.push]
|
|
59
|
+
capability: [filesystem_write, network_outbound]
|
|
60
|
+
|
|
61
|
+
git_diff:
|
|
62
|
+
mechanism: process.shell
|
|
63
|
+
effect: read_only
|
|
64
|
+
scope: [artifact.repository]
|
|
65
|
+
role: [persistence.version_control]
|
|
66
|
+
action: [retrieve.diff]
|
|
67
|
+
capability: [filesystem_read]
|
|
68
|
+
|
|
69
|
+
git_status:
|
|
70
|
+
mechanism: process.shell
|
|
71
|
+
effect: read_only
|
|
72
|
+
scope: [artifact.repository]
|
|
73
|
+
role: [persistence.version_control]
|
|
74
|
+
action: [retrieve.browse]
|
|
75
|
+
capability: [filesystem_read]
|
|
76
|
+
|
|
77
|
+
git_log:
|
|
78
|
+
mechanism: process.shell
|
|
79
|
+
effect: read_only
|
|
80
|
+
scope: [artifact.repository]
|
|
81
|
+
role: [persistence.version_control]
|
|
82
|
+
action: [retrieve.browse]
|
|
83
|
+
capability: [filesystem_read]
|
|
84
|
+
|
|
85
|
+
git_add:
|
|
86
|
+
mechanism: process.shell
|
|
87
|
+
effect: mutating
|
|
88
|
+
scope: [artifact.repository]
|
|
89
|
+
role: [persistence.version_control]
|
|
90
|
+
action: [persist.stage]
|
|
91
|
+
capability: [filesystem_write]
|
|
92
|
+
|
|
93
|
+
git_pull:
|
|
94
|
+
mechanism: process.shell
|
|
95
|
+
effect: mutating
|
|
96
|
+
scope: [artifact.repository]
|
|
97
|
+
role: [persistence.version_control]
|
|
98
|
+
action: [retrieve]
|
|
99
|
+
capability: [filesystem_write, network_outbound]
|
|
100
|
+
|
|
101
|
+
git_merge:
|
|
102
|
+
mechanism: process.shell
|
|
103
|
+
effect: mutating
|
|
104
|
+
scope: [artifact.repository]
|
|
105
|
+
role: [persistence.version_control]
|
|
106
|
+
action: [modify.merge]
|
|
107
|
+
capability: [filesystem_write]
|
|
108
|
+
|
|
109
|
+
git_rebase:
|
|
110
|
+
mechanism: process.shell
|
|
111
|
+
effect: mutating
|
|
112
|
+
scope: [artifact.repository]
|
|
113
|
+
role: [persistence.version_control]
|
|
114
|
+
action: [modify.rebase]
|
|
115
|
+
capability: [filesystem_write]
|
|
116
|
+
|
|
117
|
+
git_checkout:
|
|
118
|
+
mechanism: process.shell
|
|
119
|
+
effect: mutating
|
|
120
|
+
scope: [artifact.repository]
|
|
121
|
+
role: [persistence.version_control]
|
|
122
|
+
action: [retrieve.browse]
|
|
123
|
+
capability: [filesystem_write]
|
|
124
|
+
|
|
125
|
+
git_branch:
|
|
126
|
+
mechanism: process.shell
|
|
127
|
+
effect: read_only
|
|
128
|
+
scope: [artifact.repository]
|
|
129
|
+
role: [persistence.version_control]
|
|
130
|
+
action: [retrieve.browse]
|
|
131
|
+
capability: [filesystem_read]
|
|
132
|
+
|
|
133
|
+
report_intent:
|
|
134
|
+
mechanism: communication.system
|
|
135
|
+
effect: read_only
|
|
136
|
+
role: [communicator.system_reporter]
|
|
137
|
+
action: []
|
|
138
|
+
capability: []
|
|
139
|
+
|
|
140
|
+
ask_user:
|
|
141
|
+
mechanism: communication.user
|
|
142
|
+
effect: read_only
|
|
143
|
+
role: [communicator.user_prompt]
|
|
144
|
+
action: []
|
|
145
|
+
capability: [human_interaction]
|
|
146
|
+
|
|
147
|
+
web_fetch:
|
|
148
|
+
mechanism: network.http
|
|
149
|
+
effect: read_only
|
|
150
|
+
role: [retriever.web_scraper]
|
|
151
|
+
action: [retrieve]
|
|
152
|
+
capability: [network_outbound]
|
|
153
|
+
|
|
154
|
+
web_search:
|
|
155
|
+
mechanism: network.http
|
|
156
|
+
effect: read_only
|
|
157
|
+
role: [retriever.search_index]
|
|
158
|
+
action: [retrieve.search]
|
|
159
|
+
capability: [network_outbound]
|
|
160
|
+
|
|
161
|
+
task:
|
|
162
|
+
mechanism: delegation.agent
|
|
163
|
+
role: []
|
|
164
|
+
action: []
|
|
165
|
+
capability: [subprocess]
|
|
166
|
+
|
|
167
|
+
shell:
|
|
168
|
+
mechanism: process.shell
|
|
169
|
+
role: [executor.script_runner]
|
|
170
|
+
action: [execute.run_script]
|
|
171
|
+
capability: [subprocess]
|
|
172
|
+
|
|
173
|
+
think:
|
|
174
|
+
mechanism: communication.system
|
|
175
|
+
effect: read_only
|
|
176
|
+
role: [communicator.system_reporter]
|
|
177
|
+
action: [analyze]
|
|
178
|
+
capability: []
|
|
179
|
+
|
|
180
|
+
state_write:
|
|
181
|
+
mechanism: communication.system
|
|
182
|
+
effect: mutating
|
|
183
|
+
role: [communicator.system_reporter]
|
|
184
|
+
action: [persist]
|
|
185
|
+
capability: []
|
|
186
|
+
|
|
187
|
+
state_read:
|
|
188
|
+
mechanism: communication.system
|
|
189
|
+
effect: read_only
|
|
190
|
+
role: [communicator.system_reporter]
|
|
191
|
+
action: [retrieve]
|
|
192
|
+
capability: []
|
|
193
|
+
|
|
194
|
+
web_browse:
|
|
195
|
+
mechanism: network.http
|
|
196
|
+
effect: read_only
|
|
197
|
+
role: [retriever.web_scraper]
|
|
198
|
+
action: [retrieve.browse]
|
|
199
|
+
capability: [network_outbound, human_interaction]
|
|
200
|
+
|
|
201
|
+
codebase_search:
|
|
202
|
+
mechanism: filesystem
|
|
203
|
+
effect: read_only
|
|
204
|
+
scope: [artifact.source_code]
|
|
205
|
+
role: [retriever.search_index]
|
|
206
|
+
action: [retrieve.search]
|
|
207
|
+
capability: [filesystem_read]
|
|
208
|
+
|
|
209
|
+
delete_file:
|
|
210
|
+
mechanism: filesystem
|
|
211
|
+
effect: destructive
|
|
212
|
+
scope: [artifact.source_code]
|
|
213
|
+
role: [modifier.file_editor]
|
|
214
|
+
action: [remove]
|
|
215
|
+
capability: [filesystem_write]
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Verb prefix → (effect, action) inference for MCP tool names.
|
|
2
|
+
# When a tool name contains a recognized verb prefix, these defaults apply.
|
|
3
|
+
# Used by mcp.py for tools not matched by any server profile.
|
|
4
|
+
|
|
5
|
+
verb_inference:
|
|
6
|
+
# ─── Read-only verbs ─────────────────────────────────────────────────────────
|
|
7
|
+
get:
|
|
8
|
+
effect: read_only
|
|
9
|
+
action: retrieve
|
|
10
|
+
list:
|
|
11
|
+
effect: read_only
|
|
12
|
+
action: retrieve
|
|
13
|
+
read:
|
|
14
|
+
effect: read_only
|
|
15
|
+
action: retrieve
|
|
16
|
+
search:
|
|
17
|
+
effect: read_only
|
|
18
|
+
action: retrieve
|
|
19
|
+
query:
|
|
20
|
+
effect: read_only
|
|
21
|
+
action: retrieve
|
|
22
|
+
describe:
|
|
23
|
+
effect: read_only
|
|
24
|
+
action: analyze
|
|
25
|
+
fetch:
|
|
26
|
+
effect: read_only
|
|
27
|
+
action: retrieve
|
|
28
|
+
browse:
|
|
29
|
+
effect: read_only
|
|
30
|
+
action: retrieve
|
|
31
|
+
find:
|
|
32
|
+
effect: read_only
|
|
33
|
+
action: retrieve
|
|
34
|
+
show:
|
|
35
|
+
effect: read_only
|
|
36
|
+
action: retrieve
|
|
37
|
+
inspect:
|
|
38
|
+
effect: read_only
|
|
39
|
+
action: analyze
|
|
40
|
+
check:
|
|
41
|
+
effect: read_only
|
|
42
|
+
action: validate
|
|
43
|
+
scan:
|
|
44
|
+
effect: read_only
|
|
45
|
+
action: validate
|
|
46
|
+
view:
|
|
47
|
+
effect: read_only
|
|
48
|
+
action: retrieve
|
|
49
|
+
|
|
50
|
+
# ─── Mutating verbs (create/modify) ─────────────────────────────────────────
|
|
51
|
+
create:
|
|
52
|
+
effect: mutating
|
|
53
|
+
action: generate
|
|
54
|
+
update:
|
|
55
|
+
effect: mutating
|
|
56
|
+
action: modify
|
|
57
|
+
write:
|
|
58
|
+
effect: mutating
|
|
59
|
+
action: persist
|
|
60
|
+
set:
|
|
61
|
+
effect: mutating
|
|
62
|
+
action: persist
|
|
63
|
+
add:
|
|
64
|
+
effect: mutating
|
|
65
|
+
action: persist
|
|
66
|
+
append:
|
|
67
|
+
effect: mutating
|
|
68
|
+
action: persist
|
|
69
|
+
apply:
|
|
70
|
+
effect: mutating
|
|
71
|
+
action: execute
|
|
72
|
+
run:
|
|
73
|
+
effect: mutating
|
|
74
|
+
action: execute
|
|
75
|
+
execute:
|
|
76
|
+
effect: mutating
|
|
77
|
+
action: execute
|
|
78
|
+
start:
|
|
79
|
+
effect: mutating
|
|
80
|
+
action: execute
|
|
81
|
+
trigger:
|
|
82
|
+
effect: mutating
|
|
83
|
+
action: execute
|
|
84
|
+
deploy:
|
|
85
|
+
effect: mutating
|
|
86
|
+
action: deliver
|
|
87
|
+
publish:
|
|
88
|
+
effect: mutating
|
|
89
|
+
action: deliver
|
|
90
|
+
push:
|
|
91
|
+
effect: mutating
|
|
92
|
+
action: deliver
|
|
93
|
+
send:
|
|
94
|
+
effect: mutating
|
|
95
|
+
action: deliver
|
|
96
|
+
post:
|
|
97
|
+
effect: mutating
|
|
98
|
+
action: deliver
|
|
99
|
+
submit:
|
|
100
|
+
effect: mutating
|
|
101
|
+
action: deliver
|
|
102
|
+
upload:
|
|
103
|
+
effect: mutating
|
|
104
|
+
action: deliver
|
|
105
|
+
fork:
|
|
106
|
+
effect: mutating
|
|
107
|
+
action: generate
|
|
108
|
+
star:
|
|
109
|
+
effect: mutating
|
|
110
|
+
action: persist
|
|
111
|
+
assign:
|
|
112
|
+
effect: mutating
|
|
113
|
+
action: modify
|
|
114
|
+
merge:
|
|
115
|
+
effect: mutating
|
|
116
|
+
action: modify
|
|
117
|
+
approve:
|
|
118
|
+
effect: mutating
|
|
119
|
+
action: modify
|
|
120
|
+
dismiss:
|
|
121
|
+
effect: mutating
|
|
122
|
+
action: modify
|
|
123
|
+
resolve:
|
|
124
|
+
effect: mutating
|
|
125
|
+
action: modify
|
|
126
|
+
configure:
|
|
127
|
+
effect: mutating
|
|
128
|
+
action: configure
|
|
129
|
+
install:
|
|
130
|
+
effect: mutating
|
|
131
|
+
action: configure
|
|
132
|
+
scale:
|
|
133
|
+
effect: mutating
|
|
134
|
+
action: configure
|
|
135
|
+
build:
|
|
136
|
+
effect: mutating
|
|
137
|
+
action: transform
|
|
138
|
+
pull:
|
|
139
|
+
effect: mutating
|
|
140
|
+
action: retrieve
|
|
141
|
+
edit:
|
|
142
|
+
effect: mutating
|
|
143
|
+
action: modify
|
|
144
|
+
replace:
|
|
145
|
+
effect: mutating
|
|
146
|
+
action: modify
|
|
147
|
+
rename:
|
|
148
|
+
effect: mutating
|
|
149
|
+
action: modify
|
|
150
|
+
restore:
|
|
151
|
+
effect: mutating
|
|
152
|
+
action: modify
|
|
153
|
+
archive:
|
|
154
|
+
effect: mutating
|
|
155
|
+
action: modify
|
|
156
|
+
manage:
|
|
157
|
+
effect: mutating
|
|
158
|
+
action: modify
|
|
159
|
+
move:
|
|
160
|
+
effect: mutating
|
|
161
|
+
action: modify
|
|
162
|
+
|
|
163
|
+
# ─── Mutating verbs (browser/UI interaction) ────────────────────────────────
|
|
164
|
+
navigate:
|
|
165
|
+
effect: mutating
|
|
166
|
+
action: execute
|
|
167
|
+
click:
|
|
168
|
+
effect: mutating
|
|
169
|
+
action: execute
|
|
170
|
+
type:
|
|
171
|
+
effect: mutating
|
|
172
|
+
action: execute
|
|
173
|
+
fill:
|
|
174
|
+
effect: mutating
|
|
175
|
+
action: execute
|
|
176
|
+
select:
|
|
177
|
+
effect: mutating
|
|
178
|
+
action: execute
|
|
179
|
+
hover:
|
|
180
|
+
effect: mutating
|
|
181
|
+
action: execute
|
|
182
|
+
press:
|
|
183
|
+
effect: mutating
|
|
184
|
+
action: execute
|
|
185
|
+
drag:
|
|
186
|
+
effect: mutating
|
|
187
|
+
action: execute
|
|
188
|
+
|
|
189
|
+
# ─── Destructive verbs ──────────────────────────────────────────────────────
|
|
190
|
+
delete:
|
|
191
|
+
effect: destructive
|
|
192
|
+
action: remove
|
|
193
|
+
destroy:
|
|
194
|
+
effect: destructive
|
|
195
|
+
action: remove
|
|
196
|
+
drop:
|
|
197
|
+
effect: destructive
|
|
198
|
+
action: remove
|
|
199
|
+
remove:
|
|
200
|
+
effect: destructive
|
|
201
|
+
action: remove
|
|
202
|
+
clear:
|
|
203
|
+
effect: destructive
|
|
204
|
+
action: remove
|
|
205
|
+
terminate:
|
|
206
|
+
effect: destructive
|
|
207
|
+
action: remove
|
|
208
|
+
|
|
209
|
+
# ─── Lifecycle/cancel ───────────────────────────────────────────────────────
|
|
210
|
+
unstar:
|
|
211
|
+
effect: mutating
|
|
212
|
+
action: modify
|
|
213
|
+
stop:
|
|
214
|
+
effect: mutating
|
|
215
|
+
action: execute
|
|
216
|
+
cancel:
|
|
217
|
+
effect: mutating
|
|
218
|
+
action: execute
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""MCP (Model Context Protocol) tool classification."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from traceforge.classify.core import Classification
|
|
9
|
+
from traceforge.classify.phases import with_phase_map
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from traceforge.classify.config import ClassificationEngine
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True)
|
|
16
|
+
class McpToolOverride:
|
|
17
|
+
"""Per-tool classification override within an MCP server profile."""
|
|
18
|
+
|
|
19
|
+
effect: str | None = None
|
|
20
|
+
mechanism: str | None = None
|
|
21
|
+
role: frozenset[str] | None = None
|
|
22
|
+
action: frozenset[str] | None = None
|
|
23
|
+
scope: frozenset[str] | None = None
|
|
24
|
+
capability: frozenset[str] | None = None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(frozen=True)
|
|
28
|
+
class McpServerProfile:
|
|
29
|
+
"""Classification profile for a known MCP server."""
|
|
30
|
+
|
|
31
|
+
namespace_aliases: tuple[str, ...]
|
|
32
|
+
mechanism: str
|
|
33
|
+
role: frozenset[str] = frozenset()
|
|
34
|
+
default_effect: str | None = None
|
|
35
|
+
scope: frozenset[str] = frozenset()
|
|
36
|
+
action: frozenset[str] = frozenset()
|
|
37
|
+
capability: frozenset[str] = frozenset()
|
|
38
|
+
tool_overrides: dict[str, McpToolOverride] = field(default_factory=dict)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def extract_mcp_namespace(raw_name: str) -> str:
|
|
42
|
+
"""Extract the MCP server namespace from a raw tool name."""
|
|
43
|
+
name = raw_name.strip()
|
|
44
|
+
if name.startswith("mcp__"):
|
|
45
|
+
parts = name.split("__", 2)
|
|
46
|
+
if len(parts) == 3:
|
|
47
|
+
return parts[1].lower()
|
|
48
|
+
return ""
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _normalize_mcp_suffix(raw_name: str) -> str:
|
|
52
|
+
"""Extract and normalize the tool suffix from an MCP tool name."""
|
|
53
|
+
name = raw_name.strip()
|
|
54
|
+
if name.startswith("mcp__"):
|
|
55
|
+
parts = name.split("__", 2)
|
|
56
|
+
if len(parts) == 3:
|
|
57
|
+
return parts[2].lower().replace("-", "_")
|
|
58
|
+
return name.lower().replace("-", "_")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _infer_from_verb(
|
|
62
|
+
tool_suffix: str,
|
|
63
|
+
*,
|
|
64
|
+
engine: ClassificationEngine,
|
|
65
|
+
) -> tuple[str | None, str | None]:
|
|
66
|
+
"""Infer effect and action from tool name verb prefix."""
|
|
67
|
+
verb_map = engine.verb_inference
|
|
68
|
+
lower = tool_suffix.lower()
|
|
69
|
+
for verb, (effect, action) in verb_map.items():
|
|
70
|
+
if lower.startswith(verb + "_") or lower == verb:
|
|
71
|
+
return effect, action
|
|
72
|
+
return None, None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
_EFFECT_SEVERITY: dict[str | None, int] = {
|
|
76
|
+
None: 0,
|
|
77
|
+
"null": 0,
|
|
78
|
+
"read_only": 1,
|
|
79
|
+
"mutating": 2,
|
|
80
|
+
"destructive": 3,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
_WRITE_EFFECTS = frozenset({"mutating", "destructive"})
|
|
84
|
+
_FS_MECHANISMS = frozenset({"filesystem", "filesystem.local", "filesystem.remote"})
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _build_classification(
|
|
88
|
+
profile: McpServerProfile,
|
|
89
|
+
override: McpToolOverride | None,
|
|
90
|
+
tool_suffix: str,
|
|
91
|
+
*,
|
|
92
|
+
engine: ClassificationEngine,
|
|
93
|
+
) -> Classification:
|
|
94
|
+
"""Build a Classification from profile defaults plus any tool override."""
|
|
95
|
+
mechanism = profile.mechanism
|
|
96
|
+
role = profile.role
|
|
97
|
+
scope = profile.scope
|
|
98
|
+
action = profile.action
|
|
99
|
+
capability = profile.capability
|
|
100
|
+
effect = profile.default_effect
|
|
101
|
+
|
|
102
|
+
if override is not None:
|
|
103
|
+
if override.mechanism is not None:
|
|
104
|
+
mechanism = override.mechanism
|
|
105
|
+
if override.role is not None:
|
|
106
|
+
role = override.role
|
|
107
|
+
if override.scope is not None:
|
|
108
|
+
scope = override.scope
|
|
109
|
+
if override.action is not None:
|
|
110
|
+
action = override.action
|
|
111
|
+
if override.capability is not None:
|
|
112
|
+
capability = override.capability
|
|
113
|
+
if override.effect is not None:
|
|
114
|
+
effect = override.effect
|
|
115
|
+
|
|
116
|
+
verb_effect, verb_action = _infer_from_verb(tool_suffix, engine=engine)
|
|
117
|
+
if verb_effect is None:
|
|
118
|
+
for alias in profile.namespace_aliases:
|
|
119
|
+
prefix = alias + "_"
|
|
120
|
+
if tool_suffix.startswith(prefix):
|
|
121
|
+
verb_effect, verb_action = _infer_from_verb(
|
|
122
|
+
tool_suffix[len(prefix) :],
|
|
123
|
+
engine=engine,
|
|
124
|
+
)
|
|
125
|
+
if verb_effect is not None:
|
|
126
|
+
break
|
|
127
|
+
# Verb inference: upgrade effect when inferred verb is more dangerous than
|
|
128
|
+
# the profile default (destructive > mutating > read_only > null).
|
|
129
|
+
# Explicit per-tool overrides take priority over verb inference.
|
|
130
|
+
has_explicit_override = override is not None and override.effect is not None
|
|
131
|
+
if not has_explicit_override and verb_effect is not None:
|
|
132
|
+
if _EFFECT_SEVERITY.get(verb_effect, 0) > _EFFECT_SEVERITY.get(effect, 0):
|
|
133
|
+
effect = verb_effect
|
|
134
|
+
elif effect is None and verb_effect is not None:
|
|
135
|
+
effect = verb_effect
|
|
136
|
+
|
|
137
|
+
has_action_override = override is not None and override.action is not None
|
|
138
|
+
if not has_action_override and verb_action is not None:
|
|
139
|
+
action = frozenset({verb_action}) | action
|
|
140
|
+
|
|
141
|
+
# When verb inference upgraded the effect, ensure capabilities are consistent.
|
|
142
|
+
if (
|
|
143
|
+
effect in _WRITE_EFFECTS
|
|
144
|
+
and mechanism in _FS_MECHANISMS
|
|
145
|
+
and "filesystem_write" not in capability
|
|
146
|
+
):
|
|
147
|
+
capability = capability | frozenset({"filesystem_write"})
|
|
148
|
+
# Upgrade role from retriever → modifier.file_editor for filesystem write ops
|
|
149
|
+
if any(r.startswith("retriever.") for r in role):
|
|
150
|
+
upgraded = frozenset(
|
|
151
|
+
"modifier.file_editor" if r.startswith("retriever.") else r for r in role
|
|
152
|
+
)
|
|
153
|
+
role = upgraded
|
|
154
|
+
|
|
155
|
+
return Classification(
|
|
156
|
+
mechanism=mechanism,
|
|
157
|
+
effect=effect,
|
|
158
|
+
scope=scope,
|
|
159
|
+
role=role,
|
|
160
|
+
action=action,
|
|
161
|
+
capability=capability,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def classify_mcp_tool(
|
|
166
|
+
raw_name: str,
|
|
167
|
+
*,
|
|
168
|
+
engine: ClassificationEngine,
|
|
169
|
+
) -> Classification | None:
|
|
170
|
+
"""Classify a tool using engine-provided MCP server profiles."""
|
|
171
|
+
namespace = extract_mcp_namespace(raw_name)
|
|
172
|
+
tool_suffix = _normalize_mcp_suffix(raw_name)
|
|
173
|
+
|
|
174
|
+
alias_index = engine.mcp_alias_index
|
|
175
|
+
profile = alias_index.get(namespace) if namespace else None
|
|
176
|
+
if profile is None:
|
|
177
|
+
return None
|
|
178
|
+
|
|
179
|
+
override = profile.tool_overrides.get(tool_suffix)
|
|
180
|
+
cls = _build_classification(profile, override, tool_suffix, engine=engine)
|
|
181
|
+
return with_phase_map(cls)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Phase derivation logic — shared across tool, MCP, and shell classifiers.
|
|
2
|
+
|
|
3
|
+
Extracted to its own module to avoid circular imports between tools.py and mcp.py.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from traceforge.classify.core import Classification, PhaseSegment
|
|
9
|
+
from traceforge.classify.workflow import Phase
|
|
10
|
+
|
|
11
|
+
# Rule table: (predicate, phase) — evaluated in order, first match wins for single-phase
|
|
12
|
+
_DERIVE_RULES: list[tuple[str, str, str]] = [
|
|
13
|
+
# (kind, value, phase)
|
|
14
|
+
("action", "validate", Phase.VERIFICATION),
|
|
15
|
+
("action", "deliver", Phase.REVIEW),
|
|
16
|
+
("action", "retrieve", Phase.EXPLORATION),
|
|
17
|
+
("action", "analyze", Phase.EXPLORATION),
|
|
18
|
+
("action", "configure", Phase.IMPLEMENTATION),
|
|
19
|
+
("action", "execute", Phase.IMPLEMENTATION),
|
|
20
|
+
("action", "modify", Phase.IMPLEMENTATION),
|
|
21
|
+
("action", "persist", Phase.IMPLEMENTATION),
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def derive_phase(cls: Classification) -> str:
|
|
26
|
+
"""Derive a single phase for a tool classification.
|
|
27
|
+
|
|
28
|
+
Uses a rule table consistent with _phases_from_classification in the enricher.
|
|
29
|
+
Returns the first matching phase; VCS persist/deliver is special-cased to REVIEW.
|
|
30
|
+
"""
|
|
31
|
+
# Special case: VCS persist/deliver → review
|
|
32
|
+
if cls.has_role("persistence.version_control") and (
|
|
33
|
+
cls.has_action("persist") or cls.has_action("deliver")
|
|
34
|
+
):
|
|
35
|
+
return Phase.REVIEW
|
|
36
|
+
|
|
37
|
+
for kind, value, phase in _DERIVE_RULES:
|
|
38
|
+
if kind == "action" and cls.has_action(value):
|
|
39
|
+
return phase
|
|
40
|
+
|
|
41
|
+
# Mechanism-based fallbacks
|
|
42
|
+
if cls.mechanism.startswith("communication"):
|
|
43
|
+
return Phase.PLANNING
|
|
44
|
+
if cls.mechanism.startswith("delegation"):
|
|
45
|
+
return Phase.IMPLEMENTATION
|
|
46
|
+
if cls.mechanism == "filesystem" and cls.effect == "read_only":
|
|
47
|
+
return Phase.EXPLORATION
|
|
48
|
+
return Phase.IMPLEMENTATION
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def with_phase_map(cls: Classification) -> Classification:
|
|
52
|
+
"""Add a single-segment phase_map to a Classification.
|
|
53
|
+
|
|
54
|
+
Reuses derive_phase() to keep phase assignment consistent across all
|
|
55
|
+
tool types (native, MCP, shell single-command).
|
|
56
|
+
"""
|
|
57
|
+
phase = derive_phase(cls)
|
|
58
|
+
seg = PhaseSegment(
|
|
59
|
+
phase=phase,
|
|
60
|
+
actions=cls.action,
|
|
61
|
+
scopes=cls.scope,
|
|
62
|
+
roles=cls.role,
|
|
63
|
+
)
|
|
64
|
+
return Classification(
|
|
65
|
+
mechanism=cls.mechanism,
|
|
66
|
+
effect=cls.effect,
|
|
67
|
+
scope=cls.scope,
|
|
68
|
+
role=cls.role,
|
|
69
|
+
action=cls.action,
|
|
70
|
+
capability=cls.capability,
|
|
71
|
+
structure=cls.structure,
|
|
72
|
+
shell_dialect=cls.shell_dialect,
|
|
73
|
+
binaries=cls.binaries,
|
|
74
|
+
phase_map=(seg,),
|
|
75
|
+
)
|