panopticon-cli 0.4.33 → 0.5.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 (71) hide show
  1. package/README.md +96 -210
  2. package/dist/{agents-VLK4BMVA.js → agents-5OPQKM5K.js} +6 -5
  3. package/dist/{chunk-OMNXYPXC.js → chunk-2V4NF7J2.js} +14 -1
  4. package/dist/chunk-2V4NF7J2.js.map +1 -0
  5. package/dist/{chunk-XKT5MHPT.js → chunk-4YSYJ4HM.js} +2 -2
  6. package/dist/{chunk-XFR2DLMR.js → chunk-76F6DSVS.js} +49 -10
  7. package/dist/chunk-76F6DSVS.js.map +1 -0
  8. package/dist/{chunk-PI7Y3PSN.js → chunk-F5555J3A.js} +42 -6
  9. package/dist/chunk-F5555J3A.js.map +1 -0
  10. package/dist/{chunk-KJ2TRXNK.js → chunk-FTCPTHIJ.js} +47 -420
  11. package/dist/chunk-FTCPTHIJ.js.map +1 -0
  12. package/dist/chunk-HJSM6E6U.js +1038 -0
  13. package/dist/chunk-HJSM6E6U.js.map +1 -0
  14. package/dist/{chunk-RBUO57TC.js → chunk-NLQRED36.js} +3 -3
  15. package/dist/chunk-NLQRED36.js.map +1 -0
  16. package/dist/{chunk-ASY7T35E.js → chunk-OWHXCGVO.js} +245 -90
  17. package/dist/chunk-OWHXCGVO.js.map +1 -0
  18. package/dist/{chunk-BKCWRMUX.js → chunk-VHKSS7QX.js} +106 -11
  19. package/dist/chunk-VHKSS7QX.js.map +1 -0
  20. package/dist/{chunk-GFP3PIPB.js → chunk-YGJ54GW2.js} +1 -1
  21. package/dist/chunk-YGJ54GW2.js.map +1 -0
  22. package/dist/cli/index.js +1521 -935
  23. package/dist/cli/index.js.map +1 -1
  24. package/dist/dashboard/prompts/work-agent.md +2 -0
  25. package/dist/dashboard/public/assets/index-Ce6q21Fm.js +743 -0
  26. package/dist/dashboard/public/assets/{index-UjZq6ykz.css → index-NzpI0ItZ.css} +1 -1
  27. package/dist/dashboard/public/index.html +2 -2
  28. package/dist/dashboard/server.js +4274 -2320
  29. package/dist/{feedback-writer-LVZ5TFYZ.js → feedback-writer-VRMMWWTW.js} +2 -2
  30. package/dist/git-utils-I2UDKNZH.js +131 -0
  31. package/dist/git-utils-I2UDKNZH.js.map +1 -0
  32. package/dist/index.d.ts +12 -1
  33. package/dist/index.js +5 -3
  34. package/dist/index.js.map +1 -1
  35. package/dist/{projects-JEIVIYC6.js → projects-CFX3RTDL.js} +4 -2
  36. package/dist/{remote-workspace-AHVHQEES.js → remote-workspace-7FPGF2RM.js} +2 -2
  37. package/dist/{review-status-EPFG4XM7.js → review-status-TDPSOU5J.js} +2 -2
  38. package/dist/{specialist-context-T3NBMCIE.js → specialist-context-WGUUYDWY.js} +5 -5
  39. package/dist/{specialist-logs-CVKD3YJ3.js → specialist-logs-XJB5TCKJ.js} +5 -5
  40. package/dist/{specialists-TKAP6T6Z.js → specialists-5LBRHYFA.js} +5 -5
  41. package/dist/{traefik-QX4ZV4YG.js → traefik-WFMQX2LY.js} +3 -3
  42. package/dist/{workspace-manager-KLHUCIZV.js → workspace-manager-E434Z45T.js} +2 -2
  43. package/package.json +1 -1
  44. package/scripts/record-cost-event.js +5 -5
  45. package/scripts/stop-hook +7 -0
  46. package/scripts/work-agent-stop-hook +137 -0
  47. package/skills/myn-standards/SKILL.md +351 -0
  48. package/skills/pan-new-project/SKILL.md +304 -0
  49. package/skills/write-spec/SKILL.md +138 -0
  50. package/dist/chunk-7XNJJBH6.js +0 -538
  51. package/dist/chunk-7XNJJBH6.js.map +0 -1
  52. package/dist/chunk-ASY7T35E.js.map +0 -1
  53. package/dist/chunk-BKCWRMUX.js.map +0 -1
  54. package/dist/chunk-GFP3PIPB.js.map +0 -1
  55. package/dist/chunk-KJ2TRXNK.js.map +0 -1
  56. package/dist/chunk-OMNXYPXC.js.map +0 -1
  57. package/dist/chunk-PI7Y3PSN.js.map +0 -1
  58. package/dist/chunk-RBUO57TC.js.map +0 -1
  59. package/dist/chunk-XFR2DLMR.js.map +0 -1
  60. package/dist/dashboard/public/assets/index-kAJqtLDO.js +0 -708
  61. /package/dist/{agents-VLK4BMVA.js.map → agents-5OPQKM5K.js.map} +0 -0
  62. /package/dist/{chunk-XKT5MHPT.js.map → chunk-4YSYJ4HM.js.map} +0 -0
  63. /package/dist/{feedback-writer-LVZ5TFYZ.js.map → feedback-writer-VRMMWWTW.js.map} +0 -0
  64. /package/dist/{projects-JEIVIYC6.js.map → projects-CFX3RTDL.js.map} +0 -0
  65. /package/dist/{remote-workspace-AHVHQEES.js.map → remote-workspace-7FPGF2RM.js.map} +0 -0
  66. /package/dist/{review-status-EPFG4XM7.js.map → review-status-TDPSOU5J.js.map} +0 -0
  67. /package/dist/{specialist-context-T3NBMCIE.js.map → specialist-context-WGUUYDWY.js.map} +0 -0
  68. /package/dist/{specialist-logs-CVKD3YJ3.js.map → specialist-logs-XJB5TCKJ.js.map} +0 -0
  69. /package/dist/{specialists-TKAP6T6Z.js.map → specialists-5LBRHYFA.js.map} +0 -0
  70. /package/dist/{traefik-QX4ZV4YG.js.map → traefik-WFMQX2LY.js.map} +0 -0
  71. /package/dist/{workspace-manager-KLHUCIZV.js.map → workspace-manager-E434Z45T.js.map} +0 -0
@@ -0,0 +1,138 @@
1
+ ---
2
+ name: write-spec
3
+ description: >
4
+ Write a feature spec (*-spec.md) for an issue. The spec is the human-written
5
+ requirements document that feeds into the planning agent. Part of the
6
+ spec → plan → implement pipeline.
7
+ triggers:
8
+ - write spec
9
+ - write-spec
10
+ - create spec
11
+ - feature spec
12
+ allowed-tools:
13
+ - Read
14
+ - Write
15
+ - Bash
16
+ - Grep
17
+ - Glob
18
+ - ToolSearch
19
+ version: "1.0.0"
20
+ author: "Ed Becker"
21
+ license: "MIT"
22
+ ---
23
+
24
+ # Write Feature Spec
25
+
26
+ **Trigger:** `/write-spec <issue-id>` or `/write-spec <issue-id> <title>`
27
+
28
+ ## The Spec → Plan Pipeline
29
+
30
+ Panopticon uses a two-stage planning process:
31
+
32
+ 1. **Spec** (`*-spec.md`) — Human-written (often with AI assistance). Defines WHAT to build and WHY. Contains requirements, constraints, scope, research, and design decisions.
33
+ 2. **Plan** (`*-plan.md`) — Generated by the planning agent. Defines HOW to build it. Contains architecture, tasks with dependencies, file-level changes, and difficulty estimates.
34
+
35
+ The planning agent reads the spec as its primary input, explores the codebase, asks clarifying questions, then produces the implementation plan.
36
+
37
+ ## When to Write a Spec
38
+
39
+ - **Complex features** (multi-file, cross-cutting, architectural) — always write a spec first
40
+ - **Features with research** — when you've explored options, talked to stakeholders, or analyzed competitors
41
+ - **Features with constraints** — when there are specific technical or UX requirements the agent needs to know
42
+ - **Simple bug fixes or small changes** — skip the spec, go directly to planning or implementation
43
+
44
+ ## Execution Steps
45
+
46
+ ### 1. Identify the Issue
47
+
48
+ Parse the issue ID from the command argument. Determine the project:
49
+ - `MIN-XXX` → MYN project, docs at `/home/eltmon/Projects/myn/docs/`
50
+ - `PAN-XXX` → Panopticon project, docs at `/home/eltmon/Projects/panopticon-cli/docs/`
51
+
52
+ ### 2. Gather Context
53
+
54
+ - Fetch the issue from the tracker (Linear for MIN, GitHub for PAN) to get title, description, comments
55
+ - Check if a spec already exists at `docs/prds/active/{issue-id-lowercase}-*-spec.md`
56
+ - If updating an existing spec, read it first
57
+
58
+ ### 3. Research (Interactive)
59
+
60
+ Use AskUserQuestion or discussion to understand:
61
+ - What problem does this solve?
62
+ - Who benefits?
63
+ - What's in scope vs out of scope?
64
+ - Any technical constraints or preferences?
65
+ - Are there related specs or prior art to reference?
66
+
67
+ ### 4. Write the Spec
68
+
69
+ Create the file at: `docs/prds/active/{issue-id-lowercase}-{short-title}-spec.md`
70
+
71
+ Use this structure:
72
+
73
+ ```markdown
74
+ # {Issue ID}: {Title}
75
+
76
+ ## Problem Statement
77
+ What problem does this solve? Why does it matter?
78
+
79
+ ## Requirements
80
+ ### Must Have
81
+ - Requirement 1
82
+ - Requirement 2
83
+
84
+ ### Should Have
85
+ - Nice-to-have 1
86
+
87
+ ### Out of Scope
88
+ - Explicitly excluded items
89
+
90
+ ## Design
91
+ ### User Experience
92
+ How should it work from the user's perspective?
93
+
94
+ ### Technical Approach
95
+ High-level technical direction (NOT implementation details — that's for the plan).
96
+
97
+ ### Constraints
98
+ - Performance requirements
99
+ - Compatibility requirements
100
+ - Security considerations
101
+
102
+ ## References
103
+ - Related issues: MIN-XXX, MIN-YYY
104
+ - Prior art: links to existing code, docs, or external references
105
+ - Research: any analysis or competitor research
106
+
107
+ ## Open Questions
108
+ - Questions to resolve during planning
109
+ ```
110
+
111
+ ### 5. Confirm File Location
112
+
113
+ After writing, confirm:
114
+ ```
115
+ Spec written: docs/prds/active/{filename}-spec.md
116
+
117
+ Next steps:
118
+ 1. Review and edit the spec as needed
119
+ 2. Click "Plan" on the issue in the dashboard
120
+ 3. The planning agent will read this spec and produce an implementation plan
121
+ ```
122
+
123
+ ## File Naming Convention
124
+
125
+ | Type | Pattern | Created By | Example |
126
+ |------|---------|-----------|---------|
127
+ | Spec | `{issue-id}-{title}-spec.md` | Human (this skill) | `min-734-kaia-openclaw-a2a-spec.md` |
128
+ | Plan | `{issue-id}-plan.md` | Planning agent | `min-734-plan.md` |
129
+
130
+ Both live in `docs/prds/active/`. Specs are never overwritten by agents.
131
+
132
+ ## Important Notes
133
+
134
+ - **Specs are for humans** — write clearly, include context and rationale
135
+ - **Plans are for agents** — the planning agent produces structured, actionable tasks
136
+ - **Don't over-specify implementation** — the planning agent + codebase exploration will figure out the HOW
137
+ - **DO specify constraints** — things the agent can't discover on its own (performance targets, UX requirements, API contracts)
138
+ - **Reference existing code** — point to patterns, files, or modules the agent should follow
@@ -1,538 +0,0 @@
1
- import {
2
- SETTINGS_FILE,
3
- init_paths
4
- } from "./chunk-ZTFNYOC7.js";
5
- import {
6
- __esm,
7
- init_esm_shims
8
- } from "./chunk-ZHC57RCV.js";
9
-
10
- // src/lib/settings.ts
11
- import { readFileSync, writeFileSync, existsSync } from "fs";
12
- function deepMerge(defaults, overrides) {
13
- const result = { ...defaults };
14
- for (const key of Object.keys(overrides)) {
15
- const defaultVal = defaults[key];
16
- const overrideVal = overrides[key];
17
- if (overrideVal === void 0) continue;
18
- if (typeof defaultVal === "object" && defaultVal !== null && !Array.isArray(defaultVal) && typeof overrideVal === "object" && overrideVal !== null && !Array.isArray(overrideVal)) {
19
- result[key] = deepMerge(defaultVal, overrideVal);
20
- } else {
21
- result[key] = overrideVal;
22
- }
23
- }
24
- return result;
25
- }
26
- function loadSettings() {
27
- let settings;
28
- if (!existsSync(SETTINGS_FILE)) {
29
- settings = getDefaultSettings();
30
- } else {
31
- try {
32
- const content = readFileSync(SETTINGS_FILE, "utf8");
33
- const parsed = JSON.parse(content);
34
- settings = deepMerge(DEFAULT_SETTINGS, parsed);
35
- } catch (error) {
36
- console.error("Warning: Failed to parse settings.json, using defaults");
37
- settings = getDefaultSettings();
38
- }
39
- }
40
- const envApiKeys = {};
41
- if (process.env.OPENAI_API_KEY) envApiKeys.openai = process.env.OPENAI_API_KEY;
42
- if (process.env.GOOGLE_API_KEY) envApiKeys.google = process.env.GOOGLE_API_KEY;
43
- if (process.env.ZAI_API_KEY) envApiKeys.zai = process.env.ZAI_API_KEY;
44
- if (process.env.KIMI_API_KEY) envApiKeys.kimi = process.env.KIMI_API_KEY;
45
- settings.api_keys = {
46
- ...envApiKeys,
47
- ...settings.api_keys
48
- };
49
- return settings;
50
- }
51
- function saveSettings(settings) {
52
- const content = JSON.stringify(settings, null, 2);
53
- writeFileSync(SETTINGS_FILE, content, "utf8");
54
- }
55
- function validateSettings(settings) {
56
- if (!settings.models) {
57
- return "Missing models configuration";
58
- }
59
- if (!settings.models.specialists) {
60
- return "Missing specialists configuration";
61
- }
62
- const specialists = settings.models.specialists;
63
- if (!specialists.review_agent || !specialists.test_agent || !specialists.merge_agent) {
64
- return "Missing specialist agent model configuration";
65
- }
66
- if (!settings.models.complexity) {
67
- return "Missing complexity configuration";
68
- }
69
- const complexity = settings.models.complexity;
70
- const requiredLevels = ["trivial", "simple", "medium", "complex", "expert"];
71
- for (const level of requiredLevels) {
72
- if (!complexity[level]) {
73
- return `Missing complexity level: ${level}`;
74
- }
75
- }
76
- if (!settings.api_keys) {
77
- return "Missing api_keys configuration";
78
- }
79
- return null;
80
- }
81
- function getDefaultSettings() {
82
- return JSON.parse(JSON.stringify(DEFAULT_SETTINGS));
83
- }
84
- function getAvailableModels(settings) {
85
- const anthropicModels = [
86
- "claude-opus-4-6",
87
- "claude-sonnet-4-6",
88
- "claude-haiku-4-5"
89
- ];
90
- const openaiModels = settings.api_keys.openai ? ["gpt-5.2-codex", "o3-deep-research", "gpt-4o", "gpt-4o-mini"] : [];
91
- const googleModels = settings.api_keys.google ? ["gemini-3-pro-preview", "gemini-3-flash-preview"] : [];
92
- const zaiModels = settings.api_keys.zai ? ["glm-4.7", "glm-4.7-flash"] : [];
93
- const kimiModels = settings.api_keys.kimi ? ["kimi-k2", "kimi-k2.5"] : [];
94
- return {
95
- anthropic: anthropicModels,
96
- openai: openaiModels,
97
- google: googleModels,
98
- zai: zaiModels,
99
- kimi: kimiModels
100
- };
101
- }
102
- function isAnthropicModel(modelId) {
103
- return modelId.startsWith("claude-");
104
- }
105
- function getClaudeModelFlag(modelId) {
106
- const modelMap = {
107
- "claude-opus-4-6": "opus",
108
- "claude-sonnet-4-6": "sonnet",
109
- "claude-sonnet-4-5": "sonnet",
110
- "claude-haiku-4-5": "haiku"
111
- };
112
- return modelMap[modelId] || "sonnet";
113
- }
114
- function getAgentCommand(modelId) {
115
- if (isAnthropicModel(modelId)) {
116
- return {
117
- command: "claude",
118
- args: ["--model", getClaudeModelFlag(modelId)]
119
- };
120
- }
121
- return {
122
- command: "claude-code-router",
123
- args: []
124
- };
125
- }
126
- var DEFAULT_SETTINGS;
127
- var init_settings = __esm({
128
- "src/lib/settings.ts"() {
129
- "use strict";
130
- init_esm_shims();
131
- init_paths();
132
- DEFAULT_SETTINGS = {
133
- models: {
134
- specialists: {
135
- review_agent: "claude-opus-4-6",
136
- test_agent: "claude-sonnet-4-6",
137
- merge_agent: "claude-sonnet-4-6"
138
- },
139
- status_review: "claude-opus-4-6",
140
- complexity: {
141
- trivial: "claude-haiku-4-5",
142
- simple: "claude-haiku-4-5",
143
- medium: "kimi-k2.5",
144
- complex: "kimi-k2.5",
145
- expert: "claude-opus-4-6"
146
- }
147
- },
148
- api_keys: {}
149
- };
150
- }
151
- });
152
-
153
- // src/lib/providers.ts
154
- import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
155
- import { join } from "path";
156
- function getProviderForModel(modelId) {
157
- if (["claude-opus-4-6", "claude-sonnet-4-6", "claude-sonnet-4-5", "claude-haiku-4-5"].includes(modelId)) {
158
- return PROVIDERS.anthropic;
159
- }
160
- if (["gpt-5.2-codex", "o3-deep-research", "gpt-4o", "gpt-4o-mini"].includes(modelId)) {
161
- return PROVIDERS.openai;
162
- }
163
- if (["gemini-3-pro-preview", "gemini-3-flash-preview"].includes(modelId)) {
164
- return PROVIDERS.google;
165
- }
166
- if (["glm-4.7", "glm-4.7-flash"].includes(modelId)) {
167
- return PROVIDERS.zai;
168
- }
169
- if (["kimi-k2", "kimi-k2.5"].includes(modelId)) {
170
- return PROVIDERS.kimi;
171
- }
172
- return PROVIDERS.anthropic;
173
- }
174
- function requiresRouter(provider) {
175
- return PROVIDERS[provider].compatibility === "router";
176
- }
177
- function getRouterProviders() {
178
- return Object.values(PROVIDERS).filter((p) => p.compatibility === "router");
179
- }
180
- function getDirectProviders() {
181
- return Object.values(PROVIDERS).filter((p) => p.compatibility === "direct");
182
- }
183
- function needsRouter(apiKeys) {
184
- return !!(apiKeys.openai || apiKeys.google);
185
- }
186
- function getProviderEnv(provider, apiKey) {
187
- if (provider.compatibility === "direct") {
188
- const env = {};
189
- if (provider.baseUrl) {
190
- env.ANTHROPIC_BASE_URL = provider.baseUrl;
191
- }
192
- if (provider.name !== "anthropic") {
193
- if (provider.authType === "credential-file") {
194
- env.ANTHROPIC_AUTH_TOKEN = apiKey;
195
- env.CLAUDE_CODE_API_KEY_HELPER_TTL_MS = "60000";
196
- } else {
197
- env.ANTHROPIC_AUTH_TOKEN = apiKey;
198
- }
199
- }
200
- if (provider.name === "zai") {
201
- env.API_TIMEOUT_MS = "300000";
202
- }
203
- return env;
204
- } else {
205
- return {
206
- ANTHROPIC_BASE_URL: "http://localhost:3456",
207
- ANTHROPIC_AUTH_TOKEN: "router-managed"
208
- };
209
- }
210
- }
211
- function setupCredentialFileAuth(provider, workspacePath) {
212
- if (provider.authType !== "credential-file" || !provider.credentialHelper) return;
213
- const helperPath = provider.credentialHelper.replace("~", process.env.HOME || "");
214
- const claudeDir = join(workspacePath, ".claude");
215
- const settingsPath = join(claudeDir, "settings.local.json");
216
- if (!existsSync2(claudeDir)) {
217
- mkdirSync(claudeDir, { recursive: true });
218
- }
219
- let settings = {};
220
- if (existsSync2(settingsPath)) {
221
- try {
222
- settings = JSON.parse(readFileSync2(settingsPath, "utf-8"));
223
- } catch {
224
- }
225
- }
226
- settings.apiKeyHelper = helperPath;
227
- writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n");
228
- }
229
- var PROVIDERS;
230
- var init_providers = __esm({
231
- "src/lib/providers.ts"() {
232
- "use strict";
233
- init_esm_shims();
234
- PROVIDERS = {
235
- anthropic: {
236
- name: "anthropic",
237
- displayName: "Anthropic",
238
- compatibility: "direct",
239
- models: ["claude-opus-4-6", "claude-sonnet-4-6", "claude-sonnet-4-5", "claude-haiku-4-5"],
240
- tested: true,
241
- description: "Native Claude API"
242
- },
243
- kimi: {
244
- name: "kimi",
245
- displayName: "Kimi (Moonshot AI)",
246
- compatibility: "direct",
247
- baseUrl: "https://api.kimi.com/coding/",
248
- authType: "credential-file",
249
- credentialFile: "~/.kimi/credentials/kimi-code.json",
250
- credentialHelper: "~/.panopticon/bin/kimi-token-helper.sh",
251
- models: [],
252
- // Kimi uses same model names as Anthropic
253
- tested: true,
254
- description: "Anthropic-compatible API via Kimi Code Plan (OAuth token refresh)"
255
- },
256
- zai: {
257
- name: "zai",
258
- displayName: "Z.AI (GLM)",
259
- compatibility: "direct",
260
- baseUrl: "https://api.z.ai/api/anthropic",
261
- models: ["glm-4.7", "glm-4.7-flash"],
262
- tested: true,
263
- description: "Anthropic-compatible API, tested 2026-01-28"
264
- },
265
- openai: {
266
- name: "openai",
267
- displayName: "OpenAI",
268
- compatibility: "router",
269
- models: ["gpt-5.2-codex", "o3-deep-research", "gpt-4o", "gpt-4o-mini"],
270
- tested: false,
271
- description: "Requires claude-code-router for API translation"
272
- },
273
- google: {
274
- name: "google",
275
- displayName: "Google (Gemini)",
276
- compatibility: "router",
277
- models: ["gemini-3-pro-preview", "gemini-3-flash-preview"],
278
- tested: false,
279
- description: "Requires claude-code-router for API translation"
280
- }
281
- };
282
- }
283
- });
284
-
285
- // src/lib/config-yaml.ts
286
- import { readFileSync as readFileSync3, existsSync as existsSync3 } from "fs";
287
- import { join as join2 } from "path";
288
- import { homedir } from "os";
289
- import yaml from "js-yaml";
290
- function normalizeProviderConfig(providerConfig, fallbackKey) {
291
- if (providerConfig === void 0) {
292
- return { enabled: false };
293
- }
294
- if (typeof providerConfig === "boolean") {
295
- return { enabled: providerConfig, api_key: fallbackKey };
296
- }
297
- return {
298
- enabled: providerConfig.enabled,
299
- api_key: providerConfig.api_key || fallbackKey
300
- };
301
- }
302
- function resolveEnvVar(value) {
303
- if (!value) return void 0;
304
- return value.replace(/\$\{?([A-Z_][A-Z0-9_]*)\}?/g, (match, varName) => {
305
- const envValue = process.env[varName];
306
- return envValue !== void 0 ? envValue : match;
307
- });
308
- }
309
- function loadYamlFile(filePath) {
310
- if (!existsSync3(filePath)) {
311
- return null;
312
- }
313
- try {
314
- const content = readFileSync3(filePath, "utf-8");
315
- const parsed = yaml.load(content);
316
- return parsed || {};
317
- } catch (error) {
318
- console.error(`Error loading YAML config from ${filePath}:`, error);
319
- return null;
320
- }
321
- }
322
- function findProjectRoot(startDir = process.cwd()) {
323
- let currentDir = startDir;
324
- while (currentDir !== "/") {
325
- if (existsSync3(join2(currentDir, ".git"))) {
326
- return currentDir;
327
- }
328
- currentDir = join2(currentDir, "..");
329
- }
330
- return null;
331
- }
332
- function loadProjectConfig() {
333
- const projectRoot = findProjectRoot();
334
- if (!projectRoot) {
335
- return null;
336
- }
337
- const projectConfigPath = join2(projectRoot, ".panopticon.yaml");
338
- return loadYamlFile(projectConfigPath);
339
- }
340
- function loadGlobalConfig() {
341
- return loadYamlFile(GLOBAL_CONFIG_PATH);
342
- }
343
- function mergeShadowConfig(result, config) {
344
- if (!config?.shadow) return;
345
- if (config.shadow.enabled !== void 0) {
346
- result.enabled = config.shadow.enabled;
347
- }
348
- if (config.shadow.trackers) {
349
- if (config.shadow.trackers.linear !== void 0) {
350
- result.trackers.linear = config.shadow.trackers.linear;
351
- }
352
- if (config.shadow.trackers.github !== void 0) {
353
- result.trackers.github = config.shadow.trackers.github;
354
- }
355
- if (config.shadow.trackers.gitlab !== void 0) {
356
- result.trackers.gitlab = config.shadow.trackers.gitlab;
357
- }
358
- if (config.shadow.trackers.rally !== void 0) {
359
- result.trackers.rally = config.shadow.trackers.rally;
360
- }
361
- }
362
- }
363
- function mergeConfigs(...configs) {
364
- const result = {
365
- ...DEFAULT_CONFIG,
366
- enabledProviders: new Set(DEFAULT_CONFIG.enabledProviders),
367
- shadow: {
368
- enabled: DEFAULT_CONFIG.shadow.enabled,
369
- trackers: { ...DEFAULT_CONFIG.shadow.trackers }
370
- }
371
- };
372
- const validConfigs = configs.filter((c) => c !== null);
373
- for (const config of validConfigs.reverse()) {
374
- if (config.models?.providers) {
375
- const providers = config.models.providers;
376
- const legacyKeys = config.api_keys || {};
377
- result.enabledProviders.add("anthropic");
378
- const openai = normalizeProviderConfig(providers.openai, legacyKeys.openai);
379
- if (openai.enabled) {
380
- result.enabledProviders.add("openai");
381
- if (openai.api_key) {
382
- result.apiKeys.openai = resolveEnvVar(openai.api_key);
383
- }
384
- }
385
- const google = normalizeProviderConfig(providers.google, legacyKeys.google);
386
- if (google.enabled) {
387
- result.enabledProviders.add("google");
388
- if (google.api_key) {
389
- result.apiKeys.google = resolveEnvVar(google.api_key);
390
- }
391
- }
392
- const zai = normalizeProviderConfig(providers.zai, legacyKeys.zai);
393
- if (zai.enabled) {
394
- result.enabledProviders.add("zai");
395
- if (zai.api_key) {
396
- result.apiKeys.zai = resolveEnvVar(zai.api_key);
397
- }
398
- }
399
- const kimi = normalizeProviderConfig(providers.kimi, legacyKeys.kimi);
400
- if (kimi.enabled) {
401
- result.enabledProviders.add("kimi");
402
- if (kimi.api_key) {
403
- result.apiKeys.kimi = resolveEnvVar(kimi.api_key);
404
- }
405
- }
406
- }
407
- if (config.api_keys) {
408
- if (config.api_keys.openai) {
409
- result.apiKeys.openai = resolveEnvVar(config.api_keys.openai);
410
- result.enabledProviders.add("openai");
411
- }
412
- if (config.api_keys.google) {
413
- result.apiKeys.google = resolveEnvVar(config.api_keys.google);
414
- result.enabledProviders.add("google");
415
- }
416
- if (config.api_keys.zai) {
417
- result.apiKeys.zai = resolveEnvVar(config.api_keys.zai);
418
- result.enabledProviders.add("zai");
419
- }
420
- if (config.api_keys.kimi) {
421
- result.apiKeys.kimi = resolveEnvVar(config.api_keys.kimi);
422
- result.enabledProviders.add("kimi");
423
- }
424
- }
425
- if (config.models?.overrides) {
426
- result.overrides = {
427
- ...result.overrides,
428
- ...config.models.overrides
429
- };
430
- }
431
- if (config.models?.gemini_thinking_level) {
432
- result.geminiThinkingLevel = config.models.gemini_thinking_level;
433
- }
434
- if (config.tracker_keys) {
435
- if (config.tracker_keys.linear) {
436
- result.trackerKeys.linear = resolveEnvVar(config.tracker_keys.linear);
437
- }
438
- if (config.tracker_keys.github) {
439
- result.trackerKeys.github = resolveEnvVar(config.tracker_keys.github);
440
- }
441
- if (config.tracker_keys.gitlab) {
442
- result.trackerKeys.gitlab = resolveEnvVar(config.tracker_keys.gitlab);
443
- }
444
- if (config.tracker_keys.rally) {
445
- result.trackerKeys.rally = resolveEnvVar(config.tracker_keys.rally);
446
- }
447
- }
448
- mergeShadowConfig(result.shadow, config);
449
- }
450
- return result;
451
- }
452
- function loadConfig() {
453
- const globalConfig = loadGlobalConfig();
454
- const projectConfig = loadProjectConfig();
455
- const config = mergeConfigs(projectConfig, globalConfig);
456
- if (process.env.OPENAI_API_KEY && !config.apiKeys.openai) {
457
- config.apiKeys.openai = process.env.OPENAI_API_KEY;
458
- config.enabledProviders.add("openai");
459
- }
460
- if (process.env.GOOGLE_API_KEY && !config.apiKeys.google) {
461
- config.apiKeys.google = process.env.GOOGLE_API_KEY;
462
- config.enabledProviders.add("google");
463
- }
464
- if (process.env.ZAI_API_KEY && !config.apiKeys.zai) {
465
- config.apiKeys.zai = process.env.ZAI_API_KEY;
466
- config.enabledProviders.add("zai");
467
- }
468
- if (process.env.KIMI_API_KEY && !config.apiKeys.kimi) {
469
- config.apiKeys.kimi = process.env.KIMI_API_KEY;
470
- config.enabledProviders.add("kimi");
471
- }
472
- if (process.env.LINEAR_API_KEY && !config.trackerKeys.linear) {
473
- config.trackerKeys.linear = process.env.LINEAR_API_KEY;
474
- }
475
- if (process.env.GITHUB_TOKEN && !config.trackerKeys.github) {
476
- config.trackerKeys.github = process.env.GITHUB_TOKEN;
477
- }
478
- if (process.env.GITLAB_TOKEN && !config.trackerKeys.gitlab) {
479
- config.trackerKeys.gitlab = process.env.GITLAB_TOKEN;
480
- }
481
- if (process.env.RALLY_API_KEY && !config.trackerKeys.rally) {
482
- config.trackerKeys.rally = process.env.RALLY_API_KEY;
483
- }
484
- if (process.env.SHADOW_MODE !== void 0) {
485
- const envShadowMode = ["true", "1", "yes"].includes(process.env.SHADOW_MODE.toLowerCase());
486
- config.shadow.enabled = envShadowMode;
487
- }
488
- return config;
489
- }
490
- var DEFAULT_CONFIG, GLOBAL_CONFIG_PATH;
491
- var init_config_yaml = __esm({
492
- "src/lib/config-yaml.ts"() {
493
- "use strict";
494
- init_esm_shims();
495
- DEFAULT_CONFIG = {
496
- enabledProviders: /* @__PURE__ */ new Set(["anthropic"]),
497
- // Only Anthropic by default
498
- apiKeys: {},
499
- overrides: {},
500
- geminiThinkingLevel: 3,
501
- trackerKeys: {},
502
- shadow: {
503
- enabled: false,
504
- trackers: {
505
- linear: false,
506
- github: false,
507
- gitlab: false,
508
- rally: false
509
- }
510
- }
511
- };
512
- GLOBAL_CONFIG_PATH = join2(homedir(), ".panopticon", "config.yaml");
513
- }
514
- });
515
-
516
- export {
517
- loadSettings,
518
- saveSettings,
519
- validateSettings,
520
- getDefaultSettings,
521
- getAvailableModels,
522
- isAnthropicModel,
523
- getClaudeModelFlag,
524
- getAgentCommand,
525
- init_settings,
526
- loadConfig,
527
- init_config_yaml,
528
- PROVIDERS,
529
- getProviderForModel,
530
- requiresRouter,
531
- getRouterProviders,
532
- getDirectProviders,
533
- needsRouter,
534
- getProviderEnv,
535
- setupCredentialFileAuth,
536
- init_providers
537
- };
538
- //# sourceMappingURL=chunk-7XNJJBH6.js.map