@canonical/pragma-cli 0.30.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -35
- package/dist/pragma +0 -0
- package/package.json +15 -13
- package/src/completions/constants.ts +7 -0
- package/src/completions/handleQuery.test.ts +32 -0
- package/src/completions/handleQuery.ts +20 -1
- package/src/completions/queryCompletions.test.ts +5 -0
- package/src/completions/queryCompletions.ts +17 -3
- package/src/completions/querySocket.test.ts +31 -0
- package/src/completions/querySocket.ts +36 -9
- package/src/config/parseConfigValues.ts +2 -0
- package/src/config/readConfigLayers.test.ts +2 -0
- package/src/config/readConfigLayers.ts +3 -0
- package/src/config/types.ts +7 -0
- package/src/constants.ts +3 -1
- package/src/domains/block/commands/sample.ts +2 -1
- package/src/domains/block/formatters/list.test.ts +3 -3
- package/src/domains/block/formatters/lookup.test.ts +5 -5
- package/src/domains/block/orchestration/enrichBlocks.ts +6 -3
- package/src/domains/block/stories.ts +9 -3
- package/src/domains/config/commands/channel.test.ts +16 -0
- package/src/domains/config/commands/channel.ts +14 -1
- package/src/domains/config/commands/framework.test.ts +14 -0
- package/src/domains/config/commands/framework.ts +13 -1
- package/src/domains/config/commands/tier.test.ts +7 -0
- package/src/domains/config/commands/tier.ts +14 -1
- package/src/domains/config/operations/validateTier.test.ts +9 -0
- package/src/domains/config/operations/validateTier.ts +10 -2
- package/src/domains/create/generatorPack/bundled.ts +49 -0
- package/src/domains/create/generatorPack/compileGeneratorPack.test.ts +159 -0
- package/src/domains/create/generatorPack/compileGeneratorPack.ts +312 -0
- package/src/domains/create/index.ts +13 -7
- package/src/domains/create/mcp/specs.ts +6 -161
- package/src/domains/create/renderGeneratorUi.test.ts +103 -0
- package/src/domains/create/renderGeneratorUi.ts +65 -0
- package/src/domains/doctor/formatters/doctor.test.ts +50 -2
- package/src/domains/doctor/formatters/doctor.ts +90 -38
- package/src/domains/doctor/operations/checks/checkMcpCommands.ts +9 -6
- package/src/domains/doctor/operations/checks/checkPackageRefs.ts +30 -12
- package/src/domains/doctor/operations/checks.test.ts +6 -4
- package/src/domains/doctor/operations/types.ts +23 -1
- package/src/domains/graph/commands/inspect.test.ts +2 -2
- package/src/domains/graph/commands/query.test.ts +5 -3
- package/src/domains/graph/formatters/inspect.ts +7 -7
- package/src/domains/graph/formatters/query.test.ts +87 -0
- package/src/domains/graph/formatters/query.ts +42 -5
- package/src/domains/graph/operations/inspectUri.test.ts +5 -5
- package/src/domains/graphql/commands/serve.test.ts +12 -6
- package/src/domains/graphql/commands/serve.ts +16 -6
- package/src/domains/graphql/helpers/parsePrefixes.test.ts +3 -3
- package/src/domains/graphql/helpers/parsePrefixes.ts +3 -3
- package/src/domains/graphql/helpers/resolveSourceFiles.test.ts +9 -0
- package/src/domains/graphql/helpers/resolveSourceFiles.ts +24 -3
- package/src/domains/graphql/operations/compileSchema.test.ts +2 -2
- package/src/domains/graphql/operations/createSchemaServer.test.ts +2 -2
- package/src/domains/graphql/operations/ensureSchemaArtifact.test.ts +167 -0
- package/src/domains/graphql/operations/ensureSchemaArtifact.ts +178 -0
- package/src/domains/graphql/operations/index.ts +5 -0
- package/src/domains/info/formatters/upgrade.test.ts +7 -4
- package/src/domains/info/formatters/upgrade.ts +3 -1
- package/src/domains/info/operations/collectEntityCounts.test.ts +156 -0
- package/src/domains/info/operations/collectEntityCounts.ts +123 -0
- package/src/domains/info/operations/index.ts +1 -0
- package/src/domains/info/types.ts +13 -0
- package/src/domains/llm/capabilities.test.ts +23 -3
- package/src/domains/llm/commands/capabilities.ts +12 -3
- package/src/domains/llm/commands/llm.ts +5 -1
- package/src/domains/llm/data/commandReference.ts +20 -11
- package/src/domains/llm/data/decisionTrees.ts +10 -5
- package/src/domains/llm/data/toolCatalog.ts +49 -18
- package/src/domains/llm/formatters/orientation.ts +5 -1
- package/src/domains/llm/llmCommand.test.ts +11 -3
- package/src/domains/llm/mcp/specs.ts +3 -4
- package/src/domains/llm/operations/collectContext.test.ts +64 -0
- package/src/domains/llm/operations/collectContext.ts +7 -17
- package/src/domains/modifier/commands/sample.ts +2 -1
- package/src/domains/modifier/operations/lookup.ts +5 -3
- package/src/domains/modifier/stories.ts +9 -0
- package/src/domains/ontology/commands/list.test.ts +5 -3
- package/src/domains/ontology/commands/show.test.ts +85 -9
- package/src/domains/ontology/formatters/index.ts +1 -0
- package/src/domains/ontology/formatters/list.ts +37 -12
- package/src/domains/ontology/formatters/show.ts +358 -67
- package/src/domains/ontology/helpers/buildClassTree.ts +89 -0
- package/src/domains/ontology/helpers/completeClassNames.ts +43 -0
- package/src/domains/ontology/helpers/expandOntologyIris.ts +79 -0
- package/src/domains/ontology/helpers/index.ts +14 -0
- package/src/domains/ontology/helpers/queryClasses.ts +32 -16
- package/src/domains/ontology/helpers/queryProperties.ts +39 -6
- package/src/domains/ontology/helpers/queryTboxFacts.ts +160 -0
- package/src/domains/ontology/operations/listOntologies.test.ts +80 -3
- package/src/domains/ontology/operations/listOntologies.ts +126 -30
- package/src/domains/ontology/operations/showOntology.test.ts +131 -12
- package/src/domains/ontology/operations/showOntology.ts +289 -7
- package/src/domains/ontology/operations/showOntologyRaw.test.ts +4 -2
- package/src/domains/ontology/stories.ts +53 -8
- package/src/domains/refs/operations/updateRefs.ts +0 -1
- package/src/domains/setup/commands/index.ts +1 -0
- package/src/domains/setup/commands/setup.ts +70 -0
- package/src/domains/setup/index.ts +7 -3
- package/src/domains/setup/operations/setupLsp.test.ts +3 -2
- package/src/domains/setup/operations/setupLsp.ts +7 -2
- package/src/domains/shared/bootStore.bareCore.test.ts +63 -0
- package/src/domains/shared/bootStore.test.ts +82 -0
- package/src/domains/shared/bootStore.ts +56 -20
- package/src/domains/shared/emptyErrorRecovery.test.ts +23 -26
- package/src/domains/shared/extractLocalName.test.ts +3 -3
- package/src/domains/shared/formatters.ts +23 -2
- package/src/domains/shared/listTiers.ts +40 -0
- package/src/domains/shared/loaders/bundledLoader.ts +48 -0
- package/src/domains/shared/loaders/gitLoader.ts +1 -2
- package/src/domains/shared/loaders/localLoader.ts +2 -18
- package/src/domains/shared/loaders/readPackageDir.test.ts +58 -0
- package/src/domains/shared/loaders/readPackageDir.ts +46 -8
- package/src/domains/shared/lookupMany.test.ts +15 -0
- package/src/domains/shared/lookupMany.ts +22 -6
- package/src/domains/shared/namespaces.ts +13 -5
- package/src/domains/shared/parseSampleCount.test.ts +47 -0
- package/src/domains/shared/parseSampleCount.ts +45 -0
- package/src/domains/shared/prefixes.test.ts +211 -0
- package/src/domains/shared/prefixes.ts +182 -15
- package/src/domains/shared/renderers.ts +5 -5
- package/src/domains/shared/resolveLookupExitCode.test.ts +48 -0
- package/src/domains/shared/resolveLookupExitCode.ts +42 -0
- package/src/domains/shared/semanticPackage.test.ts +89 -0
- package/src/domains/shared/semanticPackage.ts +17 -2
- package/src/domains/shared/stories/compileLookupCommand.ts +3 -1
- package/src/domains/shared/stories/compileLookupTool.ts +1 -1
- package/src/domains/shared/stories/pack/applyPackFilters.test.ts +49 -0
- package/src/domains/shared/stories/pack/applyPackFilters.ts +46 -11
- package/src/domains/shared/stories/pack/applyPackSearch.test.ts +57 -0
- package/src/domains/shared/stories/pack/applyPackSearch.ts +41 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.test.ts +30 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.ts +75 -1
- package/src/domains/shared/stories/pack/bundled/index.ts +23 -0
- package/src/domains/shared/stories/pack/bundled/standardPack.ts +164 -0
- package/src/domains/shared/stories/pack/bundled/tierPack.ts +50 -0
- package/src/domains/shared/stories/pack/collectPackStories.test.ts +305 -7
- package/src/domains/shared/stories/pack/collectPackStories.ts +90 -8
- package/src/domains/shared/stories/pack/compilePackCommands.ts +17 -7
- package/src/domains/shared/stories/pack/compilePackStories.test.ts +607 -2
- package/src/domains/shared/stories/pack/compilePackStories.ts +544 -73
- package/src/domains/shared/stories/pack/compilePackToolSpecs.ts +20 -8
- package/src/domains/shared/stories/pack/index.ts +14 -0
- package/src/domains/shared/stories/pack/reservedVerbs.test.ts +213 -0
- package/src/domains/shared/stories/pack/reservedVerbs.ts +170 -0
- package/src/domains/shared/stories/pack/standardParity.test.ts +293 -220
- package/src/domains/shared/stories/pack/types.ts +123 -3
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.test.ts +502 -0
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.ts +475 -24
- package/src/domains/shared/stories/types.ts +9 -1
- package/src/domains/shared/types/index.ts +5 -9
- package/src/domains/shared/types/ontology.ts +119 -24
- package/src/domains/shared/types/standard.ts +9 -52
- package/src/domains/token/commands/lookup.test.ts +8 -0
- package/src/domains/token/commands/lookup.ts +12 -0
- package/src/domains/token/commands/sample.ts +2 -1
- package/src/domains/token/featureFlag.ts +17 -0
- package/src/domains/token/index.ts +5 -0
- package/src/domains/token/mcp/specs.ts +13 -1
- package/src/domains/trace/commands/resolveTraceLimit.test.ts +31 -0
- package/src/domains/trace/commands/show.ts +30 -4
- package/src/index.ts +1 -13
- package/src/mcp/registerResources.test.ts +2 -2
- package/src/mcp/resources/pickPrimaryType.test.ts +5 -5
- package/src/mcp/tools/index.ts +18 -10
- package/src/mcp/tools/registerTools.test.ts +106 -30
- package/src/pipeline/collectCommands.test.ts +151 -1
- package/src/pipeline/collectCommands.ts +34 -19
- package/src/pipeline/createProgram.test.ts +5 -1
- package/src/pipeline/createProgram.ts +2 -2
- package/src/pipeline/firstRun.test.ts +80 -0
- package/src/pipeline/firstRun.ts +99 -0
- package/src/pipeline/parseGlobalFlags.test.ts +102 -1
- package/src/pipeline/parseGlobalFlags.ts +98 -9
- package/src/pipeline/resolveCommandKind.test.ts +12 -0
- package/src/pipeline/resolveCommandKind.ts +4 -0
- package/src/pipeline/rootHelp.test.ts +87 -0
- package/src/pipeline/rootHelp.ts +265 -0
- package/src/pipeline/runCli.orchestration.test.ts +28 -12
- package/src/pipeline/runCli.test.ts +17 -4
- package/src/pipeline/runCli.ts +162 -19
- package/src/testing/cli.ts +30 -2
- package/src/testing/dsFixtures.ts +21 -4
- package/src/testing/index.ts +1 -4
- package/src/testing/integration/__snapshots__/executor-golden.test.ts.snap +4 -4
- package/src/testing/integration/cli-user-stories.test.ts +63 -0
- package/src/testing/integration/cross-binary-byte-equality.test.ts +2 -2
- package/src/testing/integration/edge-cases.test.ts +62 -50
- package/src/testing/integration/executor-execution.test.ts +6 -4
- package/src/testing/integration/executor-golden.test.ts +2 -2
- package/src/testing/integration/mcp-surface.test.ts +40 -15
- package/src/testing/integration/parity.test.ts +119 -70
- package/src/testing/setupXdgIsolation.ts +16 -5
- package/src/testing/standardParityFixtures.ts +26 -83
- package/src/testing/store.ts +4 -4
- package/src/domains/create/commands/component.test.ts +0 -166
- package/src/domains/create/commands/component.ts +0 -123
- package/src/domains/create/commands/index.ts +0 -3
- package/src/domains/create/commands/package.test.ts +0 -35
- package/src/domains/create/commands/package.ts +0 -21
- package/src/domains/create/generators.ts +0 -8
- package/src/domains/standard/commands/categories.test.ts +0 -70
- package/src/domains/standard/commands/categories.ts +0 -48
- package/src/domains/standard/commands/index.ts +0 -6
- package/src/domains/standard/commands/list.test.ts +0 -105
- package/src/domains/standard/commands/list.ts +0 -11
- package/src/domains/standard/commands/lookup.test.ts +0 -135
- package/src/domains/standard/commands/lookup.ts +0 -14
- package/src/domains/standard/commands/sample.ts +0 -57
- package/src/domains/standard/formatters/categories.test.ts +0 -44
- package/src/domains/standard/formatters/categories.ts +0 -38
- package/src/domains/standard/formatters/index.ts +0 -10
- package/src/domains/standard/formatters/list.test.ts +0 -169
- package/src/domains/standard/formatters/list.ts +0 -184
- package/src/domains/standard/formatters/lookup.test.ts +0 -95
- package/src/domains/standard/formatters/lookup.ts +0 -101
- package/src/domains/standard/formatters/sample.ts +0 -15
- package/src/domains/standard/formatters/types.ts +0 -31
- package/src/domains/standard/index.ts +0 -40
- package/src/domains/standard/mcp/index.ts +0 -2
- package/src/domains/standard/mcp/specs.ts +0 -89
- package/src/domains/standard/operations/categories.test.ts +0 -61
- package/src/domains/standard/operations/categories.ts +0 -38
- package/src/domains/standard/operations/index.ts +0 -6
- package/src/domains/standard/operations/list.test.ts +0 -75
- package/src/domains/standard/operations/list.ts +0 -64
- package/src/domains/standard/operations/lookup.test.ts +0 -58
- package/src/domains/standard/operations/lookup.ts +0 -151
- package/src/domains/standard/operations/sample.ts +0 -35
- package/src/domains/standard/orchestration/buildStandardFilters.ts +0 -11
- package/src/domains/standard/orchestration/index.ts +0 -4
- package/src/domains/standard/orchestration/resolveStandardList.ts +0 -45
- package/src/domains/standard/orchestration/resolveStandardLookup.ts +0 -33
- package/src/domains/standard/orchestration/standardEmptyError.test.ts +0 -33
- package/src/domains/standard/orchestration/standardEmptyError.ts +0 -29
- package/src/domains/standard/standardConfig.ts +0 -42
- package/src/domains/standard/stories.test.ts +0 -24
- package/src/domains/standard/stories.ts +0 -151
- package/src/domains/standard/types.ts +0 -15
- package/src/domains/tier/commands/index.ts +0 -3
- package/src/domains/tier/commands/list.test.ts +0 -67
- package/src/domains/tier/commands/list.ts +0 -9
- package/src/domains/tier/formatters/index.ts +0 -3
- package/src/domains/tier/formatters/list.test.ts +0 -33
- package/src/domains/tier/formatters/list.ts +0 -45
- package/src/domains/tier/index.ts +0 -17
- package/src/domains/tier/mcp/index.ts +0 -2
- package/src/domains/tier/mcp/specs.ts +0 -12
- package/src/domains/tier/operations/index.ts +0 -3
- package/src/domains/tier/operations/list.test.ts +0 -32
- package/src/domains/tier/operations/list.ts +0 -33
- package/src/domains/tier/stories.test.ts +0 -22
- package/src/domains/tier/stories.ts +0 -52
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import type { Store } from "@canonical/ke";
|
|
2
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
3
|
+
import { createTestStore, DS_ALL_TTL } from "#testing";
|
|
4
|
+
import listBlocks from "../../block/operations/list.js";
|
|
5
|
+
import listModifiers from "../../modifier/operations/list.js";
|
|
6
|
+
import { TTL_PREFIXES } from "../../shared/prefixes.js";
|
|
7
|
+
import { standardPack } from "../../shared/stories/pack/bundled/standardPack.js";
|
|
8
|
+
import runSelectQuery from "../../shared/stories/pack/runSelectQuery.js";
|
|
9
|
+
import type { FilterConfig } from "../../shared/types/index.js";
|
|
10
|
+
import listTokens from "../../token/operations/list.js";
|
|
11
|
+
import { collectEntityCounts } from "./collectEntityCounts.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The user-facing standard row count: the bundled pack's list query is
|
|
15
|
+
* what `standard list` serves since the standard cutover, so the info
|
|
16
|
+
* count must stay in lockstep with it (same WHERE grain the retired
|
|
17
|
+
* listStandards op used, including the OPTIONAL fan-out).
|
|
18
|
+
*/
|
|
19
|
+
async function countStandardRows(store: Store): Promise<number> {
|
|
20
|
+
const rows = await runSelectQuery(store, standardPack.list.query, "test");
|
|
21
|
+
return rows.length;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let store: Store;
|
|
25
|
+
let cleanup: () => void;
|
|
26
|
+
|
|
27
|
+
beforeAll(async () => {
|
|
28
|
+
const result = await createTestStore({ ttl: DS_ALL_TTL });
|
|
29
|
+
store = result.store;
|
|
30
|
+
cleanup = result.cleanup;
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterAll(() => cleanup());
|
|
34
|
+
|
|
35
|
+
// The block count is the only filtered count, so its parity must hold under
|
|
36
|
+
// every tier/channel combination the list op supports.
|
|
37
|
+
const configs: Record<string, FilterConfig> = {
|
|
38
|
+
"no tier, prerelease": { tier: undefined, channel: "prerelease" },
|
|
39
|
+
"no tier, normal": { tier: undefined, channel: "normal" },
|
|
40
|
+
"global tier": { tier: "global", channel: "prerelease" },
|
|
41
|
+
"apps/lxd tier": { tier: "apps/lxd", channel: "normal" },
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
describe("collectEntityCounts", () => {
|
|
45
|
+
it.each(
|
|
46
|
+
Object.entries(configs),
|
|
47
|
+
)("matches legacy list-op lengths for %s", async (_label, config) => {
|
|
48
|
+
const counts = await collectEntityCounts(store, config);
|
|
49
|
+
|
|
50
|
+
expect(counts.blocks).toBe((await listBlocks(store, config)).length);
|
|
51
|
+
expect(counts.standards).toBe(await countStandardRows(store));
|
|
52
|
+
expect(counts.modifierFamilies).toBe((await listModifiers(store)).length);
|
|
53
|
+
expect(counts.tokens).toBe((await listTokens(store)).length);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("returns positive counts on the full fixture", async () => {
|
|
57
|
+
const counts = await collectEntityCounts(store, {
|
|
58
|
+
tier: undefined,
|
|
59
|
+
channel: "prerelease",
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
expect(counts.blocks).toBeGreaterThan(0);
|
|
63
|
+
expect(counts.standards).toBeGreaterThan(0);
|
|
64
|
+
expect(counts.modifierFamilies).toBeGreaterThan(0);
|
|
65
|
+
expect(counts.tokens).toBeGreaterThan(0);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// A dedicated fan-out store (kept separate from the shared dsFixtures so its
|
|
70
|
+
// enriched cardinality does not ripple into other suites). Every entity fans
|
|
71
|
+
// its list op to MORE rows than there are distinct subjects, which is exactly
|
|
72
|
+
// where a single-variable `COUNT(DISTINCT ?subject)` would undercount — so
|
|
73
|
+
// these fixtures pin the grain-faithful subquery counts (FIX 4).
|
|
74
|
+
const FAN_OUT_TTL = `
|
|
75
|
+
${TTL_PREFIXES}
|
|
76
|
+
|
|
77
|
+
# One block asserted as TWO types -> two GROUP BY (?component ?type ?name ?tier)
|
|
78
|
+
# groups, so listBlocks emits two rows for a single subject.
|
|
79
|
+
ds:fanout.multitype a ds:Component, ds:Pattern ;
|
|
80
|
+
ds:name "MultiType" ;
|
|
81
|
+
ds:tier ds:global ;
|
|
82
|
+
ds:release ds:stable .
|
|
83
|
+
|
|
84
|
+
# A beta-only single-type block -> present under prerelease, filtered under
|
|
85
|
+
# normal, so the block count also tracks channel scoping.
|
|
86
|
+
ds:fanout.solo a ds:Component ;
|
|
87
|
+
ds:name "Solo" ;
|
|
88
|
+
ds:tier ds:global ;
|
|
89
|
+
ds:release ds:beta .
|
|
90
|
+
|
|
91
|
+
# One modifier family with TWO names -> two GROUP BY (?family ?name) groups.
|
|
92
|
+
ds:fanout.family a ds:ModifierFamily ;
|
|
93
|
+
ds:name "Alpha", "Beta" .
|
|
94
|
+
|
|
95
|
+
# One standard in TWO categories -> the OPTIONAL hasCategory fans two rows.
|
|
96
|
+
cs:fanout_cat_a a cs:Category ; cs:slug "cat-a" .
|
|
97
|
+
cs:fanout_cat_b a cs:Category ; cs:slug "cat-b" .
|
|
98
|
+
cs:fanout_std a cs:CodeStandard ;
|
|
99
|
+
cs:name "fanout/standard" ;
|
|
100
|
+
cs:description "A standard in two categories." ;
|
|
101
|
+
cs:hasCategory cs:fanout_cat_a, cs:fanout_cat_b .
|
|
102
|
+
|
|
103
|
+
# One token with TWO token types -> the OPTIONAL tokenType fans two rows.
|
|
104
|
+
ds:fanout_tt_a a ds:TokenType ; rdfs:label "TypeA" .
|
|
105
|
+
ds:fanout_tt_b a ds:TokenType ; rdfs:label "TypeB" .
|
|
106
|
+
ds:fanout.token a ds:Token ;
|
|
107
|
+
ds:tokenId "fanout.token" ;
|
|
108
|
+
ds:tokenType ds:fanout_tt_a, ds:fanout_tt_b .
|
|
109
|
+
`;
|
|
110
|
+
|
|
111
|
+
describe("collectEntityCounts fan-out parity", () => {
|
|
112
|
+
let fanStore: Store;
|
|
113
|
+
let fanCleanup: () => void;
|
|
114
|
+
|
|
115
|
+
beforeAll(async () => {
|
|
116
|
+
const result = await createTestStore({ ttl: FAN_OUT_TTL });
|
|
117
|
+
fanStore = result.store;
|
|
118
|
+
fanCleanup = result.cleanup;
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
afterAll(() => fanCleanup());
|
|
122
|
+
|
|
123
|
+
const fanConfigs: Record<string, FilterConfig> = {
|
|
124
|
+
prerelease: { tier: undefined, channel: "prerelease" },
|
|
125
|
+
normal: { tier: undefined, channel: "normal" },
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
it.each(
|
|
129
|
+
Object.entries(fanConfigs),
|
|
130
|
+
)("each count equals its legacy list-op length under %s channel", async (_label, config) => {
|
|
131
|
+
const counts = await collectEntityCounts(fanStore, config);
|
|
132
|
+
|
|
133
|
+
expect(counts.blocks).toBe((await listBlocks(fanStore, config)).length);
|
|
134
|
+
expect(counts.standards).toBe(await countStandardRows(fanStore));
|
|
135
|
+
expect(counts.modifierFamilies).toBe(
|
|
136
|
+
(await listModifiers(fanStore)).length,
|
|
137
|
+
);
|
|
138
|
+
expect(counts.tokens).toBe((await listTokens(fanStore)).length);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("actually fans each entity beyond its distinct-subject count", async () => {
|
|
142
|
+
// These are the cardinalities a naive `COUNT(DISTINCT ?subject)` would
|
|
143
|
+
// undercount (blocks 3 vs 2 subjects, families 2 vs 1, etc.).
|
|
144
|
+
const config: FilterConfig = { tier: undefined, channel: "prerelease" };
|
|
145
|
+
expect((await listBlocks(fanStore, config)).length).toBe(3);
|
|
146
|
+
expect((await listModifiers(fanStore)).length).toBe(2);
|
|
147
|
+
expect(await countStandardRows(fanStore)).toBe(2);
|
|
148
|
+
expect((await listTokens(fanStore)).length).toBe(2);
|
|
149
|
+
|
|
150
|
+
// And under normal channel the beta block drops out (2 block rows).
|
|
151
|
+
expect(
|
|
152
|
+
(await listBlocks(fanStore, { tier: undefined, channel: "normal" }))
|
|
153
|
+
.length,
|
|
154
|
+
).toBe(2);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { Store } from "@canonical/ke";
|
|
2
|
+
import { buildQuery } from "../../shared/buildQuery.js";
|
|
3
|
+
import { buildFilters } from "../../shared/filters/buildFilters.js";
|
|
4
|
+
import { P } from "../../shared/prefixes.js";
|
|
5
|
+
import type { FilterConfig } from "../../shared/types/index.js";
|
|
6
|
+
import type { EntityCounts } from "../types.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Run a `COUNT` query and parse its single `?count` binding.
|
|
10
|
+
*
|
|
11
|
+
* SPARQL `COUNT` yields a typed literal serialized as a string, so the
|
|
12
|
+
* value is parsed defensively and falls back to `0` when the query does
|
|
13
|
+
* not resolve to a select result.
|
|
14
|
+
*
|
|
15
|
+
* @param store - The ke store to query.
|
|
16
|
+
* @param query - A `SELECT (COUNT(...) AS ?count)` query string.
|
|
17
|
+
* @returns The parsed count, or `0` when unavailable.
|
|
18
|
+
* @note Queries ke store
|
|
19
|
+
*/
|
|
20
|
+
async function countRows(store: Store, query: string): Promise<number> {
|
|
21
|
+
const result = await store.query(buildQuery(query));
|
|
22
|
+
if (result.type !== "select") return 0;
|
|
23
|
+
const first = result.bindings[0];
|
|
24
|
+
return first ? Number.parseInt(first.count ?? "0", 10) || 0 : 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Count blocks, standards, modifier families, and tokens for the
|
|
29
|
+
* `pragma llm` orientation summary.
|
|
30
|
+
*
|
|
31
|
+
* Each `COUNT` reproduces the row cardinality of the matching leaf list
|
|
32
|
+
* operation, so the results equal `(await listX(...)).length` exactly:
|
|
33
|
+
*
|
|
34
|
+
* - **blocks** count the distinct `(?component, ?type, ?name, ?tier)` groups
|
|
35
|
+
* `listBlocks` emits via `GROUP BY ?component ?type ?name ?tier`, so a
|
|
36
|
+
* `COUNT(*)` over a `SELECT DISTINCT` of that exact grain (incl.
|
|
37
|
+
* `buildFilters` for identical tier/channel scoping). This stays exact even
|
|
38
|
+
* for a block asserted as several types or tiers, which would fan a
|
|
39
|
+
* single-variable `COUNT(DISTINCT ?component)` short of the grouped rows.
|
|
40
|
+
* - **modifier families** count the distinct `(?family, ?name)` groups
|
|
41
|
+
* `listModifiers` emits via `GROUP BY ?family ?name`, so a `COUNT(*)` over a
|
|
42
|
+
* `SELECT DISTINCT` of that grain — exact even for a family with two names.
|
|
43
|
+
* - **standards** and **tokens** have no `GROUP BY` and an `OPTIONAL` that
|
|
44
|
+
* fans rows out, so `COUNT(*)` over the identical `WHERE` matches row for
|
|
45
|
+
* row (including the fan-out).
|
|
46
|
+
*
|
|
47
|
+
* @note Queries ke store
|
|
48
|
+
*
|
|
49
|
+
* @param store - The ke store to query.
|
|
50
|
+
* @param config - Filter config providing tier and channel settings.
|
|
51
|
+
* @returns Entity counts matching the four leaf list operations' lengths.
|
|
52
|
+
*/
|
|
53
|
+
async function collectEntityCounts(
|
|
54
|
+
store: Store,
|
|
55
|
+
config: FilterConfig,
|
|
56
|
+
): Promise<EntityCounts> {
|
|
57
|
+
const filterClauses = buildFilters(config);
|
|
58
|
+
|
|
59
|
+
const [blocks, standards, modifierFamilies, tokens] = await Promise.all([
|
|
60
|
+
countRows(
|
|
61
|
+
store,
|
|
62
|
+
`
|
|
63
|
+
SELECT (COUNT(*) AS ?count)
|
|
64
|
+
WHERE {
|
|
65
|
+
SELECT DISTINCT ?component ?type ?name ?tier
|
|
66
|
+
WHERE {
|
|
67
|
+
VALUES ?type { ${P.ds}Component ${P.ds}Pattern ${P.ds}Layout ${P.ds}Subcomponent }
|
|
68
|
+
?component a ?type ;
|
|
69
|
+
${P.ds}name ?name ;
|
|
70
|
+
${P.ds}tier ?tier .
|
|
71
|
+
${filterClauses}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
`,
|
|
75
|
+
),
|
|
76
|
+
countRows(
|
|
77
|
+
store,
|
|
78
|
+
`
|
|
79
|
+
SELECT (COUNT(*) AS ?count)
|
|
80
|
+
WHERE {
|
|
81
|
+
?standard a ${P.cs}CodeStandard ;
|
|
82
|
+
${P.cs}name ?name ;
|
|
83
|
+
${P.cs}description ?description .
|
|
84
|
+
OPTIONAL {
|
|
85
|
+
?standard ${P.cs}hasCategory ?cat .
|
|
86
|
+
?cat ${P.cs}slug ?categoryName .
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
`,
|
|
90
|
+
),
|
|
91
|
+
countRows(
|
|
92
|
+
store,
|
|
93
|
+
`
|
|
94
|
+
SELECT (COUNT(*) AS ?count)
|
|
95
|
+
WHERE {
|
|
96
|
+
SELECT DISTINCT ?family ?name
|
|
97
|
+
WHERE {
|
|
98
|
+
?family a ${P.ds}ModifierFamily ;
|
|
99
|
+
${P.ds}name ?name .
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
`,
|
|
103
|
+
),
|
|
104
|
+
countRows(
|
|
105
|
+
store,
|
|
106
|
+
`
|
|
107
|
+
SELECT (COUNT(*) AS ?count)
|
|
108
|
+
WHERE {
|
|
109
|
+
?token a ${P.ds}Token ;
|
|
110
|
+
${P.ds}tokenId ?tokenId .
|
|
111
|
+
OPTIONAL {
|
|
112
|
+
?token ${P.ds}tokenType ?type .
|
|
113
|
+
?type ${P.rdfs}label ?typeName .
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
`,
|
|
117
|
+
),
|
|
118
|
+
]);
|
|
119
|
+
|
|
120
|
+
return { blocks, standards, modifierFamilies, tokens };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export { collectEntityCounts };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** @module Barrel for info operations. */
|
|
2
2
|
|
|
3
3
|
export { default as checkRegistryVersion } from "./checkRegistryVersion.js";
|
|
4
|
+
export { collectEntityCounts } from "./collectEntityCounts.js";
|
|
4
5
|
export { default as collectInfo } from "./collectInfo.js";
|
|
5
6
|
export { collectStoreSummary } from "./collectStoreSummary.js";
|
|
@@ -52,3 +52,16 @@ export interface StoreSummary {
|
|
|
52
52
|
readonly tripleCount: number;
|
|
53
53
|
readonly graphNames: readonly string[];
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Entity counts for the `pragma llm` orientation summary.
|
|
58
|
+
*
|
|
59
|
+
* Mirrors the row cardinality of the four leaf list operations
|
|
60
|
+
* (`listBlocks`, `listStandards`, `listModifiers`, `listTokens`).
|
|
61
|
+
*/
|
|
62
|
+
export interface EntityCounts {
|
|
63
|
+
readonly blocks: number;
|
|
64
|
+
readonly standards: number;
|
|
65
|
+
readonly modifierFamilies: number;
|
|
66
|
+
readonly tokens: number;
|
|
67
|
+
}
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
import { VERSION } from "#constants";
|
|
3
|
+
import { TOKEN_READ_SURFACE_ENABLED } from "../token/featureFlag.js";
|
|
3
4
|
import { buildCapabilitiesCommand } from "./commands/index.js";
|
|
4
5
|
import { TOOL_CATALOG } from "./data/index.js";
|
|
5
6
|
import { buildCapabilitiesData } from "./mcp/index.js";
|
|
6
7
|
|
|
8
|
+
// token_list, token_lookup, and token_sample are gated behind the token
|
|
9
|
+
// read-surface feature flag.
|
|
10
|
+
const EXPECTED_TOOL_COUNT = TOKEN_READ_SURFACE_ENABLED ? 34 : 31;
|
|
11
|
+
|
|
7
12
|
describe("buildCapabilitiesData", () => {
|
|
8
13
|
it("returns correct version", () => {
|
|
9
14
|
const data = buildCapabilitiesData();
|
|
@@ -29,7 +34,7 @@ describe("buildCapabilitiesData", () => {
|
|
|
29
34
|
it("includes every tool from TOOL_CATALOG", () => {
|
|
30
35
|
const data = buildCapabilitiesData();
|
|
31
36
|
expect(data.tools).toHaveLength(TOOL_CATALOG.length);
|
|
32
|
-
expect(data.tools).toHaveLength(
|
|
37
|
+
expect(data.tools).toHaveLength(EXPECTED_TOOL_COUNT);
|
|
33
38
|
});
|
|
34
39
|
|
|
35
40
|
it("every tool has a non-empty use_when", () => {
|
|
@@ -41,7 +46,7 @@ describe("buildCapabilitiesData", () => {
|
|
|
41
46
|
|
|
42
47
|
it("counts match tool catalog category lengths", () => {
|
|
43
48
|
const data = buildCapabilitiesData();
|
|
44
|
-
expect(data.counts.total).toBe(
|
|
49
|
+
expect(data.counts.total).toBe(EXPECTED_TOOL_COUNT);
|
|
45
50
|
expect(data.counts.read).toBe(
|
|
46
51
|
TOOL_CATALOG.filter((t) => t.category === "read").length,
|
|
47
52
|
);
|
|
@@ -79,7 +84,22 @@ describe("buildCapabilitiesCommand", () => {
|
|
|
79
84
|
expect(text).toContain("Discovery Sequence");
|
|
80
85
|
expect(text).toContain("block_list");
|
|
81
86
|
expect(text).toContain("read");
|
|
82
|
-
expect(text).toContain(
|
|
87
|
+
expect(text).toContain(`${EXPECTED_TOOL_COUNT} tools`);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("emits valid JSON when --format json is requested", async () => {
|
|
91
|
+
const cmd = buildCapabilitiesCommand();
|
|
92
|
+
const ctx = {
|
|
93
|
+
cwd: "/test",
|
|
94
|
+
globalFlags: { llm: false, format: "json" as const, verbose: false },
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const result = await cmd.execute({}, ctx);
|
|
98
|
+
if (result.tag !== "output") throw new Error("Expected output result");
|
|
99
|
+
|
|
100
|
+
const text = result.render.plain(result.value) as string;
|
|
101
|
+
expect(() => JSON.parse(text)).not.toThrow();
|
|
102
|
+
expect(JSON.parse(text)).toMatchObject({ version: expect.any(String) });
|
|
83
103
|
});
|
|
84
104
|
|
|
85
105
|
it("does not require a store or runtime", async () => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type CommandContext,
|
|
2
3
|
type CommandDefinition,
|
|
3
4
|
type CommandResult,
|
|
4
5
|
createOutputResult,
|
|
@@ -10,7 +11,8 @@ import { buildCapabilitiesData } from "../mcp/specs.js";
|
|
|
10
11
|
* Builds the `pragma capabilities` command definition.
|
|
11
12
|
*
|
|
12
13
|
* Returns a static system map: conventions, tool catalog with behavioral
|
|
13
|
-
* hints, discovery sequence, and output modes. No store needed.
|
|
14
|
+
* hints, discovery sequence, and output modes. No store needed. Honours
|
|
15
|
+
* `--format json` by emitting the structured payload so agents can parse it.
|
|
14
16
|
*
|
|
15
17
|
* @returns A command definition for `capabilities`.
|
|
16
18
|
*/
|
|
@@ -20,9 +22,16 @@ export default function buildCapabilitiesCommand(): CommandDefinition {
|
|
|
20
22
|
description:
|
|
21
23
|
"Discover conventions, available tools, and discovery sequence",
|
|
22
24
|
parameters: [],
|
|
23
|
-
execute: async (
|
|
25
|
+
execute: async (
|
|
26
|
+
_params: Record<string, unknown>,
|
|
27
|
+
ctx: CommandContext,
|
|
28
|
+
): Promise<CommandResult> => {
|
|
24
29
|
const data = buildCapabilitiesData();
|
|
25
|
-
|
|
30
|
+
const asJson = ctx?.globalFlags?.format === "json";
|
|
31
|
+
return createOutputResult(data, {
|
|
32
|
+
plain: (value) =>
|
|
33
|
+
asJson ? JSON.stringify(value, null, 2) : renderCapabilities(value),
|
|
34
|
+
});
|
|
26
35
|
},
|
|
27
36
|
};
|
|
28
37
|
}
|
|
@@ -31,7 +31,11 @@ export default function buildLlmCommand(ctx: PragmaContext): CommandDefinition {
|
|
|
31
31
|
decisionTrees: DECISION_TREES,
|
|
32
32
|
commandReference: COMMAND_REFERENCE,
|
|
33
33
|
};
|
|
34
|
-
|
|
34
|
+
const asJson = ctx?.globalFlags?.format === "json";
|
|
35
|
+
return createOutputResult(data, {
|
|
36
|
+
plain: (value) =>
|
|
37
|
+
asJson ? JSON.stringify(value, null, 2) : renderLlmOrientation(value),
|
|
38
|
+
});
|
|
35
39
|
},
|
|
36
40
|
};
|
|
37
41
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TOKEN_READ_SURFACE_ENABLED } from "../../token/featureFlag.js";
|
|
1
2
|
import type { CommandRefEntry } from "../types.js";
|
|
2
3
|
import { TOOL_CATALOG } from "./toolCatalog.js";
|
|
3
4
|
|
|
@@ -60,17 +61,25 @@ export const COMMAND_REFERENCE: readonly CommandRefEntry[] = [
|
|
|
60
61
|
tokens: "~80",
|
|
61
62
|
use_when: hintFor("modifier_lookup"),
|
|
62
63
|
},
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
64
|
+
...(TOKEN_READ_SURFACE_ENABLED
|
|
65
|
+
? ([
|
|
66
|
+
{
|
|
67
|
+
command: "token sample",
|
|
68
|
+
tokens: "~300",
|
|
69
|
+
use_when: hintFor("token_sample"),
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
command: "token list",
|
|
73
|
+
tokens: "~400",
|
|
74
|
+
use_when: hintFor("token_list"),
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
command: "token lookup <name-or-iri...> --detailed",
|
|
78
|
+
tokens: "~150",
|
|
79
|
+
use_when: hintFor("token_lookup"),
|
|
80
|
+
},
|
|
81
|
+
] as const)
|
|
82
|
+
: []),
|
|
74
83
|
{ command: "tier list", tokens: "~50", use_when: hintFor("tier_list") },
|
|
75
84
|
{
|
|
76
85
|
command: "ontology list",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TOKEN_READ_SURFACE_ENABLED } from "../../token/featureFlag.js";
|
|
1
2
|
import type { DecisionTree } from "../types.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -26,16 +27,20 @@ export const DECISION_TREES: readonly DecisionTree[] = [
|
|
|
26
27
|
no → standard list --category <cat> [~100]
|
|
27
28
|
→ standard lookup <std-iri...> --detailed [~400] Σ500`,
|
|
28
29
|
},
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
...(TOKEN_READ_SURFACE_ENABLED
|
|
31
|
+
? ([
|
|
32
|
+
{
|
|
33
|
+
intent: "Find a token",
|
|
34
|
+
tree: `? Know token IRI or name?
|
|
32
35
|
yes → token lookup <name-or-iri...> --detailed [~150]
|
|
33
36
|
no → token list --category <cat> [~100]
|
|
34
37
|
→ token lookup <name-or-iri...> --detailed [~150] Σ250`,
|
|
35
|
-
|
|
38
|
+
},
|
|
39
|
+
] as const)
|
|
40
|
+
: []),
|
|
36
41
|
{
|
|
37
42
|
intent: "Explore the design system",
|
|
38
|
-
tree: `first time → block sample / token sample / modifier sample [~300] → see real shapes
|
|
43
|
+
tree: `first time → block sample / ${TOKEN_READ_SURFACE_ENABLED ? "token sample / " : ""}modifier sample [~300] → see real shapes
|
|
39
44
|
high-level → ontology list [~80] → ontology show <ns> [~300]
|
|
40
45
|
modifiers → modifier list [~100] → modifier lookup <name-or-iri...> [~80]
|
|
41
46
|
tiers → tier list [~50]
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/** Tool catalog with behavioral hints for agent orientation. */
|
|
2
2
|
|
|
3
|
+
import { TOKEN_READ_SURFACE_ENABLED } from "../../token/featureFlag.js";
|
|
4
|
+
|
|
3
5
|
export interface ToolEntry {
|
|
4
6
|
readonly name: string;
|
|
5
7
|
readonly category: "read" | "write" | "orientation" | "diagnostic";
|
|
@@ -49,18 +51,22 @@ export const TOOL_CATALOG: readonly ToolEntry[] = [
|
|
|
49
51
|
use_when:
|
|
50
52
|
"Need values and usage details for specific modifier families by name",
|
|
51
53
|
},
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
54
|
+
...(TOKEN_READ_SURFACE_ENABLED
|
|
55
|
+
? ([
|
|
56
|
+
{
|
|
57
|
+
name: "token_list",
|
|
58
|
+
category: "read",
|
|
59
|
+
use_when:
|
|
60
|
+
"Browsing design tokens, optionally filtered by category (color, spacing, etc.)",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "token_lookup",
|
|
64
|
+
category: "read",
|
|
65
|
+
use_when:
|
|
66
|
+
"Need theme values and resolution details for specific tokens by name or IRI",
|
|
67
|
+
},
|
|
68
|
+
] as const)
|
|
69
|
+
: []),
|
|
64
70
|
{
|
|
65
71
|
name: "tier_list",
|
|
66
72
|
category: "read",
|
|
@@ -118,12 +124,16 @@ export const TOOL_CATALOG: readonly ToolEntry[] = [
|
|
|
118
124
|
use_when:
|
|
119
125
|
"See actual standard data shapes (with dos/donts) before querying — returns random instances each call",
|
|
120
126
|
},
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
+
...(TOKEN_READ_SURFACE_ENABLED
|
|
128
|
+
? ([
|
|
129
|
+
{
|
|
130
|
+
name: "token_sample",
|
|
131
|
+
category: "read",
|
|
132
|
+
use_when:
|
|
133
|
+
"See actual token data shapes (with theme values) before querying — returns random instances each call",
|
|
134
|
+
},
|
|
135
|
+
] as const)
|
|
136
|
+
: []),
|
|
127
137
|
{
|
|
128
138
|
name: "modifier_sample",
|
|
129
139
|
category: "read",
|
|
@@ -160,6 +170,27 @@ export const TOOL_CATALOG: readonly ToolEntry[] = [
|
|
|
160
170
|
use_when:
|
|
161
171
|
"Scaffolding a new npm package with proper monorepo configuration",
|
|
162
172
|
},
|
|
173
|
+
{
|
|
174
|
+
name: "create_application",
|
|
175
|
+
category: "write",
|
|
176
|
+
use_when:
|
|
177
|
+
"Scaffolding a new React application with SSR, routing, and optional Relay",
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: "create_domain",
|
|
181
|
+
category: "write",
|
|
182
|
+
use_when: "Scaffolding a new domain module inside an existing application",
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
name: "create_route",
|
|
186
|
+
category: "write",
|
|
187
|
+
use_when: "Scaffolding a new route inside an existing application",
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: "create_wrapper",
|
|
191
|
+
category: "write",
|
|
192
|
+
use_when: "Scaffolding a new wrapper component inside an application",
|
|
193
|
+
},
|
|
163
194
|
|
|
164
195
|
// — Orientation —————————————————————————————————————————————————————————————
|
|
165
196
|
{
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TOKEN_READ_SURFACE_ENABLED } from "../../token/featureFlag.js";
|
|
1
2
|
import type { LlmData } from "../types.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -22,8 +23,11 @@ export default function renderLlmOrientation(data: LlmData): string {
|
|
|
22
23
|
lines.push("");
|
|
23
24
|
lines.push("## Context");
|
|
24
25
|
lines.push(`tier: ${tierDisplay} | channel: ${ctx.channel}`);
|
|
26
|
+
const tokenCount = TOKEN_READ_SURFACE_ENABLED
|
|
27
|
+
? `, ${ctx.counts.tokens} tokens`
|
|
28
|
+
: "";
|
|
25
29
|
lines.push(
|
|
26
|
-
`data: ${ctx.counts.blocks} blocks, ${ctx.counts.standards} standards, ${ctx.counts.modifierFamilies} modifier families
|
|
30
|
+
`data: ${ctx.counts.blocks} blocks, ${ctx.counts.standards} standards, ${ctx.counts.modifierFamilies} modifier families${tokenCount}`,
|
|
27
31
|
);
|
|
28
32
|
lines.push(`namespaces: ${ctx.namespaces.join(", ")}`);
|
|
29
33
|
lines.push("");
|
|
@@ -3,6 +3,7 @@ import type { Store } from "@canonical/ke";
|
|
|
3
3
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
4
4
|
import { createTestStore, DS_ALL_TTL } from "#testing";
|
|
5
5
|
import type { PragmaContext } from "../shared/context.js";
|
|
6
|
+
import { TOKEN_READ_SURFACE_ENABLED } from "../token/featureFlag.js";
|
|
6
7
|
import { buildLlmCommand } from "./commands/index.js";
|
|
7
8
|
import type { LlmData } from "./types.js";
|
|
8
9
|
|
|
@@ -72,14 +73,21 @@ describe("buildLlmCommand", () => {
|
|
|
72
73
|
expect(value.context.namespaces.length).toBeGreaterThan(0);
|
|
73
74
|
});
|
|
74
75
|
|
|
75
|
-
it("includes all
|
|
76
|
+
it("includes all decision trees", async () => {
|
|
76
77
|
const ctx = makeCtx();
|
|
77
78
|
const cmd = buildLlmCommand(ctx);
|
|
78
79
|
const { value, text } = await executeOutput(cmd, ctx);
|
|
79
|
-
|
|
80
|
+
// "Find a token" is gated behind the token read-surface feature flag.
|
|
81
|
+
expect(value.decisionTrees).toHaveLength(
|
|
82
|
+
TOKEN_READ_SURFACE_ENABLED ? 5 : 4,
|
|
83
|
+
);
|
|
80
84
|
expect(text).toContain("Build a block");
|
|
81
85
|
expect(text).toContain("Audit standards");
|
|
82
|
-
|
|
86
|
+
if (TOKEN_READ_SURFACE_ENABLED) {
|
|
87
|
+
expect(text).toContain("Find a token");
|
|
88
|
+
} else {
|
|
89
|
+
expect(text).not.toContain("Find a token");
|
|
90
|
+
}
|
|
83
91
|
expect(text).toContain("Explore the design system");
|
|
84
92
|
expect(text).toContain("Configure");
|
|
85
93
|
});
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { VERSION } from "#constants";
|
|
6
6
|
import { buildFilterConfig } from "../../shared/filters/index.js";
|
|
7
7
|
import type { ToolSpec } from "../../shared/ToolSpec.js";
|
|
8
|
+
import { TOKEN_READ_SURFACE_ENABLED } from "../../token/featureFlag.js";
|
|
8
9
|
import {
|
|
9
10
|
COMMAND_REFERENCE,
|
|
10
11
|
DECISION_TREES,
|
|
@@ -43,14 +44,12 @@ function buildCapabilitiesData(): CapabilitiesData {
|
|
|
43
44
|
{
|
|
44
45
|
stage: 2,
|
|
45
46
|
tool: "*_sample",
|
|
46
|
-
purpose:
|
|
47
|
-
"Call block_sample, standard_sample, token_sample, or modifier_sample to see real data shapes before querying. Prevents guessing at property names.",
|
|
47
|
+
purpose: `Call block_sample, standard_sample,${TOKEN_READ_SURFACE_ENABLED ? " token_sample," : ""} or modifier_sample to see real data shapes before querying. Prevents guessing at property names.`,
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
stage: 3,
|
|
51
51
|
tool: "domain tools",
|
|
52
|
-
purpose:
|
|
53
|
-
"Query specific entities — block_list, standard_lookup, token_list, etc. Use the use_when hints above to pick the right tool.",
|
|
52
|
+
purpose: `Query specific entities — block_list, standard_lookup, ${TOKEN_READ_SURFACE_ENABLED ? "token_list, " : ""}etc. Use the use_when hints above to pick the right tool.`,
|
|
54
53
|
},
|
|
55
54
|
],
|
|
56
55
|
tools: TOOL_CATALOG,
|