Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions .agents/skills/ux-walkthrough/catalog.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Legend: **Non-AI** = works without `ansibleEnvironments.enableAiFeatures`.
| Feature | Trigger | Description | Non-AI | Implementation |
| ---------------------- | ---------------- | -------------------------------------------------------------------------------------------------- | ------ | -------------------------------- |
| Activity bar container | Ansible icon | Opens Ansible sidebar | yes | `package.json` `viewsContainers` |
| Tree views (10) | Sidebar scroll | Env, Dev Tools, Collections, Sources, EEs, Creator, Playbooks, AI Tools*, AI Skills*, Lightspeed\* | yes\* | `src/views/*Provider.ts` |
| Ansible | Activity Bar | Accordion sections (Env, Dev Tools, Collections, Sources, EEs, Creator, Playbooks, AI Tools*, AI Skills*, Lightspeed\*) | yes\* | `src/sidebar/AnsibleNavTreeProvider.ts` + `src/views/*Controller.ts` |
| Output channel | Output → Ansible | Extension logs | yes | `src/extension.ts` |

\*AI views require `enableAiFeatures`; Lightspeed view requires `ansible.lightspeed.enabled`.
Expand All @@ -25,11 +25,11 @@ Legend: **Non-AI** = works without `ansibleEnvironments.enableAiFeatures`.

| Feature | Command / trigger | Description | Non-AI | Implementation |
| -------------------- | ------------------------------------ | ------------------------------ | ------ | -------------------------------- |
| Environment Managers | View: `ansibleDevToolsEnvManagers` | Python managers → environments | yes | `EnvironmentManagersProvider.ts` |
| Environment Managers | Sidebar section: `envManagers` | Python managers → environments | yes | `EnvironmentManagersController.ts` |
| Refresh environments | `ansibleDevToolsEnvManagers.refresh` | Reload env tree | yes | |
| Create environment | `ansibleDevToolsEnvManagers.create` | One-click venv creation | yes | |
| Select environment | `ansibleDevTools.selectEnvironment` | Active env for Ansible tools | yes | |
| Ansible Dev Tools | View: `ansibleDevToolsPackages` | ADT package versions | yes | `AnsibleDevToolsProvider.ts` |
| Ansible Dev Tools | Sidebar section: `devTools` | ADT package versions | yes | `AnsibleDevToolsController.ts` |
| Install ADT | `ansibleDevToolsPackages.install` | Install meta-package | yes | |
| Upgrade ADT | `ansibleDevToolsPackages.upgrade` | Upgrade packages | yes | |
| Python status bar | `ansible.statusBar.pythonClick` | Env details QuickPick | yes | `statusBar/pythonStatusBar.ts` |
Expand Down Expand Up @@ -63,7 +63,7 @@ Legend: **Non-AI** = works without `ansibleEnvironments.enableAiFeatures`.

| Feature | Command / trigger | Description | Non-AI | Implementation |
| --------------------- | ------------------------------------------ | --------------------------- | ------ | --------------------------- |
| Collections tree | View: `ansibleDevToolsCollections` | Collection → type → plugin | yes | `CollectionsProvider.ts` |
| Collections tree | View: `ansibleDevToolsCollections` | Collection → type → plugin | yes | `CollectionsController.ts` |
| Refresh | `ansibleDevToolsCollections.refresh` | Reload index | yes | |
| Search plugins | `ansibleDevToolsCollections.search` | Keyword search | yes | |
| Plugin documentation | `ansibleDevToolsCollections.showPluginDoc` | Rich doc webview | yes | `PluginDocPanel.ts` |
Expand All @@ -78,7 +78,7 @@ Legend: **Non-AI** = works without `ansibleEnvironments.enableAiFeatures`.

| Feature | Command / trigger | Description | Non-AI | Implementation |
| ------------------- | -------------------------------------------------- | -------------------- | ------ | ------------------------------ |
| Sources tree | View: `ansibleCollectionSources` | Galaxy + GitHub orgs | yes | `CollectionSourcesProvider.ts` |
| Sources tree | View: `ansibleCollectionSources` | Galaxy + GitHub orgs | yes | `CollectionSourcesController.ts` |
| Search collections | `ansibleCollectionSources.search` | Unified search | yes | |
| Filter Galaxy | `ansibleCollectionSources.filterGalaxyCollections` | Filter list | yes | |
| Galaxy plugin docs | `ansibleCollectionSources.showGalaxyPluginDoc` | Uninstalled docs | yes | `GalaxyDocsCache` |
Expand All @@ -95,7 +95,7 @@ Legend: **Non-AI** = works without `ansibleEnvironments.enableAiFeatures`.

| Feature | Command / trigger | Description | Non-AI | Implementation |
| ---------------- | -------------------------- | ------------------------ | ------ | ------------------------ |
| Creator tree | View: `ansibleCreator` | ansible-creator commands | yes | `CreatorProvider.ts` |
| Creator tree | View: `ansibleCreator` | ansible-creator commands | yes | `CreatorController.ts` |
| Open form | `ansibleCreator.openForm` | Schema-driven webview | yes | `CreatorFormPanel.ts` |
| Live CLI preview | Creator form | Exact command preview | yes | `@ansible/ui` SchemaForm |
| AI overview\* | `ansibleCreator.aiSummary` | Chat prompt | no | |
Expand All @@ -108,7 +108,7 @@ Legend: **Non-AI** = works without `ansibleEnvironments.enableAiFeatures`.

| Feature | Command / trigger | Description | Non-AI | Implementation |
| --------------------- | ---------------------------------- | ---------------------------- | ------ | -------------------------- |
| Playbooks tree | View: `ansiblePlaybooks` | Workspace playbook discovery | yes | `PlaybooksProvider.ts` |
| Playbooks tree | View: `ansiblePlaybooks` | Workspace playbook discovery | yes | `PlaybooksController.ts` |
| Go to play | `ansiblePlaybooks.goToPlay` | Editor navigation | yes | |
| Edit config | `ansiblePlaybooks.editConfig` | Per-playbook run form | yes | `PlaybookConfigPanel.ts` |
| Edit defaults | `ansiblePlaybooks.editDefaults` | Global defaults form | yes | |
Expand All @@ -125,7 +125,7 @@ Legend: **Non-AI** = works without `ansibleEnvironments.enableAiFeatures`.

| Feature | Command / trigger | Description | Non-AI | Implementation |
| -------------- | ------------------------------------------------ | ---------------------------- | ------ | ---------------------------------- |
| EE tree | View: `ansibleExecutionEnvironments` | Images via ansible-navigator | yes | `ExecutionEnvironmentsProvider.ts` |
| EE tree | View: `ansibleExecutionEnvironments` | Images via ansible-navigator | yes | `ExecutionEnvironmentsController.ts` |
| EE detail | `ansibleExecutionEnvironments.showDetail` | Metadata webview | yes | `EEDetailPanel.ts` |
| Package detail | `ansibleExecutionEnvironments.showPackageDetail` | Python/system packages | yes | `PackageDetailPanel.ts` |
| AI summary\* | `ansibleExecutionEnvironments.aiSummary` | Chat prompt | no | |
Expand Down Expand Up @@ -155,10 +155,10 @@ See `packages/common/src/skills/`.

| Feature | Command / trigger | Description | Implementation |
| ---------------------- | ----------------------------------------- | ------------------------------------------------------ | ---------------------- |
| AI Tools tree | View: `ansibleMcpTools` | MCP tools by category | `McpToolsProvider.ts` |
| AI Tools tree | View: `ansibleMcpTools` | MCP tools by category | `McpToolsController.ts` |
| Use in Chat | `ansibleMcpTools.useInChat` | Inject tool prompt | |
| Copy prompt | `ansibleMcpTools.copyPrompt` | Opens chat with pre-filled prompt (clipboard fallback) | |
| AI Skills tree | View: `ansibleSkills` | External + builtin skills | `SkillsProvider.ts` |
| AI Skills tree | View: `ansibleSkills` | External + builtin skills | `SkillsController.ts` |
| MCP status | `ansible-environments.showMcpStatus` | Connection webview | `mcp/cursorConfig.ts` |
| Configure Cursor MCP | `ansible-environments.configureCursorMcp` | Guided setup | |
| MCP server (23+ tools) | External agent | Discovery, generation, EE, creator | `packages/mcp-server/` |
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ lib/
.ansible/
docs/.astro/
docs/dist/
packages/ui/dist-playground/
packages/common/src/skills/*.content.ts
**/*.tgz
.cursor/hooks/state/continual-learning.json
2 changes: 1 addition & 1 deletion .sdlc/adrs/ADR-006-esbuild-bundler.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ aliases during type checking but does **not** rewrite import paths in the
emitted JavaScript. As a result:

```typescript
// src/views/CollectionsProvider.ts
// src/views/CollectionsController.ts
import { CollectionsService } from '@src/services/CollectionsService';
// Compiles to:
const { CollectionsService } = require('@src/services/CollectionsService');
Expand Down
2 changes: 1 addition & 1 deletion .sdlc/adrs/ADR-013-scm-plugin-docs-via-shallow-clone.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ authoritative tool.
- `packages/services/src/SCMDocsCache.ts` — new service
- `packages/common/src/parsers/metadataDumpParser.ts` — shared
browser-safe parsing utility extracted from `CollectionsService`
- `src/views/CollectionSourcesProvider.ts` — tree expansion for
- `src/views/CollectionSourcesController.ts` — tree expansion for
GitHub collection nodes
- `packages/mcp-server/src/tools.ts` + `handlers.ts` — MCP tool

Expand Down
141 changes: 141 additions & 0 deletions .sdlc/adrs/ADR-025-host-agnostic-sidebar-navtree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# ADR-025: Sidebar NavTree

## Status

Implemented

## Date

2026-07-17

## Context

The Ansible Activity Bar previously stacked many native `TreeView` sections. Users want a
quieter drawer: accordion (one open at a time), collapsed by default, and auto-open when
Environment Managers or Ansible Dev Tools have a known issue. VS Code cannot collapse
sibling view headers from an extension
([vscode#88219](https://github.com/microsoft/vscode/issues/88219)).

We also intend a future standalone Electron app that reuses the same IA without
`vscode.TreeDataProvider`. Hydration and action dispatch must not live only inside
extension TreeProviders or React components.

## Decision

**We ship a host-agnostic sidebar NavTree: a serializable `SidebarSnapshot` model hydrated
in `@ansible/developer-services`, rendered by React in `@ansible/ui`, with VS Code as
the first host adapter (`WebviewView`). Native Activity Bar TreeViews are removed; the
hub is the sole Ansible sidebar UI.**

Concrete rules:

1. **DTOs** (`SidebarSnapshot`, nodes, welcome/inline actions) live in `@ansible/common`
(browser-safe).
2. **`SidebarModel`** in `@ansible/developer-services` builds snapshots from services /
plain inputs and computes `suggestedOpenSectionId`. Lazy-expand node builders
(`buildEeDetailNodes`, `buildPluginTypeNodes`, `patchNodeChildren`) live here too.
No `vscode` types in the DTO.
3. **React** (`SidebarShell` / `SidebarTree`) owns only presentation and ephemeral UI
state (accordion, row expand). Actions are command-id strings dispatched to the host.
4. **VS Code host** registers `ansibleNavTree` (`type: webview`) as the only view
under `views.ansible-environments`. Controllers in `src/views/` remain for command
handlers and change events; they are not `TreeDataProvider`s.
5. **AI Tools / AI Skills / Lightspeed** appear as hub sections when the corresponding
feature flags are on (not separate Activity Bar trees).
6. **Electron** later swaps only the transport (IPC vs postMessage) and command
registry; it reuses model + React.
7. **Section ids** (`SidebarSectionId`, `SidebarNodeExpand`) stay Ansible-specific for
this product IA. Generalizing the hub into a free-form accordion toolkit is out of
scope.

## Alternatives Considered

### Alternative 1: Accordion via native TreeView APIs only

**Description**: Keep seven trees; try to collapse siblings with undocumented or
future APIs.

**Pros**: Native look; no webview cost.

**Cons**: Not supported today (#88219); blocks Electron reuse.

**Why not chosen**: Cannot meet accordion / quiet-by-default requirements.

### Alternative 2: Put hydration in React / webview

**Description**: Webview calls services over a fat RPC and owns population logic.

**Pros**: Less extension code.

**Cons**: Duplicates provider logic; harder to share with MCP/Electron; browser cannot
run Node services.

**Why not chosen**: Violates ADR-011 and Electron readiness.

### Alternative 3: Keep TreeViews behind a setting (dogfood toggle)

**Description**: Hide native trees when `ansible.sidebar.navTree.enabled` is true; keep
`createTreeView` as a fallback.

**Pros**: Escape hatch during migration.

**Cons**: Dual UI maintenance; menus/`viewsWelcome` drift from hub snapshot actions.

**Why not chosen**: After side-by-side dogfood, trees were hard-removed. Commands remain;
only TreeView chrome was deleted.

## Consequences

### Positive

- Accordion and issue-driven open are owned by us.
- Same model + UI path for VS Code webview and future Electron.
- Single Activity Bar surface — no dual tree/hub chrome.

### Negative

- Webview fidelity will never be pixel-identical to native TreeView chrome.
- Controllers expose `onDidChange` for hub refresh (not TreeViews).

### Neutral

- Inline actions and welcome buttons are data on the snapshot, replacing former
`viewsWelcome` and `view/item/context` inline menus for the sidebar.

## Implementation Notes

- No `ansible.sidebar.navTree.enabled` setting — hub is always on.
- View id: `ansibleNavTree` (only entry in `views.ansible-environments`).
- Host: `src/sidebar/AnsibleNavTreeProvider.ts` (`WebviewViewProvider`).
- Controllers (hydrate + commands, no TreeViews): `src/views/*Controller.ts`.
- Bridge messages: `sidebar/setState`, `sidebar/action`, `sidebar/expandNode`,
`sidebar/ready`.
- Progressive hydrate: host posts a skeleton `SidebarSnapshot` (section headers +
loading rows) synchronously, then a full snapshot after async service work.
Incremental refreshes skip the skeleton. UI is custom `SidebarShell` /
`SidebarTree` (not react-arborist).
- Open section uses flex fill so trailing headers stay pinned to the bottom of the hub.
- Section builders live under `packages/services/src/sidebar/` with an ordered
`SECTION_REGISTRY` (see [.sdlc/docs/add-navtree-section.md](../docs/add-navtree-section.md)).

## Related Decisions

- ADR-005: Architectural invariants
- ADR-010: Shared UI package (historical; UI in `@ansible/ui`)
- ADR-011: Package architecture (`@ansible/common` / `@ansible/developer-services`)
- ADR-012: MCP tool parity (hub actions must map to existing commands/tools)

## References

- [vscode#88219](https://github.com/microsoft/vscode/issues/88219) — cannot collapse other views
- Phase 0 playground: `packages/ui/playground/sidebar-navtree.html`

---

## Revision History

| Date | Author | Change |
| ---------- | ---------- | ---------------------------------------------- |
| 2026-07-17 | bthornto | Initial decision |
| 2026-07-18 | bthornto | Progressive skeleton hydrate; custom tree UI |
| 2026-07-19 | bthornto | Hard-remove native trees; NavTree-only UI |
3 changes: 2 additions & 1 deletion .sdlc/adrs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Decisions that are fully reflected in the codebase.
| [ADR-020](ADR-020-single-repo-multi-distribution.md) | Single Repository, Multiple Distribution Formats | 2026-06-26 |
| [ADR-022](ADR-022-pnpm-package-manager.md) | Migrate to pnpm for Supply-Chain Security | 2026-06-30 |
| [ADR-024](ADR-024-host-agnostic-getting-started.md) | Host-Agnostic Getting Started (Shared Walkthrough Content) | 2026-07-17 |
| [ADR-025](ADR-025-host-agnostic-sidebar-navtree.md) | Sidebar NavTree | 2026-07-17 |

## Accepted

Expand Down Expand Up @@ -48,7 +49,7 @@ Decisions under consideration — not yet accepted or implemented.
## Creating New ADRs

1. Copy the template from `../templates/adr.md`
2. Use the next available number (currently ADR-025)
2. Use the next available number (currently ADR-026)
3. Include:
- Status (Proposed → Accepted → Implemented)
- Date
Expand Down
42 changes: 42 additions & 0 deletions .sdlc/docs/add-navtree-section.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Add an Ansible NavTree section

Use this checklist when adding a new accordion section (e.g. Molecule scenarios).
Do **not** change `SidebarShell` / `SidebarTree` for ordinary sections.

## Steps

1. **Id** — Add the section id to `SidebarSectionId` in
[`packages/common/src/sidebar.ts`](../../packages/common/src/sidebar.ts).
If the section needs lazy expand, add a `SidebarNodeExpand` variant there too.

2. **Builder** — Create `packages/services/src/sidebar/sections/<id>.ts` exporting
`build<Name>(input): SidebarSection` and, if needed,
`shouldSuggest<Name>(input): boolean`.

3. **Registry** — Register the section in
[`packages/services/src/sidebar/registry.ts`](../../packages/services/src/sidebar/registry.ts)
(`SECTION_REGISTRY` order = accordion order). Skeleton and full snapshots both
use this list.

4. **Input** — Extend `SidebarModelInput` in
[`packages/services/src/sidebar/types.ts`](../../packages/services/src/sidebar/types.ts).
Gather data in the VS Code host
([`AnsibleNavTreeProvider`](../../src/sidebar/AnsibleNavTreeProvider.ts));
shape plain DTOs in
[`assembleSidebarInput.ts`](../../packages/services/src/sidebar/assembleSidebarInput.ts)
when the mapping is non-trivial.

5. **Commands + MCP** — Wire row/header actions to existing or new commands.
Per ADR-012, user-facing capabilities need an MCP tool equivalent.

6. **Tests** — Unit-test the section builder (and suggest, if any). Keep
`SidebarModel` orchestrator tests green. Optional: add a playground mock row.

7. **Story** — If the section is user-facing, add/update a user story in
`.sdlc/user-stories.yaml` and tag WDIO coverage when applicable.

## Guarantees

- UI only renders `SidebarSnapshot` — no Ansible discovery in the webview.
- Host owns vscode config/auth/workspace; services own pure snapshot building.
- Controllers under `src/views/` expose `onDidChange` for NavTree refresh (not TreeViews).
Loading