detectkit 0.65.0__tar.gz → 0.66.0__tar.gz
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.
- {detectkit-0.65.0/detectkit.egg-info → detectkit-0.66.0}/PKG-INFO +3 -3
- {detectkit-0.65.0 → detectkit-0.66.0}/README.md +2 -2
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/__init__.py +1 -1
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/assets/claude/CLAUDE.section.md +1 -1
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/assets/claude/rules/overview.md +5 -4
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/assets/claude/rules/project.md +16 -7
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/commands/init.py +6 -3
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/config/profile.py +23 -10
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/database/duckdb_manager.py +61 -18
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/mcp/context.py +3 -2
- {detectkit-0.65.0 → detectkit-0.66.0/detectkit.egg-info}/PKG-INFO +3 -3
- {detectkit-0.65.0 → detectkit-0.66.0}/LICENSE +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/MANIFEST.in +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/__init__.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/channels/__init__.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/channels/base.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/channels/branding.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/channels/discord.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/channels/email.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/channels/factory.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/channels/googlechat.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/channels/mattermost.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/channels/ntfy.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/channels/slack.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/channels/teams.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/channels/telegram.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/channels/webhook.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/orchestrator/__init__.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/orchestrator/_base.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/orchestrator/_cooldown.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/orchestrator/_decision.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/orchestrator/_dispatch.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/orchestrator/_recovery.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/orchestrator/_replay.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/orchestrator/_types.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/alerting/orchestrator/orchestrator.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/autotune/__init__.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/autotune/_base.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/autotune/_types.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/autotune/autotuner.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/autotune/axis_spec.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/autotune/config_emitter.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/autotune/crossval.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/autotune/detector_select.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/autotune/distribution.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/autotune/grid_search.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/autotune/labels.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/autotune/result.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/autotune/runner.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/autotune/scoring.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/autotune/seasonality_search.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/autotune/settings.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/autotune/window_select.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/__init__.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/_output.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/assets/claude/rules/alerting.md +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/assets/claude/rules/autotune.md +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/assets/claude/rules/cli.md +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/assets/claude/rules/detectors.md +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/assets/claude/rules/metrics.md +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/assets/claude/skills/dtk-autotune/SKILL.md +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/assets/claude/skills/dtk-feedback/SKILL.md +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/assets/claude/skills/dtk-new-metric/SKILL.md +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/assets/claude/skills/dtk-setup-project/SKILL.md +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/assets/claude/skills/dtk-tune/SKILL.md +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/commands/__init__.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/commands/autotune.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/commands/clean.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/commands/init_claude.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/commands/mcp.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/commands/osi.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/commands/run.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/commands/test_alert.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/commands/tune.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/commands/ui.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/commands/unlock.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/main.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/config/__init__.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/config/metric_config.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/config/metric_io.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/config/project_config.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/config/validator.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/core/__init__.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/core/interval.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/core/models.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/database/__init__.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/database/_sql_manager.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/database/bigquery_manager.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/database/clickhouse_manager.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/database/internal_tables/__init__.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/database/internal_tables/_alert_states.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/database/internal_tables/_autotune_runs.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/database/internal_tables/_base.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/database/internal_tables/_datapoints.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/database/internal_tables/_detections.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/database/internal_tables/_maintenance.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/database/internal_tables/_metrics.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/database/internal_tables/_schema.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/database/internal_tables/_tasks.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/database/internal_tables/manager.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/database/manager.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/database/mysql_manager.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/database/postgres_manager.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/database/snowflake_manager.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/database/source_manager.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/database/tables.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/detectors/__init__.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/detectors/base.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/detectors/factory.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/detectors/seasonality.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/detectors/statistical/__init__.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/detectors/statistical/_windowed.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/detectors/statistical/autoreg.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/detectors/statistical/iqr.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/detectors/statistical/mad.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/detectors/statistical/manual_bounds.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/detectors/statistical/zscore.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/loaders/__init__.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/loaders/errors.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/loaders/metric_loader.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/loaders/query_template.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/mcp/__init__.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/mcp/errors.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/mcp/serialize.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/mcp/server.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/mcp/tools.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/orchestration/__init__.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/orchestration/error_dispatch.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/orchestration/task_manager/__init__.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/orchestration/task_manager/_alert_step.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/orchestration/task_manager/_base.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/orchestration/task_manager/_detect_step.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/orchestration/task_manager/_load_step.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/orchestration/task_manager/_types.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/orchestration/task_manager/manager.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/reporting/__init__.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/reporting/assets/report.js +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/reporting/builder.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/reporting/html_report.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/semantic/__init__.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/semantic/errors.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/semantic/exporter.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/semantic/importer.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/semantic/osi_model.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/semantic/query_gen.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/tuning/__init__.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/tuning/assets/tune.js +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/tuning/config_writer.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/tuning/html.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/tuning/payload.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/tuning/server.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/ui/__init__.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/ui/assets/ui.js +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/ui/html.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/ui/jobs.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/ui/metric_files.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/ui/overview.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/ui/server.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/utils/__init__.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/utils/datetime_utils.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/utils/env_interpolation.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/utils/json_utils.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit/utils/stats.py +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit.egg-info/SOURCES.txt +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit.egg-info/dependency_links.txt +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit.egg-info/entry_points.txt +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit.egg-info/requires.txt +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/detectkit.egg-info/top_level.txt +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/pyproject.toml +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/requirements.txt +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/setup.cfg +0 -0
- {detectkit-0.65.0 → detectkit-0.66.0}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: detectkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.66.0
|
|
4
4
|
Summary: Metric monitoring with automatic anomaly detection
|
|
5
5
|
Author: detectkit team
|
|
6
6
|
License: MIT
|
|
@@ -114,7 +114,7 @@ Dynamic: license-file
|
|
|
114
114
|
- **@mentions** — tag users/groups in alerts, each channel formats natively
|
|
115
115
|
- **Alert lifecycle** — consecutive anomalies, fraction-of-window rule (`anomaly_window` + `min_anomaly_share`), cooldown, recovery notifications, no-data alerts
|
|
116
116
|
- **Project-level error alerts** — catch DB outages and pipeline crashes once per run
|
|
117
|
-
- **Database agnostic** — ClickHouse, PostgreSQL, MySQL/MariaDB, DuckDB (no server needed); Snowflake and BigQuery as hybrid-mode sources
|
|
117
|
+
- **Database agnostic** — ClickHouse, PostgreSQL, MySQL/MariaDB, DuckDB (no server needed, incl. MotherDuck cloud paths); Snowflake and BigQuery as hybrid-mode sources
|
|
118
118
|
- **Hybrid mode** — read metric SQL from a billed-per-query warehouse while all pipeline state lives in a cheap local database
|
|
119
119
|
- **Idempotent** — resume from interruptions, no duplicate processing
|
|
120
120
|
- **CLI** — `dtk init`, `dtk run --select`, `dtk unlock`, `dtk clean`, tag-based selectors
|
|
@@ -130,7 +130,7 @@ With database drivers:
|
|
|
130
130
|
|
|
131
131
|
```bash
|
|
132
132
|
pip install detectkit[clickhouse] # ClickHouse
|
|
133
|
-
pip install detectkit[duckdb] # DuckDB - single file, no server
|
|
133
|
+
pip install detectkit[duckdb] # DuckDB - single file, no server (also MotherDuck md: paths)
|
|
134
134
|
pip install detectkit[snowflake] # Snowflake - source-only (hybrid mode)
|
|
135
135
|
pip install detectkit[bigquery] # BigQuery - source-only (hybrid mode)
|
|
136
136
|
pip install detectkit[all-db] # All databases
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
- **@mentions** — tag users/groups in alerts, each channel formats natively
|
|
25
25
|
- **Alert lifecycle** — consecutive anomalies, fraction-of-window rule (`anomaly_window` + `min_anomaly_share`), cooldown, recovery notifications, no-data alerts
|
|
26
26
|
- **Project-level error alerts** — catch DB outages and pipeline crashes once per run
|
|
27
|
-
- **Database agnostic** — ClickHouse, PostgreSQL, MySQL/MariaDB, DuckDB (no server needed); Snowflake and BigQuery as hybrid-mode sources
|
|
27
|
+
- **Database agnostic** — ClickHouse, PostgreSQL, MySQL/MariaDB, DuckDB (no server needed, incl. MotherDuck cloud paths); Snowflake and BigQuery as hybrid-mode sources
|
|
28
28
|
- **Hybrid mode** — read metric SQL from a billed-per-query warehouse while all pipeline state lives in a cheap local database
|
|
29
29
|
- **Idempotent** — resume from interruptions, no duplicate processing
|
|
30
30
|
- **CLI** — `dtk init`, `dtk run --select`, `dtk unlock`, `dtk clean`, tag-based selectors
|
|
@@ -40,7 +40,7 @@ With database drivers:
|
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
42
|
pip install detectkit[clickhouse] # ClickHouse
|
|
43
|
-
pip install detectkit[duckdb] # DuckDB - single file, no server
|
|
43
|
+
pip install detectkit[duckdb] # DuckDB - single file, no server (also MotherDuck md: paths)
|
|
44
44
|
pip install detectkit[snowflake] # Snowflake - source-only (hybrid mode)
|
|
45
45
|
pip install detectkit[bigquery] # BigQuery - source-only (hybrid mode)
|
|
46
46
|
pip install detectkit[all-db] # All databases
|
|
@@ -4,7 +4,7 @@ detectk - Anomaly Detection for Time-Series Metrics
|
|
|
4
4
|
A Python library for data analysts and engineers to monitor metrics with automatic anomaly detection.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
__version__ = "0.
|
|
7
|
+
__version__ = "0.66.0"
|
|
8
8
|
|
|
9
9
|
from detectkit.core.interval import Interval
|
|
10
10
|
from detectkit.core.models import ColumnDefinition, TableModel
|
|
@@ -14,7 +14,7 @@ conventions.
|
|
|
14
14
|
**Database access for _you_ (recommended, not required).** detectkit itself
|
|
15
15
|
connects to the database directly via its drivers — it **never** needs an MCP to
|
|
16
16
|
run. But you assist far better with **read access to the same database** (e.g. a
|
|
17
|
-
database MCP for the project's ClickHouse / PostgreSQL / MySQL / MariaDB / DuckDB / Snowflake / BigQuery (the last two source-only, hybrid mode)): you can inspect
|
|
17
|
+
database MCP for the project's ClickHouse / PostgreSQL / MySQL / MariaDB / DuckDB (incl. MotherDuck `md:` paths) / Snowflake / BigQuery (the last two source-only, hybrid mode)): you can inspect
|
|
18
18
|
a metric's series, find real incidents to label for `dtk autotune`, sanity-check
|
|
19
19
|
a metric query before running it, and confirm detections — instead of asking the
|
|
20
20
|
user to run every query by hand. Without it, fall back to marking incidents
|
|
@@ -6,10 +6,11 @@ metrics with automatic anomaly detection and multi-channel alerting. It is
|
|
|
6
6
|
them with one command. Core logic is pure numpy (no pandas). **ClickHouse,
|
|
7
7
|
PostgreSQL, MySQL/MariaDB and DuckDB are all fully supported** — only the
|
|
8
8
|
connection and the SQL dialect of your metric queries differ between them
|
|
9
|
-
(DuckDB additionally needs no server at all — just a local file
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
(DuckDB additionally needs no server at all — just a local file, or a
|
|
10
|
+
`md:<database>` path to run fully on **MotherDuck**, DuckDB's cloud);
|
|
11
|
+
**Snowflake** and **BigQuery** are supported as **source-only** backends
|
|
12
|
+
(hybrid mode — they run a metric's load SQL, but `_dtk_*` state must live in one
|
|
13
|
+
of the full backends).
|
|
13
14
|
|
|
14
15
|
## The pipeline: load → detect → alert
|
|
15
16
|
|
|
@@ -201,23 +201,32 @@ auto-detected at connect (`SELECT VERSION()`), so MariaDB gets its own
|
|
|
201
201
|
`VALUES()`-form upsert instead of MySQL 8.0.19's row-alias form.
|
|
202
202
|
`pip install 'detectkit[mariadb]'`.
|
|
203
203
|
|
|
204
|
-
**DuckDB** (embedded, single file; two schemas like PostgreSQL
|
|
204
|
+
**DuckDB** (embedded, single file; two schemas like PostgreSQL — or
|
|
205
|
+
**MotherDuck**, DuckDB's cloud, via a `md:` path):
|
|
205
206
|
```yaml
|
|
206
207
|
profiles:
|
|
207
208
|
prod:
|
|
208
209
|
type: duckdb
|
|
209
|
-
path: /var/lib/detectkit/warehouse.duckdb # required — file path,
|
|
210
|
+
path: /var/lib/detectkit/warehouse.duckdb # required — file path, ":memory:",
|
|
211
|
+
# # or "md:<database>" for MotherDuck
|
|
210
212
|
internal_schema: detectkit # optional — _dtk_* tables (default: "detectkit")
|
|
211
213
|
data_schema: main # optional — data queries (default: "main")
|
|
212
|
-
read_only: false # optional (default: false)
|
|
214
|
+
read_only: false # optional (default: false; local files only)
|
|
215
|
+
motherduck_token: "{{ env_var('MOTHERDUCK_TOKEN') }}" # optional — only for "md:" paths
|
|
213
216
|
settings: {} # optional — extra duckdb.connect() config options
|
|
214
217
|
```
|
|
215
|
-
> Single-writer: DuckDB allows only **one**
|
|
216
|
-
> at a time — a `dtk ui`/`dtk tune` session left open will
|
|
217
|
-
> separately spawned `dtk run` against the same file. Point a
|
|
218
|
-
> consumer at it with `read_only: true`.
|
|
218
|
+
> Single-writer (**local files only**): a local DuckDB file allows only **one**
|
|
219
|
+
> read-write connection at a time — a `dtk ui`/`dtk tune` session left open will
|
|
220
|
+
> clash with a separately spawned `dtk run` against the same file. Point a
|
|
221
|
+
> read-only consumer at it with `read_only: true`. A `md:<database>` MotherDuck
|
|
222
|
+
> path is a **served** database with no single-writer rule — `dtk ui` and a
|
|
223
|
+
> spawned `dtk run` coexist, and `read_only` doesn't apply.
|
|
219
224
|
> `path: ":memory:"` has no on-disk state, so resume/idempotency breaks across
|
|
220
225
|
> process restarts — use it for tests/scratch only, never a real project.
|
|
226
|
+
> `motherduck_token` (env-interpolated) authenticates `md:` paths; unset, the
|
|
227
|
+
> `motherduck` extension falls back to a `motherduck_token` environment
|
|
228
|
+
> variable (lowercase — the extension's own lookup name). MotherDuck is a full
|
|
229
|
+
> state backend — `_dtk_*` tables can live there — and needs no extra beyond
|
|
221
230
|
> `pip install 'detectkit[duckdb]'`.
|
|
222
231
|
|
|
223
232
|
**Snowflake** (**source-only** — hybrid mode; runs a metric's load SQL, never
|
|
@@ -127,11 +127,14 @@ _COMMENTED_EXAMPLES = {
|
|
|
127
127
|
"duckdb": """ # Example DuckDB profile (in-process, single-file; no host/port/user/password)
|
|
128
128
|
# duckdb_dev:
|
|
129
129
|
# type: duckdb
|
|
130
|
-
# path: ./detectkit.duckdb # or ':memory:'
|
|
130
|
+
# path: ./detectkit.duckdb # or ':memory:' (transient, tests-only), or
|
|
131
|
+
# # # 'md:<database>' for MotherDuck (cloud; add
|
|
132
|
+
# # # motherduck_token: "{{ env_var('MOTHERDUCK_TOKEN') }}")
|
|
131
133
|
# internal_schema: detectkit
|
|
132
134
|
# data_schema: main
|
|
133
|
-
# # Only ONE read-write connection at a time
|
|
134
|
-
# #
|
|
135
|
+
# # Only ONE read-write connection at a time (local files; md: paths are
|
|
136
|
+
# # served and don't have this limit) — don't point a scheduled `dtk run`
|
|
137
|
+
# # and a long-lived `dtk ui` at the same local file simultaneously.
|
|
135
138
|
""",
|
|
136
139
|
}
|
|
137
140
|
|
|
@@ -49,8 +49,11 @@ class ProfileConfig(BaseModel):
|
|
|
49
49
|
database: Connection-target database (PostgreSQL/MySQL/MariaDB; the
|
|
50
50
|
session default database for Snowflake; unused for DuckDB — use
|
|
51
51
|
`path` instead)
|
|
52
|
-
path: Path to the DuckDB database file,
|
|
53
|
-
in-process database
|
|
52
|
+
path: Path to the DuckDB database file, ":memory:" for a transient
|
|
53
|
+
in-process database, or "md:<database>" for a MotherDuck cloud
|
|
54
|
+
database (DuckDB only)
|
|
55
|
+
motherduck_token: MotherDuck service token for "md:" paths (DuckDB
|
|
56
|
+
only; unset -> the extension reads the motherduck_token env var)
|
|
54
57
|
account: Snowflake account identifier (e.g. "myorg-myaccount")
|
|
55
58
|
warehouse: Snowflake virtual warehouse to run load queries on
|
|
56
59
|
role: Snowflake role for the session
|
|
@@ -106,20 +109,29 @@ class ProfileConfig(BaseModel):
|
|
|
106
109
|
default=None, description="Database to connect to (PostgreSQL/MySQL/MariaDB)"
|
|
107
110
|
)
|
|
108
111
|
|
|
109
|
-
# DuckDB-only: the database file path (or ":memory:"
|
|
110
|
-
# password/database above are simply
|
|
111
|
-
# rejected, since e.g. `host` always
|
|
112
|
+
# DuckDB-only: the database file path (or ":memory:", or "md:<database>"
|
|
113
|
+
# for MotherDuck). host/port/user/password/database above are simply
|
|
114
|
+
# ignored for this backend rather than rejected, since e.g. `host` always
|
|
115
|
+
# carries its "localhost" default.
|
|
112
116
|
path: str | None = Field(
|
|
113
117
|
default=None,
|
|
114
|
-
description="Database file path,
|
|
118
|
+
description="Database file path, ':memory:' for a transient in-process "
|
|
119
|
+
"database, or 'md:<database>' for a MotherDuck cloud database (DuckDB only)",
|
|
115
120
|
)
|
|
116
121
|
read_only: bool = Field(
|
|
117
122
|
default=False,
|
|
118
123
|
description=(
|
|
119
|
-
"Open the database read-only (DuckDB only
|
|
120
|
-
"
|
|
124
|
+
"Open the database read-only (DuckDB local files only — MotherDuck "
|
|
125
|
+
"has no read-only attach) — lets a reader profile coexist with the "
|
|
126
|
+
"one process holding the file read-write"
|
|
121
127
|
),
|
|
122
128
|
)
|
|
129
|
+
motherduck_token: str | None = Field(
|
|
130
|
+
default=None,
|
|
131
|
+
description="MotherDuck service token for 'md:' paths (DuckDB only; "
|
|
132
|
+
"env-interpolated — unset falls back to the motherduck_token "
|
|
133
|
+
"environment variable read by the extension itself)",
|
|
134
|
+
)
|
|
123
135
|
|
|
124
136
|
# Snowflake-only (source-only backend). host/port are meaningless for it:
|
|
125
137
|
# the driver resolves the endpoint from `account`.
|
|
@@ -419,14 +431,15 @@ class ProfileConfig(BaseModel):
|
|
|
419
431
|
if not self.path:
|
|
420
432
|
raise ValueError(
|
|
421
433
|
"DuckDB profiles must set 'path' (the database file path, "
|
|
422
|
-
"
|
|
423
|
-
"
|
|
434
|
+
"':memory:' for a transient, tests/preview-only in-process "
|
|
435
|
+
"database, or 'md:<database>' for MotherDuck)"
|
|
424
436
|
)
|
|
425
437
|
return DuckDBDatabaseManager(
|
|
426
438
|
path=self.path,
|
|
427
439
|
internal_schema=self.get_internal_location(),
|
|
428
440
|
data_schema=self.get_data_location(),
|
|
429
441
|
read_only=self.read_only,
|
|
442
|
+
motherduck_token=self.motherduck_token,
|
|
430
443
|
settings=self.settings,
|
|
431
444
|
ensure_locations=ensure_locations,
|
|
432
445
|
)
|
|
@@ -9,10 +9,30 @@ internal/data *locations* are DuckDB **schemas** inside that one file,
|
|
|
9
9
|
created with ``CREATE SCHEMA IF NOT EXISTS`` (``main`` always exists and is
|
|
10
10
|
never explicitly created).
|
|
11
11
|
|
|
12
|
+
**MotherDuck.** The same manager also speaks `MotherDuck
|
|
13
|
+
<https://motherduck.com/>`_ — DuckDB's serverless cloud service — through the
|
|
14
|
+
same ``duckdb`` client: a ``path`` of the form ``md:<database>`` attaches the
|
|
15
|
+
named MotherDuck database (the ``motherduck`` core extension autoloads on
|
|
16
|
+
first use; the first connect downloads it, so it needs network access). Auth
|
|
17
|
+
is a service token, passed as the ``motherduck_token`` connect config (the
|
|
18
|
+
profile's ``motherduck_token`` field) — when unset, the extension itself
|
|
19
|
+
falls back to a ``motherduck_token`` environment variable. Everything below
|
|
20
|
+
the connect is identical: same SQL surface, same ``ON CONFLICT`` upsert, same
|
|
21
|
+
internal-tables flow. The **local-file operational caveats do not apply** to
|
|
22
|
+
``md:`` paths — MotherDuck is a served database, so multiple processes
|
|
23
|
+
(`dtk ui` + a concurrently spawned `dtk run`) can hold connections at once;
|
|
24
|
+
the single-writer rule below is a property of local files only. One
|
|
25
|
+
asymmetry: MotherDuck does not support DuckDB's ``read_only=True`` attach
|
|
26
|
+
flag, so the strict read-only probe (``ensure_locations=False``) skips the
|
|
27
|
+
forced read-only for ``md:`` paths — its purpose there (preventing a missing
|
|
28
|
+
local *file* from being created as a connect side effect) doesn't apply to a
|
|
29
|
+
served database; the probe still runs no DDL.
|
|
30
|
+
|
|
12
31
|
**Operational model — read this before pointing a scheduled `dtk run` and a
|
|
13
|
-
long-lived `dtk ui` at the same file.**
|
|
14
|
-
|
|
15
|
-
|
|
32
|
+
long-lived `dtk ui` at the same file.** (Local files only — ``md:`` paths
|
|
33
|
+
are served and have no single-writer rule.) A DuckDB file is held read-write
|
|
34
|
+
by **one process at a time**: a second *process* attempting a read-write
|
|
35
|
+
attach fails. (Within the single writing process DuckDB itself allows further
|
|
16
36
|
connections — they share the cached database instance and may write
|
|
17
37
|
concurrently under MVCC with optimistic-conflict errors, and a same-process
|
|
18
38
|
``read_only=True`` attach fails on the config mismatch rather than the lock —
|
|
@@ -188,21 +208,29 @@ class DuckDBDatabaseManager(SQLDatabaseManager):
|
|
|
188
208
|
|
|
189
209
|
Args:
|
|
190
210
|
path: Path to the DuckDB database file (created if it doesn't exist),
|
|
191
|
-
|
|
192
|
-
database
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
211
|
+
the literal string ``":memory:"`` for a transient in-process
|
|
212
|
+
database, or ``"md:<database>"`` for a MotherDuck cloud database
|
|
213
|
+
(see the module docstring). ``:memory:`` is **tests/preview-only**
|
|
214
|
+
— its state is not persisted to disk and is lost when the process
|
|
215
|
+
exits, which breaks detectkit's resume-from-last-timestamp
|
|
216
|
+
idempotency across runs; use a real file path (or ``md:``) for
|
|
217
|
+
anything but a one-off test.
|
|
196
218
|
internal_schema: Schema for internal ``_dtk_*`` tables.
|
|
197
219
|
data_schema: Schema for user data tables. Defaults to ``"main"``,
|
|
198
220
|
DuckDB's always-present default schema.
|
|
199
|
-
read_only: Open the file read-only
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
221
|
+
read_only: Open the file read-only (local files only — MotherDuck
|
|
222
|
+
does not support a read-only attach). Required when another
|
|
223
|
+
process already holds the file read-write (DuckDB allows many
|
|
224
|
+
concurrent *readers*, never a reader alongside a writer). A
|
|
225
|
+
read-only connection cannot create schemas/tables, so it assumes
|
|
226
|
+
the internal/data schemas already exist.
|
|
227
|
+
motherduck_token: MotherDuck service token, sent as the
|
|
228
|
+
``motherduck_token`` connect config for ``md:`` paths (ignored
|
|
229
|
+
for local paths). Unset -> the ``motherduck`` extension falls
|
|
230
|
+
back to the ``motherduck_token`` environment variable.
|
|
204
231
|
settings: Extra ``duckdb.connect`` ``config`` options (e.g.
|
|
205
|
-
``{"memory_limit": "512MB"}``)
|
|
232
|
+
``{"memory_limit": "512MB"}``); merged over the token, so an
|
|
233
|
+
explicit ``settings["motherduck_token"]`` wins.
|
|
206
234
|
ensure_locations: When False, skip creating the internal/data
|
|
207
235
|
schemas as a side effect of connecting (a strict read-only
|
|
208
236
|
probe — see
|
|
@@ -235,6 +263,7 @@ class DuckDBDatabaseManager(SQLDatabaseManager):
|
|
|
235
263
|
internal_schema: str = "detectkit",
|
|
236
264
|
data_schema: str = "main",
|
|
237
265
|
read_only: bool = False,
|
|
266
|
+
motherduck_token: str | None = None,
|
|
238
267
|
settings: dict[str, Any] | None = None,
|
|
239
268
|
ensure_locations: bool = True,
|
|
240
269
|
) -> None:
|
|
@@ -245,9 +274,12 @@ class DuckDBDatabaseManager(SQLDatabaseManager):
|
|
|
245
274
|
if not path:
|
|
246
275
|
raise ValueError(
|
|
247
276
|
"DuckDBDatabaseManager requires a non-empty `path` (a database file "
|
|
248
|
-
"path,
|
|
249
|
-
"database whose state is lost between runs
|
|
277
|
+
"path, ':memory:' for a transient, tests/preview-only in-process "
|
|
278
|
+
"database whose state is lost between runs, or 'md:<database>' for "
|
|
279
|
+
"a MotherDuck cloud database)."
|
|
250
280
|
)
|
|
281
|
+
self._is_motherduck = path.startswith("md:")
|
|
282
|
+
self._motherduck_token = motherduck_token
|
|
251
283
|
# `ensure_locations=False` is a strict read-only PROBE (see
|
|
252
284
|
# `SQLDatabaseManager.__init__`): skipping `_ensure_locations()` is
|
|
253
285
|
# not enough on its own for DuckDB, because a plain read-write
|
|
@@ -258,7 +290,12 @@ class DuckDBDatabaseManager(SQLDatabaseManager):
|
|
|
258
290
|
# place, and DuckDB rejects a read-only in-memory connection outright
|
|
259
291
|
# (`CatalogException: Cannot launch in-memory database in read-only
|
|
260
292
|
# mode!`), so forcing it there would break rather than protect.
|
|
261
|
-
|
|
293
|
+
# "md:" paths are exempted for the same shape of reason: there is no
|
|
294
|
+
# local file to create as a connect side effect (MotherDuck is a
|
|
295
|
+
# served database), and MotherDuck does not support the read-only
|
|
296
|
+
# attach flag — the probe still runs no DDL.
|
|
297
|
+
force_read_only = not ensure_locations and path != ":memory:" and not self._is_motherduck
|
|
298
|
+
self._read_only = True if force_read_only else read_only
|
|
262
299
|
# Kept separately (and typed `str`, not `str | None`) from the base
|
|
263
300
|
# class's `self._database` so `_connect()` doesn't need to narrow an
|
|
264
301
|
# Optional it knows — by the ValueError check above — can't be None.
|
|
@@ -275,7 +312,13 @@ class DuckDBDatabaseManager(SQLDatabaseManager):
|
|
|
275
312
|
)
|
|
276
313
|
|
|
277
314
|
def _connect(self) -> Any:
|
|
278
|
-
|
|
315
|
+
config = dict(self._settings or {})
|
|
316
|
+
if self._is_motherduck and self._motherduck_token:
|
|
317
|
+
# `settings` merges over the token (an explicit
|
|
318
|
+
# settings["motherduck_token"] wins), mirroring the Snowflake
|
|
319
|
+
# manager's settings-over-pin precedence.
|
|
320
|
+
config = {"motherduck_token": self._motherduck_token, **config}
|
|
321
|
+
raw = duckdb.connect(self._path, read_only=self._read_only, config=config)
|
|
279
322
|
return _DuckDBConnectionAdapter(raw)
|
|
280
323
|
|
|
281
324
|
def _ensure_locations(self) -> None:
|
|
@@ -131,14 +131,15 @@ def _resolve_duckdb_path(profile_config: ProfileConfig, project_root: Path) -> P
|
|
|
131
131
|
would land wherever the launcher happened to start the process, and could
|
|
132
132
|
even *create* a stray state file there. Every other project-relative path
|
|
133
133
|
in detectkit (metrics/, incidents/, …) is resolved against the project
|
|
134
|
-
root, so a relative DuckDB ``path`` should be too. ``":memory:"
|
|
134
|
+
root, so a relative DuckDB ``path`` should be too. ``":memory:"``,
|
|
135
|
+
``"md:..."`` (a MotherDuck database name, not a filesystem path) and an
|
|
135
136
|
already-absolute path pass through unchanged. Returns a **copy**
|
|
136
137
|
(``model_copy``) — the original ``ProfileConfig``/``ProfilesConfig`` is
|
|
137
138
|
shared state (e.g. reused by ``get_server_info``) and must not be mutated.
|
|
138
139
|
"""
|
|
139
140
|
if profile_config.type != "duckdb" or not profile_config.path:
|
|
140
141
|
return profile_config
|
|
141
|
-
if profile_config.path == ":memory:":
|
|
142
|
+
if profile_config.path == ":memory:" or profile_config.path.startswith("md:"):
|
|
142
143
|
return profile_config
|
|
143
144
|
raw_path = Path(profile_config.path)
|
|
144
145
|
if raw_path.is_absolute():
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: detectkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.66.0
|
|
4
4
|
Summary: Metric monitoring with automatic anomaly detection
|
|
5
5
|
Author: detectkit team
|
|
6
6
|
License: MIT
|
|
@@ -114,7 +114,7 @@ Dynamic: license-file
|
|
|
114
114
|
- **@mentions** — tag users/groups in alerts, each channel formats natively
|
|
115
115
|
- **Alert lifecycle** — consecutive anomalies, fraction-of-window rule (`anomaly_window` + `min_anomaly_share`), cooldown, recovery notifications, no-data alerts
|
|
116
116
|
- **Project-level error alerts** — catch DB outages and pipeline crashes once per run
|
|
117
|
-
- **Database agnostic** — ClickHouse, PostgreSQL, MySQL/MariaDB, DuckDB (no server needed); Snowflake and BigQuery as hybrid-mode sources
|
|
117
|
+
- **Database agnostic** — ClickHouse, PostgreSQL, MySQL/MariaDB, DuckDB (no server needed, incl. MotherDuck cloud paths); Snowflake and BigQuery as hybrid-mode sources
|
|
118
118
|
- **Hybrid mode** — read metric SQL from a billed-per-query warehouse while all pipeline state lives in a cheap local database
|
|
119
119
|
- **Idempotent** — resume from interruptions, no duplicate processing
|
|
120
120
|
- **CLI** — `dtk init`, `dtk run --select`, `dtk unlock`, `dtk clean`, tag-based selectors
|
|
@@ -130,7 +130,7 @@ With database drivers:
|
|
|
130
130
|
|
|
131
131
|
```bash
|
|
132
132
|
pip install detectkit[clickhouse] # ClickHouse
|
|
133
|
-
pip install detectkit[duckdb] # DuckDB - single file, no server
|
|
133
|
+
pip install detectkit[duckdb] # DuckDB - single file, no server (also MotherDuck md: paths)
|
|
134
134
|
pip install detectkit[snowflake] # Snowflake - source-only (hybrid mode)
|
|
135
135
|
pip install detectkit[bigquery] # BigQuery - source-only (hybrid mode)
|
|
136
136
|
pip install detectkit[all-db] # All databases
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/assets/claude/skills/dtk-autotune/SKILL.md
RENAMED
|
File without changes
|
{detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/assets/claude/skills/dtk-feedback/SKILL.md
RENAMED
|
File without changes
|
{detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/assets/claude/skills/dtk-new-metric/SKILL.md
RENAMED
|
File without changes
|
{detectkit-0.65.0 → detectkit-0.66.0}/detectkit/cli/assets/claude/skills/dtk-setup-project/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|