@dboio/cli 0.16.2 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +175 -138
  2. package/bin/dbo.js +2 -2
  3. package/package.json +1 -1
  4. package/plugins/claude/dbo/docs/dbo-cli-readme.md +175 -138
  5. package/src/commands/adopt.js +534 -0
  6. package/src/commands/build.js +3 -3
  7. package/src/commands/clone.js +209 -75
  8. package/src/commands/deploy.js +3 -3
  9. package/src/commands/init.js +11 -11
  10. package/src/commands/install.js +3 -3
  11. package/src/commands/login.js +2 -2
  12. package/src/commands/mv.js +15 -15
  13. package/src/commands/pull.js +1 -1
  14. package/src/commands/push.js +194 -15
  15. package/src/commands/rm.js +2 -2
  16. package/src/commands/run.js +4 -4
  17. package/src/commands/status.js +1 -1
  18. package/src/commands/sync.js +2 -2
  19. package/src/lib/config.js +186 -135
  20. package/src/lib/delta.js +119 -17
  21. package/src/lib/dependencies.js +51 -24
  22. package/src/lib/deploy-config.js +4 -4
  23. package/src/lib/domain-guard.js +8 -9
  24. package/src/lib/filenames.js +13 -2
  25. package/src/lib/ignore.js +2 -3
  26. package/src/{commands/add.js → lib/insert.js} +127 -472
  27. package/src/lib/metadata-schema.js +14 -20
  28. package/src/lib/metadata-templates.js +4 -4
  29. package/src/lib/migrations.js +1 -1
  30. package/src/lib/modify-key.js +1 -1
  31. package/src/lib/scaffold.js +5 -12
  32. package/src/lib/schema.js +67 -37
  33. package/src/lib/structure.js +6 -6
  34. package/src/lib/tagging.js +2 -2
  35. package/src/lib/ticketing.js +3 -7
  36. package/src/lib/toe-stepping.js +5 -5
  37. package/src/lib/transaction-key.js +1 -1
  38. package/src/migrations/004-rename-output-files.js +2 -2
  39. package/src/migrations/005-rename-output-metadata.js +2 -2
  40. package/src/migrations/006-remove-uid-companion-filenames.js +1 -1
  41. package/src/migrations/007-natural-entity-companion-filenames.js +1 -1
  42. package/src/migrations/008-metadata-uid-in-suffix.js +1 -1
  43. package/src/migrations/009-fix-media-collision-metadata-names.js +1 -1
  44. package/src/migrations/010-delete-paren-media-orphans.js +1 -1
  45. package/src/migrations/012-project-dir-restructure.js +211 -0
@@ -1,10 +1,9 @@
1
1
  import { readFile, writeFile, readdir, access } from 'fs/promises';
2
2
  import { join, relative, basename, extname } from 'path';
3
3
  import { EXTENSION_DESCRIPTORS_DIR, ENTITY_DIR_NAMES } from './structure.js';
4
+ import { metadataSchemaPath } from './config.js';
4
5
  import inquirer from 'inquirer';
5
6
 
6
- export const METADATA_SCHEMA_FILE = '.dbo/metadata_schema.json';
7
-
8
7
  const STATIC_DIRECTIVE_MAP = {
9
8
  docs: { entity: 'extension', descriptor: 'documentation' },
10
9
  };
@@ -67,33 +66,29 @@ export function resolveDirective(filePath) {
67
66
  }
68
67
 
69
68
  /**
70
- * Load metadata schema from .dbo/metadata_schema.json.
69
+ * Load metadata schema from .app/<shortName>.metadata_schema.json.
71
70
  */
72
71
  export async function loadMetadataSchema() {
73
72
  try {
74
- const raw = await readFile(METADATA_SCHEMA_FILE, 'utf8');
75
- try {
76
- return JSON.parse(raw);
77
- } catch {
78
- console.warn('Warning: .dbo/metadata_schema.json is malformed JSON — falling back to generic wizard');
79
- return null;
80
- }
81
- } catch (err) {
82
- if (err.code === 'ENOENT') return {};
83
- return {};
73
+ const path = await metadataSchemaPath();
74
+ const raw = await readFile(path, 'utf8');
75
+ return JSON.parse(raw);
76
+ } catch {
77
+ return null;
84
78
  }
85
79
  }
86
80
 
87
81
  /**
88
- * Save metadata schema to .dbo/metadata_schema.json.
82
+ * Save metadata schema to .app/<shortName>.metadata_schema.json.
89
83
  */
90
- export async function saveMetadataSchema(templates) {
91
- await writeFile(METADATA_SCHEMA_FILE, JSON.stringify(templates, null, 2) + '\n');
84
+ export async function saveMetadataSchema(data) {
85
+ const path = await metadataSchemaPath();
86
+ await writeFile(path, JSON.stringify(data, null, 2) + '\n');
92
87
  }
93
88
 
94
89
  /**
95
90
  * Merge extension descriptor definitions from dependency metadata_schema.json files.
96
- * For each dependency in .dbo/dependencies/<name>/, reads its .dbo/metadata_schema.json
91
+ * For each dependency in app_dependencies/<name>/, reads its .app/<name>.metadata_schema.json
97
92
  * and copies any extension descriptor entries that don't already exist locally.
98
93
  *
99
94
  * @param {Object} localSchema - The current project's metadata_schema object (mutated in place)
@@ -102,7 +97,7 @@ export async function saveMetadataSchema(templates) {
102
97
  export async function mergeDescriptorSchemaFromDependencies(localSchema) {
103
98
  if (!localSchema) return false;
104
99
 
105
- const depsRoot = join('.dbo', 'dependencies');
100
+ const depsRoot = join(process.cwd(), 'app_dependencies');
106
101
  let depNames;
107
102
  try {
108
103
  depNames = await readdir(depsRoot);
@@ -113,7 +108,7 @@ export async function mergeDescriptorSchemaFromDependencies(localSchema) {
113
108
  let merged = false;
114
109
 
115
110
  for (const depName of depNames) {
116
- const depSchemaPath = join(depsRoot, depName, '.dbo', 'metadata_schema.json');
111
+ const depSchemaPath = join(depsRoot, depName, '.app', `${depName}.metadata_schema.json`);
117
112
  let depSchema;
118
113
  try {
119
114
  depSchema = JSON.parse(await readFile(depSchemaPath, 'utf8'));
@@ -489,4 +484,3 @@ export function generateMetadataFromSchema(schema, existing = {}) {
489
484
  // Backward-compat re-exports
490
485
  export const loadMetadataTemplates = loadMetadataSchema;
491
486
  export const saveMetadataTemplates = saveMetadataSchema;
492
- export const METADATA_TEMPLATES_FILE = METADATA_SCHEMA_FILE;
@@ -3,7 +3,7 @@ import { relative, basename, extname } from 'path';
3
3
  import { EXTENSION_DESCRIPTORS_DIR, ENTITY_DIR_NAMES } from './structure.js';
4
4
  import inquirer from 'inquirer';
5
5
 
6
- const METADATA_TEMPLATES_FILE = '.dbo/metadata_templates.json';
6
+ const METADATA_TEMPLATES_FILE = '.app/metadata_templates.json';
7
7
 
8
8
  const STATIC_DIRECTIVE_MAP = {
9
9
  docs: { entity: 'extension', descriptor: 'documentation' },
@@ -67,7 +67,7 @@ export function resolveDirective(filePath) {
67
67
  }
68
68
 
69
69
  /**
70
- * Load metadata templates from .dbo/metadata_templates.json.
70
+ * Load metadata templates from .app/metadata_templates.json.
71
71
  */
72
72
  export async function loadMetadataTemplates() {
73
73
  try {
@@ -75,7 +75,7 @@ export async function loadMetadataTemplates() {
75
75
  try {
76
76
  return JSON.parse(raw);
77
77
  } catch {
78
- console.warn('Warning: .dbo/metadata_templates.json is malformed JSON — falling back to generic wizard');
78
+ console.warn('Warning: .app/metadata_templates.json is malformed JSON — falling back to generic wizard');
79
79
  return null;
80
80
  }
81
81
  } catch (err) {
@@ -85,7 +85,7 @@ export async function loadMetadataTemplates() {
85
85
  }
86
86
 
87
87
  /**
88
- * Save metadata templates to .dbo/metadata_templates.json.
88
+ * Save metadata templates to .app/metadata_templates.json.
89
89
  */
90
90
  export async function saveMetadataTemplates(templates) {
91
91
  await writeFile(METADATA_TEMPLATES_FILE, JSON.stringify(templates, null, 2) + '\n');
@@ -17,7 +17,7 @@ export async function runPendingMigrations(options = {}) {
17
17
  // --no-migrate suppresses for this invocation
18
18
  if (options.migrate === false) return;
19
19
 
20
- // No .dbo/ project in this directory — nothing to migrate
20
+ // No .app/ project in this directory — nothing to migrate
21
21
  if (!(await isInitialized())) return;
22
22
 
23
23
  // Discover migration files
@@ -104,6 +104,6 @@ export async function handleModifyKeyError() {
104
104
  }
105
105
 
106
106
  await saveAppModifyKey(key);
107
- log.info(' ModifyKey saved to .dbo/config.json for future use.');
107
+ log.info(' ModifyKey saved to .app/config.json for future use.');
108
108
  return { modifyKey: key, cancel: false };
109
109
  }
@@ -3,6 +3,7 @@ import { join } from 'path';
3
3
  import { SCAFFOLD_DIRS } from './structure.js';
4
4
  import { log } from './logger.js';
5
5
  import { applyTrashIcon } from './tagging.js';
6
+ import { appMetadataPath } from './config.js';
6
7
 
7
8
  /**
8
9
  * Scaffold the standard DBO project directory structure in cwd.
@@ -37,21 +38,12 @@ export async function scaffoldProjectDirs(cwd = process.cwd()) {
37
38
  // Best-effort: apply trash icon to the trash directory
38
39
  await applyTrashIcon(join(cwd, 'trash'));
39
40
 
40
- // Create app.json if absent
41
- const appJsonPath = join(cwd, 'app.json');
42
- try {
43
- await access(appJsonPath);
44
- } catch {
45
- await writeFile(appJsonPath, '{}\n');
46
- created.push('app.json');
47
- }
48
-
49
41
  // Create manifest.json if absent
50
42
  const manifestPath = join(cwd, 'manifest.json');
51
43
  try {
52
44
  await access(manifestPath);
53
45
  } catch {
54
- // Try to resolve values from local app.json; fall back to empty strings
46
+ // Try to resolve values from app metadata file; fall back to empty strings
55
47
  let appName = '';
56
48
  let shortName = '';
57
49
  let description = '';
@@ -59,7 +51,8 @@ export async function scaffoldProjectDirs(cwd = process.cwd()) {
59
51
  let domain = '';
60
52
 
61
53
  try {
62
- const appData = JSON.parse(await readFile(appJsonPath, 'utf8'));
54
+ const appMetaPath = await appMetadataPath();
55
+ const appData = JSON.parse(await readFile(appMetaPath, 'utf8'));
63
56
  appName = appData.Name || '';
64
57
  shortName = appData.ShortName || '';
65
58
  description = appData.Description || '';
@@ -74,7 +67,7 @@ export async function scaffoldProjectDirs(cwd = process.cwd()) {
74
67
  }
75
68
  }
76
69
  }
77
- } catch { /* app.json missing or unparseable — use empty defaults */ }
70
+ } catch { /* metadata file missing or unparseable — use empty defaults */ }
78
71
 
79
72
  const manifest = {
80
73
  name: `${appName} | ${domain}`,
package/src/lib/schema.js CHANGED
@@ -1,53 +1,83 @@
1
- import { readFile, writeFile } from 'fs/promises';
2
- import { DboClient } from './client.js';
1
+ import { readFile } from 'fs/promises';
2
+ import { join, sep } from 'path';
3
3
 
4
- export const SCHEMA_FILE = 'schema.json';
5
- const SCHEMA_API_PATH = '/api/app/object/_system';
4
+ const SYSTEM_BASELINE_PATH = join('app_dependencies', '_system', '.app', '_system.json');
6
5
 
7
- /** Fetch schema from /api/app/object/_system. Returns parsed JSON or throws. */
8
- export async function fetchSchema(options = {}) {
9
- const client = new DboClient({ domain: options.domain, verbose: options.verbose });
10
- const result = await client.get(SCHEMA_API_PATH);
11
- if (!result.ok || !result.data) throw new Error(`Schema fetch failed: HTTP ${result.status}`);
12
- return result.data;
6
+ /**
7
+ * Kept for call-site compatibility with commands that pass systemSchemaPath.
8
+ * Points to the _system dependency baseline location.
9
+ */
10
+ export const SCHEMA_FILE = SYSTEM_BASELINE_PATH;
11
+
12
+ /**
13
+ * Load schema from the _system dependency baseline.
14
+ * When inside a dependency checkout (app_dependencies/<dep>/), traverses up
15
+ * to the parent project's _system baseline.
16
+ * Returns parsed object or null if the dependency hasn't been cloned yet.
17
+ */
18
+ export async function loadSchema() {
19
+ // Try local path first (works for top-level projects)
20
+ try {
21
+ return JSON.parse(await readFile(SYSTEM_BASELINE_PATH, 'utf8'));
22
+ } catch { /* not found locally */ }
23
+
24
+ // If inside a dependency checkout, try parent project's schema
25
+ const cwd = process.cwd();
26
+ const marker = `${sep}app_dependencies${sep}`;
27
+ const depIdx = cwd.indexOf(marker);
28
+ if (depIdx >= 0) {
29
+ const parentRoot = cwd.substring(0, depIdx);
30
+ const parentSchemaPath = join(parentRoot, SYSTEM_BASELINE_PATH);
31
+ try {
32
+ return JSON.parse(await readFile(parentSchemaPath, 'utf8'));
33
+ } catch { /* parent schema not available either */ }
34
+ }
35
+
36
+ return null;
13
37
  }
14
38
 
15
39
  /**
16
- * Fetch only the _LastUpdated timestamp from the schema endpoint.
17
- * Uses the column-filter syntax: /api/app/object/_system[_LastUpdated]
18
- * Returns ISO date string or null on failure.
40
+ * saveSchema() is now a no-op schema is managed by the _system dependency clone.
41
+ * Kept for call-site compatibility.
19
42
  */
20
- export async function fetchSchemaLastUpdated(options = {}) {
21
- const client = new DboClient({ domain: options.domain, verbose: options.verbose });
22
- const result = await client.get(`${SCHEMA_API_PATH}[_LastUpdated]`);
23
- if (!result.ok || !result.data) return null;
24
- return result.data._LastUpdated || null;
43
+ export async function saveSchema(_data) {
44
+ // no-op
25
45
  }
26
46
 
27
47
  /**
28
- * Check whether the server schema is newer than the local schema.json.
29
- * Returns true if the server _LastUpdated is more recent (or local is missing).
48
+ * Check whether the _system dependency needs refreshing.
49
+ * Delegates to checkDependencyStaleness('_system').
30
50
  */
31
51
  export async function isSchemaStale(options = {}) {
32
- const local = await loadSchema();
33
- if (!local || !local._LastUpdated) return true;
34
-
35
- const serverTs = await fetchSchemaLastUpdated(options);
36
- if (!serverTs) return false; // can't determine — assume not stale
37
-
38
- return new Date(serverTs) > new Date(local._LastUpdated);
52
+ const { checkDependencyStaleness } = await import('./dependencies.js');
53
+ return checkDependencyStaleness('_system', options);
39
54
  }
40
55
 
41
- /** Load schema.json from project root. Returns parsed object or null if missing. */
42
- export async function loadSchema() {
43
- try {
44
- return JSON.parse(await readFile(SCHEMA_FILE, 'utf8'));
45
- } catch {
46
- return null;
56
+ /**
57
+ * "Fetch" schema by forcing a re-clone of the _system dependency.
58
+ * When inside a dependency checkout, falls back to direct API fetch
59
+ * (syncDependencies would skip due to the self-detection guard).
60
+ * Returns the freshly loaded schema after sync completes.
61
+ */
62
+ export async function fetchSchema(options = {}) {
63
+ const cwd = process.cwd();
64
+ if (cwd.includes(`${sep}app_dependencies${sep}`)) {
65
+ // Inside a dependency checkout — can't sync _system here.
66
+ // Try parent's schema first (already cloned by parent).
67
+ const existing = await loadSchema();
68
+ if (existing) return existing;
69
+
70
+ // Fallback: fetch directly from API (old behavior)
71
+ const { DboClient } = await import('./client.js');
72
+ const { loadConfig } = await import('./config.js');
73
+ const config = await loadConfig();
74
+ const domain = options.domain || config.domain;
75
+ const client = new DboClient({ domain, verbose: options.verbose });
76
+ const resp = await client.get('/api/app/object/_system');
77
+ return resp?.data || resp;
47
78
  }
48
- }
49
79
 
50
- /** Write schema.json to project root. */
51
- export async function saveSchema(data) {
52
- await writeFile(SCHEMA_FILE, JSON.stringify(data, null, 2) + '\n');
80
+ const { syncDependencies } = await import('./dependencies.js');
81
+ await syncDependencies({ ...options, only: ['_system'], force: true });
82
+ return loadSchema();
53
83
  }
@@ -1,7 +1,7 @@
1
1
  import { readFile, writeFile, mkdir } from 'fs/promises';
2
2
  import { join } from 'path';
3
3
 
4
- const STRUCTURE_FILE = '.dbo/structure.json';
4
+ const STRUCTURE_FILE = '.app/directories.json';
5
5
 
6
6
  /** All server-managed directories live under this subdirectory */
7
7
  export const LIB_DIR = 'lib';
@@ -271,15 +271,15 @@ export async function createDirectories(structure) {
271
271
  }
272
272
 
273
273
  /**
274
- * Save the bin structure to .dbo/structure.json.
274
+ * Save the bin structure to .app/directories.json.
275
275
  */
276
276
  export async function saveStructureFile(structure) {
277
- await mkdir('.dbo', { recursive: true });
277
+ await mkdir('.app', { recursive: true });
278
278
  await writeFile(STRUCTURE_FILE, JSON.stringify(structure, null, 2) + '\n');
279
279
  }
280
280
 
281
281
  /**
282
- * Load bin structure from .dbo/structure.json.
282
+ * Load bin structure from .app/directories.json.
283
283
  */
284
284
  export async function loadStructureFile() {
285
285
  try {
@@ -389,7 +389,7 @@ export function resolveFieldValue(value) {
389
389
  }
390
390
 
391
391
  /**
392
- * Persist descriptorMapping and extensionDescriptorDirs into .dbo/structure.json.
392
+ * Persist descriptorMapping and extensionDescriptorDirs into .app/directories.json.
393
393
  * Extends the existing structure object (already contains bin entries).
394
394
  *
395
395
  * @param {Object} structure - Current structure from loadStructureFile()
@@ -411,7 +411,7 @@ export async function saveDescriptorMapping(structure, mapping) {
411
411
  }
412
412
 
413
413
  /**
414
- * Load descriptorMapping from .dbo/structure.json.
414
+ * Load descriptorMapping from .app/directories.json.
415
415
  * Returns {} if not yet persisted.
416
416
  */
417
417
  export async function loadDescriptorMapping() {
@@ -149,13 +149,13 @@ async function _findUntrackedFiles(dir, ig, knownMetaPaths) {
149
149
  return all.filter(fp => !knownCompanions.has(fp));
150
150
  }
151
151
 
152
- // Recursively collect non-metadata, non-hidden, non-.dbo content files
152
+ // Recursively collect non-metadata, non-hidden, non-.app content files
153
153
  async function _collectContentFiles(dir, ig) {
154
154
  const results = [];
155
155
  let entries;
156
156
  try { entries = await readdir(dir, { withFileTypes: true }); } catch { return []; }
157
157
  for (const entry of entries) {
158
- if (entry.name.startsWith('.')) continue; // skip hidden and .dbo
158
+ if (entry.name.startsWith('.')) continue; // skip hidden and .app
159
159
  const fullPath = join(dir, entry.name);
160
160
  const relPath = relative(process.cwd(), fullPath).replace(/\\/g, '/');
161
161
  if (entry.isDirectory()) {
@@ -1,16 +1,12 @@
1
1
  import { readFile, writeFile, mkdir } from 'fs/promises';
2
2
  import { join } from 'path';
3
3
  import { log } from './logger.js';
4
+ import { projectDir } from './config.js';
4
5
 
5
- const DBO_DIR = '.dbo';
6
6
  const TICKETING_FILE = 'ticketing.local.json';
7
7
 
8
- function dboDir() {
9
- return join(process.cwd(), DBO_DIR);
10
- }
11
-
12
8
  function ticketingPath() {
13
- return join(dboDir(), TICKETING_FILE);
9
+ return join(projectDir(), TICKETING_FILE);
14
10
  }
15
11
 
16
12
  const DEFAULT_TICKETING = { ticket_id: null, ticketing_required: false, ticket_confirmed: false, records: [] };
@@ -48,7 +44,7 @@ export async function loadTicketing() {
48
44
  * Save ticketing.local.json.
49
45
  */
50
46
  export async function saveTicketing(data) {
51
- await mkdir(dboDir(), { recursive: true });
47
+ await mkdir(projectDir(), { recursive: true });
52
48
  await writeFile(ticketingPath(), JSON.stringify(data, null, 2) + '\n');
53
49
  }
54
50
 
@@ -87,8 +87,8 @@ export async function fetchServerRecords(client, requests) {
87
87
  * makes a single HTTP request and the server only returns records modified
88
88
  * after the given date.
89
89
  *
90
- * The response is used ONLY for comparison — it must NOT replace app.json or
91
- * app_baseline.json.
90
+ * The response is used ONLY for comparison — it must NOT replace the metadata
91
+ * or baseline files.
92
92
  *
93
93
  * @param {DboClient} client
94
94
  * @param {string} appShortName - App short name for the /api/app/object/ endpoint
@@ -181,7 +181,7 @@ function decodeServerValue(value) {
181
181
  * a newer server edit, and _LastUpdatedUserID identifies who made the change.
182
182
  *
183
183
  * @param {Object} serverEntry - Record from per-record server fetch
184
- * @param {Object} baselineEntry - Record from .app_baseline.json
184
+ * @param {Object} baselineEntry - Record from the baseline file
185
185
  * @param {Object} localMeta - Local .metadata.json object
186
186
  * @param {string} metaDir - Absolute directory of the metadata file
187
187
  * @returns {Promise<Array<{ col: string, serverValue: string, localValue: string, baselineValue: string }>>}
@@ -278,12 +278,12 @@ function findOldestBaselineDate(records, baseline) {
278
278
  * unavailable or returns no data.
279
279
  *
280
280
  * The bulk response is used ONLY for comparison — it does NOT replace
281
- * app.json or app_baseline.json.
281
+ * the metadata or baseline files.
282
282
  *
283
283
  * @param {Array<{ meta: Object, metaPath: string }>} records
284
284
  * Records about to be pushed. Each must have meta.UID, meta._entity.
285
285
  * @param {DboClient} client
286
- * @param {Object} baseline - Loaded baseline from .dbo/.app_baseline.json
286
+ * @param {Object} baseline - Loaded baseline from .app/<shortName>.json (baseline)
287
287
  * @param {Object} options - Commander options (options.yes used for auto-accept)
288
288
  * @param {string} [appShortName] - App short name for bulk fetch (optional)
289
289
  * @param {string} [serverTz] - Server timezone from config (e.g. "America/Chicago")
@@ -41,6 +41,6 @@ export async function resolveTransactionKey(options = {}) {
41
41
  }]);
42
42
 
43
43
  await saveTransactionKeyPreset(preset);
44
- log.dim(` Saved TransactionKeyPreset: ${preset} to .dbo/config.json`);
44
+ log.dim(` Saved TransactionKeyPreset: ${preset} to .app/config.json`);
45
45
  return preset;
46
46
  }
@@ -73,10 +73,10 @@ async function rewriteReferences(filePath) {
73
73
 
74
74
  /**
75
75
  * Recursively find directories that contain _output~ files.
76
- * Skips .dbo/, node_modules/, trash/, .git/.
76
+ * Skips .app/, node_modules/, trash/, .git/.
77
77
  */
78
78
  async function findDirsWithOutputFiles(root) {
79
- const SKIP = new Set(['.dbo', 'node_modules', 'trash', '.git', '.claude']);
79
+ const SKIP = new Set(['.app', 'node_modules', 'trash', '.git', '.claude', 'app_dependencies']);
80
80
  const results = [];
81
81
 
82
82
  async function walk(dir) {
@@ -57,10 +57,10 @@ export default async function run(_options) {
57
57
 
58
58
  /**
59
59
  * Recursively find directories that contain output .json files (with ~ in name).
60
- * Skips .dbo/, node_modules/, trash/, .git/.
60
+ * Skips .app/, node_modules/, trash/, .git/.
61
61
  */
62
62
  async function findDirsWithOutputJsonFiles(root) {
63
- const SKIP = new Set(['.dbo', 'node_modules', 'trash', '.git', '.claude']);
63
+ const SKIP = new Set(['.app', 'node_modules', 'trash', '.git', '.claude', 'app_dependencies']);
64
64
  const results = [];
65
65
 
66
66
  async function walk(dir) {
@@ -161,7 +161,7 @@ export default async function run(_options) {
161
161
  }
162
162
  }
163
163
 
164
- const SKIP = new Set(['.dbo', 'node_modules', 'trash', '.git', '.claude']);
164
+ const SKIP = new Set(['.app', 'node_modules', 'trash', '.git', '.claude', 'app_dependencies']);
165
165
 
166
166
  async function findAllMetadataFiles(dir) {
167
167
  const results = [];
@@ -127,7 +127,7 @@ const ENTITY_DIRS = new Set([
127
127
  'data_source', 'group', 'site', 'redirect',
128
128
  ]);
129
129
 
130
- const SKIP = new Set(['.dbo', 'node_modules', 'trash', '.git', '.claude']);
130
+ const SKIP = new Set(['.app', 'node_modules', 'trash', '.git', '.claude', 'app_dependencies']);
131
131
 
132
132
  /**
133
133
  * Find all .metadata.json files inside entity directories (lib/<entity>/).
@@ -50,7 +50,7 @@ export default async function run(_options) {
50
50
  }
51
51
  }
52
52
 
53
- const SKIP = new Set(['.dbo', 'node_modules', 'trash', '.git', '.claude']);
53
+ const SKIP = new Set(['.app', 'node_modules', 'trash', '.git', '.claude', 'app_dependencies']);
54
54
 
55
55
  async function findAllLegacyMetadataFiles(dir) {
56
56
  const results = [];
@@ -141,7 +141,7 @@ export default async function run(_options) {
141
141
  }
142
142
  }
143
143
 
144
- const SKIP = new Set(['.dbo', 'node_modules', 'trash', '.git', '.claude']);
144
+ const SKIP = new Set(['.app', 'node_modules', 'trash', '.git', '.claude', 'app_dependencies']);
145
145
 
146
146
  async function findAllMetadataFiles(dir) {
147
147
  const results = [];
@@ -41,7 +41,7 @@ export default async function run(_options) {
41
41
  }
42
42
  }
43
43
 
44
- const SKIP = new Set(['.dbo', 'node_modules', 'trash', '.git', '.claude']);
44
+ const SKIP = new Set(['.app', 'node_modules', 'trash', '.git', '.claude', 'app_dependencies']);
45
45
 
46
46
  async function findParenMediaFiles(dir) {
47
47
  const results = [];