Skip to content

feat: Unattended install#216

Merged
Pringled merged 10 commits into
mainfrom
unattended-install
Jul 3, 2026
Merged

feat: Unattended install#216
Pringled merged 10 commits into
mainfrom
unattended-install

Conversation

@Pringled

@Pringled Pringled commented Jul 3, 2026

Copy link
Copy Markdown
Member

This PR adds non-interactive flags to semble install/uninstall for sandboxed/scripted environments, e.g. semble install --agent claude --type subagent --yes. Closes #212.

Pringled added 10 commits July 2, 2026 09:37
Adds non-interactive install flags for sandboxed/scripted environments
(closes #212), e.g. `semble install --agent pi --type subagent --yes`.
…lection

run() previously took the unattended branch whenever agent_ids was not
None, including an empty list, printing a blank plan and returning
success. Mirror the interactive path's guard.
Matches the existing ContentType(str, Enum) pattern in types.py, giving
mypy real coverage on run()'s integration_ids for direct API callers
instead of an unconstrained list[str].
An explicit empty list previously fell through to "all integrations"
because of a falsy check; it should exit like agent_ids=[] does. The
CLI can't trigger this (argparse requires nargs="+"), but direct API
callers could.
… IntegrationType conversion

--yes alone still hits the interactive checkbox path (hangs in non-TTY),
so document that. Also strengthen the unattended-flags test to check
the CLI actually converts --type strings to IntegrationType, not just
equal-by-value strings.
…ions

_exit() previously always used code 0, so scripts checking $? after an
unattended run() call would see success even when nothing matched
--agent/--type. The interactive cancel/nothing-selected paths keep
exiting 0 since those reflect a deliberate user choice, not an error.
- test_run_unattended_skips_prompts now also asserts _checkbox is never
  called, not just questionary.confirm, so a branching regression that
  fell through to the interactive path would be caught.
- Drop the integration_ids=["nonexistent"] case: it's unreachable via
  the typed API (IntegrationType is a closed enum; only an empty list
  can yield zero matches), so it tested runtime string handling rather
  than the real contract.
@codecov

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
src/semble/cli.py 100.00% <100.00%> (ø)
src/semble/installer/agents.py 100.00% <100.00%> (ø)
src/semble/installer/installer.py 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@greptile-apps

greptile-apps Bot commented Jul 3, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

This looks safe to merge after tightening one CLI edge case.

  • The main unattended install and uninstall paths are covered.
  • The changed parser can silently widen the selected integration set when all is combined with specific --type values.
  • No security issues were found in the changed code.

src/semble/cli.py

Reviews (1): Last reviewed commit: "test: tighten unattended-path tests" | Re-trigger Greptile

Comment thread src/semble/cli.py
Comment on lines +232 to +237
if args.type and not args.agent:
parser.error("--type requires --agent")

from semble.installer import run

run(args.command)
integration_ids = None if not args.type or "all" in args.type else [IntegrationType(t) for t in args.type]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Mixed All Ignores Specific Types

When --type all subagent or another mixed all invocation is passed, this branch turns the selection into None, so the installer applies every integration and silently ignores the extra specific values. Since --type accepts multiple values, this can install or remove a wider scope than the caller requested.

Suggested change
if args.type and not args.agent:
parser.error("--type requires --agent")
from semble.installer import run
run(args.command)
integration_ids = None if not args.type or "all" in args.type else [IntegrationType(t) for t in args.type]
if args.type and not args.agent:
parser.error("--type requires --agent")
if args.type and "all" in args.type and len(args.type) > 1:
parser.error("--type all cannot be combined with other types")
from semble.installer import run
integration_ids = None if not args.type or "all" in args.type else [IntegrationType(t) for t in args.type]

@Pringled Pringled requested a review from stephantul July 3, 2026 07:19
@Pringled Pringled merged commit dceb360 into main Jul 3, 2026
17 checks passed
@Pringled Pringled deleted the unattended-install branch July 3, 2026 13:32
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.

Unattended semble install

1 participant