summaryrefslogtreecommitdiffhomepage
path: root/src/features/system-prompt/logic
diff options
context:
space:
mode:
Diffstat (limited to 'src/features/system-prompt/logic')
-rw-r--r--src/features/system-prompt/logic/view-model.test.ts148
-rw-r--r--src/features/system-prompt/logic/view-model.ts74
2 files changed, 111 insertions, 111 deletions
diff --git a/src/features/system-prompt/logic/view-model.test.ts b/src/features/system-prompt/logic/view-model.test.ts
index a0736cc..223327b 100644
--- a/src/features/system-prompt/logic/view-model.test.ts
+++ b/src/features/system-prompt/logic/view-model.test.ts
@@ -1,90 +1,90 @@
import type { SystemPromptVariable } from "@dispatch/transport-contract";
import { describe, expect, it } from "vitest";
import {
- buildIfNotTag,
- buildIfTag,
- buildTag,
- groupVariables,
- insertTag,
- isDynamicVariable,
+ buildIfNotTag,
+ buildIfTag,
+ buildTag,
+ groupVariables,
+ insertTag,
+ isDynamicVariable,
} from "./view-model";
describe("system-prompt view-model", () => {
- describe("buildTag", () => {
- it("builds a variable placeholder", () => {
- expect(buildTag("system", "time")).toBe("[system:time]");
- });
- });
+ describe("buildTag", () => {
+ it("builds a variable placeholder", () => {
+ expect(buildTag("system", "time")).toBe("[system:time]");
+ });
+ });
- describe("buildIfTag", () => {
- it("builds an opening conditional tag", () => {
- expect(buildIfTag("file", "AGENTS.md")).toBe("[if file:AGENTS.md]");
- });
- });
+ describe("buildIfTag", () => {
+ it("builds an opening conditional tag", () => {
+ expect(buildIfTag("file", "AGENTS.md")).toBe("[if file:AGENTS.md]");
+ });
+ });
- describe("buildIfNotTag", () => {
- it("builds a negated opening conditional tag", () => {
- expect(buildIfNotTag("prompt", "cwd")).toBe("[if !prompt:cwd]");
- });
- });
+ describe("buildIfNotTag", () => {
+ it("builds a negated opening conditional tag", () => {
+ expect(buildIfNotTag("prompt", "cwd")).toBe("[if !prompt:cwd]");
+ });
+ });
- describe("insertTag", () => {
- it("inserts a tag at the cursor position", () => {
- expect(insertTag("Hello world", "[system:time]", 5, 5)).toEqual({
- template: "Hello[system:time] world",
- cursor: 18,
- });
- });
+ describe("insertTag", () => {
+ it("inserts a tag at the cursor position", () => {
+ expect(insertTag("Hello world", "[system:time]", 5, 5)).toEqual({
+ template: "Hello[system:time] world",
+ cursor: 18,
+ });
+ });
- it("replaces the selected text", () => {
- const tag = buildTag("file", "README.md");
- expect(insertTag("Hello world", tag, 6, 11)).toEqual({
- template: `Hello ${tag}`,
- cursor: 6 + tag.length,
- });
- });
+ it("replaces the selected text", () => {
+ const tag = buildTag("file", "README.md");
+ expect(insertTag("Hello world", tag, 6, 11)).toEqual({
+ template: `Hello ${tag}`,
+ cursor: 6 + tag.length,
+ });
+ });
- it("inserts at the end", () => {
- const tag = buildTag("git", "branch");
- expect(insertTag("", tag, 0, 0)).toEqual({
- template: tag,
- cursor: tag.length,
- });
- });
- });
+ it("inserts at the end", () => {
+ const tag = buildTag("git", "branch");
+ expect(insertTag("", tag, 0, 0)).toEqual({
+ template: tag,
+ cursor: tag.length,
+ });
+ });
+ });
- describe("groupVariables", () => {
- it("groups by type in first-appearing order", () => {
- const variables: SystemPromptVariable[] = [
- { type: "system", name: "time", description: "" },
- { type: "prompt", name: "cwd", description: "" },
- { type: "system", name: "date", description: "" },
- { type: "git", name: "branch", description: "" },
- ];
- const groups = groupVariables(variables);
- expect(groups.map((g) => g.type)).toEqual(["system", "prompt", "git"]);
- expect(groups[0]?.variables.map((v) => v.name)).toEqual(["time", "date"]);
- expect(groups[1]?.variables.map((v) => v.name)).toEqual(["cwd"]);
- expect(groups[2]?.variables.map((v) => v.name)).toEqual(["branch"]);
- });
+ describe("groupVariables", () => {
+ it("groups by type in first-appearing order", () => {
+ const variables: SystemPromptVariable[] = [
+ { type: "system", name: "time", description: "" },
+ { type: "prompt", name: "cwd", description: "" },
+ { type: "system", name: "date", description: "" },
+ { type: "git", name: "branch", description: "" },
+ ];
+ const groups = groupVariables(variables);
+ expect(groups.map((g) => g.type)).toEqual(["system", "prompt", "git"]);
+ expect(groups[0]?.variables.map((v) => v.name)).toEqual(["time", "date"]);
+ expect(groups[1]?.variables.map((v) => v.name)).toEqual(["cwd"]);
+ expect(groups[2]?.variables.map((v) => v.name)).toEqual(["branch"]);
+ });
- it("returns an empty array when no variables", () => {
- expect(groupVariables([])).toEqual([]);
- });
- });
+ it("returns an empty array when no variables", () => {
+ expect(groupVariables([])).toEqual([]);
+ });
+ });
- describe("isDynamicVariable", () => {
- it("returns true when dynamic is true", () => {
- expect(
- isDynamicVariable({ type: "file", name: "path", description: "", dynamic: true }),
- ).toBe(true);
- });
+ describe("isDynamicVariable", () => {
+ it("returns true when dynamic is true", () => {
+ expect(
+ isDynamicVariable({ type: "file", name: "path", description: "", dynamic: true }),
+ ).toBe(true);
+ });
- it("returns false when dynamic is missing or false", () => {
- expect(isDynamicVariable({ type: "system", name: "time", description: "" })).toBe(false);
- expect(
- isDynamicVariable({ type: "system", name: "time", description: "", dynamic: false }),
- ).toBe(false);
- });
- });
+ it("returns false when dynamic is missing or false", () => {
+ expect(isDynamicVariable({ type: "system", name: "time", description: "" })).toBe(false);
+ expect(
+ isDynamicVariable({ type: "system", name: "time", description: "", dynamic: false }),
+ ).toBe(false);
+ });
+ });
});
diff --git a/src/features/system-prompt/logic/view-model.ts b/src/features/system-prompt/logic/view-model.ts
index 6924208..e202ee3 100644
--- a/src/features/system-prompt/logic/view-model.ts
+++ b/src/features/system-prompt/logic/view-model.ts
@@ -14,20 +14,20 @@ import type { SystemPromptVariable } from "@dispatch/transport-contract";
// ── Injected ports (composition root adapts the store to these shapes) ─────────
export type SystemPromptLoadResult =
- | { readonly ok: true; readonly template: string }
- | { readonly ok: false; readonly error: string };
+ | { readonly ok: true; readonly template: string }
+ | { readonly ok: false; readonly error: string };
export type LoadSystemPrompt = () => Promise<SystemPromptLoadResult>;
export type SystemPromptSaveResult =
- | { readonly ok: true; readonly template: string }
- | { readonly ok: false; readonly error: string };
+ | { readonly ok: true; readonly template: string }
+ | { readonly ok: false; readonly error: string };
export type SaveSystemPrompt = (template: string) => Promise<SystemPromptSaveResult>;
export type SystemPromptVariablesResult =
- | { readonly ok: true; readonly variables: readonly SystemPromptVariable[] }
- | { readonly ok: false; readonly error: string };
+ | { readonly ok: true; readonly variables: readonly SystemPromptVariable[] }
+ | { readonly ok: false; readonly error: string };
export type LoadSystemPromptVariables = () => Promise<SystemPromptVariablesResult>;
@@ -35,24 +35,24 @@ export type LoadSystemPromptVariables = () => Promise<SystemPromptVariablesResul
/** Build the literal placeholder `[type:name]` for a variable. */
export function buildTag(type: string, name: string): string {
- return `[${type}:${name}]`;
+ return `[${type}:${name}]`;
}
/** Build the literal placeholder `[if type:name]` for a conditional block. */
export function buildIfTag(type: string, name: string): string {
- return `[if ${type}:${name}]`;
+ return `[if ${type}:${name}]`;
}
/** Build the literal placeholder `[if !type:name]` for a negated conditional block. */
export function buildIfNotTag(type: string, name: string): string {
- return `[if !${type}:${name}]`;
+ return `[if !${type}:${name}]`;
}
export interface Insertion {
- /** Template text after insertion. */
- template: string;
- /** New cursor position (caret index) after insertion. */
- cursor: number;
+ /** Template text after insertion. */
+ template: string;
+ /** New cursor position (caret index) after insertion. */
+ cursor: number;
}
/**
@@ -61,24 +61,24 @@ export interface Insertion {
* the caller can restore the caret after the inserted tag.
*/
export function insertTag(
- template: string,
- tag: string,
- selectionStart: number,
- selectionEnd: number,
+ template: string,
+ tag: string,
+ selectionStart: number,
+ selectionEnd: number,
): Insertion {
- const before = template.slice(0, selectionStart);
- const after = template.slice(selectionEnd);
- const next = before + tag + after;
- return { template: next, cursor: selectionStart + tag.length };
+ const before = template.slice(0, selectionStart);
+ const after = template.slice(selectionEnd);
+ const next = before + tag + after;
+ return { template: next, cursor: selectionStart + tag.length };
}
// ── Variable grouping ─────────────────────────────────────────────────────────
export interface VariableGroup {
- /** Variable type (e.g. `"system"`, `"file"`, `"prompt"`, `"git"`). */
- readonly type: string;
- /** Variables of this type. */
- readonly variables: readonly SystemPromptVariable[];
+ /** Variable type (e.g. `"system"`, `"file"`, `"prompt"`, `"git"`). */
+ readonly type: string;
+ /** Variables of this type. */
+ readonly variables: readonly SystemPromptVariable[];
}
/**
@@ -86,21 +86,21 @@ export interface VariableGroup {
* group and the order of first-appearing types.
*/
export function groupVariables(
- variables: readonly SystemPromptVariable[],
+ variables: readonly SystemPromptVariable[],
): readonly VariableGroup[] {
- const order: string[] = [];
- const map = new Map<string, SystemPromptVariable[]>();
- for (const v of variables) {
- if (!map.has(v.type)) {
- map.set(v.type, []);
- order.push(v.type);
- }
- map.get(v.type)?.push(v);
- }
- return order.map((type) => ({ type, variables: map.get(type) ?? [] }));
+ const order: string[] = [];
+ const map = new Map<string, SystemPromptVariable[]>();
+ for (const v of variables) {
+ if (!map.has(v.type)) {
+ map.set(v.type, []);
+ order.push(v.type);
+ }
+ map.get(v.type)?.push(v);
+ }
+ return order.map((type) => ({ type, variables: map.get(type) ?? [] }));
}
/** Whether a variable is "dynamic" (any name is valid, e.g. `file:<path>`). */
export function isDynamicVariable(variable: SystemPromptVariable): boolean {
- return variable.dynamic === true;
+ return variable.dynamic === true;
}