@exaudeus/workrail 0.4.1 → 0.6.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.
@@ -0,0 +1,20 @@
1
+ import { IWorkflowStorage } from '../../types/storage';
2
+ import { ValidationEngine } from './validation-engine';
3
+ import { DocumentationResult } from '../../types/documentation-types';
4
+ export interface IDocumentationService {
5
+ resolveDocumentation(workflowId: string, mode?: 'preview' | 'full', sections?: string[], format?: string): Promise<DocumentationResult>;
6
+ isDocumentationAvailable(workflowId: string): Promise<boolean>;
7
+ getWorkrailHelp(section?: string, format?: string): Promise<DocumentationResult>;
8
+ }
9
+ export declare class DefaultDocumentationService implements IDocumentationService {
10
+ private readonly storage;
11
+ private readonly validationEngine;
12
+ constructor(storage: IWorkflowStorage, validationEngine: ValidationEngine);
13
+ resolveDocumentation(workflowId: string, mode?: 'preview' | 'full', sections?: string[], format?: string): Promise<DocumentationResult>;
14
+ isDocumentationAvailable(workflowId: string): Promise<boolean>;
15
+ getWorkrailHelp(section?: string, format?: string): Promise<DocumentationResult>;
16
+ private parseMarkdownSections;
17
+ private createPreview;
18
+ private generateFallbackDocumentation;
19
+ private generateWorkrailHelp;
20
+ }
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DefaultDocumentationService = void 0;
4
+ const error_handler_1 = require("../../core/error-handler");
5
+ class DefaultDocumentationService {
6
+ constructor(storage, validationEngine) {
7
+ this.storage = storage;
8
+ this.validationEngine = validationEngine;
9
+ }
10
+ async resolveDocumentation(workflowId, mode = 'preview', sections, format = 'text') {
11
+ try {
12
+ const workflow = await this.storage.getWorkflowById(workflowId);
13
+ if (!workflow) {
14
+ throw new error_handler_1.WorkflowNotFoundError(workflowId);
15
+ }
16
+ if (workflow.documentation) {
17
+ return {
18
+ source: 'json',
19
+ format: 'json',
20
+ content: workflow.documentation
21
+ };
22
+ }
23
+ if (this.storage.getWorkflowReadme) {
24
+ const readme = await this.storage.getWorkflowReadme(workflowId);
25
+ if (readme) {
26
+ const sections = this.parseMarkdownSections(readme);
27
+ return {
28
+ source: 'readme',
29
+ format: 'markdown',
30
+ content: mode === 'preview' ? this.createPreview(readme) : readme,
31
+ sections
32
+ };
33
+ }
34
+ }
35
+ return this.generateFallbackDocumentation(workflow, format);
36
+ }
37
+ catch (error) {
38
+ if (error instanceof error_handler_1.WorkflowNotFoundError) {
39
+ throw error;
40
+ }
41
+ return {
42
+ source: 'fallback',
43
+ format: 'text',
44
+ content: `Documentation unavailable for workflow: ${workflowId}. Error: ${error instanceof Error ? error.message : String(error)}`
45
+ };
46
+ }
47
+ }
48
+ async isDocumentationAvailable(workflowId) {
49
+ try {
50
+ const workflow = await this.storage.getWorkflowById(workflowId);
51
+ if (!workflow) {
52
+ return false;
53
+ }
54
+ if (workflow.documentation) {
55
+ return true;
56
+ }
57
+ if (this.storage.hasDocumentation) {
58
+ return await this.storage.hasDocumentation(workflowId);
59
+ }
60
+ return true;
61
+ }
62
+ catch {
63
+ return false;
64
+ }
65
+ }
66
+ async getWorkrailHelp(section, format = 'text') {
67
+ const helpContent = this.generateWorkrailHelp(section);
68
+ return {
69
+ source: 'fallback',
70
+ format: 'text',
71
+ content: helpContent
72
+ };
73
+ }
74
+ parseMarkdownSections(markdown) {
75
+ const sections = {};
76
+ const lines = markdown.split('\n');
77
+ let currentSection = '';
78
+ let currentContent = [];
79
+ for (const line of lines) {
80
+ if (line.startsWith('# ') || line.startsWith('## ')) {
81
+ if (currentSection && currentContent.length > 0) {
82
+ sections[currentSection] = currentContent.join('\n').trim();
83
+ }
84
+ currentSection = line.replace(/^#+\s*/, '').toLowerCase().replace(/\s+/g, '-');
85
+ currentContent = [];
86
+ }
87
+ else {
88
+ currentContent.push(line);
89
+ }
90
+ }
91
+ if (currentSection && currentContent.length > 0) {
92
+ sections[currentSection] = currentContent.join('\n').trim();
93
+ }
94
+ return sections;
95
+ }
96
+ createPreview(content) {
97
+ const lines = content.split('\n');
98
+ const previewLines = [];
99
+ let lineCount = 0;
100
+ const maxLines = 20;
101
+ for (const line of lines) {
102
+ if (lineCount >= maxLines) {
103
+ previewLines.push('\n[... content truncated for preview mode ...]');
104
+ break;
105
+ }
106
+ previewLines.push(line);
107
+ lineCount++;
108
+ }
109
+ return previewLines.join('\n');
110
+ }
111
+ generateFallbackDocumentation(workflow, format) {
112
+ const content = `# ${workflow.name}
113
+
114
+ ${workflow.description}
115
+
116
+ **Version:** ${workflow.version}
117
+ **Steps:** ${workflow.steps?.length || 0} steps
118
+
119
+ ${workflow.preconditions?.length ? `**Prerequisites:**\n${workflow.preconditions.map((p) => `- ${p}`).join('\n')}\n` : ''}
120
+
121
+ *This is auto-generated documentation. For detailed guidance, consider adding a documentation object to the workflow JSON or creating a README file.*`;
122
+ return {
123
+ source: 'fallback',
124
+ format: 'text',
125
+ content
126
+ };
127
+ }
128
+ generateWorkrailHelp(section) {
129
+ const baseHelp = `# WorkRail Documentation System
130
+
131
+ WorkRail provides structured workflow orchestration with built-in documentation support.
132
+
133
+ ## Available Commands
134
+ - \`workflow_list\` - List all available workflows
135
+ - \`workflow_get\` - Get detailed workflow information
136
+ - \`workflow_docs\` - Get workflow-specific documentation
137
+ - \`workrail_help\` - Get general platform help (this command)
138
+
139
+ ## Documentation Sources
140
+ WorkRail resolves documentation in this priority order:
141
+ 1. **JSON Documentation** - Structured metadata in workflow files
142
+ 2. **README Files** - Markdown documentation alongside workflows
143
+ 3. **Fallback** - Auto-generated from workflow metadata
144
+
145
+ ## Getting Help
146
+ - Use \`workflow_docs\` for specific workflow documentation
147
+ - Use \`workrail_help\` for general platform information
148
+ - Check workflow descriptions in \`workflow_list\` output`;
149
+ if (section) {
150
+ return `${baseHelp}\n\n*Note: Section-specific help for "${section}" is not yet implemented.*`;
151
+ }
152
+ return baseHelp;
153
+ }
154
+ }
155
+ exports.DefaultDocumentationService = DefaultDocumentationService;
@@ -0,0 +1,4 @@
1
+ import { IDocumentationService } from '../services/documentation-service';
2
+ import { DocumentationResult } from '../../types/documentation-types';
3
+ export declare function createGetWorkflowDocs(service: IDocumentationService): (id: string, mode?: "preview" | "full", sections?: string[], format?: string) => Promise<DocumentationResult>;
4
+ export declare function getWorkflowDocs(service: IDocumentationService, id: string, mode?: 'preview' | 'full', sections?: string[], format?: string): Promise<DocumentationResult>;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createGetWorkflowDocs = createGetWorkflowDocs;
4
+ exports.getWorkflowDocs = getWorkflowDocs;
5
+ function createGetWorkflowDocs(service) {
6
+ return async (id, mode, sections, format) => {
7
+ return await service.resolveDocumentation(id, mode, sections, format);
8
+ };
9
+ }
10
+ async function getWorkflowDocs(service, id, mode, sections, format) {
11
+ return createGetWorkflowDocs(service)(id, mode, sections, format);
12
+ }
@@ -0,0 +1,4 @@
1
+ import { IDocumentationService } from '../services/documentation-service';
2
+ import { DocumentationResult } from '../../types/documentation-types';
3
+ export declare function createGetWorkrailHelp(service: IDocumentationService): (section?: string, format?: string) => Promise<DocumentationResult>;
4
+ export declare function getWorkrailHelp(service: IDocumentationService, section?: string, format?: string): Promise<DocumentationResult>;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createGetWorkrailHelp = createGetWorkrailHelp;
4
+ exports.getWorkrailHelp = getWorkrailHelp;
5
+ function createGetWorkrailHelp(service) {
6
+ return async (section, format) => {
7
+ return await service.getWorkrailHelp(section, format);
8
+ };
9
+ }
10
+ async function getWorkrailHelp(service, section, format) {
11
+ return createGetWorkrailHelp(service)(section, format);
12
+ }
@@ -0,0 +1,37 @@
1
+ export interface DocumentationInput {
2
+ name: string;
3
+ required?: boolean;
4
+ description?: string;
5
+ }
6
+ export interface DocumentationMetadata {
7
+ summary: string;
8
+ whenToUse: string[];
9
+ whenNotToUse?: string[];
10
+ inputs: DocumentationInput[];
11
+ outputs: string[];
12
+ assumptions?: string[];
13
+ risks: string[];
14
+ relatedWorkflows?: string[];
15
+ helpHint?: string;
16
+ }
17
+ export interface SensitivityMetadata {
18
+ level?: string;
19
+ notes?: string;
20
+ }
21
+ export interface DocumentationResult {
22
+ source: 'json' | 'readme' | 'fallback';
23
+ format: 'json' | 'markdown' | 'text';
24
+ content: DocumentationMetadata | string;
25
+ sections?: Record<string, string>;
26
+ }
27
+ export interface DocumentationOptions {
28
+ mode?: 'preview' | 'full';
29
+ sections?: string[];
30
+ format?: 'text' | 'markdown' | 'json';
31
+ }
32
+ export interface DocumentationAvailability {
33
+ hasJsonDocs: boolean;
34
+ hasReadme: boolean;
35
+ hasAnyDocs: boolean;
36
+ helpHint?: string;
37
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -9,6 +9,10 @@ export interface Condition {
9
9
  gte?: number;
10
10
  lt?: number;
11
11
  lte?: number;
12
+ contains?: string;
13
+ startsWith?: string;
14
+ endsWith?: string;
15
+ matches?: string;
12
16
  and?: Condition[];
13
17
  or?: Condition[];
14
18
  not?: Condition;
@@ -2,6 +2,40 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.evaluateCondition = evaluateCondition;
4
4
  exports.validateCondition = validateCondition;
5
+ function lenientEquals(a, b) {
6
+ if (a == null && b == null) {
7
+ return true;
8
+ }
9
+ if (a == null || b == null) {
10
+ return false;
11
+ }
12
+ if (typeof a === 'string' && typeof b === 'string') {
13
+ return a.trim().toLowerCase() === b.trim().toLowerCase();
14
+ }
15
+ if ((typeof a === 'string' && typeof b === 'number') ||
16
+ (typeof a === 'number' && typeof b === 'string')) {
17
+ const numA = Number(a);
18
+ const numB = Number(b);
19
+ if (!isNaN(numA) && !isNaN(numB)) {
20
+ return numA === numB;
21
+ }
22
+ }
23
+ if (typeof a === 'string' && typeof b === 'boolean') {
24
+ const lowerA = a.trim().toLowerCase();
25
+ return (b === true && (lowerA === 'true' || lowerA === '1' || lowerA === 'yes')) ||
26
+ (b === false && (lowerA === 'false' || lowerA === '0' || lowerA === 'no'));
27
+ }
28
+ if (typeof b === 'string' && typeof a === 'boolean') {
29
+ return lenientEquals(b, a);
30
+ }
31
+ return a === b;
32
+ }
33
+ function normalizeToString(value) {
34
+ if (value == null) {
35
+ return '';
36
+ }
37
+ return String(value).trim();
38
+ }
5
39
  function evaluateCondition(condition, context = {}) {
6
40
  if (!condition || typeof condition !== 'object') {
7
41
  return true;
@@ -20,11 +54,11 @@ function evaluateCondition(condition, context = {}) {
20
54
  function evaluateConditionUnsafe(condition, context) {
21
55
  if (condition.var !== undefined) {
22
56
  const value = context[condition.var];
23
- if (condition.equals !== undefined) {
24
- return value === condition.equals;
57
+ if ('equals' in condition) {
58
+ return lenientEquals(value, condition.equals);
25
59
  }
26
- if (condition.not_equals !== undefined) {
27
- return value !== condition.not_equals;
60
+ if ('not_equals' in condition) {
61
+ return !lenientEquals(value, condition.not_equals);
28
62
  }
29
63
  if (condition.gt !== undefined) {
30
64
  return typeof value === 'number' && value > condition.gt;
@@ -38,6 +72,32 @@ function evaluateConditionUnsafe(condition, context) {
38
72
  if (condition.lte !== undefined) {
39
73
  return typeof value === 'number' && value <= condition.lte;
40
74
  }
75
+ if (condition.contains !== undefined) {
76
+ const valueStr = normalizeToString(value).toLowerCase();
77
+ const searchStr = normalizeToString(condition.contains).toLowerCase();
78
+ return valueStr.includes(searchStr);
79
+ }
80
+ if (condition.startsWith !== undefined) {
81
+ const valueStr = normalizeToString(value).toLowerCase();
82
+ const searchStr = normalizeToString(condition.startsWith).toLowerCase();
83
+ return valueStr.startsWith(searchStr);
84
+ }
85
+ if (condition.endsWith !== undefined) {
86
+ const valueStr = normalizeToString(value).toLowerCase();
87
+ const searchStr = normalizeToString(condition.endsWith).toLowerCase();
88
+ return valueStr.endsWith(searchStr);
89
+ }
90
+ if (condition.matches !== undefined) {
91
+ const valueStr = normalizeToString(value);
92
+ try {
93
+ const regex = new RegExp(condition.matches, 'i');
94
+ return regex.test(valueStr);
95
+ }
96
+ catch (error) {
97
+ console.warn('Invalid regex pattern in condition:', condition.matches);
98
+ return false;
99
+ }
100
+ }
41
101
  return !!value;
42
102
  }
43
103
  if (condition.and !== undefined) {
@@ -62,7 +122,9 @@ function validateCondition(condition) {
62
122
  return;
63
123
  }
64
124
  const supportedKeys = [
65
- 'var', 'equals', 'not_equals', 'gt', 'gte', 'lt', 'lte', 'and', 'or', 'not'
125
+ 'var', 'equals', 'not_equals', 'gt', 'gte', 'lt', 'lte',
126
+ 'contains', 'startsWith', 'endsWith', 'matches',
127
+ 'and', 'or', 'not'
66
128
  ];
67
129
  const conditionKeys = Object.keys(condition);
68
130
  const unsupportedKeys = conditionKeys.filter(key => !supportedKeys.includes(key));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exaudeus/workrail",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "description": "MCP server for structured workflow orchestration and step-by-step task guidance",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -43,7 +43,8 @@
43
43
  "Maintain existing coding conventions and architectural patterns found in the codebase.",
44
44
  "COMMIT STRATEGY: Auto-commit after successful steps for High automation; suggest for Medium/Low. Use conventional format: type(scope): description. Commit at milestones and after verification passes.",
45
45
  "When you see function calls like updateDecisionLog() or createFile(spec.md), refer to the function definitions above for full instructions.",
46
- "For resumption: Include function definitions in CONTEXT.md so new sessions understand these references. Always provide explicit workflow_get and workflow_next instructions."
46
+ "For resumption: Include function definitions in CONTEXT.md so new sessions understand these references. Always provide explicit workflow_get and workflow_next instructions.",
47
+ "Auto-advance: after completing each step, immediately get the next step; only pause for explicit requireConfirmation or missing critical inputs."
47
48
  ],
48
49
  "steps": [
49
50
  {