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.
- package/README.md +96 -210
- package/dist/{agents-VLK4BMVA.js → agents-5OPQKM5K.js} +6 -5
- package/dist/{chunk-OMNXYPXC.js → chunk-2V4NF7J2.js} +14 -1
- package/dist/chunk-2V4NF7J2.js.map +1 -0
- package/dist/{chunk-XKT5MHPT.js → chunk-4YSYJ4HM.js} +2 -2
- package/dist/{chunk-XFR2DLMR.js → chunk-76F6DSVS.js} +49 -10
- package/dist/chunk-76F6DSVS.js.map +1 -0
- package/dist/{chunk-PI7Y3PSN.js → chunk-F5555J3A.js} +42 -6
- package/dist/chunk-F5555J3A.js.map +1 -0
- package/dist/{chunk-KJ2TRXNK.js → chunk-FTCPTHIJ.js} +47 -420
- package/dist/chunk-FTCPTHIJ.js.map +1 -0
- package/dist/chunk-HJSM6E6U.js +1038 -0
- package/dist/chunk-HJSM6E6U.js.map +1 -0
- package/dist/{chunk-RBUO57TC.js → chunk-NLQRED36.js} +3 -3
- package/dist/chunk-NLQRED36.js.map +1 -0
- package/dist/{chunk-ASY7T35E.js → chunk-OWHXCGVO.js} +245 -90
- package/dist/chunk-OWHXCGVO.js.map +1 -0
- package/dist/{chunk-BKCWRMUX.js → chunk-VHKSS7QX.js} +106 -11
- package/dist/chunk-VHKSS7QX.js.map +1 -0
- package/dist/{chunk-GFP3PIPB.js → chunk-YGJ54GW2.js} +1 -1
- package/dist/chunk-YGJ54GW2.js.map +1 -0
- package/dist/cli/index.js +1521 -935
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/prompts/work-agent.md +2 -0
- package/dist/dashboard/public/assets/index-Ce6q21Fm.js +743 -0
- package/dist/dashboard/public/assets/{index-UjZq6ykz.css → index-NzpI0ItZ.css} +1 -1
- package/dist/dashboard/public/index.html +2 -2
- package/dist/dashboard/server.js +4274 -2320
- package/dist/{feedback-writer-LVZ5TFYZ.js → feedback-writer-VRMMWWTW.js} +2 -2
- package/dist/git-utils-I2UDKNZH.js +131 -0
- package/dist/git-utils-I2UDKNZH.js.map +1 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/{projects-JEIVIYC6.js → projects-CFX3RTDL.js} +4 -2
- package/dist/{remote-workspace-AHVHQEES.js → remote-workspace-7FPGF2RM.js} +2 -2
- package/dist/{review-status-EPFG4XM7.js → review-status-TDPSOU5J.js} +2 -2
- package/dist/{specialist-context-T3NBMCIE.js → specialist-context-WGUUYDWY.js} +5 -5
- package/dist/{specialist-logs-CVKD3YJ3.js → specialist-logs-XJB5TCKJ.js} +5 -5
- package/dist/{specialists-TKAP6T6Z.js → specialists-5LBRHYFA.js} +5 -5
- package/dist/{traefik-QX4ZV4YG.js → traefik-WFMQX2LY.js} +3 -3
- package/dist/{workspace-manager-KLHUCIZV.js → workspace-manager-E434Z45T.js} +2 -2
- package/package.json +1 -1
- package/scripts/record-cost-event.js +5 -5
- package/scripts/stop-hook +7 -0
- package/scripts/work-agent-stop-hook +137 -0
- package/skills/myn-standards/SKILL.md +351 -0
- package/skills/pan-new-project/SKILL.md +304 -0
- package/skills/write-spec/SKILL.md +138 -0
- package/dist/chunk-7XNJJBH6.js +0 -538
- package/dist/chunk-7XNJJBH6.js.map +0 -1
- package/dist/chunk-ASY7T35E.js.map +0 -1
- package/dist/chunk-BKCWRMUX.js.map +0 -1
- package/dist/chunk-GFP3PIPB.js.map +0 -1
- package/dist/chunk-KJ2TRXNK.js.map +0 -1
- package/dist/chunk-OMNXYPXC.js.map +0 -1
- package/dist/chunk-PI7Y3PSN.js.map +0 -1
- package/dist/chunk-RBUO57TC.js.map +0 -1
- package/dist/chunk-XFR2DLMR.js.map +0 -1
- package/dist/dashboard/public/assets/index-kAJqtLDO.js +0 -708
- /package/dist/{agents-VLK4BMVA.js.map → agents-5OPQKM5K.js.map} +0 -0
- /package/dist/{chunk-XKT5MHPT.js.map → chunk-4YSYJ4HM.js.map} +0 -0
- /package/dist/{feedback-writer-LVZ5TFYZ.js.map → feedback-writer-VRMMWWTW.js.map} +0 -0
- /package/dist/{projects-JEIVIYC6.js.map → projects-CFX3RTDL.js.map} +0 -0
- /package/dist/{remote-workspace-AHVHQEES.js.map → remote-workspace-7FPGF2RM.js.map} +0 -0
- /package/dist/{review-status-EPFG4XM7.js.map → review-status-TDPSOU5J.js.map} +0 -0
- /package/dist/{specialist-context-T3NBMCIE.js.map → specialist-context-WGUUYDWY.js.map} +0 -0
- /package/dist/{specialist-logs-CVKD3YJ3.js.map → specialist-logs-XJB5TCKJ.js.map} +0 -0
- /package/dist/{specialists-TKAP6T6Z.js.map → specialists-5LBRHYFA.js.map} +0 -0
- /package/dist/{traefik-QX4ZV4YG.js.map → traefik-WFMQX2LY.js.map} +0 -0
- /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
|
package/dist/chunk-7XNJJBH6.js
DELETED
|
@@ -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
|