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.
Files changed (205) hide show
  1. traceforge/__init__.py +72 -0
  2. traceforge/__main__.py +5 -0
  3. traceforge/_generated.py +254 -0
  4. traceforge/adapters/__init__.py +12 -0
  5. traceforge/adapters/base.py +82 -0
  6. traceforge/adapters/genai_otel.py +164 -0
  7. traceforge/adapters/mapped_json.py +297 -0
  8. traceforge/adapters/otel.py +220 -0
  9. traceforge/boundary/__init__.py +46 -0
  10. traceforge/boundary/data/boundary-model.joblib +0 -0
  11. traceforge/boundary/decode.py +87 -0
  12. traceforge/boundary/features.py +137 -0
  13. traceforge/boundary/inference.py +267 -0
  14. traceforge/boundary/inferencer.py +146 -0
  15. traceforge/classify/__init__.py +95 -0
  16. traceforge/classify/cmd.py +109 -0
  17. traceforge/classify/coding.py +213 -0
  18. traceforge/classify/config.py +554 -0
  19. traceforge/classify/core.py +266 -0
  20. traceforge/classify/data/binary_info.yaml +358 -0
  21. traceforge/classify/data/canonical_tools.yaml +251 -0
  22. traceforge/classify/data/effect_overrides.yaml +480 -0
  23. traceforge/classify/data/mcp_profiles.yaml +711 -0
  24. traceforge/classify/data/recommendation_rules.yaml +111 -0
  25. traceforge/classify/data/risk.yaml +534 -0
  26. traceforge/classify/data/shell_defaults.yaml +95 -0
  27. traceforge/classify/data/shell_rules.yaml +1192 -0
  28. traceforge/classify/data/tool_classifications.yaml +215 -0
  29. traceforge/classify/data/verb_inference.yaml +218 -0
  30. traceforge/classify/mcp.py +181 -0
  31. traceforge/classify/phases.py +75 -0
  32. traceforge/classify/powershell.py +93 -0
  33. traceforge/classify/registry.py +138 -0
  34. traceforge/classify/risk.py +553 -0
  35. traceforge/classify/rules.py +215 -0
  36. traceforge/classify/schema.yaml +282 -0
  37. traceforge/classify/shell.py +503 -0
  38. traceforge/classify/tools.py +91 -0
  39. traceforge/classify/workflow.py +32 -0
  40. traceforge/cli/__init__.py +42 -0
  41. traceforge/cli/config_cmd.py +130 -0
  42. traceforge/cli/detect.py +46 -0
  43. traceforge/cli/download_cmd.py +74 -0
  44. traceforge/cli/factory.py +60 -0
  45. traceforge/cli/gate_cmd.py +30 -0
  46. traceforge/cli/init_cmd.py +86 -0
  47. traceforge/cli/replay.py +130 -0
  48. traceforge/cli/runner.py +181 -0
  49. traceforge/cli/score.py +217 -0
  50. traceforge/cli/status.py +65 -0
  51. traceforge/cli/watch.py +297 -0
  52. traceforge/config/__init__.py +80 -0
  53. traceforge/config/defaults.py +149 -0
  54. traceforge/config/loader.py +239 -0
  55. traceforge/config/mappings.py +104 -0
  56. traceforge/config/models.py +579 -0
  57. traceforge/enricher.py +576 -0
  58. traceforge/formatting/__init__.py +12 -0
  59. traceforge/formatting/budget.py +92 -0
  60. traceforge/formatting/density.py +154 -0
  61. traceforge/gate/__init__.py +17 -0
  62. traceforge/gate/client.py +145 -0
  63. traceforge/gate/external.py +305 -0
  64. traceforge/gate/registry.py +108 -0
  65. traceforge/gate/server.py +174 -0
  66. traceforge/gates/__init__.py +8 -0
  67. traceforge/gates/pii.py +352 -0
  68. traceforge/gates/pii_patterns.yaml +228 -0
  69. traceforge/governance/__init__.py +121 -0
  70. traceforge/governance/assessor.py +441 -0
  71. traceforge/governance/budget.py +59 -0
  72. traceforge/governance/canonical.py +56 -0
  73. traceforge/governance/codec.py +414 -0
  74. traceforge/governance/context.py +249 -0
  75. traceforge/governance/drift.py +196 -0
  76. traceforge/governance/emitter.py +234 -0
  77. traceforge/governance/envelope.py +138 -0
  78. traceforge/governance/ifc.py +364 -0
  79. traceforge/governance/integrity.py +162 -0
  80. traceforge/governance/labeler.py +250 -0
  81. traceforge/governance/mcp_drift.py +328 -0
  82. traceforge/governance/monitor.py +539 -0
  83. traceforge/governance/observer.py +276 -0
  84. traceforge/governance/persistence.py +401 -0
  85. traceforge/governance/phase1.py +77 -0
  86. traceforge/governance/pii.py +276 -0
  87. traceforge/governance/pipeline.py +840 -0
  88. traceforge/governance/registry.py +110 -0
  89. traceforge/governance/results.py +183 -0
  90. traceforge/governance/risk_wrapper.py +113 -0
  91. traceforge/governance/rules.py +368 -0
  92. traceforge/governance/scorer.py +262 -0
  93. traceforge/governance/shield.py +318 -0
  94. traceforge/governance/state.py +466 -0
  95. traceforge/governance/types.py +176 -0
  96. traceforge/mappings/__init__.py +1 -0
  97. traceforge/mappings/aider.yaml +216 -0
  98. traceforge/mappings/aider_markdown.yaml +113 -0
  99. traceforge/mappings/amazonq.yaml +56 -0
  100. traceforge/mappings/antigravity.yaml +88 -0
  101. traceforge/mappings/claude.yaml +93 -0
  102. traceforge/mappings/cline.yaml +286 -0
  103. traceforge/mappings/codex.yaml +158 -0
  104. traceforge/mappings/continue_dev.yaml +57 -0
  105. traceforge/mappings/copilot.yaml +266 -0
  106. traceforge/mappings/copilot_markdown.yaml +72 -0
  107. traceforge/mappings/copilot_vscode.yaml +181 -0
  108. traceforge/mappings/crewai.yaml +592 -0
  109. traceforge/mappings/goose.yaml +128 -0
  110. traceforge/mappings/langgraph.yaml +165 -0
  111. traceforge/mappings/maf.yaml +90 -0
  112. traceforge/mappings/maf_transcript.yaml +99 -0
  113. traceforge/mappings/openai_agents.yaml +144 -0
  114. traceforge/mappings/opencode.yaml +473 -0
  115. traceforge/mappings/openhands.yaml +320 -0
  116. traceforge/mappings/pydantic_ai.yaml +183 -0
  117. traceforge/mappings/smolagents.yaml +89 -0
  118. traceforge/mappings/sweagent.yaml +82 -0
  119. traceforge/migrations/__init__.py +1 -0
  120. traceforge/migrations/env.py +60 -0
  121. traceforge/migrations/models.py +145 -0
  122. traceforge/migrations/runner.py +44 -0
  123. traceforge/migrations/script.py.mako +25 -0
  124. traceforge/migrations/versions/0001_initial.py +176 -0
  125. traceforge/migrations/versions/__init__.py +4 -0
  126. traceforge/models.py +29 -0
  127. traceforge/parsers/__init__.py +21 -0
  128. traceforge/parsers/aider.py +416 -0
  129. traceforge/parsers/base.py +226 -0
  130. traceforge/parsers/copilot.py +314 -0
  131. traceforge/phase/__init__.py +50 -0
  132. traceforge/phase/data/phase-model.joblib +0 -0
  133. traceforge/phase/data/potion-base-8M/README.md +99 -0
  134. traceforge/phase/data/potion-base-8M/config.json +13 -0
  135. traceforge/phase/data/potion-base-8M/model.safetensors +0 -0
  136. traceforge/phase/data/potion-base-8M/modules.json +14 -0
  137. traceforge/phase/data/potion-base-8M/tokenizer.json +1 -0
  138. traceforge/phase/event_rows.py +107 -0
  139. traceforge/phase/features.py +468 -0
  140. traceforge/phase/inference.py +279 -0
  141. traceforge/phase/inferencer.py +171 -0
  142. traceforge/phase/segmentation.py +258 -0
  143. traceforge/pipeline.py +891 -0
  144. traceforge/preprocessors/__init__.py +34 -0
  145. traceforge/preprocessors/amazonq.py +224 -0
  146. traceforge/preprocessors/antigravity.py +116 -0
  147. traceforge/preprocessors/claude.py +95 -0
  148. traceforge/preprocessors/cline.py +36 -0
  149. traceforge/preprocessors/codex.py +311 -0
  150. traceforge/preprocessors/continue_dev.py +119 -0
  151. traceforge/preprocessors/copilot_vscode.py +171 -0
  152. traceforge/preprocessors/goose.py +156 -0
  153. traceforge/preprocessors/maf_transcript.py +84 -0
  154. traceforge/preprocessors/openai_agents.py +86 -0
  155. traceforge/preprocessors/opencode.py +85 -0
  156. traceforge/preprocessors/openhands.py +36 -0
  157. traceforge/preprocessors/pydantic_ai.py +62 -0
  158. traceforge/preprocessors/registry.py +24 -0
  159. traceforge/preprocessors/smolagents.py +90 -0
  160. traceforge/py.typed +0 -0
  161. traceforge/sdk/__init__.py +59 -0
  162. traceforge/sdk/gate_policy.py +63 -0
  163. traceforge/sdk/gate_types.py +140 -0
  164. traceforge/sdk/pipeline.py +265 -0
  165. traceforge/sdk/verdict.py +81 -0
  166. traceforge/sinks/__init__.py +23 -0
  167. traceforge/sinks/base.py +95 -0
  168. traceforge/sinks/callback.py +132 -0
  169. traceforge/sinks/console.py +112 -0
  170. traceforge/sinks/factory.py +94 -0
  171. traceforge/sinks/jsonl.py +125 -0
  172. traceforge/sinks/otel_exporter.py +212 -0
  173. traceforge/sinks/parquet.py +260 -0
  174. traceforge/sinks/s3.py +206 -0
  175. traceforge/sinks/sqlite_output.py +234 -0
  176. traceforge/sinks/webhook.py +136 -0
  177. traceforge/sources/__init__.py +18 -0
  178. traceforge/sources/auto_detect.py +173 -0
  179. traceforge/sources/base.py +45 -0
  180. traceforge/sources/file_poll.py +136 -0
  181. traceforge/sources/file_watch.py +221 -0
  182. traceforge/sources/http_poll.py +141 -0
  183. traceforge/sources/replay.py +63 -0
  184. traceforge/sources/sqlite.py +187 -0
  185. traceforge/sources/sse.py +198 -0
  186. traceforge/telemetry/__init__.py +192 -0
  187. traceforge/title/__init__.py +23 -0
  188. traceforge/title/_resolve.py +79 -0
  189. traceforge/title/context.py +295 -0
  190. traceforge/title/data/boilerplate_files.json +14 -0
  191. traceforge/title/heuristics.py +429 -0
  192. traceforge/title/hygiene.py +90 -0
  193. traceforge/title/inference.py +314 -0
  194. traceforge/title/inferencer.py +477 -0
  195. traceforge/title/naming.py +398 -0
  196. traceforge/trace.py +291 -0
  197. traceforge/tracking/__init__.py +30 -0
  198. traceforge/tracking/models.py +115 -0
  199. traceforge/tracking/phase_tracker.py +288 -0
  200. traceforge/types.py +315 -0
  201. traceforge_toolkit-0.1.0.dist-info/METADATA +188 -0
  202. traceforge_toolkit-0.1.0.dist-info/RECORD +205 -0
  203. traceforge_toolkit-0.1.0.dist-info/WHEEL +4 -0
  204. traceforge_toolkit-0.1.0.dist-info/entry_points.txt +2 -0
  205. traceforge_toolkit-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,93 @@
1
+ """PowerShell command classification via tree-sitter AST analysis."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import re
7
+ from typing import TYPE_CHECKING
8
+
9
+ import tree_sitter as ts
10
+ import tree_sitter_powershell as tsps
11
+
12
+ from traceforge.classify.core import Classification
13
+ from traceforge.classify.coding import CodingMechanism
14
+ from traceforge.classify.shell import (
15
+ _CommandClassification,
16
+ build_classification_from_commands,
17
+ classify_single_command,
18
+ )
19
+
20
+ if TYPE_CHECKING:
21
+ from traceforge.classify.config import ClassificationEngine
22
+
23
+ _PS_LANGUAGE = ts.Language(tsps.language())
24
+ _parser = ts.Parser(_PS_LANGUAGE)
25
+ _Q_COMMANDS = ts.Query(_PS_LANGUAGE, "(command) @cmd")
26
+ _STRIP_BINARY_EXT = re.compile(r"\.(exe|cmd|bat|ps1|sh)$", re.IGNORECASE)
27
+
28
+
29
+ def _extract_from_command_node(node: ts.Node) -> tuple[str, str | None, list[str]]:
30
+ """Extract cmdlet/binary name, first positional arg, and parameters from a command node."""
31
+ name = ""
32
+ positionals: list[str] = []
33
+ parameters: list[str] = []
34
+
35
+ for child in node.children:
36
+ if child.type == "command_name" and child.text:
37
+ name = child.text.decode("utf-8").strip()
38
+ elif child.type == "command_elements":
39
+ for sub in child.children:
40
+ if sub.type == "generic_token" and sub.text:
41
+ positionals.append(sub.text.decode("utf-8"))
42
+ elif sub.type == "command_parameter" and sub.text:
43
+ parameters.append(sub.text.decode("utf-8"))
44
+
45
+ subcmd = positionals[0] if positionals else None
46
+ return name, subcmd, parameters
47
+
48
+
49
+ def classify_powershell_command(
50
+ command: str,
51
+ *,
52
+ engine: ClassificationEngine,
53
+ ) -> Classification:
54
+ """Classify a PowerShell command string into a full Classification."""
55
+ if not command or not command.strip():
56
+ return Classification(
57
+ mechanism=CodingMechanism.PROCESS_SHELL,
58
+ effect=None,
59
+ )
60
+
61
+ tree = _parser.parse(command.encode("utf-8"))
62
+ cursor = ts.QueryCursor(_Q_COMMANDS)
63
+ matches = cursor.matches(tree.root_node)
64
+
65
+ if not matches:
66
+ return Classification(
67
+ mechanism=CodingMechanism.PROCESS_SHELL,
68
+ effect=None,
69
+ capability=frozenset({"subprocess"}),
70
+ )
71
+
72
+ command_results: list[_CommandClassification] = []
73
+
74
+ for _pat, captures in matches:
75
+ for node in captures.get("cmd", []):
76
+ name, subcmd, parameters = _extract_from_command_node(node)
77
+ if not name:
78
+ continue
79
+
80
+ # Normalize: strip path/extension for non-cmdlet binaries
81
+ binary = _STRIP_BINARY_EXT.sub("", os.path.basename(name).lower())
82
+
83
+ cmd_cls = classify_single_command(binary, subcmd, parameters, engine=engine)
84
+ command_results.append(cmd_cls)
85
+
86
+ if not command_results:
87
+ return Classification(
88
+ mechanism=CodingMechanism.PROCESS_SHELL,
89
+ effect=None,
90
+ capability=frozenset({"subprocess"}),
91
+ )
92
+
93
+ return build_classification_from_commands(command_results, shell_dialect="powershell")
@@ -0,0 +1,138 @@
1
+ """Dimension registry — validates, extends, and queries hierarchical classification values."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from enum import StrEnum
6
+ from typing import Iterator
7
+
8
+
9
+ class DimensionRegistry:
10
+ """Registry for classification dimension values.
11
+
12
+ Manages hierarchical dot-path values across dimensions.
13
+ Supports validation, extension, and hierarchy queries.
14
+ """
15
+
16
+ def __init__(self) -> None:
17
+ self._dimensions: dict[str, set[str]] = {}
18
+
19
+ def register_dimension(self, name: str, enum_cls: type[StrEnum]) -> None:
20
+ """Register a new dimension with its root enum values."""
21
+ if name not in self._dimensions:
22
+ self._dimensions[name] = set()
23
+ self._dimensions[name].update(v.value for v in enum_cls)
24
+
25
+ def extend_dimension(self, name: str, enum_cls: type[StrEnum]) -> None:
26
+ """Extend an existing dimension with subtype values.
27
+
28
+ Validates that each subtype's parent segment is already registered.
29
+ """
30
+ if name not in self._dimensions:
31
+ self._dimensions[name] = set()
32
+
33
+ for v in enum_cls:
34
+ value = v.value
35
+ parent = self.parent_of(value)
36
+ if parent is not None and parent not in self._dimensions[name]:
37
+ raise ValueError(
38
+ f"Cannot register '{value}' in dimension '{name}': "
39
+ f"parent '{parent}' is not registered"
40
+ )
41
+ self._dimensions[name].add(value)
42
+
43
+ def validate(self, dimension: str, value: str) -> bool:
44
+ """Check if a value is registered in a dimension."""
45
+ if dimension not in self._dimensions:
46
+ return False
47
+ return value in self._dimensions[dimension]
48
+
49
+ def values(self, dimension: str) -> frozenset[str]:
50
+ """Get all registered values for a dimension."""
51
+ return frozenset(self._dimensions.get(dimension, set()))
52
+
53
+ def roots(self, dimension: str) -> frozenset[str]:
54
+ """Get root (non-dotted) values for a dimension."""
55
+ return frozenset(v for v in self._dimensions.get(dimension, set()) if "." not in v)
56
+
57
+ def children(self, dimension: str, parent: str) -> frozenset[str]:
58
+ """Get direct children of a value in a dimension."""
59
+ prefix = parent + "."
60
+ return frozenset(
61
+ v
62
+ for v in self._dimensions.get(dimension, set())
63
+ if v.startswith(prefix) and "." not in v[len(prefix) :]
64
+ )
65
+
66
+ def descendants(self, dimension: str, ancestor: str) -> frozenset[str]:
67
+ """Get all descendants (any depth) of a value."""
68
+ prefix = ancestor + "."
69
+ return frozenset(v for v in self._dimensions.get(dimension, set()) if v.startswith(prefix))
70
+
71
+ def is_descendant(self, dimension: str, value: str, ancestor: str) -> bool:
72
+ """Check if value descends from ancestor in the hierarchy."""
73
+ if value == ancestor:
74
+ return True
75
+ return value.startswith(ancestor + ".")
76
+
77
+ def dimensions(self) -> Iterator[str]:
78
+ """Iterate over registered dimension names."""
79
+ return iter(self._dimensions)
80
+
81
+ @staticmethod
82
+ def parent_of(value: str) -> str | None:
83
+ """Get the parent segment of a dot-path value."""
84
+ if "." not in value:
85
+ return None
86
+ return value.rsplit(".", 1)[0]
87
+
88
+
89
+ def build_default_registry() -> DimensionRegistry:
90
+ """Build the default registry with core + coding dimensions."""
91
+ from traceforge.classify.coding import (
92
+ CodingAction,
93
+ CodingMechanism,
94
+ CodingRole,
95
+ CodingScope,
96
+ ShellDialect,
97
+ )
98
+ from traceforge.classify.core import (
99
+ Action,
100
+ Capability,
101
+ Effect,
102
+ Mechanism,
103
+ Role,
104
+ Scope,
105
+ Structure,
106
+ )
107
+
108
+ reg = DimensionRegistry()
109
+
110
+ # Core roots
111
+ reg.register_dimension("mechanism", Mechanism)
112
+ reg.register_dimension("effect", Effect)
113
+ reg.register_dimension("scope", Scope)
114
+ reg.register_dimension("role", Role)
115
+ reg.register_dimension("action", Action)
116
+ reg.register_dimension("capability", Capability)
117
+ reg.register_dimension("structure", Structure)
118
+
119
+ # Coding extensions
120
+ reg.extend_dimension("mechanism", CodingMechanism)
121
+ reg.extend_dimension("scope", CodingScope)
122
+ reg.extend_dimension("role", CodingRole)
123
+ reg.extend_dimension("action", CodingAction)
124
+ reg.register_dimension("shell_dialect", ShellDialect)
125
+
126
+ return reg
127
+
128
+
129
+ # Module-level default registry (built on first access)
130
+ _default_registry: DimensionRegistry | None = None
131
+
132
+
133
+ def get_default_registry() -> DimensionRegistry:
134
+ """Get the default registry (lazily built, cached)."""
135
+ global _default_registry
136
+ if _default_registry is None:
137
+ _default_registry = build_default_registry()
138
+ return _default_registry