@townco/agent 0.1.22 → 0.1.23

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.
Files changed (44) hide show
  1. package/dist/acp-server/adapter.d.ts +10 -14
  2. package/dist/acp-server/cli.d.ts +1 -3
  3. package/dist/acp-server/cli.js +5 -9
  4. package/dist/acp-server/http.d.ts +1 -3
  5. package/dist/bin.js +0 -0
  6. package/dist/definition/index.d.ts +6 -0
  7. package/dist/definition/index.js +9 -0
  8. package/dist/index.js +11 -5
  9. package/dist/runner/agent-runner.d.ts +6 -0
  10. package/dist/runner/index.d.ts +1 -3
  11. package/dist/runner/index.js +14 -18
  12. package/dist/runner/langchain/index.js +10 -0
  13. package/dist/runner/langchain/tools/todo.d.ts +32 -48
  14. package/dist/runner/langchain/tools/web_search.d.ts +1 -1
  15. package/dist/runner/tools.d.ts +16 -0
  16. package/dist/runner/tools.js +10 -1
  17. package/dist/scaffold/copy-gui.js +7 -81
  18. package/dist/scaffold/copy-tui.js +1 -65
  19. package/dist/scaffold/index.d.ts +2 -0
  20. package/dist/scaffold/index.js +26 -31
  21. package/dist/scaffold/project-scaffold.d.ts +12 -0
  22. package/dist/scaffold/project-scaffold.js +314 -0
  23. package/dist/storage/index.d.ts +5 -0
  24. package/dist/storage/index.js +60 -24
  25. package/dist/templates/index.d.ts +7 -2
  26. package/dist/templates/index.js +13 -16
  27. package/dist/tsconfig.tsbuildinfo +1 -1
  28. package/dist/utils/index.d.ts +1 -0
  29. package/dist/utils/index.js +1 -0
  30. package/dist/utils/tool.d.ts +36 -0
  31. package/dist/utils/tool.js +33 -0
  32. package/index.ts +11 -7
  33. package/package.json +6 -5
  34. package/templates/index.ts +23 -18
  35. package/dist/definition/mcp.d.ts +0 -0
  36. package/dist/definition/mcp.js +0 -0
  37. package/dist/definition/tools/todo.d.ts +0 -49
  38. package/dist/definition/tools/todo.js +0 -80
  39. package/dist/definition/tools/web_search.d.ts +0 -4
  40. package/dist/definition/tools/web_search.js +0 -26
  41. package/dist/dev-agent/index.d.ts +0 -2
  42. package/dist/dev-agent/index.js +0 -18
  43. package/dist/example.d.ts +0 -2
  44. package/dist/example.js +0 -19
@@ -2,18 +2,14 @@ import * as acp from "@agentclientprotocol/sdk";
2
2
  import type { AgentRunner } from "../runner";
3
3
  /** Adapts an Agent to speak the ACP protocol */
4
4
  export declare class AgentAcpAdapter implements acp.Agent {
5
- private connection;
6
- private sessions;
7
- private agent;
8
- constructor(agent: AgentRunner, connection: acp.AgentSideConnection);
9
- initialize(_params: acp.InitializeRequest): Promise<acp.InitializeResponse>;
10
- newSession(_params: acp.NewSessionRequest): Promise<acp.NewSessionResponse>;
11
- authenticate(
12
- _params: acp.AuthenticateRequest,
13
- ): Promise<acp.AuthenticateResponse | undefined>;
14
- setSessionMode(
15
- _params: acp.SetSessionModeRequest,
16
- ): Promise<acp.SetSessionModeResponse>;
17
- prompt(params: acp.PromptRequest): Promise<acp.PromptResponse>;
18
- cancel(params: acp.CancelNotification): Promise<void>;
5
+ private connection;
6
+ private sessions;
7
+ private agent;
8
+ constructor(agent: AgentRunner, connection: acp.AgentSideConnection);
9
+ initialize(_params: acp.InitializeRequest): Promise<acp.InitializeResponse>;
10
+ newSession(_params: acp.NewSessionRequest): Promise<acp.NewSessionResponse>;
11
+ authenticate(_params: acp.AuthenticateRequest): Promise<acp.AuthenticateResponse | undefined>;
12
+ setSessionMode(_params: acp.SetSessionModeRequest): Promise<acp.SetSessionModeResponse>;
13
+ prompt(params: acp.PromptRequest): Promise<acp.PromptResponse>;
14
+ cancel(params: acp.CancelNotification): Promise<void>;
19
15
  }
@@ -1,5 +1,3 @@
1
1
  import type { AgentDefinition } from "../definition";
2
2
  import { type AgentRunner } from "../runner";
3
- export declare function makeStdioTransport(
4
- agent: AgentRunner | AgentDefinition,
5
- ): void;
3
+ export declare function makeStdioTransport(agent: AgentRunner | AgentDefinition): void;
@@ -3,13 +3,9 @@ import * as acp from "@agentclientprotocol/sdk";
3
3
  import { makeRunnerFromDefinition } from "../runner";
4
4
  import { AgentAcpAdapter } from "./adapter";
5
5
  export function makeStdioTransport(agent) {
6
- const agentRunner =
7
- "definition" in agent ? agent : makeRunnerFromDefinition(agent);
8
- const input = Writable.toWeb(process.stdout);
9
- const output = Readable.toWeb(process.stdin);
10
- const stream = acp.ndJsonStream(input, output);
11
- new acp.AgentSideConnection(
12
- (conn) => new AgentAcpAdapter(agentRunner, conn),
13
- stream,
14
- );
6
+ const agentRunner = "definition" in agent ? agent : makeRunnerFromDefinition(agent);
7
+ const input = Writable.toWeb(process.stdout);
8
+ const output = Readable.toWeb(process.stdin);
9
+ const stream = acp.ndJsonStream(input, output);
10
+ new acp.AgentSideConnection((conn) => new AgentAcpAdapter(agentRunner, conn), stream);
15
11
  }
@@ -1,5 +1,3 @@
1
1
  import type { AgentDefinition } from "../definition";
2
2
  import { type AgentRunner } from "../runner";
3
- export declare function makeHttpTransport(
4
- agent: AgentRunner | AgentDefinition,
5
- ): void;
3
+ export declare function makeHttpTransport(agent: AgentRunner | AgentDefinition): void;
package/dist/bin.js CHANGED
File without changes
@@ -22,6 +22,12 @@ export declare const AgentDefinitionSchema: z.ZodObject<{
22
22
  }, z.core.$strip>, z.ZodObject<{
23
23
  type: z.ZodLiteral<"filesystem">;
24
24
  working_directory: z.ZodOptional<z.ZodString>;
25
+ }, z.core.$strip>, z.ZodObject<{
26
+ type: z.ZodLiteral<"direct">;
27
+ name: z.ZodString;
28
+ description: z.ZodString;
29
+ fn: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
30
+ schema: z.ZodAny;
25
31
  }, z.core.$strip>]>>>;
26
32
  mcps: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
27
33
  name: z.ZodString;
@@ -35,11 +35,20 @@ const FilesystemToolSchema = z.object({
35
35
  /** If omitted, defaults to process.cwd() at runtime */
36
36
  working_directory: z.string().optional(),
37
37
  });
38
+ /** Direct tool configuration schema (for tools imported directly in code). */
39
+ const DirectToolSchema = z.object({
40
+ type: z.literal("direct"),
41
+ name: z.string(),
42
+ description: z.string(),
43
+ fn: z.function(),
44
+ schema: z.any(), // Accept any Zod schema
45
+ });
38
46
  /** Tool schema - can be a string (built-in tool) or custom tool object. */
39
47
  const ToolSchema = z.union([
40
48
  z.string(),
41
49
  CustomToolSchema,
42
50
  FilesystemToolSchema,
51
+ DirectToolSchema,
43
52
  ]);
44
53
  /** Agent definition schema. */
45
54
  export const AgentDefinitionSchema = z.object({
package/dist/index.js CHANGED
@@ -1,9 +1,15 @@
1
- import { readFileSync } from "node:fs";
2
- import { join } from "node:path";
3
1
  import { makeHttpTransport, makeStdioTransport } from "./acp-server";
4
- // Load agent definition from shared JSON file at repo root
5
- const configPath = join(import.meta.dir, "../../agent.json");
6
- const exampleAgent = JSON.parse(readFileSync(configPath, "utf-8"));
2
+ const exampleAgent = {
3
+ model: "claude-sonnet-4-5-20250929",
4
+ systemPrompt: "You are a helpful assistant.",
5
+ tools: [
6
+ "todo_write",
7
+ "get_weather",
8
+ "web_search",
9
+ { type: "filesystem", working_directory: "/Users/michael/code/town" },
10
+ ],
11
+ mcps: [],
12
+ };
7
13
  // Parse transport type from command line argument
8
14
  const transport = process.argv[2] || "stdio";
9
15
  if (transport === "http") {
@@ -9,6 +9,12 @@ export declare const zAgentRunnerParams: z.ZodObject<{
9
9
  }, z.core.$strip>, z.ZodObject<{
10
10
  type: z.ZodLiteral<"filesystem">;
11
11
  working_directory: z.ZodOptional<z.ZodString>;
12
+ }, z.core.$strip>, z.ZodObject<{
13
+ type: z.ZodLiteral<"direct">;
14
+ name: z.ZodString;
15
+ description: z.ZodString;
16
+ fn: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
17
+ schema: z.ZodAny;
12
18
  }, z.core.$strip>]>>>;
13
19
  mcps: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
14
20
  name: z.ZodString;
@@ -1,6 +1,4 @@
1
1
  import type { AgentDefinition } from "../definition";
2
2
  import { type AgentRunner } from "./agent-runner";
3
3
  export type { AgentRunner };
4
- export declare const makeRunnerFromDefinition: (
5
- definition: AgentDefinition,
6
- ) => AgentRunner;
4
+ export declare const makeRunnerFromDefinition: (definition: AgentDefinition) => AgentRunner;
@@ -1,22 +1,18 @@
1
1
  import { zAgentRunnerParams } from "./agent-runner";
2
2
  import { LangchainAgent } from "./langchain";
3
3
  export const makeRunnerFromDefinition = (definition) => {
4
- const agentRunnerParams = zAgentRunnerParams.safeParse(definition);
5
- if (!agentRunnerParams.success) {
6
- throw new Error(
7
- `Invalid agent definition: ${agentRunnerParams.error.message}`,
8
- );
9
- }
10
- switch (definition.harnessImplementation) {
11
- case undefined:
12
- case "langchain": {
13
- return new LangchainAgent(agentRunnerParams.data);
14
- }
15
- default: {
16
- const _exhaustiveCheck = definition.harnessImplementation;
17
- throw new Error(
18
- `Unsupported harness implementation: ${definition.harnessImplementation}`,
19
- );
20
- }
21
- }
4
+ const agentRunnerParams = zAgentRunnerParams.safeParse(definition);
5
+ if (!agentRunnerParams.success) {
6
+ throw new Error(`Invalid agent definition: ${agentRunnerParams.error.message}`);
7
+ }
8
+ switch (definition.harnessImplementation) {
9
+ case undefined:
10
+ case "langchain": {
11
+ return new LangchainAgent(agentRunnerParams.data);
12
+ }
13
+ default: {
14
+ const _exhaustiveCheck = definition.harnessImplementation;
15
+ throw new Error(`Unsupported harness implementation: ${definition.harnessImplementation}`);
16
+ }
17
+ }
22
18
  };
@@ -73,6 +73,16 @@ export class LangchainAgent {
73
73
  process.cwd();
74
74
  enabledTools.push(...makeFilesystemTools(wd));
75
75
  }
76
+ else if (type === "direct") {
77
+ // Handle direct tool objects (imported in code)
78
+ // biome-ignore lint/suspicious/noExplicitAny: mlai unsure how to best type this
79
+ const addedTool = tool(t.fn, {
80
+ name: t.name,
81
+ description: t.description,
82
+ schema: t.schema,
83
+ });
84
+ enabledTools.push(addedTool);
85
+ }
76
86
  }
77
87
  }
78
88
  // Built-in tools from registry
@@ -1,49 +1,33 @@
1
1
  import { z } from "zod";
2
- export declare const todoItemSchema: z.ZodObject<
3
- {
4
- content: z.ZodString;
5
- status: z.ZodEnum<{
6
- pending: "pending";
7
- in_progress: "in_progress";
8
- completed: "completed";
9
- }>;
10
- activeForm: z.ZodString;
11
- },
12
- z.core.$strip
13
- >;
14
- export declare const todoWrite: import("langchain").DynamicStructuredTool<
15
- z.ZodObject<
16
- {
17
- todos: z.ZodArray<
18
- z.ZodObject<
19
- {
20
- content: z.ZodString;
21
- status: z.ZodEnum<{
22
- pending: "pending";
23
- in_progress: "in_progress";
24
- completed: "completed";
25
- }>;
26
- activeForm: z.ZodString;
27
- },
28
- z.core.$strip
29
- >
30
- >;
31
- },
32
- z.core.$strip
33
- >,
34
- {
35
- todos: {
36
- content: string;
37
- status: "pending" | "in_progress" | "completed";
38
- activeForm: string;
39
- }[];
40
- },
41
- {
42
- todos: {
43
- content: string;
44
- status: "pending" | "in_progress" | "completed";
45
- activeForm: string;
46
- }[];
47
- },
48
- string
49
- >;
2
+ export declare const todoItemSchema: z.ZodObject<{
3
+ content: z.ZodString;
4
+ status: z.ZodEnum<{
5
+ pending: "pending";
6
+ in_progress: "in_progress";
7
+ completed: "completed";
8
+ }>;
9
+ activeForm: z.ZodString;
10
+ }, z.core.$strip>;
11
+ export declare const todoWrite: import("langchain").DynamicStructuredTool<z.ZodObject<{
12
+ todos: z.ZodArray<z.ZodObject<{
13
+ content: z.ZodString;
14
+ status: z.ZodEnum<{
15
+ pending: "pending";
16
+ in_progress: "in_progress";
17
+ completed: "completed";
18
+ }>;
19
+ activeForm: z.ZodString;
20
+ }, z.core.$strip>>;
21
+ }, z.core.$strip>, {
22
+ todos: {
23
+ content: string;
24
+ status: "pending" | "in_progress" | "completed";
25
+ activeForm: string;
26
+ }[];
27
+ }, {
28
+ todos: {
29
+ content: string;
30
+ status: "pending" | "in_progress" | "completed";
31
+ activeForm: string;
32
+ }[];
33
+ }, string>;
@@ -1,4 +1,4 @@
1
1
  import { ExaSearchResults } from "@langchain/exa";
2
2
  export declare function makeWebSearchTool(): ExaSearchResults<{
3
- text: true;
3
+ text: true;
4
4
  }>;
@@ -1,6 +1,14 @@
1
1
  import { z } from "zod";
2
2
  /** Built-in tool types. */
3
3
  export declare const zBuiltInToolType: z.ZodUnion<readonly [z.ZodLiteral<"todo_write">, z.ZodLiteral<"get_weather">, z.ZodLiteral<"web_search">, z.ZodLiteral<"filesystem">]>;
4
+ /** Direct tool object schema (for tools imported directly in code). */
5
+ declare const zDirectTool: z.ZodObject<{
6
+ type: z.ZodLiteral<"direct">;
7
+ name: z.ZodString;
8
+ description: z.ZodString;
9
+ fn: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
10
+ schema: z.ZodAny;
11
+ }, z.core.$strip>;
4
12
  /** Tool type - can be a built-in tool string or custom tool object. */
5
13
  export declare const zToolType: z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodLiteral<"todo_write">, z.ZodLiteral<"get_weather">, z.ZodLiteral<"web_search">, z.ZodLiteral<"filesystem">]>, z.ZodObject<{
6
14
  type: z.ZodLiteral<"custom">;
@@ -8,6 +16,14 @@ export declare const zToolType: z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodL
8
16
  }, z.core.$strip>, z.ZodObject<{
9
17
  type: z.ZodLiteral<"filesystem">;
10
18
  working_directory: z.ZodOptional<z.ZodString>;
19
+ }, z.core.$strip>, z.ZodObject<{
20
+ type: z.ZodLiteral<"direct">;
21
+ name: z.ZodString;
22
+ description: z.ZodString;
23
+ fn: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
24
+ schema: z.ZodAny;
11
25
  }, z.core.$strip>]>;
12
26
  export type ToolType = z.infer<typeof zToolType>;
13
27
  export type BuiltInToolType = z.infer<typeof zBuiltInToolType>;
28
+ export type DirectTool = z.infer<typeof zDirectTool>;
29
+ export {};
@@ -6,7 +6,7 @@ export const zBuiltInToolType = z.union([
6
6
  z.literal("web_search"),
7
7
  z.literal("filesystem"),
8
8
  ]);
9
- /** Custom tool schema. */
9
+ /** Custom tool schema (loaded from module path). */
10
10
  const zCustomTool = z.object({
11
11
  type: z.literal("custom"),
12
12
  modulePath: z.string(),
@@ -16,9 +16,18 @@ const zFilesystemTool = z.object({
16
16
  type: z.literal("filesystem"),
17
17
  working_directory: z.string().optional(),
18
18
  });
19
+ /** Direct tool object schema (for tools imported directly in code). */
20
+ const zDirectTool = z.object({
21
+ type: z.literal("direct"),
22
+ name: z.string(),
23
+ description: z.string(),
24
+ fn: z.function(),
25
+ schema: z.any(), // Accept any Zod schema
26
+ });
19
27
  /** Tool type - can be a built-in tool string or custom tool object. */
20
28
  export const zToolType = z.union([
21
29
  zBuiltInToolType,
22
30
  zCustomTool,
23
31
  zFilesystemTool,
32
+ zDirectTool,
24
33
  ]);
@@ -1,4 +1,4 @@
1
- import { cp, mkdir, writeFile } from "node:fs/promises";
1
+ import { cp, mkdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { createRequire } from "node:module";
3
3
  import { dirname, join } from "node:path";
4
4
  const require = createRequire(import.meta.url);
@@ -34,84 +34,10 @@ export async function copyGuiApp(agentPath) {
34
34
  const targetPath = join(guiDir, item);
35
35
  await cp(sourcePath, targetPath, { recursive: true });
36
36
  }
37
- // Create a standalone tsconfig.json for the GUI (can't extend from workspace tsconfig)
38
- const guiTsConfig = {
39
- compilerOptions: {
40
- allowArbitraryExtensions: true,
41
- allowUnreachableCode: false,
42
- allowUnusedLabels: false,
43
- declaration: true,
44
- emitDecoratorMetadata: true,
45
- esModuleInterop: true,
46
- exactOptionalPropertyTypes: true,
47
- experimentalDecorators: true,
48
- jsx: "react-jsx",
49
- lib: ["DOM", "ESNext"],
50
- module: "ESNext",
51
- moduleResolution: "bundler",
52
- noFallthroughCasesInSwitch: true,
53
- noImplicitAny: true,
54
- noImplicitOverride: true,
55
- noImplicitReturns: true,
56
- noUncheckedIndexedAccess: true,
57
- noUncheckedSideEffectImports: true,
58
- noUnusedLocals: false,
59
- noUnusedParameters: true,
60
- resolveJsonModule: true,
61
- skipLibCheck: true,
62
- strict: true,
63
- stripInternal: true,
64
- target: "ESNext",
65
- verbatimModuleSyntax: true,
66
- outDir: "./dist",
67
- rootDir: "./src",
68
- },
69
- include: ["src/**/*"],
70
- exclude: ["node_modules", "dist"],
71
- };
72
- await writeFile(join(guiDir, "tsconfig.json"), JSON.stringify(guiTsConfig, null, 2));
73
- // Generate a custom package.json for the GUI
74
- // Use @townco/ui as a dependency instead of copying files
75
- const packageJson = {
76
- name: "agent-gui",
77
- version: "0.0.1",
78
- type: "module",
79
- private: true,
80
- scripts: {
81
- dev: "vite",
82
- build: "vite build",
83
- preview: "vite preview",
84
- },
85
- dependencies: {
86
- "@townco/ui": "^0.1.0",
87
- "@agentclientprotocol/sdk": "^0.5.1",
88
- "@radix-ui/react-dialog": "^1.1.15",
89
- "@radix-ui/react-label": "^2.1.8",
90
- "@radix-ui/react-select": "^2.2.6",
91
- "@radix-ui/react-slot": "^1.2.4",
92
- "@radix-ui/react-tabs": "^1.1.13",
93
- "class-variance-authority": "^0.7.1",
94
- clsx: "^2.1.1",
95
- "lucide-react": "^0.552.0",
96
- react: "^19.2.0",
97
- "react-dom": "^19.2.0",
98
- "react-markdown": "^10.1.0",
99
- "remark-gfm": "^4.0.1",
100
- "tailwind-merge": "^3.3.1",
101
- zod: "^4.1.12",
102
- zustand: "^5.0.8",
103
- },
104
- devDependencies: {
105
- "@tailwindcss/postcss": "^4.1.17",
106
- "@types/react": "^19.2.2",
107
- "@types/react-dom": "^19.2.2",
108
- "@vitejs/plugin-react": "^5.1.0",
109
- autoprefixer: "^10.4.21",
110
- postcss: "^8.5.6",
111
- tailwindcss: "^4.1.17",
112
- typescript: "^5.9.3",
113
- vite: "^7.2.1",
114
- },
115
- };
116
- await writeFile(join(guiDir, "package.json"), JSON.stringify(packageJson, null, 2));
37
+ // Fix the @source path in index.css to point to the correct node_modules location
38
+ // In agent projects, node_modules is at the project root (4 levels up from gui/src/)
39
+ const indexCssPath = join(guiDir, "src", "index.css");
40
+ const indexCssContent = await readFile(indexCssPath, "utf-8");
41
+ const updatedContent = indexCssContent.replace('@source "../node_modules/@townco/ui/dist/**/*.{js,d.ts}";', '@source "../../../../node_modules/@townco/ui/dist/**/*.{js,d.ts}";');
42
+ await writeFile(indexCssPath, updatedContent, "utf-8");
117
43
  }
@@ -1,4 +1,4 @@
1
- import { cp, mkdir, writeFile } from "node:fs/promises";
1
+ import { cp, mkdir } from "node:fs/promises";
2
2
  import { createRequire } from "node:module";
3
3
  import { dirname, join } from "node:path";
4
4
  const require = createRequire(import.meta.url);
@@ -29,68 +29,4 @@ export async function copyTuiApp(agentPath) {
29
29
  const targetPath = join(tuiDir, item);
30
30
  await cp(sourcePath, targetPath, { recursive: true });
31
31
  }
32
- // Create a standalone tsconfig.json for the TUI
33
- const tuiTsConfig = {
34
- compilerOptions: {
35
- allowArbitraryExtensions: true,
36
- allowUnreachableCode: false,
37
- allowUnusedLabels: false,
38
- declaration: true,
39
- emitDecoratorMetadata: true,
40
- esModuleInterop: true,
41
- exactOptionalPropertyTypes: true,
42
- experimentalDecorators: true,
43
- jsx: "react-jsx",
44
- lib: ["ESNext"],
45
- module: "ESNext",
46
- moduleResolution: "bundler",
47
- noFallthroughCasesInSwitch: true,
48
- noImplicitAny: true,
49
- noImplicitOverride: true,
50
- noImplicitReturns: true,
51
- noUncheckedIndexedAccess: true,
52
- noUncheckedSideEffectImports: true,
53
- noUnusedLocals: false,
54
- noUnusedParameters: true,
55
- resolveJsonModule: true,
56
- skipLibCheck: true,
57
- strict: true,
58
- stripInternal: true,
59
- target: "ESNext",
60
- verbatimModuleSyntax: true,
61
- outDir: "./dist",
62
- rootDir: "./src",
63
- },
64
- include: ["src/**/*"],
65
- exclude: ["node_modules", "dist"],
66
- };
67
- await writeFile(join(tuiDir, "tsconfig.json"), JSON.stringify(tuiTsConfig, null, 2));
68
- // Generate a custom package.json for the TUI
69
- const packageJson = {
70
- name: "agent-tui",
71
- version: "0.0.1",
72
- type: "module",
73
- private: true,
74
- bin: {
75
- "agent-tui": "./dist/index.js",
76
- },
77
- scripts: {
78
- build: "tsc",
79
- start: "bun dist/index.js",
80
- },
81
- dependencies: {
82
- "@townco/ui": "^0.1.0",
83
- "@optique/core": "^0.6.2",
84
- "@optique/run": "^0.6.2",
85
- ink: "^6.4.0",
86
- "ink-text-input": "^6.0.0",
87
- react: "^19.2.0",
88
- },
89
- devDependencies: {
90
- "@types/node": "^24.10.0",
91
- "@types/react": "^19.2.2",
92
- typescript: "^5.9.3",
93
- },
94
- };
95
- await writeFile(join(tuiDir, "package.json"), JSON.stringify(packageJson, null, 2));
96
32
  }
@@ -5,6 +5,7 @@ export interface ScaffoldOptions {
5
5
  overwrite?: boolean;
6
6
  includeGui?: boolean;
7
7
  includeTui?: boolean;
8
+ agentsDir: string;
8
9
  }
9
10
  export interface ScaffoldResult {
10
11
  success: boolean;
@@ -15,3 +16,4 @@ export interface ScaffoldResult {
15
16
  * Scaffold a new agent package
16
17
  */
17
18
  export declare function scaffoldAgent(options: ScaffoldOptions): Promise<ScaffoldResult>;
19
+ export { scaffoldProject } from "./project-scaffold";
@@ -1,52 +1,47 @@
1
1
  import { spawn } from "node:child_process";
2
- import { chmod, mkdir, writeFile } from "node:fs/promises";
2
+ import { mkdir, stat, writeFile } from "node:fs/promises";
3
3
  import { join } from "node:path";
4
- import { agentExists, ensureAgentsDir, getAgentPath } from "../storage";
5
- import { generateAgentJson, generateBinTs, generateEnvExample, generateGitignore, generateIndexTs, generatePackageJson, generateReadme, generateTsConfig, getTemplateVars, } from "../templates";
4
+ import { generateBinTs, generateIndexTs, getTemplateVars } from "../templates";
6
5
  import { copyGuiApp } from "./copy-gui";
7
6
  import { copyTuiApp } from "./copy-tui";
8
7
  /**
9
8
  * Scaffold a new agent package
10
9
  */
11
10
  export async function scaffoldAgent(options) {
12
- const { name, definition, overwrite = false, includeGui = true, includeTui = true, } = options;
11
+ const { name, definition, overwrite = false, includeGui = true, includeTui = true, agentsDir, } = options;
13
12
  try {
14
- // Ensure base directory exists
15
- await ensureAgentsDir();
13
+ // Ensure the agents directory exists
14
+ await mkdir(agentsDir, { recursive: true });
16
15
  // Check if agent already exists
17
- const exists = await agentExists(name);
18
- if (exists && !overwrite) {
19
- return {
20
- success: false,
21
- path: getAgentPath(name),
22
- error: `Agent "${name}" already exists. Use overwrite option to replace it.`,
23
- };
16
+ const agentPath = join(agentsDir, name);
17
+ try {
18
+ const agentStat = await stat(agentPath);
19
+ if (agentStat.isDirectory() && !overwrite) {
20
+ return {
21
+ success: false,
22
+ path: agentPath,
23
+ error: `Agent "${name}" already exists. Use overwrite option to replace it.`,
24
+ };
25
+ }
26
+ }
27
+ catch {
28
+ // Agent doesn't exist, which is fine
24
29
  }
25
- const agentPath = getAgentPath(name);
26
30
  // Create the agent directory
27
31
  await mkdir(agentPath, { recursive: true });
28
32
  const vars = getTemplateVars(name, definition);
29
- // Generate all template files
33
+ // Generate template files - just the essentials
30
34
  const files = [
31
- { path: "package.json", content: generatePackageJson(vars) },
32
- { path: "agent.json", content: generateAgentJson(vars) },
33
- { path: "index.ts", content: generateIndexTs() },
35
+ { path: "index.ts", content: await generateIndexTs(vars) },
34
36
  { path: "bin.ts", content: generateBinTs(), executable: true },
35
- { path: "tsconfig.json", content: generateTsConfig() },
36
- { path: "README.md", content: generateReadme(vars) },
37
- { path: ".gitignore", content: generateGitignore() },
38
37
  ];
39
- // Add .env.example if needed
40
- const envExample = generateEnvExample(vars);
41
- if (envExample) {
42
- files.push({ path: ".env.example", content: envExample });
43
- }
44
38
  // Write all files
45
39
  for (const file of files) {
46
40
  const filePath = join(agentPath, file.path);
47
41
  await writeFile(filePath, file.content, "utf-8");
48
- // Make executable if needed
49
- if (file.executable) {
42
+ // Make executable if specified
43
+ if ("executable" in file && file.executable) {
44
+ const { chmod } = await import("node:fs/promises");
50
45
  await chmod(filePath, 0o755);
51
46
  }
52
47
  }
@@ -64,8 +59,6 @@ export async function scaffoldAgent(options) {
64
59
  const tuiPath = join(agentPath, "tui");
65
60
  await runBunInstall(tuiPath);
66
61
  }
67
- // Run bun install in agent root to fetch dependencies from npm
68
- await runBunInstall(agentPath);
69
62
  return {
70
63
  success: true,
71
64
  path: agentPath,
@@ -74,7 +67,7 @@ export async function scaffoldAgent(options) {
74
67
  catch (error) {
75
68
  return {
76
69
  success: false,
77
- path: getAgentPath(name),
70
+ path: join(agentsDir, name),
78
71
  error: error instanceof Error ? error.message : String(error),
79
72
  };
80
73
  }
@@ -101,3 +94,5 @@ function runBunInstall(agentPath) {
101
94
  });
102
95
  });
103
96
  }
97
+ // Export project scaffolding
98
+ export { scaffoldProject } from "./project-scaffold";
@@ -0,0 +1,12 @@
1
+ export interface ScaffoldProjectOptions {
2
+ path: string;
3
+ }
4
+ export interface ScaffoldProjectResult {
5
+ success: boolean;
6
+ path: string;
7
+ error?: string;
8
+ }
9
+ /**
10
+ * Scaffold a new standalone project
11
+ */
12
+ export declare function scaffoldProject(options: ScaffoldProjectOptions): Promise<ScaffoldProjectResult>;