@dailephd/my-dev-kit-lab 0.4.9 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +20 -2
- package/dist/src/cli/help.js +19 -4
- package/dist/src/commands/runExperimentDescribeCommand.js +11 -2
- package/dist/src/commands/runExperimentListCommand.js +3 -0
- package/dist/src/commands/runExperimentRunCommand.js +27 -7
- package/dist/src/evaluation/runMyDevKitRetrieval.js +82 -53
- package/dist/src/experiments/defaultRegistry.js +2 -0
- package/dist/src/experiments/plugins/index.js +1 -0
- package/dist/src/experiments/plugins/warmIndexReuse/config.js +58 -0
- package/dist/src/experiments/plugins/warmIndexReuse/execution.js +158 -0
- package/dist/src/experiments/plugins/warmIndexReuse/executionArtifact.js +77 -0
- package/dist/src/experiments/plugins/warmIndexReuse/fakeAgentEvaluation.js +118 -0
- package/dist/src/experiments/plugins/warmIndexReuse/index.js +8 -0
- package/dist/src/experiments/plugins/warmIndexReuse/metrics.js +257 -0
- package/dist/src/experiments/plugins/warmIndexReuse/plugin.js +212 -0
- package/dist/src/experiments/plugins/warmIndexReuse/selection.js +85 -0
- package/dist/src/experiments/plugins/warmIndexReuse/warmIndexSession.js +43 -0
- package/dist/src/plots/buildWarmIndexPlotData.js +112 -0
- package/dist/src/plots/index.js +1 -0
- package/dist/src/plots/writePlotArtifacts.js +8 -1
- package/dist/src/report/experiments/buildPluginExperimentReport.js +18 -2
- package/dist/src/report/experiments/buildWarmIndexReuseReport.js +91 -0
- package/dist/src/report/experiments/index.js +3 -0
- package/dist/src/report/experiments/renderPluginExperimentReportHtml.js +3 -0
- package/dist/src/report/experiments/renderPluginExperimentReportText.js +73 -0
- package/dist/src/report/experiments/renderWarmIndexReuseHtml.js +150 -0
- package/dist/src/report/experiments/warmIndexReuseReportModel.js +1 -0
- package/docs/METRICS.md +64 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@ All notable changes to my-dev-kit-lab are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.5.0] - 2026-09-21
|
|
8
|
+
|
|
9
|
+
Warm-index reuse experiment support.
|
|
10
|
+
|
|
11
|
+
- Added the experimental `warm-index-reuse` experiment plugin, registered alongside `context-strategy-comparison` and reachable through `experiment list`, `experiment describe`, and `experiment run`.
|
|
12
|
+
- Built exactly one my-dev-kit index per benchmark-project group and reused it across every task in that group, with a matched `raw-full-file` baseline for each task; cases that disagree on target or source roots fail structurally instead of being indexed.
|
|
13
|
+
- Separated index construction from retrieval: one measured `index` command per project, and search/lookup/slice/source against the prepared index for each task, without re-indexing.
|
|
14
|
+
- Added deterministic fake-agent evaluation of each task side with context evidence, reusing the existing prompt generator, answer parser, run classifier, and correctness scorer.
|
|
15
|
+
- Reported index build duration, raw context-construction and warm retrieval durations, raw and retrieved context size, estimated context tokens, amortized index build duration (build duration / task ordinal), cumulative component durations, cumulative estimated context tokens, fake-agent correctness, fake-agent total tokens, and cumulative fake-agent total tokens, each with explicit `available`/`unavailable`/`not-applicable` availability and strict-prefix cumulative rules.
|
|
16
|
+
- Added a bounded warm-index section to the plugin `report.json`, `report.txt`, and `report.html`, explaining cold-start versus warm-reuse cost, with a neutral interpretation and explicit limitations. The bounded `warm-index-execution.json` records execution evidence without context text.
|
|
17
|
+
- Added four warm-index plots through the existing `plots generate` command: amortized index cost, raw versus retrieved context size, fake-agent correctness, and cumulative fake-agent token usage.
|
|
18
|
+
- Added the warm-index-only `experiment run --kit-command <command>` option (default `npx @dailephd/my-dev-kit@latest`), grouped `experiment run --help` options by plugin, and corrected `plots generate` help to describe supported inputs.
|
|
19
|
+
- Extended the exact packed-package acceptance gate with installed warm-index execution, reports, and plots while preserving installed-package and target immutability.
|
|
20
|
+
- Limitations: agent evidence comes from the deterministic fake agent only; fake-agent correctness and token totals are simulated harness evidence, not real-model results or provider billing telemetry; estimated context tokens remain a separate context-size estimate; cumulative durations are component sums, not wall-clock latency; no token-savings percentage, break-even task, winner, or ranking is calculated; the bundled cases provide one task per benchmark project; real-agent warm-index campaigns, warm-index screenshots, and gallery integration are not included.
|
|
21
|
+
|
|
7
22
|
## [0.4.9] - 2026-09-20
|
|
8
23
|
|
|
9
24
|
Semantic native-select option selection for declarative browser tutorials.
|
package/README.md
CHANGED
|
@@ -6,13 +6,16 @@ my-dev-kit provides local repository indexing and graph-guided retrieval. my-dev
|
|
|
6
6
|
|
|
7
7
|
Whole-ecosystem workflows are centralized in [my-dev-kit/docs/ECOSYSTEM_DEVELOPMENT_WORKFLOWS.md](https://github.com/dailephd/my-dev-kit/blob/main/docs/ECOSYSTEM_DEVELOPMENT_WORKFLOWS.md). Its [command-surface compatibility map](https://github.com/dailephd/my-dev-kit/blob/main/docs/ECOSYSTEM_DEVELOPMENT_WORKFLOWS.md#915-command-surface-compatibility-map) documents which Lab outputs can safely seed my-dev-kit investigation, Observer reference work, or Orchestrator evidence, and which look-alike artifacts are not direct inputs.
|
|
8
8
|
|
|
9
|
-
The latest release is v0.4.9
|
|
9
|
+
The latest release is v0.5.0 (warm-index reuse experiment support). The previous release, v0.4.9, added semantic native-select option selection for declarative browser tutorials through the value-only `select-option` action backed by Playwright `Locator.selectOption({ value })`. The v0.4.5 context-integrity evaluation remains intentionally frozen against the published `@dailephd/my-dev-kit@1.10.4` and `@dailephd/my-dev-kit-orchestrator@1.2.3` contracts; those versions are historical validation baselines, not a statement that they are the ecosystem's current releases. See [docs/CURRENT_STATE.md](docs/CURRENT_STATE.md).
|
|
10
|
+
|
|
11
|
+
The current release includes v0.5.0 warm-index reuse. The package and installed CLI are version 0.5.0; v0.4.9 remains the previous release. See [docs/CURRENT_STATE.md](docs/CURRENT_STATE.md) for the validated release state.
|
|
10
12
|
|
|
11
13
|
v0.4.6 adds a supported `my-dev-kit-lab` installed CLI router (`--help`, `--version`, `security validate`, `audit`, the `experiment` family, `report render`, `plots generate`, `gallery build`, `demo final`, and the historical direct final-demo invocation form), a writable lab workspace model kept separate from the installed package and the inspected target, and a permanent packed-tarball installation/execution acceptance gate (`npm run verify:packed-package`). See [docs/ROADMAP.md](docs/ROADMAP.md) and [docs/CURRENT_STATE.md](docs/CURRENT_STATE.md) for status detail and [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the runtime path model. The "Installed CLI" section below documents the shipped command surface; the source-checkout `npm run` workflow in Quickstart remains available for contributors.
|
|
12
14
|
|
|
13
15
|
## Current capabilities
|
|
14
16
|
|
|
15
17
|
- **Run context-strategy experiments:** compare `raw-full-file` with `my-dev-kit-guided` using deterministic fixtures or locally configured Codex and Claude CLIs.
|
|
18
|
+
- **Measure warm-index reuse (v0.5.0):** the `warm-index-reuse` experiment plugin builds one my-dev-kit index per benchmark project, reuses it across that project's tasks, and compares every task with a matched `raw-full-file` baseline. Reports separate one-time index-build cost from per-task retrieval cost, show amortized index cost and cumulative measurements, and include deterministic fake-agent correctness and token evidence; `plots generate` renders four warm-index SVG charts. Estimated context tokens (a character-based context-size estimate) and fake-agent total tokens (simulated harness telemetry) are reported separately, and neither is provider billing telemetry. See [docs/WORKFLOWS.md](docs/WORKFLOWS.md#warm-index-reuse-experiment) and [docs/METRICS.md](docs/METRICS.md#warm-index-reuse-metrics).
|
|
16
19
|
- **Audit repository health:** run conservative code-rot detectors for TypeScript/JavaScript, Python, Java, and Kotlin, or adapt security findings into the common audit report.
|
|
17
20
|
- **Validate CLI/package security:** inspect dependencies, package contents, path and subprocess boundaries, malformed inputs, optional static scanners, and bounded fuzz targets.
|
|
18
21
|
- **Validate Android projects:** run nineteen static checks by default, with Gradle operations, external tools, and network access available only through explicit opt-in flags.
|
|
@@ -153,6 +156,19 @@ npm run experiment:run -- `
|
|
|
153
156
|
--no-screenshot
|
|
154
157
|
```
|
|
155
158
|
|
|
159
|
+
Run the warm-index reuse plugin deterministically with the fake my-dev-kit fixture (omit `--kit-command` to use the default `npx @dailephd/my-dev-kit@latest`):
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
npm run experiment:run -- \
|
|
163
|
+
--experiment warm-index-reuse \
|
|
164
|
+
--cases tests/fixtures/warm-index-reuse/multi-task-cases.json \
|
|
165
|
+
--kit-command "node tests/fixtures/fake-my-dev-kit-cli.js" \
|
|
166
|
+
--out lab-output/warm-index-reuse
|
|
167
|
+
npm run generate-experiment-plots -- --experiment lab-output/warm-index-reuse --out lab-output/warm-index-plots
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
The bundled `examples/token-savings-cases.json` currently has one task per benchmark project, so multi-task reuse needs a cases file with several tasks per project, such as the test fixture above; an expanded bundled warm-index benchmark suite is planned.
|
|
171
|
+
|
|
156
172
|
When `--target` is omitted, the experiment runs in self mode against my-dev-kit-lab. When `--target <path>` is provided, the lab remains the tool root and the target project is inspected separately. Generated experiment outputs stay under lab-controlled output directories by default, not inside the target project.
|
|
157
173
|
|
|
158
174
|
---
|
|
@@ -170,6 +186,7 @@ When `--target` is omitted, the experiment runs in self mode against my-dev-kit-
|
|
|
170
186
|
| Plugin experiment report JSON | `lab-output/experiments/<plugin>/<target>/<run>/report.json` |
|
|
171
187
|
| Plugin experiment report HTML | `lab-output/experiments/<plugin>/<target>/<run>/report.html` |
|
|
172
188
|
| Plugin experiment report text | `lab-output/experiments/<plugin>/<target>/<run>/report.txt` |
|
|
189
|
+
| Warm-index execution evidence | `<experiment out>/warm-index-execution.json` (plus `indexes/`, `commands/`, and `agents/` beneath the same output root) |
|
|
173
190
|
| Plot data | `lab-output/<plots>/plot-data.json` |
|
|
174
191
|
| SVG charts | `lab-output/<plots>/charts/*.svg` |
|
|
175
192
|
| Gallery manifest | `lab-output/<gallery>/gallery-manifest.json` |
|
|
@@ -203,7 +220,8 @@ See [docs/METRICS.md](docs/METRICS.md) for full metric definitions.
|
|
|
203
220
|
- Claude does not expose token totals; token savings comparisons are unavailable for Claude runs
|
|
204
221
|
- Codex may expose token totals but can produce timeouts or invalid-output runs
|
|
205
222
|
- Small projects may make raw-full-file cheaper than my-dev-kit-guided; larger localized tasks are where my-dev-kit is expected to become more useful
|
|
206
|
-
- The generic experiment-plugin framework
|
|
223
|
+
- The generic experiment-plugin framework has two plugins: `context-strategy-comparison` and the current-release v0.5.0 `warm-index-reuse`. Later plugins such as incremental-change and context-window scaling are future roadmap work
|
|
224
|
+
- `warm-index-reuse` uses the deterministic fake agent only; selectable Codex/Claude warm-index campaigns remain planned for v0.5.2, the expanded benchmark suite remains planned for v0.5.1, and warm-index screenshots/gallery integration are not implemented. Its component-duration sums are not wall-clock latency, and it calculates no token-savings percentage, break-even task, winner, or ranking
|
|
207
225
|
- The current release does not guarantee token savings; it produces auditable evidence for specific cases, targets, agents, and strategies
|
|
208
226
|
- Provider telemetry dashboards, semantic LLM judging, and cloud API billing integration are not yet implemented
|
|
209
227
|
- The six new stage-context strategies have no CLI flags yet, are configured programmatically, and do not yet include plots, screenshots, or gallery integration
|
package/dist/src/cli/help.js
CHANGED
|
@@ -142,7 +142,7 @@ export function renderExperimentRunHelp() {
|
|
|
142
142
|
"Required:",
|
|
143
143
|
" --experiment <id> Registered experiment plugin id (see \"my-dev-kit-lab experiment list\")",
|
|
144
144
|
"",
|
|
145
|
-
"
|
|
145
|
+
"Common options (all plugins):",
|
|
146
146
|
" --target <path> Local target project path (default: self)",
|
|
147
147
|
" --out <dir> Output directory for experiment artifacts",
|
|
148
148
|
" (default: beneath the workspace when installed)",
|
|
@@ -152,6 +152,15 @@ export function renderExperimentRunHelp() {
|
|
|
152
152
|
" (default: the bundled package resource)",
|
|
153
153
|
" --case <ids> Comma-separated case ids to run",
|
|
154
154
|
" --benchmark-project <ids> Comma-separated benchmark project ids to run",
|
|
155
|
+
"",
|
|
156
|
+
"warm-index-reuse only:",
|
|
157
|
+
" --kit-command <command> my-dev-kit command used to build one index per benchmark",
|
|
158
|
+
" project and retrieve per task",
|
|
159
|
+
" (default: npx @dailephd/my-dev-kit@latest)",
|
|
160
|
+
" Agent evidence uses the deterministic fake agent only;",
|
|
161
|
+
" agent options below are not accepted by this plugin.",
|
|
162
|
+
"",
|
|
163
|
+
"context-strategy-comparison only:",
|
|
155
164
|
" --agents <list> Comma-separated agent ids (default: fake-agent)",
|
|
156
165
|
" --strategies <list> Comma-separated strategies: raw-full-file,my-dev-kit-guided",
|
|
157
166
|
" --complexities <list> Comma-separated complexity levels: short,medium,long,multi-step",
|
|
@@ -238,19 +247,25 @@ export function renderPlotsHelp() {
|
|
|
238
247
|
" my-dev-kit-lab plots --help",
|
|
239
248
|
"",
|
|
240
249
|
"Commands:",
|
|
241
|
-
" generate Generate experiment plot artifacts from controlled-experiment
|
|
250
|
+
" generate Generate experiment plot artifacts from controlled-experiment or supported",
|
|
251
|
+
" plugin experiment artifacts (warm-index-reuse).",
|
|
242
252
|
" Run \"my-dev-kit-lab plots generate --help\" for details."
|
|
243
253
|
].join("\n");
|
|
244
254
|
}
|
|
245
255
|
export function renderPlotsGenerateHelp() {
|
|
246
256
|
return [
|
|
247
|
-
"my-dev-kit-lab plots generate - generate chart/plot artifacts from
|
|
257
|
+
"my-dev-kit-lab plots generate - generate chart/plot artifacts from experiment artifacts",
|
|
248
258
|
"",
|
|
249
259
|
"Usage:",
|
|
250
260
|
" my-dev-kit-lab plots generate --experiment <dir> --out <dir>",
|
|
251
261
|
"",
|
|
262
|
+
"Supported inputs:",
|
|
263
|
+
" - legacy controlled-experiment output directories",
|
|
264
|
+
" - warm-index-reuse plugin output directories (containing its report.json)",
|
|
265
|
+
" Other plugin outputs are not plotted by this command.",
|
|
266
|
+
"",
|
|
252
267
|
"Required:",
|
|
253
|
-
" --experiment <dir> Path to a
|
|
268
|
+
" --experiment <dir> Path to a supported experiment output directory",
|
|
254
269
|
" --out <dir> Output directory for plot artifacts"
|
|
255
270
|
].join("\n");
|
|
256
271
|
}
|
|
@@ -106,6 +106,9 @@ function readConfigFields(plugin, required) {
|
|
|
106
106
|
}));
|
|
107
107
|
}
|
|
108
108
|
function readSupportedVariants(plugin) {
|
|
109
|
+
if (plugin.supportedVariants) {
|
|
110
|
+
return [...plugin.supportedVariants];
|
|
111
|
+
}
|
|
109
112
|
const strategies = readArrayField(plugin.defaultConfig, "strategies");
|
|
110
113
|
return strategies.filter((value) => typeof value === "string");
|
|
111
114
|
}
|
|
@@ -139,9 +142,15 @@ function describeExpectedReports(plugin) {
|
|
|
139
142
|
return `Writes supported outputs: ${plugin.metadata.supportedOutputs.join(", ")}.`;
|
|
140
143
|
}
|
|
141
144
|
function describeExamples(plugin) {
|
|
145
|
+
// Only advertise agent-matrix options for plugins that declare them.
|
|
146
|
+
const agentOptions = hasConfigField(plugin, "agents") ? " --agents fake-agent --complexities short" : "";
|
|
147
|
+
const screenshotOption = hasConfigField(plugin, "agents") ? " --no-screenshot" : "";
|
|
142
148
|
return [
|
|
143
149
|
`my-dev-kit-lab experiment describe --experiment ${plugin.metadata.id}`,
|
|
144
|
-
`my-dev-kit-lab experiment run --experiment ${plugin.metadata.id}
|
|
145
|
-
`my-dev-kit-lab experiment run --experiment ${plugin.metadata.id} --target "Z:\\Users\\newuser\\Projects\\my-dev-kit-v1"
|
|
150
|
+
`my-dev-kit-lab experiment run --experiment ${plugin.metadata.id}${agentOptions}`,
|
|
151
|
+
`my-dev-kit-lab experiment run --experiment ${plugin.metadata.id} --target "Z:\\Users\\newuser\\Projects\\my-dev-kit-v1"${agentOptions}${screenshotOption}`
|
|
146
152
|
];
|
|
147
153
|
}
|
|
154
|
+
function hasConfigField(plugin, name) {
|
|
155
|
+
return (plugin.configDefinition?.fields ?? []).some((field) => field.name === name);
|
|
156
|
+
}
|
|
@@ -29,6 +29,9 @@ function toListedExperiment(metadata, plugin) {
|
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
31
|
function readSupportedVariants(plugin) {
|
|
32
|
+
if (plugin.supportedVariants) {
|
|
33
|
+
return [...plugin.supportedVariants];
|
|
34
|
+
}
|
|
32
35
|
const strategies = readArrayField(plugin.defaultConfig, "strategies");
|
|
33
36
|
return strategies.filter((value) => typeof value === "string");
|
|
34
37
|
}
|
|
@@ -2,7 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
import { parseAgentCommandTemplate } from "../agents/index.js";
|
|
3
3
|
import { parseAgentId } from "../agents/agentRegistry.js";
|
|
4
4
|
import { readBenchmarkProjectProfiles, readEvaluationCases } from "../evaluation/index.js";
|
|
5
|
-
import { contextStrategyComparisonPlugin, createDefaultExperimentPluginRegistry, resolveExperimentTarget, runExperiment } from "../experiments/index.js";
|
|
5
|
+
import { contextStrategyComparisonPlugin, createDefaultExperimentPluginRegistry, resolveExperimentTarget, runExperiment, warmIndexReusePlugin } from "../experiments/index.js";
|
|
6
6
|
import { buildDefaultExperimentOutputRoot } from "../experiments/outputPaths.js";
|
|
7
7
|
import { parsePromptComplexityLevel, parsePromptStrategy } from "../prompts/index.js";
|
|
8
8
|
import { writePluginExperimentReports } from "../report/index.js";
|
|
@@ -100,6 +100,7 @@ export function parseRunExperimentArgs(argv) {
|
|
|
100
100
|
let continueOnFailure;
|
|
101
101
|
let requireAgents;
|
|
102
102
|
let includeRealAgents;
|
|
103
|
+
let kitCommand;
|
|
103
104
|
const commandTemplates = {};
|
|
104
105
|
for (let index = 0; index < argv.length; index += 1) {
|
|
105
106
|
const arg = argv[index];
|
|
@@ -157,6 +158,9 @@ export function parseRunExperimentArgs(argv) {
|
|
|
157
158
|
else if (arg === "--command-template-claude") {
|
|
158
159
|
commandTemplates.claude = parseAgentCommandTemplate(readRequiredValue(argv, ++index, "--command-template-claude"));
|
|
159
160
|
}
|
|
161
|
+
else if (arg === "--kit-command") {
|
|
162
|
+
kitCommand = readRequiredValue(argv, ++index, "--kit-command");
|
|
163
|
+
}
|
|
160
164
|
else if (arg === "--no-screenshot") {
|
|
161
165
|
// The plugin-aware report path does not capture screenshots yet; accept this
|
|
162
166
|
// flag so smoke commands can share the legacy demo option set.
|
|
@@ -168,6 +172,9 @@ export function parseRunExperimentArgs(argv) {
|
|
|
168
172
|
if (!experimentId) {
|
|
169
173
|
throw new Error("Usage: --experiment <id> [--target <path>] [--out <directory>]");
|
|
170
174
|
}
|
|
175
|
+
if (kitCommand !== undefined && experimentId !== warmIndexReusePlugin.metadata.id) {
|
|
176
|
+
throw new Error(`--kit-command is only supported for --experiment ${warmIndexReusePlugin.metadata.id}.`);
|
|
177
|
+
}
|
|
171
178
|
return {
|
|
172
179
|
experimentId,
|
|
173
180
|
targetPath,
|
|
@@ -185,7 +192,8 @@ export function parseRunExperimentArgs(argv) {
|
|
|
185
192
|
continueOnFailure,
|
|
186
193
|
requireAgents,
|
|
187
194
|
includeRealAgents,
|
|
188
|
-
commandTemplates: Object.keys(commandTemplates).length > 0 ? commandTemplates : undefined
|
|
195
|
+
commandTemplates: Object.keys(commandTemplates).length > 0 ? commandTemplates : undefined,
|
|
196
|
+
kitCommand
|
|
189
197
|
})
|
|
190
198
|
};
|
|
191
199
|
}
|
|
@@ -212,13 +220,25 @@ function generateExperimentRunId(pluginId) {
|
|
|
212
220
|
return `${pluginId}-${timestamp}`;
|
|
213
221
|
}
|
|
214
222
|
async function loadPluginInputs(args, toolRoot, context) {
|
|
215
|
-
if (args.experimentId
|
|
216
|
-
|
|
223
|
+
if (args.experimentId === contextStrategyComparisonPlugin.metadata.id) {
|
|
224
|
+
const validation = contextStrategyComparisonPlugin.validateConfig(args.config);
|
|
225
|
+
if (!validation.valid || !validation.config) {
|
|
226
|
+
throw new Error(`Invalid context strategy comparison config: ${validation.errors.join("; ")}`);
|
|
227
|
+
}
|
|
228
|
+
return loadCasesAndProjectProfiles(args, toolRoot, context);
|
|
217
229
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
230
|
+
if (args.experimentId === warmIndexReusePlugin.metadata.id) {
|
|
231
|
+
const validation = warmIndexReusePlugin.validateConfig(args.config);
|
|
232
|
+
if (!validation.valid || !validation.config) {
|
|
233
|
+
throw new Error(`Invalid warm index reuse config: ${validation.errors.join("; ")}`);
|
|
234
|
+
}
|
|
235
|
+
return loadCasesAndProjectProfiles(args, toolRoot, context);
|
|
221
236
|
}
|
|
237
|
+
return undefined;
|
|
238
|
+
}
|
|
239
|
+
// Shared by both registered plugins: explicit paths resolve against toolRoot, defaults come from
|
|
240
|
+
// bundled package resources so they do not depend on the invocation cwd.
|
|
241
|
+
async function loadCasesAndProjectProfiles(args, toolRoot, context) {
|
|
222
242
|
const projectProfilesPath = args.config.projectProfilesPath
|
|
223
243
|
? path.resolve(toolRoot, args.config.projectProfilesPath)
|
|
224
244
|
: resolvePackageResource(context, DEFAULT_PROJECT_PROFILES_RESOURCE);
|
|
@@ -39,52 +39,71 @@ function pickCandidateFields(candidate) {
|
|
|
39
39
|
symbol: readString("symbol", "name", "label")
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
function skippedRetrieval(caseId, warnings, commands, durationMs) {
|
|
43
|
+
return {
|
|
44
|
+
caseId,
|
|
45
|
+
skipped: true,
|
|
46
|
+
warnings,
|
|
47
|
+
totalChars: 0,
|
|
48
|
+
totalEstimatedTokens: 0,
|
|
49
|
+
tokenCountMethod,
|
|
50
|
+
contextText: "",
|
|
51
|
+
filesRead: [],
|
|
52
|
+
commands,
|
|
53
|
+
durationMs
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Runs exactly one my-dev-kit `index` command for the target and writes the index only to
|
|
58
|
+
* `indexDir`. Performs no search/lookup/slice/source work. With `requireKit` a failed index
|
|
59
|
+
* throws; otherwise the failure is returned as `ok: false` with a warning.
|
|
60
|
+
*/
|
|
61
|
+
export async function buildMyDevKitIndex(options) {
|
|
44
62
|
const warnings = [];
|
|
45
|
-
const
|
|
46
|
-
const indexesDir = path.join(options.outputDir, "indexes", options.evaluationCase.id);
|
|
47
|
-
const commands = [];
|
|
48
|
-
const indexCommand = await runMeasuredCommand({
|
|
63
|
+
const command = await runMeasuredCommand({
|
|
49
64
|
commandId: "index",
|
|
50
65
|
commandString: options.kitCommand,
|
|
51
66
|
cwd: process.cwd(),
|
|
52
|
-
outDir: commandsDir,
|
|
67
|
+
outDir: options.commandsDir,
|
|
53
68
|
extraArgs: [
|
|
54
69
|
"index",
|
|
55
70
|
"--root",
|
|
56
|
-
options.
|
|
57
|
-
...options.
|
|
71
|
+
options.target.absoluteTargetRoot,
|
|
72
|
+
...options.target.sourceRoots.flatMap((sourceRoot) => ["--src", sourceRoot]),
|
|
58
73
|
"--out",
|
|
59
|
-
|
|
74
|
+
options.indexDir,
|
|
60
75
|
"--json"
|
|
61
76
|
]
|
|
62
77
|
});
|
|
63
|
-
|
|
64
|
-
if (!indexCommand.ok) {
|
|
78
|
+
if (!command.ok) {
|
|
65
79
|
if (options.requireKit) {
|
|
66
|
-
throw new Error(
|
|
80
|
+
throw new Error(command.error || `my-dev-kit index failed with exit code ${command.exitCode}`);
|
|
67
81
|
}
|
|
68
82
|
warnings.push("my-dev-kit index command was unavailable or failed.");
|
|
69
|
-
return {
|
|
70
|
-
caseId: options.evaluationCase.id,
|
|
71
|
-
skipped: true,
|
|
72
|
-
warnings,
|
|
73
|
-
totalChars: 0,
|
|
74
|
-
totalEstimatedTokens: 0,
|
|
75
|
-
tokenCountMethod,
|
|
76
|
-
contextText: "",
|
|
77
|
-
filesRead: [],
|
|
78
|
-
commands,
|
|
79
|
-
durationMs: Date.now() - started
|
|
80
|
-
};
|
|
81
83
|
}
|
|
84
|
+
return {
|
|
85
|
+
ok: command.ok,
|
|
86
|
+
indexDir: options.indexDir,
|
|
87
|
+
durationMs: command.durationMs,
|
|
88
|
+
warnings,
|
|
89
|
+
command
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Runs search -> lookup -> slice -> source against an already prepared index. Never invokes
|
|
94
|
+
* `index`; `durationMs` covers retrieval only.
|
|
95
|
+
*/
|
|
96
|
+
export async function runMyDevKitRetrievalFromIndex(options) {
|
|
97
|
+
const started = Date.now();
|
|
98
|
+
const warnings = [];
|
|
99
|
+
const commands = [];
|
|
100
|
+
const { commandsDir, indexDir } = options;
|
|
82
101
|
const searchCommand = await runMeasuredCommand({
|
|
83
102
|
commandId: "search",
|
|
84
103
|
commandString: options.kitCommand,
|
|
85
104
|
cwd: process.cwd(),
|
|
86
105
|
outDir: commandsDir,
|
|
87
|
-
extraArgs: ["search", "--index",
|
|
106
|
+
extraArgs: ["search", "--index", indexDir, "--query", options.evaluationCase.query, "--json"]
|
|
88
107
|
});
|
|
89
108
|
commands.push(searchCommand);
|
|
90
109
|
if (!searchCommand.ok) {
|
|
@@ -92,36 +111,14 @@ export async function runMyDevKitRetrieval(options) {
|
|
|
92
111
|
throw new Error(searchCommand.error || `my-dev-kit search failed with exit code ${searchCommand.exitCode}`);
|
|
93
112
|
}
|
|
94
113
|
warnings.push("my-dev-kit search command failed.");
|
|
95
|
-
return
|
|
96
|
-
caseId: options.evaluationCase.id,
|
|
97
|
-
skipped: true,
|
|
98
|
-
warnings,
|
|
99
|
-
totalChars: 0,
|
|
100
|
-
totalEstimatedTokens: 0,
|
|
101
|
-
tokenCountMethod,
|
|
102
|
-
contextText: "",
|
|
103
|
-
filesRead: [],
|
|
104
|
-
commands,
|
|
105
|
-
durationMs: Date.now() - started
|
|
106
|
-
};
|
|
114
|
+
return skippedRetrieval(options.evaluationCase.id, warnings, commands, Date.now() - started);
|
|
107
115
|
}
|
|
108
116
|
const searchPayload = parseJsonIfPossible(searchCommand.stdout);
|
|
109
117
|
const candidates = readSearchResults(searchPayload);
|
|
110
118
|
const selected = candidates[0];
|
|
111
119
|
if (!selected) {
|
|
112
120
|
warnings.push("No my-dev-kit search candidate was found.");
|
|
113
|
-
return
|
|
114
|
-
caseId: options.evaluationCase.id,
|
|
115
|
-
skipped: true,
|
|
116
|
-
warnings,
|
|
117
|
-
totalChars: 0,
|
|
118
|
-
totalEstimatedTokens: 0,
|
|
119
|
-
tokenCountMethod,
|
|
120
|
-
contextText: "",
|
|
121
|
-
filesRead: [],
|
|
122
|
-
commands,
|
|
123
|
-
durationMs: Date.now() - started
|
|
124
|
-
};
|
|
121
|
+
return skippedRetrieval(options.evaluationCase.id, warnings, commands, Date.now() - started);
|
|
125
122
|
}
|
|
126
123
|
const candidate = pickCandidateFields(selected);
|
|
127
124
|
const selectedNodeId = candidate.nodeId;
|
|
@@ -136,7 +133,7 @@ export async function runMyDevKitRetrieval(options) {
|
|
|
136
133
|
commandString: options.kitCommand,
|
|
137
134
|
cwd: process.cwd(),
|
|
138
135
|
outDir: commandsDir,
|
|
139
|
-
extraArgs: ["lookup", "--index",
|
|
136
|
+
extraArgs: ["lookup", "--index", indexDir, "--node", selectedNodeId, "--json"]
|
|
140
137
|
});
|
|
141
138
|
commands.push(lookupCommand);
|
|
142
139
|
if (lookupCommand.ok) {
|
|
@@ -150,7 +147,7 @@ export async function runMyDevKitRetrieval(options) {
|
|
|
150
147
|
commandString: options.kitCommand,
|
|
151
148
|
cwd: process.cwd(),
|
|
152
149
|
outDir: commandsDir,
|
|
153
|
-
extraArgs: ["slice", "--index",
|
|
150
|
+
extraArgs: ["slice", "--index", indexDir, "--node", selectedNodeId, "--json"]
|
|
154
151
|
});
|
|
155
152
|
commands.push(sliceCommand);
|
|
156
153
|
if (sliceCommand.ok) {
|
|
@@ -164,7 +161,7 @@ export async function runMyDevKitRetrieval(options) {
|
|
|
164
161
|
commandString: options.kitCommand,
|
|
165
162
|
cwd: process.cwd(),
|
|
166
163
|
outDir: commandsDir,
|
|
167
|
-
extraArgs: ["source", "--index",
|
|
164
|
+
extraArgs: ["source", "--index", indexDir, "--node", selectedNodeId, "--max-lines", "160", "--format", "numbered"]
|
|
168
165
|
});
|
|
169
166
|
commands.push(sourceCommand);
|
|
170
167
|
if (sourceCommand.ok) {
|
|
@@ -195,3 +192,35 @@ export async function runMyDevKitRetrieval(options) {
|
|
|
195
192
|
durationMs: Date.now() - started
|
|
196
193
|
};
|
|
197
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Legacy per-case lifecycle: builds a case-specific index and retrieves from it. `commands`
|
|
197
|
+
* starts with the index command and `durationMs` covers the whole lifecycle including indexing.
|
|
198
|
+
*/
|
|
199
|
+
export async function runMyDevKitRetrieval(options) {
|
|
200
|
+
const started = Date.now();
|
|
201
|
+
const commandsDir = path.join(options.outputDir, "commands", options.evaluationCase.id);
|
|
202
|
+
const indexDir = path.join(options.outputDir, "indexes", options.evaluationCase.id);
|
|
203
|
+
const index = await buildMyDevKitIndex({
|
|
204
|
+
target: options.evaluationCase,
|
|
205
|
+
kitCommand: options.kitCommand,
|
|
206
|
+
indexDir,
|
|
207
|
+
commandsDir,
|
|
208
|
+
requireKit: options.requireKit
|
|
209
|
+
});
|
|
210
|
+
if (!index.ok) {
|
|
211
|
+
return skippedRetrieval(options.evaluationCase.id, [...index.warnings], [index.command], Date.now() - started);
|
|
212
|
+
}
|
|
213
|
+
const retrieval = await runMyDevKitRetrievalFromIndex({
|
|
214
|
+
evaluationCase: options.evaluationCase,
|
|
215
|
+
kitCommand: options.kitCommand,
|
|
216
|
+
indexDir,
|
|
217
|
+
commandsDir,
|
|
218
|
+
requireKit: options.requireKit
|
|
219
|
+
});
|
|
220
|
+
return {
|
|
221
|
+
...retrieval,
|
|
222
|
+
warnings: [...index.warnings, ...retrieval.warnings],
|
|
223
|
+
commands: [index.command, ...retrieval.commands],
|
|
224
|
+
durationMs: Date.now() - started
|
|
225
|
+
};
|
|
226
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { ExperimentPluginRegistry } from "./registry.js";
|
|
2
2
|
import { contextStrategyComparisonPlugin } from "./plugins/contextStrategyComparison/index.js";
|
|
3
|
+
import { warmIndexReusePlugin } from "./plugins/warmIndexReuse/index.js";
|
|
3
4
|
export function createDefaultExperimentPluginRegistry() {
|
|
4
5
|
const registry = new ExperimentPluginRegistry();
|
|
5
6
|
registry.register(contextStrategyComparisonPlugin);
|
|
7
|
+
registry.register(warmIndexReusePlugin);
|
|
6
8
|
return registry;
|
|
7
9
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { invalidExperimentConfig, isPlainObject, mergeConfig, validExperimentConfig } from "../../config.js";
|
|
2
|
+
export const defaultWarmIndexReuseConfig = {
|
|
3
|
+
casesPath: "examples/token-savings-cases.json",
|
|
4
|
+
projectProfilesPath: "benchmarks/contracts/benchmark-project-profiles.json",
|
|
5
|
+
outDir: "lab-output/warm-index-reuse",
|
|
6
|
+
kitCommand: "npx @dailephd/my-dev-kit@latest",
|
|
7
|
+
};
|
|
8
|
+
const SUPPORTED_FIELDS = [
|
|
9
|
+
"casesPath",
|
|
10
|
+
"projectProfilesPath",
|
|
11
|
+
"outDir",
|
|
12
|
+
"kitCommand",
|
|
13
|
+
"caseIds",
|
|
14
|
+
"benchmarkProjects",
|
|
15
|
+
];
|
|
16
|
+
export const warmIndexReuseConfigDefinition = {
|
|
17
|
+
fields: [
|
|
18
|
+
{ name: "casesPath", type: "string", required: true, description: "Evaluation cases file." },
|
|
19
|
+
{ name: "outDir", type: "string", required: true, description: "Experiment output root." },
|
|
20
|
+
{ name: "projectProfilesPath", type: "string", description: "Benchmark project profiles file." },
|
|
21
|
+
{ name: "kitCommand", type: "string", description: "my-dev-kit command used for index and retrieval." },
|
|
22
|
+
{ name: "caseIds", type: "array", description: "Evaluation case IDs to include." },
|
|
23
|
+
{ name: "benchmarkProjects", type: "array", description: "Benchmark project IDs to include." },
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Closed validation: fields outside the warm-index contract (for example agent-matrix settings)
|
|
28
|
+
* are rejected instead of being silently ignored.
|
|
29
|
+
*/
|
|
30
|
+
export function validateWarmIndexReuseConfig(config) {
|
|
31
|
+
if (config !== undefined && !isPlainObject(config)) {
|
|
32
|
+
return invalidExperimentConfig(["warm index reuse config must be an object."]);
|
|
33
|
+
}
|
|
34
|
+
const errors = [];
|
|
35
|
+
const unsupported = Object.keys(config ?? {}).filter((key) => !SUPPORTED_FIELDS.includes(key));
|
|
36
|
+
if (unsupported.length > 0) {
|
|
37
|
+
errors.push(`Unsupported warm-index-reuse config field(s): ${unsupported.sort().join(", ")}.`);
|
|
38
|
+
}
|
|
39
|
+
const normalized = mergeConfig(defaultWarmIndexReuseConfig, config);
|
|
40
|
+
for (const field of ["casesPath", "projectProfilesPath", "outDir", "kitCommand"]) {
|
|
41
|
+
const value = normalized[field];
|
|
42
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
43
|
+
errors.push(`${field} must be a non-empty string.`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
for (const field of ["caseIds", "benchmarkProjects"]) {
|
|
47
|
+
const value = normalized[field];
|
|
48
|
+
if (value === undefined)
|
|
49
|
+
continue;
|
|
50
|
+
if (!Array.isArray(value) || value.length === 0 || !value.every((item) => typeof item === "string" && item.trim())) {
|
|
51
|
+
errors.push(`${field} must be a non-empty array of non-empty strings when provided.`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (errors.length > 0) {
|
|
55
|
+
return invalidExperimentConfig(errors);
|
|
56
|
+
}
|
|
57
|
+
return validExperimentConfig(normalized);
|
|
58
|
+
}
|