@syrin/cli 1.3.1 → 1.4.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.
Files changed (150) hide show
  1. package/README.md +36 -0
  2. package/dist/cli/commands/config.d.ts +47 -0
  3. package/dist/cli/commands/config.js +360 -0
  4. package/dist/cli/commands/dev.d.ts +6 -0
  5. package/dist/cli/commands/dev.js +67 -15
  6. package/dist/cli/commands/doctor.js +49 -13
  7. package/dist/cli/commands/init.d.ts +2 -0
  8. package/dist/cli/commands/init.js +89 -18
  9. package/dist/cli/commands/status.d.ts +10 -0
  10. package/dist/cli/commands/status.js +162 -0
  11. package/dist/cli/index.js +211 -12
  12. package/dist/cli/prompts/init-prompt.d.ts +18 -0
  13. package/dist/cli/prompts/init-prompt.js +159 -99
  14. package/dist/cli/utils/command-error-handler.js +2 -5
  15. package/dist/config/env-checker.d.ts +12 -2
  16. package/dist/config/env-checker.js +88 -38
  17. package/dist/config/env-templates.d.ts +15 -0
  18. package/dist/config/env-templates.js +49 -0
  19. package/dist/config/generator.js +17 -0
  20. package/dist/config/global-loader.d.ts +50 -0
  21. package/dist/config/global-loader.js +244 -0
  22. package/dist/config/loader.d.ts +28 -0
  23. package/dist/config/loader.js +95 -9
  24. package/dist/config/merger.d.ts +37 -0
  25. package/dist/config/merger.js +68 -0
  26. package/dist/config/schema.d.ts +26 -1
  27. package/dist/config/schema.js +73 -8
  28. package/dist/config/types.d.ts +19 -0
  29. package/dist/config/types.js +26 -1
  30. package/dist/constants/messages.d.ts +7 -0
  31. package/dist/constants/messages.js +8 -0
  32. package/dist/constants/paths.d.ts +6 -0
  33. package/dist/constants/paths.js +10 -0
  34. package/dist/events/emitter.js +7 -7
  35. package/dist/index.js +0 -0
  36. package/dist/presentation/config-ui.d.ts +34 -0
  37. package/dist/presentation/config-ui.js +139 -0
  38. package/dist/presentation/doctor-ui.d.ts +11 -0
  39. package/dist/presentation/doctor-ui.js +52 -1
  40. package/dist/presentation/init-ui.d.ts +9 -0
  41. package/dist/presentation/init-ui.js +33 -0
  42. package/dist/runtime/analysis/analyser.js +2 -2
  43. package/dist/runtime/analysis/rules/warnings/w104-generic-description.d.ts +1 -1
  44. package/dist/runtime/analysis/rules/warnings/w104-generic-description.js +1 -1
  45. package/dist/runtime/dev/event-mapper.js +19 -3
  46. package/dist/runtime/dev/session.d.ts +4 -0
  47. package/dist/runtime/dev/session.js +52 -3
  48. package/dist/runtime/llm/ollama.js +4 -4
  49. package/dist/runtime/mcp/client/manager.js +3 -3
  50. package/dist/runtime/sandbox/executor.js +5 -5
  51. package/dist/runtime/test/orchestrator.js +4 -4
  52. package/dist/utils/editor.d.ts +37 -0
  53. package/dist/utils/editor.js +137 -0
  54. package/dist/utils/logger.d.ts +24 -6
  55. package/dist/utils/logger.js +51 -8
  56. package/package.json +4 -4
  57. package/dist/runtime/analysis/rules/errors/e001-missing-output-schema.d.ts +0 -22
  58. package/dist/runtime/analysis/rules/errors/e001-missing-output-schema.js +0 -30
  59. package/dist/runtime/analysis/rules/errors/e002-underspecified-input.d.ts +0 -24
  60. package/dist/runtime/analysis/rules/errors/e002-underspecified-input.js +0 -52
  61. package/dist/runtime/analysis/rules/errors/e003-type-mismatch.d.ts +0 -23
  62. package/dist/runtime/analysis/rules/errors/e003-type-mismatch.js +0 -73
  63. package/dist/runtime/analysis/rules/errors/e004-free-text-propagation.d.ts +0 -23
  64. package/dist/runtime/analysis/rules/errors/e004-free-text-propagation.js +0 -47
  65. package/dist/runtime/analysis/rules/errors/e005-tool-ambiguity.d.ts +0 -25
  66. package/dist/runtime/analysis/rules/errors/e005-tool-ambiguity.js +0 -73
  67. package/dist/runtime/analysis/rules/errors/e006-param-not-in-description.d.ts +0 -22
  68. package/dist/runtime/analysis/rules/errors/e006-param-not-in-description.js +0 -57
  69. package/dist/runtime/analysis/rules/errors/e007-output-not-guaranteed.d.ts +0 -23
  70. package/dist/runtime/analysis/rules/errors/e007-output-not-guaranteed.js +0 -56
  71. package/dist/runtime/analysis/rules/errors/e008-circular-dependency.d.ts +0 -22
  72. package/dist/runtime/analysis/rules/errors/e008-circular-dependency.js +0 -84
  73. package/dist/runtime/analysis/rules/errors/e009-implicit-user-input.d.ts +0 -23
  74. package/dist/runtime/analysis/rules/errors/e009-implicit-user-input.js +0 -89
  75. package/dist/runtime/analysis/rules/errors/e010-non-serializable.d.ts +0 -25
  76. package/dist/runtime/analysis/rules/errors/e010-non-serializable.js +0 -46
  77. package/dist/runtime/analysis/rules/errors/e011-missing-tool-description.d.ts +0 -24
  78. package/dist/runtime/analysis/rules/errors/e011-missing-tool-description.js +0 -33
  79. package/dist/runtime/analysis/rules/errors/e012-side-effect-detected.d.ts +0 -39
  80. package/dist/runtime/analysis/rules/errors/e012-side-effect-detected.js +0 -40
  81. package/dist/runtime/analysis/rules/errors/e013-non-deterministic-output.d.ts +0 -37
  82. package/dist/runtime/analysis/rules/errors/e013-non-deterministic-output.js +0 -34
  83. package/dist/runtime/analysis/rules/errors/e013-output-explosion.d.ts +0 -39
  84. package/dist/runtime/analysis/rules/errors/e013-output-explosion.js +0 -36
  85. package/dist/runtime/analysis/rules/errors/e014-hidden-dependency.d.ts +0 -42
  86. package/dist/runtime/analysis/rules/errors/e014-hidden-dependency.js +0 -46
  87. package/dist/runtime/analysis/rules/errors/e014-output-explosion.d.ts +0 -39
  88. package/dist/runtime/analysis/rules/errors/e014-output-explosion.js +0 -36
  89. package/dist/runtime/analysis/rules/errors/e015-hidden-dependency.d.ts +0 -42
  90. package/dist/runtime/analysis/rules/errors/e015-hidden-dependency.js +0 -46
  91. package/dist/runtime/analysis/rules/errors/e015-unbounded-execution.d.ts +0 -44
  92. package/dist/runtime/analysis/rules/errors/e015-unbounded-execution.js +0 -66
  93. package/dist/runtime/analysis/rules/errors/e016-output-validation-failed.d.ts +0 -43
  94. package/dist/runtime/analysis/rules/errors/e016-output-validation-failed.js +0 -42
  95. package/dist/runtime/analysis/rules/errors/e016-unbounded-execution.d.ts +0 -44
  96. package/dist/runtime/analysis/rules/errors/e016-unbounded-execution.js +0 -66
  97. package/dist/runtime/analysis/rules/errors/e017-input-validation-failed.d.ts +0 -57
  98. package/dist/runtime/analysis/rules/errors/e017-input-validation-failed.js +0 -80
  99. package/dist/runtime/analysis/rules/errors/e017-output-validation-failed.d.ts +0 -43
  100. package/dist/runtime/analysis/rules/errors/e017-output-validation-failed.js +0 -42
  101. package/dist/runtime/analysis/rules/errors/e018-input-validation-failed.d.ts +0 -57
  102. package/dist/runtime/analysis/rules/errors/e018-input-validation-failed.js +0 -80
  103. package/dist/runtime/analysis/rules/errors/e018-tool-execution-failed.d.ts +0 -38
  104. package/dist/runtime/analysis/rules/errors/e018-tool-execution-failed.js +0 -37
  105. package/dist/runtime/analysis/rules/errors/e019-tool-execution-failed.d.ts +0 -38
  106. package/dist/runtime/analysis/rules/errors/e019-tool-execution-failed.js +0 -37
  107. package/dist/runtime/analysis/rules/errors/e019-unexpected-test-result.d.ts +0 -65
  108. package/dist/runtime/analysis/rules/errors/e019-unexpected-test-result.js +0 -109
  109. package/dist/runtime/analysis/rules/errors/e020-unexpected-test-result.d.ts +0 -65
  110. package/dist/runtime/analysis/rules/errors/e020-unexpected-test-result.js +0 -109
  111. package/dist/runtime/analysis/rules/warnings/w001-implicit-dependency.d.ts +0 -22
  112. package/dist/runtime/analysis/rules/warnings/w001-implicit-dependency.js +0 -39
  113. package/dist/runtime/analysis/rules/warnings/w002-free-text-without-normalization.d.ts +0 -24
  114. package/dist/runtime/analysis/rules/warnings/w002-free-text-without-normalization.js +0 -40
  115. package/dist/runtime/analysis/rules/warnings/w003-missing-examples.d.ts +0 -22
  116. package/dist/runtime/analysis/rules/warnings/w003-missing-examples.js +0 -84
  117. package/dist/runtime/analysis/rules/warnings/w004-overloaded-responsibility.d.ts +0 -23
  118. package/dist/runtime/analysis/rules/warnings/w004-overloaded-responsibility.js +0 -96
  119. package/dist/runtime/analysis/rules/warnings/w005-generic-description.d.ts +0 -53
  120. package/dist/runtime/analysis/rules/warnings/w005-generic-description.js +0 -108
  121. package/dist/runtime/analysis/rules/warnings/w006-optional-as-required.d.ts +0 -22
  122. package/dist/runtime/analysis/rules/warnings/w006-optional-as-required.js +0 -44
  123. package/dist/runtime/analysis/rules/warnings/w007-broad-output-schema.d.ts +0 -23
  124. package/dist/runtime/analysis/rules/warnings/w007-broad-output-schema.js +0 -37
  125. package/dist/runtime/analysis/rules/warnings/w008-multiple-entry-points.d.ts +0 -22
  126. package/dist/runtime/analysis/rules/warnings/w008-multiple-entry-points.js +0 -97
  127. package/dist/runtime/analysis/rules/warnings/w009-hidden-side-effects.d.ts +0 -23
  128. package/dist/runtime/analysis/rules/warnings/w009-hidden-side-effects.js +0 -88
  129. package/dist/runtime/analysis/rules/warnings/w010-output-not-reusable.d.ts +0 -22
  130. package/dist/runtime/analysis/rules/warnings/w010-output-not-reusable.js +0 -81
  131. package/dist/runtime/analysis/rules/warnings/w021-weak-schema.d.ts +0 -40
  132. package/dist/runtime/analysis/rules/warnings/w021-weak-schema.js +0 -32
  133. package/dist/runtime/analysis/rules/warnings/w022-high-entropy-output.d.ts +0 -39
  134. package/dist/runtime/analysis/rules/warnings/w022-high-entropy-output.js +0 -36
  135. package/dist/runtime/analysis/rules/warnings/w023-unstable-defaults.d.ts +0 -38
  136. package/dist/runtime/analysis/rules/warnings/w023-unstable-defaults.js +0 -36
  137. package/dist/runtime/test/dependency-tracker.d.ts +0 -66
  138. package/dist/runtime/test/dependency-tracker.js +0 -80
  139. package/dist/runtime/test/formatters.d.ts +0 -18
  140. package/dist/runtime/test/formatters.js +0 -172
  141. package/dist/runtime/test/input-generator.d.ts +0 -33
  142. package/dist/runtime/test/input-generator.js +0 -498
  143. package/dist/runtime/test/mcp-root-detector.d.ts +0 -31
  144. package/dist/runtime/test/mcp-root-detector.js +0 -105
  145. package/dist/runtime/test/retry-tester.d.ts +0 -44
  146. package/dist/runtime/test/retry-tester.js +0 -103
  147. package/dist/runtime/test/synthetic-input-generator.d.ts +0 -11
  148. package/dist/runtime/test/synthetic-input-generator.js +0 -154
  149. package/dist/runtime/test/test-runner.d.ts +0 -28
  150. package/dist/runtime/test/test-runner.js +0 -55
@@ -1,96 +0,0 @@
1
- /**
2
- * W004: Overloaded Tool Responsibility
3
- *
4
- * Condition:
5
- * - Tool does multiple conceptual things
6
- * - Description contains multiple verbs or intents
7
- *
8
- * Why:
9
- * - Tool selection becomes unstable
10
- * - Hard to compose
11
- */
12
- import { BaseRule } from '../base.js';
13
- /**
14
- * Count verbs in a description.
15
- */
16
- function countVerbs(description) {
17
- const commonVerbs = [
18
- 'get',
19
- 'set',
20
- 'create',
21
- 'delete',
22
- 'update',
23
- 'find',
24
- 'search',
25
- 'fetch',
26
- 'send',
27
- 'receive',
28
- 'process',
29
- 'handle',
30
- 'execute',
31
- 'run',
32
- 'call',
33
- 'invoke',
34
- 'parse',
35
- 'format',
36
- 'validate',
37
- 'transform',
38
- 'convert',
39
- 'merge',
40
- 'split',
41
- 'filter',
42
- 'sort',
43
- 'save',
44
- 'load',
45
- 'read',
46
- 'write',
47
- ];
48
- const descLower = description.toLowerCase();
49
- let verbCount = 0;
50
- for (const verb of commonVerbs) {
51
- // Check if verb appears as a word (not just as part of another word)
52
- const regex = new RegExp(`\\b${verb}\\b`, 'i');
53
- if (regex.test(descLower)) {
54
- verbCount++;
55
- }
56
- }
57
- return verbCount;
58
- }
59
- /**
60
- * Count distinct intents (action + object pairs).
61
- */
62
- function countIntents(description) {
63
- // Simple heuristic: count "and" and "or" which often separate intents
64
- const connectors = [' and ', ' or ', ', and ', ', or '];
65
- let intentCount = 1; // Start with 1
66
- const descLower = description.toLowerCase();
67
- for (const connector of connectors) {
68
- const matches = descLower.split(connector);
69
- if (matches.length > 1) {
70
- intentCount = Math.max(intentCount, matches.length);
71
- }
72
- }
73
- return intentCount;
74
- }
75
- class W004OverloadedResponsibilityRule extends BaseRule {
76
- id = 'W004';
77
- severity = 'warning';
78
- ruleName = 'Overloaded Tool Responsibility';
79
- description = 'Tool appears to handle multiple responsibilities. Tool selection becomes unstable.';
80
- check(ctx) {
81
- const diagnostics = [];
82
- for (const tool of ctx.tools) {
83
- const description = tool.description || '';
84
- // Count verbs and intents
85
- const verbCount = countVerbs(description);
86
- const intentCount = countIntents(description);
87
- // If too many verbs (>3) or multiple intents (>2), it's overloaded
88
- if (verbCount > 3 || intentCount > 2) {
89
- diagnostics.push(this.createDiagnostic(`Tool "${tool.name}" appears to handle multiple responsibilities.`, tool.name, undefined, `Split "${tool.name}" into multiple focused tools, each handling a single responsibility.`));
90
- }
91
- }
92
- return diagnostics;
93
- }
94
- }
95
- export const W004OverloadedResponsibility = new W004OverloadedResponsibilityRule();
96
- //# sourceMappingURL=w004-overloaded-responsibility.js.map
@@ -1,53 +0,0 @@
1
- /**
2
- * W005: Tool Description Too Generic
3
- *
4
- * Condition:
5
- * - Description uses vague verbs:
6
- * - "get"
7
- * - "handle"
8
- * - "process"
9
- * - No concrete nouns
10
- *
11
- * Why:
12
- * - LLM cannot discriminate tools
13
- */
14
- import { BaseRule } from '../base.js';
15
- import type { AnalysisContext, Diagnostic } from '../../types.js';
16
- /**
17
- * Default list of concrete nouns that make a description specific.
18
- * This list can be extended or overridden when creating a custom rule instance.
19
- *
20
- * @example
21
- * ```ts
22
- * import { createW005Rule, DEFAULT_CONCRETE_NOUNS } from './w005-generic-description.js';
23
- * const customNouns = [...DEFAULT_CONCRETE_NOUNS, 'invoice', 'report'];
24
- * const customRule = createW005Rule(customNouns);
25
- * ```
26
- */
27
- export declare const DEFAULT_CONCRETE_NOUNS: readonly ["weather", "location", "user", "file", "data", "email", "message", "order", "payment", "product", "customer", "account", "transaction", "request", "response"];
28
- declare class W005GenericDescriptionRule extends BaseRule {
29
- private readonly concreteNouns;
30
- readonly id = "W005";
31
- readonly severity: "warning";
32
- readonly ruleName = "Tool Description Too Generic";
33
- readonly description = "Description of tool is too generic. LLM cannot discriminate tools.";
34
- constructor(concreteNouns?: readonly string[]);
35
- check(ctx: AnalysisContext): Diagnostic[];
36
- }
37
- /**
38
- * Create a W005 rule instance with custom concrete nouns.
39
- *
40
- * @param concreteNouns - Optional list of concrete nouns (defaults to DEFAULT_CONCRETE_NOUNS)
41
- * @returns A new W005GenericDescriptionRule instance
42
- *
43
- * @example
44
- * ```ts
45
- * import { createW005Rule, DEFAULT_CONCRETE_NOUNS } from './w005-generic-description.js';
46
- * const customNouns = [...DEFAULT_CONCRETE_NOUNS, 'invoice', 'report'];
47
- * const customRule = createW005Rule(customNouns);
48
- * ```
49
- */
50
- export declare function createW005Rule(concreteNouns?: readonly string[]): W005GenericDescriptionRule;
51
- export declare const W005GenericDescription: W005GenericDescriptionRule;
52
- export {};
53
- //# sourceMappingURL=w005-generic-description.d.ts.map
@@ -1,108 +0,0 @@
1
- /**
2
- * W005: Tool Description Too Generic
3
- *
4
- * Condition:
5
- * - Description uses vague verbs:
6
- * - "get"
7
- * - "handle"
8
- * - "process"
9
- * - No concrete nouns
10
- *
11
- * Why:
12
- * - LLM cannot discriminate tools
13
- */
14
- import { BaseRule } from '../base.js';
15
- /**
16
- * Default list of concrete nouns that make a description specific.
17
- * This list can be extended or overridden when creating a custom rule instance.
18
- *
19
- * @example
20
- * ```ts
21
- * import { createW005Rule, DEFAULT_CONCRETE_NOUNS } from './w005-generic-description.js';
22
- * const customNouns = [...DEFAULT_CONCRETE_NOUNS, 'invoice', 'report'];
23
- * const customRule = createW005Rule(customNouns);
24
- * ```
25
- */
26
- export const DEFAULT_CONCRETE_NOUNS = [
27
- 'weather',
28
- 'location',
29
- 'user',
30
- 'file',
31
- 'data',
32
- 'email',
33
- 'message',
34
- 'order',
35
- 'payment',
36
- 'product',
37
- 'customer',
38
- 'account',
39
- 'transaction',
40
- 'request',
41
- 'response',
42
- ];
43
- /**
44
- * Check if description is too generic.
45
- *
46
- * @param description - Tool description to check
47
- * @param concreteNouns - Optional list of concrete nouns (defaults to DEFAULT_CONCRETE_NOUNS)
48
- */
49
- function isGenericDescription(description, concreteNouns = DEFAULT_CONCRETE_NOUNS) {
50
- const descLower = description.toLowerCase();
51
- // Vague verbs
52
- const vagueVerbs = ['get', 'handle', 'process', 'do', 'make', 'use', 'call'];
53
- // Check for vague verbs without concrete nouns
54
- let hasVagueVerb = false;
55
- for (const verb of vagueVerbs) {
56
- const regex = new RegExp(`\\b${verb}\\b`, 'i');
57
- if (regex.test(descLower)) {
58
- hasVagueVerb = true;
59
- break;
60
- }
61
- }
62
- if (!hasVagueVerb) {
63
- return false;
64
- }
65
- // Check if description has concrete nouns
66
- const hasConcreteNoun = concreteNouns.some(noun => descLower.includes(noun));
67
- // If has vague verb but no concrete noun, it's generic
68
- return !hasConcreteNoun;
69
- }
70
- class W005GenericDescriptionRule extends BaseRule {
71
- concreteNouns;
72
- id = 'W005';
73
- severity = 'warning';
74
- ruleName = 'Tool Description Too Generic';
75
- description = 'Description of tool is too generic. LLM cannot discriminate tools.';
76
- constructor(concreteNouns = DEFAULT_CONCRETE_NOUNS) {
77
- super();
78
- this.concreteNouns = concreteNouns;
79
- }
80
- check(ctx) {
81
- const diagnostics = [];
82
- for (const tool of ctx.tools) {
83
- const description = tool.description || '';
84
- if (isGenericDescription(description, this.concreteNouns)) {
85
- diagnostics.push(this.createDiagnostic(`Description of "${tool.name}" is too generic.`, tool.name, undefined, `Make the description more specific by including concrete nouns and specific actions (e.g., "Get weather data for a location" instead of "Get data").`));
86
- }
87
- }
88
- return diagnostics;
89
- }
90
- }
91
- /**
92
- * Create a W005 rule instance with custom concrete nouns.
93
- *
94
- * @param concreteNouns - Optional list of concrete nouns (defaults to DEFAULT_CONCRETE_NOUNS)
95
- * @returns A new W005GenericDescriptionRule instance
96
- *
97
- * @example
98
- * ```ts
99
- * import { createW005Rule, DEFAULT_CONCRETE_NOUNS } from './w005-generic-description.js';
100
- * const customNouns = [...DEFAULT_CONCRETE_NOUNS, 'invoice', 'report'];
101
- * const customRule = createW005Rule(customNouns);
102
- * ```
103
- */
104
- export function createW005Rule(concreteNouns) {
105
- return new W005GenericDescriptionRule(concreteNouns);
106
- }
107
- export const W005GenericDescription = new W005GenericDescriptionRule();
108
- //# sourceMappingURL=w005-generic-description.js.map
@@ -1,22 +0,0 @@
1
- /**
2
- * W006: Optional Input Used as Required Downstream
3
- *
4
- * Condition:
5
- * - Input marked optional
6
- * - Treated as required in chaining
7
- *
8
- * Why:
9
- * - Hidden contract violation
10
- */
11
- import { BaseRule } from '../base.js';
12
- import type { AnalysisContext, Diagnostic } from '../../types.js';
13
- declare class W006OptionalAsRequiredRule extends BaseRule {
14
- readonly id = "W006";
15
- readonly severity: "warning";
16
- readonly ruleName = "Optional Input Used as Required Downstream";
17
- readonly description = "Optional input is treated as required downstream. Hidden contract violation.";
18
- check(ctx: AnalysisContext): Diagnostic[];
19
- }
20
- export declare const W006OptionalAsRequired: W006OptionalAsRequiredRule;
21
- export {};
22
- //# sourceMappingURL=w006-optional-as-required.d.ts.map
@@ -1,44 +0,0 @@
1
- /**
2
- * W006: Optional Input Used as Required Downstream
3
- *
4
- * Condition:
5
- * - Input marked optional
6
- * - Treated as required in chaining
7
- *
8
- * Why:
9
- * - Hidden contract violation
10
- */
11
- import { BaseRule } from '../base.js';
12
- class W006OptionalAsRequiredRule extends BaseRule {
13
- id = 'W006';
14
- severity = 'warning';
15
- ruleName = 'Optional Input Used as Required Downstream';
16
- description = 'Optional input is treated as required downstream. Hidden contract violation.';
17
- check(ctx) {
18
- const diagnostics = [];
19
- // Check high-confidence dependencies (>= 0.8)
20
- const highConfidenceDeps = ctx.dependencies.filter(d => d.confidence >= 0.8);
21
- for (const dep of highConfidenceDeps) {
22
- // Find the source output
23
- const fromTool = ctx.indexes.toolIndex.get(dep.fromTool.toLowerCase());
24
- const toTool = ctx.indexes.toolIndex.get(dep.toTool.toLowerCase());
25
- if (!fromTool || !toTool) {
26
- continue;
27
- }
28
- const fromField = fromTool.outputs.find(f => f.name === dep.fromField);
29
- const toField = toTool.inputs.find(f => f.name === dep.toField);
30
- if (!fromField || !toField) {
31
- continue;
32
- }
33
- // Check if source is optional/nullable but target is required
34
- const isSourceOptional = fromField.nullable === true;
35
- const isTargetRequired = toField.required === true;
36
- if (isSourceOptional && isTargetRequired) {
37
- diagnostics.push(this.createDiagnostic(`Source field "${dep.fromTool}.${dep.fromField}" is nullable/optional but is being wired into required input "${dep.toTool}.${dep.toField}".`, dep.toTool, dep.toField, `Make "${dep.fromTool}.${dep.fromField}" non-nullable, or make "${dep.toTool}.${dep.toField}" optional.`));
38
- }
39
- }
40
- return diagnostics;
41
- }
42
- }
43
- export const W006OptionalAsRequired = new W006OptionalAsRequiredRule();
44
- //# sourceMappingURL=w006-optional-as-required.js.map
@@ -1,23 +0,0 @@
1
- /**
2
- * W007: Output Schema Too Broad
3
- *
4
- * Condition:
5
- * - Output type is object with no properties
6
- * - Or any
7
- *
8
- * Why:
9
- * - No contract enforcement
10
- * - Breaks evolution
11
- */
12
- import { BaseRule } from '../base.js';
13
- import type { AnalysisContext, Diagnostic } from '../../types.js';
14
- declare class W007BroadOutputSchemaRule extends BaseRule {
15
- readonly id = "W007";
16
- readonly severity: "warning";
17
- readonly ruleName = "Output Schema Too Broad";
18
- readonly description = "Output schema of tool is too broad. No contract enforcement.";
19
- check(ctx: AnalysisContext): Diagnostic[];
20
- }
21
- export declare const W007BroadOutputSchema: W007BroadOutputSchemaRule;
22
- export {};
23
- //# sourceMappingURL=w007-broad-output-schema.d.ts.map
@@ -1,37 +0,0 @@
1
- /**
2
- * W007: Output Schema Too Broad
3
- *
4
- * Condition:
5
- * - Output type is object with no properties
6
- * - Or any
7
- *
8
- * Why:
9
- * - No contract enforcement
10
- * - Breaks evolution
11
- */
12
- import { BaseRule } from '../base.js';
13
- class W007BroadOutputSchemaRule extends BaseRule {
14
- id = 'W007';
15
- severity = 'warning';
16
- ruleName = 'Output Schema Too Broad';
17
- description = 'Output schema of tool is too broad. No contract enforcement.';
18
- check(ctx) {
19
- const diagnostics = [];
20
- for (const tool of ctx.tools) {
21
- for (const output of tool.outputs) {
22
- // Check if output is "any" type
23
- if (output.type === 'any') {
24
- diagnostics.push(this.createDiagnostic(`Output schema of "${tool.name}" (field: "${output.name}") is too broad (type: "any").`, tool.name, output.name, `Specify a concrete type for "${output.name}" instead of "any".`));
25
- }
26
- // Check if output is object with no properties
27
- if (output.type === 'object' &&
28
- (!output.properties || output.properties.length === 0)) {
29
- diagnostics.push(this.createDiagnostic(`Output schema of "${tool.name}" (field: "${output.name}") is an object with no properties defined.`, tool.name, output.name, `Define the properties of the object schema for "${output.name}".`));
30
- }
31
- }
32
- }
33
- return diagnostics;
34
- }
35
- }
36
- export const W007BroadOutputSchema = new W007BroadOutputSchemaRule();
37
- //# sourceMappingURL=w007-broad-output-schema.js.map
@@ -1,22 +0,0 @@
1
- /**
2
- * W008: Multiple Entry Points for Same Concept
3
- *
4
- * Condition:
5
- * - Multiple tools ask user for same conceptual data
6
- * (e.g. location, user_id)
7
- *
8
- * Why:
9
- * - Conflicting sources of truth
10
- */
11
- import { BaseRule } from '../base.js';
12
- import type { AnalysisContext, Diagnostic } from '../../types.js';
13
- declare class W008MultipleEntryPointsRule extends BaseRule {
14
- readonly id = "W008";
15
- readonly severity: "warning";
16
- readonly ruleName = "Multiple Entry Points for Same Concept";
17
- readonly description = "Multiple tools capture the same concept. Conflicting sources of truth.";
18
- check(ctx: AnalysisContext): Diagnostic[];
19
- }
20
- export declare const W008MultipleEntryPoints: W008MultipleEntryPointsRule;
21
- export {};
22
- //# sourceMappingURL=w008-multiple-entry-points.d.ts.map
@@ -1,97 +0,0 @@
1
- /**
2
- * W008: Multiple Entry Points for Same Concept
3
- *
4
- * Condition:
5
- * - Multiple tools ask user for same conceptual data
6
- * (e.g. location, user_id)
7
- *
8
- * Why:
9
- * - Conflicting sources of truth
10
- */
11
- import { BaseRule } from '../base.js';
12
- import { escapeRegex } from '../../utils.js';
13
- /**
14
- * Extract the concept from an input field name.
15
- */
16
- function extractConcept(fieldName) {
17
- // Split field name into tokens (on non-word characters and camelCase boundaries)
18
- // Preserve case for camelCase detection, then lowercase tokens individually
19
- const tokens = fieldName
20
- .split(/[^\w]+|(?<=[a-z])(?=[A-Z])/)
21
- .filter(t => t.length > 0)
22
- .map(t => t.toLowerCase());
23
- const name = fieldName.toLowerCase();
24
- // Common concepts
25
- const concepts = [
26
- [['location', 'loc', 'place', 'address', 'city', 'country'], 'location'],
27
- [['user', 'user_id', 'userid', 'username', 'person'], 'user'],
28
- [['email', 'e-mail', 'mail'], 'email'],
29
- [['phone', 'telephone', 'mobile'], 'phone'],
30
- [['name', 'fullname', 'full_name'], 'name'],
31
- [['id', 'identifier'], 'id'],
32
- ];
33
- for (const [keywords, concept] of concepts) {
34
- for (const keyword of keywords) {
35
- // Check for exact token match or word-boundary match
36
- // Escape keyword to prevent regex injection
37
- const escapedKeyword = escapeRegex(keyword);
38
- const keywordRegex = new RegExp(`\\b${escapedKeyword}\\b`, 'i');
39
- if (tokens.includes(keyword) || keywordRegex.test(name)) {
40
- return concept;
41
- }
42
- }
43
- }
44
- return null;
45
- }
46
- class W008MultipleEntryPointsRule extends BaseRule {
47
- id = 'W008';
48
- severity = 'warning';
49
- ruleName = 'Multiple Entry Points for Same Concept';
50
- description = 'Multiple tools capture the same concept. Conflicting sources of truth.';
51
- check(ctx) {
52
- const diagnostics = [];
53
- // Map concept to tools that have it
54
- const conceptToTools = new Map();
55
- for (const tool of ctx.tools) {
56
- for (const input of tool.inputs) {
57
- // Only check user-facing inputs
58
- if (!input.required) {
59
- continue;
60
- }
61
- const concept = extractConcept(input.name);
62
- if (!concept) {
63
- continue;
64
- }
65
- if (!conceptToTools.has(concept)) {
66
- conceptToTools.set(concept, []);
67
- }
68
- conceptToTools.get(concept).push({
69
- tool: tool.name,
70
- field: input.name,
71
- });
72
- }
73
- }
74
- // Check for concepts that appear in multiple tools
75
- for (const [concept, tools] of conceptToTools.entries()) {
76
- if (tools.length > 1) {
77
- const toolNames = tools.map(t => `"${t.tool}"`).join(', ');
78
- // Group by tool name in case same tool appears multiple times
79
- const toolFieldsMap = new Map();
80
- for (const toolEntry of tools) {
81
- if (!toolFieldsMap.has(toolEntry.tool)) {
82
- toolFieldsMap.set(toolEntry.tool, []);
83
- }
84
- toolFieldsMap.get(toolEntry.tool).push(toolEntry.field);
85
- }
86
- const context = {};
87
- for (const [tool, fields] of toolFieldsMap.entries()) {
88
- context[tool] = fields.join(', ');
89
- }
90
- diagnostics.push(this.createDiagnostic(`Multiple tools capture the same concept: "${concept}" (${toolNames}).`, undefined, undefined, `Consolidate "${concept}" collection into a single tool to avoid conflicting sources of truth.`, context));
91
- }
92
- }
93
- return diagnostics;
94
- }
95
- }
96
- export const W008MultipleEntryPoints = new W008MultipleEntryPointsRule();
97
- //# sourceMappingURL=w008-multiple-entry-points.js.map
@@ -1,23 +0,0 @@
1
- /**
2
- * W009: Hidden Side Effects
3
- *
4
- * Condition:
5
- * - Tool name/description suggests mutation
6
- * - But schema does not reflect it
7
- *
8
- * Why:
9
- * - Execution surprises
10
- * - Hard to reason
11
- */
12
- import { BaseRule } from '../base.js';
13
- import type { AnalysisContext, Diagnostic } from '../../types.js';
14
- declare class W009HiddenSideEffectsRule extends BaseRule {
15
- readonly id = "W009";
16
- readonly severity: "warning";
17
- readonly ruleName = "Hidden Side Effects";
18
- readonly description = "Tool appears to have side effects not reflected in schema. Execution surprises.";
19
- check(ctx: AnalysisContext): Diagnostic[];
20
- }
21
- export declare const W009HiddenSideEffects: W009HiddenSideEffectsRule;
22
- export {};
23
- //# sourceMappingURL=w009-hidden-side-effects.d.ts.map
@@ -1,88 +0,0 @@
1
- /**
2
- * W009: Hidden Side Effects
3
- *
4
- * Condition:
5
- * - Tool name/description suggests mutation
6
- * - But schema does not reflect it
7
- *
8
- * Why:
9
- * - Execution surprises
10
- * - Hard to reason
11
- */
12
- import { BaseRule } from '../base.js';
13
- /**
14
- * Check if a tool name/description suggests mutation.
15
- */
16
- function suggestsMutation(toolName, description) {
17
- const combined = `${toolName} ${description}`.toLowerCase();
18
- // Mutation verbs
19
- const mutationVerbs = [
20
- 'create',
21
- 'delete',
22
- 'remove',
23
- 'update',
24
- 'modify',
25
- 'change',
26
- 'set',
27
- 'save',
28
- 'write',
29
- 'add',
30
- 'insert',
31
- 'destroy',
32
- 'drop',
33
- 'clear',
34
- 'reset',
35
- ];
36
- return mutationVerbs.some(verb => {
37
- const regex = new RegExp(`\\b${verb}\\b`, 'i');
38
- return regex.test(combined);
39
- });
40
- }
41
- /**
42
- * Check if schema reflects mutation (has outputs that suggest state change).
43
- */
44
- function schemaReflectsMutation(tool) {
45
- // If no outputs, it might be a mutation (void operation)
46
- if (tool.outputs.length === 0) {
47
- return true;
48
- }
49
- // Check if outputs suggest mutation confirmation
50
- const mutationOutputs = [
51
- 'success',
52
- 'id',
53
- 'result',
54
- 'status',
55
- 'created',
56
- 'updated',
57
- 'deleted',
58
- ];
59
- for (const output of tool.outputs) {
60
- const name = output.name.toLowerCase();
61
- const desc = (output.description || '').toLowerCase();
62
- if (mutationOutputs.some(mo => name.includes(mo) || desc.includes(mo))) {
63
- return true;
64
- }
65
- }
66
- return false;
67
- }
68
- class W009HiddenSideEffectsRule extends BaseRule {
69
- id = 'W009';
70
- severity = 'warning';
71
- ruleName = 'Hidden Side Effects';
72
- description = 'Tool appears to have side effects not reflected in schema. Execution surprises.';
73
- check(ctx) {
74
- const diagnostics = [];
75
- for (const tool of ctx.tools) {
76
- const description = tool.description || '';
77
- const suggestsMut = suggestsMutation(tool.name, description);
78
- const reflectsMut = schemaReflectsMutation(tool);
79
- // If it suggests mutation but schema doesn't reflect it
80
- if (suggestsMut && !reflectsMut) {
81
- diagnostics.push(this.createDiagnostic(`Tool "${tool.name}" appears to have side effects not reflected in schema.`, tool.name, undefined, `Update the output schema of "${tool.name}" to reflect state changes (e.g., add success status, created ID, etc.).`));
82
- }
83
- }
84
- return diagnostics;
85
- }
86
- }
87
- export const W009HiddenSideEffects = new W009HiddenSideEffectsRule();
88
- //# sourceMappingURL=w009-hidden-side-effects.js.map
@@ -1,22 +0,0 @@
1
- /**
2
- * W010: Tool Output Not Reusable
3
- *
4
- * Condition:
5
- * - Output tailored only for natural language
6
- * - Not structured for reuse
7
- *
8
- * Why:
9
- * - Limits composability
10
- */
11
- import { BaseRule } from '../base.js';
12
- import type { AnalysisContext, Diagnostic } from '../../types.js';
13
- declare class W010OutputNotReusableRule extends BaseRule {
14
- readonly id = "W010";
15
- readonly severity: "warning";
16
- readonly ruleName = "Tool Output Not Reusable";
17
- readonly description = "Output of tool is not designed for reuse. Limits composability.";
18
- check(ctx: AnalysisContext): Diagnostic[];
19
- }
20
- export declare const W010OutputNotReusable: W010OutputNotReusableRule;
21
- export {};
22
- //# sourceMappingURL=w010-output-not-reusable.d.ts.map