@duckcodeailabs/dql-agent 1.6.21 → 1.6.22
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/dist/agent-run-engine.d.ts +287 -0
- package/dist/agent-run-engine.d.ts.map +1 -0
- package/dist/agent-run-engine.js +916 -0
- package/dist/agent-run-engine.js.map +1 -0
- package/dist/agent-run-gates.d.ts +26 -0
- package/dist/agent-run-gates.d.ts.map +1 -0
- package/dist/agent-run-gates.js +170 -0
- package/dist/agent-run-gates.js.map +1 -0
- package/dist/agent-run-planner.d.ts +32 -0
- package/dist/agent-run-planner.d.ts.map +1 -0
- package/dist/agent-run-planner.js +280 -0
- package/dist/agent-run-planner.js.map +1 -0
- package/dist/answer-loop.d.ts +49 -0
- package/dist/answer-loop.d.ts.map +1 -1
- package/dist/answer-loop.js +292 -17
- package/dist/answer-loop.js.map +1 -1
- package/dist/app-builder.d.ts +6 -0
- package/dist/app-builder.d.ts.map +1 -1
- package/dist/app-builder.js +95 -2
- package/dist/app-builder.js.map +1 -1
- package/dist/app-planner.d.ts +78 -0
- package/dist/app-planner.d.ts.map +1 -0
- package/dist/app-planner.js +145 -0
- package/dist/app-planner.js.map +1 -0
- package/dist/embeddings/provider.d.ts +66 -0
- package/dist/embeddings/provider.d.ts.map +1 -0
- package/dist/embeddings/provider.js +105 -0
- package/dist/embeddings/provider.js.map +1 -0
- package/dist/hints/git-store.d.ts +69 -0
- package/dist/hints/git-store.d.ts.map +1 -0
- package/dist/hints/git-store.js +244 -0
- package/dist/hints/git-store.js.map +1 -0
- package/dist/hints/retrieval.d.ts +49 -0
- package/dist/hints/retrieval.d.ts.map +1 -0
- package/dist/hints/retrieval.js +60 -0
- package/dist/hints/retrieval.js.map +1 -0
- package/dist/hints/store.d.ts +39 -0
- package/dist/hints/store.d.ts.map +1 -0
- package/dist/hints/store.js +279 -0
- package/dist/hints/store.js.map +1 -0
- package/dist/hints/types.d.ts +147 -0
- package/dist/hints/types.d.ts.map +1 -0
- package/dist/hints/types.js +92 -0
- package/dist/hints/types.js.map +1 -0
- package/dist/index.d.ts +43 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -3
- package/dist/index.js.map +1 -1
- package/dist/intent-controller.d.ts +63 -0
- package/dist/intent-controller.d.ts.map +1 -0
- package/dist/intent-controller.js +150 -0
- package/dist/intent-controller.js.map +1 -0
- package/dist/kg/build.d.ts.map +1 -1
- package/dist/kg/build.js +2 -0
- package/dist/kg/build.js.map +1 -1
- package/dist/kg/sqlite-fts.js +4 -0
- package/dist/kg/sqlite-fts.js.map +1 -1
- package/dist/kg/types.d.ts +10 -0
- package/dist/kg/types.d.ts.map +1 -1
- package/dist/metadata/catalog.d.ts +81 -4
- package/dist/metadata/catalog.d.ts.map +1 -1
- package/dist/metadata/catalog.js +208 -1
- package/dist/metadata/catalog.js.map +1 -1
- package/dist/metadata/drafts.d.ts +22 -0
- package/dist/metadata/drafts.d.ts.map +1 -1
- package/dist/metadata/drafts.js +126 -2
- package/dist/metadata/drafts.js.map +1 -1
- package/dist/metadata/grain-gate.d.ts +61 -0
- package/dist/metadata/grain-gate.d.ts.map +1 -0
- package/dist/metadata/grain-gate.js +265 -0
- package/dist/metadata/grain-gate.js.map +1 -0
- package/dist/metadata/identity.d.ts +47 -0
- package/dist/metadata/identity.d.ts.map +1 -0
- package/dist/metadata/identity.js +147 -0
- package/dist/metadata/identity.js.map +1 -0
- package/dist/metadata/metric-match.d.ts +93 -0
- package/dist/metadata/metric-match.d.ts.map +1 -0
- package/dist/metadata/metric-match.js +233 -0
- package/dist/metadata/metric-match.js.map +1 -0
- package/dist/metadata/sql-grounding.d.ts +149 -0
- package/dist/metadata/sql-grounding.d.ts.map +1 -0
- package/dist/metadata/sql-grounding.js +397 -0
- package/dist/metadata/sql-grounding.js.map +1 -0
- package/dist/metadata/sql-retrieval.d.ts +29 -0
- package/dist/metadata/sql-retrieval.d.ts.map +1 -0
- package/dist/metadata/sql-retrieval.js +79 -0
- package/dist/metadata/sql-retrieval.js.map +1 -0
- package/dist/narrate.d.ts +52 -0
- package/dist/narrate.d.ts.map +1 -0
- package/dist/narrate.js +263 -0
- package/dist/narrate.js.map +1 -0
- package/dist/propose/build-from-prompt.d.ts +160 -0
- package/dist/propose/build-from-prompt.d.ts.map +1 -0
- package/dist/propose/build-from-prompt.js +815 -0
- package/dist/propose/build-from-prompt.js.map +1 -0
- package/dist/propose/classify.d.ts +55 -0
- package/dist/propose/classify.d.ts.map +1 -0
- package/dist/propose/classify.js +167 -0
- package/dist/propose/classify.js.map +1 -0
- package/dist/propose/config.d.ts +39 -0
- package/dist/propose/config.d.ts.map +1 -0
- package/dist/propose/config.js +52 -0
- package/dist/propose/config.js.map +1 -0
- package/dist/propose/dbt-artifacts.d.ts +134 -0
- package/dist/propose/dbt-artifacts.d.ts.map +1 -0
- package/dist/propose/dbt-artifacts.js +320 -0
- package/dist/propose/dbt-artifacts.js.map +1 -0
- package/dist/propose/edit-block.d.ts +74 -0
- package/dist/propose/edit-block.d.ts.map +1 -0
- package/dist/propose/edit-block.js +133 -0
- package/dist/propose/edit-block.js.map +1 -0
- package/dist/propose/enrich.d.ts +48 -0
- package/dist/propose/enrich.d.ts.map +1 -0
- package/dist/propose/enrich.js +125 -0
- package/dist/propose/enrich.js.map +1 -0
- package/dist/propose/generate-sql.d.ts +61 -0
- package/dist/propose/generate-sql.d.ts.map +1 -0
- package/dist/propose/generate-sql.js +180 -0
- package/dist/propose/generate-sql.js.map +1 -0
- package/dist/propose/index.d.ts +24 -0
- package/dist/propose/index.d.ts.map +1 -0
- package/dist/propose/index.js +15 -0
- package/dist/propose/index.js.map +1 -0
- package/dist/propose/propose.d.ts +226 -0
- package/dist/propose/propose.d.ts.map +1 -0
- package/dist/propose/propose.js +668 -0
- package/dist/propose/propose.js.map +1 -0
- package/dist/propose/reflect-block.d.ts +99 -0
- package/dist/propose/reflect-block.d.ts.map +1 -0
- package/dist/propose/reflect-block.js +220 -0
- package/dist/propose/reflect-block.js.map +1 -0
- package/dist/propose/write-draft.d.ts +90 -0
- package/dist/propose/write-draft.d.ts.map +1 -0
- package/dist/propose/write-draft.js +185 -0
- package/dist/propose/write-draft.js.map +1 -0
- package/dist/providers/claude.d.ts +13 -1
- package/dist/providers/claude.d.ts.map +1 -1
- package/dist/providers/claude.js +20 -2
- package/dist/providers/claude.js.map +1 -1
- package/dist/providers/gemini.d.ts +13 -1
- package/dist/providers/gemini.d.ts.map +1 -1
- package/dist/providers/gemini.js +24 -3
- package/dist/providers/gemini.js.map +1 -1
- package/dist/providers/index.d.ts +3 -3
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +3 -3
- package/dist/providers/index.js.map +1 -1
- package/dist/research-loop.d.ts +62 -0
- package/dist/research-loop.d.ts.map +1 -0
- package/dist/research-loop.js +210 -0
- package/dist/research-loop.js.map +1 -0
- package/dist/skills/defaults.d.ts +37 -0
- package/dist/skills/defaults.d.ts.map +1 -0
- package/dist/skills/defaults.js +152 -0
- package/dist/skills/defaults.js.map +1 -0
- package/dist/skills/loader.d.ts +60 -0
- package/dist/skills/loader.d.ts.map +1 -1
- package/dist/skills/loader.js +164 -2
- package/dist/skills/loader.js.map +1 -1
- package/package.json +7 -5
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Research loop (P4) — a grounded, ReAct-style planner for "research / follow-up"
|
|
3
|
+
* questions, so the agent behaves like a real assistant: it DECIDES whether to
|
|
4
|
+
* answer, to research across multiple steps, or to ask a smart follow-up — instead
|
|
5
|
+
* of always generating one query.
|
|
6
|
+
*
|
|
7
|
+
* The "tools" the loop reasons over are DQL's own assets: governed metrics, certified
|
|
8
|
+
* blocks, and their dimensions/lineage. Every step is bound to a REAL asset (no
|
|
9
|
+
* hallucinated tool calls), and every follow-up offers concrete options drawn from
|
|
10
|
+
* the catalog. The first decision reuses the P0 intent controller; this module turns
|
|
11
|
+
* that decision into a grounded plan.
|
|
12
|
+
*/
|
|
13
|
+
import { decideAgentAction } from './intent-controller.js';
|
|
14
|
+
import { matchSemanticMetric } from './metadata/metric-match.js';
|
|
15
|
+
const TIME_DIM_RE = /(_at$|_date$|_time$|_ts$|^date$|^month$|^week$|^day$|ordered_at|created)/i;
|
|
16
|
+
function humanize(name) {
|
|
17
|
+
return name.replace(/[_.]+/g, ' ').replace(/\s+/g, ' ').trim();
|
|
18
|
+
}
|
|
19
|
+
function requestedDimensions(question) {
|
|
20
|
+
const dims = [];
|
|
21
|
+
const re = /\bby\s+([a-z_][a-z0-9_]*)/gi;
|
|
22
|
+
let m;
|
|
23
|
+
while ((m = re.exec(question.toLowerCase()))) {
|
|
24
|
+
if (m[1] && m[1] !== 'the' && !dims.includes(m[1]))
|
|
25
|
+
dims.push(m[1]);
|
|
26
|
+
}
|
|
27
|
+
return dims;
|
|
28
|
+
}
|
|
29
|
+
function blocksForMetric(blocks, metric) {
|
|
30
|
+
if (!metric)
|
|
31
|
+
return [];
|
|
32
|
+
const leaf = metric.toLowerCase().split('.').pop();
|
|
33
|
+
return blocks.filter((b) => {
|
|
34
|
+
const ref = (b.metricRef ?? '').toLowerCase();
|
|
35
|
+
return ref === metric.toLowerCase() || ref.split('.').pop() === leaf;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function timeDimensionOf(blocks) {
|
|
39
|
+
for (const b of blocks) {
|
|
40
|
+
for (const f of [...(b.allowedFilters ?? []), ...(b.dimensions ?? [])]) {
|
|
41
|
+
if (TIME_DIM_RE.test(f))
|
|
42
|
+
return f;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
function categoricalDimensionsOf(blocks) {
|
|
48
|
+
const out = [];
|
|
49
|
+
for (const b of blocks) {
|
|
50
|
+
for (const f of b.allowedFilters ?? []) {
|
|
51
|
+
if (!TIME_DIM_RE.test(f) && !out.includes(f))
|
|
52
|
+
out.push(f);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Plan how to handle a research / follow-up question. Returns the decision plus a
|
|
59
|
+
* grounded plan: a direct answer, a multi-step research trace, or a follow-up with
|
|
60
|
+
* concrete options. Deterministic + offline; the metric match reuses the spec-17 matcher.
|
|
61
|
+
*/
|
|
62
|
+
export async function planResearch(input) {
|
|
63
|
+
// Prefer a metric that already has certified blocks (so the research plan can use
|
|
64
|
+
// its time/breakdown dimensions), falling back to the best overall match.
|
|
65
|
+
const backedNames = new Set(input.blocks
|
|
66
|
+
.map((b) => b.metricRef?.toLowerCase())
|
|
67
|
+
.filter((ref) => Boolean(ref))
|
|
68
|
+
.flatMap((ref) => [ref, ref.split('.').pop() ?? ref]));
|
|
69
|
+
const backedMetrics = input.metrics.filter((m) => backedNames.has(m.name.toLowerCase()) || backedNames.has(m.name.toLowerCase().split('.').pop() ?? ''));
|
|
70
|
+
const match = (backedMetrics.length > 0 ? await matchSemanticMetric(input.question, backedMetrics).catch(() => null) : null) ??
|
|
71
|
+
(await matchSemanticMetric(input.question, input.metrics).catch(() => null));
|
|
72
|
+
const metricName = match?.metric.name;
|
|
73
|
+
const metricBlocks = blocksForMetric(input.blocks, metricName);
|
|
74
|
+
const decision = decideAgentAction({
|
|
75
|
+
question: input.question,
|
|
76
|
+
intent: input.intent ?? 'definition_lookup',
|
|
77
|
+
signals: {
|
|
78
|
+
metricScore: match?.score,
|
|
79
|
+
certifiedScore: metricBlocks.length > 0 ? 0.85 : undefined,
|
|
80
|
+
hasRetrieval: input.metrics.length > 0 || input.blocks.length > 0,
|
|
81
|
+
},
|
|
82
|
+
isFollowUp: input.isFollowUp,
|
|
83
|
+
history: input.history,
|
|
84
|
+
});
|
|
85
|
+
const sources = new Set();
|
|
86
|
+
if (metricName)
|
|
87
|
+
sources.add(metricName);
|
|
88
|
+
for (const b of metricBlocks)
|
|
89
|
+
sources.add(b.name);
|
|
90
|
+
// ── compose_app: hand off to the app planner (P1). ───────────────────────────
|
|
91
|
+
if (decision.action === 'compose_app') {
|
|
92
|
+
return {
|
|
93
|
+
decision: 'compose_app',
|
|
94
|
+
confidence: decision.confidence,
|
|
95
|
+
rationale: decision.reason,
|
|
96
|
+
steps: [{
|
|
97
|
+
thought: 'This asks to assemble a decision surface, not answer one question.',
|
|
98
|
+
action: { kind: 'compose_app', target: metricName ?? humanize(input.question) },
|
|
99
|
+
expectation: 'A planned app: KPI + trend + breakdowns over the certified blocks.',
|
|
100
|
+
}],
|
|
101
|
+
sources: Array.from(sources),
|
|
102
|
+
done: false,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
// ── clarify: ask before researching, with concrete options. ──────────────────
|
|
106
|
+
if (decision.action === 'clarify') {
|
|
107
|
+
const followUp = buildFollowUp(input, match, metricBlocks, decision.clarifyingQuestion);
|
|
108
|
+
return {
|
|
109
|
+
decision: 'clarify',
|
|
110
|
+
confidence: decision.confidence,
|
|
111
|
+
rationale: decision.reason,
|
|
112
|
+
steps: [],
|
|
113
|
+
followUp,
|
|
114
|
+
sources: Array.from(sources),
|
|
115
|
+
done: false,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
// ── investigate: a grounded, multi-step ReAct research trace. ────────────────
|
|
119
|
+
if (decision.action === 'investigate') {
|
|
120
|
+
const steps = [];
|
|
121
|
+
const label = metricName ? humanize(metricName) : humanize(input.question);
|
|
122
|
+
if (metricName) {
|
|
123
|
+
steps.push({
|
|
124
|
+
thought: `Establish the baseline for ${label}.`,
|
|
125
|
+
action: { kind: 'lookup_metric', target: metricName },
|
|
126
|
+
expectation: `The current ${label} value to reason from.`,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
const timeDim = timeDimensionOf(metricBlocks);
|
|
130
|
+
if (timeDim) {
|
|
131
|
+
steps.push({
|
|
132
|
+
thought: `See how ${label} moved over time to locate the change.`,
|
|
133
|
+
action: { kind: 'compare_time', target: timeDim },
|
|
134
|
+
expectation: `When ${label} shifted, and by how much.`,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
const dims = (requestedDimensions(input.question).filter((d) => !TIME_DIM_RE.test(d))
|
|
138
|
+
.filter((d) => categoricalDimensionsOf(metricBlocks).includes(d)));
|
|
139
|
+
const breakdownDim = dims[0] ?? categoricalDimensionsOf(metricBlocks)[0];
|
|
140
|
+
if (breakdownDim) {
|
|
141
|
+
steps.push({
|
|
142
|
+
thought: `Attribute the change — break ${label} down by ${humanize(breakdownDim)}.`,
|
|
143
|
+
action: { kind: 'breakdown', target: breakdownDim },
|
|
144
|
+
expectation: `Which ${humanize(breakdownDim)} segments drove the move.`,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
else if (metricName) {
|
|
148
|
+
steps.push({
|
|
149
|
+
thought: `No breakdown dimension is certified yet — check lineage for drivers.`,
|
|
150
|
+
action: { kind: 'check_lineage', target: metricName },
|
|
151
|
+
expectation: `Upstream models/metrics that feed ${label}.`,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
decision: 'investigate',
|
|
156
|
+
confidence: decision.confidence,
|
|
157
|
+
rationale: decision.reason,
|
|
158
|
+
steps,
|
|
159
|
+
sources: Array.from(sources),
|
|
160
|
+
done: false,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
// ── answer: a single grounded step. ──────────────────────────────────────────
|
|
164
|
+
const answerStep = metricBlocks.length > 0
|
|
165
|
+
? {
|
|
166
|
+
thought: `A certified block answers this directly.`,
|
|
167
|
+
action: { kind: 'lookup_block', target: metricBlocks[0].name },
|
|
168
|
+
expectation: `The certified ${humanize(metricBlocks[0].name)} result.`,
|
|
169
|
+
}
|
|
170
|
+
: {
|
|
171
|
+
thought: metricName ? `The governed metric answers this directly.` : `Answer from the catalog.`,
|
|
172
|
+
action: { kind: 'lookup_metric', target: metricName ?? humanize(input.question) },
|
|
173
|
+
expectation: metricName ? `The governed ${humanize(metricName)} value.` : `A grounded answer.`,
|
|
174
|
+
};
|
|
175
|
+
return {
|
|
176
|
+
decision: 'answer',
|
|
177
|
+
confidence: decision.confidence,
|
|
178
|
+
rationale: decision.reason,
|
|
179
|
+
steps: [answerStep],
|
|
180
|
+
sources: Array.from(sources),
|
|
181
|
+
done: true,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Build a follow-up that offers REAL options: nearby metrics to disambiguate, or the
|
|
186
|
+
* certified breakdown dimensions to pick from — never an open-ended "tell me more".
|
|
187
|
+
*/
|
|
188
|
+
function buildFollowUp(input, match, metricBlocks, controllerQuestion) {
|
|
189
|
+
// No confident metric → ask which subject, offering the closest catalog metrics.
|
|
190
|
+
if (!match) {
|
|
191
|
+
const options = input.metrics.slice(0, 4).map((m) => humanize(m.name));
|
|
192
|
+
return {
|
|
193
|
+
question: controllerQuestion ?? 'Which metric did you mean?',
|
|
194
|
+
options,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
// A metric matched but the breakdown is ambiguous → offer the certified dimensions.
|
|
198
|
+
const dims = categoricalDimensionsOf(metricBlocks);
|
|
199
|
+
if (dims.length > 0 && requestedDimensions(input.question).length === 0) {
|
|
200
|
+
return {
|
|
201
|
+
question: `How should I break ${humanize(match.metric.name)} down?`,
|
|
202
|
+
options: dims.map(humanize).concat('overall (no breakdown)'),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
question: controllerQuestion ?? `Do you want ${humanize(match.metric.name)} overall, or filtered to a period?`,
|
|
207
|
+
options: ['overall', 'this year', 'a custom period'],
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=research-loop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"research-loop.js","sourceRoot":"","sources":["../src/research-loop.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default starter Skills (spec 16) — `seedDefaultSkills` writes three EDITABLE
|
|
3
|
+
* starters into `.dql/skills/`, idempotently (it NEVER clobbers a file the user
|
|
4
|
+
* has edited). The starters are:
|
|
5
|
+
*
|
|
6
|
+
* (a) Metrics glossary — pre-filled from the semantic layer (MetricFlow
|
|
7
|
+
* metrics → vocabulary + preferred_metrics).
|
|
8
|
+
* (b) SQL conventions — prefer {{ ref() }} / qualified relations, exclude
|
|
9
|
+
* test/internal accounts, one-row-per-grain, prefer certified blocks.
|
|
10
|
+
* (c) Domain rules — a commented template for the team to fill in.
|
|
11
|
+
*
|
|
12
|
+
* Idempotency is by file existence: a starter is only written when its
|
|
13
|
+
* `.skill.md` does not already exist. Re-running `seedDefaultSkills` after a
|
|
14
|
+
* user edits a starter is a no-op for that file.
|
|
15
|
+
*/
|
|
16
|
+
import { type SemanticLayer } from '@duckcodeailabs/dql-core';
|
|
17
|
+
import { type Skill } from './loader.js';
|
|
18
|
+
export interface SeedDefaultSkillsOptions {
|
|
19
|
+
/**
|
|
20
|
+
* Explicit semantic layer to source the metrics glossary from. When omitted,
|
|
21
|
+
* the project's configured semantic layer is loaded (best-effort).
|
|
22
|
+
*/
|
|
23
|
+
semanticLayer?: SemanticLayer;
|
|
24
|
+
}
|
|
25
|
+
export interface SeedDefaultSkillsResult {
|
|
26
|
+
/** Skills written this run (did not already exist). */
|
|
27
|
+
created: Skill[];
|
|
28
|
+
/** Starter ids skipped because their file already existed. */
|
|
29
|
+
skipped: string[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Seed the three editable starter skills into `.dql/skills/`. Idempotent: a
|
|
33
|
+
* starter is written only when its file does not already exist, so user edits
|
|
34
|
+
* are never clobbered.
|
|
35
|
+
*/
|
|
36
|
+
export declare function seedDefaultSkills(projectRoot: string, options?: SeedDefaultSkillsOptions): SeedDefaultSkillsResult;
|
|
37
|
+
//# sourceMappingURL=defaults.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/skills/defaults.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAyB,KAAK,KAAK,EAAwB,MAAM,aAAa,CAAC;AAEtF,MAAM,WAAW,wBAAwB;IACvC;;;OAGG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,MAAM,WAAW,uBAAuB;IACtC,uDAAuD;IACvD,OAAO,EAAE,KAAK,EAAE,CAAC;IACjB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AA0HD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,wBAA6B,GACrC,uBAAuB,CAkBzB"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default starter Skills (spec 16) — `seedDefaultSkills` writes three EDITABLE
|
|
3
|
+
* starters into `.dql/skills/`, idempotently (it NEVER clobbers a file the user
|
|
4
|
+
* has edited). The starters are:
|
|
5
|
+
*
|
|
6
|
+
* (a) Metrics glossary — pre-filled from the semantic layer (MetricFlow
|
|
7
|
+
* metrics → vocabulary + preferred_metrics).
|
|
8
|
+
* (b) SQL conventions — prefer {{ ref() }} / qualified relations, exclude
|
|
9
|
+
* test/internal accounts, one-row-per-grain, prefer certified blocks.
|
|
10
|
+
* (c) Domain rules — a commented template for the team to fill in.
|
|
11
|
+
*
|
|
12
|
+
* Idempotency is by file existence: a starter is only written when its
|
|
13
|
+
* `.skill.md` does not already exist. Re-running `seedDefaultSkills` after a
|
|
14
|
+
* user edits a starter is a no-op for that file.
|
|
15
|
+
*/
|
|
16
|
+
import { existsSync } from 'node:fs';
|
|
17
|
+
import { loadProjectConfig, resolveSemanticLayerWithDiagnostics, } from '@duckcodeailabs/dql-core';
|
|
18
|
+
import { skillPath, writeSkill } from './loader.js';
|
|
19
|
+
/** Best-effort load of the project's semantic-layer metrics for the glossary. */
|
|
20
|
+
function loadMetrics(projectRoot, explicit) {
|
|
21
|
+
try {
|
|
22
|
+
const layer = explicit ?? resolveProjectSemanticLayer(projectRoot);
|
|
23
|
+
if (!layer)
|
|
24
|
+
return [];
|
|
25
|
+
const metrics = layer.listMetrics();
|
|
26
|
+
return metrics.map((m) => ({ name: m.name, label: m.label, description: m.description }));
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function resolveProjectSemanticLayer(projectRoot) {
|
|
33
|
+
const config = loadProjectConfig(projectRoot);
|
|
34
|
+
const semanticConfig = config.semanticLayer?.provider
|
|
35
|
+
? config.semanticLayer
|
|
36
|
+
: config.semanticLayer?.path
|
|
37
|
+
? { provider: 'dql', path: config.semanticLayer.path }
|
|
38
|
+
: undefined;
|
|
39
|
+
const configured = resolveSemanticLayerWithDiagnostics(semanticConfig, projectRoot).layer;
|
|
40
|
+
if (configured)
|
|
41
|
+
return configured;
|
|
42
|
+
if (config.dbt?.projectDir) {
|
|
43
|
+
return resolveSemanticLayerWithDiagnostics({ provider: 'dbt', projectPath: config.dbt.projectDir }, projectRoot).layer ?? undefined;
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
/** (a) Metrics glossary, pre-filled from the semantic layer. */
|
|
48
|
+
function metricsGlossarySkill(metrics) {
|
|
49
|
+
const vocabulary = {};
|
|
50
|
+
for (const metric of metrics) {
|
|
51
|
+
const term = (metric.label && metric.label.trim()) || metric.name;
|
|
52
|
+
vocabulary[term] = `metric:${metric.name}`;
|
|
53
|
+
}
|
|
54
|
+
const bodyLines = [
|
|
55
|
+
'# Metrics glossary',
|
|
56
|
+
'',
|
|
57
|
+
'Business terms map to certified semantic-layer metrics. Prefer these metrics',
|
|
58
|
+
'over ad-hoc SQL aggregations when a question matches one of them.',
|
|
59
|
+
'',
|
|
60
|
+
];
|
|
61
|
+
if (metrics.length > 0) {
|
|
62
|
+
bodyLines.push('Known metrics:');
|
|
63
|
+
for (const metric of metrics) {
|
|
64
|
+
bodyLines.push(`- **${(metric.label && metric.label.trim()) || metric.name}** (\`${metric.name}\`)${metric.description ? ` — ${metric.description}` : ''}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
bodyLines.push('_No semantic-layer metrics were found when this glossary was seeded._');
|
|
69
|
+
bodyLines.push('_Add metrics to your semantic layer and re-run `dql init` to refresh, or edit this file._');
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
id: 'metrics-glossary',
|
|
73
|
+
scope: 'project',
|
|
74
|
+
description: 'Business terms mapped to certified semantic-layer metrics.',
|
|
75
|
+
preferredMetrics: metrics.map((m) => m.name),
|
|
76
|
+
vocabulary,
|
|
77
|
+
body: bodyLines.join('\n'),
|
|
78
|
+
isStarter: true,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/** (b) SQL conventions — house rules for grounded, certified-first SQL. */
|
|
82
|
+
function sqlConventionsSkill() {
|
|
83
|
+
return {
|
|
84
|
+
id: 'sql-conventions',
|
|
85
|
+
scope: 'project',
|
|
86
|
+
description: 'House SQL conventions for grounded, certified-first analytics.',
|
|
87
|
+
body: [
|
|
88
|
+
'# SQL conventions',
|
|
89
|
+
'',
|
|
90
|
+
'Apply these conventions to all generated SQL:',
|
|
91
|
+
'',
|
|
92
|
+
"- **Reference relations correctly.** In governed BLOCK SQL, reference tables via `{{ ref('<model>') }}`.",
|
|
93
|
+
' In ad-hoc CELL SQL, use the fully-qualified relation `database.schema.table`. Never a bare model name.',
|
|
94
|
+
'- **Prefer certified blocks.** When a certified block answers the question at the right grain, reuse it',
|
|
95
|
+
' instead of regenerating SQL.',
|
|
96
|
+
'- **Exclude test / internal accounts.** Filter out seed, test, demo, and internal records',
|
|
97
|
+
' (e.g. `WHERE NOT is_test_account`) unless the question explicitly asks for them.',
|
|
98
|
+
'- **One row per grain.** Make the grain explicit and ensure the query returns exactly one row per grain',
|
|
99
|
+
' (no accidental fan-out from un-deduplicated joins).',
|
|
100
|
+
'- **Read-only only.** Generate a single `SELECT` / `WITH` query; never DML or DDL.',
|
|
101
|
+
].join('\n'),
|
|
102
|
+
isStarter: true,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/** (c) Domain rules — a commented template for the team to fill in. */
|
|
106
|
+
function domainRulesSkill() {
|
|
107
|
+
return {
|
|
108
|
+
id: 'domain-rules',
|
|
109
|
+
scope: 'project',
|
|
110
|
+
description: 'Team-specific domain rules and business logic (template — fill in).',
|
|
111
|
+
body: [
|
|
112
|
+
'# Domain rules',
|
|
113
|
+
'',
|
|
114
|
+
'Fill in the business rules specific to your domain. Examples to replace:',
|
|
115
|
+
'',
|
|
116
|
+
'<!--',
|
|
117
|
+
'- "Active customer" = a customer with an order in the last 90 days.',
|
|
118
|
+
'- Revenue excludes refunds and chargebacks.',
|
|
119
|
+
'- The fiscal year starts in February.',
|
|
120
|
+
'- Region "EMEA" includes the UK.',
|
|
121
|
+
'-->',
|
|
122
|
+
'',
|
|
123
|
+
'_This is a starter template. Edit it with your team\'s definitions; re-running',
|
|
124
|
+
'`dql init` will not overwrite your changes._',
|
|
125
|
+
].join('\n'),
|
|
126
|
+
isStarter: true,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Seed the three editable starter skills into `.dql/skills/`. Idempotent: a
|
|
131
|
+
* starter is written only when its file does not already exist, so user edits
|
|
132
|
+
* are never clobbered.
|
|
133
|
+
*/
|
|
134
|
+
export function seedDefaultSkills(projectRoot, options = {}) {
|
|
135
|
+
const metrics = loadMetrics(projectRoot, options.semanticLayer);
|
|
136
|
+
const starters = [
|
|
137
|
+
metricsGlossarySkill(metrics),
|
|
138
|
+
sqlConventionsSkill(),
|
|
139
|
+
domainRulesSkill(),
|
|
140
|
+
];
|
|
141
|
+
const created = [];
|
|
142
|
+
const skipped = [];
|
|
143
|
+
for (const starter of starters) {
|
|
144
|
+
if (existsSync(skillPath(projectRoot, starter.id))) {
|
|
145
|
+
skipped.push(starter.id);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
created.push(writeSkill(projectRoot, starter));
|
|
149
|
+
}
|
|
150
|
+
return { created, skipped };
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=defaults.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/skills/defaults.ts"],"names":[],"mappings":"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"}
|
package/dist/skills/loader.d.ts
CHANGED
|
@@ -17,8 +17,16 @@
|
|
|
17
17
|
*/
|
|
18
18
|
export interface Skill {
|
|
19
19
|
id: string;
|
|
20
|
+
/**
|
|
21
|
+
* Skill scope. PROJECT skills (`user` empty) are shared with everyone;
|
|
22
|
+
* PERSONAL skills (`user` set) are bound to a single user. Derived from
|
|
23
|
+
* `user` on parse; persisted faithfully on write.
|
|
24
|
+
*/
|
|
25
|
+
scope: 'project' | 'personal';
|
|
20
26
|
/** Optional user this skill is bound to. Empty = project-level skill. */
|
|
21
27
|
user?: string;
|
|
28
|
+
/** Optional domain this skill belongs to (spec 17, part B). */
|
|
29
|
+
domain?: string;
|
|
22
30
|
description?: string;
|
|
23
31
|
preferredMetrics: string[];
|
|
24
32
|
preferredBlocks: string[];
|
|
@@ -26,6 +34,8 @@ export interface Skill {
|
|
|
26
34
|
/** Markdown body (everything after the closing `---`). */
|
|
27
35
|
body: string;
|
|
28
36
|
sourcePath: string;
|
|
37
|
+
/** True for the editable starter skills seeded by `seedDefaultSkills`. */
|
|
38
|
+
isStarter?: boolean;
|
|
29
39
|
}
|
|
30
40
|
export interface SkillLoadResult {
|
|
31
41
|
skills: Skill[];
|
|
@@ -41,6 +51,36 @@ export interface SkillLoadResult {
|
|
|
41
51
|
export declare function loadSkills(projectRoot: string): SkillLoadResult;
|
|
42
52
|
/** Public for tests. */
|
|
43
53
|
export declare function parseSkill(raw: string, path: string): Skill | null;
|
|
54
|
+
/** Skills directory for a project. */
|
|
55
|
+
export declare function skillsDir(projectRoot: string): string;
|
|
56
|
+
/** Resolve the `.skill.md` path a skill should be written to. */
|
|
57
|
+
export declare function skillPath(projectRoot: string, id: string): string;
|
|
58
|
+
/**
|
|
59
|
+
* Render a Skill back to the EXACT `.skill.md` format `parseSkill` consumes, so
|
|
60
|
+
* `parseSkill(renderSkill(s))` round-trips. Only emits keys that have content.
|
|
61
|
+
*/
|
|
62
|
+
export declare function renderSkill(skill: Skill): string;
|
|
63
|
+
export interface WriteSkillInput {
|
|
64
|
+
id: string;
|
|
65
|
+
scope: 'project' | 'personal';
|
|
66
|
+
user?: string;
|
|
67
|
+
domain?: string;
|
|
68
|
+
description?: string;
|
|
69
|
+
preferredMetrics?: string[];
|
|
70
|
+
preferredBlocks?: string[];
|
|
71
|
+
vocabulary?: Record<string, string>;
|
|
72
|
+
body?: string;
|
|
73
|
+
isStarter?: boolean;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Write a skill to `.dql/skills/<id>.skill.md` (overwriting if present). Returns
|
|
77
|
+
* the persisted Skill as re-read from disk so callers see the canonical form.
|
|
78
|
+
*/
|
|
79
|
+
export declare function writeSkill(projectRoot: string, input: WriteSkillInput): Skill;
|
|
80
|
+
/** Alias for `writeSkill` — create-or-update by id. */
|
|
81
|
+
export declare function upsertSkill(projectRoot: string, input: WriteSkillInput): Skill;
|
|
82
|
+
/** Delete `.dql/skills/<id>.skill.md`. Returns true when a file was removed. */
|
|
83
|
+
export declare function deleteSkill(projectRoot: string, id: string): boolean;
|
|
44
84
|
/**
|
|
45
85
|
* Compose Skills into a single system-prompt fragment for the agent.
|
|
46
86
|
* Filtered by user when a persona is active.
|
|
@@ -48,4 +88,24 @@ export declare function parseSkill(raw: string, path: string): Skill | null;
|
|
|
48
88
|
export declare function buildSkillsPrompt(skills: Skill[], userId: string | null): string;
|
|
49
89
|
export declare function activeSkills(skills: Skill[], userId: string | null): Skill[];
|
|
50
90
|
export declare function buildSkillBlockHints(skills: Skill[], userId: string | null): string[];
|
|
91
|
+
export interface SelectRelevantSkillsOptions {
|
|
92
|
+
/** Active user — gates personal skills (only this user's are eligible). */
|
|
93
|
+
userId?: string | null;
|
|
94
|
+
/** Max skills to return after pinned ones. Default 6. */
|
|
95
|
+
budget?: number;
|
|
96
|
+
/**
|
|
97
|
+
* Skill ids that are ALWAYS kept regardless of match (e.g. SQL conventions).
|
|
98
|
+
* Defaults to the seeded `sql-conventions` starter. Pinned skills are only
|
|
99
|
+
* kept when they are in-scope for the active user.
|
|
100
|
+
*/
|
|
101
|
+
pinnedIds?: string[];
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Rank in-scope skills by topic/vocabulary match to `question` and return the
|
|
105
|
+
* SELECTED subset the answer-loop / build engine should inject — not all skills.
|
|
106
|
+
* Pinned project skills (SQL conventions by default) are always kept. The rest
|
|
107
|
+
* are ordered by lexical overlap (vocabulary + description + body) and capped to
|
|
108
|
+
* the budget. Deterministic and offline.
|
|
109
|
+
*/
|
|
110
|
+
export declare function selectRelevantSkills(skills: Skill[], question: string, options?: SelectRelevantSkillsOptions): Skill[];
|
|
51
111
|
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/skills/loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/skills/loader.ts"],"names":[],"mappings":"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"}
|