@cubis/foundry 0.3.40 → 0.3.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/README.md +67 -3
  2. package/bin/cubis.js +360 -26
  3. package/mcp/README.md +72 -8
  4. package/mcp/config.json +3 -0
  5. package/mcp/dist/index.js +315 -68
  6. package/mcp/src/config/index.test.ts +1 -0
  7. package/mcp/src/config/schema.ts +5 -0
  8. package/mcp/src/index.ts +40 -9
  9. package/mcp/src/server.ts +66 -10
  10. package/mcp/src/telemetry/tokenBudget.ts +114 -0
  11. package/mcp/src/tools/index.ts +7 -0
  12. package/mcp/src/tools/skillBrowseCategory.ts +22 -5
  13. package/mcp/src/tools/skillBudgetReport.ts +128 -0
  14. package/mcp/src/tools/skillGet.ts +18 -0
  15. package/mcp/src/tools/skillListCategories.ts +19 -6
  16. package/mcp/src/tools/skillSearch.ts +22 -5
  17. package/mcp/src/tools/skillTools.test.ts +61 -9
  18. package/mcp/src/vault/manifest.test.ts +19 -1
  19. package/mcp/src/vault/manifest.ts +12 -1
  20. package/mcp/src/vault/scanner.test.ts +1 -0
  21. package/mcp/src/vault/scanner.ts +1 -0
  22. package/mcp/src/vault/types.ts +6 -0
  23. package/package.json +1 -1
  24. package/workflows/workflows/agent-environment-setup/platforms/antigravity/rules/GEMINI.md +28 -0
  25. package/workflows/workflows/agent-environment-setup/platforms/codex/rules/AGENTS.md +31 -2
  26. package/workflows/workflows/agent-environment-setup/platforms/copilot/rules/AGENTS.md +28 -0
  27. package/workflows/workflows/agent-environment-setup/platforms/copilot/rules/copilot-instructions.md +28 -0
  28. package/workflows/workflows/agent-environment-setup/platforms/cursor/rules/.cursorrules +28 -0
  29. package/workflows/workflows/agent-environment-setup/platforms/windsurf/rules/.windsurfrules +28 -0
@@ -8,6 +8,11 @@
8
8
  import { z } from "zod";
9
9
  import type { VaultManifest } from "../vault/types.js";
10
10
  import { enrichWithDescriptions } from "../vault/manifest.js";
11
+ import {
12
+ buildSkillToolMetrics,
13
+ estimateTokensFromBytes,
14
+ estimateTokensFromText,
15
+ } from "../telemetry/tokenBudget.js";
11
16
 
12
17
  export const skillSearchName = "skill_search";
13
18
 
@@ -27,6 +32,7 @@ export async function handleSkillSearch(
27
32
  args: z.infer<typeof skillSearchSchema>,
28
33
  manifest: VaultManifest,
29
34
  summaryMaxLength: number,
35
+ charsPerToken: number,
30
36
  ) {
31
37
  const { query } = args;
32
38
  const lower = query.toLowerCase();
@@ -56,17 +62,28 @@ export async function handleSkillSearch(
56
62
  category: s.category,
57
63
  description: s.description ?? "(no description)",
58
64
  }));
65
+ const payload = { query, results, count: results.length };
66
+ const text = JSON.stringify(payload, null, 2);
67
+ const selectedSkillsEstimatedTokens = matches.reduce(
68
+ (sum, skill) => sum + estimateTokensFromBytes(skill.fileBytes, charsPerToken),
69
+ 0,
70
+ );
71
+ const metrics = buildSkillToolMetrics({
72
+ charsPerToken,
73
+ fullCatalogEstimatedTokens: manifest.fullCatalogEstimatedTokens,
74
+ responseEstimatedTokens: estimateTokensFromText(text, charsPerToken),
75
+ selectedSkillsEstimatedTokens,
76
+ });
59
77
 
60
78
  return {
61
79
  content: [
62
80
  {
63
81
  type: "text" as const,
64
- text: JSON.stringify(
65
- { query, results, count: results.length },
66
- null,
67
- 2,
68
- ),
82
+ text,
69
83
  },
70
84
  ],
85
+ structuredContent: {
86
+ metrics,
87
+ },
71
88
  };
72
89
  }
@@ -1,9 +1,10 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { mkdtempSync, writeFileSync } from "node:fs";
2
+ import { mkdtempSync, statSync, writeFileSync } from "node:fs";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
5
  import type { VaultManifest } from "../vault/types.js";
6
6
  import { handleSkillBrowseCategory } from "./skillBrowseCategory.js";
7
+ import { handleSkillBudgetReport } from "./skillBudgetReport.js";
7
8
  import { handleSkillGet } from "./skillGet.js";
8
9
  import { handleSkillListCategories } from "./skillListCategories.js";
9
10
  import { handleSkillSearch } from "./skillSearch.js";
@@ -12,6 +13,10 @@ function payload(result: { content: Array<{ text: string }> }): Record<string, u
12
13
  return JSON.parse(result.content[0].text) as Record<string, unknown>;
13
14
  }
14
15
 
16
+ function metrics(result: { structuredContent?: Record<string, unknown> }): Record<string, unknown> {
17
+ return (result.structuredContent?.metrics || {}) as Record<string, unknown>;
18
+ }
19
+
15
20
  function createSkillFile(id: string, description: string, body = "# Content"): string {
16
21
  const dir = mkdtempSync(path.join(os.tmpdir(), `mcp-skill-${id}-`));
17
22
  const file = path.join(dir, "SKILL.md");
@@ -32,27 +37,47 @@ function createManifest(): VaultManifest {
32
37
  "fastapi-expert",
33
38
  "FastAPI async backend patterns",
34
39
  );
40
+ const reactBytes = statSync(reactFile).size;
41
+ const fastapiBytes = statSync(fastapiFile).size;
42
+ const fullCatalogBytes = reactBytes + fastapiBytes;
35
43
 
36
44
  return {
37
45
  categories: ["backend", "frontend"],
38
46
  skills: [
39
- { id: "react-expert", category: "frontend", path: reactFile },
40
- { id: "fastapi-expert", category: "backend", path: fastapiFile },
47
+ {
48
+ id: "react-expert",
49
+ category: "frontend",
50
+ path: reactFile,
51
+ fileBytes: reactBytes,
52
+ },
53
+ {
54
+ id: "fastapi-expert",
55
+ category: "backend",
56
+ path: fastapiFile,
57
+ fileBytes: fastapiBytes,
58
+ },
41
59
  ],
60
+ fullCatalogBytes,
61
+ fullCatalogEstimatedTokens: Math.ceil(fullCatalogBytes / 4),
42
62
  };
43
63
  }
44
64
 
45
65
  describe("skill tools", () => {
46
66
  it("lists categories with skill counts", () => {
47
67
  const manifest = createManifest();
48
- const result = handleSkillListCategories(manifest);
68
+ const result = handleSkillListCategories(manifest, 4);
49
69
  const data = payload(result);
70
+ const toolMetrics = metrics(result);
50
71
 
51
72
  expect(data.totalSkills).toBe(2);
52
73
  expect(data.categories).toEqual([
53
74
  { category: "backend", skillCount: 1 },
54
75
  { category: "frontend", skillCount: 1 },
55
76
  ]);
77
+ expect(toolMetrics.estimatorVersion).toBeDefined();
78
+ expect(toolMetrics.fullCatalogEstimatedTokens).toBe(
79
+ manifest.fullCatalogEstimatedTokens,
80
+ );
56
81
  });
57
82
 
58
83
  it("browses a category with enriched descriptions", async () => {
@@ -61,8 +86,10 @@ describe("skill tools", () => {
61
86
  { category: "frontend" },
62
87
  manifest,
63
88
  200,
89
+ 4,
64
90
  );
65
91
  const data = payload(result);
92
+ const toolMetrics = metrics(result);
66
93
 
67
94
  expect(data.category).toBe("frontend");
68
95
  expect(data.count).toBe(1);
@@ -72,12 +99,13 @@ describe("skill tools", () => {
72
99
  description: "React performance and architecture guidance",
73
100
  },
74
101
  ]);
102
+ expect(toolMetrics.selectedSkillsEstimatedTokens).toBeGreaterThan(0);
75
103
  });
76
104
 
77
105
  it("throws when browsing an unknown category", async () => {
78
106
  const manifest = createManifest();
79
107
  await expect(
80
- handleSkillBrowseCategory({ category: "mobile" }, manifest, 200),
108
+ handleSkillBrowseCategory({ category: "mobile" }, manifest, 200, 4),
81
109
  ).rejects.toThrow('Category not found: "mobile"');
82
110
  });
83
111
 
@@ -85,7 +113,7 @@ describe("skill tools", () => {
85
113
  const manifest = createManifest();
86
114
 
87
115
  const byId = payload(
88
- await handleSkillSearch({ query: "react" }, manifest, 200),
116
+ await handleSkillSearch({ query: "react" }, manifest, 200, 4),
89
117
  );
90
118
  expect(byId.count).toBe(1);
91
119
  expect(byId.results).toEqual([
@@ -97,7 +125,7 @@ describe("skill tools", () => {
97
125
  ]);
98
126
 
99
127
  const byDescription = payload(
100
- await handleSkillSearch({ query: "async backend" }, manifest, 200),
128
+ await handleSkillSearch({ query: "async backend" }, manifest, 200, 4),
101
129
  );
102
130
  expect(byDescription.count).toBe(1);
103
131
  expect(byDescription.results).toEqual([
@@ -111,16 +139,40 @@ describe("skill tools", () => {
111
139
 
112
140
  it("returns full skill content for skill_get", async () => {
113
141
  const manifest = createManifest();
114
- const result = await handleSkillGet({ id: "react-expert" }, manifest);
142
+ const result = await handleSkillGet({ id: "react-expert" }, manifest, 4);
143
+ const toolMetrics = metrics(result);
115
144
 
116
145
  expect(result.content[0].text).toContain("# Content");
117
146
  expect(result.content[0].text).toContain("description: React performance");
147
+ expect(toolMetrics.loadedSkillEstimatedTokens).toBeGreaterThan(0);
118
148
  });
119
149
 
120
150
  it("throws when skill_get cannot find the requested skill", async () => {
121
151
  const manifest = createManifest();
122
- await expect(handleSkillGet({ id: "missing" }, manifest)).rejects.toThrow(
152
+ await expect(handleSkillGet({ id: "missing" }, manifest, 4)).rejects.toThrow(
123
153
  'Skill not found: "missing"',
124
154
  );
125
155
  });
156
+
157
+ it("returns consolidated budget rollup for selected and loaded skills", () => {
158
+ const manifest = createManifest();
159
+ const result = handleSkillBudgetReport(
160
+ {
161
+ selectedSkillIds: ["react-expert", "missing-skill"],
162
+ loadedSkillIds: ["react-expert"],
163
+ },
164
+ manifest,
165
+ 4,
166
+ );
167
+ const data = payload(result);
168
+
169
+ expect(data.skillLog).toBeDefined();
170
+ expect(data.contextBudget).toMatchObject({
171
+ fullCatalogEstimatedTokens: manifest.fullCatalogEstimatedTokens,
172
+ estimated: true,
173
+ });
174
+ expect(
175
+ (data.skillLog as Record<string, unknown>).unknownSelectedSkillIds,
176
+ ).toContain("missing-skill");
177
+ });
126
178
  });
@@ -3,6 +3,7 @@ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
5
  import {
6
+ buildManifest,
6
7
  enrichWithDescriptions,
7
8
  extractDescription,
8
9
  parseDescriptionFromFrontmatter,
@@ -109,11 +110,12 @@ describe("manifest enrichment", () => {
109
110
 
110
111
  const enriched = await enrichWithDescriptions(
111
112
  [
112
- { id: "alpha", category: "general", path: fileA },
113
+ { id: "alpha", category: "general", path: fileA, fileBytes: 64 },
113
114
  {
114
115
  id: "beta",
115
116
  category: "general",
116
117
  path: fileB,
118
+ fileBytes: 64,
117
119
  description: "Already populated",
118
120
  },
119
121
  ],
@@ -124,3 +126,19 @@ describe("manifest enrichment", () => {
124
126
  expect(enriched[1].description).toBe("Already populated");
125
127
  });
126
128
  });
129
+
130
+ describe("buildManifest", () => {
131
+ it("computes full catalog byte and token totals", () => {
132
+ const manifest = buildManifest(
133
+ [
134
+ { id: "a", category: "general", path: "/tmp/a.md", fileBytes: 20 },
135
+ { id: "b", category: "frontend", path: "/tmp/b.md", fileBytes: 12 },
136
+ ],
137
+ 4,
138
+ );
139
+
140
+ expect(manifest.categories).toEqual(["frontend", "general"]);
141
+ expect(manifest.fullCatalogBytes).toBe(32);
142
+ expect(manifest.fullCatalogEstimatedTokens).toBe(8);
143
+ });
144
+ });
@@ -8,19 +8,30 @@
8
8
  import { readFile } from "node:fs/promises";
9
9
  import type { SkillPointer, VaultManifest } from "./types.js";
10
10
  import { logger } from "../utils/logger.js";
11
+ import { estimateTokensFromBytes } from "../telemetry/tokenBudget.js";
11
12
 
12
13
  /**
13
14
  * Build a VaultManifest from scanned skill pointers.
14
15
  * Categories are derived from the pointers.
15
16
  */
16
- export function buildManifest(skills: SkillPointer[]): VaultManifest {
17
+ export function buildManifest(
18
+ skills: SkillPointer[],
19
+ charsPerToken: number,
20
+ ): VaultManifest {
17
21
  const categorySet = new Set<string>();
22
+ let fullCatalogBytes = 0;
18
23
  for (const skill of skills) {
19
24
  categorySet.add(skill.category);
25
+ fullCatalogBytes += skill.fileBytes;
20
26
  }
21
27
  return {
22
28
  categories: [...categorySet].sort(),
23
29
  skills,
30
+ fullCatalogBytes,
31
+ fullCatalogEstimatedTokens: estimateTokensFromBytes(
32
+ fullCatalogBytes,
33
+ charsPerToken,
34
+ ),
24
35
  };
25
36
  }
26
37
 
@@ -52,6 +52,7 @@ describe("scanVaultRoots", () => {
52
52
  expect(byId["react-expert"]).toBe("frontend");
53
53
  expect(byId["database-design"]).toBe("data");
54
54
  expect(byId["custom-skill"]).toBe("general");
55
+ expect(skills.every((skill) => skill.fileBytes > 0)).toBe(true);
55
56
  });
56
57
 
57
58
  it("skips missing roots and continues scanning valid roots", async () => {
@@ -46,6 +46,7 @@ export async function scanVaultRoots(
46
46
  id: entry,
47
47
  category: deriveCategory(entry),
48
48
  path: skillFile,
49
+ fileBytes: skillStat.size,
49
50
  });
50
51
  }
51
52
  }
@@ -9,6 +9,8 @@ export interface SkillPointer {
9
9
  category: string;
10
10
  /** Absolute path to the SKILL.md file. */
11
11
  path: string;
12
+ /** Skill file size in bytes. */
13
+ fileBytes: number;
12
14
  /** Short description extracted from frontmatter (truncated). */
13
15
  description?: string;
14
16
  }
@@ -18,4 +20,8 @@ export interface VaultManifest {
18
20
  categories: string[];
19
21
  /** All skill pointers (metadata only – no full content). */
20
22
  skills: SkillPointer[];
23
+ /** Total bytes across all discovered SKILL.md files. */
24
+ fullCatalogBytes: number;
25
+ /** Estimated full-catalog token usage (deterministic estimator). */
26
+ fullCatalogEstimatedTokens: number;
21
27
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cubis/foundry",
3
- "version": "0.3.40",
3
+ "version": "0.3.42",
4
4
  "description": "Cubis Foundry CLI for workflow-first AI agent environments",
5
5
  "type": "module",
6
6
  "bin": {
@@ -68,6 +68,32 @@ Use the best specialist first:
68
68
 
69
69
  ## 5) Skill Loading Policy
70
70
 
71
+ ## MCP-first Skill Discovery Order (Required)
72
+
73
+ 1. Use `skill_search` first to narrow candidate skills.
74
+ 2. Use `skill_browse_category` second to inspect category-level candidates.
75
+ 3. Use `skill_get` only for final selected skills that must be loaded.
76
+ 4. Keep pointer-first flow; avoid loading full skill text prematurely.
77
+
78
+ ## Skill Log Completion Block (Required)
79
+
80
+ After finishing skill selection/loading, publish:
81
+
82
+ - `selected_skills`: skill IDs selected for the task
83
+ - `loaded_skills`: skill IDs loaded via `skill_get`
84
+ - `skipped_skills`: considered but not loaded
85
+
86
+ ## Context Budget Block (Required, Estimated)
87
+
88
+ Immediately after the Skill Log block, publish estimated budget fields:
89
+
90
+ - `full_catalog_est_tokens`
91
+ - `loaded_est_tokens`
92
+ - `estimated_savings_tokens`
93
+ - `estimated_savings_percent`
94
+
95
+ Mark all context/token values as deterministic estimates (not provider metering).
96
+
71
97
  ### Smart Skill Selection (Adaptive)
72
98
 
73
99
  Use an adaptive load policy to control context size:
@@ -85,6 +111,8 @@ Use an adaptive load policy to control context size:
85
111
  4. If a mapped skill is missing, continue with best fallback and state it.
86
112
  5. Keep user-visible decision logs concise: selected skill(s) and one-line rationale.
87
113
 
114
+ After the skill log is complete, append the Context Budget block in the same response/update.
115
+
88
116
  ## 6) Socratic Gate (Before Complex Work)
89
117
 
90
118
  Before multi-file or architecture-impacting changes, ask targeted questions when requirements are unclear:
@@ -14,12 +14,13 @@ This file defines mandatory behavior for Codex projects installed via `cbx workf
14
14
  Before executing workflows, agents, or code edits, publish a short `Decision Log` that is visible to the user:
15
15
 
16
16
  1. Rule file(s) read at startup (at minimum `AGENTS.md`, plus any additional rule files loaded).
17
- 2. Workflow decision (`$workflow-*` or direct mode) and why it was chosen.
18
- 3. Agent routing decision (`$agent-*` or direct mode) and why it was chosen.
17
+ 2. Workflow decision ($workflow-* or direct mode) and why it was chosen.
18
+ 3. Agent routing decision ($agent-* or direct mode) and why it was chosen.
19
19
  4. Skill loading decision (skill names loaded) and why they were chosen.
20
20
 
21
21
  If routing changes during the task, publish a `Decision Update` before continuing.
22
22
  Keep this user-visible summary concise and factual; do not expose private chain-of-thought.
23
+ When mentioning wrappers in user-visible logs, use raw $workflow-* and $agent-* tokens (no backticks) so Codex can render icon/blue mention styling.
23
24
 
24
25
  ## 2) Skill-Based Workflow
25
26
 
@@ -63,6 +64,32 @@ Use the best specialist first:
63
64
 
64
65
  ## 5) Skill Loading Policy
65
66
 
67
+ ## MCP-first Skill Discovery Order (Required)
68
+
69
+ 1. Use `skill_search` first to narrow candidate skills.
70
+ 2. Use `skill_browse_category` second to inspect category-level candidates.
71
+ 3. Use `skill_get` only for final selected skills that must be loaded.
72
+ 4. Keep pointer-first flow; avoid loading full skill text prematurely.
73
+
74
+ ## Skill Log Completion Block (Required)
75
+
76
+ After finishing skill selection/loading, publish:
77
+
78
+ - `selected_skills`: skill IDs selected for the task
79
+ - `loaded_skills`: skill IDs loaded via `skill_get`
80
+ - `skipped_skills`: considered but not loaded
81
+
82
+ ## Context Budget Block (Required, Estimated)
83
+
84
+ Immediately after the Skill Log block, publish estimated budget fields:
85
+
86
+ - `full_catalog_est_tokens`
87
+ - `loaded_est_tokens`
88
+ - `estimated_savings_tokens`
89
+ - `estimated_savings_percent`
90
+
91
+ Mark all context/token values as deterministic estimates (not provider metering).
92
+
66
93
  ### Smart Skill Selection (Adaptive)
67
94
 
68
95
  Use an adaptive load policy to control context size:
@@ -80,6 +107,8 @@ Use an adaptive load policy to control context size:
80
107
  4. If a mapped skill is missing, continue with best fallback and state it.
81
108
  5. Keep user-visible decision logs concise: selected skill(s) and one-line rationale.
82
109
 
110
+ After the skill log is complete, append the Context Budget block in the same response/update.
111
+
83
112
  ## 6) Socratic Gate (Before Complex Work)
84
113
 
85
114
  Before multi-file or architecture-impacting changes, ask targeted questions when requirements are unclear:
@@ -72,6 +72,32 @@ When authoring custom Copilot assets, keep frontmatter schema compatible:
72
72
 
73
73
  ## 6) Skill Loading Policy
74
74
 
75
+ ## MCP-first Skill Discovery Order (Required)
76
+
77
+ 1. Use `skill_search` first to narrow candidate skills.
78
+ 2. Use `skill_browse_category` second to inspect category-level candidates.
79
+ 3. Use `skill_get` only for final selected skills that must be loaded.
80
+ 4. Keep pointer-first flow; avoid loading full skill text prematurely.
81
+
82
+ ## Skill Log Completion Block (Required)
83
+
84
+ After finishing skill selection/loading, publish:
85
+
86
+ - `selected_skills`: skill IDs selected for the task
87
+ - `loaded_skills`: skill IDs loaded via `skill_get`
88
+ - `skipped_skills`: considered but not loaded
89
+
90
+ ## Context Budget Block (Required, Estimated)
91
+
92
+ Immediately after the Skill Log block, publish estimated budget fields:
93
+
94
+ - `full_catalog_est_tokens`
95
+ - `loaded_est_tokens`
96
+ - `estimated_savings_tokens`
97
+ - `estimated_savings_percent`
98
+
99
+ Mark all context/token values as deterministic estimates (not provider metering).
100
+
75
101
  ### Smart Skill Selection (Adaptive)
76
102
 
77
103
  Use an adaptive load policy to control context size:
@@ -89,6 +115,8 @@ Use an adaptive load policy to control context size:
89
115
  4. If a mapped skill is missing, continue with best fallback and state it.
90
116
  5. Keep user-visible decision logs concise: selected skill(s) and one-line rationale.
91
117
 
118
+ After the skill log is complete, append the Context Budget block in the same response/update.
119
+
92
120
  ## 7) Socratic Gate (Before Complex Work)
93
121
 
94
122
  Before multi-file or architecture-impacting changes, ask targeted questions when requirements are unclear:
@@ -72,6 +72,32 @@ When authoring custom Copilot assets, keep frontmatter schema compatible:
72
72
 
73
73
  ## 6) Skill Loading Policy
74
74
 
75
+ ## MCP-first Skill Discovery Order (Required)
76
+
77
+ 1. Use `skill_search` first to narrow candidate skills.
78
+ 2. Use `skill_browse_category` second to inspect category-level candidates.
79
+ 3. Use `skill_get` only for final selected skills that must be loaded.
80
+ 4. Keep pointer-first flow; avoid loading full skill text prematurely.
81
+
82
+ ## Skill Log Completion Block (Required)
83
+
84
+ After finishing skill selection/loading, publish:
85
+
86
+ - `selected_skills`: skill IDs selected for the task
87
+ - `loaded_skills`: skill IDs loaded via `skill_get`
88
+ - `skipped_skills`: considered but not loaded
89
+
90
+ ## Context Budget Block (Required, Estimated)
91
+
92
+ Immediately after the Skill Log block, publish estimated budget fields:
93
+
94
+ - `full_catalog_est_tokens`
95
+ - `loaded_est_tokens`
96
+ - `estimated_savings_tokens`
97
+ - `estimated_savings_percent`
98
+
99
+ Mark all context/token values as deterministic estimates (not provider metering).
100
+
75
101
  ### Smart Skill Selection (TIER 0)
76
102
 
77
103
  Before starting ANY task, the agent MUST:
@@ -88,6 +114,8 @@ Before starting ANY task, the agent MUST:
88
114
  3. Keep context lean; avoid loading unrelated skill documents.
89
115
  4. If a mapped skill is missing, continue with best fallback and state it.
90
116
 
117
+ After the skill log is complete, append the Context Budget block in the same response/update.
118
+
91
119
  ## 7) Socratic Gate (Before Complex Work)
92
120
 
93
121
  Before multi-file or architecture-impacting changes, ask targeted questions when requirements are unclear:
@@ -32,11 +32,39 @@ Before starting ANY task, the agent MUST:
32
32
 
33
33
  ## 3) Skill Loading Policy
34
34
 
35
+ ## MCP-first Skill Discovery Order (Required)
36
+
37
+ 1. Use `skill_search` first to narrow candidate skills.
38
+ 2. Use `skill_browse_category` second to inspect category-level candidates.
39
+ 3. Use `skill_get` only for final selected skills that must be loaded.
40
+ 4. Keep pointer-first flow; avoid loading full skill text prematurely.
41
+
42
+ ## Skill Log Completion Block (Required)
43
+
44
+ After finishing skill selection/loading, publish:
45
+
46
+ - `selected_skills`: skill IDs selected for the task
47
+ - `loaded_skills`: skill IDs loaded via `skill_get`
48
+ - `skipped_skills`: considered but not loaded
49
+
50
+ ## Context Budget Block (Required, Estimated)
51
+
52
+ Immediately after the Skill Log block, publish estimated budget fields:
53
+
54
+ - `full_catalog_est_tokens`
55
+ - `loaded_est_tokens`
56
+ - `estimated_savings_tokens`
57
+ - `estimated_savings_percent`
58
+
59
+ Mark all context/token values as deterministic estimates (not provider metering).
60
+
35
61
  1. Load only skills needed for the active request.
36
62
  2. Prefer progressive disclosure: start from `SKILL.md`, then specific sections.
37
63
  3. Keep context lean; avoid loading unrelated skill documents.
38
64
  4. If a mapped skill is missing, continue with best fallback and state it.
39
65
 
66
+ After the skill log is complete, append the Context Budget block in the same response/update.
67
+
40
68
  ## 4) Request Classifier
41
69
 
42
70
  1. Question/explanation requests: answer directly.
@@ -32,11 +32,39 @@ Before starting ANY task, the agent MUST:
32
32
 
33
33
  ## 3) Skill Loading Policy
34
34
 
35
+ ## MCP-first Skill Discovery Order (Required)
36
+
37
+ 1. Use `skill_search` first to narrow candidate skills.
38
+ 2. Use `skill_browse_category` second to inspect category-level candidates.
39
+ 3. Use `skill_get` only for final selected skills that must be loaded.
40
+ 4. Keep pointer-first flow; avoid loading full skill text prematurely.
41
+
42
+ ## Skill Log Completion Block (Required)
43
+
44
+ After finishing skill selection/loading, publish:
45
+
46
+ - `selected_skills`: skill IDs selected for the task
47
+ - `loaded_skills`: skill IDs loaded via `skill_get`
48
+ - `skipped_skills`: considered but not loaded
49
+
50
+ ## Context Budget Block (Required, Estimated)
51
+
52
+ Immediately after the Skill Log block, publish estimated budget fields:
53
+
54
+ - `full_catalog_est_tokens`
55
+ - `loaded_est_tokens`
56
+ - `estimated_savings_tokens`
57
+ - `estimated_savings_percent`
58
+
59
+ Mark all context/token values as deterministic estimates (not provider metering).
60
+
35
61
  1. Load only skills needed for the active request.
36
62
  2. Prefer progressive disclosure: start from `SKILL.md`, then specific sections.
37
63
  3. Keep context lean; avoid loading unrelated skill documents.
38
64
  4. If a mapped skill is missing, continue with best fallback and state it.
39
65
 
66
+ After the skill log is complete, append the Context Budget block in the same response/update.
67
+
40
68
  ## 4) Request Classifier
41
69
 
42
70
  1. Question/explanation requests: answer directly.