@positronic/template-new-project 0.0.2

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.
@@ -0,0 +1,8 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(npm test)"
5
+ ],
6
+ "deny": []
7
+ }
8
+ }
package/CLAUDE.md ADDED
@@ -0,0 +1,215 @@
1
+ # Maintainer's Guide to the Positronic Project Template & Caz
2
+
3
+ This document serves as the complete guide for maintainers of the `@positronic/template-new-project`. It provides a deep dive into the structure of this template, its configuration, and the underlying scaffolding engine, `caz`, that powers it.
4
+
5
+ This guide assumes no prior knowledge of the `caz` codebase.
6
+
7
+ ## Part 1: The `@positronic/template-new-project`
8
+
9
+ This project is a `caz` template. Its sole purpose is to generate new Positronic projects by running a command like `npx @positronic/template-new-project <new-project-name>`.
10
+
11
+ ### The Anatomy of the Template
12
+
13
+ The template is controlled by its `index.js` file, which defines how `caz` should behave. The actual files to be generated live inside the `template/` directory.
14
+
15
+ #### Key Files
16
+
17
+ * **`index.js`**: The brain of the template. This is a configuration file that tells `caz` what questions to ask, what logic to run before generating files, and how to prepare the files.
18
+ * **`package.json`**: This defines the template itself, including its dependencies like `caz`. Note that the dependencies here are for the *template*, not the *generated project*.
19
+ * **`template/`**: This directory contains the skeleton of the project that will be created. Files and filenames here can contain template variables.
20
+
21
+ #### The Generation Process at a Glance
22
+
23
+ When a user runs this template, `caz` performs the following high-level steps:
24
+
25
+ 1. **Prompts**: Asks the user a series of questions defined in `index.js`.
26
+ 2. **Setup**: Runs custom logic to process the user's answers and prepare dynamic variables.
27
+ 3. **Prepare**: Prepares the list of files to be generated, renaming special files like `_gitignore`.
28
+ 4. **Render & Emit**: Copies files from the `template/` directory, injecting variables and processing logic.
29
+ 5. **Install & Initialize**: (Optionally) installs dependencies and initializes a Git repository in the new project.
30
+
31
+ ### In-Depth Configuration (`index.js`)
32
+
33
+ The `index.js` file exports a configuration object that `caz` uses to drive the entire process. Let's break down its key sections.
34
+
35
+ #### **1. Prompts (`prompts` array)**
36
+
37
+ This array defines the interactive questions `caz` will ask the user.
38
+
39
+ * **`name`**: The name of the new project. Defaults to `my-positronic-project`.
40
+ * **`backend`**: A selection list for the deployment backend. This is critical as it drives which dependencies and configurations are included. Currently, only 'Cloudflare' is enabled.
41
+ * **`install`**: A confirmation to ask whether dependencies should be installed automatically.
42
+ * **`pm`**: A selection list for the package manager (npm, pnpm, yarn), which only appears if the user opts to install dependencies.
43
+
44
+ #### **2. Pre-Generation Logic (`setup` hook)**
45
+
46
+ This asynchronous function runs after the user has answered the prompts but before any files are generated. It's used for complex, dynamic configuration.
47
+
48
+ * **Local Development with `POSITRONIC_LOCAL_PATH`**: This is a crucial feature for maintainers. If the `POSITRONIC_LOCAL_PATH` environment variable is set (pointing to the root of a local monorepo), the template will map the `@positronic/*` dependencies to local `file:` paths instead of fetching them from `npm`. This allows for simultaneous development and testing of the core libraries and the template.
49
+ * **Backend Package Mapping**: It maps the user's `backend` choice (e.g., 'cloudflare') to a specific npm package name (e.g., `@positronic/cloudflare`).
50
+ * **Dynamic Versioning**: It sets the versions for `@positronic/core`, `@positronic/cloudflare`, and `@positronic/client-vercel` to either `'latest'` or a local `file:` path.
51
+ * **Installation Control**: It sets `ctx.config.install` to the chosen package manager or `false`, telling `caz` whether to run an installation step later.
52
+ * **Context Augmentation**: It adds new properties to `ctx.answers` (like `projectName`, `positronicCoreVersion`, etc.) which can then be used as variables in the template files.
53
+
54
+ #### **3. File Preparation (`prepare` hook)**
55
+
56
+ This function runs after `caz` has gathered all the template files but before they are rendered and written to disk. It allows for final manipulation of the file list.
57
+
58
+ * **Renaming System Files**: It renames `_gitignore` to `.gitignore` and `_env` to `.env`. This is a common pattern to prevent the template's own `.gitignore` or `.env` from affecting the template repository itself.
59
+ * **Marking Files as Binary**: It iterates through the files and marks any markdown files in the `docs/` directory as binary. This tells `caz`'s rendering engine to skip them, preventing it from trying to inject template variables into the documentation.
60
+
61
+ ### The Template Files (`template/` directory)
62
+
63
+ Files within this directory are the blueprint for the generated project. They utilize Lodash template syntax.
64
+
65
+ * **Variable Injection**: Simple variables are injected using `<%=...%>`.
66
+ * Example from `template/positronic.config.json`: `"projectName": "<%= name %>"`
67
+ * **Conditional Logic**: JavaScript logic can be embedded with `<%...%>`. This is used powerfully in `template/package.json` to conditionally include dependencies based on the backend choice.
68
+ ```json
69
+ "dependencies": {
70
+ ...
71
+ "@positronic/core": "<%= positronicCoreVersion %>"<% if (backend === 'cloudflare') { %>,
72
+ "@positronic/cloudflare": "<%= positronicCloudflareVersion %>"<% } %>
73
+ }
74
+ ```
75
+ * **File Renaming**: `caz` also supports renaming files that contain `{variable}` syntax in their names, though this template does not currently use that feature.
76
+
77
+ ***
78
+
79
+ ## Part 2: `caz` - The Scaffolding Engine
80
+
81
+ `caz` is the engine that interprets and executes our template. Understanding its workflow is key to understanding why the template is built the way it is.
82
+
83
+ ### The Core `caz` Workflow (Middleware Chain)
84
+
85
+ `caz` processes a project in a strict, sequential order of middleware.
86
+
87
+ 1. **`confirm`**: Checks if the target project directory exists. If it's not empty, it prompts the user to either merge, overwrite, or cancel. The `--force` flag bypasses this.
88
+ 2. **`resolve`**: Locates the template. It can be a local path (`./my-template`) or a remote repository (`zce/nm`). Remote templates are downloaded and cached in `~/.cache/caz` for offline use and speed.
89
+ 3. **`load`**: Loads the template's configuration (`index.js`). **Crucially, it also runs `npm install --production` inside the template's directory**, installing any `dependencies` listed in the template's `package.json`.
90
+ 4. **`inquire`**: Presents the interactive prompts from the `prompts` array to the user. `caz` automatically provides smart defaults and validation for common fields like `name`, `version`, `author`, `email`, and `url` by reading system `.npmrc` and `.gitconfig` files.
91
+ 5. **`setup`**: Executes the `setup` hook from the template config, as described above.
92
+ 6. **`prepare`**: Reads all files from the `source` directory (`template/` by default). It applies any `filters` defined in the config to conditionally exclude files. After this, it runs the `prepare` hook.
93
+ 7. **`rename`**: Scans filenames for `{variable}` patterns and renames them based on user answers.
94
+ 8. **`render`**: Scans the content of all non-binary files for template syntax (`<%=...%>` or `${...}`) and renders them using the user's answers and any configured `helpers`.
95
+ 9. **`emit`**: Writes the final, rendered files to the destination project directory. It runs an `emit` hook if one is defined.
96
+ 10. **`install`**: If `config.install` is set to `'npm'`, `'yarn'`, or `'pnpm'`, it runs the corresponding installation command in the new project directory.
97
+ 11. **`init`**: If `config.init` is `true` (or if a `.gitignore` file is present), it runs `git init`, `git add`, and `git commit`.
98
+ 12. **`complete`**: Executes the `complete` hook, which is typically used to print a "getting started" message to the user. If not defined, it prints a default message listing the created files.
99
+
100
+ ***
101
+
102
+ ## Part 3: Practical Maintainer Tasks
103
+
104
+ With this knowledge, here is how to approach common maintenance tasks.
105
+
106
+ ### How to Add a New Backend (e.g., "Fly.io")
107
+
108
+ 1. **Modify Prompts (`index.js`)**: Add a new choice to the `backend` prompt. You can disable it initially if it's a work in progress.
109
+ ```javascript
110
+ {
111
+ name: 'backend',
112
+ type: 'select',
113
+ message: 'Select your deployment backend',
114
+ choices: [
115
+ { title: 'Cloudflare', value: 'cloudflare' },
116
+ // ...
117
+ { title: 'Fly.io', value: 'fly', disabled: false }, // Add new choice
118
+ { title: 'None (Core only)', value: 'none' }
119
+ ]
120
+ }
121
+ ```
122
+ 2. **Update Setup Logic (`index.js`)**:
123
+ * Add the new backend to `backendPackageMap`.
124
+ * Add logic to handle the `file:` path for the new backend when `POSITRONIC_LOCAL_PATH` is set.
125
+ * Add the new dependency version to `ctx.answers`.
126
+ 3. **Update Template Files (`template/package.json`)**: Add a conditional block to include the new dependency.
127
+ ```json
128
+ "dependencies": {
129
+ ...
130
+ <% if (backend === 'fly') { %>
131
+ "@positronic/fly": "<%= positronicFlyVersion %>"
132
+ <% } %>
133
+ }
134
+ ```
135
+ 4. **Add New Files**: If the new backend requires specific files (e.g., `fly.toml`), add them to the `template/` directory. You can use `filters` in `index.js` to ensure they are only included when that backend is selected.
136
+
137
+ ### How to Test Changes Locally
138
+
139
+ This is the most important workflow for a maintainer.
140
+
141
+ 1. Make your changes to the `template-new-project` repository.
142
+ 2. In your terminal, navigate to a directory *outside* of the project.
143
+ 3. To simulate a normal user's experience, run:
144
+ ```bash
145
+ # Use 'npx caz' to run caz without a global install
146
+ # Point it to the local directory of your template
147
+ # 'my-test-app' is the output directory
148
+ npx caz ./path/to/template-new-project my-test-app --force
149
+ ```
150
+ 4. To test with local monorepo packages, set the environment variable first:
151
+ ```bash
152
+ # Point to the root of your positronic monorepo
153
+ export POSITRONIC_LOCAL_PATH=~/dev/positronic
154
+
155
+ npx caz ./path/to/template-new-project my-local-test-app --force
156
+ ```
157
+ 5. Inspect the generated `my-test-app` or `my-local-test-app` directory to ensure the files and `package.json` are correct. The `--force` flag is useful for quickly re-running the command.
158
+
159
+ ### Gotcha: Accidental Template Processing
160
+
161
+ The `caz` rendering engine is greedy. It scans every non-binary file for its template syntax. If it finds a match, it will attempt to process the *entire file*, which can cause unexpected behavior or break the scaffolding process.
162
+
163
+ **Important**: Any file containing text that matches the `<%= ... %>` or `${...}` patterns will be treated as a template and processed by the engine, which can cause errors if the content is not a valid template variable or expression.
164
+
165
+ For example, imagine you add a new shell script to the template called `configure.sh`:
166
+
167
+ ```sh
168
+ #!/bin/bash
169
+
170
+ # This script configures the local environment
171
+ echo "Setting up for user: ${USER}"
172
+ ```
173
+
174
+ When `caz` processes this file, it will see `${USER}` and interpret it as a template variable. It will search for a `USER` key in the answers provided by the user during the prompts. If it's not found, the output will likely be `Setting up for user: undefined`, or it could even throw an error, breaking the project generation.
175
+
176
+ ### How to Prevent Accidental Processing
177
+
178
+ The codebase reveals two ways to handle this, depending on your goal.
179
+
180
+ 1. **Escaping the Syntax (The Preferred Method)**
181
+
182
+ If you need the literal syntax (like `${USER}`) to appear in the final generated file, you must escape it using the template engine itself. The `caz` documentation (`docs/create-template.md`) explicitly describes this:
183
+
184
+ * To output a literal `<%= name %>`, you must write `<%= '\<%= name %\>' %>` in your template file.
185
+ * To output a literal `${name}`, you must write `<%= '${name}' %>` in your template file.
186
+
187
+ 2. **Marking the File as Binary**
188
+
189
+ If a file should be copied verbatim with no processing whatsoever, the solution is to instruct `caz` to treat it as a binary file. The renderer explicitly skips binary files.
190
+
191
+ The `@positronic/template-new-project` template already does this for its documentation files as a preventative measure. You can add a similar rule in the `prepare` hook within `index.js`:
192
+
193
+ ```javascript
194
+ // in index.js
195
+ prepare: async ctx => {
196
+ // ... existing prepare logic ...
197
+
198
+ // Add a rule to treat all .sh files as binary
199
+ ctx.files.forEach(file => {
200
+ if (file.path.endsWith('.sh')) {
201
+ // This is a made-up property for the example, but it would
202
+ // need to be supported by caz's `isBinary` check.
203
+ // A more robust way is to check the file contents.
204
+ // The key insight is that the prepare hook is where you would
205
+ // manipulate files before rendering.
206
+ // The current template shows a real example for .md files:
207
+ if (file.path.startsWith('docs/') && file.path.endsWith('.md')) {
208
+ file.binary = true; // This is a conceptual flag.
209
+ // The actual implementation is in caz's `isBinary` check.
210
+ }
211
+ }
212
+ });
213
+ }
214
+ ```
215
+ This tells the `render` step to ignore the file completely, ensuring it is copied as-is.
package/index.js ADDED
@@ -0,0 +1,180 @@
1
+ const { name, version } = require('./package.json')
2
+ const path = require('path')
3
+ const { existsSync } = require('fs')
4
+
5
+ module.exports = {
6
+ name,
7
+ version,
8
+ prompts: [
9
+ {
10
+ name: 'name',
11
+ type: 'text',
12
+ message: 'Project name',
13
+ initial: 'my-positronic-project'
14
+ },
15
+ {
16
+ name: 'backend',
17
+ type: 'select',
18
+ message: 'Select your deployment backend',
19
+ hint: ' ',
20
+ choices: [
21
+ { title: 'Cloudflare', value: 'cloudflare' },
22
+ { title: 'AWS Lambda', value: 'aws', disabled: true },
23
+ { title: 'Azure Functions', value: 'azure', disabled: true },
24
+ { title: 'Google Cloud Functions', value: 'gcp', disabled: true },
25
+ { title: 'Fly.io', value: 'fly', disabled: true },
26
+ { title: 'None (Core only)', value: 'none' }
27
+ ],
28
+ initial: 0
29
+ },
30
+ {
31
+ name: 'install',
32
+ type: 'confirm',
33
+ message: 'Install dependencies',
34
+ initial: true
35
+ },
36
+ {
37
+ name: 'pm',
38
+ type: prev => prev ? 'select' : null,
39
+ message: 'Package manager',
40
+ hint: ' ',
41
+ choices: [
42
+ { title: 'npm', value: 'npm' },
43
+ { title: 'pnpm', value: 'pnpm' },
44
+ { title: 'yarn', value: 'yarn' }
45
+ ]
46
+ },
47
+ {
48
+ name: 'claudemd',
49
+ type: 'confirm',
50
+ message: 'Initialize CLAUDE.md for AI-assisted development',
51
+ initial: true
52
+ }
53
+ ],
54
+ setup: async ctx => {
55
+ const devRootPath = process.env.POSITRONIC_LOCAL_PATH;
56
+ let coreVersion = 'latest';
57
+ let cloudflareVersion = 'latest';
58
+ let clientVercelVersion = 'latest';
59
+
60
+ // Map backend selection to package names
61
+ const backendPackageMap = {
62
+ 'cloudflare': '@positronic/cloudflare',
63
+ 'aws': '@positronic/aws-lambda', // Future
64
+ 'azure': '@positronic/azure-functions', // Future
65
+ 'gcp': '@positronic/gcp-functions', // Future
66
+ 'fly': '@positronic/fly', // Future
67
+ 'none': null
68
+ };
69
+
70
+ ctx.answers.backendPackage = backendPackageMap[ctx.answers.backend];
71
+
72
+ if (devRootPath) {
73
+ console.log(`Found POSITRONIC_LOCAL_PATH: ${devRootPath}. Using local file path for @positronic/core.`);
74
+ const corePath = path.resolve(devRootPath, 'packages', 'core');
75
+ coreVersion = `file:${corePath}`;
76
+ console.log(` - Mapping @positronic/core to ${coreVersion}`);
77
+
78
+ // Handle backend packages for local development
79
+ if (ctx.answers.backend === 'cloudflare') {
80
+ const cloudflarePath = path.resolve(devRootPath, 'packages', 'cloudflare');
81
+ if (existsSync(cloudflarePath)) {
82
+ cloudflareVersion = `file:${cloudflarePath}`;
83
+ // Also update the backend package for the config file
84
+ ctx.answers.backendPackage = `file:${cloudflarePath}`;
85
+ console.log(` - Mapping @positronic/cloudflare to ${cloudflareVersion}`);
86
+ }
87
+ }
88
+ // Add similar blocks for other backends when they exist
89
+ // Example for future backends:
90
+ // if (ctx.answers.backend === 'aws') {
91
+ // const awsPath = path.resolve(devRootPath, 'packages', 'aws-lambda');
92
+ // if (existsSync(awsPath)) {
93
+ // ctx.answers.backendPackage = `file:${awsPath}`;
94
+ // }
95
+ // }
96
+
97
+ const clientVercelPath = path.resolve(devRootPath, 'packages', 'client-vercel');
98
+ if (existsSync(clientVercelPath)) {
99
+ clientVercelVersion = `file:${clientVercelPath}`;
100
+ }
101
+ }
102
+
103
+ ctx.answers.positronicCoreVersion = coreVersion;
104
+ if (ctx.answers.backend === 'cloudflare') {
105
+ ctx.answers.positronicCloudflareVersion = cloudflareVersion;
106
+ }
107
+
108
+ ctx.answers.positronicClientVercelVersion = clientVercelVersion;
109
+
110
+ if (ctx.answers.install) {
111
+ const pm = ctx.answers.pm;
112
+ if (!pm) {
113
+ ctx.config.install = 'npm';
114
+ ctx.answers.pm = 'npm';
115
+ } else {
116
+ ctx.config.install = pm;
117
+ }
118
+ } else {
119
+ ctx.config.install = false;
120
+ }
121
+
122
+ // Disable git initialization in test environments
123
+ if (process.env.NODE_ENV === 'test') {
124
+ ctx.config.init = false;
125
+ }
126
+
127
+ ctx.answers.projectName = ctx.answers.name;
128
+ },
129
+ prepare: async ctx => {
130
+ // Find our specially named gitignore file in the list of files to be processed
131
+ const gitignoreFile = ctx.files.find(file => file.path === '_gitignore');
132
+ if (gitignoreFile) {
133
+ // Change its path to '.gitignore' so it's correctly named in the generated project
134
+ gitignoreFile.path = '.gitignore';
135
+ }
136
+
137
+ // Find our specially named env file in the list of files to be processed
138
+ const envFile = ctx.files.find(file => file.path === '_env');
139
+ if (envFile) {
140
+ // Change its path to '.env' so it's correctly named in the generated project
141
+ envFile.path = '.env';
142
+ }
143
+
144
+ // Remove CLAUDE.md if user chose not to include it
145
+ if (!ctx.answers.claudemd) {
146
+ ctx.files = ctx.files.filter(file => file.path !== 'CLAUDE.md');
147
+ }
148
+ },
149
+ complete: async ctx => {
150
+ // Display getting started message
151
+ console.log('\n✨ Project created successfully!\n');
152
+ console.log(`📁 Project location: ${ctx.dest}\n`);
153
+
154
+ console.log('🚀 Getting started:\n');
155
+ console.log(` cd ${ctx.answers.name}`);
156
+
157
+ if (!ctx.answers.install) {
158
+ console.log(` ${ctx.answers.pm || 'npm'} install`);
159
+ }
160
+
161
+ console.log(' px server # Start the development server');
162
+ console.log(' px brain list # List available brains');
163
+ console.log(' px brain run example # Run the example brain\n');
164
+
165
+ if (ctx.answers.backend === 'cloudflare') {
166
+ console.log('☁️ Cloudflare deployment:\n');
167
+ console.log(' wrangler login # Authenticate with Cloudflare');
168
+ console.log(' px deploy # Deploy to Cloudflare Workers\n');
169
+ }
170
+
171
+ console.log('📚 Resources:\n');
172
+ console.log(' • Documentation: https://positronic.dev');
173
+ console.log(' • GitHub: https://github.com/positronic-ai/positronic');
174
+ console.log(' • CLI Help: px --help\n');
175
+
176
+ if (ctx.answers.claudemd) {
177
+ console.log('💡 Pro tip: Check out CLAUDE.md in your project for AI-assisted development guidance!\n');
178
+ }
179
+ }
180
+ }
package/package.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "@positronic/template-new-project",
3
+ "version": "0.0.2",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "description": "Caz template for generating a new Positronic project.",
8
+ "main": "index.js",
9
+ "scripts": {
10
+ "build": "",
11
+ "clean": "rm -rf node_modules"
12
+ },
13
+ "author": "",
14
+ "license": "MIT",
15
+ "devDependencies": {
16
+ "caz": "^2.0.0"
17
+ }
18
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "<%= projectName %>-server",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "type": "module",
6
+ "main": "src/index.ts",
7
+ "scripts": {},
8
+ "dependencies": {
9
+ "@positronic/core": "<%= positronicCoreVersion %>", <% if (backend === 'cloudflare') { %>
10
+ "@positronic/cloudflare": "<%= positronicCloudflareVersion %>", <% } %>
11
+ "zod": "^3.24.1"
12
+ },
13
+ "devDependencies": {
14
+ "@cloudflare/workers-types": "^4.20240405.0",
15
+ "typescript": "^5.0.0"
16
+ }
17
+ }
@@ -0,0 +1,35 @@
1
+ import {
2
+ api as app,
3
+ setManifest,
4
+ setBrainRunner,
5
+ BrainRunnerDO,
6
+ MonitorDO,
7
+ ScheduleDO,
8
+ PositronicManifest,
9
+ } from "@positronic/cloudflare";
10
+ // Import the generated manifest - NOTE the .js extension for runtime compatibility
11
+ // @ts-expect-error - _manifest.js is generated during template processing
12
+ import { staticManifest } from "./_manifest.js";
13
+ import { runner } from "./runner.js";
14
+ // Configure the manifest to use the statically generated list
15
+ const manifest = new PositronicManifest({
16
+ staticManifest,
17
+ });
18
+
19
+ setManifest(manifest);
20
+ setBrainRunner(runner);
21
+
22
+ // Define Env type based on wrangler.jsonc bindings
23
+ interface Env {
24
+ BRAIN_RUNNER_DO: DurableObjectNamespace<BrainRunnerDO>;
25
+ MONITOR_DO: DurableObjectNamespace<MonitorDO>;
26
+ SCHEDULE_DO: DurableObjectNamespace<ScheduleDO>;
27
+ RESOURCES_BUCKET: R2Bucket;
28
+ }
29
+
30
+ // Export the API handler and Durable Objects
31
+ export default {
32
+ fetch: app.fetch,
33
+ } satisfies ExportedHandler<Env>;
34
+
35
+ export { BrainRunnerDO, MonitorDO, ScheduleDO };
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "lib": ["ESNext"],
6
+ "moduleResolution": "node",
7
+ "esModuleInterop": true,
8
+ "strict": true,
9
+ "resolveJsonModule": true,
10
+ "isolatedModules": true,
11
+ "skipLibCheck": true,
12
+ "incremental": true,
13
+ "types": ["@cloudflare/workers-types"],
14
+ "outDir": "dist",
15
+ "rootDir": "src"
16
+ },
17
+ "include": ["src/**/*.ts"],
18
+ "exclude": ["node_modules", "dist"]
19
+ }
@@ -0,0 +1,53 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/cloudflare/wrangler/main/config-schema.json",
3
+ "name": "<%= projectName %>",
4
+ "main": "src/index.ts",
5
+ "compatibility_date": "2024-09-23",
6
+ "compatibility_flags": ["nodejs_compat", "nodejs_compat_populate_process_env"],
7
+ "workers_dev": true,
8
+ "migrations": [
9
+ {
10
+ "tag": "v1",
11
+ "new_sqlite_classes": ["BrainRunnerDO", "MonitorDO", "ScheduleDO"]
12
+ }
13
+ ],
14
+ "durable_objects": {
15
+ "bindings": [
16
+ { "name": "BRAIN_RUNNER_DO", "class_name": "BrainRunnerDO" },
17
+ { "name": "MONITOR_DO", "class_name": "MonitorDO" },
18
+ { "name": "SCHEDULE_DO", "class_name": "ScheduleDO" }
19
+ ]
20
+ },
21
+ "r2_buckets": [
22
+ {
23
+ "binding": "RESOURCES_BUCKET",
24
+ "bucket_name": "<%= projectName %>"
25
+ }
26
+ ],
27
+ "vars": {
28
+ "R2_BUCKET_NAME": "<%= projectName %>"
29
+ },
30
+ "env": {
31
+ "production": {
32
+ "name": "<%= projectName %>",
33
+ "workers_dev": false,
34
+ "vars": {
35
+ "NODE_ENV": "production",
36
+ "R2_BUCKET_NAME": "<%= projectName %>"
37
+ },
38
+ "durable_objects": {
39
+ "bindings": [
40
+ { "name": "BRAIN_RUNNER_DO", "class_name": "BrainRunnerDO" },
41
+ { "name": "MONITOR_DO", "class_name": "MonitorDO" },
42
+ { "name": "SCHEDULE_DO", "class_name": "ScheduleDO" }
43
+ ]
44
+ },
45
+ "r2_buckets": [
46
+ {
47
+ "binding": "RESOURCES_BUCKET",
48
+ "bucket_name": "<%= projectName %>"
49
+ }
50
+ ]
51
+ }
52
+ }
53
+ }
@@ -0,0 +1,120 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ This is a Positronic project - an AI-powered framework for building and running "brains" (stateful AI workflows) that can be deployed to various cloud backends. It provides a fluent DSL for defining AI workflows, resource management, and a CLI for development and deployment.
8
+
9
+ ## Project Structure
10
+
11
+ - **`/brains`** - AI workflow definitions using the Brain DSL
12
+ - **`/resources`** - Files and documents that brains can access via the resource system
13
+ - **`/tests`** - Test files for brains (kept separate to avoid deployment issues)
14
+ - **`/docs`** - Documentation including brain testing guide
15
+ - **`/runner.ts`** - The main entry point for running brains locally
16
+ - **`/positronic.config.json`** - Project configuration
17
+
18
+ ## Key Commands
19
+
20
+ ### Development
21
+
22
+ - `px brain run <brain-name>` - Run a brain workflow
23
+ - `px brain list` - List all available brains
24
+ - `px resource list` - List all available resources
25
+ - `px server` - Start the local development server (add `-d` for background mode)
26
+
27
+ ### Testing & Building
28
+
29
+ - `npm test` - Run tests (uses Jest with @positronic/core/testing utilities)
30
+ - `npm run build` - Build the project
31
+ - `npm run dev` - Start development mode with hot reload
32
+
33
+ For testing guidance, see `/docs/brain-testing-guide.md`
34
+
35
+ ## Brain DSL
36
+
37
+ The Brain DSL provides a fluent API for defining AI workflows:
38
+
39
+ ```typescript
40
+ import { brain } from '@positronic/core';
41
+
42
+ const myBrain = brain('my-brain')
43
+ .step('Initialize', ({ state }) => ({
44
+ ...state,
45
+ initialized: true
46
+ }))
47
+ .step('Process', async ({ state, resources }) => {
48
+ // Access resources
49
+ const doc = await resources.get('example.md');
50
+ return {
51
+ ...state,
52
+ processed: true,
53
+ content: doc.content
54
+ };
55
+ });
56
+
57
+ export default myBrain;
58
+ ```
59
+
60
+ ## Resource System
61
+
62
+ Resources are files that brains can access during execution. They're stored in the `/resources` directory and are automatically typed based on the manifest.
63
+
64
+ ## Development Workflow
65
+
66
+ 1. Define your brain in `/brains`
67
+ 2. Add any required resources to `/resources`
68
+ 3. Run `px brain run <brain-name>` to test locally
69
+ 4. Deploy using backend-specific commands
70
+
71
+ ## Backend-Specific Notes
72
+
73
+ <% if (backend === 'cloudflare') { %>
74
+ ### Cloudflare Workers
75
+
76
+ This project is configured for Cloudflare Workers deployment:
77
+
78
+ - Uses Durable Objects for state persistence
79
+ - R2 for resource storage
80
+ - Requires Cloudflare account and API keys
81
+
82
+ Deployment:
83
+ ```bash
84
+ # Configure Cloudflare credentials
85
+ wrangler login
86
+
87
+ # Deploy
88
+ px deploy
89
+ ```
90
+ <% } else if (backend === 'none') { %>
91
+ ### Core Only
92
+
93
+ This project uses only the Positronic core without a specific deployment backend. You can:
94
+
95
+ - Run brains locally using `px brain run`
96
+ - Add a backend later by installing the appropriate package
97
+ - Use the framework for local AI workflow development
98
+ <% } %>
99
+
100
+ ## Best Practices
101
+
102
+ 1. **State Management**: Keep brain state minimal and serializable
103
+ 2. **Resource Naming**: Use descriptive names for resources (e.g., `prompt-templates/customer-support.md`)
104
+ 3. **Error Handling**: Always handle potential errors in brain steps
105
+ 4. **Testing**: Write tests for your brains focusing on outcomes, not implementation details (see `/docs/brain-testing-guide.md`)
106
+
107
+ ## Getting Help
108
+
109
+ - Documentation: https://positronic.dev
110
+ - GitHub: https://github.com/positronic-ai/positronic
111
+ - CLI Help: `px --help` or `px <command> --help`
112
+
113
+ ## Project-Level Patterns
114
+
115
+ For project structure, the project brain pattern, and other Positronic conventions, see:
116
+ @docs/positronic-guide.md
117
+
118
+ ## Additional Tips for AI Agents
119
+
120
+ @docs/tips-for-agents.md