deepctl-core 0.2.11__tar.gz → 0.2.13__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.
Files changed (22) hide show
  1. {deepctl_core-0.2.11 → deepctl_core-0.2.13}/PKG-INFO +1 -1
  2. {deepctl_core-0.2.11 → deepctl_core-0.2.13}/pyproject.toml +1 -1
  3. {deepctl_core-0.2.11 → deepctl_core-0.2.13}/src/deepctl_core/base_command.py +92 -3
  4. {deepctl_core-0.2.11 → deepctl_core-0.2.13}/src/deepctl_core.egg-info/PKG-INFO +1 -1
  5. {deepctl_core-0.2.11 → deepctl_core-0.2.13}/README.md +0 -0
  6. {deepctl_core-0.2.11 → deepctl_core-0.2.13}/setup.cfg +0 -0
  7. {deepctl_core-0.2.11 → deepctl_core-0.2.13}/src/deepctl_core/__init__.py +0 -0
  8. {deepctl_core-0.2.11 → deepctl_core-0.2.13}/src/deepctl_core/auth.py +0 -0
  9. {deepctl_core-0.2.11 → deepctl_core-0.2.13}/src/deepctl_core/base_group_command.py +0 -0
  10. {deepctl_core-0.2.11 → deepctl_core-0.2.13}/src/deepctl_core/client.py +0 -0
  11. {deepctl_core-0.2.11 → deepctl_core-0.2.13}/src/deepctl_core/config.py +0 -0
  12. {deepctl_core-0.2.11 → deepctl_core-0.2.13}/src/deepctl_core/models.py +0 -0
  13. {deepctl_core-0.2.11 → deepctl_core-0.2.13}/src/deepctl_core/output.py +0 -0
  14. {deepctl_core-0.2.11 → deepctl_core-0.2.13}/src/deepctl_core/plugin_env.py +0 -0
  15. {deepctl_core-0.2.11 → deepctl_core-0.2.13}/src/deepctl_core/plugin_manager.py +0 -0
  16. {deepctl_core-0.2.11 → deepctl_core-0.2.13}/src/deepctl_core/py.typed +0 -0
  17. {deepctl_core-0.2.11 → deepctl_core-0.2.13}/src/deepctl_core/skill_generator.py +0 -0
  18. {deepctl_core-0.2.11 → deepctl_core-0.2.13}/src/deepctl_core/timing.py +0 -0
  19. {deepctl_core-0.2.11 → deepctl_core-0.2.13}/src/deepctl_core.egg-info/SOURCES.txt +0 -0
  20. {deepctl_core-0.2.11 → deepctl_core-0.2.13}/src/deepctl_core.egg-info/dependency_links.txt +0 -0
  21. {deepctl_core-0.2.11 → deepctl_core-0.2.13}/src/deepctl_core.egg-info/requires.txt +0 -0
  22. {deepctl_core-0.2.11 → deepctl_core-0.2.13}/src/deepctl_core.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepctl-core
3
- Version: 0.2.11
3
+ Version: 0.2.13
4
4
  Summary: Core components for deepctl
5
5
  Author-email: Deepgram <devrel@deepgram.com>
6
6
  Maintainer-email: Deepgram <devrel@deepgram.com>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "deepctl-core"
7
- version = "0.2.11" # x-release-please-version
7
+ version = "0.2.13" # x-release-please-version
8
8
  description = "Core components for deepctl"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -39,6 +39,7 @@ class BaseCommand(ABC):
39
39
  raise ValueError("Command must have a name")
40
40
  if not self.help:
41
41
  raise ValueError("Command must have help text")
42
+ self._guided: bool = True
42
43
 
43
44
  def execute(self, ctx: click.Context, **kwargs: Any) -> None:
44
45
  """Execute the command with Click context.
@@ -104,26 +105,112 @@ class BaseCommand(ABC):
104
105
  )
105
106
  raise click.ClickException("Project ID required")
106
107
 
108
+ self._guided = self.is_guided(ctx)
109
+ self._tag_telemetry_start(ctx)
110
+
107
111
  # Execute the command
108
112
  try:
109
113
  with TimingContext(f"command_{self.name}_handler"):
110
114
  result = self.handle(config, auth_manager, client, **kwargs)
111
115
 
116
+ status = "ok"
117
+ if result is not None and hasattr(result, "status"):
118
+ status = str(result.status)
119
+ self._tag_telemetry_status(status)
120
+
112
121
  # Handle command result
113
122
  if result is not None:
114
123
  with TimingContext("output_processing"):
115
- self.output_result(result, config)
124
+ try:
125
+ self.output_result(result, config)
126
+ except (BrokenPipeError, OSError):
127
+ # Downstream stream closed (e.g. an MCP host disconnected
128
+ # stdio after `dg mcp` finished). Nothing useful to log
129
+ # here because the logger writes to the same closed stream.
130
+ pass
131
+ except ValueError as exc:
132
+ if "closed file" not in str(exc):
133
+ raise
116
134
 
117
135
  except KeyboardInterrupt:
136
+ self._tag_telemetry_status("cancelled")
118
137
  stderr_console.print("\n[yellow]Command cancelled by user[/yellow]")
119
138
  raise click.Abort()
120
139
 
121
140
  except Exception as e:
141
+ self._tag_telemetry_status("error")
122
142
  print_error(f"Command failed: {e}")
123
143
  if config.get("output.verbose", False):
124
144
  stderr_console.print_exception()
125
145
  raise click.ClickException(str(e))
126
146
 
147
+ def is_guided(self, ctx: click.Context) -> bool:
148
+ """True only when the user invoked this command with no input at all.
149
+
150
+ 'No input' = no positional arg, no flag, no option value, no env-var
151
+ override. The single rule for every command: any user-provided signal
152
+ means scripting intent — skip prompts. Only the bare invocation gets
153
+ the guided/interactive flow.
154
+
155
+ Returns False whenever telemetry's `_agentic` heuristic fires
156
+ (CI=1, --agent-friendly, --non-interactive, AI tool env vars, etc.).
157
+ """
158
+ if _agentic:
159
+ return False
160
+ for param in ctx.command.params:
161
+ if not param.name:
162
+ continue
163
+ src = ctx.get_parameter_source(param.name)
164
+ if src is not None and src.name in ("COMMANDLINE", "ENVIRONMENT"):
165
+ return False
166
+ return True
167
+
168
+ def _tag_telemetry_start(self, ctx: click.Context) -> None:
169
+ """Annotate the active Sentry transaction with command-level usage signal.
170
+
171
+ Sets the transaction name to the full Click command path
172
+ (e.g. 'deepctl debug audio' instead of just 'debug') and tags it with
173
+ the user-provided flag NAMES (never values), the requested output
174
+ format, and the auth method. All values are bounded enums or
175
+ already-public flag identifiers — no PII risk.
176
+
177
+ Wrapped in a bare except so a Sentry hiccup, missing scope, or unknown
178
+ Click parameter-source enum value can never crash the user's command.
179
+ """
180
+ try:
181
+ import sentry_sdk
182
+
183
+ scope = sentry_sdk.get_current_scope()
184
+ transaction = getattr(scope, "transaction", None)
185
+ if transaction is not None and ctx.command_path:
186
+ transaction.name = ctx.command_path
187
+
188
+ used: list[str] = []
189
+ for param in ctx.command.params:
190
+ if not param.name:
191
+ continue
192
+ src = ctx.get_parameter_source(param.name)
193
+ if src is not None and src.name == "COMMANDLINE":
194
+ used.append(param.name)
195
+ scope.set_tag("cmd.flags", ",".join(sorted(used)) or "(none)")
196
+ scope.set_tag("cmd.output_format", ctx.params.get("output") or "default")
197
+ except Exception:
198
+ pass
199
+
200
+ def _tag_telemetry_status(self, status: str) -> None:
201
+ """Tag the active Sentry transaction with the command outcome.
202
+
203
+ Status is a bounded enum: 'ok', 'cancelled', 'error', or whatever the
204
+ command's BaseResult.status returned (also bounded by the result
205
+ model). Bare except so telemetry can't crash the command teardown.
206
+ """
207
+ try:
208
+ import sentry_sdk
209
+
210
+ sentry_sdk.get_current_scope().set_tag("cmd.status", status)
211
+ except Exception:
212
+ pass
213
+
127
214
  @abstractmethod
128
215
  def handle(
129
216
  self,
@@ -294,7 +381,7 @@ class BaseCommand(ABC):
294
381
  Returns:
295
382
  True if confirmed, False otherwise
296
383
  """
297
- if _agentic or not self.ci_friendly:
384
+ if _agentic or not self.ci_friendly or not getattr(self, "_guided", True):
298
385
  return default
299
386
 
300
387
  try:
@@ -318,7 +405,9 @@ class BaseCommand(ABC):
318
405
  Returns:
319
406
  User input
320
407
  """
321
- if (_agentic or not self.ci_friendly) and default is not None:
408
+ if (
409
+ _agentic or not self.ci_friendly or not getattr(self, "_guided", True)
410
+ ) and default is not None:
322
411
  return default
323
412
 
324
413
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepctl-core
3
- Version: 0.2.11
3
+ Version: 0.2.13
4
4
  Summary: Core components for deepctl
5
5
  Author-email: Deepgram <devrel@deepgram.com>
6
6
  Maintainer-email: Deepgram <devrel@deepgram.com>
File without changes
File without changes