berget 2.2.6 → 2.2.8
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.
- package/.github/workflows/publish.yml +2 -2
- package/.github/workflows/test.yml +10 -4
- package/.husky/pre-commit +1 -0
- package/.prettierignore +15 -0
- package/.prettierrc +7 -3
- package/CONTRIBUTING.md +38 -0
- package/README.md +2 -148
- package/dist/index.js +10 -11
- package/dist/package.json +30 -2
- package/dist/src/agents/app.js +28 -0
- package/dist/src/agents/backend.js +25 -0
- package/dist/src/agents/devops.js +34 -0
- package/dist/src/agents/frontend.js +25 -0
- package/dist/src/agents/fullstack.js +25 -0
- package/dist/src/agents/index.js +61 -0
- package/dist/src/agents/quality.js +70 -0
- package/dist/src/agents/security.js +26 -0
- package/dist/src/agents/types.js +2 -0
- package/dist/src/client.js +97 -117
- package/dist/src/commands/api-keys.js +75 -90
- package/dist/src/commands/auth.js +7 -16
- package/dist/src/commands/autocomplete.js +1 -1
- package/dist/src/commands/billing.js +6 -17
- package/dist/src/commands/chat.js +68 -101
- package/dist/src/commands/clusters.js +9 -18
- package/dist/src/commands/code/__tests__/auth-sync.test.js +351 -0
- package/dist/src/commands/code/__tests__/fake-api-key-service.js +13 -0
- package/dist/src/commands/code/__tests__/fake-auth-service.js +47 -0
- package/dist/src/commands/code/__tests__/fake-command-runner.js +21 -34
- package/dist/src/commands/code/__tests__/fake-file-store.js +20 -33
- package/dist/src/commands/code/__tests__/fake-prompter.js +83 -57
- package/dist/src/commands/code/__tests__/setup-flow.test.js +359 -92
- package/dist/src/commands/code/adapters/clack-prompter.js +15 -22
- package/dist/src/commands/code/adapters/fs-file-store.js +26 -40
- package/dist/src/commands/code/adapters/spawn-command-runner.js +27 -37
- package/dist/src/commands/code/auth-sync.js +270 -0
- package/dist/src/commands/code/errors.js +12 -9
- package/dist/src/commands/code/ports/auth-services.js +2 -0
- package/dist/src/commands/code/setup.js +387 -281
- package/dist/src/commands/code.js +205 -332
- package/dist/src/commands/index.js +5 -5
- package/dist/src/commands/models.js +6 -17
- package/dist/src/commands/users.js +5 -16
- package/dist/src/constants/command-structure.js +104 -104
- package/dist/src/services/api-key-service.js +132 -157
- package/dist/src/services/auth-service.js +89 -342
- package/dist/src/services/browser-auth.js +268 -0
- package/dist/src/services/chat-service.js +371 -401
- package/dist/src/services/cluster-service.js +47 -62
- package/dist/src/services/collaborator-service.js +10 -25
- package/dist/src/services/flux-service.js +14 -29
- package/dist/src/services/helm-service.js +10 -25
- package/dist/src/services/kubectl-service.js +16 -33
- package/dist/src/utils/config-checker.js +3 -3
- package/dist/src/utils/config-loader.js +95 -95
- package/dist/src/utils/default-api-key.js +124 -134
- package/dist/src/utils/env-manager.js +55 -66
- package/dist/src/utils/error-handler.js +20 -21
- package/dist/src/utils/logger.js +72 -65
- package/dist/src/utils/markdown-renderer.js +27 -27
- package/dist/src/utils/opencode-validator.js +63 -68
- package/dist/src/utils/token-manager.js +74 -45
- package/dist/tests/commands/chat.test.js +16 -25
- package/dist/tests/commands/code.test.js +95 -104
- package/dist/tests/utils/config-loader.test.js +48 -48
- package/dist/tests/utils/env-manager.test.js +43 -52
- package/dist/tests/utils/opencode-validator.test.js +22 -21
- package/dist/vitest.config.js +1 -1
- package/eslint.config.mjs +67 -0
- package/index.ts +35 -42
- package/package.json +30 -2
- package/src/agents/app.ts +27 -0
- package/src/agents/backend.ts +24 -0
- package/src/agents/devops.ts +33 -0
- package/src/agents/frontend.ts +24 -0
- package/src/agents/fullstack.ts +24 -0
- package/src/agents/index.ts +73 -0
- package/src/agents/quality.ts +69 -0
- package/src/agents/security.ts +26 -0
- package/src/agents/types.ts +17 -0
- package/src/client.ts +118 -152
- package/src/commands/api-keys.ts +241 -333
- package/src/commands/auth.ts +22 -27
- package/src/commands/autocomplete.ts +9 -9
- package/src/commands/billing.ts +20 -24
- package/src/commands/chat.ts +248 -338
- package/src/commands/clusters.ts +27 -26
- package/src/commands/code/__tests__/auth-sync.test.ts +482 -0
- package/src/commands/code/__tests__/fake-api-key-service.ts +13 -0
- package/src/commands/code/__tests__/fake-auth-service.ts +50 -0
- package/src/commands/code/__tests__/fake-command-runner.ts +45 -42
- package/src/commands/code/__tests__/fake-file-store.ts +32 -23
- package/src/commands/code/__tests__/fake-prompter.ts +116 -77
- package/src/commands/code/__tests__/setup-flow.test.ts +624 -268
- package/src/commands/code/adapters/clack-prompter.ts +53 -39
- package/src/commands/code/adapters/fs-file-store.ts +32 -27
- package/src/commands/code/adapters/spawn-command-runner.ts +38 -29
- package/src/commands/code/auth-sync.ts +329 -0
- package/src/commands/code/errors.ts +18 -18
- package/src/commands/code/ports/auth-services.ts +14 -0
- package/src/commands/code/ports/command-runner.ts +8 -4
- package/src/commands/code/ports/file-store.ts +5 -4
- package/src/commands/code/ports/prompter.ts +24 -18
- package/src/commands/code/setup.ts +570 -340
- package/src/commands/code.ts +338 -539
- package/src/commands/index.ts +20 -19
- package/src/commands/models.ts +28 -32
- package/src/commands/users.ts +15 -21
- package/src/constants/command-structure.ts +134 -157
- package/src/services/api-key-service.ts +105 -122
- package/src/services/auth-service.ts +99 -345
- package/src/services/browser-auth.ts +296 -0
- package/src/services/chat-service.ts +265 -299
- package/src/services/cluster-service.ts +42 -45
- package/src/services/collaborator-service.ts +14 -19
- package/src/services/flux-service.ts +23 -25
- package/src/services/helm-service.ts +19 -21
- package/src/services/kubectl-service.ts +17 -19
- package/src/types/api.d.ts +1905 -1907
- package/src/types/json.d.ts +2 -2
- package/src/utils/config-checker.ts +10 -10
- package/src/utils/config-loader.ts +162 -178
- package/src/utils/default-api-key.ts +114 -125
- package/src/utils/env-manager.ts +53 -57
- package/src/utils/error-handler.ts +61 -56
- package/src/utils/logger.ts +79 -73
- package/src/utils/markdown-renderer.ts +31 -31
- package/src/utils/opencode-validator.ts +85 -89
- package/src/utils/token-manager.ts +108 -87
- package/templates/agents/app.md +1 -0
- package/templates/agents/backend.md +1 -0
- package/templates/agents/devops.md +2 -0
- package/templates/agents/frontend.md +1 -0
- package/templates/agents/fullstack.md +1 -0
- package/templates/agents/quality.md +45 -40
- package/templates/agents/security.md +1 -0
- package/tests/commands/chat.test.ts +53 -62
- package/tests/commands/code.test.ts +265 -310
- package/tests/utils/config-loader.test.ts +189 -188
- package/tests/utils/env-manager.test.ts +110 -113
- package/tests/utils/opencode-validator.test.ts +52 -56
- package/tsconfig.json +4 -3
- package/vitest.config.ts +3 -3
- package/AGENTS.md +0 -374
- package/TODO.md +0 -19
|
@@ -1,47 +1,50 @@
|
|
|
1
|
-
import type { CommandRunner } from '../ports/command-runner'
|
|
1
|
+
import type { CommandRunner } from '../ports/command-runner';
|
|
2
2
|
|
|
3
3
|
type Handler = {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
4
|
+
match: (command: string, arguments_: readonly string[]) => boolean;
|
|
5
|
+
response: ((command: string, arguments_: readonly string[]) => Error | string) | Error | string;
|
|
6
|
+
};
|
|
7
7
|
|
|
8
8
|
export class FakeCommandRunner implements CommandRunner {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
9
|
+
get calls() {
|
|
10
|
+
return this._calls;
|
|
11
|
+
}
|
|
12
|
+
private _calls: Array<{ args: string[]; command: string; options?: { cwd?: string } }> = [];
|
|
13
|
+
|
|
14
|
+
private handlers: Handler[] = [];
|
|
15
|
+
|
|
16
|
+
checkInstalled(binary: string): Promise<boolean> {
|
|
17
|
+
this._calls.push({ args: [], command: `check:${binary}` });
|
|
18
|
+
return Promise.resolve(this.handlers.some((h) => h.match(binary, ['--version'])) || false);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
handle(match: RegExp | string, response: Error | string): this {
|
|
22
|
+
this.handlers.push({
|
|
23
|
+
match: (cmd, arguments_) => {
|
|
24
|
+
const full = `${cmd} ${arguments_.join(' ')}`;
|
|
25
|
+
if (typeof match === 'string') return full.startsWith(match);
|
|
26
|
+
return match.test(full);
|
|
27
|
+
},
|
|
28
|
+
response,
|
|
29
|
+
});
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async run(
|
|
34
|
+
command: string,
|
|
35
|
+
arguments_: readonly string[],
|
|
36
|
+
options?: { cwd?: string },
|
|
37
|
+
): Promise<string> {
|
|
38
|
+
this._calls.push({ args: [...arguments_], command, options });
|
|
39
|
+
const handler = this.handlers.find((h) => h.match(command, arguments_));
|
|
40
|
+
if (!handler) throw new Error(`Unexpected command: ${command} ${arguments_.join(' ')}`);
|
|
41
|
+
|
|
42
|
+
const result =
|
|
43
|
+
typeof handler.response === 'function'
|
|
44
|
+
? handler.response(command, arguments_)
|
|
45
|
+
: handler.response;
|
|
46
|
+
|
|
47
|
+
if (result instanceof Error) throw result;
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
47
50
|
}
|
|
@@ -1,35 +1,44 @@
|
|
|
1
|
-
import type { FileStore } from '../ports/file-store'
|
|
1
|
+
import type { FileStore } from '../ports/file-store';
|
|
2
2
|
|
|
3
3
|
export interface FileEntry {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
content: string;
|
|
5
|
+
isDirectory?: boolean;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export class FakeFileStore implements FileStore {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
private _chmodCalls: Array<{ mode: number; path: string }> = [];
|
|
10
|
+
private dirs: Set<string> = new Set();
|
|
11
|
+
private files: Map<string, string> = new Map();
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
async chmod(path: string, mode: number): Promise<void> {
|
|
14
|
+
this._chmodCalls.push({ mode, path });
|
|
15
|
+
}
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
async exists(path: string): Promise<boolean> {
|
|
18
|
+
return this.files.has(path) || this.dirs.has(path);
|
|
19
|
+
}
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
getChmodCalls(): Array<{ mode: number; path: string }> {
|
|
22
|
+
return this._chmodCalls;
|
|
23
|
+
}
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
getWrittenFiles(): Map<string, string> {
|
|
26
|
+
return new Map(this.files);
|
|
27
|
+
}
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
async mkdir(path: string): Promise<void> {
|
|
30
|
+
this.dirs.add(path);
|
|
31
|
+
}
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
async readFile(path: string): Promise<null | string> {
|
|
34
|
+
return this.files.get(path) ?? null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
seed(path: string, content: string): void {
|
|
38
|
+
this.files.set(path, content);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async writeFile(path: string, content: string): Promise<void> {
|
|
42
|
+
this.files.set(path, content);
|
|
43
|
+
}
|
|
35
44
|
}
|
|
@@ -1,83 +1,122 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { Prompter, Spinner } from '../ports/prompter'
|
|
1
|
+
import type { Prompter, Spinner } from '../ports/prompter';
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
import { CancelledError } from '../errors';
|
|
4
|
+
|
|
5
|
+
export const CANCEL = Symbol('cancel');
|
|
5
6
|
|
|
6
7
|
type PromptEntry =
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
8
|
+
| { kind: 'confirm'; match?: RegExp; response: boolean | symbol }
|
|
9
|
+
| { kind: 'multiselect'; match?: RegExp; response: (string | symbol)[] }
|
|
10
|
+
| { kind: 'select'; match?: RegExp; response: string | symbol }
|
|
11
|
+
| { kind: 'text'; match?: RegExp; response: string | symbol };
|
|
12
|
+
|
|
13
|
+
export const select = <T>(value: symbol | T, match?: RegExp | string): PromptEntry => ({
|
|
14
|
+
kind: 'select',
|
|
15
|
+
match: typeof match === 'string' ? new RegExp(match) : match,
|
|
16
|
+
response: typeof value === 'symbol' ? value : String(value),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export const text = (value: string | symbol, match?: RegExp | string): PromptEntry => ({
|
|
20
|
+
kind: 'text',
|
|
21
|
+
match: typeof match === 'string' ? new RegExp(match) : match,
|
|
22
|
+
response: value,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export const confirm = (value: boolean | symbol, match?: RegExp | string): PromptEntry => ({
|
|
26
|
+
kind: 'confirm',
|
|
27
|
+
match: typeof match === 'string' ? new RegExp(match) : match,
|
|
28
|
+
response: value,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export const multiselect = <T>(values: symbol | T[], match?: RegExp | string): PromptEntry => ({
|
|
32
|
+
kind: 'multiselect',
|
|
33
|
+
match: typeof match === 'string' ? new RegExp(match) : match,
|
|
34
|
+
response: values === CANCEL ? [CANCEL] : ((values as T[]).map(String) as (string | symbol)[]),
|
|
35
|
+
});
|
|
27
36
|
|
|
28
37
|
export class FakePrompter implements Prompter {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
38
|
+
get calls() {
|
|
39
|
+
return this._calls;
|
|
40
|
+
}
|
|
41
|
+
private _calls: Array<{ args: unknown; method: string }> = [];
|
|
42
|
+
|
|
43
|
+
private _cursor = 0;
|
|
44
|
+
|
|
45
|
+
constructor(private readonly _script: PromptEntry[]) {}
|
|
46
|
+
assertExhausted() {
|
|
47
|
+
if (this._cursor !== this._script.length) {
|
|
48
|
+
throw new Error(`Script not exhausted: ${this._script.length - this._cursor} entries left`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async confirm(options: { message: string }): Promise<boolean> {
|
|
52
|
+
this._calls.push({ args: options, method: 'confirm' });
|
|
53
|
+
const entry = this._script[this._cursor++];
|
|
54
|
+
if (!entry)
|
|
55
|
+
throw new Error(`No script entry for confirm #${this._cursor} (${options.message})`);
|
|
56
|
+
if (entry.kind !== 'confirm')
|
|
57
|
+
throw new Error(`Expected confirm, got ${entry.kind} for ${options.message}`);
|
|
58
|
+
if (entry.match && !entry.match.test(options.message))
|
|
59
|
+
throw new Error(`Message mismatch: got "${options.message}"`);
|
|
60
|
+
if (entry.response === CANCEL) throw new CancelledError();
|
|
61
|
+
return entry.response as boolean;
|
|
62
|
+
}
|
|
63
|
+
intro(message: string): void {
|
|
64
|
+
this._calls.push({ args: { message }, method: 'intro' });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async multiselect<T>(options: { message: string }): Promise<T[]> {
|
|
68
|
+
this._calls.push({ args: options, method: 'multiselect' });
|
|
69
|
+
const entry = this._script[this._cursor++];
|
|
70
|
+
if (!entry)
|
|
71
|
+
throw new Error(`No script entry for multiselect #${this._cursor} (${options.message})`);
|
|
72
|
+
if (entry.kind !== 'multiselect')
|
|
73
|
+
throw new Error(`Expected multiselect, got ${entry.kind} for ${options.message}`);
|
|
74
|
+
if (entry.match && !entry.match.test(options.message))
|
|
75
|
+
throw new Error(`Message mismatch: got "${options.message}"`);
|
|
76
|
+
if (entry.response.includes(CANCEL)) throw new CancelledError();
|
|
77
|
+
return entry.response as T[];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
note(message: string, title?: string): void {
|
|
81
|
+
this._calls.push({ args: { message, title }, method: 'note' });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
outro(message: string): void {
|
|
85
|
+
this._calls.push({ args: { message }, method: 'outro' });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async select<T>(options: { message: string }): Promise<T> {
|
|
89
|
+
this._calls.push({ args: options, method: 'select' });
|
|
90
|
+
const entry = this._script[this._cursor++];
|
|
91
|
+
if (!entry) throw new Error(`No script entry for select #${this._cursor} (${options.message})`);
|
|
92
|
+
if (entry.kind !== 'select')
|
|
93
|
+
throw new Error(`Expected select, got ${entry.kind} for ${options.message}`);
|
|
94
|
+
if (entry.match && !entry.match.test(options.message))
|
|
95
|
+
throw new Error(`Message mismatch: got "${options.message}"`);
|
|
96
|
+
if (entry.response === CANCEL) throw new CancelledError();
|
|
97
|
+
return entry.response as T;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
spinner(): Spinner {
|
|
101
|
+
return {
|
|
102
|
+
start: (message: string) => {
|
|
103
|
+
this._calls.push({ args: { message: message }, method: 'spinner.start' });
|
|
104
|
+
},
|
|
105
|
+
stop: (message: string) => {
|
|
106
|
+
this._calls.push({ args: { message: message }, method: 'spinner.stop' });
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async text(options: { message: string }): Promise<string> {
|
|
112
|
+
this._calls.push({ args: options, method: 'text' });
|
|
113
|
+
const entry = this._script[this._cursor++];
|
|
114
|
+
if (!entry) throw new Error(`No script entry for text #${this._cursor} (${options.message})`);
|
|
115
|
+
if (entry.kind !== 'text')
|
|
116
|
+
throw new Error(`Expected text, got ${entry.kind} for ${options.message}`);
|
|
117
|
+
if (entry.match && !entry.match.test(options.message))
|
|
118
|
+
throw new Error(`Message mismatch: got "${options.message}"`);
|
|
119
|
+
if (entry.response === CANCEL) throw new CancelledError();
|
|
120
|
+
return entry.response as string;
|
|
121
|
+
}
|
|
83
122
|
}
|