deepctl-core 0.2.11__tar.gz → 0.2.12__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.12}/PKG-INFO +1 -1
  2. {deepctl_core-0.2.11 → deepctl_core-0.2.12}/pyproject.toml +1 -1
  3. {deepctl_core-0.2.11 → deepctl_core-0.2.12}/src/deepctl_core/base_command.py +82 -2
  4. {deepctl_core-0.2.11 → deepctl_core-0.2.12}/src/deepctl_core.egg-info/PKG-INFO +1 -1
  5. {deepctl_core-0.2.11 → deepctl_core-0.2.12}/README.md +0 -0
  6. {deepctl_core-0.2.11 → deepctl_core-0.2.12}/setup.cfg +0 -0
  7. {deepctl_core-0.2.11 → deepctl_core-0.2.12}/src/deepctl_core/__init__.py +0 -0
  8. {deepctl_core-0.2.11 → deepctl_core-0.2.12}/src/deepctl_core/auth.py +0 -0
  9. {deepctl_core-0.2.11 → deepctl_core-0.2.12}/src/deepctl_core/base_group_command.py +0 -0
  10. {deepctl_core-0.2.11 → deepctl_core-0.2.12}/src/deepctl_core/client.py +0 -0
  11. {deepctl_core-0.2.11 → deepctl_core-0.2.12}/src/deepctl_core/config.py +0 -0
  12. {deepctl_core-0.2.11 → deepctl_core-0.2.12}/src/deepctl_core/models.py +0 -0
  13. {deepctl_core-0.2.11 → deepctl_core-0.2.12}/src/deepctl_core/output.py +0 -0
  14. {deepctl_core-0.2.11 → deepctl_core-0.2.12}/src/deepctl_core/plugin_env.py +0 -0
  15. {deepctl_core-0.2.11 → deepctl_core-0.2.12}/src/deepctl_core/plugin_manager.py +0 -0
  16. {deepctl_core-0.2.11 → deepctl_core-0.2.12}/src/deepctl_core/py.typed +0 -0
  17. {deepctl_core-0.2.11 → deepctl_core-0.2.12}/src/deepctl_core/skill_generator.py +0 -0
  18. {deepctl_core-0.2.11 → deepctl_core-0.2.12}/src/deepctl_core/timing.py +0 -0
  19. {deepctl_core-0.2.11 → deepctl_core-0.2.12}/src/deepctl_core.egg-info/SOURCES.txt +0 -0
  20. {deepctl_core-0.2.11 → deepctl_core-0.2.12}/src/deepctl_core.egg-info/dependency_links.txt +0 -0
  21. {deepctl_core-0.2.11 → deepctl_core-0.2.12}/src/deepctl_core.egg-info/requires.txt +0 -0
  22. {deepctl_core-0.2.11 → deepctl_core-0.2.12}/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.12
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.12" # 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,103 @@ 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
124
  self.output_result(result, config)
116
125
 
117
126
  except KeyboardInterrupt:
127
+ self._tag_telemetry_status("cancelled")
118
128
  stderr_console.print("\n[yellow]Command cancelled by user[/yellow]")
119
129
  raise click.Abort()
120
130
 
121
131
  except Exception as e:
132
+ self._tag_telemetry_status("error")
122
133
  print_error(f"Command failed: {e}")
123
134
  if config.get("output.verbose", False):
124
135
  stderr_console.print_exception()
125
136
  raise click.ClickException(str(e))
126
137
 
138
+ def is_guided(self, ctx: click.Context) -> bool:
139
+ """True only when the user invoked this command with no input at all.
140
+
141
+ 'No input' = no positional arg, no flag, no option value, no env-var
142
+ override. The single rule for every command: any user-provided signal
143
+ means scripting intent — skip prompts. Only the bare invocation gets
144
+ the guided/interactive flow.
145
+
146
+ Returns False whenever telemetry's `_agentic` heuristic fires
147
+ (CI=1, --agent-friendly, --non-interactive, AI tool env vars, etc.).
148
+ """
149
+ if _agentic:
150
+ return False
151
+ for param in ctx.command.params:
152
+ if not param.name:
153
+ continue
154
+ src = ctx.get_parameter_source(param.name)
155
+ if src is not None and src.name in ("COMMANDLINE", "ENVIRONMENT"):
156
+ return False
157
+ return True
158
+
159
+ def _tag_telemetry_start(self, ctx: click.Context) -> None:
160
+ """Annotate the active Sentry transaction with command-level usage signal.
161
+
162
+ Sets the transaction name to the full Click command path
163
+ (e.g. 'deepctl debug audio' instead of just 'debug') and tags it with
164
+ the user-provided flag NAMES (never values), the requested output
165
+ format, and the auth method. All values are bounded enums or
166
+ already-public flag identifiers — no PII risk.
167
+
168
+ Wrapped in a bare except so a Sentry hiccup, missing scope, or unknown
169
+ Click parameter-source enum value can never crash the user's command.
170
+ """
171
+ try:
172
+ import sentry_sdk
173
+
174
+ scope = sentry_sdk.get_current_scope()
175
+ transaction = getattr(scope, "transaction", None)
176
+ if transaction is not None and ctx.command_path:
177
+ transaction.name = ctx.command_path
178
+
179
+ used: list[str] = []
180
+ for param in ctx.command.params:
181
+ if not param.name:
182
+ continue
183
+ src = ctx.get_parameter_source(param.name)
184
+ if src is not None and src.name == "COMMANDLINE":
185
+ used.append(param.name)
186
+ scope.set_tag("cmd.flags", ",".join(sorted(used)) or "(none)")
187
+ scope.set_tag("cmd.output_format", ctx.params.get("output") or "default")
188
+ except Exception:
189
+ pass
190
+
191
+ def _tag_telemetry_status(self, status: str) -> None:
192
+ """Tag the active Sentry transaction with the command outcome.
193
+
194
+ Status is a bounded enum: 'ok', 'cancelled', 'error', or whatever the
195
+ command's BaseResult.status returned (also bounded by the result
196
+ model). Bare except so telemetry can't crash the command teardown.
197
+ """
198
+ try:
199
+ import sentry_sdk
200
+
201
+ sentry_sdk.get_current_scope().set_tag("cmd.status", status)
202
+ except Exception:
203
+ pass
204
+
127
205
  @abstractmethod
128
206
  def handle(
129
207
  self,
@@ -294,7 +372,7 @@ class BaseCommand(ABC):
294
372
  Returns:
295
373
  True if confirmed, False otherwise
296
374
  """
297
- if _agentic or not self.ci_friendly:
375
+ if _agentic or not self.ci_friendly or not getattr(self, "_guided", True):
298
376
  return default
299
377
 
300
378
  try:
@@ -318,7 +396,9 @@ class BaseCommand(ABC):
318
396
  Returns:
319
397
  User input
320
398
  """
321
- if (_agentic or not self.ci_friendly) and default is not None:
399
+ if (
400
+ _agentic or not self.ci_friendly or not getattr(self, "_guided", True)
401
+ ) and default is not None:
322
402
  return default
323
403
 
324
404
  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.12
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