@appweaver/cli 1.0.24 → 1.1.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appweaver/cli",
3
- "version": "1.0.24",
3
+ "version": "1.1.0",
4
4
  "description": "Appweaver - the backend framework for AI-first development (@cli)",
5
5
  "author": "Luka Matosevic",
6
6
  "license": "MIT",
@@ -141,6 +141,10 @@ Use `createAuthModel` and `createAuthService` for authenticatable users. They mu
141
141
  `createAuthModel` adds: `email`, `passwordHash`, `verifiedEmail`, `twoFactorAuth`, `enabled`, `logoutAt` scalars; a
142
142
  virtual `password` field; a `roles` relation; and optional `apiKeys` relation.
143
143
 
144
+ `createAuthService` supports an optional `registrationData` callback to customize the registration payload and an
145
+ optional `checkOAuth2User` callback to allow or reject OAuth2 registrations/logins (return nothing to proceed, or a
146
+ string/`Error` to abort).
147
+
144
148
  ```ts
145
149
  // src/resources/user/model.ts
146
150
  import { createAuthModel } from '@appweaver/core';
@@ -241,7 +245,7 @@ export async function createAdminUser(): Promise<void> {
241
245
  data: {
242
246
  firstName: 'Admin',
243
247
  lastName: 'Admin',
244
- email: 'admin@appweaver.com',
248
+ email: 'admin@appweaver.co',
245
249
  roles: {
246
250
  connectOrCreate: [
247
251
  {
package/skill/SKILL.md CHANGED
@@ -78,7 +78,7 @@ create-weaver-app MyBlogAPI "My own CMS for blogging" --database postgresql --no
78
78
  ```
79
79
 
80
80
  This creates a `./my-blog-api` directory, installs all dependencies, and runs the initial schema and type generation.
81
- Default test runner is `jest` with `swc` transpiler.
81
+ The default test runner is `jest` with `swc` transpiler.
82
82
 
83
83
  **Example — Bun project with Sqlite:**
84
84
 
@@ -86,8 +86,8 @@ Default test runner is `jest` with `swc` transpiler.
86
86
  create-weaver-app BunApp "Bun application with simple API" --bun --database sqlite
87
87
  ```
88
88
 
89
- This creates a `./bun-app` directory, installs all dependencies using bun package manager, and runs the initial
90
- schema and type generation. Default test runner is `bun`.
89
+ This creates a `./bun-app` directory, installs all dependencies using bun package manager, and runs the initial schema
90
+ and type generation. The default test runner is `bun`.
91
91
 
92
92
  After the application is scaffolded, the following commands need to be run to finish the application setup:
93
93
 
@@ -314,11 +314,14 @@ Use `createAuthModel` and `createAuthService` instead of `createModel`/`createSe
314
314
  authenticatable user. They cannot be used independently! If an auth model is created, then also auth service must exist.
315
315
 
316
316
  `createAuthModel` extends the config with: `email`, `passwordHash`, `verifiedEmail`, `twoFactorAuth`, `enabled`,
317
- `logoutAt` scalars; a virtual `password` field (write-only); a `roles` relation; and an optional `apiKeys` relation (
318
- when `SECURITY_API_KEY_ENABLED` is set).
317
+ `logoutAt` scalars; a virtual `password` field (write-only); a `roles` relation; and an optional `apiKeys` relation
318
+ (when `SECURITY_API_KEY_ENABLED` is set).
319
319
 
320
- `createAuthService` extends the config with automatic password hashing on create/update and an optional
321
- `registrationData` callback to customize registration payload.
320
+ `createAuthService` extends the config with automatic password hashing on create/update, an optional
321
+ `registrationData` callback to customize registration payload (for OAuth2 logins its `additionalData` argument includes
322
+ `firstName`, `lastName`, `avatarUrl`, and — when `SECURITY_OAUTH2_FETCH_AVATAR_ENABLED` is set — a downloaded
323
+ `avatarFile`), and an optional `checkOAuth2User` callback invoked before a user is registered or authenticated via
324
+ OAuth2 (return nothing to proceed, or a string/`Error`/`HttpError` to abort the login with an error).
322
325
 
323
326
  ```ts
324
327
  // src/resources/user/model.ts
@@ -356,7 +359,9 @@ export default createAuthService({
356
359
 
357
360
  Use `registerRoute` to register a custom [Fastify route](https://fastify.dev/docs/latest/Reference/Routes/) handler. The
358
361
  handler is a Fastify plugin function that defines one or more routes. An optional config object controls authentication,
359
- caching, and reCAPTCHA behavior.
362
+ caching, and reCAPTCHA behavior. When a custom route's 2xx response schema references resource output models (`<Name>`,
363
+ `<Name>Single` or `<Name>Multiple` — directly or nested inside custom schemas), virtual field values (e.g. `File.url`)
364
+ are projected onto the response payload automatically before serialization.
360
365
 
361
366
  ```ts
362
367
  // src/plugins/custom-route.ts
@@ -422,8 +427,8 @@ registerPlugin('audit-log', async (server) => {
422
427
 
423
428
  ### Dependency injection
424
429
 
425
- Use `define` to register a value or class in the app context, and `inject` to retrieve it. Class constructors are
426
- lazily instantiated as singletons on the first injection.
430
+ Use `define` to register a value or class in the app context, and `inject` to retrieve it. Class constructors are lazily
431
+ instantiated as singletons on the first injection.
427
432
 
428
433
  ```ts
429
434
  import { Cache } from '@appweaver/common';
@@ -472,7 +477,7 @@ export async function createAdminUser(): Promise<void> {
472
477
  data: {
473
478
  firstName: 'Admin',
474
479
  lastName: 'Admin',
475
- email: 'admin@appweaver.com',
480
+ email: 'admin@appweaver.co',
476
481
  phone: '01234435',
477
482
  roles: {
478
483
  connectOrCreate: [
@@ -554,7 +559,6 @@ weaver update # update all @appweaver/* packages
554
559
  weaver update @appweaver/core @appweaver/cli # update specific packages
555
560
  weaver update --targetVersion 1.2.3 # update to a specific version
556
561
  weaver update --noSkill # skip updating AI agent skill files (.claude, .agents, …)
557
- weaver update --noGuidelines # skip updating AI agent guideline files (AGENTS.md, CLAUDE.md)
558
562
  weaver update --force # force update despite peerDependency mismatches
559
563
  ```
560
564
 
@@ -205,10 +205,9 @@ Update the Appweaver packages.
205
205
 
206
206
  **Options:**
207
207
 
208
- | Option | Description | Default |
209
- |-----------------------------------|-------------------------------------------------------------------------|------------|
210
- | `--targetVersion [targetVersion]` | The version to update the packages to | `"latest"` |
208
+ | Option | Description | Default |
209
+ |-----------------------------------|-----------------------------------------------------------------------------|------------|
210
+ | `--targetVersion [targetVersion]` | The version to update the packages to | `"latest"` |
211
211
  | `--noSkill` | Skip updating AI agents skill files in agent dirs (`.claude`, `.agents`, …) | `false` |
212
- | `--noGuidelines` | Skip updating AI agents guideline files (`AGENTS.md`, `CLAUDE.md`) | `false` |
213
- | `-f, --force` | Force update despite peerDependency version mismatches | `false` |
214
- | `--verbose` | Print verbose output | `false` |
212
+ | `-f, --force` | Force update despite peerDependency version mismatches | `false` |
213
+ | `--verbose` | Print verbose output | `false` |
@@ -256,9 +256,11 @@ The config object is frozen with `Object.freeze()` after loading to prevent runt
256
256
 
257
257
  #### OAuth2 general
258
258
 
259
- | Property | Type | Default | Description |
260
- |-----------------------------|---------|----------|--------------------------------------------------------------|
261
- | `SECURITY_OAUTH2_STATE_TTL` | integer | `600000` | OAuth2 state parameter TTL in milliseconds (default 10 min). |
259
+ | Property | Type | Default | Description |
260
+ |----------------------------------------|---------|----------|-----------------------------------------------------------------------------------------------------------------------------------|
261
+ | `SECURITY_OAUTH2_STATE_TTL` | integer | `600000` | OAuth2 state parameter TTL in milliseconds (default 10 min). |
262
+ | `SECURITY_OAUTH2_REGISTRATION_ENABLED` | boolean | `true` | Allow registering new users via OAuth2 login. When `false`, only already existing users (matched by email) can log in via OAuth2. |
263
+ | `SECURITY_OAUTH2_FETCH_AVATAR_ENABLED` | boolean | `false` | Download the user's avatar from the OAuth2 provider during registration and pass it as `avatarFile` to `registrationData`. |
262
264
 
263
265
  #### OAuth2 Google
264
266
 
@@ -1,9 +1,9 @@
1
1
  # Resources
2
2
 
3
- Resources are the core building blocks of an Appweaver application. There are four resource types that form a
4
- dependency chain: **model** → **service** → **routes** → **policy**. Each resource type is created using a
5
- corresponding factory function and autoloaded from `src/resources/*/` on application start. Source directory and
6
- resources pattern could be changed with `APP_SOURCE_PATH` and `RESOURCE_{MODEL,SERVICE,...}_PATTERN` config variables.
3
+ Resources are the core building blocks of an Appweaver application. There are four resource types that form a dependency
4
+ chain: **model** → **service** → **routes** → **policy**. Each resource type is created using a corresponding factory
5
+ function and autoloaded from `src/resources/*/` on application start. Source directory and resources pattern could be
6
+ changed with `APP_SOURCE_PATH` and `RESOURCE_{MODEL,SERVICE,...}_PATTERN` config variables.
7
7
 
8
8
  - A **model** is always required.
9
9
  - A **service** requires a model.
@@ -14,9 +14,9 @@ resources pattern could be changed with `APP_SOURCE_PATH` and `RESOURCE_{MODEL,S
14
14
 
15
15
  ## createModel
16
16
 
17
- Creates a resource model definition. The model defines database fields, relations, files, virtual fields, DTOs for
18
- CRUD operations, and index configuration. It is used to generate Prisma schema, TypeScript types, and route
19
- request/response schemas.
17
+ Creates a resource model definition. The model defines database fields, relations, files, virtual fields, DTOs for CRUD
18
+ operations, and index configuration. It is used to generate Prisma schema, TypeScript types, and route request/response
19
+ schemas.
20
20
 
21
21
  ```ts
22
22
  import { createModel } from '@appweaver/core';
@@ -505,8 +505,8 @@ are passed through unchanged.
505
505
  | `maxHeight` | number | Maximum height. Only downscales if the image exceeds this dimension. |
506
506
  | `fit` | ImageFit | How the image fits the target dimensions: `'inside'` (default), `'contain'`, `'cover'`, `'fill'`, `'outside'`. |
507
507
 
508
- `width`/`height` take precedence over `maxWidth`/`maxHeight`. When using `maxWidth`/`maxHeight`, images smaller than
509
- the specified dimensions are not enlarged.
508
+ `width`/`height` take precedence over `maxWidth`/`maxHeight`. When using `maxWidth`/`maxHeight`, images smaller than the
509
+ specified dimensions are not enlarged.
510
510
 
511
511
  ```ts
512
512
  // Compress and limit dimensions
@@ -566,6 +566,17 @@ const config = {
566
566
  | `output.type` | `'always'` \| `'single'` \| `'multiple'` \| `'none'` | When the virtual field appears in output. |
567
567
  | `output.value` | primitive \| function | Computed value or transformer for output. |
568
568
 
569
+ Virtual output values are applied automatically to responses of resource CRUD routes (including nested relation and file
570
+ objects) and to responses of custom `registerRoute` routes whose 2xx response schemas reference resource output models.
571
+ To apply them manually on a raw resource object (e.g. one fetched directly through a Prisma client), use the
572
+ `projectVirtualFields` helper:
573
+
574
+ ```ts
575
+ import { projectVirtualFields } from '@appweaver/core';
576
+
577
+ const projected = projectVirtualFields(post, 'Post'); // sets virtual values, recursing into relations and files
578
+ ```
579
+
569
580
  ### Operation config (read, create, update)
570
581
 
571
582
  Control which fields appear in each DTO. Use `pick` for an allowlist or `omit` for a deny-list.
@@ -988,8 +999,8 @@ registerModel(
988
999
 
989
1000
  ## registerPlugin
990
1001
 
991
- Registers a custom Fastify plugin. Plugins are wrapped with `fastify-plugin` so their decorators and hooks are scoped
992
- to the entire server instance.
1002
+ Registers a custom Fastify plugin. Plugins are wrapped with `fastify-plugin` so their decorators and hooks are scoped to
1003
+ the entire server instance.
993
1004
 
994
1005
  ```ts
995
1006
  import { registerPlugin } from '@appweaver/core';
@@ -212,7 +212,9 @@ follow the same flow pattern.
212
212
  6. Server verifies the state token (one-time use)
213
213
  7. Server exchanges the code for an access token with the provider
214
214
  8. Server fetches user info from the provider
215
- 9. Server creates or finds the user by email
215
+ 9. Server finds the user by email and invokes the optional checkOAuth2User callback
216
+ (aborts with an error when the callback returns a string or an Error).
217
+ New users are registered unless SECURITY_OAUTH2_REGISTRATION_ENABLED=false
216
218
  10. Server generates an authentication OTT
217
219
  11. Server redirects to the original URL with the token:
218
220
  -> https://myapp.com/dashboard?token={ott}
@@ -259,7 +261,7 @@ SECURITY_OAUTH2_GOOGLE_CLIENT_SECRET=your-google-client-secret
259
261
 
260
262
  **Scopes**: `profile`, `email`
261
263
 
262
- **User info extracted**: `email`, `given_name` (firstName), `family_name` (lastName)
264
+ **User info extracted**: `email`, `given_name` (firstName), `family_name` (lastName), `picture` (avatarUrl)
263
265
 
264
266
  **Google Cloud Console setup:**
265
267
 
@@ -304,7 +306,7 @@ SECURITY_OAUTH2_FACEBOOK_CLIENT_SECRET=your-facebook-app-secret
304
306
 
305
307
  **Scopes**: `public_profile`, `email`
306
308
 
307
- **User info extracted**: `email`, `name` (split into firstName/lastName)
309
+ **User info extracted**: `email`, `name` (split into firstName/lastName), `picture` (avatarUrl)
308
310
 
309
311
  **Facebook Developer Console setup:**
310
312
 
@@ -346,7 +348,48 @@ For any OpenID Connect-compatible provider (Keycloak, Auth0, etc.).
346
348
 
347
349
  **User info endpoint**: `{issuer}/protocol/openid-connect/userinfo`
348
350
 
349
- **Standard claims expected**: `sub`, `email`, `given_name`, `family_name`
351
+ **Standard claims expected**: `sub`, `email`, `given_name`, `family_name`, `picture` (optional, avatarUrl)
352
+
353
+ ### OAuth2 registration control and hooks
354
+
355
+ **Disable OAuth2 registration** — set `SECURITY_OAUTH2_REGISTRATION_ENABLED=false` (JSON:
356
+ `security.oauth2.registrationEnabled`) to prevent new users from being created during OAuth2 login. Only users that
357
+ already exist in the database (matched by email) can then log in via OAuth2; unknown emails receive a 403 error.
358
+
359
+ **`checkOAuth2User` callback** — an optional callback on `createAuthService` invoked on every OAuth2 login, before a
360
+ user is registered or authenticated. It receives the auth source, the user info extracted from the provider, and the
361
+ existing auth user (or `null` when the user would be newly registered). Return nothing to proceed, or return a string,
362
+ `Error`, or `HttpError` to abort the flow (a 403 error is thrown, or the `HttpError` as-is):
363
+
364
+ ```ts
365
+ // src/resources/user/service.ts
366
+ import { AuthSource } from '@appweaver/common';
367
+ import { createAuthService, HttpError } from '@appweaver/core';
368
+
369
+ export default createAuthService({
370
+ modelName: 'User',
371
+ checkOAuth2User: (source, userInfo, authUser) => {
372
+ if (!userInfo.email.endsWith('@mycompany.com')) {
373
+ return new HttpError('Only company accounts are allowed', 403);
374
+ }
375
+ if (!authUser && source === AuthSource.OAuth2Facebook) {
376
+ return 'New accounts cannot be created via Facebook';
377
+ }
378
+ // Return nothing to proceed with registration/login
379
+ },
380
+ registrationData: (source, email, password, additionalData) => ({
381
+ email,
382
+ password,
383
+ name: `${additionalData?.firstName} ${additionalData?.lastName}`
384
+ })
385
+ });
386
+ ```
387
+
388
+ **User avatar** — the provider's avatar/picture URL is passed to `registrationData` as `additionalData.avatarUrl`.
389
+ When `SECURITY_OAUTH2_FETCH_AVATAR_ENABLED=true` (JSON: `security.oauth2.fetchAvatarEnabled`), the avatar image is
390
+ also downloaded during registration and passed as `additionalData.avatarFile`
391
+ (`{ name, mimeType, size, data: Buffer }`), so it can be mapped to a model field or stored via the file service. The
392
+ download is best-effort: failures are logged and registration proceeds without the file.
350
393
 
351
394
  ### Client-side OAuth2 integration example
352
395
 
@@ -1,8 +1,8 @@
1
1
  # Storage
2
2
 
3
- The storage module handles file persistence: storing, streaming, and deleting binary content. The default
4
- implementation (`FilesystemStorage`) writes files to a local directory using a configurable name pattern. The module
5
- supports range-based streaming for efficient large-file delivery (e.g., video, audio).
3
+ The storage module handles file persistence: storing, streaming, and deleting binary content. The default implementation
4
+ (`FilesystemStorage`) writes files to a local directory using a configurable name pattern. The module supports
5
+ range-based streaming for efficient large-file delivery (e.g., video, audio).
6
6
 
7
7
  ## Injecting Storage
8
8
 
@@ -221,6 +221,35 @@ export class PostService {
221
221
  }
222
222
  ```
223
223
 
224
+ ### File integrity (checksum)
225
+
226
+ When a file is uploaded through `FileService.saveFile()` (or the resource file upload routes), a **SHA-256 checksum**
227
+ (hex-encoded) of the stored content is calculated during the upload and persisted on the `File` record in the
228
+ `checksum` field. The checksum is calculated over the exact bytes written to storage (i.e., after any configured image
229
+ processing), so it can be used at any later point to verify that the file on disk has not been modified or corrupted.
230
+
231
+ The checksum is included in file API responses, so clients can verify downloaded content against it.
232
+
233
+ To verify a file's integrity, recalculate the checksum with the `makeHash` utility from `@appweaver/common` and
234
+ compare it with the stored value:
235
+
236
+ ```ts
237
+ import { createReadStream } from 'node:fs';
238
+ import { makeHash } from '@appweaver/common';
239
+
240
+ // From a readable stream (no memory buffering, works for large files) — returns a promise
241
+ const checksum = await makeHash(createReadStream('/path/to/stored/file'));
242
+
243
+ // Or from a Buffer / string — returns the hash synchronously
244
+ // const checksum = makeHash(downloadedBuffer);
245
+
246
+ if (checksum !== file.checksum) {
247
+ throw new Error(`File ${file.name} has been modified or corrupted`);
248
+ }
249
+ ```
250
+
251
+ Stored file checksums always use the defaults: **`sha256` + `hex`**.
252
+
224
253
  ### Streaming a file
225
254
 
226
255
  `FileService.stream()` handles authorization checks (public, private, or protected) and range-based requests
@@ -14,14 +14,12 @@ function updateCommand(program) {
14
14
  'Defaults to all currently installed @appweaver/* packages.')
15
15
  .option('--targetVersion [targetVersion]', 'The version to update the packages.', 'latest')
16
16
  .option('--noSkill', 'Skip updating AI agents skill files in the agent directories (e.g. .claude, .agents) of the current project.')
17
- .option('--noGuidelines', 'Skip updating AI agents guideline files (e.g. AGENTS.md, CLAUDE.md) in the current project.')
18
17
  .option('-f, --force', 'Force update despite peerDependency version mismatches.')
19
18
  .option('--verbose', 'Print verbose output.')
20
19
  .action(async (packages, _, command) => {
21
20
  const quiet = !command.getOptionValue('verbose');
22
21
  const force = command.getOptionValue('force');
23
22
  const updateSkill = !command.getOptionValue('noSkill');
24
- const updateGuidelines = !command.getOptionValue('noGuidelines');
25
23
  const targetVersion = command.getOptionValue('targetVersion');
26
24
  // Load all currently installed packages
27
25
  const installedPackages = {};
@@ -73,8 +71,8 @@ function updateCommand(program) {
73
71
  }
74
72
  const status = await (0, update_packages_1.updatePackages)(appweaverPackages, targetVersion, force, quiet);
75
73
  if (status === 0) {
76
- if (updateSkill || updateGuidelines) {
77
- await (0, update_skill_1.updateSkillFiles)(quiet, updateSkill, updateGuidelines);
74
+ if (updateSkill) {
75
+ await (0, update_skill_1.updateSkillFiles)(quiet);
78
76
  }
79
77
  console.log(`Successfully updated packages to ${targetVersion} version.`);
80
78
  }
@@ -1,10 +1,13 @@
1
1
  /**
2
- * Updates skill files and AI guidelines in the project by copying the skill directory
3
- * to specified agent directories and updating references in guideline files.
2
+ * Updates the Appweaver skill files in the project by copying the skill
3
+ * directory (including the framework GUIDELINES.md) into every discovered agent
4
+ * directory (e.g. `.claude`, `.agents`).
4
5
  *
5
- * @param {boolean} quiet - If true, suppresses logging output; otherwise, logs actions performed.
6
- * @param {boolean} updateSkill - If true, copies skill files into agent directories (e.g. .claude, .agents).
7
- * @param {boolean} updateGuidelines - If true, updates AI guideline files (e.g. AGENTS.md, CLAUDE.md).
6
+ * The project's own root guidelines file (`AGENTS.md` / `CLAUDE.md`) is never
7
+ * touched here — it only references the framework guidelines from the skills
8
+ * directory, so it can be freely extended in the project.
9
+ *
10
+ * @param {boolean} quiet - If true, suppresses logging output; otherwise, logs actions are performed.
8
11
  * @return {Promise<void>} A promise that resolves when the update process is complete.
9
12
  */
10
- export declare function updateSkillFiles(quiet: boolean, updateSkill?: boolean, updateGuidelines?: boolean): Promise<void>;
13
+ export declare function updateSkillFiles(quiet: boolean): Promise<void>;
@@ -8,18 +8,18 @@ const node_fs_1 = __importDefault(require("node:fs"));
8
8
  const promises_1 = __importDefault(require("node:fs/promises"));
9
9
  const node_path_1 = __importDefault(require("node:path"));
10
10
  /**
11
- * Updates skill files and AI guidelines in the project by copying the skill directory
12
- * to specified agent directories and updating references in guideline files.
11
+ * Updates the Appweaver skill files in the project by copying the skill
12
+ * directory (including the framework GUIDELINES.md) into every discovered agent
13
+ * directory (e.g. `.claude`, `.agents`).
13
14
  *
14
- * @param {boolean} quiet - If true, suppresses logging output; otherwise, logs actions performed.
15
- * @param {boolean} updateSkill - If true, copies skill files into agent directories (e.g. .claude, .agents).
16
- * @param {boolean} updateGuidelines - If true, updates AI guideline files (e.g. AGENTS.md, CLAUDE.md).
15
+ * The project's own root guidelines file (`AGENTS.md` / `CLAUDE.md`) is never
16
+ * touched here — it only references the framework guidelines from the skills
17
+ * directory, so it can be freely extended in the project.
18
+ *
19
+ * @param {boolean} quiet - If true, suppresses logging output; otherwise, logs actions are performed.
17
20
  * @return {Promise<void>} A promise that resolves when the update process is complete.
18
21
  */
19
- async function updateSkillFiles(quiet, updateSkill = true, updateGuidelines = true) {
20
- if (!updateSkill && !updateGuidelines) {
21
- return;
22
- }
22
+ async function updateSkillFiles(quiet) {
23
23
  const projectDir = process.cwd();
24
24
  const skillDir = node_path_1.default.join(__dirname, '..', 'skill');
25
25
  if (!(await exists(skillDir))) {
@@ -28,9 +28,6 @@ async function updateSkillFiles(quiet, updateSkill = true, updateGuidelines = tr
28
28
  }
29
29
  return;
30
30
  }
31
- const guidelinesFilePath = node_path_1.default.join(skillDir, 'GUIDELINES.md');
32
- const guidelinesContents = await promises_1.default.readFile(guidelinesFilePath, 'utf8');
33
- const foundAgentDirs = [];
34
31
  for (const agentDir of [
35
32
  '.claude',
36
33
  '.junie',
@@ -44,60 +41,13 @@ async function updateSkillFiles(quiet, updateSkill = true, updateGuidelines = tr
44
41
  if (!(await exists(agentDirPath))) {
45
42
  continue;
46
43
  }
47
- foundAgentDirs.push(agentDir);
48
- // Skip copying skill files when disabled, but keep the discovered agent
49
- // dir so guideline references can still point to it.
50
- if (!updateSkill) {
51
- continue;
52
- }
53
44
  // Copy skill directory to {agentDir}/skills/appweaver/
54
45
  const skillDestPath = node_path_1.default.join(agentDirPath, 'skills', 'appweaver');
55
- await promises_1.default.cp(skillDir, skillDestPath, {
56
- recursive: true,
57
- filter: (src) => !src.endsWith('GUIDELINES.md')
58
- });
46
+ await promises_1.default.cp(skillDir, skillDestPath, { recursive: true });
59
47
  if (!quiet) {
60
48
  console.log(`Updated skill files in ${node_path_1.default.join(agentDir, 'skills', 'appweaver')}\n`);
61
49
  }
62
50
  }
63
- // Nothing more to do when guideline files should not be updated
64
- if (!updateGuidelines) {
65
- return;
66
- }
67
- let firstAgentDir = foundAgentDirs[0];
68
- for (const guidelinesFile of ['AGENTS.md', 'CLAUDE.md']) {
69
- const guidelinesFilePath = node_path_1.default.join(projectDir, guidelinesFile);
70
- // Update only agent guidelines files that already exist
71
- if (!(await exists(guidelinesFilePath))) {
72
- continue;
73
- }
74
- // If no agent-specific dir was discovered, fall back to a generic .agents
75
- // dir and, unless skill updates are disabled, populate it with skill files.
76
- if (!firstAgentDir) {
77
- firstAgentDir = '.agents';
78
- if (updateSkill) {
79
- const skillDestPath = node_path_1.default.join(node_path_1.default.join(projectDir, firstAgentDir), 'skills', 'appweaver');
80
- await promises_1.default.cp(skillDir, skillDestPath, {
81
- recursive: true,
82
- filter: (src) => !src.endsWith('GUIDELINES.md')
83
- });
84
- }
85
- }
86
- // Replace guideline file path references with path references in first
87
- // discovered agents dir
88
- const referencesPath = node_path_1.default
89
- .join(firstAgentDir, 'skills', 'appweaver', 'references')
90
- .replace(/\\/g, '/');
91
- const guidelinesContent = guidelinesContents.replace(/(\[.+]\()references\/(.+\))/g, `$1${referencesPath}/$2`);
92
- await promises_1.default.writeFile(guidelinesFilePath, guidelinesContent, {
93
- encoding: 'utf8'
94
- });
95
- if (!quiet) {
96
- console.log(`Updated AI guidelines file ${guidelinesFile}\n`);
97
- }
98
- // Update only the first found guidelines file
99
- break;
100
- }
101
51
  }
102
52
  async function exists(filePath) {
103
53
  try {