# How to Create Custom Features Using the `Feature` Base Class in agent-core-v2

> Learn to build custom features in agent-core-v2 by extending the Feature base class. Implement setup() for automatic discovery and management by FeatureManagerService.

- Repository: [Moonshot AI/kimi-code](https://github.com/MoonshotAI/kimi-code)
- Tags: how-to-guide
- Published: 2026-08-16

---

**Extend the abstract `Feature` class and implement `setup()` to register lifecycle-aware capabilities that are automatically discovered and managed by `FeatureManagerService`.**

The `agent-core-v2` package in the [MoonshotAI/kimi-code](https://github.com/MoonshotAI/kimi-code) repository provides a modular Feature system for extending Kimi Code sessions. The `Feature` base class—located at [`src/features/feature.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/src/features/feature.ts)—extends `Service` to provide dependency injection support and standardized lifecycle hooks. This architecture lets you add custom capabilities without modifying core engine code.

## Understanding the Feature Architecture

### The `Feature` Class Hierarchy

In `agent-core-v2`, every capability is modeled as a **Feature**. The inheritance chain is:

```

Service (DI container base)
    └── Feature (abstract)
            └── YourCustomFeature

```

The `Feature` class in [`src/features/feature.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/src/features/feature.ts) defines three async lifecycle methods:

- **`setup()`** – Called once during session assembly; use this to register commands, subscribe to events, or initialize state
- **`start()`** – Called when the session becomes active
- **`stop()`** – Called during session shutdown for cleanup

These methods are invoked by `FeatureManagerService` ([`src/app/feature/featureManagerService.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/src/app/feature/featureManagerService.ts)), which orchestrates all features for a session.

### Automatic Discovery via `FeatureAssemblyService`

You do not manually register features. Instead, `FeatureAssemblyService` ([`src/features/featureAssemblyService.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/src/features/featureAssemblyService.ts)) scans the `src/features/**` directory, discovers all concrete `Feature` subclasses, and registers them with `FeatureManagerService`.

The registration flow:

```

FeatureAssemblyService ──► discovers MyFeature
         │
         ▼
FeatureManagerService ───► calls MyFeature.setup()
         │
         ▼
    MyFeature.start() / stop() during session lifecycle

```

## Creating a Custom Feature: Step-by-Step

### Step 1: Extend `Feature` and Implement `setup()`

Create a file under `src/features/<your-feature>/` that extends the base class. Inject required services via the constructor using the `@inject` decorator.

```typescript
// src/features/myFeature/myFeature.ts
import { Feature } from '#/features/feature';
import { CommandRegistry } from '#/services/commandRegistry';
import { inject } from '#/di';

export class MyFeature extends Feature {
  constructor(
    @inject('CommandRegistry') private readonly commands: CommandRegistry
  ) {
    super();
  }

  async setup(): Promise<void> {
    // Register commands, event handlers, or initial state
    this.commands.register('my:greet', async () => {
      console.log('Greeting from MyFeature!');
    });
  }

  async start(): Promise<void> {
    console.log('MyFeature activated');
  }

  async stop(): Promise<void> {
    console.log('MyFeature shutting down');
  }
}

```

### Step 2: Export for Discovery

Export your feature from an [`index.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/index.ts) so the assembly scanner can locate it:

```typescript
// src/features/myFeature/index.ts
export * from './myFeature';

```

No additional registration code is required—the glob pattern in `FeatureAssemblyService` picks up all feature directories automatically.

## Built-in Feature Examples

The `agent-core-v2` source contains several reference implementations demonstrating patterns for `Feature` subclassing.

### `PlanFeature` – Complex Setup Implementation

[`src/features/plan/planFeature.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/src/features/plan/planFeature.ts) shows a feature that registers multiple commands and integrates with planning services:

```typescript
// From src/features/plan/planFeature.ts (lines 1-45)
import { Feature } from '#/features/feature';
// ... other imports

export class PlanFeature extends Feature {
  async setup(): Promise<void> {
    // Registers plan-related commands and subscriptions
    // Implementation details in source...
  }
}

```

This example demonstrates injecting multiple services and coordinating between them during `setup()`.

### `SessionInitFeature` – Initialization-Order Pattern

[`src/features/sessionInit/sessionInitFeature.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/src/features/sessionInit/sessionInitFeature.ts) illustrates a feature that must run early in the session lifecycle. It uses `setup()` to configure initial session state before other features start:

```typescript
// From src/features/sessionInit/sessionInitFeature.ts
export class SessionInitFeature extends Feature {
  async setup(): Promise<void> {
    // Configure session defaults, load persisted state, etc.
  }
}

```

Reference the corresponding test at [`test/features/sessionInit/sessionInitFeature.test.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/test/features/sessionInit/sessionInitFeature.test.ts) for the testing pattern.

## Testing Your Custom Feature

The `agent-core-v2` test utilities provide `createTestScope()` for isolated feature testing. This pattern mirrors the actual DI container used in production.

```typescript
// test/features/myFeature/myFeature.test.ts
import { createTestScope } from '#/test/util';
import { MyFeature } from '#/features/myFeature';

test('MyFeature registers greet command', async () => {
  const { scope, commandRegistry } = await createTestScope();
  
  await scope.register(MyFeature);
  await scope.start();

  expect(commandRegistry.has('my:greet')).toBe(true);
});

```

The `createTestScope()` helper instantiates a minimal DI container with mocked services, letting you verify feature behavior without a full session.

## Key Source Files for Custom Feature Development

| File | Purpose | GitHub Link |
|------|---------|-------------|
| [`src/features/feature.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/src/features/feature.ts) | Abstract `Feature` base class with lifecycle definitions | [View source](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core-v2/src/features/feature.ts) |
| [`src/app/feature/featureManagerService.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/src/app/feature/featureManagerService.ts) | Orchestrates feature lifecycle per session | [View source](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core-v2/src/app/feature/featureManagerService.ts) |
| [`src/features/featureAssemblyService.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/src/features/featureAssemblyService.ts) | Discovers and registers `Feature` subclasses | [View source](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core-v2/src/features/featureAssemblyService.ts) |
| [`src/features/plan/planFeature.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/src/features/plan/planFeature.ts) | Reference: multi-service feature implementation | [View source](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core-v2/src/features/plan/planFeature.ts) |
| [`src/features/sessionInit/sessionInitFeature.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/src/features/sessionInit/sessionInitFeature.ts) | Reference: initialization-order pattern | [View source](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core-v2/src/features/sessionInit/sessionInitFeature.ts) |
| [`test/features/sessionInit/sessionInitFeature.test.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/test/features/sessionInit/sessionInitFeature.test.ts) | Reference: feature unit testing pattern | [View source](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core-v2/test/features/sessionInit/sessionInitFeature.test.ts) |

## Summary

- **Extend `Feature`** from [`src/features/feature.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/src/features/feature.ts) to create custom capabilities with automatic DI and lifecycle management
- **Implement `setup()`** to register commands, handlers, and initial state—this is the required hook for feature activation
- **Export your class** from a feature directory; `FeatureAssemblyService` discovers it automatically via directory scanning
- **Use `createTestScope()`** from test utilities to write isolated unit tests against the DI container
- **Reference `PlanFeature` and `SessionInitFeature`** in the source for production patterns of varying complexity

## Frequently Asked Questions

### What methods must I implement when extending `Feature`?

Only `setup()` is abstract and must be implemented. The `start()` and `stop()` methods are optional lifecycle hooks provided by the base class—override them if your feature needs activation or cleanup behavior.

### How does dependency injection work with custom features?

The `Feature` class extends `Service`, making it DI-aware. Decorate constructor parameters with `@inject('ServiceName')` to receive injected instances. The `FeatureManagerService` resolves all dependencies when instantiating your feature during session assembly.

### Can I control the order in which features initialize?

Feature discovery order depends on filesystem glob results, but you can implement dependency-like ordering by having features check for or wait on shared services during `setup()`. For strict ordering requirements, examine `SessionInitFeature` as a pattern for early-initialization features.

### Where should I place my custom feature files?

Create a directory under `packages/agent-core-v2/src/features/<your-feature-name>/` containing your implementation and an [`index.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/index.ts) export. The `FeatureAssemblyService` glob pattern `src/features/**` automatically includes any TypeScript files in this structure.