Skip to content
Merged
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
14 changes: 14 additions & 0 deletions .beads/issues.jsonl

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .beads/last-touched
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ade-5.3
ade-6.4.1
29 changes: 29 additions & 0 deletions .vibe/beads-state-ade-main-iazal7.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"conversationId": "ade-main-iazal7",
"projectPath": "/Users/oliverjaegle/projects/privat/codemcp/ade",
"epicId": "ade-6",
"phaseTasks": [
{
"phaseId": "explore",
"phaseName": "Explore",
"taskId": "ade-6.1"
},
{
"phaseId": "plan",
"phaseName": "Plan",
"taskId": "ade-6.2"
},
{
"phaseId": "code",
"phaseName": "Code",
"taskId": "ade-6.3"
},
{
"phaseId": "commit",
"phaseName": "Commit",
"taskId": "ade-6.4"
}
],
"createdAt": "2026-03-19T15:48:55.317Z",
"updatedAt": "2026-03-19T15:48:55.317Z"
}
169 changes: 169 additions & 0 deletions .vibe/development-plan-extensibility.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# Development Plan: ade (main branch)

*Generated on 2026-03-19 by Vibe Feature MCP*
*Workflow: [epcc](https://mrsimpson.github.io/responsible-vibe-mcp/workflows/epcc)*

## Goal

Make the ADE project extensible so that forks/downstream consumers can add new facets, options, and harness writers without modifying the upstream source files. The goal is upstream-compatibility — consumers should be able to pull upstream changes without merge conflicts caused by modifications to core catalog or registry files.

## Explore
<!-- beads-phase-id: ade-6.1 -->
### Tasks
- [x] Explore codebase structure: packages/core, packages/harnesses, packages/cli
- [x] Understand how catalog, registry, and writers are wired together
- [x] Identify all the places consumers currently need to modify to extend

### Findings

**Current extension points require modifying upstream files:**
1. **Adding a new facet/option** → must edit `packages/core/src/catalog/index.ts` (`getDefaultCatalog()`) and add a new file in `catalog/facets/`
2. **Adding a new provision writer** → must edit `packages/core/src/registry.ts` (`createDefaultRegistry()`)
3. **Adding a new harness writer** → must edit `packages/harnesses/src/index.ts` (`allHarnessWriters` array + `getHarnessWriter` + `getHarnessIds`)
4. **Wiring it all into the CLI** → `packages/cli/src/index.ts` calls `getDefaultCatalog()` and relies on `allHarnessWriters` from harnesses

**Existing infrastructure that's already extensible:**
- `WriterRegistry` + `registerProvisionWriter/registerAgentWriter` already exist as a general-purpose registry
- `Catalog` and `Facet` types are public and composable
- `createRegistry()` + `createDefaultRegistry()` already allow building custom registries
- `resolve()` accepts a `Catalog` and `WriterRegistry` — so it's already injection-ready
- `runSetup(projectRoot, catalog)` already takes a `catalog` parameter

**Key insight:** The CLI's `index.ts` is the main wiring point. It hardcodes `getDefaultCatalog()` and `allHarnessWriters`. The CLI needs a plugin/extension loading mechanism so forks can inject their extensions without modifying the upstream entry point.

## Plan
<!-- beads-phase-id: ade-6.2 -->

### Phase Entrance Criteria:
- [x] The codebase has been thoroughly explored
- [x] Current extension points and blockers are identified
- [x] It's clear what's in scope and what's out of scope

### Tasks

*Tasks managed via `bd` CLI*

## Code
<!-- beads-phase-id: ade-6.3 -->

### Phase Entrance Criteria:
- [ ] Design is documented and reviewed
- [ ] Scope is agreed upon: what changes are needed and where
- [ ] No open design questions remain

### Tasks

*Tasks managed via `bd` CLI*

## Commit
<!-- beads-phase-id: ade-6.4 -->

### Phase Entrance Criteria:
- [ ] All code changes are implemented and tested
- [ ] Existing tests pass
- [ ] The extension mechanism works end-to-end (catalog + writers + harnesses injectable)

### Tasks
- [ ] Squash WIP commits: `git reset --soft <first commit of this branch>`. Then, create a conventional commit. In the message, first summarize the intentions and key decisions from the development plan. Then, add a brief summary of the key changes and their side effects and dependencies

*Tasks managed via `bd` CLI*

## Key Decisions

### KD-1: Extension model — `ade.extensions.mjs` with declarative contributions

A consumer creates `ade.extensions.mjs` (or `.js`) in their project (or in a forked CLI's src dir).
The CLI loads it via dynamic `import()` at startup and merges contributions before resolving.

The extension file exports a default object conforming to `AdeExtensions`:

```ts
interface AdeExtensions {
// Contribute new options into existing facets (e.g. add "SAP" to "architecture")
facetContributions?: Record<string, Option[]>
// Register entirely new facets
facets?: Facet[]
// Register new provision writers (e.g. a "sap-config" writer)
provisionWriters?: ProvisionWriterDef[]
// Register new harness writers (e.g. a custom IDE)
harnessWriters?: HarnessWriter[]
}
```

**Why this model:**
- `facetContributions` (keyed by facet id) is the right primitive for the SAP use case:
SAP is a new *option* inside the existing `architecture` facet, not a new facet.
- `facets` covers the case where a consumer wants to add a completely new facet (e.g. "cloud-provider").
- `provisionWriters` and `harnessWriters` cover the writer extension cases.
- The consumer never needs to modify upstream files.
- Dynamic `import()` means no build step required for `.mjs` extensions.

### KD-2: Loading location

The extension file is resolved relative to the **project root** passed to the CLI.
Search order: `ade.extensions.mjs` → `ade.extensions.js` (first match wins).
This works for both the "consumer uses npx @codemcp/ade setup" case and the "fork scenario".

### KD-3: Type exports

`AdeExtensions`, `HarnessWriter` (re-exported from harnesses), and all catalog/writer types
are already exported from `@codemcp/ade-core`. We need to add `AdeExtensions` to the exports.
No breaking changes to existing APIs.

### KD-4: SAP example placement

The SAP example (`sap` option on the `architecture` facet) will be implemented as a concrete
`ade.extensions.mjs` example file **in the repo root** (not bundled into the catalog).
This doubles as the integration test and documentation for the extension mechanism.

### KD-5: Type safety for extension loading → documented in `docs/adrs/0002-extension-file-type-safety.md`

Runtime Zod validation (always) + `jiti` for `.ts` extension files + `AdeExtensions` type export for JSDoc consumers.

## Notes

**SAP Architecture option shape** (what the consumer writes):
```js
// ade.extensions.mjs
import { } from '@codemcp/ade-core' // types only if needed

const sapOption = {
id: 'sap',
label: 'SAP',
description: 'SAP development with ABAP, CAP (Cloud Application Programming model), and BTP',
recipe: [
{
writer: 'skills',
config: {
skills: [
{ name: 'sap-architecture', description: '...', body: '...' },
{ name: 'sap-design', description: '...', body: '...' },
]
}
}
],
docsets: [
{ id: 'cap-docs', label: 'SAP CAP', origin: 'https://github.com/SAP/cloud-sdk.git', description: '...' }
]
}

export default {
facetContributions: {
architecture: [sapOption]
}
}
```

The CLI then calls `mergeExtensions(catalog, registry, extensions)` before running setup/install.

**Files to create/modify:**
1. `packages/core/src/types.ts` — add `AdeExtensions` type
2. `packages/core/src/catalog/index.ts` — add `mergeExtensions(catalog, extensions)` function
3. `packages/core/src/index.ts` — export `AdeExtensions` and `mergeExtensions`
4. `packages/cli/src/extensions.ts` — new file: `loadExtensions(projectRoot)` using dynamic import
5. `packages/cli/src/index.ts` — load extensions and pass merged catalog/registry to setup/install
6. `packages/harnesses/src/index.ts` — expose `mergeHarnessWriters` or accept additional writers
7. `ade.extensions.mjs` — example file in repo root with the SAP architecture option

---
*This plan is maintained by the LLM and uses beads CLI for task management. Tool responses provide guidance on which bd commands to use for task management.*
66 changes: 66 additions & 0 deletions ade.extensions.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* ade.extensions.mjs — SAP BTP / ABAP architecture extension example
*
* Place this file in your project root to extend the default ADE catalog
* without modifying any upstream source files.
*
* This file serves as both documentation and an integration example.
* It is by no means functional! It's about providing options with dependent
* skills and documentation sources.
* TypeScript consumers can use ade.extensions.ts with full IDE type-checking.
*
* @type {import('@codemcp/ade-core').AdeExtensions}
*/
export default {
facetContributions: {
architecture: [
{
id: "sap-abap",
label: "SAP BTP / ABAP",
description:
"SAP Business Technology Platform with ABAP Cloud development",
recipe: [
{
writer: "skills",
config: {
skills: [
{
name: "sap-abap-architecture",
source: "your-org/ade-sap/skills/sap-abap-architecture"
},
{
name: "sap-abap-code",
source: "your-org/ade-sap/skills/sap-abap-code"
},
{
name: "sap-abap-testing",
source: "your-org/ade-sap/skills/sap-abap-testing"
}
]
}
},
{
writer: "knowledge",
config: {
sources: [
{
name: "sap-abap-docs",
origin: "https://your-serialized-version-of-abap-docs.git",
description: "Official SAP ABAP Cloud development guide"
}
]
}
}
],
docsets: [
{
id: "sap-btp-docs",
label: "SAP BTP",
origin: "https://your-serialized-version-of-btp-docs.git",
description: "SAP Business Technology Platform documentation"
}
]
}
]
}
};
97 changes: 97 additions & 0 deletions docs/adr/0002-extension-file-type-safety.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# ADR 0002: Type Safety Strategy for Extension File Loading

## Status

Proposed

## Context

ADE is being made extensible: consumers who fork the repository or use it as an upstream
can place an `ade.extensions.mjs` (or `.js`, `.ts`) file in their project root to contribute
new catalog options, facets, provision writers, and harness writers without modifying
upstream files.

The CLI loads this file at runtime via dynamic `import()`. This creates a type safety gap:

- `import()` returns `Promise<unknown>`, so the loaded module has no compile-time shape
guarantee on the CLI side.
- Consumers writing `.mjs`/`.js` extension files have no TypeScript compiler checking their
exported object against the `AdeExtensions` interface.
- Shape errors (wrong property name, wrong recipe format, missing required field) would
surface as confusing runtime failures deep inside `resolve()` or a writer, not at the
point of authoring the extension.

Three levels of type safety are available:

**Level 1 — JSDoc `@type` annotation (authoring-time, opt-in)**
The consumer annotates their JS extension file with
`/** @type {import('@codemcp/ade-core').AdeExtensions} */`.
This provides IDE autocompletion and type hints in editors with JSDoc awareness (VS Code).
No build step. No CLI-side guarantee — the annotation is advisory only and silently ignored
if the consumer doesn't use it.

**Level 2 — Runtime Zod validation (load-time, mandatory)**
The CLI validates the loaded module against a Zod schema derived from `AdeExtensions`
immediately after import. Rejects invalid extensions with a structured, actionable error
message before any catalog or registry mutation happens.
No authoring-time feedback, but errors surface at `ade setup` time rather than
mid-resolution.

**Level 3 — TypeScript extension files via `jiti` (authoring-time, opt-in)**
The CLI's extension loader also accepts `ade.extensions.ts`. Loading it requires an
in-process TypeScript runner. `jiti` (by the Nuxt/unjs team) is the established solution:
it strips types at load time using `oxc-transform` (zero native binaries, fast, ESM-native).
Consumers writing `.ts` extension files get full TypeScript compiler checking and IDE
support. `jiti` is already used by Vite, Nuxt, and most of the unjs ecosystem.

These levels are not mutually exclusive. The fork scenario (consumer edits source TypeScript
directly in a forked CLI) already has full compile-time safety through the TypeScript
compiler — no additional mechanism needed there.

## Decision

We will implement **Level 2 (Zod runtime validation) combined with Level 3 (`.ts` support
via `jiti`)**, and export the `AdeExtensions` type from `@codemcp/ade-core` to enable
Level 1 as a zero-cost baseline for JS consumers.

Concretely:

1. `AdeExtensions` is defined as a TypeScript interface in `@codemcp/ade-core` and exported
from its public index.

2. A corresponding `AdeExtensionsSchema` Zod schema is defined alongside the interface.
The CLI's `loadExtensions(projectRoot)` function validates every loaded extension object
against this schema and throws a structured error if validation fails.

3. The extension loader searches for files in this order:
`ade.extensions.ts` → `ade.extensions.mjs` → `ade.extensions.js`
The first match is loaded. `.ts` files are loaded via `jiti`; `.mjs`/`.js` files via
native `import()`.

4. `jiti` is added as a production dependency of `@codemcp/ade-cli`.

## Consequences

**Easier:**

- Consumers writing `ade.extensions.ts` get full IDE type checking and compile-time errors
— the same authoring experience as editing the upstream source directly.
- All consumers (JS and TS) get clear, structured error messages at `ade setup` time if
their extension file is malformed, rather than cryptic failures during resolution.
- The `AdeExtensions` type and JSDoc `@type` path give JS consumers a zero-friction
upgrade path toward type safety without requiring a build step.
- The fork scenario (editing CLI TypeScript source directly) retains full compile-time
safety with no additional tooling.

**More difficult / trade-offs:**

- Adding `jiti` as a dependency introduces a transitive dependency surface (~400 kB
unpacked, no native binaries). This is a deliberate trade-off accepted in exchange for
`.ts` extension support.
- Zod must be kept as a runtime dependency of `@codemcp/ade-core` (it already is, or will
be added). The `AdeExtensionsSchema` must be kept in sync with the `AdeExtensions`
interface — a dual-maintenance surface. A build-time `zod-to-ts` or `ts-to-zod` step
could eliminate this in the future if drift becomes a problem.
- Dynamic loading of arbitrary user files (via `import()` or `jiti`) means the CLI cannot
be fully type-checked end-to-end at its own build time. The Zod boundary is the explicit
trust boundary between upstream-typed code and user-supplied code.
Loading