@tanstack/intent 0.0.14 → 0.0.19
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 +42 -5
- package/dist/cli.d.mts +6 -1
- package/dist/cli.mjs +249 -170
- package/dist/display-CuCDLPP_.mjs +3 -0
- package/dist/index.d.mts +7 -7
- package/dist/index.mjs +37 -14
- package/dist/install-prompt-C0M-U3WZ.mjs +59 -0
- package/dist/intent-library.mjs +5 -49
- package/dist/{library-scanner-B1tmOzwf.mjs → library-scanner-CU0OozQE.mjs} +2 -2
- package/dist/library-scanner.d.mts +4 -4
- package/dist/library-scanner.mjs +2 -2
- package/dist/scanner-B1UvuEBQ.mjs +4 -0
- package/dist/scanner-f82qRq7h.mjs +338 -0
- package/dist/setup-6m3IfxyO.d.mts +30 -0
- package/dist/setup-CncHbQlb.mjs +360 -0
- package/dist/setup.d.mts +2 -2
- package/dist/setup.mjs +3 -2
- package/dist/staleness-CWWuoPop.mjs +4 -0
- package/dist/{staleness-DJfMKH62.mjs → staleness-D_ZiK4Tf.mjs} +24 -3
- package/dist/{types-BmnI8kFB.d.mts → types-ddLtccfV.d.mts} +30 -7
- package/dist/{utils-CDJzAdjD.mjs → utils-DY1eH2E_.mjs} +82 -4
- package/dist/utils-XSyO19J6.mjs +3 -0
- package/meta/domain-discovery/SKILL.md +95 -20
- package/meta/feedback-collection/SKILL.md +20 -1
- package/meta/generate-skill/SKILL.md +56 -5
- package/meta/templates/workflows/check-skills.yml +4 -4
- package/meta/templates/workflows/{notify-playbooks.yml → notify-intent.yml} +4 -4
- package/meta/tree-generator/SKILL.md +2 -2
- package/package.json +4 -3
- package/dist/scanner-CECGXgox.mjs +0 -4
- package/dist/scanner-CY40iozO.mjs +0 -218
- package/dist/setup-CANkTz55.d.mts +0 -18
- package/dist/setup-Nif1-nhS.mjs +0 -211
- package/dist/staleness-C1h7RuZ9.mjs +0 -4
- /package/dist/{display-D_XzuGnu.mjs → display-DhsUxNJW.mjs} +0 -0
|
@@ -76,23 +76,33 @@ These rules override any other reasoning. No exceptions.
|
|
|
76
76
|
STOP and WAIT for their reply. Do not answer your own questions. Do
|
|
77
77
|
not infer answers from documentation. Do not skip questions because
|
|
78
78
|
you believe you already know the answer.
|
|
79
|
-
3. **
|
|
79
|
+
3. **Never ask factual questions you can answer by searching the
|
|
80
|
+
codebase.** Before asking any question, determine whether the answer
|
|
81
|
+
is a deterministic fact (how many X exist, what versions are
|
|
82
|
+
supported, which files implement Y) or a judgment call (which ones
|
|
83
|
+
matter, what should we prioritize, what do developers struggle with).
|
|
84
|
+
Factual questions must be answered by searching the code — grep,
|
|
85
|
+
glob, read files. Only ask the maintainer for priorities, opinions,
|
|
86
|
+
trade-offs, and implicit knowledge that cannot be found in code or
|
|
87
|
+
docs. Asking the maintainer a question whose answer is sitting in
|
|
88
|
+
the codebase wastes their time and erodes trust in the process.
|
|
89
|
+
4. **Do not convert open-ended questions into multiple-choice,
|
|
80
90
|
yes/no, or confirmation prompts.** The question templates in each
|
|
81
91
|
sub-section are open-ended by design. Present them as open-ended
|
|
82
92
|
questions. The maintainer's unprompted answers surface knowledge that
|
|
83
93
|
pre-structured options suppress.
|
|
84
|
-
|
|
94
|
+
5. **Minimum question counts are enforced.** Each sub-section specifies
|
|
85
95
|
a question count range (e.g. "2–4 questions"). You must ask at least
|
|
86
96
|
the minimum number. Asking zero questions in any sub-section is a
|
|
87
97
|
protocol violation.
|
|
88
|
-
|
|
98
|
+
6. **STOP gates are mandatory.** At the boundaries marked `── STOP ──`
|
|
89
99
|
below, you must halt execution and wait for the maintainer's response
|
|
90
100
|
or acknowledgment before proceeding. Do not continue past a STOP gate
|
|
91
101
|
in the same message.
|
|
92
|
-
|
|
102
|
+
7. **If the maintainer asks to skip an interview phase**, explain the
|
|
93
103
|
value of the phase and what will be lost. Proceed with skipping only
|
|
94
104
|
if they confirm a second time.
|
|
95
|
-
|
|
105
|
+
8. **Rich documentation makes interviews MORE valuable, not less.**
|
|
96
106
|
When docs are comprehensive, the interview surfaces what docs miss:
|
|
97
107
|
implicit knowledge, AI-specific failure modes, undocumented tradeoffs,
|
|
98
108
|
and the maintainer's prioritization of what matters most. Never
|
|
@@ -115,6 +125,15 @@ reading exhaustively yet.
|
|
|
115
125
|
4. **AGENTS.md or .cursorrules** — if the library already has agent
|
|
116
126
|
guidance, read it. This is high-signal for what the maintainer
|
|
117
127
|
considers important
|
|
128
|
+
5. **All in-repo documentation** — list every `.md` file in the `docs/`
|
|
129
|
+
directory (and any other documentation directories like `guides/`,
|
|
130
|
+
`reference/`, `wiki/`). Read every file. This is NOT the exhaustive
|
|
131
|
+
external doc reading from Phase 3 — this is reading what the
|
|
132
|
+
maintainer committed to the repository, which is fast and
|
|
133
|
+
high-signal. In-repo docs often contain migration guides, backward
|
|
134
|
+
compatibility notes, architecture decisions, and other context that
|
|
135
|
+
prevents you from asking factual questions the docs already answer.
|
|
136
|
+
Do not sample a subset — read them all before the first interview.
|
|
118
137
|
|
|
119
138
|
### 1b — Read peer dependency constraints
|
|
120
139
|
|
|
@@ -258,13 +277,57 @@ sample a subset and extrapolate.
|
|
|
258
277
|
3. **API reference** — scan for exports, type signatures, option shapes
|
|
259
278
|
4. **Changelog for major versions** — API renames, removed exports,
|
|
260
279
|
behavioral changes
|
|
261
|
-
5. **GitHub issues and discussions** —
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
280
|
+
5. **GitHub issues and discussions** — this is one of the highest-yield
|
|
281
|
+
sources for failure modes and skill content. Docs describe intended
|
|
282
|
+
behavior; issues reveal actual behavior and real developer confusion.
|
|
283
|
+
|
|
284
|
+
**How to search.** Use `gh search issues` and `gh search prs` (or the
|
|
285
|
+
GitHub web search UI) against the library's repo. Run multiple passes:
|
|
286
|
+
- **High-engagement issues:** sort by reactions or comments to find the
|
|
287
|
+
problems that affect the most developers. These are skill-worthy
|
|
288
|
+
even if already fixed — agents trained on older data still hit them.
|
|
289
|
+
- **Label-based scans:** look for labels like `bug`, `question`,
|
|
290
|
+
`documentation`, `breaking-change`, `good first issue`, `FAQ`,
|
|
291
|
+
`help wanted`. Each label category yields different signal:
|
|
292
|
+
- `bug` + `closed` → failure modes with known fixes (wrong/correct pairs)
|
|
293
|
+
- `question` → developer confusion that skills should preempt
|
|
294
|
+
- `breaking-change` → migration-boundary mistakes
|
|
295
|
+
- **Keyword searches:** search for the skill's primary APIs, hooks,
|
|
296
|
+
and config options by name. E.g. `useQuery stale` or `hydration SSR`.
|
|
297
|
+
- **Recent vs. historical:** scan the last 6–12 months of open issues
|
|
298
|
+
for current pain points. Then scan older closed issues for patterns
|
|
299
|
+
that are now fixed but still appear in agent training data.
|
|
300
|
+
|
|
301
|
+
**GitHub Discussions** are equally important when the repo uses them.
|
|
302
|
+
Discussions surface "how do I..." patterns and architectural questions
|
|
303
|
+
that issues don't capture. Search the Discussions tab (or use
|
|
304
|
+
`gh api` to query discussions) for:
|
|
305
|
+
- Unanswered or long-thread questions (signal: docs are insufficient)
|
|
306
|
+
- Threads marked as "Answered" with a non-obvious solution (skill content)
|
|
307
|
+
- Recurring themes across multiple threads (systemic confusion)
|
|
308
|
+
|
|
309
|
+
**What to extract from issues/discussions:**
|
|
310
|
+
- Frequently reported confusion patterns → candidate failure modes
|
|
311
|
+
- Workarounds that developers use before a fix ships → "wrong pattern"
|
|
312
|
+
examples that agents will reproduce
|
|
313
|
+
- Recurring "how do I X with Y" threads → composition skill candidates
|
|
314
|
+
- Misunderstandings about defaults or config → skill content gaps
|
|
315
|
+
- Feature requests with many upvotes that change API design → signals
|
|
316
|
+
of where the API surface is unintuitive
|
|
317
|
+
- What users are implicitly arguing for architecturally — not just
|
|
318
|
+
"people are confused about X" but "users keep expecting X to work
|
|
319
|
+
like Y, which reveals a tension between [design force] and
|
|
320
|
+
[design force]"
|
|
321
|
+
|
|
322
|
+
**What NOT to extract:** one-off bugs already fixed, feature requests
|
|
323
|
+
unrelated to current API surface, issues about build tooling or CI
|
|
324
|
+
that don't affect library usage patterns.
|
|
325
|
+
|
|
326
|
+
**Fallback.** If no web access is available, check for FAQ.md,
|
|
327
|
+
TROUBLESHOOTING.md, docs/faq, or KNOWN_ISSUES.md as proxies. Also
|
|
328
|
+
scan the repo's `.github/ISSUE_TEMPLATE/` for hints about common
|
|
329
|
+
issue categories.
|
|
330
|
+
|
|
268
331
|
6. **Source code** — verify ambiguities from docs, check defaults, find
|
|
269
332
|
assertions and invariant checks. For monorepos, read the 2–3 core
|
|
270
333
|
packages deeply. For adapter packages, read one representative adapter
|
|
@@ -289,6 +352,10 @@ Log every:
|
|
|
289
352
|
rejects a subtype of X
|
|
290
353
|
- Source assertion: any `if (!x) throw`, `invariant()`, or `assert()` with
|
|
291
354
|
the error message text
|
|
355
|
+
- Issue/discussion pattern: any recurring confusion, workaround, or
|
|
356
|
+
misunderstanding surfaced from GitHub issues or discussions — note the
|
|
357
|
+
issue/discussion URL, the core misunderstanding, and whether it's
|
|
358
|
+
resolved or still active
|
|
292
359
|
|
|
293
360
|
### What to extract from migration guides specifically
|
|
294
361
|
|
|
@@ -390,14 +457,16 @@ For each skill, extract failure modes that pass all three tests:
|
|
|
390
457
|
|
|
391
458
|
**Where to find them:**
|
|
392
459
|
|
|
393
|
-
| Source | What to extract
|
|
394
|
-
| -------------------- |
|
|
395
|
-
| Migration guides | Every breaking change → old pattern is the wrong code
|
|
396
|
-
| Doc callouts | Any "note", "warning", "avoid" with surrounding context
|
|
397
|
-
| Source assertions | `throw` and `invariant()` messages describe the failure
|
|
398
|
-
| Default values | Undocumented or surprising defaults that cause wrong behavior
|
|
399
|
-
| Type precision | Source type more restrictive than docs imply
|
|
400
|
-
| Environment branches | `typeof window`, SSR flags, `NODE_ENV` — behavior differs silently
|
|
460
|
+
| Source | What to extract |
|
|
461
|
+
| -------------------- | -------------------------------------------------------------------- |
|
|
462
|
+
| Migration guides | Every breaking change → old pattern is the wrong code |
|
|
463
|
+
| Doc callouts | Any "note", "warning", "avoid" with surrounding context |
|
|
464
|
+
| Source assertions | `throw` and `invariant()` messages describe the failure |
|
|
465
|
+
| Default values | Undocumented or surprising defaults that cause wrong behavior |
|
|
466
|
+
| Type precision | Source type more restrictive than docs imply |
|
|
467
|
+
| Environment branches | `typeof window`, SSR flags, `NODE_ENV` — behavior differs silently |
|
|
468
|
+
| GitHub issues | Recurring bug reports with workarounds → wrong/correct code pairs |
|
|
469
|
+
| GitHub discussions | "How do I…" threads with non-obvious answers → missing skill content |
|
|
401
470
|
|
|
402
471
|
Target 3 failure modes per skill minimum. Complex skills target 5–6.
|
|
403
472
|
|
|
@@ -574,6 +643,12 @@ Adapt from this bank of gap-targeted question templates:
|
|
|
574
643
|
What should an agent know about using them together?"
|
|
575
644
|
- "The API reference shows [type signature], but the guide examples use
|
|
576
645
|
a different shape. Which is accurate?"
|
|
646
|
+
- "I found [N] GitHub issues/discussions where developers struggled with
|
|
647
|
+
[X]. The common workaround seems to be [Y] — is that the recommended
|
|
648
|
+
approach, or is there a better pattern that should be documented?"
|
|
649
|
+
- "GitHub discussions show developers repeatedly asking how to combine
|
|
650
|
+
[feature A] with [feature B]. Is there an intended integration pattern,
|
|
651
|
+
or is this a gap in the current API?"
|
|
577
652
|
|
|
578
653
|
### 4c — AI-agent-specific failure modes (2–4 questions)
|
|
579
654
|
|
|
@@ -36,7 +36,7 @@ during the session:
|
|
|
36
36
|
|
|
37
37
|
- **Loaded and used:** Skills you read and actively followed.
|
|
38
38
|
- **Available but not loaded:** Skills that were installed (discoverable via
|
|
39
|
-
`intent list`) but you never read. This is important — many issues stem from
|
|
39
|
+
`npx @tanstack/intent@latest list`) but you never read. This is important — many issues stem from
|
|
40
40
|
the agent not loading the right skill, not from the skill itself being wrong.
|
|
41
41
|
|
|
42
42
|
### 1b: Gap detection
|
|
@@ -172,6 +172,25 @@ Determine the target repo from the skill's package. The repo is typically
|
|
|
172
172
|
derivable from the `repository` field in the package's `package.json`, or
|
|
173
173
|
from the `sources` field in the SKILL.md frontmatter.
|
|
174
174
|
|
|
175
|
+
### Link to existing issues/discussions
|
|
176
|
+
|
|
177
|
+
Before creating a new issue, search the target repo for existing issues or
|
|
178
|
+
discussions that match the feedback. Use `gh search issues` or the GitHub
|
|
179
|
+
web search with keywords from the "What Failed" and "Missing" sections.
|
|
180
|
+
|
|
181
|
+
- If an **open issue** already describes the same problem, comment on it
|
|
182
|
+
with the feedback instead of creating a duplicate. Reference the skill
|
|
183
|
+
name and version in your comment.
|
|
184
|
+
- If a **closed issue** describes a problem the skill still gets wrong
|
|
185
|
+
(regression or stale skill content), reference the closed issue in the
|
|
186
|
+
new feedback issue body: `Related to #[number] — this was fixed in the
|
|
187
|
+
library but the skill still describes the old behavior.`
|
|
188
|
+
- If a **discussion thread** covers the same topic, link to it in the
|
|
189
|
+
feedback issue body so maintainers can see the community context.
|
|
190
|
+
|
|
191
|
+
This prevents duplicate issues and gives maintainers richer context for
|
|
192
|
+
improving skills.
|
|
193
|
+
|
|
175
194
|
### Privacy check
|
|
176
195
|
|
|
177
196
|
Before submitting, determine whether the user's project is public or private.
|
|
@@ -111,6 +111,49 @@ cannot already know:
|
|
|
111
111
|
- Framework-specific gotchas (hydration, hook rules, provider ordering)
|
|
112
112
|
- **Constraints and invariants** — ordering requirements, lifecycle rules,
|
|
113
113
|
things enforced by runtime assertions
|
|
114
|
+
- **Issue/discussion-sourced patterns** — real developer mistakes and
|
|
115
|
+
confusion surfaced from GitHub issues and discussions (see below)
|
|
116
|
+
|
|
117
|
+
### 2b — Scan GitHub issues and discussions
|
|
118
|
+
|
|
119
|
+
Before writing the skill body, search the library's GitHub repo for issues
|
|
120
|
+
and discussions relevant to THIS skill's topic. This step is important for
|
|
121
|
+
both initial generation and regeneration — community feedback reveals
|
|
122
|
+
failure modes that docs miss.
|
|
123
|
+
|
|
124
|
+
**Search strategy:**
|
|
125
|
+
|
|
126
|
+
1. Search issues for the skill's primary APIs, hooks, and config options
|
|
127
|
+
by name (e.g. `useQuery invalidation`, `createRouter middleware`)
|
|
128
|
+
2. Filter to high-signal threads: sort by reactions/comments, focus on
|
|
129
|
+
closed bugs with workarounds and open questions with long threads
|
|
130
|
+
3. Search Discussions (if the repo uses them) for "how do I…" threads
|
|
131
|
+
related to the skill's topic
|
|
132
|
+
4. Check for issues labeled `bug`, `question`, `breaking-change` that
|
|
133
|
+
mention concepts this skill covers
|
|
134
|
+
|
|
135
|
+
**What to incorporate:**
|
|
136
|
+
|
|
137
|
+
- **Recurring bug workarounds** → add as Common Mistakes entries with
|
|
138
|
+
wrong/correct code pairs. Cite the issue URL in the `Source` field.
|
|
139
|
+
- **Frequently asked questions** → if the answer is non-obvious, add it
|
|
140
|
+
to Core Patterns or as a dedicated pattern section
|
|
141
|
+
- **Misunderstandings about defaults** → add to Common Mistakes with the
|
|
142
|
+
incorrect assumption as the "wrong" pattern
|
|
143
|
+
- **Resolved issues that changed behavior** → if the old behavior is
|
|
144
|
+
still in agent training data, add as a migration-boundary mistake
|
|
145
|
+
|
|
146
|
+
**What NOT to incorporate:**
|
|
147
|
+
|
|
148
|
+
- One-off bugs already fixed with no broader pattern
|
|
149
|
+
- Feature requests for APIs that don't exist yet
|
|
150
|
+
- Issues about tooling, CI, or build that don't affect library usage
|
|
151
|
+
- Stale threads (>2 years old) about behavior that has fundamentally changed
|
|
152
|
+
|
|
153
|
+
**Fallback:** If no web access is available, check for FAQ.md,
|
|
154
|
+
TROUBLESHOOTING.md, or docs/faq in the repo. Also check whether the
|
|
155
|
+
domain_map.yaml already contains issue-sourced failure modes from
|
|
156
|
+
domain-discovery — use those directly.
|
|
114
157
|
|
|
115
158
|
### What NOT to extract
|
|
116
159
|
|
|
@@ -353,12 +396,20 @@ output in this order:
|
|
|
353
396
|
When regenerating a stale skill (triggered by skill-staleness-check):
|
|
354
397
|
|
|
355
398
|
1. Read the existing SKILL.md and the source diff that triggered staleness
|
|
356
|
-
2.
|
|
357
|
-
|
|
358
|
-
|
|
399
|
+
2. Scan GitHub issues and discussions opened since the skill was last
|
|
400
|
+
generated (use `library_version` or file timestamps as the baseline).
|
|
401
|
+
Look for new failure modes, resolved confusion, or changed patterns
|
|
402
|
+
related to this skill's topic. Apply the same search strategy from
|
|
403
|
+
Step 2b but scoped to the time window since last generation.
|
|
404
|
+
3. Determine which sections are affected by the source change AND by
|
|
405
|
+
any new issue/discussion findings
|
|
406
|
+
4. Update only affected sections — preserve all other content
|
|
407
|
+
5. If a breaking change occurred, add the old pattern as a new Common
|
|
359
408
|
Mistake entry (wrong/correct pair)
|
|
360
|
-
|
|
361
|
-
|
|
409
|
+
6. If issues/discussions reveal new failure modes not in the existing
|
|
410
|
+
skill, add them to Common Mistakes with issue URLs as sources
|
|
411
|
+
7. Bump `library_version` in frontmatter
|
|
412
|
+
8. Validate the complete file against Step 5 checks
|
|
362
413
|
|
|
363
414
|
Do not rewrite the entire skill for a minor source change. Surgical
|
|
364
415
|
updates preserve review effort and reduce diff noise.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# Triggers: new release published, or manual workflow_dispatch.
|
|
8
8
|
#
|
|
9
9
|
# Template variables (replaced by `intent setup`):
|
|
10
|
-
# {{
|
|
10
|
+
# {{PACKAGE_LABEL}} — e.g. @tanstack/query or my-workspace workspace
|
|
11
11
|
|
|
12
12
|
name: Check Skills
|
|
13
13
|
|
|
@@ -36,12 +36,12 @@ jobs:
|
|
|
36
36
|
node-version: 20
|
|
37
37
|
|
|
38
38
|
- name: Install intent
|
|
39
|
-
run: npm install
|
|
39
|
+
run: npm install -g @tanstack/intent
|
|
40
40
|
|
|
41
41
|
- name: Check staleness
|
|
42
42
|
id: stale
|
|
43
43
|
run: |
|
|
44
|
-
OUTPUT=$(
|
|
44
|
+
OUTPUT=$(intent stale --json 2>&1) || true
|
|
45
45
|
echo "$OUTPUT"
|
|
46
46
|
|
|
47
47
|
# Check if any skills need review
|
|
@@ -81,7 +81,7 @@ jobs:
|
|
|
81
81
|
const summary = lines.join('\n');
|
|
82
82
|
|
|
83
83
|
const prompt = [
|
|
84
|
-
'Review and update the following stale intent skills for {{
|
|
84
|
+
'Review and update the following stale intent skills for {{PACKAGE_LABEL}}:',
|
|
85
85
|
'',
|
|
86
86
|
...stale.map(s => '- ' + s.skill + ': ' + s.reasons.join(', ')),
|
|
87
87
|
'',
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
# as the INTENT_NOTIFY_TOKEN repository secret.
|
|
10
10
|
#
|
|
11
11
|
# Template variables (replaced by `intent setup`):
|
|
12
|
-
# {{
|
|
13
|
-
# {{DOCS_PATH}}
|
|
14
|
-
# {{SRC_PATH}}
|
|
12
|
+
# {{PAYLOAD_PACKAGE}} — e.g. @tanstack/query or my-workspace workspace
|
|
13
|
+
# {{DOCS_PATH}} — e.g. docs/**
|
|
14
|
+
# {{SRC_PATH}} — e.g. packages/query-core/src/**
|
|
15
15
|
|
|
16
16
|
name: Notify Intent
|
|
17
17
|
|
|
@@ -46,7 +46,7 @@ jobs:
|
|
|
46
46
|
event-type: skill-check
|
|
47
47
|
client-payload: |
|
|
48
48
|
{
|
|
49
|
-
"package": "{{
|
|
49
|
+
"package": "{{PAYLOAD_PACKAGE}}",
|
|
50
50
|
"sha": "${{ github.sha }}",
|
|
51
51
|
"changed_files": ${{ steps.changes.outputs.files }}
|
|
52
52
|
}
|
|
@@ -40,7 +40,7 @@ Every skill has a `type` field in its frontmatter. Valid types:
|
|
|
40
40
|
| `composition` | Integration between two or more libraries | `electric-drizzle` |
|
|
41
41
|
| `security` | Audit checklist or security validation | `electric-security-check` |
|
|
42
42
|
|
|
43
|
-
Agents discover skills via `tanstack
|
|
43
|
+
Agents discover skills via `npx @tanstack/intent list` and read them directly
|
|
44
44
|
from `node_modules`. Framework skills declare a `requires` dependency on
|
|
45
45
|
their core skill so agents load them in the right order.
|
|
46
46
|
|
|
@@ -274,7 +274,7 @@ packages/
|
|
|
274
274
|
│ └── package.json # Add "skills" to files array
|
|
275
275
|
```
|
|
276
276
|
|
|
277
|
-
Run `intent edit-package-json` to wire each package's `package.json`
|
|
277
|
+
Run `npx @tanstack/intent@latest edit-package-json` to wire each package's `package.json`
|
|
278
278
|
automatically (adds `"skills"`, `"bin"`, and `"!skills/_artifacts"` to the
|
|
279
279
|
`files` array, and adds the `bin` entry if missing).
|
|
280
280
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/intent",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.19",
|
|
4
4
|
"description": "Ship compositional knowledge for AI coding agents alongside your npm packages",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"types": "./dist/index.d.mts"
|
|
15
15
|
},
|
|
16
16
|
"./intent-library": {
|
|
17
|
-
"import": "./dist/intent-library.mjs"
|
|
17
|
+
"import": "./dist/intent-library.mjs",
|
|
18
|
+
"types": "./dist/intent-library.d.mts"
|
|
18
19
|
}
|
|
19
20
|
},
|
|
20
21
|
"bin": {
|
|
@@ -32,7 +33,7 @@
|
|
|
32
33
|
"tsdown": "^0.19.0"
|
|
33
34
|
},
|
|
34
35
|
"scripts": {
|
|
35
|
-
"prepack": "
|
|
36
|
+
"prepack": "npm run build",
|
|
36
37
|
"build": "tsdown src/index.ts src/cli.ts src/setup.ts src/intent-library.ts src/library-scanner.ts --format esm --dts",
|
|
37
38
|
"test:lib": "vitest run",
|
|
38
39
|
"test:types": "tsc --noEmit"
|
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
import { i as resolveDepDir, n as getDeps, r as parseFrontmatter } from "./utils-CDJzAdjD.mjs";
|
|
2
|
-
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
3
|
-
import { join, relative, sep } from "node:path";
|
|
4
|
-
|
|
5
|
-
//#region src/scanner.ts
|
|
6
|
-
function detectPackageManager(root) {
|
|
7
|
-
if (existsSync(join(root, ".pnp.cjs")) || existsSync(join(root, ".pnp.js"))) throw new Error("Yarn PnP is not yet supported. Add `nodeLinker: node-modules` to your .yarnrc.yml to use intent.");
|
|
8
|
-
if (existsSync(join(root, "deno.json")) && !existsSync(join(root, "node_modules"))) throw new Error("Deno without node_modules is not yet supported. Add `\"nodeModulesDir\": \"auto\"` to your deno.json to use intent.");
|
|
9
|
-
if (existsSync(join(root, "pnpm-lock.yaml"))) return "pnpm";
|
|
10
|
-
if (existsSync(join(root, "bun.lockb")) || existsSync(join(root, "bun.lock"))) return "bun";
|
|
11
|
-
if (existsSync(join(root, "yarn.lock"))) return "yarn";
|
|
12
|
-
if (existsSync(join(root, "package-lock.json"))) return "npm";
|
|
13
|
-
return "unknown";
|
|
14
|
-
}
|
|
15
|
-
function validateIntentField(_pkgName, intent) {
|
|
16
|
-
if (!intent || typeof intent !== "object") return null;
|
|
17
|
-
const pb = intent;
|
|
18
|
-
if (pb.version !== 1) return null;
|
|
19
|
-
if (typeof pb.repo !== "string" || !pb.repo) return null;
|
|
20
|
-
if (typeof pb.docs !== "string" || !pb.docs) return null;
|
|
21
|
-
const requires = Array.isArray(pb.requires) ? pb.requires.filter((r) => typeof r === "string") : void 0;
|
|
22
|
-
return {
|
|
23
|
-
version: 1,
|
|
24
|
-
repo: pb.repo,
|
|
25
|
-
docs: pb.docs,
|
|
26
|
-
requires
|
|
27
|
-
};
|
|
28
|
-
}
|
|
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) {
|
|
54
|
-
const skills = [];
|
|
55
|
-
function walk(dir) {
|
|
56
|
-
let entries;
|
|
57
|
-
try {
|
|
58
|
-
entries = readdirSync(dir, {
|
|
59
|
-
withFileTypes: true,
|
|
60
|
-
encoding: "utf8"
|
|
61
|
-
});
|
|
62
|
-
} catch {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
for (const entry of entries) {
|
|
66
|
-
if (!entry.isDirectory()) continue;
|
|
67
|
-
const childDir = join(dir, entry.name);
|
|
68
|
-
const skillFile = join(childDir, "SKILL.md");
|
|
69
|
-
if (existsSync(skillFile)) {
|
|
70
|
-
const fm = parseFrontmatter(skillFile);
|
|
71
|
-
const relName = relative(skillsDir, childDir).split(sep).join("/");
|
|
72
|
-
const desc = typeof fm?.description === "string" ? fm.description.replace(/\s+/g, " ").trim() : "";
|
|
73
|
-
skills.push({
|
|
74
|
-
name: typeof fm?.name === "string" ? fm.name : relName,
|
|
75
|
-
path: skillFile,
|
|
76
|
-
description: desc,
|
|
77
|
-
type: typeof fm?.type === "string" ? fm.type : void 0,
|
|
78
|
-
framework: typeof fm?.framework === "string" ? fm.framework : void 0
|
|
79
|
-
});
|
|
80
|
-
walk(childDir);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
walk(skillsDir);
|
|
85
|
-
return skills;
|
|
86
|
-
}
|
|
87
|
-
function topoSort(packages) {
|
|
88
|
-
const byName = new Map(packages.map((p) => [p.name, p]));
|
|
89
|
-
const visited = /* @__PURE__ */ new Set();
|
|
90
|
-
const sorted = [];
|
|
91
|
-
function visit(name) {
|
|
92
|
-
if (visited.has(name)) return;
|
|
93
|
-
visited.add(name);
|
|
94
|
-
const pkg = byName.get(name);
|
|
95
|
-
if (!pkg) return;
|
|
96
|
-
for (const dep of pkg.intent.requires ?? []) visit(dep);
|
|
97
|
-
sorted.push(pkg);
|
|
98
|
-
}
|
|
99
|
-
for (const pkg of packages) visit(pkg.name);
|
|
100
|
-
return sorted;
|
|
101
|
-
}
|
|
102
|
-
async function scanForIntents(root) {
|
|
103
|
-
const projectRoot = root ?? process.cwd();
|
|
104
|
-
const packageManager = detectPackageManager(projectRoot);
|
|
105
|
-
const nodeModulesDir = join(projectRoot, "node_modules");
|
|
106
|
-
const packages = [];
|
|
107
|
-
const warnings = [];
|
|
108
|
-
if (!existsSync(nodeModulesDir)) return {
|
|
109
|
-
packageManager,
|
|
110
|
-
packages,
|
|
111
|
-
warnings
|
|
112
|
-
};
|
|
113
|
-
const packageDirs = [];
|
|
114
|
-
let topEntries;
|
|
115
|
-
try {
|
|
116
|
-
topEntries = readdirSync(nodeModulesDir, {
|
|
117
|
-
withFileTypes: true,
|
|
118
|
-
encoding: "utf8"
|
|
119
|
-
});
|
|
120
|
-
} catch {
|
|
121
|
-
return {
|
|
122
|
-
packageManager,
|
|
123
|
-
packages,
|
|
124
|
-
warnings
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
for (const entry of topEntries) {
|
|
128
|
-
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
129
|
-
const dirPath = join(nodeModulesDir, entry.name);
|
|
130
|
-
if (entry.name.startsWith("@")) {
|
|
131
|
-
let scopedEntries;
|
|
132
|
-
try {
|
|
133
|
-
scopedEntries = readdirSync(dirPath, {
|
|
134
|
-
withFileTypes: true,
|
|
135
|
-
encoding: "utf8"
|
|
136
|
-
});
|
|
137
|
-
} catch {
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
for (const scoped of scopedEntries) {
|
|
141
|
-
if (!scoped.isDirectory() && !scoped.isSymbolicLink()) continue;
|
|
142
|
-
packageDirs.push({ dirPath: join(dirPath, scoped.name) });
|
|
143
|
-
}
|
|
144
|
-
} else if (!entry.name.startsWith(".")) packageDirs.push({ dirPath });
|
|
145
|
-
}
|
|
146
|
-
const foundNames = /* @__PURE__ */ new Set();
|
|
147
|
-
/**
|
|
148
|
-
* Try to register a package with a skills/ directory. Reads its
|
|
149
|
-
* package.json, validates intent config, discovers skills, and pushes
|
|
150
|
-
* to `packages`. Returns true if the package was registered.
|
|
151
|
-
*/
|
|
152
|
-
function tryRegister(dirPath, fallbackName) {
|
|
153
|
-
const skillsDir = join(dirPath, "skills");
|
|
154
|
-
if (!existsSync(skillsDir)) return false;
|
|
155
|
-
let pkgJson;
|
|
156
|
-
try {
|
|
157
|
-
pkgJson = JSON.parse(readFileSync(join(dirPath, "package.json"), "utf8"));
|
|
158
|
-
} catch {
|
|
159
|
-
warnings.push(`Could not read package.json for ${dirPath}`);
|
|
160
|
-
return false;
|
|
161
|
-
}
|
|
162
|
-
const name = typeof pkgJson.name === "string" ? pkgJson.name : fallbackName;
|
|
163
|
-
if (foundNames.has(name)) return false;
|
|
164
|
-
const intent = validateIntentField(name, pkgJson.intent) ?? deriveIntentConfig(pkgJson);
|
|
165
|
-
if (!intent) {
|
|
166
|
-
warnings.push(`${name} has a skills/ directory but could not determine repo/docs from package.json (add a "repository" field or explicit "intent" config)`);
|
|
167
|
-
return false;
|
|
168
|
-
}
|
|
169
|
-
packages.push({
|
|
170
|
-
name,
|
|
171
|
-
version: typeof pkgJson.version === "string" ? pkgJson.version : "0.0.0",
|
|
172
|
-
intent,
|
|
173
|
-
skills: discoverSkills(skillsDir, name)
|
|
174
|
-
});
|
|
175
|
-
foundNames.add(name);
|
|
176
|
-
return true;
|
|
177
|
-
}
|
|
178
|
-
for (const { dirPath } of packageDirs) tryRegister(dirPath, "unknown");
|
|
179
|
-
const walkVisited = /* @__PURE__ */ new Set();
|
|
180
|
-
function walkDeps(pkgDir, pkgName) {
|
|
181
|
-
if (walkVisited.has(pkgName)) return;
|
|
182
|
-
walkVisited.add(pkgName);
|
|
183
|
-
let pkgJson;
|
|
184
|
-
try {
|
|
185
|
-
pkgJson = JSON.parse(readFileSync(join(pkgDir, "package.json"), "utf8"));
|
|
186
|
-
} catch {
|
|
187
|
-
warnings.push(`Could not read package.json for ${pkgName} (skipping dependency walk)`);
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
for (const depName of getDeps(pkgJson)) {
|
|
191
|
-
if (foundNames.has(depName) || walkVisited.has(depName)) continue;
|
|
192
|
-
const depDir = resolveDepDir(depName, pkgDir, pkgName, nodeModulesDir);
|
|
193
|
-
if (!depDir) continue;
|
|
194
|
-
tryRegister(depDir, depName);
|
|
195
|
-
walkDeps(depDir, depName);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
for (const pkg of [...packages]) walkDeps(join(nodeModulesDir, pkg.name), pkg.name);
|
|
199
|
-
let projectPkg = null;
|
|
200
|
-
try {
|
|
201
|
-
projectPkg = JSON.parse(readFileSync(join(projectRoot, "package.json"), "utf8"));
|
|
202
|
-
} catch (err) {
|
|
203
|
-
if (!(err && typeof err === "object" && "code" in err && err.code === "ENOENT")) warnings.push(`Could not read project package.json: ${err instanceof Error ? err.message : String(err)}`);
|
|
204
|
-
}
|
|
205
|
-
if (projectPkg) for (const depName of getDeps(projectPkg, true)) {
|
|
206
|
-
if (walkVisited.has(depName)) continue;
|
|
207
|
-
const depDir = join(nodeModulesDir, depName);
|
|
208
|
-
if (existsSync(join(depDir, "package.json"))) walkDeps(depDir, depName);
|
|
209
|
-
}
|
|
210
|
-
return {
|
|
211
|
-
packageManager,
|
|
212
|
-
packages: topoSort(packages),
|
|
213
|
-
warnings
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
//#endregion
|
|
218
|
-
export { scanForIntents as t };
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
//#region src/setup.d.ts
|
|
2
|
-
interface AddLibraryBinResult {
|
|
3
|
-
shim: string | null;
|
|
4
|
-
skipped: string | null;
|
|
5
|
-
}
|
|
6
|
-
interface EditPackageJsonResult {
|
|
7
|
-
added: string[];
|
|
8
|
-
alreadyPresent: string[];
|
|
9
|
-
}
|
|
10
|
-
interface SetupGithubActionsResult {
|
|
11
|
-
workflows: string[];
|
|
12
|
-
skipped: string[];
|
|
13
|
-
}
|
|
14
|
-
declare function runAddLibraryBin(root: string): AddLibraryBinResult;
|
|
15
|
-
declare function runEditPackageJson(root: string): EditPackageJsonResult;
|
|
16
|
-
declare function runSetupGithubActions(root: string, metaDir: string): SetupGithubActionsResult;
|
|
17
|
-
//#endregion
|
|
18
|
-
export { runEditPackageJson as a, runAddLibraryBin as i, EditPackageJsonResult as n, runSetupGithubActions as o, SetupGithubActionsResult as r, AddLibraryBinResult as t };
|