@tanstack/intent 0.0.5 → 0.0.7
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 +11 -12
- package/dist/cli.mjs +161 -135
- package/dist/display-D_XzuGnu.mjs +54 -0
- package/dist/index.d.mts +3 -17
- package/dist/index.mjs +248 -7
- package/dist/intent-library.mjs +7 -51
- package/dist/{library-scanner-BrznE00j.mjs → library-scanner-V9sTOhrb.mjs} +5 -2
- package/dist/library-scanner.d.mts +1 -1
- package/dist/library-scanner.mjs +2 -2
- package/dist/scanner-DkShtCWX.mjs +4 -0
- package/dist/{scanner-CpsJAHXT.mjs → scanner-OmHt14bs.mjs} +41 -8
- package/dist/{setup-N5dttGp_.d.mts → setup-BJ4giTKA.d.mts} +0 -1
- package/dist/{setup-CNGz26qL.mjs → setup-DGvdyKEq.mjs} +31 -22
- package/dist/setup.d.mts +1 -1
- package/dist/setup.mjs +1 -1
- package/dist/staleness-B5gUj7FR.mjs +4 -0
- package/dist/{staleness-CnomT9Hm.mjs → staleness-lP6B0O4z.mjs} +1 -1
- package/dist/{types-kbQfN_is.d.mts → types-BmnI8kFB.d.mts} +1 -1
- package/dist/{utils-DjkEPBxu.mjs → utils-DH3jY3CI.mjs} +1 -1
- package/meta/domain-discovery/SKILL.md +90 -7
- package/meta/feedback-collection/SKILL.md +73 -62
- package/meta/generate-skill/SKILL.md +9 -0
- package/meta/tree-generator/SKILL.md +16 -2
- package/package.json +1 -1
- package/dist/feedback-DKreHfB1.mjs +0 -300
- package/dist/feedback-FIUBOL0g.mjs +0 -3
- package/dist/init-DEzzXm9j.mjs +0 -3
- package/dist/init-DNxmjQfU.mjs +0 -70
- package/dist/scanner-BuWPDJ4P.mjs +0 -4
- package/dist/staleness-DyhsrqQ5.mjs +0 -4
|
@@ -1,300 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { execSync } from "node:child_process";
|
|
4
|
-
|
|
5
|
-
//#region src/feedback.ts
|
|
6
|
-
const META_FEEDBACK_REPO = "TanStack/intent";
|
|
7
|
-
const SECRET_PATTERNS = [
|
|
8
|
-
/(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{36,}/,
|
|
9
|
-
/(?:sk|pk)[-_](?:live|test)[-_][A-Za-z0-9]{24,}/,
|
|
10
|
-
/AKIA[0-9A-Z]{16}/,
|
|
11
|
-
/-----BEGIN (?:RSA |EC )?PRIVATE KEY-----/,
|
|
12
|
-
/eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/,
|
|
13
|
-
/(?:Bearer|token)\s+[A-Za-z0-9_\-.~+/]{20,}/i,
|
|
14
|
-
/[A-Za-z0-9]{32,}(?=.*(?:key|secret|token|password))/i
|
|
15
|
-
];
|
|
16
|
-
function containsSecrets(text) {
|
|
17
|
-
return SECRET_PATTERNS.some((pattern) => pattern.test(text));
|
|
18
|
-
}
|
|
19
|
-
function hasGhCli() {
|
|
20
|
-
try {
|
|
21
|
-
execSync("gh --version", { stdio: "ignore" });
|
|
22
|
-
return true;
|
|
23
|
-
} catch {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
function getHomeConfigDir() {
|
|
28
|
-
return process.env.XDG_CONFIG_HOME ?? join(process.env.HOME ?? process.env.USERPROFILE ?? "", ".config");
|
|
29
|
-
}
|
|
30
|
-
function resolveFrequency(root) {
|
|
31
|
-
const userConfigPath = join(getHomeConfigDir(), "intent", "config.json");
|
|
32
|
-
try {
|
|
33
|
-
const userCfg = JSON.parse(readFileSync(userConfigPath, "utf8"));
|
|
34
|
-
if (userCfg.feedback?.frequency) return userCfg.feedback.frequency;
|
|
35
|
-
} catch {}
|
|
36
|
-
const projectConfigPath = join(root, "intent.config.json");
|
|
37
|
-
try {
|
|
38
|
-
const projCfg = JSON.parse(readFileSync(projectConfigPath, "utf8"));
|
|
39
|
-
if (projCfg.feedback?.frequency) return projCfg.feedback.frequency;
|
|
40
|
-
} catch {}
|
|
41
|
-
return "every-5";
|
|
42
|
-
}
|
|
43
|
-
const REQUIRED_FIELDS = [
|
|
44
|
-
"skill",
|
|
45
|
-
"package",
|
|
46
|
-
"skillVersion",
|
|
47
|
-
"task",
|
|
48
|
-
"whatWorked",
|
|
49
|
-
"whatFailed",
|
|
50
|
-
"missing",
|
|
51
|
-
"selfCorrections",
|
|
52
|
-
"userRating"
|
|
53
|
-
];
|
|
54
|
-
function validatePayload(payload) {
|
|
55
|
-
const errors = [];
|
|
56
|
-
if (!payload || typeof payload !== "object") return {
|
|
57
|
-
valid: false,
|
|
58
|
-
errors: ["Payload must be a JSON object"]
|
|
59
|
-
};
|
|
60
|
-
const obj = payload;
|
|
61
|
-
for (const field of REQUIRED_FIELDS) if (typeof obj[field] !== "string" || obj[field].trim() === "") errors.push(`Missing or empty required field: ${field}`);
|
|
62
|
-
if (obj.userRating && ![
|
|
63
|
-
"good",
|
|
64
|
-
"mixed",
|
|
65
|
-
"bad"
|
|
66
|
-
].includes(obj.userRating)) errors.push("userRating must be one of: good, mixed, bad");
|
|
67
|
-
if (containsSecrets(Object.values(obj).filter((v) => typeof v === "string").join("\n"))) errors.push("Payload appears to contain secrets or tokens — submission rejected");
|
|
68
|
-
return {
|
|
69
|
-
valid: errors.length === 0,
|
|
70
|
-
errors
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
const META_REQUIRED_FIELDS = [
|
|
74
|
-
"metaSkill",
|
|
75
|
-
"library",
|
|
76
|
-
"agentUsed",
|
|
77
|
-
"artifactQuality",
|
|
78
|
-
"whatWorked",
|
|
79
|
-
"whatFailed",
|
|
80
|
-
"suggestions",
|
|
81
|
-
"userRating"
|
|
82
|
-
];
|
|
83
|
-
const VALID_META_SKILLS = [
|
|
84
|
-
"domain-discovery",
|
|
85
|
-
"tree-generator",
|
|
86
|
-
"generate-skill",
|
|
87
|
-
"skill-staleness-check"
|
|
88
|
-
];
|
|
89
|
-
const VALID_AGENTS = [
|
|
90
|
-
"oz",
|
|
91
|
-
"claude-code",
|
|
92
|
-
"cursor",
|
|
93
|
-
"copilot",
|
|
94
|
-
"codex",
|
|
95
|
-
"other"
|
|
96
|
-
];
|
|
97
|
-
const VALID_QUALITY_RATINGS = [
|
|
98
|
-
"good",
|
|
99
|
-
"mixed",
|
|
100
|
-
"bad"
|
|
101
|
-
];
|
|
102
|
-
function validateMetaPayload(payload) {
|
|
103
|
-
const errors = [];
|
|
104
|
-
if (!payload || typeof payload !== "object") return {
|
|
105
|
-
valid: false,
|
|
106
|
-
errors: ["Payload must be a JSON object"]
|
|
107
|
-
};
|
|
108
|
-
const obj = payload;
|
|
109
|
-
for (const field of META_REQUIRED_FIELDS) if (typeof obj[field] !== "string" || obj[field].trim() === "") errors.push(`Missing or empty required field: ${field}`);
|
|
110
|
-
if (obj.metaSkill && !VALID_META_SKILLS.includes(obj.metaSkill)) errors.push(`metaSkill must be one of: ${VALID_META_SKILLS.join(", ")}`);
|
|
111
|
-
if (obj.agentUsed && !VALID_AGENTS.includes(obj.agentUsed)) errors.push(`agentUsed must be one of: ${VALID_AGENTS.join(", ")}`);
|
|
112
|
-
if (obj.artifactQuality && !VALID_QUALITY_RATINGS.includes(obj.artifactQuality)) errors.push("artifactQuality must be one of: good, mixed, bad");
|
|
113
|
-
if (obj.userRating && !VALID_QUALITY_RATINGS.includes(obj.userRating)) errors.push("userRating must be one of: good, mixed, bad");
|
|
114
|
-
if (containsSecrets(Object.values(obj).filter((v) => typeof v === "string").join("\n"))) errors.push("Payload appears to contain secrets or tokens — submission rejected");
|
|
115
|
-
return {
|
|
116
|
-
valid: errors.length === 0,
|
|
117
|
-
errors
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
function metaToMarkdown(payload) {
|
|
121
|
-
const lines = [
|
|
122
|
-
`# Meta-Skill Feedback: ${payload.metaSkill}`,
|
|
123
|
-
"",
|
|
124
|
-
`**Library:** ${payload.library}`,
|
|
125
|
-
`**Agent:** ${payload.agentUsed}`,
|
|
126
|
-
`**Artifact quality:** ${payload.artifactQuality}`,
|
|
127
|
-
`**Rating:** ${payload.userRating}`
|
|
128
|
-
];
|
|
129
|
-
if (payload.interviewQuality) lines.push(`**Interview quality:** ${payload.interviewQuality}`);
|
|
130
|
-
if (payload.failureModeQuality) lines.push(`**Failure mode quality:** ${payload.failureModeQuality}`);
|
|
131
|
-
lines.push("", "## What Worked", payload.whatWorked, "", "## What Failed", payload.whatFailed, "", "## Suggestions", payload.suggestions);
|
|
132
|
-
return lines.join("\n") + "\n";
|
|
133
|
-
}
|
|
134
|
-
function toMarkdown(payload) {
|
|
135
|
-
const lines = [
|
|
136
|
-
`# Skill Feedback: ${payload.skill}`,
|
|
137
|
-
"",
|
|
138
|
-
`**Package:** ${payload.package}`,
|
|
139
|
-
`**Skill version:** ${payload.skillVersion}`,
|
|
140
|
-
`**Rating:** ${payload.userRating}`,
|
|
141
|
-
"",
|
|
142
|
-
"## Task",
|
|
143
|
-
payload.task,
|
|
144
|
-
"",
|
|
145
|
-
"## What Worked",
|
|
146
|
-
payload.whatWorked,
|
|
147
|
-
"",
|
|
148
|
-
"## What Failed",
|
|
149
|
-
payload.whatFailed,
|
|
150
|
-
"",
|
|
151
|
-
"## Missing",
|
|
152
|
-
payload.missing,
|
|
153
|
-
"",
|
|
154
|
-
"## Self-Corrections",
|
|
155
|
-
payload.selfCorrections
|
|
156
|
-
];
|
|
157
|
-
if (payload.userComments) lines.push("", "## User Comments", payload.userComments);
|
|
158
|
-
return lines.join("\n") + "\n";
|
|
159
|
-
}
|
|
160
|
-
function submitFeedback(payload, repo, opts) {
|
|
161
|
-
const md = toMarkdown(payload);
|
|
162
|
-
if (opts.ghAvailable) try {
|
|
163
|
-
execSync(`gh issue create --repo ${repo} --title "${`Skill Feedback: ${payload.skill} (${payload.userRating})`.replace(/"/g, "\\\"")}" --body -`, {
|
|
164
|
-
input: md,
|
|
165
|
-
stdio: [
|
|
166
|
-
"pipe",
|
|
167
|
-
"pipe",
|
|
168
|
-
"pipe"
|
|
169
|
-
]
|
|
170
|
-
});
|
|
171
|
-
return {
|
|
172
|
-
method: "gh",
|
|
173
|
-
detail: `Submitted issue to ${repo}`
|
|
174
|
-
};
|
|
175
|
-
} catch {}
|
|
176
|
-
if (opts.outputPath) {
|
|
177
|
-
writeFileSync(opts.outputPath, md, "utf8");
|
|
178
|
-
return {
|
|
179
|
-
method: "file",
|
|
180
|
-
detail: `Saved to ${opts.outputPath}`
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
return {
|
|
184
|
-
method: "stdout",
|
|
185
|
-
detail: md
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
function submitMetaFeedback(payload, opts) {
|
|
189
|
-
const md = metaToMarkdown(payload);
|
|
190
|
-
if (opts.ghAvailable) try {
|
|
191
|
-
execSync(`gh issue create --repo ${META_FEEDBACK_REPO} --title "${`Meta-Skill Feedback: ${payload.metaSkill} (${payload.userRating})`.replace(/"/g, "\\\"")}" --label "feedback:${payload.metaSkill}" --body -`, {
|
|
192
|
-
input: md,
|
|
193
|
-
stdio: [
|
|
194
|
-
"pipe",
|
|
195
|
-
"pipe",
|
|
196
|
-
"pipe"
|
|
197
|
-
]
|
|
198
|
-
});
|
|
199
|
-
return {
|
|
200
|
-
method: "gh",
|
|
201
|
-
detail: `Submitted issue to ${META_FEEDBACK_REPO}`
|
|
202
|
-
};
|
|
203
|
-
} catch {}
|
|
204
|
-
if (opts.outputPath) {
|
|
205
|
-
writeFileSync(opts.outputPath, md, "utf8");
|
|
206
|
-
return {
|
|
207
|
-
method: "file",
|
|
208
|
-
detail: `Saved to ${opts.outputPath}`
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
return {
|
|
212
|
-
method: "stdout",
|
|
213
|
-
detail: md
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
function runFeedback(args) {
|
|
217
|
-
const isMeta = args.includes("--meta");
|
|
218
|
-
const submitFlag = args.includes("--submit");
|
|
219
|
-
const fileIdx = args.indexOf("--file");
|
|
220
|
-
const filePath = fileIdx !== -1 ? args[fileIdx + 1] : void 0;
|
|
221
|
-
if (!submitFlag || !filePath) {
|
|
222
|
-
if (isMeta) console.error("Usage: intent feedback --meta --submit --file <path>");
|
|
223
|
-
else console.error("Usage: intent feedback --submit --file <path>");
|
|
224
|
-
process.exit(1);
|
|
225
|
-
}
|
|
226
|
-
if (!existsSync(filePath)) {
|
|
227
|
-
console.error(`File not found: ${filePath}`);
|
|
228
|
-
process.exit(1);
|
|
229
|
-
}
|
|
230
|
-
let raw;
|
|
231
|
-
try {
|
|
232
|
-
raw = JSON.parse(readFileSync(filePath, "utf8"));
|
|
233
|
-
} catch {
|
|
234
|
-
console.error("Invalid JSON in feedback file");
|
|
235
|
-
process.exit(1);
|
|
236
|
-
}
|
|
237
|
-
const ghAvailable = hasGhCli();
|
|
238
|
-
if (resolveFrequency(process.cwd()) === "never") {
|
|
239
|
-
console.log("Feedback is disabled (frequency: never)");
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
const dateSuffix = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
243
|
-
if (isMeta) {
|
|
244
|
-
const validation$1 = validateMetaPayload(raw);
|
|
245
|
-
if (!validation$1.valid) {
|
|
246
|
-
console.error("Meta-feedback validation failed:");
|
|
247
|
-
for (const err of validation$1.errors) console.error(` - ${err}`);
|
|
248
|
-
process.exit(1);
|
|
249
|
-
}
|
|
250
|
-
const payload$1 = raw;
|
|
251
|
-
const fallbackPath$1 = `intent-meta-feedback-${dateSuffix}.md`;
|
|
252
|
-
const result$1 = submitMetaFeedback(payload$1, {
|
|
253
|
-
ghAvailable,
|
|
254
|
-
outputPath: ghAvailable ? void 0 : fallbackPath$1
|
|
255
|
-
});
|
|
256
|
-
switch (result$1.method) {
|
|
257
|
-
case "gh":
|
|
258
|
-
console.log(`✓ ${result$1.detail}`);
|
|
259
|
-
break;
|
|
260
|
-
case "file":
|
|
261
|
-
console.log(`✓ ${result$1.detail}`);
|
|
262
|
-
console.log(`Open a GitHub Discussion at https://github.com/${META_FEEDBACK_REPO}/discussions/new?category=Feedback`);
|
|
263
|
-
break;
|
|
264
|
-
case "stdout":
|
|
265
|
-
console.log("--- Meta-feedback markdown (copy/paste to discussion) ---");
|
|
266
|
-
console.log(result$1.detail);
|
|
267
|
-
break;
|
|
268
|
-
}
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
const validation = validatePayload(raw);
|
|
272
|
-
if (!validation.valid) {
|
|
273
|
-
console.error("Feedback validation failed:");
|
|
274
|
-
for (const err of validation.errors) console.error(` - ${err}`);
|
|
275
|
-
process.exit(1);
|
|
276
|
-
}
|
|
277
|
-
const payload = raw;
|
|
278
|
-
const repo = payload.package.replace(/^@/, "").replace(/\//, "/");
|
|
279
|
-
const fallbackPath = `intent-feedback-${dateSuffix}.md`;
|
|
280
|
-
const result = submitFeedback(payload, repo, {
|
|
281
|
-
ghAvailable,
|
|
282
|
-
outputPath: ghAvailable ? void 0 : fallbackPath
|
|
283
|
-
});
|
|
284
|
-
switch (result.method) {
|
|
285
|
-
case "gh":
|
|
286
|
-
console.log(`✓ ${result.detail}`);
|
|
287
|
-
break;
|
|
288
|
-
case "file":
|
|
289
|
-
console.log(`✓ ${result.detail}`);
|
|
290
|
-
console.log("You can manually open an issue with this content.");
|
|
291
|
-
break;
|
|
292
|
-
case "stdout":
|
|
293
|
-
console.log("--- Feedback markdown (copy/paste to issue) ---");
|
|
294
|
-
console.log(result.detail);
|
|
295
|
-
break;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
//#endregion
|
|
300
|
-
export { runFeedback as a, toMarkdown as c, resolveFrequency as i, validateMetaPayload as l, hasGhCli as n, submitFeedback as o, metaToMarkdown as r, submitMetaFeedback as s, containsSecrets as t, validatePayload as u };
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import { a as runFeedback, 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-DKreHfB1.mjs";
|
|
2
|
-
|
|
3
|
-
export { runFeedback };
|
package/dist/init-DEzzXm9j.mjs
DELETED
package/dist/init-DNxmjQfU.mjs
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
|
|
4
|
-
//#region src/init.ts
|
|
5
|
-
const AGENT_CONFIG_FILES = [
|
|
6
|
-
"AGENTS.md",
|
|
7
|
-
"CLAUDE.md",
|
|
8
|
-
".cursorrules",
|
|
9
|
-
".github/copilot-instructions.md"
|
|
10
|
-
];
|
|
11
|
-
const INTENT_BLOCK_MARKER = "## Intent Skills";
|
|
12
|
-
const INTENT_BLOCK = `## Intent Skills
|
|
13
|
-
|
|
14
|
-
This project uses TanStack Intent. Run \`npx intent list\` to discover
|
|
15
|
-
available AI coding skills. Before working with a library that has skills,
|
|
16
|
-
read the relevant SKILL.md file at the path shown in the list output.
|
|
17
|
-
`;
|
|
18
|
-
const DEFAULT_CONFIG = { feedback: { frequency: "every-5" } };
|
|
19
|
-
function detectAgentConfigs(root) {
|
|
20
|
-
return AGENT_CONFIG_FILES.map((f) => join(root, f)).filter((f) => existsSync(f));
|
|
21
|
-
}
|
|
22
|
-
function hasIntentBlock(filePath) {
|
|
23
|
-
try {
|
|
24
|
-
return readFileSync(filePath, "utf8").includes(INTENT_BLOCK_MARKER);
|
|
25
|
-
} catch {
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
function injectIntentBlock(filePath) {
|
|
30
|
-
if (hasIntentBlock(filePath)) return false;
|
|
31
|
-
let content;
|
|
32
|
-
try {
|
|
33
|
-
content = readFileSync(filePath, "utf8");
|
|
34
|
-
} catch {
|
|
35
|
-
content = "";
|
|
36
|
-
}
|
|
37
|
-
const separator = content.length > 0 && !content.endsWith("\n\n") ? "\n\n" : "";
|
|
38
|
-
writeFileSync(filePath, content + separator + INTENT_BLOCK);
|
|
39
|
-
return true;
|
|
40
|
-
}
|
|
41
|
-
function writeProjectConfig(root) {
|
|
42
|
-
const configPath = join(root, "intent.config.json");
|
|
43
|
-
if (!existsSync(configPath)) writeFileSync(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n");
|
|
44
|
-
return configPath;
|
|
45
|
-
}
|
|
46
|
-
function readProjectConfig(root) {
|
|
47
|
-
const configPath = join(root, "intent.config.json");
|
|
48
|
-
try {
|
|
49
|
-
return JSON.parse(readFileSync(configPath, "utf8"));
|
|
50
|
-
} catch {
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
function runInit(root) {
|
|
55
|
-
const detected = detectAgentConfigs(root);
|
|
56
|
-
const injected = [];
|
|
57
|
-
const skipped = [];
|
|
58
|
-
const created = [];
|
|
59
|
-
for (const filePath of detected) if (injectIntentBlock(filePath)) injected.push(filePath);
|
|
60
|
-
else skipped.push(filePath);
|
|
61
|
-
return {
|
|
62
|
-
injected,
|
|
63
|
-
skipped,
|
|
64
|
-
created,
|
|
65
|
-
configPath: writeProjectConfig(root)
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
//#endregion
|
|
70
|
-
export { runInit as a, readProjectConfig as i, hasIntentBlock as n, writeProjectConfig as o, injectIntentBlock as r, detectAgentConfigs as t };
|