@syrin/cli 1.3.2 → 1.4.1

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 +184 -152
  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 +23 -23
  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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syrin/cli",
3
- "version": "1.3.2",
3
+ "version": "1.4.1",
4
4
  "description": "Syrin is a runtime intelligence system that makes MCP servers debuggable, testable, and safe to run in production.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -23,7 +23,7 @@
23
23
  "LICENSE"
24
24
  ],
25
25
  "engines": {
26
- "node": ">=18.0.0"
26
+ "node": ">=20.12.0"
27
27
  },
28
28
  "keywords": [
29
29
  "mcp",
@@ -55,52 +55,52 @@
55
55
  },
56
56
  "repository": {
57
57
  "type": "git",
58
- "url": "git+https://github.com/syrin-labs/cli.git"
58
+ "url": "git+https://github.com/Syrin-Labs/cli.git"
59
59
  },
60
60
  "author": "Syrin Labs Team",
61
61
  "license": "ISC",
62
62
  "bugs": {
63
- "url": "https://github.com/syrin-labs/cli/issues"
63
+ "url": "https://github.com/Syrin-Labs/cli/issues"
64
64
  },
65
- "homepage": "https://github.com/syrin-labs/cli#readme",
65
+ "homepage": "https://github.com/Syrin-Labs/cli#readme",
66
66
  "publishConfig": {
67
67
  "access": "public"
68
68
  },
69
69
  "devDependencies": {
70
70
  "@types/inquirer": "^9.0.9",
71
71
  "@types/js-yaml": "^4.0.9",
72
- "@types/node": "^25.0.3",
73
- "@types/react": "^19.2.7",
72
+ "@types/node": "^25.2.0",
73
+ "@types/react": "^19.2.10",
74
74
  "@types/uuid": "^10.0.0",
75
- "@typescript-eslint/eslint-plugin": "^8.52.0",
76
- "@typescript-eslint/parser": "^8.52.0",
77
- "@vitest/coverage-v8": "^3.2.4",
78
- "@vitest/ui": "^3.2.4",
75
+ "@typescript-eslint/eslint-plugin": "^8.54.0",
76
+ "@typescript-eslint/parser": "^8.54.0",
77
+ "@vitest/coverage-v8": "^4.0.18",
78
+ "@vitest/ui": "^4.0.18",
79
79
  "eslint": "^9.39.2",
80
80
  "eslint-config-prettier": "^10.1.8",
81
81
  "eslint-import-resolver-typescript": "^4.4.4",
82
82
  "eslint-plugin-import": "^2.32.0",
83
- "eslint-plugin-prettier": "^5.5.4",
84
- "globals": "^17.0.0",
85
- "prettier": "^3.7.4",
83
+ "eslint-plugin-prettier": "^5.5.5",
84
+ "globals": "^17.3.0",
85
+ "prettier": "^3.8.1",
86
86
  "tsc-alias": "^1.8.16",
87
87
  "tsconfig-paths": "^4.2.0",
88
88
  "typescript": "^5.9.3",
89
- "vitest": "^3.2.4"
89
+ "vitest": "^4.0.18"
90
90
  },
91
91
  "dependencies": {
92
- "@anthropic-ai/sdk": "^0.71.2",
93
- "@apidevtools/json-schema-ref-parser": "^15.1.3",
94
- "@modelcontextprotocol/sdk": "^1.25.1",
95
- "commander": "^14.0.2",
92
+ "@anthropic-ai/sdk": "^0.72.1",
93
+ "@apidevtools/json-schema-ref-parser": "^15.2.2",
94
+ "@modelcontextprotocol/sdk": "^1.25.3",
95
+ "commander": "^14.0.3",
96
96
  "ink": "^6.6.0",
97
97
  "ink-text-input": "^6.0.0",
98
- "inquirer": "^13.1.0",
98
+ "inquirer": "^13.2.2",
99
99
  "js-yaml": "^4.1.1",
100
100
  "ollama": "^0.6.3",
101
- "openai": "^6.15.0",
102
- "react": "^19.2.3",
101
+ "openai": "^6.17.0",
102
+ "react": "^19.2.4",
103
103
  "uuid": "^13.0.0",
104
- "zod": "^4.3.5"
104
+ "zod": "^4.3.6"
105
105
  }
106
106
  }
@@ -1,22 +0,0 @@
1
- /**
2
- * E001: Missing Output Schema
3
- *
4
- * Condition: Tool does not declare an output schema
5
- *
6
- * Why this is fatal:
7
- * - Downstream tools cannot reason about outputs
8
- * - LLM will invent structure
9
- * - Reproducibility is impossible
10
- */
11
- import { BaseRule } from '../base.js';
12
- import type { AnalysisContext, Diagnostic } from '../../types.js';
13
- declare class E001MissingOutputSchemaRule extends BaseRule {
14
- readonly id: "E001";
15
- readonly severity: "error";
16
- readonly ruleName = "Missing Output Schema";
17
- readonly description = "Tool does not declare an output schema. Downstream tools cannot safely consume its output.";
18
- check(ctx: AnalysisContext): Diagnostic[];
19
- }
20
- export declare const E001MissingOutputSchema: E001MissingOutputSchemaRule;
21
- export {};
22
- //# sourceMappingURL=e001-missing-output-schema.d.ts.map
@@ -1,30 +0,0 @@
1
- /**
2
- * E001: Missing Output Schema
3
- *
4
- * Condition: Tool does not declare an output schema
5
- *
6
- * Why this is fatal:
7
- * - Downstream tools cannot reason about outputs
8
- * - LLM will invent structure
9
- * - Reproducibility is impossible
10
- */
11
- import { BaseRule } from '../base.js';
12
- import { ERROR_CODES } from '../error-codes.js';
13
- class E001MissingOutputSchemaRule extends BaseRule {
14
- id = ERROR_CODES.E001;
15
- severity = 'error';
16
- ruleName = 'Missing Output Schema';
17
- description = 'Tool does not declare an output schema. Downstream tools cannot safely consume its output.';
18
- check(ctx) {
19
- const diagnostics = [];
20
- for (const tool of ctx.tools) {
21
- // Check if tool has no output fields
22
- if (!tool.outputs || tool.outputs.length === 0) {
23
- diagnostics.push(this.createDiagnostic(`Tool "${tool.name}" has no output schema. Downstream tools cannot safely consume its output.`, tool.name, undefined, 'Add an output schema to the tool definition to specify the structure of the output.'));
24
- }
25
- }
26
- return diagnostics;
27
- }
28
- }
29
- export const E001MissingOutputSchema = new E001MissingOutputSchemaRule();
30
- //# sourceMappingURL=e001-missing-output-schema.js.map
@@ -1,24 +0,0 @@
1
- /**
2
- * E002: Underspecified Required Input
3
- *
4
- * Condition:
5
- * - Input parameter is required AND
6
- * - Type is broad (string, any, object) AND
7
- * - No description, constraints, enum, regex, or example
8
- *
9
- * Why:
10
- * - LLM will hallucinate values
11
- * - Tool invocation becomes nondeterministic
12
- */
13
- import { BaseRule } from '../base.js';
14
- import type { AnalysisContext, Diagnostic } from '../../types.js';
15
- declare class E002UnderspecifiedRequiredInputRule extends BaseRule {
16
- readonly id: "E002";
17
- readonly severity: "error";
18
- readonly ruleName = "Underspecified Required Input";
19
- readonly description = "Required parameter is underspecified. LLM may pass invalid or ambiguous values.";
20
- check(ctx: AnalysisContext): Diagnostic[];
21
- }
22
- export declare const E002UnderspecifiedRequiredInput: E002UnderspecifiedRequiredInputRule;
23
- export {};
24
- //# sourceMappingURL=e002-underspecified-input.d.ts.map
@@ -1,52 +0,0 @@
1
- /**
2
- * E002: Underspecified Required Input
3
- *
4
- * Condition:
5
- * - Input parameter is required AND
6
- * - Type is broad (string, any, object) AND
7
- * - No description, constraints, enum, regex, or example
8
- *
9
- * Why:
10
- * - LLM will hallucinate values
11
- * - Tool invocation becomes nondeterministic
12
- */
13
- import { BaseRule } from '../base.js';
14
- import { ERROR_CODES } from '../error-codes.js';
15
- class E002UnderspecifiedRequiredInputRule extends BaseRule {
16
- id = ERROR_CODES.E002;
17
- severity = 'error';
18
- ruleName = 'Underspecified Required Input';
19
- description = 'Required parameter is underspecified. LLM may pass invalid or ambiguous values.';
20
- check(ctx) {
21
- const diagnostics = [];
22
- // Broad types that need constraints
23
- const broadTypes = new Set(['string', 'any', 'object']);
24
- for (const tool of ctx.tools) {
25
- for (const input of tool.inputs) {
26
- // Check if type is broad
27
- if (!broadTypes.has(input.type)) {
28
- continue;
29
- }
30
- // Check if it has any constraints
31
- const hasDescription = Boolean(input.description && input.description.trim());
32
- const hasEnum = Boolean(input.enum && input.enum.length > 0);
33
- const hasPattern = Boolean(input.pattern);
34
- const hasExample = input.example !== undefined;
35
- // If it's broad and has no constraints, it's underspecified
36
- if (!hasDescription && !hasEnum && !hasPattern && !hasExample) {
37
- if (input.required) {
38
- // Required parameter without constraints - error
39
- diagnostics.push(this.createDiagnostic(`Required parameter "${input.name}" in tool "${tool.name}" is underspecified. LLM may pass invalid or ambiguous values.`, tool.name, input.name, `Add constraints to "${input.name}": provide a description, enum values, regex pattern, or example.`));
40
- }
41
- else {
42
- // Optional parameter without constraints - flag as warning since it's less critical
43
- diagnostics.push(this.createDiagnostic(`Optional parameter "${input.name}" in tool "${tool.name}" is underspecified. LLM may pass invalid or ambiguous values.`, tool.name, input.name, `Add constraints to "${input.name}": provide a description, enum values, regex pattern, or example.`, undefined, 'warning'));
44
- }
45
- }
46
- }
47
- }
48
- return diagnostics;
49
- }
50
- }
51
- export const E002UnderspecifiedRequiredInput = new E002UnderspecifiedRequiredInputRule();
52
- //# sourceMappingURL=e002-underspecified-input.js.map
@@ -1,23 +0,0 @@
1
- /**
2
- * E003: Unsafe Tool Chaining (Type Mismatch)
3
- *
4
- * Condition:
5
- * - Tool A output flows into Tool B input AND
6
- * - Output type incompatible with input type
7
- *
8
- * Why:
9
- * - Tool chains silently break
10
- * - Bugs appear "random"
11
- */
12
- import { BaseRule } from '../base.js';
13
- import type { AnalysisContext, Diagnostic } from '../../types.js';
14
- declare class E003TypeMismatchRule extends BaseRule {
15
- readonly id: "E003";
16
- readonly severity: "error";
17
- readonly ruleName = "Unsafe Tool Chaining (Type Mismatch)";
18
- readonly description = "Output type incompatible with downstream input type. Tool chains will break.";
19
- check(ctx: AnalysisContext): Diagnostic[];
20
- }
21
- export declare const E003TypeMismatch: E003TypeMismatchRule;
22
- export {};
23
- //# sourceMappingURL=e003-type-mismatch.d.ts.map
@@ -1,73 +0,0 @@
1
- /**
2
- * E003: Unsafe Tool Chaining (Type Mismatch)
3
- *
4
- * Condition:
5
- * - Tool A output flows into Tool B input AND
6
- * - Output type incompatible with input type
7
- *
8
- * Why:
9
- * - Tool chains silently break
10
- * - Bugs appear "random"
11
- */
12
- import { BaseRule } from '../base.js';
13
- import { ERROR_CODES } from '../error-codes.js';
14
- /**
15
- * Check if two types are incompatible.
16
- */
17
- function areTypesIncompatible(outputType, inputType) {
18
- // Exact match is always compatible
19
- if (outputType === inputType) {
20
- return false;
21
- }
22
- // String can accept almost anything (it's broad)
23
- if (inputType === 'string') {
24
- return false;
25
- }
26
- // Incompatible type pairs
27
- const incompatiblePairs = [
28
- ['string', 'number'],
29
- ['string', 'boolean'],
30
- ['string', 'integer'],
31
- ['number', 'boolean'],
32
- ['boolean', 'number'],
33
- ['array', 'object'],
34
- ['object', 'array'],
35
- ];
36
- for (const [from, to] of incompatiblePairs) {
37
- if (outputType === from && inputType === to) {
38
- return true;
39
- }
40
- }
41
- return false;
42
- }
43
- class E003TypeMismatchRule extends BaseRule {
44
- id = ERROR_CODES.E003;
45
- severity = 'error';
46
- ruleName = 'Unsafe Tool Chaining (Type Mismatch)';
47
- description = 'Output type incompatible with downstream input type. Tool chains will break.';
48
- check(ctx) {
49
- const diagnostics = [];
50
- // Only check high-confidence dependencies (>= 0.8)
51
- const highConfidenceDeps = ctx.dependencies.filter(d => d.confidence >= 0.8);
52
- for (const dep of highConfidenceDeps) {
53
- // Find the output and input fields
54
- const fromTool = ctx.indexes.toolIndex.get(dep.fromTool.toLowerCase());
55
- const toTool = ctx.indexes.toolIndex.get(dep.toTool.toLowerCase());
56
- if (!fromTool || !toTool) {
57
- continue;
58
- }
59
- const fromField = fromTool.outputs.find(f => f.name === dep.fromField);
60
- const toField = toTool.inputs.find(f => f.name === dep.toField);
61
- if (!fromField || !toField) {
62
- continue;
63
- }
64
- // Check type compatibility
65
- if (areTypesIncompatible(fromField.type, toField.type)) {
66
- diagnostics.push(this.createDiagnostic(`Tool "${dep.toTool}" parameter "${dep.toField}" depends on output from "${dep.fromTool}", but types are incompatible (${fromField.type} → ${toField.type}).`, dep.toTool, dep.toField, `Ensure "${dep.fromTool}" outputs ${toField.type}, or modify "${dep.toTool}" to accept ${fromField.type}.`));
67
- }
68
- }
69
- return diagnostics;
70
- }
71
- }
72
- export const E003TypeMismatch = new E003TypeMismatchRule();
73
- //# sourceMappingURL=e003-type-mismatch.js.map
@@ -1,23 +0,0 @@
1
- /**
2
- * E004: Unsafe Tool Chaining (Free Text Propagation)
3
- *
4
- * Condition:
5
- * - Output is unconstrained string
6
- * - Used as input to another tool
7
- *
8
- * Why:
9
- * - LLM passes sentences instead of data
10
- * - Most common real-world failure
11
- */
12
- import { BaseRule } from '../base.js';
13
- import type { AnalysisContext, Diagnostic } from '../../types.js';
14
- declare class E004FreeTextPropagationRule extends BaseRule {
15
- readonly id: "E004";
16
- readonly severity: "error";
17
- readonly ruleName = "Unsafe Tool Chaining (Free Text Propagation)";
18
- readonly description = "Free-text output is used by another tool. This is unsafe without constraints.";
19
- check(ctx: AnalysisContext): Diagnostic[];
20
- }
21
- export declare const E004FreeTextPropagation: E004FreeTextPropagationRule;
22
- export {};
23
- //# sourceMappingURL=e004-free-text-propagation.d.ts.map
@@ -1,47 +0,0 @@
1
- /**
2
- * E004: Unsafe Tool Chaining (Free Text Propagation)
3
- *
4
- * Condition:
5
- * - Output is unconstrained string
6
- * - Used as input to another tool
7
- *
8
- * Why:
9
- * - LLM passes sentences instead of data
10
- * - Most common real-world failure
11
- */
12
- import { BaseRule } from '../base.js';
13
- import { ERROR_CODES } from '../error-codes.js';
14
- class E004FreeTextPropagationRule extends BaseRule {
15
- id = ERROR_CODES.E004;
16
- severity = 'error';
17
- ruleName = 'Unsafe Tool Chaining (Free Text Propagation)';
18
- description = 'Free-text output is used by another tool. This is unsafe without constraints.';
19
- check(ctx) {
20
- const diagnostics = [];
21
- // Only check high-confidence dependencies (>= 0.8)
22
- const highConfidenceDeps = ctx.dependencies.filter(d => d.confidence >= 0.8);
23
- for (const dep of highConfidenceDeps) {
24
- // Find the output field
25
- const fromTool = ctx.indexes.toolIndex.get(dep.fromTool.toLowerCase());
26
- if (!fromTool) {
27
- continue;
28
- }
29
- const fromField = fromTool.outputs.find(f => f.name === dep.fromField);
30
- if (!fromField) {
31
- continue;
32
- }
33
- // Check if output is unconstrained string
34
- const isString = fromField.type === 'string';
35
- const hasEnum = Boolean(fromField.enum && fromField.enum.length > 0);
36
- const hasPattern = Boolean(fromField.pattern);
37
- const hasDescription = Boolean(fromField.description && fromField.description.trim());
38
- // If it's a string with no constraints, it's free text
39
- if (isString && !hasEnum && !hasPattern && !hasDescription) {
40
- diagnostics.push(this.createDiagnostic(`Free-text output from "${dep.fromTool}" (field: "${dep.fromField}") is used by "${dep.toTool}". This is unsafe without constraints.`, dep.toTool, dep.toField, `Constrain the output of "${dep.fromTool}" by adding enum values, regex pattern, or clear description of expected format.`));
41
- }
42
- }
43
- return diagnostics;
44
- }
45
- }
46
- export const E004FreeTextPropagation = new E004FreeTextPropagationRule();
47
- //# sourceMappingURL=e004-free-text-propagation.js.map
@@ -1,25 +0,0 @@
1
- /**
2
- * E005: Hard Tool Ambiguity
3
- *
4
- * Condition:
5
- * - Two or more tools:
6
- * - Overlapping descriptions
7
- * - Overlapping schemas
8
- * - No clear differentiator
9
- *
10
- * Why:
11
- * - Tool selection becomes nondeterministic
12
- * - Agent behavior changes across runs/models
13
- */
14
- import { BaseRule } from '../base.js';
15
- import type { AnalysisContext, Diagnostic } from '../../types.js';
16
- declare class E005ToolAmbiguityRule extends BaseRule {
17
- readonly id: "E005";
18
- readonly severity: "error";
19
- readonly ruleName = "Hard Tool Ambiguity";
20
- readonly description = "Multiple tools match the same intent. LLM tool selection is ambiguous.";
21
- check(ctx: AnalysisContext): Diagnostic[];
22
- }
23
- export declare const E005ToolAmbiguity: E005ToolAmbiguityRule;
24
- export {};
25
- //# sourceMappingURL=e005-tool-ambiguity.d.ts.map
@@ -1,73 +0,0 @@
1
- /**
2
- * E005: Hard Tool Ambiguity
3
- *
4
- * Condition:
5
- * - Two or more tools:
6
- * - Overlapping descriptions
7
- * - Overlapping schemas
8
- * - No clear differentiator
9
- *
10
- * Why:
11
- * - Tool selection becomes nondeterministic
12
- * - Agent behavior changes across runs/models
13
- */
14
- import { BaseRule } from '../base.js';
15
- import { ERROR_CODES } from '../error-codes.js';
16
- /**
17
- * Calculate similarity between two sets of tokens.
18
- */
19
- function tokenSimilarity(tokens1, tokens2) {
20
- if (tokens1.size === 0 || tokens2.size === 0) {
21
- return 0.0;
22
- }
23
- const intersection = new Set([...tokens1].filter(t => tokens2.has(t)));
24
- const union = new Set([...tokens1, ...tokens2]);
25
- return intersection.size / union.size;
26
- }
27
- /**
28
- * Calculate schema overlap between two tools.
29
- */
30
- function schemaOverlap(inputs1, inputs2) {
31
- const names1 = new Set(inputs1.map(i => i.name.toLowerCase()));
32
- const names2 = new Set(inputs2.map(i => i.name.toLowerCase()));
33
- const nameOverlapSet = new Set([...names1].filter(n => names2.has(n)));
34
- const nameOverlap = nameOverlapSet.size;
35
- const totalNames = new Set([...names1, ...names2]).size;
36
- if (totalNames === 0) {
37
- return 0.0;
38
- }
39
- return nameOverlap / totalNames;
40
- }
41
- class E005ToolAmbiguityRule extends BaseRule {
42
- id = ERROR_CODES.E005;
43
- severity = 'error';
44
- ruleName = 'Hard Tool Ambiguity';
45
- description = 'Multiple tools match the same intent. LLM tool selection is ambiguous.';
46
- check(ctx) {
47
- const diagnostics = [];
48
- for (let i = 0; i < ctx.tools.length; i++) {
49
- const tool1 = ctx.tools[i];
50
- if (!tool1)
51
- continue;
52
- for (let j = i + 1; j < ctx.tools.length; j++) {
53
- const tool2 = ctx.tools[j];
54
- if (!tool2)
55
- continue;
56
- // Calculate description similarity
57
- const descSimilarity = tokenSimilarity(tool1.descriptionTokens, tool2.descriptionTokens);
58
- // Calculate schema overlap
59
- const inputOverlap = schemaOverlap(tool1.inputs, tool2.inputs);
60
- const outputOverlap = schemaOverlap(tool1.outputs, tool2.outputs);
61
- const schemaOverlapScore = (inputOverlap + outputOverlap) / 2;
62
- // If both description and schema are very similar, it's ambiguous
63
- // Threshold: >0.7 description similarity AND >0.5 schema overlap
64
- if (descSimilarity > 0.7 && schemaOverlapScore > 0.5) {
65
- diagnostics.push(this.createDiagnostic(`Multiple tools match the same intent: "${tool1.name}", "${tool2.name}". LLM tool selection is ambiguous.`, undefined, undefined, `Differentiate "${tool1.name}" and "${tool2.name}" by making their descriptions and schemas more distinct.`));
66
- }
67
- }
68
- }
69
- return diagnostics;
70
- }
71
- }
72
- export const E005ToolAmbiguity = new E005ToolAmbiguityRule();
73
- //# sourceMappingURL=e005-tool-ambiguity.js.map
@@ -1,22 +0,0 @@
1
- /**
2
- * E006: Required Input Not Mentioned in Description
3
- *
4
- * Condition:
5
- * - Required input exists
6
- * - Tool description does not reference it directly or indirectly
7
- *
8
- * Why:
9
- * - LLM does not know parameter exists or matters
10
- */
11
- import { BaseRule } from '../base.js';
12
- import type { AnalysisContext, Diagnostic } from '../../types.js';
13
- declare class E006ParamNotInDescriptionRule extends BaseRule {
14
- readonly id: "E006";
15
- readonly severity: "error";
16
- readonly ruleName = "Required Input Not Mentioned in Description";
17
- readonly description = "Required parameter is not referenced in tool description. LLM may not know parameter exists.";
18
- check(ctx: AnalysisContext): Diagnostic[];
19
- }
20
- export declare const E006ParamNotInDescription: E006ParamNotInDescriptionRule;
21
- export {};
22
- //# sourceMappingURL=e006-param-not-in-description.d.ts.map
@@ -1,57 +0,0 @@
1
- /**
2
- * E006: Required Input Not Mentioned in Description
3
- *
4
- * Condition:
5
- * - Required input exists
6
- * - Tool description does not reference it directly or indirectly
7
- *
8
- * Why:
9
- * - LLM does not know parameter exists or matters
10
- */
11
- import { BaseRule } from '../base.js';
12
- import { ERROR_CODES } from '../error-codes.js';
13
- /**
14
- * Check if a parameter name appears in the description (case-insensitive).
15
- */
16
- function isParameterMentioned(description, paramName) {
17
- const descLower = description.toLowerCase();
18
- const paramLower = paramName.toLowerCase();
19
- // Exact match
20
- if (descLower.includes(paramLower)) {
21
- return true;
22
- }
23
- // Check if words from param name appear in description
24
- const paramWords = paramLower.split(/\W+/).filter(w => w.length > 2);
25
- const descWords = descLower.split(/\W+/);
26
- for (const word of paramWords) {
27
- if (descWords.includes(word)) {
28
- return true;
29
- }
30
- }
31
- return false;
32
- }
33
- class E006ParamNotInDescriptionRule extends BaseRule {
34
- id = ERROR_CODES.E006;
35
- severity = 'error';
36
- ruleName = 'Required Input Not Mentioned in Description';
37
- description = 'Required parameter is not referenced in tool description. LLM may not know parameter exists.';
38
- check(ctx) {
39
- const diagnostics = [];
40
- for (const tool of ctx.tools) {
41
- const description = tool.description || '';
42
- for (const input of tool.inputs) {
43
- // Only check required inputs
44
- if (!input.required) {
45
- continue;
46
- }
47
- // Check if parameter is mentioned in description
48
- if (!isParameterMentioned(description, input.name)) {
49
- diagnostics.push(this.createDiagnostic(`Required parameter "${input.name}" is not referenced in "${tool.name}" description.`, tool.name, input.name, `Mention "${input.name}" in the tool description so the LLM knows it exists and is required.`));
50
- }
51
- }
52
- }
53
- return diagnostics;
54
- }
55
- }
56
- export const E006ParamNotInDescription = new E006ParamNotInDescriptionRule();
57
- //# sourceMappingURL=e006-param-not-in-description.js.map
@@ -1,23 +0,0 @@
1
- /**
2
- * E007: Output Used Downstream but Not Guaranteed
3
- *
4
- * Condition:
5
- * - Tool output is optional / nullable
6
- * - Used downstream without fallback
7
- *
8
- * Why:
9
- * - Silent null propagation
10
- * - Hard-to-debug failures
11
- */
12
- import { BaseRule } from '../base.js';
13
- import type { AnalysisContext, Diagnostic } from '../../types.js';
14
- declare class E007OutputNotGuaranteedRule extends BaseRule {
15
- readonly id: "E007";
16
- readonly severity: "error";
17
- readonly ruleName = "Output Used Downstream but Not Guaranteed";
18
- readonly description = "Output of tool is not guaranteed, but is used by downstream tools without fallback.";
19
- check(ctx: AnalysisContext): Diagnostic[];
20
- }
21
- export declare const E007OutputNotGuaranteed: E007OutputNotGuaranteedRule;
22
- export {};
23
- //# sourceMappingURL=e007-output-not-guaranteed.d.ts.map
@@ -1,56 +0,0 @@
1
- /**
2
- * E007: Output Used Downstream but Not Guaranteed
3
- *
4
- * Condition:
5
- * - Tool output is optional / nullable
6
- * - Used downstream without fallback
7
- *
8
- * Why:
9
- * - Silent null propagation
10
- * - Hard-to-debug failures
11
- */
12
- import { BaseRule } from '../base.js';
13
- import { ERROR_CODES } from '../error-codes.js';
14
- class E007OutputNotGuaranteedRule extends BaseRule {
15
- id = ERROR_CODES.E007;
16
- severity = 'error';
17
- ruleName = 'Output Used Downstream but Not Guaranteed';
18
- description = 'Output of tool is not guaranteed, but is used by downstream tools without fallback.';
19
- check(ctx) {
20
- const diagnostics = [];
21
- // Only check high-confidence dependencies (>= 0.8)
22
- const highConfidenceDeps = ctx.dependencies.filter(d => d.confidence >= 0.8);
23
- for (const dep of highConfidenceDeps) {
24
- // Find the output field
25
- const fromTool = ctx.indexes.toolIndex.get(dep.fromTool.toLowerCase());
26
- if (!fromTool) {
27
- continue;
28
- }
29
- const fromField = fromTool.outputs.find(f => f.name === dep.fromField);
30
- if (!fromField) {
31
- continue;
32
- }
33
- // Find the downstream input field
34
- const toTool = ctx.indexes.toolIndex.get(dep.toTool.toLowerCase());
35
- if (!toTool) {
36
- continue;
37
- }
38
- const toField = toTool.inputs.find(f => f.name === dep.toField);
39
- if (!toField) {
40
- continue;
41
- }
42
- // Check if output is not guaranteed and downstream requires it without fallback
43
- // Optional upstream always triggers when downstream requires it
44
- // Nullable upstream only triggers when downstream is required AND not nullable (can't handle null)
45
- if ((!fromField.required && toField.required === true) ||
46
- (fromField.nullable === true &&
47
- toField.required === true &&
48
- toField.nullable !== true)) {
49
- diagnostics.push(this.createDiagnostic(`Output of "${dep.fromTool}" (field: "${dep.fromField}") is nullable but is used by "${dep.toTool}" without fallback.`, dep.fromTool, dep.fromField, `Make the output of "${dep.fromTool}" non-nullable, or ensure "${dep.toTool}" handles null values.`));
50
- }
51
- }
52
- return diagnostics;
53
- }
54
- }
55
- export const E007OutputNotGuaranteed = new E007OutputNotGuaranteedRule();
56
- //# sourceMappingURL=e007-output-not-guaranteed.js.map