@townco/agent 0.1.22 → 0.1.24

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 (48) 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/acp-server/http.js +2 -2
  6. package/dist/bin.js +0 -0
  7. package/dist/definition/index.d.ts +6 -0
  8. package/dist/definition/index.js +9 -0
  9. package/dist/index.js +11 -5
  10. package/dist/runner/agent-runner.d.ts +6 -0
  11. package/dist/runner/index.d.ts +1 -3
  12. package/dist/runner/index.js +14 -18
  13. package/dist/runner/langchain/index.js +35 -23
  14. package/dist/runner/langchain/tools/todo.d.ts +32 -48
  15. package/dist/runner/langchain/tools/web_search.d.ts +1 -1
  16. package/dist/runner/tools.d.ts +16 -0
  17. package/dist/runner/tools.js +10 -1
  18. package/dist/scaffold/claude-scaffold.d.ts +8 -0
  19. package/dist/scaffold/claude-scaffold.js +58 -0
  20. package/dist/scaffold/copy-gui.js +7 -81
  21. package/dist/scaffold/copy-tui.js +1 -65
  22. package/dist/scaffold/index.d.ts +3 -0
  23. package/dist/scaffold/index.js +27 -31
  24. package/dist/scaffold/project-scaffold.d.ts +12 -0
  25. package/dist/scaffold/project-scaffold.js +317 -0
  26. package/dist/scaffold/templates/dot-claude/CLAUDE-append.md +71 -0
  27. package/dist/storage/index.d.ts +5 -0
  28. package/dist/storage/index.js +60 -24
  29. package/dist/templates/index.d.ts +7 -2
  30. package/dist/templates/index.js +13 -16
  31. package/dist/tsconfig.tsbuildinfo +1 -1
  32. package/dist/utils/index.d.ts +1 -0
  33. package/dist/utils/index.js +1 -0
  34. package/dist/utils/tool.d.ts +36 -0
  35. package/dist/utils/tool.js +33 -0
  36. package/index.ts +11 -7
  37. package/package.json +7 -6
  38. package/templates/index.ts +23 -18
  39. package/dist/definition/mcp.d.ts +0 -0
  40. package/dist/definition/mcp.js +0 -0
  41. package/dist/definition/tools/todo.d.ts +0 -49
  42. package/dist/definition/tools/todo.js +0 -80
  43. package/dist/definition/tools/web_search.d.ts +0 -4
  44. package/dist/definition/tools/web_search.js +0 -26
  45. package/dist/dev-agent/index.d.ts +0 -2
  46. package/dist/dev-agent/index.js +0 -18
  47. package/dist/example.d.ts +0 -2
  48. package/dist/example.js +0 -19
@@ -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,5 @@ export interface ScaffoldResult {
15
16
  * Scaffold a new agent package
16
17
  */
17
18
  export declare function scaffoldAgent(options: ScaffoldOptions): Promise<ScaffoldResult>;
19
+ export { initForClaudeCode } from "./claude-scaffold";
20
+ 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,6 @@ function runBunInstall(agentPath) {
101
94
  });
102
95
  });
103
96
  }
97
+ export { initForClaudeCode } from "./claude-scaffold";
98
+ // Export project scaffolding
99
+ 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>;
@@ -0,0 +1,317 @@
1
+ import { spawn } from "node:child_process";
2
+ import { mkdir, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { initForClaudeCode } from "./claude-scaffold.js";
5
+ /**
6
+ * Generate project package.json
7
+ */
8
+ function generateProjectPackageJson() {
9
+ const pkg = {
10
+ name: "town-agents-project",
11
+ version: "0.1.0",
12
+ type: "module",
13
+ private: true,
14
+ scripts: {},
15
+ dependencies: {
16
+ "@agentclientprotocol/sdk": "^0.5.1",
17
+ "@optique/core": "^0.6.2",
18
+ "@optique/run": "^0.6.2",
19
+ "@radix-ui/react-dialog": "^1.1.15",
20
+ "@radix-ui/react-label": "^2.1.8",
21
+ "@radix-ui/react-select": "^2.2.6",
22
+ "@radix-ui/react-slot": "^1.2.4",
23
+ "@radix-ui/react-tabs": "^1.1.13",
24
+ "@townco/ui": "^0.1.0",
25
+ "@townco/agent": "^0.1.20",
26
+ "class-variance-authority": "^0.7.1",
27
+ clsx: "^2.1.1",
28
+ "ink-text-input": "^6.0.0",
29
+ ink: "^6.4.0",
30
+ "lucide-react": "^0.552.0",
31
+ "react-dom": "^19.2.0",
32
+ "react-markdown": "^10.1.0",
33
+ react: "^19.2.0",
34
+ "remark-gfm": "^4.0.1",
35
+ "tailwind-merge": "^3.3.1",
36
+ zod: "^4.1.12",
37
+ zustand: "^5.0.8",
38
+ },
39
+ devDependencies: {
40
+ "@tailwindcss/postcss": "^4.1.17",
41
+ "@types/react": "^19.2.2",
42
+ "@types/react-dom": "^19.2.2",
43
+ "@vitejs/plugin-react": "^5.1.0",
44
+ autoprefixer: "^10.4.21",
45
+ postcss: "^8.5.6",
46
+ tailwindcss: "^4.1.17",
47
+ typescript: "^5.9.3",
48
+ vite: "^7.2.1",
49
+ },
50
+ };
51
+ return JSON.stringify(pkg, null, 2);
52
+ }
53
+ /**
54
+ * Generate add-two-numbers tool
55
+ */
56
+ function generateAddTwoNumbersTool() {
57
+ return `// biome-ignore lint/suspicious/noExplicitAny: .
58
+ export const schema = (z: any) =>
59
+ z.object({
60
+ a: z.number().describe("First number"),
61
+ b: z.number().describe("Second number"),
62
+ });
63
+
64
+ export const name = "add_two_numbers";
65
+ export const description = "Add two numbers together and return the result";
66
+
67
+ export default function addTwoNumbers(input: unknown) {
68
+ const { a, b } = input as { a: number; b: number };
69
+ return { result: a + b };
70
+ }
71
+ `;
72
+ }
73
+ /**
74
+ * Generate project tsconfig.json
75
+ */
76
+ function generateProjectTsConfig() {
77
+ const config = {
78
+ compilerOptions: {
79
+ target: "ESNext",
80
+ module: "ESNext",
81
+ moduleResolution: "bundler",
82
+ lib: ["ESNext"],
83
+ outDir: "./dist",
84
+ strict: true,
85
+ esModuleInterop: true,
86
+ skipLibCheck: true,
87
+ forceConsistentCasingInFileNames: true,
88
+ resolveJsonModule: true,
89
+ allowSyntheticDefaultImports: true,
90
+ },
91
+ include: ["agents/**/*.ts", "tools/**/*.ts"],
92
+ exclude: ["node_modules", "dist"],
93
+ };
94
+ return JSON.stringify(config, null, 2);
95
+ }
96
+ /**
97
+ * Generate project README.md
98
+ */
99
+ function generateProjectReadme() {
100
+ return `# Town Agents Project
101
+
102
+ A standalone project for developing Town agents with custom tools.
103
+
104
+ ## Project Structure
105
+
106
+ \`\`\`
107
+ .
108
+ ├── agents/ # Agent implementations (created with 'town create')
109
+ ├── tools/ # Custom tools
110
+ │ └── add-two-numbers.ts # Example tool
111
+ ├── package.json # Project dependencies
112
+ └── tsconfig.json # TypeScript configuration
113
+ \`\`\`
114
+
115
+ ## Getting Started
116
+
117
+ 1. Install dependencies (if not already done):
118
+
119
+ \`\`\`bash
120
+ bun install
121
+ \`\`\`
122
+
123
+ 2. Create your first agent:
124
+
125
+ \`\`\`bash
126
+ town create
127
+ \`\`\`
128
+
129
+ This will launch an interactive wizard to create a new agent in the \`agents/\` directory.
130
+ The agent will include GUI and TUI interfaces for interaction.
131
+
132
+ ## Creating Agents
133
+
134
+ Run \`town create\` inside your project directory to create new agents.
135
+ Each agent will be placed in \`agents/<agent-name>/\` with the following structure:
136
+
137
+ \`\`\`
138
+ agents/my-agent/
139
+ ├── agent.json # Agent configuration
140
+ ├── index.ts # Agent entry point
141
+ ├── gui/ # Web-based GUI interface
142
+ └── tui/ # Terminal UI interface
143
+ \`\`\`
144
+
145
+ ## Running Agents
146
+
147
+ Each agent can be run in different modes:
148
+
149
+ \`\`\`bash
150
+ # CLI mode (stdio)
151
+ bun agents/<agent-name>/index.ts stdio
152
+
153
+ # HTTP server mode
154
+ PORT=3100 bun agents/<agent-name>/index.ts http
155
+ \`\`\`
156
+
157
+ ## Creating Custom Tools
158
+
159
+ Tools are defined in the \`tools/\` directory. Each tool exports:
160
+
161
+ - \`schema\`: A Zod schema factory function
162
+ - \`name\`: The tool name
163
+ - \`description\`: Tool description
164
+ - \`default\`: The tool implementation function
165
+
166
+ Example:
167
+
168
+ \`\`\`typescript
169
+ export const schema = (z: any) =>
170
+ z.object({
171
+ input: z.string(),
172
+ });
173
+
174
+ export const name = "my_tool";
175
+ export const description = "My custom tool";
176
+
177
+ export default function myTool(input: unknown) {
178
+ // Implementation
179
+ return { result: "success" };
180
+ }
181
+ \`\`\`
182
+
183
+ To use a tool in an agent, import it in the agent's \`index.ts\`:
184
+
185
+ \`\`\`typescript
186
+ import myTool, { name, description, schema } from "../../tools/my-tool.js";
187
+
188
+ const agent: AgentDefinition = {
189
+ // ...
190
+ tools: [
191
+ {
192
+ type: "direct",
193
+ name,
194
+ description,
195
+ fn: myTool,
196
+ schema: schema(z),
197
+ },
198
+ ],
199
+ };
200
+ \`\`\`
201
+ `;
202
+ }
203
+ /**
204
+ * Generate .gitignore
205
+ */
206
+ function generateProjectGitignore() {
207
+ return `node_modules
208
+ dist
209
+ .env
210
+ *.log
211
+ `;
212
+ }
213
+ /**
214
+ * Run bun install
215
+ */
216
+ function runBunInstall(projectPath) {
217
+ return new Promise((resolve, reject) => {
218
+ const bunInstall = spawn("bun", ["install"], {
219
+ cwd: projectPath,
220
+ stdio: "pipe",
221
+ });
222
+ let stdout = "";
223
+ let stderr = "";
224
+ // Capture stdout
225
+ bunInstall.stdout?.on("data", (data) => {
226
+ stdout += data.toString();
227
+ });
228
+ // Capture stderr
229
+ bunInstall.stderr?.on("data", (data) => {
230
+ stderr += data.toString();
231
+ });
232
+ bunInstall.on("close", (code) => {
233
+ if (code === 0) {
234
+ resolve();
235
+ }
236
+ else {
237
+ // Print captured output on failure
238
+ if (stdout) {
239
+ process.stdout.write(stdout);
240
+ }
241
+ if (stderr) {
242
+ process.stderr.write(stderr);
243
+ }
244
+ reject(new Error(`bun install failed with code ${code}`));
245
+ }
246
+ });
247
+ bunInstall.on("error", (error) => {
248
+ // Print captured output on error
249
+ if (stdout) {
250
+ process.stdout.write(stdout);
251
+ }
252
+ if (stderr) {
253
+ process.stderr.write(stderr);
254
+ }
255
+ reject(error);
256
+ });
257
+ });
258
+ }
259
+ /**
260
+ * Scaffold a new standalone project
261
+ */
262
+ export async function scaffoldProject(options) {
263
+ const { path: projectPath } = options;
264
+ try {
265
+ // Create project root directory
266
+ await mkdir(projectPath, { recursive: true });
267
+ // Create agents directory (empty, to be populated by 'town create')
268
+ const agentsDir = join(projectPath, "agents");
269
+ await mkdir(agentsDir, { recursive: true });
270
+ // Create tools directory
271
+ const toolsDir = join(projectPath, "tools");
272
+ await mkdir(toolsDir, { recursive: true });
273
+ // Generate and write files
274
+ const files = [
275
+ {
276
+ path: "package.json",
277
+ content: generateProjectPackageJson(),
278
+ },
279
+ {
280
+ path: "tsconfig.json",
281
+ content: generateProjectTsConfig(),
282
+ },
283
+ {
284
+ path: ".gitignore",
285
+ content: generateProjectGitignore(),
286
+ },
287
+ {
288
+ path: "README.md",
289
+ content: generateProjectReadme(),
290
+ },
291
+ {
292
+ path: join("tools", "add-two-numbers.ts"),
293
+ content: generateAddTwoNumbersTool(),
294
+ },
295
+ ];
296
+ // Write all files
297
+ for (const file of files) {
298
+ const filePath = join(projectPath, file.path);
299
+ await writeFile(filePath, file.content, "utf-8");
300
+ }
301
+ // Run bun install
302
+ await runBunInstall(projectPath);
303
+ // Initialize Claude Code workspace integration
304
+ await initForClaudeCode(projectPath);
305
+ return {
306
+ success: true,
307
+ path: projectPath,
308
+ };
309
+ }
310
+ catch (error) {
311
+ return {
312
+ success: false,
313
+ path: projectPath,
314
+ error: error instanceof Error ? error.message : String(error),
315
+ };
316
+ }
317
+ }
@@ -0,0 +1,71 @@
1
+ # Town Agent SDK
2
+ This project has code for agents developed using the Town agent SDK. The
3
+ structure of this repository is:
4
+ - `agents/`: code for main agent loop and configuration
5
+ - `tools/`: code for function-based tools that are shared by agents
6
+
7
+ ## Writing custom tools
8
+ You may add one of the built-in tools to the agent SDK (e.g. `web_search`), or
9
+ you may implement your own. To do this:
10
+ 1. add a file `tools/<tool-name>.ts` with the following structure:
11
+ ```
12
+ import * as z from 'zod';
13
+
14
+ export const schema = z.object({
15
+ a: z.number().describe("First number"),
16
+ b: z.number().describe("Second number"),
17
+ });
18
+
19
+ export const name = "add_two_numbers";
20
+ export const description = "Add two numbers together and return the result";
21
+
22
+ export default function addTwoNumbers(input: unknown) {
23
+ const { a, b } = input as { a: number; b: number };
24
+ return { result: a + b };
25
+ }
26
+ ```
27
+ where
28
+ - `schema` is a zod schema for the tool input
29
+ - `name` is the name of the tool (given to the LLM)
30
+ - `description` describes the tool (given to the LLM)
31
+ - default-exported function containing the body of the function
32
+
33
+ 2. Modify the agent code to import the `createTool` utility and the new tool
34
+ ```
35
+ import { createTool } from '@townco/agent/utils';
36
+ import * as newCoolTool from '../../tools/new-cool-tool';
37
+ ```
38
+ and modify the agent's tools list
39
+ ```
40
+ const agent: AgentDefinition = {
41
+ model: "claude-sonnet-4-5-20250929",
42
+ systemPrompt: "You are a helpful assistant.\n",
43
+ tools: ["todo_write", "web_search", createTool(newCoolTool)],
44
+ };
45
+ ```
46
+
47
+ ## Configuring MCPs
48
+ You may configure stdio and streamable HTTP-based MCPs. Here is an example:
49
+ ```
50
+ const agent: AgentDefinition = {
51
+ model: "claude-sonnet-4-5-20250929",
52
+ systemPrompt: "You are a helpful assistant.\n",
53
+ tools: ["todo_write", "web_search"],
54
+ mcps: [
55
+ {
56
+ transport: "http",
57
+ name: "foobar",
58
+ url: "http://town.com:8080/mcp",
59
+ headers: {
60
+ 'Authorization': `Bearer ${process.env.MCP_API_KEY}`
61
+ },
62
+ },
63
+ {
64
+ transport: 'stdio',
65
+ name: 'local-mcp',
66
+ command: 'node',
67
+ args: ['path/to/local/mcp.js'],
68
+ },
69
+ ],
70
+ };
71
+ ```
@@ -22,3 +22,8 @@ export declare function deleteAgent(name: string): Promise<void>;
22
22
  * Ensure the agents directory exists
23
23
  */
24
24
  export declare function ensureAgentsDir(): Promise<void>;
25
+ /**
26
+ * Check if we're inside a Town project by looking for project markers
27
+ * Returns the project root path if found, null otherwise
28
+ */
29
+ export declare function isInsideTownProject(startPath?: string): Promise<string | null>;