@symplek/cli 0.0.0-stage → 0.2.0-beta.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.
Files changed (75) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/LICENSE +21 -0
  3. package/README.md +74 -2
  4. package/bin/symplek.js +5 -0
  5. package/dist/cli.d.ts +3 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +44 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/commands/add.d.ts +36 -0
  10. package/dist/commands/add.d.ts.map +1 -0
  11. package/dist/commands/add.js +467 -0
  12. package/dist/commands/add.js.map +1 -0
  13. package/dist/commands/generate.d.ts +26 -0
  14. package/dist/commands/generate.d.ts.map +1 -0
  15. package/dist/commands/generate.js +491 -0
  16. package/dist/commands/generate.js.map +1 -0
  17. package/dist/commands/init.d.ts +46 -0
  18. package/dist/commands/init.d.ts.map +1 -0
  19. package/dist/commands/init.js +350 -0
  20. package/dist/commands/init.js.map +1 -0
  21. package/dist/commands/migrate.d.ts +41 -0
  22. package/dist/commands/migrate.d.ts.map +1 -0
  23. package/dist/commands/migrate.js +518 -0
  24. package/dist/commands/migrate.js.map +1 -0
  25. package/dist/commands/validate.d.ts +252 -0
  26. package/dist/commands/validate.d.ts.map +1 -0
  27. package/dist/commands/validate.js +515 -0
  28. package/dist/commands/validate.js.map +1 -0
  29. package/dist/index.d.ts +7 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +46 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/models.d.ts +24 -0
  34. package/dist/models.d.ts.map +1 -0
  35. package/dist/models.js +39 -0
  36. package/dist/models.js.map +1 -0
  37. package/dist/naming.d.ts +20 -0
  38. package/dist/naming.d.ts.map +1 -0
  39. package/dist/naming.js +48 -0
  40. package/dist/naming.js.map +1 -0
  41. package/dist/templates/agent/agent.ts.template +41 -0
  42. package/dist/templates/agent/general.yaml.template +34 -0
  43. package/dist/templates/agent/researcher.yaml.template +42 -0
  44. package/dist/templates/agent/reviewer.yaml.template +51 -0
  45. package/dist/templates/project/full/.github/workflows/ci.yml.template +34 -0
  46. package/dist/templates/project/full/.gitignore.template +6 -0
  47. package/dist/templates/project/full/Dockerfile.template +43 -0
  48. package/dist/templates/project/full/README.md.template +77 -0
  49. package/dist/templates/project/full/agents/example.yaml.template +16 -0
  50. package/dist/templates/project/full/package.json.template +30 -0
  51. package/dist/templates/project/full/src/index.ts.template +43 -0
  52. package/dist/templates/project/full/src/symplek.config.ts.template +20 -0
  53. package/dist/templates/project/full/src/tools/index.ts.template +73 -0
  54. package/dist/templates/project/full/tests/example.test.ts.template +49 -0
  55. package/dist/templates/project/full/tsconfig.json.template +18 -0
  56. package/dist/templates/project/minimal/.gitignore.template +6 -0
  57. package/dist/templates/project/minimal/agents/example.yaml.template +16 -0
  58. package/dist/templates/project/minimal/package.json.template +27 -0
  59. package/dist/templates/project/minimal/src/index.ts.template +43 -0
  60. package/dist/templates/project/minimal/src/symplek.config.ts.template +20 -0
  61. package/dist/templates/project/minimal/tsconfig.json.template +18 -0
  62. package/dist/templates/project/standard/.gitignore.template +6 -0
  63. package/dist/templates/project/standard/README.md.template +77 -0
  64. package/dist/templates/project/standard/agents/example.yaml.template +16 -0
  65. package/dist/templates/project/standard/package.json.template +30 -0
  66. package/dist/templates/project/standard/src/index.ts.template +43 -0
  67. package/dist/templates/project/standard/src/symplek.config.ts.template +20 -0
  68. package/dist/templates/project/standard/src/tools/index.ts.template +73 -0
  69. package/dist/templates/project/standard/tests/example.test.ts.template +49 -0
  70. package/dist/templates/project/standard/tsconfig.json.template +18 -0
  71. package/dist/utils/templates.d.ts +41 -0
  72. package/dist/utils/templates.d.ts.map +1 -0
  73. package/dist/utils/templates.js +133 -0
  74. package/dist/utils/templates.js.map +1 -0
  75. package/package.json +60 -3
package/dist/models.js ADDED
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ /**
3
+ * The one place the CLI knows about model ids.
4
+ *
5
+ * Providers add and retire models far more often than this CLI is released,
6
+ * so the CLI does not decide which models exist. Any well-formed id passes
7
+ * `validate` and `generate`; the provider is the authority on whether it is
8
+ * live. The list below is only what `symplek add agent` offers in its picker.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.MODEL_ID_HINT = exports.MODEL_ID_PATTERN = exports.DEFAULT_MODEL = exports.SUGGESTED_MODELS = void 0;
12
+ exports.isModelId = isModelId;
13
+ /** Offered by the interactive picker. Mirrors @symplek/core's default model table. */
14
+ exports.SUGGESTED_MODELS = [
15
+ "gpt-4o",
16
+ "gpt-4o-mini",
17
+ "claude-opus-5-5",
18
+ "claude-sonnet-5-5",
19
+ "claude-haiku-5-5",
20
+ "gemini-3.8-flash",
21
+ "gemini-3.5-flash-lite",
22
+ ];
23
+ /** Used when the caller names no model. */
24
+ exports.DEFAULT_MODEL = exports.SUGGESTED_MODELS[0];
25
+ /**
26
+ * The shape of a model id: letters, digits, and `. _ : / -`, starting with a
27
+ * letter or digit. Wide enough for dated snapshots, fine-tune ids and
28
+ * `vendor/model` routes; narrow enough that an id can be written into YAML or
29
+ * TypeScript without quoting hazards (no whitespace, quotes or newlines).
30
+ */
31
+ exports.MODEL_ID_PATTERN = "^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$";
32
+ const MODEL_ID_REGEX = new RegExp(exports.MODEL_ID_PATTERN);
33
+ /** True when `value` is a well-formed model id. */
34
+ function isModelId(value) {
35
+ return typeof value === "string" && MODEL_ID_REGEX.test(value);
36
+ }
37
+ /** One-line hint shown when a model id is missing or malformed. */
38
+ exports.MODEL_ID_HINT = `Use a provider model id, for example: ${exports.SUGGESTED_MODELS.join(", ")}`;
39
+ //# sourceMappingURL=models.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AA2BH,8BAEC;AA3BD,sFAAsF;AACzE,QAAA,gBAAgB,GAAG;IAC9B,QAAQ;IACR,aAAa;IACb,iBAAiB;IACjB,mBAAmB;IACnB,kBAAkB;IAClB,kBAAkB;IAClB,uBAAuB;CACf,CAAC;AAEX,2CAA2C;AAC9B,QAAA,aAAa,GAAW,wBAAgB,CAAC,CAAC,CAAC,CAAC;AAEzD;;;;;GAKG;AACU,QAAA,gBAAgB,GAAG,sCAAsC,CAAC;AAEvE,MAAM,cAAc,GAAG,IAAI,MAAM,CAAC,wBAAgB,CAAC,CAAC;AAEpD,mDAAmD;AACnD,SAAgB,SAAS,CAAC,KAAc;IACtC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACjE,CAAC;AAED,mEAAmE;AACtD,QAAA,aAAa,GAAG,yCAAyC,wBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The one place the CLI turns an agent's name into a JavaScript identifier.
3
+ *
4
+ * An agent is named in kebab-case (`code-reviewer`) and exported from its
5
+ * generated file in camelCase (`codeReviewer`). `add`, `validate` and
6
+ * `generate` all use the rules here, so a name one of them accepts is a name
7
+ * the others can turn into a file that parses.
8
+ */
9
+ /** Lowercase letters and digits in hyphen-separated segments, starting with a letter. */
10
+ export declare const KEBAB_CASE_REGEX: RegExp;
11
+ /**
12
+ * `code-reviewer` to `codeReviewer`. A segment may start with a digit
13
+ * (`agent-2` to `agent2`), so the hyphen is dropped before digits too.
14
+ */
15
+ export declare function kebabToCamel(name: string): string;
16
+ /** True when `word` cannot be used as an exported function's name. */
17
+ export declare function isReservedWord(word: string): boolean;
18
+ /** Why `name` cannot be an agent name, or undefined when it can. */
19
+ export declare function agentNameProblem(name: string): string | undefined;
20
+ //# sourceMappingURL=naming.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"naming.d.ts","sourceRoot":"","sources":["../src/naming.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,yFAAyF;AACzF,eAAO,MAAM,gBAAgB,QAAkC,CAAC;AAahE;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,oEAAoE;AACpE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQjE"}
package/dist/naming.js ADDED
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ /**
3
+ * The one place the CLI turns an agent's name into a JavaScript identifier.
4
+ *
5
+ * An agent is named in kebab-case (`code-reviewer`) and exported from its
6
+ * generated file in camelCase (`codeReviewer`). `add`, `validate` and
7
+ * `generate` all use the rules here, so a name one of them accepts is a name
8
+ * the others can turn into a file that parses.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.KEBAB_CASE_REGEX = void 0;
12
+ exports.kebabToCamel = kebabToCamel;
13
+ exports.isReservedWord = isReservedWord;
14
+ exports.agentNameProblem = agentNameProblem;
15
+ /** Lowercase letters and digits in hyphen-separated segments, starting with a letter. */
16
+ exports.KEBAB_CASE_REGEX = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
17
+ /** Words that cannot be the name of an exported function. */
18
+ const RESERVED_WORDS = new Set([
19
+ "arguments", "await", "break", "case", "catch", "class", "const", "continue",
20
+ "debugger", "default", "delete", "do", "else", "enum", "eval", "export",
21
+ "extends", "false", "finally", "for", "function", "if", "implements",
22
+ "import", "in", "instanceof", "interface", "let", "new", "null", "package",
23
+ "private", "protected", "public", "return", "static", "super", "switch",
24
+ "this", "throw", "true", "try", "typeof", "var", "void", "while", "with",
25
+ "yield",
26
+ ]);
27
+ /**
28
+ * `code-reviewer` to `codeReviewer`. A segment may start with a digit
29
+ * (`agent-2` to `agent2`), so the hyphen is dropped before digits too.
30
+ */
31
+ function kebabToCamel(name) {
32
+ return name.replace(/-([a-z0-9])/g, (_, char) => char.toUpperCase());
33
+ }
34
+ /** True when `word` cannot be used as an exported function's name. */
35
+ function isReservedWord(word) {
36
+ return RESERVED_WORDS.has(word);
37
+ }
38
+ /** Why `name` cannot be an agent name, or undefined when it can. */
39
+ function agentNameProblem(name) {
40
+ if (!exports.KEBAB_CASE_REGEX.test(name)) {
41
+ return "Agent name must be kebab-case: lowercase letters, numbers and hyphens, starting with a letter (e.g. my-agent)";
42
+ }
43
+ if (isReservedWord(kebabToCamel(name))) {
44
+ return `"${name}" is a JavaScript reserved word, so it cannot name the exported agent function. Pick another name (e.g. ${name}-agent)`;
45
+ }
46
+ return undefined;
47
+ }
48
+ //# sourceMappingURL=naming.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"naming.js","sourceRoot":"","sources":["../src/naming.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAoBH,oCAEC;AAGD,wCAEC;AAGD,4CAQC;AApCD,yFAAyF;AAC5E,QAAA,gBAAgB,GAAG,+BAA+B,CAAC;AAEhE,6DAA6D;AAC7D,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;IAC7B,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU;IAC5E,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ;IACvE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,YAAY;IACpE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS;IAC1E,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ;IACvE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IACxE,OAAO;CACR,CAAC,CAAC;AAEH;;;GAGG;AACH,SAAgB,YAAY,CAAC,IAAY;IACvC,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC,EAAE,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;AAC/E,CAAC;AAED,sEAAsE;AACtE,SAAgB,cAAc,CAAC,IAAY;IACzC,OAAO,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAClC,CAAC;AAED,oEAAoE;AACpE,SAAgB,gBAAgB,CAAC,IAAY;IAC3C,IAAI,CAAC,wBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,OAAO,+GAA+G,CAAC;IACzH,CAAC;IACD,IAAI,cAAc,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QACvC,OAAO,IAAI,IAAI,2GAA2G,IAAI,SAAS,CAAC;IAC1I,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * {{name}} Agent
3
+ * TypeScript agent definition
4
+ * Created with: symplek add agent {{name}}
5
+ *
6
+ * Before using this agent, set a provider key in the environment (for
7
+ * example OPENAI_API_KEY), or call
8
+ * Symplek.configure({ provider, apiKey }) from @symplek/core/server.
9
+ */
10
+ import { AgentUnit, createContext } from "@symplek/core";
11
+ import { ai } from "@symplek/core/server";
12
+
13
+ /**
14
+ * Run the {{name}} agent with a prompt.
15
+ * Returns an AgentUnit<string> — chain operators (.retry, .timeout, …) before calling .run().
16
+ */
17
+ export function {{agentName}}(prompt: string): AgentUnit<string> {
18
+ return AgentUnit.from((_ctx) =>
19
+ ai(prompt, {
20
+ model: "{{model}}",
21
+ system: `You are {{name}}, a helpful AI assistant.
22
+
23
+ Your responsibilities:
24
+ - Answer questions clearly and accurately
25
+ - Provide helpful explanations
26
+
27
+ Be concise, accurate, and friendly.`,
28
+ }),
29
+ );
30
+ }
31
+
32
+ export type AgentResult = Awaited<ReturnType<ReturnType<typeof {{agentName}}>["run"]>>;
33
+
34
+ /**
35
+ * Convenience helper: run the agent and return its string value.
36
+ */
37
+ export async function run(prompt: string): Promise<string> {
38
+ const ctx = createContext();
39
+ const result = await {{agentName}}(prompt).run(ctx);
40
+ return result.value as string;
41
+ }
@@ -0,0 +1,34 @@
1
+ # General conversational agent
2
+ # Created with: symplek add agent {{name}} --type general
3
+
4
+ name: {{name}}
5
+ version: "1.0.0"
6
+
7
+ model: "{{model}}"
8
+
9
+ systemPrompt: |
10
+ You are {{name}}, a helpful AI assistant.
11
+
12
+ Your responsibilities:
13
+ - Answer questions clearly and accurately
14
+ - Provide helpful explanations
15
+ - Admit when you don't know something
16
+ - Be friendly and professional
17
+
18
+ Guidelines:
19
+ - Be concise but thorough
20
+ - Ask clarifying questions when needed
21
+ - Provide examples when helpful
22
+
23
+ temperature: 0.7
24
+ maxTokens: 2048
25
+
26
+ # Optional: enable tools when you have defined them in your project.
27
+ # tools:
28
+ # - search
29
+ # - calculator
30
+
31
+ # Optional: episodic memory — store recent turns for context.
32
+ # memory:
33
+ # episodic: true
34
+ # maxEpisodes: 10
@@ -0,0 +1,42 @@
1
+ # Research-focused agent
2
+ # Created with: symplek add agent {{name}} --type researcher
3
+
4
+ name: {{name}}
5
+ version: "1.0.0"
6
+
7
+ model: "{{model}}"
8
+
9
+ systemPrompt: |
10
+ You are {{name}}, an expert research assistant.
11
+
12
+ Your responsibilities:
13
+ - Conduct thorough research on given topics
14
+ - Synthesize information from multiple sources
15
+ - Provide well-structured summaries
16
+ - Cite sources when available
17
+ - Identify knowledge gaps
18
+
19
+ Research methodology:
20
+ 1. Understand the research question
21
+ 2. Break down into sub-questions
22
+ 3. Gather relevant information
23
+ 4. Analyze and synthesize findings
24
+ 5. Present conclusions with evidence
25
+
26
+ Output format:
27
+ - Start with key findings
28
+ - Provide supporting details
29
+ - Note limitations and uncertainties
30
+ - Suggest areas for further research
31
+
32
+ temperature: 0.5
33
+ maxTokens: 4096
34
+
35
+ tools:
36
+ - search
37
+ - fetch_url
38
+
39
+ # Optional: episodic memory to track research threads across turns.
40
+ # memory:
41
+ # episodic: true
42
+ # maxEpisodes: 20
@@ -0,0 +1,51 @@
1
+ # Code review agent
2
+ # Created with: symplek add agent {{name}} --type reviewer
3
+
4
+ name: {{name}}
5
+ version: "1.0.0"
6
+
7
+ model: "{{model}}"
8
+
9
+ systemPrompt: |
10
+ You are {{name}}, an expert code reviewer.
11
+
12
+ Your responsibilities:
13
+ - Review code for correctness, style, and best practices
14
+ - Identify bugs, security issues, and performance problems
15
+ - Suggest improvements and alternatives
16
+ - Explain reasoning behind suggestions
17
+ - Be constructive and educational
18
+
19
+ Review priorities (in order):
20
+ 1. Security vulnerabilities
21
+ 2. Correctness bugs
22
+ 3. Performance issues
23
+ 4. Code style and readability
24
+ 5. Best practices and patterns
25
+ 6. Documentation
26
+
27
+ Guidelines:
28
+ - Be specific with line numbers and code examples
29
+ - Explain WHY something is an issue
30
+ - Provide concrete fix suggestions
31
+ - Acknowledge good code when you see it
32
+ - Consider context and trade-offs
33
+
34
+ Severity levels:
35
+ - CRITICAL: Security vulnerabilities, data loss risks
36
+ - HIGH: Bugs that will cause failures
37
+ - MEDIUM: Performance issues, poor patterns
38
+ - LOW: Style issues, minor improvements
39
+ - SUGGESTION: Nice-to-have improvements
40
+
41
+ temperature: 0.3
42
+ maxTokens: 4096
43
+
44
+ tools:
45
+ - read_file
46
+ - search_code
47
+
48
+ # Optional: episodic memory to track ongoing review context.
49
+ # memory:
50
+ # episodic: true
51
+ # maxEpisodes: 15
@@ -0,0 +1,34 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - uses: pnpm/action-setup@v4
16
+ with:
17
+ version: 9
18
+
19
+ - uses: actions/setup-node@v4
20
+ with:
21
+ node-version: "20"
22
+ cache: pnpm
23
+
24
+ - name: Install dependencies
25
+ run: pnpm install --frozen-lockfile
26
+
27
+ - name: Typecheck
28
+ run: pnpm typecheck
29
+
30
+ - name: Test
31
+ run: pnpm test
32
+
33
+ - name: Build
34
+ run: pnpm build
@@ -0,0 +1,6 @@
1
+ node_modules/
2
+ dist/
3
+ .env
4
+ .env.*
5
+ !.env.example
6
+ *.log
@@ -0,0 +1,43 @@
1
+ # {{name}} — Symplek agent runtime image.
2
+ #
3
+ # Multi-stage build:
4
+ # 1. Install all deps + compile TypeScript to dist/.
5
+ # 2. Ship a slim runtime image with only production deps + dist/.
6
+ #
7
+ # Build: docker build -t {{name}} .
8
+ # Run: docker run --rm -e OPENAI_API_KEY=$OPENAI_API_KEY {{name}}
9
+
10
+ # ─── Stage 1: build ──────────────────────────────────────────────────────────
11
+ FROM node:20-alpine AS build
12
+ WORKDIR /app
13
+ RUN corepack enable
14
+
15
+ # Copy lockfile if present (won't fail if it's missing).
16
+ COPY package.json ./
17
+ COPY pnpm-lock.yaml* ./
18
+
19
+ RUN pnpm install
20
+
21
+ COPY tsconfig.json ./
22
+ COPY src ./src
23
+ COPY agents ./agents
24
+
25
+ RUN pnpm build
26
+
27
+ # ─── Stage 2: runtime ────────────────────────────────────────────────────────
28
+ FROM node:20-alpine
29
+ WORKDIR /app
30
+ RUN corepack enable && adduser -D -u 1001 symplek
31
+
32
+ COPY package.json ./
33
+ COPY pnpm-lock.yaml* ./
34
+
35
+ RUN pnpm install --prod
36
+
37
+ COPY --from=build /app/dist ./dist
38
+ COPY --from=build /app/agents ./agents
39
+
40
+ USER symplek
41
+ ENV NODE_ENV=production
42
+
43
+ CMD ["node", "dist/src/index.js"]
@@ -0,0 +1,77 @@
1
+ # {{name}}
2
+
3
+ {{description}}
4
+
5
+ Built with [Symplek](https://github.com/symplek/symplek) — composable AI agents for TypeScript.
6
+
7
+ ---
8
+
9
+ ## Setup
10
+
11
+ ```bash
12
+ pnpm install # or npm install / yarn / bun
13
+ export OPENAI_API_KEY=sk-... # provider env var from src/symplek.config.ts
14
+ pnpm dev # watch mode
15
+ pnpm start # one-shot
16
+ ```
17
+
18
+ To switch providers, edit `src/symplek.config.ts` and install the matching SDK
19
+ (`pnpm add @anthropic-ai/sdk` for Anthropic, etc.). Symplek auto-detects which
20
+ provider is configured at runtime.
21
+
22
+ ---
23
+
24
+ ## Project layout
25
+
26
+ ```
27
+ {{name}}/
28
+ ├── src/
29
+ │ ├── index.ts # entry — runs the example agent
30
+ │ ├── symplek.config.ts # Symplek runtime configuration (provider, API key)
31
+ │ └── tools/index.ts # composable helpers (resilient, withProvenance)
32
+ ├── agents/
33
+ │ └── {{agentName}}.yaml # declarative agent — `symplek generate` compiles it
34
+ └── tests/
35
+ └── example.test.ts # vitest unit tests
36
+ ```
37
+
38
+ ---
39
+
40
+ ## What's where
41
+
42
+ - **`src/symplek.config.ts`** — registers your AI provider. Edit here to swap
43
+ OpenAI for Anthropic, change the default model, set a base URL, etc.
44
+ - **`src/tools/index.ts`** — your project's local helpers. Compose `AgentUnit`
45
+ operators into project-specific patterns here so the rest of your code stays
46
+ readable.
47
+ - **`agents/`** — YAML agent definitions. Run `symplek generate` to compile them
48
+ into typed TypeScript wrappers next to them, as `agents/<name>.ts`.
49
+
50
+ ---
51
+
52
+ ## Scripts
53
+
54
+ | Command | What it does |
55
+ |---|---|
56
+ | `pnpm dev` | Run the entry in watch mode via `tsx` |
57
+ | `pnpm start` | Run once |
58
+ | `pnpm build` | Compile TypeScript to `dist/` |
59
+ | `pnpm typecheck` | TypeScript check without emit |
60
+ | `pnpm test` | Run vitest once |
61
+ | `pnpm test:watch` | Run vitest in watch mode |
62
+
63
+ ---
64
+
65
+ ## Next steps
66
+
67
+ - Replace the example in `src/index.ts` with your real agent.
68
+ - Edit `agents/{{agentName}}.yaml`, then run `symplek generate` to add typed wrappers.
69
+ - Add more agents in `agents/`; the CLI handles wiring.
70
+ - See [github.com/symplek/symplek](https://github.com/symplek/symplek) for the operator
71
+ reference (consensus, debate, dialectic, rehearse, replay, supervised, etc.).
72
+
73
+ ---
74
+
75
+ ## License
76
+
77
+ ISC
@@ -0,0 +1,16 @@
1
+ # Symplek agent definition — declarative form.
2
+ #
3
+ # Compile to TypeScript with:
4
+ # pnpm exec symplek generate
5
+ #
6
+ # This produces `agents/{{agentName}}.ts` that you can import and call
7
+ # from your application code. YAML lets non-engineers tune prompts without
8
+ # touching the source.
9
+
10
+ name: {{agentName}}
11
+ description: Summarises text in one clear sentence.
12
+ model: "{{model}}"
13
+
14
+ systemPrompt: |
15
+ You summarise the user's input in exactly one clear sentence.
16
+ Never embellish. Never editorialise. Faithful to the input only.
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "{{name}}",
3
+ "version": "{{version}}",
4
+ "description": "{{description}}",
5
+ "type": "module",
6
+ "private": true,
7
+ "scripts": {
8
+ "dev": "tsx watch src/index.ts",
9
+ "start": "tsx src/index.ts",
10
+ "build": "tsc",
11
+ "typecheck": "tsc --noEmit",
12
+ "test": "vitest run",
13
+ "test:watch": "vitest"
14
+ },
15
+ "dependencies": {
16
+ "@symplek/core": "^1.0.0-beta.1",
17
+ "zod": "^3.25.76",
18
+ "openai": "^4.104.0"
19
+ },
20
+ "devDependencies": {
21
+ "@symplek/cli": "^{{cliVersion}}",
22
+ "@types/node": "^22.14.0",
23
+ "tsx": "^4.19.0",
24
+ "typescript": "^5.9.0",
25
+ "vitest": "^2.1.0"
26
+ },
27
+ "engines": {
28
+ "node": ">=20.19"
29
+ }
30
+ }
@@ -0,0 +1,43 @@
1
+ import "./symplek.config.js";
2
+ import { createContext } from "@symplek/core";
3
+ import { Symplek } from "@symplek/core/server";
4
+
5
+ /**
6
+ * {{name}} — a minimal Symplek agent.
7
+ *
8
+ * Demonstrates the core pattern:
9
+ * 1. `Symplek(prompt)` builds an AI call.
10
+ * 2. `.coordinate()` lifts it into the AgentUnit<T> algebra.
11
+ * 3. Layer safety operators on top (retry, timeout, fallback, …).
12
+ * 4. `.run(ctx)` executes and returns a TrackedResult.
13
+ *
14
+ * Scripts:
15
+ * pnpm dev — watch mode (re-runs on file change)
16
+ * pnpm start — one-shot
17
+ */
18
+
19
+ if (!process.env.OPENAI_API_KEY) {
20
+ console.error("[{{name}}] OPENAI_API_KEY is not set.");
21
+ console.error("Set it and re-run: export OPENAI_API_KEY=sk-...");
22
+ process.exit(1);
23
+ }
24
+
25
+ async function main(): Promise<void> {
26
+ const ctx = createContext();
27
+ const input = "The quick brown fox jumps over the lazy dog.";
28
+
29
+ const result = await Symplek(`Summarise in one sentence: ${input}`)
30
+ .coordinate()
31
+ .retry(2)
32
+ .timeout(15_000)
33
+ .run(ctx);
34
+
35
+ console.log("Input: ", input);
36
+ console.log("Summary: ", result.value);
37
+ console.log("Confidence: ", result.confidence);
38
+ }
39
+
40
+ main().catch((err: unknown) => {
41
+ console.error("[{{name}}] agent failed:", err);
42
+ process.exit(1);
43
+ });
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Symplek runtime configuration for {{name}}.
3
+ *
4
+ * Imported for side effect by `src/index.ts` before any agent runs.
5
+ * Set OPENAI_API_KEY in your environment:
6
+ *
7
+ * export OPENAI_API_KEY=sk-...
8
+ *
9
+ * To swap providers, change `provider` and the matching env var. Symplek
10
+ * supports `openai`, `anthropic`, and `google` out of the box — install
11
+ * the corresponding SDK (`pnpm add @anthropic-ai/sdk` etc.) and update
12
+ * this file.
13
+ */
14
+ import { Symplek } from "@symplek/core/server";
15
+
16
+ Symplek.configure({
17
+ provider: "openai",
18
+ apiKey: process.env.OPENAI_API_KEY ?? "",
19
+ model: "{{model}}",
20
+ });
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Project-local helpers for {{name}}.
3
+ *
4
+ * Compose Symplek operators into patterns specific to your project here so
5
+ * your business code stays declarative. Each helper is one operator chain.
6
+ */
7
+ import {
8
+ type AgentUnit,
9
+ isTracked,
10
+ type ResultSource,
11
+ type TrackedResult,
12
+ } from "@symplek/core";
13
+
14
+ export interface ProvenanceOptions {
15
+ /** Source category — one of Symplek's `ResultSource` literals. */
16
+ source: ResultSource;
17
+ /** Caller's confidence in this value, in [0, 1]. */
18
+ confidence: number;
19
+ /** Optional identifier of the verifier (audit trail). */
20
+ verifiedBy?: string;
21
+ }
22
+
23
+ /**
24
+ * Wrap an `AgentUnit<T>` so its output carries provenance metadata.
25
+ *
26
+ * Downstream callers can introspect `result.value`, `result.source`,
27
+ * `result.confidence` — Symplek's TrackedResult shape — without any extra
28
+ * plumbing.
29
+ */
30
+ export function withProvenance<T>(
31
+ unit: AgentUnit<T>,
32
+ opts: ProvenanceOptions,
33
+ ): AgentUnit<TrackedResult<T>> {
34
+ return unit.tracked({
35
+ source: opts.source,
36
+ confidence: opts.confidence,
37
+ verifiedBy: opts.verifiedBy,
38
+ });
39
+ }
40
+
41
+ export interface ResilienceOptions<T> {
42
+ /** Max attempts including the first. Default 3. */
43
+ maxAttempts?: number;
44
+ /** Hard timeout in milliseconds. Default 15s. */
45
+ timeoutMs?: number;
46
+ /** Static fallback value if all attempts fail. */
47
+ fallback?: T;
48
+ }
49
+
50
+ /**
51
+ * Apply the standard resilience triad: retry on transient failure, hard
52
+ * timeout, fallback to a known-safe value if all else fails.
53
+ *
54
+ * @example
55
+ * const safe = resilient(
56
+ * Symplek(prompt).coordinate(),
57
+ * { maxAttempts: 3, timeoutMs: 10_000, fallback: "" },
58
+ * );
59
+ */
60
+ export function resilient<T>(
61
+ unit: AgentUnit<T>,
62
+ options: ResilienceOptions<T> = {},
63
+ ): AgentUnit<T> {
64
+ const { maxAttempts = 3, timeoutMs = 15_000, fallback } = options;
65
+ let chain = unit.retry(maxAttempts).timeout(timeoutMs);
66
+ if (fallback !== undefined) {
67
+ chain = chain.orElse(fallback);
68
+ }
69
+ return chain;
70
+ }
71
+
72
+ /** Re-export Symplek's tracked-result type guard for convenience. */
73
+ export { isTracked };