thepopebot 1.2.56 → 1.2.57

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.
package/README.md CHANGED
@@ -130,55 +130,74 @@ docker compose up -d
130
130
 
131
131
  ---
132
132
 
133
- ## CLI Commands
134
-
135
- | Command | Description |
136
- |---------|-------------|
137
- | `npx thepopebot init` | Scaffold a new project, or check for updated templates in an existing one |
138
- | `npx thepopebot diff [file]` | List files that differ from package templates, or show the diff for a specific file |
139
- | `npx thepopebot reset [file]` | List all template files, or restore a specific file (or directory) to the package default |
140
- | `npm run setup` | Run the full interactive setup wizard |
141
- | `npm run setup-telegram` | Reconfigure the Telegram webhook (useful when your ngrok URL changes) |
142
-
143
- ---
144
-
145
133
  ## Updating
146
134
 
147
- When thepopebot is updated via npm, your customizations are always preserved — template changes are never applied automatically. To update:
135
+ **1. Update the package**
148
136
 
149
137
  ```bash
150
138
  npm update thepopebot
139
+ ```
140
+
141
+ **2. Scaffold and update templates**
142
+
143
+ ```bash
151
144
  npx thepopebot init
152
145
  ```
153
146
 
154
- `init` will report which templates have drifted (new files are created automatically, existing files are never overwritten):
147
+ This scaffolds any new template files, runs `npm install`, and updates `THEPOPEBOT_VERSION` in your local `.env`.
148
+
149
+ **Managed files** — Infrastructure files tightly coupled to the package version (GitHub Actions workflows, docker-compose, event-handler Dockerfile) are **auto-updated** to match the new package version. These files must stay in your project because GitHub and Docker require them at specific paths, but they shouldn't drift from the package.
150
+
151
+ **User-editable files** — Configuration and app files you're expected to customize (`config/`, `app/`, job Dockerfile) are **never overwritten**. If they differ from the package template, `init` reports the drift so you can review at your own pace:
155
152
 
156
153
  ```
157
154
  Updated templates available:
158
155
  These files differ from the current package templates.
159
156
 
160
157
  config/CRONS.json
161
- .github/workflows/run-job.yml
162
158
 
163
159
  To view differences: npx thepopebot diff <file>
164
160
  To reset to default: npx thepopebot reset <file>
165
161
  ```
166
162
 
167
- Review and accept changes at your own pace:
168
-
169
163
  ```bash
170
164
  npx thepopebot diff config/CRONS.json # see what changed
171
165
  npx thepopebot reset config/CRONS.json # accept the new template
172
166
  ```
173
167
 
174
- Or manually merge the changes if you want to keep some of your edits.
168
+ If you customize managed files (e.g., adding steps to a workflow), use `--no-managed` to skip auto-updates and handle them yourself:
169
+
170
+ ```bash
171
+ npx thepopebot init --no-managed
172
+ ```
173
+
174
+ **3. Rebuild for local dev**
175
+
176
+ ```bash
177
+ npm run build
178
+ ```
175
179
 
176
- After updating, restart Docker to pick up new image versions:
180
+ **4. Commit and push**
177
181
 
178
182
  ```bash
179
- docker compose up -d
183
+ git add -A && git commit -m "upgrade thepopebot to vX.X.X"
184
+ git push
180
185
  ```
181
186
 
187
+ Pushing to `main` triggers the `rebuild-event-handler.yml` workflow on your server. It detects the version change, runs `thepopebot init`, updates `THEPOPEBOT_VERSION` in the server's `.env`, pulls the new Docker image, restarts the container, rebuilds `.next`, and reloads PM2 — no manual `docker compose` needed.
188
+
189
+ ---
190
+
191
+ ## CLI Commands
192
+
193
+ | Command | Description |
194
+ |---------|-------------|
195
+ | `npx thepopebot init` | Scaffold a new project, or check for updated templates in an existing one |
196
+ | `npx thepopebot diff [file]` | List files that differ from package templates, or show the diff for a specific file |
197
+ | `npx thepopebot reset [file]` | List all template files, or restore a specific file (or directory) to the package default |
198
+ | `npm run setup` | Run the full interactive setup wizard |
199
+ | `npm run setup-telegram` | Reconfigure the Telegram webhook (useful when your ngrok URL changes) |
200
+
182
201
  ---
183
202
 
184
203
  ## Docs
package/bin/cli.js CHANGED
@@ -11,6 +11,20 @@ const __dirname = path.dirname(__filename);
11
11
  const command = process.argv[2];
12
12
  const args = process.argv.slice(3);
13
13
 
14
+ // Files tightly coupled to the package version that are auto-updated by init.
15
+ // These live in the user's project because GitHub/Docker require them at specific paths,
16
+ // but they shouldn't drift from the package version.
17
+ const MANAGED_PATHS = [
18
+ '.github/workflows/',
19
+ 'docker/event-handler/',
20
+ 'docker-compose.yml',
21
+ '.dockerignore',
22
+ ];
23
+
24
+ function isManaged(relPath) {
25
+ return MANAGED_PATHS.some(p => relPath === p || relPath.startsWith(p));
26
+ }
27
+
14
28
  function printUsage() {
15
29
  console.log(`
16
30
  Usage: thepopebot <command>
@@ -48,6 +62,7 @@ function init() {
48
62
  const cwd = process.cwd();
49
63
  const packageDir = path.join(__dirname, '..');
50
64
  const templatesDir = path.join(packageDir, 'templates');
65
+ const noManaged = args.includes('--no-managed');
51
66
 
52
67
  console.log('\nScaffolding thepopebot project...\n');
53
68
 
@@ -55,6 +70,7 @@ function init() {
55
70
  const created = [];
56
71
  const skipped = [];
57
72
  const changed = [];
73
+ const updated = [];
58
74
 
59
75
  for (const relPath of templateFiles) {
60
76
  const src = path.join(templatesDir, relPath);
@@ -72,6 +88,12 @@ function init() {
72
88
  const destContent = fs.readFileSync(dest);
73
89
  if (srcContent.equals(destContent)) {
74
90
  skipped.push(relPath);
91
+ } else if (!noManaged && isManaged(relPath)) {
92
+ // Managed file differs — auto-update to match package
93
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
94
+ fs.copyFileSync(src, dest);
95
+ updated.push(relPath);
96
+ console.log(` Updated ${relPath}`);
75
97
  } else {
76
98
  changed.push(relPath);
77
99
  console.log(` Skipped ${relPath} (already exists)`);
@@ -121,6 +143,14 @@ function init() {
121
143
  }
122
144
  }
123
145
 
146
+ // Report updated managed files
147
+ if (updated.length > 0) {
148
+ console.log('\n Updated managed files:');
149
+ for (const file of updated) {
150
+ console.log(` ${file}`);
151
+ }
152
+ }
153
+
124
154
  // Report changed templates
125
155
  if (changed.length > 0) {
126
156
  console.log('\n Updated templates available:');
@@ -0,0 +1,52 @@
1
+ CREATE TABLE `chats` (
2
+ `id` text PRIMARY KEY NOT NULL,
3
+ `user_id` text NOT NULL,
4
+ `title` text DEFAULT 'New Chat' NOT NULL,
5
+ `starred` integer DEFAULT 0 NOT NULL,
6
+ `created_at` integer NOT NULL,
7
+ `updated_at` integer NOT NULL
8
+ );
9
+ --> statement-breakpoint
10
+ CREATE TABLE `messages` (
11
+ `id` text PRIMARY KEY NOT NULL,
12
+ `chat_id` text NOT NULL,
13
+ `role` text NOT NULL,
14
+ `content` text NOT NULL,
15
+ `created_at` integer NOT NULL
16
+ );
17
+ --> statement-breakpoint
18
+ CREATE TABLE `notifications` (
19
+ `id` text PRIMARY KEY NOT NULL,
20
+ `notification` text NOT NULL,
21
+ `payload` text NOT NULL,
22
+ `read` integer DEFAULT 0 NOT NULL,
23
+ `created_at` integer NOT NULL
24
+ );
25
+ --> statement-breakpoint
26
+ CREATE TABLE `settings` (
27
+ `id` text PRIMARY KEY NOT NULL,
28
+ `type` text NOT NULL,
29
+ `key` text NOT NULL,
30
+ `value` text NOT NULL,
31
+ `created_by` text,
32
+ `created_at` integer NOT NULL,
33
+ `updated_at` integer NOT NULL
34
+ );
35
+ --> statement-breakpoint
36
+ CREATE TABLE `subscriptions` (
37
+ `id` text PRIMARY KEY NOT NULL,
38
+ `platform` text NOT NULL,
39
+ `channel_id` text NOT NULL,
40
+ `created_at` integer NOT NULL
41
+ );
42
+ --> statement-breakpoint
43
+ CREATE TABLE `users` (
44
+ `id` text PRIMARY KEY NOT NULL,
45
+ `email` text NOT NULL,
46
+ `password_hash` text NOT NULL,
47
+ `role` text DEFAULT 'admin' NOT NULL,
48
+ `created_at` integer NOT NULL,
49
+ `updated_at` integer NOT NULL
50
+ );
51
+ --> statement-breakpoint
52
+ CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);
@@ -0,0 +1,321 @@
1
+ {
2
+ "version": "6",
3
+ "dialect": "sqlite",
4
+ "id": "3312cdb9-4f3b-4004-921b-d6598d2eb2a1",
5
+ "prevId": "00000000-0000-0000-0000-000000000000",
6
+ "tables": {
7
+ "chats": {
8
+ "name": "chats",
9
+ "columns": {
10
+ "id": {
11
+ "name": "id",
12
+ "type": "text",
13
+ "primaryKey": true,
14
+ "notNull": true,
15
+ "autoincrement": false
16
+ },
17
+ "user_id": {
18
+ "name": "user_id",
19
+ "type": "text",
20
+ "primaryKey": false,
21
+ "notNull": true,
22
+ "autoincrement": false
23
+ },
24
+ "title": {
25
+ "name": "title",
26
+ "type": "text",
27
+ "primaryKey": false,
28
+ "notNull": true,
29
+ "autoincrement": false,
30
+ "default": "'New Chat'"
31
+ },
32
+ "starred": {
33
+ "name": "starred",
34
+ "type": "integer",
35
+ "primaryKey": false,
36
+ "notNull": true,
37
+ "autoincrement": false,
38
+ "default": 0
39
+ },
40
+ "created_at": {
41
+ "name": "created_at",
42
+ "type": "integer",
43
+ "primaryKey": false,
44
+ "notNull": true,
45
+ "autoincrement": false
46
+ },
47
+ "updated_at": {
48
+ "name": "updated_at",
49
+ "type": "integer",
50
+ "primaryKey": false,
51
+ "notNull": true,
52
+ "autoincrement": false
53
+ }
54
+ },
55
+ "indexes": {},
56
+ "foreignKeys": {},
57
+ "compositePrimaryKeys": {},
58
+ "uniqueConstraints": {},
59
+ "checkConstraints": {}
60
+ },
61
+ "messages": {
62
+ "name": "messages",
63
+ "columns": {
64
+ "id": {
65
+ "name": "id",
66
+ "type": "text",
67
+ "primaryKey": true,
68
+ "notNull": true,
69
+ "autoincrement": false
70
+ },
71
+ "chat_id": {
72
+ "name": "chat_id",
73
+ "type": "text",
74
+ "primaryKey": false,
75
+ "notNull": true,
76
+ "autoincrement": false
77
+ },
78
+ "role": {
79
+ "name": "role",
80
+ "type": "text",
81
+ "primaryKey": false,
82
+ "notNull": true,
83
+ "autoincrement": false
84
+ },
85
+ "content": {
86
+ "name": "content",
87
+ "type": "text",
88
+ "primaryKey": false,
89
+ "notNull": true,
90
+ "autoincrement": false
91
+ },
92
+ "created_at": {
93
+ "name": "created_at",
94
+ "type": "integer",
95
+ "primaryKey": false,
96
+ "notNull": true,
97
+ "autoincrement": false
98
+ }
99
+ },
100
+ "indexes": {},
101
+ "foreignKeys": {},
102
+ "compositePrimaryKeys": {},
103
+ "uniqueConstraints": {},
104
+ "checkConstraints": {}
105
+ },
106
+ "notifications": {
107
+ "name": "notifications",
108
+ "columns": {
109
+ "id": {
110
+ "name": "id",
111
+ "type": "text",
112
+ "primaryKey": true,
113
+ "notNull": true,
114
+ "autoincrement": false
115
+ },
116
+ "notification": {
117
+ "name": "notification",
118
+ "type": "text",
119
+ "primaryKey": false,
120
+ "notNull": true,
121
+ "autoincrement": false
122
+ },
123
+ "payload": {
124
+ "name": "payload",
125
+ "type": "text",
126
+ "primaryKey": false,
127
+ "notNull": true,
128
+ "autoincrement": false
129
+ },
130
+ "read": {
131
+ "name": "read",
132
+ "type": "integer",
133
+ "primaryKey": false,
134
+ "notNull": true,
135
+ "autoincrement": false,
136
+ "default": 0
137
+ },
138
+ "created_at": {
139
+ "name": "created_at",
140
+ "type": "integer",
141
+ "primaryKey": false,
142
+ "notNull": true,
143
+ "autoincrement": false
144
+ }
145
+ },
146
+ "indexes": {},
147
+ "foreignKeys": {},
148
+ "compositePrimaryKeys": {},
149
+ "uniqueConstraints": {},
150
+ "checkConstraints": {}
151
+ },
152
+ "settings": {
153
+ "name": "settings",
154
+ "columns": {
155
+ "id": {
156
+ "name": "id",
157
+ "type": "text",
158
+ "primaryKey": true,
159
+ "notNull": true,
160
+ "autoincrement": false
161
+ },
162
+ "type": {
163
+ "name": "type",
164
+ "type": "text",
165
+ "primaryKey": false,
166
+ "notNull": true,
167
+ "autoincrement": false
168
+ },
169
+ "key": {
170
+ "name": "key",
171
+ "type": "text",
172
+ "primaryKey": false,
173
+ "notNull": true,
174
+ "autoincrement": false
175
+ },
176
+ "value": {
177
+ "name": "value",
178
+ "type": "text",
179
+ "primaryKey": false,
180
+ "notNull": true,
181
+ "autoincrement": false
182
+ },
183
+ "created_by": {
184
+ "name": "created_by",
185
+ "type": "text",
186
+ "primaryKey": false,
187
+ "notNull": false,
188
+ "autoincrement": false
189
+ },
190
+ "created_at": {
191
+ "name": "created_at",
192
+ "type": "integer",
193
+ "primaryKey": false,
194
+ "notNull": true,
195
+ "autoincrement": false
196
+ },
197
+ "updated_at": {
198
+ "name": "updated_at",
199
+ "type": "integer",
200
+ "primaryKey": false,
201
+ "notNull": true,
202
+ "autoincrement": false
203
+ }
204
+ },
205
+ "indexes": {},
206
+ "foreignKeys": {},
207
+ "compositePrimaryKeys": {},
208
+ "uniqueConstraints": {},
209
+ "checkConstraints": {}
210
+ },
211
+ "subscriptions": {
212
+ "name": "subscriptions",
213
+ "columns": {
214
+ "id": {
215
+ "name": "id",
216
+ "type": "text",
217
+ "primaryKey": true,
218
+ "notNull": true,
219
+ "autoincrement": false
220
+ },
221
+ "platform": {
222
+ "name": "platform",
223
+ "type": "text",
224
+ "primaryKey": false,
225
+ "notNull": true,
226
+ "autoincrement": false
227
+ },
228
+ "channel_id": {
229
+ "name": "channel_id",
230
+ "type": "text",
231
+ "primaryKey": false,
232
+ "notNull": true,
233
+ "autoincrement": false
234
+ },
235
+ "created_at": {
236
+ "name": "created_at",
237
+ "type": "integer",
238
+ "primaryKey": false,
239
+ "notNull": true,
240
+ "autoincrement": false
241
+ }
242
+ },
243
+ "indexes": {},
244
+ "foreignKeys": {},
245
+ "compositePrimaryKeys": {},
246
+ "uniqueConstraints": {},
247
+ "checkConstraints": {}
248
+ },
249
+ "users": {
250
+ "name": "users",
251
+ "columns": {
252
+ "id": {
253
+ "name": "id",
254
+ "type": "text",
255
+ "primaryKey": true,
256
+ "notNull": true,
257
+ "autoincrement": false
258
+ },
259
+ "email": {
260
+ "name": "email",
261
+ "type": "text",
262
+ "primaryKey": false,
263
+ "notNull": true,
264
+ "autoincrement": false
265
+ },
266
+ "password_hash": {
267
+ "name": "password_hash",
268
+ "type": "text",
269
+ "primaryKey": false,
270
+ "notNull": true,
271
+ "autoincrement": false
272
+ },
273
+ "role": {
274
+ "name": "role",
275
+ "type": "text",
276
+ "primaryKey": false,
277
+ "notNull": true,
278
+ "autoincrement": false,
279
+ "default": "'admin'"
280
+ },
281
+ "created_at": {
282
+ "name": "created_at",
283
+ "type": "integer",
284
+ "primaryKey": false,
285
+ "notNull": true,
286
+ "autoincrement": false
287
+ },
288
+ "updated_at": {
289
+ "name": "updated_at",
290
+ "type": "integer",
291
+ "primaryKey": false,
292
+ "notNull": true,
293
+ "autoincrement": false
294
+ }
295
+ },
296
+ "indexes": {
297
+ "users_email_unique": {
298
+ "name": "users_email_unique",
299
+ "columns": [
300
+ "email"
301
+ ],
302
+ "isUnique": true
303
+ }
304
+ },
305
+ "foreignKeys": {},
306
+ "compositePrimaryKeys": {},
307
+ "uniqueConstraints": {},
308
+ "checkConstraints": {}
309
+ }
310
+ },
311
+ "views": {},
312
+ "enums": {},
313
+ "_meta": {
314
+ "schemas": {},
315
+ "tables": {},
316
+ "columns": {}
317
+ },
318
+ "internal": {
319
+ "indexes": {}
320
+ }
321
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "version": "7",
3
+ "dialect": "sqlite",
4
+ "entries": [
5
+ {
6
+ "idx": 0,
7
+ "version": "6",
8
+ "when": 1771448716757,
9
+ "tag": "0000_initial",
10
+ "breakpoints": true
11
+ }
12
+ ]
13
+ }
package/lib/db/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
+ import { fileURLToPath } from 'url';
3
4
  import Database from 'better-sqlite3';
4
5
  import { drizzle } from 'drizzle-orm/better-sqlite3';
6
+ import { migrate } from 'drizzle-orm/better-sqlite3/migrator';
5
7
  import { thepopebotDb, dataDir } from '../paths.js';
6
8
  import * as schema from './schema.js';
7
9
 
@@ -25,84 +27,24 @@ export function getDb() {
25
27
  }
26
28
 
27
29
  /**
28
- * Initialize the database — create tables if they don't exist.
30
+ * Initialize the database — apply pending migrations.
29
31
  * Called from instrumentation.js at server startup.
32
+ * Uses Drizzle Kit migrations from the package's drizzle/ folder.
30
33
  */
31
34
  export function initDatabase() {
32
- // Ensure data directory exists
33
35
  if (!fs.existsSync(dataDir)) {
34
36
  fs.mkdirSync(dataDir, { recursive: true });
35
37
  }
36
38
 
37
39
  const sqlite = new Database(thepopebotDb);
38
40
  sqlite.pragma('journal_mode = WAL');
41
+ const db = drizzle(sqlite, { schema });
39
42
 
40
- sqlite.exec(`
41
- CREATE TABLE IF NOT EXISTS users (
42
- id TEXT PRIMARY KEY,
43
- email TEXT NOT NULL UNIQUE,
44
- password_hash TEXT NOT NULL,
45
- role TEXT NOT NULL DEFAULT 'admin',
46
- created_at INTEGER NOT NULL,
47
- updated_at INTEGER NOT NULL
48
- )
49
- `);
43
+ // Resolve migrations folder relative to this package (not process.cwd())
44
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
45
+ const migrationsFolder = path.join(__dirname, '..', '..', 'drizzle');
50
46
 
51
- sqlite.exec(`
52
- CREATE TABLE IF NOT EXISTS chats (
53
- id TEXT PRIMARY KEY,
54
- user_id TEXT NOT NULL,
55
- title TEXT NOT NULL DEFAULT 'New Chat',
56
- created_at INTEGER NOT NULL,
57
- updated_at INTEGER NOT NULL
58
- )
59
- `);
60
-
61
- sqlite.exec(`
62
- CREATE TABLE IF NOT EXISTS messages (
63
- id TEXT PRIMARY KEY,
64
- chat_id TEXT NOT NULL,
65
- role TEXT NOT NULL,
66
- content TEXT NOT NULL,
67
- created_at INTEGER NOT NULL
68
- )
69
- `);
70
-
71
- sqlite.exec(`
72
- CREATE TABLE IF NOT EXISTS notifications (
73
- id TEXT PRIMARY KEY,
74
- notification TEXT NOT NULL,
75
- payload TEXT NOT NULL,
76
- read INTEGER NOT NULL DEFAULT 0,
77
- created_at INTEGER NOT NULL
78
- )
79
- `);
80
-
81
- sqlite.exec(`
82
- CREATE TABLE IF NOT EXISTS subscriptions (
83
- id TEXT PRIMARY KEY,
84
- platform TEXT NOT NULL,
85
- channel_id TEXT NOT NULL,
86
- created_at INTEGER NOT NULL
87
- )
88
- `);
89
-
90
- sqlite.exec(`
91
- CREATE TABLE IF NOT EXISTS settings (
92
- id TEXT PRIMARY KEY,
93
- type TEXT NOT NULL,
94
- key TEXT NOT NULL,
95
- value TEXT NOT NULL,
96
- created_by TEXT,
97
- created_at INTEGER NOT NULL,
98
- updated_at INTEGER NOT NULL
99
- )
100
- `);
101
-
102
- // Migration: add starred column to chats
103
- try {
104
- sqlite.exec(`ALTER TABLE chats ADD COLUMN starred INTEGER NOT NULL DEFAULT 0`);
105
- } catch {}
47
+ migrate(db, { migrationsFolder });
106
48
 
107
49
  sqlite.close();
108
50
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thepopebot",
3
- "version": "1.2.56",
3
+ "version": "1.2.57",
4
4
  "type": "module",
5
5
  "description": "Create autonomous AI agents with a two-layer architecture: Next.js Event Handler + Docker Agent.",
6
6
  "bin": {
@@ -25,13 +25,16 @@
25
25
  "api/",
26
26
  "config/",
27
27
  "setup/",
28
- "templates/"
28
+ "templates/",
29
+ "drizzle/"
29
30
  ],
30
31
  "scripts": {
31
32
  "build": "esbuild lib/chat/components/*.jsx lib/chat/components/**/*.jsx --outdir=lib/chat/components --format=esm --jsx=automatic --outbase=lib/chat/components",
32
33
  "prepublishOnly": "npm run build",
33
34
  "local": "bash bin/local.sh",
34
35
  "postinstall": "if [ -f bin/postinstall.js ]; then node bin/postinstall.js; fi",
36
+ "db:generate": "drizzle-kit generate",
37
+ "db:studio": "drizzle-kit studio",
35
38
  "test": "echo \"No tests yet\" && exit 0"
36
39
  },
37
40
  "keywords": [
@@ -85,6 +88,7 @@
85
88
  }
86
89
  },
87
90
  "devDependencies": {
91
+ "drizzle-kit": "^0.31.9",
88
92
  "esbuild": "^0.27.3"
89
93
  },
90
94
  "engines": {
@@ -40,7 +40,6 @@ jobs:
40
40
  if [ -n "$OLD_TPB" ] && [ "$OLD_TPB" != "$NEW_TPB" ]; then
41
41
  # Version changed — run thepopebot init to scaffold new templates
42
42
  npx thepopebot init
43
- npm install --omit=dev
44
43
 
45
44
  # Commit any template changes from init
46
45
  git add -A
@@ -26,7 +26,7 @@ jobs:
26
26
  git config user.name "thepopebot"
27
27
  git config user.email "thepopebot@users.noreply.github.com"
28
28
 
29
- npm install
29
+ npm install --omit=dev
30
30
  OLD_VERSION=$(node -p "require(\"./node_modules/thepopebot/package.json\").version")
31
31
  npm update thepopebot
32
32
  NEW_VERSION=$(node -p "require(\"./node_modules/thepopebot/package.json\").version")
@@ -55,12 +55,19 @@ This is an autonomous AI agent powered by [thepopebot](https://github.com/stephe
55
55
 
56
56
  ### Updating thepopebot
57
57
 
58
- When the package is updated via `npm update thepopebot`, template changes are **not** applied automatically — your customizations are preserved. To check for updates:
58
+ When the package is updated via `npm update thepopebot`, run `npx thepopebot init` to apply changes:
59
59
 
60
- 1. `npm update thepopebot` updates the package
61
- 2. `npx thepopebot init` reports which templates have drifted (does not overwrite)
62
- 3. `npx thepopebot diff <file>` — review what changed
63
- 4. `npx thepopebot reset <file>` — accept the new template, or manually merge
60
+ - **Managed files** (GitHub workflows, docker-compose, event-handler Dockerfile) are **auto-updated** to match the package version. These are infrastructure files that must stay in the project but shouldn't drift.
61
+ - **User-editable files** (config, app pages, job Dockerfile) are **never overwritten**. Drifted files are reported so you can review them.
62
+
63
+ ```
64
+ 1. npm update thepopebot — updates the package
65
+ 2. npx thepopebot init — auto-updates managed files, reports drifted user files
66
+ 3. npx thepopebot diff <file> — review what changed in a user-editable file
67
+ 4. npx thepopebot reset <file> — accept the new template, or manually merge
68
+ ```
69
+
70
+ Use `npx thepopebot init --no-managed` to skip auto-updates of managed files (for power users who customize workflows).
64
71
 
65
72
  ## API Endpoints
66
73