@stan-chen/simple-cli 0.2.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 (87) hide show
  1. package/README.md +287 -0
  2. package/dist/cli.d.ts +6 -0
  3. package/dist/cli.js +259 -0
  4. package/dist/commands/add.d.ts +9 -0
  5. package/dist/commands/add.js +50 -0
  6. package/dist/commands/git/commit.d.ts +12 -0
  7. package/dist/commands/git/commit.js +97 -0
  8. package/dist/commands/git/status.d.ts +6 -0
  9. package/dist/commands/git/status.js +42 -0
  10. package/dist/commands/index.d.ts +16 -0
  11. package/dist/commands/index.js +376 -0
  12. package/dist/commands/mcp/status.d.ts +6 -0
  13. package/dist/commands/mcp/status.js +31 -0
  14. package/dist/commands/swarm.d.ts +36 -0
  15. package/dist/commands/swarm.js +236 -0
  16. package/dist/commands.d.ts +32 -0
  17. package/dist/commands.js +427 -0
  18. package/dist/context.d.ts +116 -0
  19. package/dist/context.js +327 -0
  20. package/dist/index.d.ts +6 -0
  21. package/dist/index.js +109 -0
  22. package/dist/lib/agent.d.ts +98 -0
  23. package/dist/lib/agent.js +281 -0
  24. package/dist/lib/editor.d.ts +74 -0
  25. package/dist/lib/editor.js +441 -0
  26. package/dist/lib/git.d.ts +164 -0
  27. package/dist/lib/git.js +351 -0
  28. package/dist/lib/ui.d.ts +159 -0
  29. package/dist/lib/ui.js +252 -0
  30. package/dist/mcp/client.d.ts +22 -0
  31. package/dist/mcp/client.js +81 -0
  32. package/dist/mcp/manager.d.ts +186 -0
  33. package/dist/mcp/manager.js +442 -0
  34. package/dist/prompts/provider.d.ts +22 -0
  35. package/dist/prompts/provider.js +78 -0
  36. package/dist/providers/index.d.ts +15 -0
  37. package/dist/providers/index.js +82 -0
  38. package/dist/providers/multi.d.ts +11 -0
  39. package/dist/providers/multi.js +28 -0
  40. package/dist/registry.d.ts +24 -0
  41. package/dist/registry.js +379 -0
  42. package/dist/repoMap.d.ts +5 -0
  43. package/dist/repoMap.js +79 -0
  44. package/dist/router.d.ts +41 -0
  45. package/dist/router.js +108 -0
  46. package/dist/skills.d.ts +25 -0
  47. package/dist/skills.js +288 -0
  48. package/dist/swarm/coordinator.d.ts +86 -0
  49. package/dist/swarm/coordinator.js +257 -0
  50. package/dist/swarm/index.d.ts +28 -0
  51. package/dist/swarm/index.js +29 -0
  52. package/dist/swarm/task.d.ts +104 -0
  53. package/dist/swarm/task.js +221 -0
  54. package/dist/swarm/types.d.ts +132 -0
  55. package/dist/swarm/types.js +37 -0
  56. package/dist/swarm/worker.d.ts +107 -0
  57. package/dist/swarm/worker.js +299 -0
  58. package/dist/tools/analyzeFile.d.ts +16 -0
  59. package/dist/tools/analyzeFile.js +43 -0
  60. package/dist/tools/git.d.ts +40 -0
  61. package/dist/tools/git.js +236 -0
  62. package/dist/tools/glob.d.ts +34 -0
  63. package/dist/tools/glob.js +165 -0
  64. package/dist/tools/grep.d.ts +53 -0
  65. package/dist/tools/grep.js +296 -0
  66. package/dist/tools/linter.d.ts +35 -0
  67. package/dist/tools/linter.js +349 -0
  68. package/dist/tools/listDir.d.ts +29 -0
  69. package/dist/tools/listDir.js +50 -0
  70. package/dist/tools/memory.d.ts +34 -0
  71. package/dist/tools/memory.js +215 -0
  72. package/dist/tools/readFiles.d.ts +25 -0
  73. package/dist/tools/readFiles.js +31 -0
  74. package/dist/tools/reloadTools.d.ts +11 -0
  75. package/dist/tools/reloadTools.js +22 -0
  76. package/dist/tools/runCommand.d.ts +32 -0
  77. package/dist/tools/runCommand.js +79 -0
  78. package/dist/tools/scraper.d.ts +31 -0
  79. package/dist/tools/scraper.js +211 -0
  80. package/dist/tools/writeFiles.d.ts +63 -0
  81. package/dist/tools/writeFiles.js +87 -0
  82. package/dist/ui/server.d.ts +5 -0
  83. package/dist/ui/server.js +74 -0
  84. package/dist/watcher.d.ts +35 -0
  85. package/dist/watcher.js +164 -0
  86. package/docs/assets/logo.jpeg +0 -0
  87. package/package.json +78 -0
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Task Queue - Priority-based task queue with dependency tracking
3
+ */
4
+ import type { SwarmTask, TaskResult, FailedTask } from './types.js';
5
+ export declare class TaskQueue {
6
+ private tasks;
7
+ private pending;
8
+ private running;
9
+ private completed;
10
+ private failed;
11
+ private results;
12
+ private failures;
13
+ private attempts;
14
+ /**
15
+ * Add a task to the queue
16
+ */
17
+ addTask(task: SwarmTask): void;
18
+ /**
19
+ * Add multiple tasks
20
+ */
21
+ addTasks(tasks: SwarmTask[]): void;
22
+ /**
23
+ * Get next available task (respects dependencies and priorities)
24
+ */
25
+ getNextTask(): SwarmTask | null;
26
+ /**
27
+ * Get all tasks that are ready to run
28
+ */
29
+ getAvailableTasks(): SwarmTask[];
30
+ /**
31
+ * Check if a task's dependencies are all completed
32
+ */
33
+ areDependenciesMet(task: SwarmTask): boolean;
34
+ /**
35
+ * Mark a task as completed
36
+ */
37
+ completeTask(taskId: string, result: TaskResult): void;
38
+ /**
39
+ * Mark a task as failed (may retry)
40
+ */
41
+ failTask(taskId: string, error: string, maxRetries: number): boolean;
42
+ /**
43
+ * Get number of attempts for a task
44
+ */
45
+ getAttempts(taskId: string): number;
46
+ /**
47
+ * Peek at next task without removing it
48
+ */
49
+ peekNextTask(): SwarmTask | null;
50
+ /**
51
+ * Check if all tasks are done (completed or failed)
52
+ */
53
+ isDone(): boolean;
54
+ /**
55
+ * Check if there's any work to do
56
+ */
57
+ hasWork(): boolean;
58
+ /**
59
+ * Check if any tasks are currently running
60
+ */
61
+ hasRunning(): boolean;
62
+ /**
63
+ * Get queue statistics
64
+ */
65
+ getStats(): {
66
+ total: number;
67
+ pending: number;
68
+ running: number;
69
+ completed: number;
70
+ failed: number;
71
+ };
72
+ /**
73
+ * Get all results
74
+ */
75
+ getResults(): TaskResult[];
76
+ /**
77
+ * Get all failures
78
+ */
79
+ getFailures(): FailedTask[];
80
+ /**
81
+ * Get a specific task
82
+ */
83
+ getTask(taskId: string): SwarmTask | undefined;
84
+ /**
85
+ * Get all tasks
86
+ */
87
+ getAllTasks(): SwarmTask[];
88
+ /**
89
+ * Clear the queue
90
+ */
91
+ clear(): void;
92
+ /**
93
+ * Cancel a pending task
94
+ */
95
+ cancelTask(taskId: string): boolean;
96
+ /**
97
+ * Check if a task is blocked by failed dependencies
98
+ */
99
+ isBlocked(taskId: string): boolean;
100
+ /**
101
+ * Skip tasks that are blocked by failed dependencies
102
+ */
103
+ skipBlockedTasks(): string[];
104
+ }
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Task Queue - Priority-based task queue with dependency tracking
3
+ */
4
+ import { swarmTaskSchema } from './types.js';
5
+ export class TaskQueue {
6
+ tasks = new Map();
7
+ pending = new Set();
8
+ running = new Set();
9
+ completed = new Set();
10
+ failed = new Set();
11
+ results = new Map();
12
+ failures = new Map();
13
+ attempts = new Map();
14
+ /**
15
+ * Add a task to the queue
16
+ */
17
+ addTask(task) {
18
+ const validated = swarmTaskSchema.parse(task);
19
+ this.tasks.set(validated.id, validated);
20
+ this.pending.add(validated.id);
21
+ this.attempts.set(validated.id, 0);
22
+ }
23
+ /**
24
+ * Add multiple tasks
25
+ */
26
+ addTasks(tasks) {
27
+ for (const task of tasks) {
28
+ this.addTask(task);
29
+ }
30
+ }
31
+ /**
32
+ * Get next available task (respects dependencies and priorities)
33
+ */
34
+ getNextTask() {
35
+ const available = this.getAvailableTasks();
36
+ if (available.length === 0)
37
+ return null;
38
+ // Sort by priority (1 = highest)
39
+ available.sort((a, b) => a.priority - b.priority);
40
+ const task = available[0];
41
+ this.pending.delete(task.id);
42
+ this.running.add(task.id);
43
+ return task;
44
+ }
45
+ /**
46
+ * Get all tasks that are ready to run
47
+ */
48
+ getAvailableTasks() {
49
+ const available = [];
50
+ for (const taskId of this.pending) {
51
+ const task = this.tasks.get(taskId);
52
+ if (this.areDependenciesMet(task)) {
53
+ available.push(task);
54
+ }
55
+ }
56
+ return available;
57
+ }
58
+ /**
59
+ * Check if a task's dependencies are all completed
60
+ */
61
+ areDependenciesMet(task) {
62
+ if (!task.dependencies || task.dependencies.length === 0) {
63
+ return true;
64
+ }
65
+ return task.dependencies.every(depId => this.completed.has(depId));
66
+ }
67
+ /**
68
+ * Mark a task as completed
69
+ */
70
+ completeTask(taskId, result) {
71
+ this.running.delete(taskId);
72
+ this.completed.add(taskId);
73
+ this.results.set(taskId, result);
74
+ }
75
+ /**
76
+ * Mark a task as failed (may retry)
77
+ */
78
+ failTask(taskId, error, maxRetries) {
79
+ const attempts = (this.attempts.get(taskId) || 0) + 1;
80
+ this.attempts.set(taskId, attempts);
81
+ if (attempts < maxRetries) {
82
+ // Retry: move back to pending
83
+ this.running.delete(taskId);
84
+ this.pending.add(taskId);
85
+ return true; // Will retry
86
+ }
87
+ // Max retries exceeded
88
+ this.running.delete(taskId);
89
+ this.failed.add(taskId);
90
+ const task = this.tasks.get(taskId);
91
+ this.failures.set(taskId, {
92
+ task,
93
+ error,
94
+ attempts,
95
+ });
96
+ return false; // No more retries
97
+ }
98
+ /**
99
+ * Get number of attempts for a task
100
+ */
101
+ getAttempts(taskId) {
102
+ return this.attempts.get(taskId) || 0;
103
+ }
104
+ /**
105
+ * Peek at next task without removing it
106
+ */
107
+ peekNextTask() {
108
+ const available = this.getAvailableTasks();
109
+ if (available.length === 0)
110
+ return null;
111
+ available.sort((a, b) => a.priority - b.priority);
112
+ return available[0];
113
+ }
114
+ /**
115
+ * Check if all tasks are done (completed or failed)
116
+ */
117
+ isDone() {
118
+ return this.pending.size === 0 && this.running.size === 0;
119
+ }
120
+ /**
121
+ * Check if there's any work to do
122
+ */
123
+ hasWork() {
124
+ return this.pending.size > 0 || this.running.size > 0;
125
+ }
126
+ /**
127
+ * Check if any tasks are currently running
128
+ */
129
+ hasRunning() {
130
+ return this.running.size > 0;
131
+ }
132
+ /**
133
+ * Get queue statistics
134
+ */
135
+ getStats() {
136
+ return {
137
+ total: this.tasks.size,
138
+ pending: this.pending.size,
139
+ running: this.running.size,
140
+ completed: this.completed.size,
141
+ failed: this.failed.size,
142
+ };
143
+ }
144
+ /**
145
+ * Get all results
146
+ */
147
+ getResults() {
148
+ return Array.from(this.results.values());
149
+ }
150
+ /**
151
+ * Get all failures
152
+ */
153
+ getFailures() {
154
+ return Array.from(this.failures.values());
155
+ }
156
+ /**
157
+ * Get a specific task
158
+ */
159
+ getTask(taskId) {
160
+ return this.tasks.get(taskId);
161
+ }
162
+ /**
163
+ * Get all tasks
164
+ */
165
+ getAllTasks() {
166
+ return Array.from(this.tasks.values());
167
+ }
168
+ /**
169
+ * Clear the queue
170
+ */
171
+ clear() {
172
+ this.tasks.clear();
173
+ this.pending.clear();
174
+ this.running.clear();
175
+ this.completed.clear();
176
+ this.failed.clear();
177
+ this.results.clear();
178
+ this.failures.clear();
179
+ this.attempts.clear();
180
+ }
181
+ /**
182
+ * Cancel a pending task
183
+ */
184
+ cancelTask(taskId) {
185
+ if (this.pending.has(taskId)) {
186
+ this.pending.delete(taskId);
187
+ this.tasks.delete(taskId);
188
+ return true;
189
+ }
190
+ return false;
191
+ }
192
+ /**
193
+ * Check if a task is blocked by failed dependencies
194
+ */
195
+ isBlocked(taskId) {
196
+ const task = this.tasks.get(taskId);
197
+ if (!task || !task.dependencies)
198
+ return false;
199
+ return task.dependencies.some(depId => this.failed.has(depId));
200
+ }
201
+ /**
202
+ * Skip tasks that are blocked by failed dependencies
203
+ */
204
+ skipBlockedTasks() {
205
+ const skipped = [];
206
+ for (const taskId of [...this.pending]) {
207
+ if (this.isBlocked(taskId)) {
208
+ this.pending.delete(taskId);
209
+ this.failed.add(taskId);
210
+ const task = this.tasks.get(taskId);
211
+ this.failures.set(taskId, {
212
+ task,
213
+ error: 'Skipped due to failed dependency',
214
+ attempts: 0,
215
+ });
216
+ skipped.push(taskId);
217
+ }
218
+ }
219
+ return skipped;
220
+ }
221
+ }
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Swarm Types - Type definitions for swarm coordination
3
+ */
4
+ import { z } from 'zod';
5
+ export type Priority = 1 | 2 | 3;
6
+ export type TaskType = 'implement' | 'test' | 'refactor' | 'review' | 'fix';
7
+ export type WorkerState = 'idle' | 'running' | 'completed' | 'failed';
8
+ export declare const taskScopeSchema: z.ZodObject<{
9
+ files: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
10
+ directories: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
11
+ pattern: z.ZodOptional<z.ZodString>;
12
+ }, "strip", z.ZodTypeAny, {
13
+ files?: string[] | undefined;
14
+ pattern?: string | undefined;
15
+ directories?: string[] | undefined;
16
+ }, {
17
+ files?: string[] | undefined;
18
+ pattern?: string | undefined;
19
+ directories?: string[] | undefined;
20
+ }>;
21
+ export type TaskScope = z.infer<typeof taskScopeSchema>;
22
+ export declare const swarmTaskSchema: z.ZodObject<{
23
+ id: z.ZodString;
24
+ type: z.ZodEnum<["implement", "test", "refactor", "review", "fix"]>;
25
+ description: z.ZodString;
26
+ scope: z.ZodObject<{
27
+ files: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
28
+ directories: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
29
+ pattern: z.ZodOptional<z.ZodString>;
30
+ }, "strip", z.ZodTypeAny, {
31
+ files?: string[] | undefined;
32
+ pattern?: string | undefined;
33
+ directories?: string[] | undefined;
34
+ }, {
35
+ files?: string[] | undefined;
36
+ pattern?: string | undefined;
37
+ directories?: string[] | undefined;
38
+ }>;
39
+ dependencies: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
40
+ priority: z.ZodDefault<z.ZodUnion<[z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<3>]>>;
41
+ timeout: z.ZodDefault<z.ZodNumber>;
42
+ retries: z.ZodDefault<z.ZodNumber>;
43
+ }, "strip", z.ZodTypeAny, {
44
+ type: "test" | "refactor" | "review" | "implement" | "fix";
45
+ timeout: number;
46
+ description: string;
47
+ id: string;
48
+ scope: {
49
+ files?: string[] | undefined;
50
+ pattern?: string | undefined;
51
+ directories?: string[] | undefined;
52
+ };
53
+ dependencies: string[];
54
+ priority: 1 | 2 | 3;
55
+ retries: number;
56
+ }, {
57
+ type: "test" | "refactor" | "review" | "implement" | "fix";
58
+ description: string;
59
+ id: string;
60
+ scope: {
61
+ files?: string[] | undefined;
62
+ pattern?: string | undefined;
63
+ directories?: string[] | undefined;
64
+ };
65
+ timeout?: number | undefined;
66
+ dependencies?: string[] | undefined;
67
+ priority?: 1 | 2 | 3 | undefined;
68
+ retries?: number | undefined;
69
+ }>;
70
+ export type SwarmTask = z.infer<typeof swarmTaskSchema>;
71
+ export interface WorkerResult {
72
+ success: boolean;
73
+ filesChanged: string[];
74
+ commitHash?: string;
75
+ error?: string;
76
+ duration: number;
77
+ output?: string;
78
+ }
79
+ export interface WorkerStatus {
80
+ id: string;
81
+ pid?: number;
82
+ state: WorkerState;
83
+ currentTask?: string;
84
+ startedAt?: number;
85
+ completedAt?: number;
86
+ result?: WorkerResult;
87
+ }
88
+ export interface RetryPolicy {
89
+ maxRetries: number;
90
+ backoffMs: number;
91
+ backoffMultiplier: number;
92
+ maxBackoffMs: number;
93
+ }
94
+ export interface CoordinatorOptions {
95
+ cwd?: string;
96
+ concurrency?: number;
97
+ branch?: string;
98
+ yolo?: boolean;
99
+ retryPolicy?: Partial<RetryPolicy>;
100
+ timeout?: number;
101
+ }
102
+ export interface SwarmResult {
103
+ total: number;
104
+ completed: number;
105
+ failed: number;
106
+ skipped: number;
107
+ duration: number;
108
+ successRate: number;
109
+ results: TaskResult[];
110
+ failedTasks: FailedTask[];
111
+ }
112
+ export interface TaskResult {
113
+ task: SwarmTask;
114
+ workerId: string;
115
+ result: WorkerResult;
116
+ }
117
+ export interface FailedTask {
118
+ task: SwarmTask;
119
+ error: string;
120
+ attempts: number;
121
+ }
122
+ export type SwarmEventMap = {
123
+ 'task:start': (task: SwarmTask, workerId: string) => void;
124
+ 'task:complete': (task: SwarmTask, result: WorkerResult) => void;
125
+ 'task:fail': (task: SwarmTask, error: Error) => void;
126
+ 'task:retry': (task: SwarmTask, attempt: number) => void;
127
+ 'worker:spawn': (worker: WorkerStatus) => void;
128
+ 'worker:exit': (worker: WorkerStatus, code: number) => void;
129
+ 'swarm:complete': (result: SwarmResult) => void;
130
+ };
131
+ export declare const DEFAULT_RETRY_POLICY: RetryPolicy;
132
+ export declare const DEFAULT_COORDINATOR_OPTIONS: Required<CoordinatorOptions>;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Swarm Types - Type definitions for swarm coordination
3
+ */
4
+ import { z } from 'zod';
5
+ // Task scope schema
6
+ export const taskScopeSchema = z.object({
7
+ files: z.array(z.string()).optional(),
8
+ directories: z.array(z.string()).optional(),
9
+ pattern: z.string().optional(),
10
+ });
11
+ // Task definition schema
12
+ export const swarmTaskSchema = z.object({
13
+ id: z.string(),
14
+ type: z.enum(['implement', 'test', 'refactor', 'review', 'fix']),
15
+ description: z.string(),
16
+ scope: taskScopeSchema,
17
+ dependencies: z.array(z.string()).optional().default([]),
18
+ priority: z.union([z.literal(1), z.literal(2), z.literal(3)]).default(2),
19
+ timeout: z.number().default(300000), // 5 minutes
20
+ retries: z.number().default(2),
21
+ });
22
+ // Default retry policy
23
+ export const DEFAULT_RETRY_POLICY = {
24
+ maxRetries: 3,
25
+ backoffMs: 1000,
26
+ backoffMultiplier: 2,
27
+ maxBackoffMs: 30000,
28
+ };
29
+ // Default coordinator options
30
+ export const DEFAULT_COORDINATOR_OPTIONS = {
31
+ cwd: process.cwd(),
32
+ concurrency: 4,
33
+ branch: 'main',
34
+ yolo: false,
35
+ retryPolicy: DEFAULT_RETRY_POLICY,
36
+ timeout: 600000, // 10 minutes
37
+ };
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Worker - Spawns and manages Simple-CLI worker processes
3
+ */
4
+ import { EventEmitter } from 'events';
5
+ import type { SwarmTask, WorkerStatus, WorkerResult } from './types.js';
6
+ export interface WorkerOptions {
7
+ cwd: string;
8
+ yolo: boolean;
9
+ timeout: number;
10
+ env?: Record<string, string>;
11
+ }
12
+ export declare class Worker extends EventEmitter {
13
+ readonly id: string;
14
+ private process;
15
+ private state;
16
+ private currentTask;
17
+ private startedAt;
18
+ private output;
19
+ private options;
20
+ constructor(options: WorkerOptions);
21
+ /**
22
+ * Get current worker status
23
+ */
24
+ getStatus(): WorkerStatus;
25
+ /**
26
+ * Execute a task
27
+ */
28
+ execute(task: SwarmTask): Promise<WorkerResult>;
29
+ /**
30
+ * Build prompt from task
31
+ */
32
+ private buildPrompt;
33
+ /**
34
+ * Parse changed files from output
35
+ */
36
+ private parseChangedFiles;
37
+ /**
38
+ * Parse commit hash from output
39
+ */
40
+ private parseCommitHash;
41
+ /**
42
+ * Kill the worker process
43
+ */
44
+ kill(): void;
45
+ /**
46
+ * Check if worker is busy
47
+ */
48
+ isBusy(): boolean;
49
+ /**
50
+ * Check if worker is available
51
+ */
52
+ isAvailable(): boolean;
53
+ /**
54
+ * Reset worker for reuse
55
+ */
56
+ reset(): void;
57
+ /**
58
+ * Get task output
59
+ */
60
+ getOutput(): string;
61
+ }
62
+ /**
63
+ * Worker pool for managing multiple workers
64
+ */
65
+ export declare class WorkerPool {
66
+ private workers;
67
+ private inUse;
68
+ private options;
69
+ private maxWorkers;
70
+ constructor(maxWorkers: number, options: WorkerOptions);
71
+ /**
72
+ * Get an available worker (or create one if pool not full)
73
+ */
74
+ getWorker(): Worker | null;
75
+ /**
76
+ * Release a worker back to the pool
77
+ */
78
+ releaseWorker(worker: Worker): void;
79
+ /**
80
+ * Get all workers
81
+ */
82
+ getAllWorkers(): Worker[];
83
+ /**
84
+ * Get worker by ID
85
+ */
86
+ getWorkerById(id: string): Worker | undefined;
87
+ /**
88
+ * Get number of busy workers
89
+ */
90
+ getBusyCount(): number;
91
+ /**
92
+ * Get number of available workers
93
+ */
94
+ getAvailableCount(): number;
95
+ /**
96
+ * Kill all workers
97
+ */
98
+ killAll(): void;
99
+ /**
100
+ * Get pool status
101
+ */
102
+ getStatus(): {
103
+ total: number;
104
+ busy: number;
105
+ available: number;
106
+ };
107
+ }