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.
Files changed (145) hide show
  1. package/.github/workflows/publish.yml +2 -2
  2. package/.github/workflows/test.yml +10 -4
  3. package/.husky/pre-commit +1 -0
  4. package/.prettierignore +15 -0
  5. package/.prettierrc +7 -3
  6. package/CONTRIBUTING.md +38 -0
  7. package/README.md +2 -148
  8. package/dist/index.js +10 -11
  9. package/dist/package.json +30 -2
  10. package/dist/src/agents/app.js +28 -0
  11. package/dist/src/agents/backend.js +25 -0
  12. package/dist/src/agents/devops.js +34 -0
  13. package/dist/src/agents/frontend.js +25 -0
  14. package/dist/src/agents/fullstack.js +25 -0
  15. package/dist/src/agents/index.js +61 -0
  16. package/dist/src/agents/quality.js +70 -0
  17. package/dist/src/agents/security.js +26 -0
  18. package/dist/src/agents/types.js +2 -0
  19. package/dist/src/client.js +97 -117
  20. package/dist/src/commands/api-keys.js +75 -90
  21. package/dist/src/commands/auth.js +7 -16
  22. package/dist/src/commands/autocomplete.js +1 -1
  23. package/dist/src/commands/billing.js +6 -17
  24. package/dist/src/commands/chat.js +68 -101
  25. package/dist/src/commands/clusters.js +9 -18
  26. package/dist/src/commands/code/__tests__/auth-sync.test.js +351 -0
  27. package/dist/src/commands/code/__tests__/fake-api-key-service.js +13 -0
  28. package/dist/src/commands/code/__tests__/fake-auth-service.js +47 -0
  29. package/dist/src/commands/code/__tests__/fake-command-runner.js +21 -34
  30. package/dist/src/commands/code/__tests__/fake-file-store.js +20 -33
  31. package/dist/src/commands/code/__tests__/fake-prompter.js +83 -57
  32. package/dist/src/commands/code/__tests__/setup-flow.test.js +359 -92
  33. package/dist/src/commands/code/adapters/clack-prompter.js +15 -22
  34. package/dist/src/commands/code/adapters/fs-file-store.js +26 -40
  35. package/dist/src/commands/code/adapters/spawn-command-runner.js +27 -37
  36. package/dist/src/commands/code/auth-sync.js +270 -0
  37. package/dist/src/commands/code/errors.js +12 -9
  38. package/dist/src/commands/code/ports/auth-services.js +2 -0
  39. package/dist/src/commands/code/setup.js +387 -281
  40. package/dist/src/commands/code.js +205 -332
  41. package/dist/src/commands/index.js +5 -5
  42. package/dist/src/commands/models.js +6 -17
  43. package/dist/src/commands/users.js +5 -16
  44. package/dist/src/constants/command-structure.js +104 -104
  45. package/dist/src/services/api-key-service.js +132 -157
  46. package/dist/src/services/auth-service.js +89 -342
  47. package/dist/src/services/browser-auth.js +268 -0
  48. package/dist/src/services/chat-service.js +371 -401
  49. package/dist/src/services/cluster-service.js +47 -62
  50. package/dist/src/services/collaborator-service.js +10 -25
  51. package/dist/src/services/flux-service.js +14 -29
  52. package/dist/src/services/helm-service.js +10 -25
  53. package/dist/src/services/kubectl-service.js +16 -33
  54. package/dist/src/utils/config-checker.js +3 -3
  55. package/dist/src/utils/config-loader.js +95 -95
  56. package/dist/src/utils/default-api-key.js +124 -134
  57. package/dist/src/utils/env-manager.js +55 -66
  58. package/dist/src/utils/error-handler.js +20 -21
  59. package/dist/src/utils/logger.js +72 -65
  60. package/dist/src/utils/markdown-renderer.js +27 -27
  61. package/dist/src/utils/opencode-validator.js +63 -68
  62. package/dist/src/utils/token-manager.js +74 -45
  63. package/dist/tests/commands/chat.test.js +16 -25
  64. package/dist/tests/commands/code.test.js +95 -104
  65. package/dist/tests/utils/config-loader.test.js +48 -48
  66. package/dist/tests/utils/env-manager.test.js +43 -52
  67. package/dist/tests/utils/opencode-validator.test.js +22 -21
  68. package/dist/vitest.config.js +1 -1
  69. package/eslint.config.mjs +67 -0
  70. package/index.ts +35 -42
  71. package/package.json +30 -2
  72. package/src/agents/app.ts +27 -0
  73. package/src/agents/backend.ts +24 -0
  74. package/src/agents/devops.ts +33 -0
  75. package/src/agents/frontend.ts +24 -0
  76. package/src/agents/fullstack.ts +24 -0
  77. package/src/agents/index.ts +73 -0
  78. package/src/agents/quality.ts +69 -0
  79. package/src/agents/security.ts +26 -0
  80. package/src/agents/types.ts +17 -0
  81. package/src/client.ts +118 -152
  82. package/src/commands/api-keys.ts +241 -333
  83. package/src/commands/auth.ts +22 -27
  84. package/src/commands/autocomplete.ts +9 -9
  85. package/src/commands/billing.ts +20 -24
  86. package/src/commands/chat.ts +248 -338
  87. package/src/commands/clusters.ts +27 -26
  88. package/src/commands/code/__tests__/auth-sync.test.ts +482 -0
  89. package/src/commands/code/__tests__/fake-api-key-service.ts +13 -0
  90. package/src/commands/code/__tests__/fake-auth-service.ts +50 -0
  91. package/src/commands/code/__tests__/fake-command-runner.ts +45 -42
  92. package/src/commands/code/__tests__/fake-file-store.ts +32 -23
  93. package/src/commands/code/__tests__/fake-prompter.ts +116 -77
  94. package/src/commands/code/__tests__/setup-flow.test.ts +624 -268
  95. package/src/commands/code/adapters/clack-prompter.ts +53 -39
  96. package/src/commands/code/adapters/fs-file-store.ts +32 -27
  97. package/src/commands/code/adapters/spawn-command-runner.ts +38 -29
  98. package/src/commands/code/auth-sync.ts +329 -0
  99. package/src/commands/code/errors.ts +18 -18
  100. package/src/commands/code/ports/auth-services.ts +14 -0
  101. package/src/commands/code/ports/command-runner.ts +8 -4
  102. package/src/commands/code/ports/file-store.ts +5 -4
  103. package/src/commands/code/ports/prompter.ts +24 -18
  104. package/src/commands/code/setup.ts +570 -340
  105. package/src/commands/code.ts +338 -539
  106. package/src/commands/index.ts +20 -19
  107. package/src/commands/models.ts +28 -32
  108. package/src/commands/users.ts +15 -21
  109. package/src/constants/command-structure.ts +134 -157
  110. package/src/services/api-key-service.ts +105 -122
  111. package/src/services/auth-service.ts +99 -345
  112. package/src/services/browser-auth.ts +296 -0
  113. package/src/services/chat-service.ts +265 -299
  114. package/src/services/cluster-service.ts +42 -45
  115. package/src/services/collaborator-service.ts +14 -19
  116. package/src/services/flux-service.ts +23 -25
  117. package/src/services/helm-service.ts +19 -21
  118. package/src/services/kubectl-service.ts +17 -19
  119. package/src/types/api.d.ts +1905 -1907
  120. package/src/types/json.d.ts +2 -2
  121. package/src/utils/config-checker.ts +10 -10
  122. package/src/utils/config-loader.ts +162 -178
  123. package/src/utils/default-api-key.ts +114 -125
  124. package/src/utils/env-manager.ts +53 -57
  125. package/src/utils/error-handler.ts +61 -56
  126. package/src/utils/logger.ts +79 -73
  127. package/src/utils/markdown-renderer.ts +31 -31
  128. package/src/utils/opencode-validator.ts +85 -89
  129. package/src/utils/token-manager.ts +108 -87
  130. package/templates/agents/app.md +1 -0
  131. package/templates/agents/backend.md +1 -0
  132. package/templates/agents/devops.md +2 -0
  133. package/templates/agents/frontend.md +1 -0
  134. package/templates/agents/fullstack.md +1 -0
  135. package/templates/agents/quality.md +45 -40
  136. package/templates/agents/security.md +1 -0
  137. package/tests/commands/chat.test.ts +53 -62
  138. package/tests/commands/code.test.ts +265 -310
  139. package/tests/utils/config-loader.test.ts +189 -188
  140. package/tests/utils/env-manager.test.ts +110 -113
  141. package/tests/utils/opencode-validator.test.ts +52 -56
  142. package/tsconfig.json +4 -3
  143. package/vitest.config.ts +3 -3
  144. package/AGENTS.md +0 -374
  145. package/TODO.md +0 -19
@@ -1,134 +1,125 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
12
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
4
  };
14
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_fs_1 = __importDefault(require("node:fs"));
7
+ const promises_1 = require("node:fs/promises");
8
+ const node_path_1 = __importDefault(require("node:path"));
15
9
  const vitest_1 = require("vitest");
16
- const fs_1 = __importDefault(require("fs"));
17
- const promises_1 = require("fs/promises");
18
- const path_1 = __importDefault(require("path"));
19
10
  const env_manager_1 = require("../../src/utils/env-manager");
20
11
  vitest_1.vi.mock('fs');
21
12
  vitest_1.vi.mock('fs/promises');
22
13
  vitest_1.vi.mock('path');
23
- const mockFs = vitest_1.vi.mocked(fs_1.default);
14
+ const mockFs = vitest_1.vi.mocked(node_fs_1.default);
24
15
  const mockWriteFile = vitest_1.vi.mocked(promises_1.writeFile);
25
- const mockPath = vitest_1.vi.mocked(path_1.default);
16
+ const mockPath = vitest_1.vi.mocked(node_path_1.default);
26
17
  (0, vitest_1.describe)('env-manager', () => {
27
- const testEnvPath = '/test/.env';
18
+ const testEnvironmentPath = '/test/.env';
28
19
  const testCwd = '/test';
29
20
  (0, vitest_1.beforeEach)(() => {
30
21
  vitest_1.vi.clearAllMocks();
31
- mockPath.join.mockReturnValue(testEnvPath);
22
+ mockPath.join.mockReturnValue(testEnvironmentPath);
32
23
  vitest_1.vi.spyOn(process, 'cwd').mockReturnValue(testCwd);
33
24
  });
34
25
  (0, vitest_1.afterEach)(() => {
35
26
  vitest_1.vi.restoreAllMocks();
36
27
  });
37
28
  (0, vitest_1.describe)('updateEnvFile', () => {
38
- (0, vitest_1.it)('should create a new .env file with the key when file does not exist', () => __awaiter(void 0, void 0, void 0, function* () {
29
+ (0, vitest_1.it)('should create a new .env file with the key when file does not exist', async () => {
39
30
  mockFs.existsSync.mockReturnValue(false);
40
- yield (0, env_manager_1.updateEnvFile)({
31
+ await (0, env_manager_1.updateEnvFile)({
32
+ comment: 'Test comment',
41
33
  key: 'TEST_KEY',
42
34
  value: 'test_value',
43
- comment: 'Test comment',
44
35
  });
45
- (0, vitest_1.expect)(mockFs.existsSync).toHaveBeenCalledWith(testEnvPath);
46
- (0, vitest_1.expect)(mockWriteFile).toHaveBeenCalledWith(testEnvPath, '# Test comment\nTEST_KEY=test_value\n');
47
- }));
48
- (0, vitest_1.it)('should append to existing .env file when key does not exist', () => __awaiter(void 0, void 0, void 0, function* () {
36
+ (0, vitest_1.expect)(mockFs.existsSync).toHaveBeenCalledWith(testEnvironmentPath);
37
+ (0, vitest_1.expect)(mockWriteFile).toHaveBeenCalledWith(testEnvironmentPath, '# Test comment\nTEST_KEY=test_value\n');
38
+ });
39
+ (0, vitest_1.it)('should append to existing .env file when key does not exist', async () => {
49
40
  const existingContent = 'EXISTING_KEY=existing_value\n';
50
41
  mockFs.existsSync.mockReturnValue(true);
51
42
  mockFs.readFileSync.mockReturnValue(existingContent);
52
- yield (0, env_manager_1.updateEnvFile)({
43
+ await (0, env_manager_1.updateEnvFile)({
44
+ comment: 'Test comment',
53
45
  key: 'NEW_KEY',
54
46
  value: 'new_value',
55
- comment: 'Test comment',
56
47
  });
57
- (0, vitest_1.expect)(mockWriteFile).toHaveBeenCalledWith(testEnvPath, 'EXISTING_KEY=existing_value\nNEW_KEY=new_value\n');
58
- }));
59
- (0, vitest_1.it)('should not update when key already exists and force is false', () => __awaiter(void 0, void 0, void 0, function* () {
48
+ (0, vitest_1.expect)(mockWriteFile).toHaveBeenCalledWith(testEnvironmentPath, 'EXISTING_KEY=existing_value\nNEW_KEY=new_value\n');
49
+ });
50
+ (0, vitest_1.it)('should not update when key already exists and force is false', async () => {
60
51
  const existingContent = 'EXISTING_KEY=existing_value\nTEST_KEY=old_value\n';
61
52
  mockFs.existsSync.mockReturnValue(true);
62
53
  mockFs.readFileSync.mockReturnValue(existingContent);
63
54
  const consoleSpy = vitest_1.vi.spyOn(console, 'log').mockImplementation(() => { });
64
- yield (0, env_manager_1.updateEnvFile)({
55
+ await (0, env_manager_1.updateEnvFile)({
65
56
  key: 'TEST_KEY',
66
57
  value: 'new_value',
67
58
  });
68
59
  (0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining('TEST_KEY already exists in .env - leaving unchanged'));
69
60
  (0, vitest_1.expect)(mockWriteFile).not.toHaveBeenCalled();
70
61
  consoleSpy.mockRestore();
71
- }));
72
- (0, vitest_1.it)('should update existing key when force is true', () => __awaiter(void 0, void 0, void 0, function* () {
62
+ });
63
+ (0, vitest_1.it)('should update existing key when force is true', async () => {
73
64
  const existingContent = 'EXISTING_KEY=existing_value\nTEST_KEY=old_value\n';
74
65
  mockFs.existsSync.mockReturnValue(true);
75
66
  mockFs.readFileSync.mockReturnValue(existingContent);
76
- yield (0, env_manager_1.updateEnvFile)({
67
+ await (0, env_manager_1.updateEnvFile)({
68
+ force: true,
77
69
  key: 'TEST_KEY',
78
70
  value: 'new_value',
79
- force: true,
80
71
  });
81
- (0, vitest_1.expect)(mockWriteFile).toHaveBeenCalledWith(testEnvPath, 'EXISTING_KEY=existing_value\nTEST_KEY=new_value\n');
82
- }));
83
- (0, vitest_1.it)('should handle complex values with quotes and special characters', () => __awaiter(void 0, void 0, void 0, function* () {
72
+ (0, vitest_1.expect)(mockWriteFile).toHaveBeenCalledWith(testEnvironmentPath, 'EXISTING_KEY=existing_value\nTEST_KEY=new_value\n');
73
+ });
74
+ (0, vitest_1.it)('should handle complex values with quotes and special characters', async () => {
84
75
  mockFs.existsSync.mockReturnValue(false);
85
- yield (0, env_manager_1.updateEnvFile)({
76
+ await (0, env_manager_1.updateEnvFile)({
77
+ comment: 'Complex test',
86
78
  key: 'COMPLEX_KEY',
87
79
  value: 'value with "quotes" and $special',
88
- comment: 'Complex test',
89
80
  });
90
- (0, vitest_1.expect)(mockWriteFile).toHaveBeenCalledWith(testEnvPath, '# Complex test\nCOMPLEX_KEY=value with "quotes" and $special\n');
91
- }));
92
- (0, vitest_1.it)('should use custom env path when provided', () => __awaiter(void 0, void 0, void 0, function* () {
81
+ (0, vitest_1.expect)(mockWriteFile).toHaveBeenCalledWith(testEnvironmentPath, '# Complex test\nCOMPLEX_KEY=value with "quotes" and $special\n');
82
+ });
83
+ (0, vitest_1.it)('should use custom env path when provided', async () => {
93
84
  const customPath = '/custom/.env';
94
85
  mockFs.existsSync.mockReturnValue(false);
95
- yield (0, env_manager_1.updateEnvFile)({
86
+ await (0, env_manager_1.updateEnvFile)({
96
87
  envPath: customPath,
97
88
  key: 'TEST_KEY',
98
89
  value: 'test_value',
99
90
  });
100
91
  (0, vitest_1.expect)(mockFs.existsSync).toHaveBeenCalledWith(customPath);
101
92
  (0, vitest_1.expect)(mockWriteFile).toHaveBeenCalledWith(customPath, 'TEST_KEY=test_value\n');
102
- }));
103
- (0, vitest_1.it)('should throw error when write fails', () => __awaiter(void 0, void 0, void 0, function* () {
93
+ });
94
+ (0, vitest_1.it)('should throw error when write fails', async () => {
104
95
  mockFs.existsSync.mockReturnValue(false);
105
96
  mockWriteFile.mockRejectedValue(new Error('Write error'));
106
- yield (0, vitest_1.expect)((0, env_manager_1.updateEnvFile)({
97
+ await (0, vitest_1.expect)((0, env_manager_1.updateEnvFile)({
107
98
  key: 'TEST_KEY',
108
99
  value: 'test_value',
109
100
  })).rejects.toThrow('Write error');
110
- }));
101
+ });
111
102
  });
112
103
  (0, vitest_1.describe)('hasEnvKey', () => {
113
104
  (0, vitest_1.it)('should return false when .env file does not exist', () => {
114
105
  mockFs.existsSync.mockReturnValue(false);
115
- const result = (0, env_manager_1.hasEnvKey)(testEnvPath, 'TEST_KEY');
106
+ const result = (0, env_manager_1.hasEnvKey)(testEnvironmentPath, 'TEST_KEY');
116
107
  (0, vitest_1.expect)(result).toBe(false);
117
- (0, vitest_1.expect)(mockFs.existsSync).toHaveBeenCalledWith(testEnvPath);
108
+ (0, vitest_1.expect)(mockFs.existsSync).toHaveBeenCalledWith(testEnvironmentPath);
118
109
  (0, vitest_1.expect)(mockFs.readFileSync).not.toHaveBeenCalled();
119
110
  });
120
111
  (0, vitest_1.it)('should return true when key exists in .env file', () => {
121
112
  const existingContent = 'KEY1=value1\nTEST_KEY=test_value\nKEY2=value2\n';
122
113
  mockFs.existsSync.mockReturnValue(true);
123
114
  mockFs.readFileSync.mockReturnValue(existingContent);
124
- const result = (0, env_manager_1.hasEnvKey)(testEnvPath, 'TEST_KEY');
115
+ const result = (0, env_manager_1.hasEnvKey)(testEnvironmentPath, 'TEST_KEY');
125
116
  (0, vitest_1.expect)(result).toBe(true);
126
117
  });
127
118
  (0, vitest_1.it)('should return false when key does not exist in .env file', () => {
128
119
  const existingContent = 'KEY1=value1\nKEY2=value2\n';
129
120
  mockFs.existsSync.mockReturnValue(true);
130
121
  mockFs.readFileSync.mockReturnValue(existingContent);
131
- const result = (0, env_manager_1.hasEnvKey)(testEnvPath, 'TEST_KEY');
122
+ const result = (0, env_manager_1.hasEnvKey)(testEnvironmentPath, 'TEST_KEY');
132
123
  (0, vitest_1.expect)(result).toBe(false);
133
124
  });
134
125
  (0, vitest_1.it)('should return false when .env file is malformed', () => {
@@ -136,13 +127,13 @@ const mockPath = vitest_1.vi.mocked(path_1.default);
136
127
  mockFs.readFileSync.mockImplementation(() => {
137
128
  throw new Error('Read error');
138
129
  });
139
- const result = (0, env_manager_1.hasEnvKey)(testEnvPath, 'TEST_KEY');
130
+ const result = (0, env_manager_1.hasEnvKey)(testEnvironmentPath, 'TEST_KEY');
140
131
  (0, vitest_1.expect)(result).toBe(false);
141
132
  });
142
133
  (0, vitest_1.it)('should use default path when not provided', () => {
143
134
  mockFs.existsSync.mockReturnValue(false);
144
135
  (0, env_manager_1.hasEnvKey)(undefined, 'TEST_KEY');
145
- (0, vitest_1.expect)(mockFs.existsSync).toHaveBeenCalledWith(testEnvPath);
136
+ (0, vitest_1.expect)(mockFs.existsSync).toHaveBeenCalledWith(testEnvironmentPath);
146
137
  });
147
138
  });
148
139
  });
@@ -1,26 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const node_fs_1 = require("node:fs");
3
4
  const vitest_1 = require("vitest");
4
5
  const opencode_validator_1 = require("../../src/utils/opencode-validator");
5
- const fs_1 = require("fs");
6
6
  (0, vitest_1.describe)('OpenCode Validator', () => {
7
7
  (0, vitest_1.it)('should validate a correct OpenCode configuration', () => {
8
8
  const validConfig = {
9
9
  $schema: 'https://opencode.ai/config.json',
10
- username: 'test-user',
11
- model: 'gpt-4',
12
10
  agent: {
13
11
  test: {
14
12
  model: 'gpt-4',
15
- temperature: 0.7,
16
- prompt: 'Test agent',
17
13
  permission: {
18
- edit: 'allow',
19
14
  bash: 'allow',
15
+ edit: 'allow',
20
16
  webfetch: 'allow',
21
17
  },
18
+ prompt: 'Test agent',
19
+ temperature: 0.7,
22
20
  },
23
21
  },
22
+ model: 'gpt-4',
23
+ username: 'test-user',
24
24
  };
25
25
  const result = (0, opencode_validator_1.validateOpenCodeConfig)(validConfig);
26
26
  (0, vitest_1.expect)(result.valid).toBe(true);
@@ -28,20 +28,20 @@ const fs_1 = require("fs");
28
28
  });
29
29
  (0, vitest_1.it)('should reject invalid configuration', () => {
30
30
  const invalidConfig = {
31
- username: 123, // Should be string
32
- model: 'gpt-4',
33
31
  agent: {
34
32
  test: {
35
33
  model: 'gpt-4',
36
- temperature: 'high', // Should be number
37
- prompt: 'Test agent',
38
34
  permission: {
39
- edit: 'invalid', // Should be enum value
40
35
  bash: 'allow',
36
+ edit: 'invalid', // Should be enum value
41
37
  webfetch: 'allow',
42
38
  },
39
+ prompt: 'Test agent',
40
+ temperature: 'high', // Should be number
43
41
  },
44
42
  },
43
+ model: 'gpt-4',
44
+ username: 123, // Should be string
45
45
  };
46
46
  const result = (0, opencode_validator_1.validateOpenCodeConfig)(invalidConfig);
47
47
  (0, vitest_1.expect)(result.valid).toBe(false);
@@ -50,23 +50,23 @@ const fs_1 = require("fs");
50
50
  });
51
51
  (0, vitest_1.it)('should fix common configuration issues', () => {
52
52
  const configWithIssues = {
53
- username: 'test-user',
54
- model: 'gpt-4',
55
- tools: {
56
- compact: { threshold: 80000 }, // Should be boolean
57
- },
58
53
  maxTokens: 4000, // Invalid property
54
+ model: 'gpt-4',
59
55
  provider: {
60
56
  berget: {
61
57
  models: {
62
58
  'test-model': {
63
- name: 'Test Model',
64
- maxTokens: 4000, // Should be moved to limit.context
65
59
  contextWindow: 8000, // Should be moved to limit.context
60
+ maxTokens: 4000, // Should be moved to limit.context
61
+ name: 'Test Model',
66
62
  },
67
63
  },
68
64
  },
69
65
  },
66
+ tools: {
67
+ compact: { threshold: 80000 }, // Should be boolean
68
+ },
69
+ username: 'test-user',
70
70
  };
71
71
  const fixed = (0, opencode_validator_1.fixOpenCodeConfig)(configWithIssues);
72
72
  // tools.compact should be boolean
@@ -80,10 +80,9 @@ const fs_1 = require("fs");
80
80
  (0, vitest_1.expect)(fixed.provider.berget.models['test-model'].contextWindow).toBeUndefined();
81
81
  });
82
82
  (0, vitest_1.it)('should validate the current opencode.json file', () => {
83
- var _a;
84
83
  let currentConfig;
85
84
  try {
86
- currentConfig = JSON.parse((0, fs_1.readFileSync)('opencode.json', 'utf8'));
85
+ currentConfig = JSON.parse((0, node_fs_1.readFileSync)('opencode.json', 'utf8'));
87
86
  }
88
87
  catch (error) {
89
88
  // Skip when opencode.json is not present (e.g. in CI or clean checkouts)
@@ -98,7 +97,9 @@ const fs_1 = require("fs");
98
97
  (0, vitest_1.expect)(result.valid).toBe(true);
99
98
  if (!result.valid) {
100
99
  console.log('Fixed opencode.json validation errors:');
101
- (_a = result.errors) === null || _a === void 0 ? void 0 : _a.forEach((err) => console.log(` - ${err}`));
100
+ if (result.errors)
101
+ for (const error of result.errors)
102
+ console.log(` - ${error}`);
102
103
  }
103
104
  });
104
105
  });
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const config_1 = require("vitest/config");
4
4
  exports.default = (0, config_1.defineConfig)({
5
5
  test: {
6
- globals: true,
7
6
  environment: 'node',
7
+ globals: true,
8
8
  },
9
9
  });
@@ -0,0 +1,67 @@
1
+ import vitest from '@vitest/eslint-plugin';
2
+ import perfectionist from 'eslint-plugin-perfectionist';
3
+ import prettier from 'eslint-plugin-prettier';
4
+ import promise from 'eslint-plugin-promise';
5
+ import tseslint from 'typescript-eslint';
6
+
7
+ export default tseslint.config(
8
+ {
9
+ ignores: [
10
+ 'node_modules/**',
11
+ 'dist/**',
12
+ 'build/**',
13
+ '*.lock',
14
+ '.husky/**',
15
+ 'coverage/**',
16
+ '.nyc_output/**',
17
+ 'test-results/**',
18
+ 'playwright-report/**',
19
+ '.pi/**',
20
+ 'src/types/api.d.ts',
21
+ ],
22
+ },
23
+ ...tseslint.configs.recommended,
24
+ ...tseslint.configs.strict,
25
+ promise.configs['flat/recommended'],
26
+ perfectionist.configs['recommended-natural'],
27
+ {
28
+ files: ['**/*.test.ts', '**/*.spec.ts', '**/*.test.tsx', '**/*.spec.tsx'],
29
+ languageOptions: {
30
+ globals: {
31
+ ...vitest.environments.env.globals,
32
+ },
33
+ },
34
+ plugins: {
35
+ vitest,
36
+ },
37
+ rules: {
38
+ ...vitest.configs.recommended.rules,
39
+ '@typescript-eslint/no-explicit-any': 'off',
40
+ '@typescript-eslint/no-non-null-assertion': 'off',
41
+ },
42
+ },
43
+ {
44
+ files: ['**/*.ts', '**/*.tsx'],
45
+ languageOptions: {
46
+ ecmaVersion: 'latest',
47
+ parserOptions: {
48
+ project: './tsconfig.json',
49
+ tsconfigRootDir: process.cwd(),
50
+ },
51
+ sourceType: 'commonjs',
52
+ },
53
+ plugins: {
54
+ prettier,
55
+ },
56
+ rules: {
57
+ ...prettier.configs.recommended.rules,
58
+ '@typescript-eslint/no-dynamic-delete': 'off',
59
+ '@typescript-eslint/no-explicit-any': 'off',
60
+ '@typescript-eslint/no-non-null-assertion': 'off',
61
+ '@typescript-eslint/no-unused-vars': [
62
+ 'error',
63
+ { argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
64
+ ],
65
+ },
66
+ },
67
+ );
package/index.ts CHANGED
@@ -1,12 +1,11 @@
1
- #!/usr/bin/env node
1
+ import chalk from 'chalk';
2
+ import { Option, program } from 'commander';
2
3
 
3
- import { program, Option } from 'commander'
4
- import { registerCommands } from './src/commands'
5
- import { checkBergetConfig } from './src/utils/config-checker'
6
- import chalk from 'chalk'
7
- import { version } from './package.json'
8
- process.env.DOTENV_CONFIG_OVERRIDE = 'true'
9
- import 'dotenv/config'
4
+ import { version } from './package.json';
5
+ import { registerCommands } from './src/commands';
6
+ import { checkBergetConfig } from './src/utils/config-checker';
7
+ process.env.DOTENV_CONFIG_OVERRIDE = 'true';
8
+ import 'dotenv/config';
10
9
 
11
10
  // Set version and description
12
11
  program
@@ -20,72 +19,66 @@ program
20
19
  \\____/ \\___|_| \\__, |\\___|\\_\\_ \\_| |_/\\___/
21
20
  __/ |
22
21
  |___/ AI on European terms
23
- Version: ${version}`
22
+ Version: ${version}`,
24
23
  )
25
24
  .version(version, '-v, --version')
26
25
  .addOption(new Option('--local').default(false).hideHelp())
27
26
  .addOption(new Option('--stage').default(false).hideHelp())
28
- .option('--debug', 'Enable debug output', false)
27
+ .option('--debug', 'Enable debug output', false);
29
28
 
30
29
  // Register all commands
31
- registerCommands(program)
30
+ registerCommands(program);
32
31
 
33
32
  // Check for .bergetconfig if not running a command
34
33
  if (process.argv.length <= 2) {
35
- checkBergetConfig()
34
+ checkBergetConfig();
36
35
 
37
36
  // Show the full help (including logo and commands)
38
- program.outputHelp()
39
- process.exit(0)
37
+ program.outputHelp();
38
+ process.exit(0);
40
39
  }
41
40
 
42
41
  // Add helpful suggestions for common command mistakes
43
42
  const commonMistakes: Record<string, string> = {
44
- login: 'auth login',
45
- logout: 'auth logout',
46
- whoami: 'auth whoami',
47
- 'list-models': 'models list',
48
- 'list-keys': 'api-keys list',
49
43
  'create-key': 'api-keys create',
44
+ init: 'code init',
50
45
  'list-clusters': 'clusters list',
46
+ 'list-keys': 'api-keys list',
47
+ 'list-models': 'models list',
48
+ login: 'auth login',
49
+ logout: 'auth logout',
51
50
  usage: 'billing usage',
52
- init: 'code init',
53
- }
51
+ whoami: 'auth whoami',
52
+ };
54
53
 
55
54
  // Add error handler for unknown commands
56
55
  program.on('command:*', (operands) => {
57
- const unknownCommand = operands[0] as string
58
- console.error(chalk.red(`Error: unknown command '${unknownCommand}'`))
56
+ const unknownCommand = operands[0] as string;
57
+ console.error(chalk.red(`Error: unknown command '${unknownCommand}'`));
59
58
 
60
59
  // Check if this is a known mistake and suggest the correct command
61
60
  if (unknownCommand in commonMistakes) {
62
61
  console.log(
63
- chalk.yellow(
64
- `Did you mean? ${chalk.bold(
65
- `berget ${commonMistakes[unknownCommand]}`
66
- )}`
67
- )
68
- )
62
+ chalk.yellow(`Did you mean? ${chalk.bold(`berget ${commonMistakes[unknownCommand]}`)}`),
63
+ );
69
64
  } else {
70
65
  // Try to find similar commands
71
- const availableCommands = program.commands.map((cmd) => cmd.name())
66
+ const availableCommands = program.commands.map((cmd) => cmd.name());
72
67
  const similarCommands = availableCommands.filter(
73
- (cmd) => cmd.includes(unknownCommand) || unknownCommand.includes(cmd)
74
- )
68
+ (cmd) => cmd.includes(unknownCommand) || unknownCommand.includes(cmd),
69
+ );
75
70
 
76
71
  if (similarCommands.length > 0) {
77
- console.log(chalk.yellow('Similar commands:'))
78
- similarCommands.forEach((cmd) => {
79
- console.log(chalk.yellow(` ${chalk.bold(`berget ${cmd}`)}`))
80
- })
72
+ console.log(chalk.yellow('Similar commands:'));
73
+ for (const cmd of similarCommands) {
74
+ console.log(chalk.yellow(` ${chalk.bold(`berget ${cmd}`)}`));
75
+ }
81
76
  }
82
77
 
83
- console.log(
84
- chalk.blue('\nRun `berget --help` for a list of available commands.')
85
- )
78
+ console.log(chalk.blue('\nRun `berget --help` for a list of available commands.'));
86
79
  }
87
80
 
88
- process.exit(1)
89
- })
81
+ process.exit(1);
82
+ });
90
83
 
91
- program.parse(process.argv)
84
+ program.parse(process.argv);
package/package.json CHANGED
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "name": "berget",
3
- "version": "2.2.6",
3
+ "version": "2.2.8",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "berget": "dist/index.js"
7
7
  },
8
8
  "private": false,
9
+ "engines": {
10
+ "node": ">=20.0.0"
11
+ },
9
12
  "publishConfig": {
10
13
  "access": "public"
11
14
  },
@@ -17,8 +20,14 @@
17
20
  "build": "tsc",
18
21
  "test": "vitest",
19
22
  "test:run": "vitest run",
23
+ "typecheck": "tsc --noEmit",
24
+ "lint": "eslint . --ext .ts,.tsx,.js",
25
+ "lint:fix": "eslint . --ext .ts,.tsx,.js --fix",
26
+ "format": "prettier --write .",
27
+ "format:check": "prettier --check .",
20
28
  "prepublishOnly": "npm run build",
21
- "generate-types": "openapi-typescript https://api.berget.ai/openapi.json -o src/types/api.d.ts"
29
+ "generate-types": "openapi-typescript https://api.berget.ai/openapi.json -o src/types/api.d.ts",
30
+ "prepare": "husky"
22
31
  },
23
32
  "author": "Berget AI AB",
24
33
  "license": "MIT",
@@ -28,8 +37,18 @@
28
37
  "@types/marked": "^5.0.2",
29
38
  "@types/marked-terminal": "^6.1.1",
30
39
  "@types/node": "^20.11.20",
40
+ "@vitest/eslint-plugin": "^1.6.17",
41
+ "eslint": "^10.3.0",
42
+ "eslint-config-prettier": "^10.1.8",
43
+ "eslint-plugin-perfectionist": "^5.9.0",
44
+ "eslint-plugin-prettier": "^5.5.5",
45
+ "eslint-plugin-promise": "^7.3.0",
46
+ "husky": "^9.1.7",
47
+ "lint-staged": "^17.0.4",
48
+ "prettier": "^3.8.3",
31
49
  "tsx": "^4.19.3",
32
50
  "typescript": "^5.3.3",
51
+ "typescript-eslint": "^8.59.3",
33
52
  "vitest": "^1.0.0"
34
53
  },
35
54
  "dependencies": {
@@ -48,5 +67,14 @@
48
67
  "openapi-typescript": "^6.7.4",
49
68
  "readline": "^1.3.0",
50
69
  "zod": "^4.1.12"
70
+ },
71
+ "lint-staged": {
72
+ "*.{ts,tsx}": [
73
+ "eslint --fix",
74
+ "prettier --write"
75
+ ],
76
+ "*.{json,yml,yaml,md}": [
77
+ "prettier --write"
78
+ ]
51
79
  }
52
80
  }
@@ -0,0 +1,27 @@
1
+ import { Agent } from './types.js';
2
+
3
+ export const agent: Agent = {
4
+ config: {
5
+ description: 'Expo + React Native apps; props-first, offline-aware, shared tokens.',
6
+ mode: 'primary',
7
+ name: 'app',
8
+ permission: {
9
+ bash: 'deny',
10
+ edit: 'allow',
11
+ webfetch: 'allow',
12
+ },
13
+ temperature: 0.4,
14
+ top_p: 0.9,
15
+ },
16
+ systemPrompt: `You are Berget Code App agent. Voice: Scandinavian calm—precise, concise, confident. Expo + React Native + TypeScript. Structure by components/hooks/services/navigation. Components are pure; data via props; refactor shared logic into hooks/stores. Share tokens with frontend. Mock data in /data via typed hooks; later replace with live APIs. Offline via SQLite/MMKV; notifications via Expo. Request permissions only when needed. Subtle, meaningful motion; light/dark parity.
17
+
18
+ GIT WORKFLOW RULES (CRITICAL):
19
+ - NEVER push directly to main branch - ALWAYS use pull requests
20
+ - NEVER use 'git add .' - ALWAYS add specific files with 'git add path/to/file'
21
+ - ALWAYS clean up test files, documentation files, and temporary artifacts before committing
22
+ - ALWAYS ensure git history maintains production quality - no test commits, no debugging code
23
+ - ALWAYS create descriptive commit messages following project conventions
24
+ - ALWAYS run tests and build before creating PR
25
+
26
+ CRITICAL: When all app implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.`,
27
+ };
@@ -0,0 +1,24 @@
1
+ import { Agent } from './types.js';
2
+
3
+ export const agent: Agent = {
4
+ config: {
5
+ description: 'Functional, modular Koa + TypeScript services',
6
+ mode: 'primary',
7
+ name: 'backend',
8
+ },
9
+ systemPrompt: `# Backend Agent
10
+
11
+ Functional, modular Koa + TypeScript services with schema-first approach and code quality focus.
12
+
13
+ **Use when:**
14
+
15
+ - Working with Koa routers and services
16
+ - Backend development in /services
17
+ - API development and database work
18
+
19
+ **Key features:**
20
+
21
+ - Zod validation and OpenAPI generation
22
+ - Code quality and refactoring principles
23
+ - PR workflow integration`,
24
+ };
@@ -0,0 +1,33 @@
1
+ import { Agent } from './types.js';
2
+
3
+ export const agent: Agent = {
4
+ config: {
5
+ description: 'Declarative GitOps infra with FluxCD, Kustomize, Helm, operators.',
6
+ mode: 'primary',
7
+ name: 'devops',
8
+ permission: {
9
+ bash: 'allow',
10
+ edit: 'allow',
11
+ webfetch: 'allow',
12
+ },
13
+ temperature: 0.3,
14
+ top_p: 0.8,
15
+ },
16
+ systemPrompt: `You are Berget Code DevOps agent. Voice: Scandinavian calm—precise, concise, confident. Start simple: k8s/{deployment,service,ingress}. Add FluxCD sync to repo and image automation. Use Kustomize bases/overlays (staging, production). Add dependencies via Helm from upstream sources; prefer native operators when available (CloudNativePG, cert-manager, external-dns). SemVer with -rc tags keeps CI environments current. Observability with Prometheus/Grafana. No manual kubectl in production—Git is the source of truth.
17
+
18
+ GIT WORKFLOW RULES (CRITICAL):
19
+ - NEVER push directly to main branch - ALWAYS use pull requests
20
+ - NEVER use 'git add .' - ALWAYS add specific files with 'git add path/to/file'
21
+ - ALWAYS clean up test files, documentation files, and temporary artifacts before committing
22
+ - ALWAYS ensure git history maintains production quality - no test commits, no debugging code
23
+ - ALWAYS create descriptive commit messages following project conventions
24
+ - ALWAYS run tests and build before creating PR
25
+
26
+ Helm Values Configuration Process:
27
+ 1. Documentation First Approach: Always fetch official documentation from Artifact Hub/GitHub for the specific chart version before writing values. Search Artifact Hub for exact chart version documentation, check the chart's GitHub repository for official docs and examples, verify the exact version being used in the deployment.
28
+ 2. Validation Requirements: Check for available validation schemas before committing YAML files. Use Helm's built-in validation tools (helm lint, helm template). Validate against JSON schema if available for the chart. Ensure YAML syntax correctness with linters.
29
+ 3. Standard Workflow: Identify chart name and exact version. Fetch official documentation from Artifact Hub/GitHub. Check for available schemas and validation tools. Write values according to official documentation. Validate against schema (if available). Test with helm template or helm lint. Commit validated YAML files.
30
+ 4. Quality Assurance: Never commit unvalidated Helm values. Use helm dependency update when adding new charts. Test rendering with helm template --dry-run before deployment. Document any custom values with comments referencing official docs.
31
+
32
+ CRITICAL: When all devops implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.`,
33
+ };