@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
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,249 @@
|
|
|
1
|
-
import { n as parseFrontmatter, t as findSkillFiles } from "./utils-
|
|
2
|
-
import { t as scanForIntents } from "./scanner-
|
|
3
|
-
import { t as checkStaleness } from "./staleness-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import { n as parseFrontmatter, t as findSkillFiles } from "./utils-DH3jY3CI.mjs";
|
|
2
|
+
import { t as scanForIntents } from "./scanner-OmHt14bs.mjs";
|
|
3
|
+
import { t as checkStaleness } from "./staleness-lP6B0O4z.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
|
-
|
|
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 };
|
package/dist/intent-library.mjs
CHANGED
|
@@ -1,57 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./utils-
|
|
3
|
-
import { t as
|
|
2
|
+
import "./utils-DH3jY3CI.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 {
|
|
@@ -90,6 +42,10 @@ async function cmdList() {
|
|
|
90
42
|
});
|
|
91
43
|
console.log();
|
|
92
44
|
}
|
|
45
|
+
console.log(`Feedback:`);
|
|
46
|
+
console.log(` After completing your task, collect feedback on skill usage.`);
|
|
47
|
+
console.log(` Load: node_modules/@tanstack/intent/meta/feedback-collection/SKILL.md`);
|
|
48
|
+
console.log();
|
|
93
49
|
if (result.warnings.length > 0) {
|
|
94
50
|
console.log(`Warnings:`);
|
|
95
51
|
for (const w of result.warnings) console.log(` ⚠ ${w}`);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as parseFrontmatter } from "./utils-
|
|
1
|
+
import { n as parseFrontmatter } from "./utils-DH3jY3CI.mjs";
|
|
2
2
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
3
3
|
import { dirname, join, relative, sep } from "node:path";
|
|
4
4
|
|
|
@@ -37,7 +37,10 @@ function discoverSkills(skillsDir) {
|
|
|
37
37
|
function walk(dir) {
|
|
38
38
|
let entries;
|
|
39
39
|
try {
|
|
40
|
-
entries = readdirSync(dir, {
|
|
40
|
+
entries = readdirSync(dir, {
|
|
41
|
+
withFileTypes: true,
|
|
42
|
+
encoding: "utf8"
|
|
43
|
+
});
|
|
41
44
|
} catch {
|
|
42
45
|
return;
|
|
43
46
|
}
|
package/dist/library-scanner.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as parseFrontmatter } from "./utils-
|
|
1
|
+
import { n as parseFrontmatter } from "./utils-DH3jY3CI.mjs";
|
|
2
2
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
3
3
|
import { join, relative, sep } from "node:path";
|
|
4
4
|
|
|
@@ -12,7 +12,7 @@ function detectPackageManager(root) {
|
|
|
12
12
|
if (existsSync(join(root, "package-lock.json"))) return "npm";
|
|
13
13
|
return "unknown";
|
|
14
14
|
}
|
|
15
|
-
function validateIntentField(
|
|
15
|
+
function validateIntentField(_pkgName, intent) {
|
|
16
16
|
if (!intent || typeof intent !== "object") return null;
|
|
17
17
|
const pb = intent;
|
|
18
18
|
if (pb.version !== 1) return null;
|
|
@@ -26,12 +26,39 @@ function validateIntentField(pkgName, intent) {
|
|
|
26
26
|
requires
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Derive an IntentConfig from standard package.json fields when no explicit
|
|
31
|
+
* `intent` field is present. A package with a `skills/` directory signals
|
|
32
|
+
* intent support; `repo` and `docs` are derived from `repository` and
|
|
33
|
+
* `homepage`.
|
|
34
|
+
*/
|
|
35
|
+
function deriveIntentConfig(pkgJson) {
|
|
36
|
+
let repo = null;
|
|
37
|
+
if (typeof pkgJson.repository === "string") repo = pkgJson.repository;
|
|
38
|
+
else if (pkgJson.repository && typeof pkgJson.repository === "object" && typeof pkgJson.repository.url === "string") {
|
|
39
|
+
repo = pkgJson.repository.url;
|
|
40
|
+
repo = repo.replace(/^git\+/, "").replace(/\.git$/, "").replace(/^https?:\/\/github\.com\//, "");
|
|
41
|
+
}
|
|
42
|
+
const docs = typeof pkgJson.homepage === "string" ? pkgJson.homepage : void 0;
|
|
43
|
+
if (!repo) return null;
|
|
44
|
+
const intentPartial = pkgJson.intent;
|
|
45
|
+
const requires = intentPartial && Array.isArray(intentPartial.requires) ? intentPartial.requires.filter((r) => typeof r === "string") : void 0;
|
|
46
|
+
return {
|
|
47
|
+
version: 1,
|
|
48
|
+
repo,
|
|
49
|
+
docs: docs ?? "",
|
|
50
|
+
requires
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function discoverSkills(skillsDir, _baseName) {
|
|
30
54
|
const skills = [];
|
|
31
55
|
function walk(dir) {
|
|
32
56
|
let entries;
|
|
33
57
|
try {
|
|
34
|
-
entries = readdirSync(dir, {
|
|
58
|
+
entries = readdirSync(dir, {
|
|
59
|
+
withFileTypes: true,
|
|
60
|
+
encoding: "utf8"
|
|
61
|
+
});
|
|
35
62
|
} catch {
|
|
36
63
|
return;
|
|
37
64
|
}
|
|
@@ -86,7 +113,10 @@ async function scanForIntents(root) {
|
|
|
86
113
|
const packageDirs = [];
|
|
87
114
|
let topEntries;
|
|
88
115
|
try {
|
|
89
|
-
topEntries = readdirSync(nodeModulesDir, {
|
|
116
|
+
topEntries = readdirSync(nodeModulesDir, {
|
|
117
|
+
withFileTypes: true,
|
|
118
|
+
encoding: "utf8"
|
|
119
|
+
});
|
|
90
120
|
} catch {
|
|
91
121
|
return {
|
|
92
122
|
packageManager,
|
|
@@ -100,7 +130,10 @@ async function scanForIntents(root) {
|
|
|
100
130
|
if (entry.name.startsWith("@")) {
|
|
101
131
|
let scopedEntries;
|
|
102
132
|
try {
|
|
103
|
-
scopedEntries = readdirSync(dirPath, {
|
|
133
|
+
scopedEntries = readdirSync(dirPath, {
|
|
134
|
+
withFileTypes: true,
|
|
135
|
+
encoding: "utf8"
|
|
136
|
+
});
|
|
104
137
|
} catch {
|
|
105
138
|
continue;
|
|
106
139
|
}
|
|
@@ -123,9 +156,9 @@ async function scanForIntents(root) {
|
|
|
123
156
|
}
|
|
124
157
|
const pkgName = typeof pkgJson.name === "string" ? pkgJson.name : "unknown";
|
|
125
158
|
const pkgVersion = typeof pkgJson.version === "string" ? pkgJson.version : "0.0.0";
|
|
126
|
-
const intent = validateIntentField(pkgName, pkgJson.intent);
|
|
159
|
+
const intent = validateIntentField(pkgName, pkgJson.intent) ?? deriveIntentConfig(pkgJson);
|
|
127
160
|
if (!intent) {
|
|
128
|
-
warnings.push(`${pkgName} has a skills/ directory but
|
|
161
|
+
warnings.push(`${pkgName} has a skills/ directory but could not determine repo/docs from package.json (add a "repository" field or explicit "intent" config)`);
|
|
129
162
|
continue;
|
|
130
163
|
}
|
|
131
164
|
const skills = discoverSkills(skillsDir, pkgName);
|
|
@@ -47,36 +47,50 @@ function copyTemplates(srcDir, destDir, vars) {
|
|
|
47
47
|
skipped
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
|
-
|
|
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
|
|
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
|
|
59
|
-
if (
|
|
60
|
-
result.skipped.push(
|
|
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,
|
|
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
|
-
const doOz = doAll || args.includes("--oz");
|
|
71
87
|
const doShim = doAll || args.includes("--shim");
|
|
72
|
-
const
|
|
73
|
-
const installWorkflows = doWorkflows ||
|
|
74
|
-
const
|
|
75
|
-
const installShim = doShim || defaultAll;
|
|
88
|
+
const noFlagsGiven = !doWorkflows && !doShim;
|
|
89
|
+
const installWorkflows = doWorkflows || noFlagsGiven;
|
|
90
|
+
const installShim = doShim || noFlagsGiven;
|
|
76
91
|
const vars = detectVars(root);
|
|
77
92
|
const result = {
|
|
78
93
|
workflows: [],
|
|
79
|
-
oz: [],
|
|
80
94
|
skipped: [],
|
|
81
95
|
shim: null
|
|
82
96
|
};
|
|
@@ -86,23 +100,18 @@ function runSetup(root, metaDir, args) {
|
|
|
86
100
|
result.workflows = copied;
|
|
87
101
|
result.skipped.push(...skipped);
|
|
88
102
|
}
|
|
89
|
-
if (installOz) {
|
|
90
|
-
const { copied, skipped } = copyTemplates(join(templatesDir, "oz"), join(root, ".intent", "oz"), vars);
|
|
91
|
-
result.oz = copied;
|
|
92
|
-
result.skipped.push(...skipped);
|
|
93
|
-
}
|
|
94
103
|
if (installShim) generateShim(root, result);
|
|
95
104
|
for (const f of result.workflows) console.log(`✓ Copied workflow: ${f}`);
|
|
96
|
-
for (const f of result.oz) console.log(`✓ Copied Oz prompt: ${f}`);
|
|
97
105
|
for (const f of result.skipped) console.log(` Already exists: ${f}`);
|
|
98
106
|
if (result.shim) {
|
|
107
|
+
const shimRelative = result.shim.replace(root + "/", "./");
|
|
99
108
|
console.log(`✓ Generated intent shim: ${result.shim}`);
|
|
100
109
|
console.log(`\n Add to your package.json:`);
|
|
101
|
-
console.log(` "bin": { "intent": "
|
|
102
|
-
console.log(`\n Add bin
|
|
110
|
+
console.log(` "bin": { "intent": "${shimRelative}" }`);
|
|
111
|
+
console.log(`\n Add "bin" to your package.json "files" array.`);
|
|
103
112
|
}
|
|
104
|
-
if (result.workflows.length === 0 && result.
|
|
105
|
-
else if (result.workflows.length > 0
|
|
113
|
+
if (result.workflows.length === 0 && result.shim === null && result.skipped.length === 0) console.log("No templates directory found. Is @tanstack/intent installed?");
|
|
114
|
+
else if (result.workflows.length > 0) {
|
|
106
115
|
console.log(`\nTemplate variables applied:`);
|
|
107
116
|
console.log(` Package: ${vars.PACKAGE_NAME}`);
|
|
108
117
|
console.log(` Repo: ${vars.REPO}`);
|
package/dist/setup.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as runSetup, t as SetupResult } from "./setup-
|
|
1
|
+
import { n as runSetup, t as SetupResult } from "./setup-BJ4giTKA.mjs";
|
|
2
2
|
export { SetupResult, runSetup };
|
package/dist/setup.mjs
CHANGED
|
@@ -48,7 +48,7 @@ interface FeedbackPayload {
|
|
|
48
48
|
userComments?: string;
|
|
49
49
|
}
|
|
50
50
|
type MetaSkillName = 'domain-discovery' | 'tree-generator' | 'generate-skill' | 'skill-staleness-check';
|
|
51
|
-
type AgentName = '
|
|
51
|
+
type AgentName = 'claude-code' | 'cursor' | 'copilot' | 'codex' | 'other';
|
|
52
52
|
interface MetaFeedbackPayload {
|
|
53
53
|
metaSkill: MetaSkillName;
|
|
54
54
|
library: string;
|