Skip to content

CLI v5#222

Open
maximelb wants to merge 77 commits intomasterfrom
cli-v2
Open

CLI v5#222
maximelb wants to merge 77 commits intomasterfrom
cli-v2

Conversation

@maximelb
Copy link
Contributor

Description of the change

Temporary PR to trigger the test validations.

Type of change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

maximelb and others added 30 commits February 11, 2026 14:23
Full rewrite of the LimaCharlie Python SDK and CLI (v2.0.0) with
AI/LLM-first discoverability. All commands follow a consistent
noun-verb pattern with rich help, --explain flags, and multiple
output formats (json/yaml/csv/table/jsonl).

Core infrastructure:
- Click-based CLI with auto-discovered command modules
- HTTP client with JWT auth, retry with backoff, rate limiting
- Config system supporting ~/.limacharlie, env vars, named environments
- Structured error hierarchy with suggestion messages
- Output formatting with jmespath filtering

SDK (31 modules in limacharlie/sdk/):
- Organization, Sensor, D&R Rules, FP Rules, Hive, Outputs
- Artifacts, Payloads, Search, Extensions, Installation/Ingestion Keys
- Users, Groups, API Keys, Billing, Spout, Replay
- Integrity, Exfil, Logging Rules, Configs (sync), AI generation
- Investigations, USP, Jobs, YARA, ARL

CLI (49 command modules in limacharlie/commands/):
- Full CRUD for all resource types
- Hive shortcuts: secret, lookup, playbook, adapter, cloud-sensor, sop, note
- Help system: discover, help topics, cheatsheets, schema
- Streaming, sync, search with LCQL support

Tests:
- 481 unit tests (v2) covering all SDK and core modules
- 25 integration test files (63 tests) for API validation
- Removed 3 obsolete v1 unit tests replaced by v2 equivalents

Packaging:
- Entry point updated to limacharlie.cli:main
- Added click>=8.0 and jmespath dependencies

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- organization.py: service_request now base64-encodes request_data,
  get_urls unwraps data["url"], get_detections/get_audit_logs use
  correct response keys (detects/events) with next_cursor and
  gzip decompression, get_available_services unwraps replicants key,
  get_jobs decompresses compressed response
- sensor.py: get_info extracts data["info"], is_online checks
  data["online"] dict, get_tags handles v1 {sid: {tag: null}} format,
  is_isolated/is_sealed use should_isolate/should_seal fields,
  get_events uses next_cursor with decompression, get_children_events
  decompresses, get_overview extracts overview key
- search.py: adds https:// prefix and /v1 suffix to search URL,
  includes oid in all request bodies, sends times as strings
- extensions.py: rewritten to use correct extension/request/{name}
  endpoint with gzip+base64 encoded gzdata (was incorrectly going
  through service_request)
- configs.py: uses Extensions class directly instead of nonexistent
  org.extension_request method
- artifacts.py: rewritten to use ingestion URL with Basic auth
  headers matching v1 Logs.py upload pattern
- insight.py: search_ioc info param now configurable (was hardcoded),
  batch_search uses form-encoded params matching v1
- replay.py: passes is_async=True matching v1 behavior
- client.py: added unwrap() for gzip+base64 decompression
- cli.py: import errors logged when LC_DEBUG is set
- artifact.py CLI: --type/--start/--end/--limit no longer silently ignored
- sensor.py CLI: fixed --offset+--limit pagination
- _hive_shortcut.py: YAML-first parsing, supports usr_mtd/etag,
  better --confirm error message

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bug fixes:
- Fix HiveRecord crash from invalid kwargs in _hive_shortcut.py
- Fix org rename() sending wrong query param (newName -> name)
- Fix subscribe/unsubscribe extension not splitting res_cat/res_name
- Fix replay double-serialization of detect/respond via service_request
- Fix on_refresh_auth callback inconsistency (now always passes client)
- Fix sensor set-version --version incorrectly required
- Fix org delete --confirm-token incorrectly required
- Remove sensor upgrade --selector that was silently ignored
- Fix ioc hosts using invalid IOC type instead of find_sensors_by_hostname
- Add defensive .get() for bare resp["key"] in org and sensor SDK
- Fix register_explain using spaces instead of dots in hive shortcuts

New features:
- Add OAuth browser-based login: limacharlie auth login --oauth
- Support Google and Microsoft providers with --provider flag
- Support headless OAuth with --no-browser flag
- MFA/2FA handled automatically via existing infrastructure

Documentation:
- Rewrite README with comprehensive CLI reference for all command groups
- Add accurate Python SDK examples matching actual v2 API patterns
- Document both API key and OAuth authentication flows
- Add SDK class reference table
- Move legacy v1 docs to bottom, v2 content first

Tests:
- Add tests for rename, subscribe/unsubscribe param splitting
- Add tests for get_all_tags defensive .get() and find_sensors_by_hostname
- Add tests for on_refresh_auth callback consistency
- Add tests for HiveRecord raw dict construction
- Add test for replay non-double-serialization

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SDK fixes discovered by running integration tests against the live API:
- organization.py: fix add_api_key param (permissions→perms, json→join)
- organization.py: add default start/end time for get_jobs()
- artifacts.py: add required start/end params to list() with defaults
- search.py: add required startTime/endTime defaults to validate()
- integrity.py: replace non-existent get_rule action with list+filter
- logging_rules.py: same get_rule fix as integrity
- exfil.py: fix all action names, split delete into delete_event/delete_watch

CLI fixes:
- commands/exfil.py: add --type option to delete for event vs watch rules
- commands/integrity.py: handle None return from get() with error message
- commands/logging_cmd.py: same not-found handling as integrity
- commands/artifact.py: pass start/end to SDK, remove redundant filtering

Tests: 65 CLI integration tests covering all command groups (63 pass,
2 skip due to missing hive permissions on test API key).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove deprecated REST endpoints (GET/POST/DELETE /rules/{oid} and
/fp/{oid}) from the SDK and rewrite CLI commands to use the Hive API.
Rename `limacharlie rule` to `limacharlie dr` with namespace-to-hive
mapping (general→dr-general, managed→dr-managed, service→dr-service).
Replace FP commands with a 3-line hive shortcut backed by the fp hive.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix Click command naming: add explicit @group.command("set") in dr.py
  and hive.py to prevent Click deriving "set-cmd"/"set-record" names
- Fix HiveRecord construction crash in extension.py config-set (use
  raw= pattern instead of invalid kwargs)
- Fix payloads SDK: implement two-step signed URL pattern for upload
  and download matching v1 behavior
- Fix OAuth token persistence: save refreshed tokens to config file
- Fix exception swallowing in dr_rules.py/fp_rules.py: only catch
  ApiError with 404 status, re-raise other errors
- Fix insight.py: decompress response when is_compressed=true is set
- Fix hive.py rename: remove double URL encoding of new_name
- Update payload CLI download command to handle raw bytes from SDK
- Add test for non-404 errors propagating in DR rules get

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New `limacharlie auth signup` command that lets users create a brand
new LimaCharlie account and organization directly from the CLI.

The flow: OAuth authentication -> user profile creation via the signUp
Cloud Function -> new organization creation -> credentials saved.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New `limacharlie download` command group with sensor, adapter, and list
subcommands. Downloads pre-built binaries from downloads.limacharlie.io
for all supported platforms (Windows, Linux, macOS, Chrome, AIX,
FreeBSD, OpenBSD, NetBSD, Solaris) with sensible default filenames,
auto-executable permissions, and stdout piping support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Modernize the CLI v2 codebase with three major improvements:

- Add `from __future__ import annotations` and full type annotations to
  all 80+ Python files (core, SDK, and command modules)
- Convert LimaCharlieContext and HiveRecord to @DataClass, add
  Credentials TypedDict to config.py
- Migrate packaging from setup.py/setup.cfg/requirements.txt to
  pyproject.toml with setuptools backend, add PEP 561 py.typed marker
- Update Dockerfile and CI (cloudbuild) for new packaging
- Add tests for dataclass conversions, type infrastructure, and packaging

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add consistent type annotations to _make_explain_callback inner
  functions across all 15 command files that were missing them
- Remove unused imports (signal, sys, os) from stream.py, artifact.py,
  output_cmd.py
- Fix spotcheck.py register_explain key from space to dot separator
- Fix test_packaging.py tomllib import for Python 3.9/3.10 compat
- Add tomli backport to pyproject.toml dev dependencies

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Current PyPI latest is 4.11.3, so 2.0.0 would be a downgrade.
Version 5.0.0 signals the major rewrite and sorts above 4.x.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix AI methods: remove spurious {oid} from paths, use "query" not "description"
- Fix org param names: "permission"->"perm", "email"->"member_email"
- Fix groups set_permissions: "permissions" dict -> "perm" list
- Rewrite investigations SDK to use Hive for CRUD operations
- Remove non-existent endpoints: artifacts.get, billing.get_sku_definitions,
  ingestion_keys.configure_usp, insight.get_object_timeline,
  insight.get_host_count_per_platform, org.find_sensors_by_ip,
  org.configure_usp_key, org.convert_extension_rules
- Remove CLI commands for removed endpoints (artifact get, billing skus,
  extension convert-rules)
- Update investigation CLI commands to use --name with Hive-based SDK
- Update tests for new investigation Hive interface

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update README.md investigation examples and all _EXPLAIN_* texts in
investigation.py to use --name instead of --id, matching the Hive-based
CLI commands.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously only ~8% of SDK methods had tests verifying actual HTTP
contracts (URL paths, methods, param names, body fields). This led to
bugs shipping undetected (wrong param names, wrong paths, wrong body
fields). Now every SDK method has a unit test verifying its full HTTP
contract.

- 16 new test files for ai, artifacts, billing, exfil, extensions,
  firehose, fp_rules, insight, integrity, investigations, jobs,
  logging_rules, payloads, replay, wrappers, yara
- Expanded organization (17→107 tests), sensor (13→27), spout, dr_rules,
  hive tests with full contract verification
- Slimmed test_sdk_misc.py to only ARL/USP/downloads (moved rest to
  dedicated files)
- Total: 814 passing tests (up from ~564)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SDK fixes:
- config.py: Clear stale uid from config when explicit api_key provided
- Manager.py: Only inherit GLOBAL_UID when api_key also from globals
- organization.py: get_all_tags handles null, unwrap api_keys/installation_keys
- exfil.py: Split string path into list for service request
- usp.py: Wrap single json_input dict in list

Integration test fixes:
- Fix exfil/hive/artifacts/outputs/search/AI/groups/replay tests
- Handle user-only auth endpoints gracefully
- Use valid FP rule data for hive CRUD test
- Fix CLI command names for search tests

Unit test updates:
- Update exfil path and USP json_input assertions to match SDK fixes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Table output now auto-truncates large dicts ({N keys}) and long lists
([N items]) to fit the terminal. --wide/-W disables truncation, and
--filter applies a JMESPath expression to transform output data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds ~40 new tests across two files:
- test_output.py: truncation, max_value_width, _table_value, wide mode,
  module-level filter expr, and detect_output_format tests
- test_cli_commands.py: CliRunner tests for global --wide/--filter flags,
  auth (whoami, test, list-envs), org (info, urls, errors), sensor (list,
  get, delete without confirm), DR (list), and --explain flag

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove the infrastructure-service code path and use_extension toggle from
Configs — all sync now goes through ext-infrastructure.  Drop the legacy
--rules / --fps CLI flags (and sync_rules / sync_fps SDK params) in favor
of hive flags (--hive-dr-general, --hive-fp, etc.) matching the old v1 CLI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…unknown api key"

When users login with OAuth but don't set a default org, org-scoped commands
would fail with the cryptic JWT endpoint error "unknown api key". Now we catch
the missing OID before hitting the network and suggest use-org, --oid flag
placement, or LC_OID env var.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two issues caused OAuth login to fail with "unknown api key":

1. All _get_org helpers double-resolved credentials — they called
   resolve_credentials() then re-passed api_key explicitly to Client(),
   which cleared OAuth creds inside Client's own resolve_credentials().
   Fix: let Client resolve credentials once from oid + environment.

2. OAuth login preserved any stale api_key already in the config file.
   Fix: clear api_key from config on successful OAuth login.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ut help text

- adapter.py now uses "external adapter" noun to distinguish from cloud adapters
- Hive shortcut factory uses proper a/an article for nouns starting with vowels
- Subcommand help text uses the noun instead of generic "records"
- Group help drops the internal hive name for cleaner output

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Mock resolve_credentials so the test doesn't pick up api_key from
the developer's ~/.limacharlie config file.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ud adapters

The bare 'adapter' name was ambiguous — cloud sensors are often called
"cloud adapters". Now 'ext-adapter' and 'cloud-sensor' are clearly distinct.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename 'ext-adapter' to 'external-adapter' for clarity, and rename
'cloud-sensor' to 'cloud-adapter' so both adapter types use consistent
naming.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reflect the cloud-sensor → cloud-adapter and adapter → external-adapter
renames in the hive shortcuts section.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Strip top-level wrapper keys (payloads, users, user_permissions, groups,
errors) so CLI output shows useful data directly instead of a single
wrapper object. Follows the existing resp.get("key", resp) pattern
already used by get_api_keys, get_installation_keys, get_outputs, etc.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement _is_list_of_dicts() to detect dict-of-dicts data (e.g.
payloads keyed by name) and flatten it into a list-of-dicts for proper
columnar table display. Adds a "name" column from the dict key when
values don't already contain one.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The seal/unseal subcommands have nothing to do with network policy;
'endpoint-policy' better describes the full scope of the command group
(network isolation + configuration sealing).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a unified --ai-help flag that generates compact markdown help at
three levels (top-level overview, command group, individual command).
Remove the per-command --explain boilerplate from all 40 command files
(-1,407 lines) while keeping the explain text registry that feeds
--ai-help context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
dzimine-lc and others added 16 commits February 22, 2026 21:52
The v2 SDK and CLI are fully independent of the legacy v1 code.
This removes all legacy modules to make a clean break for the 5.0
release.

- Delete 26 legacy PascalCase domain modules (Manager.py, Sensor.py, etc.)
- Delete 6 legacy-only utility files (utils.py, request_utils.py, etc.)
- Rewrite __init__.py to only re-export __version__ from client.py
- Delete 9 legacy unit tests and 8 legacy integration tests
- Update test_user_agent.py to use build_user_agent() directly
- Rename integration test_v2_*.py → test_*.py
- Remove legacy v1 SDK section and migration table from README
- Update CLAUDE.md to reflect single-SDK architecture

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Add `limacharlie api` raw API command

Adds a generic escape-hatch command (like `gh api`) for making
authenticated HTTP requests to any LimaCharlie API endpoint,
useful for accessing unwrapped endpoints, scripting, and debugging.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix ctx.exit() code propagation in standalone_mode=False

With standalone_mode=False, Click catches Exit exceptions internally and
returns the exit code as a return value. main() was not checking this,
so non-zero exit codes from ctx.exit() were silently lost.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix cloudbuild_pr.yaml: use `--version` flag instead of `version` subcommand

The v2 CLI uses `limacharlie --version` (a Click version flag), not
`limacharlie version` (the old v1 subcommand). The sanity check steps
were still using the old form, causing build failures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix cloudbuild_pr.yaml parallel step race on build/ directory

Test steps and wheel/sdist steps all wrote to /workspace/build/
concurrently, causing intermittent ENOENT failures during wheel
builds. Fix by having test steps copy source to container-local
/tmp/src before pip install, so each step builds in isolation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix integration tests: subscribe required services, fix sync assertion

- Add session-scoped conftest fixture that subscribes the test org to
  required replicant services (integrity, exfil, logging, yara) before
  tests run, and removes only newly-added subscriptions at teardown.
- Fix test_sync assertion: configs.push() returns a tuple, not a list.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Remove replicant-dependent integration tests

Replicant services (integrity, exfil, logging, yara, replay) have been
superseded by Extensions. Remove integration tests that depend on these
services and the conftest fixture that auto-subscribed them.

SDK classes and their unit tests are kept — unit tests mock
service_request so they don't require live replicant subscriptions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Address PR review: form-encoded default, path safety, TTY guard

Addresses all 5 inline review comments from @tomaz-lc:

1. OID path injection (line 211): URL-encode the OID value via
   urllib.parse.quote() when expanding {oid} placeholders.

2. @file path traversal (line 101): New _safe_read_file() resolves
   symlinks via os.path.realpath() and rejects non-regular files.

3. stdin TTY guard (line 100): New _read_stdin() checks sys.stdin.isatty()
   and prints guidance before blocking on terminal input.

4. Content-type override (line 234): New --content-type flag for
   explicit control over --input body content type.

5. Form-encoded body support (line 244): Fields from -f/-F now default
   to form-encoded body (matching LC API conventions). New --json flag
   sends as JSON instead, preserving typed -F coercion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add documentation for `limacharlie api` command and `Client.raw_request()`

Document the new raw API escape-hatch command in CLI docs (other-commands.md)
with full usage examples, options table, and exit codes. Add SDK docs for
Client.raw_request() method. Update index tables to include the api command.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: dzimine-lc <dmitri@limacharlie.io>
Investigations are moving to Ticketing, so the dedicated CLI command
group, SDK module, and associated tests are no longer needed.

The `investigation_id` parameter used in tasking and streaming is
unrelated and remains unchanged.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The AI SDK was sending requests with JSON body (Content-Type:
application/json) but the backend only parses form-encoded bodies
(application/x-www-form-urlencoded). This caused all ai commands
(generate-query, generate-detection, generate-response, etc.) to fail
with '{"error": "query is required"}'. Switch all AI methods from
raw_body/content_type to params for proper form encoding.

Also add "ticketing" as a target alias in the api command, mapping to
https://ticketing.limacharlie.io, and update the --ai-help text and
--target help string to document it.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Use setuptools-scm to derive package version from git tags (no hardcoded
version), and add a GHA workflow that runs unit tests, builds, and publishes
to TestPyPI using Trusted Publishers (OIDC). Will switch to production PyPI
in a follow-up PR.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Update cloudbuild-pr image

* fixing image version
* Fix incorrect location names in org create CLI help

The help text and examples listed locations (us, ca, eu, emea) that
don't exist in the API. Updated to match the actual availableLocs
in lc_api-go: usa, canada, europe, uk, india, australia, auto.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Default org create location to auto

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Expose the existing SDK `InstallationKeys.get(iid)` method as a new
`limacharlie installation-key get --iid <IID>` CLI subcommand so users
can retrieve a single key's data without listing all keys.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
After successfully creating an organization, print the web app URL
(https://app.limacharlie.io/orgs/<oid>) so users can quickly navigate
to manage the new org. Suppressed in --quiet mode.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Switch from TestPyPi to PyPi

* removing old deployment
* Add `dr convert-rules` CLI command for mass rule conversion

Adds a new CLI command that mass-converts external detection rules
(Sigma, Splunk SPL, Elastic KQL, etc.) to LimaCharlie D&R format
using the AI API and creates them in the dr-general hive. Supports
crawling GitHub repositories or local directories for rule files.

Key features:
- GitHub crawler with intelligent file filtering (Trees API + fallback)
- Local directory crawler with same filtering heuristics
- Parallel AI conversion with configurable worker count
- Live progress display with ETA
- Dry-run mode and YAML file output
- Prefix, tagging, and enabled/disabled control for created rules

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Improve dr convert-rules reliability and UX

- Parse ref and path from full GitHub URLs (e.g.
  --github https://github.com/SigmaHQ/sigma/tree/master/rules/network/cisco
  now auto-extracts ref=master, path=rules/network/cisco)
- Prime JWT before spawning parallel workers to avoid redundant OAuth refreshes
- Add pipeline-level retries with jitter for transient errors (429 rate limits,
  500/502/503 server errors, MCP timeouts) so the thundering-herd effect from
  parallel workers doesn't cause permanent failures
- Bump default parallelism from 5 to 10

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Add --use flag to org create to set new org as default

When passed, the --use flag automatically sets the newly created
organization as the default OID for subsequent commands, saving
an extra `limacharlie auth use-org` call.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add --get flag to installation-key create for full key details

When passed, the --get flag automatically fetches the full
installation key details after creation, equivalent to a
follow-up `limacharlie installation-key get --iid <IID>`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Use OID-less client for org create to avoid stale OID errors

The org create endpoint (orgs/new) doesn't require an org-scoped
JWT. Using oid="-" gets a user-only JWT, preventing auth failures
when the previously saved default org has been deleted.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
dzimine-lc and others added 13 commits February 27, 2026 13:50
CLI commands/ (40 files):
- Remove redundant docstrings from ~197 @group.command() functions that
  duplicated _EXPLAIN_* text. --help now shows clean usage + options;
  --ai-help is unchanged.
- Colocate each _EXPLAIN_* string and register_explain() call with its
  command's # --- section instead of bunching them at the top of the file.

SDK sdk/ (20 files):
- Add missing Returns/Yields sections to all public methods that have
  an Args section (~55 additions).
- Add docstrings to 10 bare methods in organization.py (group and
  extension management).
- Fix HiveRecord.from_raw Returns type annotation.
- Add .claude/docs/sdk-docstring-conventions.md for code review reference.
The search tool help text, cheatsheets, and integration tests used
invented LCQL syntax (event_type(), event_type ==, <=, >=) that
doesn't exist in the actual LCQL grammar. Replaced with correct
pipe-separated format and valid operators per the PEG grammar.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: Add AI session creation via ai_agent Hive definitions

Enable starting AI sessions from the CLI using ai_agent Hive definitions,
matching the capability already available in D&R rules. Resolves all
hive://secret/ references in credentials, environment, and MCP server configs.

Usage: limacharlie ai start-session --definition <ai_agent_name>

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Support both API key and OAuth JWT auth for ai-sessions

Use raw API key when available (backward compatible), fall back to JWT
auth for OAuth-authenticated users.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Add ticket CLI commands for SOC ticket management

Integrates the ext-ticketing system with the LimaCharlie CLI,
providing full ticket lifecycle management, investigation tracking,
and reporting capabilities.

New commands:
- ticket list/get/update/add-note/bulk-update/merge
- ticket entity add/list/update/remove/search (IOC management)
- ticket telemetry add/list/update/remove (event linking)
- ticket artifact add/list/remove (forensic references)
- ticket detection add/list/remove (detection linking)
- ticket report/dashboard (SOC metrics)
- ticket config-get/config-set (org configuration)
- ticket assignees

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add tests for ticket SDK and CLI commands

106 tests covering all ticketing SDK methods (41) and CLI commands (65),
including argument validation, stdin/file input, quiet mode, and error cases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add ticket create command to CLI and SDK

The ticket CLI was missing a 'create' command, causing AI agents (Claude
Code) to get stuck in loops trying to find how to create tickets.  Ticket
creation goes through the ext-ticketing extension callback, not the REST
API, so the new SDK method routes through Extensions.request() with the
create_ticket action.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add AI generation cross-references to CLI explain texts

Direct AI agents to use 'ai generate-*' commands instead of writing
LCQL queries, D&R rules, and sensor selectors from scratch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add ticket export command with --with-data option

Add 'limacharlie ticket export --id <ID>' to aggregate all ticket
components (ticket record, timeline, detections, entities, telemetry,
artifacts) into a single JSON output.

When --with-data <DIR> is specified, the command creates a directory
containing the ticket metadata plus actual detection records, telemetry
events, and artifact binaries fetched from the LimaCharlie APIs.
Fetch failures are warned and skipped.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Harden ticket export: sanitize filenames and add error-path tests

Defense-in-depth: use _safe_filename() to strip path separators from
IDs used as filenames in --with-data export. Add tests for telemetry
fetch errors, artifact fetch errors, and entries with missing IDs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Switch ticket API from UUID ticket_id to integer ticket_number

The ext-ticketing API now uses OID + ticket_number (integer) instead of
UUID ticket_id for all endpoints. Update SDK, CLI, and tests accordingly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add ticket tags support to SDK and CLI

Add tag filtering to list_tickets (--tag with AND logic), tag option to
update command, and ticket tag set/add/remove subcommand group for
convenient tag management. Includes full test coverage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix case-insensitive tag matching in tag_add and tag_remove CLI commands

The backend uses case-insensitive dedup (case-preserved) for tags, but
the CLI was using case-sensitive Python operations. This caused tag_add
to create duplicates like "phishing" and "PHISHING", and tag_remove to
miss tags with different casing. Also adds --tag to _EXPLAIN_UPDATE text
and fixes a variable shadowing bug in tag_add (loop var `t` shadowed
the Ticketing client `t`).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add closed beta note to ticket CLI help text

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Adds sensor_id filtering to the ticket list command and SDK, enabling
efficient lookup of tickets where ANY detection came from a given
sensor. Backed by a new Spanner index in ext-ticketing.

- SDK: sensor_id param on Ticketing.list_tickets()
- CLI: --sid option on `ticket list`
- Tests for both SDK and CLI layers

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Adds `hive enable` and `hive disable` subcommands that toggle the
usr_mtd.enabled flag without touching record data. Also adds the
same commands to all hive shortcut groups (secret, lookup, dr, etc.).

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The hive API replaces usr_mtd wholesale, so sending only
{enabled: false} would wipe tags, expiry, and comment.
Now reads metadata first via get_metadata(), modifies only
the enabled flag, and writes back the full usr_mtd.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- `limacharlie help auth` now works as a shortcut for `limacharlie help topic auth`.
  The help group falls through to topic lookup for unknown subcommand names.
- `auth whoami` now shows stored credential info (OID, UID) like the old CLI,
  and expands the perms list in full instead of truncating to "[N items]".
- `sync pull/push` now passes --debug through to the Client for request tracing.
- `client.refresh_jwt` logs credential summary (oid, uid presence, key prefix)
  when debug is enabled, to help diagnose cross-org UNAUTHORIZED issues.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The user/orgs API defaults to limit=10, but list_accessible_orgs
made a single call without paginating, so only the first page was
returned. Now when neither offset nor limit is explicitly provided
the SDK auto-paginates through all pages. Explicit offset/limit
still does a single-page fetch for manual pagination.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Support full detection object in ticket create and detection add

Callers can now pass --detection with a full LC detection JSON object
instead of individual --detection-id/--detection-cat/etc flags. The
backend extracts fields automatically. Individual flags still work
and take precedence.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix test assertions to include new detection parameter

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Support full event object in telemetry add

Callers can now pass --event with a full LC event JSON object when
adding telemetry to a ticket. The backend extracts routing.this → atom,
routing.sid → sid, routing.event_type → event_type automatically.
Individual flags still work and take precedence.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Simplify ticket API to require full detection/event objects only

Remove support for bare detection IDs and individual field parameters.
All detection and telemetry operations now require the full JSON object,
which the backend extracts fields from automatically.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Allow creating tickets without a detection

Make --detection optional in create command and SDK. Analysts can now
create empty investigation container tickets without an initial
detection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix SDK ticketing tests and JSON-encode detection for extension schema

- JSON-encode detection dict in create_ticket() to satisfy the
  extension schema "json" type validation.
- Update test_sdk_ticketing.py to match the full-object-only API:
  create_ticket takes optional detection dict, add_detection takes
  full detection dict, add_telemetry takes full event dict.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Hive.set() always included `data: json.dumps(record.data)` in the
request params, even when data was None (metadata-only update via
enable/disable). This sent `data: "null"` to the server, which caused
the secret hive validator to reject the request with "secret value
empty".

Only include the data field when record.data is not None.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Add --data option to ai start-session CLI command

When starting an AI session from the CLI there is no event to apply
the definition's data transform to. The new --data flag accepts a JSON
dictionary that is appended to the prompt as event data, matching the
format used by the D&R engine.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Use YAML instead of JSON for data appended to prompt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Permissions are now omitted from whoami output by default to keep
responses compact (saves hundreds of tokens for LLM-driven workflows).

--show-perms opts in to the full permission listing.
--check-perm NAME returns a simple {"perm": "...", "has_perm": bool}
result, useful for quick authorization checks without parsing the
full permission set.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants