tracekite-core 0.1.1__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.
- tracekite/__init__.py +0 -0
- tracekite/_control_plane/confidence.yml +259 -0
- tracekite/_control_plane/internal_namespaces.yml +29 -0
- tracekite/_control_plane/promotions.yml +10 -0
- tracekite/_control_plane/repos.yml +9 -0
- tracekite/_control_plane/service_aliases.yml +9 -0
- tracekite/_control_plane/vendor_catalog.yml +57 -0
- tracekite/agent_setup.py +120 -0
- tracekite/answer.py +198 -0
- tracekite/cli.py +299 -0
- tracekite/cli_emit.py +19 -0
- tracekite/cli_explain.py +96 -0
- tracekite/cli_inspect.py +281 -0
- tracekite/completeness.py +82 -0
- tracekite/config.py +105 -0
- tracekite/db/__init__.py +0 -0
- tracekite/db/artifact.py +265 -0
- tracekite/db/artifact_diff.py +129 -0
- tracekite/db/artifact_migrations.py +152 -0
- tracekite/db/artifact_reader.py +123 -0
- tracekite/db/constraints.py +121 -0
- tracekite/db/graph_store.py +89 -0
- tracekite/db/impact_reader.py +64 -0
- tracekite/db/memory_store.py +268 -0
- tracekite/db/migrate.py +80 -0
- tracekite/db/neo4j_client.py +61 -0
- tracekite/db/neo4j_graph_store.py +183 -0
- tracekite/db/sqlite_store.py +242 -0
- tracekite/db/store_config.py +67 -0
- tracekite/engine_config.py +84 -0
- tracekite/engine_health.py +147 -0
- tracekite/facade.py +282 -0
- tracekite/ingest_client.py +216 -0
- tracekite/main.py +81 -0
- tracekite/mcp_envelope.py +114 -0
- tracekite/mcp_server.py +298 -0
- tracekite/metrics_export.py +143 -0
- tracekite/middleware/__init__.py +0 -0
- tracekite/middleware/auth.py +160 -0
- tracekite/models/__init__.py +0 -0
- tracekite/models/api_models.py +134 -0
- tracekite/models/graph_models.py +170 -0
- tracekite/parsers/__init__.py +0 -0
- tracekite/parsers/asyncapi_parser.py +288 -0
- tracekite/parsers/base.py +91 -0
- tracekite/parsers/brace_blocks.py +33 -0
- tracekite/parsers/config_parser.py +306 -0
- tracekite/parsers/cron_parser.py +69 -0
- tracekite/parsers/dependency_parser.py +1470 -0
- tracekite/parsers/docker_parser.py +262 -0
- tracekite/parsers/gateway_parser.py +913 -0
- tracekite/parsers/graphql_parser.py +175 -0
- tracekite/parsers/helm_refs.py +29 -0
- tracekite/parsers/iac_parser.py +234 -0
- tracekite/parsers/iac_units.py +152 -0
- tracekite/parsers/java_parser.py +199 -0
- tracekite/parsers/javascript_parser.py +211 -0
- tracekite/parsers/kotlin_parser.py +225 -0
- tracekite/parsers/kubernetes_parser.py +739 -0
- tracekite/parsers/mcp_manifest_parser.py +156 -0
- tracekite/parsers/migration_parser.py +306 -0
- tracekite/parsers/openapi_parser.py +73 -0
- tracekite/parsers/ownership_parser.py +622 -0
- tracekite/parsers/parser_registry.py +462 -0
- tracekite/parsers/pipeline_parser.py +807 -0
- tracekite/parsers/proto_parser.py +282 -0
- tracekite/parsers/python_parser.py +234 -0
- tracekite/parsers/scip_parser.py +115 -0
- tracekite/parsers/tree_sitter/README.md +36 -0
- tracekite/parsers/tree_sitter/__init__.py +0 -0
- tracekite/parsers/tree_sitter/adapter.py +668 -0
- tracekite/parsers/tree_sitter/core/__init__.py +0 -0
- tracekite/parsers/tree_sitter/core/base_parser.py +34 -0
- tracekite/parsers/tree_sitter/core/extractor.py +721 -0
- tracekite/parsers/tree_sitter/core/models.py +686 -0
- tracekite/parsers/tree_sitter/core/parser.py +391 -0
- tracekite/parsers/tree_sitter/core/queries/__init__.py +0 -0
- tracekite/parsers/tree_sitter/core/queries/files/__init__.py +0 -0
- tracekite/parsers/tree_sitter/core/queries/files/java/__init__.py +0 -0
- tracekite/parsers/tree_sitter/core/queries/files/java/sql_annotations.scm +13 -0
- tracekite/parsers/tree_sitter/core/queries/files/javascript/__init__.py +0 -0
- tracekite/parsers/tree_sitter/core/queries/files/kotlin/__init__.py +0 -0
- tracekite/parsers/tree_sitter/core/queries/files/python/__init__.py +0 -0
- tracekite/parsers/tree_sitter/core/queries/files/python/sql_annotations.scm +13 -0
- tracekite/parsers/tree_sitter/core/queries/files/typescript/__init__.py +0 -0
- tracekite/parsers/tree_sitter/core/queries/files/typescript/sql_annotations.scm +13 -0
- tracekite/parsers/tree_sitter/core/queries/query_engine.py +619 -0
- tracekite/parsers/tree_sitter/core/queries/query_loader.py +67 -0
- tracekite/parsers/yaml_shapes.py +27 -0
- tracekite/routes/__init__.py +0 -0
- tracekite/routes/graph.py +87 -0
- tracekite/routes/health.py +36 -0
- tracekite/routes/impact.py +38 -0
- tracekite/routes/jobs.py +45 -0
- tracekite/routes/links.py +237 -0
- tracekite/routes/repos.py +151 -0
- tracekite/routes/rollup.py +51 -0
- tracekite/routes/trace.py +243 -0
- tracekite/run_logging.py +126 -0
- tracekite/scan_meta.py +208 -0
- tracekite/services/__init__.py +0 -0
- tracekite/services/absence.py +93 -0
- tracekite/services/agents_extractor.py +708 -0
- tracekite/services/calibration.py +1377 -0
- tracekite/services/calibration_estates.py +271 -0
- tracekite/services/calibration_io.py +96 -0
- tracekite/services/calibration_tiers.py +129 -0
- tracekite/services/call_graph_resolver.py +166 -0
- tracekite/services/claim_sink.py +46 -0
- tracekite/services/claims.py +304 -0
- tracekite/services/control_plane.py +25 -0
- tracekite/services/csharp_route_extractor.py +198 -0
- tracekite/services/data_extractor.py +459 -0
- tracekite/services/env_extractor.py +149 -0
- tracekite/services/evidence_reverify.py +105 -0
- tracekite/services/extraction_coverage.py +85 -0
- tracekite/services/file_classifier.py +127 -0
- tracekite/services/file_scanner.py +165 -0
- tracekite/services/flag_guards.py +98 -0
- tracekite/services/framework_routes.py +82 -0
- tracekite/services/go_route_extractor.py +426 -0
- tracekite/services/graph_factories.py +214 -0
- tracekite/services/graph_properties.py +29 -0
- tracekite/services/graph_reader.py +405 -0
- tracekite/services/graph_writer.py +276 -0
- tracekite/services/grpc_extractor.py +169 -0
- tracekite/services/grpc_gateway_routes.py +144 -0
- tracekite/services/http_call_extractor.py +546 -0
- tracekite/services/http_call_js.py +141 -0
- tracekite/services/http_call_url.py +64 -0
- tracekite/services/ingest_artifacts.py +297 -0
- tracekite/services/ingest_claims.py +1746 -0
- tracekite/services/ingest_config_defs.py +148 -0
- tracekite/services/ingest_deps.py +112 -0
- tracekite/services/ingest_source.py +300 -0
- tracekite/services/ingest_upload.py +92 -0
- tracekite/services/ingestion_service.py +232 -0
- tracekite/services/job_handlers.py +131 -0
- tracekite/services/job_queue.py +229 -0
- tracekite/services/js_route_extractor.py +855 -0
- tracekite/services/jvm_route_extractor.py +267 -0
- tracekite/services/link_writer.py +124 -0
- tracekite/services/linker/__init__.py +3 -0
- tracekite/services/linker/alias_suggestions.py +111 -0
- tracekite/services/linker/base.py +507 -0
- tracekite/services/linker/confidence_math.py +75 -0
- tracekite/services/linker/contract_drift.py +80 -0
- tracekite/services/linker/crossings.py +48 -0
- tracekite/services/linker/deprecations.py +60 -0
- tracekite/services/linker/edge_policy.py +87 -0
- tracekite/services/linker/engine.py +303 -0
- tracekite/services/linker/history.py +185 -0
- tracekite/services/linker/hooks.py +125 -0
- tracekite/services/linker/impact.py +255 -0
- tracekite/services/linker/incremental.py +202 -0
- tracekite/services/linker/map_view.py +76 -0
- tracekite/services/linker/modules.py +63 -0
- tracekite/services/linker/normalize.py +287 -0
- tracekite/services/linker/partitions.py +139 -0
- tracekite/services/linker/path_algebra.py +124 -0
- tracekite/services/linker/ports.py +65 -0
- tracekite/services/linker/r0_alias.py +303 -0
- tracekite/services/linker/r10_dataset.py +135 -0
- tracekite/services/linker/r11_agent.py +159 -0
- tracekite/services/linker/r12_owner.py +89 -0
- tracekite/services/linker/r13_webhook.py +80 -0
- tracekite/services/linker/r14_operation.py +97 -0
- tracekite/services/linker/r1_compose.py +65 -0
- tracekite/services/linker/r2_k8s.py +219 -0
- tracekite/services/linker/r3_library.py +160 -0
- tracekite/services/linker/r4_gateway.py +95 -0
- tracekite/services/linker/r5_grpc.py +174 -0
- tracekite/services/linker/r6_topic.py +290 -0
- tracekite/services/linker/r7_http.py +259 -0
- tracekite/services/linker/r8_graphql.py +134 -0
- tracekite/services/linker/r9_env.py +105 -0
- tracekite/services/linker/r9_env_index.py +174 -0
- tracekite/services/linker/reconciliation.py +82 -0
- tracekite/services/linker/registry.py +169 -0
- tracekite/services/linker/reliability.py +138 -0
- tracekite/services/linker/review_labels.py +36 -0
- tracekite/services/linker/rollup_view.py +110 -0
- tracekite/services/linker/rollups.py +155 -0
- tracekite/services/linker/service.py +121 -0
- tracekite/services/linker/traverse.py +47 -0
- tracekite/services/linker/values.py +143 -0
- tracekite/services/linker/vendors.py +106 -0
- tracekite/services/linker_store.py +119 -0
- tracekite/services/manifest_lines.py +142 -0
- tracekite/services/messaging_extractor.py +693 -0
- tracekite/services/parallel_map.py +178 -0
- tracekite/services/parse_budget.py +54 -0
- tracekite/services/php_route_extractor.py +100 -0
- tracekite/services/rb_route_extractor.py +139 -0
- tracekite/services/redaction.py +152 -0
- tracekite/services/reingest.py +90 -0
- tracekite/services/repo_service.py +221 -0
- tracekite/services/repo_summary.py +26 -0
- tracekite/services/scan.py +299 -0
- tracekite/services/scip_import.py +132 -0
- tracekite/services/sharded_scan.py +110 -0
- tracekite/services/sink_merge.py +53 -0
- tracekite/source_meta.py +158 -0
- tracekite/status_classify.py +64 -0
- tracekite/telemetry.py +92 -0
- tracekite/utils/__init__.py +0 -0
- tracekite/utils/canonical.py +78 -0
- tracekite/utils/evidence.py +77 -0
- tracekite/utils/hashing.py +141 -0
- tracekite/utils/paths.py +110 -0
- tracekite/utils/rendezvous_ids.py +82 -0
- tracekite/wire.py +143 -0
- tracekite_core-0.1.1.dist-info/METADATA +78 -0
- tracekite_core-0.1.1.dist-info/RECORD +217 -0
- tracekite_core-0.1.1.dist-info/WHEEL +4 -0
- tracekite_core-0.1.1.dist-info/entry_points.txt +2 -0
- tracekite_core-0.1.1.dist-info/licenses/LICENSE +202 -0
tracekite/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# Confidence registry: every resolver tier's served confidence plus its
|
|
2
|
+
# measured P/R row (design §8). Invariant 7: no resolver tier serves
|
|
3
|
+
# `active` edges at or above the floor to an org-facing deployment without a
|
|
4
|
+
# measured row. `measured: null` marks provisional values awaiting the
|
|
5
|
+
# microSecEnD + adversarial-fixture calibration run (M2).
|
|
6
|
+
#
|
|
7
|
+
# This is the single copy. Loaded by tracekite/services/linker/base.py:load_confidence();
|
|
8
|
+
# override the directory with KG_CONFIG_DIR.
|
|
9
|
+
version: 1
|
|
10
|
+
floor: 0.6
|
|
11
|
+
fusion_cap: 0.99
|
|
12
|
+
# Most contracts one call site may match before the match is declined
|
|
13
|
+
# outright. A call site matching everything has matched nothing, so past this
|
|
14
|
+
# the resolver counts `fanout_exceeded` and emits nothing — never a truncated
|
|
15
|
+
# slice, which would read as a resolved match while hiding the ambiguity.
|
|
16
|
+
fanout_cap: 32
|
|
17
|
+
will_break_threshold: null # derived from the measured table (M2), never a constant
|
|
18
|
+
# Multiplier applied to an edge whose cited evidence no longer says what it
|
|
19
|
+
# said (F5 re-verification). Rot ages an assertion, it does not refute it:
|
|
20
|
+
# the edge is downgraded — below the floor it becomes `candidate` — and
|
|
21
|
+
# never deleted.
|
|
22
|
+
stale_evidence_factor: 0.7
|
|
23
|
+
# Most route-table applications a single call may chain through (edge
|
|
24
|
+
# gateway -> mesh -> service) before NORMALIZE declines it as unresolved.
|
|
25
|
+
# Deeper chains are counted, never truncated to a partial hop.
|
|
26
|
+
gateway_chain_max_hops: 3
|
|
27
|
+
|
|
28
|
+
# --- R0 alias ---------------------------------------------------------------
|
|
29
|
+
# built_from_config was removed with the config-filename BUILT_FROM exclusion:
|
|
30
|
+
# a Spring Cloud Config repo holding customers-service.yml must never claim to
|
|
31
|
+
# build that service, so no edge is ever priced at that tier.
|
|
32
|
+
r0:
|
|
33
|
+
resolved_to: 0.98
|
|
34
|
+
has_alias: 0.9
|
|
35
|
+
built_from_build_context: 0.95 # descriptor points at a directory in this repo
|
|
36
|
+
built_from_app_name: 0.85 # in-code application name
|
|
37
|
+
built_from_descriptor: 0.75 # compose/k8s names it; image may build elsewhere
|
|
38
|
+
belongs_to: 0.95 # module read off the evidence path
|
|
39
|
+
|
|
40
|
+
# --- R1 compose topology (benchmark-proven 0.98-0.99) -----------------------
|
|
41
|
+
# `links` entries fold into depends_on at emission (compose v1 legacy), so
|
|
42
|
+
# there is no separate links tier — a priced-but-unreachable tier is exactly
|
|
43
|
+
# the schema fiction Invariant 7 forbids (calibration finding).
|
|
44
|
+
r1:
|
|
45
|
+
depends_on: 0.96
|
|
46
|
+
env_host: 0.95
|
|
47
|
+
# `DB_HOST=${DB_HOST:-platform-db}`: the default that runs unless an
|
|
48
|
+
# operator overrides it. Authored and citable, but strictly weaker than a
|
|
49
|
+
# literal — an override changes the answer and nothing in the repo says
|
|
50
|
+
# whether one is set — so it prices below env_host, the same way route_dev
|
|
51
|
+
# sits below route_declared.
|
|
52
|
+
env_host_default: 0.85
|
|
53
|
+
|
|
54
|
+
# --- R2 kubernetes (benchmark P 0.97 / R 0.56) ------------------------------
|
|
55
|
+
# DNS aliases register into R0's alias table and are priced there
|
|
56
|
+
# (r0.has_alias); no edge is emitted at a dns_alias tier (calibration finding).
|
|
57
|
+
r2:
|
|
58
|
+
selector_match: 0.97 # Service selector matches workload pod labels
|
|
59
|
+
env_host: 0.95 # literal env value naming an in-cluster service
|
|
60
|
+
built_from_gitops: 0.97 # ArgoCD/Flux declares the source repo
|
|
61
|
+
deployed_as: 0.97 # the manifest itself names the workload
|
|
62
|
+
policy: 0.9 # mesh/network policy joined by selector
|
|
63
|
+
|
|
64
|
+
# --- R9 environment indirection ---------------------------------------------
|
|
65
|
+
# Multi-environment conflicts DECLINE (r9.ambiguous_across_envs) rather than
|
|
66
|
+
# emit at a lower tier — decline-don't-guess won (calibration finding).
|
|
67
|
+
r9:
|
|
68
|
+
env_resolved: 0.85 # code reads VAR; a manifest defines VAR's host
|
|
69
|
+
|
|
70
|
+
# --- R4 gateway route tables ------------------------------------------------
|
|
71
|
+
r4:
|
|
72
|
+
route_declared: 0.98
|
|
73
|
+
route_dev: 0.7 # vite/webpack devServer proxies: real wiring, dev-only
|
|
74
|
+
|
|
75
|
+
# --- R5 gRPC (globally unique proto keys; no service hint needed) -----------
|
|
76
|
+
r5:
|
|
77
|
+
declared: 0.98 # the .proto itself declares the operation
|
|
78
|
+
exact: 0.95 # stub names the fully-qualified package.Service
|
|
79
|
+
service_name: 0.9 # stub names only the service; package inferred
|
|
80
|
+
calls_service: 0.9
|
|
81
|
+
|
|
82
|
+
# --- R3 libraries (version-free purl keys; internal boundary gates joins) ---
|
|
83
|
+
r3:
|
|
84
|
+
publishes: 0.98 # the repo's own manifest names the coordinate
|
|
85
|
+
lockfile_resolved: 0.95 # consumer pin from a lockfile/SBOM
|
|
86
|
+
manifest_declared: 0.9 # consumer range from a manifest
|
|
87
|
+
|
|
88
|
+
# --- R11 agent contracts (MCP server name plays the role of a proto package) -
|
|
89
|
+
r11:
|
|
90
|
+
registration: 0.95 # @mcp.tool / server.tool in the implementing repo
|
|
91
|
+
config: 0.85 # an mcp.json entry naming the server
|
|
92
|
+
card: 0.95 # .well-known/agent.json declares the skill
|
|
93
|
+
url_resolved: 0.9 # a call site's URL matches a card's URL
|
|
94
|
+
|
|
95
|
+
# --- R10 datasets (identity strength varies by namespace) -------------------
|
|
96
|
+
r10:
|
|
97
|
+
declared: 0.95 # a migration/ORM entity/dbt schema declares the table
|
|
98
|
+
literal: 0.9 # a distinctive-namespace name at a call site
|
|
99
|
+
inferred: 0.75 # ORM entity without an explicit table name
|
|
100
|
+
|
|
101
|
+
# --- R12 ownership ----------------------------------------------------------
|
|
102
|
+
r13:
|
|
103
|
+
registered: 0.85 # callback joined to the receiver's own route
|
|
104
|
+
|
|
105
|
+
r12:
|
|
106
|
+
catalog: 0.95 # Backstage names owner + entity in one authored place
|
|
107
|
+
codeowners: 0.9 # path-pattern ownership; repo-level is exact
|
|
108
|
+
observability: 0.8 # a DD_TAGS team is operational, not organizational
|
|
109
|
+
|
|
110
|
+
# --- R14 cross-protocol operations ------------------------------------------
|
|
111
|
+
r14:
|
|
112
|
+
http_binding: 0.97 # the proto's own google.api.http declares it
|
|
113
|
+
event_schema: 0.85 # avro record name equals the rpc's qualified output
|
|
114
|
+
|
|
115
|
+
# --- R6 topics (no service hint exists or is needed; the name is the key) ---
|
|
116
|
+
r6:
|
|
117
|
+
declared: 0.98 # AsyncAPI/Terraform/Avro declares the destination
|
|
118
|
+
literal: 0.9 # a literal destination at a producer/consumer site
|
|
119
|
+
env_resolved: 0.85 # destination via env/config indirection (hmac join)
|
|
120
|
+
fanout: 0.95 # SNS->SQS subscription declared in IaC
|
|
121
|
+
|
|
122
|
+
# --- R8 GraphQL (Type.field carries no package, so weaker than R5) ----------
|
|
123
|
+
r8:
|
|
124
|
+
declared: 0.98 # the SDL declares the root field
|
|
125
|
+
federated: 0.95 # federation names the owning subgraph
|
|
126
|
+
field_name: 0.8 # field name only; no package, no federation
|
|
127
|
+
|
|
128
|
+
# --- R7 http contract matching ----------------------------------------------
|
|
129
|
+
r7:
|
|
130
|
+
hint_exact: 0.95 # qualified service hint + exact path template
|
|
131
|
+
hint_template: 0.85 # qualified service hint + positional template
|
|
132
|
+
# Callee named by a gateway's route table rather than by the call site.
|
|
133
|
+
# Strictly weaker than a direct hint: it assumes the route in force at link
|
|
134
|
+
# time is the one that served the call, so a stale or overridden route table
|
|
135
|
+
# would mislead. Rated below the direct tiers to keep that honest.
|
|
136
|
+
gateway_exact: 0.88
|
|
137
|
+
gateway_template: 0.80
|
|
138
|
+
exposes: 0.98 # provider-side declaration
|
|
139
|
+
|
|
140
|
+
# --- Intra-repo call graph (pre-linker) -------------------------------------
|
|
141
|
+
resolvers:
|
|
142
|
+
call_graph_resolver:
|
|
143
|
+
unique_match:
|
|
144
|
+
confidence: 0.9
|
|
145
|
+
measured: null # provisional — calibration lands in M2
|
|
146
|
+
ambiguous_match:
|
|
147
|
+
confidence: 0.7
|
|
148
|
+
measured: null # provisional — calibration lands in M2
|
|
149
|
+
# SCIP references are compiler-grade resolution; rated above the
|
|
150
|
+
# name-resolution heuristic but never 1.0 — the index can lag the tree.
|
|
151
|
+
scip_import:
|
|
152
|
+
reference:
|
|
153
|
+
confidence: 0.95
|
|
154
|
+
measured: null # provisional — labelled index corpus pending
|
|
155
|
+
|
|
156
|
+
# Resolvers not yet landed (R2 k8s, R3 library, R5 grpc, R6 topic, R9 env) add
|
|
157
|
+
# their tiers here as they ship. Design §6.2 targets: r2 0.97, r3 0.6-1.0 by
|
|
158
|
+
# tier, r5 high (globally unique keys), r6 0.85-0.95 literals.
|
|
159
|
+
|
|
160
|
+
# --- MEASURED (generated by tools/calibrate.py; do not edit) ---
|
|
161
|
+
measured:
|
|
162
|
+
corpus: fixtures-v1
|
|
163
|
+
at: '2026-07-29T22:46:58+00:00'
|
|
164
|
+
rows:
|
|
165
|
+
r0.belongs_to: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
166
|
+
r0.built_from_app_name: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
167
|
+
r0.built_from_build_context: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
168
|
+
r0.built_from_descriptor: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
169
|
+
r0.has_alias: {precision: 1.0, precision_lo: 0.4385, precision_hi: 1.0, recall: 1.0, support: 3, fp: 0}
|
|
170
|
+
r0.resolved_to: {precision: 1.0, precision_lo: 0.3424, precision_hi: 1.0, recall: 1.0, support: 2, fp: 0}
|
|
171
|
+
r1.depends_on: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
172
|
+
r1.env_host: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
173
|
+
r10.declared: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
174
|
+
r10.inferred: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
175
|
+
r10.literal: {precision: 1.0, precision_lo: 0.3424, precision_hi: 1.0, recall: 1.0, support: 2, fp: 0}
|
|
176
|
+
r11.card: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
177
|
+
r11.config: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
178
|
+
r11.registration: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
179
|
+
r11.url_resolved: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
180
|
+
r12.catalog: {precision: 1.0, precision_lo: 0.3424, precision_hi: 1.0, recall: 1.0, support: 2, fp: 0}
|
|
181
|
+
r12.codeowners: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
182
|
+
r12.observability: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
183
|
+
r13.registered: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
184
|
+
r2.built_from_gitops: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
185
|
+
r2.deployed_as: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
186
|
+
r2.env_host: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
187
|
+
r2.policy: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
188
|
+
r2.selector_match: {precision: 1.0, precision_lo: 0.3424, precision_hi: 1.0, recall: 1.0, support: 2, fp: 0}
|
|
189
|
+
r3.lockfile_resolved: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
190
|
+
r3.manifest_declared: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
191
|
+
r3.publishes: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
192
|
+
r4.route_declared: {precision: 1.0, precision_lo: 0.3424, precision_hi: 1.0, recall: 1.0, support: 2, fp: 0}
|
|
193
|
+
r4.route_dev: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
194
|
+
r5.calls_service: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
195
|
+
r5.declared: {precision: 1.0, precision_lo: 0.3424, precision_hi: 1.0, recall: 1.0, support: 2, fp: 0}
|
|
196
|
+
r5.exact: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
197
|
+
r5.service_name: {precision: 1.0, precision_lo: 0.3424, precision_hi: 1.0, recall: 1.0, support: 2, fp: 0}
|
|
198
|
+
r6.declared: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
199
|
+
r6.env_resolved: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
200
|
+
r6.fanout: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
201
|
+
r6.literal: {precision: 1.0, precision_lo: 0.3424, precision_hi: 1.0, recall: 1.0, support: 2, fp: 0}
|
|
202
|
+
r7.exposes: {precision: 1.0, precision_lo: 0.3424, precision_hi: 1.0, recall: 1.0, support: 2, fp: 0}
|
|
203
|
+
r7.gateway_exact: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
204
|
+
r7.gateway_template: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
205
|
+
r7.hint_exact: {precision: 1.0, precision_lo: 0.4385, precision_hi: 1.0, recall: 1.0, support: 3, fp: 0}
|
|
206
|
+
r7.hint_template: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
207
|
+
r8.declared: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
208
|
+
r8.federated: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
209
|
+
r8.field_name: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
210
|
+
r9.env_resolved: {precision: 1.0, precision_lo: 0.2065, precision_hi: 1.0, recall: 1.0, support: 1, fp: 0}
|
|
211
|
+
derived:
|
|
212
|
+
unattributed_labels: {tp: 0, fp: 0}
|
|
213
|
+
rows:
|
|
214
|
+
r0.belongs_to: {served: 0.95, derived: 0.95, basis: prior, tp: 1, fp: 0}
|
|
215
|
+
r0.built_from_app_name: {served: 0.85, derived: 0.85, basis: prior, tp: 1, fp: 0}
|
|
216
|
+
r0.built_from_build_context: {served: 0.95, derived: 0.95, basis: prior, tp: 1, fp: 0}
|
|
217
|
+
r0.built_from_descriptor: {served: 0.75, derived: 0.75, basis: prior, tp: 1, fp: 0}
|
|
218
|
+
r0.has_alias: {served: 0.9, derived: 0.9, basis: prior, tp: 3, fp: 0}
|
|
219
|
+
r0.resolved_to: {served: 0.98, derived: 0.98, basis: prior, tp: 2, fp: 0}
|
|
220
|
+
r1.depends_on: {served: 0.96, derived: 0.96, basis: prior, tp: 1, fp: 0}
|
|
221
|
+
r1.env_host: {served: 0.95, derived: 0.95, basis: prior, tp: 1, fp: 0}
|
|
222
|
+
r10.declared: {served: 0.95, derived: 0.95, basis: prior, tp: 1, fp: 0}
|
|
223
|
+
r10.inferred: {served: 0.75, derived: 0.75, basis: prior, tp: 1, fp: 0}
|
|
224
|
+
r10.literal: {served: 0.9, derived: 0.9, basis: prior, tp: 2, fp: 0}
|
|
225
|
+
r11.card: {served: 0.95, derived: 0.95, basis: prior, tp: 1, fp: 0}
|
|
226
|
+
r11.config: {served: 0.85, derived: 0.85, basis: prior, tp: 1, fp: 0}
|
|
227
|
+
r11.registration: {served: 0.95, derived: 0.95, basis: prior, tp: 1, fp: 0}
|
|
228
|
+
r11.url_resolved: {served: 0.9, derived: 0.9, basis: prior, tp: 1, fp: 0}
|
|
229
|
+
r12.catalog: {served: 0.95, derived: 0.95, basis: prior, tp: 2, fp: 0}
|
|
230
|
+
r12.codeowners: {served: 0.9, derived: 0.9, basis: prior, tp: 1, fp: 0}
|
|
231
|
+
r12.observability: {served: 0.8, derived: 0.8, basis: prior, tp: 1, fp: 0}
|
|
232
|
+
r13.registered: {served: 0.85, derived: 0.85, basis: prior, tp: 1, fp: 0}
|
|
233
|
+
r2.built_from_gitops: {served: 0.97, derived: 0.97, basis: prior, tp: 1, fp: 0}
|
|
234
|
+
r2.deployed_as: {served: 0.97, derived: 0.97, basis: prior, tp: 1, fp: 0}
|
|
235
|
+
r2.env_host: {served: 0.95, derived: 0.95, basis: prior, tp: 1, fp: 0}
|
|
236
|
+
r2.policy: {served: 0.9, derived: 0.9, basis: prior, tp: 1, fp: 0}
|
|
237
|
+
r2.selector_match: {served: 0.97, derived: 0.97, basis: prior, tp: 2, fp: 0}
|
|
238
|
+
r3.lockfile_resolved: {served: 0.95, derived: 0.95, basis: prior, tp: 1, fp: 0}
|
|
239
|
+
r3.manifest_declared: {served: 0.9, derived: 0.9, basis: prior, tp: 1, fp: 0}
|
|
240
|
+
r3.publishes: {served: 0.98, derived: 0.98, basis: prior, tp: 1, fp: 0}
|
|
241
|
+
r4.route_declared: {served: 0.98, derived: 0.98, basis: prior, tp: 2, fp: 0}
|
|
242
|
+
r4.route_dev: {served: 0.7, derived: 0.7, basis: prior, tp: 1, fp: 0}
|
|
243
|
+
r5.calls_service: {served: 0.9, derived: 0.9, basis: prior, tp: 1, fp: 0}
|
|
244
|
+
r5.declared: {served: 0.98, derived: 0.98, basis: prior, tp: 2, fp: 0}
|
|
245
|
+
r5.exact: {served: 0.95, derived: 0.95, basis: prior, tp: 1, fp: 0}
|
|
246
|
+
r5.service_name: {served: 0.9, derived: 0.9, basis: prior, tp: 2, fp: 0}
|
|
247
|
+
r6.declared: {served: 0.98, derived: 0.98, basis: prior, tp: 1, fp: 0}
|
|
248
|
+
r6.env_resolved: {served: 0.85, derived: 0.85, basis: prior, tp: 1, fp: 0}
|
|
249
|
+
r6.fanout: {served: 0.95, derived: 0.95, basis: prior, tp: 1, fp: 0}
|
|
250
|
+
r6.literal: {served: 0.9, derived: 0.9, basis: prior, tp: 2, fp: 0}
|
|
251
|
+
r7.exposes: {served: 0.98, derived: 0.98, basis: prior, tp: 2, fp: 0}
|
|
252
|
+
r7.gateway_exact: {served: 0.88, derived: 0.88, basis: prior, tp: 1, fp: 0}
|
|
253
|
+
r7.gateway_template: {served: 0.8, derived: 0.8, basis: prior, tp: 1, fp: 0}
|
|
254
|
+
r7.hint_exact: {served: 0.95, derived: 0.95, basis: prior, tp: 3, fp: 0}
|
|
255
|
+
r7.hint_template: {served: 0.85, derived: 0.85, basis: prior, tp: 1, fp: 0}
|
|
256
|
+
r8.declared: {served: 0.98, derived: 0.98, basis: prior, tp: 1, fp: 0}
|
|
257
|
+
r8.federated: {served: 0.95, derived: 0.95, basis: prior, tp: 1, fp: 0}
|
|
258
|
+
r8.field_name: {served: 0.8, derived: 0.8, basis: prior, tp: 1, fp: 0}
|
|
259
|
+
r9.env_resolved: {served: 0.85, derived: 0.85, basis: prior, tp: 1, fp: 0}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Which package coordinates are *ours* (design §12).
|
|
2
|
+
#
|
|
3
|
+
# R3 links a consumed library to its Library rendezvous only when the
|
|
4
|
+
# coordinate is internal — matched here — or when some ingested repo publishes
|
|
5
|
+
# it (publish-side identity beats this file). Everything else is an external
|
|
6
|
+
# dependency: counted (`r3.external_skipped`), never linked, because a graph
|
|
7
|
+
# where every repo depends on `lodash` answers no blast-radius question.
|
|
8
|
+
#
|
|
9
|
+
# Registries are informational: they qualify where internal
|
|
10
|
+
# artifacts live so operators can audit the boundary; image_registries gate
|
|
11
|
+
# image-identity claims (R0-R2).
|
|
12
|
+
version: 1
|
|
13
|
+
ecosystems:
|
|
14
|
+
maven:
|
|
15
|
+
namespaces: [] # e.g. [org.acme, com.acme]
|
|
16
|
+
npm:
|
|
17
|
+
scopes: [] # e.g. ["@acme"]
|
|
18
|
+
golang:
|
|
19
|
+
module_prefixes: [] # e.g. [github.com/acme/]
|
|
20
|
+
pypi:
|
|
21
|
+
prefixes: [] # e.g. [acme-]
|
|
22
|
+
nuget:
|
|
23
|
+
prefixes: [] # e.g. [Acme.]
|
|
24
|
+
gem:
|
|
25
|
+
prefixes: []
|
|
26
|
+
cargo:
|
|
27
|
+
prefixes: []
|
|
28
|
+
registries: [] # e.g. [artifactory.acme.io, npm.pkg.github.com]
|
|
29
|
+
image_registries: []
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Append-only ledger of D6 human promotions, replayed by link_full so the
|
|
2
|
+
# graph stays disposable (design §12, invariant 12). Entries are appended by
|
|
3
|
+
# the review-queue UI (M3); never edit past entries.
|
|
4
|
+
promotions: []
|
|
5
|
+
# Entry shape:
|
|
6
|
+
# promotions:
|
|
7
|
+
# - claim_key: "http:GET /billing/{}"
|
|
8
|
+
# target: "global:HttpContract:GET /billing/{}"
|
|
9
|
+
# promoted_by: "user@example.com"
|
|
10
|
+
# at: "2026-07-26T00:00:00Z"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# The roster: which repositories belong in the graph (design §12, D11).
|
|
2
|
+
# The census tool can generate a first draft. auto_refresh opts a repo into
|
|
3
|
+
# the M2 heartbeat (git ls-remote drift detection, 6h jittered cycle).
|
|
4
|
+
repos: []
|
|
5
|
+
# Example:
|
|
6
|
+
# repos:
|
|
7
|
+
# - url: https://github.com/myorg/payment-service
|
|
8
|
+
# branch: main # optional, defaults to the remote default branch
|
|
9
|
+
# auto_refresh: true # D11 heartbeat opt-in
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Manual alias overrides — always win over resolver output (design §12, R0).
|
|
2
|
+
# Unused or conflicting entries are reported by the linker, never silently
|
|
3
|
+
# dropped. Alias keys are scope-qualified: "{scope}:{name}".
|
|
4
|
+
services: {}
|
|
5
|
+
# Example:
|
|
6
|
+
# services:
|
|
7
|
+
# payments:
|
|
8
|
+
# aliases: ["compose-project-a:payment", "prod:payment-svc", "discovery:PAYMENT-SERVICE"]
|
|
9
|
+
# repo: myorg_payment-service
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Third-party services an estate calls but does not own (roadmap C2).
|
|
2
|
+
#
|
|
3
|
+
# A call to `api.stripe.com` is qualified — the code names a host — but no
|
|
4
|
+
# internal service answers it, so the join declines and the call vanishes
|
|
5
|
+
# into `r7.unmatched_qualified`. That is indistinguishable from a broken
|
|
6
|
+
# internal call, and it hides a real dependency: an outage at a vendor breaks
|
|
7
|
+
# the same consumers an outage at an internal service would.
|
|
8
|
+
#
|
|
9
|
+
# Classifying is not linking. Nothing here creates an edge; it turns an
|
|
10
|
+
# anonymous decline into a named one, so "we call Stripe from three services"
|
|
11
|
+
# becomes a countable fact rather than an absence.
|
|
12
|
+
#
|
|
13
|
+
# `suffixes` match the end of a host, case-insensitively. Keep them specific:
|
|
14
|
+
# a suffix that matches too broadly would classify an internal host as a
|
|
15
|
+
# vendor and hide a genuine missing edge behind a plausible label.
|
|
16
|
+
version: 1
|
|
17
|
+
|
|
18
|
+
vendors:
|
|
19
|
+
stripe:
|
|
20
|
+
category: payments
|
|
21
|
+
suffixes: [api.stripe.com, files.stripe.com]
|
|
22
|
+
twilio:
|
|
23
|
+
category: messaging
|
|
24
|
+
suffixes: [api.twilio.com, lookups.twilio.com, verify.twilio.com]
|
|
25
|
+
sendgrid:
|
|
26
|
+
category: messaging
|
|
27
|
+
suffixes: [api.sendgrid.com]
|
|
28
|
+
segment:
|
|
29
|
+
category: analytics
|
|
30
|
+
suffixes: [api.segment.io, cdn.segment.com]
|
|
31
|
+
aws_s3:
|
|
32
|
+
category: storage
|
|
33
|
+
suffixes: [s3.amazonaws.com, .s3.amazonaws.com]
|
|
34
|
+
aws_sqs:
|
|
35
|
+
category: queue
|
|
36
|
+
suffixes: [sqs.amazonaws.com, .queue.amazonaws.com]
|
|
37
|
+
github:
|
|
38
|
+
category: vcs
|
|
39
|
+
suffixes: [api.github.com]
|
|
40
|
+
slack:
|
|
41
|
+
category: messaging
|
|
42
|
+
suffixes: [slack.com/api, hooks.slack.com, api.slack.com]
|
|
43
|
+
datadog:
|
|
44
|
+
category: observability
|
|
45
|
+
suffixes: [api.datadoghq.com, http-intake.logs.datadoghq.com]
|
|
46
|
+
openai:
|
|
47
|
+
category: ai
|
|
48
|
+
suffixes: [api.openai.com]
|
|
49
|
+
anthropic:
|
|
50
|
+
category: ai
|
|
51
|
+
suffixes: [api.anthropic.com]
|
|
52
|
+
auth0:
|
|
53
|
+
category: identity
|
|
54
|
+
suffixes: [.auth0.com]
|
|
55
|
+
google_maps:
|
|
56
|
+
category: geo
|
|
57
|
+
suffixes: [maps.googleapis.com]
|
tracekite/agent_setup.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""Explicit, fail-closed installation of the TraceKite agent skill.
|
|
2
|
+
|
|
3
|
+
MCP clients do not share a configuration schema. Registering the server is
|
|
4
|
+
therefore kept out of this module and documented per client; this installer
|
|
5
|
+
only writes skill locations whose global contract is published.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
SKILL_CONTENT = """---
|
|
13
|
+
name: tracekite
|
|
14
|
+
description: Cross-repository dependency, caller, and impact tracing with file:line evidence. Use for "who calls this", "who breaks if I change this", "what depends on this service", "what does this endpoint reach" — and before any filesystem search for a caller or consumer that may live in another repository.
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# TraceKite
|
|
18
|
+
|
|
19
|
+
TraceKite answers dependency questions from a pre-indexed evidence graph, so
|
|
20
|
+
one query replaces a repository-wide search.
|
|
21
|
+
|
|
22
|
+
Its edges are joined from configuration, manifests and route tables as well as
|
|
23
|
+
source, so they include callers no text search can reach: a URL built from an
|
|
24
|
+
environment variable, a route registered in another language, a gateway that
|
|
25
|
+
rewrites the path before forwarding it. Searching for those returns nothing
|
|
26
|
+
even when the dependency is real.
|
|
27
|
+
|
|
28
|
+
## Query these before searching the filesystem
|
|
29
|
+
|
|
30
|
+
- `services()` — repository-backed services in the loaded graph.
|
|
31
|
+
- `consumers_of(node_id)` — active incoming edges with evidence spans.
|
|
32
|
+
- `trace(from_id, to_id)` — active paths between exact node IDs.
|
|
33
|
+
- `deprecations()` — deprecated contracts and their active consumers.
|
|
34
|
+
|
|
35
|
+
Call them inline and directly. A caller, dependency or impact question these
|
|
36
|
+
tools answer should not become a grep sweep or a delegated search agent: the
|
|
37
|
+
graph already holds the answer, with provenance, at a fraction of the context.
|
|
38
|
+
|
|
39
|
+
## Reading a result
|
|
40
|
+
|
|
41
|
+
Every edge cites `file:line` on both sides of the join. When you need
|
|
42
|
+
method-level detail, open the cited location — do not re-read the file to
|
|
43
|
+
rediscover what the citation already names.
|
|
44
|
+
|
|
45
|
+
An empty or declined result is evidence that TraceKite could not establish the
|
|
46
|
+
relationship, not an invitation to infer one. A `found: false` answer carries
|
|
47
|
+
`candidates`; re-query with one of those rather than guessing an ID. Never
|
|
48
|
+
assert an edge the tools did not return.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class SkillConflictError(RuntimeError):
|
|
53
|
+
"""An existing skill differs, so overwriting it would destroy user data."""
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(frozen=True)
|
|
57
|
+
class SkillTarget:
|
|
58
|
+
clients: frozenset[str]
|
|
59
|
+
relative_path: tuple[str, ...]
|
|
60
|
+
label: str
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
TARGETS = (
|
|
64
|
+
SkillTarget(frozenset({"claude"}),
|
|
65
|
+
(".claude", "skills", "tracekite", "SKILL.md"),
|
|
66
|
+
"Claude Code"),
|
|
67
|
+
SkillTarget(frozenset({"codex"}),
|
|
68
|
+
(".agents", "skills", "tracekite", "SKILL.md"),
|
|
69
|
+
"Codex"),
|
|
70
|
+
SkillTarget(frozenset({"kimi"}),
|
|
71
|
+
(".kimi", "skills", "tracekite", "SKILL.md"),
|
|
72
|
+
"Kimi"),
|
|
73
|
+
SkillTarget(frozenset({"antigravity"}),
|
|
74
|
+
(".gemini", "config", "skills", "tracekite", "SKILL.md"),
|
|
75
|
+
"Antigravity"),
|
|
76
|
+
)
|
|
77
|
+
SUPPORTED_CLIENTS = frozenset(c for target in TARGETS for c in target.clients)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _selected_targets(clients: list[str] | None) -> list[SkillTarget]:
|
|
81
|
+
selected = set(clients or ["all"])
|
|
82
|
+
unknown = selected - SUPPORTED_CLIENTS - {"all"}
|
|
83
|
+
if unknown:
|
|
84
|
+
raise ValueError(f"unsupported client(s): {sorted(unknown)}")
|
|
85
|
+
if "all" in selected:
|
|
86
|
+
return list(TARGETS)
|
|
87
|
+
return [target for target in TARGETS if target.clients & selected]
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def install_skills(clients: list[str] | None = None,
|
|
91
|
+
home: Path | None = None) -> list[tuple[Path, str]]:
|
|
92
|
+
"""Install selected skills without modifying or replacing existing data."""
|
|
93
|
+
root = home or Path.home()
|
|
94
|
+
targets = [(root.joinpath(*target.relative_path), target.label)
|
|
95
|
+
for target in _selected_targets(clients)]
|
|
96
|
+
|
|
97
|
+
conflicts = [path for path, _label in targets
|
|
98
|
+
if path.exists() and path.read_text(encoding="utf-8") !=
|
|
99
|
+
SKILL_CONTENT]
|
|
100
|
+
if conflicts:
|
|
101
|
+
rendered = ", ".join(str(path) for path in conflicts)
|
|
102
|
+
raise SkillConflictError(
|
|
103
|
+
f"refusing to overwrite existing skill file(s): {rendered}")
|
|
104
|
+
|
|
105
|
+
for path, _label in targets:
|
|
106
|
+
if path.exists():
|
|
107
|
+
continue
|
|
108
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
109
|
+
path.write_text(SKILL_CONTENT, encoding="utf-8")
|
|
110
|
+
return targets
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def cmd_install_skill(args) -> int:
|
|
114
|
+
targets = install_skills(getattr(args, "client", None))
|
|
115
|
+
print("Installed or verified TraceKite skill:")
|
|
116
|
+
for path, label in targets:
|
|
117
|
+
print(f" {label}: {path}")
|
|
118
|
+
print("MCP registration is client-specific; see "
|
|
119
|
+
"docs/plugins-and-mcp-guide.md.")
|
|
120
|
+
return 0
|