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
24 changes: 20 additions & 4 deletions PRD.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ MVP success means DevMap can:

## 9. Core Commands — MVP

Four commands. No more, no less.
Four core project commands plus one configuration command.

> Nothing removed. Nothing added until MVP is shipped.

Expand Down Expand Up @@ -422,6 +422,21 @@ No issues found.

---

### `devmap config model`

Set a global Groq model override or restore automatic command-based routing.

```bash
devmap config model llama-3.1-8b-instant
devmap config model openai/gpt-oss-120b
devmap config model auto
```

The override applies to AI-powered commands. `auto` restores the defaults in
the model routing table.

---

## 10. Generated Files

DevMap uses generated files to create reusable context for humans and AI agents.
Expand Down Expand Up @@ -538,9 +553,10 @@ ai/

| Command | Model | Reason |
|---|---|---|
| `analyze`, `ask` | `openai/gpt-oss-20b` | Production model, fast and cost-efficient |
| `analyze --deep` | `llama-3.3-70b-versatile` | Better reasoning for detailed explanations |
| Fallback | `llama-3.3-70b-versatile` | Production fallback when primary model is unavailable |
| `ask` | `llama-3.1-8b-instant` | Fast model for focused codebase questions |
| `analyze` | `openai/gpt-oss-20b` | Balanced architecture interpretation |
| `analyze --deep` | `openai/gpt-oss-120b` | Heavy cross-module reasoning |
| Fallback | `openai/gpt-oss-20b` | Production fallback when a different primary model is unavailable |

If a model becomes unavailable, DevMap gracefully falls back. No raw provider errors shown to users.

Expand Down
9 changes: 6 additions & 3 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -491,13 +491,16 @@ MVP default model routing:

| Command | Model |
| ---------------- | ------------------------- |
| `ask` | `llama-3.1-8b-instant` |
| `analyze` | `openai/gpt-oss-20b` |
| `ask` | `openai/gpt-oss-20b` |
| `analyze --deep` | `llama-3.3-70b-versatile` |
| Fallback | `llama-3.3-70b-versatile` |
| `analyze --deep` | `openai/gpt-oss-120b` |
| Fallback | `openai/gpt-oss-20b` |

If a model is unavailable, DevMap should fall back gracefully.
Only Groq production models should be used as public defaults.

Users can override automatic routing with `devmap config model <model>`.
Running `devmap config model auto` restores the defaults above.

Raw provider errors should not be shown directly to users.

Expand Down
44 changes: 34 additions & 10 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@

## Overview

DevMap MVP provides four core commands:
DevMap MVP provides four core project commands and one configuration command:

* `devmap init`
* `devmap analyze`
* `devmap ask`
* `devmap doctor`

No additional commands should be added until the MVP is shipped.
* `devmap doctor`
* `devmap config model`

No additional product commands should be added until the MVP is shipped.

Future commands are documented in:

Expand Down Expand Up @@ -366,7 +367,7 @@ Use existing snapshot or re-analyze first?

---

## `devmap doctor`
## `devmap doctor`

Run diagnostics for DevMap setup.

Expand Down Expand Up @@ -435,11 +436,34 @@ Issues found:
* Output must be readable
* Errors must be actionable
* Do not expose raw stack traces
* Mention what command the user should run next

---

## Global Flags
* Mention what command the user should run next

---

## `devmap config model`

Set a global model override for AI-powered commands.

### Usage

```bash
devmap config model llama-3.1-8b-instant
devmap config model openai/gpt-oss-120b
devmap config model auto
```

`auto` restores command-based routing:

* `ask` uses `llama-3.1-8b-instant`
* `analyze` uses `openai/gpt-oss-20b`
* `analyze --deep` uses `openai/gpt-oss-120b`

The command preserves the configured provider and API key. DevMap must be
initialized before changing the model.

---

## Global Flags

Available globally:

Expand Down
10 changes: 10 additions & 0 deletions docs/for-me-personal/PROGRESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ Terakhir diperbarui: 2026-06-14

## Update 2026-06-14

### Model Routing And Config

- Default `devmap ask` memakai `llama-3.1-8b-instant`.
- Standard `devmap analyze` tetap memakai `openai/gpt-oss-20b`.
- `devmap analyze --deep` memakai `openai/gpt-oss-120b`.
- Fallback model memakai `openai/gpt-oss-20b`.
- Command `devmap config model <model>` dapat menetapkan override global tanpa
mengubah provider atau API key.
- `devmap config model auto` mengembalikan routing default per command.

### Context Builder Token Optimization

- Pertanyaan navigasi English seperti `where` dan `find` sekarang memakai
Expand Down
26 changes: 26 additions & 0 deletions docs/for-me-personal/TEST.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,32 @@ Periksa `Relevant Files` dan prompt token usage. Query pertama seharusnya
memprioritaskan production CLI source dan memakai context jauh lebih kecil
daripada default lama lima file dengan maksimal 200 baris per file.

## Model Routing And Override

Focused automated test:

```powershell
pnpm --filter devmap exec tsx --test test/config-command.test.ts test/analyze-ai.test.ts test/ask-command.test.ts
```

Expected automatic routing:

- `ask`: `llama-3.1-8b-instant`
- `analyze`: `openai/gpt-oss-20b`
- `analyze --deep`: `openai/gpt-oss-120b`
- fallback: `openai/gpt-oss-20b`

Manual override:

```powershell
pnpm dev:cli config model openai/gpt-oss-120b
pnpm dev:cli doctor
pnpm dev:cli config model auto
```

The first command should preserve the existing API key and provider. The last
command should restore automatic command-based routing.

## Urutan Testing Yang Direkomendasikan

Untuk development harian:
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/ai/groq.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ const MAX_RATE_LIMIT_DELAY_MS = 10_000;
const MAX_RATE_LIMIT_RETRIES = 3;

export const DEFAULT_AI_MODELS = {
ask: "openai/gpt-oss-20b",
ask: "llama-3.1-8b-instant",
analyze: "openai/gpt-oss-20b",
deepAnalyze: "llama-3.3-70b-versatile",
fallback: "llama-3.3-70b-versatile"
deepAnalyze: "openai/gpt-oss-120b",
fallback: "openai/gpt-oss-20b"
} as const;

export type GroqClientDependencies = {
Expand Down
43 changes: 43 additions & 0 deletions packages/cli/src/commands/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {
readConfig,
writeConfig,
type DevmapConfig
} from "../utils/config.js";
import { output } from "../utils/output.js";

export type ConfigDependencies = {
loadConfig?: () => Promise<DevmapConfig | null>;
persistConfig?: (config: DevmapConfig) => Promise<void>;
};

export async function configModelCommand(
model: string,
dependencies: ConfigDependencies = {}
): Promise<void> {
const selectedModel = model.trim();
if (!selectedModel) {
output.error("Model name cannot be empty.");
return;
}

const loadConfig = dependencies.loadConfig ?? readConfig;
const persistConfig = dependencies.persistConfig ?? writeConfig;
const config = await loadConfig();

if (!config) {
output.error("DevMap is not configured yet.");
output.note("Run devmap init before changing the model.");
return;
}

await persistConfig({
...config,
model: selectedModel
});

output.success(
selectedModel === "auto"
? "Restored automatic command-based model routing."
: `Default model override set to ${selectedModel}.`
);
}
9 changes: 9 additions & 0 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { Command } from "commander";
import { analyzeCommand } from "./commands/analyze.js";
import { askCommand } from "./commands/ask.js";
import { configModelCommand } from "./commands/config.js";
import { doctorCommand } from "./commands/doctor.js";
import { initCommand } from "./commands/init.js";
import { printHelp } from "./utils/help.js";
Expand Down Expand Up @@ -34,6 +35,14 @@ program
.argument("<question...>", "question to ask")
.action(askCommand);

program
.command("config")
.description("Update DevMap configuration")
.command("model")
.description("Set a model override or restore automatic routing")
.argument("<model>", "Groq model ID or auto")
.action(configModelCommand);

program
.command("doctor")
.description("Diagnose DevMap setup")
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/utils/help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const commands = [
["init", "Initialize DevMap configuration"],
["analyze", "Analyze project structure"],
["ask <question>", "Ask about your codebase"],
["config model", "Set model override or automatic routing"],
["doctor", "Diagnose DevMap setup"]
] as const;

Expand Down
51 changes: 51 additions & 0 deletions packages/cli/test/analyze-ai.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import { DEFAULT_AI_MODELS } from "../src/ai/groq.js";
import type {
AiClient,
AiCompletionRequest,
Expand Down Expand Up @@ -124,6 +125,56 @@ test("analyze warns and continues when package.json is malformed", async () => {
}
});

test("analyze auto routing uses 20B normally and 120B for deep analysis", async () => {
const projectRoot = await mkdtemp(join(tmpdir(), "devmap-model-routing-"));
const requests: AiCompletionRequest[] = [];
const client: AiClient = {
async complete(request): Promise<AiCompletionResult> {
requests.push(request);
return {
content: "Architecture result.",
model: request.model
};
}
};

try {
await writeFile(
join(projectRoot, "package.json"),
JSON.stringify({ name: "model-routing-fixture" }),
"utf8"
);
await writeFile(join(projectRoot, "index.ts"), "export const ready = true;\n", "utf8");

const dependencies = {
loadConfig: async () => ({
provider: "groq" as const,
apiKey: "gsk_fixture",
model: "auto"
}),
createAiClient: () => client
};

await captureOutput(() => analyzeCommand(
projectRoot,
{ fresh: true },
dependencies
));
await captureOutput(() => analyzeCommand(
projectRoot,
{ deep: true, fresh: true },
dependencies
));

assert.equal(requests[0]?.model, DEFAULT_AI_MODELS.analyze);
assert.equal(requests[1]?.model, DEFAULT_AI_MODELS.deepAnalyze);
assert.equal(requests[0]?.fallbackModel, DEFAULT_AI_MODELS.fallback);
assert.equal(requests[1]?.fallbackModel, DEFAULT_AI_MODELS.fallback);
} finally {
await rm(projectRoot, { recursive: true, force: true });
}
});

function stripAnsi(value: string): string {
return value.replace(/\u001B\[[0-9;]*m/g, "");
}
Expand Down
Loading
Loading