panopticon-cli 0.6.8 → 0.6.9
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/dist/{agents-D_2oRFVf.js → agents-BQOqo27C.js} +1 -1
- package/dist/{agents-CfFDs52G.js → agents-DezveQ1x.js} +4 -4
- package/dist/{agents-CfFDs52G.js.map → agents-DezveQ1x.js.map} +1 -1
- package/dist/cli/index.js +34 -34
- package/dist/{config-yaml-DGbLSMCa.js → config-yaml-BHD2Qdd8.js} +22 -1
- package/dist/config-yaml-BHD2Qdd8.js.map +1 -0
- package/dist/{config-yaml-Dqt4FWQH.js → config-yaml-IlSnFzJQ.js} +1 -1
- package/dist/dashboard/{agent-enrichment-DdO7ZqjI.js → agent-enrichment-BKZjVvlL.js} +3 -3
- package/dist/dashboard/{agent-enrichment-DdO7ZqjI.js.map → agent-enrichment-BKZjVvlL.js.map} +1 -1
- package/dist/dashboard/{agent-enrichment-dLeGE1fX.js → agent-enrichment-iY3_PylI.js} +1 -1
- package/dist/dashboard/{agents-DCpQQ_W5.js → agents-BQWA-Vps.js} +4 -4
- package/dist/dashboard/{agents-DCpQQ_W5.js.map → agents-BQWA-Vps.js.map} +1 -1
- package/dist/dashboard/{agents-Dgh2TjSp.js → agents-Dinc9j_8.js} +1 -1
- package/dist/dashboard/{config-yaml-DkresmrS.js → config-yaml-CNNnB4Mu.js} +1 -1
- package/dist/dashboard/{config-yaml-DSfYpzN6.js → config-yaml-DUu0JI25.js} +22 -1
- package/dist/dashboard/{config-yaml-DSfYpzN6.js.map → config-yaml-DUu0JI25.js.map} +1 -1
- package/dist/dashboard/{factory-C8nhLGHB.js → factory-CBY0WWeE.js} +2 -2
- package/dist/dashboard/{factory-C8nhLGHB.js.map → factory-CBY0WWeE.js.map} +1 -1
- package/dist/dashboard/{inspect-agent-7eour7EA.js → inspect-agent-KKOeNR7E.js} +3 -3
- package/dist/dashboard/{inspect-agent-7eour7EA.js.map → inspect-agent-KKOeNR7E.js.map} +1 -1
- package/dist/dashboard/{issue-service-singleton-Wv4xBm3y.js → issue-service-singleton-BCZ62hLj.js} +3 -3
- package/dist/dashboard/{issue-service-singleton-Wv4xBm3y.js.map → issue-service-singleton-BCZ62hLj.js.map} +1 -1
- package/dist/dashboard/{issue-service-singleton-Co__-6kL.js → issue-service-singleton-BGKf0A95.js} +1 -1
- package/dist/dashboard/{lifecycle-BcUmtkR4.js → lifecycle-Dpgg-IeP.js} +1 -1
- package/dist/dashboard/{merge-agent-CGN3TT0a.js → merge-agent-CqvQu-n_.js} +1 -1
- package/dist/dashboard/{merge-agent-yudQOPZc.js → merge-agent-Dxxc4JEE.js} +5 -5
- package/dist/dashboard/{merge-agent-yudQOPZc.js.map → merge-agent-Dxxc4JEE.js.map} +1 -1
- package/dist/dashboard/public/assets/{dist-C-wcq54x.js → dist-DS1gmhe1.js} +1 -1
- package/dist/dashboard/public/assets/index-DjGsaJLv.js +212 -0
- package/dist/dashboard/public/index.html +1 -1
- package/dist/dashboard/{review-status-BtXqWBhS.js → review-status-Dww2OKUX.js} +1 -1
- package/dist/dashboard/{review-status-Bymwzh2i.js → review-status-d_wOE-XQ.js} +3 -3
- package/dist/dashboard/{review-status-Bymwzh2i.js.map → review-status-d_wOE-XQ.js.map} +1 -1
- package/dist/dashboard/server.js +97 -97
- package/dist/dashboard/settings-BHlDG7TK.js.map +1 -1
- package/dist/dashboard/{spawn-planning-session-D5hrVdWM.js → spawn-planning-session-D5uEpHzf.js} +1 -1
- package/dist/dashboard/{spawn-planning-session-33Jf-d5T.js → spawn-planning-session-DtbNfA2Q.js} +3 -3
- package/dist/dashboard/{spawn-planning-session-33Jf-d5T.js.map → spawn-planning-session-DtbNfA2Q.js.map} +1 -1
- package/dist/dashboard/{specialist-context-DGukHSn8.js → specialist-context-CEKqWqyF.js} +4 -4
- package/dist/dashboard/{specialist-context-DGukHSn8.js.map → specialist-context-CEKqWqyF.js.map} +1 -1
- package/dist/dashboard/{specialist-logs-CIw4qfTy.js → specialist-logs-CBGVRoQF.js} +1 -1
- package/dist/dashboard/{specialists-Cp-PgspS.js → specialists-sIFlMd3s.js} +1 -1
- package/dist/dashboard/{specialists-B_zrayaP.js → specialists-saEYE0-z.js} +20 -20
- package/dist/dashboard/{specialists-B_zrayaP.js.map → specialists-saEYE0-z.js.map} +1 -1
- package/dist/dashboard/{test-agent-queue-ypF_ecHo.js → test-agent-queue-7jXB2KkN.js} +3 -3
- package/dist/dashboard/{test-agent-queue-ypF_ecHo.js.map → test-agent-queue-7jXB2KkN.js.map} +1 -1
- package/dist/dashboard/{tracker-config-BP59uH4V.js → tracker-config-BX6ijWOc.js} +1 -1
- package/dist/dashboard/{tracker-config-e7ph1QqT.js → tracker-config-tD22z5sv.js} +2 -2
- package/dist/dashboard/{tracker-config-e7ph1QqT.js.map → tracker-config-tD22z5sv.js.map} +1 -1
- package/dist/dashboard/{work-agent-prompt-fCg67nyo.js → work-agent-prompt-D3tPzPvb.js} +2 -2
- package/dist/dashboard/{work-agent-prompt-fCg67nyo.js.map → work-agent-prompt-D3tPzPvb.js.map} +1 -1
- package/dist/dashboard/{work-type-router-CWVW2Wk_.js → work-type-router-7kwLSwrP.js} +4 -2
- package/dist/dashboard/work-type-router-7kwLSwrP.js.map +1 -0
- package/dist/dashboard/{work-type-router-Di5gCQwh.js → work-type-router-ByOOudGz.js} +1 -1
- package/dist/dashboard/workflows-BDpPjK18.js +2 -0
- package/dist/dashboard/{workflows-BSMipN07.js → workflows-DcEeDkbS.js} +3 -3
- package/dist/dashboard/{workflows-BSMipN07.js.map → workflows-DcEeDkbS.js.map} +1 -1
- package/dist/{factory-BRBGw6OB.js → factory-BR48tuUR.js} +1 -1
- package/dist/{factory-DzsOiZVc.js → factory-D6LJaZ__.js} +2 -2
- package/dist/{factory-DzsOiZVc.js.map → factory-D6LJaZ__.js.map} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -3
- package/dist/{merge-agent-DlUiUanN.js → merge-agent-BBwHwpn2.js} +3 -3
- package/dist/{merge-agent-DlUiUanN.js.map → merge-agent-BBwHwpn2.js.map} +1 -1
- package/dist/{review-status-DEDvCKMP.js → review-status-Ba6llgCb.js} +3 -3
- package/dist/{review-status-DEDvCKMP.js.map → review-status-Ba6llgCb.js.map} +1 -1
- package/dist/{review-status-D6H2WOw8.js → review-status-Chxzuwn2.js} +1 -1
- package/dist/{settings-BcWPTrua.js → settings-A-CWz_ph.js} +6 -2
- package/dist/{settings-BcWPTrua.js.map → settings-A-CWz_ph.js.map} +1 -1
- package/dist/{specialist-context-BAUWL1Fl.js → specialist-context-B3lknlwi.js} +4 -4
- package/dist/{specialist-context-BAUWL1Fl.js.map → specialist-context-B3lknlwi.js.map} +1 -1
- package/dist/{specialist-logs-DQKKQV9B.js → specialist-logs-DDyY4xqo.js} +1 -1
- package/dist/{specialists-D7Kj5o6s.js → specialists-DvTYu1VZ.js} +20 -20
- package/dist/{specialists-D7Kj5o6s.js.map → specialists-DvTYu1VZ.js.map} +1 -1
- package/dist/{specialists-Bfb9ATzw.js → specialists-DyB4IRlM.js} +1 -1
- package/dist/sync-CLVqiGl4.js +2 -0
- package/dist/{sync-DMfgd389.js → sync-DTHFlEc-.js} +2 -2
- package/dist/{sync-DMfgd389.js.map → sync-DTHFlEc-.js.map} +1 -1
- package/dist/{tracker-BhYYvU3p.js → tracker-CYpb7oUa.js} +2 -2
- package/dist/{tracker-BhYYvU3p.js.map → tracker-CYpb7oUa.js.map} +1 -1
- package/dist/{work-type-router-CHjciPyS.js → work-type-router-oCgTPXsP.js} +4 -2
- package/dist/work-type-router-oCgTPXsP.js.map +1 -0
- package/package.json +1 -1
- package/dist/config-yaml-DGbLSMCa.js.map +0 -1
- package/dist/dashboard/public/assets/index-DKlrFY1k.js +0 -212
- package/dist/dashboard/work-type-router-CWVW2Wk_.js.map +0 -1
- package/dist/dashboard/workflows-DaYWQIS2.js +0 -2
- package/dist/sync-TL6y-8K6.js +0 -2
- package/dist/work-type-router-CHjciPyS.js.map +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -2,27 +2,27 @@
|
|
|
2
2
|
import { r as __require } from "../chunk-ruWRV7i3.js";
|
|
3
3
|
import { A as SKILLS_DIR, B as TRAEFIK_CERTS_DIR, G as init_paths, L as SOURCE_TRAEFIK_TEMPLATES, O as PROJECT_PRDS_SUBDIR, R as SYNC_TARGET, S as PRD_DRAFTS_DIR, T as PROJECT_PRDS_ACTIVE_SUBDIR, V as TRAEFIK_DIR, b as PANOPTICON_HOME$1, d as CLAUDE_MD_TEMPLATES, f as COMMANDS_DIR, l as CERTS_DIR, m as CONFIG_FILE$1, t as AGENTS_DIR, u as CLAUDE_DIR, v as INIT_DIRS, w as PROJECT_DOCS_SUBDIR } from "../paths-CDJ_HsbN.js";
|
|
4
4
|
import { a as init_config, n as getDashboardApiUrl, o as loadConfig$1, r as getDefaultConfig, s as saveConfig } from "../config-BQNKsi9G.js";
|
|
5
|
-
import { a as addAlias, c as getShellRcFile, o as detectShell, s as getAliasInstructions } from "../tracker-
|
|
6
|
-
import { _ as cleanOldBackups, b as listBackups, m as refreshCache, x as restoreBackup } from "../settings-
|
|
5
|
+
import { a as addAlias, c as getShellRcFile, o as detectShell, s as getAliasInstructions } from "../tracker-CYpb7oUa.js";
|
|
6
|
+
import { _ as cleanOldBackups, b as listBackups, m as refreshCache, x as restoreBackup } from "../settings-A-CWz_ph.js";
|
|
7
7
|
import { n as extractPrefix, r as init_issue_id, t as extractNumber } from "../issue-id-CAcekoIw.js";
|
|
8
8
|
import { S as unregisterProject, a as findProjectByTeam, c as getProject, f as hasProjects, g as loadProjectsConfig, h as listProjects, m as initializeProjectsConfig, p as init_projects, r as extractTeamPrefix, s as getIssuePrefix, t as PROJECTS_CONFIG_FILE, v as registerProject, y as resolveProjectFromIssue } from "../projects-Bk-5QhFQ.js";
|
|
9
|
-
import { i as needsMigration, n as hasLegacySettings, r as migrateConfig, t as syncCommand$1 } from "../sync-
|
|
9
|
+
import { i as needsMigration, n as hasLegacySettings, r as migrateConfig, t as syncCommand$1 } from "../sync-DTHFlEc-.js";
|
|
10
10
|
import { n as init_workspace_config, r as replacePlaceholders } from "../workspace-config-fUafvYMp.js";
|
|
11
11
|
import { a as detectDnsSyncMethod, f as syncDnsToWindows, m as init_platform, o as ensureBaseDomain, p as detectPlatform$1, s as init_dns } from "../dns-Yxq4NNS7.js";
|
|
12
12
|
import { d as mergeSkillsIntoWorkspace, i as init_workspace_manager, l as applyProjectTemplateOverlay, n as createWorkspace, s as removeWorkspace, t as addReposToWorkspace, u as init_skills_merge } from "../workspace-manager-DuLhnzJV.js";
|
|
13
13
|
import { d as sessionExists, i as createSession, l as sendKeys, o as init_tmux, s as killSession, u as sendKeysAsync } from "../tmux-D6Ah4I8z.js";
|
|
14
|
-
import { a as generateFixedPointPrompt, c as popFromHook, i as clearHook, l as pushToHook, n as init_work_type_router, r as checkHook, s as init_hooks, t as getModelId, u as sendMail } from "../work-type-router-
|
|
15
|
-
import { C as init_config$1, D as getAgentRankings, E as getAgentCV, O as init_cv, S as getHealthThresholdsMs, T as formatCV, _ as saveAgentState, a as getAgentRuntimeState, b as stopAgent, c as getProviderEnvForModel, d as init_agents, f as listRunningAgents, g as saveAgentRuntimeState, m as recoverAgent, n as detectCrashedAgents, o as getAgentState, p as messageAgent, r as getAgentDir, t as autoRecoverAgents, v as saveSessionId, w as loadCloisterConfig, y as spawnAgent } from "../agents-
|
|
16
|
-
import { n as init_config_yaml, r as loadConfig$2 } from "../config-yaml-
|
|
14
|
+
import { a as generateFixedPointPrompt, c as popFromHook, i as clearHook, l as pushToHook, n as init_work_type_router, r as checkHook, s as init_hooks, t as getModelId, u as sendMail } from "../work-type-router-oCgTPXsP.js";
|
|
15
|
+
import { C as init_config$1, D as getAgentRankings, E as getAgentCV, O as init_cv, S as getHealthThresholdsMs, T as formatCV, _ as saveAgentState, a as getAgentRuntimeState, b as stopAgent, c as getProviderEnvForModel, d as init_agents, f as listRunningAgents, g as saveAgentRuntimeState, m as recoverAgent, n as detectCrashedAgents, o as getAgentState, p as messageAgent, r as getAgentDir, t as autoRecoverAgents, v as saveSessionId, w as loadCloisterConfig, y as spawnAgent } from "../agents-DezveQ1x.js";
|
|
16
|
+
import { n as init_config_yaml, r as loadConfig$2 } from "../config-yaml-BHD2Qdd8.js";
|
|
17
17
|
import { i as NotImplementedError, o as init_interface } from "../rally-Dy00NElU.js";
|
|
18
|
-
import { n as createTrackerFromConfig, o as init_factory, t as createTracker } from "../factory-
|
|
18
|
+
import { n as createTrackerFromConfig, o as init_factory, t as createTracker } from "../factory-D6LJaZ__.js";
|
|
19
19
|
import { n as resolveGitHubIssue, r as resolveTrackerType, t as isGitHubIssue } from "../tracker-utils-ChQyut8w.js";
|
|
20
20
|
import { a as isShadowed, c as needsSync, d as updateTrackerStatusCache, i as getUnsyncedHistory, n as getPendingSyncCount, o as listShadowedIssues, r as getShadowState, s as markAsSynced, t as createShadowState, u as updateShadowState } from "../shadow-state-CE3dQfll.js";
|
|
21
21
|
import { a as findRemoteWorkspaceMetadata, i as deleteWorkspaceMetadata, n as isRemoteAvailable, o as loadWorkspaceMetadata, r as WORKSPACES_DIR, s as saveWorkspaceMetadata, t as createFlyProviderFromConfig } from "../remote-CYiOJg0q.js";
|
|
22
22
|
import { a as spawnRemoteAgent, n as isRemoteAgentRunning, o as createFlyProvider } from "../remote-agents-CZXrUF4f.js";
|
|
23
|
-
import { At as formatCost, Bt as init_io, Ct as getProjectDirs, D as getSpecialistStatus, Dt as checkBudget, Et as parseClaudeSession, Ft as getWeeklySummary, H as recordWake, Ht as updateItemStatus, It as init_cost, Lt as readIssueCosts, Mt as getAllBudgets, Nt as getDailySummary, O as getTmuxSessionName, Ot as createBudget, Pt as getMonthlySummary, Rt as readTodayCosts, T as getSpecialistMetadata, Tt as init_jsonl_parser, Ut as updateSubItemStatus, Vt as readWorkspacePlan, Y as spawnEphemeralSpecialist, a as completeSpecialistTask, f as getAllProjectSpecialistStatuses, i as clearSessionId, it as wakeSpecialistOrQueue, j as init_specialists, jt as generateReport$1, kt as deleteBudget, p as getAllSpecialistStatus, q as setSessionId, r as checkSpecialistQueue, w as getSessionId, wt as getSessionFiles, zt as summarizeCosts } from "../specialists-
|
|
23
|
+
import { At as formatCost, Bt as init_io, Ct as getProjectDirs, D as getSpecialistStatus, Dt as checkBudget, Et as parseClaudeSession, Ft as getWeeklySummary, H as recordWake, Ht as updateItemStatus, It as init_cost, Lt as readIssueCosts, Mt as getAllBudgets, Nt as getDailySummary, O as getTmuxSessionName, Ot as createBudget, Pt as getMonthlySummary, Rt as readTodayCosts, T as getSpecialistMetadata, Tt as init_jsonl_parser, Ut as updateSubItemStatus, Vt as readWorkspacePlan, Y as spawnEphemeralSpecialist, a as completeSpecialistTask, f as getAllProjectSpecialistStatuses, i as clearSessionId, it as wakeSpecialistOrQueue, j as init_specialists, jt as generateReport$1, kt as deleteBudget, p as getAllSpecialistStatus, q as setSessionId, r as checkSpecialistQueue, w as getSessionId, wt as getSessionFiles, zt as summarizeCosts } from "../specialists-DvTYu1VZ.js";
|
|
24
24
|
import { i as init_tldr_daemon, n as getTldrDaemonService, r as getTldrMetrics } from "../tldr-daemon-CFx4LXAl.js";
|
|
25
|
-
import { a as saveReviewStatuses, c as getDatabase, i as loadReviewStatuses, l as init_database, o as setReviewStatus, r as init_review_status, s as closeDatabase } from "../review-status-
|
|
25
|
+
import { a as saveReviewStatuses, c as getDatabase, i as loadReviewStatuses, l as init_database, o as setReviewStatus, r as init_review_status, s as closeDatabase } from "../review-status-Ba6llgCb.js";
|
|
26
26
|
import { i as stepSkipped, n as stepFailed, r as stepOk } from "../types-BhJj1SP1.js";
|
|
27
27
|
import { r as findWorkspacePath, t as archivePlanning } from "../archive-planning-D97ziGec.js";
|
|
28
28
|
import "../clean-planning-D_lz4aQq.js";
|
|
@@ -1054,7 +1054,7 @@ async function handleRemoteWorkspace(issueId, options, spinner) {
|
|
|
1054
1054
|
} else if (isGitHubIssue(issueId)) {
|
|
1055
1055
|
const gh = resolveGitHubIssue(issueId);
|
|
1056
1056
|
if (gh.isGitHub) try {
|
|
1057
|
-
const { loadConfig: loadYamlConfig } = await import("../config-yaml-
|
|
1057
|
+
const { loadConfig: loadYamlConfig } = await import("../config-yaml-IlSnFzJQ.js");
|
|
1058
1058
|
const token = loadYamlConfig().config.trackerKeys?.github || process.env.GITHUB_TOKEN;
|
|
1059
1059
|
if (token) {
|
|
1060
1060
|
const { Octokit } = await import("@octokit/rest");
|
|
@@ -2177,7 +2177,7 @@ async function doneCommand$1(id, options = {}) {
|
|
|
2177
2177
|
const issueId = id.replace(/^agent-/i, "").toUpperCase();
|
|
2178
2178
|
const agentId = `agent-${issueId.toLowerCase()}`;
|
|
2179
2179
|
if (!options.force) {
|
|
2180
|
-
const { getAgentState } = await import("../agents-
|
|
2180
|
+
const { getAgentState } = await import("../agents-BQOqo27C.js");
|
|
2181
2181
|
const workspacePath = getAgentState(agentId)?.workspace;
|
|
2182
2182
|
if (workspacePath && existsSync(workspacePath)) {
|
|
2183
2183
|
const failures = [];
|
|
@@ -2271,7 +2271,7 @@ async function doneCommand$1(id, options = {}) {
|
|
|
2271
2271
|
else console.log(chalk.yellow(` ⚠ Failed to update Linear status`));
|
|
2272
2272
|
} else console.log(chalk.dim(" LINEAR_API_KEY not set - skipping status update"));
|
|
2273
2273
|
}
|
|
2274
|
-
const { getAgentState, saveAgentState } = await import("../agents-
|
|
2274
|
+
const { getAgentState, saveAgentState } = await import("../agents-BQOqo27C.js");
|
|
2275
2275
|
const existingState = getAgentState(agentId);
|
|
2276
2276
|
if (existingState) {
|
|
2277
2277
|
existingState.status = "stopped";
|
|
@@ -2782,7 +2782,7 @@ async function planCommand(id, options = {}) {
|
|
|
2782
2782
|
let issueData;
|
|
2783
2783
|
if (trackerType === "github" && ghResolution.isGitHub) {
|
|
2784
2784
|
spinner.text = "Fetching issue from GitHub...";
|
|
2785
|
-
const { loadConfig: loadYamlConfig } = await import("../config-yaml-
|
|
2785
|
+
const { loadConfig: loadYamlConfig } = await import("../config-yaml-IlSnFzJQ.js");
|
|
2786
2786
|
const token = loadYamlConfig().config.trackerKeys?.github || process.env.GITHUB_TOKEN;
|
|
2787
2787
|
if (!token) {
|
|
2788
2788
|
spinner.fail("GitHub token not found");
|
|
@@ -2807,7 +2807,7 @@ async function planCommand(id, options = {}) {
|
|
|
2807
2807
|
};
|
|
2808
2808
|
} else if (trackerType === "rally") {
|
|
2809
2809
|
spinner.text = "Fetching issue from Rally...";
|
|
2810
|
-
const { createTracker } = await import("../factory-
|
|
2810
|
+
const { createTracker } = await import("../factory-BR48tuUR.js");
|
|
2811
2811
|
const { resolveProjectFromIssue } = await import("../projects-DhU7rAVN.js");
|
|
2812
2812
|
const project = resolveProjectFromIssue(id);
|
|
2813
2813
|
const rallyProject = project ? (await import("../projects-DhU7rAVN.js")).getProject(project.projectKey)?.rally_project : void 0;
|
|
@@ -4575,7 +4575,7 @@ async function reopenCommand(id, options = {}) {
|
|
|
4575
4575
|
console.log(chalk.green(`✓ ${issue.identifier} reopened and ready for re-work`));
|
|
4576
4576
|
console.log("");
|
|
4577
4577
|
try {
|
|
4578
|
-
const { getAgentState } = await import("../agents-
|
|
4578
|
+
const { getAgentState } = await import("../agents-BQOqo27C.js");
|
|
4579
4579
|
const agentState = getAgentState(`agent-${id.toLowerCase()}`);
|
|
4580
4580
|
if (agentState?.status === "running" || agentState?.status === "starting") {
|
|
4581
4581
|
console.log(chalk.dim("Agent is still running. Send it context about the re-work:"));
|
|
@@ -6004,7 +6004,7 @@ async function verifyBranchMerged(ctx) {
|
|
|
6004
6004
|
const branchName = `feature/${ctx.issueId.toLowerCase()}`;
|
|
6005
6005
|
try {
|
|
6006
6006
|
try {
|
|
6007
|
-
const { loadReviewStatuses } = await import("../review-status-
|
|
6007
|
+
const { loadReviewStatuses } = await import("../review-status-Chxzuwn2.js");
|
|
6008
6008
|
if (loadReviewStatuses()[ctx.issueId.toUpperCase()]?.mergeStatus === "merged") return stepOk(step, ["Merge specialist confirmed merge completed"]);
|
|
6009
6009
|
} catch {}
|
|
6010
6010
|
const { stdout: branchExists } = await execAsync$15(`git branch --list "${branchName}" 2>/dev/null || true`, {
|
|
@@ -6069,7 +6069,7 @@ async function verifyBranchMerged(ctx) {
|
|
|
6069
6069
|
async function clearReviewStatusStep(issueId) {
|
|
6070
6070
|
const step = "clear-review-status";
|
|
6071
6071
|
try {
|
|
6072
|
-
const { clearReviewStatus } = await import("../review-status-
|
|
6072
|
+
const { clearReviewStatus } = await import("../review-status-Chxzuwn2.js");
|
|
6073
6073
|
clearReviewStatus(issueId.toUpperCase());
|
|
6074
6074
|
return stepOk(step, ["Review status cleared"]);
|
|
6075
6075
|
} catch {
|
|
@@ -10423,7 +10423,7 @@ async function checkOrphanedReviewStatuses() {
|
|
|
10423
10423
|
const { resolveProjectFromIssue } = await import("../projects-DhU7rAVN.js");
|
|
10424
10424
|
const resolved = resolveProjectFromIssue(issueId);
|
|
10425
10425
|
if (resolved) {
|
|
10426
|
-
const { spawnEphemeralSpecialist } = await import("../specialists-
|
|
10426
|
+
const { spawnEphemeralSpecialist } = await import("../specialists-DyB4IRlM.js");
|
|
10427
10427
|
const result = await spawnEphemeralSpecialist(resolved.projectKey, "review-agent", {
|
|
10428
10428
|
issueId,
|
|
10429
10429
|
workspace,
|
|
@@ -10436,7 +10436,7 @@ async function checkOrphanedReviewStatuses() {
|
|
|
10436
10436
|
actions.push(`Re-dispatched pending review for ${issueId} via ${resolved.projectKey}/review-agent (deacon-orphan-recovery)`);
|
|
10437
10437
|
console.log(`[deacon] Re-dispatched review for ${issueId} after orphan/pending detection (project: ${resolved.projectKey})`);
|
|
10438
10438
|
} else if (result.error === "specialist_busy") {
|
|
10439
|
-
const { submitToSpecialistQueue } = await import("../specialists-
|
|
10439
|
+
const { submitToSpecialistQueue } = await import("../specialists-DyB4IRlM.js");
|
|
10440
10440
|
submitToSpecialistQueue("review-agent", {
|
|
10441
10441
|
priority: "high",
|
|
10442
10442
|
source: "deacon-orphan-recovery",
|
|
@@ -10477,7 +10477,7 @@ async function checkOrphanedReviewStatuses() {
|
|
|
10477
10477
|
const { resolveProjectFromIssue } = await import("../projects-DhU7rAVN.js");
|
|
10478
10478
|
const resolved = resolveProjectFromIssue(issueId);
|
|
10479
10479
|
if (resolved) {
|
|
10480
|
-
const { spawnEphemeralSpecialist } = await import("../specialists-
|
|
10480
|
+
const { spawnEphemeralSpecialist } = await import("../specialists-DyB4IRlM.js");
|
|
10481
10481
|
const result = await spawnEphemeralSpecialist(resolved.projectKey, "test-agent", {
|
|
10482
10482
|
issueId,
|
|
10483
10483
|
workspace,
|
|
@@ -10489,7 +10489,7 @@ async function checkOrphanedReviewStatuses() {
|
|
|
10489
10489
|
actions.push(`Re-dispatched orphaned test for ${issueId} via ${resolved.projectKey}/test-agent (deacon-orphan-recovery)`);
|
|
10490
10490
|
console.log(`[deacon] Re-dispatched test for ${issueId} after orphan detection (project: ${resolved.projectKey})`);
|
|
10491
10491
|
} else if (result.error === "specialist_busy") {
|
|
10492
|
-
const { submitToSpecialistQueue } = await import("../specialists-
|
|
10492
|
+
const { submitToSpecialistQueue } = await import("../specialists-DyB4IRlM.js");
|
|
10493
10493
|
submitToSpecialistQueue("test-agent", {
|
|
10494
10494
|
priority: "high",
|
|
10495
10495
|
source: "deacon-orphan-recovery",
|
|
@@ -10858,8 +10858,8 @@ async function patrolWorkAgentResolutions() {
|
|
|
10858
10858
|
async function checkSpecialistQueues() {
|
|
10859
10859
|
const actions = [];
|
|
10860
10860
|
try {
|
|
10861
|
-
const { checkSpecialistQueue, spawnEphemeralSpecialist, getTmuxSessionName, isRunning } = await import("../specialists-
|
|
10862
|
-
const { getAgentRuntimeState } = await import("../agents-
|
|
10861
|
+
const { checkSpecialistQueue, spawnEphemeralSpecialist, getTmuxSessionName, isRunning } = await import("../specialists-DyB4IRlM.js");
|
|
10862
|
+
const { getAgentRuntimeState } = await import("../agents-BQOqo27C.js");
|
|
10863
10863
|
const { resolveProjectFromIssue } = await import("../projects-DhU7rAVN.js");
|
|
10864
10864
|
for (const specialistType of [
|
|
10865
10865
|
"review-agent",
|
|
@@ -11040,7 +11040,7 @@ async function runPatrol() {
|
|
|
11040
11040
|
statuses[issueId].mergeStatus = "merged";
|
|
11041
11041
|
statuses[issueId].readyForMerge = false;
|
|
11042
11042
|
writeFileSync(REVIEW_STATUS_FILE$1, JSON.stringify(statuses, null, 2), "utf-8");
|
|
11043
|
-
const { postMergeLifecycle } = await import("../merge-agent-
|
|
11043
|
+
const { postMergeLifecycle } = await import("../merge-agent-BBwHwpn2.js");
|
|
11044
11044
|
postMergeLifecycle(issueId, resolved.projectPath).catch((err) => console.warn(`[deacon] postMergeLifecycle failed for ${issueId}: ${err}`));
|
|
11045
11045
|
actions.push(`Auto-completed stale merge for ${issueId}`);
|
|
11046
11046
|
} else {
|
|
@@ -11298,7 +11298,7 @@ var CloisterService = class {
|
|
|
11298
11298
|
}
|
|
11299
11299
|
try {
|
|
11300
11300
|
if (existsSync(AGENTS_DIR)) {
|
|
11301
|
-
const { isRunning: isSpecialistRunning } = await import("../specialists-
|
|
11301
|
+
const { isRunning: isSpecialistRunning } = await import("../specialists-DyB4IRlM.js");
|
|
11302
11302
|
const specialistPattern = /^specialist-(.+)-(review-agent|test-agent|merge-agent)$/;
|
|
11303
11303
|
const entries = readdirSync(AGENTS_DIR, { withFileTypes: true });
|
|
11304
11304
|
for (const entry of entries) {
|
|
@@ -11330,7 +11330,7 @@ var CloisterService = class {
|
|
|
11330
11330
|
try {
|
|
11331
11331
|
const reviewStatuses = loadReviewStatuses();
|
|
11332
11332
|
const { resolveProjectFromIssue } = await import("../projects-DhU7rAVN.js");
|
|
11333
|
-
const { submitToSpecialistQueue, getTmuxSessionName, getAllProjectSpecialistStatuses } = await import("../specialists-
|
|
11333
|
+
const { submitToSpecialistQueue, getTmuxSessionName, getAllProjectSpecialistStatuses } = await import("../specialists-DyB4IRlM.js");
|
|
11334
11334
|
const activeReviewIssues = /* @__PURE__ */ new Set();
|
|
11335
11335
|
try {
|
|
11336
11336
|
const projSpecs = await getAllProjectSpecialistStatuses();
|
|
@@ -11554,7 +11554,7 @@ var CloisterService = class {
|
|
|
11554
11554
|
const retryCount = this.processedCompletions.get(dir.name) || 0;
|
|
11555
11555
|
if (retryCount >= 3) continue;
|
|
11556
11556
|
const issueId = dir.name.replace("agent-", "").toUpperCase();
|
|
11557
|
-
const { getReviewStatus } = await import("../review-status-
|
|
11557
|
+
const { getReviewStatus } = await import("../review-status-Chxzuwn2.js");
|
|
11558
11558
|
const existingReview = getReviewStatus(issueId);
|
|
11559
11559
|
if (existingReview && ["reviewing", "passed"].includes(existingReview.reviewStatus || "")) {
|
|
11560
11560
|
console.log(`🔔 Cloister: Completion marker for ${issueId} — review already ${existingReview.reviewStatus}, marking processed`);
|
|
@@ -13015,7 +13015,7 @@ const execAsync$6 = promisify(exec);
|
|
|
13015
13015
|
*/
|
|
13016
13016
|
async function listLogsCommand(project, type, options) {
|
|
13017
13017
|
try {
|
|
13018
|
-
const { listRunLogs } = await import("../specialist-logs-
|
|
13018
|
+
const { listRunLogs } = await import("../specialist-logs-DDyY4xqo.js");
|
|
13019
13019
|
const runs = listRunLogs(project, type, { limit: options.limit ? parseInt(options.limit) : 10 });
|
|
13020
13020
|
if (options.json) {
|
|
13021
13021
|
console.log(JSON.stringify(runs, null, 2));
|
|
@@ -13054,7 +13054,7 @@ async function listLogsCommand(project, type, options) {
|
|
|
13054
13054
|
*/
|
|
13055
13055
|
async function viewLogCommand(project, type, runId, options) {
|
|
13056
13056
|
try {
|
|
13057
|
-
const { getRunLog, parseLogMetadata, getRunLogPath } = await import("../specialist-logs-
|
|
13057
|
+
const { getRunLog, parseLogMetadata, getRunLogPath } = await import("../specialist-logs-DDyY4xqo.js");
|
|
13058
13058
|
const content = getRunLog(project, type, runId);
|
|
13059
13059
|
if (!content) {
|
|
13060
13060
|
console.error(`❌ Run log not found: ${runId}`);
|
|
@@ -13085,8 +13085,8 @@ async function viewLogCommand(project, type, runId, options) {
|
|
|
13085
13085
|
*/
|
|
13086
13086
|
async function tailLogCommand(project, type) {
|
|
13087
13087
|
try {
|
|
13088
|
-
const { getRunLogPath } = await import("../specialist-logs-
|
|
13089
|
-
const { getProjectSpecialistMetadata } = await import("../specialists-
|
|
13088
|
+
const { getRunLogPath } = await import("../specialist-logs-DDyY4xqo.js");
|
|
13089
|
+
const { getProjectSpecialistMetadata } = await import("../specialists-DyB4IRlM.js");
|
|
13090
13090
|
const metadata = getProjectSpecialistMetadata(project, type);
|
|
13091
13091
|
if (!metadata.currentRun) {
|
|
13092
13092
|
console.error(`❌ No active run for ${project}/${type}`);
|
|
@@ -13153,7 +13153,7 @@ async function cleanupLogsCommand(projectOrAll, type, options) {
|
|
|
13153
13153
|
console.log(" Use --force to confirm.");
|
|
13154
13154
|
process.exit(1);
|
|
13155
13155
|
}
|
|
13156
|
-
const { cleanupAllLogs } = await import("../specialist-logs-
|
|
13156
|
+
const { cleanupAllLogs } = await import("../specialist-logs-DDyY4xqo.js");
|
|
13157
13157
|
console.log("🧹 Cleaning up old logs for all projects...\n");
|
|
13158
13158
|
const results = cleanupAllLogs();
|
|
13159
13159
|
console.log(`\n✅ Cleanup complete: deleted ${results.totalDeleted} old logs\n`);
|
|
@@ -13174,7 +13174,7 @@ async function cleanupLogsCommand(projectOrAll, type, options) {
|
|
|
13174
13174
|
console.log(" Use --force to confirm.");
|
|
13175
13175
|
process.exit(1);
|
|
13176
13176
|
}
|
|
13177
|
-
const { cleanupOldLogs } = await import("../specialist-logs-
|
|
13177
|
+
const { cleanupOldLogs } = await import("../specialist-logs-DDyY4xqo.js");
|
|
13178
13178
|
const { getSpecialistRetention } = await import("../projects-DhU7rAVN.js");
|
|
13179
13179
|
const retention = getSpecialistRetention(projectOrAll);
|
|
13180
13180
|
console.log(`🧹 Cleaning up old logs for ${projectOrAll}/${type}...`);
|
|
@@ -16139,7 +16139,7 @@ program.command("up").description("Start dashboard (and Traefik if enabled)").op
|
|
|
16139
16139
|
const origWrite = process.stdout.write;
|
|
16140
16140
|
const origErrWrite = process.stderr.write;
|
|
16141
16141
|
try {
|
|
16142
|
-
const { syncCommand } = await import("../sync-
|
|
16142
|
+
const { syncCommand } = await import("../sync-CLVqiGl4.js");
|
|
16143
16143
|
process.stdout.write = () => true;
|
|
16144
16144
|
process.stderr.write = () => true;
|
|
16145
16145
|
await syncCommand({});
|
|
@@ -311,6 +311,27 @@ var init_model_capabilities = __esmMin((() => {
|
|
|
311
311
|
},
|
|
312
312
|
notes: "Fast and affordable. Good for quick iterations and exploration."
|
|
313
313
|
},
|
|
314
|
+
"glm-5.1": {
|
|
315
|
+
model: "glm-5.1",
|
|
316
|
+
provider: "zai",
|
|
317
|
+
displayName: "GLM 5.1",
|
|
318
|
+
costPer1MTokens: 8,
|
|
319
|
+
contextWindow: 256e3,
|
|
320
|
+
skills: {
|
|
321
|
+
"code-generation": 95,
|
|
322
|
+
"code-review": 92,
|
|
323
|
+
debugging: 90,
|
|
324
|
+
planning: 88,
|
|
325
|
+
documentation: 88,
|
|
326
|
+
testing: 88,
|
|
327
|
+
security: 80,
|
|
328
|
+
performance: 85,
|
|
329
|
+
synthesis: 90,
|
|
330
|
+
speed: 78,
|
|
331
|
+
"context-length": 98
|
|
332
|
+
},
|
|
333
|
+
notes: "Flagship Z.AI model. 256K context, top-tier reasoning."
|
|
334
|
+
},
|
|
314
335
|
"kimi-k2": {
|
|
315
336
|
model: "kimi-k2",
|
|
316
337
|
provider: "kimi",
|
|
@@ -725,4 +746,4 @@ var init_config_yaml = __esmMin((() => {
|
|
|
725
746
|
//#endregion
|
|
726
747
|
export { getModelCapability as a, MODEL_CAPABILITIES as i, init_config_yaml as n, init_model_capabilities as o, loadConfig as r, hasGlobalConfig as t };
|
|
727
748
|
|
|
728
|
-
//# sourceMappingURL=config-yaml-
|
|
749
|
+
//# sourceMappingURL=config-yaml-BHD2Qdd8.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-yaml-BHD2Qdd8.js","names":[],"sources":["../src/lib/model-capabilities.ts","../src/lib/config-yaml.ts"],"sourcesContent":["/**\n * Model Capability Matrix\n *\n * Defines capability scores for each model across different skill dimensions.\n * This enables intelligent model selection based on what the user has enabled\n * rather than static presets.\n *\n * Scores: 0-100 where 100 = best in class\n * Cost: $/1M tokens (input + output average)\n *\n * Last updated: 2026-01-29\n * Sources:\n * - SWE-bench Verified leaderboard (vals.ai)\n * - LiveCodeBench v6\n * - LMSYS Chatbot Arena\n * - Artificial Analysis\n * - Official provider pricing pages\n */\n\nimport { ModelId } from './settings.js';\n\n/**\n * Model ID deprecation mapping\n *\n * Maps deprecated model IDs to their current replacements.\n * When a model ID changes (e.g., claude-opus-4-5 → claude-opus-4-6),\n * add the mapping here to enable automatic migration.\n *\n * Strategy: Single-hop only. When a newer version arrives (e.g., 4-7),\n * add both old→new mappings (4-5→4-7 and 4-6→4-7).\n */\nexport const MODEL_DEPRECATIONS: Record<string, ModelId> = {\n 'claude-opus-4-5': 'claude-opus-4-6',\n 'claude-sonnet-4-5': 'claude-sonnet-4-6',\n};\n\n/**\n * Resolve a model ID to its current version\n *\n * If the model ID is deprecated, returns the replacement.\n * Otherwise, returns the model ID unchanged.\n *\n * @param modelId - Model ID to resolve (may be deprecated)\n * @returns Current model ID\n */\nexport function resolveModelId(modelId: string): ModelId {\n return (MODEL_DEPRECATIONS[modelId] as ModelId) || (modelId as ModelId);\n}\n\n/**\n * Skill dimensions that models are evaluated on\n */\nexport type SkillDimension =\n | 'code-generation' // Writing new code\n | 'code-review' // Finding issues in code\n | 'debugging' // Root cause analysis\n | 'planning' // Architecture and strategy\n | 'documentation' // Writing docs, PRDs\n | 'testing' // Test generation and analysis\n | 'security' // Security analysis\n | 'performance' // Performance optimization\n | 'synthesis' // Combining information\n | 'speed' // Response latency\n | 'context-length'; // Max context window\n\n/**\n * Capability profile for a single model\n */\nexport interface ModelCapability {\n /** Model identifier */\n model: ModelId;\n /** Provider for this model */\n provider: 'anthropic' | 'openai' | 'google' | 'zai' | 'kimi' | 'minimax' | 'openrouter';\n /** Display name */\n displayName: string;\n /** Cost per 1M tokens (average of input/output) in USD */\n costPer1MTokens: number;\n /** Capability scores (0-100) for each skill dimension */\n skills: Record<SkillDimension, number>;\n /** Context window size in tokens */\n contextWindow: number;\n /** Additional notes about this model's strengths */\n notes?: string;\n}\n\n/**\n * Master capability database\n *\n * Scores are based on:\n * - Public benchmarks (HumanEval, SWE-bench, MBPP)\n * - Community consensus\n * - Practical experience\n *\n * These are baseline scores - run Kimi 2.5 research to refine.\n */\nexport const MODEL_CAPABILITIES: Record<ModelId, ModelCapability> = {\n // ═══════════════════════════════════════════════════════════════════════════\n // ANTHROPIC MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'claude-opus-4-6': {\n model: 'claude-opus-4-6',\n provider: 'anthropic',\n displayName: 'Claude Opus 4.6',\n costPer1MTokens: 45.0, // $5 in / $25 out → same pricing as 4.5\n contextWindow: 200000, // 1M available via opt-in beta, but we use 200K\n skills: {\n 'code-generation': 96, // 80.9% SWE-bench (first >80%), 89.4% Aider Polyglot\n 'code-review': 98,\n debugging: 97,\n planning: 99, // User confirms: \"Opus 4.6 planning for sure\"\n documentation: 95,\n testing: 92,\n security: 98, // Best for security review\n performance: 90,\n synthesis: 98, // Best for combining info across domains\n speed: 40, // Slower but 76% more token efficient\n 'context-length': 95,\n },\n notes: 'Successor to Opus 4.5. Same pricing, 1M context available (opt-in beta). Best for planning, security, complex reasoning.',\n },\n\n 'claude-sonnet-4-6': {\n model: 'claude-sonnet-4-6',\n provider: 'anthropic',\n displayName: 'Claude Sonnet 4.6',\n costPer1MTokens: 9.0, // $3 in / $15 out → avg ~$9\n contextWindow: 200000,\n skills: {\n 'code-generation': 94,\n 'code-review': 94,\n debugging: 92,\n planning: 90,\n documentation: 92,\n testing: 92,\n security: 88,\n performance: 88,\n synthesis: 90,\n speed: 70,\n 'context-length': 95,\n },\n notes: 'Successor to Sonnet 4.5. Same pricing tier. Improved coding and reasoning.',\n },\n\n 'claude-sonnet-4-5': {\n model: 'claude-sonnet-4-5',\n provider: 'anthropic',\n displayName: 'Claude Sonnet 4.5',\n costPer1MTokens: 9.0, // $3 in / $15 out → avg ~$9\n contextWindow: 200000,\n skills: {\n 'code-generation': 92, // 77.2% SWE-bench (82% parallel), beats GPT-5 Codex (74.5%)\n 'code-review': 92,\n debugging: 90,\n planning: 88,\n documentation: 90, // 100% AIME with Python\n testing: 90, // 50% Terminal-Bench, 61.4% OSWorld\n security: 85,\n performance: 85,\n synthesis: 88,\n speed: 70,\n 'context-length': 95,\n },\n notes: 'Best value: 77.2% SWE-bench at 1/5th Opus cost. Beats GPT-5 Codex.',\n },\n\n 'claude-haiku-4-5': {\n model: 'claude-haiku-4-5',\n provider: 'anthropic',\n displayName: 'Claude Haiku 4.5',\n costPer1MTokens: 4.0, // $0.80 in / $4 out → avg ~$2.4\n contextWindow: 200000,\n skills: {\n 'code-generation': 75,\n 'code-review': 72,\n debugging: 70,\n planning: 65,\n documentation: 75,\n testing: 70,\n security: 60,\n performance: 65,\n synthesis: 68,\n speed: 95, // Fastest Anthropic\n 'context-length': 95,\n },\n notes: 'Fast and cheap, good for simple tasks and exploration',\n },\n\n // ═══════════════════════════════════════════════════════════════════════════\n // OPENAI MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'gpt-5.2-codex': {\n model: 'gpt-5.2-codex',\n provider: 'openai',\n displayName: 'GPT-5.2 Codex',\n costPer1MTokens: 75.0, // Premium tier ~$75/M\n contextWindow: 128000,\n skills: {\n 'code-generation': 95, // 80% SWE-bench Verified, 55.6% SWE-bench Pro\n 'code-review': 90,\n debugging: 92, // 92.4% GPQA Diamond\n planning: 88,\n documentation: 85,\n testing: 90,\n security: 85,\n performance: 88, // 52.9% ARC-AGI-2 (best reasoning)\n synthesis: 88, // 100% AIME 2025 without tools\n speed: 55,\n 'context-length': 75,\n },\n notes: 'Premium coding: 80% SWE-bench. Best raw reasoning (52.9% ARC-AGI-2). Expensive.',\n },\n\n 'o3-deep-research': {\n model: 'o3-deep-research',\n provider: 'openai',\n displayName: 'O3 Deep Research',\n costPer1MTokens: 100.0, // Expensive reasoning model\n contextWindow: 200000,\n skills: {\n 'code-generation': 85,\n 'code-review': 95,\n debugging: 98, // Best for debugging\n planning: 95,\n documentation: 88,\n testing: 85,\n security: 92,\n performance: 92,\n synthesis: 95,\n speed: 20, // Very slow (reasoning chains)\n 'context-length': 95,\n },\n notes: 'Deep reasoning model, excellent for complex debugging and analysis',\n },\n\n 'gpt-4o': {\n model: 'gpt-4o',\n provider: 'openai',\n displayName: 'GPT-4o',\n costPer1MTokens: 15.0, // $5 in / $15 out\n contextWindow: 128000,\n skills: {\n 'code-generation': 88,\n 'code-review': 85,\n debugging: 85,\n planning: 82,\n documentation: 88,\n testing: 82,\n security: 78,\n performance: 80,\n synthesis: 85,\n speed: 75,\n 'context-length': 75,\n },\n notes: 'Good all-rounder, competitive with Sonnet',\n },\n\n 'gpt-4o-mini': {\n model: 'gpt-4o-mini',\n provider: 'openai',\n displayName: 'GPT-4o Mini',\n costPer1MTokens: 1.0, // Very cheap\n contextWindow: 128000,\n skills: {\n 'code-generation': 72,\n 'code-review': 68,\n debugging: 65,\n planning: 60,\n documentation: 70,\n testing: 65,\n security: 55,\n performance: 60,\n synthesis: 62,\n speed: 92,\n 'context-length': 75,\n },\n notes: 'Budget option, good for simple tasks',\n },\n\n // ═══════════════════════════════════════════════════════════════════════════\n // GOOGLE MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'gemini-3-pro-preview': {\n model: 'gemini-3-pro-preview',\n provider: 'google',\n displayName: 'Gemini 3 Pro',\n costPer1MTokens: 12.0, // $4.2 in / $18.9 out\n contextWindow: 1000000, // 1M context!\n skills: {\n 'code-generation': 90, // 2439 Elo LiveCodeBench Pro (first >1500 on LMArena)\n 'code-review': 88,\n debugging: 85,\n planning: 85,\n documentation: 88,\n testing: 85, // ~95% AIME 2025\n security: 78,\n performance: 85, // Strong multimodal\n synthesis: 90, // Best for combining large codebases\n speed: 80,\n 'context-length': 100, // Best context - 1M tokens\n },\n notes: 'First to exceed 1500 Elo on LMArena. Best for large codebase analysis with 1M context.',\n },\n\n 'gemini-3-flash-preview': {\n model: 'gemini-3-flash-preview',\n provider: 'google',\n displayName: 'Gemini 3 Flash',\n costPer1MTokens: 0.5, // Very cheap\n contextWindow: 1000000,\n skills: {\n 'code-generation': 75,\n 'code-review': 70,\n debugging: 68,\n planning: 62,\n documentation: 72,\n testing: 68,\n security: 55,\n performance: 65,\n synthesis: 70,\n speed: 98, // Fastest overall\n 'context-length': 100,\n },\n notes: 'Extremely fast and cheap, huge context, great for exploration',\n },\n\n 'gemini-2.5-pro': {\n model: 'gemini-2.5-pro',\n provider: 'google',\n displayName: 'Gemini 2.5 Pro',\n costPer1MTokens: 12.0,\n contextWindow: 1000000,\n skills: {\n 'code-generation': 92,\n 'code-review': 90,\n debugging: 88,\n planning: 88,\n documentation: 90,\n testing: 87,\n security: 82,\n performance: 88,\n synthesis: 92,\n speed: 75,\n 'context-length': 100,\n },\n notes: 'Advanced reasoning and code capabilities with 1M context',\n },\n\n 'gemini-2.5-flash': {\n model: 'gemini-2.5-flash',\n provider: 'google',\n displayName: 'Gemini 2.5 Flash',\n costPer1MTokens: 0.6,\n contextWindow: 1000000,\n skills: {\n 'code-generation': 78,\n 'code-review': 73,\n debugging: 70,\n planning: 65,\n documentation: 75,\n testing: 70,\n security: 58,\n performance: 68,\n synthesis: 73,\n speed: 95,\n 'context-length': 100,\n },\n notes: 'Fast and efficient with large context support',\n },\n\n // ═══════════════════════════════════════════════════════════════════════════\n // Z.AI MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'glm-4.7': {\n model: 'glm-4.7',\n provider: 'zai',\n displayName: 'GLM 4.7',\n costPer1MTokens: 5.0,\n contextWindow: 200000, // 200K context, 128K output\n skills: {\n 'code-generation': 88, // 73.8% SWE-bench, 84.9 LiveCodeBench v6 (open-source SOTA)\n 'code-review': 85,\n debugging: 85, // Strong debugging with Interleaved Thinking\n planning: 82, // 95.7% AIME 2025 (beats Gemini 3 & GPT-5.1)\n documentation: 80,\n testing: 82, // 87.4 τ²-Bench (SOTA for tool use)\n security: 72,\n performance: 78,\n synthesis: 85, // Preserved Thinking retains context across turns\n speed: 80,\n 'context-length': 95, // 200K context\n },\n notes: 'Top open-source for agentic coding. 73.8% SWE-bench, best tool use. 400B params with Interleaved Thinking.',\n },\n\n 'glm-4.7-flash': {\n model: 'glm-4.7-flash',\n provider: 'zai',\n displayName: 'GLM 4.7 Flash',\n costPer1MTokens: 1.5,\n contextWindow: 128000,\n skills: {\n 'code-generation': 72,\n 'code-review': 68,\n debugging: 65,\n planning: 62,\n documentation: 70,\n testing: 65,\n security: 55,\n performance: 62,\n synthesis: 65,\n speed: 92, // Fast inference\n 'context-length': 75,\n },\n notes: 'Fast and affordable. Good for quick iterations and exploration.',\n },\n\n 'glm-5.1': {\n model: 'glm-5.1',\n provider: 'zai',\n displayName: 'GLM 5.1',\n costPer1MTokens: 8.0,\n contextWindow: 256000,\n skills: {\n 'code-generation': 95,\n 'code-review': 92,\n debugging: 90,\n planning: 88,\n documentation: 88,\n testing: 88,\n security: 80,\n performance: 85,\n synthesis: 90,\n speed: 78,\n 'context-length': 98,\n },\n notes: 'Flagship Z.AI model. 256K context, top-tier reasoning.',\n },\n\n // ═══════════════════════════════════════════════════════════════════════════\n // KIMI MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'kimi-k2': {\n model: 'kimi-k2',\n provider: 'kimi',\n displayName: 'Kimi K2',\n costPer1MTokens: 1.4, // $0.16 in / $2.63 out → very cheap\n contextWindow: 131000,\n skills: {\n 'code-generation': 82, // 65.8% SWE-bench (beats GPT-4.1 at 54.6%)\n 'code-review': 80,\n debugging: 78,\n planning: 75,\n documentation: 80,\n testing: 75,\n security: 70,\n performance: 72,\n synthesis: 78,\n speed: 80,\n 'context-length': 75,\n },\n notes: 'Strong value: 65.8% SWE-bench at very low cost. Good for routine tasks.',\n },\n\n 'kimi-k2.5': {\n model: 'kimi-k2.5',\n provider: 'kimi',\n displayName: 'Kimi K2.5',\n costPer1MTokens: 8.0, // ~5.1x cheaper than GPT-5.2\n contextWindow: 256000,\n skills: {\n 'code-generation': 92, // 76.8% SWE-bench, 85 LiveCodeBench v6\n 'code-review': 90,\n debugging: 90, // Strong analytical capabilities\n planning: 88, // User confirms \"highly capable\"\n documentation: 88,\n testing: 88, // 92% coding accuracy\n security: 82,\n performance: 85,\n synthesis: 92, // Can coordinate 100 sub-agents, 1500 tool calls\n speed: 75, // MoE: 1T total params, 32B active\n 'context-length': 98, // 256K context\n },\n notes: 'Best open-source coding model. 5x cheaper than GPT-5.2. Excellent for frontend dev and multi-agent orchestration.',\n },\n\n // ═══════════════════════════════════════════════════════════════════════════\n // MINIMAX MODELS\n // ═══════════════════════════════════════════════════════════════════════════\n\n 'minimax-m2.7': {\n model: 'minimax-m2.7',\n provider: 'minimax',\n displayName: 'MiniMax M2.7',\n costPer1MTokens: 1.5, // $0.30/M in + $1.20/M out, blended ~$0.06/M with auto-cache\n contextWindow: 204800,\n skills: {\n 'code-generation': 90, // 56.22% SWE-Pro (Opus ~57-58%), 55.6% VIBE-Pro\n 'code-review': 88,\n debugging: 88, // 57.0% Terminal Bench 2\n planning: 85,\n documentation: 85,\n testing: 86,\n security: 80,\n performance: 82,\n synthesis: 90, // Self-evolving agent, 97% skill adherence on complex tasks\n speed: 80, // 10B active params (MoE)\n 'context-length': 92, // 204K context\n },\n notes: '10B active params, 56.22% SWE-Pro, 1495 ELO GDPval-AA. $0.06/M blended with auto-cache.',\n },\n\n 'minimax-m2.7-highspeed': {\n model: 'minimax-m2.7-highspeed',\n provider: 'minimax',\n displayName: 'MiniMax M2.7 Highspeed',\n costPer1MTokens: 1.5, // Same pricing as M2.7\n contextWindow: 204800,\n skills: {\n 'code-generation': 90,\n 'code-review': 88,\n debugging: 88,\n planning: 85,\n documentation: 85,\n testing: 86,\n security: 80,\n performance: 82,\n synthesis: 90,\n speed: 92, // 100 tps, 3x faster than Opus\n 'context-length': 92,\n },\n notes: 'Identical quality to M2.7, 100 tps (3x Opus speed). Best for high-throughput agent work.',\n },\n};\n\n/**\n * Get capability profile for a model\n */\nexport function getModelCapability(model: ModelId): ModelCapability {\n return MODEL_CAPABILITIES[model];\n}\n\n/**\n * Get all models sorted by a specific skill (descending)\n */\nexport function getModelsBySkill(skill: SkillDimension): ModelId[] {\n return (Object.keys(MODEL_CAPABILITIES) as ModelId[]).sort(\n (a, b) => MODEL_CAPABILITIES[b].skills[skill] - MODEL_CAPABILITIES[a].skills[skill]\n );\n}\n\n/**\n * Get all models for a provider\n */\nexport function getModelsForProvider(\n provider: ModelCapability['provider']\n): ModelId[] {\n return (Object.keys(MODEL_CAPABILITIES) as ModelId[]).filter(\n (model) => MODEL_CAPABILITIES[model].provider === provider\n );\n}\n\n/**\n * Get cheapest models (sorted by cost ascending)\n */\nexport function getCheapestModels(): ModelId[] {\n return (Object.keys(MODEL_CAPABILITIES) as ModelId[]).sort(\n (a, b) => MODEL_CAPABILITIES[a].costPer1MTokens - MODEL_CAPABILITIES[b].costPer1MTokens\n );\n}\n\n/**\n * Calculate cost efficiency score for a skill\n * Higher = better value (skill score / cost)\n */\nexport function getValueScore(model: ModelId, skill: SkillDimension): number {\n const cap = MODEL_CAPABILITIES[model];\n return cap.skills[skill] / Math.log10(cap.costPer1MTokens + 1);\n}\n\n/**\n * Get all skill dimensions\n */\nexport function getAllSkillDimensions(): SkillDimension[] {\n return [\n 'code-generation',\n 'code-review',\n 'debugging',\n 'planning',\n 'documentation',\n 'testing',\n 'security',\n 'performance',\n 'synthesis',\n 'speed',\n 'context-length',\n ];\n}\n","/**\n * YAML Configuration Loader\n *\n * Loads and merges configuration from:\n * 1. Global config: ~/.panopticon/config.yaml\n * 2. Per-project config: .pan.yaml (project root, falls back to .panopticon.yaml with deprecation warning)\n *\n * Uses smart (capability-based) model selection - no legacy presets.\n */\n\nimport { readFileSync, existsSync, writeFileSync, copyFileSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport yaml from 'js-yaml';\nimport { WorkTypeId } from './work-types.js';\nimport { ModelId } from './settings.js';\nimport { ModelProvider } from './model-fallback.js';\nimport { MODEL_DEPRECATIONS, resolveModelId } from './model-capabilities.js';\n\n/**\n * Provider configuration (enable/disable + API keys)\n */\nexport interface ProviderConfig {\n /** Whether this provider is enabled */\n enabled: boolean;\n /** API key (optional, can use env var) */\n api_key?: string;\n}\n\n/**\n * Shadow mode configuration\n */\nexport interface ShadowConfig {\n /** Global shadow mode default */\n enabled?: boolean;\n\n /** Per-tracker overrides */\n trackers?: {\n linear?: boolean;\n github?: boolean;\n gitlab?: boolean;\n rally?: boolean;\n };\n}\n\n/**\n * Complete configuration structure (YAML schema)\n */\nexport interface YamlConfig {\n /** Model configuration */\n models?: {\n /** Provider enable/disable and API keys */\n providers?: {\n anthropic?: ProviderConfig | boolean;\n openai?: ProviderConfig | boolean;\n google?: ProviderConfig | boolean;\n zai?: ProviderConfig | boolean;\n kimi?: ProviderConfig | boolean;\n minimax?: ProviderConfig | boolean;\n openrouter?: ProviderConfig | boolean;\n };\n\n /** Per-work-type overrides (explicit model for specific tasks) */\n overrides?: Partial<Record<WorkTypeId, ModelId>>;\n\n /** Gemini thinking level (1-4) */\n gemini_thinking_level?: 1 | 2 | 3 | 4;\n };\n\n /** OpenRouter-specific configuration */\n openrouter?: {\n /** Favorite model IDs to show in ModelPicker */\n favorites?: string[];\n };\n\n /** Legacy API keys (for backward compatibility) */\n api_keys?: {\n openai?: string;\n google?: string;\n zai?: string;\n kimi?: string;\n minimax?: string;\n openrouter?: string;\n };\n\n /** Tracker API keys (override environment variables) */\n tracker_keys?: {\n linear?: string;\n github?: string;\n gitlab?: string;\n rally?: string;\n };\n\n /** Shadow mode configuration */\n shadow?: ShadowConfig;\n\n /** Multi-tool sync configuration */\n tools?: {\n /**\n * Additional AI tools to sync skills to.\n * Supported: 'cursor' | 'codex' | 'windsurf' | 'cline' | 'copilot' | 'aider'\n * Per-project .pan.yaml values merge additively with global config.\n */\n also_sync?: string[];\n };\n}\n\n/**\n * Normalized shadow configuration\n */\nexport interface NormalizedShadowConfig {\n /** Global shadow mode enabled */\n enabled: boolean;\n\n /** Per-tracker overrides */\n trackers: {\n linear: boolean;\n github: boolean;\n gitlab: boolean;\n rally: boolean;\n };\n}\n\n/**\n * Normalized configuration (after loading and merging)\n */\nexport interface NormalizedConfig {\n /** Enabled providers */\n enabledProviders: Set<ModelProvider>;\n\n /** API keys by provider */\n apiKeys: {\n openai?: string;\n google?: string;\n zai?: string;\n kimi?: string;\n minimax?: string;\n openrouter?: string;\n };\n\n /** OpenRouter favorite model IDs (shown in ModelPicker) */\n openrouterFavorites: string[];\n\n /** Per-work-type overrides */\n overrides: Partial<Record<WorkTypeId, ModelId>>;\n\n /** Gemini thinking level */\n geminiThinkingLevel: 1 | 2 | 3 | 4;\n\n /** Tracker API keys */\n trackerKeys: {\n linear?: string;\n github?: string;\n gitlab?: string;\n rally?: string;\n };\n\n /** Shadow mode configuration */\n shadow: NormalizedShadowConfig;\n}\n\n/**\n * Model ID migration result\n *\n * Returned when deprecated model IDs are automatically migrated\n * during config load.\n */\nexport interface MigrationResult {\n /** List of migrated model IDs */\n migrated: Array<{\n /** Work type that was migrated */\n workType: WorkTypeId;\n /** Old (deprecated) model ID */\n from: string;\n /** New (current) model ID */\n to: string;\n }>;\n /** Whether config.yaml was backed up before migration */\n backedUp: boolean;\n}\n\n/**\n * Config load result (config + optional migration info)\n */\nexport interface ConfigLoadResult {\n /** Normalized configuration */\n config: NormalizedConfig;\n /** Migration result (if any deprecated models were migrated) */\n migration?: MigrationResult;\n}\n\n/**\n * Default configuration (used when no config files exist)\n */\nconst DEFAULT_CONFIG: NormalizedConfig = {\n enabledProviders: new Set(['anthropic']), // Only Anthropic by default\n apiKeys: {},\n openrouterFavorites: [],\n overrides: {},\n geminiThinkingLevel: 3,\n trackerKeys: {},\n shadow: {\n enabled: false,\n trackers: {\n linear: false,\n github: false,\n gitlab: false,\n rally: false,\n },\n },\n};\n\n/**\n * Path to global config file\n */\nconst GLOBAL_CONFIG_PATH = join(homedir(), '.panopticon', 'config.yaml');\n\n/**\n * Normalize a provider config (handle both boolean and object forms)\n */\nfunction normalizeProviderConfig(\n providerConfig: ProviderConfig | boolean | undefined,\n fallbackKey?: string\n): { enabled: boolean; api_key?: string } {\n if (providerConfig === undefined) {\n return { enabled: false };\n }\n\n if (typeof providerConfig === 'boolean') {\n return { enabled: providerConfig, api_key: fallbackKey };\n }\n\n return {\n enabled: providerConfig.enabled,\n api_key: providerConfig.api_key || fallbackKey,\n };\n}\n\n/**\n * Resolve environment variables in config values.\n * If the env var is not set, returns the original reference (e.g., \"$OPENAI_API_KEY\")\n * so the UI can show that it's configured via env var but not resolved.\n */\nfunction resolveEnvVar(value: string | undefined): string | undefined {\n if (!value) return undefined;\n\n // Replace $VAR_NAME or ${VAR_NAME} with environment variable\n // If env var is not set, keep the original reference\n return value.replace(/\\$\\{?([A-Z_][A-Z0-9_]*)\\}?/g, (match, varName) => {\n const envValue = process.env[varName];\n return envValue !== undefined ? envValue : match; // Keep $VAR_NAME if not set\n });\n}\n\n/**\n * Load and parse a YAML config file\n */\nfunction loadYamlFile(filePath: string): YamlConfig | null {\n if (!existsSync(filePath)) {\n return null;\n }\n\n try {\n const content = readFileSync(filePath, 'utf-8');\n const parsed = yaml.load(content) as YamlConfig;\n return parsed || {};\n } catch (error) {\n console.error(`Error loading YAML config from ${filePath}:`, error);\n return null;\n }\n}\n\n/**\n * Find project root by looking for .git directory\n */\nfunction findProjectRoot(startDir: string = process.cwd()): string | null {\n let currentDir = startDir;\n\n while (currentDir !== '/') {\n if (existsSync(join(currentDir, '.git'))) {\n return currentDir;\n }\n currentDir = join(currentDir, '..');\n }\n\n return null;\n}\n\n/**\n * Load per-project config (.pan.yaml in project root, with fallback to .panopticon.yaml)\n */\nfunction loadProjectConfig(): YamlConfig | null {\n const projectRoot = findProjectRoot();\n if (!projectRoot) {\n return null;\n }\n\n const newConfigPath = join(projectRoot, '.pan.yaml');\n if (existsSync(newConfigPath)) {\n return loadYamlFile(newConfigPath);\n }\n\n const legacyConfigPath = join(projectRoot, '.panopticon.yaml');\n if (existsSync(legacyConfigPath)) {\n process.stderr.write(\n `[panopticon] Deprecation warning: .panopticon.yaml is deprecated. Rename it to .pan.yaml.\\n`\n );\n return loadYamlFile(legacyConfigPath);\n }\n\n return null;\n}\n\n/**\n * Load global config (~/.panopticon/config.yaml)\n */\nfunction loadGlobalConfig(): YamlConfig | null {\n return loadYamlFile(GLOBAL_CONFIG_PATH);\n}\n\n/**\n * Merge shadow configuration from multiple sources\n */\nfunction mergeShadowConfig(\n result: NormalizedShadowConfig,\n config: YamlConfig | null\n): void {\n if (!config?.shadow) return;\n\n // Merge global enabled flag\n if (config.shadow.enabled !== undefined) {\n result.enabled = config.shadow.enabled;\n }\n\n // Merge per-tracker overrides\n if (config.shadow.trackers) {\n if (config.shadow.trackers.linear !== undefined) {\n result.trackers.linear = config.shadow.trackers.linear;\n }\n if (config.shadow.trackers.github !== undefined) {\n result.trackers.github = config.shadow.trackers.github;\n }\n if (config.shadow.trackers.gitlab !== undefined) {\n result.trackers.gitlab = config.shadow.trackers.gitlab;\n }\n if (config.shadow.trackers.rally !== undefined) {\n result.trackers.rally = config.shadow.trackers.rally;\n }\n }\n}\n\n/**\n * Merge multiple configs with precedence: project > global > defaults\n */\nfunction mergeConfigs(...configs: (YamlConfig | null)[]): NormalizedConfig {\n const result: NormalizedConfig = {\n ...DEFAULT_CONFIG,\n enabledProviders: new Set(DEFAULT_CONFIG.enabledProviders),\n shadow: {\n enabled: DEFAULT_CONFIG.shadow.enabled,\n trackers: { ...DEFAULT_CONFIG.shadow.trackers },\n },\n };\n\n // Filter out null configs\n const validConfigs = configs.filter((c): c is YamlConfig => c !== null);\n\n // Merge in reverse order (lowest precedence first)\n for (const config of validConfigs.reverse()) {\n // Merge providers\n if (config.models?.providers) {\n const providers = config.models.providers;\n const legacyKeys = config.api_keys || {};\n\n // Anthropic\n const anthropic = normalizeProviderConfig(providers.anthropic);\n if (anthropic.enabled) {\n result.enabledProviders.add('anthropic');\n }\n\n // OpenAI\n const openai = normalizeProviderConfig(providers.openai, legacyKeys.openai);\n if (openai.enabled) {\n result.enabledProviders.add('openai');\n if (openai.api_key) {\n result.apiKeys.openai = resolveEnvVar(openai.api_key);\n }\n }\n\n // Google\n const google = normalizeProviderConfig(providers.google, legacyKeys.google);\n if (google.enabled) {\n result.enabledProviders.add('google');\n if (google.api_key) {\n result.apiKeys.google = resolveEnvVar(google.api_key);\n }\n }\n\n // Z.AI\n const zai = normalizeProviderConfig(providers.zai, legacyKeys.zai);\n if (zai.enabled) {\n result.enabledProviders.add('zai');\n if (zai.api_key) {\n result.apiKeys.zai = resolveEnvVar(zai.api_key);\n }\n }\n\n // Kimi\n const kimi = normalizeProviderConfig(providers.kimi, legacyKeys.kimi);\n if (kimi.enabled) {\n result.enabledProviders.add('kimi');\n if (kimi.api_key) {\n result.apiKeys.kimi = resolveEnvVar(kimi.api_key);\n }\n }\n\n // MiniMax\n const minimax = normalizeProviderConfig(providers.minimax, legacyKeys.minimax);\n if (minimax.enabled) {\n result.enabledProviders.add('minimax');\n if (minimax.api_key) {\n result.apiKeys.minimax = resolveEnvVar(minimax.api_key);\n }\n }\n\n // OpenRouter\n const openrouter = normalizeProviderConfig(providers.openrouter);\n if (openrouter.enabled) {\n result.enabledProviders.add('openrouter');\n if (openrouter.api_key) {\n result.apiKeys.openrouter = resolveEnvVar(openrouter.api_key);\n }\n }\n }\n\n // Merge OpenRouter favorites\n if (config.openrouter?.favorites) {\n result.openrouterFavorites = config.openrouter.favorites;\n }\n\n // Merge legacy API keys (for backward compatibility)\n // If a `models.providers` section exists, the enabled state is already set above — don't\n // override it here. Only auto-enable from API keys when there's no explicit providers config.\n if (config.api_keys) {\n const hasProvidersConfig = !!config.models?.providers;\n if (config.api_keys.openai) {\n result.apiKeys.openai = resolveEnvVar(config.api_keys.openai);\n if (!hasProvidersConfig) result.enabledProviders.add('openai');\n }\n if (config.api_keys.google) {\n result.apiKeys.google = resolveEnvVar(config.api_keys.google);\n if (!hasProvidersConfig) result.enabledProviders.add('google');\n }\n if (config.api_keys.zai) {\n result.apiKeys.zai = resolveEnvVar(config.api_keys.zai);\n if (!hasProvidersConfig) result.enabledProviders.add('zai');\n }\n if (config.api_keys.kimi) {\n result.apiKeys.kimi = resolveEnvVar(config.api_keys.kimi);\n if (!hasProvidersConfig) result.enabledProviders.add('kimi');\n }\n if (config.api_keys.minimax) {\n result.apiKeys.minimax = resolveEnvVar(config.api_keys.minimax);\n if (!hasProvidersConfig) result.enabledProviders.add('minimax');\n }\n if (config.api_keys.openrouter) {\n result.apiKeys.openrouter = resolveEnvVar(config.api_keys.openrouter);\n if (!hasProvidersConfig) result.enabledProviders.add('openrouter');\n }\n }\n\n // Merge overrides\n if (config.models?.overrides) {\n result.overrides = {\n ...result.overrides,\n ...config.models.overrides,\n };\n }\n\n // Merge Gemini thinking level\n if (config.models?.gemini_thinking_level) {\n result.geminiThinkingLevel = config.models.gemini_thinking_level;\n }\n\n // Merge tracker keys\n if (config.tracker_keys) {\n if (config.tracker_keys.linear) {\n result.trackerKeys.linear = resolveEnvVar(config.tracker_keys.linear);\n }\n if (config.tracker_keys.github) {\n result.trackerKeys.github = resolveEnvVar(config.tracker_keys.github);\n }\n if (config.tracker_keys.gitlab) {\n result.trackerKeys.gitlab = resolveEnvVar(config.tracker_keys.gitlab);\n }\n if (config.tracker_keys.rally) {\n result.trackerKeys.rally = resolveEnvVar(config.tracker_keys.rally);\n }\n }\n\n // Merge shadow configuration\n mergeShadowConfig(result.shadow, config);\n }\n\n return result;\n}\n\n/**\n * Detect deprecated model IDs in config overrides\n *\n * Returns array of migrations to perform, or empty array if none found.\n */\nfunction detectDeprecatedModels(config: YamlConfig | null): Array<{\n workType: WorkTypeId;\n from: string;\n to: string;\n}> {\n if (!config?.models?.overrides) {\n return [];\n }\n\n const migrations: Array<{ workType: WorkTypeId; from: string; to: string }> = [];\n\n for (const [workType, modelId] of Object.entries(config.models.overrides)) {\n if (modelId && MODEL_DEPRECATIONS[modelId]) {\n migrations.push({\n workType: workType as WorkTypeId,\n from: modelId,\n to: MODEL_DEPRECATIONS[modelId],\n });\n }\n }\n\n return migrations;\n}\n\n/**\n * Apply deprecation migrations to a YamlConfig (in-place)\n */\nfunction applyMigrations(\n config: YamlConfig,\n migrations: Array<{ workType: WorkTypeId; from: string; to: string }>\n): void {\n if (!config.models) {\n config.models = {};\n }\n if (!config.models.overrides) {\n config.models.overrides = {};\n }\n\n for (const { workType, to } of migrations) {\n config.models.overrides[workType] = to as ModelId;\n }\n}\n\n/**\n * Create backup of global config file\n */\nfunction backupGlobalConfig(): boolean {\n try {\n const backupPath = `${GLOBAL_CONFIG_PATH}.bak`;\n copyFileSync(GLOBAL_CONFIG_PATH, backupPath);\n console.log(`✓ Backed up config.yaml → config.yaml.bak`);\n return true;\n } catch (error) {\n console.error(`Failed to create config backup:`, error);\n return false;\n }\n}\n\n/**\n * Write YamlConfig back to global config file\n */\nfunction writeGlobalConfig(config: YamlConfig): void {\n const yamlContent = yaml.dump(config, {\n indent: 2,\n lineWidth: 100,\n noRefs: true,\n });\n\n writeFileSync(GLOBAL_CONFIG_PATH, yamlContent, 'utf-8');\n}\n\n/**\n * Load complete configuration (global + project + defaults)\n * Also loads API keys from environment variables as fallback\n *\n * IMPORTANT: This function may modify config.yaml if deprecated model IDs\n * are detected. A backup is created before any modifications.\n */\nexport function loadConfig(): ConfigLoadResult {\n let globalConfig = loadGlobalConfig();\n const projectConfig = loadProjectConfig();\n\n // Check for deprecated models in global config\n let migrationResult: MigrationResult | undefined;\n if (globalConfig && hasGlobalConfig()) {\n const migrations = detectDeprecatedModels(globalConfig);\n\n if (migrations.length > 0) {\n // Create backup\n const backedUp = backupGlobalConfig();\n\n // Apply migrations to global config\n applyMigrations(globalConfig, migrations);\n\n // Write migrated config back to disk\n writeGlobalConfig(globalConfig);\n\n // Log migrations\n console.log('\\n🔄 Model ID Migration:');\n for (const { workType, from, to } of migrations) {\n console.log(` ${workType}: ${from} → ${to}`);\n }\n console.log('');\n\n migrationResult = { migrated: migrations, backedUp };\n }\n }\n\n const config = mergeConfigs(projectConfig, globalConfig);\n\n // Load API keys from environment variables as fallback\n // This allows using ~/.panopticon.env for API keys\n if (process.env.OPENAI_API_KEY && !config.apiKeys.openai) {\n config.apiKeys.openai = process.env.OPENAI_API_KEY;\n config.enabledProviders.add('openai');\n }\n if (process.env.GOOGLE_API_KEY && !config.apiKeys.google) {\n config.apiKeys.google = process.env.GOOGLE_API_KEY;\n config.enabledProviders.add('google');\n }\n if (process.env.ZAI_API_KEY && !config.apiKeys.zai) {\n config.apiKeys.zai = process.env.ZAI_API_KEY;\n config.enabledProviders.add('zai');\n }\n if (process.env.KIMI_API_KEY && !config.apiKeys.kimi) {\n config.apiKeys.kimi = process.env.KIMI_API_KEY;\n config.enabledProviders.add('kimi');\n }\n if (process.env.OPENROUTER_API_KEY && !config.apiKeys.openrouter) {\n config.apiKeys.openrouter = process.env.OPENROUTER_API_KEY;\n config.enabledProviders.add('openrouter');\n }\n\n // Load tracker API keys from environment variables as fallback\n if (process.env.LINEAR_API_KEY && !config.trackerKeys.linear) {\n config.trackerKeys.linear = process.env.LINEAR_API_KEY;\n }\n if (process.env.GITHUB_TOKEN && !config.trackerKeys.github) {\n config.trackerKeys.github = process.env.GITHUB_TOKEN;\n }\n if (process.env.GITLAB_TOKEN && !config.trackerKeys.gitlab) {\n config.trackerKeys.gitlab = process.env.GITLAB_TOKEN;\n }\n if (process.env.RALLY_API_KEY && !config.trackerKeys.rally) {\n config.trackerKeys.rally = process.env.RALLY_API_KEY;\n }\n\n // Load shadow mode from environment as fallback\n // Environment variable takes precedence over config file\n if (process.env.SHADOW_MODE !== undefined) {\n const envShadowMode = ['true', '1', 'yes'].includes(process.env.SHADOW_MODE.toLowerCase());\n config.shadow.enabled = envShadowMode;\n }\n\n return { config, migration: migrationResult };\n}\n\n/**\n * Check if a project-level config exists (.pan.yaml or .panopticon.yaml)\n */\nexport function hasProjectConfig(): boolean {\n const projectRoot = findProjectRoot();\n if (!projectRoot) return false;\n return existsSync(join(projectRoot, '.pan.yaml')) || existsSync(join(projectRoot, '.panopticon.yaml'));\n}\n\n/**\n * Check if global config exists\n */\nexport function hasGlobalConfig(): boolean {\n return existsSync(GLOBAL_CONFIG_PATH);\n}\n\n/**\n * Get path to global config file\n */\nexport function getGlobalConfigPath(): string {\n return GLOBAL_CONFIG_PATH;\n}\n\n/**\n * Get path to project config file (null if not in a project).\n * Returns .pan.yaml if it exists, falls back to .panopticon.yaml, otherwise returns .pan.yaml as default.\n */\nexport function getProjectConfigPath(): string | null {\n const projectRoot = findProjectRoot();\n if (!projectRoot) return null;\n if (existsSync(join(projectRoot, '.pan.yaml'))) {\n return join(projectRoot, '.pan.yaml');\n }\n if (existsSync(join(projectRoot, '.panopticon.yaml'))) {\n return join(projectRoot, '.panopticon.yaml');\n }\n return join(projectRoot, '.pan.yaml');\n}\n"],"mappings":";;;;;;;;;AA8hBA,SAAgB,mBAAmB,OAAiC;AAClE,QAAO,mBAAmB;;;;AAhgBf,sBAA8C;EACzD,mBAAmB;EACnB,qBAAqB;EACtB;AA6DY,sBAAuD;EAKlE,mBAAmB;GACjB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,qBAAqB;GACnB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,qBAAqB;GACnB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,oBAAoB;GAClB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAMD,iBAAiB;GACf,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,oBAAoB;GAClB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,UAAU;GACR,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,eAAe;GACb,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAMD,wBAAwB;GACtB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,0BAA0B;GACxB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,kBAAkB;GAChB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,oBAAoB;GAClB,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAMD,WAAW;GACT,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,iBAAiB;GACf,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,WAAW;GACT,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAMD,WAAW;GACT,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IACX,UAAU;IACV,eAAe;IACf,SAAS;IACT,UAAU;IACV,aAAa;IACb,WAAW;IACX,OAAO;IACP,kBAAkB;IACnB;GACD,OAAO;GACR;EAED,aAAa;GACX,OAAO;GACP,UAAU;GACV,aAAa;GACb,iBAAiB;GACjB,eAAe;GACf,QAAQ;IACN,mBAAmB;IACnB,eAAe;IACf,WAAW;IAC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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { _ as init_paths, h as encodeClaudeProjectDir } from "./paths-BDyJ7BiV.js";
|
|
2
2
|
import { n as extractPrefix, r as init_issue_id } from "./issue-id-vwYJdsf8.js";
|
|
3
3
|
import { p as init_projects, y as resolveProjectFromIssue } from "./projects-CFVl4oHn.js";
|
|
4
|
-
import { i as init_tracker_config, t as getGitHubConfig } from "./tracker-config-
|
|
5
|
-
import { a as getAgentDir, m as init_agents, s as getAgentRuntimeState } from "./agents-
|
|
4
|
+
import { i as init_tracker_config, t as getGitHubConfig } from "./tracker-config-tD22z5sv.js";
|
|
5
|
+
import { a as getAgentDir, m as init_agents, s as getAgentRuntimeState } from "./agents-BQWA-Vps.js";
|
|
6
6
|
import { existsSync, readFileSync, readdirSync, statSync } from "fs";
|
|
7
7
|
import { join } from "path";
|
|
8
8
|
import { homedir } from "os";
|
|
@@ -168,4 +168,4 @@ async function computeAgentEnrichment(agentId, startedAt, hasActiveSpecialist, s
|
|
|
168
168
|
//#endregion
|
|
169
169
|
export { getAgentPendingQuestions as a, getPendingQuestions as c, getAgentJsonlPath as i, getActiveSessionPath as n, getAgentWorkspace as o, getAgentJsonlMtime as r, getClaudeProjectDir as s, computeAgentEnrichment as t };
|
|
170
170
|
|
|
171
|
-
//# sourceMappingURL=agent-enrichment-
|
|
171
|
+
//# sourceMappingURL=agent-enrichment-BKZjVvlL.js.map
|
package/dist/dashboard/{agent-enrichment-DdO7ZqjI.js.map → agent-enrichment-BKZjVvlL.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-enrichment-DdO7ZqjI.js","names":[],"sources":["../../src/lib/agent-enrichment.ts"],"sourcesContent":["/**\n * Agent enrichment utilities (PAN-440)\n *\n * Shared functions for computing enrichment fields:\n * agentPhase, hasPendingQuestion, pendingQuestionCount, resolution, resolutionCount\n *\n * Used by both the legacy REST /api/agents endpoint and the new\n * AgentEnrichmentService background poller.\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from 'fs'\nimport { readFile } from 'fs/promises'\nimport { homedir } from 'os'\nimport { join } from 'path'\nimport { encodeClaudeProjectDir } from './paths.js'\nimport { promisify } from 'util'\nimport { exec } from 'child_process'\nimport { getAgentRuntimeState, getAgentDir } from './agents.js'\nimport { resolveProjectFromIssue } from './projects.js'\nimport { getGitHubConfig } from '../dashboard/server/services/tracker-config.js'\nimport { extractPrefix } from './issue-id.js'\n\nconst execAsync = promisify(exec)\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface QuestionOption { label: string; description: string }\nexport interface Question { question: string; header: string; options: QuestionOption[]; multiSelect: boolean }\nexport interface PendingQuestion { toolId: string; timestamp: string; questions: Question[] }\n\nexport interface AgentEnrichment {\n agentPhase: 'planning' | 'exploration' | 'implementation' | 'testing' | 'documentation' | 'pre_push' | 'post_push'\n hasPendingQuestion: boolean\n pendingQuestionCount: number\n resolution: string\n resolutionCount: number\n}\n\n// ─── JSONL path helpers ───────────────────────────────────────────────────────\n\nexport function getClaudeProjectDir(workspacePath: string): string {\n return join(homedir(), '.claude', 'projects', encodeClaudeProjectDir(workspacePath))\n}\n\nexport function getActiveSessionPath(projectDir: string): string | null {\n if (!existsSync(projectDir)) return null\n try {\n const files = readdirSync(projectDir)\n .filter(f => f.endsWith('.jsonl'))\n .map(f => ({\n name: f,\n path: join(projectDir, f),\n mtime: statSync(join(projectDir, f)).mtime.getTime(),\n }))\n .sort((a, b) => b.mtime - a.mtime)\n return files.length > 0 ? files[0].path : null\n } catch {\n return null\n }\n}\n\nfunction getProjectPathByPrefix(issuePrefix: string): string {\n const issueId = `${issuePrefix}-1`\n const resolved = resolveProjectFromIssue(issueId)\n if (resolved) return resolved.projectPath\n const config = getGitHubConfig()\n if (config) {\n for (const { owner, repo, prefix } of config.repos) {\n const repoPrefix = prefix || repo.toUpperCase().replace(/-CLI$/, '').replace(/-/g, '')\n if (repoPrefix.toUpperCase() === issuePrefix.toUpperCase()) {\n const possiblePaths = [\n join(homedir(), 'Projects', repo),\n join(homedir(), 'Projects', repo.replace(/-cli$/, '')),\n join(homedir(), 'Projects', owner, repo),\n ]\n for (const path of possiblePaths) {\n if (existsSync(path)) return path\n }\n }\n }\n }\n return join(homedir(), 'Projects')\n}\n\nexport async function getAgentWorkspace(agentId: string): Promise<string | null> {\n const stateFile = join(getAgentDir(agentId), 'state.json')\n if (existsSync(stateFile)) {\n try {\n const state = JSON.parse(readFileSync(stateFile, 'utf-8'))\n if (state.workspace) return state.workspace\n } catch {}\n }\n try {\n const { stdout: paneCwd } = await execAsync(\n `tmux display-message -t ${agentId} -p '#{pane_current_path}' 2>/dev/null`,\n { encoding: 'utf-8' }\n )\n const trimmed = paneCwd.trim()\n if (trimmed && existsSync(trimmed)) return trimmed\n } catch {}\n const issueId = agentId.replace(/^(agent-|planning-)/, '').toUpperCase()\n const prefix = extractPrefix(issueId)\n if (!prefix) return null\n try {\n const projectPath = getProjectPathByPrefix(prefix)\n const workspacePath = join(projectPath, 'workspaces', `feature-${issueId.toLowerCase()}`)\n if (existsSync(workspacePath)) return workspacePath\n return projectPath\n } catch {\n return null\n }\n}\n\nexport async function getAgentJsonlPath(agentId: string): Promise<string | null> {\n const workspace = await getAgentWorkspace(agentId)\n if (!workspace) return null\n const projectDir = getClaudeProjectDir(workspace)\n return getActiveSessionPath(projectDir)\n}\n\n// ─── JSONL scanning ───────────────────────────────────────────────────────────\n\nexport async function getPendingQuestions(jsonlPath: string): Promise<PendingQuestion[]> {\n if (!existsSync(jsonlPath)) return []\n try {\n const content = await readFile(jsonlPath, 'utf-8')\n const lines = content.split('\\n').filter(line => line.trim())\n const toolCalls = new Map<string, PendingQuestion>()\n const answeredIds = new Set<string>()\n for (const line of lines) {\n try {\n const entry = JSON.parse(line)\n const messageContent = entry.message?.content\n if (!Array.isArray(messageContent)) continue\n for (const item of messageContent) {\n if (item.type === 'tool_use' && item.name === 'AskUserQuestion') {\n toolCalls.set(item.id, {\n toolId: item.id,\n timestamp: entry.timestamp || new Date().toISOString(),\n questions: item.input?.questions || [],\n })\n }\n if (item.type === 'tool_result' && item.tool_use_id) {\n answeredIds.add(item.tool_use_id)\n }\n }\n } catch {}\n }\n return Array.from(toolCalls.entries())\n .filter(([id]) => !answeredIds.has(id))\n .map(([, question]) => question)\n } catch {\n return []\n }\n}\n\nexport async function getAgentPendingQuestions(agentId: string): Promise<PendingQuestion[]> {\n const jsonlPath = await getAgentJsonlPath(agentId)\n if (!jsonlPath) return []\n return getPendingQuestions(jsonlPath)\n}\n\n/**\n * Get the mtime (ms since epoch) of the agent's active JSONL session file.\n * Returns null if the file doesn't exist or the path can't be resolved.\n * Used by AgentEnrichmentService to skip JSONL scans when the file is unchanged.\n */\nexport async function getAgentJsonlMtime(agentId: string): Promise<number | null> {\n const jsonlPath = await getAgentJsonlPath(agentId)\n if (!jsonlPath || !existsSync(jsonlPath)) return null\n try {\n return statSync(jsonlPath).mtime.getTime()\n } catch {\n return null\n }\n}\n\n// ─── Enrichment computation ───────────────────────────────────────────────────\n\n/**\n * Compute the full enrichment snapshot for a single agent.\n *\n * @param agentId - Agent session name (e.g. 'agent-pan-440', 'planning-pan-440')\n * @param startedAt - ISO timestamp when the agent started (filters stale questions)\n * @param hasActiveSpecialist - Whether the agent's issue has an active specialist running\n * @param skipJsonlScan - Skip JSONL file scan (use when mtime is unchanged); still reads runtime state\n */\nexport async function computeAgentEnrichment(\n agentId: string,\n startedAt?: string,\n hasActiveSpecialist?: boolean,\n skipJsonlScan?: boolean,\n): Promise<AgentEnrichment> {\n const isPlanning = agentId.startsWith('planning-')\n\n // Read state.json for current phase\n const stateFile = join(getAgentDir(agentId), 'state.json')\n let statePhase: string | undefined\n if (existsSync(stateFile)) {\n try {\n const state = JSON.parse(readFileSync(stateFile, 'utf-8'))\n statePhase = state.phase\n } catch {}\n }\n\n const agentPhase = (isPlanning ? 'planning' : (statePhase || 'implementation')) as AgentEnrichment['agentPhase']\n\n // Get runtime state for resolution + idle detection\n const runtimeState = getAgentRuntimeState(agentId)\n const isIdle = runtimeState?.state === 'idle' ||\n (runtimeState?.currentTool === 'AskUserQuestion')\n\n // Get pending questions, filtered by agent start time\n // Skip JSONL scan when mtime is unchanged (optimization for static TUI sessions)\n let pendingQuestions: PendingQuestion[] = []\n if (!skipJsonlScan) {\n pendingQuestions = await getAgentPendingQuestions(agentId)\n if (pendingQuestions.length > 0 && startedAt) {\n const agentStartTime = new Date(startedAt).getTime()\n pendingQuestions = pendingQuestions.filter(q => {\n const qTime = new Date(q.timestamp).getTime()\n return !isNaN(qTime) && qTime >= agentStartTime\n })\n }\n }\n\n const hasPendingQuestion =\n !hasActiveSpecialist &&\n (pendingQuestions.length > 0 || isIdle || runtimeState?.resolution === 'needs_input')\n\n return {\n agentPhase,\n hasPendingQuestion,\n pendingQuestionCount: pendingQuestions.length,\n resolution: runtimeState?.resolution || 'working',\n resolutionCount: runtimeState?.resolutionCount || 0,\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"agent-enrichment-BKZjVvlL.js","names":[],"sources":["../../src/lib/agent-enrichment.ts"],"sourcesContent":["/**\n * Agent enrichment utilities (PAN-440)\n *\n * Shared functions for computing enrichment fields:\n * agentPhase, hasPendingQuestion, pendingQuestionCount, resolution, resolutionCount\n *\n * Used by both the legacy REST /api/agents endpoint and the new\n * AgentEnrichmentService background poller.\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from 'fs'\nimport { readFile } from 'fs/promises'\nimport { homedir } from 'os'\nimport { join } from 'path'\nimport { encodeClaudeProjectDir } from './paths.js'\nimport { promisify } from 'util'\nimport { exec } from 'child_process'\nimport { getAgentRuntimeState, getAgentDir } from './agents.js'\nimport { resolveProjectFromIssue } from './projects.js'\nimport { getGitHubConfig } from '../dashboard/server/services/tracker-config.js'\nimport { extractPrefix } from './issue-id.js'\n\nconst execAsync = promisify(exec)\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface QuestionOption { label: string; description: string }\nexport interface Question { question: string; header: string; options: QuestionOption[]; multiSelect: boolean }\nexport interface PendingQuestion { toolId: string; timestamp: string; questions: Question[] }\n\nexport interface AgentEnrichment {\n agentPhase: 'planning' | 'exploration' | 'implementation' | 'testing' | 'documentation' | 'pre_push' | 'post_push'\n hasPendingQuestion: boolean\n pendingQuestionCount: number\n resolution: string\n resolutionCount: number\n}\n\n// ─── JSONL path helpers ───────────────────────────────────────────────────────\n\nexport function getClaudeProjectDir(workspacePath: string): string {\n return join(homedir(), '.claude', 'projects', encodeClaudeProjectDir(workspacePath))\n}\n\nexport function getActiveSessionPath(projectDir: string): string | null {\n if (!existsSync(projectDir)) return null\n try {\n const files = readdirSync(projectDir)\n .filter(f => f.endsWith('.jsonl'))\n .map(f => ({\n name: f,\n path: join(projectDir, f),\n mtime: statSync(join(projectDir, f)).mtime.getTime(),\n }))\n .sort((a, b) => b.mtime - a.mtime)\n return files.length > 0 ? files[0].path : null\n } catch {\n return null\n }\n}\n\nfunction getProjectPathByPrefix(issuePrefix: string): string {\n const issueId = `${issuePrefix}-1`\n const resolved = resolveProjectFromIssue(issueId)\n if (resolved) return resolved.projectPath\n const config = getGitHubConfig()\n if (config) {\n for (const { owner, repo, prefix } of config.repos) {\n const repoPrefix = prefix || repo.toUpperCase().replace(/-CLI$/, '').replace(/-/g, '')\n if (repoPrefix.toUpperCase() === issuePrefix.toUpperCase()) {\n const possiblePaths = [\n join(homedir(), 'Projects', repo),\n join(homedir(), 'Projects', repo.replace(/-cli$/, '')),\n join(homedir(), 'Projects', owner, repo),\n ]\n for (const path of possiblePaths) {\n if (existsSync(path)) return path\n }\n }\n }\n }\n return join(homedir(), 'Projects')\n}\n\nexport async function getAgentWorkspace(agentId: string): Promise<string | null> {\n const stateFile = join(getAgentDir(agentId), 'state.json')\n if (existsSync(stateFile)) {\n try {\n const state = JSON.parse(readFileSync(stateFile, 'utf-8'))\n if (state.workspace) return state.workspace\n } catch {}\n }\n try {\n const { stdout: paneCwd } = await execAsync(\n `tmux display-message -t ${agentId} -p '#{pane_current_path}' 2>/dev/null`,\n { encoding: 'utf-8' }\n )\n const trimmed = paneCwd.trim()\n if (trimmed && existsSync(trimmed)) return trimmed\n } catch {}\n const issueId = agentId.replace(/^(agent-|planning-)/, '').toUpperCase()\n const prefix = extractPrefix(issueId)\n if (!prefix) return null\n try {\n const projectPath = getProjectPathByPrefix(prefix)\n const workspacePath = join(projectPath, 'workspaces', `feature-${issueId.toLowerCase()}`)\n if (existsSync(workspacePath)) return workspacePath\n return projectPath\n } catch {\n return null\n }\n}\n\nexport async function getAgentJsonlPath(agentId: string): Promise<string | null> {\n const workspace = await getAgentWorkspace(agentId)\n if (!workspace) return null\n const projectDir = getClaudeProjectDir(workspace)\n return getActiveSessionPath(projectDir)\n}\n\n// ─── JSONL scanning ───────────────────────────────────────────────────────────\n\nexport async function getPendingQuestions(jsonlPath: string): Promise<PendingQuestion[]> {\n if (!existsSync(jsonlPath)) return []\n try {\n const content = await readFile(jsonlPath, 'utf-8')\n const lines = content.split('\\n').filter(line => line.trim())\n const toolCalls = new Map<string, PendingQuestion>()\n const answeredIds = new Set<string>()\n for (const line of lines) {\n try {\n const entry = JSON.parse(line)\n const messageContent = entry.message?.content\n if (!Array.isArray(messageContent)) continue\n for (const item of messageContent) {\n if (item.type === 'tool_use' && item.name === 'AskUserQuestion') {\n toolCalls.set(item.id, {\n toolId: item.id,\n timestamp: entry.timestamp || new Date().toISOString(),\n questions: item.input?.questions || [],\n })\n }\n if (item.type === 'tool_result' && item.tool_use_id) {\n answeredIds.add(item.tool_use_id)\n }\n }\n } catch {}\n }\n return Array.from(toolCalls.entries())\n .filter(([id]) => !answeredIds.has(id))\n .map(([, question]) => question)\n } catch {\n return []\n }\n}\n\nexport async function getAgentPendingQuestions(agentId: string): Promise<PendingQuestion[]> {\n const jsonlPath = await getAgentJsonlPath(agentId)\n if (!jsonlPath) return []\n return getPendingQuestions(jsonlPath)\n}\n\n/**\n * Get the mtime (ms since epoch) of the agent's active JSONL session file.\n * Returns null if the file doesn't exist or the path can't be resolved.\n * Used by AgentEnrichmentService to skip JSONL scans when the file is unchanged.\n */\nexport async function getAgentJsonlMtime(agentId: string): Promise<number | null> {\n const jsonlPath = await getAgentJsonlPath(agentId)\n if (!jsonlPath || !existsSync(jsonlPath)) return null\n try {\n return statSync(jsonlPath).mtime.getTime()\n } catch {\n return null\n }\n}\n\n// ─── Enrichment computation ───────────────────────────────────────────────────\n\n/**\n * Compute the full enrichment snapshot for a single agent.\n *\n * @param agentId - Agent session name (e.g. 'agent-pan-440', 'planning-pan-440')\n * @param startedAt - ISO timestamp when the agent started (filters stale questions)\n * @param hasActiveSpecialist - Whether the agent's issue has an active specialist running\n * @param skipJsonlScan - Skip JSONL file scan (use when mtime is unchanged); still reads runtime state\n */\nexport async function computeAgentEnrichment(\n agentId: string,\n startedAt?: string,\n hasActiveSpecialist?: boolean,\n skipJsonlScan?: boolean,\n): Promise<AgentEnrichment> {\n const isPlanning = agentId.startsWith('planning-')\n\n // Read state.json for current phase\n const stateFile = join(getAgentDir(agentId), 'state.json')\n let statePhase: string | undefined\n if (existsSync(stateFile)) {\n try {\n const state = JSON.parse(readFileSync(stateFile, 'utf-8'))\n statePhase = state.phase\n } catch {}\n }\n\n const agentPhase = (isPlanning ? 'planning' : (statePhase || 'implementation')) as AgentEnrichment['agentPhase']\n\n // Get runtime state for resolution + idle detection\n const runtimeState = getAgentRuntimeState(agentId)\n const isIdle = runtimeState?.state === 'idle' ||\n (runtimeState?.currentTool === 'AskUserQuestion')\n\n // Get pending questions, filtered by agent start time\n // Skip JSONL scan when mtime is unchanged (optimization for static TUI sessions)\n let pendingQuestions: PendingQuestion[] = []\n if (!skipJsonlScan) {\n pendingQuestions = await getAgentPendingQuestions(agentId)\n if (pendingQuestions.length > 0 && startedAt) {\n const agentStartTime = new Date(startedAt).getTime()\n pendingQuestions = pendingQuestions.filter(q => {\n const qTime = new Date(q.timestamp).getTime()\n return !isNaN(qTime) && qTime >= agentStartTime\n })\n }\n }\n\n const hasPendingQuestion =\n !hasActiveSpecialist &&\n (pendingQuestions.length > 0 || isIdle || runtimeState?.resolution === 'needs_input')\n\n return {\n agentPhase,\n hasPendingQuestion,\n pendingQuestionCount: pendingQuestions.length,\n resolution: runtimeState?.resolution || 'working',\n resolutionCount: runtimeState?.resolutionCount || 0,\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as getAgentPendingQuestions, c as getPendingQuestions, i as getAgentJsonlPath, n as getActiveSessionPath, o as getAgentWorkspace, r as getAgentJsonlMtime, s as getClaudeProjectDir, t as computeAgentEnrichment } from "./agent-enrichment-
|
|
1
|
+
import { a as getAgentPendingQuestions, c as getPendingQuestions, i as getAgentJsonlPath, n as getActiveSessionPath, o as getAgentWorkspace, r as getAgentJsonlMtime, s as getClaudeProjectDir, t as computeAgentEnrichment } from "./agent-enrichment-BKZjVvlL.js";
|
|
2
2
|
export { computeAgentEnrichment, getActiveSessionPath, getAgentJsonlMtime, getAgentJsonlPath, getAgentPendingQuestions, getAgentWorkspace, getClaudeProjectDir, getPendingQuestions };
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { n as __esmMin } from "./chunk-DORXReHP.js";
|
|
2
2
|
import { _ as init_paths, s as PANOPTICON_HOME, t as AGENTS_DIR } from "./paths-BDyJ7BiV.js";
|
|
3
|
-
import { i as loadConfig, r as init_config_yaml } from "./config-yaml-
|
|
3
|
+
import { i as loadConfig, r as init_config_yaml } from "./config-yaml-DUu0JI25.js";
|
|
4
4
|
import { i as findProjectByPath, p as init_projects, s as getIssuePrefix } from "./projects-CFVl4oHn.js";
|
|
5
5
|
import { a as init_providers, i as getProviderForModel, n as clearCredentialFileAuth, o as setupCredentialFileAuth, r as getProviderEnv } from "./providers-B5Y4H2Mg.js";
|
|
6
6
|
import { a as getAgentSessions, i as createSession, l as sendKeysAsync, o as init_tmux, s as killSession, t as capturePane, u as sessionExists } from "./tmux-LwG0tHhU.js";
|
|
7
7
|
import { a as init_config$1, c as require_toml, o as loadConfig$1 } from "./config-CDkGjnwy.js";
|
|
8
8
|
import { i as generateFixedPointPrompt, o as initHook, s as init_hooks, t as checkHook } from "./hooks-CjqXOlNb.js";
|
|
9
|
-
import { a as getModelId, s as init_work_type_router } from "./work-type-router-
|
|
10
|
-
import { n as createTrackerFromConfig, r as init_factory, t as createTracker } from "./factory-
|
|
9
|
+
import { a as getModelId, s as init_work_type_router } from "./work-type-router-7kwLSwrP.js";
|
|
10
|
+
import { n as createTrackerFromConfig, r as init_factory, t as createTracker } from "./factory-CBY0WWeE.js";
|
|
11
11
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
12
12
|
import { join, resolve } from "path";
|
|
13
13
|
import { homedir } from "os";
|
|
@@ -1053,4 +1053,4 @@ var init_agents = __esmMin((() => {
|
|
|
1053
1053
|
//#endregion
|
|
1054
1054
|
export { saveCloisterConfig as A, stopAgent as C, getHealthThresholdsMs as D, warnOnBareNumericIssueIds as E, init_config as O, spawnAgent as S, transitionIssueToInReview as T, recoverAgent as _, getAgentDir as a, saveAgentState as b, getAgentState as c, getProviderExportsForModel as d, getProviderTmuxFlags as f, messageAgent as g, listRunningAgents as h, getActivity as i, shouldAutoStart as j, loadCloisterConfig as k, getLatestSessionId as l, init_agents as m, autoRecoverAgents as n, getAgentRuntimeFile as o, getSessionId as p, detectCrashedAgents as r, getAgentRuntimeState as s, appendActivity as t, getProviderEnvForModel as u, resumeAgent as v, transitionIssueToInProgress as w, saveSessionId as x, saveAgentRuntimeState as y };
|
|
1055
1055
|
|
|
1056
|
-
//# sourceMappingURL=agents-
|
|
1056
|
+
//# sourceMappingURL=agents-BQWA-Vps.js.map
|