@canonical/pragma-cli 0.27.1-experimental.0 → 0.29.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/dist/pragma +0 -0
- package/package.json +16 -17
- package/src/config/readConfig.ts +38 -2
- package/src/config/types.ts +12 -1
- package/src/config/writeConfig.ts +12 -0
- package/src/constants.ts +12 -1
- package/src/domains/block/commands/index.ts +2 -1
- package/src/domains/block/commands/sample.ts +55 -0
- package/src/domains/block/formatters/index.ts +2 -1
- package/src/domains/block/formatters/sample.ts +21 -0
- package/src/domains/block/index.ts +3 -3
- package/src/domains/block/mcp/specs.ts +48 -0
- package/src/domains/block/operations/index.ts +2 -1
- package/src/domains/block/operations/lookup.ts +9 -0
- package/src/domains/block/operations/sample.ts +41 -0
- package/src/domains/block/orchestration/blockEmptyError.test.ts +26 -0
- package/src/domains/block/orchestration/resolveBlockLookup.ts +10 -3
- package/src/domains/config/commands/framework.test.ts +88 -0
- package/src/domains/config/commands/framework.ts +85 -0
- package/src/domains/config/commands/index.ts +2 -0
- package/src/domains/config/commands/trace.test.ts +148 -0
- package/src/domains/config/commands/trace.ts +80 -0
- package/src/domains/config/formatters/framework.ts +34 -0
- package/src/domains/config/formatters/index.ts +2 -0
- package/src/domains/config/formatters/trace.ts +28 -0
- package/src/domains/config/index.ts +15 -2
- package/src/domains/config/operations/index.ts +1 -0
- package/src/domains/config/operations/validateFramework.ts +24 -0
- package/src/domains/config/tasks/index.ts +2 -0
- package/src/domains/config/tasks/setChannel.ts +7 -1
- package/src/domains/config/tasks/setFramework.ts +40 -0
- package/src/domains/config/tasks/setTier.ts +7 -1
- package/src/domains/config/tasks/setTrace.ts +38 -0
- package/src/domains/doctor/operations/checks/checkKeStore.ts +5 -5
- package/src/domains/doctor/operations/checks/checkPackageRefs.ts +35 -41
- package/src/domains/doctor/operations/checks.test.ts +4 -1
- package/src/domains/doctor/operations/runChecks.test.ts +72 -0
- package/src/domains/doctor/operations/runChecks.ts +48 -12
- package/src/domains/graphql/commands/build.test.ts +194 -0
- package/src/domains/graphql/commands/build.ts +125 -0
- package/src/domains/graphql/commands/check.test.ts +139 -0
- package/src/domains/graphql/commands/check.ts +87 -0
- package/src/domains/graphql/commands/index.ts +4 -0
- package/src/domains/graphql/formatters/index.ts +3 -0
- package/src/domains/graphql/formatters/report.ts +78 -0
- package/src/domains/graphql/helpers/index.ts +4 -0
- package/src/domains/graphql/helpers/parsePrefixes.test.ts +44 -0
- package/src/domains/graphql/helpers/parsePrefixes.ts +38 -0
- package/src/domains/graphql/helpers/resolveSourceFiles.test.ts +56 -0
- package/src/domains/graphql/helpers/resolveSourceFiles.ts +45 -0
- package/src/domains/graphql/index.ts +21 -0
- package/src/domains/graphql/operations/compileSchema.test.ts +119 -0
- package/src/domains/graphql/operations/compileSchema.ts +109 -0
- package/src/domains/graphql/operations/index.ts +7 -0
- package/src/domains/graphql/types.ts +29 -0
- package/src/domains/info/operations/collectInfo.test.ts +2 -2
- package/src/domains/info/operations/collectInfo.ts +4 -4
- package/src/domains/llm/capabilities.test.ts +91 -0
- package/src/domains/llm/commands/capabilities.ts +28 -0
- package/src/domains/llm/commands/index.ts +1 -0
- package/src/domains/llm/data/commandReference.ts +90 -18
- package/src/domains/llm/data/decisionTrees.ts +15 -13
- package/src/domains/llm/data/index.ts +2 -0
- package/src/domains/llm/data/toolCatalog.ts +184 -0
- package/src/domains/llm/formatters/capabilities.ts +50 -0
- package/src/domains/llm/formatters/index.ts +1 -0
- package/src/domains/llm/index.ts +3 -2
- package/src/domains/llm/mcp/index.ts +1 -1
- package/src/domains/llm/mcp/specs.ts +59 -52
- package/src/domains/llm/types.ts +40 -0
- package/src/domains/modifier/commands/index.ts +2 -1
- package/src/domains/modifier/commands/sample.ts +53 -0
- package/src/domains/modifier/formatters/index.ts +2 -1
- package/src/domains/modifier/formatters/sample.ts +15 -0
- package/src/domains/modifier/index.ts +7 -3
- package/src/domains/modifier/mcp/specs.ts +47 -0
- package/src/domains/modifier/operations/index.ts +2 -1
- package/src/domains/modifier/operations/lookup.ts +9 -0
- package/src/domains/modifier/operations/sample.ts +32 -0
- package/src/domains/modifier/orchestration/modifierEmptyError.test.ts +26 -0
- package/src/domains/modifier/orchestration/modifierEmptyError.ts +7 -2
- package/src/domains/modifier/orchestration/resolveModifierLookup.ts +14 -2
- package/src/domains/refs/operations/index.ts +1 -1
- package/src/domains/refs/operations/paths.ts +29 -1
- package/src/domains/refs/operations/updateRefs.test.ts +6 -3
- package/src/domains/refs/operations/updateRefs.ts +3 -2
- package/src/domains/shared/bootStore.ts +88 -49
- package/src/domains/shared/contracts.ts +1 -0
- package/src/domains/shared/emptyErrorRecovery.test.ts +109 -0
- package/src/domains/shared/loaders/bundledLoader.ts +108 -0
- package/src/domains/shared/loaders/gitLoader.ts +31 -0
- package/src/domains/shared/loaders/index.ts +6 -0
- package/src/domains/shared/loaders/localLoader.ts +73 -0
- package/src/domains/shared/loaders/readPackageDir.ts +112 -0
- package/src/domains/shared/lookupMany.ts +9 -1
- package/src/domains/shared/packages.ts +21 -120
- package/src/domains/shared/pickRandom.ts +22 -0
- package/src/domains/shared/runtime.ts +6 -3
- package/src/domains/shared/sampleFormatters.ts +47 -0
- package/src/domains/shared/semanticPackage.ts +96 -0
- package/src/domains/shared/suggestions/detectCrossDomain.test.ts +43 -0
- package/src/domains/shared/suggestions/detectCrossDomain.ts +116 -0
- package/src/domains/shared/suggestions/expandGlob.test.ts +92 -0
- package/src/domains/shared/suggestions/expandGlob.ts +51 -0
- package/src/domains/shared/suggestions/expandLookupQueries.ts +57 -0
- package/src/domains/shared/suggestions/index.ts +7 -0
- package/src/domains/shared/suggestions/listDomainNames.ts +61 -0
- package/src/domains/shared/suggestions/suggestNames.test.ts +70 -0
- package/src/domains/shared/suggestions/suggestNames.ts +97 -0
- package/src/domains/shared/types/index.ts +1 -0
- package/src/domains/shared/types/runtime.ts +4 -1
- package/src/domains/shared/types/sample.ts +17 -0
- package/src/domains/skill/helpers/resolveSkillSources.ts +49 -16
- package/src/domains/standard/commands/index.ts +2 -1
- package/src/domains/standard/commands/sample.ts +57 -0
- package/src/domains/standard/formatters/index.ts +2 -1
- package/src/domains/standard/formatters/sample.ts +15 -0
- package/src/domains/standard/index.ts +8 -1
- package/src/domains/standard/mcp/specs.ts +48 -1
- package/src/domains/standard/operations/index.ts +2 -1
- package/src/domains/standard/operations/lookup.ts +9 -0
- package/src/domains/standard/operations/sample.ts +35 -0
- package/src/domains/standard/orchestration/resolveStandardLookup.ts +14 -2
- package/src/domains/standard/orchestration/standardEmptyError.test.ts +33 -0
- package/src/domains/standard/orchestration/standardEmptyError.ts +16 -6
- package/src/domains/token/commands/index.ts +2 -1
- package/src/domains/token/commands/sample.ts +53 -0
- package/src/domains/token/formatters/index.ts +2 -1
- package/src/domains/token/formatters/sample.ts +16 -0
- package/src/domains/token/index.ts +8 -1
- package/src/domains/token/mcp/specs.ts +47 -1
- package/src/domains/token/operations/index.ts +2 -1
- package/src/domains/token/operations/lookup.ts +9 -0
- package/src/domains/token/operations/sample.ts +32 -0
- package/src/domains/token/orchestration/resolveTokenLookup.ts +14 -2
- package/src/domains/token/orchestration/tokenEmptyError.test.ts +33 -0
- package/src/domains/token/orchestration/tokenEmptyError.ts +5 -2
- package/src/domains/trace/commands/clear.ts +71 -0
- package/src/domains/trace/commands/index.ts +5 -0
- package/src/domains/trace/commands/sessions.ts +32 -0
- package/src/domains/trace/commands/show.ts +145 -0
- package/src/domains/trace/formatters/index.ts +9 -0
- package/src/domains/trace/formatters/sessions.ts +80 -0
- package/src/domains/trace/formatters/show.ts +122 -0
- package/src/domains/trace/index.ts +17 -0
- package/src/domains/trace/operations/index.ts +8 -0
- package/src/domains/trace/operations/listSessions.test.ts +55 -0
- package/src/domains/trace/operations/listSessions.ts +52 -0
- package/src/domains/trace/operations/readTraceLog.test.ts +100 -0
- package/src/domains/trace/operations/readTraceLog.ts +104 -0
- package/src/domains/trace/tracePlugin.test.ts +171 -0
- package/src/domains/trace/tracePlugin.ts +159 -0
- package/src/domains/trace/types.ts +65 -0
- package/src/error/PragmaError.ts +15 -2
- package/src/error/types.ts +15 -1
- package/src/mcp/error/serializeErrorPayload.ts +7 -0
- package/src/mcp/error/types.ts +8 -0
- package/src/mcp/registerResources.test.ts +38 -16
- package/src/mcp/registerResources.ts +57 -8
- package/src/mcp/tools/registerTools.test.ts +22 -7
- package/src/mcp/utils/wrapTool.test.ts +1 -0
- package/src/pipeline/collectCommands.ts +7 -1
- package/src/pipeline/renderError.ts +15 -0
- package/src/pipeline/resolveCommandKind.ts +8 -1
- package/src/pipeline/runCli.ts +12 -1
- package/src/testing/createTestMcpClient.ts +1 -0
- package/src/testing/graphqlFixtures.ts +72 -0
- package/src/testing/index.ts +7 -0
- package/src/testing/integration/mcp-surface.test.ts +11 -5
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { CapabilitiesData } from "../types.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Render capabilities data as compact terminal text.
|
|
5
|
+
*
|
|
6
|
+
* @param data - The structured capabilities payload.
|
|
7
|
+
* @returns Plain text for terminal output.
|
|
8
|
+
*/
|
|
9
|
+
export default function renderCapabilities(data: CapabilitiesData): string {
|
|
10
|
+
const lines: string[] = [];
|
|
11
|
+
|
|
12
|
+
lines.push(`pragma v${data.version}`);
|
|
13
|
+
lines.push("");
|
|
14
|
+
|
|
15
|
+
// — Conventions
|
|
16
|
+
lines.push("Conventions");
|
|
17
|
+
lines.push(` ${data.conventions.system}`);
|
|
18
|
+
lines.push(` ${data.conventions.model}`);
|
|
19
|
+
lines.push(` ${data.conventions.querying}`);
|
|
20
|
+
lines.push("");
|
|
21
|
+
|
|
22
|
+
// — Discovery sequence
|
|
23
|
+
lines.push("Discovery Sequence");
|
|
24
|
+
for (const stage of data.discovery_sequence) {
|
|
25
|
+
lines.push(` ${stage.stage}. ${stage.tool} — ${stage.purpose}`);
|
|
26
|
+
}
|
|
27
|
+
lines.push("");
|
|
28
|
+
|
|
29
|
+
// — Tools by category
|
|
30
|
+
const categories = ["read", "write", "orientation", "diagnostic"] as const;
|
|
31
|
+
for (const cat of categories) {
|
|
32
|
+
const tools = data.tools.filter((t) => t.category === cat);
|
|
33
|
+
lines.push(`${cat} (${tools.length})`);
|
|
34
|
+
for (const tool of tools) {
|
|
35
|
+
lines.push(` ${tool.name} — ${tool.use_when}`);
|
|
36
|
+
}
|
|
37
|
+
lines.push("");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// — Counts + limits
|
|
41
|
+
const { counts } = data;
|
|
42
|
+
lines.push(
|
|
43
|
+
`${counts.total} tools: ${counts.read} read, ${counts.write} write, ${counts.orientation} orientation, ${counts.diagnostic} diagnostic`,
|
|
44
|
+
);
|
|
45
|
+
lines.push(
|
|
46
|
+
`output: ${data.limits.output_modes.join(", ")} | condensed: ${data.limits.condensed_available ? "yes" : "no"}`,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
return lines.join("\n");
|
|
50
|
+
}
|
package/src/domains/llm/index.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module LLM domain barrel.
|
|
3
3
|
*
|
|
4
|
-
* Provides the `pragma
|
|
4
|
+
* Provides the `pragma capabilities` and `pragma llm` commands
|
|
5
|
+
* for LLM orientation context generation.
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
|
-
export { buildLlmCommand } from "./commands/index.js";
|
|
8
|
+
export { buildCapabilitiesCommand, buildLlmCommand } from "./commands/index.js";
|
|
8
9
|
export { specs as mcpSpecs } from "./mcp/index.js";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** @module LLM domain MCP barrel — capabilities, llm. */
|
|
2
|
-
export { default as specs } from "./specs.js";
|
|
2
|
+
export { buildCapabilitiesData, default as specs } from "./specs.js";
|
|
@@ -2,74 +2,80 @@
|
|
|
2
2
|
* MCP tool specs for llm domain — capabilities, llm.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { VERSION } from "#constants";
|
|
5
6
|
import { buildFilterConfig } from "../../shared/filters/index.js";
|
|
6
7
|
import type { ToolSpec } from "../../shared/ToolSpec.js";
|
|
7
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
COMMAND_REFERENCE,
|
|
10
|
+
DECISION_TREES,
|
|
11
|
+
TOOL_CATALOG,
|
|
12
|
+
} from "../data/index.js";
|
|
8
13
|
import { collectContext } from "../operations/index.js";
|
|
9
|
-
import type { LlmData } from "../types.js";
|
|
14
|
+
import type { CapabilitiesData, LlmData } from "../types.js";
|
|
10
15
|
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
16
|
+
/** Build the static capabilities response. */
|
|
17
|
+
function buildCapabilitiesData(): CapabilitiesData {
|
|
18
|
+
const counts = {
|
|
19
|
+
total: TOOL_CATALOG.length,
|
|
20
|
+
read: TOOL_CATALOG.filter((t) => t.category === "read").length,
|
|
21
|
+
write: TOOL_CATALOG.filter((t) => t.category === "write").length,
|
|
22
|
+
orientation: TOOL_CATALOG.filter((t) => t.category === "orientation")
|
|
23
|
+
.length,
|
|
24
|
+
diagnostic: TOOL_CATALOG.filter((t) => t.category === "diagnostic").length,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
version: VERSION,
|
|
29
|
+
conventions: {
|
|
30
|
+
system:
|
|
31
|
+
"Pragma is a CLI and MCP server for querying a design system knowledge graph — blocks, tokens, modifiers, standards, and ontologies.",
|
|
32
|
+
model:
|
|
33
|
+
"Data is scoped by tier (hierarchical, e.g. global > apps > apps/lxd) and channel (normal, experimental, prerelease). Set these via config_tier and config_channel.",
|
|
34
|
+
querying:
|
|
35
|
+
"All queries run against an RDF triple store. Prefixed IRIs (e.g. ds:global.component.button) identify entities. Use ontology_list to discover namespaces.",
|
|
36
|
+
},
|
|
37
|
+
discovery_sequence: [
|
|
38
|
+
{
|
|
39
|
+
stage: 1,
|
|
40
|
+
tool: "capabilities",
|
|
41
|
+
purpose: "Understand conventions, available tools, and how to navigate",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
stage: 2,
|
|
45
|
+
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.",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
stage: 3,
|
|
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.",
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
tools: TOOL_CATALOG,
|
|
57
|
+
counts,
|
|
58
|
+
limits: {
|
|
59
|
+
output_modes: ["text", "json", "llm"],
|
|
60
|
+
condensed_available: true,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
47
64
|
|
|
48
65
|
const specs: readonly ToolSpec[] = [
|
|
49
66
|
{
|
|
50
67
|
name: "capabilities",
|
|
51
68
|
description:
|
|
52
|
-
"
|
|
69
|
+
"Discover pragma conventions, available tools with behavioral hints, and discovery sequence. Call this first at session start.",
|
|
53
70
|
readOnly: true,
|
|
54
71
|
async execute() {
|
|
55
|
-
return {
|
|
56
|
-
data: {
|
|
57
|
-
tools: allToolNames,
|
|
58
|
-
counts: {
|
|
59
|
-
total: allToolNames.length,
|
|
60
|
-
read: toolNames.read.length,
|
|
61
|
-
write: toolNames.write.length,
|
|
62
|
-
orientation: toolNames.orientation.length,
|
|
63
|
-
diagnostic: toolNames.diagnostic.length,
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
};
|
|
72
|
+
return { data: buildCapabilitiesData() };
|
|
67
73
|
},
|
|
68
74
|
},
|
|
69
75
|
{
|
|
70
76
|
name: "llm",
|
|
71
77
|
description:
|
|
72
|
-
"Get LLM orientation
|
|
78
|
+
"Get live LLM orientation — context snapshot (tier, channel, entity counts), decision trees for common intents, and command reference with token costs. Call after capabilities when starting design system work.",
|
|
73
79
|
readOnly: true,
|
|
74
80
|
async execute(rt) {
|
|
75
81
|
const filters = buildFilterConfig(rt);
|
|
@@ -84,4 +90,5 @@ const specs: readonly ToolSpec[] = [
|
|
|
84
90
|
},
|
|
85
91
|
];
|
|
86
92
|
|
|
93
|
+
export { buildCapabilitiesData };
|
|
87
94
|
export default specs;
|
package/src/domains/llm/types.ts
CHANGED
|
@@ -1,3 +1,42 @@
|
|
|
1
|
+
import type { ToolEntry } from "./data/toolCatalog.js";
|
|
2
|
+
|
|
3
|
+
// — Capabilities ——————————————————————————————————————————————————————————————
|
|
4
|
+
|
|
5
|
+
/** Structured system map returned by `pragma capabilities`. */
|
|
6
|
+
export interface CapabilitiesData {
|
|
7
|
+
readonly version: string;
|
|
8
|
+
readonly conventions: {
|
|
9
|
+
readonly system: string;
|
|
10
|
+
readonly model: string;
|
|
11
|
+
readonly querying: string;
|
|
12
|
+
};
|
|
13
|
+
readonly discovery_sequence: readonly DiscoveryStage[];
|
|
14
|
+
readonly tools: readonly ToolEntry[];
|
|
15
|
+
readonly counts: ToolCounts;
|
|
16
|
+
readonly limits: {
|
|
17
|
+
readonly output_modes: readonly string[];
|
|
18
|
+
readonly condensed_available: boolean;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** A single stage in the discovery flow. */
|
|
23
|
+
export interface DiscoveryStage {
|
|
24
|
+
readonly stage: number;
|
|
25
|
+
readonly tool: string;
|
|
26
|
+
readonly purpose: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Tool counts by category. */
|
|
30
|
+
export interface ToolCounts {
|
|
31
|
+
readonly total: number;
|
|
32
|
+
readonly read: number;
|
|
33
|
+
readonly write: number;
|
|
34
|
+
readonly orientation: number;
|
|
35
|
+
readonly diagnostic: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// — LLM orientation ———————————————————————————————————————————————————————————
|
|
39
|
+
|
|
1
40
|
/** Dynamic context snapshot: tier, channel, entity counts, and namespaces. */
|
|
2
41
|
export interface LlmContext {
|
|
3
42
|
readonly tier: string | undefined;
|
|
@@ -22,6 +61,7 @@ export interface DecisionTree {
|
|
|
22
61
|
export interface CommandRefEntry {
|
|
23
62
|
readonly command: string;
|
|
24
63
|
readonly tokens: string;
|
|
64
|
+
readonly use_when: string;
|
|
25
65
|
}
|
|
26
66
|
|
|
27
67
|
/** Complete payload rendered by `pragma llm`: context, trees, and reference. */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
/** @module Re-exports modifier command builders (`lookup`, `
|
|
1
|
+
/** @module Re-exports modifier command builders (`list`, `lookup`, `sample`). */
|
|
2
2
|
|
|
3
3
|
export { default as listCommand } from "./list.js";
|
|
4
4
|
export { default as lookupCommand } from "./lookup.js";
|
|
5
|
+
export { default as sampleCommand } from "./sample.js";
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wires the `pragma modifier sample` CLI command.
|
|
3
|
+
*
|
|
4
|
+
* Returns 1–5 complete modifier family instances as exemplars so agents
|
|
5
|
+
* can learn the data shape before writing queries.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
type CommandDefinition,
|
|
10
|
+
createOutputResult,
|
|
11
|
+
} from "@canonical/cli-core";
|
|
12
|
+
import type { PragmaContext } from "../../shared/context.js";
|
|
13
|
+
import { selectFormatter } from "../../shared/formatters.js";
|
|
14
|
+
import type { ModifierFamily, SampleOutput } from "../../shared/types/index.js";
|
|
15
|
+
import { sampleFormatters } from "../formatters/index.js";
|
|
16
|
+
import { sampleModifiers } from "../operations/index.js";
|
|
17
|
+
|
|
18
|
+
const NEXT_STEPS = [
|
|
19
|
+
"Use modifier_lookup to inspect specific modifier families by name.",
|
|
20
|
+
"Use modifier_list to see all available modifier families.",
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
export default function sampleCommand(ctx: PragmaContext): CommandDefinition {
|
|
24
|
+
return {
|
|
25
|
+
path: ["modifier", "sample"],
|
|
26
|
+
description:
|
|
27
|
+
"Return randomly selected complete modifier family instances as exemplars for shape discovery",
|
|
28
|
+
parameters: [
|
|
29
|
+
{
|
|
30
|
+
name: "count",
|
|
31
|
+
type: "string",
|
|
32
|
+
description: "Number of samples to return (1–5, default 2)",
|
|
33
|
+
required: false,
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
meta: {
|
|
37
|
+
examples: ["pragma modifier sample", "pragma modifier sample --count 3"],
|
|
38
|
+
},
|
|
39
|
+
async execute(params) {
|
|
40
|
+
const count = Number(params.count ?? 2);
|
|
41
|
+
const result = await sampleModifiers(ctx.store, count);
|
|
42
|
+
|
|
43
|
+
const output: SampleOutput<ModifierFamily> = {
|
|
44
|
+
...result,
|
|
45
|
+
nextSteps: NEXT_STEPS,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
return createOutputResult(output, {
|
|
49
|
+
plain: selectFormatter(ctx, sampleFormatters),
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
/** @module Re-exports modifier formatters (`lookupFormatters`, `
|
|
1
|
+
/** @module Re-exports modifier formatters (`listFormatters`, `lookupFormatters`, `sampleFormatters`). */
|
|
2
2
|
|
|
3
3
|
export { default as listFormatters } from "./list.js";
|
|
4
4
|
export {
|
|
5
5
|
createInkLookupOptions,
|
|
6
6
|
default as lookupFormatters,
|
|
7
7
|
} from "./lookup.js";
|
|
8
|
+
export { default as sampleFormatters } from "./sample.js";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sample formatter for the modifier domain.
|
|
3
|
+
*
|
|
4
|
+
* Delegates to the shared factory, rendering each item via the
|
|
5
|
+
* existing modifier lookup formatter.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import createSampleFormatters from "../../shared/sampleFormatters.js";
|
|
9
|
+
import type { ModifierFamily } from "../../shared/types/index.js";
|
|
10
|
+
import lookupFormatters from "./lookup.js";
|
|
11
|
+
|
|
12
|
+
export default createSampleFormatters<ModifierFamily>(
|
|
13
|
+
"modifiers",
|
|
14
|
+
(family, mode) => lookupFormatters[mode](family),
|
|
15
|
+
);
|
|
@@ -7,15 +7,19 @@
|
|
|
7
7
|
|
|
8
8
|
import type { CommandDefinition } from "@canonical/cli-core";
|
|
9
9
|
import type { PragmaContext } from "../shared/context.js";
|
|
10
|
-
import { listCommand, lookupCommand } from "./commands/index.js";
|
|
10
|
+
import { listCommand, lookupCommand, sampleCommand } from "./commands/index.js";
|
|
11
11
|
|
|
12
12
|
export function commands(ctx: PragmaContext): CommandDefinition[] {
|
|
13
|
-
return [listCommand(ctx), lookupCommand(ctx)];
|
|
13
|
+
return [listCommand(ctx), lookupCommand(ctx), sampleCommand(ctx)];
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export { specs as mcpSpecs } from "./mcp/index.js";
|
|
17
17
|
export { modifierConfig } from "./modifierConfig.js";
|
|
18
|
-
export {
|
|
18
|
+
export {
|
|
19
|
+
listModifiers,
|
|
20
|
+
lookupModifier,
|
|
21
|
+
sampleModifiers,
|
|
22
|
+
} from "./operations/index.js";
|
|
19
23
|
export {
|
|
20
24
|
modifierEmptyError,
|
|
21
25
|
resolveModifierList,
|
|
@@ -9,7 +9,9 @@ import type { ToolSpec } from "../../shared/ToolSpec.js";
|
|
|
9
9
|
import {
|
|
10
10
|
listFormatters as modifierListFmt,
|
|
11
11
|
lookupFormatters as modifierLookupFmt,
|
|
12
|
+
sampleFormatters as modifierSampleFmt,
|
|
12
13
|
} from "../formatters/index.js";
|
|
14
|
+
import { sampleModifiers } from "../operations/index.js";
|
|
13
15
|
import {
|
|
14
16
|
resolveModifierList,
|
|
15
17
|
resolveModifierLookup,
|
|
@@ -92,6 +94,51 @@ const specs: readonly ToolSpec[] = [
|
|
|
92
94
|
return { data: result, meta: { count: result.results.length } };
|
|
93
95
|
},
|
|
94
96
|
},
|
|
97
|
+
{
|
|
98
|
+
name: "modifier_sample",
|
|
99
|
+
description:
|
|
100
|
+
"Return 1–5 randomly selected complete modifier family instances as exemplars. Use BEFORE writing queries to see actual data shapes, property names, and value formats. Each call returns different instances.",
|
|
101
|
+
params: {
|
|
102
|
+
count: {
|
|
103
|
+
type: "string",
|
|
104
|
+
description: "Number of samples (1–5, default 2)",
|
|
105
|
+
optional: true,
|
|
106
|
+
},
|
|
107
|
+
condensed: {
|
|
108
|
+
type: "boolean",
|
|
109
|
+
description: "Token-optimized output",
|
|
110
|
+
optional: true,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
readOnly: true,
|
|
114
|
+
async execute(rt, { count, condensed }) {
|
|
115
|
+
const n = Number(count ?? 2);
|
|
116
|
+
const result = await sampleModifiers(rt.store, n);
|
|
117
|
+
const nextSteps = [
|
|
118
|
+
`These are ${result.samples.length} of ${result.totalCount} total modifier families.`,
|
|
119
|
+
"Use modifier_lookup to inspect specific modifier families by name.",
|
|
120
|
+
"Use modifier_list to see all available modifier families.",
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
if (condensed) {
|
|
124
|
+
const text = modifierSampleFmt.llm({ ...result, nextSteps });
|
|
125
|
+
return {
|
|
126
|
+
condensed: true,
|
|
127
|
+
text,
|
|
128
|
+
tokens: `~${Math.ceil(text.length / 4)}`,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
data: {
|
|
134
|
+
samples: result.samples,
|
|
135
|
+
totalCount: result.totalCount,
|
|
136
|
+
nextSteps,
|
|
137
|
+
},
|
|
138
|
+
meta: { count: result.samples.length },
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
},
|
|
95
142
|
] as const;
|
|
96
143
|
|
|
97
144
|
export default specs;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
/** @module Re-exports modifier operations (`lookupModifier`, `
|
|
1
|
+
/** @module Re-exports modifier operations (`listModifiers`, `lookupModifier`, `sampleModifiers`). */
|
|
2
2
|
|
|
3
3
|
export { default as listModifiers } from "./list.js";
|
|
4
4
|
export { default as lookupModifier } from "./lookup.js";
|
|
5
|
+
export { default as sampleModifiers } from "./sample.js";
|
|
@@ -13,6 +13,11 @@ import { escapeSparqlValue } from "@canonical/ke";
|
|
|
13
13
|
import { PragmaError } from "#error";
|
|
14
14
|
import { buildQuery } from "../../shared/buildQuery.js";
|
|
15
15
|
import { P } from "../../shared/prefixes.js";
|
|
16
|
+
import {
|
|
17
|
+
detectCrossDomain,
|
|
18
|
+
listDomainNames,
|
|
19
|
+
suggestNames,
|
|
20
|
+
} from "../../shared/suggestions/index.js";
|
|
16
21
|
import type { ModifierFamily } from "../../shared/types/index.js";
|
|
17
22
|
|
|
18
23
|
export default async function lookupModifier(
|
|
@@ -39,12 +44,16 @@ export default async function lookupModifier(
|
|
|
39
44
|
);
|
|
40
45
|
|
|
41
46
|
if (result.type !== "select" || result.bindings.length === 0) {
|
|
47
|
+
const candidates = await listDomainNames(store, "modifier");
|
|
48
|
+
const crossDomain = await detectCrossDomain(name, "modifier", store);
|
|
42
49
|
throw PragmaError.notFound("modifier", name, {
|
|
50
|
+
suggestions: suggestNames(name, candidates),
|
|
43
51
|
recovery: {
|
|
44
52
|
message: "List available modifiers.",
|
|
45
53
|
cli: "pragma modifier list",
|
|
46
54
|
mcp: { tool: "modifier_list" },
|
|
47
55
|
},
|
|
56
|
+
crossDomain: crossDomain ?? undefined,
|
|
48
57
|
});
|
|
49
58
|
}
|
|
50
59
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a small number of complete modifier family instances as exemplars.
|
|
3
|
+
*
|
|
4
|
+
* Composes the existing list and lookup operations to avoid duplicating
|
|
5
|
+
* any SPARQL logic. Agents use this to learn the data shape before querying.
|
|
6
|
+
*
|
|
7
|
+
* @param store - ke store to query
|
|
8
|
+
* @param count - number of samples (clamped 1–5, default 2)
|
|
9
|
+
* @returns sample result with detailed modifier families and total count
|
|
10
|
+
* @note Queries ke store
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { Store } from "@canonical/ke";
|
|
14
|
+
import pickRandom from "../../shared/pickRandom.js";
|
|
15
|
+
import type { ModifierFamily, SampleResult } from "../../shared/types/index.js";
|
|
16
|
+
import listModifiers from "./list.js";
|
|
17
|
+
import lookupModifier from "./lookup.js";
|
|
18
|
+
|
|
19
|
+
export default async function sampleModifiers(
|
|
20
|
+
store: Store,
|
|
21
|
+
count = 2,
|
|
22
|
+
): Promise<SampleResult<ModifierFamily>> {
|
|
23
|
+
const clamped = Math.max(1, Math.min(5, count));
|
|
24
|
+
const all = await listModifiers(store);
|
|
25
|
+
const selected = pickRandom(all, clamped);
|
|
26
|
+
|
|
27
|
+
const samples = await Promise.all(
|
|
28
|
+
selected.map((m) => lookupModifier(store, m.name)),
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return { samples, totalCount: all.length };
|
|
32
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import modifierEmptyError from "./modifierEmptyError.js";
|
|
3
|
+
|
|
4
|
+
describe("modifierEmptyError", () => {
|
|
5
|
+
it("recovers via a runnable install command (store-empty is its only scenario)", () => {
|
|
6
|
+
const err = modifierEmptyError();
|
|
7
|
+
|
|
8
|
+
// `modifier list` takes no filters, so an empty result is always
|
|
9
|
+
// store-empty: the recovery is the install, not a widen.
|
|
10
|
+
expect(err.recovery).toEqual({
|
|
11
|
+
message: "Install the design system packages that provide modifiers.",
|
|
12
|
+
cli: "bun add -D @canonical/design-system",
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("cites a real default package, not @canonical/ds-global", () => {
|
|
17
|
+
const err = modifierEmptyError();
|
|
18
|
+
expect(err.recovery?.cli).toContain("@canonical/design-system");
|
|
19
|
+
expect(err.recovery?.cli).not.toContain("ds-global");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("has no mcp retry for store-empty (re-listing would return empty again)", () => {
|
|
23
|
+
const err = modifierEmptyError();
|
|
24
|
+
expect(err.recovery?.mcp).toBeUndefined();
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { PragmaError } from "#error";
|
|
2
2
|
|
|
3
3
|
export default function modifierEmptyError(): PragmaError {
|
|
4
|
+
// `modifier list` takes no filters, so an empty result always means the
|
|
5
|
+
// design system packages that supply modifier triples are absent. The
|
|
6
|
+
// recovery is the runnable install command, not a (non-existent) widen.
|
|
4
7
|
return PragmaError.emptyResults("modifier", {
|
|
5
8
|
recovery: {
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
// The base error already states "No modifiers found"; the recovery adds
|
|
10
|
+
// only the cause + the runnable fix (the cli line is what renders on CLI).
|
|
11
|
+
message: "Install the design system packages that provide modifiers.",
|
|
12
|
+
cli: "bun add -D @canonical/design-system",
|
|
8
13
|
},
|
|
9
14
|
});
|
|
10
15
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { LookupContract, LookupResult } from "../../shared/contracts.js";
|
|
2
2
|
import lookupMany from "../../shared/lookupMany.js";
|
|
3
3
|
import type { PragmaRuntime } from "../../shared/runtime.js";
|
|
4
|
+
import { expandLookupQueries } from "../../shared/suggestions/index.js";
|
|
4
5
|
import type { ModifierFamily } from "../../shared/types/index.js";
|
|
5
6
|
import { lookupModifier } from "../operations/index.js";
|
|
6
7
|
|
|
@@ -8,11 +9,22 @@ export default async function resolveModifierLookup(
|
|
|
8
9
|
store: Pick<PragmaRuntime, "store">["store"],
|
|
9
10
|
queries: readonly string[],
|
|
10
11
|
): Promise<LookupContract<ModifierFamily>> {
|
|
11
|
-
const
|
|
12
|
+
const { names, globErrors } = await expandLookupQueries(
|
|
12
13
|
queries,
|
|
13
|
-
|
|
14
|
+
store,
|
|
15
|
+
"modifier",
|
|
14
16
|
);
|
|
15
17
|
|
|
18
|
+
const lookupResult: LookupResult<ModifierFamily> =
|
|
19
|
+
names.length > 0
|
|
20
|
+
? await lookupMany(names, (query) => lookupModifier(store, query))
|
|
21
|
+
: { results: [], errors: [] };
|
|
22
|
+
|
|
23
|
+
const result: LookupResult<ModifierFamily> = {
|
|
24
|
+
results: lookupResult.results,
|
|
25
|
+
errors: [...lookupResult.errors, ...globErrors],
|
|
26
|
+
};
|
|
27
|
+
|
|
16
28
|
return {
|
|
17
29
|
params: { names: queries },
|
|
18
30
|
result,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
export { cloneRef, fetchRef, isSha, pruneCache } from "./gitOps.js";
|
|
4
4
|
export type { PackageRef, RawPackageEntry } from "./parseRef.js";
|
|
5
5
|
export { parsePackageEntry } from "./parseRef.js";
|
|
6
|
-
export { cacheRoot, gitCacheDir, globalConfigDir } from "./paths.js";
|
|
6
|
+
export { cacheRoot, dataRoot, gitCacheDir, globalConfigDir } from "./paths.js";
|
|
7
7
|
export { default as readGlobalRefs } from "./readGlobalRefs.js";
|
|
8
8
|
|
|
9
9
|
export type { UpdateRefsOptions, UpdateResult } from "./updateRefs.js";
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Cross-platform path resolution for pragma cache and
|
|
2
|
+
* Cross-platform path resolution for pragma cache, config, and data.
|
|
3
3
|
*
|
|
4
4
|
* Respects XDG Base Directory Specification:
|
|
5
5
|
* - XDG_CACHE_HOME for cache (default: ~/.cache)
|
|
6
6
|
* - XDG_CONFIG_HOME for global config (default: ~/.config)
|
|
7
|
+
* - XDG_DATA_HOME for persistent data (default: ~/.local/share)
|
|
7
8
|
*
|
|
8
9
|
* Override via PRAGMA_CACHE_DIR for CI and testing.
|
|
9
10
|
* Uses os.homedir() for macOS/WSL/Linux portability.
|
|
@@ -42,6 +43,19 @@ export function globalConfigDir(): string {
|
|
|
42
43
|
return join(base, "pragma");
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Root directory for pragma persistent data (extracted skills, etc.).
|
|
48
|
+
*
|
|
49
|
+
* Resolution order:
|
|
50
|
+
* 1. XDG_DATA_HOME/pragma (XDG spec)
|
|
51
|
+
* 2. ~/.local/share/pragma (default)
|
|
52
|
+
*/
|
|
53
|
+
export function dataRoot(): string {
|
|
54
|
+
const xdg = process.env.XDG_DATA_HOME;
|
|
55
|
+
const base = xdg ?? join(homedir(), ".local", "share");
|
|
56
|
+
return join(base, "pragma");
|
|
57
|
+
}
|
|
58
|
+
|
|
45
59
|
/**
|
|
46
60
|
* Cache directory for a specific git-ref-resolved package.
|
|
47
61
|
*
|
|
@@ -54,6 +68,20 @@ export function gitCacheDir(pkg: string, ref: string): string {
|
|
|
54
68
|
return join(cacheRoot(), "refs", pkg, sanitizedRef);
|
|
55
69
|
}
|
|
56
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Root directory for query trace logs.
|
|
73
|
+
*
|
|
74
|
+
* Resolution order:
|
|
75
|
+
* 1. PRAGMA_TRACE_DIR environment variable (CI/testing override)
|
|
76
|
+
* 2. XDG_DATA_HOME/pragma/traces (default)
|
|
77
|
+
*/
|
|
78
|
+
export function traceDir(): string {
|
|
79
|
+
if (process.env.PRAGMA_TRACE_DIR) {
|
|
80
|
+
return process.env.PRAGMA_TRACE_DIR;
|
|
81
|
+
}
|
|
82
|
+
return join(dataRoot(), "traces");
|
|
83
|
+
}
|
|
84
|
+
|
|
57
85
|
/**
|
|
58
86
|
* Sanitize a git ref for use as a directory name.
|
|
59
87
|
* Replaces characters that are invalid or confusing in path segments.
|