@tanstack/intent 0.0.6 → 0.0.8

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/index.mjs CHANGED
@@ -1,8 +1,249 @@
1
1
  import { n as parseFrontmatter, t as findSkillFiles } from "./utils-DH3jY3CI.mjs";
2
2
  import { t as scanForIntents } from "./scanner-OmHt14bs.mjs";
3
3
  import { t as checkStaleness } from "./staleness-lP6B0O4z.mjs";
4
- import { c as toMarkdown, i as resolveFrequency, l as validateMetaPayload, n as hasGhCli, o as submitFeedback, r as metaToMarkdown, s as submitMetaFeedback, t as containsSecrets, u as validatePayload } from "./feedback-BTSaBAS_.mjs";
5
- import { a as runInit, i as readProjectConfig, n as hasIntentBlock, o as writeProjectConfig, r as injectIntentBlock, t as detectAgentConfigs } from "./init-DUiDMMrN.mjs";
6
- import { t as runSetup } from "./setup-BvrZZLsA.mjs";
4
+ import { t as runSetup } from "./setup-DGvdyKEq.mjs";
5
+ import { readFileSync, writeFileSync } from "node:fs";
6
+ import { join } from "node:path";
7
+ import { execFileSync, execSync } from "node:child_process";
7
8
 
8
- export { checkStaleness, containsSecrets, detectAgentConfigs, findSkillFiles, hasGhCli, hasIntentBlock, injectIntentBlock, metaToMarkdown, parseFrontmatter, readProjectConfig, resolveFrequency, runInit, runSetup, scanForIntents, submitFeedback, submitMetaFeedback, toMarkdown, validateMetaPayload, validatePayload, writeProjectConfig };
9
+ //#region src/feedback.ts
10
+ const META_FEEDBACK_REPO = "TanStack/intent";
11
+ const SECRET_PATTERNS = [
12
+ /(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{36,}/,
13
+ /(?:sk|pk)[-_](?:live|test)[-_][A-Za-z0-9]{24,}/,
14
+ /AKIA[0-9A-Z]{16}/,
15
+ /-----BEGIN (?:RSA |EC )?PRIVATE KEY-----/,
16
+ /eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/,
17
+ /(?:Bearer|token)\s+[A-Za-z0-9_\-.~+/]{20,}/i,
18
+ /[A-Za-z0-9]{32,}(?=.*(?:key|secret|token|password))/i
19
+ ];
20
+ function containsSecrets(text) {
21
+ return SECRET_PATTERNS.some((pattern) => pattern.test(text));
22
+ }
23
+ function hasGhCli() {
24
+ try {
25
+ execSync("gh --version", { stdio: "ignore" });
26
+ return true;
27
+ } catch {
28
+ return false;
29
+ }
30
+ }
31
+ function getHomeConfigDir() {
32
+ return process.env.XDG_CONFIG_HOME ?? join(process.env.HOME ?? process.env.USERPROFILE ?? "", ".config");
33
+ }
34
+ function resolveFrequency(root) {
35
+ const userConfigPath = join(getHomeConfigDir(), "intent", "config.json");
36
+ try {
37
+ const userCfg = JSON.parse(readFileSync(userConfigPath, "utf8"));
38
+ if (userCfg.feedback?.frequency) return userCfg.feedback.frequency;
39
+ } catch {}
40
+ const projectConfigPath = join(root, "intent.config.json");
41
+ try {
42
+ const projCfg = JSON.parse(readFileSync(projectConfigPath, "utf8"));
43
+ if (projCfg.feedback?.frequency) return projCfg.feedback.frequency;
44
+ } catch {}
45
+ return "every-5";
46
+ }
47
+ const REQUIRED_FIELDS = [
48
+ "skill",
49
+ "package",
50
+ "skillVersion",
51
+ "task",
52
+ "whatWorked",
53
+ "whatFailed",
54
+ "missing",
55
+ "selfCorrections",
56
+ "userRating"
57
+ ];
58
+ function validatePayload(payload) {
59
+ const errors = [];
60
+ if (!payload || typeof payload !== "object") return {
61
+ valid: false,
62
+ errors: ["Payload must be a JSON object"]
63
+ };
64
+ const obj = payload;
65
+ for (const field of REQUIRED_FIELDS) if (typeof obj[field] !== "string" || obj[field].trim() === "") errors.push(`Missing or empty required field: ${field}`);
66
+ if (obj.userRating && ![
67
+ "good",
68
+ "mixed",
69
+ "bad"
70
+ ].includes(obj.userRating)) errors.push("userRating must be one of: good, mixed, bad");
71
+ if (containsSecrets(Object.values(obj).filter((v) => typeof v === "string").join("\n"))) errors.push("Payload appears to contain secrets or tokens — submission rejected");
72
+ return {
73
+ valid: errors.length === 0,
74
+ errors
75
+ };
76
+ }
77
+ const META_REQUIRED_FIELDS = [
78
+ "metaSkill",
79
+ "library",
80
+ "agentUsed",
81
+ "artifactQuality",
82
+ "whatWorked",
83
+ "whatFailed",
84
+ "suggestions",
85
+ "userRating"
86
+ ];
87
+ const VALID_META_SKILLS = [
88
+ "domain-discovery",
89
+ "tree-generator",
90
+ "generate-skill",
91
+ "skill-staleness-check"
92
+ ];
93
+ const VALID_AGENTS = [
94
+ "claude-code",
95
+ "cursor",
96
+ "copilot",
97
+ "codex",
98
+ "other"
99
+ ];
100
+ const VALID_QUALITY_RATINGS = [
101
+ "good",
102
+ "mixed",
103
+ "bad"
104
+ ];
105
+ function validateMetaPayload(payload) {
106
+ const errors = [];
107
+ if (!payload || typeof payload !== "object") return {
108
+ valid: false,
109
+ errors: ["Payload must be a JSON object"]
110
+ };
111
+ const obj = payload;
112
+ for (const field of META_REQUIRED_FIELDS) if (typeof obj[field] !== "string" || obj[field].trim() === "") errors.push(`Missing or empty required field: ${field}`);
113
+ if (obj.metaSkill && !VALID_META_SKILLS.includes(obj.metaSkill)) errors.push(`metaSkill must be one of: ${VALID_META_SKILLS.join(", ")}`);
114
+ if (obj.agentUsed && !VALID_AGENTS.includes(obj.agentUsed)) errors.push(`agentUsed must be one of: ${VALID_AGENTS.join(", ")}`);
115
+ if (obj.artifactQuality && !VALID_QUALITY_RATINGS.includes(obj.artifactQuality)) errors.push("artifactQuality must be one of: good, mixed, bad");
116
+ if (obj.userRating && !VALID_QUALITY_RATINGS.includes(obj.userRating)) errors.push("userRating must be one of: good, mixed, bad");
117
+ if (containsSecrets(Object.values(obj).filter((v) => typeof v === "string").join("\n"))) errors.push("Payload appears to contain secrets or tokens — submission rejected");
118
+ return {
119
+ valid: errors.length === 0,
120
+ errors
121
+ };
122
+ }
123
+ function metaToMarkdown(payload) {
124
+ const lines = [
125
+ `# Meta-Skill Feedback: ${payload.metaSkill}`,
126
+ "",
127
+ `**Library:** ${payload.library}`,
128
+ `**Agent:** ${payload.agentUsed}`,
129
+ `**Artifact quality:** ${payload.artifactQuality}`,
130
+ `**Rating:** ${payload.userRating}`
131
+ ];
132
+ if (payload.interviewQuality) lines.push(`**Interview quality:** ${payload.interviewQuality}`);
133
+ if (payload.failureModeQuality) lines.push(`**Failure mode quality:** ${payload.failureModeQuality}`);
134
+ lines.push("", "## What Worked", payload.whatWorked, "", "## What Failed", payload.whatFailed, "", "## Suggestions", payload.suggestions);
135
+ return lines.join("\n") + "\n";
136
+ }
137
+ function toMarkdown(payload) {
138
+ const lines = [
139
+ `# Skill Feedback: ${payload.skill}`,
140
+ "",
141
+ `**Package:** ${payload.package}`,
142
+ `**Skill version:** ${payload.skillVersion}`,
143
+ `**Rating:** ${payload.userRating}`,
144
+ "",
145
+ "## Task",
146
+ payload.task,
147
+ "",
148
+ "## What Worked",
149
+ payload.whatWorked,
150
+ "",
151
+ "## What Failed",
152
+ payload.whatFailed,
153
+ "",
154
+ "## Missing",
155
+ payload.missing,
156
+ "",
157
+ "## Self-Corrections",
158
+ payload.selfCorrections
159
+ ];
160
+ if (payload.userComments) lines.push("", "## User Comments", payload.userComments);
161
+ return lines.join("\n") + "\n";
162
+ }
163
+ function submitFeedback(payload, repo, opts) {
164
+ const md = toMarkdown(payload);
165
+ if (opts.ghAvailable) try {
166
+ execFileSync("gh", [
167
+ "issue",
168
+ "create",
169
+ "--repo",
170
+ repo,
171
+ "--title",
172
+ `Skill Feedback: ${payload.skill} (${payload.userRating})`,
173
+ "--body",
174
+ "-"
175
+ ], {
176
+ input: md,
177
+ stdio: [
178
+ "pipe",
179
+ "pipe",
180
+ "pipe"
181
+ ]
182
+ });
183
+ return {
184
+ method: "gh",
185
+ detail: `Submitted issue to ${repo}`
186
+ };
187
+ } catch (err) {
188
+ const msg = err instanceof Error ? err.message : String(err);
189
+ console.error(`GitHub submission failed: ${msg}`);
190
+ console.error("Falling back to file output.");
191
+ }
192
+ if (opts.outputPath) {
193
+ writeFileSync(opts.outputPath, md, "utf8");
194
+ return {
195
+ method: "file",
196
+ detail: `Saved to ${opts.outputPath}`
197
+ };
198
+ }
199
+ return {
200
+ method: "stdout",
201
+ detail: md
202
+ };
203
+ }
204
+ function submitMetaFeedback(payload, opts) {
205
+ const md = metaToMarkdown(payload);
206
+ if (opts.ghAvailable) try {
207
+ execFileSync("gh", [
208
+ "issue",
209
+ "create",
210
+ "--repo",
211
+ META_FEEDBACK_REPO,
212
+ "--title",
213
+ `Meta-Skill Feedback: ${payload.metaSkill} (${payload.userRating})`,
214
+ "--label",
215
+ `feedback:${payload.metaSkill}`,
216
+ "--body",
217
+ "-"
218
+ ], {
219
+ input: md,
220
+ stdio: [
221
+ "pipe",
222
+ "pipe",
223
+ "pipe"
224
+ ]
225
+ });
226
+ return {
227
+ method: "gh",
228
+ detail: `Submitted issue to ${META_FEEDBACK_REPO}`
229
+ };
230
+ } catch (err) {
231
+ const msg = err instanceof Error ? err.message : String(err);
232
+ console.error(`GitHub submission failed: ${msg}`);
233
+ console.error("Falling back to file output.");
234
+ }
235
+ if (opts.outputPath) {
236
+ writeFileSync(opts.outputPath, md, "utf8");
237
+ return {
238
+ method: "file",
239
+ detail: `Saved to ${opts.outputPath}`
240
+ };
241
+ }
242
+ return {
243
+ method: "stdout",
244
+ detail: md
245
+ };
246
+ }
247
+
248
+ //#endregion
249
+ export { checkStaleness, containsSecrets, findSkillFiles, hasGhCli, metaToMarkdown, parseFrontmatter, resolveFrequency, runSetup, scanForIntents, submitFeedback, submitMetaFeedback, toMarkdown, validateMetaPayload, validatePayload };
@@ -1,57 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import "./utils-DH3jY3CI.mjs";
3
- import { t as scanLibrary } from "./library-scanner-D0aP7is_.mjs";
3
+ import { n as printSkillTree, r as printTable, t as computeSkillNameWidth } from "./display-D_XzuGnu.mjs";
4
+ import { t as scanLibrary } from "./library-scanner-V9sTOhrb.mjs";
4
5
 
5
6
  //#region src/intent-library.ts
6
- function padColumn(text, width) {
7
- return text.length >= width ? text + " " : text.padEnd(width);
8
- }
9
- function printTable(headers, rows) {
10
- const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length)) + 2);
11
- const headerLine = headers.map((h, i) => padColumn(h, widths[i])).join("");
12
- const separator = widths.map((w) => "─".repeat(w)).join("");
13
- console.log(headerLine);
14
- console.log(separator);
15
- for (const row of rows) console.log(row.map((cell, i) => padColumn(cell, widths[i])).join(""));
16
- }
17
- function printSkillTree(skills, opts) {
18
- const roots = [];
19
- const children = /* @__PURE__ */ new Map();
20
- for (const skill of skills) {
21
- const slashIdx = skill.name.indexOf("/");
22
- if (slashIdx === -1) roots.push(skill.name);
23
- else {
24
- const parent = skill.name.slice(0, slashIdx);
25
- if (!children.has(parent)) children.set(parent, []);
26
- children.get(parent).push(skill);
27
- }
28
- }
29
- if (roots.length === 0) {
30
- for (const skill of skills) if (!roots.includes(skill.name)) roots.push(skill.name);
31
- }
32
- for (const rootName of roots) {
33
- const rootSkill = skills.find((s) => s.name === rootName);
34
- if (!rootSkill) continue;
35
- printSkillLine(rootName, rootSkill, 4, opts);
36
- for (const sub of children.get(rootName) ?? []) printSkillLine(sub.name.slice(sub.name.indexOf("/") + 1), sub, 6, opts);
37
- }
38
- }
39
- function printSkillLine(displayName, skill, indent, opts) {
40
- const nameStr = " ".repeat(indent) + displayName;
41
- const padding = " ".repeat(Math.max(2, opts.nameWidth - nameStr.length));
42
- const typeCol = opts.showTypes ? (skill.type ? `[${skill.type}]` : "").padEnd(14) : "";
43
- console.log(`${nameStr}${padding}${typeCol}${skill.description}`);
44
- }
45
- function computeSkillNameWidth(allPackageSkills) {
46
- let max = 0;
47
- for (const skills of allPackageSkills) for (const s of skills) {
48
- const slashIdx = s.name.indexOf("/");
49
- const displayName = slashIdx === -1 ? s.name : s.name.slice(slashIdx + 1);
50
- const indent = slashIdx === -1 ? 4 : 6;
51
- max = Math.max(max, indent + displayName.length);
52
- }
53
- return max + 2;
54
- }
55
7
  async function cmdList() {
56
8
  let result;
57
9
  try {
@@ -92,8 +44,7 @@ async function cmdList() {
92
44
  }
93
45
  console.log(`Feedback:`);
94
46
  console.log(` After completing your task, collect feedback on skill usage.`);
95
- console.log(` Instructions: node_modules/@tanstack/intent/meta/feedback-collection/SKILL.md`);
96
- console.log(` Submit: npx intent feedback --submit --file intent-feedback.json`);
47
+ console.log(` Load: node_modules/@tanstack/intent/meta/feedback-collection/SKILL.md`);
97
48
  console.log();
98
49
  if (result.warnings.length > 0) {
99
50
  console.log(`Warnings:`);
@@ -1,4 +1,4 @@
1
- import { l as SkillEntry } from "./types-B20LP96b.mjs";
1
+ import { l as SkillEntry } from "./types-BmnI8kFB.mjs";
2
2
 
3
3
  //#region src/library-scanner.d.ts
4
4
  interface LibraryPackage {
@@ -1,4 +1,4 @@
1
1
  import "./utils-DH3jY3CI.mjs";
2
- import { t as scanLibrary } from "./library-scanner-D0aP7is_.mjs";
2
+ import { t as scanLibrary } from "./library-scanner-V9sTOhrb.mjs";
3
3
 
4
4
  export { scanLibrary };
@@ -47,30 +47,47 @@ function copyTemplates(srcDir, destDir, vars) {
47
47
  skipped
48
48
  };
49
49
  }
50
- const SHIM_CONTENT = `#!/usr/bin/env node
50
+ function getShimContent(ext) {
51
+ return `#!/usr/bin/env node
51
52
  // Auto-generated by @tanstack/intent setup
52
53
  // Exposes the intent end-user CLI for consumers of this library.
53
54
  // Commit this file, then add to your package.json:
54
- // "bin": { "intent": "./bin/intent.js" }
55
+ // "bin": { "intent": "./bin/intent.${ext}" }
55
56
  await import('@tanstack/intent/intent-library')
56
57
  `;
58
+ }
59
+ function detectShimExtension(root) {
60
+ try {
61
+ if (JSON.parse(readFileSync(join(root, "package.json"), "utf8")).type === "module") return "js";
62
+ } catch {}
63
+ return "mjs";
64
+ }
65
+ function findExistingShim(root) {
66
+ const shimJs = join(root, "bin", "intent.js");
67
+ if (existsSync(shimJs)) return shimJs;
68
+ const shimMjs = join(root, "bin", "intent.mjs");
69
+ if (existsSync(shimMjs)) return shimMjs;
70
+ return null;
71
+ }
57
72
  function generateShim(root, result) {
58
- const shimPath = join(root, "bin", "intent.js");
59
- if (existsSync(shimPath)) {
60
- result.skipped.push(shimPath);
73
+ const existingShim = findExistingShim(root);
74
+ if (existingShim) {
75
+ result.skipped.push(existingShim);
61
76
  return;
62
77
  }
78
+ const ext = detectShimExtension(root);
79
+ const shimPath = join(root, "bin", `intent.${ext}`);
63
80
  mkdirSync(join(root, "bin"), { recursive: true });
64
- writeFileSync(shimPath, SHIM_CONTENT);
81
+ writeFileSync(shimPath, getShimContent(ext));
65
82
  result.shim = shimPath;
66
83
  }
67
84
  function runSetup(root, metaDir, args) {
68
85
  const doAll = args.includes("--all");
69
86
  const doWorkflows = doAll || args.includes("--workflows");
70
87
  const doShim = doAll || args.includes("--shim");
71
- const defaultAll = !doWorkflows && !doShim;
72
- const installWorkflows = doWorkflows || defaultAll;
73
- const installShim = doShim || defaultAll;
88
+ const noFlagsGiven = !doWorkflows && !doShim;
89
+ const installWorkflows = doWorkflows || noFlagsGiven;
90
+ const installShim = doShim || noFlagsGiven;
74
91
  const vars = detectVars(root);
75
92
  const result = {
76
93
  workflows: [],
@@ -87,10 +104,11 @@ function runSetup(root, metaDir, args) {
87
104
  for (const f of result.workflows) console.log(`✓ Copied workflow: ${f}`);
88
105
  for (const f of result.skipped) console.log(` Already exists: ${f}`);
89
106
  if (result.shim) {
107
+ const shimRelative = result.shim.replace(root + "/", "./");
90
108
  console.log(`✓ Generated intent shim: ${result.shim}`);
91
109
  console.log(`\n Add to your package.json:`);
92
- console.log(` "bin": { "intent": "./bin/intent.js" }`);
93
- console.log(`\n Add bin/intent.js to your package.json "files" array.`);
110
+ console.log(` "bin": { "intent": "${shimRelative}" }`);
111
+ console.log(`\n Add "bin" to your package.json "files" array.`);
94
112
  }
95
113
  if (result.workflows.length === 0 && result.shim === null && result.skipped.length === 0) console.log("No templates directory found. Is @tanstack/intent installed?");
96
114
  else if (result.workflows.length > 0) {
package/dist/setup.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { n as runSetup, t as SetupResult } from "./setup-D2Bwubpw.mjs";
1
+ import { n as runSetup, t as SetupResult } from "./setup-BJ4giTKA.mjs";
2
2
  export { SetupResult, runSetup };
package/dist/setup.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { t as runSetup } from "./setup-BvrZZLsA.mjs";
1
+ import { t as runSetup } from "./setup-DGvdyKEq.mjs";
2
2
 
3
3
  export { runSetup };
@@ -337,7 +337,30 @@ only considers one side.
337
337
  Target 2–4 tensions. If you find none, the skills may be too isolated —
338
338
  revisit whether you're missing cross-connections.
339
339
 
340
- ### 3f — Identify gaps
340
+ ### 3f — Map cross-references
341
+
342
+ Beyond tensions (conflicts) and shared failure modes, identify skills
343
+ that illuminate each other without conflicting. A cross-reference means:
344
+ "an agent loading skill A would produce better code if it knew about
345
+ skill B." These become "See also" pointers in the generated SKILL.md
346
+ files.
347
+
348
+ For each pair, note:
349
+
350
+ - Which skill references which (can be bidirectional)
351
+ - Why awareness of the other skill improves output
352
+
353
+ Examples:
354
+
355
+ - A quickstart skill references the security checklist ("after setup, audit")
356
+ - A state management skill references an SSR skill ("state hydration
357
+ requires understanding SSR lifecycle")
358
+ - A data writing skill references a data reading skill ("writes affect
359
+ how queries invalidate")
360
+
361
+ Output these in the `cross_references` section of domain_map.yaml.
362
+
363
+ ### 3g — Identify gaps
341
364
 
342
365
  For each skill, explicitly list what you could NOT determine from docs
343
366
  and source alone. These become interview questions in Phase 4.
@@ -351,7 +374,7 @@ Common gaps:
351
374
  - "GitHub issues show confusion about X but docs don't address it"
352
375
  - "I found two patterns for doing X — unclear which is current/preferred"
353
376
 
354
- ### 3g — Discover composition targets
377
+ ### 3h — Discover composition targets
355
378
 
356
379
  Scan `package.json` for peer dependencies, optional dependencies, and
357
380
  `peerDependenciesMeta`. Scan example directories and integration tests
@@ -363,7 +386,7 @@ for import patterns. For each frequently co-used library, log:
363
386
 
364
387
  These become targeted composition questions in Phase 4e.
365
388
 
366
- ### 3h — Produce the draft
389
+ ### 3i — Produce the draft
367
390
 
368
391
  Write the full `domain_map.yaml` (format in Output Artifacts below) with
369
392
  a `status: draft` field. Flag every gap in the `gaps` section.
@@ -405,7 +428,7 @@ Follow up on any corrections. Then:
405
428
 
406
429
  ### 4b — Gap-targeted questions (3–8 questions)
407
430
 
408
- For each gap flagged in Phase 3f, ask a specific question. These are not
431
+ For each gap flagged in Phase 3g, ask a specific question. These are not
409
432
  generic — they reference what you found:
410
433
 
411
434
  **Instead of:** "What do developers get wrong?"
@@ -472,7 +495,7 @@ These surface knowledge that doesn't appear in any docs:
472
495
 
473
496
  ### 4e — Composition questions (if library interacts with others)
474
497
 
475
- Use what you discovered in Phase 3g. For each integration target
498
+ Use what you discovered in Phase 3h. For each integration target
476
499
  identified from peer dependencies and example code, ask targeted
477
500
  questions:
478
501
 
@@ -573,6 +596,11 @@ tensions:
573
596
  description: '[what conflicts — one sentence]'
574
597
  implication: '[what an agent gets wrong when it only considers one side]'
575
598
 
599
+ cross_references:
600
+ - from: '[skill-slug]'
601
+ to: '[skill-slug]'
602
+ reason: '[why loading one skill benefits from awareness of the other]'
603
+
576
604
  gaps:
577
605
  - skill: '[skill slug]'
578
606
  question: '[what still needs input]'
@@ -618,6 +646,12 @@ not promotional.]
618
646
  | -------------- | ------------------- | ----------------------- |
619
647
  | [short phrase] | [slug-a] ↔ [slug-b] | [what agents get wrong] |
620
648
 
649
+ ## Cross-References
650
+
651
+ | From | To | Reason |
652
+ | ------ | ------ | ----------------------------------------- |
653
+ | [slug] | [slug] | [why awareness of one improves the other] |
654
+
621
655
  ## Subsystems & Reference Candidates
622
656
 
623
657
  | Skill | Subsystems | Reference candidates |
@@ -672,6 +706,7 @@ not promotional.]
672
706
  | Subsystems flagged | Skills with 3+ adapters/backends list them as subsystems |
673
707
  | Dense surfaces flagged | Topics with >10 patterns noted as reference_candidates |
674
708
  | Lifecycle skills considered | Suggest journey skills when docs have the material |
709
+ | Cross-references mapped | Skills that illuminate each other get "See also" pointers |
675
710
 
676
711
  ---
677
712
 
@@ -4,12 +4,11 @@ description: >
4
4
  Collect structured feedback about skill usage after completing a coding task.
5
5
  Activate at the end of any session where one or more SKILL.md files were
6
6
  loaded. Captures agent signals (gaps, errors, corrections, human interventions)
7
- and brief human input, then submits via the intent feedback CLI.
7
+ and brief human input, then submits directly via gh CLI or provides manual
8
+ submission instructions.
8
9
  metadata:
9
- version: '1.0'
10
+ version: '2.0'
10
11
  category: meta-tooling
11
- output_artifacts:
12
- - intent-feedback.json
13
12
  ---
14
13
 
15
14
  # Skill Feedback Collection
@@ -83,74 +82,86 @@ If the human gives an explicit rating, use that instead.
83
82
 
84
83
  ---
85
84
 
86
- ## Phase 3 — Build the Payload
87
-
88
- Construct one JSON payload per skill used. The schema must match exactly:
89
-
90
- ```json
91
- {
92
- "skill": "<skill name from SKILL.md frontmatter>",
93
- "package": "<npm package name that contains the skill>",
94
- "skillVersion": "<metadata.version from SKILL.md frontmatter, or library_version>",
95
- "task": "<one-sentence summary of what the human asked you to do>",
96
- "whatWorked": "<patterns/instructions from the skill that were accurate and helpful>",
97
- "whatFailed": "<errors from 1b — what the skill got wrong>",
98
- "missing": "<gaps from 1a — what the skill should have covered>",
99
- "selfCorrections": "<fixes you applied from 1b + interventions from 1c>",
100
- "userRating": "good | mixed | bad",
101
- "userComments": "<optional — direct quotes or paraphrased human input from Phase 2>"
102
- }
103
- ```
85
+ ## Phase 3 — Build the Feedback
104
86
 
105
- ### Field derivation guide
87
+ Write one Markdown feedback file per skill used. Only include skills that were
88
+ actually used during the session — skip any that were loaded but never
89
+ referenced.
90
+
91
+ ### Template
92
+
93
+ ```markdown
94
+ # Skill Feedback: [skill name from SKILL.md frontmatter]
95
+
96
+ **Package:** [npm package name that contains the skill]
97
+ **Skill version:** [metadata.version or library_version from frontmatter]
98
+ **Rating:** [good | mixed | bad]
99
+
100
+ ## Task
101
+
102
+ [one-sentence summary of what the human asked you to do]
103
+
104
+ ## What Worked
105
+
106
+ [patterns/instructions from the skill that were accurate and helpful]
107
+
108
+ ## What Failed
109
+
110
+ [from 1b — skill instructions that produced errors]
111
+
112
+ ## Missing
106
113
 
107
- | Field | Source |
108
- | ----------------- | ------------------------------------------------------------------ |
109
- | `skill` | Frontmatter `name` field of the SKILL.md you loaded |
110
- | `package` | The npm package the skill lives in (e.g. `@tanstack/query-intent`) |
111
- | `skillVersion` | Frontmatter `metadata.version` or `library_version` |
112
- | `task` | Summarize the human's original request in one sentence |
113
- | `whatWorked` | List skill sections/patterns that were correct and useful |
114
- | `whatFailed` | From 1b — skill instructions that produced errors |
115
- | `missing` | From 1a gaps where the skill was silent |
116
- | `selfCorrections` | From 1b fixes + 1c human interventions, combined |
117
- | `userRating` | From Phase 2 sentiment analysis or explicit rating |
118
- | `userComments` | From Phase 2 answers, keep brief |
119
-
120
- ### Example
121
-
122
- ```json
123
- {
124
- "skill": "tanstack-query/core",
125
- "package": "@anthropic/tanstack-query-intent",
126
- "skillVersion": "1.0",
127
- "task": "Add optimistic updates to a mutation with rollback on error",
128
- "whatWorked": "Setup pattern was correct. onMutate/onError/onSettled lifecycle was accurate.",
129
- "whatFailed": "Cache key format used array syntax that doesn't match v5 — had to switch to queryOptions pattern.",
130
- "missing": "No guidance on TypeScript generics for mutation variables. Had to read source.",
131
- "selfCorrections": "Fixed cache key format. Human corrected the rollback type to include undefined.",
132
- "userRating": "mixed",
133
- "userComments": "The optimistic update pattern was helpful but the cache key thing wasted 10 minutes."
134
- }
114
+ [from 1a — gaps where the skill should have covered]
115
+
116
+ ## Self-Corrections
117
+
118
+ [from 1b fixes + 1c human interventions, combined]
119
+
120
+ ## User Comments
121
+
122
+ [optional direct quotes or paraphrased human input from Phase 2]
135
123
  ```
136
124
 
137
- If multiple skills were loaded, produce one payload per skill. Only include
138
- skills that were actually used during the session — skip any that were loaded
139
- but never referenced.
125
+ ### Field derivation guide
126
+
127
+ | Field | Source |
128
+ | ---------------- | ------------------------------------------------------------------ |
129
+ | Skill name | Frontmatter `name` field of the SKILL.md you loaded |
130
+ | Package | The npm package the skill lives in (e.g. `@tanstack/query-intent`) |
131
+ | Skill version | Frontmatter `metadata.version` or `library_version` |
132
+ | Task | Summarize the human's original request in one sentence |
133
+ | What Worked | List skill sections/patterns that were correct and useful |
134
+ | What Failed | From 1b — skill instructions that produced errors |
135
+ | Missing | From 1a — gaps where the skill was silent |
136
+ | Self-Corrections | From 1b fixes + 1c human interventions, combined |
137
+ | Rating | From Phase 2 sentiment analysis or explicit rating |
138
+ | User Comments | From Phase 2 answers, keep brief |
140
139
 
141
140
  ---
142
141
 
143
142
  ## Phase 4 — Submit
144
143
 
145
- 1. Save the payload to `intent-feedback.json` in the project root.
146
- If multiple skills, save as a JSON array.
144
+ Determine the target repo from the skill's package. The repo is typically
145
+ derivable from the `repository` field in the package's `package.json`, or
146
+ from the `sources` field in the SKILL.md frontmatter.
147
+
148
+ ### If `gh` CLI is available
149
+
150
+ Submit directly as a GitHub issue:
151
+
152
+ ```bash
153
+ gh issue create --repo [owner/repo] --title "Skill Feedback: [skill-name] ([rating])" --label "feedback:[skill-name]" --body-file intent-feedback.md
154
+ ```
155
+
156
+ If the label doesn't exist, omit the `--label` flag — don't let a missing
157
+ label block submission.
147
158
 
148
- 2. Submit:
159
+ If submission succeeds, delete the feedback file.
149
160
 
150
- ```bash
151
- npx intent feedback --submit --file intent-feedback.json
152
- ```
161
+ ### If `gh` CLI is not available
153
162
 
154
- 3. If the submission succeeds, delete `intent-feedback.json`.
163
+ Tell the human:
155
164
 
156
- 4. If it fails, tell the human and leave the file for manual retry.
165
+ > "I've written skill feedback to `intent-feedback.md`. To submit it,
166
+ > open an issue at https://github.com/[owner/repo]/issues and paste the
167
+ > contents."