feat: implement .apply REPL dot-command (Approach 1: Minimalist Function Injection)#1789
feat: implement .apply REPL dot-command (Approach 1: Minimalist Function Injection)#1789
Conversation
pmcelhaney
left a comment
There was a problem hiding this comment.
Add documentation and a changeset
Head branch was pushed to by a user without write access
Added a "Scenario scripts with |
pmcelhaney
left a comment
There was a problem hiding this comment.
The code generator should create a default ./scenarios/index.ts file if one doesn't already exist.
It should import the ApplyContext type and have some commented out example code to demonstrate how to use it.
src/typescript-generator/generate.ts
Outdated
| /** Create a new route builder for a given path */ | ||
| route: (path: string) => unknown; | ||
| } | ||
| `; |
There was a problem hiding this comment.
ApplyContext should import the Context class:
// This file is generated by Counterfact. Do not edit manually.
import type { Context } from "../routes/_.context";
export interface ApplyContext {
/** Root context, same as loadContext("/") */
context: Context;
/** Load a context object for a specific path */
loadContext: (path: string) => Record<string, unknown>;
/** Named route builders stored in the REPL execution context */
routes: Record<string, unknown>;
/** Create a new route builder for a given path */
route: (path: string) => unknown;
}There was a problem hiding this comment.
Updated in commit 3bdc527. ApplyContext now imports Context from ../routes/_.context and uses it as the type for the context field.
…enarios/index.ts scaffold Agent-Logs-Url: https://github.com/counterfact/api-simulator/sessions/979d2099-b16b-4dce-9b48-6eca562985a4
Done in commit |
pmcelhaney
left a comment
There was a problem hiding this comment.
The apply context type file should also look for _.context.ts files in subdirectories.
If found it should import them and use them to narrow the type of loadContext().
This means that the file will need to be regenerate whenever a _.context.ts file is added or removed.
…erloads in apply-context.ts Agent-Logs-Url: https://github.com/counterfact/api-simulator/sessions/002ff83c-c6b9-43de-b74c-8b883456bd47
Done in commit
The file is always overwritten on every generation run, so adding or removing a |
|
@copilot in the REPl add tab completion for .apply |
Done in commit
Completions are read by scanning files with a regex for |
|
@copilot the completer should only look for exported functions |
Done in commit |
pmcelhaney
left a comment
There was a problem hiding this comment.
Create a ScenarioRegistry modeled after Registry and ContextRegistry.
It should not be the REPL's responsibility to load modules from the scenario directory. They should be loaded by the ModuleLoader and added the ScenarioRegistry. The ScenarioRegistry will be passed to the REPL.
Done in commit
|
Summary
Implements
.applyas a REPL dot-command that dynamically imports a TypeScript/JavaScript scenario file and calls a named export, injecting the live REPL environment (context,loadContext,routes,route) as its argument.Path resolution:
.apply fooscenarios/index.tsfoo.apply foo/barscenarios/foo.tsbar.apply foo/bar/bazscenarios/foo/bar.tsbazExample scenario script:
Then in the REPL:
.apply soldPetsThe
ApplyContexttype references the generatedContextclass fromroutes/_.context, giving scenario scripts full type safety against the user's own context shape. Additionally,loadContext()is typed with narrowed overloads for every_.context.tsfile found in subdirectories ofroutes/, so calls likeloadContext("/pets")return the specificPetsContexttype rather thanRecord<string, unknown>. Overloads are sorted deepest-first so TypeScript resolves the most specific type. Theapply-context.tsfile is always regenerated, so adding or removing a_.context.tsin any subdirectory is automatically reflected on the next generation run.Code generation also scaffolds a
scenarios/index.tsfile (only when it does not already exist) that importsApplyContextand includes generic commented-out example code covering context mutation,loadContext, and route builder storage.Scenario files are loaded by
ModuleLoaderinto a newScenarioRegistry(modeled afterRegistryandContextRegistry), which is then passed to the REPL. The REPL no longer does any file I/O — the.applycommand and tab completion look up modules fromScenarioRegistrydirectly.ScenarioRegistrystores modules keyed by slash-delimited relative file path (e.g."index","myscript","foo/bar") and exposesgetModule,getExportedFunctionNames, andgetFileKeys.The REPL completer supports tab completion for
.apply: typing.apply <Tab>suggests exported function names from the"index"scenario and file-key prefixes (e.g.myscript/); typing.apply myscript/<Tab>suggests exported function names from the"myscript"scenario asmyscript/<name>. Only exported functions are included —const,let,var, andclassexports are excluded.Original Prompt
Implement
.applyas a REPL dot-command (Approach 1: Minimalist Function Injection). The command dynamically imports a TypeScript/JavaScript scenario file and calls a named export, injecting the live REPL environment as its argument.Manual acceptance tests
.apply foocallsfoo(ctx)fromscenarios/index.ts, printingApplied fooand mutating context as expected.apply foo/barcallsbar(ctx)fromscenarios/foo.ts;.apply foo/bar/bazcallsbaz(ctx)fromscenarios/foo/bar.tsctx.routesinside a script are accessible asroutes.myNamein the REPL after the command completes.apply missing(no matching scenario in registry) prints a descriptive error messagescenarios/index.tsexists with anApplyContextimport and commented-out generic example code; re-running code generation does not overwrite itroutes/pets/_.context.tspresent,loadContext("/pets")is typed to returnPetsContext; deeper overloads appear before shallower ones in the generatedapply-context.ts.apply <Tab>shows only exported function names and file-key prefixes;.apply myscript/<Tab>shows exported function names asmyscript/<name>— non-function exports (const,class, etc.) are excludedTasks
src/server/scenario-registry.ts(new):ScenarioRegistryclass modeled afterRegistryandContextRegistry. Stores scenario modules keyed by slash-delimited relative file path. Providesadd,remove,getModule,getExportedFunctionNames(returns only function-valued exports), andgetFileKeys.src/server/module-loader.ts: Extended with optionalscenariosPathandscenarioRegistryconstructor params.load()scans the scenarios directory and populatesScenarioRegistry;watch()keeps it up to date on file changes. A missing scenarios directory is handled gracefully.src/repl/repl.ts: Added.applycommand with path traversal guard (rejects../.segments); looks up module viascenarioRegistry.getModule(fileKey)and calls the named export with anApplyContext-shaped object. Removed all file-system helpers (getExportedNames,getScenarioFilePrefixes,getApplyCompletions).createCompleter()now acceptsScenarioRegistryinstead ofscenariosDir; tab completions are derived from registry data. Added JSDoc tocreateCompleter.src/app.ts: CreatesScenarioRegistry, passes it toModuleLoader(withscenarios/path) and tostartRepl.src/typescript-generator/generate.ts: AddedcollectContextFiles(),buildApplyContextContent(),routePathToAlias(),writeDefaultScenariosIndex(), andwriteApplyContextType()for generatingtypes/apply-context.tswith narrowedloadContextoverloads and scaffoldingscenarios/index.ts.test/server/scenario-registry.test.ts(new): Unit tests forScenarioRegistrycoveringadd,remove,getModule,getExportedFunctionNames(functions only),getFileKeys, and overwrite behaviour.test/server/module-loader.test.ts: Added 3 scenario-loading tests: populating registry onload(), nested file keys, and graceful handling of a missing scenarios directory.test/repl/repl.test.ts: Updated.applytests to use pre-populatedScenarioRegistry(no temporary files); updated tab completion tests to useScenarioRegistry; allusingTemporaryFilesusage removed from.applyand completion tests.test/typescript-generator/generate.test.ts: Added 4 tests forapply-context.tsgeneration covering fallback types, root-only context, root + subdirectory context (overload order), and parameterised path context..changeset/apply-command.md: Minor version changeset.docs/usage.md: Added "Scenario scripts with.apply" subsection covering path resolution, a full annotated example script, and how to use storedctx.routesentries from the REPL after applying.