@duckcodeailabs/dql-cli 1.6.18 → 1.6.20

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 (79) hide show
  1. package/dist/apps-api.d.ts +41 -1
  2. package/dist/apps-api.d.ts.map +1 -1
  3. package/dist/apps-api.js +297 -18
  4. package/dist/apps-api.js.map +1 -1
  5. package/dist/args.d.ts +4 -0
  6. package/dist/args.d.ts.map +1 -1
  7. package/dist/args.js +10 -1
  8. package/dist/args.js.map +1 -1
  9. package/dist/assets/dql-notebook/assets/index-Dgqvyn5s.js +3886 -0
  10. package/dist/assets/dql-notebook/index.html +1 -1
  11. package/dist/block-studio-import.d.ts +99 -3
  12. package/dist/block-studio-import.d.ts.map +1 -1
  13. package/dist/block-studio-import.js +772 -27
  14. package/dist/block-studio-import.js.map +1 -1
  15. package/dist/commands/agent.d.ts.map +1 -1
  16. package/dist/commands/agent.js +12 -1
  17. package/dist/commands/agent.js.map +1 -1
  18. package/dist/commands/app.d.ts +2 -1
  19. package/dist/commands/app.d.ts.map +1 -1
  20. package/dist/commands/app.js +22 -4
  21. package/dist/commands/app.js.map +1 -1
  22. package/dist/commands/certify.d.ts.map +1 -1
  23. package/dist/commands/certify.js +104 -10
  24. package/dist/commands/certify.js.map +1 -1
  25. package/dist/commands/compile.d.ts +1 -0
  26. package/dist/commands/compile.d.ts.map +1 -1
  27. package/dist/commands/compile.js +26 -4
  28. package/dist/commands/compile.js.map +1 -1
  29. package/dist/commands/doctor.d.ts.map +1 -1
  30. package/dist/commands/doctor.js +345 -6
  31. package/dist/commands/doctor.js.map +1 -1
  32. package/dist/commands/import.d.ts.map +1 -1
  33. package/dist/commands/import.js +45 -70
  34. package/dist/commands/import.js.map +1 -1
  35. package/dist/commands/lineage.d.ts +2 -0
  36. package/dist/commands/lineage.d.ts.map +1 -1
  37. package/dist/commands/lineage.js +111 -0
  38. package/dist/commands/lineage.js.map +1 -1
  39. package/dist/commands/migrate.d.ts +1 -0
  40. package/dist/commands/migrate.d.ts.map +1 -1
  41. package/dist/commands/migrate.js +106 -2
  42. package/dist/commands/migrate.js.map +1 -1
  43. package/dist/commands/new.d.ts.map +1 -1
  44. package/dist/commands/new.js +246 -9
  45. package/dist/commands/new.js.map +1 -1
  46. package/dist/commands/promote.d.ts +11 -0
  47. package/dist/commands/promote.d.ts.map +1 -0
  48. package/dist/commands/promote.js +236 -0
  49. package/dist/commands/promote.js.map +1 -0
  50. package/dist/commands/semantic.js +2 -2
  51. package/dist/commands/semantic.js.map +1 -1
  52. package/dist/index.js +48 -8
  53. package/dist/index.js.map +1 -1
  54. package/dist/llm/providers/claude-agent-sdk.js +1 -1
  55. package/dist/llm/providers/claude-agent-sdk.js.map +1 -1
  56. package/dist/llm/tools.js +1 -1
  57. package/dist/llm/tools.js.map +1 -1
  58. package/dist/local-runtime.d.ts +176 -5
  59. package/dist/local-runtime.d.ts.map +1 -1
  60. package/dist/local-runtime.js +3695 -146
  61. package/dist/local-runtime.js.map +1 -1
  62. package/dist/package.json +10 -10
  63. package/dist/promote-from-draft.d.ts +11 -10
  64. package/dist/promote-from-draft.d.ts.map +1 -1
  65. package/dist/promote-from-draft.js +53 -30
  66. package/dist/promote-from-draft.js.map +1 -1
  67. package/dist/schedule/runner.js +2 -2
  68. package/dist/schedule/runner.js.map +1 -1
  69. package/dist/semantic-import.d.ts.map +1 -1
  70. package/dist/semantic-import.js +28 -12
  71. package/dist/semantic-import.js.map +1 -1
  72. package/package.json +11 -11
  73. package/dist/assets/dql-notebook/assets/index-BbGJ8gZk.js +0 -3639
  74. package/dist/assets/dql-notebook/assets/index-C4oZK5xm.js +0 -3612
  75. package/dist/assets/dql-notebook/assets/index-CEMXuPm0.js +0 -3802
  76. package/dist/assets/dql-notebook/assets/index-CIMLd3Cb.js +0 -3289
  77. package/dist/assets/dql-notebook/assets/index-DISMWw19.js +0 -3780
  78. package/dist/assets/dql-notebook/assets/index-DPcxWQuS.js +0 -3859
  79. package/dist/assets/dql-notebook/assets/index-McpLeeN0.js +0 -3802
@@ -4,7 +4,7 @@
4
4
  * dispatcher — returns `true` if the request was handled, `false` otherwise.
5
5
  */
6
6
  import type { IncomingMessage, ServerResponse } from 'node:http';
7
- import { type AppDocument } from '@duckcodeailabs/dql-core';
7
+ import { type AppDocument, type DashboardDisplayMetadata, type DashboardDocument } from '@duckcodeailabs/dql-core';
8
8
  import { type LocalAppInvestigationIntent } from '@duckcodeailabs/dql-project';
9
9
  interface Ctx {
10
10
  req: IncomingMessage;
@@ -20,6 +20,7 @@ export declare function handleAppsApi(ctx: Ctx): Promise<boolean>;
20
20
  type AppListEntry = {
21
21
  id: string;
22
22
  name: string;
23
+ filePath: string;
23
24
  domain: string;
24
25
  subdomain?: string;
25
26
  groups: string[];
@@ -63,6 +64,19 @@ interface AppRecommendationRequest {
63
64
  audience?: string;
64
65
  certifiedOnly?: boolean;
65
66
  }
67
+ interface VisualizationRecommendationRequest {
68
+ blockRef?: string;
69
+ resultSchema?: unknown;
70
+ rowSample?: Array<Record<string, unknown>>;
71
+ rows?: Array<Record<string, unknown>>;
72
+ appAudience?: string;
73
+ audience?: string;
74
+ prompt?: string;
75
+ filters?: unknown;
76
+ allowedVisualizations?: string[];
77
+ component?: DashboardDisplayMetadata['component'];
78
+ defaultVisualization?: string;
79
+ }
66
80
  interface AppCreateRequest {
67
81
  name?: string;
68
82
  domain?: string;
@@ -122,6 +136,18 @@ interface BlockCandidate {
122
136
  reasons: string[];
123
137
  }
124
138
  export declare function recommendBlocks(projectRoot: string, input: AppRecommendationRequest): BlockCandidate[];
139
+ export declare function recommendVisualization(projectRoot: string, input: VisualizationRecommendationRequest): {
140
+ ok: true;
141
+ display: DashboardDisplayMetadata;
142
+ evidence: Array<{
143
+ source: string;
144
+ reason: string;
145
+ }>;
146
+ warnings: string[];
147
+ } | {
148
+ ok: false;
149
+ error: string;
150
+ };
125
151
  export declare function generateAppPackage(projectRoot: string, input: AppGenerateRequest): Promise<{
126
152
  ok: true;
127
153
  plan: unknown;
@@ -157,8 +183,22 @@ declare function buildDeterministicInvestigationSql(projectRoot: string, input:
157
183
  sourceBlockPath: string;
158
184
  sourceBlockName: string;
159
185
  } | undefined;
186
+ export declare function createDashboardForApp(projectRoot: string, appId: string, input: {
187
+ id?: string;
188
+ title?: string;
189
+ description?: string;
190
+ }): {
191
+ ok: true;
192
+ dashboard: DashboardDocument;
193
+ path: string;
194
+ } | {
195
+ ok: false;
196
+ error: string;
197
+ };
160
198
  declare function loadAppById(projectRoot: string, id: string): {
161
199
  app: AppDocument;
200
+ appDir: string;
201
+ appPath: string;
162
202
  dashboards: Array<{
163
203
  id: string;
164
204
  title: string;
@@ -1 +1 @@
1
- {"version":3,"file":"apps-api.d.ts","sourceRoot":"","sources":["../src/apps-api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"apps-api.d.ts","sourceRoot":"","sources":["../src/apps-api.ts"],"names":[],"mappings":"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"}
package/dist/apps-api.js CHANGED
@@ -9,6 +9,21 @@ import { loadAppDocument, findAppDocuments, loadDashboardDocument, findDashboard
9
9
  import { defaultPersonaRegistry, defaultLocalAppsDbPath, LocalAppStorage, personaFromMember, } from '@duckcodeailabs/dql-project';
10
10
  export async function handleAppsApi(ctx) {
11
11
  const { req, res, path, projectRoot } = ctx;
12
+ if (req.method === 'POST' && path === '/api/visualizations/recommend') {
13
+ try {
14
+ const body = await readJson(req);
15
+ const result = recommendVisualization(projectRoot, body);
16
+ if (!result.ok) {
17
+ sendJson(res, 400, result);
18
+ return true;
19
+ }
20
+ sendJson(res, 200, result);
21
+ }
22
+ catch (err) {
23
+ sendJson(res, 500, { ok: false, error: err.message });
24
+ }
25
+ return true;
26
+ }
12
27
  // ── Apps ────────────────────────────────────────────────────────────────
13
28
  if (req.method === 'GET' && path === '/api/apps') {
14
29
  const apps = collectAppsList(projectRoot);
@@ -107,7 +122,7 @@ export async function handleAppsApi(ctx) {
107
122
  sendJson(res, 404, { error: `App "${appId}" not found` });
108
123
  return true;
109
124
  }
110
- sendJson(res, 200, { notebooks: listNotebookCandidates(projectRoot, loaded.app, join(projectRoot, 'apps', appId)) });
125
+ sendJson(res, 200, { notebooks: listNotebookCandidates(projectRoot, loaded.app, loaded.appDir) });
111
126
  return true;
112
127
  }
113
128
  m = path.match(/^\/api\/apps\/([^/]+)\/notebooks\/create$/);
@@ -612,6 +627,7 @@ function collectAppsList(projectRoot) {
612
627
  out.push({
613
628
  id: document.id,
614
629
  name: document.name,
630
+ filePath: relative(projectRoot, appDir),
615
631
  domain: document.domain,
616
632
  subdomain: document.subdomain,
617
633
  groups: document.groups ?? [],
@@ -710,6 +726,79 @@ export function recommendBlocks(projectRoot, input) {
710
726
  .sort((a, b) => b.score - a.score || new Date(b.lastModified).getTime() - new Date(a.lastModified).getTime())
711
727
  .slice(0, 50);
712
728
  }
729
+ export function recommendVisualization(projectRoot, input) {
730
+ const blockRef = cleanString(input.blockRef);
731
+ const prompt = cleanString(input.prompt).toLowerCase();
732
+ const audience = cleanString(input.appAudience) || cleanString(input.audience);
733
+ const rows = Array.isArray(input.rowSample) ? input.rowSample : Array.isArray(input.rows) ? input.rows : [];
734
+ const columns = extractRecommendationColumns(input.resultSchema, rows);
735
+ const block = blockRef
736
+ ? collectBlockCandidates(projectRoot).find((candidate) => candidate.id === blockRef ||
737
+ candidate.name === blockRef ||
738
+ candidate.path === blockRef ||
739
+ candidate.path.endsWith(`/${blockRef}`))
740
+ : undefined;
741
+ const evidence = [];
742
+ const warnings = [];
743
+ if (block) {
744
+ evidence.push({ source: block.path, reason: `Block hint: ${block.chartType ?? 'table'}; status: ${block.status}; domain: ${block.domain}` });
745
+ }
746
+ else if (blockRef) {
747
+ warnings.push(`Block reference was not found locally: ${blockRef}`);
748
+ }
749
+ if (columns.length > 0) {
750
+ evidence.push({ source: 'result_schema', reason: `${columns.length} result column(s) were inspected.` });
751
+ }
752
+ else {
753
+ warnings.push('No result schema or row sample was provided, so recommendation used block metadata and prompt only.');
754
+ }
755
+ if (audience) {
756
+ evidence.push({ source: 'audience', reason: `Audience: ${audience}` });
757
+ }
758
+ const requestedViz = cleanString(input.defaultVisualization);
759
+ if (requestedViz && !isSupportedVizType(requestedViz)) {
760
+ return { ok: false, error: `Unsupported visualization: ${requestedViz}` };
761
+ }
762
+ const defaultVisualization = requestedViz
763
+ ? normalizeVizType(requestedViz)
764
+ : recommendVizType({ columns, rows, prompt, blockChartType: block?.chartType });
765
+ const component = input.component ?? componentForViz(defaultVisualization, prompt);
766
+ if (!componentVizCompatible(component, defaultVisualization)) {
767
+ return {
768
+ ok: false,
769
+ error: `${component} cannot use ${defaultVisualization}. Choose one of ${allowedVisualizationsForComponent(component, defaultVisualization).join(', ')}.`,
770
+ };
771
+ }
772
+ const unsupportedAllowed = (input.allowedVisualizations ?? []).filter((value) => !isSupportedVizType(value));
773
+ if (unsupportedAllowed.length > 0) {
774
+ return { ok: false, error: `Unsupported allowed visualization(s): ${unsupportedAllowed.join(', ')}` };
775
+ }
776
+ const supportedAllowed = (input.allowedVisualizations ?? [])
777
+ .map((value) => normalizeVizType(value))
778
+ .filter((value, index, arr) => arr.indexOf(value) === index);
779
+ if (supportedAllowed.length > 0 && !supportedAllowed.includes(defaultVisualization)) {
780
+ return {
781
+ ok: false,
782
+ error: `Requested visualization ${defaultVisualization} is outside allowed visualizations: ${supportedAllowed.join(', ')}.`,
783
+ };
784
+ }
785
+ const allowedVisualizations = supportedAllowed.length > 0
786
+ ? supportedAllowed
787
+ : allowedVisualizationsForComponent(component, defaultVisualization);
788
+ const fieldHints = fieldHintsForColumns(columns, rows, prompt);
789
+ const display = {
790
+ mode: block ? 'block_hint' : 'ai_generated',
791
+ component,
792
+ defaultVisualization,
793
+ allowedVisualizations,
794
+ ...(Object.keys(fieldHints).length > 0 ? { fieldHints } : {}),
795
+ layoutIntent: layoutIntentForComponent(component),
796
+ rationale: recommendationRationale(component, defaultVisualization, block, columns, prompt),
797
+ trustState: block?.status === 'certified' ? 'certified' : 'review_required',
798
+ reviewStatus: block?.status === 'certified' ? 'certified' : 'review_required',
799
+ };
800
+ return { ok: true, display, evidence, warnings };
801
+ }
713
802
  export async function generateAppPackage(projectRoot, input) {
714
803
  const prompt = cleanString(input.prompt);
715
804
  if (!prompt)
@@ -758,7 +847,7 @@ export function createAppPackage(projectRoot, input) {
758
847
  if (!domain)
759
848
  return { ok: false, error: 'domain is required' };
760
849
  const id = suggestAppId(name);
761
- const appDir = join(projectRoot, 'apps', id);
850
+ const appDir = resolveAppPackageDir(projectRoot, domain, id);
762
851
  if (existsSync(appDir))
763
852
  return { ok: false, error: `App already exists: ${id}` };
764
853
  const dashboardTitle = cleanString(input.dashboardTitle) || 'Overview';
@@ -901,6 +990,7 @@ function dashboardItemForBlock(block, chartType, x, y, size, index) {
901
990
  h: size.h,
902
991
  block: { blockId: block.name },
903
992
  viz: { type: chartType },
993
+ display: displayForBlockHint(block, chartType),
904
994
  title: block.name,
905
995
  };
906
996
  }
@@ -957,8 +1047,183 @@ function normalizeVizType(chartType) {
957
1047
  return 'map';
958
1048
  if (normalized === 'funnel')
959
1049
  return 'funnel';
1050
+ if (normalized === 'text')
1051
+ return 'text';
1052
+ if (normalized === 'heading')
1053
+ return 'heading';
960
1054
  return 'table';
961
1055
  }
1056
+ function isSupportedVizType(chartType) {
1057
+ const normalized = (chartType ?? '').toLowerCase().replace(/-/g, '_');
1058
+ return [
1059
+ 'single', 'single_value', 'kpi', 'line', 'bar', 'grouped_bar', 'stacked_bar',
1060
+ 'area', 'pie', 'donut', 'scatter', 'heatmap', 'histogram', 'waterfall',
1061
+ 'gauge', 'pivot', 'map', 'funnel', 'table', 'text', 'heading',
1062
+ ].includes(normalized);
1063
+ }
1064
+ function displayForBlockHint(block, chartType) {
1065
+ const component = componentForViz(chartType, `${block.name} ${block.description} ${block.tags.join(' ')}`.toLowerCase());
1066
+ return {
1067
+ mode: 'block_hint',
1068
+ component,
1069
+ defaultVisualization: chartType,
1070
+ allowedVisualizations: allowedVisualizationsForComponent(component, chartType),
1071
+ layoutIntent: layoutIntentForComponent(component),
1072
+ rationale: `Block-level visualization is treated as a display hint for ${block.name}; apps and notebooks may override it.`,
1073
+ trustState: block.status === 'certified' ? 'certified' : 'draft_ready',
1074
+ reviewStatus: block.status === 'certified' ? 'certified' : 'draft_ready',
1075
+ };
1076
+ }
1077
+ function displayForAiPin(title, chartType, input) {
1078
+ const text = `${title} ${cleanString(input.question)} ${cleanString(input.answer)}`.toLowerCase();
1079
+ const component = componentForViz(chartType, text);
1080
+ const certified = input.certification === 'certified' || input.reviewStatus === 'certified';
1081
+ return {
1082
+ mode: 'ai_generated',
1083
+ component,
1084
+ defaultVisualization: chartType,
1085
+ allowedVisualizations: allowedVisualizationsForComponent(component, chartType),
1086
+ layoutIntent: layoutIntentForComponent(component),
1087
+ rationale: 'AI-generated research pin saved as governed presentation metadata; promote the SQL to a draft block before treating it as reusable logic.',
1088
+ trustState: certified ? 'certified' : 'review_required',
1089
+ reviewStatus: certified ? 'certified' : 'review_required',
1090
+ };
1091
+ }
1092
+ function extractRecommendationColumns(schema, rows) {
1093
+ const rawColumns = Array.isArray(schema)
1094
+ ? schema
1095
+ : schema && typeof schema === 'object' && Array.isArray(schema.columns)
1096
+ ? schema.columns
1097
+ : [];
1098
+ const columns = rawColumns.flatMap((column) => {
1099
+ if (typeof column === 'string')
1100
+ return [{ name: column }];
1101
+ if (column && typeof column === 'object') {
1102
+ const record = column;
1103
+ const name = cleanString(record.name) || cleanString(record.field) || cleanString(record.id);
1104
+ if (!name)
1105
+ return [];
1106
+ const type = cleanString(record.type) || cleanString(record.dataType);
1107
+ return [{ name, ...(type ? { type } : {}) }];
1108
+ }
1109
+ return [];
1110
+ });
1111
+ if (columns.length > 0)
1112
+ return columns;
1113
+ const first = rows.find((row) => row && typeof row === 'object');
1114
+ return first ? Object.keys(first).map((name) => ({ name, type: typeof first[name] })) : [];
1115
+ }
1116
+ function recommendVizType(input) {
1117
+ const names = input.columns.map((column) => column.name.toLowerCase());
1118
+ const hasTime = names.some((name) => /\b(date|time|week|month|quarter|year|season|period)\b/.test(name));
1119
+ const measures = input.columns.filter((column) => isMeasureColumn(column, input.rows));
1120
+ const dimensions = input.columns.filter((column) => !isMeasureColumn(column, input.rows));
1121
+ if (/\bpivot|matrix|cross.?tab\b/.test(input.prompt))
1122
+ return 'pivot';
1123
+ if (/\btrend|over time|weekly|monthly|daily|quarterly\b/.test(input.prompt) || (hasTime && measures.length > 0))
1124
+ return 'line';
1125
+ if (/\btop|bottom|rank|ranking|leader|leaderboard|players?|scorers?\b/.test(input.prompt))
1126
+ return 'bar';
1127
+ if (measures.length === 1 && dimensions.length === 0)
1128
+ return 'single_value';
1129
+ if (dimensions.length > 0 && measures.length > 0)
1130
+ return 'bar';
1131
+ return normalizeVizType(input.blockChartType);
1132
+ }
1133
+ function isMeasureColumn(column, rows) {
1134
+ const type = (column.type ?? '').toLowerCase();
1135
+ if (/\b(number|numeric|decimal|double|float|integer|int|bigint|real)\b/.test(type))
1136
+ return true;
1137
+ const name = column.name.toLowerCase();
1138
+ if (/\b(count|sum|avg|average|total|revenue|arr|amount|rate|score|points?|goals?|rank|value)\b/.test(name))
1139
+ return true;
1140
+ const sample = rows.map((row) => row[column.name]).find((value) => value !== null && value !== undefined);
1141
+ return typeof sample === 'number';
1142
+ }
1143
+ function fieldHintsForColumns(columns, rows, prompt) {
1144
+ const hints = {};
1145
+ const measure = columns.find((column) => isMeasureColumn(column, rows));
1146
+ const time = columns.find((column) => /\b(date|time|week|month|quarter|year|season|period)\b/i.test(column.name));
1147
+ const rank = columns.find((column) => /\b(rank|position)\b/i.test(column.name));
1148
+ const label = columns.find((column) => !isMeasureColumn(column, rows) && /\b(name|player|customer|account|team|segment|region|category|label)\b/i.test(column.name)) ?? columns.find((column) => !isMeasureColumn(column, rows));
1149
+ if (label)
1150
+ hints.label = label.name;
1151
+ if (measure)
1152
+ hints.value = measure.name;
1153
+ if (time)
1154
+ hints.time = time.name;
1155
+ if (rank || /\brank|top|bottom\b/i.test(prompt))
1156
+ hints.rank = rank?.name ?? 'rank';
1157
+ if (time && measure) {
1158
+ hints.x = time.name;
1159
+ hints.y = measure.name;
1160
+ }
1161
+ else if (label && measure) {
1162
+ hints.x = label.name;
1163
+ hints.y = measure.name;
1164
+ }
1165
+ return hints;
1166
+ }
1167
+ function componentForViz(viz, text) {
1168
+ if (viz === 'single_value' || viz === 'kpi' || viz === 'gauge')
1169
+ return 'KpiMetric';
1170
+ if (viz === 'line' || viz === 'area')
1171
+ return 'TrendPanel';
1172
+ if (viz === 'pivot')
1173
+ return 'PivotTable';
1174
+ if (viz === 'text' || viz === 'heading')
1175
+ return /\btrust|evidence|caveat|quality\b/.test(text) ? 'TrustCallout' : 'NarrativePanel';
1176
+ if (/\btop|bottom|rank|ranking|leader|leaderboard|scorer|player\b/.test(text))
1177
+ return 'RankingPanel';
1178
+ if (viz === 'bar' || viz === 'grouped_bar' || viz === 'stacked_bar' || viz === 'pie' || viz === 'donut')
1179
+ return 'RankingPanel';
1180
+ return 'EvidenceTable';
1181
+ }
1182
+ function allowedVisualizationsForComponent(component, primary) {
1183
+ const base = new Set([primary]);
1184
+ for (const viz of compatibleVisualizationsForComponent(component))
1185
+ base.add(viz);
1186
+ return Array.from(base);
1187
+ }
1188
+ function compatibleVisualizationsForComponent(component) {
1189
+ if (component === 'KpiMetric') {
1190
+ return ['single_value', 'kpi', 'gauge', 'table'];
1191
+ }
1192
+ if (component === 'TrendPanel')
1193
+ return ['line', 'area', 'bar', 'table'];
1194
+ if (component === 'RankingPanel')
1195
+ return ['bar', 'grouped_bar', 'stacked_bar', 'pie', 'donut', 'table'];
1196
+ if (component === 'PivotTable')
1197
+ return ['pivot', 'table', 'bar'];
1198
+ if (component === 'EvidenceTable')
1199
+ return ['table', 'bar', 'scatter', 'heatmap', 'histogram', 'waterfall', 'map', 'funnel'];
1200
+ return ['text', 'heading'];
1201
+ }
1202
+ function componentVizCompatible(component, viz) {
1203
+ return compatibleVisualizationsForComponent(component).includes(viz);
1204
+ }
1205
+ function layoutIntentForComponent(component) {
1206
+ if (component === 'KpiMetric' || component === 'TrustCallout' || component === 'ResearchActions')
1207
+ return 'compact';
1208
+ if (component === 'TrendPanel' || component === 'RankingPanel')
1209
+ return 'wide';
1210
+ if (component === 'PivotTable' || component === 'EvidenceTable')
1211
+ return 'standard';
1212
+ if (component === 'BusinessBrief')
1213
+ return 'wide';
1214
+ return 'standard';
1215
+ }
1216
+ function recommendationRationale(component, viz, block, columns, prompt) {
1217
+ if (block) {
1218
+ return `Recommended ${component} with ${viz} from the block display hint and result shape. The certified block remains the business logic source.`;
1219
+ }
1220
+ if (columns.length > 0) {
1221
+ return `Recommended ${component} with ${viz} from ${columns.length} output column(s) and the app prompt. This is consumer-level metadata pending review.`;
1222
+ }
1223
+ return prompt
1224
+ ? `Recommended ${component} with ${viz} from the app prompt. Review against previewed fields before sharing.`
1225
+ : `Recommended ${component} with ${viz} as a safe table-first presentation fallback.`;
1226
+ }
962
1227
  function appReadme(app, audience, blocks, dashboardId = 'overview') {
963
1228
  return [
964
1229
  `# ${app.name}`,
@@ -989,18 +1254,19 @@ function appReadme(app, audience, blocks, dashboardId = 'overview') {
989
1254
  ].join('\n');
990
1255
  }
991
1256
  function collectBlockCandidates(projectRoot) {
992
- const blocksDir = join(projectRoot, 'blocks');
993
1257
  const blocks = [];
994
- if (!existsSync(blocksDir))
995
- return blocks;
1258
+ const seen = new Set();
996
1259
  const scanDir = (dir) => {
1260
+ if (!existsSync(dir))
1261
+ return;
997
1262
  for (const entry of readdirSyncSafe(dir)) {
998
1263
  const filePath = join(dir, entry.name);
999
1264
  if (entry.isDirectory()) {
1000
1265
  scanDir(filePath);
1001
1266
  }
1002
- else if (entry.isFile() && entry.name.endsWith('.dql')) {
1267
+ else if (entry.isFile() && entry.name.endsWith('.dql') && !seen.has(filePath)) {
1003
1268
  try {
1269
+ seen.add(filePath);
1004
1270
  const source = readFileSync(filePath, 'utf-8');
1005
1271
  const stat = statSyncSafe(filePath);
1006
1272
  const name = matchString(source, /block\s+"([^"]+)"/) ?? entry.name.replace(/\.dql$/, '');
@@ -1012,7 +1278,7 @@ function collectBlockCandidates(projectRoot) {
1012
1278
  status: matchString(source, /status\s*=\s*"([^"]+)"/) ?? 'draft',
1013
1279
  owner: matchString(source, /owner\s*=\s*"([^"]+)"/),
1014
1280
  tags,
1015
- path: filePath.slice(projectRoot.length + 1),
1281
+ path: relative(projectRoot, filePath).replaceAll('\\', '/'),
1016
1282
  lastModified: stat?.mtime.toISOString() ?? new Date(0).toISOString(),
1017
1283
  description: matchString(source, /description\s*=\s*"((?:[^"\\]|\\.)*)"/) ?? '',
1018
1284
  llmContext: matchString(source, /llmContext\s*=\s*"((?:[^"\\]|\\.)*)"/),
@@ -1027,7 +1293,8 @@ function collectBlockCandidates(projectRoot) {
1027
1293
  }
1028
1294
  }
1029
1295
  };
1030
- scanDir(blocksDir);
1296
+ scanDir(join(projectRoot, 'blocks'));
1297
+ scanDir(join(projectRoot, 'domains'));
1031
1298
  return blocks;
1032
1299
  }
1033
1300
  function readdirSyncSafe(dir) {
@@ -1902,6 +2169,14 @@ function slugify(value) {
1902
2169
  .replace(/[^a-z0-9]+/g, '-')
1903
2170
  .replace(/^-+|-+$/g, '');
1904
2171
  }
2172
+ function resolveAppPackageDir(projectRoot, domain, id) {
2173
+ const domainSlug = slugify(domain);
2174
+ const domainDir = domainSlug ? join(projectRoot, 'domains', domainSlug) : '';
2175
+ if (domainDir && existsSync(domainDir)) {
2176
+ return join(domainDir, 'apps', id);
2177
+ }
2178
+ return join(projectRoot, 'apps', id);
2179
+ }
1905
2180
  function titleFromPath(path) {
1906
2181
  return basename(path)
1907
2182
  .replace(/\.(dqlnb|dql)$/i, '')
@@ -1925,7 +2200,7 @@ function appAllowsExecute(app, domain) {
1925
2200
  return policy.accessLevel === 'execute' || policy.accessLevel === 'admin';
1926
2201
  });
1927
2202
  }
1928
- function createDashboardForApp(projectRoot, appId, input) {
2203
+ export function createDashboardForApp(projectRoot, appId, input) {
1929
2204
  const loaded = loadAppById(projectRoot, appId);
1930
2205
  if (!loaded)
1931
2206
  return { ok: false, error: `App "${appId}" not found` };
@@ -1933,7 +2208,7 @@ function createDashboardForApp(projectRoot, appId, input) {
1933
2208
  const id = slugify(cleanString(input.id) || title) || `page-${Date.now()}`;
1934
2209
  if (!/^[a-z0-9][a-z0-9_-]*$/i.test(id))
1935
2210
  return { ok: false, error: 'dashboard id must be folder-safe' };
1936
- const appDir = join(projectRoot, 'apps', appId);
2211
+ const appDir = loaded.appDir;
1937
2212
  const dashboardPath = join(appDir, 'dashboards', `${id}.dqld`);
1938
2213
  if (existsSync(dashboardPath))
1939
2214
  return { ok: false, error: `Dashboard already exists: ${id}` };
@@ -2010,11 +2285,13 @@ function createAiPinTile(projectRoot, appId, input) {
2010
2285
  evidence: input.evidence,
2011
2286
  followUps: Array.isArray(input.followUps) ? input.followUps.filter((item) => typeof item === 'string') : [],
2012
2287
  });
2288
+ const vizType = normalizeVizTypeFromChart(input.chartConfig);
2013
2289
  const tile = {
2014
2290
  i: tileId,
2015
2291
  ...nextTilePosition(loaded.dashboard),
2016
2292
  aiPin: { id: pin.id },
2017
- viz: { type: normalizeVizTypeFromChart(input.chartConfig) },
2293
+ viz: { type: vizType },
2294
+ display: displayForAiPin(title, vizType, input),
2018
2295
  title,
2019
2296
  };
2020
2297
  const dashboard = {
@@ -2057,7 +2334,7 @@ function promoteAiPinToDraftBlock(projectRoot, appId, pinId) {
2057
2334
  analysisPlan?.dimensions?.length ? `Dimensions: ${analysisPlan.dimensions.join(', ')}` : '',
2058
2335
  analysisPlan?.measures?.length ? `Measures: ${analysisPlan.measures.join(', ')}` : '',
2059
2336
  ].filter(Boolean).join(' | ');
2060
- const draftDir = join(projectRoot, 'apps', appId, 'drafts');
2337
+ const draftDir = join(loaded.appDir, 'drafts');
2061
2338
  const blockPath = join(draftDir, `${blockName}.dql`);
2062
2339
  mkdirSync(draftDir, { recursive: true });
2063
2340
  const source = [
@@ -2138,6 +2415,8 @@ function loadAppById(projectRoot, id) {
2138
2415
  }
2139
2416
  return {
2140
2417
  app: document,
2418
+ appDir,
2419
+ appPath: relative(projectRoot, appDir),
2141
2420
  dashboards,
2142
2421
  notebooks: listAppNotebookRefs(projectRoot, document, appDir),
2143
2422
  drafts: listAppDrafts(projectRoot, appDir),
@@ -2182,8 +2461,8 @@ export function createNotebookForApp(projectRoot, appId, input) {
2182
2461
  return { ok: false, error: `App "${appId}" not found` };
2183
2462
  const title = cleanString(input.title) || cleanString(input.name) || 'App analysis';
2184
2463
  const slug = slugify(cleanString(input.name) || title) || `notebook-${Date.now()}`;
2185
- const appDir = join(projectRoot, 'apps', appId);
2186
- const relPath = `apps/${appId}/notebooks/${slug}.dqlnb`;
2464
+ const appDir = loaded.appDir;
2465
+ const relPath = relative(projectRoot, join(appDir, 'notebooks', `${slug}.dqlnb`)).replaceAll('\\', '/');
2187
2466
  const absPath = join(projectRoot, relPath);
2188
2467
  if (existsSync(absPath))
2189
2468
  return { ok: false, error: `Notebook already exists: ${relPath}` };
@@ -2488,10 +2767,10 @@ function writeDashboard(projectRoot, appId, dashboardId, payload) {
2488
2767
  return { ok: false, error: `dashboard.id (${document.id}) does not match URL :did (${dashboardId})` };
2489
2768
  }
2490
2769
  // Confirm the App exists.
2491
- const appDir = join(projectRoot, 'apps', appId);
2492
- if (!existsSync(join(appDir, 'dql.app.json'))) {
2493
- return { ok: false, error: `App "${appId}" not found at ${appDir}` };
2494
- }
2770
+ const loaded = loadAppById(projectRoot, appId);
2771
+ if (!loaded)
2772
+ return { ok: false, error: `App "${appId}" not found` };
2773
+ const appDir = loaded.appDir;
2495
2774
  const dashboardPath = join(appDir, 'dashboards', `${dashboardId}.dqld`);
2496
2775
  mkdirSync(dirname(dashboardPath), { recursive: true });
2497
2776
  writeFileSync(dashboardPath, JSON.stringify(document, null, 2) + '\n', 'utf-8');