@llui/compiler 0.3.0
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/LICENSE +21 -0
- package/dist/accessor-resolver.d.ts +58 -0
- package/dist/accessor-resolver.d.ts.map +1 -0
- package/dist/accessor-resolver.js +119 -0
- package/dist/accessor-resolver.js.map +1 -0
- package/dist/binding-descriptors.d.ts +105 -0
- package/dist/binding-descriptors.d.ts.map +1 -0
- package/dist/binding-descriptors.js +340 -0
- package/dist/binding-descriptors.js.map +1 -0
- package/dist/collect-deps.d.ts +49 -0
- package/dist/collect-deps.d.ts.map +1 -0
- package/dist/collect-deps.js +444 -0
- package/dist/collect-deps.js.map +1 -0
- package/dist/compiler-cache.d.ts +20 -0
- package/dist/compiler-cache.d.ts.map +1 -0
- package/dist/compiler-cache.js +20 -0
- package/dist/compiler-cache.js.map +1 -0
- package/dist/cross-file-resolver.d.ts +109 -0
- package/dist/cross-file-resolver.d.ts.map +1 -0
- package/dist/cross-file-resolver.js +530 -0
- package/dist/cross-file-resolver.js.map +1 -0
- package/dist/cross-file-walker.d.ts +63 -0
- package/dist/cross-file-walker.d.ts.map +1 -0
- package/dist/cross-file-walker.js +516 -0
- package/dist/cross-file-walker.js.map +1 -0
- package/dist/diagnostic.d.ts +76 -0
- package/dist/diagnostic.d.ts.map +1 -0
- package/dist/diagnostic.js +59 -0
- package/dist/diagnostic.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/introspection-factory.d.ts +54 -0
- package/dist/introspection-factory.d.ts.map +1 -0
- package/dist/introspection-factory.js +46 -0
- package/dist/introspection-factory.js.map +1 -0
- package/dist/manifest.d.ts +144 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +209 -0
- package/dist/manifest.js.map +1 -0
- package/dist/module.d.ts +222 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +256 -0
- package/dist/module.js.map +1 -0
- package/dist/modules/_element-helpers.d.ts +4 -0
- package/dist/modules/_element-helpers.d.ts.map +1 -0
- package/dist/modules/_element-helpers.js +138 -0
- package/dist/modules/_element-helpers.js.map +1 -0
- package/dist/modules/_msg-variants.d.ts +10 -0
- package/dist/modules/_msg-variants.d.ts.map +1 -0
- package/dist/modules/_msg-variants.js +97 -0
- package/dist/modules/_msg-variants.js.map +1 -0
- package/dist/modules/_shared.d.ts +16 -0
- package/dist/modules/_shared.d.ts.map +1 -0
- package/dist/modules/_shared.js +30 -0
- package/dist/modules/_shared.js.map +1 -0
- package/dist/modules/accessibility.d.ts +3 -0
- package/dist/modules/accessibility.d.ts.map +1 -0
- package/dist/modules/accessibility.js +82 -0
- package/dist/modules/accessibility.js.map +1 -0
- package/dist/modules/accessor-side-effect.d.ts +3 -0
- package/dist/modules/accessor-side-effect.d.ts.map +1 -0
- package/dist/modules/accessor-side-effect.js +113 -0
- package/dist/modules/accessor-side-effect.js.map +1 -0
- package/dist/modules/agent-emits-drift.d.ts +3 -0
- package/dist/modules/agent-emits-drift.d.ts.map +1 -0
- package/dist/modules/agent-emits-drift.js +158 -0
- package/dist/modules/agent-emits-drift.js.map +1 -0
- package/dist/modules/agent-example-on-payload.d.ts +3 -0
- package/dist/modules/agent-example-on-payload.d.ts.map +1 -0
- package/dist/modules/agent-example-on-payload.js +53 -0
- package/dist/modules/agent-example-on-payload.js.map +1 -0
- package/dist/modules/agent-exclusive-annotations.d.ts +3 -0
- package/dist/modules/agent-exclusive-annotations.d.ts.map +1 -0
- package/dist/modules/agent-exclusive-annotations.js +68 -0
- package/dist/modules/agent-exclusive-annotations.js.map +1 -0
- package/dist/modules/agent-missing-intent.d.ts +3 -0
- package/dist/modules/agent-missing-intent.d.ts.map +1 -0
- package/dist/modules/agent-missing-intent.js +47 -0
- package/dist/modules/agent-missing-intent.js.map +1 -0
- package/dist/modules/agent-msg-resolvable.d.ts +3 -0
- package/dist/modules/agent-msg-resolvable.d.ts.map +1 -0
- package/dist/modules/agent-msg-resolvable.js +161 -0
- package/dist/modules/agent-msg-resolvable.js.map +1 -0
- package/dist/modules/agent-nonextractable-handler.d.ts +3 -0
- package/dist/modules/agent-nonextractable-handler.d.ts.map +1 -0
- package/dist/modules/agent-nonextractable-handler.js +127 -0
- package/dist/modules/agent-nonextractable-handler.js.map +1 -0
- package/dist/modules/agent-optional-field-undocumented.d.ts +3 -0
- package/dist/modules/agent-optional-field-undocumented.d.ts.map +1 -0
- package/dist/modules/agent-optional-field-undocumented.js +67 -0
- package/dist/modules/agent-optional-field-undocumented.js.map +1 -0
- package/dist/modules/agent-tagsend-translator-missing.d.ts +3 -0
- package/dist/modules/agent-tagsend-translator-missing.d.ts.map +1 -0
- package/dist/modules/agent-tagsend-translator-missing.js +58 -0
- package/dist/modules/agent-tagsend-translator-missing.js.map +1 -0
- package/dist/modules/agent-warning-on-confirm.d.ts +3 -0
- package/dist/modules/agent-warning-on-confirm.d.ts.map +1 -0
- package/dist/modules/agent-warning-on-confirm.js +46 -0
- package/dist/modules/agent-warning-on-confirm.js.map +1 -0
- package/dist/modules/async-update.d.ts +3 -0
- package/dist/modules/async-update.d.ts.map +1 -0
- package/dist/modules/async-update.js +86 -0
- package/dist/modules/async-update.js.map +1 -0
- package/dist/modules/binding-descriptors.d.ts +4 -0
- package/dist/modules/binding-descriptors.d.ts.map +1 -0
- package/dist/modules/binding-descriptors.js +48 -0
- package/dist/modules/binding-descriptors.js.map +1 -0
- package/dist/modules/bitmask-overflow.d.ts +3 -0
- package/dist/modules/bitmask-overflow.d.ts.map +1 -0
- package/dist/modules/bitmask-overflow.js +152 -0
- package/dist/modules/bitmask-overflow.js.map +1 -0
- package/dist/modules/compiler-stamp.d.ts +3 -0
- package/dist/modules/compiler-stamp.d.ts.map +1 -0
- package/dist/modules/compiler-stamp.js +44 -0
- package/dist/modules/compiler-stamp.js.map +1 -0
- package/dist/modules/component-meta.d.ts +3 -0
- package/dist/modules/component-meta.d.ts.map +1 -0
- package/dist/modules/component-meta.js +44 -0
- package/dist/modules/component-meta.js.map +1 -0
- package/dist/modules/controlled-input.d.ts +3 -0
- package/dist/modules/controlled-input.d.ts.map +1 -0
- package/dist/modules/controlled-input.js +68 -0
- package/dist/modules/controlled-input.js.map +1 -0
- package/dist/modules/core-synthesis.d.ts +18 -0
- package/dist/modules/core-synthesis.d.ts.map +1 -0
- package/dist/modules/core-synthesis.js +748 -0
- package/dist/modules/core-synthesis.js.map +1 -0
- package/dist/modules/direct-state-in-view.d.ts +3 -0
- package/dist/modules/direct-state-in-view.d.ts.map +1 -0
- package/dist/modules/direct-state-in-view.js +103 -0
- package/dist/modules/direct-state-in-view.js.map +1 -0
- package/dist/modules/each-closure-violation.d.ts +3 -0
- package/dist/modules/each-closure-violation.d.ts.map +1 -0
- package/dist/modules/each-closure-violation.js +255 -0
- package/dist/modules/each-closure-violation.js.map +1 -0
- package/dist/modules/each-memo.d.ts +15 -0
- package/dist/modules/each-memo.d.ts.map +1 -0
- package/dist/modules/each-memo.js +115 -0
- package/dist/modules/each-memo.js.map +1 -0
- package/dist/modules/effect-without-handler.d.ts +3 -0
- package/dist/modules/effect-without-handler.d.ts.map +1 -0
- package/dist/modules/effect-without-handler.js +92 -0
- package/dist/modules/effect-without-handler.js.map +1 -0
- package/dist/modules/element-rewrite.d.ts +22 -0
- package/dist/modules/element-rewrite.d.ts.map +1 -0
- package/dist/modules/element-rewrite.js +1017 -0
- package/dist/modules/element-rewrite.js.map +1 -0
- package/dist/modules/empty-props.d.ts +3 -0
- package/dist/modules/empty-props.d.ts.map +1 -0
- package/dist/modules/empty-props.js +50 -0
- package/dist/modules/empty-props.js.map +1 -0
- package/dist/modules/exhaustive-effect-handling.d.ts +3 -0
- package/dist/modules/exhaustive-effect-handling.d.ts.map +1 -0
- package/dist/modules/exhaustive-effect-handling.js +61 -0
- package/dist/modules/exhaustive-effect-handling.js.map +1 -0
- package/dist/modules/exhaustive-update.d.ts +3 -0
- package/dist/modules/exhaustive-update.d.ts.map +1 -0
- package/dist/modules/exhaustive-update.js +146 -0
- package/dist/modules/exhaustive-update.js.map +1 -0
- package/dist/modules/forgotten-spread.d.ts +3 -0
- package/dist/modules/forgotten-spread.d.ts.map +1 -0
- package/dist/modules/forgotten-spread.js +51 -0
- package/dist/modules/forgotten-spread.js.map +1 -0
- package/dist/modules/form-boilerplate.d.ts +3 -0
- package/dist/modules/form-boilerplate.d.ts.map +1 -0
- package/dist/modules/form-boilerplate.js +101 -0
- package/dist/modules/form-boilerplate.js.map +1 -0
- package/dist/modules/imperative-dom-in-view.d.ts +3 -0
- package/dist/modules/imperative-dom-in-view.d.ts.map +1 -0
- package/dist/modules/imperative-dom-in-view.js +123 -0
- package/dist/modules/imperative-dom-in-view.js.map +1 -0
- package/dist/modules/item-dedup.d.ts +7 -0
- package/dist/modules/item-dedup.d.ts.map +1 -0
- package/dist/modules/item-dedup.js +204 -0
- package/dist/modules/item-dedup.js.map +1 -0
- package/dist/modules/map-on-state-array.d.ts +3 -0
- package/dist/modules/map-on-state-array.d.ts.map +1 -0
- package/dist/modules/map-on-state-array.js +84 -0
- package/dist/modules/map-on-state-array.js.map +1 -0
- package/dist/modules/mask-legend.d.ts +10 -0
- package/dist/modules/mask-legend.d.ts.map +1 -0
- package/dist/modules/mask-legend.js +50 -0
- package/dist/modules/mask-legend.js.map +1 -0
- package/dist/modules/missing-memo.d.ts +3 -0
- package/dist/modules/missing-memo.d.ts.map +1 -0
- package/dist/modules/missing-memo.js +114 -0
- package/dist/modules/missing-memo.js.map +1 -0
- package/dist/modules/msg-annotations.d.ts +9 -0
- package/dist/modules/msg-annotations.d.ts.map +1 -0
- package/dist/modules/msg-annotations.js +54 -0
- package/dist/modules/msg-annotations.js.map +1 -0
- package/dist/modules/msg-schema.d.ts +10 -0
- package/dist/modules/msg-schema.d.ts.map +1 -0
- package/dist/modules/msg-schema.js +70 -0
- package/dist/modules/msg-schema.js.map +1 -0
- package/dist/modules/namespace-import.d.ts +3 -0
- package/dist/modules/namespace-import.d.ts.map +1 -0
- package/dist/modules/namespace-import.js +80 -0
- package/dist/modules/namespace-import.js.map +1 -0
- package/dist/modules/nested-send-in-update.d.ts +3 -0
- package/dist/modules/nested-send-in-update.d.ts.map +1 -0
- package/dist/modules/nested-send-in-update.js +77 -0
- package/dist/modules/nested-send-in-update.js.map +1 -0
- package/dist/modules/no-barrel-import-when-subpath-exists.d.ts +3 -0
- package/dist/modules/no-barrel-import-when-subpath-exists.d.ts.map +1 -0
- package/dist/modules/no-barrel-import-when-subpath-exists.js +100 -0
- package/dist/modules/no-barrel-import-when-subpath-exists.js.map +1 -0
- package/dist/modules/no-eager-item-accessor.d.ts +3 -0
- package/dist/modules/no-eager-item-accessor.d.ts.map +1 -0
- package/dist/modules/no-eager-item-accessor.js +74 -0
- package/dist/modules/no-eager-item-accessor.js.map +1 -0
- package/dist/modules/no-let-reactive-accessor.d.ts +3 -0
- package/dist/modules/no-let-reactive-accessor.d.ts.map +1 -0
- package/dist/modules/no-let-reactive-accessor.js +227 -0
- package/dist/modules/no-let-reactive-accessor.js.map +1 -0
- package/dist/modules/no-list-render-in-sample.d.ts +3 -0
- package/dist/modules/no-list-render-in-sample.d.ts.map +1 -0
- package/dist/modules/no-list-render-in-sample.js +89 -0
- package/dist/modules/no-list-render-in-sample.js.map +1 -0
- package/dist/modules/no-sample-in-accessor.d.ts +3 -0
- package/dist/modules/no-sample-in-accessor.d.ts.map +1 -0
- package/dist/modules/no-sample-in-accessor.js +141 -0
- package/dist/modules/no-sample-in-accessor.js.map +1 -0
- package/dist/modules/no-sample-in-reactive-position.d.ts +3 -0
- package/dist/modules/no-sample-in-reactive-position.d.ts.map +1 -0
- package/dist/modules/no-sample-in-reactive-position.js +72 -0
- package/dist/modules/no-sample-in-reactive-position.js.map +1 -0
- package/dist/modules/pure-update-function.d.ts +3 -0
- package/dist/modules/pure-update-function.d.ts.map +1 -0
- package/dist/modules/pure-update-function.js +127 -0
- package/dist/modules/pure-update-function.js.map +1 -0
- package/dist/modules/reactive-paths.d.ts +3 -0
- package/dist/modules/reactive-paths.d.ts.map +1 -0
- package/dist/modules/reactive-paths.js +77 -0
- package/dist/modules/reactive-paths.js.map +1 -0
- package/dist/modules/row-factory.d.ts +12 -0
- package/dist/modules/row-factory.d.ts.map +1 -0
- package/dist/modules/row-factory.js +385 -0
- package/dist/modules/row-factory.js.map +1 -0
- package/dist/modules/schema-hash.d.ts +15 -0
- package/dist/modules/schema-hash.d.ts.map +1 -0
- package/dist/modules/schema-hash.js +70 -0
- package/dist/modules/schema-hash.js.map +1 -0
- package/dist/modules/spread-in-children.d.ts +3 -0
- package/dist/modules/spread-in-children.d.ts.map +1 -0
- package/dist/modules/spread-in-children.js +144 -0
- package/dist/modules/spread-in-children.js.map +1 -0
- package/dist/modules/state-mutation.d.ts +3 -0
- package/dist/modules/state-mutation.d.ts.map +1 -0
- package/dist/modules/state-mutation.js +138 -0
- package/dist/modules/state-mutation.js.map +1 -0
- package/dist/modules/state-schema.d.ts +8 -0
- package/dist/modules/state-schema.d.ts.map +1 -0
- package/dist/modules/state-schema.js +55 -0
- package/dist/modules/state-schema.js.map +1 -0
- package/dist/modules/static-items.d.ts +3 -0
- package/dist/modules/static-items.d.ts.map +1 -0
- package/dist/modules/static-items.js +125 -0
- package/dist/modules/static-items.js.map +1 -0
- package/dist/modules/static-on.d.ts +3 -0
- package/dist/modules/static-on.d.ts.map +1 -0
- package/dist/modules/static-on.js +100 -0
- package/dist/modules/static-on.js.map +1 -0
- package/dist/modules/string-effect-callback.d.ts +3 -0
- package/dist/modules/string-effect-callback.d.ts.map +1 -0
- package/dist/modules/string-effect-callback.js +50 -0
- package/dist/modules/string-effect-callback.js.map +1 -0
- package/dist/modules/structural-mask.d.ts +8 -0
- package/dist/modules/structural-mask.d.ts.map +1 -0
- package/dist/modules/structural-mask.js +76 -0
- package/dist/modules/structural-mask.js.map +1 -0
- package/dist/modules/subapp-requires-reason.d.ts +3 -0
- package/dist/modules/subapp-requires-reason.d.ts.map +1 -0
- package/dist/modules/subapp-requires-reason.js +129 -0
- package/dist/modules/subapp-requires-reason.js.map +1 -0
- package/dist/modules/text-mask.d.ts +12 -0
- package/dist/modules/text-mask.d.ts.map +1 -0
- package/dist/modules/text-mask.js +63 -0
- package/dist/modules/text-mask.js.map +1 -0
- package/dist/modules/view-bag-import.d.ts +3 -0
- package/dist/modules/view-bag-import.d.ts.map +1 -0
- package/dist/modules/view-bag-import.js +80 -0
- package/dist/modules/view-bag-import.js.map +1 -0
- package/dist/msg-annotations.d.ts +104 -0
- package/dist/msg-annotations.d.ts.map +1 -0
- package/dist/msg-annotations.js +242 -0
- package/dist/msg-annotations.js.map +1 -0
- package/dist/msg-schema.d.ts +130 -0
- package/dist/msg-schema.d.ts.map +1 -0
- package/dist/msg-schema.js +770 -0
- package/dist/msg-schema.js.map +1 -0
- package/dist/schema-hash.d.ts +16 -0
- package/dist/schema-hash.d.ts.map +1 -0
- package/dist/schema-hash.js +31 -0
- package/dist/schema-hash.js.map +1 -0
- package/dist/state-schema.d.ts +41 -0
- package/dist/state-schema.d.ts.map +1 -0
- package/dist/state-schema.js +156 -0
- package/dist/state-schema.js.map +1 -0
- package/dist/transform.d.ts +109 -0
- package/dist/transform.d.ts.map +1 -0
- package/dist/transform.js +1390 -0
- package/dist/transform.js.map +1 -0
- package/dist/version.d.ts +11 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +11 -0
- package/dist/version.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// `agent-missing-intent` — errors when a Msg union variant has no
|
|
2
|
+
// `@intent("...")` JSDoc tag. The intent label is what Claude sees;
|
|
3
|
+
// without one, the agent surface synthesizes from the variant name
|
|
4
|
+
// alone (often unclear). Variants annotated `@humanOnly` are skipped.
|
|
5
|
+
// Migrated from `@llui/eslint-plugin/src/rules/agent-missing-intent.ts`.
|
|
6
|
+
import ts from 'typescript';
|
|
7
|
+
import { rangeFromOffsets } from '../diagnostic.js';
|
|
8
|
+
import { forEachMsgVariant } from './_msg-variants.js';
|
|
9
|
+
export function agentMissingIntentModule() {
|
|
10
|
+
return {
|
|
11
|
+
name: 'agent-missing-intent',
|
|
12
|
+
compilerVersion: '^0.3.0',
|
|
13
|
+
diagnostics: [
|
|
14
|
+
{
|
|
15
|
+
id: 'llui/agent-missing-intent',
|
|
16
|
+
description: 'Msg variant is missing @intent("...") — Claude will see a synthesized label.',
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
visitors: {
|
|
20
|
+
[ts.SyntaxKind.SourceFile]: (ctx, node) => {
|
|
21
|
+
const visited = node;
|
|
22
|
+
const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true);
|
|
23
|
+
forEachMsgVariant(sf, ({ variant, node: typeLit, leadingCommentText }) => {
|
|
24
|
+
if (/@humanOnly\b/.test(leadingCommentText))
|
|
25
|
+
return;
|
|
26
|
+
if (/@intent\s*\(\s*["“]([^"”]*)["”]\s*\)/.test(leadingCommentText))
|
|
27
|
+
return;
|
|
28
|
+
ctx.reportDiagnostic({
|
|
29
|
+
id: 'llui/agent-missing-intent',
|
|
30
|
+
severity: 'error',
|
|
31
|
+
category: 'agent',
|
|
32
|
+
message: `Msg variant "${variant}" is missing \`@intent("...")\` — without an explicit ` +
|
|
33
|
+
`intent label, the agent surface synthesizes from the variant name and Claude ` +
|
|
34
|
+
`gets a noisier hint. Add a JSDoc block above this variant: ` +
|
|
35
|
+
`\`/** @intent("Verb-phrase describing what this does") */\` ` +
|
|
36
|
+
`(or \`@humanOnly\` if this variant is not LLM-dispatchable).`,
|
|
37
|
+
location: {
|
|
38
|
+
file: sf.fileName,
|
|
39
|
+
range: rangeFromOffsets(sf.text, typeLit.getStart(sf), typeLit.getEnd()),
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=agent-missing-intent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-missing-intent.js","sourceRoot":"","sources":["../../src/modules/agent-missing-intent.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,oEAAoE;AACpE,mEAAmE;AACnE,sEAAsE;AACtE,yEAAyE;AAEzE,OAAO,EAAE,MAAM,YAAY,CAAA;AAC3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAEnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAEtD,MAAM,UAAU,wBAAwB;IACtC,OAAO;QACL,IAAI,EAAE,sBAAsB;QAC5B,eAAe,EAAE,QAAQ;QACzB,WAAW,EAAE;YACX;gBACE,EAAE,EAAE,2BAA2B;gBAC/B,WAAW,EAAE,8EAA8E;aAC5F;SACF;QACD,QAAQ,EAAE;YACR,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;gBACxC,MAAM,OAAO,GAAG,IAAqB,CAAA;gBACrC,MAAM,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;gBAC5F,iBAAiB,CAAC,EAAE,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,kBAAkB,EAAE,EAAE,EAAE;oBACvE,IAAI,cAAc,CAAC,IAAI,CAAC,kBAAkB,CAAC;wBAAE,OAAM;oBACnD,IAAI,sCAAsC,CAAC,IAAI,CAAC,kBAAkB,CAAC;wBAAE,OAAM;oBAC3E,GAAG,CAAC,gBAAgB,CAAC;wBACnB,EAAE,EAAE,2BAA2B;wBAC/B,QAAQ,EAAE,OAAO;wBACjB,QAAQ,EAAE,OAAO;wBACjB,OAAO,EACL,gBAAgB,OAAO,wDAAwD;4BAC/E,+EAA+E;4BAC/E,6DAA6D;4BAC7D,8DAA8D;4BAC9D,8DAA8D;wBAChE,QAAQ,EAAE;4BACR,IAAI,EAAE,EAAE,CAAC,QAAQ;4BACjB,KAAK,EAAE,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;yBACzE;qBACF,CAAC,CAAA;gBACJ,CAAC,CAAC,CAAA;YACJ,CAAC;SACF;KACF,CAAA;AACH,CAAC","sourcesContent":["// `agent-missing-intent` — errors when a Msg union variant has no\n// `@intent(\"...\")` JSDoc tag. The intent label is what Claude sees;\n// without one, the agent surface synthesizes from the variant name\n// alone (often unclear). Variants annotated `@humanOnly` are skipped.\n// Migrated from `@llui/eslint-plugin/src/rules/agent-missing-intent.ts`.\n\nimport ts from 'typescript'\nimport { rangeFromOffsets } from '../diagnostic.js'\nimport type { CompilerModule } from '../module.js'\nimport { forEachMsgVariant } from './_msg-variants.js'\n\nexport function agentMissingIntentModule(): CompilerModule {\n return {\n name: 'agent-missing-intent',\n compilerVersion: '^0.3.0',\n diagnostics: [\n {\n id: 'llui/agent-missing-intent',\n description: 'Msg variant is missing @intent(\"...\") — Claude will see a synthesized label.',\n },\n ],\n visitors: {\n [ts.SyntaxKind.SourceFile]: (ctx, node) => {\n const visited = node as ts.SourceFile\n const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true)\n forEachMsgVariant(sf, ({ variant, node: typeLit, leadingCommentText }) => {\n if (/@humanOnly\\b/.test(leadingCommentText)) return\n if (/@intent\\s*\\(\\s*[\"“]([^\"”]*)[\"”]\\s*\\)/.test(leadingCommentText)) return\n ctx.reportDiagnostic({\n id: 'llui/agent-missing-intent',\n severity: 'error',\n category: 'agent',\n message:\n `Msg variant \"${variant}\" is missing \\`@intent(\"...\")\\` — without an explicit ` +\n `intent label, the agent surface synthesizes from the variant name and Claude ` +\n `gets a noisier hint. Add a JSDoc block above this variant: ` +\n `\\`/** @intent(\"Verb-phrase describing what this does\") */\\` ` +\n `(or \\`@humanOnly\\` if this variant is not LLM-dispatchable).`,\n location: {\n file: sf.fileName,\n range: rangeFromOffsets(sf.text, typeLit.getStart(sf), typeLit.getEnd()),\n },\n })\n })\n },\n },\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-msg-resolvable.d.ts","sourceRoot":"","sources":["../../src/modules/agent-msg-resolvable.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAyClD,wBAAgB,wBAAwB,IAAI,cAAc,CA8GzD"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// `agent-msg-resolvable` — verifies the `Msg` type argument of every
|
|
2
|
+
// `component<S, M, E>()` call resolves to a type the compiler can
|
|
3
|
+
// statically reach: either a local `type M = …` declaration or a named
|
|
4
|
+
// import from another module.
|
|
5
|
+
//
|
|
6
|
+
// Catches the silent-failure case where M references something the
|
|
7
|
+
// resolver can't follow — a typo, a missing barrel re-export, a
|
|
8
|
+
// namespace import — which would silently disable agent metadata
|
|
9
|
+
// emission. Migrated from
|
|
10
|
+
// `@llui/eslint-plugin/src/rules/agent-msg-resolvable.ts`.
|
|
11
|
+
//
|
|
12
|
+
// File-local check: the compiler doesn't run a full cross-file resolver
|
|
13
|
+
// here (it's available in the broader transform pipeline but not
|
|
14
|
+
// threaded into this module). The file-local form catches:
|
|
15
|
+
// - the M type arg isn't a plain identifier (generics, inline unions,
|
|
16
|
+
// namespace-qualified names);
|
|
17
|
+
// - the M identifier isn't declared locally and isn't named-imported
|
|
18
|
+
// from any source;
|
|
19
|
+
// - the M identifier comes from a namespace import (`import * as`).
|
|
20
|
+
import ts from 'typescript';
|
|
21
|
+
import { rangeFromOffsets } from '../diagnostic.js';
|
|
22
|
+
function collectImports(sf) {
|
|
23
|
+
const info = { named: new Set(), defaults: new Set(), namespaces: new Set() };
|
|
24
|
+
for (const stmt of sf.statements) {
|
|
25
|
+
if (!ts.isImportDeclaration(stmt))
|
|
26
|
+
continue;
|
|
27
|
+
const clause = stmt.importClause;
|
|
28
|
+
if (!clause)
|
|
29
|
+
continue;
|
|
30
|
+
if (clause.name)
|
|
31
|
+
info.defaults.add(clause.name.text);
|
|
32
|
+
if (clause.namedBindings) {
|
|
33
|
+
if (ts.isNamespaceImport(clause.namedBindings)) {
|
|
34
|
+
info.namespaces.add(clause.namedBindings.name.text);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
for (const el of clause.namedBindings.elements) {
|
|
38
|
+
info.named.add(el.name.text);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return info;
|
|
44
|
+
}
|
|
45
|
+
function collectLocalTypeNames(sf) {
|
|
46
|
+
const out = new Set();
|
|
47
|
+
for (const stmt of sf.statements) {
|
|
48
|
+
if (ts.isTypeAliasDeclaration(stmt))
|
|
49
|
+
out.add(stmt.name.text);
|
|
50
|
+
else if (ts.isInterfaceDeclaration(stmt))
|
|
51
|
+
out.add(stmt.name.text);
|
|
52
|
+
else if (ts.isClassDeclaration(stmt) && stmt.name)
|
|
53
|
+
out.add(stmt.name.text);
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
export function agentMsgResolvableModule() {
|
|
58
|
+
return {
|
|
59
|
+
name: 'agent-msg-resolvable',
|
|
60
|
+
compilerVersion: '^0.3.0',
|
|
61
|
+
diagnostics: [
|
|
62
|
+
{
|
|
63
|
+
id: 'llui/agent-msg-resolvable',
|
|
64
|
+
description: 'component<>() Msg type arg must be locally declared or named-imported.',
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
visitors: {
|
|
68
|
+
[ts.SyntaxKind.SourceFile]: (ctx, node) => {
|
|
69
|
+
const visited = node;
|
|
70
|
+
const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true);
|
|
71
|
+
const imports = collectImports(sf);
|
|
72
|
+
const locals = collectLocalTypeNames(sf);
|
|
73
|
+
const walk = (n) => {
|
|
74
|
+
if (ts.isCallExpression(n) &&
|
|
75
|
+
ts.isIdentifier(n.expression) &&
|
|
76
|
+
n.expression.text === 'component' &&
|
|
77
|
+
n.typeArguments &&
|
|
78
|
+
n.typeArguments.length >= 2) {
|
|
79
|
+
const stateArg = n.typeArguments[0];
|
|
80
|
+
const msgArg = n.typeArguments[1];
|
|
81
|
+
const effectArg = n.typeArguments[2];
|
|
82
|
+
const stateText = stateArg.getText(sf);
|
|
83
|
+
const msgText = msgArg.getText(sf);
|
|
84
|
+
const effectText = effectArg ? effectArg.getText(sf) : 'never';
|
|
85
|
+
// Keyword Msg types — `never`, `void`, `undefined`, `unknown`,
|
|
86
|
+
// `any` — are the canonical "no messages" shape for
|
|
87
|
+
// utility/side-effect components like a Vike Page that only
|
|
88
|
+
// renders. Skip the resolvability check; no annotations
|
|
89
|
+
// are expected for those.
|
|
90
|
+
if (msgArg.kind === ts.SyntaxKind.NeverKeyword ||
|
|
91
|
+
msgArg.kind === ts.SyntaxKind.VoidKeyword ||
|
|
92
|
+
msgArg.kind === ts.SyntaxKind.UndefinedKeyword ||
|
|
93
|
+
msgArg.kind === ts.SyntaxKind.UnknownKeyword ||
|
|
94
|
+
msgArg.kind === ts.SyntaxKind.AnyKeyword) {
|
|
95
|
+
ts.forEachChild(n, walk);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
// Identifier-typed arg is the only resolvable shape.
|
|
99
|
+
if (!ts.isTypeReferenceNode(msgArg) || !ts.isIdentifier(msgArg.typeName)) {
|
|
100
|
+
ctx.reportDiagnostic({
|
|
101
|
+
id: 'llui/agent-msg-resolvable',
|
|
102
|
+
severity: 'error',
|
|
103
|
+
category: 'agent',
|
|
104
|
+
message: `\`component<${stateText}, ${msgText}, ${effectText}>()\`: Msg type argument ` +
|
|
105
|
+
`is not a plain identifier. The LLui compiler can only chase identifier-typed ` +
|
|
106
|
+
`type arguments — generics like \`Msg<T>\`, namespace-qualified names like ` +
|
|
107
|
+
`\`m.Msg\`, or inline literal unions are not followed. Replace with a named ` +
|
|
108
|
+
`type alias declared in this file or named-imported from another.`,
|
|
109
|
+
location: {
|
|
110
|
+
file: sf.fileName,
|
|
111
|
+
range: rangeFromOffsets(sf.text, msgArg.getStart(sf), msgArg.getEnd()),
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
ts.forEachChild(n, walk);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const msgName = msgArg.typeName.text;
|
|
118
|
+
if (locals.has(msgName)) {
|
|
119
|
+
ts.forEachChild(n, walk);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (imports.namespaces.has(msgName)) {
|
|
123
|
+
ctx.reportDiagnostic({
|
|
124
|
+
id: 'llui/agent-msg-resolvable',
|
|
125
|
+
severity: 'error',
|
|
126
|
+
category: 'agent',
|
|
127
|
+
message: `\`component<${stateText}, ${msgName}, ${effectText}>()\`: Msg type "${msgName}" ` +
|
|
128
|
+
`comes from a namespace import (\`import * as ...\`). The cross-file resolver ` +
|
|
129
|
+
`does not follow namespace imports — replace with a named import: ` +
|
|
130
|
+
`\`import { ${msgName} } from "..."\`.`,
|
|
131
|
+
location: {
|
|
132
|
+
file: sf.fileName,
|
|
133
|
+
range: rangeFromOffsets(sf.text, msgArg.getStart(sf), msgArg.getEnd()),
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
else if (!imports.named.has(msgName) && !imports.defaults.has(msgName)) {
|
|
138
|
+
ctx.reportDiagnostic({
|
|
139
|
+
id: 'llui/agent-msg-resolvable',
|
|
140
|
+
severity: 'error',
|
|
141
|
+
category: 'agent',
|
|
142
|
+
message: `\`component<${stateText}, ${msgName}, ${effectText}>()\`: Msg type "${msgName}" ` +
|
|
143
|
+
`is neither declared in this file nor imported with a named import. The plugin ` +
|
|
144
|
+
`will emit no annotations and LAP validation will silently disable. Either ` +
|
|
145
|
+
`declare \`type ${msgName} = ...\` here, import it with ` +
|
|
146
|
+
`\`import { ${msgName} } from "..."\`, or replace with an inline union.`,
|
|
147
|
+
location: {
|
|
148
|
+
file: sf.fileName,
|
|
149
|
+
range: rangeFromOffsets(sf.text, msgArg.getStart(sf), msgArg.getEnd()),
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
ts.forEachChild(n, walk);
|
|
155
|
+
};
|
|
156
|
+
walk(sf);
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=agent-msg-resolvable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-msg-resolvable.js","sourceRoot":"","sources":["../../src/modules/agent-msg-resolvable.ts"],"names":[],"mappings":"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","sourcesContent":["// `agent-msg-resolvable` — verifies the `Msg` type argument of every\n// `component<S, M, E>()` call resolves to a type the compiler can\n// statically reach: either a local `type M = …` declaration or a named\n// import from another module.\n//\n// Catches the silent-failure case where M references something the\n// resolver can't follow — a typo, a missing barrel re-export, a\n// namespace import — which would silently disable agent metadata\n// emission. Migrated from\n// `@llui/eslint-plugin/src/rules/agent-msg-resolvable.ts`.\n//\n// File-local check: the compiler doesn't run a full cross-file resolver\n// here (it's available in the broader transform pipeline but not\n// threaded into this module). The file-local form catches:\n// - the M type arg isn't a plain identifier (generics, inline unions,\n// namespace-qualified names);\n// - the M identifier isn't declared locally and isn't named-imported\n// from any source;\n// - the M identifier comes from a namespace import (`import * as`).\n\nimport ts from 'typescript'\nimport { rangeFromOffsets } from '../diagnostic.js'\nimport type { CompilerModule } from '../module.js'\n\ninterface ImportInfo {\n /** Imported via `import { Name } from '...'` (named/value or type). */\n named: Set<string>\n /** Imported via `import Name from '...'` (default). */\n defaults: Set<string>\n /** Imported via `import * as Name from '...'`. */\n namespaces: Set<string>\n}\n\nfunction collectImports(sf: ts.SourceFile): ImportInfo {\n const info: ImportInfo = { named: new Set(), defaults: new Set(), namespaces: new Set() }\n for (const stmt of sf.statements) {\n if (!ts.isImportDeclaration(stmt)) continue\n const clause = stmt.importClause\n if (!clause) continue\n if (clause.name) info.defaults.add(clause.name.text)\n if (clause.namedBindings) {\n if (ts.isNamespaceImport(clause.namedBindings)) {\n info.namespaces.add(clause.namedBindings.name.text)\n } else {\n for (const el of clause.namedBindings.elements) {\n info.named.add(el.name.text)\n }\n }\n }\n }\n return info\n}\n\nfunction collectLocalTypeNames(sf: ts.SourceFile): Set<string> {\n const out = new Set<string>()\n for (const stmt of sf.statements) {\n if (ts.isTypeAliasDeclaration(stmt)) out.add(stmt.name.text)\n else if (ts.isInterfaceDeclaration(stmt)) out.add(stmt.name.text)\n else if (ts.isClassDeclaration(stmt) && stmt.name) out.add(stmt.name.text)\n }\n return out\n}\n\nexport function agentMsgResolvableModule(): CompilerModule {\n return {\n name: 'agent-msg-resolvable',\n compilerVersion: '^0.3.0',\n diagnostics: [\n {\n id: 'llui/agent-msg-resolvable',\n description: 'component<>() Msg type arg must be locally declared or named-imported.',\n },\n ],\n visitors: {\n [ts.SyntaxKind.SourceFile]: (ctx, node) => {\n const visited = node as ts.SourceFile\n const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true)\n const imports = collectImports(sf)\n const locals = collectLocalTypeNames(sf)\n const walk = (n: ts.Node): void => {\n if (\n ts.isCallExpression(n) &&\n ts.isIdentifier(n.expression) &&\n n.expression.text === 'component' &&\n n.typeArguments &&\n n.typeArguments.length >= 2\n ) {\n const stateArg = n.typeArguments[0]!\n const msgArg = n.typeArguments[1]!\n const effectArg = n.typeArguments[2]\n const stateText = stateArg.getText(sf)\n const msgText = msgArg.getText(sf)\n const effectText = effectArg ? effectArg.getText(sf) : 'never'\n\n // Keyword Msg types — `never`, `void`, `undefined`, `unknown`,\n // `any` — are the canonical \"no messages\" shape for\n // utility/side-effect components like a Vike Page that only\n // renders. Skip the resolvability check; no annotations\n // are expected for those.\n if (\n msgArg.kind === ts.SyntaxKind.NeverKeyword ||\n msgArg.kind === ts.SyntaxKind.VoidKeyword ||\n msgArg.kind === ts.SyntaxKind.UndefinedKeyword ||\n msgArg.kind === ts.SyntaxKind.UnknownKeyword ||\n msgArg.kind === ts.SyntaxKind.AnyKeyword\n ) {\n ts.forEachChild(n, walk)\n return\n }\n // Identifier-typed arg is the only resolvable shape.\n if (!ts.isTypeReferenceNode(msgArg) || !ts.isIdentifier(msgArg.typeName)) {\n ctx.reportDiagnostic({\n id: 'llui/agent-msg-resolvable',\n severity: 'error',\n category: 'agent',\n message:\n `\\`component<${stateText}, ${msgText}, ${effectText}>()\\`: Msg type argument ` +\n `is not a plain identifier. The LLui compiler can only chase identifier-typed ` +\n `type arguments — generics like \\`Msg<T>\\`, namespace-qualified names like ` +\n `\\`m.Msg\\`, or inline literal unions are not followed. Replace with a named ` +\n `type alias declared in this file or named-imported from another.`,\n location: {\n file: sf.fileName,\n range: rangeFromOffsets(sf.text, msgArg.getStart(sf), msgArg.getEnd()),\n },\n })\n ts.forEachChild(n, walk)\n return\n }\n const msgName = msgArg.typeName.text\n if (locals.has(msgName)) {\n ts.forEachChild(n, walk)\n return\n }\n if (imports.namespaces.has(msgName)) {\n ctx.reportDiagnostic({\n id: 'llui/agent-msg-resolvable',\n severity: 'error',\n category: 'agent',\n message:\n `\\`component<${stateText}, ${msgName}, ${effectText}>()\\`: Msg type \"${msgName}\" ` +\n `comes from a namespace import (\\`import * as ...\\`). The cross-file resolver ` +\n `does not follow namespace imports — replace with a named import: ` +\n `\\`import { ${msgName} } from \"...\"\\`.`,\n location: {\n file: sf.fileName,\n range: rangeFromOffsets(sf.text, msgArg.getStart(sf), msgArg.getEnd()),\n },\n })\n } else if (!imports.named.has(msgName) && !imports.defaults.has(msgName)) {\n ctx.reportDiagnostic({\n id: 'llui/agent-msg-resolvable',\n severity: 'error',\n category: 'agent',\n message:\n `\\`component<${stateText}, ${msgName}, ${effectText}>()\\`: Msg type \"${msgName}\" ` +\n `is neither declared in this file nor imported with a named import. The plugin ` +\n `will emit no annotations and LAP validation will silently disable. Either ` +\n `declare \\`type ${msgName} = ...\\` here, import it with ` +\n `\\`import { ${msgName} } from \"...\"\\`, or replace with an inline union.`,\n location: {\n file: sf.fileName,\n range: rangeFromOffsets(sf.text, msgArg.getStart(sf), msgArg.getEnd()),\n },\n })\n }\n }\n ts.forEachChild(n, walk)\n }\n walk(sf)\n },\n },\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-nonextractable-handler.d.ts","sourceRoot":"","sources":["../../src/modules/agent-nonextractable-handler.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AA8BlD,wBAAgB,gCAAgC,IAAI,cAAc,CA6EjE"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// `agent-nonextractable-handler` — errors when a `send(...)` call inside
|
|
2
|
+
// view() has a first argument that is NOT an object literal with a
|
|
3
|
+
// string-literal `type` field. Statically-extractable handlers are how
|
|
4
|
+
// the agent's `list_actions` advertises affordances; dynamic shapes
|
|
5
|
+
// like `send(makeMsg())` or `send({ type: variant })` are invisible to
|
|
6
|
+
// the static walker. Migrated from
|
|
7
|
+
// `@llui/eslint-plugin/src/rules/agent-nonextractable-handler.ts`.
|
|
8
|
+
//
|
|
9
|
+
// Note: the ESLint rule gated on whether `@llui/agent` is installed in
|
|
10
|
+
// the consumer's nearest package.json. The compiler doesn't have a
|
|
11
|
+
// natural way to walk up to package.json from inside a transform —
|
|
12
|
+
// and the consumer's intent is captured by the build pipeline already
|
|
13
|
+
// (if you don't ship agent metadata, the diagnostic is information-
|
|
14
|
+
// only). Erroring unconditionally is consistent with the
|
|
15
|
+
// LLM-first stance: agent-extractable handlers are always preferable.
|
|
16
|
+
import ts from 'typescript';
|
|
17
|
+
import { rangeFromOffsets } from '../diagnostic.js';
|
|
18
|
+
import { findComponentCalls } from './_shared.js';
|
|
19
|
+
function findViewProperty(call) {
|
|
20
|
+
const arg = call.arguments[0];
|
|
21
|
+
if (!arg || !ts.isObjectLiteralExpression(arg))
|
|
22
|
+
return undefined;
|
|
23
|
+
for (const prop of arg.properties) {
|
|
24
|
+
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'view') {
|
|
25
|
+
return prop;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
function readTypeLiteral(obj) {
|
|
31
|
+
for (const prop of obj.properties) {
|
|
32
|
+
if (!ts.isPropertyAssignment(prop))
|
|
33
|
+
continue;
|
|
34
|
+
const isTypeKey = (ts.isIdentifier(prop.name) && prop.name.text === 'type') ||
|
|
35
|
+
(ts.isStringLiteral(prop.name) && prop.name.text === 'type');
|
|
36
|
+
if (!isTypeKey)
|
|
37
|
+
continue;
|
|
38
|
+
if (ts.isStringLiteral(prop.initializer))
|
|
39
|
+
return prop.initializer.text;
|
|
40
|
+
if (ts.isNoSubstitutionTemplateLiteral(prop.initializer)) {
|
|
41
|
+
return prop.initializer.text;
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
export function agentNonextractableHandlerModule() {
|
|
48
|
+
return {
|
|
49
|
+
name: 'agent-nonextractable-handler',
|
|
50
|
+
compilerVersion: '^0.3.0',
|
|
51
|
+
diagnostics: [
|
|
52
|
+
{
|
|
53
|
+
id: 'llui/agent-nonextractable-handler',
|
|
54
|
+
description: "send() in view with non-literal-typed argument — Claude's list_actions won't advertise this action.",
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
visitors: {
|
|
58
|
+
[ts.SyntaxKind.SourceFile]: (ctx, node) => {
|
|
59
|
+
const visited = node;
|
|
60
|
+
const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true);
|
|
61
|
+
for (const call of findComponentCalls(sf)) {
|
|
62
|
+
const viewProp = findViewProperty(call);
|
|
63
|
+
if (!viewProp)
|
|
64
|
+
continue;
|
|
65
|
+
const fn = viewProp.initializer;
|
|
66
|
+
if (!ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn))
|
|
67
|
+
continue;
|
|
68
|
+
if (!fn.body)
|
|
69
|
+
continue;
|
|
70
|
+
const walk = (n) => {
|
|
71
|
+
if (ts.isCallExpression(n) &&
|
|
72
|
+
ts.isIdentifier(n.expression) &&
|
|
73
|
+
n.expression.text === 'send') {
|
|
74
|
+
const first = n.arguments[0];
|
|
75
|
+
let nonextractable = false;
|
|
76
|
+
// Translator-passthrough exemption: `(m) => send(m)` or
|
|
77
|
+
// `(m) => send(m as never)` is a library-Msg forwarder.
|
|
78
|
+
// The actual dispatched type comes from the library's
|
|
79
|
+
// own variants, surfaced via the universal handler-tagger
|
|
80
|
+
// (binding-descriptors), not from this call site. The
|
|
81
|
+
// ESLint rule didn't have this exemption explicitly, but
|
|
82
|
+
// form-validation's `form.connect((s) => s.form, (m) => send(m as never))`
|
|
83
|
+
// pattern is the canonical case — flagging it is noise.
|
|
84
|
+
const isPassthroughId = (e) => {
|
|
85
|
+
let cur = e;
|
|
86
|
+
while (ts.isAsExpression(cur) || ts.isParenthesizedExpression(cur)) {
|
|
87
|
+
cur = cur.expression;
|
|
88
|
+
}
|
|
89
|
+
return ts.isIdentifier(cur);
|
|
90
|
+
};
|
|
91
|
+
if (!first) {
|
|
92
|
+
nonextractable = true;
|
|
93
|
+
}
|
|
94
|
+
else if (isPassthroughId(first)) {
|
|
95
|
+
// Bare identifier (possibly `as`-cast) — passthrough.
|
|
96
|
+
}
|
|
97
|
+
else if (!ts.isObjectLiteralExpression(first)) {
|
|
98
|
+
nonextractable = true;
|
|
99
|
+
}
|
|
100
|
+
else if (readTypeLiteral(first) === null) {
|
|
101
|
+
nonextractable = true;
|
|
102
|
+
}
|
|
103
|
+
if (nonextractable) {
|
|
104
|
+
ctx.reportDiagnostic({
|
|
105
|
+
id: 'llui/agent-nonextractable-handler',
|
|
106
|
+
severity: 'error',
|
|
107
|
+
category: 'agent',
|
|
108
|
+
message: `\`send()\` call in view isn't statically extractable; the agent's ` +
|
|
109
|
+
`\`list_actions\` won't advertise this action. Prefer \`send({ type: 'literal' })\` ` +
|
|
110
|
+
`with a string-literal \`type\`; avoid computed dispatch (\`send(makeMsg())\`, ` +
|
|
111
|
+
`\`send({ type: variant })\`).`,
|
|
112
|
+
location: {
|
|
113
|
+
file: sf.fileName,
|
|
114
|
+
range: rangeFromOffsets(sf.text, n.getStart(sf), n.getEnd()),
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
ts.forEachChild(n, walk);
|
|
120
|
+
};
|
|
121
|
+
walk(fn.body);
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=agent-nonextractable-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-nonextractable-handler.js","sourceRoot":"","sources":["../../src/modules/agent-nonextractable-handler.ts"],"names":[],"mappings":"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","sourcesContent":["// `agent-nonextractable-handler` — errors when a `send(...)` call inside\n// view() has a first argument that is NOT an object literal with a\n// string-literal `type` field. Statically-extractable handlers are how\n// the agent's `list_actions` advertises affordances; dynamic shapes\n// like `send(makeMsg())` or `send({ type: variant })` are invisible to\n// the static walker. Migrated from\n// `@llui/eslint-plugin/src/rules/agent-nonextractable-handler.ts`.\n//\n// Note: the ESLint rule gated on whether `@llui/agent` is installed in\n// the consumer's nearest package.json. The compiler doesn't have a\n// natural way to walk up to package.json from inside a transform —\n// and the consumer's intent is captured by the build pipeline already\n// (if you don't ship agent metadata, the diagnostic is information-\n// only). Erroring unconditionally is consistent with the\n// LLM-first stance: agent-extractable handlers are always preferable.\n\nimport ts from 'typescript'\nimport { rangeFromOffsets } from '../diagnostic.js'\nimport type { CompilerModule } from '../module.js'\nimport { findComponentCalls } from './_shared.js'\n\nfunction findViewProperty(call: ts.CallExpression): ts.PropertyAssignment | undefined {\n const arg = call.arguments[0]\n if (!arg || !ts.isObjectLiteralExpression(arg)) return undefined\n for (const prop of arg.properties) {\n if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'view') {\n return prop\n }\n }\n return undefined\n}\n\nfunction readTypeLiteral(obj: ts.ObjectLiteralExpression): string | null {\n for (const prop of obj.properties) {\n if (!ts.isPropertyAssignment(prop)) continue\n const isTypeKey =\n (ts.isIdentifier(prop.name) && prop.name.text === 'type') ||\n (ts.isStringLiteral(prop.name) && prop.name.text === 'type')\n if (!isTypeKey) continue\n if (ts.isStringLiteral(prop.initializer)) return prop.initializer.text\n if (ts.isNoSubstitutionTemplateLiteral(prop.initializer)) {\n return prop.initializer.text\n }\n return null\n }\n return null\n}\n\nexport function agentNonextractableHandlerModule(): CompilerModule {\n return {\n name: 'agent-nonextractable-handler',\n compilerVersion: '^0.3.0',\n diagnostics: [\n {\n id: 'llui/agent-nonextractable-handler',\n description:\n \"send() in view with non-literal-typed argument — Claude's list_actions won't advertise this action.\",\n },\n ],\n visitors: {\n [ts.SyntaxKind.SourceFile]: (ctx, node) => {\n const visited = node as ts.SourceFile\n const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true)\n for (const call of findComponentCalls(sf)) {\n const viewProp = findViewProperty(call)\n if (!viewProp) continue\n const fn = viewProp.initializer\n if (!ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn)) continue\n if (!fn.body) continue\n const walk = (n: ts.Node): void => {\n if (\n ts.isCallExpression(n) &&\n ts.isIdentifier(n.expression) &&\n n.expression.text === 'send'\n ) {\n const first = n.arguments[0]\n let nonextractable = false\n // Translator-passthrough exemption: `(m) => send(m)` or\n // `(m) => send(m as never)` is a library-Msg forwarder.\n // The actual dispatched type comes from the library's\n // own variants, surfaced via the universal handler-tagger\n // (binding-descriptors), not from this call site. The\n // ESLint rule didn't have this exemption explicitly, but\n // form-validation's `form.connect((s) => s.form, (m) => send(m as never))`\n // pattern is the canonical case — flagging it is noise.\n const isPassthroughId = (e: ts.Node): boolean => {\n let cur: ts.Node = e\n while (ts.isAsExpression(cur) || ts.isParenthesizedExpression(cur)) {\n cur = cur.expression\n }\n return ts.isIdentifier(cur)\n }\n if (!first) {\n nonextractable = true\n } else if (isPassthroughId(first)) {\n // Bare identifier (possibly `as`-cast) — passthrough.\n } else if (!ts.isObjectLiteralExpression(first)) {\n nonextractable = true\n } else if (readTypeLiteral(first) === null) {\n nonextractable = true\n }\n if (nonextractable) {\n ctx.reportDiagnostic({\n id: 'llui/agent-nonextractable-handler',\n severity: 'error',\n category: 'agent',\n message:\n `\\`send()\\` call in view isn't statically extractable; the agent's ` +\n `\\`list_actions\\` won't advertise this action. Prefer \\`send({ type: 'literal' })\\` ` +\n `with a string-literal \\`type\\`; avoid computed dispatch (\\`send(makeMsg())\\`, ` +\n `\\`send({ type: variant })\\`).`,\n location: {\n file: sf.fileName,\n range: rangeFromOffsets(sf.text, n.getStart(sf), n.getEnd()),\n },\n })\n }\n }\n ts.forEachChild(n, walk)\n }\n walk(fn.body)\n }\n },\n },\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-optional-field-undocumented.d.ts","sourceRoot":"","sources":["../../src/modules/agent-optional-field-undocumented.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAKlD,wBAAgB,oCAAoC,IAAI,cAAc,CAsDrE"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// `agent-optional-field-undocumented` — errors when a Msg variant's
|
|
2
|
+
// optional field (TS `?:`) lacks any leading JSDoc. Optional fields
|
|
3
|
+
// are the most ambiguous part of a payload from an LLM's perspective —
|
|
4
|
+
// some hint is required (`@should("…")` is the LLui idiom, but any
|
|
5
|
+
// JSDoc satisfies the rule). Variants annotated `@humanOnly` are
|
|
6
|
+
// skipped — the LLM never sees them. Migrated from
|
|
7
|
+
// `@llui/eslint-plugin/src/rules/agent-optional-field-undocumented.ts`.
|
|
8
|
+
import ts from 'typescript';
|
|
9
|
+
import { rangeFromOffsets } from '../diagnostic.js';
|
|
10
|
+
import { forEachMsgVariant } from './_msg-variants.js';
|
|
11
|
+
const JSDOC_BLOCK_RE = /\/\*\*[\s\S]*?\*\//;
|
|
12
|
+
export function agentOptionalFieldUndocumentedModule() {
|
|
13
|
+
return {
|
|
14
|
+
name: 'agent-optional-field-undocumented',
|
|
15
|
+
compilerVersion: '^0.3.0',
|
|
16
|
+
diagnostics: [
|
|
17
|
+
{
|
|
18
|
+
id: 'llui/agent-optional-field-undocumented',
|
|
19
|
+
description: 'Optional Msg-variant field has no JSDoc — add @should("hint") or any JSDoc.',
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
visitors: {
|
|
23
|
+
[ts.SyntaxKind.SourceFile]: (ctx, node) => {
|
|
24
|
+
const visited = node;
|
|
25
|
+
const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true);
|
|
26
|
+
forEachMsgVariant(sf, ({ variant, node: typeLit, leadingCommentText }) => {
|
|
27
|
+
if (/@humanOnly\b/.test(leadingCommentText))
|
|
28
|
+
return;
|
|
29
|
+
let prevEnd = typeLit.getStart(sf) + 1; // skip the opening brace
|
|
30
|
+
for (const sig of typeLit.members) {
|
|
31
|
+
if (!ts.isPropertySignature(sig) || !sig.name || !ts.isIdentifier(sig.name)) {
|
|
32
|
+
prevEnd = sig.getEnd();
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const fieldName = sig.name.text;
|
|
36
|
+
if (fieldName === 'type') {
|
|
37
|
+
prevEnd = sig.getEnd();
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (!sig.questionToken) {
|
|
41
|
+
prevEnd = sig.getEnd();
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const text = sf.text.slice(prevEnd, sig.getStart(sf));
|
|
45
|
+
if (!JSDOC_BLOCK_RE.test(text)) {
|
|
46
|
+
ctx.reportDiagnostic({
|
|
47
|
+
id: 'llui/agent-optional-field-undocumented',
|
|
48
|
+
severity: 'error',
|
|
49
|
+
category: 'agent',
|
|
50
|
+
message: `Msg variant "${variant}" has optional field "${fieldName}" with no JSDoc. ` +
|
|
51
|
+
`Add \`@should("hint")\` if the LLM should fill it in unless it has a specific ` +
|
|
52
|
+
`reason not to, or any JSDoc block to mark it intentionally undocumented for the agent. ` +
|
|
53
|
+
`Example: \`/** @should("the user's display name") */ name?: string\`.`,
|
|
54
|
+
location: {
|
|
55
|
+
file: sf.fileName,
|
|
56
|
+
range: rangeFromOffsets(sf.text, sig.getStart(sf), sig.getEnd()),
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
prevEnd = sig.getEnd();
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=agent-optional-field-undocumented.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-optional-field-undocumented.js","sourceRoot":"","sources":["../../src/modules/agent-optional-field-undocumented.ts"],"names":[],"mappings":"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","sourcesContent":["// `agent-optional-field-undocumented` — errors when a Msg variant's\n// optional field (TS `?:`) lacks any leading JSDoc. Optional fields\n// are the most ambiguous part of a payload from an LLM's perspective —\n// some hint is required (`@should(\"…\")` is the LLui idiom, but any\n// JSDoc satisfies the rule). Variants annotated `@humanOnly` are\n// skipped — the LLM never sees them. Migrated from\n// `@llui/eslint-plugin/src/rules/agent-optional-field-undocumented.ts`.\n\nimport ts from 'typescript'\nimport { rangeFromOffsets } from '../diagnostic.js'\nimport type { CompilerModule } from '../module.js'\nimport { forEachMsgVariant } from './_msg-variants.js'\n\nconst JSDOC_BLOCK_RE = /\\/\\*\\*[\\s\\S]*?\\*\\//\n\nexport function agentOptionalFieldUndocumentedModule(): CompilerModule {\n return {\n name: 'agent-optional-field-undocumented',\n compilerVersion: '^0.3.0',\n diagnostics: [\n {\n id: 'llui/agent-optional-field-undocumented',\n description: 'Optional Msg-variant field has no JSDoc — add @should(\"hint\") or any JSDoc.',\n },\n ],\n visitors: {\n [ts.SyntaxKind.SourceFile]: (ctx, node) => {\n const visited = node as ts.SourceFile\n const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true)\n forEachMsgVariant(sf, ({ variant, node: typeLit, leadingCommentText }) => {\n if (/@humanOnly\\b/.test(leadingCommentText)) return\n let prevEnd = typeLit.getStart(sf) + 1 // skip the opening brace\n for (const sig of typeLit.members) {\n if (!ts.isPropertySignature(sig) || !sig.name || !ts.isIdentifier(sig.name)) {\n prevEnd = sig.getEnd()\n continue\n }\n const fieldName = sig.name.text\n if (fieldName === 'type') {\n prevEnd = sig.getEnd()\n continue\n }\n if (!sig.questionToken) {\n prevEnd = sig.getEnd()\n continue\n }\n const text = sf.text.slice(prevEnd, sig.getStart(sf))\n if (!JSDOC_BLOCK_RE.test(text)) {\n ctx.reportDiagnostic({\n id: 'llui/agent-optional-field-undocumented',\n severity: 'error',\n category: 'agent',\n message:\n `Msg variant \"${variant}\" has optional field \"${fieldName}\" with no JSDoc. ` +\n `Add \\`@should(\"hint\")\\` if the LLM should fill it in unless it has a specific ` +\n `reason not to, or any JSDoc block to mark it intentionally undocumented for the agent. ` +\n `Example: \\`/** @should(\"the user's display name\") */ name?: string\\`.`,\n location: {\n file: sf.fileName,\n range: rangeFromOffsets(sf.text, sig.getStart(sf), sig.getEnd()),\n },\n })\n }\n prevEnd = sig.getEnd()\n }\n })\n },\n },\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-tagsend-translator-missing.d.ts","sourceRoot":"","sources":["../../src/modules/agent-tagsend-translator-missing.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAElD,wBAAgB,mCAAmC,IAAI,cAAc,CAkDpE"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// `agent-tagsend-translator-missing` — errors when `*.connect(get, send,
|
|
2
|
+
// …)` is called with the raw component `send` as its 2nd argument. The
|
|
3
|
+
// library's internal Msgs flow through `send` directly and surface in
|
|
4
|
+
// the agent's `list_actions` via `tagSend`, leaking library plumbing
|
|
5
|
+
// (`move`, `drop`, `cancel`, etc.) into the agent affordance list.
|
|
6
|
+
// The fix is to wrap with a translator: `(libMsg) => send({ type: '…',
|
|
7
|
+
// msg: libMsg })`. Migrated from
|
|
8
|
+
// `@llui/eslint-plugin/src/rules/agent-tagsend-translator-missing.ts`.
|
|
9
|
+
import ts from 'typescript';
|
|
10
|
+
import { rangeFromOffsets } from '../diagnostic.js';
|
|
11
|
+
export function agentTagsendTranslatorMissingModule() {
|
|
12
|
+
return {
|
|
13
|
+
name: 'agent-tagsend-translator-missing',
|
|
14
|
+
compilerVersion: '^0.3.0',
|
|
15
|
+
diagnostics: [
|
|
16
|
+
{
|
|
17
|
+
id: 'llui/agent-tagsend-translator-missing',
|
|
18
|
+
description: '`*.connect(get, send, ...)` passes raw `send` — library Msgs leak into agent affordances.',
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
visitors: {
|
|
22
|
+
[ts.SyntaxKind.SourceFile]: (ctx, node) => {
|
|
23
|
+
const visited = node;
|
|
24
|
+
const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true);
|
|
25
|
+
const walk = (n) => {
|
|
26
|
+
if (ts.isCallExpression(n) &&
|
|
27
|
+
ts.isPropertyAccessExpression(n.expression) &&
|
|
28
|
+
ts.isIdentifier(n.expression.name) &&
|
|
29
|
+
n.expression.name.text === 'connect' &&
|
|
30
|
+
n.arguments.length >= 2) {
|
|
31
|
+
const sendArg = n.arguments[1];
|
|
32
|
+
if (ts.isIdentifier(sendArg) && sendArg.text === 'send') {
|
|
33
|
+
const calleeName = ts.isIdentifier(n.expression.expression)
|
|
34
|
+
? n.expression.expression.text
|
|
35
|
+
: '<lib>';
|
|
36
|
+
ctx.reportDiagnostic({
|
|
37
|
+
id: 'llui/agent-tagsend-translator-missing',
|
|
38
|
+
severity: 'error',
|
|
39
|
+
category: 'agent',
|
|
40
|
+
message: `\`${calleeName}.connect(...)\` receives the raw component \`send\` as its 2nd ` +
|
|
41
|
+
`argument. Library-internal Msgs will leak into the agent's \`list_actions\` via ` +
|
|
42
|
+
`\`tagSend\`. Wrap with a translator that maps library Msgs to your domain Msgs: ` +
|
|
43
|
+
`\`(libMsg) => send({ type: '<YourMsg>', msg: libMsg })\`.`,
|
|
44
|
+
location: {
|
|
45
|
+
file: sf.fileName,
|
|
46
|
+
range: rangeFromOffsets(sf.text, n.getStart(sf), n.getEnd()),
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
ts.forEachChild(n, walk);
|
|
52
|
+
};
|
|
53
|
+
walk(sf);
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=agent-tagsend-translator-missing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-tagsend-translator-missing.js","sourceRoot":"","sources":["../../src/modules/agent-tagsend-translator-missing.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,uEAAuE;AACvE,sEAAsE;AACtE,qEAAqE;AACrE,mEAAmE;AACnE,uEAAuE;AACvE,iCAAiC;AACjC,uEAAuE;AAEvE,OAAO,EAAE,MAAM,YAAY,CAAA;AAC3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAGnD,MAAM,UAAU,mCAAmC;IACjD,OAAO;QACL,IAAI,EAAE,kCAAkC;QACxC,eAAe,EAAE,QAAQ;QACzB,WAAW,EAAE;YACX;gBACE,EAAE,EAAE,uCAAuC;gBAC3C,WAAW,EACT,2FAA2F;aAC9F;SACF;QACD,QAAQ,EAAE;YACR,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;gBACxC,MAAM,OAAO,GAAG,IAAqB,CAAA;gBACrC,MAAM,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;gBAC5F,MAAM,IAAI,GAAG,CAAC,CAAU,EAAQ,EAAE;oBAChC,IACE,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC;wBACtB,EAAE,CAAC,0BAA0B,CAAC,CAAC,CAAC,UAAU,CAAC;wBAC3C,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC;wBAClC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS;wBACpC,CAAC,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,EACvB,CAAC;wBACD,MAAM,OAAO,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAE,CAAA;wBAC/B,IAAI,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;4BACxD,MAAM,UAAU,GAAG,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC;gCACzD,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI;gCAC9B,CAAC,CAAC,OAAO,CAAA;4BACX,GAAG,CAAC,gBAAgB,CAAC;gCACnB,EAAE,EAAE,uCAAuC;gCAC3C,QAAQ,EAAE,OAAO;gCACjB,QAAQ,EAAE,OAAO;gCACjB,OAAO,EACL,KAAK,UAAU,iEAAiE;oCAChF,kFAAkF;oCAClF,kFAAkF;oCAClF,2DAA2D;gCAC7D,QAAQ,EAAE;oCACR,IAAI,EAAE,EAAE,CAAC,QAAQ;oCACjB,KAAK,EAAE,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;iCAC7D;6BACF,CAAC,CAAA;wBACJ,CAAC;oBACH,CAAC;oBACD,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;gBAC1B,CAAC,CAAA;gBACD,IAAI,CAAC,EAAE,CAAC,CAAA;YACV,CAAC;SACF;KACF,CAAA;AACH,CAAC","sourcesContent":["// `agent-tagsend-translator-missing` — errors when `*.connect(get, send,\n// …)` is called with the raw component `send` as its 2nd argument. The\n// library's internal Msgs flow through `send` directly and surface in\n// the agent's `list_actions` via `tagSend`, leaking library plumbing\n// (`move`, `drop`, `cancel`, etc.) into the agent affordance list.\n// The fix is to wrap with a translator: `(libMsg) => send({ type: '…',\n// msg: libMsg })`. Migrated from\n// `@llui/eslint-plugin/src/rules/agent-tagsend-translator-missing.ts`.\n\nimport ts from 'typescript'\nimport { rangeFromOffsets } from '../diagnostic.js'\nimport type { CompilerModule } from '../module.js'\n\nexport function agentTagsendTranslatorMissingModule(): CompilerModule {\n return {\n name: 'agent-tagsend-translator-missing',\n compilerVersion: '^0.3.0',\n diagnostics: [\n {\n id: 'llui/agent-tagsend-translator-missing',\n description:\n '`*.connect(get, send, ...)` passes raw `send` — library Msgs leak into agent affordances.',\n },\n ],\n visitors: {\n [ts.SyntaxKind.SourceFile]: (ctx, node) => {\n const visited = node as ts.SourceFile\n const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true)\n const walk = (n: ts.Node): void => {\n if (\n ts.isCallExpression(n) &&\n ts.isPropertyAccessExpression(n.expression) &&\n ts.isIdentifier(n.expression.name) &&\n n.expression.name.text === 'connect' &&\n n.arguments.length >= 2\n ) {\n const sendArg = n.arguments[1]!\n if (ts.isIdentifier(sendArg) && sendArg.text === 'send') {\n const calleeName = ts.isIdentifier(n.expression.expression)\n ? n.expression.expression.text\n : '<lib>'\n ctx.reportDiagnostic({\n id: 'llui/agent-tagsend-translator-missing',\n severity: 'error',\n category: 'agent',\n message:\n `\\`${calleeName}.connect(...)\\` receives the raw component \\`send\\` as its 2nd ` +\n `argument. Library-internal Msgs will leak into the agent's \\`list_actions\\` via ` +\n `\\`tagSend\\`. Wrap with a translator that maps library Msgs to your domain Msgs: ` +\n `\\`(libMsg) => send({ type: '<YourMsg>', msg: libMsg })\\`.`,\n location: {\n file: sf.fileName,\n range: rangeFromOffsets(sf.text, n.getStart(sf), n.getEnd()),\n },\n })\n }\n }\n ts.forEachChild(n, walk)\n }\n walk(sf)\n },\n },\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-warning-on-confirm.d.ts","sourceRoot":"","sources":["../../src/modules/agent-warning-on-confirm.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAGlD,wBAAgB,2BAA2B,IAAI,cAAc,CAkC5D"}
|