panopticon-cli 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +358 -8
- package/dist/{chunk-J7JUNJGH.js → chunk-B2JBBOJN.js} +530 -119
- package/dist/chunk-B2JBBOJN.js.map +1 -0
- package/dist/chunk-PSJRCUOA.js +177 -0
- package/dist/chunk-PSJRCUOA.js.map +1 -0
- package/dist/chunk-SG7O6I7R.js +155 -0
- package/dist/chunk-SG7O6I7R.js.map +1 -0
- package/dist/cli/index.js +5686 -2942
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +42 -3
- package/dist/index.js +43 -25
- package/dist/index.js.map +1 -1
- package/dist/projects-6JVKIYIH.js +34 -0
- package/dist/projects-6JVKIYIH.js.map +1 -0
- package/package.json +8 -3
- package/templates/claude-md/sections/beads.md +32 -0
- package/templates/claude-md/sections/commands-skills.md +15 -0
- package/templates/claude-md/sections/warnings.md +35 -0
- package/templates/claude-md/sections/workspace-info.md +12 -0
- package/dist/chunk-J7JUNJGH.js.map +0 -1
|
@@ -1,76 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
-
}) : x)(function(x) {
|
|
5
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
-
});
|
|
8
|
-
var __commonJS = (cb, mod) => function __require2() {
|
|
9
|
-
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
// src/lib/paths.ts
|
|
13
|
-
import { homedir } from "os";
|
|
14
|
-
import { join } from "path";
|
|
15
|
-
import { fileURLToPath } from "url";
|
|
16
|
-
import { dirname } from "path";
|
|
17
|
-
var PANOPTICON_HOME = join(homedir(), ".panopticon");
|
|
18
|
-
var CONFIG_DIR = PANOPTICON_HOME;
|
|
19
|
-
var SKILLS_DIR = join(PANOPTICON_HOME, "skills");
|
|
20
|
-
var COMMANDS_DIR = join(PANOPTICON_HOME, "commands");
|
|
21
|
-
var AGENTS_DIR = join(PANOPTICON_HOME, "agents");
|
|
22
|
-
var BACKUPS_DIR = join(PANOPTICON_HOME, "backups");
|
|
23
|
-
var COSTS_DIR = join(PANOPTICON_HOME, "costs");
|
|
24
|
-
var TRAEFIK_DIR = join(PANOPTICON_HOME, "traefik");
|
|
25
|
-
var TRAEFIK_DYNAMIC_DIR = join(TRAEFIK_DIR, "dynamic");
|
|
26
|
-
var TRAEFIK_CERTS_DIR = join(TRAEFIK_DIR, "certs");
|
|
27
|
-
var CERTS_DIR = join(PANOPTICON_HOME, "certs");
|
|
28
|
-
var CONFIG_FILE = join(CONFIG_DIR, "config.toml");
|
|
29
|
-
var CLAUDE_DIR = join(homedir(), ".claude");
|
|
30
|
-
var CODEX_DIR = join(homedir(), ".codex");
|
|
31
|
-
var CURSOR_DIR = join(homedir(), ".cursor");
|
|
32
|
-
var GEMINI_DIR = join(homedir(), ".gemini");
|
|
33
|
-
var SYNC_TARGETS = {
|
|
34
|
-
claude: {
|
|
35
|
-
skills: join(CLAUDE_DIR, "skills"),
|
|
36
|
-
commands: join(CLAUDE_DIR, "commands")
|
|
37
|
-
},
|
|
38
|
-
codex: {
|
|
39
|
-
skills: join(CODEX_DIR, "skills"),
|
|
40
|
-
commands: join(CODEX_DIR, "commands")
|
|
41
|
-
},
|
|
42
|
-
cursor: {
|
|
43
|
-
skills: join(CURSOR_DIR, "skills"),
|
|
44
|
-
commands: join(CURSOR_DIR, "commands")
|
|
45
|
-
},
|
|
46
|
-
gemini: {
|
|
47
|
-
skills: join(GEMINI_DIR, "skills"),
|
|
48
|
-
commands: join(GEMINI_DIR, "commands")
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
var TEMPLATES_DIR = join(PANOPTICON_HOME, "templates");
|
|
52
|
-
var CLAUDE_MD_TEMPLATES = join(TEMPLATES_DIR, "claude-md", "sections");
|
|
53
|
-
var currentFile = fileURLToPath(import.meta.url);
|
|
54
|
-
var distDir = dirname(currentFile);
|
|
55
|
-
var packageRoot = dirname(distDir);
|
|
56
|
-
var SOURCE_TEMPLATES_DIR = join(packageRoot, "templates");
|
|
57
|
-
var SOURCE_TRAEFIK_TEMPLATES = join(SOURCE_TEMPLATES_DIR, "traefik");
|
|
58
|
-
var INIT_DIRS = [
|
|
59
|
-
PANOPTICON_HOME,
|
|
60
|
-
SKILLS_DIR,
|
|
61
|
-
COMMANDS_DIR,
|
|
1
|
+
import {
|
|
62
2
|
AGENTS_DIR,
|
|
63
3
|
BACKUPS_DIR,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
4
|
+
BIN_DIR,
|
|
5
|
+
COMMANDS_DIR,
|
|
6
|
+
CONFIG_FILE,
|
|
7
|
+
SKILLS_DIR,
|
|
8
|
+
SOURCE_SCRIPTS_DIR,
|
|
9
|
+
SYNC_TARGETS,
|
|
10
|
+
init_esm_shims,
|
|
11
|
+
init_paths
|
|
12
|
+
} from "./chunk-SG7O6I7R.js";
|
|
72
13
|
|
|
73
14
|
// src/lib/config.ts
|
|
15
|
+
init_esm_shims();
|
|
16
|
+
init_paths();
|
|
74
17
|
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
75
18
|
import { parse, stringify } from "@iarna/toml";
|
|
76
19
|
var DEFAULT_CONFIG = {
|
|
@@ -95,6 +38,20 @@ var DEFAULT_CONFIG = {
|
|
|
95
38
|
api_port: 3002
|
|
96
39
|
}
|
|
97
40
|
};
|
|
41
|
+
function deepMerge(defaults, overrides) {
|
|
42
|
+
const result = { ...defaults };
|
|
43
|
+
for (const key of Object.keys(overrides)) {
|
|
44
|
+
const defaultVal = defaults[key];
|
|
45
|
+
const overrideVal = overrides[key];
|
|
46
|
+
if (overrideVal === void 0) continue;
|
|
47
|
+
if (typeof defaultVal === "object" && defaultVal !== null && !Array.isArray(defaultVal) && typeof overrideVal === "object" && overrideVal !== null && !Array.isArray(overrideVal)) {
|
|
48
|
+
result[key] = deepMerge(defaultVal, overrideVal);
|
|
49
|
+
} else {
|
|
50
|
+
result[key] = overrideVal;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
98
55
|
function loadConfig() {
|
|
99
56
|
if (!existsSync(CONFIG_FILE)) {
|
|
100
57
|
return DEFAULT_CONFIG;
|
|
@@ -102,7 +59,7 @@ function loadConfig() {
|
|
|
102
59
|
try {
|
|
103
60
|
const content = readFileSync(CONFIG_FILE, "utf8");
|
|
104
61
|
const parsed = parse(content);
|
|
105
|
-
return
|
|
62
|
+
return deepMerge(DEFAULT_CONFIG, parsed);
|
|
106
63
|
} catch (error) {
|
|
107
64
|
console.error("Warning: Failed to parse config, using defaults");
|
|
108
65
|
return DEFAULT_CONFIG;
|
|
@@ -117,9 +74,10 @@ function getDefaultConfig() {
|
|
|
117
74
|
}
|
|
118
75
|
|
|
119
76
|
// src/lib/shell.ts
|
|
77
|
+
init_esm_shims();
|
|
120
78
|
import { existsSync as existsSync2, readFileSync as readFileSync2, appendFileSync } from "fs";
|
|
121
|
-
import { homedir
|
|
122
|
-
import { join
|
|
79
|
+
import { homedir } from "os";
|
|
80
|
+
import { join } from "path";
|
|
123
81
|
function detectShell() {
|
|
124
82
|
const shell = process.env.SHELL || "";
|
|
125
83
|
if (shell.includes("zsh")) return "zsh";
|
|
@@ -128,16 +86,16 @@ function detectShell() {
|
|
|
128
86
|
return "unknown";
|
|
129
87
|
}
|
|
130
88
|
function getShellRcFile(shell) {
|
|
131
|
-
const home =
|
|
89
|
+
const home = homedir();
|
|
132
90
|
switch (shell) {
|
|
133
91
|
case "zsh":
|
|
134
|
-
return
|
|
92
|
+
return join(home, ".zshrc");
|
|
135
93
|
case "bash":
|
|
136
|
-
const bashrc =
|
|
94
|
+
const bashrc = join(home, ".bashrc");
|
|
137
95
|
if (existsSync2(bashrc)) return bashrc;
|
|
138
|
-
return
|
|
96
|
+
return join(home, ".bash_profile");
|
|
139
97
|
case "fish":
|
|
140
|
-
return
|
|
98
|
+
return join(home, ".config", "fish", "config.fish");
|
|
141
99
|
default:
|
|
142
100
|
return null;
|
|
143
101
|
}
|
|
@@ -168,20 +126,22 @@ function getAliasInstructions(shell) {
|
|
|
168
126
|
}
|
|
169
127
|
|
|
170
128
|
// src/lib/backup.ts
|
|
129
|
+
init_esm_shims();
|
|
130
|
+
init_paths();
|
|
171
131
|
import { existsSync as existsSync3, mkdirSync, readdirSync, cpSync, rmSync } from "fs";
|
|
172
|
-
import { join as
|
|
132
|
+
import { join as join2, basename } from "path";
|
|
173
133
|
function createBackupTimestamp() {
|
|
174
134
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
175
135
|
}
|
|
176
136
|
function createBackup(sourceDirs) {
|
|
177
137
|
const timestamp = createBackupTimestamp();
|
|
178
|
-
const backupPath =
|
|
138
|
+
const backupPath = join2(BACKUPS_DIR, timestamp);
|
|
179
139
|
mkdirSync(backupPath, { recursive: true });
|
|
180
140
|
const targets = [];
|
|
181
141
|
for (const sourceDir of sourceDirs) {
|
|
182
142
|
if (!existsSync3(sourceDir)) continue;
|
|
183
143
|
const targetName = basename(sourceDir);
|
|
184
|
-
const targetPath =
|
|
144
|
+
const targetPath = join2(backupPath, targetName);
|
|
185
145
|
cpSync(sourceDir, targetPath, { recursive: true });
|
|
186
146
|
targets.push(targetName);
|
|
187
147
|
}
|
|
@@ -195,7 +155,7 @@ function listBackups() {
|
|
|
195
155
|
if (!existsSync3(BACKUPS_DIR)) return [];
|
|
196
156
|
const entries = readdirSync(BACKUPS_DIR, { withFileTypes: true });
|
|
197
157
|
return entries.filter((e) => e.isDirectory()).map((e) => {
|
|
198
|
-
const backupPath =
|
|
158
|
+
const backupPath = join2(BACKUPS_DIR, e.name);
|
|
199
159
|
const contents = readdirSync(backupPath);
|
|
200
160
|
return {
|
|
201
161
|
timestamp: e.name,
|
|
@@ -205,14 +165,14 @@ function listBackups() {
|
|
|
205
165
|
}).sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
206
166
|
}
|
|
207
167
|
function restoreBackup(timestamp, targetDirs) {
|
|
208
|
-
const backupPath =
|
|
168
|
+
const backupPath = join2(BACKUPS_DIR, timestamp);
|
|
209
169
|
if (!existsSync3(backupPath)) {
|
|
210
170
|
throw new Error(`Backup not found: ${timestamp}`);
|
|
211
171
|
}
|
|
212
172
|
const contents = readdirSync(backupPath, { withFileTypes: true });
|
|
213
173
|
for (const entry of contents) {
|
|
214
174
|
if (!entry.isDirectory()) continue;
|
|
215
|
-
const sourcePath =
|
|
175
|
+
const sourcePath = join2(backupPath, entry.name);
|
|
216
176
|
const targetPath = targetDirs[entry.name];
|
|
217
177
|
if (!targetPath) continue;
|
|
218
178
|
if (existsSync3(targetPath)) {
|
|
@@ -234,8 +194,10 @@ function cleanOldBackups(keepCount = 10) {
|
|
|
234
194
|
}
|
|
235
195
|
|
|
236
196
|
// src/lib/sync.ts
|
|
237
|
-
|
|
238
|
-
|
|
197
|
+
init_esm_shims();
|
|
198
|
+
init_paths();
|
|
199
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readdirSync as readdirSync2, symlinkSync, unlinkSync, lstatSync, readlinkSync, rmSync as rmSync2, copyFileSync, chmodSync } from "fs";
|
|
200
|
+
import { join as join3 } from "path";
|
|
239
201
|
function removeTarget(targetPath) {
|
|
240
202
|
const stats = lstatSync(targetPath);
|
|
241
203
|
if (stats.isDirectory() && !stats.isSymbolicLink()) {
|
|
@@ -260,13 +222,14 @@ function planSync(runtime) {
|
|
|
260
222
|
const plan = {
|
|
261
223
|
runtime,
|
|
262
224
|
skills: [],
|
|
263
|
-
commands: []
|
|
225
|
+
commands: [],
|
|
226
|
+
agents: []
|
|
264
227
|
};
|
|
265
228
|
if (existsSync4(SKILLS_DIR)) {
|
|
266
229
|
const skills = readdirSync2(SKILLS_DIR, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
267
230
|
for (const skill of skills) {
|
|
268
|
-
const sourcePath =
|
|
269
|
-
const targetPath =
|
|
231
|
+
const sourcePath = join3(SKILLS_DIR, skill.name);
|
|
232
|
+
const targetPath = join3(targets.skills, skill.name);
|
|
270
233
|
let status = "new";
|
|
271
234
|
if (existsSync4(targetPath)) {
|
|
272
235
|
if (isPanopticonSymlink(targetPath)) {
|
|
@@ -281,8 +244,8 @@ function planSync(runtime) {
|
|
|
281
244
|
if (existsSync4(COMMANDS_DIR)) {
|
|
282
245
|
const commands = readdirSync2(COMMANDS_DIR, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
283
246
|
for (const cmd of commands) {
|
|
284
|
-
const sourcePath =
|
|
285
|
-
const targetPath =
|
|
247
|
+
const sourcePath = join3(COMMANDS_DIR, cmd.name);
|
|
248
|
+
const targetPath = join3(targets.commands, cmd.name);
|
|
286
249
|
let status = "new";
|
|
287
250
|
if (existsSync4(targetPath)) {
|
|
288
251
|
if (isPanopticonSymlink(targetPath)) {
|
|
@@ -294,6 +257,22 @@ function planSync(runtime) {
|
|
|
294
257
|
plan.commands.push({ name: cmd.name, sourcePath, targetPath, status });
|
|
295
258
|
}
|
|
296
259
|
}
|
|
260
|
+
if (existsSync4(AGENTS_DIR)) {
|
|
261
|
+
const agents = readdirSync2(AGENTS_DIR, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md"));
|
|
262
|
+
for (const agent of agents) {
|
|
263
|
+
const sourcePath = join3(AGENTS_DIR, agent.name);
|
|
264
|
+
const targetPath = join3(targets.agents, agent.name);
|
|
265
|
+
let status = "new";
|
|
266
|
+
if (existsSync4(targetPath)) {
|
|
267
|
+
if (isPanopticonSymlink(targetPath)) {
|
|
268
|
+
status = "symlink";
|
|
269
|
+
} else {
|
|
270
|
+
status = "conflict";
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
plan.agents.push({ name: agent.name, sourcePath, targetPath, status });
|
|
274
|
+
}
|
|
275
|
+
}
|
|
297
276
|
return plan;
|
|
298
277
|
}
|
|
299
278
|
function executeSync(runtime, options = {}) {
|
|
@@ -306,6 +285,7 @@ function executeSync(runtime, options = {}) {
|
|
|
306
285
|
const targets = SYNC_TARGETS[runtime];
|
|
307
286
|
mkdirSync2(targets.skills, { recursive: true });
|
|
308
287
|
mkdirSync2(targets.commands, { recursive: true });
|
|
288
|
+
mkdirSync2(targets.agents, { recursive: true });
|
|
309
289
|
for (const item of plan.skills) {
|
|
310
290
|
if (options.dryRun) {
|
|
311
291
|
if (item.status === "new" || item.status === "symlink") {
|
|
@@ -344,10 +324,62 @@ function executeSync(runtime, options = {}) {
|
|
|
344
324
|
symlinkSync(item.sourcePath, item.targetPath);
|
|
345
325
|
result.created.push(item.name);
|
|
346
326
|
}
|
|
327
|
+
for (const item of plan.agents) {
|
|
328
|
+
if (options.dryRun) {
|
|
329
|
+
if (item.status === "new" || item.status === "symlink") {
|
|
330
|
+
result.created.push(item.name);
|
|
331
|
+
} else {
|
|
332
|
+
result.conflicts.push(item.name);
|
|
333
|
+
}
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
if (item.status === "conflict" && !options.force) {
|
|
337
|
+
result.conflicts.push(item.name);
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
if (existsSync4(item.targetPath)) {
|
|
341
|
+
removeTarget(item.targetPath);
|
|
342
|
+
}
|
|
343
|
+
symlinkSync(item.sourcePath, item.targetPath);
|
|
344
|
+
result.created.push(item.name);
|
|
345
|
+
}
|
|
346
|
+
return result;
|
|
347
|
+
}
|
|
348
|
+
function planHooksSync() {
|
|
349
|
+
const hooks = [];
|
|
350
|
+
if (!existsSync4(SOURCE_SCRIPTS_DIR)) {
|
|
351
|
+
return hooks;
|
|
352
|
+
}
|
|
353
|
+
const scripts = readdirSync2(SOURCE_SCRIPTS_DIR, { withFileTypes: true }).filter((entry) => entry.isFile() && !entry.name.startsWith(".") && !entry.name.includes("."));
|
|
354
|
+
for (const script of scripts) {
|
|
355
|
+
const sourcePath = join3(SOURCE_SCRIPTS_DIR, script.name);
|
|
356
|
+
const targetPath = join3(BIN_DIR, script.name);
|
|
357
|
+
let status = "new";
|
|
358
|
+
if (existsSync4(targetPath)) {
|
|
359
|
+
status = "updated";
|
|
360
|
+
}
|
|
361
|
+
hooks.push({ name: script.name, sourcePath, targetPath, status });
|
|
362
|
+
}
|
|
363
|
+
return hooks;
|
|
364
|
+
}
|
|
365
|
+
function syncHooks() {
|
|
366
|
+
const result = { synced: [], errors: [] };
|
|
367
|
+
mkdirSync2(BIN_DIR, { recursive: true });
|
|
368
|
+
const hooks = planHooksSync();
|
|
369
|
+
for (const hook of hooks) {
|
|
370
|
+
try {
|
|
371
|
+
copyFileSync(hook.sourcePath, hook.targetPath);
|
|
372
|
+
chmodSync(hook.targetPath, 493);
|
|
373
|
+
result.synced.push(hook.name);
|
|
374
|
+
} catch (error) {
|
|
375
|
+
result.errors.push(`${hook.name}: ${error}`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
347
378
|
return result;
|
|
348
379
|
}
|
|
349
380
|
|
|
350
381
|
// src/lib/tracker/interface.ts
|
|
382
|
+
init_esm_shims();
|
|
351
383
|
var NotImplementedError = class extends Error {
|
|
352
384
|
constructor(feature) {
|
|
353
385
|
super(`Not implemented: ${feature}`);
|
|
@@ -368,6 +400,7 @@ var TrackerAuthError = class extends Error {
|
|
|
368
400
|
};
|
|
369
401
|
|
|
370
402
|
// src/lib/tracker/linear.ts
|
|
403
|
+
init_esm_shims();
|
|
371
404
|
import { LinearClient } from "@linear/sdk";
|
|
372
405
|
var STATE_MAP = {
|
|
373
406
|
backlog: "open",
|
|
@@ -577,6 +610,7 @@ var LinearTracker = class {
|
|
|
577
610
|
};
|
|
578
611
|
|
|
579
612
|
// src/lib/tracker/github.ts
|
|
613
|
+
init_esm_shims();
|
|
580
614
|
import { Octokit } from "@octokit/rest";
|
|
581
615
|
var GitHubTracker = class {
|
|
582
616
|
name = "github";
|
|
@@ -737,6 +771,7 @@ var GitHubTracker = class {
|
|
|
737
771
|
};
|
|
738
772
|
|
|
739
773
|
// src/lib/tracker/gitlab.ts
|
|
774
|
+
init_esm_shims();
|
|
740
775
|
var GitLabTracker = class {
|
|
741
776
|
constructor(token, projectId) {
|
|
742
777
|
this.token = token;
|
|
@@ -785,6 +820,385 @@ var GitLabTracker = class {
|
|
|
785
820
|
}
|
|
786
821
|
};
|
|
787
822
|
|
|
823
|
+
// src/lib/tracker/factory.ts
|
|
824
|
+
init_esm_shims();
|
|
825
|
+
|
|
826
|
+
// src/lib/tracker/rally.ts
|
|
827
|
+
init_esm_shims();
|
|
828
|
+
import rally from "rally";
|
|
829
|
+
var STATE_MAP2 = {
|
|
830
|
+
Defined: "open",
|
|
831
|
+
"In-Progress": "in_progress",
|
|
832
|
+
Completed: "closed",
|
|
833
|
+
Accepted: "closed"
|
|
834
|
+
};
|
|
835
|
+
var PRIORITY_MAP = {
|
|
836
|
+
"Resolve Immediately": 0,
|
|
837
|
+
High: 1,
|
|
838
|
+
Normal: 2,
|
|
839
|
+
Low: 3
|
|
840
|
+
};
|
|
841
|
+
var REVERSE_PRIORITY_MAP = {
|
|
842
|
+
0: "Resolve Immediately",
|
|
843
|
+
1: "High",
|
|
844
|
+
2: "Normal",
|
|
845
|
+
3: "Low",
|
|
846
|
+
4: "Low"
|
|
847
|
+
};
|
|
848
|
+
var RallyTracker = class {
|
|
849
|
+
name = "rally";
|
|
850
|
+
restApi;
|
|
851
|
+
workspace;
|
|
852
|
+
project;
|
|
853
|
+
constructor(config) {
|
|
854
|
+
if (!config.apiKey) {
|
|
855
|
+
throw new TrackerAuthError("rally", "API key is required");
|
|
856
|
+
}
|
|
857
|
+
this.restApi = rally({
|
|
858
|
+
apiKey: config.apiKey,
|
|
859
|
+
server: config.server || "https://rally1.rallydev.com",
|
|
860
|
+
requestOptions: {
|
|
861
|
+
headers: {
|
|
862
|
+
"X-RallyIntegrationType": "Panopticon",
|
|
863
|
+
"X-RallyIntegrationName": "Panopticon CLI",
|
|
864
|
+
"X-RallyIntegrationVendor": "Mind Your Now",
|
|
865
|
+
"X-RallyIntegrationVersion": "0.2.0"
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
});
|
|
869
|
+
this.workspace = config.workspace;
|
|
870
|
+
this.project = config.project;
|
|
871
|
+
}
|
|
872
|
+
async listIssues(filters) {
|
|
873
|
+
const query = {
|
|
874
|
+
type: "artifact",
|
|
875
|
+
// Query all artifact types
|
|
876
|
+
fetch: [
|
|
877
|
+
"FormattedID",
|
|
878
|
+
"Name",
|
|
879
|
+
"Description",
|
|
880
|
+
"ScheduleState",
|
|
881
|
+
"State",
|
|
882
|
+
// For Defects
|
|
883
|
+
"Tags",
|
|
884
|
+
"Owner",
|
|
885
|
+
"Priority",
|
|
886
|
+
"DueDate",
|
|
887
|
+
"CreationDate",
|
|
888
|
+
"LastUpdateDate",
|
|
889
|
+
"Parent",
|
|
890
|
+
"_type"
|
|
891
|
+
],
|
|
892
|
+
limit: filters?.limit ?? 50,
|
|
893
|
+
query: this.buildQueryString(filters)
|
|
894
|
+
};
|
|
895
|
+
if (this.workspace) {
|
|
896
|
+
query.workspace = this.workspace;
|
|
897
|
+
}
|
|
898
|
+
if (this.project) {
|
|
899
|
+
query.project = this.project;
|
|
900
|
+
query.projectScopeDown = true;
|
|
901
|
+
}
|
|
902
|
+
try {
|
|
903
|
+
const result = await this.queryRally(query);
|
|
904
|
+
return result.Results.map((artifact) => this.normalizeIssue(artifact));
|
|
905
|
+
} catch (error) {
|
|
906
|
+
if (error.message?.includes("Unauthorized") || error.message?.includes("401")) {
|
|
907
|
+
throw new TrackerAuthError("rally", "Invalid API key or insufficient permissions");
|
|
908
|
+
}
|
|
909
|
+
throw error;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
async getIssue(id) {
|
|
913
|
+
try {
|
|
914
|
+
const query = {
|
|
915
|
+
type: "artifact",
|
|
916
|
+
fetch: [
|
|
917
|
+
"FormattedID",
|
|
918
|
+
"Name",
|
|
919
|
+
"Description",
|
|
920
|
+
"ScheduleState",
|
|
921
|
+
"State",
|
|
922
|
+
"Tags",
|
|
923
|
+
"Owner",
|
|
924
|
+
"Priority",
|
|
925
|
+
"DueDate",
|
|
926
|
+
"CreationDate",
|
|
927
|
+
"LastUpdateDate",
|
|
928
|
+
"Parent",
|
|
929
|
+
"_type"
|
|
930
|
+
],
|
|
931
|
+
query: `(FormattedID = "${id}")`
|
|
932
|
+
};
|
|
933
|
+
if (this.workspace) {
|
|
934
|
+
query.workspace = this.workspace;
|
|
935
|
+
}
|
|
936
|
+
const result = await this.queryRally(query);
|
|
937
|
+
if (!result.Results || result.Results.length === 0) {
|
|
938
|
+
throw new IssueNotFoundError(id, "rally");
|
|
939
|
+
}
|
|
940
|
+
return this.normalizeIssue(result.Results[0]);
|
|
941
|
+
} catch (error) {
|
|
942
|
+
if (error instanceof IssueNotFoundError) throw error;
|
|
943
|
+
throw new IssueNotFoundError(id, "rally");
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
async updateIssue(id, update) {
|
|
947
|
+
const issue = await this.getIssue(id);
|
|
948
|
+
const query = {
|
|
949
|
+
type: "artifact",
|
|
950
|
+
fetch: ["ObjectID", "_ref", "_type"],
|
|
951
|
+
query: `(FormattedID = "${id}")`
|
|
952
|
+
};
|
|
953
|
+
if (this.workspace) {
|
|
954
|
+
query.workspace = this.workspace;
|
|
955
|
+
}
|
|
956
|
+
const result = await this.queryRally(query);
|
|
957
|
+
if (!result.Results || result.Results.length === 0) {
|
|
958
|
+
throw new IssueNotFoundError(id, "rally");
|
|
959
|
+
}
|
|
960
|
+
const artifact = result.Results[0];
|
|
961
|
+
const updatePayload = {};
|
|
962
|
+
if (update.title !== void 0) {
|
|
963
|
+
updatePayload.Name = update.title;
|
|
964
|
+
}
|
|
965
|
+
if (update.description !== void 0) {
|
|
966
|
+
updatePayload.Description = update.description;
|
|
967
|
+
}
|
|
968
|
+
if (update.state !== void 0) {
|
|
969
|
+
const rallyState = this.reverseMapState(update.state);
|
|
970
|
+
if (artifact._type === "Defect") {
|
|
971
|
+
updatePayload.State = rallyState;
|
|
972
|
+
} else {
|
|
973
|
+
updatePayload.ScheduleState = rallyState;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
if (update.priority !== void 0) {
|
|
977
|
+
updatePayload.Priority = REVERSE_PRIORITY_MAP[update.priority] || "Normal";
|
|
978
|
+
}
|
|
979
|
+
if (update.dueDate !== void 0) {
|
|
980
|
+
updatePayload.DueDate = update.dueDate;
|
|
981
|
+
}
|
|
982
|
+
if (Object.keys(updatePayload).length > 0) {
|
|
983
|
+
await this.updateRally(artifact._type.toLowerCase(), artifact._ref, updatePayload);
|
|
984
|
+
}
|
|
985
|
+
return this.getIssue(id);
|
|
986
|
+
}
|
|
987
|
+
async createIssue(newIssue) {
|
|
988
|
+
if (!this.project && !newIssue.team) {
|
|
989
|
+
throw new Error("Project is required to create an issue. Set it in config or provide team field.");
|
|
990
|
+
}
|
|
991
|
+
const project = newIssue.team || this.project;
|
|
992
|
+
const createPayload = {
|
|
993
|
+
Name: newIssue.title,
|
|
994
|
+
Description: newIssue.description || "",
|
|
995
|
+
Project: project
|
|
996
|
+
};
|
|
997
|
+
if (newIssue.priority !== void 0) {
|
|
998
|
+
createPayload.Priority = REVERSE_PRIORITY_MAP[newIssue.priority] || "Normal";
|
|
999
|
+
}
|
|
1000
|
+
if (newIssue.dueDate) {
|
|
1001
|
+
createPayload.DueDate = newIssue.dueDate;
|
|
1002
|
+
}
|
|
1003
|
+
if (this.workspace) {
|
|
1004
|
+
createPayload.Workspace = this.workspace;
|
|
1005
|
+
}
|
|
1006
|
+
const result = await this.createRally("hierarchicalrequirement", createPayload);
|
|
1007
|
+
return this.getIssue(result.Object.FormattedID);
|
|
1008
|
+
}
|
|
1009
|
+
async getComments(issueId) {
|
|
1010
|
+
const issue = await this.getIssue(issueId);
|
|
1011
|
+
const query = {
|
|
1012
|
+
type: "artifact",
|
|
1013
|
+
fetch: ["ObjectID", "_ref", "Discussion"],
|
|
1014
|
+
query: `(FormattedID = "${issueId}")`
|
|
1015
|
+
};
|
|
1016
|
+
if (this.workspace) {
|
|
1017
|
+
query.workspace = this.workspace;
|
|
1018
|
+
}
|
|
1019
|
+
const result = await this.queryRally(query);
|
|
1020
|
+
if (!result.Results || result.Results.length === 0) {
|
|
1021
|
+
return [];
|
|
1022
|
+
}
|
|
1023
|
+
const artifact = result.Results[0];
|
|
1024
|
+
if (!artifact.Discussion) {
|
|
1025
|
+
return [];
|
|
1026
|
+
}
|
|
1027
|
+
const postsQuery = {
|
|
1028
|
+
type: "conversationpost",
|
|
1029
|
+
fetch: ["ObjectID", "Text", "User", "CreationDate", "PostNumber"],
|
|
1030
|
+
query: `(Discussion = "${artifact.Discussion._ref}")`,
|
|
1031
|
+
order: "PostNumber"
|
|
1032
|
+
};
|
|
1033
|
+
const postsResult = await this.queryRally(postsQuery);
|
|
1034
|
+
return (postsResult.Results || []).map((post) => ({
|
|
1035
|
+
id: post.ObjectID,
|
|
1036
|
+
issueId,
|
|
1037
|
+
body: post.Text || "",
|
|
1038
|
+
author: post.User?._refObjectName || "Unknown",
|
|
1039
|
+
createdAt: post.CreationDate,
|
|
1040
|
+
updatedAt: post.CreationDate
|
|
1041
|
+
// Rally doesn't track comment updates separately
|
|
1042
|
+
}));
|
|
1043
|
+
}
|
|
1044
|
+
async addComment(issueId, body) {
|
|
1045
|
+
const query = {
|
|
1046
|
+
type: "artifact",
|
|
1047
|
+
fetch: ["ObjectID", "_ref", "Discussion"],
|
|
1048
|
+
query: `(FormattedID = "${issueId}")`
|
|
1049
|
+
};
|
|
1050
|
+
if (this.workspace) {
|
|
1051
|
+
query.workspace = this.workspace;
|
|
1052
|
+
}
|
|
1053
|
+
const result = await this.queryRally(query);
|
|
1054
|
+
if (!result.Results || result.Results.length === 0) {
|
|
1055
|
+
throw new IssueNotFoundError(issueId, "rally");
|
|
1056
|
+
}
|
|
1057
|
+
const artifact = result.Results[0];
|
|
1058
|
+
let discussionRef = artifact.Discussion?._ref;
|
|
1059
|
+
if (!discussionRef) {
|
|
1060
|
+
const discussionResult = await this.createRally("conversationpost", {
|
|
1061
|
+
Artifact: artifact._ref,
|
|
1062
|
+
Text: body
|
|
1063
|
+
});
|
|
1064
|
+
return {
|
|
1065
|
+
id: discussionResult.Object.ObjectID,
|
|
1066
|
+
issueId,
|
|
1067
|
+
body,
|
|
1068
|
+
author: "Panopticon",
|
|
1069
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1070
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
const postResult = await this.createRally("conversationpost", {
|
|
1074
|
+
Artifact: artifact._ref,
|
|
1075
|
+
Text: body
|
|
1076
|
+
});
|
|
1077
|
+
return {
|
|
1078
|
+
id: postResult.Object.ObjectID,
|
|
1079
|
+
issueId,
|
|
1080
|
+
body,
|
|
1081
|
+
author: "Panopticon",
|
|
1082
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1083
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
async transitionIssue(id, state) {
|
|
1087
|
+
await this.updateIssue(id, { state });
|
|
1088
|
+
}
|
|
1089
|
+
async linkPR(issueId, prUrl) {
|
|
1090
|
+
await this.addComment(issueId, `Linked Pull Request: ${prUrl}`);
|
|
1091
|
+
}
|
|
1092
|
+
// Private helper methods
|
|
1093
|
+
buildQueryString(filters) {
|
|
1094
|
+
const conditions = [];
|
|
1095
|
+
if (filters?.state && !filters.includeClosed) {
|
|
1096
|
+
const rallyState = this.reverseMapState(filters.state);
|
|
1097
|
+
conditions.push(`((ScheduleState = "${rallyState}") OR (State = "${rallyState}"))`);
|
|
1098
|
+
}
|
|
1099
|
+
if (!filters?.includeClosed) {
|
|
1100
|
+
conditions.push('((ScheduleState != "Completed") AND (ScheduleState != "Accepted") AND (State != "Closed"))');
|
|
1101
|
+
}
|
|
1102
|
+
if (filters?.assignee) {
|
|
1103
|
+
conditions.push(`(Owner.Name contains "${filters.assignee}")`);
|
|
1104
|
+
}
|
|
1105
|
+
if (filters?.labels && filters.labels.length > 0) {
|
|
1106
|
+
const labelConditions = filters.labels.map(
|
|
1107
|
+
(label) => `(Tags.Name contains "${label}")`
|
|
1108
|
+
);
|
|
1109
|
+
conditions.push(`(${labelConditions.join(" AND ")})`);
|
|
1110
|
+
}
|
|
1111
|
+
if (filters?.query) {
|
|
1112
|
+
conditions.push(`((Name contains "${filters.query}") OR (Description contains "${filters.query}"))`);
|
|
1113
|
+
}
|
|
1114
|
+
return conditions.length > 0 ? conditions.join(" AND ") : "";
|
|
1115
|
+
}
|
|
1116
|
+
normalizeIssue(rallyArtifact) {
|
|
1117
|
+
const stateValue = rallyArtifact.ScheduleState || rallyArtifact.State || "Defined";
|
|
1118
|
+
const state = this.mapState(stateValue);
|
|
1119
|
+
const labels = [];
|
|
1120
|
+
if (rallyArtifact.Tags && rallyArtifact.Tags._tagsNameArray) {
|
|
1121
|
+
labels.push(...rallyArtifact.Tags._tagsNameArray);
|
|
1122
|
+
}
|
|
1123
|
+
const priority = rallyArtifact.Priority ? PRIORITY_MAP[rallyArtifact.Priority] ?? 2 : void 0;
|
|
1124
|
+
const baseUrl = this.restApi.server.replace("/slm/webservice/", "");
|
|
1125
|
+
const url = `${baseUrl}/#/detail/${rallyArtifact._type.toLowerCase()}/${rallyArtifact.ObjectID}`;
|
|
1126
|
+
return {
|
|
1127
|
+
id: rallyArtifact.ObjectID,
|
|
1128
|
+
ref: rallyArtifact.FormattedID,
|
|
1129
|
+
title: rallyArtifact.Name || "",
|
|
1130
|
+
description: rallyArtifact.Description || "",
|
|
1131
|
+
state,
|
|
1132
|
+
labels,
|
|
1133
|
+
assignee: rallyArtifact.Owner?._refObjectName,
|
|
1134
|
+
url,
|
|
1135
|
+
tracker: "rally",
|
|
1136
|
+
priority,
|
|
1137
|
+
dueDate: rallyArtifact.DueDate,
|
|
1138
|
+
createdAt: rallyArtifact.CreationDate,
|
|
1139
|
+
updatedAt: rallyArtifact.LastUpdateDate
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
mapState(rallyState) {
|
|
1143
|
+
return STATE_MAP2[rallyState] ?? "open";
|
|
1144
|
+
}
|
|
1145
|
+
reverseMapState(state) {
|
|
1146
|
+
switch (state) {
|
|
1147
|
+
case "open":
|
|
1148
|
+
return "Defined";
|
|
1149
|
+
case "in_progress":
|
|
1150
|
+
return "In-Progress";
|
|
1151
|
+
case "closed":
|
|
1152
|
+
return "Completed";
|
|
1153
|
+
default:
|
|
1154
|
+
return "Defined";
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
// Rally API wrapper methods
|
|
1158
|
+
queryRally(queryConfig) {
|
|
1159
|
+
return new Promise((resolve, reject) => {
|
|
1160
|
+
this.restApi.query(queryConfig, (error, result) => {
|
|
1161
|
+
if (error) {
|
|
1162
|
+
reject(new Error(error.message || "Rally API query failed"));
|
|
1163
|
+
} else {
|
|
1164
|
+
resolve(result);
|
|
1165
|
+
}
|
|
1166
|
+
});
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
createRally(type, data) {
|
|
1170
|
+
return new Promise((resolve, reject) => {
|
|
1171
|
+
this.restApi.create({
|
|
1172
|
+
type,
|
|
1173
|
+
data,
|
|
1174
|
+
fetch: ["FormattedID", "ObjectID", "_ref"]
|
|
1175
|
+
}, (error, result) => {
|
|
1176
|
+
if (error) {
|
|
1177
|
+
reject(new Error(error.message || "Rally API create failed"));
|
|
1178
|
+
} else {
|
|
1179
|
+
resolve(result);
|
|
1180
|
+
}
|
|
1181
|
+
});
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
updateRally(type, ref, data) {
|
|
1185
|
+
return new Promise((resolve, reject) => {
|
|
1186
|
+
this.restApi.update({
|
|
1187
|
+
type,
|
|
1188
|
+
ref,
|
|
1189
|
+
data,
|
|
1190
|
+
fetch: ["FormattedID", "ObjectID"]
|
|
1191
|
+
}, (error, result) => {
|
|
1192
|
+
if (error) {
|
|
1193
|
+
reject(new Error(error.message || "Rally API update failed"));
|
|
1194
|
+
} else {
|
|
1195
|
+
resolve(result);
|
|
1196
|
+
}
|
|
1197
|
+
});
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
1200
|
+
};
|
|
1201
|
+
|
|
788
1202
|
// src/lib/tracker/factory.ts
|
|
789
1203
|
function createTracker(config) {
|
|
790
1204
|
switch (config.type) {
|
|
@@ -826,6 +1240,21 @@ function createTracker(config) {
|
|
|
826
1240
|
}
|
|
827
1241
|
return new GitLabTracker(token, config.projectId);
|
|
828
1242
|
}
|
|
1243
|
+
case "rally": {
|
|
1244
|
+
const apiKey = config.apiKeyEnv ? process.env[config.apiKeyEnv] : process.env.RALLY_API_KEY;
|
|
1245
|
+
if (!apiKey) {
|
|
1246
|
+
throw new TrackerAuthError(
|
|
1247
|
+
"rally",
|
|
1248
|
+
`API key not found. Set ${config.apiKeyEnv ?? "RALLY_API_KEY"} environment variable.`
|
|
1249
|
+
);
|
|
1250
|
+
}
|
|
1251
|
+
return new RallyTracker({
|
|
1252
|
+
apiKey,
|
|
1253
|
+
server: config.server,
|
|
1254
|
+
workspace: config.workspace,
|
|
1255
|
+
project: config.project
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
829
1258
|
default:
|
|
830
1259
|
throw new Error(`Unknown tracker type: ${config.type}`);
|
|
831
1260
|
}
|
|
@@ -858,9 +1287,10 @@ function getAllTrackers(trackersConfig) {
|
|
|
858
1287
|
}
|
|
859
1288
|
|
|
860
1289
|
// src/lib/tracker/linking.ts
|
|
1290
|
+
init_esm_shims();
|
|
861
1291
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
862
|
-
import { join as
|
|
863
|
-
import { homedir as
|
|
1292
|
+
import { join as join4 } from "path";
|
|
1293
|
+
import { homedir as homedir2 } from "os";
|
|
864
1294
|
function parseIssueRef(ref) {
|
|
865
1295
|
if (ref.startsWith("github#")) {
|
|
866
1296
|
return { tracker: "github", ref: `#${ref.slice(7)}` };
|
|
@@ -892,7 +1322,7 @@ var LinkManager = class {
|
|
|
892
1322
|
storePath;
|
|
893
1323
|
store;
|
|
894
1324
|
constructor(storePath) {
|
|
895
|
-
this.storePath = storePath ??
|
|
1325
|
+
this.storePath = storePath ?? join4(homedir2(), ".panopticon", "links.json");
|
|
896
1326
|
this.store = this.load();
|
|
897
1327
|
}
|
|
898
1328
|
load() {
|
|
@@ -908,7 +1338,7 @@ var LinkManager = class {
|
|
|
908
1338
|
return { version: 1, links: [] };
|
|
909
1339
|
}
|
|
910
1340
|
save() {
|
|
911
|
-
const dir =
|
|
1341
|
+
const dir = join4(this.storePath, "..");
|
|
912
1342
|
if (!existsSync5(dir)) {
|
|
913
1343
|
mkdirSync3(dir, { recursive: true });
|
|
914
1344
|
}
|
|
@@ -998,31 +1428,10 @@ function getLinkManager() {
|
|
|
998
1428
|
return _linkManager;
|
|
999
1429
|
}
|
|
1000
1430
|
|
|
1431
|
+
// src/lib/tracker/index.ts
|
|
1432
|
+
init_esm_shims();
|
|
1433
|
+
|
|
1001
1434
|
export {
|
|
1002
|
-
__require,
|
|
1003
|
-
__commonJS,
|
|
1004
|
-
PANOPTICON_HOME,
|
|
1005
|
-
CONFIG_DIR,
|
|
1006
|
-
SKILLS_DIR,
|
|
1007
|
-
COMMANDS_DIR,
|
|
1008
|
-
AGENTS_DIR,
|
|
1009
|
-
BACKUPS_DIR,
|
|
1010
|
-
COSTS_DIR,
|
|
1011
|
-
TRAEFIK_DIR,
|
|
1012
|
-
TRAEFIK_DYNAMIC_DIR,
|
|
1013
|
-
TRAEFIK_CERTS_DIR,
|
|
1014
|
-
CERTS_DIR,
|
|
1015
|
-
CONFIG_FILE,
|
|
1016
|
-
CLAUDE_DIR,
|
|
1017
|
-
CODEX_DIR,
|
|
1018
|
-
CURSOR_DIR,
|
|
1019
|
-
GEMINI_DIR,
|
|
1020
|
-
SYNC_TARGETS,
|
|
1021
|
-
TEMPLATES_DIR,
|
|
1022
|
-
CLAUDE_MD_TEMPLATES,
|
|
1023
|
-
SOURCE_TEMPLATES_DIR,
|
|
1024
|
-
SOURCE_TRAEFIK_TEMPLATES,
|
|
1025
|
-
INIT_DIRS,
|
|
1026
1435
|
loadConfig,
|
|
1027
1436
|
saveConfig,
|
|
1028
1437
|
getDefaultConfig,
|
|
@@ -1039,6 +1448,8 @@ export {
|
|
|
1039
1448
|
isPanopticonSymlink,
|
|
1040
1449
|
planSync,
|
|
1041
1450
|
executeSync,
|
|
1451
|
+
planHooksSync,
|
|
1452
|
+
syncHooks,
|
|
1042
1453
|
NotImplementedError,
|
|
1043
1454
|
IssueNotFoundError,
|
|
1044
1455
|
TrackerAuthError,
|
|
@@ -1055,4 +1466,4 @@ export {
|
|
|
1055
1466
|
LinkManager,
|
|
1056
1467
|
getLinkManager
|
|
1057
1468
|
};
|
|
1058
|
-
//# sourceMappingURL=chunk-
|
|
1469
|
+
//# sourceMappingURL=chunk-B2JBBOJN.js.map
|