@sylphx/flow 3.19.0 → 3.20.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 (41) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/assets/agents/builder.md +12 -3
  3. package/package.json +1 -3
  4. package/src/commands/flow/execute-v2.ts +126 -128
  5. package/src/commands/flow-command.ts +52 -42
  6. package/src/commands/hook-command.ts +161 -20
  7. package/src/config/index.ts +0 -20
  8. package/src/core/agent-loader.ts +2 -2
  9. package/src/core/attach-manager.ts +5 -1
  10. package/src/core/cleanup-handler.ts +20 -16
  11. package/src/core/flow-executor.ts +93 -62
  12. package/src/core/functional/index.ts +0 -11
  13. package/src/core/index.ts +1 -1
  14. package/src/core/project-manager.ts +14 -29
  15. package/src/core/secrets-manager.ts +15 -18
  16. package/src/core/session-manager.ts +4 -8
  17. package/src/core/target-manager.ts +6 -3
  18. package/src/core/upgrade-manager.ts +1 -1
  19. package/src/index.ts +1 -1
  20. package/src/services/auto-upgrade.ts +6 -14
  21. package/src/services/config-service.ts +7 -23
  22. package/src/services/index.ts +1 -1
  23. package/src/targets/claude-code.ts +14 -8
  24. package/src/targets/functional/claude-code-logic.ts +11 -7
  25. package/src/targets/opencode.ts +61 -39
  26. package/src/targets/shared/mcp-transforms.ts +20 -43
  27. package/src/types/agent.types.ts +5 -3
  28. package/src/types/mcp.types.ts +38 -1
  29. package/src/types.ts +4 -0
  30. package/src/utils/agent-enhancer.ts +1 -1
  31. package/src/utils/errors.ts +13 -0
  32. package/src/utils/files/file-operations.ts +16 -0
  33. package/src/utils/index.ts +1 -1
  34. package/src/core/error-handling.ts +0 -482
  35. package/src/core/functional/async.ts +0 -101
  36. package/src/core/functional/either.ts +0 -109
  37. package/src/core/functional/error-handler.ts +0 -135
  38. package/src/core/functional/pipe.ts +0 -189
  39. package/src/core/functional/validation.ts +0 -138
  40. package/src/types/mcp-config.types.ts +0 -448
  41. package/src/utils/error-handler.ts +0 -53
@@ -1,135 +0,0 @@
1
- /**
2
- * Functional error handling utilities
3
- * Replaces exception-based error handling with Result-based approach
4
- *
5
- * DESIGN RATIONALE:
6
- * - Errors as values, not exceptions
7
- * - Explicit error handling in function signatures
8
- * - Composable error handling
9
- * - Separation of error handling from business logic
10
- */
11
-
12
- import type { AppError } from './error-types.js';
13
- import { formatError, toAppError } from './error-types.js';
14
- import type { Result } from './result.js';
15
- import { failure, success } from './result.js';
16
-
17
- /**
18
- * Execute a function and convert exceptions to Result
19
- */
20
- export const execute = <T>(fn: () => T): Result<T, AppError> => {
21
- try {
22
- return success(fn());
23
- } catch (error) {
24
- return failure(toAppError(error));
25
- }
26
- };
27
-
28
- /**
29
- * Execute an async function and convert exceptions to Result
30
- */
31
- export const executeAsync = async <T>(fn: () => Promise<T>): Promise<Result<T, AppError>> => {
32
- try {
33
- const value = await fn();
34
- return success(value);
35
- } catch (error) {
36
- return failure(toAppError(error));
37
- }
38
- };
39
-
40
- /**
41
- * Log error to console (side effect)
42
- */
43
- export const logError = (error: AppError): void => {
44
- console.error(formatError(error));
45
- };
46
-
47
- /**
48
- * Log error and exit process (side effect)
49
- * Only use at top-level command handlers
50
- */
51
- export const exitWithError = (error: AppError, exitCode = 1): never => {
52
- logError(error);
53
- process.exit(exitCode);
54
- };
55
-
56
- /**
57
- * Convert Result to exit code
58
- * 0 for success, 1 for failure
59
- * Use at top-level command handlers
60
- */
61
- export const toExitCode = <T>(result: Result<T, AppError>): number => {
62
- if (result._tag === 'Success') {
63
- return 0;
64
- }
65
- logError(result.error);
66
- return 1;
67
- };
68
-
69
- /**
70
- * Retry logic with exponential backoff
71
- * Retries a function that returns a Result
72
- */
73
- export const retry = async <T>(
74
- fn: () => Promise<Result<T, AppError>>,
75
- options: {
76
- maxRetries: number;
77
- delayMs: number;
78
- backoff?: number;
79
- onRetry?: (error: AppError, attempt: number) => void;
80
- }
81
- ): Promise<Result<T, AppError>> => {
82
- const { maxRetries, delayMs, backoff = 2, onRetry } = options;
83
-
84
- let lastError: AppError = { type: 'unknown', message: 'Unknown error', code: 'UNKNOWN' };
85
- let currentDelay = delayMs;
86
-
87
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
88
- const result = await fn();
89
-
90
- if (result._tag === 'Success') {
91
- return result;
92
- }
93
-
94
- lastError = result.error;
95
-
96
- if (attempt < maxRetries) {
97
- if (onRetry) {
98
- onRetry(lastError, attempt + 1);
99
- }
100
-
101
- await new Promise((resolve) => setTimeout(resolve, currentDelay));
102
- currentDelay *= backoff;
103
- }
104
- }
105
-
106
- return failure(lastError);
107
- };
108
-
109
- /**
110
- * Create an async handler that wraps a function returning Result
111
- * Useful for command handlers
112
- */
113
- export const createAsyncHandler =
114
- <T extends Record<string, any>>(handler: (options: T) => Promise<Result<void, AppError>>) =>
115
- async (options: T): Promise<void> => {
116
- const result = await handler(options);
117
-
118
- if (result._tag === 'Failure') {
119
- exitWithError(result.error);
120
- }
121
- };
122
-
123
- /**
124
- * Create a sync handler that wraps a function returning Result
125
- * Useful for command handlers
126
- */
127
- export const createSyncHandler =
128
- <T extends Record<string, any>>(handler: (options: T) => Result<void, AppError>) =>
129
- (options: T): void => {
130
- const result = handler(options);
131
-
132
- if (result._tag === 'Failure') {
133
- exitWithError(result.error);
134
- }
135
- };
@@ -1,189 +0,0 @@
1
- /**
2
- * Function composition utilities
3
- * Enable declarative data transformation pipelines
4
- *
5
- * DESIGN RATIONALE:
6
- * - Left-to-right composition (more readable than f(g(h(x))))
7
- * - Type-safe (TypeScript infers types through the pipeline)
8
- * - Point-free style support
9
- * - Declarative over imperative
10
- */
11
-
12
- /**
13
- * Compose functions left-to-right
14
- * pipe(value, fn1, fn2, fn3) === fn3(fn2(fn1(value)))
15
- */
16
- export function pipe<A>(value: A): A;
17
- export function pipe<A, B>(value: A, fn1: (a: A) => B): B;
18
- export function pipe<A, B, C>(value: A, fn1: (a: A) => B, fn2: (b: B) => C): C;
19
- export function pipe<A, B, C, D>(value: A, fn1: (a: A) => B, fn2: (b: B) => C, fn3: (c: C) => D): D;
20
- export function pipe<A, B, C, D, E>(
21
- value: A,
22
- fn1: (a: A) => B,
23
- fn2: (b: B) => C,
24
- fn3: (c: C) => D,
25
- fn4: (d: D) => E
26
- ): E;
27
- export function pipe<A, B, C, D, E, F>(
28
- value: A,
29
- fn1: (a: A) => B,
30
- fn2: (b: B) => C,
31
- fn3: (c: C) => D,
32
- fn4: (d: D) => E,
33
- fn5: (e: E) => F
34
- ): F;
35
- export function pipe<A, B, C, D, E, F, G>(
36
- value: A,
37
- fn1: (a: A) => B,
38
- fn2: (b: B) => C,
39
- fn3: (c: C) => D,
40
- fn4: (d: D) => E,
41
- fn5: (e: E) => F,
42
- fn6: (f: F) => G
43
- ): G;
44
- export function pipe<A, B, C, D, E, F, G, H>(
45
- value: A,
46
- fn1: (a: A) => B,
47
- fn2: (b: B) => C,
48
- fn3: (c: C) => D,
49
- fn4: (d: D) => E,
50
- fn5: (e: E) => F,
51
- fn6: (f: F) => G,
52
- fn7: (g: G) => H
53
- ): H;
54
- export function pipe<A, B, C, D, E, F, G, H, I>(
55
- value: A,
56
- fn1: (a: A) => B,
57
- fn2: (b: B) => C,
58
- fn3: (c: C) => D,
59
- fn4: (d: D) => E,
60
- fn5: (e: E) => F,
61
- fn6: (f: F) => G,
62
- fn7: (g: G) => H,
63
- fn8: (h: H) => I
64
- ): I;
65
- export function pipe<A, B, C, D, E, F, G, H, I, J>(
66
- value: A,
67
- fn1: (a: A) => B,
68
- fn2: (b: B) => C,
69
- fn3: (c: C) => D,
70
- fn4: (d: D) => E,
71
- fn5: (e: E) => F,
72
- fn6: (f: F) => G,
73
- fn7: (g: G) => H,
74
- fn8: (h: H) => I,
75
- fn9: (i: I) => J
76
- ): J;
77
- export function pipe(value: any, ...fns: Array<(arg: any) => any>): any {
78
- return fns.reduce((acc, fn) => fn(acc), value);
79
- }
80
-
81
- /**
82
- * Create a composed function from multiple functions
83
- * flow(fn1, fn2, fn3) creates a new function that applies fn1, then fn2, then fn3
84
- */
85
- export function flow<A, B>(fn1: (a: A) => B): (a: A) => B;
86
- export function flow<A, B, C>(fn1: (a: A) => B, fn2: (b: B) => C): (a: A) => C;
87
- export function flow<A, B, C, D>(fn1: (a: A) => B, fn2: (b: B) => C, fn3: (c: C) => D): (a: A) => D;
88
- export function flow<A, B, C, D, E>(
89
- fn1: (a: A) => B,
90
- fn2: (b: B) => C,
91
- fn3: (c: C) => D,
92
- fn4: (d: D) => E
93
- ): (a: A) => E;
94
- export function flow<A, B, C, D, E, F>(
95
- fn1: (a: A) => B,
96
- fn2: (b: B) => C,
97
- fn3: (c: C) => D,
98
- fn4: (d: D) => E,
99
- fn5: (e: E) => F
100
- ): (a: A) => F;
101
- export function flow<A, B, C, D, E, F, G>(
102
- fn1: (a: A) => B,
103
- fn2: (b: B) => C,
104
- fn3: (c: C) => D,
105
- fn4: (d: D) => E,
106
- fn5: (e: E) => F,
107
- fn6: (f: F) => G
108
- ): (a: A) => G;
109
- export function flow<A, B, C, D, E, F, G, H>(
110
- fn1: (a: A) => B,
111
- fn2: (b: B) => C,
112
- fn3: (c: C) => D,
113
- fn4: (d: D) => E,
114
- fn5: (e: E) => F,
115
- fn6: (f: F) => G,
116
- fn7: (g: G) => H
117
- ): (a: A) => H;
118
- export function flow<A, B, C, D, E, F, G, H, I>(
119
- fn1: (a: A) => B,
120
- fn2: (b: B) => C,
121
- fn3: (c: C) => D,
122
- fn4: (d: D) => E,
123
- fn5: (e: E) => F,
124
- fn6: (f: F) => G,
125
- fn7: (g: G) => H,
126
- fn8: (h: H) => I
127
- ): (a: A) => I;
128
- export function flow<A, B, C, D, E, F, G, H, I, J>(
129
- fn1: (a: A) => B,
130
- fn2: (b: B) => C,
131
- fn3: (c: C) => D,
132
- fn4: (d: D) => E,
133
- fn5: (e: E) => F,
134
- fn6: (f: F) => G,
135
- fn7: (g: G) => H,
136
- fn8: (h: H) => I,
137
- fn9: (i: I) => J
138
- ): (a: A) => J;
139
- export function flow(...fns: Array<(arg: any) => any>): (arg: any) => any {
140
- return (value: any) => pipe(value, ...fns);
141
- }
142
-
143
- /**
144
- * Identity function - returns input unchanged
145
- * Useful as a no-op transformation or default
146
- */
147
- export const identity = <T>(value: T): T => value;
148
-
149
- /**
150
- * Constant function - returns a function that always returns the same value
151
- * Useful for providing defaults
152
- */
153
- export const constant =
154
- <T>(value: T) =>
155
- (): T =>
156
- value;
157
-
158
- /**
159
- * Tap - run a side effect and return the original value
160
- * Useful for debugging or logging in a pipeline
161
- */
162
- export const tap =
163
- <T>(fn: (value: T) => void) =>
164
- (value: T): T => {
165
- fn(value);
166
- return value;
167
- };
168
-
169
- /**
170
- * Memoize - cache function results
171
- * Only for pure functions with string/number arguments
172
- */
173
- export const memoize = <Args extends Array<string | number>, R>(
174
- fn: (...args: Args) => R
175
- ): ((...args: Args) => R) => {
176
- const cache = new Map<string, R>();
177
-
178
- return (...args: Args): R => {
179
- const key = JSON.stringify(args);
180
-
181
- if (cache.has(key)) {
182
- return cache.get(key)!;
183
- }
184
-
185
- const result = fn(...args);
186
- cache.set(key, result);
187
- return result;
188
- };
189
- };
@@ -1,138 +0,0 @@
1
- /**
2
- * Validation utilities for accumulating errors
3
- * Unlike Result which short-circuits on first error,
4
- * Validation accumulates all errors
5
- *
6
- * DESIGN RATIONALE:
7
- * - Form validation needs all errors, not just first
8
- * - Applicative functor for combining validations
9
- * - Errors accumulated in array
10
- * - Success only if all validations pass
11
- */
12
-
13
- import type { Result } from './result.js';
14
- import { failure, isSuccess, success } from './result.js';
15
-
16
- /**
17
- * Validation result with accumulated errors
18
- */
19
- export type Validation<T, E = string> = Result<T, E[]>;
20
-
21
- /**
22
- * Create a successful validation
23
- */
24
- export const valid = <T, E = string>(value: T): Validation<T, E> => success(value);
25
-
26
- /**
27
- * Create a failed validation with one or more errors
28
- */
29
- export const invalid = <T, E = string>(...errors: E[]): Validation<T, E> => failure(errors);
30
-
31
- /**
32
- * Combine multiple validations
33
- * Collects all errors if any validation fails
34
- */
35
- export const combine = <T, E = string>(validations: Validation<T, E>[]): Validation<T[], E> => {
36
- const values: T[] = [];
37
- const errors: E[] = [];
38
-
39
- for (const validation of validations) {
40
- if (isSuccess(validation)) {
41
- values.push(validation.value);
42
- } else {
43
- errors.push(...validation.error);
44
- }
45
- }
46
-
47
- if (errors.length > 0) {
48
- return invalid(...errors);
49
- }
50
-
51
- return valid(values);
52
- };
53
-
54
- /**
55
- * Validate a value against multiple validators
56
- * Returns first error or success
57
- */
58
- export const validateAll =
59
- <T, E = string>(...validators: Array<(value: T) => Validation<T, E>>) =>
60
- (value: T): Validation<T, E> => {
61
- const errors: E[] = [];
62
-
63
- for (const validator of validators) {
64
- const result = validator(value);
65
- if (!isSuccess(result)) {
66
- errors.push(...result.error);
67
- }
68
- }
69
-
70
- if (errors.length > 0) {
71
- return invalid(...errors);
72
- }
73
-
74
- return valid(value);
75
- };
76
-
77
- /**
78
- * Common validators
79
- */
80
-
81
- export const nonEmpty =
82
- (message: string) =>
83
- (value: string): Validation<string, string> => {
84
- if (value.trim().length === 0) {
85
- return invalid(message);
86
- }
87
- return valid(value);
88
- };
89
-
90
- export const minLength =
91
- (min: number, message: string) =>
92
- (value: string): Validation<string, string> => {
93
- if (value.length < min) {
94
- return invalid(message);
95
- }
96
- return valid(value);
97
- };
98
-
99
- export const maxLength =
100
- (max: number, message: string) =>
101
- (value: string): Validation<string, string> => {
102
- if (value.length > max) {
103
- return invalid(message);
104
- }
105
- return valid(value);
106
- };
107
-
108
- export const matches =
109
- (pattern: RegExp, message: string) =>
110
- (value: string): Validation<string, string> => {
111
- if (!pattern.test(value)) {
112
- return invalid(message);
113
- }
114
- return valid(value);
115
- };
116
-
117
- export const isEmail = (message: string) => matches(/^[^\s@]+@[^\s@]+\.[^\s@]+$/, message);
118
-
119
- export const isUrl = (message: string) => matches(/^https?:\/\/.+/, message);
120
-
121
- export const isNumber =
122
- (message: string) =>
123
- (value: string): Validation<number, string> => {
124
- const num = Number(value);
125
- if (Number.isNaN(num)) {
126
- return invalid(message);
127
- }
128
- return valid(num);
129
- };
130
-
131
- export const range =
132
- (min: number, max: number, message: string) =>
133
- (value: number): Validation<number, string> => {
134
- if (value < min || value > max) {
135
- return invalid(message);
136
- }
137
- return valid(value);
138
- };