panopticon-cli 0.3.2 → 0.3.4
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 +239 -1
- package/dist/{chunk-PSJRCUOA.js → chunk-BH6BR26M.js} +3 -7
- package/dist/{chunk-PSJRCUOA.js.map → chunk-BH6BR26M.js.map} +1 -1
- package/dist/{chunk-B2JBBOJN.js → chunk-C6A7S65K.js} +3 -24
- package/dist/chunk-C6A7S65K.js.map +1 -0
- package/dist/chunk-P5TQ5C3J.js +103 -0
- package/dist/chunk-P5TQ5C3J.js.map +1 -0
- package/dist/cli/index.js +1499 -1300
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +3 -9
- package/dist/index.js.map +1 -1
- package/dist/{projects-6JVKIYIH.js → projects-54CV437J.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-B2JBBOJN.js.map +0 -1
- package/dist/chunk-SG7O6I7R.js +0 -155
- package/dist/chunk-SG7O6I7R.js.map +0 -1
- /package/dist/{projects-6JVKIYIH.js.map → projects-54CV437J.js.map} +0 -0
package/README.md
CHANGED
|
@@ -340,6 +340,44 @@ Cloister manages specialized agents that handle specific phases of the developme
|
|
|
340
340
|
| **review-agent** | Code review before merge | After tests pass (manual trigger) |
|
|
341
341
|
| **merge-agent** | Handles git merge and conflict resolution | "Approve & Merge" button |
|
|
342
342
|
|
|
343
|
+
#### Merge Agent Workflow
|
|
344
|
+
|
|
345
|
+
The merge-agent is a specialist that handles **ALL** merges, not just conflicts. This ensures:
|
|
346
|
+
- It sees all code changes coming through the pipeline
|
|
347
|
+
- It builds context about the codebase over time
|
|
348
|
+
- When conflicts DO occur, it has better understanding for intelligent resolution
|
|
349
|
+
- Tests are always run before completing the merge
|
|
350
|
+
|
|
351
|
+
**Workflow:**
|
|
352
|
+
|
|
353
|
+
1. **Pull latest main** - Ensures local main is up-to-date
|
|
354
|
+
2. **Analyze incoming changes** - Reviews what the feature branch contains
|
|
355
|
+
3. **Perform merge** - Merges feature branch into main
|
|
356
|
+
4. **Resolve conflicts** - If conflicts exist, uses AI to resolve them intelligently
|
|
357
|
+
5. **Run tests** - Verifies the merge didn't break anything
|
|
358
|
+
6. **Commit merge** - Commits the merge with descriptive message
|
|
359
|
+
7. **Report results** - Returns success/failure with details
|
|
360
|
+
|
|
361
|
+
**Triggering merge-agent:**
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
# Via dashboard - click "Approve & Merge" on an issue card
|
|
365
|
+
# merge-agent is ALWAYS invoked, regardless of whether conflicts exist
|
|
366
|
+
|
|
367
|
+
# Via CLI
|
|
368
|
+
pan specialists wake merge-agent --issue MIN-123
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
The merge-agent uses a specialized prompt template that instructs it to:
|
|
372
|
+
- Never force-push
|
|
373
|
+
- Always run tests before completing
|
|
374
|
+
- Document conflict resolution decisions
|
|
375
|
+
- Provide detailed feedback on what was merged
|
|
376
|
+
|
|
377
|
+
#### Specialist Auto-Initialization
|
|
378
|
+
|
|
379
|
+
When Cloister starts, it automatically initializes specialists that don't exist yet. This ensures the test-agent, review-agent, and merge-agent are ready to receive wake signals without manual setup.
|
|
380
|
+
|
|
343
381
|
### Automatic Handoffs
|
|
344
382
|
|
|
345
383
|
Cloister detects situations that require intervention:
|
|
@@ -349,8 +387,55 @@ Cloister detects situations that require intervention:
|
|
|
349
387
|
| **stuck_escalation** | No activity for 30+ minutes | Escalate to more capable model |
|
|
350
388
|
| **complexity_upgrade** | Task complexity exceeds model capability | Route to Opus |
|
|
351
389
|
| **implementation_complete** | Agent signals work is done | Wake test-agent |
|
|
390
|
+
| **test_failure** | Tests fail repeatedly | Escalate model or request help |
|
|
391
|
+
| **planning_complete** | Planning session finishes | Transition to implementation |
|
|
352
392
|
| **merge_requested** | User clicks "Approve & Merge" | Wake merge-agent |
|
|
353
393
|
|
|
394
|
+
### Handoff Methods
|
|
395
|
+
|
|
396
|
+
Cloister supports two handoff methods, automatically selected based on agent type:
|
|
397
|
+
|
|
398
|
+
| Method | When Used | How It Works |
|
|
399
|
+
|--------|-----------|--------------|
|
|
400
|
+
| **Kill & Spawn** | General agents (agent-min-123, etc.) | 1. Captures full context (STATE.md, beads, git state)<br>2. Kills tmux session<br>3. Spawns new agent with handoff prompt<br>4. New agent continues work with preserved context |
|
|
401
|
+
| **Specialist Wake** | Permanent specialists (merge-agent, test-agent) | 1. Captures handoff context<br>2. Sends wake message to existing session<br>3. Specialist resumes with context injection |
|
|
402
|
+
|
|
403
|
+
**Kill & Spawn** is used for temporary agents that work on specific issues. It creates a clean handoff by:
|
|
404
|
+
- Capturing the agent's current understanding (from STATE.md)
|
|
405
|
+
- Preserving beads task progress and open items
|
|
406
|
+
- Including relevant git diff and file context
|
|
407
|
+
- Building a comprehensive handoff prompt for the new model
|
|
408
|
+
|
|
409
|
+
**Specialist Wake** is used for permanent specialists that persist across multiple issues. It avoids the overhead of killing/respawning by injecting context into the existing session.
|
|
410
|
+
|
|
411
|
+
### Handoff Context Capture
|
|
412
|
+
|
|
413
|
+
When a handoff occurs, Cloister captures:
|
|
414
|
+
|
|
415
|
+
```json
|
|
416
|
+
{
|
|
417
|
+
"agentId": "agent-min-123",
|
|
418
|
+
"issueId": "MIN-123",
|
|
419
|
+
"currentModel": "sonnet",
|
|
420
|
+
"targetModel": "opus",
|
|
421
|
+
"reason": "stuck_escalation",
|
|
422
|
+
"handoffCount": 1,
|
|
423
|
+
"state": {
|
|
424
|
+
"phase": "implementation",
|
|
425
|
+
"complexity": "complex",
|
|
426
|
+
"lastActivity": "2024-01-22T10:30:00-08:00"
|
|
427
|
+
},
|
|
428
|
+
"beadsTasks": [...],
|
|
429
|
+
"gitContext": {
|
|
430
|
+
"branch": "feature/min-123",
|
|
431
|
+
"uncommittedChanges": ["src/auth.ts", "src/tests/auth.test.ts"],
|
|
432
|
+
"recentCommits": [...]
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
Handoff prompts are saved to `~/.panopticon/agents/{agent-id}/handoffs/` for debugging.
|
|
438
|
+
|
|
354
439
|
### Heartbeat Monitoring
|
|
355
440
|
|
|
356
441
|
Agents send heartbeats via Claude Code hooks. Cloister tracks:
|
|
@@ -373,6 +458,41 @@ Heartbeat files are stored in `~/.panopticon/heartbeats/`:
|
|
|
373
458
|
}
|
|
374
459
|
```
|
|
375
460
|
|
|
461
|
+
### Heartbeat Hook Installation
|
|
462
|
+
|
|
463
|
+
The heartbeat hook is automatically synced to `~/.panopticon/bin/heartbeat-hook` via `pan sync`. It's also installed automatically when you install or upgrade Panopticon via npm.
|
|
464
|
+
|
|
465
|
+
**Manual installation:**
|
|
466
|
+
```bash
|
|
467
|
+
pan sync # Syncs all skills, agents, AND hooks
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
**Hook configuration in `~/.claude/settings.json`:**
|
|
471
|
+
```json
|
|
472
|
+
{
|
|
473
|
+
"hooks": {
|
|
474
|
+
"PostToolUse": [
|
|
475
|
+
{
|
|
476
|
+
"matcher": "*",
|
|
477
|
+
"hooks": [
|
|
478
|
+
{
|
|
479
|
+
"type": "command",
|
|
480
|
+
"command": "~/.panopticon/bin/heartbeat-hook"
|
|
481
|
+
}
|
|
482
|
+
]
|
|
483
|
+
}
|
|
484
|
+
]
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
**Hook resilience:** The heartbeat hook is designed to fail silently if:
|
|
490
|
+
- The heartbeats directory doesn't exist
|
|
491
|
+
- Write permissions are missing
|
|
492
|
+
- The hook script has errors
|
|
493
|
+
|
|
494
|
+
This prevents hook failures from interrupting agent work.
|
|
495
|
+
|
|
376
496
|
### Configuration
|
|
377
497
|
|
|
378
498
|
Cloister configuration lives in `~/.panopticon/cloister/config.json`:
|
|
@@ -398,6 +518,77 @@ Cloister configuration lives in `~/.panopticon/cloister/config.json`:
|
|
|
398
518
|
|
|
399
519
|
---
|
|
400
520
|
|
|
521
|
+
## Model Routing & Complexity Detection
|
|
522
|
+
|
|
523
|
+
Cloister automatically routes tasks to the appropriate model based on detected complexity, optimizing for cost while ensuring quality.
|
|
524
|
+
|
|
525
|
+
### Complexity Levels
|
|
526
|
+
|
|
527
|
+
| Level | Model | Use Case |
|
|
528
|
+
|-------|-------|----------|
|
|
529
|
+
| **trivial** | Haiku | Typos, comments, documentation updates |
|
|
530
|
+
| **simple** | Haiku | Small fixes, test additions, minor changes |
|
|
531
|
+
| **medium** | Sonnet | Features, components, integrations |
|
|
532
|
+
| **complex** | Sonnet/Opus | Refactors, migrations, redesigns |
|
|
533
|
+
| **expert** | Opus | Architecture, security, performance optimization |
|
|
534
|
+
|
|
535
|
+
### Complexity Detection Signals
|
|
536
|
+
|
|
537
|
+
Complexity is detected from multiple signals (in priority order):
|
|
538
|
+
|
|
539
|
+
1. **Explicit field** - Task has a `complexity` field set (e.g., in beads)
|
|
540
|
+
2. **Labels/tags** - Issue labels like `architecture`, `security`, `refactor`
|
|
541
|
+
3. **Keywords** - Title/description contains keywords like "migration", "overhaul"
|
|
542
|
+
4. **File count** - Number of files changed (>20 files = complex)
|
|
543
|
+
5. **Time estimate** - If estimate exceeds thresholds
|
|
544
|
+
|
|
545
|
+
**Keyword patterns:**
|
|
546
|
+
```javascript
|
|
547
|
+
{
|
|
548
|
+
trivial: ['typo', 'rename', 'comment', 'documentation', 'readme'],
|
|
549
|
+
simple: ['add comment', 'update docs', 'fix typo', 'small fix'],
|
|
550
|
+
medium: ['feature', 'endpoint', 'component', 'service'],
|
|
551
|
+
complex: ['refactor', 'migration', 'redesign', 'overhaul'],
|
|
552
|
+
expert: ['architecture', 'security', 'performance optimization']
|
|
553
|
+
}
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
### Configuring Model Routing
|
|
557
|
+
|
|
558
|
+
Edit `~/.panopticon/cloister/config.json`:
|
|
559
|
+
|
|
560
|
+
```json
|
|
561
|
+
{
|
|
562
|
+
"model_selection": {
|
|
563
|
+
"default_model": "sonnet",
|
|
564
|
+
"complexity_routing": {
|
|
565
|
+
"trivial": "haiku",
|
|
566
|
+
"simple": "haiku",
|
|
567
|
+
"medium": "sonnet",
|
|
568
|
+
"complex": "sonnet",
|
|
569
|
+
"expert": "opus"
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### Cost Optimization
|
|
576
|
+
|
|
577
|
+
Model routing helps optimize costs:
|
|
578
|
+
|
|
579
|
+
| Model | Relative Cost | Best For |
|
|
580
|
+
|-------|---------------|----------|
|
|
581
|
+
| Haiku | 1x (cheapest) | Simple tasks, bulk operations |
|
|
582
|
+
| Sonnet | 3x | Most development work |
|
|
583
|
+
| Opus | 15x | Complex architecture, critical fixes |
|
|
584
|
+
|
|
585
|
+
A typical agent run might:
|
|
586
|
+
1. Start on Haiku for initial exploration
|
|
587
|
+
2. Escalate to Sonnet for implementation
|
|
588
|
+
3. Escalate to Opus only if stuck or complexity detected
|
|
589
|
+
|
|
590
|
+
---
|
|
591
|
+
|
|
401
592
|
## Multi-Project Support
|
|
402
593
|
|
|
403
594
|
Panopticon supports managing multiple projects with intelligent issue routing.
|
|
@@ -462,9 +653,22 @@ pan sync --dry-run # Preview what will be synced
|
|
|
462
653
|
pan doctor # Check system health
|
|
463
654
|
pan skills # List available skills
|
|
464
655
|
pan status # Show running agents
|
|
656
|
+
pan up # Start dashboard (Docker or minimal)
|
|
657
|
+
pan down # Stop dashboard and services
|
|
465
658
|
```
|
|
466
659
|
|
|
467
|
-
|
|
660
|
+
#### What `pan sync` Does
|
|
661
|
+
|
|
662
|
+
`pan sync` synchronizes Panopticon assets to all supported AI tools:
|
|
663
|
+
|
|
664
|
+
| Asset Type | Source | Destinations |
|
|
665
|
+
|------------|--------|--------------|
|
|
666
|
+
| **Skills** | `~/.panopticon/skills/` | `~/.claude/skills/`, `~/.codex/skills/`, `~/.gemini/skills/` |
|
|
667
|
+
| **Agents** | `~/.panopticon/agents/*.md` | `~/.claude/agents/` |
|
|
668
|
+
| **Commands** | `~/.panopticon/commands/` | `~/.claude/commands/` |
|
|
669
|
+
| **Hooks** | `src/hooks/` (in package) | `~/.panopticon/bin/` |
|
|
670
|
+
|
|
671
|
+
**Automatic sync:** Hooks are also synced automatically when you install or upgrade Panopticon via npm (`postinstall` hook).
|
|
468
672
|
|
|
469
673
|
### Agent Management
|
|
470
674
|
|
|
@@ -1029,6 +1233,7 @@ This ensures every Panopticon-managed project has a well-defined canonical PRD t
|
|
|
1029
1233
|
hook.json # GUPP work queue
|
|
1030
1234
|
cv.json # Work history
|
|
1031
1235
|
mail/ # Incoming messages
|
|
1236
|
+
handoffs/ # Handoff prompts (for debugging)
|
|
1032
1237
|
|
|
1033
1238
|
cloister/ # Cloister AI lifecycle manager
|
|
1034
1239
|
config.json # Cloister settings
|
|
@@ -1038,6 +1243,9 @@ This ensures every Panopticon-managed project has a well-defined canonical PRD t
|
|
|
1038
1243
|
heartbeats/ # Real-time agent activity
|
|
1039
1244
|
agent-min-123.json # Last heartbeat from agent
|
|
1040
1245
|
|
|
1246
|
+
logs/ # Log files
|
|
1247
|
+
handoffs.jsonl # All handoff events (for analytics)
|
|
1248
|
+
|
|
1041
1249
|
costs/ # Raw cost logs (JSONL)
|
|
1042
1250
|
backups/ # Sync backups
|
|
1043
1251
|
traefik/ # Traefik reverse proxy config
|
|
@@ -1045,6 +1253,36 @@ This ensures every Panopticon-managed project has a well-defined canonical PRD t
|
|
|
1045
1253
|
certs/ # TLS certificates
|
|
1046
1254
|
```
|
|
1047
1255
|
|
|
1256
|
+
### Agent State Management
|
|
1257
|
+
|
|
1258
|
+
Each agent's state is tracked in `~/.panopticon/agents/{agent-id}/state.json`:
|
|
1259
|
+
|
|
1260
|
+
```json
|
|
1261
|
+
{
|
|
1262
|
+
"id": "agent-min-123",
|
|
1263
|
+
"issueId": "MIN-123",
|
|
1264
|
+
"workspace": "/home/user/projects/myapp/workspaces/feature-min-123",
|
|
1265
|
+
"branch": "feature/min-123",
|
|
1266
|
+
"phase": "implementation",
|
|
1267
|
+
"model": "sonnet",
|
|
1268
|
+
"complexity": "medium",
|
|
1269
|
+
"handoffCount": 0,
|
|
1270
|
+
"sessionId": "abc123",
|
|
1271
|
+
"createdAt": "2024-01-22T10:00:00-08:00",
|
|
1272
|
+
"updatedAt": "2024-01-22T10:30:00-08:00"
|
|
1273
|
+
}
|
|
1274
|
+
```
|
|
1275
|
+
|
|
1276
|
+
| Field | Description |
|
|
1277
|
+
|-------|-------------|
|
|
1278
|
+
| `phase` | Current work phase: `planning`, `implementation`, `testing`, `review`, `merging` |
|
|
1279
|
+
| `model` | Current model: `haiku`, `sonnet`, `opus` |
|
|
1280
|
+
| `complexity` | Detected complexity: `trivial`, `simple`, `medium`, `complex`, `expert` |
|
|
1281
|
+
| `handoffCount` | Number of times the agent has been handed off to a different model |
|
|
1282
|
+
| `sessionId` | Claude Code session ID (for resuming after handoff) |
|
|
1283
|
+
|
|
1284
|
+
**State Cleanup:** When an agent is killed or aborted (`pan work kill`), Panopticon automatically cleans up its state files to prevent stale data from affecting future runs.
|
|
1285
|
+
|
|
1048
1286
|
## Health Monitoring (Deacon Pattern)
|
|
1049
1287
|
|
|
1050
1288
|
Panopticon implements the Deacon pattern for stuck agent detection:
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
-
PANOPTICON_HOME
|
|
3
|
-
|
|
4
|
-
init_paths
|
|
5
|
-
} from "./chunk-SG7O6I7R.js";
|
|
2
|
+
PANOPTICON_HOME
|
|
3
|
+
} from "./chunk-P5TQ5C3J.js";
|
|
6
4
|
|
|
7
5
|
// src/lib/projects.ts
|
|
8
|
-
init_esm_shims();
|
|
9
|
-
init_paths();
|
|
10
6
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
11
7
|
import { join } from "path";
|
|
12
8
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
@@ -174,4 +170,4 @@ export {
|
|
|
174
170
|
createDefaultProjectsConfig,
|
|
175
171
|
initializeProjectsConfig
|
|
176
172
|
};
|
|
177
|
-
//# sourceMappingURL=chunk-
|
|
173
|
+
//# sourceMappingURL=chunk-BH6BR26M.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/projects.ts"],"sourcesContent":["/**\n * Project Registry - Multi-project support for Panopticon\n *\n * Maps Linear team prefixes and labels to project paths for workspace creation.\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { join } from 'path';\nimport { parse as parseYaml, stringify as stringifyYaml } from 'yaml';\nimport { PANOPTICON_HOME } from './paths.js';\n\nexport const PROJECTS_CONFIG_FILE = join(PANOPTICON_HOME, 'projects.yaml');\n\n/**\n * Issue routing rule - routes issues with certain labels to specific paths\n */\nexport interface IssueRoutingRule {\n labels?: string[];\n default?: boolean;\n path: string;\n}\n\n/**\n * Project configuration\n */\nexport interface ProjectConfig {\n name: string;\n path: string;\n linear_team?: string;\n issue_routing?: IssueRoutingRule[];\n}\n\n/**\n * Full projects configuration file\n */\nexport interface ProjectsConfig {\n projects: Record<string, ProjectConfig>;\n}\n\n/**\n * Resolved project info for workspace creation\n */\nexport interface ResolvedProject {\n projectKey: string;\n projectName: string;\n projectPath: string;\n linearTeam?: string;\n}\n\n/**\n * Load projects configuration from ~/.panopticon/projects.yaml\n */\nexport function loadProjectsConfig(): ProjectsConfig {\n if (!existsSync(PROJECTS_CONFIG_FILE)) {\n return { projects: {} };\n }\n\n try {\n const content = readFileSync(PROJECTS_CONFIG_FILE, 'utf-8');\n const config = parseYaml(content) as ProjectsConfig;\n return config || { projects: {} };\n } catch (error: any) {\n console.error(`Failed to parse projects.yaml: ${error.message}`);\n return { projects: {} };\n }\n}\n\n/**\n * Save projects configuration\n */\nexport function saveProjectsConfig(config: ProjectsConfig): void {\n const dir = PANOPTICON_HOME;\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n const yaml = stringifyYaml(config, { indent: 2 });\n writeFileSync(PROJECTS_CONFIG_FILE, yaml, 'utf-8');\n}\n\n/**\n * Get a list of all registered projects\n */\nexport function listProjects(): Array<{ key: string; config: ProjectConfig }> {\n const config = loadProjectsConfig();\n return Object.entries(config.projects).map(([key, projectConfig]) => ({\n key,\n config: projectConfig,\n }));\n}\n\n/**\n * Add or update a project in the registry\n */\nexport function registerProject(key: string, projectConfig: ProjectConfig): void {\n const config = loadProjectsConfig();\n config.projects[key] = projectConfig;\n saveProjectsConfig(config);\n}\n\n/**\n * Remove a project from the registry\n */\nexport function unregisterProject(key: string): boolean {\n const config = loadProjectsConfig();\n if (config.projects[key]) {\n delete config.projects[key];\n saveProjectsConfig(config);\n return true;\n }\n return false;\n}\n\n/**\n * Extract Linear team prefix from an issue ID\n * E.g., \"MIN-123\" -> \"MIN\", \"PAN-456\" -> \"PAN\"\n */\nexport function extractTeamPrefix(issueId: string): string | null {\n const match = issueId.match(/^([A-Z]+)-\\d+$/i);\n return match ? match[1].toUpperCase() : null;\n}\n\n/**\n * Find project by Linear team prefix\n */\nexport function findProjectByTeam(teamPrefix: string): ProjectConfig | null {\n const config = loadProjectsConfig();\n\n for (const [, projectConfig] of Object.entries(config.projects)) {\n if (projectConfig.linear_team?.toUpperCase() === teamPrefix.toUpperCase()) {\n return projectConfig;\n }\n }\n\n return null;\n}\n\n/**\n * Resolve the correct project path for an issue based on labels\n *\n * @param project - The project config\n * @param labels - Array of label names from the Linear issue\n * @returns The resolved path (may differ from project.path based on routing rules)\n */\nexport function resolveProjectPath(project: ProjectConfig, labels: string[] = []): string {\n if (!project.issue_routing || project.issue_routing.length === 0) {\n return project.path;\n }\n\n // Normalize labels to lowercase for comparison\n const normalizedLabels = labels.map(l => l.toLowerCase());\n\n // First, check label-based routing rules\n for (const rule of project.issue_routing) {\n if (rule.labels && rule.labels.length > 0) {\n const ruleLabels = rule.labels.map(l => l.toLowerCase());\n const hasMatch = ruleLabels.some(label => normalizedLabels.includes(label));\n if (hasMatch) {\n return rule.path;\n }\n }\n }\n\n // Then, find default rule\n for (const rule of project.issue_routing) {\n if (rule.default) {\n return rule.path;\n }\n }\n\n // Fall back to project path\n return project.path;\n}\n\n/**\n * Resolve project from an issue ID (and optional labels)\n *\n * @param issueId - Linear issue ID (e.g., \"MIN-123\")\n * @param labels - Optional array of label names\n * @returns Resolved project info or null if not found\n */\nexport function resolveProjectFromIssue(\n issueId: string,\n labels: string[] = []\n): ResolvedProject | null {\n const teamPrefix = extractTeamPrefix(issueId);\n if (!teamPrefix) {\n return null;\n }\n\n const config = loadProjectsConfig();\n\n // Find project by team prefix\n for (const [key, projectConfig] of Object.entries(config.projects)) {\n if (projectConfig.linear_team?.toUpperCase() === teamPrefix) {\n const resolvedPath = resolveProjectPath(projectConfig, labels);\n return {\n projectKey: key,\n projectName: projectConfig.name,\n projectPath: resolvedPath,\n linearTeam: projectConfig.linear_team,\n };\n }\n }\n\n return null;\n}\n\n/**\n * Get a project by key\n */\nexport function getProject(key: string): ProjectConfig | null {\n const config = loadProjectsConfig();\n return config.projects[key] || null;\n}\n\n/**\n * Check if projects.yaml exists and has any projects\n */\nexport function hasProjects(): boolean {\n const config = loadProjectsConfig();\n return Object.keys(config.projects).length > 0;\n}\n\n/**\n * Create a default projects.yaml with example structure\n */\nexport function createDefaultProjectsConfig(): ProjectsConfig {\n const defaultConfig: ProjectsConfig = {\n projects: {\n // Example project - commented out in actual file\n },\n };\n\n return defaultConfig;\n}\n\n/**\n * Initialize projects.yaml with example configuration\n */\nexport function initializeProjectsConfig(): void {\n if (existsSync(PROJECTS_CONFIG_FILE)) {\n console.log(`Projects config already exists at ${PROJECTS_CONFIG_FILE}`);\n return;\n }\n\n const exampleYaml = `# Panopticon Project Registry\n# Maps Linear teams to project paths for workspace creation\n\nprojects:\n # Example: Mind Your Now project\n # myn:\n # name: \"Mind Your Now\"\n # path: /home/user/projects/myn\n # linear_team: MIN\n # issue_routing:\n # # Route docs/marketing issues to docs repo\n # - labels: [docs, marketing, seo, landing-pages]\n # path: /home/user/projects/myn/docs\n # # Default: main repo\n # - default: true\n # path: /home/user/projects/myn\n\n # Example: Panopticon itself\n # panopticon:\n # name: \"Panopticon\"\n # path: /home/user/projects/panopticon\n # linear_team: PAN\n`;\n\n const dir = PANOPTICON_HOME;\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n writeFileSync(PROJECTS_CONFIG_FILE, exampleYaml, 'utf-8');\n console.log(`Created example projects config at ${PROJECTS_CONFIG_FILE}`);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/projects.ts"],"sourcesContent":["/**\n * Project Registry - Multi-project support for Panopticon\n *\n * Maps Linear team prefixes and labels to project paths for workspace creation.\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { join } from 'path';\nimport { parse as parseYaml, stringify as stringifyYaml } from 'yaml';\nimport { PANOPTICON_HOME } from './paths.js';\n\nexport const PROJECTS_CONFIG_FILE = join(PANOPTICON_HOME, 'projects.yaml');\n\n/**\n * Issue routing rule - routes issues with certain labels to specific paths\n */\nexport interface IssueRoutingRule {\n labels?: string[];\n default?: boolean;\n path: string;\n}\n\n/**\n * Project configuration\n */\nexport interface ProjectConfig {\n name: string;\n path: string;\n linear_team?: string;\n issue_routing?: IssueRoutingRule[];\n}\n\n/**\n * Full projects configuration file\n */\nexport interface ProjectsConfig {\n projects: Record<string, ProjectConfig>;\n}\n\n/**\n * Resolved project info for workspace creation\n */\nexport interface ResolvedProject {\n projectKey: string;\n projectName: string;\n projectPath: string;\n linearTeam?: string;\n}\n\n/**\n * Load projects configuration from ~/.panopticon/projects.yaml\n */\nexport function loadProjectsConfig(): ProjectsConfig {\n if (!existsSync(PROJECTS_CONFIG_FILE)) {\n return { projects: {} };\n }\n\n try {\n const content = readFileSync(PROJECTS_CONFIG_FILE, 'utf-8');\n const config = parseYaml(content) as ProjectsConfig;\n return config || { projects: {} };\n } catch (error: any) {\n console.error(`Failed to parse projects.yaml: ${error.message}`);\n return { projects: {} };\n }\n}\n\n/**\n * Save projects configuration\n */\nexport function saveProjectsConfig(config: ProjectsConfig): void {\n const dir = PANOPTICON_HOME;\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n const yaml = stringifyYaml(config, { indent: 2 });\n writeFileSync(PROJECTS_CONFIG_FILE, yaml, 'utf-8');\n}\n\n/**\n * Get a list of all registered projects\n */\nexport function listProjects(): Array<{ key: string; config: ProjectConfig }> {\n const config = loadProjectsConfig();\n return Object.entries(config.projects).map(([key, projectConfig]) => ({\n key,\n config: projectConfig,\n }));\n}\n\n/**\n * Add or update a project in the registry\n */\nexport function registerProject(key: string, projectConfig: ProjectConfig): void {\n const config = loadProjectsConfig();\n config.projects[key] = projectConfig;\n saveProjectsConfig(config);\n}\n\n/**\n * Remove a project from the registry\n */\nexport function unregisterProject(key: string): boolean {\n const config = loadProjectsConfig();\n if (config.projects[key]) {\n delete config.projects[key];\n saveProjectsConfig(config);\n return true;\n }\n return false;\n}\n\n/**\n * Extract Linear team prefix from an issue ID\n * E.g., \"MIN-123\" -> \"MIN\", \"PAN-456\" -> \"PAN\"\n */\nexport function extractTeamPrefix(issueId: string): string | null {\n const match = issueId.match(/^([A-Z]+)-\\d+$/i);\n return match ? match[1].toUpperCase() : null;\n}\n\n/**\n * Find project by Linear team prefix\n */\nexport function findProjectByTeam(teamPrefix: string): ProjectConfig | null {\n const config = loadProjectsConfig();\n\n for (const [, projectConfig] of Object.entries(config.projects)) {\n if (projectConfig.linear_team?.toUpperCase() === teamPrefix.toUpperCase()) {\n return projectConfig;\n }\n }\n\n return null;\n}\n\n/**\n * Resolve the correct project path for an issue based on labels\n *\n * @param project - The project config\n * @param labels - Array of label names from the Linear issue\n * @returns The resolved path (may differ from project.path based on routing rules)\n */\nexport function resolveProjectPath(project: ProjectConfig, labels: string[] = []): string {\n if (!project.issue_routing || project.issue_routing.length === 0) {\n return project.path;\n }\n\n // Normalize labels to lowercase for comparison\n const normalizedLabels = labels.map(l => l.toLowerCase());\n\n // First, check label-based routing rules\n for (const rule of project.issue_routing) {\n if (rule.labels && rule.labels.length > 0) {\n const ruleLabels = rule.labels.map(l => l.toLowerCase());\n const hasMatch = ruleLabels.some(label => normalizedLabels.includes(label));\n if (hasMatch) {\n return rule.path;\n }\n }\n }\n\n // Then, find default rule\n for (const rule of project.issue_routing) {\n if (rule.default) {\n return rule.path;\n }\n }\n\n // Fall back to project path\n return project.path;\n}\n\n/**\n * Resolve project from an issue ID (and optional labels)\n *\n * @param issueId - Linear issue ID (e.g., \"MIN-123\")\n * @param labels - Optional array of label names\n * @returns Resolved project info or null if not found\n */\nexport function resolveProjectFromIssue(\n issueId: string,\n labels: string[] = []\n): ResolvedProject | null {\n const teamPrefix = extractTeamPrefix(issueId);\n if (!teamPrefix) {\n return null;\n }\n\n const config = loadProjectsConfig();\n\n // Find project by team prefix\n for (const [key, projectConfig] of Object.entries(config.projects)) {\n if (projectConfig.linear_team?.toUpperCase() === teamPrefix) {\n const resolvedPath = resolveProjectPath(projectConfig, labels);\n return {\n projectKey: key,\n projectName: projectConfig.name,\n projectPath: resolvedPath,\n linearTeam: projectConfig.linear_team,\n };\n }\n }\n\n return null;\n}\n\n/**\n * Get a project by key\n */\nexport function getProject(key: string): ProjectConfig | null {\n const config = loadProjectsConfig();\n return config.projects[key] || null;\n}\n\n/**\n * Check if projects.yaml exists and has any projects\n */\nexport function hasProjects(): boolean {\n const config = loadProjectsConfig();\n return Object.keys(config.projects).length > 0;\n}\n\n/**\n * Create a default projects.yaml with example structure\n */\nexport function createDefaultProjectsConfig(): ProjectsConfig {\n const defaultConfig: ProjectsConfig = {\n projects: {\n // Example project - commented out in actual file\n },\n };\n\n return defaultConfig;\n}\n\n/**\n * Initialize projects.yaml with example configuration\n */\nexport function initializeProjectsConfig(): void {\n if (existsSync(PROJECTS_CONFIG_FILE)) {\n console.log(`Projects config already exists at ${PROJECTS_CONFIG_FILE}`);\n return;\n }\n\n const exampleYaml = `# Panopticon Project Registry\n# Maps Linear teams to project paths for workspace creation\n\nprojects:\n # Example: Mind Your Now project\n # myn:\n # name: \"Mind Your Now\"\n # path: /home/user/projects/myn\n # linear_team: MIN\n # issue_routing:\n # # Route docs/marketing issues to docs repo\n # - labels: [docs, marketing, seo, landing-pages]\n # path: /home/user/projects/myn/docs\n # # Default: main repo\n # - default: true\n # path: /home/user/projects/myn\n\n # Example: Panopticon itself\n # panopticon:\n # name: \"Panopticon\"\n # path: /home/user/projects/panopticon\n # linear_team: PAN\n`;\n\n const dir = PANOPTICON_HOME;\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n writeFileSync(PROJECTS_CONFIG_FILE, exampleYaml, 'utf-8');\n console.log(`Created example projects config at ${PROJECTS_CONFIG_FILE}`);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -6,14 +6,10 @@ import {
|
|
|
6
6
|
CONFIG_FILE,
|
|
7
7
|
SKILLS_DIR,
|
|
8
8
|
SOURCE_SCRIPTS_DIR,
|
|
9
|
-
SYNC_TARGETS
|
|
10
|
-
|
|
11
|
-
init_paths
|
|
12
|
-
} from "./chunk-SG7O6I7R.js";
|
|
9
|
+
SYNC_TARGETS
|
|
10
|
+
} from "./chunk-P5TQ5C3J.js";
|
|
13
11
|
|
|
14
12
|
// src/lib/config.ts
|
|
15
|
-
init_esm_shims();
|
|
16
|
-
init_paths();
|
|
17
13
|
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
18
14
|
import { parse, stringify } from "@iarna/toml";
|
|
19
15
|
var DEFAULT_CONFIG = {
|
|
@@ -74,7 +70,6 @@ function getDefaultConfig() {
|
|
|
74
70
|
}
|
|
75
71
|
|
|
76
72
|
// src/lib/shell.ts
|
|
77
|
-
init_esm_shims();
|
|
78
73
|
import { existsSync as existsSync2, readFileSync as readFileSync2, appendFileSync } from "fs";
|
|
79
74
|
import { homedir } from "os";
|
|
80
75
|
import { join } from "path";
|
|
@@ -126,8 +121,6 @@ function getAliasInstructions(shell) {
|
|
|
126
121
|
}
|
|
127
122
|
|
|
128
123
|
// src/lib/backup.ts
|
|
129
|
-
init_esm_shims();
|
|
130
|
-
init_paths();
|
|
131
124
|
import { existsSync as existsSync3, mkdirSync, readdirSync, cpSync, rmSync } from "fs";
|
|
132
125
|
import { join as join2, basename } from "path";
|
|
133
126
|
function createBackupTimestamp() {
|
|
@@ -194,8 +187,6 @@ function cleanOldBackups(keepCount = 10) {
|
|
|
194
187
|
}
|
|
195
188
|
|
|
196
189
|
// src/lib/sync.ts
|
|
197
|
-
init_esm_shims();
|
|
198
|
-
init_paths();
|
|
199
190
|
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readdirSync as readdirSync2, symlinkSync, unlinkSync, lstatSync, readlinkSync, rmSync as rmSync2, copyFileSync, chmodSync } from "fs";
|
|
200
191
|
import { join as join3 } from "path";
|
|
201
192
|
function removeTarget(targetPath) {
|
|
@@ -379,7 +370,6 @@ function syncHooks() {
|
|
|
379
370
|
}
|
|
380
371
|
|
|
381
372
|
// src/lib/tracker/interface.ts
|
|
382
|
-
init_esm_shims();
|
|
383
373
|
var NotImplementedError = class extends Error {
|
|
384
374
|
constructor(feature) {
|
|
385
375
|
super(`Not implemented: ${feature}`);
|
|
@@ -400,7 +390,6 @@ var TrackerAuthError = class extends Error {
|
|
|
400
390
|
};
|
|
401
391
|
|
|
402
392
|
// src/lib/tracker/linear.ts
|
|
403
|
-
init_esm_shims();
|
|
404
393
|
import { LinearClient } from "@linear/sdk";
|
|
405
394
|
var STATE_MAP = {
|
|
406
395
|
backlog: "open",
|
|
@@ -610,7 +599,6 @@ var LinearTracker = class {
|
|
|
610
599
|
};
|
|
611
600
|
|
|
612
601
|
// src/lib/tracker/github.ts
|
|
613
|
-
init_esm_shims();
|
|
614
602
|
import { Octokit } from "@octokit/rest";
|
|
615
603
|
var GitHubTracker = class {
|
|
616
604
|
name = "github";
|
|
@@ -771,7 +759,6 @@ var GitHubTracker = class {
|
|
|
771
759
|
};
|
|
772
760
|
|
|
773
761
|
// src/lib/tracker/gitlab.ts
|
|
774
|
-
init_esm_shims();
|
|
775
762
|
var GitLabTracker = class {
|
|
776
763
|
constructor(token, projectId) {
|
|
777
764
|
this.token = token;
|
|
@@ -820,11 +807,7 @@ var GitLabTracker = class {
|
|
|
820
807
|
}
|
|
821
808
|
};
|
|
822
809
|
|
|
823
|
-
// src/lib/tracker/factory.ts
|
|
824
|
-
init_esm_shims();
|
|
825
|
-
|
|
826
810
|
// src/lib/tracker/rally.ts
|
|
827
|
-
init_esm_shims();
|
|
828
811
|
import rally from "rally";
|
|
829
812
|
var STATE_MAP2 = {
|
|
830
813
|
Defined: "open",
|
|
@@ -1287,7 +1270,6 @@ function getAllTrackers(trackersConfig) {
|
|
|
1287
1270
|
}
|
|
1288
1271
|
|
|
1289
1272
|
// src/lib/tracker/linking.ts
|
|
1290
|
-
init_esm_shims();
|
|
1291
1273
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
1292
1274
|
import { join as join4 } from "path";
|
|
1293
1275
|
import { homedir as homedir2 } from "os";
|
|
@@ -1428,9 +1410,6 @@ function getLinkManager() {
|
|
|
1428
1410
|
return _linkManager;
|
|
1429
1411
|
}
|
|
1430
1412
|
|
|
1431
|
-
// src/lib/tracker/index.ts
|
|
1432
|
-
init_esm_shims();
|
|
1433
|
-
|
|
1434
1413
|
export {
|
|
1435
1414
|
loadConfig,
|
|
1436
1415
|
saveConfig,
|
|
@@ -1466,4 +1445,4 @@ export {
|
|
|
1466
1445
|
LinkManager,
|
|
1467
1446
|
getLinkManager
|
|
1468
1447
|
};
|
|
1469
|
-
//# sourceMappingURL=chunk-
|
|
1448
|
+
//# sourceMappingURL=chunk-C6A7S65K.js.map
|