@dboio/cli 0.15.2 → 0.16.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.
package/README.md CHANGED
@@ -134,8 +134,7 @@ my-project/
134
134
  │ ├── entity_column_value/ # Column value definitions
135
135
  │ ├── extension/ # Extension entity records (organized by descriptor type)
136
136
  │ │ ├── widget/
137
- │ │ ├── documentation/
138
- │ │ └── _unsupported/
137
+ │ │ └── documentation/
139
138
  │ ├── integration/ # Integration entity records
140
139
  │ ├── security/ # Security policy records
141
140
  │ ├── security_column/ # Column-level security records
@@ -148,6 +147,7 @@ my-project/
148
147
  ├── trash/ # Staged soft-deleted files from dbo rm
149
148
  ├── docs/ # Project documentation and docs entities
150
149
  ├── app.json # Clone of original app JSON with @references
150
+ ├── schema.json # Instance-level entity/column schema fetched from the server during `dbo init`. Refreshed explicitly with `dbo clone --schema`.
151
151
  ├── manifest.json # PWA web app manifest (auto-generated from app metadata)
152
152
  ├── .gitignore # Tells Git to ignore files in the repo sync
153
153
  ├── .dboignore # Tells dbo cli to ignore files in commands
@@ -178,7 +178,7 @@ All configuration is **directory-scoped**. Each project folder maintains its own
178
178
  | `credentials.json` | Username, user ID, UID, name, email (no password) | Gitignored (per-user) |
179
179
  | `cookies.txt` | Session cookie (Netscape format) | Gitignored (per-user) |
180
180
  | `structure.json` | Bin directory mapping (created by `dbo clone`) | Committable (shared) |
181
- | `metadata_templates.json` | Column templates per entity/descriptor (auto-generated by `dbo clone`) | Committable (shared) |
181
+ | `metadata_schema.json` | Column templates per entity/descriptor (auto-generated by `dbo clone`; renamed from `metadata_templates.json`) | Committable (shared) |
182
182
  | `synchronize.json` | Pending deletions and sync state (updated by `dbo rm` and `dbo push`) | Committable (shared) |
183
183
  | `.app_baseline.json` | Server-state snapshot for delta detection — stores column values at last clone/push so `dbo push` can detect which columns changed locally. Read-only (chmod 444); auto-migrated from legacy root `.app.json`. | Committable (shared) |
184
184
  | `scripts.json` | Build/push lifecycle hooks (see [Script Hooks](#script-hooks)) | Committable (shared) |
@@ -201,6 +201,8 @@ All configuration is **directory-scoped**. Each project folder maintains its own
201
201
  > | Output | `Sales.metadata~ghi789.json` |
202
202
  > | Extension | `MyExtension.metadata~jkl012.json` |
203
203
 
204
+ > **Upgrading to 0.16.0+**: `.dbo/metadata_templates.json` is renamed to `.dbo/metadata_schema.json`. The `_contentColumns` field in metadata files is renamed to `_companionReferenceColumns`. Extension companion file prompts during `dbo clone` are replaced by automatic derivation from `descriptor_definition` `form-control-code` data. Migration 011 runs automatically on first use.
205
+
204
206
  #### Automatic migrations
205
207
 
206
208
  When the CLI version is upgraded, one-time migration scripts in `tools/cli/src/migrations/` run automatically on the first command invocation in a project directory. Completed migration IDs are recorded in `.dbo/config.local.json` under `_completedMigrations` (per-user, gitignored) so each developer runs them independently and they never re-fire.
@@ -241,9 +243,37 @@ Use `dbo status` to see how many pending migrations exist.
241
243
  | `cloneSource` | `"default"` \| file path \| URL | Where the last `dbo clone` fetched app JSON from. `"default"` = server fetch via `AppShortName`; any other value = the explicit local file path or URL used. Set automatically after each successful clone. |
242
244
  | `ContentPlacement` | `bin` \| `path` | Where to place content files during clone (default: `bin`) |
243
245
  | `<Entity>FilenameCol` | column name | Filename column for entity-dir records (e.g., `ExtensionFilenameCol`) |
246
+ | `dependencies` | string[] | Array of app short-names to auto-clone as read-only local checkouts. Default: `["_system"]` |
247
+ | `dependencyLastUpdated` | object | Map of dependency short-name to last-cloned `_LastUpdated` ISO string |
244
248
 
245
249
  **File placement:** All content, media, and output files are placed by BinID into the `lib/bins/` directory structure. Records without a BinID go into `lib/bins/` root. Media always uses BinID — no FullPath option. Bins with `Name=null` (legacy) map directly to `lib/bins/` root.
246
250
 
251
+ ### App Dependencies
252
+
253
+ The CLI automatically maintains local read-only checkouts of related apps under `.dbo/dependencies/<shortname>/`. By default, the `_system` app (which contains all entity, column, and extension descriptors for the connected instance) is always included.
254
+
255
+ #### How it works
256
+
257
+ - After `dbo init` or `dbo clone`, dependency apps are cloned into `.dbo/dependencies/`.
258
+ - If an `app.json` contains a `Dependencies` column, those app short-names are merged into `.dbo/config.json` and also cloned.
259
+ - You can add dependencies explicitly with `dbo clone --dependencies operator,launchpad`.
260
+ - Dependencies are only re-cloned when the server's `_LastUpdated` is newer than the locally stored timestamp — so subsequent runs are fast.
261
+ - Dependency clone output is suppressed — a single summary line shows synced/failed/up-to-date status.
262
+ - `.dbo/dependencies/` is excluded from `.gitignore` and `.dboignore` automatically.
263
+
264
+ #### Schema merging from dependencies
265
+
266
+ After dependency cloning, extension descriptor definitions from dependency schemas (`.dbo/dependencies/<name>/.dbo/metadata_schema.json`) are merged into the local `.dbo/metadata_schema.json`. This is useful when an app has no `descriptor_definition` extensions of its own but depends on an app (like `operator`) that does — the operator's descriptor definitions (widget, include, control, etc.) become available for local extension processing.
267
+
268
+ #### Flags
269
+
270
+ | Flag | Commands | Effect |
271
+ |------|----------|--------|
272
+ | `--no-deps` | `dbo init`, `dbo clone` | Skip dependency syncing |
273
+ | `--dependencies <list>` | `dbo clone` | Add and sync specific dependencies (comma-separated short-names) |
274
+ | `--force` | `dbo clone` | Re-clone all dependencies regardless of staleness |
275
+ | `--schema` | `dbo clone` | Re-clone all dependencies regardless of staleness |
276
+
247
277
  ### Root-level project files
248
278
 
249
279
  #### `app.json`
@@ -367,6 +397,7 @@ dbo init --scaffold --yes # scaffold dirs non-intera
367
397
  | `--dboignore` | Create `.dboignore` with default patterns (use with `--force` to overwrite existing) |
368
398
  | `-y, --yes` | Skip all interactive prompts (legacy migration, Claude Code setup) |
369
399
  | `--non-interactive` | Alias for `--yes` |
400
+ | `--no-deps` | Skip dependency cloning after init |
370
401
 
371
402
  ---
372
403
 
@@ -402,9 +433,14 @@ dbo clone -e extension --descriptor-types false # Clone extensions flat (no de
402
433
  | `--documentation-only` | When used with `-e extension`, clone only documentation extensions |
403
434
  | `--descriptor-types <bool>` | Sort extensions into descriptor sub-directories (default: `true`). Set to `false` to use flat `extension/` layout |
404
435
  | `--domain <host>` | Override domain. Triggers a domain-change confirmation prompt when it differs from the project reference domain |
436
+ | `--schema` | Re-fetch `schema.json` from server before cloning |
405
437
  | `--force` | Skip source mismatch confirmation and change detection; re-processes all files |
406
438
  | `-y, --yes` | Auto-accept all prompts (also skips source mismatch confirmation) |
407
439
  | `-v, --verbose` | Show HTTP request details |
440
+ | `--no-deps` | Skip dependency cloning after clone |
441
+ | `--dependencies <list>` | Comma-separated app short-names to add and sync as dependencies (e.g. `--dependencies operator,launchpad`) |
442
+ | `--configure` | Re-prompt for filename columns, companion file extraction, and documentation placement preferences |
443
+ | `--media-placement <type>` | Set media placement: `fullpath` or `binpath` (default: `bin`) |
408
444
 
409
445
  #### Domain change detection
410
446
 
@@ -414,17 +450,19 @@ The project's reference domain is stored in `app.json._domain` (committed to git
414
450
 
415
451
  #### What clone does
416
452
 
417
- 1. **Loads app JSON** — from a local file, server API, or interactive prompt. A spinner shows progress while fetching from the server (responses can be slow as the JSON is assembled on demand)
418
- 2. **Updates `.dbo/config.json`** saves `AppID`, `AppUID`, `AppName`, `AppShortName`, `AppModifyKey` (if the app is locked), and `cloneSource` (the source used for this clone)
419
- 3. **Updates `package.json`**populates `name`, `productName`, `description`, `homepage`, and `deploy` script
420
- 4. **Creates directories**processes `children.bin` to build the directory hierarchy based on `ParentBinID` relationships
421
- 5. **Saves `.dbo/structure.json`** — maps BinIDs to directory paths for file placement
422
- 6. **Writes content files** — decodes base64 content, creates `*.metadata.json` + content files in the correct bin directory. When a record's `Extension` field is empty, prompts you to choose from `css`, `js`, `html`, `xml`, `txt`, `md`, `cs`, `json`, `sql` (or skip to keep no extension). The chosen extension is saved back into the `metadata.json` `Extension` field
423
- 7. **Downloads media files** fetches binary files (images, CSS, fonts) from the server using a fallback chain: `FullPath` directly (`/media/{app}/{path}`) → `/dir/` route → `/api/media/{uid}`, and saves with metadata. Errors are logged to `.dbo/errors.log`
424
- 8. **Processes entity-dir records** — entities matching project directories (`extension`, `app_version`, `data_source`, `site`, `group`, `integration`, `automation`) are saved as `.metadata.json` files in their corresponding directory (e.g., `extension/`, `data_source/`)
425
- 9. **Processes other entities** — remaining entities with a `BinID` are placed in the corresponding bin directory
426
- 10. **Saves `app.json`**clone of the original JSON with processed entries replaced by `@path/to/*.metadata.json` references
427
- 11. **Orphan cleanup** — any local `.metadata.json` files whose UID is absent from the server response are automatically moved to `trash/` along with their companion content and media files. This prevents stale records (deleted server-side) from causing false positives in `dbo push`. Skipped during `--entity-filter` clones
453
+ 1. **Fetches schema** — downloads `schema.json` from the server if missing or explicitly requested (`--schema`). Regenerates `.dbo/metadata_schema.json` from the schema
454
+ 2. **Syncs dependencies** — clones dependency apps into `.dbo/dependencies/<shortname>/` (unless `--no-deps`). Merges extension descriptor definitions from dependency schemas into the local `.dbo/metadata_schema.json` (e.g., operator provides descriptor definitions for apps that lack their own)
455
+ 3. **Loads app JSON** from a local file, server API, or interactive prompt. A spinner shows progress while fetching from the server (responses can be slow as the JSON is assembled on demand)
456
+ 4. **Updates `.dbo/config.json`**saves `AppID`, `AppUID`, `AppName`, `AppShortName`, `AppModifyKey` (if the app is locked), and `cloneSource` (the source used for this clone)
457
+ 5. **Updates `package.json`** — populates `name`, `productName`, `description`, `homepage`, and `deploy` script
458
+ 6. **Creates directories** — processes `children.bin` to build the directory hierarchy based on `ParentBinID` relationships
459
+ 7. **Saves `.dbo/structure.json`**maps BinIDs to directory paths for file placement
460
+ 8. **Writes content files** — decodes base64 content, creates `*.metadata.json` + content files in the correct bin directory. Filename columns and companion file extraction preferences are auto-applied with sensible defaults on first clone (use `--configure` to re-prompt)
461
+ 9. **Downloads media files** — fetches binary files (images, CSS, fonts) from the server using a fallback chain: `FullPath` directly (`/media/{app}/{path}`) `/dir/` route `/api/media/{uid}`, and saves with metadata. 404 errors create stale metadata to prevent re-prompting. Errors are logged to `.dbo/errors.log`
462
+ 10. **Processes entity-dir records** entities matching project directories (`extension`, `app_version`, `data_source`, `site`, `group`, `integration`, `automation`) are saved as `.metadata.json` files in their corresponding directory (e.g., `extension/`, `data_source/`)
463
+ 11. **Processes other entities** — remaining entities with a `BinID` are placed in the corresponding bin directory
464
+ 12. **Saves `app.json`** — clone of the original JSON with processed entries replaced by `@path/to/*.metadata.json` references
465
+ 13. **Orphan cleanup** — any local `.metadata.json` files whose UID is absent from the server response are automatically moved to `trash/` along with their companion content and media files. This prevents stale records (deleted server-side) from causing false positives in `dbo push`. Skipped during `--entity-filter` clones
428
466
 
429
467
  #### Clone source tracking
430
468
 
@@ -454,6 +492,8 @@ If the initial clone attempt fails (network error, file not found, empty respons
454
492
 
455
493
  When cloning an app that was already cloned locally, the CLI detects existing files and compares modification times against the server's `_LastUpdated`. You'll be prompted to overwrite, compare differences, or skip — same as `dbo pull`. Use `-y` to auto-accept all changes.
456
494
 
495
+ Change detection only checks companion content and media files for user edits — metadata file mtimes are not considered, since they can be bumped by CLI-internal operations (migrations, legacy companion renames).
496
+
457
497
  #### Collision detection
458
498
 
459
499
  When multiple records would create files at the same path (e.g., a `content` record and a `media` record both named `colors.css`), the CLI detects the collision before writing any files and prompts you to choose which record to keep:
@@ -471,7 +511,9 @@ In non-interactive mode (`-y`), the first record is kept and others are auto-sta
471
511
 
472
512
  #### Stale media cleanup
473
513
 
474
- During media downloads, files returning 404 (no longer exist on the server) are collected as "stale records". After all downloads complete, you'll be prompted to stage them for deletion:
514
+ During media downloads, files returning 404 (no longer exist on the server) are collected as "stale records". Metadata is still written for 404 files (marked with `_stale404: true`) so they are not re-prompted on subsequent clones.
515
+
516
+ After all downloads complete, you'll be prompted to stage stale records for deletion:
475
517
 
476
518
  ```
477
519
  Found 3 stale media record(s) (404 - files no longer exist on server)
@@ -507,9 +549,9 @@ Entity types that correspond to project directories (`extension`, `app_version`,
507
549
 
508
550
  For each entity type, the CLI prompts to choose which column becomes the filename (defaults to `Name`, fallback `UID`). The choice is saved per entity type in `config.json` (e.g., `ExtensionFilenameCol`).
509
551
 
510
- If any columns contain base64-encoded content, the CLI prompts to extract them as companion files. Extracted columns produce files named `<name>.<Column>.<ext>` alongside the metadata, with `@reference` entries in the metadata and a `_contentColumns` array.
552
+ If any columns contain base64-encoded content, the CLI prompts to extract them as companion files. Extracted columns produce files named `<name>.<Column>.<ext>` alongside the metadata, with `@reference` entries in the metadata and a `_companionReferenceColumns` array (legacy name: `_contentColumns`, supported for backward compatibility).
511
553
 
512
- **Companion file naming convention:** Only `.metadata.json` files carry the `~UID` suffix (e.g. `Add-Asst-Execute-Security~wxl6ivcwfkix3zgantszjg.metadata.json`). Companion content files use the natural base name without `~UID` (e.g. `Add-Asst-Execute-Security.String16.js`). If two records share the same name within a directory, the second gets a `-1` suffix (e.g. `Add-Asst-Execute-Security-1.String16.js` with metadata `Add-Asst-Execute-Security-1~otheruid.metadata.json`). Migration 007 automatically renames legacy `~UID` companion files to the natural convention.
554
+ **Companion file naming convention:** Only `.metadata.json` files carry the `~UID` suffix (e.g. `Add-Asst-Execute-Security.metadata~wxl6ivcwfkix3zgantszjg.json`). Companion content files use the natural base name without `~UID` (e.g. `Add-Asst-Execute-Security.String5.html`). When a `{title}` is defined in the `@reference` expression (see [Metadata Schema](#metadata-schema)), the title replaces the column name in the filename (e.g. `Add-Asst-Execute-Security.Add-Form-Parameters.js` instead of `Add-Asst-Execute-Security.String5.js`). If two records share the same name within a directory, the second gets a `-1` suffix. Migration 007 automatically renames legacy `~UID` companion files to the natural convention.
513
555
 
514
556
  Use `-y` to skip prompts (uses `Name` column, no content extraction).
515
557
 
@@ -521,7 +563,7 @@ During clone, the CLI:
521
563
 
522
564
  1. **Pre-scans** all extension records for `descriptor_definition` entries and builds a `String1 → Name` mapping
523
565
  2. **Creates sub-directories** under `extension/` for each mapped descriptor (e.g., `extension/Documentation/`, `extension/Includes/`)
524
- 3. **Always creates** `extension/_unsupported/` — extensions with an unmapped or null `Descriptor` are placed here
566
+ 3. **Places unmapped extensions** directly in `extension/` — extensions with an unmapped or null `Descriptor` are placed in the root `extension/` directory (the legacy `_unsupported/` sub-directory is migrated automatically by Migration 011)
525
567
  4. **Prompts per descriptor** — filename column and content extraction prompts fire once per unique `Descriptor` value (not once for all extensions)
526
568
  5. **Persists the mapping** in `.dbo/structure.json` under `descriptorMapping`
527
569
 
@@ -575,7 +617,6 @@ project/
575
617
  MyDoc.uid1.String10.md # ← extracted content column
576
618
  Includes/
577
619
  Header.uid2.metadata.json
578
- _unsupported/ # ← always created (unmapped/null descriptors)
579
620
  docs/ # ← root-placed documentation MD files (optional)
580
621
  MyDoc.md
581
622
  data_source/
@@ -683,9 +724,9 @@ Key points:
683
724
  - `CustomSQL` content is extracted to companion `.sql` files per extraction rules
684
725
  - Re-cloning moves orphaned old-format child `.json` files to `/trash`
685
726
 
686
- #### Metadata Templates
727
+ #### Metadata Schema
687
728
 
688
- During `dbo clone`, the CLI auto-generates `.dbo/metadata_templates.json` — a file that records which columns each entity/descriptor uses. This file is seeded from the first cloned record of each type and can be manually edited afterwards.
729
+ During `dbo clone`, the CLI auto-generates `.dbo/metadata_schema.json` (formerly `metadata_templates.json`) — a file that records which columns each entity/descriptor uses. This file is seeded from the first cloned record of each type and can be manually edited afterwards.
689
730
 
690
731
  **File format:**
691
732
 
@@ -706,9 +747,46 @@ During `dbo clone`, the CLI auto-generates `.dbo/metadata_templates.json` — a
706
747
  | `Name` | Plain column — populated from filename or left empty |
707
748
  | `Descriptor=documentation` | Literal value — always set to `documentation` |
708
749
  | `String10=@reference` | Content reference — links to the file being added |
750
+ | `String5=@reference:@Name[js]` | Content reference with filename source and extension |
709
751
 
710
752
  `dbo add` uses these templates to auto-detect the entity type from a file's directory placement and generate metadata without prompting. For example, `dbo add extension/include/nav.html` resolves to entity `extension` / descriptor `include` and applies the matching template.
711
753
 
754
+ #### `@reference` expression syntax
755
+
756
+ The `=@reference` expression controls how companion files are named during `dbo clone` and `dbo add`. The full grammar:
757
+
758
+ ```
759
+ Column=@reference[:filenameSource][extPart][{title}]
760
+ ```
761
+
762
+ **Components:**
763
+
764
+ | Part | Syntax | Description |
765
+ |------|--------|-------------|
766
+ | Filename source | `:@ColumnName` | Use another column's value as the base filename (e.g., `:@Name`) |
767
+ | Filename fallback | `:@Name\|\|fallback` | Use `fallback` if the column value is empty |
768
+ | Extension | `[ext]` | Literal extension (e.g., `[js]`, `[css]`, `[html]`) |
769
+ | Extension from column | `[@ColumnName]` | Read extension from another column (e.g., `[@Extension]`) |
770
+ | Extension fallback | `[@Extension\|\|html]` | Use `html` if the column value is empty |
771
+ | Title | `{Title Text}` | Human-readable name for the companion file segment (replaces column name) |
772
+
773
+ **Examples:**
774
+
775
+ Given a record with `Name="My-Widget"`, `Extension="html"`, `Ext=""` (empty):
776
+
777
+ | Expression | Companion filename | Description |
778
+ |---|---|---|
779
+ | `Content=@reference` | `My-Widget.Content.txt` | Basic — column name as segment, default ext |
780
+ | `Content=@reference:@Name[@Extension]` | `My-Widget.html` | Name and extension both from record columns |
781
+ | `Content=@reference:@Name\|\|untitled[@Extension]` | `My-Widget.html` | Uses Name; would use `untitled` if Name were empty |
782
+ | `String5=@reference:@Name[js]` | `My-Widget.String5.js` | Literal `.js` extension |
783
+ | `String5=@reference:@Name[js]{Add Form Parameters}` | `My-Widget.Add-Form-Parameters.js` | Title replaces `String5` in filename |
784
+ | `CustomSQL=@reference[sql]` | `My-Widget.CustomSQL.sql` | No filename source, literal extension |
785
+ | `Text=@reference:@Name[@Ext\|\|js]` | `My-Widget.Text.js` | `Ext` column is empty, falls back to `.js` |
786
+ | `Text=@reference:@Name[@Extension\|\|js]` | `My-Widget.Text.html` | `Extension` column has value, fallback not used |
787
+
788
+ **Auto-generation from `descriptor_definition`**: During `dbo clone`, the CLI parses `form-control-code` from `descriptor_definition` extension records to auto-populate `@reference` entries per descriptor. If `form-control-title` is also present, the column's title is included as the `{title}` suffix — producing human-readable companion filenames instead of raw column names like `String5`.
789
+
712
790
  ---
713
791
 
714
792
  ### `dbo login`
@@ -778,7 +856,7 @@ Plugin scopes (project or global) are displayed when plugins have been installed
778
856
 
779
857
  ### `dbo input`
780
858
 
781
- Submit CRUD operations (add, edit, delete records) to DBO.io. This is the core data manipulation command.
859
+ Submit raw CRUD operations (add, edit, delete records) to DBO.io. This is a low-level command that sends input expressions directly to the server without any AppID injection or metadata awareness — use `dbo push` for metadata-driven deployments.
782
860
 
783
861
  ```bash
784
862
  # Add a record
@@ -1285,7 +1363,7 @@ After a pull, metadata files contain `@filename` references for content columns:
1285
1363
  ```json
1286
1364
  {
1287
1365
  "_entity": "content",
1288
- "_contentColumns": ["Content"],
1366
+ "_companionReferenceColumns": ["Content"],
1289
1367
  "UID": "abc123",
1290
1368
  "Name": "colors",
1291
1369
  "Content": "@colors.css",
@@ -1481,7 +1559,7 @@ Minimal (no optional fields provided):
1481
1559
  "Path": "assets/css/colors.css",
1482
1560
  "Content": "@colors.css",
1483
1561
  "_entity": "content",
1484
- "_contentColumns": ["Content"]
1562
+ "_companionReferenceColumns": ["Content"]
1485
1563
  }
1486
1564
  ```
1487
1565
 
@@ -1495,7 +1573,7 @@ With optional fields (only included if the user provides values during the wizar
1495
1573
  "Path": "assets/css/colors.css",
1496
1574
  "Content": "@colors.css",
1497
1575
  "_entity": "content",
1498
- "_contentColumns": ["Content"]
1576
+ "_companionReferenceColumns": ["Content"]
1499
1577
  }
1500
1578
  ```
1501
1579
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dboio/cli",
3
- "version": "0.15.2",
3
+ "version": "0.16.2",
4
4
  "description": "CLI for the DBO.io framework",
5
5
  "type": "module",
6
6
  "bin": {
@@ -134,8 +134,7 @@ my-project/
134
134
  │ ├── entity_column_value/ # Column value definitions
135
135
  │ ├── extension/ # Extension entity records (organized by descriptor type)
136
136
  │ │ ├── widget/
137
- │ │ ├── documentation/
138
- │ │ └── _unsupported/
137
+ │ │ └── documentation/
139
138
  │ ├── integration/ # Integration entity records
140
139
  │ ├── security/ # Security policy records
141
140
  │ ├── security_column/ # Column-level security records
@@ -148,6 +147,7 @@ my-project/
148
147
  ├── trash/ # Staged soft-deleted files from dbo rm
149
148
  ├── docs/ # Project documentation and docs entities
150
149
  ├── app.json # Clone of original app JSON with @references
150
+ ├── schema.json # Instance-level entity/column schema fetched from the server during `dbo init`. Refreshed explicitly with `dbo clone --schema`.
151
151
  ├── manifest.json # PWA web app manifest (auto-generated from app metadata)
152
152
  ├── .gitignore # Tells Git to ignore files in the repo sync
153
153
  ├── .dboignore # Tells dbo cli to ignore files in commands
@@ -178,7 +178,7 @@ All configuration is **directory-scoped**. Each project folder maintains its own
178
178
  | `credentials.json` | Username, user ID, UID, name, email (no password) | Gitignored (per-user) |
179
179
  | `cookies.txt` | Session cookie (Netscape format) | Gitignored (per-user) |
180
180
  | `structure.json` | Bin directory mapping (created by `dbo clone`) | Committable (shared) |
181
- | `metadata_templates.json` | Column templates per entity/descriptor (auto-generated by `dbo clone`) | Committable (shared) |
181
+ | `metadata_schema.json` | Column templates per entity/descriptor (auto-generated by `dbo clone`; renamed from `metadata_templates.json`) | Committable (shared) |
182
182
  | `synchronize.json` | Pending deletions and sync state (updated by `dbo rm` and `dbo push`) | Committable (shared) |
183
183
  | `.app_baseline.json` | Server-state snapshot for delta detection — stores column values at last clone/push so `dbo push` can detect which columns changed locally. Read-only (chmod 444); auto-migrated from legacy root `.app.json`. | Committable (shared) |
184
184
  | `scripts.json` | Build/push lifecycle hooks (see [Script Hooks](#script-hooks)) | Committable (shared) |
@@ -201,6 +201,8 @@ All configuration is **directory-scoped**. Each project folder maintains its own
201
201
  > | Output | `Sales.metadata~ghi789.json` |
202
202
  > | Extension | `MyExtension.metadata~jkl012.json` |
203
203
 
204
+ > **Upgrading to 0.16.0+**: `.dbo/metadata_templates.json` is renamed to `.dbo/metadata_schema.json`. The `_contentColumns` field in metadata files is renamed to `_companionReferenceColumns`. Extension companion file prompts during `dbo clone` are replaced by automatic derivation from `descriptor_definition` `form-control-code` data. Migration 011 runs automatically on first use.
205
+
204
206
  #### Automatic migrations
205
207
 
206
208
  When the CLI version is upgraded, one-time migration scripts in `tools/cli/src/migrations/` run automatically on the first command invocation in a project directory. Completed migration IDs are recorded in `.dbo/config.local.json` under `_completedMigrations` (per-user, gitignored) so each developer runs them independently and they never re-fire.
@@ -241,9 +243,37 @@ Use `dbo status` to see how many pending migrations exist.
241
243
  | `cloneSource` | `"default"` \| file path \| URL | Where the last `dbo clone` fetched app JSON from. `"default"` = server fetch via `AppShortName`; any other value = the explicit local file path or URL used. Set automatically after each successful clone. |
242
244
  | `ContentPlacement` | `bin` \| `path` | Where to place content files during clone (default: `bin`) |
243
245
  | `<Entity>FilenameCol` | column name | Filename column for entity-dir records (e.g., `ExtensionFilenameCol`) |
246
+ | `dependencies` | string[] | Array of app short-names to auto-clone as read-only local checkouts. Default: `["_system"]` |
247
+ | `dependencyLastUpdated` | object | Map of dependency short-name to last-cloned `_LastUpdated` ISO string |
244
248
 
245
249
  **File placement:** All content, media, and output files are placed by BinID into the `lib/bins/` directory structure. Records without a BinID go into `lib/bins/` root. Media always uses BinID — no FullPath option. Bins with `Name=null` (legacy) map directly to `lib/bins/` root.
246
250
 
251
+ ### App Dependencies
252
+
253
+ The CLI automatically maintains local read-only checkouts of related apps under `.dbo/dependencies/<shortname>/`. By default, the `_system` app (which contains all entity, column, and extension descriptors for the connected instance) is always included.
254
+
255
+ #### How it works
256
+
257
+ - After `dbo init` or `dbo clone`, dependency apps are cloned into `.dbo/dependencies/`.
258
+ - If an `app.json` contains a `Dependencies` column, those app short-names are merged into `.dbo/config.json` and also cloned.
259
+ - You can add dependencies explicitly with `dbo clone --dependencies operator,launchpad`.
260
+ - Dependencies are only re-cloned when the server's `_LastUpdated` is newer than the locally stored timestamp — so subsequent runs are fast.
261
+ - Dependency clone output is suppressed — a single summary line shows synced/failed/up-to-date status.
262
+ - `.dbo/dependencies/` is excluded from `.gitignore` and `.dboignore` automatically.
263
+
264
+ #### Schema merging from dependencies
265
+
266
+ After dependency cloning, extension descriptor definitions from dependency schemas (`.dbo/dependencies/<name>/.dbo/metadata_schema.json`) are merged into the local `.dbo/metadata_schema.json`. This is useful when an app has no `descriptor_definition` extensions of its own but depends on an app (like `operator`) that does — the operator's descriptor definitions (widget, include, control, etc.) become available for local extension processing.
267
+
268
+ #### Flags
269
+
270
+ | Flag | Commands | Effect |
271
+ |------|----------|--------|
272
+ | `--no-deps` | `dbo init`, `dbo clone` | Skip dependency syncing |
273
+ | `--dependencies <list>` | `dbo clone` | Add and sync specific dependencies (comma-separated short-names) |
274
+ | `--force` | `dbo clone` | Re-clone all dependencies regardless of staleness |
275
+ | `--schema` | `dbo clone` | Re-clone all dependencies regardless of staleness |
276
+
247
277
  ### Root-level project files
248
278
 
249
279
  #### `app.json`
@@ -367,6 +397,7 @@ dbo init --scaffold --yes # scaffold dirs non-intera
367
397
  | `--dboignore` | Create `.dboignore` with default patterns (use with `--force` to overwrite existing) |
368
398
  | `-y, --yes` | Skip all interactive prompts (legacy migration, Claude Code setup) |
369
399
  | `--non-interactive` | Alias for `--yes` |
400
+ | `--no-deps` | Skip dependency cloning after init |
370
401
 
371
402
  ---
372
403
 
@@ -402,9 +433,14 @@ dbo clone -e extension --descriptor-types false # Clone extensions flat (no de
402
433
  | `--documentation-only` | When used with `-e extension`, clone only documentation extensions |
403
434
  | `--descriptor-types <bool>` | Sort extensions into descriptor sub-directories (default: `true`). Set to `false` to use flat `extension/` layout |
404
435
  | `--domain <host>` | Override domain. Triggers a domain-change confirmation prompt when it differs from the project reference domain |
436
+ | `--schema` | Re-fetch `schema.json` from server before cloning |
405
437
  | `--force` | Skip source mismatch confirmation and change detection; re-processes all files |
406
438
  | `-y, --yes` | Auto-accept all prompts (also skips source mismatch confirmation) |
407
439
  | `-v, --verbose` | Show HTTP request details |
440
+ | `--no-deps` | Skip dependency cloning after clone |
441
+ | `--dependencies <list>` | Comma-separated app short-names to add and sync as dependencies (e.g. `--dependencies operator,launchpad`) |
442
+ | `--configure` | Re-prompt for filename columns, companion file extraction, and documentation placement preferences |
443
+ | `--media-placement <type>` | Set media placement: `fullpath` or `binpath` (default: `bin`) |
408
444
 
409
445
  #### Domain change detection
410
446
 
@@ -414,17 +450,19 @@ The project's reference domain is stored in `app.json._domain` (committed to git
414
450
 
415
451
  #### What clone does
416
452
 
417
- 1. **Loads app JSON** — from a local file, server API, or interactive prompt. A spinner shows progress while fetching from the server (responses can be slow as the JSON is assembled on demand)
418
- 2. **Updates `.dbo/config.json`** saves `AppID`, `AppUID`, `AppName`, `AppShortName`, `AppModifyKey` (if the app is locked), and `cloneSource` (the source used for this clone)
419
- 3. **Updates `package.json`**populates `name`, `productName`, `description`, `homepage`, and `deploy` script
420
- 4. **Creates directories**processes `children.bin` to build the directory hierarchy based on `ParentBinID` relationships
421
- 5. **Saves `.dbo/structure.json`** — maps BinIDs to directory paths for file placement
422
- 6. **Writes content files** — decodes base64 content, creates `*.metadata.json` + content files in the correct bin directory. When a record's `Extension` field is empty, prompts you to choose from `css`, `js`, `html`, `xml`, `txt`, `md`, `cs`, `json`, `sql` (or skip to keep no extension). The chosen extension is saved back into the `metadata.json` `Extension` field
423
- 7. **Downloads media files** fetches binary files (images, CSS, fonts) from the server using a fallback chain: `FullPath` directly (`/media/{app}/{path}`) → `/dir/` route → `/api/media/{uid}`, and saves with metadata. Errors are logged to `.dbo/errors.log`
424
- 8. **Processes entity-dir records** — entities matching project directories (`extension`, `app_version`, `data_source`, `site`, `group`, `integration`, `automation`) are saved as `.metadata.json` files in their corresponding directory (e.g., `extension/`, `data_source/`)
425
- 9. **Processes other entities** — remaining entities with a `BinID` are placed in the corresponding bin directory
426
- 10. **Saves `app.json`**clone of the original JSON with processed entries replaced by `@path/to/*.metadata.json` references
427
- 11. **Orphan cleanup** — any local `.metadata.json` files whose UID is absent from the server response are automatically moved to `trash/` along with their companion content and media files. This prevents stale records (deleted server-side) from causing false positives in `dbo push`. Skipped during `--entity-filter` clones
453
+ 1. **Fetches schema** — downloads `schema.json` from the server if missing or explicitly requested (`--schema`). Regenerates `.dbo/metadata_schema.json` from the schema
454
+ 2. **Syncs dependencies** — clones dependency apps into `.dbo/dependencies/<shortname>/` (unless `--no-deps`). Merges extension descriptor definitions from dependency schemas into the local `.dbo/metadata_schema.json` (e.g., operator provides descriptor definitions for apps that lack their own)
455
+ 3. **Loads app JSON** from a local file, server API, or interactive prompt. A spinner shows progress while fetching from the server (responses can be slow as the JSON is assembled on demand)
456
+ 4. **Updates `.dbo/config.json`**saves `AppID`, `AppUID`, `AppName`, `AppShortName`, `AppModifyKey` (if the app is locked), and `cloneSource` (the source used for this clone)
457
+ 5. **Updates `package.json`** — populates `name`, `productName`, `description`, `homepage`, and `deploy` script
458
+ 6. **Creates directories** — processes `children.bin` to build the directory hierarchy based on `ParentBinID` relationships
459
+ 7. **Saves `.dbo/structure.json`**maps BinIDs to directory paths for file placement
460
+ 8. **Writes content files** — decodes base64 content, creates `*.metadata.json` + content files in the correct bin directory. Filename columns and companion file extraction preferences are auto-applied with sensible defaults on first clone (use `--configure` to re-prompt)
461
+ 9. **Downloads media files** — fetches binary files (images, CSS, fonts) from the server using a fallback chain: `FullPath` directly (`/media/{app}/{path}`) `/dir/` route `/api/media/{uid}`, and saves with metadata. 404 errors create stale metadata to prevent re-prompting. Errors are logged to `.dbo/errors.log`
462
+ 10. **Processes entity-dir records** entities matching project directories (`extension`, `app_version`, `data_source`, `site`, `group`, `integration`, `automation`) are saved as `.metadata.json` files in their corresponding directory (e.g., `extension/`, `data_source/`)
463
+ 11. **Processes other entities** — remaining entities with a `BinID` are placed in the corresponding bin directory
464
+ 12. **Saves `app.json`** — clone of the original JSON with processed entries replaced by `@path/to/*.metadata.json` references
465
+ 13. **Orphan cleanup** — any local `.metadata.json` files whose UID is absent from the server response are automatically moved to `trash/` along with their companion content and media files. This prevents stale records (deleted server-side) from causing false positives in `dbo push`. Skipped during `--entity-filter` clones
428
466
 
429
467
  #### Clone source tracking
430
468
 
@@ -454,6 +492,8 @@ If the initial clone attempt fails (network error, file not found, empty respons
454
492
 
455
493
  When cloning an app that was already cloned locally, the CLI detects existing files and compares modification times against the server's `_LastUpdated`. You'll be prompted to overwrite, compare differences, or skip — same as `dbo pull`. Use `-y` to auto-accept all changes.
456
494
 
495
+ Change detection only checks companion content and media files for user edits — metadata file mtimes are not considered, since they can be bumped by CLI-internal operations (migrations, legacy companion renames).
496
+
457
497
  #### Collision detection
458
498
 
459
499
  When multiple records would create files at the same path (e.g., a `content` record and a `media` record both named `colors.css`), the CLI detects the collision before writing any files and prompts you to choose which record to keep:
@@ -471,7 +511,9 @@ In non-interactive mode (`-y`), the first record is kept and others are auto-sta
471
511
 
472
512
  #### Stale media cleanup
473
513
 
474
- During media downloads, files returning 404 (no longer exist on the server) are collected as "stale records". After all downloads complete, you'll be prompted to stage them for deletion:
514
+ During media downloads, files returning 404 (no longer exist on the server) are collected as "stale records". Metadata is still written for 404 files (marked with `_stale404: true`) so they are not re-prompted on subsequent clones.
515
+
516
+ After all downloads complete, you'll be prompted to stage stale records for deletion:
475
517
 
476
518
  ```
477
519
  Found 3 stale media record(s) (404 - files no longer exist on server)
@@ -507,9 +549,9 @@ Entity types that correspond to project directories (`extension`, `app_version`,
507
549
 
508
550
  For each entity type, the CLI prompts to choose which column becomes the filename (defaults to `Name`, fallback `UID`). The choice is saved per entity type in `config.json` (e.g., `ExtensionFilenameCol`).
509
551
 
510
- If any columns contain base64-encoded content, the CLI prompts to extract them as companion files. Extracted columns produce files named `<name>.<Column>.<ext>` alongside the metadata, with `@reference` entries in the metadata and a `_contentColumns` array.
552
+ If any columns contain base64-encoded content, the CLI prompts to extract them as companion files. Extracted columns produce files named `<name>.<Column>.<ext>` alongside the metadata, with `@reference` entries in the metadata and a `_companionReferenceColumns` array (legacy name: `_contentColumns`, supported for backward compatibility).
511
553
 
512
- **Companion file naming convention:** Only `.metadata.json` files carry the `~UID` suffix (e.g. `Add-Asst-Execute-Security~wxl6ivcwfkix3zgantszjg.metadata.json`). Companion content files use the natural base name without `~UID` (e.g. `Add-Asst-Execute-Security.String16.js`). If two records share the same name within a directory, the second gets a `-1` suffix (e.g. `Add-Asst-Execute-Security-1.String16.js` with metadata `Add-Asst-Execute-Security-1~otheruid.metadata.json`). Migration 007 automatically renames legacy `~UID` companion files to the natural convention.
554
+ **Companion file naming convention:** Only `.metadata.json` files carry the `~UID` suffix (e.g. `Add-Asst-Execute-Security.metadata~wxl6ivcwfkix3zgantszjg.json`). Companion content files use the natural base name without `~UID` (e.g. `Add-Asst-Execute-Security.String5.html`). When a `{title}` is defined in the `@reference` expression (see [Metadata Schema](#metadata-schema)), the title replaces the column name in the filename (e.g. `Add-Asst-Execute-Security.Add-Form-Parameters.js` instead of `Add-Asst-Execute-Security.String5.js`). If two records share the same name within a directory, the second gets a `-1` suffix. Migration 007 automatically renames legacy `~UID` companion files to the natural convention.
513
555
 
514
556
  Use `-y` to skip prompts (uses `Name` column, no content extraction).
515
557
 
@@ -521,7 +563,7 @@ During clone, the CLI:
521
563
 
522
564
  1. **Pre-scans** all extension records for `descriptor_definition` entries and builds a `String1 → Name` mapping
523
565
  2. **Creates sub-directories** under `extension/` for each mapped descriptor (e.g., `extension/Documentation/`, `extension/Includes/`)
524
- 3. **Always creates** `extension/_unsupported/` — extensions with an unmapped or null `Descriptor` are placed here
566
+ 3. **Places unmapped extensions** directly in `extension/` — extensions with an unmapped or null `Descriptor` are placed in the root `extension/` directory (the legacy `_unsupported/` sub-directory is migrated automatically by Migration 011)
525
567
  4. **Prompts per descriptor** — filename column and content extraction prompts fire once per unique `Descriptor` value (not once for all extensions)
526
568
  5. **Persists the mapping** in `.dbo/structure.json` under `descriptorMapping`
527
569
 
@@ -575,7 +617,6 @@ project/
575
617
  MyDoc.uid1.String10.md # ← extracted content column
576
618
  Includes/
577
619
  Header.uid2.metadata.json
578
- _unsupported/ # ← always created (unmapped/null descriptors)
579
620
  docs/ # ← root-placed documentation MD files (optional)
580
621
  MyDoc.md
581
622
  data_source/
@@ -683,9 +724,9 @@ Key points:
683
724
  - `CustomSQL` content is extracted to companion `.sql` files per extraction rules
684
725
  - Re-cloning moves orphaned old-format child `.json` files to `/trash`
685
726
 
686
- #### Metadata Templates
727
+ #### Metadata Schema
687
728
 
688
- During `dbo clone`, the CLI auto-generates `.dbo/metadata_templates.json` — a file that records which columns each entity/descriptor uses. This file is seeded from the first cloned record of each type and can be manually edited afterwards.
729
+ During `dbo clone`, the CLI auto-generates `.dbo/metadata_schema.json` (formerly `metadata_templates.json`) — a file that records which columns each entity/descriptor uses. This file is seeded from the first cloned record of each type and can be manually edited afterwards.
689
730
 
690
731
  **File format:**
691
732
 
@@ -706,9 +747,46 @@ During `dbo clone`, the CLI auto-generates `.dbo/metadata_templates.json` — a
706
747
  | `Name` | Plain column — populated from filename or left empty |
707
748
  | `Descriptor=documentation` | Literal value — always set to `documentation` |
708
749
  | `String10=@reference` | Content reference — links to the file being added |
750
+ | `String5=@reference:@Name[js]` | Content reference with filename source and extension |
709
751
 
710
752
  `dbo add` uses these templates to auto-detect the entity type from a file's directory placement and generate metadata without prompting. For example, `dbo add extension/include/nav.html` resolves to entity `extension` / descriptor `include` and applies the matching template.
711
753
 
754
+ #### `@reference` expression syntax
755
+
756
+ The `=@reference` expression controls how companion files are named during `dbo clone` and `dbo add`. The full grammar:
757
+
758
+ ```
759
+ Column=@reference[:filenameSource][extPart][{title}]
760
+ ```
761
+
762
+ **Components:**
763
+
764
+ | Part | Syntax | Description |
765
+ |------|--------|-------------|
766
+ | Filename source | `:@ColumnName` | Use another column's value as the base filename (e.g., `:@Name`) |
767
+ | Filename fallback | `:@Name\|\|fallback` | Use `fallback` if the column value is empty |
768
+ | Extension | `[ext]` | Literal extension (e.g., `[js]`, `[css]`, `[html]`) |
769
+ | Extension from column | `[@ColumnName]` | Read extension from another column (e.g., `[@Extension]`) |
770
+ | Extension fallback | `[@Extension\|\|html]` | Use `html` if the column value is empty |
771
+ | Title | `{Title Text}` | Human-readable name for the companion file segment (replaces column name) |
772
+
773
+ **Examples:**
774
+
775
+ Given a record with `Name="My-Widget"`, `Extension="html"`, `Ext=""` (empty):
776
+
777
+ | Expression | Companion filename | Description |
778
+ |---|---|---|
779
+ | `Content=@reference` | `My-Widget.Content.txt` | Basic — column name as segment, default ext |
780
+ | `Content=@reference:@Name[@Extension]` | `My-Widget.html` | Name and extension both from record columns |
781
+ | `Content=@reference:@Name\|\|untitled[@Extension]` | `My-Widget.html` | Uses Name; would use `untitled` if Name were empty |
782
+ | `String5=@reference:@Name[js]` | `My-Widget.String5.js` | Literal `.js` extension |
783
+ | `String5=@reference:@Name[js]{Add Form Parameters}` | `My-Widget.Add-Form-Parameters.js` | Title replaces `String5` in filename |
784
+ | `CustomSQL=@reference[sql]` | `My-Widget.CustomSQL.sql` | No filename source, literal extension |
785
+ | `Text=@reference:@Name[@Ext\|\|js]` | `My-Widget.Text.js` | `Ext` column is empty, falls back to `.js` |
786
+ | `Text=@reference:@Name[@Extension\|\|js]` | `My-Widget.Text.html` | `Extension` column has value, fallback not used |
787
+
788
+ **Auto-generation from `descriptor_definition`**: During `dbo clone`, the CLI parses `form-control-code` from `descriptor_definition` extension records to auto-populate `@reference` entries per descriptor. If `form-control-title` is also present, the column's title is included as the `{title}` suffix — producing human-readable companion filenames instead of raw column names like `String5`.
789
+
712
790
  ---
713
791
 
714
792
  ### `dbo login`
@@ -778,7 +856,7 @@ Plugin scopes (project or global) are displayed when plugins have been installed
778
856
 
779
857
  ### `dbo input`
780
858
 
781
- Submit CRUD operations (add, edit, delete records) to DBO.io. This is the core data manipulation command.
859
+ Submit raw CRUD operations (add, edit, delete records) to DBO.io. This is a low-level command that sends input expressions directly to the server without any AppID injection or metadata awareness — use `dbo push` for metadata-driven deployments.
782
860
 
783
861
  ```bash
784
862
  # Add a record
@@ -1285,7 +1363,7 @@ After a pull, metadata files contain `@filename` references for content columns:
1285
1363
  ```json
1286
1364
  {
1287
1365
  "_entity": "content",
1288
- "_contentColumns": ["Content"],
1366
+ "_companionReferenceColumns": ["Content"],
1289
1367
  "UID": "abc123",
1290
1368
  "Name": "colors",
1291
1369
  "Content": "@colors.css",
@@ -1481,7 +1559,7 @@ Minimal (no optional fields provided):
1481
1559
  "Path": "assets/css/colors.css",
1482
1560
  "Content": "@colors.css",
1483
1561
  "_entity": "content",
1484
- "_contentColumns": ["Content"]
1562
+ "_companionReferenceColumns": ["Content"]
1485
1563
  }
1486
1564
  ```
1487
1565
 
@@ -1495,7 +1573,7 @@ With optional fields (only included if the user provides values during the wizar
1495
1573
  "Path": "assets/css/colors.css",
1496
1574
  "Content": "@colors.css",
1497
1575
  "_entity": "content",
1498
- "_contentColumns": ["Content"]
1576
+ "_companionReferenceColumns": ["Content"]
1499
1577
  }
1500
1578
  ```
1501
1579