prjct-cli 0.42.0 → 0.44.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/core/agentic/command-executor.ts +15 -5
  3. package/core/ai-tools/formatters.ts +302 -0
  4. package/core/ai-tools/generator.ts +124 -0
  5. package/core/ai-tools/index.ts +15 -0
  6. package/core/ai-tools/registry.ts +195 -0
  7. package/core/cli/linear.ts +61 -2
  8. package/core/commands/analysis.ts +36 -2
  9. package/core/commands/commands.ts +2 -2
  10. package/core/commands/planning.ts +8 -4
  11. package/core/commands/shipping.ts +9 -7
  12. package/core/commands/workflow.ts +67 -17
  13. package/core/index.ts +3 -1
  14. package/core/infrastructure/ai-provider.ts +11 -36
  15. package/core/integrations/issue-tracker/types.ts +7 -1
  16. package/core/integrations/linear/client.ts +56 -24
  17. package/core/integrations/linear/index.ts +3 -0
  18. package/core/integrations/linear/sync.ts +313 -0
  19. package/core/schemas/index.ts +3 -0
  20. package/core/schemas/issues.ts +144 -0
  21. package/core/schemas/state.ts +3 -0
  22. package/core/services/sync-service.ts +71 -4
  23. package/core/utils/agent-stream.ts +138 -0
  24. package/core/utils/branding.ts +2 -3
  25. package/core/utils/next-steps.ts +95 -0
  26. package/core/utils/output.ts +26 -0
  27. package/core/workflow/index.ts +6 -0
  28. package/core/workflow/state-machine.ts +185 -0
  29. package/dist/bin/prjct.mjs +2382 -541
  30. package/package.json +1 -1
  31. package/templates/_bases/tracker-base.md +11 -0
  32. package/templates/commands/done.md +18 -13
  33. package/templates/commands/git.md +143 -54
  34. package/templates/commands/merge.md +121 -13
  35. package/templates/commands/review.md +1 -1
  36. package/templates/commands/ship.md +165 -20
  37. package/templates/commands/sync.md +17 -0
  38. package/templates/commands/task.md +123 -17
  39. package/templates/global/ANTIGRAVITY.md +2 -4
  40. package/templates/global/CLAUDE.md +115 -28
  41. package/templates/global/CURSOR.mdc +1 -3
  42. package/templates/global/GEMINI.md +2 -4
  43. package/templates/global/WINDSURF.md +1 -3
  44. package/templates/subagents/workflow/prjct-shipper.md +1 -2
@@ -16,10 +16,10 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
16
16
  if (typeof require !== "undefined") return require.apply(this, arguments);
17
17
  throw Error('Dynamic require of "' + x + '" is not supported');
18
18
  });
19
- var __glob = (map) => (path41) => {
20
- var fn = map[path41];
19
+ var __glob = (map) => (path44) => {
20
+ var fn = map[path44];
21
21
  if (fn) return fn();
22
- throw new Error("Module not found in bundle: " + path41);
22
+ throw new Error("Module not found in bundle: " + path44);
23
23
  };
24
24
  var __esm = (fn, res) => function __init() {
25
25
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
@@ -3330,7 +3330,12 @@ var init_state = __esm({
3330
3330
  // Subtask tracking for fragmented tasks
3331
3331
  subtasks: z2.array(SubtaskSchema).optional(),
3332
3332
  currentSubtaskIndex: z2.number().optional(),
3333
- subtaskProgress: SubtaskProgressSchema.optional()
3333
+ subtaskProgress: SubtaskProgressSchema.optional(),
3334
+ // Linear integration - bidirectional sync
3335
+ linearId: z2.string().optional(),
3336
+ // "PRJ-123" - Linear identifier
3337
+ linearUuid: z2.string().optional()
3338
+ // Linear internal UUID for API calls
3334
3339
  });
3335
3340
  PreviousTaskSchema = z2.object({
3336
3341
  id: z2.string(),
@@ -3399,26 +3404,108 @@ var init_state = __esm({
3399
3404
  }
3400
3405
  });
3401
3406
 
3402
- // core/schemas/project.ts
3407
+ // core/schemas/issues.ts
3403
3408
  import { z as z3 } from "zod";
3409
+ function createEmptyIssues(provider) {
3410
+ return {
3411
+ provider,
3412
+ lastSync: "",
3413
+ staleAfter: 18e5,
3414
+ issues: {}
3415
+ };
3416
+ }
3417
+ var IssueProviderSchema, IssueStatusSchema, IssuePrioritySchema, IssueTypeSchema, CachedIssueSchema, IssuesJsonSchema, SyncResultSchema, parseIssues;
3418
+ var init_issues = __esm({
3419
+ "core/schemas/issues.ts"() {
3420
+ "use strict";
3421
+ IssueProviderSchema = z3.enum(["linear", "jira", "github", "monday", "asana", "none"]);
3422
+ IssueStatusSchema = z3.enum(["backlog", "todo", "in_progress", "in_review", "done", "cancelled"]);
3423
+ IssuePrioritySchema = z3.enum(["none", "urgent", "high", "medium", "low"]);
3424
+ IssueTypeSchema = z3.enum(["feature", "bug", "improvement", "task", "chore", "epic"]);
3425
+ CachedIssueSchema = z3.object({
3426
+ // Core identifiers
3427
+ id: z3.string(),
3428
+ // Provider UUID
3429
+ identifier: z3.string(),
3430
+ // Human-readable ID (e.g., "PRJ-123")
3431
+ // Issue content
3432
+ title: z3.string(),
3433
+ description: z3.string().optional(),
3434
+ // State
3435
+ status: IssueStatusSchema,
3436
+ priority: IssuePrioritySchema,
3437
+ type: IssueTypeSchema.optional(),
3438
+ // Metadata
3439
+ assignee: z3.object({
3440
+ id: z3.string(),
3441
+ name: z3.string(),
3442
+ email: z3.string().optional()
3443
+ }).optional(),
3444
+ labels: z3.array(z3.string()).default([]),
3445
+ team: z3.object({
3446
+ id: z3.string(),
3447
+ name: z3.string(),
3448
+ key: z3.string().optional()
3449
+ }).optional(),
3450
+ project: z3.object({
3451
+ id: z3.string(),
3452
+ name: z3.string()
3453
+ }).optional(),
3454
+ // URLs and timestamps
3455
+ url: z3.string(),
3456
+ createdAt: z3.string(),
3457
+ // ISO8601 from provider
3458
+ updatedAt: z3.string(),
3459
+ // ISO8601 from provider
3460
+ fetchedAt: z3.string()
3461
+ // ISO8601 when we cached it
3462
+ });
3463
+ IssuesJsonSchema = z3.object({
3464
+ // Provider info
3465
+ provider: IssueProviderSchema,
3466
+ // Sync metadata
3467
+ lastSync: z3.string(),
3468
+ // ISO8601 of last full sync
3469
+ staleAfter: z3.number().default(18e5),
3470
+ // 30 minutes in ms
3471
+ // Issues map: identifier -> issue
3472
+ issues: z3.record(z3.string(), CachedIssueSchema)
3473
+ });
3474
+ SyncResultSchema = z3.object({
3475
+ provider: IssueProviderSchema,
3476
+ fetched: z3.number(),
3477
+ updated: z3.number(),
3478
+ errors: z3.array(z3.object({
3479
+ issueId: z3.string(),
3480
+ error: z3.string()
3481
+ })),
3482
+ timestamp: z3.string()
3483
+ });
3484
+ parseIssues = /* @__PURE__ */ __name((data) => IssuesJsonSchema.parse(data), "parseIssues");
3485
+ __name(createEmptyIssues, "createEmptyIssues");
3486
+ }
3487
+ });
3488
+
3489
+ // core/schemas/project.ts
3490
+ import { z as z4 } from "zod";
3404
3491
  var ProjectItemSchema, DEFAULT_PROJECT;
3405
3492
  var init_project = __esm({
3406
3493
  "core/schemas/project.ts"() {
3407
3494
  "use strict";
3408
- ProjectItemSchema = z3.object({
3409
- projectId: z3.string(),
3410
- name: z3.string(),
3411
- repoPath: z3.string(),
3412
- description: z3.string().optional(),
3413
- version: z3.string().optional(),
3414
- cliVersion: z3.string().optional(),
3495
+ ProjectItemSchema = z4.object({
3496
+ projectId: z4.string(),
3497
+ name: z4.string(),
3498
+ repoPath: z4.string(),
3499
+ description: z4.string().optional(),
3500
+ version: z4.string().optional(),
3501
+ cliVersion: z4.string().optional(),
3415
3502
  // prjct-cli version used to sync
3416
- techStack: z3.array(z3.string()),
3417
- fileCount: z3.number(),
3418
- commitCount: z3.number(),
3419
- createdAt: z3.string(),
3503
+ techStack: z4.array(z4.string()),
3504
+ fileCount: z4.number(),
3505
+ commitCount: z4.number(),
3506
+ createdAt: z4.string(),
3420
3507
  // ISO8601
3421
- lastSync: z3.string()
3508
+ lastSync: z4.string()
3422
3509
  // ISO8601
3423
3510
  });
3424
3511
  DEFAULT_PROJECT = {
@@ -3439,74 +3526,74 @@ var init_agents = __esm({
3439
3526
  });
3440
3527
 
3441
3528
  // core/schemas/ideas.ts
3442
- import { z as z4 } from "zod";
3529
+ import { z as z5 } from "zod";
3443
3530
  var IdeaPrioritySchema, IdeaStatusSchema, ImpactLevelSchema, ImpactEffortSchema, TechStackSchema, IdeaModuleSchema, IdeaRoleSchema, IdeaItemSchema, IdeasJsonSchema, DEFAULT_IDEA;
3444
3531
  var init_ideas = __esm({
3445
3532
  "core/schemas/ideas.ts"() {
3446
3533
  "use strict";
3447
- IdeaPrioritySchema = z4.enum(["low", "medium", "high"]);
3448
- IdeaStatusSchema = z4.enum(["pending", "converted", "completed", "archived"]);
3449
- ImpactLevelSchema = z4.enum(["high", "medium", "low"]);
3450
- ImpactEffortSchema = z4.object({
3534
+ IdeaPrioritySchema = z5.enum(["low", "medium", "high"]);
3535
+ IdeaStatusSchema = z5.enum(["pending", "converted", "completed", "archived"]);
3536
+ ImpactLevelSchema = z5.enum(["high", "medium", "low"]);
3537
+ ImpactEffortSchema = z5.object({
3451
3538
  impact: ImpactLevelSchema,
3452
3539
  effort: ImpactLevelSchema
3453
3540
  });
3454
- TechStackSchema = z4.object({
3455
- frontend: z4.string().optional(),
3541
+ TechStackSchema = z5.object({
3542
+ frontend: z5.string().optional(),
3456
3543
  // "Next.js 14, HeroUI"
3457
- backend: z4.string().optional(),
3544
+ backend: z5.string().optional(),
3458
3545
  // "Supabase (Auth, DB, RLS, Realtime)"
3459
- payments: z4.string().optional(),
3546
+ payments: z5.string().optional(),
3460
3547
  // "Stripe Billing"
3461
- ai: z4.string().optional(),
3548
+ ai: z5.string().optional(),
3462
3549
  // "Vercel AI SDK"
3463
- deploy: z4.string().optional(),
3550
+ deploy: z5.string().optional(),
3464
3551
  // "Vercel"
3465
- other: z4.array(z4.string()).optional()
3552
+ other: z5.array(z5.string()).optional()
3466
3553
  });
3467
- IdeaModuleSchema = z4.object({
3468
- name: z4.string(),
3554
+ IdeaModuleSchema = z5.object({
3555
+ name: z5.string(),
3469
3556
  // "Multi-tenant"
3470
- description: z4.string()
3557
+ description: z5.string()
3471
3558
  // "Empresas con RLS estricto"
3472
3559
  });
3473
- IdeaRoleSchema = z4.object({
3474
- name: z4.string(),
3560
+ IdeaRoleSchema = z5.object({
3561
+ name: z5.string(),
3475
3562
  // "SUPER_ADMIN"
3476
- description: z4.string().optional()
3563
+ description: z5.string().optional()
3477
3564
  });
3478
- IdeaItemSchema = z4.object({
3479
- id: z4.string(),
3565
+ IdeaItemSchema = z5.object({
3566
+ id: z5.string(),
3480
3567
  // idea_xxxxxxxx
3481
- text: z4.string(),
3568
+ text: z5.string(),
3482
3569
  // Title/summary
3483
- details: z4.string().optional(),
3570
+ details: z5.string().optional(),
3484
3571
  priority: IdeaPrioritySchema,
3485
3572
  status: IdeaStatusSchema,
3486
- tags: z4.array(z4.string()),
3487
- addedAt: z4.string(),
3573
+ tags: z5.array(z5.string()),
3574
+ addedAt: z5.string(),
3488
3575
  // ISO8601
3489
- completedAt: z4.string().optional(),
3490
- convertedTo: z4.string().optional(),
3576
+ completedAt: z5.string().optional(),
3577
+ convertedTo: z5.string().optional(),
3491
3578
  // Source documentation
3492
- source: z4.string().optional(),
3493
- sourceFiles: z4.array(z4.string()).optional(),
3579
+ source: z5.string().optional(),
3580
+ sourceFiles: z5.array(z5.string()).optional(),
3494
3581
  // Enriched fields from MD
3495
- painPoints: z4.array(z4.string()).optional(),
3496
- solutions: z4.array(z4.string()).optional(),
3497
- filesAffected: z4.array(z4.string()).optional(),
3582
+ painPoints: z5.array(z5.string()).optional(),
3583
+ solutions: z5.array(z5.string()).optional(),
3584
+ filesAffected: z5.array(z5.string()).optional(),
3498
3585
  impactEffort: ImpactEffortSchema.optional(),
3499
- implementationNotes: z4.string().optional(),
3586
+ implementationNotes: z5.string().optional(),
3500
3587
  // Technical spec fields for ZERO DATA LOSS
3501
3588
  stack: TechStackSchema.optional(),
3502
- modules: z4.array(IdeaModuleSchema).optional(),
3503
- roles: z4.array(IdeaRoleSchema).optional(),
3504
- risks: z4.array(z4.string()).optional(),
3505
- risksCount: z4.number().optional()
3589
+ modules: z5.array(IdeaModuleSchema).optional(),
3590
+ roles: z5.array(IdeaRoleSchema).optional(),
3591
+ risks: z5.array(z5.string()).optional(),
3592
+ risksCount: z5.number().optional()
3506
3593
  });
3507
- IdeasJsonSchema = z4.object({
3508
- ideas: z4.array(IdeaItemSchema),
3509
- lastUpdated: z4.string()
3594
+ IdeasJsonSchema = z5.object({
3595
+ ideas: z5.array(IdeaItemSchema),
3596
+ lastUpdated: z5.string()
3510
3597
  });
3511
3598
  DEFAULT_IDEA = {
3512
3599
  priority: "medium",
@@ -3518,174 +3605,174 @@ var init_ideas = __esm({
3518
3605
  });
3519
3606
 
3520
3607
  // core/schemas/roadmap.ts
3521
- import { z as z5 } from "zod";
3608
+ import { z as z6 } from "zod";
3522
3609
  var FeatureStatusSchema, FeatureImpactSchema, FeatureTypeSchema, PhaseStatusSchema, QuarterStatusSchema, InferredFromSchema, FeatureTaskSchema, RoadmapPhaseSchema, RoadmapStrategySchema, FeatureDurationSchema, GitCommitSchema, EffortEstimateSchema, EffortActualSchema, FeatureEffortSchema, QuarterCapacitySchema, QuarterSchema, FeatureItemSchema, BacklogItemSchema, RoadmapJsonSchema, DEFAULT_FEATURE;
3523
3610
  var init_roadmap = __esm({
3524
3611
  "core/schemas/roadmap.ts"() {
3525
3612
  "use strict";
3526
- FeatureStatusSchema = z5.enum(["planned", "active", "completed", "shipped"]);
3527
- FeatureImpactSchema = z5.enum(["low", "medium", "high"]);
3528
- FeatureTypeSchema = z5.enum(["feature", "breaking_change", "refactor", "infrastructure"]);
3529
- PhaseStatusSchema = z5.enum(["completed", "active", "planned"]);
3530
- QuarterStatusSchema = z5.enum(["planned", "active", "completed"]);
3531
- InferredFromSchema = z5.enum(["git", "git-branch", "manual", "prd"]);
3532
- FeatureTaskSchema = z5.object({
3533
- id: z5.string(),
3613
+ FeatureStatusSchema = z6.enum(["planned", "active", "completed", "shipped"]);
3614
+ FeatureImpactSchema = z6.enum(["low", "medium", "high"]);
3615
+ FeatureTypeSchema = z6.enum(["feature", "breaking_change", "refactor", "infrastructure"]);
3616
+ PhaseStatusSchema = z6.enum(["completed", "active", "planned"]);
3617
+ QuarterStatusSchema = z6.enum(["planned", "active", "completed"]);
3618
+ InferredFromSchema = z6.enum(["git", "git-branch", "manual", "prd"]);
3619
+ FeatureTaskSchema = z6.object({
3620
+ id: z6.string(),
3534
3621
  // task_xxxxxxxx
3535
- description: z5.string(),
3536
- completed: z5.boolean(),
3537
- completedAt: z5.string().optional()
3622
+ description: z6.string(),
3623
+ completed: z6.boolean(),
3624
+ completedAt: z6.string().optional()
3538
3625
  });
3539
- RoadmapPhaseSchema = z5.object({
3540
- id: z5.string(),
3626
+ RoadmapPhaseSchema = z6.object({
3627
+ id: z6.string(),
3541
3628
  // P0, P1, etc.
3542
- name: z5.string(),
3629
+ name: z6.string(),
3543
3630
  status: PhaseStatusSchema,
3544
- completedAt: z5.string().optional()
3631
+ completedAt: z6.string().optional()
3545
3632
  });
3546
- RoadmapStrategySchema = z5.object({
3547
- goal: z5.string(),
3548
- phases: z5.array(RoadmapPhaseSchema),
3549
- successMetrics: z5.array(z5.string()).optional()
3633
+ RoadmapStrategySchema = z6.object({
3634
+ goal: z6.string(),
3635
+ phases: z6.array(RoadmapPhaseSchema),
3636
+ successMetrics: z6.array(z6.string()).optional()
3550
3637
  });
3551
- FeatureDurationSchema = z5.object({
3552
- hours: z5.number(),
3553
- minutes: z5.number(),
3554
- totalMinutes: z5.number(),
3555
- display: z5.string().optional()
3638
+ FeatureDurationSchema = z6.object({
3639
+ hours: z6.number(),
3640
+ minutes: z6.number(),
3641
+ totalMinutes: z6.number(),
3642
+ display: z6.string().optional()
3556
3643
  });
3557
- GitCommitSchema = z5.object({
3558
- hash: z5.string(),
3559
- message: z5.string(),
3560
- date: z5.string(),
3561
- author: z5.string().optional()
3644
+ GitCommitSchema = z6.object({
3645
+ hash: z6.string(),
3646
+ message: z6.string(),
3647
+ date: z6.string(),
3648
+ author: z6.string().optional()
3562
3649
  });
3563
- EffortEstimateSchema = z5.object({
3564
- hours: z5.number(),
3565
- confidence: z5.enum(["low", "medium", "high"]).optional(),
3566
- breakdown: z5.array(z5.object({
3567
- area: z5.string(),
3568
- hours: z5.number()
3650
+ EffortEstimateSchema = z6.object({
3651
+ hours: z6.number(),
3652
+ confidence: z6.enum(["low", "medium", "high"]).optional(),
3653
+ breakdown: z6.array(z6.object({
3654
+ area: z6.string(),
3655
+ hours: z6.number()
3569
3656
  })).optional()
3570
3657
  });
3571
- EffortActualSchema = z5.object({
3572
- hours: z5.number().optional(),
3573
- commits: z5.number().optional(),
3574
- linesAdded: z5.number().optional(),
3575
- linesRemoved: z5.number().optional()
3658
+ EffortActualSchema = z6.object({
3659
+ hours: z6.number().optional(),
3660
+ commits: z6.number().optional(),
3661
+ linesAdded: z6.number().optional(),
3662
+ linesRemoved: z6.number().optional()
3576
3663
  });
3577
- FeatureEffortSchema = z5.object({
3664
+ FeatureEffortSchema = z6.object({
3578
3665
  estimated: EffortEstimateSchema.nullable(),
3579
3666
  actual: EffortActualSchema.nullable()
3580
3667
  });
3581
- QuarterCapacitySchema = z5.object({
3582
- totalHours: z5.number(),
3583
- allocatedHours: z5.number(),
3584
- bufferPercent: z5.number().optional()
3668
+ QuarterCapacitySchema = z6.object({
3669
+ totalHours: z6.number(),
3670
+ allocatedHours: z6.number(),
3671
+ bufferPercent: z6.number().optional()
3585
3672
  // % reserved for unknowns
3586
3673
  });
3587
- QuarterSchema = z5.object({
3588
- id: z5.string(),
3674
+ QuarterSchema = z6.object({
3675
+ id: z6.string(),
3589
3676
  // Q1-2026
3590
- name: z5.string(),
3677
+ name: z6.string(),
3591
3678
  // "Q1 2026"
3592
- theme: z5.string().optional(),
3679
+ theme: z6.string().optional(),
3593
3680
  // "Foundation"
3594
- goals: z5.array(z5.string()).optional(),
3595
- features: z5.array(z5.string()),
3681
+ goals: z6.array(z6.string()).optional(),
3682
+ features: z6.array(z6.string()),
3596
3683
  // Feature IDs
3597
3684
  capacity: QuarterCapacitySchema.optional(),
3598
3685
  status: QuarterStatusSchema,
3599
- startDate: z5.string().optional(),
3686
+ startDate: z6.string().optional(),
3600
3687
  // ISO8601
3601
- endDate: z5.string().optional()
3688
+ endDate: z6.string().optional()
3602
3689
  // ISO8601
3603
3690
  });
3604
- FeatureItemSchema = z5.object({
3605
- id: z5.string(),
3691
+ FeatureItemSchema = z6.object({
3692
+ id: z6.string(),
3606
3693
  // feat_xxxxxxxx
3607
- name: z5.string(),
3608
- description: z5.string().optional(),
3609
- date: z5.string(),
3694
+ name: z6.string(),
3695
+ description: z6.string().optional(),
3696
+ date: z6.string(),
3610
3697
  // YYYY-MM-DD creation date
3611
3698
  status: FeatureStatusSchema,
3612
3699
  impact: FeatureImpactSchema,
3613
- effort: z5.string().optional(),
3614
- progress: z5.number(),
3700
+ effort: z6.string().optional(),
3701
+ progress: z6.number(),
3615
3702
  // 0-100
3616
3703
  // Enriched fields from MD
3617
3704
  type: FeatureTypeSchema.optional(),
3618
- roi: z5.number().optional(),
3705
+ roi: z6.number().optional(),
3619
3706
  // 1-5 from star count
3620
- why: z5.array(z5.string()).optional(),
3621
- technicalNotes: z5.array(z5.string()).optional(),
3622
- compatibility: z5.string().optional(),
3623
- phase: z5.string().optional(),
3707
+ why: z6.array(z6.string()).optional(),
3708
+ technicalNotes: z6.array(z6.string()).optional(),
3709
+ compatibility: z6.string().optional(),
3710
+ phase: z6.string().optional(),
3624
3711
  // P0, P1, etc.
3625
- tasks: z5.array(FeatureTaskSchema),
3626
- createdAt: z5.string(),
3712
+ tasks: z6.array(FeatureTaskSchema),
3713
+ createdAt: z6.string(),
3627
3714
  // ISO8601
3628
- shippedAt: z5.string().optional(),
3629
- version: z5.string().optional(),
3715
+ shippedAt: z6.string().optional(),
3716
+ version: z6.string().optional(),
3630
3717
  // ZERO DATA LOSS - additional fields
3631
3718
  duration: FeatureDurationSchema.optional(),
3632
- taskCount: z5.number().optional(),
3633
- agent: z5.string().optional(),
3719
+ taskCount: z6.number().optional(),
3720
+ agent: z6.string().optional(),
3634
3721
  // "fe+be", "fe", "be"
3635
- sprintName: z5.string().optional(),
3636
- completedDate: z5.string().optional(),
3722
+ sprintName: z6.string().optional(),
3723
+ completedDate: z6.string().optional(),
3637
3724
  // =========================================================================
3638
3725
  // AI ORCHESTRATION FIELDS (v0.29.0)
3639
3726
  // =========================================================================
3640
3727
  // PRD Integration
3641
- prdId: z5.string().nullable().optional(),
3728
+ prdId: z6.string().nullable().optional(),
3642
3729
  // Link to PRD (prd_xxxxxxxx)
3643
3730
  // Legacy Support (for existing projects)
3644
- legacy: z5.boolean().optional(),
3731
+ legacy: z6.boolean().optional(),
3645
3732
  // true = no PRD required
3646
3733
  inferredFrom: InferredFromSchema.optional(),
3647
3734
  // git, git-branch, manual, prd
3648
3735
  // Quarter Planning
3649
- quarter: z5.string().nullable().optional(),
3736
+ quarter: z6.string().nullable().optional(),
3650
3737
  // Q1-2026, etc.
3651
3738
  // Dependency Tracking
3652
- dependencies: z5.array(z5.string()).optional(),
3739
+ dependencies: z6.array(z6.string()).optional(),
3653
3740
  // Feature IDs this depends on
3654
- blockedBy: z5.array(z5.string()).optional(),
3741
+ blockedBy: z6.array(z6.string()).optional(),
3655
3742
  // Feature IDs blocking this
3656
3743
  // Effort Tracking (for PRD comparison)
3657
3744
  effortTracking: FeatureEffortSchema.optional(),
3658
3745
  // Value Scoring (calculated from PRD)
3659
- valueScore: z5.number().optional(),
3746
+ valueScore: z6.number().optional(),
3660
3747
  // Calculated priority score
3661
3748
  // Git Data (for legacy features)
3662
- commits: z5.array(GitCommitSchema).optional(),
3749
+ commits: z6.array(GitCommitSchema).optional(),
3663
3750
  // Commits for this feature
3664
- branch: z5.string().optional(),
3751
+ branch: z6.string().optional(),
3665
3752
  // Branch name (for active)
3666
- commitsAhead: z5.number().optional()
3753
+ commitsAhead: z6.number().optional()
3667
3754
  // Commits ahead of main
3668
3755
  });
3669
- BacklogItemSchema = z5.object({
3670
- id: z5.string(),
3671
- title: z5.string(),
3672
- prdId: z5.string().nullable().optional(),
3673
- valueScore: z5.number().optional(),
3674
- effortEstimate: z5.number().optional(),
3675
- reason: z5.string().optional()
3756
+ BacklogItemSchema = z6.object({
3757
+ id: z6.string(),
3758
+ title: z6.string(),
3759
+ prdId: z6.string().nullable().optional(),
3760
+ valueScore: z6.number().optional(),
3761
+ effortEstimate: z6.number().optional(),
3762
+ reason: z6.string().optional()
3676
3763
  // Why in backlog
3677
3764
  });
3678
- RoadmapJsonSchema = z5.object({
3765
+ RoadmapJsonSchema = z6.object({
3679
3766
  strategy: RoadmapStrategySchema.nullable().optional(),
3680
- features: z5.array(FeatureItemSchema),
3681
- backlog: z5.array(z5.union([z5.string(), BacklogItemSchema])),
3767
+ features: z6.array(FeatureItemSchema),
3768
+ backlog: z6.array(z6.union([z6.string(), BacklogItemSchema])),
3682
3769
  // Support both formats
3683
- lastUpdated: z5.string(),
3770
+ lastUpdated: z6.string(),
3684
3771
  // AI ORCHESTRATION FIELDS (v0.29.0)
3685
- quarters: z5.array(QuarterSchema).optional(),
3772
+ quarters: z6.array(QuarterSchema).optional(),
3686
3773
  // Metadata (for git-inferred roadmaps)
3687
- generatedFrom: z5.enum(["git-history", "manual", "prd"]).optional(),
3688
- generatedAt: z5.string().optional()
3774
+ generatedFrom: z6.enum(["git-history", "manual", "prd"]).optional(),
3775
+ generatedAt: z6.string().optional()
3689
3776
  });
3690
3777
  DEFAULT_FEATURE = {
3691
3778
  date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
@@ -3703,64 +3790,64 @@ var init_roadmap = __esm({
3703
3790
  });
3704
3791
 
3705
3792
  // core/schemas/shipped.ts
3706
- import { z as z6 } from "zod";
3793
+ import { z as z7 } from "zod";
3707
3794
  var ShipTypeSchema, CheckStatusSchema, ChangeTypeSchema, DurationSchema, CodeMetricsSchema, ShipChangeSchema, QualityMetricsSchema, CommitInfoSchema, ShippedItemSchema, ShippedJsonSchema;
3708
3795
  var init_shipped = __esm({
3709
3796
  "core/schemas/shipped.ts"() {
3710
3797
  "use strict";
3711
- ShipTypeSchema = z6.enum(["feature", "fix", "improvement", "refactor"]);
3712
- CheckStatusSchema = z6.enum(["pass", "warning", "fail", "skipped"]);
3713
- ChangeTypeSchema = z6.enum(["added", "changed", "fixed", "removed"]);
3714
- DurationSchema = z6.object({
3715
- hours: z6.number(),
3716
- minutes: z6.number(),
3717
- totalMinutes: z6.number()
3798
+ ShipTypeSchema = z7.enum(["feature", "fix", "improvement", "refactor"]);
3799
+ CheckStatusSchema = z7.enum(["pass", "warning", "fail", "skipped"]);
3800
+ ChangeTypeSchema = z7.enum(["added", "changed", "fixed", "removed"]);
3801
+ DurationSchema = z7.object({
3802
+ hours: z7.number(),
3803
+ minutes: z7.number(),
3804
+ totalMinutes: z7.number()
3718
3805
  });
3719
- CodeMetricsSchema = z6.object({
3720
- filesChanged: z6.number().nullable().optional(),
3721
- linesAdded: z6.number().nullable().optional(),
3722
- linesRemoved: z6.number().nullable().optional(),
3723
- commits: z6.number().nullable().optional()
3806
+ CodeMetricsSchema = z7.object({
3807
+ filesChanged: z7.number().nullable().optional(),
3808
+ linesAdded: z7.number().nullable().optional(),
3809
+ linesRemoved: z7.number().nullable().optional(),
3810
+ commits: z7.number().nullable().optional()
3724
3811
  });
3725
- ShipChangeSchema = z6.object({
3726
- description: z6.string(),
3812
+ ShipChangeSchema = z7.object({
3813
+ description: z7.string(),
3727
3814
  type: ChangeTypeSchema.optional()
3728
3815
  });
3729
- QualityMetricsSchema = z6.object({
3816
+ QualityMetricsSchema = z7.object({
3730
3817
  lintStatus: CheckStatusSchema.nullable().optional(),
3731
- lintDetails: z6.string().optional(),
3818
+ lintDetails: z7.string().optional(),
3732
3819
  testStatus: CheckStatusSchema.nullable().optional(),
3733
- testDetails: z6.string().optional()
3820
+ testDetails: z7.string().optional()
3734
3821
  });
3735
- CommitInfoSchema = z6.object({
3736
- hash: z6.string().optional(),
3737
- message: z6.string().optional(),
3738
- branch: z6.string().optional()
3822
+ CommitInfoSchema = z7.object({
3823
+ hash: z7.string().optional(),
3824
+ message: z7.string().optional(),
3825
+ branch: z7.string().optional()
3739
3826
  });
3740
- ShippedItemSchema = z6.object({
3741
- id: z6.string(),
3827
+ ShippedItemSchema = z7.object({
3828
+ id: z7.string(),
3742
3829
  // ship_xxxxxxxx
3743
- name: z6.string(),
3744
- version: z6.string().nullable().optional(),
3830
+ name: z7.string(),
3831
+ version: z7.string().nullable().optional(),
3745
3832
  type: ShipTypeSchema,
3746
- agent: z6.string().optional(),
3833
+ agent: z7.string().optional(),
3747
3834
  // "fe+be", "be", "fe"
3748
- description: z6.string().optional(),
3749
- changes: z6.array(ShipChangeSchema),
3750
- codeSnippets: z6.array(z6.string()).optional(),
3835
+ description: z7.string().optional(),
3836
+ changes: z7.array(ShipChangeSchema),
3837
+ codeSnippets: z7.array(z7.string()).optional(),
3751
3838
  commit: CommitInfoSchema.optional(),
3752
3839
  codeMetrics: CodeMetricsSchema.optional(),
3753
3840
  qualityMetrics: QualityMetricsSchema.optional(),
3754
- quantitativeImpact: z6.string().optional(),
3841
+ quantitativeImpact: z7.string().optional(),
3755
3842
  duration: DurationSchema.optional(),
3756
- tasksCompleted: z6.number().nullable().optional(),
3757
- shippedAt: z6.string(),
3843
+ tasksCompleted: z7.number().nullable().optional(),
3844
+ shippedAt: z7.string(),
3758
3845
  // ISO8601
3759
- featureId: z6.string().optional()
3846
+ featureId: z7.string().optional()
3760
3847
  });
3761
- ShippedJsonSchema = z6.object({
3762
- items: z6.array(ShippedItemSchema),
3763
- lastUpdated: z6.string()
3848
+ ShippedJsonSchema = z7.object({
3849
+ items: z7.array(ShippedItemSchema),
3850
+ lastUpdated: z7.string()
3764
3851
  });
3765
3852
  }
3766
3853
  });
@@ -3783,15 +3870,15 @@ var init_analysis = __esm({
3783
3870
  });
3784
3871
 
3785
3872
  // core/schemas/outcomes.ts
3786
- import { z as z7 } from "zod";
3873
+ import { z as z8 } from "zod";
3787
3874
  var QualityScoreSchema, SuccessLevelSchema, WorthAssessmentSchema, VarianceReasonSchema, EffortComparisonSchema, MetricResultSchema, AcceptanceCriteriaResultSchema, SuccessTrackingSchema, LearningSchema, LearningsSchema, ROIAssessmentSchema, TaskOutcomeSchema, FeatureOutcomeSchema, AggregateMetricsSchema, OutcomesJsonSchema;
3788
3875
  var init_outcomes = __esm({
3789
3876
  "core/schemas/outcomes.ts"() {
3790
3877
  "use strict";
3791
- QualityScoreSchema = z7.number().min(1).max(5);
3792
- SuccessLevelSchema = z7.enum(["exceeded", "met", "partial", "failed"]);
3793
- WorthAssessmentSchema = z7.enum(["definitely", "probably", "maybe", "no"]);
3794
- VarianceReasonSchema = z7.enum([
3878
+ QualityScoreSchema = z8.number().min(1).max(5);
3879
+ SuccessLevelSchema = z8.enum(["exceeded", "met", "partial", "failed"]);
3880
+ WorthAssessmentSchema = z8.enum(["definitely", "probably", "maybe", "no"]);
3881
+ VarianceReasonSchema = z8.enum([
3795
3882
  "scope_creep",
3796
3883
  "underestimated_complexity",
3797
3884
  "technical_debt",
@@ -3802,54 +3889,54 @@ var init_outcomes = __esm({
3802
3889
  "team_changes",
3803
3890
  "other"
3804
3891
  ]);
3805
- EffortComparisonSchema = z7.object({
3806
- estimated: z7.object({
3807
- hours: z7.number(),
3808
- confidence: z7.enum(["low", "medium", "high"]).optional(),
3809
- source: z7.enum(["prd", "manual", "historical"]).optional()
3892
+ EffortComparisonSchema = z8.object({
3893
+ estimated: z8.object({
3894
+ hours: z8.number(),
3895
+ confidence: z8.enum(["low", "medium", "high"]).optional(),
3896
+ source: z8.enum(["prd", "manual", "historical"]).optional()
3810
3897
  }),
3811
- actual: z7.object({
3812
- hours: z7.number(),
3813
- commits: z7.number().optional(),
3814
- linesAdded: z7.number().optional(),
3815
- linesRemoved: z7.number().optional(),
3816
- sessions: z7.number().optional()
3898
+ actual: z8.object({
3899
+ hours: z8.number(),
3900
+ commits: z8.number().optional(),
3901
+ linesAdded: z8.number().optional(),
3902
+ linesRemoved: z8.number().optional(),
3903
+ sessions: z8.number().optional()
3817
3904
  // Number of work sessions
3818
3905
  }),
3819
- variance: z7.object({
3820
- hours: z7.number(),
3906
+ variance: z8.object({
3907
+ hours: z8.number(),
3821
3908
  // actual - estimated
3822
- percentage: z7.number(),
3909
+ percentage: z8.number(),
3823
3910
  // ((actual - estimated) / estimated) * 100
3824
3911
  reason: VarianceReasonSchema.optional(),
3825
- explanation: z7.string().optional()
3912
+ explanation: z8.string().optional()
3826
3913
  })
3827
3914
  });
3828
- MetricResultSchema = z7.object({
3829
- name: z7.string(),
3830
- baseline: z7.number().nullable(),
3831
- target: z7.number(),
3832
- actual: z7.number(),
3833
- unit: z7.string(),
3834
- achieved: z7.boolean(),
3915
+ MetricResultSchema = z8.object({
3916
+ name: z8.string(),
3917
+ baseline: z8.number().nullable(),
3918
+ target: z8.number(),
3919
+ actual: z8.number(),
3920
+ unit: z8.string(),
3921
+ achieved: z8.boolean(),
3835
3922
  // actual >= target (or <= for decrease metrics)
3836
- percentOfTarget: z7.number()
3923
+ percentOfTarget: z8.number()
3837
3924
  // (actual / target) * 100
3838
3925
  });
3839
- AcceptanceCriteriaResultSchema = z7.object({
3840
- criteria: z7.string(),
3841
- met: z7.boolean(),
3842
- notes: z7.string().optional()
3926
+ AcceptanceCriteriaResultSchema = z8.object({
3927
+ criteria: z8.string(),
3928
+ met: z8.boolean(),
3929
+ notes: z8.string().optional()
3843
3930
  });
3844
- SuccessTrackingSchema = z7.object({
3845
- metrics: z7.array(MetricResultSchema),
3846
- acceptanceCriteria: z7.array(AcceptanceCriteriaResultSchema),
3931
+ SuccessTrackingSchema = z8.object({
3932
+ metrics: z8.array(MetricResultSchema),
3933
+ acceptanceCriteria: z8.array(AcceptanceCriteriaResultSchema),
3847
3934
  overallSuccess: SuccessLevelSchema,
3848
- successScore: z7.number().min(0).max(100)
3935
+ successScore: z8.number().min(0).max(100)
3849
3936
  // Percentage of metrics/criteria met
3850
3937
  });
3851
- LearningSchema = z7.object({
3852
- category: z7.enum([
3938
+ LearningSchema = z8.object({
3939
+ category: z8.enum([
3853
3940
  "estimation",
3854
3941
  "technical",
3855
3942
  "process",
@@ -3859,62 +3946,62 @@ var init_outcomes = __esm({
3859
3946
  "testing",
3860
3947
  "other"
3861
3948
  ]),
3862
- insight: z7.string(),
3863
- actionable: z7.boolean(),
3864
- action: z7.string().optional()
3949
+ insight: z8.string(),
3950
+ actionable: z8.boolean(),
3951
+ action: z8.string().optional()
3865
3952
  // What to do differently next time
3866
3953
  });
3867
- LearningsSchema = z7.object({
3868
- whatWorked: z7.array(z7.string()),
3869
- whatDidnt: z7.array(z7.string()),
3870
- surprises: z7.array(z7.string()),
3871
- recommendations: z7.array(LearningSchema)
3954
+ LearningsSchema = z8.object({
3955
+ whatWorked: z8.array(z8.string()),
3956
+ whatDidnt: z8.array(z8.string()),
3957
+ surprises: z8.array(z8.string()),
3958
+ recommendations: z8.array(LearningSchema)
3872
3959
  });
3873
- ROIAssessmentSchema = z7.object({
3874
- valueDelivered: z7.number().min(1).max(10),
3960
+ ROIAssessmentSchema = z8.object({
3961
+ valueDelivered: z8.number().min(1).max(10),
3875
3962
  // Subjective 1-10 score
3876
- userImpact: z7.enum(["none", "low", "medium", "high", "critical"]),
3877
- businessImpact: z7.enum(["none", "low", "medium", "high", "critical"]),
3963
+ userImpact: z8.enum(["none", "low", "medium", "high", "critical"]),
3964
+ businessImpact: z8.enum(["none", "low", "medium", "high", "critical"]),
3878
3965
  // Calculated: (valueDelivered * 10) / (actual hours)
3879
- roiScore: z7.number(),
3966
+ roiScore: z8.number(),
3880
3967
  // Would you build this again knowing what you know now?
3881
3968
  worthIt: WorthAssessmentSchema,
3882
- worthItReason: z7.string().optional(),
3969
+ worthItReason: z8.string().optional(),
3883
3970
  // Comparison to alternatives
3884
- alternativeConsidered: z7.string().optional(),
3885
- betterAlternativeExists: z7.boolean().optional()
3971
+ alternativeConsidered: z8.string().optional(),
3972
+ betterAlternativeExists: z8.boolean().optional()
3886
3973
  });
3887
- TaskOutcomeSchema = z7.object({
3888
- id: z7.string(),
3974
+ TaskOutcomeSchema = z8.object({
3975
+ id: z8.string(),
3889
3976
  // out_task_xxxxxxxx
3890
- taskId: z7.string(),
3891
- description: z7.string(),
3977
+ taskId: z8.string(),
3978
+ description: z8.string(),
3892
3979
  // Time tracking
3893
- estimatedMinutes: z7.number().optional(),
3894
- actualMinutes: z7.number(),
3980
+ estimatedMinutes: z8.number().optional(),
3981
+ actualMinutes: z8.number(),
3895
3982
  // Quality
3896
- completedAsPlanned: z7.boolean(),
3983
+ completedAsPlanned: z8.boolean(),
3897
3984
  qualityScore: QualityScoreSchema,
3898
3985
  // Context
3899
- blockers: z7.array(z7.string()),
3900
- agentUsed: z7.string().optional(),
3901
- skillsUsed: z7.array(z7.string()).optional(),
3986
+ blockers: z8.array(z8.string()),
3987
+ agentUsed: z8.string().optional(),
3988
+ skillsUsed: z8.array(z8.string()).optional(),
3902
3989
  // Timestamps
3903
- startedAt: z7.string(),
3904
- completedAt: z7.string()
3990
+ startedAt: z8.string(),
3991
+ completedAt: z8.string()
3905
3992
  });
3906
- FeatureOutcomeSchema = z7.object({
3907
- id: z7.string(),
3993
+ FeatureOutcomeSchema = z8.object({
3994
+ id: z8.string(),
3908
3995
  // out_feat_xxxxxxxx
3909
3996
  // Links
3910
- featureId: z7.string(),
3911
- featureName: z7.string(),
3912
- prdId: z7.string().nullable(),
3997
+ featureId: z8.string(),
3998
+ featureName: z8.string(),
3999
+ prdId: z8.string().nullable(),
3913
4000
  // null for legacy features
3914
4001
  // Version info
3915
- version: z7.string().optional(),
3916
- branch: z7.string().optional(),
3917
- prUrl: z7.string().optional(),
4002
+ version: z8.string().optional(),
4003
+ branch: z8.string().optional(),
4004
+ prUrl: z8.string().optional(),
3918
4005
  // Effort
3919
4006
  effort: EffortComparisonSchema,
3920
4007
  // Success (only if PRD exists)
@@ -3926,57 +4013,57 @@ var init_outcomes = __esm({
3926
4013
  // Overall rating
3927
4014
  rating: QualityScoreSchema,
3928
4015
  // Task outcomes (sub-tasks)
3929
- taskOutcomes: z7.array(TaskOutcomeSchema).optional(),
4016
+ taskOutcomes: z8.array(TaskOutcomeSchema).optional(),
3930
4017
  // Timestamps
3931
- startedAt: z7.string(),
3932
- shippedAt: z7.string(),
3933
- reviewedAt: z7.string().optional(),
4018
+ startedAt: z8.string(),
4019
+ shippedAt: z8.string(),
4020
+ reviewedAt: z8.string().optional(),
3934
4021
  // When impact was captured
3935
4022
  // Metadata
3936
- reviewedBy: z7.string().optional(),
4023
+ reviewedBy: z8.string().optional(),
3937
4024
  // Who filled out the impact review
3938
- legacy: z7.boolean().optional()
4025
+ legacy: z8.boolean().optional()
3939
4026
  // Legacy feature (no PRD)
3940
4027
  });
3941
- AggregateMetricsSchema = z7.object({
3942
- totalFeatures: z7.number(),
3943
- averageEstimationAccuracy: z7.number(),
4028
+ AggregateMetricsSchema = z8.object({
4029
+ totalFeatures: z8.number(),
4030
+ averageEstimationAccuracy: z8.number(),
3944
4031
  // Percentage
3945
- averageSuccessRate: z7.number(),
4032
+ averageSuccessRate: z8.number(),
3946
4033
  // Percentage
3947
- averageROI: z7.number(),
4034
+ averageROI: z8.number(),
3948
4035
  // By category
3949
- bySuccessLevel: z7.object({
3950
- exceeded: z7.number(),
3951
- met: z7.number(),
3952
- partial: z7.number(),
3953
- failed: z7.number()
4036
+ bySuccessLevel: z8.object({
4037
+ exceeded: z8.number(),
4038
+ met: z8.number(),
4039
+ partial: z8.number(),
4040
+ failed: z8.number()
3954
4041
  }),
3955
4042
  // Variance patterns
3956
- variancePatterns: z7.array(z7.object({
4043
+ variancePatterns: z8.array(z8.object({
3957
4044
  reason: VarianceReasonSchema,
3958
- count: z7.number(),
3959
- averageVariance: z7.number()
4045
+ count: z8.number(),
4046
+ averageVariance: z8.number()
3960
4047
  })),
3961
4048
  // Top learnings (aggregated)
3962
- topLearnings: z7.array(z7.object({
3963
- insight: z7.string(),
3964
- frequency: z7.number()
4049
+ topLearnings: z8.array(z8.object({
4050
+ insight: z8.string(),
4051
+ frequency: z8.number()
3965
4052
  }))
3966
4053
  });
3967
- OutcomesJsonSchema = z7.object({
3968
- outcomes: z7.array(FeatureOutcomeSchema),
3969
- taskOutcomes: z7.array(TaskOutcomeSchema).optional(),
4054
+ OutcomesJsonSchema = z8.object({
4055
+ outcomes: z8.array(FeatureOutcomeSchema),
4056
+ taskOutcomes: z8.array(TaskOutcomeSchema).optional(),
3970
4057
  // Standalone task outcomes
3971
4058
  aggregates: AggregateMetricsSchema.optional(),
3972
- lastUpdated: z7.string(),
3973
- lastAggregated: z7.string().optional()
4059
+ lastUpdated: z8.string(),
4060
+ lastAggregated: z8.string().optional()
3974
4061
  });
3975
4062
  }
3976
4063
  });
3977
4064
 
3978
4065
  // core/schemas/permissions.ts
3979
- import { z as z8 } from "zod";
4066
+ import { z as z9 } from "zod";
3980
4067
  function buildDefaultPermissions() {
3981
4068
  const bash = {};
3982
4069
  for (const pattern of DEFAULT_BASH_ALLOW) {
@@ -4009,20 +4096,20 @@ var PermissionLevelSchema, FileOperationSchema, BashPermissionSchema, FilePermis
4009
4096
  var init_permissions = __esm({
4010
4097
  "core/schemas/permissions.ts"() {
4011
4098
  "use strict";
4012
- PermissionLevelSchema = z8.enum(["allow", "deny", "ask"]);
4013
- FileOperationSchema = z8.enum(["read", "write", "delete", "create"]);
4014
- BashPermissionSchema = z8.record(z8.string(), PermissionLevelSchema);
4015
- FilePermissionSchema = z8.record(z8.string(), PermissionLevelSchema);
4016
- WebPermissionSchema = z8.object({
4017
- enabled: z8.boolean().default(true),
4018
- allowedDomains: z8.array(z8.string()).optional(),
4019
- blockedDomains: z8.array(z8.string()).optional()
4099
+ PermissionLevelSchema = z9.enum(["allow", "deny", "ask"]);
4100
+ FileOperationSchema = z9.enum(["read", "write", "delete", "create"]);
4101
+ BashPermissionSchema = z9.record(z9.string(), PermissionLevelSchema);
4102
+ FilePermissionSchema = z9.record(z9.string(), PermissionLevelSchema);
4103
+ WebPermissionSchema = z9.object({
4104
+ enabled: z9.boolean().default(true),
4105
+ allowedDomains: z9.array(z9.string()).optional(),
4106
+ blockedDomains: z9.array(z9.string()).optional()
4020
4107
  });
4021
- PermissionsConfigSchema = z8.object({
4108
+ PermissionsConfigSchema = z9.object({
4022
4109
  /** Bash command permissions - glob patterns */
4023
4110
  bash: BashPermissionSchema.optional(),
4024
4111
  /** File operation permissions - glob patterns */
4025
- files: z8.object({
4112
+ files: z9.object({
4026
4113
  read: FilePermissionSchema.optional(),
4027
4114
  write: FilePermissionSchema.optional(),
4028
4115
  delete: FilePermissionSchema.optional()
@@ -4030,11 +4117,11 @@ var init_permissions = __esm({
4030
4117
  /** Web fetch permissions */
4031
4118
  web: WebPermissionSchema.optional(),
4032
4119
  /** Skill invocation permissions */
4033
- skills: z8.record(z8.string(), PermissionLevelSchema).optional(),
4120
+ skills: z9.record(z9.string(), PermissionLevelSchema).optional(),
4034
4121
  /** Doom loop protection - prevent infinite retries */
4035
- doomLoop: z8.object({
4036
- enabled: z8.boolean().default(true),
4037
- maxRetries: z8.number().default(3)
4122
+ doomLoop: z9.object({
4123
+ enabled: z9.boolean().default(true),
4124
+ maxRetries: z9.number().default(3)
4038
4125
  }).optional(),
4039
4126
  /** External directory access */
4040
4127
  externalDirectories: PermissionLevelSchema.default("ask")
@@ -4093,12 +4180,16 @@ import { homedir } from "os";
4093
4180
  function generateUUID() {
4094
4181
  return crypto3.randomUUID();
4095
4182
  }
4183
+ function getProjectPath2(projectId) {
4184
+ return join(GLOBAL_STORAGE, projectId);
4185
+ }
4096
4186
  var GLOBAL_STORAGE;
4097
4187
  var init_schemas = __esm({
4098
4188
  "core/schemas/schemas.ts"() {
4099
4189
  "use strict";
4100
4190
  __name(generateUUID, "generateUUID");
4101
4191
  GLOBAL_STORAGE = join(homedir(), ".prjct-cli", "projects");
4192
+ __name(getProjectPath2, "getProjectPath");
4102
4193
  }
4103
4194
  });
4104
4195
 
@@ -4107,6 +4198,7 @@ var init_schemas2 = __esm({
4107
4198
  "core/schemas/index.ts"() {
4108
4199
  "use strict";
4109
4200
  init_state();
4201
+ init_issues();
4110
4202
  init_project();
4111
4203
  init_agents();
4112
4204
  init_ideas();
@@ -10004,6 +10096,120 @@ var init_orchestrator_executor = __esm({
10004
10096
  }
10005
10097
  });
10006
10098
 
10099
+ // core/utils/agent-stream.ts
10100
+ import chalk from "chalk";
10101
+ function getIcon(domain) {
10102
+ return DOMAIN_ICONS[domain.toLowerCase()] || DOMAIN_ICONS.default;
10103
+ }
10104
+ var DOMAIN_ICONS, AgentStream, agentStream;
10105
+ var init_agent_stream = __esm({
10106
+ "core/utils/agent-stream.ts"() {
10107
+ "use strict";
10108
+ DOMAIN_ICONS = {
10109
+ database: "\u{1F4BE}",
10110
+ backend: "\u{1F527}",
10111
+ frontend: "\u{1F4E6}",
10112
+ testing: "\u{1F9EA}",
10113
+ devops: "\u{1F680}",
10114
+ uxui: "\u{1F3A8}",
10115
+ security: "\u{1F512}",
10116
+ docs: "\u{1F4DD}",
10117
+ api: "\u{1F310}",
10118
+ default: "\u26A1"
10119
+ };
10120
+ __name(getIcon, "getIcon");
10121
+ AgentStream = class {
10122
+ static {
10123
+ __name(this, "AgentStream");
10124
+ }
10125
+ currentAgent = null;
10126
+ startTime = 0;
10127
+ quiet = false;
10128
+ /**
10129
+ * Set quiet mode (no output)
10130
+ */
10131
+ setQuiet(quiet) {
10132
+ this.quiet = quiet;
10133
+ }
10134
+ /**
10135
+ * Show orchestration start
10136
+ */
10137
+ orchestrate(domains) {
10138
+ if (this.quiet) return;
10139
+ console.log(chalk.cyan(`
10140
+ \u{1F3AF} Orchestrating: ${domains.join(", ")} domains detected
10141
+ `));
10142
+ }
10143
+ /**
10144
+ * Start an agent activity block
10145
+ */
10146
+ startAgent(name, domain, description) {
10147
+ if (this.quiet) return;
10148
+ this.currentAgent = name;
10149
+ this.startTime = Date.now();
10150
+ const icon = getIcon(domain);
10151
+ console.log(chalk.cyan(`\u250C\u2500 ${icon} ${name} (${domain})`));
10152
+ if (description) {
10153
+ console.log(chalk.dim(`\u2502 ${description}`));
10154
+ }
10155
+ }
10156
+ /**
10157
+ * Show progress within current agent
10158
+ */
10159
+ progress(message) {
10160
+ if (this.quiet || !this.currentAgent) return;
10161
+ console.log(chalk.dim(`\u2502 \u2514\u2500\u2500 ${message}`));
10162
+ }
10163
+ /**
10164
+ * Show multiple progress items
10165
+ */
10166
+ progressList(items) {
10167
+ if (this.quiet || !this.currentAgent) return;
10168
+ for (const item of items) {
10169
+ console.log(chalk.dim(`\u2502 \u2514\u2500\u2500 ${item}`));
10170
+ }
10171
+ }
10172
+ /**
10173
+ * End current agent block
10174
+ */
10175
+ endAgent(success = true) {
10176
+ if (this.quiet || !this.currentAgent) return;
10177
+ const duration = Date.now() - this.startTime;
10178
+ const durationStr = this.formatDuration(duration);
10179
+ const icon = success ? chalk.green("\u2713") : chalk.red("\u2717");
10180
+ const status = success ? "Complete" : "Failed";
10181
+ console.log(`\u2514\u2500 ${icon} ${status} ${chalk.dim(`(${durationStr})`)}
10182
+ `);
10183
+ this.currentAgent = null;
10184
+ }
10185
+ /**
10186
+ * Show a simple status line (no block)
10187
+ */
10188
+ status(icon, message) {
10189
+ if (this.quiet) return;
10190
+ console.log(`${icon} ${message}`);
10191
+ }
10192
+ /**
10193
+ * Show task completion summary
10194
+ */
10195
+ complete(taskName, totalDuration) {
10196
+ if (this.quiet) return;
10197
+ const durationStr = totalDuration ? ` ${chalk.dim(`[${this.formatDuration(totalDuration)}]`)}` : "";
10198
+ console.log(chalk.green(`\u2705 ${taskName}${durationStr}`));
10199
+ }
10200
+ /**
10201
+ * Format duration in human-readable form
10202
+ */
10203
+ formatDuration(ms) {
10204
+ if (ms < 1e3) return `${ms}ms`;
10205
+ const seconds = (ms / 1e3).toFixed(1);
10206
+ return `${seconds}s`;
10207
+ }
10208
+ };
10209
+ agentStream = new AgentStream();
10210
+ }
10211
+ });
10212
+
10007
10213
  // core/agentic/command-executor.ts
10008
10214
  import fs23 from "fs";
10009
10215
  import path22 from "path";
@@ -10041,6 +10247,7 @@ var init_command_executor = __esm({
10041
10247
  init_plan_mode();
10042
10248
  init_template_executor();
10043
10249
  init_orchestrator_executor();
10250
+ init_agent_stream();
10044
10251
  RUNNING_FILE = path22.join(os10.homedir(), ".prjct-cli", ".running");
10045
10252
  __name(signalStart, "signalStart");
10046
10253
  __name(signalEnd, "signalEnd");
@@ -10129,11 +10336,16 @@ var init_command_executor = __esm({
10129
10336
  taskDescription,
10130
10337
  projectPath
10131
10338
  );
10132
- console.log(`\u{1F3AF} Orchestrator:`);
10133
- console.log(` \u2192 Domains: ${orchestratorContext.detectedDomains.join(", ")}`);
10134
- console.log(` \u2192 Agents: ${orchestratorContext.agents.map((a) => a.name).join(", ") || "none loaded"}`);
10339
+ if (orchestratorContext.detectedDomains.length > 0) {
10340
+ agentStream.orchestrate(orchestratorContext.detectedDomains);
10341
+ }
10342
+ for (const agent of orchestratorContext.agents) {
10343
+ const domain = agent.domain || agent.name.replace(".md", "");
10344
+ agentStream.startAgent(agent.name, domain, `Loading ${domain} specialist...`);
10345
+ agentStream.endAgent(true);
10346
+ }
10135
10347
  if (orchestratorContext.requiresFragmentation && orchestratorContext.subtasks) {
10136
- console.log(` \u2192 Subtasks: ${orchestratorContext.subtasks.length}`);
10348
+ agentStream.status("\u{1F4CB}", `${orchestratorContext.subtasks.length} subtasks planned`);
10137
10349
  }
10138
10350
  } catch (error) {
10139
10351
  console.warn(`\u26A0\uFE0F Orchestrator warning: ${error.message}`);
@@ -10321,7 +10533,7 @@ import https from "https";
10321
10533
  import fs24 from "fs";
10322
10534
  import path23 from "path";
10323
10535
  import os11 from "os";
10324
- import chalk from "chalk";
10536
+ import chalk2 from "chalk";
10325
10537
  var UpdateChecker, update_checker_default;
10326
10538
  var init_update_checker = __esm({
10327
10539
  "core/infrastructure/update-checker.ts"() {
@@ -10484,7 +10696,7 @@ var init_update_checker = __esm({
10484
10696
  if (!result || !result.updateAvailable) {
10485
10697
  return null;
10486
10698
  }
10487
- return "\n" + chalk.yellow("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510") + "\n" + chalk.yellow("\u2502") + " " + chalk.bold("Update available!") + " " + chalk.dim(`${result.currentVersion} \u2192 ${result.latestVersion}`) + " " + chalk.yellow("\u2502") + "\n" + chalk.yellow("\u2502") + " " + chalk.yellow("\u2502") + "\n" + chalk.yellow("\u2502") + " Run: " + chalk.cyan("npm update -g prjct-cli") + " " + chalk.yellow("\u2502") + "\n" + chalk.yellow("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518") + "\n";
10699
+ return "\n" + chalk2.yellow("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510") + "\n" + chalk2.yellow("\u2502") + " " + chalk2.bold("Update available!") + " " + chalk2.dim(`${result.currentVersion} \u2192 ${result.latestVersion}`) + " " + chalk2.yellow("\u2502") + "\n" + chalk2.yellow("\u2502") + " " + chalk2.yellow("\u2502") + "\n" + chalk2.yellow("\u2502") + " Run: " + chalk2.cyan("npm update -g prjct-cli") + " " + chalk2.yellow("\u2502") + "\n" + chalk2.yellow("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518") + "\n";
10488
10700
  }
10489
10701
  };
10490
10702
  update_checker_default = UpdateChecker;
@@ -10492,7 +10704,7 @@ var init_update_checker = __esm({
10492
10704
  });
10493
10705
 
10494
10706
  // core/utils/branding.ts
10495
- import chalk2 from "chalk";
10707
+ import chalk3 from "chalk";
10496
10708
  var SPINNER_FRAMES, SPINNER_SPEED, branding, branding_default;
10497
10709
  var init_branding = __esm({
10498
10710
  "core/utils/branding.ts"() {
@@ -10512,9 +10724,9 @@ var init_branding = __esm({
10512
10724
  },
10513
10725
  // CLI output (with chalk colors)
10514
10726
  cli: {
10515
- header: /* @__PURE__ */ __name(() => chalk2.cyan.bold("\u26A1") + " " + chalk2.cyan("prjct"), "header"),
10516
- footer: /* @__PURE__ */ __name(() => chalk2.dim("\u26A1 prjct"), "footer"),
10517
- spin: /* @__PURE__ */ __name((frame2, msg) => chalk2.cyan("\u26A1") + " " + chalk2.cyan("prjct") + " " + chalk2.cyan(SPINNER_FRAMES[frame2 % 10]) + " " + chalk2.dim(msg || ""), "spin")
10727
+ header: /* @__PURE__ */ __name(() => chalk3.cyan.bold("\u26A1") + " " + chalk3.cyan("prjct"), "header"),
10728
+ footer: /* @__PURE__ */ __name(() => chalk3.dim("\u26A1 prjct"), "footer"),
10729
+ spin: /* @__PURE__ */ __name((frame2, msg) => chalk3.cyan("\u26A1") + " " + chalk3.cyan("prjct") + " " + chalk3.cyan(SPINNER_FRAMES[frame2 % 10]) + " " + chalk3.dim(msg || ""), "spin")
10518
10730
  },
10519
10731
  // Template (plain text)
10520
10732
  template: {
@@ -10543,7 +10755,7 @@ Designed for [Claude](https://www.anthropic.com/claude)`,
10543
10755
  });
10544
10756
 
10545
10757
  // core/utils/output.ts
10546
- import chalk3 from "chalk";
10758
+ import chalk4 from "chalk";
10547
10759
  var FRAMES, SPEED, interval, frame, truncate, clear, out, output_default;
10548
10760
  var init_output = __esm({
10549
10761
  "core/utils/output.ts"() {
@@ -10576,17 +10788,17 @@ var init_output = __esm({
10576
10788
  },
10577
10789
  done(msg) {
10578
10790
  this.stop();
10579
- console.log(`${chalk3.green("\u2713")} ${truncate(msg, 65)}`);
10791
+ console.log(`${chalk4.green("\u2713")} ${truncate(msg, 65)}`);
10580
10792
  return this;
10581
10793
  },
10582
10794
  fail(msg) {
10583
10795
  this.stop();
10584
- console.log(`${chalk3.red("\u2717")} ${truncate(msg, 65)}`);
10796
+ console.log(`${chalk4.red("\u2717")} ${truncate(msg, 65)}`);
10585
10797
  return this;
10586
10798
  },
10587
10799
  warn(msg) {
10588
10800
  this.stop();
10589
- console.log(`${chalk3.yellow("\u26A0")} ${truncate(msg, 65)}`);
10801
+ console.log(`${chalk4.yellow("\u26A0")} ${truncate(msg, 65)}`);
10590
10802
  return this;
10591
10803
  },
10592
10804
  stop() {
@@ -10596,6 +10808,28 @@ var init_output = __esm({
10596
10808
  clear();
10597
10809
  }
10598
10810
  return this;
10811
+ },
10812
+ // Step counter: [3/7] Running tests...
10813
+ step(current, total, msg) {
10814
+ this.stop();
10815
+ const counter = chalk4.dim(`[${current}/${total}]`);
10816
+ interval = setInterval(() => {
10817
+ process.stdout.write(`\r${branding_default.cli.spin(frame++, `${counter} ${truncate(msg, 35)}`)}`);
10818
+ }, SPEED);
10819
+ return this;
10820
+ },
10821
+ // Progress bar: [████░░░░] 50% Analyzing...
10822
+ progress(current, total, msg) {
10823
+ this.stop();
10824
+ const percent = Math.round(current / total * 100);
10825
+ const filled = Math.round(percent / 10);
10826
+ const empty = 10 - filled;
10827
+ const bar = chalk4.cyan("\u2588".repeat(filled)) + chalk4.dim("\u2591".repeat(empty));
10828
+ const text = msg ? ` ${truncate(msg, 25)}` : "";
10829
+ interval = setInterval(() => {
10830
+ process.stdout.write(`\r${branding_default.cli.spin(frame++, `[${bar}] ${percent}%${text}`)}`);
10831
+ }, SPEED);
10832
+ return this;
10599
10833
  }
10600
10834
  };
10601
10835
  output_default = out;
@@ -10980,6 +11214,206 @@ var init_agent_service = __esm({
10980
11214
  }
10981
11215
  });
10982
11216
 
11217
+ // core/workflow/state-machine.ts
11218
+ var WORKFLOW_STATES, WorkflowStateMachine, workflowStateMachine;
11219
+ var init_state_machine = __esm({
11220
+ "core/workflow/state-machine.ts"() {
11221
+ "use strict";
11222
+ WORKFLOW_STATES = {
11223
+ idle: {
11224
+ transitions: ["task", "next"],
11225
+ prompt: "p. task <description> Start working",
11226
+ description: "No active task"
11227
+ },
11228
+ working: {
11229
+ transitions: ["done", "pause"],
11230
+ prompt: "p. done Complete task | p. pause Switch context",
11231
+ description: "Task in progress"
11232
+ },
11233
+ paused: {
11234
+ transitions: ["resume", "task"],
11235
+ prompt: "p. resume Continue | p. task <new> Start different",
11236
+ description: "Task paused"
11237
+ },
11238
+ completed: {
11239
+ transitions: ["ship", "task", "next"],
11240
+ prompt: "p. ship Ship it | p. task <next> Start next",
11241
+ description: "Task completed"
11242
+ },
11243
+ shipped: {
11244
+ transitions: ["task", "next"],
11245
+ prompt: "p. task <description> Start new task",
11246
+ description: "Feature shipped"
11247
+ }
11248
+ };
11249
+ WorkflowStateMachine = class {
11250
+ static {
11251
+ __name(this, "WorkflowStateMachine");
11252
+ }
11253
+ /**
11254
+ * Get current state from storage state
11255
+ */
11256
+ getCurrentState(storageState) {
11257
+ const task = storageState?.currentTask;
11258
+ if (!task) {
11259
+ return "idle";
11260
+ }
11261
+ const status = task.status?.toLowerCase();
11262
+ switch (status) {
11263
+ case "in_progress":
11264
+ case "working":
11265
+ return "working";
11266
+ case "paused":
11267
+ return "paused";
11268
+ case "completed":
11269
+ case "done":
11270
+ return "completed";
11271
+ case "shipped":
11272
+ return "shipped";
11273
+ default:
11274
+ return task ? "working" : "idle";
11275
+ }
11276
+ }
11277
+ /**
11278
+ * Check if a command is valid for the current state
11279
+ */
11280
+ canTransition(currentState, command) {
11281
+ const stateConfig = WORKFLOW_STATES[currentState];
11282
+ if (stateConfig.transitions.includes(command)) {
11283
+ return { valid: true };
11284
+ }
11285
+ const validCommands = stateConfig.transitions.map((c) => `p. ${c}`).join(", ");
11286
+ return {
11287
+ valid: false,
11288
+ error: `Cannot run 'p. ${command}' in ${currentState} state`,
11289
+ suggestion: `Valid commands: ${validCommands}`
11290
+ };
11291
+ }
11292
+ /**
11293
+ * Get the next state after a command
11294
+ */
11295
+ getNextState(currentState, command) {
11296
+ switch (command) {
11297
+ case "task":
11298
+ return "working";
11299
+ case "done":
11300
+ return "completed";
11301
+ case "pause":
11302
+ return "paused";
11303
+ case "resume":
11304
+ return "working";
11305
+ case "ship":
11306
+ return "shipped";
11307
+ case "next":
11308
+ return currentState;
11309
+ // next doesn't change state
11310
+ default:
11311
+ return currentState;
11312
+ }
11313
+ }
11314
+ /**
11315
+ * Get state definition
11316
+ */
11317
+ getStateInfo(state) {
11318
+ return WORKFLOW_STATES[state];
11319
+ }
11320
+ /**
11321
+ * Get prompt for current state
11322
+ */
11323
+ getPrompt(state) {
11324
+ return WORKFLOW_STATES[state].prompt;
11325
+ }
11326
+ /**
11327
+ * Get valid commands for current state
11328
+ */
11329
+ getValidCommands(state) {
11330
+ return WORKFLOW_STATES[state].transitions;
11331
+ }
11332
+ /**
11333
+ * Format next steps for display
11334
+ */
11335
+ formatNextSteps(state) {
11336
+ const stateConfig = WORKFLOW_STATES[state];
11337
+ return stateConfig.transitions.map((cmd) => {
11338
+ switch (cmd) {
11339
+ case "task":
11340
+ return "p. task <desc> Start new task";
11341
+ case "done":
11342
+ return "p. done Complete current task";
11343
+ case "pause":
11344
+ return "p. pause Pause and switch context";
11345
+ case "resume":
11346
+ return "p. resume Continue paused task";
11347
+ case "ship":
11348
+ return "p. ship Ship the feature";
11349
+ case "next":
11350
+ return "p. next View task queue";
11351
+ default:
11352
+ return `p. ${cmd}`;
11353
+ }
11354
+ });
11355
+ }
11356
+ };
11357
+ workflowStateMachine = new WorkflowStateMachine();
11358
+ }
11359
+ });
11360
+
11361
+ // core/utils/next-steps.ts
11362
+ import chalk5 from "chalk";
11363
+ function showNextSteps(command, options = {}) {
11364
+ if (options.quiet) return;
11365
+ const resultingState = COMMAND_TO_STATE[command] || "idle";
11366
+ const validCommands = workflowStateMachine.getValidCommands(resultingState);
11367
+ if (validCommands.length === 0) return;
11368
+ const steps = validCommands.map((cmd) => ({
11369
+ cmd: `p. ${cmd}`,
11370
+ desc: CMD_DESCRIPTIONS[cmd] || cmd
11371
+ }));
11372
+ console.log(chalk5.dim("\nNext:"));
11373
+ for (const step of steps) {
11374
+ const cmd = chalk5.cyan(step.cmd.padEnd(12));
11375
+ console.log(chalk5.dim(` ${cmd} \u2192 ${step.desc}`));
11376
+ }
11377
+ }
11378
+ function showStateInfo(state) {
11379
+ const info = workflowStateMachine.getStateInfo(state);
11380
+ console.log(chalk5.dim(`\u{1F4CD} State: ${chalk5.white(state.toUpperCase())} - ${info.description}`));
11381
+ }
11382
+ var CMD_DESCRIPTIONS, COMMAND_TO_STATE;
11383
+ var init_next_steps = __esm({
11384
+ "core/utils/next-steps.ts"() {
11385
+ "use strict";
11386
+ init_state_machine();
11387
+ CMD_DESCRIPTIONS = {
11388
+ task: "Start new task",
11389
+ done: "Complete current task",
11390
+ pause: "Pause and switch context",
11391
+ resume: "Continue paused task",
11392
+ ship: "Ship the feature",
11393
+ next: "View task queue",
11394
+ sync: "Analyze project",
11395
+ bug: "Report a bug",
11396
+ idea: "Capture an idea"
11397
+ };
11398
+ COMMAND_TO_STATE = {
11399
+ task: "working",
11400
+ done: "completed",
11401
+ "done-subtask": "working",
11402
+ // Still working on subtasks
11403
+ pause: "paused",
11404
+ resume: "working",
11405
+ ship: "shipped",
11406
+ next: "idle",
11407
+ sync: "idle",
11408
+ init: "idle",
11409
+ bug: "working",
11410
+ idea: "idle"
11411
+ };
11412
+ __name(showNextSteps, "showNextSteps");
11413
+ __name(showStateInfo, "showStateInfo");
11414
+ }
11415
+ });
11416
+
10983
11417
  // core/domain/analyzer.ts
10984
11418
  import fs27 from "fs/promises";
10985
11419
  import path26 from "path";
@@ -11536,6 +11970,7 @@ var init_analysis2 = __esm({
11536
11970
  init_generator();
11537
11971
  init_command_installer();
11538
11972
  init_services();
11973
+ init_next_steps();
11539
11974
  AnalysisCommands = class extends PrjctCommandsBase {
11540
11975
  static {
11541
11976
  __name(this, "AnalysisCommands");
@@ -11694,12 +12129,13 @@ var init_analysis2 = __esm({
11694
12129
  *
11695
12130
  * This eliminates the need for Claude to make 50+ individual tool calls.
11696
12131
  */
11697
- async sync(projectPath = process.cwd()) {
12132
+ async sync(projectPath = process.cwd(), options = {}) {
11698
12133
  try {
11699
12134
  const initResult = await this.ensureProjectInit(projectPath);
11700
12135
  if (!initResult.success) return initResult;
12136
+ const startTime = Date.now();
11701
12137
  console.log("\u{1F504} Syncing project...\n");
11702
- const result = await syncService.sync(projectPath);
12138
+ const result = await syncService.sync(projectPath, { aiTools: options.aiTools });
11703
12139
  if (!result.success) {
11704
12140
  console.error("\u274C Sync failed:", result.error);
11705
12141
  return { success: false, error: result.error };
@@ -11726,6 +12162,15 @@ var init_analysis2 = __esm({
11726
12162
  console.log(`\u251C\u2500\u2500 ${file}`);
11727
12163
  }
11728
12164
  console.log("");
12165
+ if (result.aiTools && result.aiTools.length > 0) {
12166
+ const successTools = result.aiTools.filter((t) => t.success);
12167
+ console.log(`\u{1F916} AI Tools Context (${successTools.length})`);
12168
+ for (const tool of result.aiTools) {
12169
+ const status = tool.success ? "\u2713" : "\u2717";
12170
+ console.log(`\u251C\u2500\u2500 ${status} ${tool.outputFile} (${tool.toolId})`);
12171
+ }
12172
+ console.log("");
12173
+ }
11729
12174
  const workflowAgents = result.agents.filter((a) => a.type === "workflow").map((a) => a.name);
11730
12175
  const domainAgents = result.agents.filter((a) => a.type === "domain").map((a) => a.name);
11731
12176
  console.log(`\u{1F916} Agents Regenerated (${result.agents.length})`);
@@ -11744,9 +12189,26 @@ var init_analysis2 = __esm({
11744
12189
  } else {
11745
12190
  console.log("\u2728 Repository is clean!\n");
11746
12191
  }
12192
+ showNextSteps("sync");
12193
+ const elapsed = Date.now() - startTime;
12194
+ const contextFilesCount = result.contextFiles.length + (result.aiTools?.filter((t) => t.success).length || 0);
12195
+ const agentCount = result.agents.length;
12196
+ console.log("\u2500".repeat(45));
12197
+ console.log(`\u{1F4CA} Sync Summary`);
12198
+ console.log(` Stack: ${result.stats.ecosystem} (${result.stats.frameworks.join(", ") || "no frameworks"})`);
12199
+ console.log(` Files: ${result.stats.fileCount} analyzed \u2192 ${contextFilesCount} context files`);
12200
+ console.log(` Agents: ${agentCount} (${result.agents.filter((a) => a.type === "domain").length} domain)`);
12201
+ console.log(` Time: ${(elapsed / 1e3).toFixed(1)}s`);
12202
+ console.log("");
11747
12203
  return {
11748
12204
  success: true,
11749
- data: result
12205
+ data: result,
12206
+ metrics: {
12207
+ elapsed,
12208
+ contextFilesCount,
12209
+ agentCount,
12210
+ fileCount: result.stats.fileCount
12211
+ }
11750
12212
  };
11751
12213
  } catch (error) {
11752
12214
  console.error("\u274C Error:", error.message);
@@ -11779,6 +12241,7 @@ var init_planning = __esm({
11779
12241
  init_storage2();
11780
12242
  init_author_detector();
11781
12243
  init_command_installer();
12244
+ init_next_steps();
11782
12245
  _analysisCommands = null;
11783
12246
  __name(getAnalysisCommands, "getAnalysisCommands");
11784
12247
  PlanningCommands = class extends PrjctCommandsBase {
@@ -11796,7 +12259,7 @@ var init_planning = __esm({
11796
12259
  output_default.warn("already initialized");
11797
12260
  return { success: false, message: "Already initialized" };
11798
12261
  }
11799
- output_default.spin("initializing...");
12262
+ output_default.step(1, 4, "Detecting author...");
11800
12263
  const detectedAuthor = await author_detector_default.detect();
11801
12264
  const author = {
11802
12265
  name: detectedAuthor.name || void 0,
@@ -11805,7 +12268,7 @@ var init_planning = __esm({
11805
12268
  };
11806
12269
  const config = await config_manager_default.createConfig(projectPath, author);
11807
12270
  const projectId = config.projectId;
11808
- output_default.spin("creating structure...");
12271
+ output_default.step(2, 4, "Creating structure...");
11809
12272
  await path_manager_default.ensureProjectStructure(projectId);
11810
12273
  const globalPath = path_manager_default.getGlobalProjectPath(projectId);
11811
12274
  const baseFiles = {
@@ -11832,11 +12295,11 @@ var init_planning = __esm({
11832
12295
  const isEmpty = await this._detectEmptyDirectory(projectPath);
11833
12296
  const hasCode = await this._detectExistingCode(projectPath);
11834
12297
  if (hasCode || !isEmpty) {
11835
- output_default.spin("analyzing project...");
12298
+ output_default.step(3, 4, "Analyzing project...");
11836
12299
  const analysis2 = await getAnalysisCommands();
11837
12300
  const analysisResult = await analysis2.analyze({}, projectPath);
11838
12301
  if (analysisResult.success) {
11839
- output_default.spin("generating agents...");
12302
+ output_default.step(4, 4, "Generating agents...");
11840
12303
  await analysis2.sync(projectPath);
11841
12304
  output_default.done("initialized");
11842
12305
  return { success: true, mode: "existing", projectId };
@@ -11866,6 +12329,7 @@ Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}
11866
12329
  }
11867
12330
  await command_installer_default.installGlobalConfig();
11868
12331
  output_default.done("initialized");
12332
+ showNextSteps("init");
11869
12333
  return { success: true, projectId };
11870
12334
  } catch (error) {
11871
12335
  output_default.fail(error.message);
@@ -11969,6 +12433,7 @@ Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}
11969
12433
  timestamp: date_helper_default.getTimestamp()
11970
12434
  });
11971
12435
  output_default.done(`bug [${severity}] \u2192 ${agent}`);
12436
+ showNextSteps("bug");
11972
12437
  return { success: true, bug: description, severity, agent };
11973
12438
  } catch (error) {
11974
12439
  output_default.fail(error.message);
@@ -12097,6 +12562,7 @@ Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}
12097
12562
  timestamp: date_helper_default.getTimestamp()
12098
12563
  });
12099
12564
  output_default.done(`idea captured: ${description.slice(0, 40)}`);
12565
+ showNextSteps("idea");
12100
12566
  return { success: true, mode: "capture", idea: description };
12101
12567
  }
12102
12568
  } catch (error) {
@@ -12121,8 +12587,8 @@ Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}
12121
12587
  const globalPath2 = path_manager_default.getGlobalProjectPath(projectId);
12122
12588
  const specsPath2 = path29.join(globalPath2, "planning", "specs");
12123
12589
  try {
12124
- const fs34 = await import("fs/promises");
12125
- const files = await fs34.readdir(specsPath2);
12590
+ const fs37 = await import("fs/promises");
12591
+ const files = await fs37.readdir(specsPath2);
12126
12592
  const specs = files.filter((f) => f.endsWith(".md") && f !== ".gitkeep");
12127
12593
  if (specs.length === 0) {
12128
12594
  output_default.warn("no specs yet");
@@ -12507,52 +12973,466 @@ var init_breakdown_service = __esm({
12507
12973
  }
12508
12974
  });
12509
12975
 
12510
- // core/services/sync-service.ts
12511
- import fs29 from "fs/promises";
12976
+ // core/ai-tools/registry.ts
12977
+ import { execSync as execSync4 } from "child_process";
12978
+ import fs29 from "fs";
12512
12979
  import path30 from "path";
12513
- import { exec as exec5 } from "child_process";
12514
- import { promisify as promisify5 } from "util";
12515
- var execAsync3, SyncService, syncService;
12516
- var init_sync_service = __esm({
12517
- "core/services/sync-service.ts"() {
12980
+ import os12 from "os";
12981
+ function getAIToolConfig(id) {
12982
+ return AI_TOOLS[id] || null;
12983
+ }
12984
+ function commandExists(cmd) {
12985
+ try {
12986
+ execSync4(`which ${cmd}`, { stdio: "ignore" });
12987
+ return true;
12988
+ } catch {
12989
+ return false;
12990
+ }
12991
+ }
12992
+ function detectInstalledTools(repoPath = process.cwd()) {
12993
+ const detected = [];
12994
+ if (commandExists("claude")) {
12995
+ detected.push("claude");
12996
+ }
12997
+ if (commandExists("cursor") || fs29.existsSync(path30.join(repoPath, ".cursor"))) {
12998
+ detected.push("cursor");
12999
+ }
13000
+ if (fs29.existsSync(path30.join(repoPath, ".github"))) {
13001
+ detected.push("copilot");
13002
+ }
13003
+ if (commandExists("windsurf") || fs29.existsSync(path30.join(repoPath, ".windsurf"))) {
13004
+ detected.push("windsurf");
13005
+ }
13006
+ if (fs29.existsSync(path30.join(repoPath, ".continue")) || fs29.existsSync(path30.join(os12.homedir(), ".continue"))) {
13007
+ detected.push("continue");
13008
+ }
13009
+ return detected;
13010
+ }
13011
+ function resolveToolIds(mode, repoPath = process.cwd()) {
13012
+ if (mode === "auto") {
13013
+ const detected = detectInstalledTools(repoPath);
13014
+ return detected.length > 0 ? detected : ["claude"];
13015
+ }
13016
+ if (mode === "all") {
13017
+ return SUPPORTED_AI_TOOLS;
13018
+ }
13019
+ return mode.filter((id) => AI_TOOLS[id]);
13020
+ }
13021
+ var AI_TOOLS, DEFAULT_AI_TOOLS, SUPPORTED_AI_TOOLS;
13022
+ var init_registry = __esm({
13023
+ "core/ai-tools/registry.ts"() {
12518
13024
  "use strict";
12519
- init_path_manager();
12520
- init_config_manager();
12521
- init_date_helper();
12522
- execAsync3 = promisify5(exec5);
12523
- SyncService = class {
12524
- static {
12525
- __name(this, "SyncService");
12526
- }
12527
- projectPath;
12528
- projectId = null;
12529
- globalPath = "";
12530
- cliVersion = "0.0.0";
12531
- constructor() {
12532
- this.projectPath = process.cwd();
13025
+ AI_TOOLS = {
13026
+ claude: {
13027
+ id: "claude",
13028
+ name: "Claude Code",
13029
+ outputFile: "CLAUDE.md",
13030
+ outputPath: "global",
13031
+ maxTokens: 3e3,
13032
+ format: "detailed",
13033
+ description: "Anthropic Claude Code CLI"
13034
+ },
13035
+ cursor: {
13036
+ id: "cursor",
13037
+ name: "Cursor",
13038
+ outputFile: ".cursorrules",
13039
+ outputPath: "repo",
13040
+ maxTokens: 2e3,
13041
+ format: "concise",
13042
+ description: "Cursor AI Editor"
13043
+ },
13044
+ copilot: {
13045
+ id: "copilot",
13046
+ name: "GitHub Copilot",
13047
+ outputFile: ".github/copilot-instructions.md",
13048
+ outputPath: "repo",
13049
+ maxTokens: 1500,
13050
+ format: "minimal",
13051
+ description: "GitHub Copilot"
13052
+ },
13053
+ windsurf: {
13054
+ id: "windsurf",
13055
+ name: "Windsurf",
13056
+ outputFile: ".windsurfrules",
13057
+ outputPath: "repo",
13058
+ maxTokens: 2e3,
13059
+ format: "concise",
13060
+ description: "Codeium Windsurf Editor"
13061
+ },
13062
+ continue: {
13063
+ id: "continue",
13064
+ name: "Continue.dev",
13065
+ outputFile: ".continue/config.json",
13066
+ outputPath: "repo",
13067
+ maxTokens: 1500,
13068
+ format: "json",
13069
+ description: "Continue.dev open-source AI assistant"
12533
13070
  }
12534
- /**
12535
- * Main sync method - does everything in one call
12536
- */
12537
- async sync(projectPath = process.cwd()) {
12538
- this.projectPath = projectPath;
12539
- try {
12540
- this.projectId = await config_manager_default.getProjectId(projectPath);
12541
- if (!this.projectId) {
12542
- return {
12543
- success: false,
12544
- projectId: "",
12545
- cliVersion: "",
12546
- git: this.emptyGitData(),
12547
- stats: this.emptyStats(),
12548
- commands: this.emptyCommands(),
12549
- stack: this.emptyStack(),
12550
- agents: [],
12551
- skills: [],
12552
- contextFiles: [],
12553
- error: "No prjct project. Run p. init first."
12554
- };
12555
- }
13071
+ };
13072
+ DEFAULT_AI_TOOLS = ["claude", "cursor", "copilot", "windsurf"];
13073
+ SUPPORTED_AI_TOOLS = Object.keys(AI_TOOLS);
13074
+ __name(getAIToolConfig, "getAIToolConfig");
13075
+ __name(commandExists, "commandExists");
13076
+ __name(detectInstalledTools, "detectInstalledTools");
13077
+ __name(resolveToolIds, "resolveToolIds");
13078
+ }
13079
+ });
13080
+
13081
+ // core/ai-tools/formatters.ts
13082
+ function formatForClaude(ctx, config) {
13083
+ return `# ${ctx.name} - Project Rules
13084
+ <!-- projectId: ${ctx.projectId} -->
13085
+ <!-- Generated: ${(/* @__PURE__ */ new Date()).toISOString()} -->
13086
+ <!-- Ecosystem: ${ctx.ecosystem} | Type: ${ctx.projectType} -->
13087
+
13088
+ ## THIS PROJECT (${ctx.ecosystem})
13089
+
13090
+ **Type:** ${ctx.projectType}
13091
+ **Path:** ${ctx.repoPath}
13092
+
13093
+ ### Commands (USE THESE, NOT OTHERS)
13094
+
13095
+ | Action | Command |
13096
+ |--------|---------|
13097
+ | Install dependencies | \`${ctx.commands.install}\` |
13098
+ | Run dev server | \`${ctx.commands.dev}\` |
13099
+ | Run tests | \`${ctx.commands.test}\` |
13100
+ | Build | \`${ctx.commands.build}\` |
13101
+ | Lint | \`${ctx.commands.lint}\` |
13102
+ | Format | \`${ctx.commands.format}\` |
13103
+
13104
+ ### Code Conventions
13105
+
13106
+ - **Languages**: ${ctx.languages.join(", ") || "Not detected"}
13107
+ - **Frameworks**: ${ctx.frameworks.join(", ") || "Not detected"}
13108
+
13109
+ ---
13110
+
13111
+ ## PRJCT RULES
13112
+
13113
+ ### Path Resolution
13114
+ **ALL prjct writes go to**: \`~/.prjct-cli/projects/${ctx.projectId}/\`
13115
+ - NEVER write to \`.prjct/\`
13116
+ - NEVER write to \`./\` for prjct data
13117
+
13118
+ ### Workflow
13119
+ \`\`\`
13120
+ p. sync \u2192 p. task "desc" \u2192 [work] \u2192 p. done \u2192 p. ship
13121
+ \`\`\`
13122
+
13123
+ | Command | Action |
13124
+ |---------|--------|
13125
+ | \`p. sync\` | Re-analyze project |
13126
+ | \`p. task X\` | Start task |
13127
+ | \`p. done\` | Complete subtask |
13128
+ | \`p. ship X\` | Ship feature |
13129
+
13130
+ ---
13131
+
13132
+ ## PROJECT STATE
13133
+
13134
+ | Field | Value |
13135
+ |-------|-------|
13136
+ | Name | ${ctx.name} |
13137
+ | Version | ${ctx.version} |
13138
+ | Ecosystem | ${ctx.ecosystem} |
13139
+ | Branch | ${ctx.branch} |
13140
+ | Files | ~${ctx.fileCount} |
13141
+ | Commits | ${ctx.commits} |
13142
+
13143
+ ---
13144
+
13145
+ ## AGENTS
13146
+
13147
+ Load from \`~/.prjct-cli/projects/${ctx.projectId}/agents/\`:
13148
+
13149
+ **Workflow**: ${ctx.agents.workflow.join(", ")}
13150
+ **Domain**: ${ctx.agents.domain.join(", ") || "none"}
13151
+ `;
13152
+ }
13153
+ function formatForCursor(ctx, config) {
13154
+ const rules = [];
13155
+ rules.push(`You are working on ${ctx.name}, a ${ctx.projectType} ${ctx.ecosystem} project.`);
13156
+ rules.push("");
13157
+ rules.push("## Tech Stack");
13158
+ if (ctx.languages.length > 0) {
13159
+ rules.push(`- Languages: ${ctx.languages.join(", ")}`);
13160
+ }
13161
+ if (ctx.frameworks.length > 0) {
13162
+ rules.push(`- Frameworks: ${ctx.frameworks.join(", ")}`);
13163
+ }
13164
+ rules.push("");
13165
+ rules.push("## Commands");
13166
+ rules.push(`- Install: \`${ctx.commands.install}\``);
13167
+ rules.push(`- Dev: \`${ctx.commands.dev}\``);
13168
+ rules.push(`- Test: \`${ctx.commands.test}\``);
13169
+ rules.push(`- Build: \`${ctx.commands.build}\``);
13170
+ rules.push("");
13171
+ rules.push("## Code Style");
13172
+ rules.push(`- Follow ${ctx.ecosystem} conventions`);
13173
+ rules.push("- Match existing code patterns in this project");
13174
+ rules.push("- Use idiomatic constructs for the language");
13175
+ rules.push("");
13176
+ rules.push("## Best Practices");
13177
+ rules.push("- Write clean, readable code");
13178
+ rules.push("- Add comments only for complex logic");
13179
+ rules.push("- Keep functions small and focused");
13180
+ rules.push("- Handle errors appropriately");
13181
+ rules.push("- Write tests for new functionality");
13182
+ return rules.join("\n");
13183
+ }
13184
+ function formatForCopilot(ctx, config) {
13185
+ const lines = [];
13186
+ lines.push("# Copilot Instructions");
13187
+ lines.push("");
13188
+ lines.push(`This is ${ctx.name}, a ${ctx.ecosystem} project.`);
13189
+ lines.push("");
13190
+ lines.push("## Project Info");
13191
+ lines.push(`- Type: ${ctx.projectType}`);
13192
+ lines.push(`- Stack: ${ctx.frameworks.join(", ") || ctx.ecosystem}`);
13193
+ lines.push("");
13194
+ lines.push("## Conventions");
13195
+ lines.push(`- Follow ${ctx.ecosystem} conventions`);
13196
+ lines.push("- Match existing code patterns");
13197
+ lines.push("- Keep code clean and readable");
13198
+ lines.push("");
13199
+ lines.push("## Commands");
13200
+ lines.push(`- Test: \`${ctx.commands.test}\``);
13201
+ lines.push(`- Build: \`${ctx.commands.build}\``);
13202
+ return lines.join("\n");
13203
+ }
13204
+ function formatForWindsurf(ctx, config) {
13205
+ const rules = [];
13206
+ rules.push(`# ${ctx.name}`);
13207
+ rules.push("");
13208
+ rules.push(`${ctx.projectType} project using ${ctx.ecosystem}.`);
13209
+ rules.push("");
13210
+ rules.push("## Stack");
13211
+ rules.push(`- ${ctx.languages.join(", ")}`);
13212
+ if (ctx.frameworks.length > 0) {
13213
+ rules.push(`- ${ctx.frameworks.join(", ")}`);
13214
+ }
13215
+ rules.push("");
13216
+ rules.push("## Commands");
13217
+ rules.push(`\`\`\`bash`);
13218
+ rules.push(`# Install`);
13219
+ rules.push(ctx.commands.install);
13220
+ rules.push(`# Dev`);
13221
+ rules.push(ctx.commands.dev);
13222
+ rules.push(`# Test`);
13223
+ rules.push(ctx.commands.test);
13224
+ rules.push(`# Build`);
13225
+ rules.push(ctx.commands.build);
13226
+ rules.push(`\`\`\``);
13227
+ rules.push("");
13228
+ rules.push("## Rules");
13229
+ rules.push(`- Follow ${ctx.ecosystem} conventions`);
13230
+ rules.push("- Match existing project patterns");
13231
+ rules.push("- Clean code, minimal comments");
13232
+ rules.push("- Test new functionality");
13233
+ return rules.join("\n");
13234
+ }
13235
+ function formatForContinue(ctx, config) {
13236
+ const systemMessage = [
13237
+ `You are working on ${ctx.name}, a ${ctx.projectType} ${ctx.ecosystem} project.`,
13238
+ "",
13239
+ `Stack: ${ctx.languages.join(", ")}${ctx.frameworks.length > 0 ? ` with ${ctx.frameworks.join(", ")}` : ""}`,
13240
+ "",
13241
+ "Commands:",
13242
+ `- Install: ${ctx.commands.install}`,
13243
+ `- Dev: ${ctx.commands.dev}`,
13244
+ `- Test: ${ctx.commands.test}`,
13245
+ `- Build: ${ctx.commands.build}`,
13246
+ "",
13247
+ `Follow ${ctx.ecosystem} conventions. Match existing code patterns.`
13248
+ ].join("\n");
13249
+ const continueConfig = {
13250
+ systemMessage,
13251
+ models: [],
13252
+ contextProviders: [
13253
+ { name: "code" },
13254
+ { name: "docs" },
13255
+ { name: "diff" },
13256
+ { name: "terminal" },
13257
+ { name: "problems" },
13258
+ { name: "folder" },
13259
+ { name: "codebase" }
13260
+ ],
13261
+ slashCommands: [
13262
+ { name: "edit", description: "Edit selected code" },
13263
+ { name: "comment", description: "Add comments to code" },
13264
+ { name: "share", description: "Export conversation" },
13265
+ { name: "cmd", description: "Run terminal command" }
13266
+ ],
13267
+ customCommands: [
13268
+ {
13269
+ name: "test",
13270
+ prompt: `Write tests for the selected code. Use the project's testing conventions. Test command: ${ctx.commands.test}`
13271
+ }
13272
+ ]
13273
+ };
13274
+ return JSON.stringify(continueConfig, null, 2);
13275
+ }
13276
+ function getFormatter(toolId) {
13277
+ const formatters = {
13278
+ claude: formatForClaude,
13279
+ cursor: formatForCursor,
13280
+ copilot: formatForCopilot,
13281
+ windsurf: formatForWindsurf,
13282
+ continue: formatForContinue
13283
+ };
13284
+ return formatters[toolId] || null;
13285
+ }
13286
+ var init_formatters = __esm({
13287
+ "core/ai-tools/formatters.ts"() {
13288
+ "use strict";
13289
+ __name(formatForClaude, "formatForClaude");
13290
+ __name(formatForCursor, "formatForCursor");
13291
+ __name(formatForCopilot, "formatForCopilot");
13292
+ __name(formatForWindsurf, "formatForWindsurf");
13293
+ __name(formatForContinue, "formatForContinue");
13294
+ __name(getFormatter, "getFormatter");
13295
+ }
13296
+ });
13297
+
13298
+ // core/ai-tools/generator.ts
13299
+ import fs30 from "fs/promises";
13300
+ import path31 from "path";
13301
+ async function generateAIToolContexts(context2, globalPath, repoPath, toolIds = DEFAULT_AI_TOOLS) {
13302
+ const results = [];
13303
+ for (const toolId of toolIds) {
13304
+ const config = getAIToolConfig(toolId);
13305
+ if (!config) {
13306
+ results.push({
13307
+ toolId,
13308
+ outputFile: "",
13309
+ outputPath: "",
13310
+ success: false,
13311
+ error: `Unknown tool: ${toolId}`
13312
+ });
13313
+ continue;
13314
+ }
13315
+ const result = await generateForTool(context2, config, globalPath, repoPath);
13316
+ results.push(result);
13317
+ }
13318
+ return results;
13319
+ }
13320
+ async function generateForTool(context2, config, globalPath, repoPath) {
13321
+ const formatter = getFormatter(config.id);
13322
+ if (!formatter) {
13323
+ return {
13324
+ toolId: config.id,
13325
+ outputFile: config.outputFile,
13326
+ outputPath: "",
13327
+ success: false,
13328
+ error: `No formatter for: ${config.id}`
13329
+ };
13330
+ }
13331
+ try {
13332
+ const content = formatter(context2, config);
13333
+ let outputPath;
13334
+ if (config.outputPath === "repo") {
13335
+ outputPath = path31.join(repoPath, config.outputFile);
13336
+ } else {
13337
+ outputPath = path31.join(globalPath, "context", config.outputFile);
13338
+ }
13339
+ await fs30.mkdir(path31.dirname(outputPath), { recursive: true });
13340
+ await fs30.writeFile(outputPath, content, "utf-8");
13341
+ return {
13342
+ toolId: config.id,
13343
+ outputFile: config.outputFile,
13344
+ outputPath,
13345
+ success: true
13346
+ };
13347
+ } catch (error) {
13348
+ return {
13349
+ toolId: config.id,
13350
+ outputFile: config.outputFile,
13351
+ outputPath: "",
13352
+ success: false,
13353
+ error: error.message
13354
+ };
13355
+ }
13356
+ }
13357
+ var init_generator2 = __esm({
13358
+ "core/ai-tools/generator.ts"() {
13359
+ "use strict";
13360
+ init_registry();
13361
+ init_formatters();
13362
+ __name(generateAIToolContexts, "generateAIToolContexts");
13363
+ __name(generateForTool, "generateForTool");
13364
+ }
13365
+ });
13366
+
13367
+ // core/ai-tools/index.ts
13368
+ var init_ai_tools = __esm({
13369
+ "core/ai-tools/index.ts"() {
13370
+ "use strict";
13371
+ init_registry();
13372
+ init_formatters();
13373
+ init_generator2();
13374
+ }
13375
+ });
13376
+
13377
+ // core/services/sync-service.ts
13378
+ import fs31 from "fs/promises";
13379
+ import path32 from "path";
13380
+ import { exec as exec5 } from "child_process";
13381
+ import { promisify as promisify5 } from "util";
13382
+ var execAsync3, SyncService, syncService;
13383
+ var init_sync_service = __esm({
13384
+ "core/services/sync-service.ts"() {
13385
+ "use strict";
13386
+ init_path_manager();
13387
+ init_config_manager();
13388
+ init_date_helper();
13389
+ init_ai_tools();
13390
+ execAsync3 = promisify5(exec5);
13391
+ SyncService = class {
13392
+ static {
13393
+ __name(this, "SyncService");
13394
+ }
13395
+ projectPath;
13396
+ projectId = null;
13397
+ globalPath = "";
13398
+ cliVersion = "0.0.0";
13399
+ constructor() {
13400
+ this.projectPath = process.cwd();
13401
+ }
13402
+ /**
13403
+ * Main sync method - does everything in one call
13404
+ */
13405
+ async sync(projectPath = process.cwd(), options = {}) {
13406
+ this.projectPath = projectPath;
13407
+ let aiToolIds;
13408
+ if (!options.aiTools || options.aiTools.length === 0) {
13409
+ aiToolIds = DEFAULT_AI_TOOLS;
13410
+ } else if (options.aiTools[0] === "auto") {
13411
+ aiToolIds = detectInstalledTools(projectPath);
13412
+ if (aiToolIds.length === 0) aiToolIds = ["claude"];
13413
+ } else if (options.aiTools[0] === "all") {
13414
+ aiToolIds = resolveToolIds("all", projectPath);
13415
+ } else {
13416
+ aiToolIds = options.aiTools;
13417
+ }
13418
+ try {
13419
+ this.projectId = await config_manager_default.getProjectId(projectPath);
13420
+ if (!this.projectId) {
13421
+ return {
13422
+ success: false,
13423
+ projectId: "",
13424
+ cliVersion: "",
13425
+ git: this.emptyGitData(),
13426
+ stats: this.emptyStats(),
13427
+ commands: this.emptyCommands(),
13428
+ stack: this.emptyStack(),
13429
+ agents: [],
13430
+ skills: [],
13431
+ contextFiles: [],
13432
+ aiTools: [],
13433
+ error: "No prjct project. Run p. init first."
13434
+ };
13435
+ }
12556
13436
  this.globalPath = path_manager_default.getGlobalProjectPath(this.projectId);
12557
13437
  this.cliVersion = await this.getCliVersion();
12558
13438
  await this.ensureDirectories();
@@ -12563,6 +13443,31 @@ var init_sync_service = __esm({
12563
13443
  const agents = await this.generateAgents(stack, stats);
12564
13444
  const skills = this.configureSkills(agents);
12565
13445
  const contextFiles = await this.generateContextFiles(git, stats, commands, agents);
13446
+ const projectContext = {
13447
+ projectId: this.projectId,
13448
+ name: stats.name,
13449
+ version: stats.version,
13450
+ ecosystem: stats.ecosystem,
13451
+ projectType: stats.projectType,
13452
+ languages: stats.languages,
13453
+ frameworks: stats.frameworks,
13454
+ repoPath: this.projectPath,
13455
+ branch: git.branch,
13456
+ fileCount: stats.fileCount,
13457
+ commits: git.commits,
13458
+ hasChanges: git.hasChanges,
13459
+ commands,
13460
+ agents: {
13461
+ workflow: agents.filter((a) => a.type === "workflow").map((a) => a.name),
13462
+ domain: agents.filter((a) => a.type === "domain").map((a) => a.name)
13463
+ }
13464
+ };
13465
+ const aiToolResults = await generateAIToolContexts(
13466
+ projectContext,
13467
+ this.globalPath,
13468
+ this.projectPath,
13469
+ aiToolIds
13470
+ );
12566
13471
  await this.updateProjectJson(git, stats);
12567
13472
  await this.updateStateJson(stats, stack);
12568
13473
  await this.logToMemory(git, stats);
@@ -12576,7 +13481,12 @@ var init_sync_service = __esm({
12576
13481
  stack,
12577
13482
  agents,
12578
13483
  skills,
12579
- contextFiles
13484
+ contextFiles,
13485
+ aiTools: aiToolResults.map((r) => ({
13486
+ toolId: r.toolId,
13487
+ outputFile: r.outputFile,
13488
+ success: r.success
13489
+ }))
12580
13490
  };
12581
13491
  } catch (error) {
12582
13492
  return {
@@ -12590,6 +13500,7 @@ var init_sync_service = __esm({
12590
13500
  agents: [],
12591
13501
  skills: [],
12592
13502
  contextFiles: [],
13503
+ aiTools: [],
12593
13504
  error: error.message
12594
13505
  };
12595
13506
  }
@@ -12600,7 +13511,7 @@ var init_sync_service = __esm({
12600
13511
  async ensureDirectories() {
12601
13512
  const dirs = ["storage", "context", "agents", "memory", "analysis", "config", "sync"];
12602
13513
  for (const dir of dirs) {
12603
- await fs29.mkdir(path30.join(this.globalPath, dir), { recursive: true });
13514
+ await fs31.mkdir(path32.join(this.globalPath, dir), { recursive: true });
12604
13515
  }
12605
13516
  }
12606
13517
  // ==========================================================================
@@ -12670,7 +13581,7 @@ var init_sync_service = __esm({
12670
13581
  const stats = {
12671
13582
  fileCount: 0,
12672
13583
  version: "0.0.0",
12673
- name: path30.basename(this.projectPath),
13584
+ name: path32.basename(this.projectPath),
12674
13585
  ecosystem: "unknown",
12675
13586
  projectType: "simple",
12676
13587
  languages: [],
@@ -12686,8 +13597,8 @@ var init_sync_service = __esm({
12686
13597
  stats.fileCount = 0;
12687
13598
  }
12688
13599
  try {
12689
- const pkgPath = path30.join(this.projectPath, "package.json");
12690
- const pkg = JSON.parse(await fs29.readFile(pkgPath, "utf-8"));
13600
+ const pkgPath = path32.join(this.projectPath, "package.json");
13601
+ const pkg = JSON.parse(await fs31.readFile(pkgPath, "utf-8"));
12691
13602
  stats.version = pkg.version || "0.0.0";
12692
13603
  stats.name = pkg.name || stats.name;
12693
13604
  stats.ecosystem = "JavaScript";
@@ -12797,8 +13708,8 @@ var init_sync_service = __esm({
12797
13708
  frameworks: []
12798
13709
  };
12799
13710
  try {
12800
- const pkgPath = path30.join(this.projectPath, "package.json");
12801
- const pkg = JSON.parse(await fs29.readFile(pkgPath, "utf-8"));
13711
+ const pkgPath = path32.join(this.projectPath, "package.json");
13712
+ const pkg = JSON.parse(await fs31.readFile(pkgPath, "utf-8"));
12802
13713
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
12803
13714
  if (deps.react || deps.vue || deps.svelte || deps["@angular/core"]) {
12804
13715
  stack.hasFrontend = true;
@@ -12831,12 +13742,12 @@ var init_sync_service = __esm({
12831
13742
  // ==========================================================================
12832
13743
  async generateAgents(stack, stats) {
12833
13744
  const agents = [];
12834
- const agentsPath = path30.join(this.globalPath, "agents");
13745
+ const agentsPath = path32.join(this.globalPath, "agents");
12835
13746
  try {
12836
- const files = await fs29.readdir(agentsPath);
13747
+ const files = await fs31.readdir(agentsPath);
12837
13748
  for (const file of files) {
12838
13749
  if (file.endsWith(".md")) {
12839
- await fs29.unlink(path30.join(agentsPath, file));
13750
+ await fs31.unlink(path32.join(agentsPath, file));
12840
13751
  }
12841
13752
  }
12842
13753
  } catch {
@@ -12873,7 +13784,7 @@ var init_sync_service = __esm({
12873
13784
  async generateWorkflowAgent(name, agentsPath) {
12874
13785
  let content = "";
12875
13786
  try {
12876
- const templatePath = path30.join(
13787
+ const templatePath = path32.join(
12877
13788
  __dirname,
12878
13789
  "..",
12879
13790
  "..",
@@ -12882,16 +13793,16 @@ var init_sync_service = __esm({
12882
13793
  "workflow",
12883
13794
  `${name}.md`
12884
13795
  );
12885
- content = await fs29.readFile(templatePath, "utf-8");
13796
+ content = await fs31.readFile(templatePath, "utf-8");
12886
13797
  } catch {
12887
13798
  content = this.generateMinimalWorkflowAgent(name);
12888
13799
  }
12889
- await fs29.writeFile(path30.join(agentsPath, `${name}.md`), content, "utf-8");
13800
+ await fs31.writeFile(path32.join(agentsPath, `${name}.md`), content, "utf-8");
12890
13801
  }
12891
13802
  async generateDomainAgent(name, agentsPath, stats, stack) {
12892
13803
  let content = "";
12893
13804
  try {
12894
- const templatePath = path30.join(
13805
+ const templatePath = path32.join(
12895
13806
  __dirname,
12896
13807
  "..",
12897
13808
  "..",
@@ -12900,14 +13811,14 @@ var init_sync_service = __esm({
12900
13811
  "domain",
12901
13812
  `${name}.md`
12902
13813
  );
12903
- content = await fs29.readFile(templatePath, "utf-8");
13814
+ content = await fs31.readFile(templatePath, "utf-8");
12904
13815
  content = content.replace("{projectName}", stats.name);
12905
13816
  content = content.replace("{frameworks}", stack.frameworks.join(", ") || "None detected");
12906
13817
  content = content.replace("{ecosystem}", stats.ecosystem);
12907
13818
  } catch {
12908
13819
  content = this.generateMinimalDomainAgent(name, stats, stack);
12909
13820
  }
12910
- await fs29.writeFile(path30.join(agentsPath, `${name}.md`), content, "utf-8");
13821
+ await fs31.writeFile(path32.join(agentsPath, `${name}.md`), content, "utf-8");
12911
13822
  }
12912
13823
  generateMinimalWorkflowAgent(name) {
12913
13824
  const descriptions = {
@@ -12975,8 +13886,8 @@ You are the ${name} expert for this project. Apply best practices for the detect
12975
13886
  })),
12976
13887
  agentSkillMap: Object.fromEntries(skills.map((s) => [s.agent, s.skill]))
12977
13888
  };
12978
- fs29.writeFile(
12979
- path30.join(this.globalPath, "config", "skills.json"),
13889
+ fs31.writeFile(
13890
+ path32.join(this.globalPath, "config", "skills.json"),
12980
13891
  JSON.stringify(skillsConfig, null, 2),
12981
13892
  "utf-8"
12982
13893
  ).catch(() => {
@@ -12987,7 +13898,7 @@ You are the ${name} expert for this project. Apply best practices for the detect
12987
13898
  // CONTEXT FILE GENERATION
12988
13899
  // ==========================================================================
12989
13900
  async generateContextFiles(git, stats, commands, agents) {
12990
- const contextPath = path30.join(this.globalPath, "context");
13901
+ const contextPath = path32.join(this.globalPath, "context");
12991
13902
  const files = [];
12992
13903
  await this.generateClaudeMd(contextPath, git, stats, commands, agents);
12993
13904
  files.push("context/CLAUDE.md");
@@ -13073,13 +13984,13 @@ Load from \`~/.prjct-cli/projects/${this.projectId}/agents/\`:
13073
13984
  **Workflow**: ${workflowAgents.join(", ")}
13074
13985
  **Domain**: ${domainAgents.join(", ") || "none"}
13075
13986
  `;
13076
- await fs29.writeFile(path30.join(contextPath, "CLAUDE.md"), content, "utf-8");
13987
+ await fs31.writeFile(path32.join(contextPath, "CLAUDE.md"), content, "utf-8");
13077
13988
  }
13078
13989
  async generateNowMd(contextPath) {
13079
13990
  let currentTask = null;
13080
13991
  try {
13081
- const statePath = path30.join(this.globalPath, "storage", "state.json");
13082
- const state = JSON.parse(await fs29.readFile(statePath, "utf-8"));
13992
+ const statePath = path32.join(this.globalPath, "storage", "state.json");
13993
+ const state = JSON.parse(await fs31.readFile(statePath, "utf-8"));
13083
13994
  currentTask = state.currentTask;
13084
13995
  } catch {
13085
13996
  }
@@ -13095,41 +14006,41 @@ _No active task_
13095
14006
 
13096
14007
  Use \`p. task "description"\` to start working.
13097
14008
  `;
13098
- await fs29.writeFile(path30.join(contextPath, "now.md"), content, "utf-8");
14009
+ await fs31.writeFile(path32.join(contextPath, "now.md"), content, "utf-8");
13099
14010
  }
13100
14011
  async generateNextMd(contextPath) {
13101
14012
  let queue = { tasks: [] };
13102
14013
  try {
13103
- const queuePath = path30.join(this.globalPath, "storage", "queue.json");
13104
- queue = JSON.parse(await fs29.readFile(queuePath, "utf-8"));
14014
+ const queuePath = path32.join(this.globalPath, "storage", "queue.json");
14015
+ queue = JSON.parse(await fs31.readFile(queuePath, "utf-8"));
13105
14016
  } catch {
13106
14017
  }
13107
14018
  const content = `# NEXT
13108
14019
 
13109
14020
  ${queue.tasks.length > 0 ? queue.tasks.map((t, i) => `${i + 1}. ${t.description}${t.priority ? ` [${t.priority}]` : ""}`).join("\n") : "_Empty queue_"}
13110
14021
  `;
13111
- await fs29.writeFile(path30.join(contextPath, "next.md"), content, "utf-8");
14022
+ await fs31.writeFile(path32.join(contextPath, "next.md"), content, "utf-8");
13112
14023
  }
13113
14024
  async generateIdeasMd(contextPath) {
13114
14025
  let ideas = { ideas: [] };
13115
14026
  try {
13116
- const ideasPath = path30.join(this.globalPath, "storage", "ideas.json");
13117
- ideas = JSON.parse(await fs29.readFile(ideasPath, "utf-8"));
14027
+ const ideasPath = path32.join(this.globalPath, "storage", "ideas.json");
14028
+ ideas = JSON.parse(await fs31.readFile(ideasPath, "utf-8"));
13118
14029
  } catch {
13119
14030
  }
13120
14031
  const content = `# IDEAS
13121
14032
 
13122
14033
  ${ideas.ideas.length > 0 ? ideas.ideas.map((i) => `- ${i.text}${i.priority ? ` [${i.priority}]` : ""}`).join("\n") : "_No ideas captured yet_"}
13123
14034
  `;
13124
- await fs29.writeFile(path30.join(contextPath, "ideas.md"), content, "utf-8");
14035
+ await fs31.writeFile(path32.join(contextPath, "ideas.md"), content, "utf-8");
13125
14036
  }
13126
14037
  async generateShippedMd(contextPath) {
13127
14038
  let shipped = {
13128
14039
  shipped: []
13129
14040
  };
13130
14041
  try {
13131
- const shippedPath = path30.join(this.globalPath, "storage", "shipped.json");
13132
- shipped = JSON.parse(await fs29.readFile(shippedPath, "utf-8"));
14042
+ const shippedPath = path32.join(this.globalPath, "storage", "shipped.json");
14043
+ shipped = JSON.parse(await fs31.readFile(shippedPath, "utf-8"));
13133
14044
  } catch {
13134
14045
  }
13135
14046
  const content = `# SHIPPED \u{1F680}
@@ -13138,16 +14049,16 @@ ${shipped.shipped.length > 0 ? shipped.shipped.slice(-10).map((s) => `- **${s.na
13138
14049
 
13139
14050
  **Total shipped:** ${shipped.shipped.length}
13140
14051
  `;
13141
- await fs29.writeFile(path30.join(contextPath, "shipped.md"), content, "utf-8");
14052
+ await fs31.writeFile(path32.join(contextPath, "shipped.md"), content, "utf-8");
13142
14053
  }
13143
14054
  // ==========================================================================
13144
14055
  // PROJECT.JSON UPDATE
13145
14056
  // ==========================================================================
13146
14057
  async updateProjectJson(git, stats) {
13147
- const projectJsonPath = path30.join(this.globalPath, "project.json");
14058
+ const projectJsonPath = path32.join(this.globalPath, "project.json");
13148
14059
  let existing = {};
13149
14060
  try {
13150
- existing = JSON.parse(await fs29.readFile(projectJsonPath, "utf-8"));
14061
+ existing = JSON.parse(await fs31.readFile(projectJsonPath, "utf-8"));
13151
14062
  } catch {
13152
14063
  }
13153
14064
  const updated = {
@@ -13166,16 +14077,16 @@ ${shipped.shipped.length > 0 ? shipped.shipped.slice(-10).map((s) => `- **${s.na
13166
14077
  createdAt: existing.createdAt || date_helper_default.getTimestamp(),
13167
14078
  lastSync: date_helper_default.getTimestamp()
13168
14079
  };
13169
- await fs29.writeFile(projectJsonPath, JSON.stringify(updated, null, 2), "utf-8");
14080
+ await fs31.writeFile(projectJsonPath, JSON.stringify(updated, null, 2), "utf-8");
13170
14081
  }
13171
14082
  // ==========================================================================
13172
14083
  // STATE.JSON UPDATE
13173
14084
  // ==========================================================================
13174
14085
  async updateStateJson(stats, stack) {
13175
- const statePath = path30.join(this.globalPath, "storage", "state.json");
14086
+ const statePath = path32.join(this.globalPath, "storage", "state.json");
13176
14087
  let state = {};
13177
14088
  try {
13178
- state = JSON.parse(await fs29.readFile(statePath, "utf-8"));
14089
+ state = JSON.parse(await fs31.readFile(statePath, "utf-8"));
13179
14090
  } catch {
13180
14091
  }
13181
14092
  state.projectId = this.projectId;
@@ -13202,13 +14113,13 @@ ${shipped.shipped.length > 0 ? shipped.shipped.slice(-10).map((s) => `- **${s.na
13202
14113
  lastAction: "Synced project",
13203
14114
  nextAction: 'Run `p. task "description"` to start working'
13204
14115
  };
13205
- await fs29.writeFile(statePath, JSON.stringify(state, null, 2), "utf-8");
14116
+ await fs31.writeFile(statePath, JSON.stringify(state, null, 2), "utf-8");
13206
14117
  }
13207
14118
  // ==========================================================================
13208
14119
  // MEMORY LOGGING
13209
14120
  // ==========================================================================
13210
14121
  async logToMemory(git, stats) {
13211
- const memoryPath = path30.join(this.globalPath, "memory", "events.jsonl");
14122
+ const memoryPath = path32.join(this.globalPath, "memory", "events.jsonl");
13212
14123
  const event = {
13213
14124
  ts: date_helper_default.getTimestamp(),
13214
14125
  action: "sync",
@@ -13217,14 +14128,14 @@ ${shipped.shipped.length > 0 ? shipped.shipped.slice(-10).map((s) => `- **${s.na
13217
14128
  fileCount: stats.fileCount,
13218
14129
  commitCount: git.commits
13219
14130
  };
13220
- await fs29.appendFile(memoryPath, JSON.stringify(event) + "\n", "utf-8");
14131
+ await fs31.appendFile(memoryPath, JSON.stringify(event) + "\n", "utf-8");
13221
14132
  }
13222
14133
  // ==========================================================================
13223
14134
  // HELPERS
13224
14135
  // ==========================================================================
13225
14136
  async fileExists(filename) {
13226
14137
  try {
13227
- await fs29.access(path30.join(this.projectPath, filename));
14138
+ await fs31.access(path32.join(this.projectPath, filename));
13228
14139
  return true;
13229
14140
  } catch {
13230
14141
  return false;
@@ -13232,8 +14143,8 @@ ${shipped.shipped.length > 0 ? shipped.shipped.slice(-10).map((s) => `- **${s.na
13232
14143
  }
13233
14144
  async getCliVersion() {
13234
14145
  try {
13235
- const pkgPath = path30.join(__dirname, "..", "..", "package.json");
13236
- const pkg = JSON.parse(await fs29.readFile(pkgPath, "utf-8"));
14146
+ const pkgPath = path32.join(__dirname, "..", "..", "package.json");
14147
+ const pkg = JSON.parse(await fs31.readFile(pkgPath, "utf-8"));
13237
14148
  return pkg.version || "0.0.0";
13238
14149
  } catch {
13239
14150
  return "0.0.0";
@@ -13378,6 +14289,886 @@ var init_base = __esm({
13378
14289
  }
13379
14290
  });
13380
14291
 
14292
+ // core/utils/keychain.ts
14293
+ import { exec as exec6 } from "child_process";
14294
+ import { promisify as promisify6 } from "util";
14295
+ async function getCredential(key) {
14296
+ if (process.platform !== "darwin") {
14297
+ return getEnvFallback(key);
14298
+ }
14299
+ try {
14300
+ const { stdout } = await execAsync4(
14301
+ `security find-generic-password -s "${SERVICE_NAME}" -a "${key}" -w 2>/dev/null`
14302
+ );
14303
+ return stdout.trim() || null;
14304
+ } catch (_error) {
14305
+ return getEnvFallback(key);
14306
+ }
14307
+ }
14308
+ function getEnvFallback(key) {
14309
+ const envMap = {
14310
+ "linear-api-key": "LINEAR_API_KEY",
14311
+ "jira-api-token": "JIRA_API_TOKEN"
14312
+ };
14313
+ const envVar = envMap[key];
14314
+ return process.env[envVar] || null;
14315
+ }
14316
+ var execAsync4, SERVICE_NAME;
14317
+ var init_keychain = __esm({
14318
+ "core/utils/keychain.ts"() {
14319
+ "use strict";
14320
+ execAsync4 = promisify6(exec6);
14321
+ SERVICE_NAME = "prjct-cli";
14322
+ __name(getCredential, "getCredential");
14323
+ __name(getEnvFallback, "getEnvFallback");
14324
+ }
14325
+ });
14326
+
14327
+ // core/integrations/linear/client.ts
14328
+ import { LinearClient as LinearSDK } from "@linear/sdk";
14329
+ var LINEAR_STATUS_MAP, LINEAR_PRIORITY_MAP, PRIORITY_TO_LINEAR, LinearProvider, linearProvider;
14330
+ var init_client = __esm({
14331
+ "core/integrations/linear/client.ts"() {
14332
+ "use strict";
14333
+ init_keychain();
14334
+ LINEAR_STATUS_MAP = {
14335
+ backlog: "backlog",
14336
+ unstarted: "todo",
14337
+ started: "in_progress",
14338
+ completed: "done",
14339
+ canceled: "cancelled",
14340
+ cancelled: "cancelled"
14341
+ };
14342
+ LINEAR_PRIORITY_MAP = {
14343
+ 0: "none",
14344
+ 1: "urgent",
14345
+ 2: "high",
14346
+ 3: "medium",
14347
+ 4: "low"
14348
+ };
14349
+ PRIORITY_TO_LINEAR = {
14350
+ none: 0,
14351
+ urgent: 1,
14352
+ high: 2,
14353
+ medium: 3,
14354
+ low: 4
14355
+ };
14356
+ LinearProvider = class {
14357
+ static {
14358
+ __name(this, "LinearProvider");
14359
+ }
14360
+ name = "linear";
14361
+ displayName = "Linear";
14362
+ sdk = null;
14363
+ config = null;
14364
+ /**
14365
+ * Check if provider is configured
14366
+ */
14367
+ isConfigured() {
14368
+ return this.sdk !== null && this.config?.enabled === true;
14369
+ }
14370
+ /**
14371
+ * Initialize with config
14372
+ * Looks for API key in: 1) config.apiKey, 2) macOS Keychain, 3) LINEAR_API_KEY env var
14373
+ */
14374
+ async initialize(config) {
14375
+ this.config = config;
14376
+ const apiKey = config.apiKey || await getCredential("linear-api-key");
14377
+ if (!apiKey) {
14378
+ throw new Error(
14379
+ "LINEAR_API_KEY not configured. Run `p. linear setup` to configure."
14380
+ );
14381
+ }
14382
+ this.sdk = new LinearSDK({ apiKey });
14383
+ try {
14384
+ const viewer = await this.sdk.viewer;
14385
+ console.error(`[linear] Connected as ${viewer.name} (${viewer.email})`);
14386
+ } catch (error) {
14387
+ this.sdk = null;
14388
+ throw new Error(`Linear connection failed: ${error.message}`);
14389
+ }
14390
+ }
14391
+ /**
14392
+ * Get issues assigned to current user
14393
+ * Filters by configured team if defaultTeamId is set
14394
+ */
14395
+ async fetchAssignedIssues(options) {
14396
+ if (!this.sdk) throw new Error("Linear not initialized");
14397
+ const viewer = await this.sdk.viewer;
14398
+ const filter = {};
14399
+ if (!options?.includeCompleted) {
14400
+ filter.state = { type: { nin: ["completed", "canceled"] } };
14401
+ }
14402
+ if (this.config?.defaultTeamId) {
14403
+ filter.team = { id: { eq: this.config.defaultTeamId } };
14404
+ }
14405
+ const assignedIssues = await viewer.assignedIssues({
14406
+ first: options?.limit || 50,
14407
+ filter: Object.keys(filter).length > 0 ? filter : void 0
14408
+ });
14409
+ return Promise.all(
14410
+ assignedIssues.nodes.map((issue) => this.mapIssue(issue))
14411
+ );
14412
+ }
14413
+ /**
14414
+ * Get issues from a team
14415
+ */
14416
+ async fetchTeamIssues(teamId, options) {
14417
+ if (!this.sdk) throw new Error("Linear not initialized");
14418
+ const team = await this.sdk.team(teamId);
14419
+ const issues = await team.issues({
14420
+ first: options?.limit || 50,
14421
+ filter: options?.includeCompleted ? void 0 : { state: { type: { nin: ["completed", "canceled"] } } }
14422
+ });
14423
+ return Promise.all(issues.nodes.map((issue) => this.mapIssue(issue)));
14424
+ }
14425
+ /**
14426
+ * Get a single issue by ID or identifier (e.g., "PRJ-123")
14427
+ */
14428
+ async fetchIssue(id) {
14429
+ if (!this.sdk) throw new Error("Linear not initialized");
14430
+ try {
14431
+ if (id.includes("-") && /^[A-Z]+-\d+$/.test(id)) {
14432
+ const match = id.match(/^([A-Z]+)-(\d+)$/);
14433
+ if (!match) return null;
14434
+ const [, teamKey, numberStr] = match;
14435
+ const issueNumber = parseInt(numberStr, 10);
14436
+ const teams = await this.sdk.teams({ first: 50 });
14437
+ const team = teams.nodes.find((t) => t.key === teamKey);
14438
+ if (!team) return null;
14439
+ const issues = await team.issues({
14440
+ first: 1,
14441
+ filter: { number: { eq: issueNumber } }
14442
+ });
14443
+ if (issues.nodes.length > 0) {
14444
+ return this.mapIssue(issues.nodes[0]);
14445
+ }
14446
+ return null;
14447
+ }
14448
+ const issue = await this.sdk.issue(id);
14449
+ return this.mapIssue(issue);
14450
+ } catch (_error) {
14451
+ return null;
14452
+ }
14453
+ }
14454
+ /**
14455
+ * Create a new issue
14456
+ */
14457
+ async createIssue(input) {
14458
+ if (!this.sdk) throw new Error("Linear not initialized");
14459
+ const teamId = input.teamId || this.config?.defaultTeamId;
14460
+ if (!teamId) {
14461
+ throw new Error("Team ID required for creating issues");
14462
+ }
14463
+ const payload = await this.sdk.createIssue({
14464
+ teamId,
14465
+ title: input.title,
14466
+ description: input.description,
14467
+ priority: input.priority ? PRIORITY_TO_LINEAR[input.priority] : void 0,
14468
+ projectId: input.projectId || this.config?.defaultProjectId,
14469
+ assigneeId: input.assigneeId,
14470
+ labelIds: input.labels ? await this.resolveLabelIds(teamId, input.labels) : void 0
14471
+ });
14472
+ const createdIssue = await payload.issue;
14473
+ if (!createdIssue) {
14474
+ throw new Error("Failed to create issue");
14475
+ }
14476
+ return this.mapIssue(createdIssue);
14477
+ }
14478
+ /**
14479
+ * Update an issue
14480
+ */
14481
+ async updateIssue(id, input) {
14482
+ if (!this.sdk) throw new Error("Linear not initialized");
14483
+ const issue = await this.fetchIssue(id);
14484
+ if (!issue) {
14485
+ throw new Error(`Issue ${id} not found`);
14486
+ }
14487
+ const updatePayload = {};
14488
+ if (input.title !== void 0) updatePayload.title = input.title;
14489
+ if (input.description !== void 0) updatePayload.description = input.description;
14490
+ if (input.priority !== void 0) updatePayload.priority = PRIORITY_TO_LINEAR[input.priority];
14491
+ if (input.assigneeId !== void 0) updatePayload.assigneeId = input.assigneeId;
14492
+ if (input.stateId !== void 0) updatePayload.stateId = input.stateId;
14493
+ if (input.projectId !== void 0) updatePayload.projectId = input.projectId;
14494
+ if (input.labels !== void 0 && issue.team) {
14495
+ updatePayload.labelIds = await this.resolveLabelIds(issue.team.id, input.labels);
14496
+ }
14497
+ await this.sdk.updateIssue(issue.id, updatePayload);
14498
+ const updated = await this.fetchIssue(issue.id);
14499
+ if (!updated) {
14500
+ throw new Error("Failed to fetch updated issue");
14501
+ }
14502
+ return updated;
14503
+ }
14504
+ /**
14505
+ * Mark issue as in progress
14506
+ */
14507
+ async markInProgress(id) {
14508
+ if (!this.sdk) throw new Error("Linear not initialized");
14509
+ const issue = await this.fetchIssue(id);
14510
+ if (!issue) throw new Error(`Issue ${id} not found`);
14511
+ const linearIssue = await this.sdk.issue(issue.id);
14512
+ const team = await linearIssue.team;
14513
+ if (!team) throw new Error("Issue has no team");
14514
+ const states = await team.states();
14515
+ const startedState = states.nodes.find((s) => s.type === "started");
14516
+ if (startedState) {
14517
+ await this.sdk.updateIssue(issue.id, { stateId: startedState.id });
14518
+ }
14519
+ }
14520
+ /**
14521
+ * Mark issue as done
14522
+ */
14523
+ async markDone(id) {
14524
+ if (!this.sdk) throw new Error("Linear not initialized");
14525
+ const issue = await this.fetchIssue(id);
14526
+ if (!issue) throw new Error(`Issue ${id} not found`);
14527
+ const linearIssue = await this.sdk.issue(issue.id);
14528
+ const team = await linearIssue.team;
14529
+ if (!team) throw new Error("Issue has no team");
14530
+ const states = await team.states();
14531
+ const doneState = states.nodes.find((s) => s.type === "completed");
14532
+ if (doneState) {
14533
+ await this.sdk.updateIssue(issue.id, { stateId: doneState.id });
14534
+ }
14535
+ }
14536
+ /**
14537
+ * Add a comment to an issue
14538
+ */
14539
+ async addComment(id, body) {
14540
+ if (!this.sdk) throw new Error("Linear not initialized");
14541
+ const issue = await this.fetchIssue(id);
14542
+ if (!issue) throw new Error(`Issue ${id} not found`);
14543
+ await this.sdk.createComment({
14544
+ issueId: issue.id,
14545
+ body
14546
+ });
14547
+ }
14548
+ /**
14549
+ * Get available teams
14550
+ */
14551
+ async getTeams() {
14552
+ if (!this.sdk) throw new Error("Linear not initialized");
14553
+ const teams = await this.sdk.teams({ first: 50 });
14554
+ return teams.nodes.map((team) => ({
14555
+ id: team.id,
14556
+ name: team.name,
14557
+ key: team.key
14558
+ }));
14559
+ }
14560
+ /**
14561
+ * Get available projects
14562
+ */
14563
+ async getProjects() {
14564
+ if (!this.sdk) throw new Error("Linear not initialized");
14565
+ const projects = await this.sdk.projects({ first: 50 });
14566
+ return projects.nodes.map((project) => ({
14567
+ id: project.id,
14568
+ name: project.name
14569
+ }));
14570
+ }
14571
+ // =============================================================================
14572
+ // Private Helpers
14573
+ // =============================================================================
14574
+ /**
14575
+ * Map Linear issue to normalized Issue
14576
+ */
14577
+ async mapIssue(linearIssue) {
14578
+ const state = await linearIssue.state;
14579
+ const assignee = await linearIssue.assignee;
14580
+ const team = await linearIssue.team;
14581
+ const project = await linearIssue.project;
14582
+ const labels = await linearIssue.labels();
14583
+ return {
14584
+ id: linearIssue.id,
14585
+ externalId: linearIssue.identifier,
14586
+ provider: "linear",
14587
+ title: linearIssue.title,
14588
+ description: linearIssue.description || void 0,
14589
+ status: LINEAR_STATUS_MAP[state?.type || "backlog"] || "backlog",
14590
+ priority: LINEAR_PRIORITY_MAP[linearIssue.priority] || "none",
14591
+ type: this.inferType(linearIssue.title, labels.nodes.map((l) => l.name)),
14592
+ assignee: assignee ? {
14593
+ id: assignee.id,
14594
+ name: assignee.name,
14595
+ email: assignee.email
14596
+ } : void 0,
14597
+ labels: labels.nodes.map((l) => l.name),
14598
+ team: team ? {
14599
+ id: team.id,
14600
+ name: team.name,
14601
+ key: team.key
14602
+ } : void 0,
14603
+ project: project ? {
14604
+ id: project.id,
14605
+ name: project.name
14606
+ } : void 0,
14607
+ url: linearIssue.url,
14608
+ createdAt: linearIssue.createdAt.toISOString(),
14609
+ updatedAt: linearIssue.updatedAt.toISOString(),
14610
+ raw: linearIssue
14611
+ };
14612
+ }
14613
+ /**
14614
+ * Infer issue type from title and labels
14615
+ */
14616
+ inferType(title, labels) {
14617
+ const titleLower = title.toLowerCase();
14618
+ const labelsLower = labels.map((l) => l.toLowerCase());
14619
+ if (labelsLower.includes("bug") || titleLower.includes("fix") || titleLower.includes("bug")) {
14620
+ return "bug";
14621
+ }
14622
+ if (labelsLower.includes("feature") || titleLower.includes("add") || titleLower.includes("implement")) {
14623
+ return "feature";
14624
+ }
14625
+ if (labelsLower.includes("improvement") || titleLower.includes("improve") || titleLower.includes("enhance")) {
14626
+ return "improvement";
14627
+ }
14628
+ if (labelsLower.includes("chore") || titleLower.includes("chore") || titleLower.includes("deps")) {
14629
+ return "chore";
14630
+ }
14631
+ return "task";
14632
+ }
14633
+ /**
14634
+ * Resolve label names to IDs
14635
+ */
14636
+ async resolveLabelIds(teamId, labelNames) {
14637
+ if (!this.sdk) return [];
14638
+ const team = await this.sdk.team(teamId);
14639
+ const labels = await team.labels();
14640
+ return labels.nodes.filter((label) => labelNames.includes(label.name)).map((label) => label.id);
14641
+ }
14642
+ };
14643
+ linearProvider = new LinearProvider();
14644
+ }
14645
+ });
14646
+
14647
+ // core/integrations/linear/cache.ts
14648
+ function clearLinearCache() {
14649
+ issueCache.clear();
14650
+ assignedIssuesCache.clear();
14651
+ teamsCache.clear();
14652
+ projectsCache.clear();
14653
+ }
14654
+ function getLinearCacheStats() {
14655
+ return {
14656
+ issues: issueCache.stats(),
14657
+ assignedIssues: assignedIssuesCache.stats(),
14658
+ teams: teamsCache.stats(),
14659
+ projects: projectsCache.stats()
14660
+ };
14661
+ }
14662
+ var LINEAR_CACHE_TTL, issueCache, assignedIssuesCache, teamsCache, projectsCache;
14663
+ var init_cache2 = __esm({
14664
+ "core/integrations/linear/cache.ts"() {
14665
+ "use strict";
14666
+ init_cache();
14667
+ LINEAR_CACHE_TTL = 5 * 60 * 1e3;
14668
+ issueCache = new TTLCache({
14669
+ ttl: LINEAR_CACHE_TTL,
14670
+ maxSize: 100
14671
+ });
14672
+ assignedIssuesCache = new TTLCache({
14673
+ ttl: LINEAR_CACHE_TTL,
14674
+ maxSize: 10
14675
+ });
14676
+ teamsCache = new TTLCache({
14677
+ ttl: LINEAR_CACHE_TTL,
14678
+ maxSize: 5
14679
+ });
14680
+ projectsCache = new TTLCache({
14681
+ ttl: LINEAR_CACHE_TTL,
14682
+ maxSize: 5
14683
+ });
14684
+ __name(clearLinearCache, "clearLinearCache");
14685
+ __name(getLinearCacheStats, "getLinearCacheStats");
14686
+ }
14687
+ });
14688
+
14689
+ // core/integrations/linear/service.ts
14690
+ var LinearService, linearService;
14691
+ var init_service = __esm({
14692
+ "core/integrations/linear/service.ts"() {
14693
+ "use strict";
14694
+ init_client();
14695
+ init_cache2();
14696
+ LinearService = class {
14697
+ static {
14698
+ __name(this, "LinearService");
14699
+ }
14700
+ initialized = false;
14701
+ userId = null;
14702
+ /**
14703
+ * Check if service is ready
14704
+ */
14705
+ isReady() {
14706
+ return this.initialized && linearProvider.isConfigured();
14707
+ }
14708
+ /**
14709
+ * Initialize the service with config
14710
+ * Must be called before any operations
14711
+ */
14712
+ async initialize(config) {
14713
+ if (this.initialized) return;
14714
+ await linearProvider.initialize(config);
14715
+ this.initialized = true;
14716
+ }
14717
+ /**
14718
+ * Initialize from API key directly
14719
+ * Convenience method for simple setup
14720
+ */
14721
+ async initializeFromApiKey(apiKey, teamId) {
14722
+ const config = {
14723
+ enabled: true,
14724
+ provider: "linear",
14725
+ apiKey,
14726
+ defaultTeamId: teamId,
14727
+ syncOn: { task: true, done: true, ship: true },
14728
+ enrichment: { enabled: true, updateProvider: true }
14729
+ };
14730
+ await this.initialize(config);
14731
+ }
14732
+ /**
14733
+ * Get issues assigned to current user (cached)
14734
+ */
14735
+ async fetchAssignedIssues(options) {
14736
+ this.ensureInitialized();
14737
+ const cacheKey = `assigned:${this.userId || "me"}`;
14738
+ const cached = assignedIssuesCache.get(cacheKey);
14739
+ if (cached) {
14740
+ return cached;
14741
+ }
14742
+ const issues = await linearProvider.fetchAssignedIssues(options);
14743
+ assignedIssuesCache.set(cacheKey, issues);
14744
+ for (const issue of issues) {
14745
+ issueCache.set(`issue:${issue.id}`, issue);
14746
+ issueCache.set(`issue:${issue.externalId}`, issue);
14747
+ }
14748
+ return issues;
14749
+ }
14750
+ /**
14751
+ * Get issues from a team (cached)
14752
+ */
14753
+ async fetchTeamIssues(teamId, options) {
14754
+ this.ensureInitialized();
14755
+ const cacheKey = `team:${teamId}`;
14756
+ const cached = assignedIssuesCache.get(cacheKey);
14757
+ if (cached) {
14758
+ return cached;
14759
+ }
14760
+ const issues = await linearProvider.fetchTeamIssues(teamId, options);
14761
+ assignedIssuesCache.set(cacheKey, issues);
14762
+ for (const issue of issues) {
14763
+ issueCache.set(`issue:${issue.id}`, issue);
14764
+ issueCache.set(`issue:${issue.externalId}`, issue);
14765
+ }
14766
+ return issues;
14767
+ }
14768
+ /**
14769
+ * Get a single issue by ID or identifier (cached)
14770
+ * Accepts UUID or identifier like "PRJ-123"
14771
+ */
14772
+ async fetchIssue(id) {
14773
+ this.ensureInitialized();
14774
+ const cacheKey = `issue:${id}`;
14775
+ const cached = issueCache.get(cacheKey);
14776
+ if (cached) {
14777
+ return cached;
14778
+ }
14779
+ const issue = await linearProvider.fetchIssue(id);
14780
+ if (issue) {
14781
+ issueCache.set(`issue:${issue.id}`, issue);
14782
+ issueCache.set(`issue:${issue.externalId}`, issue);
14783
+ }
14784
+ return issue;
14785
+ }
14786
+ /**
14787
+ * Create a new issue (invalidates assigned cache)
14788
+ */
14789
+ async createIssue(input) {
14790
+ this.ensureInitialized();
14791
+ const issue = await linearProvider.createIssue(input);
14792
+ issueCache.set(`issue:${issue.id}`, issue);
14793
+ issueCache.set(`issue:${issue.externalId}`, issue);
14794
+ assignedIssuesCache.clear();
14795
+ return issue;
14796
+ }
14797
+ /**
14798
+ * Update an issue (invalidates cache for that issue)
14799
+ */
14800
+ async updateIssue(id, input) {
14801
+ this.ensureInitialized();
14802
+ const issue = await linearProvider.updateIssue(id, input);
14803
+ issueCache.set(`issue:${issue.id}`, issue);
14804
+ issueCache.set(`issue:${issue.externalId}`, issue);
14805
+ return issue;
14806
+ }
14807
+ /**
14808
+ * Mark issue as in progress (invalidates cache)
14809
+ */
14810
+ async markInProgress(id) {
14811
+ this.ensureInitialized();
14812
+ await linearProvider.markInProgress(id);
14813
+ issueCache.delete(`issue:${id}`);
14814
+ assignedIssuesCache.clear();
14815
+ }
14816
+ /**
14817
+ * Mark issue as done (invalidates cache)
14818
+ */
14819
+ async markDone(id) {
14820
+ this.ensureInitialized();
14821
+ await linearProvider.markDone(id);
14822
+ issueCache.delete(`issue:${id}`);
14823
+ assignedIssuesCache.clear();
14824
+ }
14825
+ /**
14826
+ * Add a comment to an issue
14827
+ */
14828
+ async addComment(id, body) {
14829
+ this.ensureInitialized();
14830
+ await linearProvider.addComment(id, body);
14831
+ }
14832
+ /**
14833
+ * Get available teams (cached)
14834
+ */
14835
+ async getTeams() {
14836
+ this.ensureInitialized();
14837
+ const cached = teamsCache.get("teams");
14838
+ if (cached) {
14839
+ return cached;
14840
+ }
14841
+ const teams = await linearProvider.getTeams();
14842
+ teamsCache.set("teams", teams);
14843
+ return teams;
14844
+ }
14845
+ /**
14846
+ * Get available projects (cached)
14847
+ */
14848
+ async getProjects() {
14849
+ this.ensureInitialized();
14850
+ const cached = projectsCache.get("projects");
14851
+ if (cached) {
14852
+ return cached;
14853
+ }
14854
+ const projects = await linearProvider.getProjects();
14855
+ projectsCache.set("projects", projects);
14856
+ return projects;
14857
+ }
14858
+ /**
14859
+ * Clear all caches
14860
+ */
14861
+ clearCache() {
14862
+ clearLinearCache();
14863
+ }
14864
+ /**
14865
+ * Get cache statistics for debugging
14866
+ */
14867
+ getCacheStats() {
14868
+ return getLinearCacheStats();
14869
+ }
14870
+ /**
14871
+ * Ensure service is initialized
14872
+ */
14873
+ ensureInitialized() {
14874
+ if (!this.initialized) {
14875
+ throw new Error(
14876
+ "Linear service not initialized. Call linearService.initialize() first or run `p. linear setup`."
14877
+ );
14878
+ }
14879
+ }
14880
+ };
14881
+ linearService = new LinearService();
14882
+ }
14883
+ });
14884
+
14885
+ // core/integrations/linear/sync.ts
14886
+ import { readFile as readFile2, writeFile as writeFile2, mkdir } from "fs/promises";
14887
+ import { existsSync } from "fs";
14888
+ import { join as join2 } from "path";
14889
+ var DEFAULT_STALE_AFTER, LinearSync, linearSync;
14890
+ var init_sync = __esm({
14891
+ "core/integrations/linear/sync.ts"() {
14892
+ "use strict";
14893
+ init_service();
14894
+ init_schemas();
14895
+ init_issues();
14896
+ DEFAULT_STALE_AFTER = 30 * 60 * 1e3;
14897
+ LinearSync = class {
14898
+ static {
14899
+ __name(this, "LinearSync");
14900
+ }
14901
+ /**
14902
+ * Pull all assigned issues from Linear and store in issues.json
14903
+ * This is the main sync operation - call on `p. sync`
14904
+ */
14905
+ async pullAll(projectId) {
14906
+ const storagePath = join2(getProjectPath2(projectId), "storage");
14907
+ const issuesPath = join2(storagePath, "issues.json");
14908
+ if (!existsSync(storagePath)) {
14909
+ await mkdir(storagePath, { recursive: true });
14910
+ }
14911
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
14912
+ const errors = [];
14913
+ try {
14914
+ const issues = await linearService.fetchAssignedIssues({ limit: 100 });
14915
+ const issuesMap = {};
14916
+ for (const issue of issues) {
14917
+ try {
14918
+ issuesMap[issue.externalId] = this.toCachedIssue(issue, timestamp);
14919
+ } catch (err) {
14920
+ errors.push({
14921
+ issueId: issue.externalId || issue.id,
14922
+ error: err.message
14923
+ });
14924
+ }
14925
+ }
14926
+ const issuesJson = {
14927
+ provider: "linear",
14928
+ lastSync: timestamp,
14929
+ staleAfter: DEFAULT_STALE_AFTER,
14930
+ issues: issuesMap
14931
+ };
14932
+ await writeFile2(issuesPath, JSON.stringify(issuesJson, null, 2));
14933
+ return {
14934
+ provider: "linear",
14935
+ fetched: issues.length,
14936
+ updated: Object.keys(issuesMap).length,
14937
+ errors,
14938
+ timestamp
14939
+ };
14940
+ } catch (err) {
14941
+ errors.push({
14942
+ issueId: "all",
14943
+ error: err.message
14944
+ });
14945
+ return {
14946
+ provider: "linear",
14947
+ fetched: 0,
14948
+ updated: 0,
14949
+ errors,
14950
+ timestamp
14951
+ };
14952
+ }
14953
+ }
14954
+ /**
14955
+ * Get issue from local cache, fetch from API if not found or stale
14956
+ * Local-first approach for performance
14957
+ */
14958
+ async getIssue(projectId, identifier) {
14959
+ const issuesJson = await this.loadIssues(projectId);
14960
+ if (issuesJson && issuesJson.issues[identifier]) {
14961
+ const cachedIssue = issuesJson.issues[identifier];
14962
+ const fetchedAt = new Date(cachedIssue.fetchedAt).getTime();
14963
+ const now = Date.now();
14964
+ const issueStaleness = 10 * 60 * 1e3;
14965
+ if (now - fetchedAt < issueStaleness) {
14966
+ return cachedIssue;
14967
+ }
14968
+ }
14969
+ try {
14970
+ const issue = await linearService.fetchIssue(identifier);
14971
+ if (!issue) return null;
14972
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
14973
+ const cachedIssue = this.toCachedIssue(issue, timestamp);
14974
+ await this.updateIssueInCache(projectId, identifier, cachedIssue);
14975
+ return cachedIssue;
14976
+ } catch {
14977
+ if (issuesJson?.issues[identifier]) {
14978
+ return issuesJson.issues[identifier];
14979
+ }
14980
+ return null;
14981
+ }
14982
+ }
14983
+ /**
14984
+ * Get issue from local cache ONLY (no API call)
14985
+ * Use for fast lookups when you know the issue should be cached
14986
+ */
14987
+ async getIssueLocal(projectId, identifier) {
14988
+ const issuesJson = await this.loadIssues(projectId);
14989
+ return issuesJson?.issues[identifier] || null;
14990
+ }
14991
+ /**
14992
+ * Push local status change to Linear
14993
+ * Called when task status changes (in_progress, done)
14994
+ */
14995
+ async pushStatus(projectId, identifier, status) {
14996
+ if (status === "in_progress") {
14997
+ await linearService.markInProgress(identifier);
14998
+ } else if (status === "done") {
14999
+ await linearService.markDone(identifier);
15000
+ }
15001
+ const issuesJson = await this.loadIssues(projectId);
15002
+ if (issuesJson?.issues[identifier]) {
15003
+ const cachedStatus = status === "done" ? "done" : "in_progress";
15004
+ issuesJson.issues[identifier].status = cachedStatus;
15005
+ issuesJson.issues[identifier].fetchedAt = (/* @__PURE__ */ new Date()).toISOString();
15006
+ await this.saveIssues(projectId, issuesJson);
15007
+ }
15008
+ }
15009
+ /**
15010
+ * Check if the local issues cache is stale
15011
+ * Staleness = lastSync is older than staleAfter threshold
15012
+ */
15013
+ async isStale(projectId) {
15014
+ const issuesJson = await this.loadIssues(projectId);
15015
+ if (!issuesJson || !issuesJson.lastSync) {
15016
+ return true;
15017
+ }
15018
+ const lastSyncTime = new Date(issuesJson.lastSync).getTime();
15019
+ const now = Date.now();
15020
+ const staleAfter = issuesJson.staleAfter || DEFAULT_STALE_AFTER;
15021
+ return now - lastSyncTime > staleAfter;
15022
+ }
15023
+ /**
15024
+ * Get sync status for display
15025
+ */
15026
+ async getSyncStatus(projectId) {
15027
+ const issuesJson = await this.loadIssues(projectId);
15028
+ if (!issuesJson) {
15029
+ return {
15030
+ hasCache: false,
15031
+ lastSync: null,
15032
+ issueCount: 0,
15033
+ isStale: true
15034
+ };
15035
+ }
15036
+ return {
15037
+ hasCache: true,
15038
+ lastSync: issuesJson.lastSync || null,
15039
+ issueCount: Object.keys(issuesJson.issues).length,
15040
+ isStale: await this.isStale(projectId)
15041
+ };
15042
+ }
15043
+ /**
15044
+ * List all cached issues
15045
+ */
15046
+ async listCachedIssues(projectId) {
15047
+ const issuesJson = await this.loadIssues(projectId);
15048
+ if (!issuesJson) return [];
15049
+ return Object.values(issuesJson.issues);
15050
+ }
15051
+ // =============================================================================
15052
+ // Private Helpers
15053
+ // =============================================================================
15054
+ /**
15055
+ * Load issues.json from disk
15056
+ */
15057
+ async loadIssues(projectId) {
15058
+ const issuesPath = join2(getProjectPath2(projectId), "storage", "issues.json");
15059
+ if (!existsSync(issuesPath)) {
15060
+ return null;
15061
+ }
15062
+ try {
15063
+ const content = await readFile2(issuesPath, "utf-8");
15064
+ return parseIssues(JSON.parse(content));
15065
+ } catch {
15066
+ return null;
15067
+ }
15068
+ }
15069
+ /**
15070
+ * Save issues.json to disk
15071
+ */
15072
+ async saveIssues(projectId, issuesJson) {
15073
+ const storagePath = join2(getProjectPath2(projectId), "storage");
15074
+ const issuesPath = join2(storagePath, "issues.json");
15075
+ if (!existsSync(storagePath)) {
15076
+ await mkdir(storagePath, { recursive: true });
15077
+ }
15078
+ await writeFile2(issuesPath, JSON.stringify(issuesJson, null, 2));
15079
+ }
15080
+ /**
15081
+ * Update a single issue in the cache
15082
+ */
15083
+ async updateIssueInCache(projectId, identifier, issue) {
15084
+ let issuesJson = await this.loadIssues(projectId);
15085
+ if (!issuesJson) {
15086
+ issuesJson = createEmptyIssues("linear");
15087
+ }
15088
+ issuesJson.issues[identifier] = issue;
15089
+ await this.saveIssues(projectId, issuesJson);
15090
+ }
15091
+ /**
15092
+ * Convert API Issue to CachedIssue format
15093
+ */
15094
+ toCachedIssue(issue, timestamp) {
15095
+ return {
15096
+ id: issue.id,
15097
+ identifier: issue.externalId,
15098
+ title: issue.title,
15099
+ description: issue.description,
15100
+ status: issue.status,
15101
+ priority: issue.priority,
15102
+ type: issue.type,
15103
+ assignee: issue.assignee,
15104
+ labels: issue.labels,
15105
+ team: issue.team,
15106
+ project: issue.project,
15107
+ url: issue.url,
15108
+ createdAt: issue.createdAt,
15109
+ updatedAt: issue.updatedAt,
15110
+ fetchedAt: timestamp
15111
+ };
15112
+ }
15113
+ };
15114
+ linearSync = new LinearSync();
15115
+ }
15116
+ });
15117
+
15118
+ // core/integrations/linear/index.ts
15119
+ var init_linear = __esm({
15120
+ "core/integrations/linear/index.ts"() {
15121
+ "use strict";
15122
+ init_client();
15123
+ init_service();
15124
+ init_sync();
15125
+ init_cache2();
15126
+ }
15127
+ });
15128
+
15129
+ // core/utils/project-credentials.ts
15130
+ import fs32 from "fs";
15131
+ import path33 from "path";
15132
+ import os13 from "os";
15133
+ function getCredentialsPath(projectId) {
15134
+ return path33.join(
15135
+ os13.homedir(),
15136
+ ".prjct-cli",
15137
+ "projects",
15138
+ projectId,
15139
+ "config",
15140
+ "credentials.json"
15141
+ );
15142
+ }
15143
+ async function getProjectCredentials(projectId) {
15144
+ const credPath = getCredentialsPath(projectId);
15145
+ if (!fs32.existsSync(credPath)) {
15146
+ return {};
15147
+ }
15148
+ try {
15149
+ return JSON.parse(fs32.readFileSync(credPath, "utf-8"));
15150
+ } catch (error) {
15151
+ console.error("[project-credentials] Failed to read credentials:", error.message);
15152
+ return {};
15153
+ }
15154
+ }
15155
+ async function getLinearApiKey(projectId) {
15156
+ const projectCreds = await getProjectCredentials(projectId);
15157
+ if (projectCreds.linear?.apiKey) {
15158
+ return projectCreds.linear.apiKey;
15159
+ }
15160
+ return getCredential("linear-api-key");
15161
+ }
15162
+ var init_project_credentials = __esm({
15163
+ "core/utils/project-credentials.ts"() {
15164
+ "use strict";
15165
+ init_keychain();
15166
+ __name(getCredentialsPath, "getCredentialsPath");
15167
+ __name(getProjectCredentials, "getProjectCredentials");
15168
+ __name(getLinearApiKey, "getLinearApiKey");
15169
+ }
15170
+ });
15171
+
13381
15172
  // core/commands/workflow.ts
13382
15173
  var WorkflowCommands;
13383
15174
  var init_workflow = __esm({
@@ -13388,6 +15179,9 @@ var init_workflow = __esm({
13388
15179
  init_storage2();
13389
15180
  init_template_executor();
13390
15181
  init_command_executor();
15182
+ init_next_steps();
15183
+ init_linear();
15184
+ init_project_credentials();
13391
15185
  WorkflowCommands = class extends PrjctCommandsBase {
13392
15186
  static {
13393
15187
  __name(this, "WorkflowCommands");
@@ -13410,23 +15204,39 @@ var init_workflow = __esm({
13410
15204
  output_default.fail(result.error || "Failed to execute task");
13411
15205
  return { success: false, error: result.error };
13412
15206
  }
15207
+ let linearId;
15208
+ let taskDescription = task;
15209
+ const linearPattern = /^[A-Z]+-\d+$/;
15210
+ if (linearPattern.test(task)) {
15211
+ try {
15212
+ const creds = await getProjectCredentials(projectId);
15213
+ const apiKey = await getLinearApiKey(projectId);
15214
+ if (apiKey && creds.linear?.teamId) {
15215
+ await linearService.initializeFromApiKey(
15216
+ apiKey,
15217
+ creds.linear.teamId
15218
+ );
15219
+ const issue = await linearService.fetchIssue(task);
15220
+ if (issue) {
15221
+ linearId = task;
15222
+ taskDescription = `${task}: ${issue.title}`;
15223
+ await linearService.markInProgress(task);
15224
+ }
15225
+ }
15226
+ } catch {
15227
+ }
15228
+ }
13413
15229
  await stateStorage.startTask(projectId, {
13414
15230
  id: generateUUID(),
13415
- description: task,
13416
- sessionId: generateUUID()
15231
+ description: taskDescription,
15232
+ sessionId: generateUUID(),
15233
+ linearId
13417
15234
  });
13418
- if (result.orchestratorContext) {
13419
- const oc = result.orchestratorContext;
13420
- const agentsList2 = oc.agents.map((a) => a.name).join(", ") || "none";
13421
- const domainsList = oc.detectedDomains.join(", ");
13422
- console.log(`\u{1F3AF} Orchestrator: ${domainsList} \u2192 [${agentsList2}]`);
13423
- if (oc.requiresFragmentation && oc.subtasks) {
13424
- console.log(` \u2192 ${oc.subtasks.length} subtasks created`);
13425
- }
13426
- }
13427
15235
  const availableAgents = await templateExecutor.getAvailableAgents(projectPath);
13428
15236
  const agentsList = availableAgents.length > 0 ? availableAgents.join(", ") : "none (run p. sync)";
13429
- output_default.done(`${task} [specialists: ${agentsList}]`);
15237
+ output_default.done(`${task}`);
15238
+ showStateInfo("working");
15239
+ showNextSteps("task");
13430
15240
  await this.logToMemory(projectPath, "task_started", {
13431
15241
  task,
13432
15242
  agenticMode: true,
@@ -13481,7 +15291,29 @@ var init_workflow = __esm({
13481
15291
  duration = date_helper_default.calculateDuration(started);
13482
15292
  }
13483
15293
  await stateStorage.completeTask(projectId);
13484
- output_default.done(`${task}${duration ? ` (${duration})` : ""}`);
15294
+ const linearId = currentTask.linearId;
15295
+ if (linearId) {
15296
+ try {
15297
+ const creds = await getProjectCredentials(projectId);
15298
+ const apiKey = await getLinearApiKey(projectId);
15299
+ if (apiKey && creds.linear?.teamId) {
15300
+ await linearService.initializeFromApiKey(
15301
+ apiKey,
15302
+ creds.linear.teamId
15303
+ );
15304
+ await linearService.markDone(linearId);
15305
+ output_default.done(`${task}${duration ? ` (${duration})` : ""} \u2192 Linear \u2713`);
15306
+ } else {
15307
+ output_default.done(`${task}${duration ? ` (${duration})` : ""}`);
15308
+ }
15309
+ } catch {
15310
+ output_default.done(`${task}${duration ? ` (${duration})` : ""}`);
15311
+ }
15312
+ } else {
15313
+ output_default.done(`${task}${duration ? ` (${duration})` : ""}`);
15314
+ }
15315
+ showStateInfo("completed");
15316
+ showNextSteps("done");
13485
15317
  await this.logToMemory(projectPath, "task_completed", {
13486
15318
  task,
13487
15319
  duration,
@@ -13511,6 +15343,7 @@ var init_workflow = __esm({
13511
15343
  return { success: true, message: "Queue is empty" };
13512
15344
  }
13513
15345
  output_default.done(`${tasks.length} task${tasks.length !== 1 ? "s" : ""} queued`);
15346
+ showNextSteps("next");
13514
15347
  return { success: true, tasks, count: tasks.length };
13515
15348
  } catch (error) {
13516
15349
  output_default.fail(error.message);
@@ -13537,6 +15370,8 @@ var init_workflow = __esm({
13537
15370
  await stateStorage.pauseTask(projectId, reason2);
13538
15371
  const taskDesc = currentTask.description.slice(0, 40);
13539
15372
  output_default.done(`paused: ${taskDesc}${reason2 ? ` (${reason2})` : ""}`);
15373
+ showStateInfo("paused");
15374
+ showNextSteps("pause");
13540
15375
  await this.logToMemory(projectPath, "task_paused", {
13541
15376
  task: currentTask.description,
13542
15377
  reason: reason2,
@@ -13571,6 +15406,8 @@ var init_workflow = __esm({
13571
15406
  return { success: false, message: "No paused task found" };
13572
15407
  }
13573
15408
  output_default.done(`resumed: ${resumed.description.slice(0, 40)}`);
15409
+ showStateInfo("working");
15410
+ showNextSteps("resume");
13574
15411
  await this.logToMemory(projectPath, "task_resumed", {
13575
15412
  task: resumed.description,
13576
15413
  timestamp: date_helper_default.getTimestamp()
@@ -13586,18 +15423,18 @@ var init_workflow = __esm({
13586
15423
  });
13587
15424
 
13588
15425
  // core/utils/project-commands.ts
13589
- import path31 from "path";
15426
+ import path34 from "path";
13590
15427
  async function detectPackageManager(projectPath, pkg) {
13591
15428
  const declared = pkg?.packageManager?.trim().toLowerCase();
13592
15429
  if (declared?.startsWith("pnpm@")) return "pnpm";
13593
15430
  if (declared?.startsWith("yarn@")) return "yarn";
13594
15431
  if (declared?.startsWith("bun@")) return "bun";
13595
15432
  if (declared?.startsWith("npm@")) return "npm";
13596
- if (await fileExists(path31.join(projectPath, "pnpm-lock.yaml"))) return "pnpm";
13597
- if (await fileExists(path31.join(projectPath, "yarn.lock"))) return "yarn";
13598
- if (await fileExists(path31.join(projectPath, "bun.lockb"))) return "bun";
13599
- if (await fileExists(path31.join(projectPath, "bun.lock"))) return "bun";
13600
- if (await fileExists(path31.join(projectPath, "package-lock.json"))) return "npm";
15433
+ if (await fileExists(path34.join(projectPath, "pnpm-lock.yaml"))) return "pnpm";
15434
+ if (await fileExists(path34.join(projectPath, "yarn.lock"))) return "yarn";
15435
+ if (await fileExists(path34.join(projectPath, "bun.lockb"))) return "bun";
15436
+ if (await fileExists(path34.join(projectPath, "bun.lock"))) return "bun";
15437
+ if (await fileExists(path34.join(projectPath, "package-lock.json"))) return "npm";
13601
15438
  return "npm";
13602
15439
  }
13603
15440
  function pmRun(pm, scriptName) {
@@ -13613,7 +15450,7 @@ function pmTest(pm) {
13613
15450
  return "npm test";
13614
15451
  }
13615
15452
  async function detectProjectCommands(projectPath) {
13616
- const pkgPath = path31.join(projectPath, "package.json");
15453
+ const pkgPath = path34.join(projectPath, "package.json");
13617
15454
  const pkg = await readJson(pkgPath, null);
13618
15455
  if (pkg) {
13619
15456
  const pm = await detectPackageManager(projectPath, pkg);
@@ -13630,27 +15467,27 @@ async function detectProjectCommands(projectPath) {
13630
15467
  }
13631
15468
  return result;
13632
15469
  }
13633
- if (await fileExists(path31.join(projectPath, "pytest.ini"))) {
15470
+ if (await fileExists(path34.join(projectPath, "pytest.ini"))) {
13634
15471
  return { stack: "python", test: { tool: "pytest", command: "pytest" } };
13635
15472
  }
13636
- const pyproject = await readFile(path31.join(projectPath, "pyproject.toml"), "");
15473
+ const pyproject = await readFile(path34.join(projectPath, "pyproject.toml"), "");
13637
15474
  if (pyproject.includes("[tool.pytest") || pyproject.includes("pytest")) {
13638
15475
  return { stack: "python", test: { tool: "pytest", command: "pytest" } };
13639
15476
  }
13640
- if (await fileExists(path31.join(projectPath, "Cargo.toml"))) {
15477
+ if (await fileExists(path34.join(projectPath, "Cargo.toml"))) {
13641
15478
  return { stack: "rust", test: { tool: "cargo", command: "cargo test" } };
13642
15479
  }
13643
- if (await fileExists(path31.join(projectPath, "go.mod"))) {
15480
+ if (await fileExists(path34.join(projectPath, "go.mod"))) {
13644
15481
  return { stack: "go", test: { tool: "go", command: "go test ./..." } };
13645
15482
  }
13646
15483
  const files = await listFiles(projectPath);
13647
15484
  if (files.some((f) => f.endsWith(".sln") || f.endsWith(".csproj") || f.endsWith(".fsproj"))) {
13648
15485
  return { stack: "dotnet", test: { tool: "dotnet", command: "dotnet test" } };
13649
15486
  }
13650
- if (await fileExists(path31.join(projectPath, "pom.xml"))) {
15487
+ if (await fileExists(path34.join(projectPath, "pom.xml"))) {
13651
15488
  return { stack: "java", test: { tool: "maven", command: "mvn test" } };
13652
15489
  }
13653
- if (await fileExists(path31.join(projectPath, "gradlew")) && (await fileExists(path31.join(projectPath, "build.gradle")) || await fileExists(path31.join(projectPath, "build.gradle.kts")))) {
15490
+ if (await fileExists(path34.join(projectPath, "gradlew")) && (await fileExists(path34.join(projectPath, "build.gradle")) || await fileExists(path34.join(projectPath, "build.gradle.kts")))) {
13654
15491
  return { stack: "java", test: { tool: "gradle", command: "./gradlew test" } };
13655
15492
  }
13656
15493
  return { stack: "unknown" };
@@ -13667,7 +15504,7 @@ var init_project_commands = __esm({
13667
15504
  });
13668
15505
 
13669
15506
  // core/commands/shipping.ts
13670
- import path32 from "path";
15507
+ import path35 from "path";
13671
15508
  var ShippingCommands;
13672
15509
  var init_shipping = __esm({
13673
15510
  "core/commands/shipping.ts"() {
@@ -13677,6 +15514,7 @@ var init_shipping = __esm({
13677
15514
  init_project_commands();
13678
15515
  init_base();
13679
15516
  init_storage2();
15517
+ init_next_steps();
13680
15518
  ShippingCommands = class extends PrjctCommandsBase {
13681
15519
  static {
13682
15520
  __name(this, "ShippingCommands");
@@ -13719,17 +15557,17 @@ ${result.stderr}`.trim();
13719
15557
  const currentTask = await stateStorage.getCurrentTask(projectId);
13720
15558
  featureName = currentTask?.description || "current work";
13721
15559
  }
13722
- output_default.spin(`shipping ${featureName}...`);
15560
+ output_default.step(1, 5, `Linting ${featureName}...`);
13723
15561
  const lintResult = await this._runLint(projectPath);
13724
- output_default.spin("running tests...");
15562
+ output_default.step(2, 5, "Running tests...");
13725
15563
  const testResult = await this._runTests(projectPath);
13726
- output_default.spin("updating version...");
15564
+ output_default.step(3, 5, "Updating version...");
13727
15565
  const newVersion = await this._bumpVersion(projectPath);
13728
15566
  await this._updateChangelog(featureName, newVersion, projectPath);
13729
- output_default.spin("committing...");
15567
+ output_default.step(4, 5, "Committing...");
13730
15568
  const commitResult = await this._createShipCommit(featureName, projectPath);
13731
15569
  if (commitResult.success) {
13732
- output_default.spin("pushing...");
15570
+ output_default.step(5, 5, "Pushing...");
13733
15571
  await this._gitPush(projectPath);
13734
15572
  }
13735
15573
  await shippedStorage.addShipped(projectId, {
@@ -13753,6 +15591,7 @@ ${result.stderr}`.trim();
13753
15591
  });
13754
15592
  }
13755
15593
  output_default.done(`v${newVersion} shipped`);
15594
+ showNextSteps("ship");
13756
15595
  return { success: true, feature: featureName, version: newVersion };
13757
15596
  } catch (error) {
13758
15597
  output_default.fail(error.message);
@@ -13796,7 +15635,7 @@ ${result.stderr}`.trim();
13796
15635
  */
13797
15636
  async _bumpVersion(projectPath) {
13798
15637
  try {
13799
- const pkgPath = path32.join(projectPath, "package.json");
15638
+ const pkgPath = path35.join(projectPath, "package.json");
13800
15639
  const pkg = await file_helper_exports.readJson(pkgPath, { version: "0.0.0" });
13801
15640
  const oldVersion = pkg?.version || "0.0.0";
13802
15641
  const [major, minor, patch] = oldVersion.split(".").map(Number);
@@ -13818,7 +15657,7 @@ ${result.stderr}`.trim();
13818
15657
  */
13819
15658
  async _updateChangelog(feature, version, projectPath) {
13820
15659
  try {
13821
- const changelogPath = path32.join(projectPath, "CHANGELOG.md");
15660
+ const changelogPath = path35.join(projectPath, "CHANGELOG.md");
13822
15661
  const changelog = await file_helper_exports.readFile(changelogPath, "# Changelog\n\n");
13823
15662
  const entry = `## [${version}] - ${date_helper_default.formatDate(/* @__PURE__ */ new Date())}
13824
15663
 
@@ -13877,7 +15716,7 @@ Designed for [Claude](https://www.anthropic.com/claude)`;
13877
15716
 
13878
15717
  // core/commands/registry.ts
13879
15718
  var CommandRegistry, commandRegistry;
13880
- var init_registry = __esm({
15719
+ var init_registry2 = __esm({
13881
15720
  "core/commands/registry.ts"() {
13882
15721
  "use strict";
13883
15722
  init_config_manager();
@@ -14214,12 +16053,12 @@ var init_registry = __esm({
14214
16053
  });
14215
16054
 
14216
16055
  // core/commands/analytics.ts
14217
- import path33 from "path";
16056
+ import path36 from "path";
14218
16057
  var AnalyticsCommands;
14219
16058
  var init_analytics = __esm({
14220
16059
  "core/commands/analytics.ts"() {
14221
16060
  "use strict";
14222
- init_registry();
16061
+ init_registry2();
14223
16062
  init_base();
14224
16063
  init_storage2();
14225
16064
  AnalyticsCommands = class extends PrjctCommandsBase {
@@ -14239,7 +16078,7 @@ var init_analytics = __esm({
14239
16078
  output_default.fail("no project ID");
14240
16079
  return { success: false, error: "No project ID found" };
14241
16080
  }
14242
- const projectName = path33.basename(projectPath);
16081
+ const projectName = path36.basename(projectPath);
14243
16082
  const currentTask = await stateStorage.getCurrentTask(projectId);
14244
16083
  const queueTasks = await queueStorage.getActiveTasks(projectId);
14245
16084
  const shipped = await shippedStorage.getRecent(projectId, 5);
@@ -14477,7 +16316,7 @@ ${catInfo?.title || cat}:`);
14477
16316
  });
14478
16317
 
14479
16318
  // core/commands/cleanup.ts
14480
- import path34 from "path";
16319
+ import path37 from "path";
14481
16320
  async function cleanupMemory(projectPath) {
14482
16321
  const projectId = await config_manager_default.getProjectId(projectPath);
14483
16322
  const results = { rotated: [], totalSize: 0, freedSpace: 0 };
@@ -14493,7 +16332,7 @@ async function cleanupMemory(projectPath) {
14493
16332
  results.totalSize += sizeMB;
14494
16333
  const rotated = await jsonl_helper_default.rotateJsonLinesIfNeeded(filePath, 10);
14495
16334
  if (rotated) {
14496
- results.rotated.push(path34.basename(filePath));
16335
+ results.rotated.push(path37.basename(filePath));
14497
16336
  results.freedSpace += sizeMB;
14498
16337
  }
14499
16338
  }
@@ -14600,7 +16439,7 @@ var init_cleanup = __esm({
14600
16439
  });
14601
16440
 
14602
16441
  // core/commands/design.ts
14603
- import path35 from "path";
16442
+ import path38 from "path";
14604
16443
  async function design(target = null, options = {}, projectPath = process.cwd()) {
14605
16444
  try {
14606
16445
  const designType = options.type || "architecture";
@@ -14612,7 +16451,7 @@ async function design(target = null, options = {}, projectPath = process.cwd())
14612
16451
  const designTarget = target || "system";
14613
16452
  output_default.spin(`designing ${designType}...`);
14614
16453
  const projectId = await config_manager_default.getProjectId(projectPath);
14615
- const designsPath = path35.join(
16454
+ const designsPath = path38.join(
14616
16455
  path_manager_default.getGlobalProjectPath(projectId),
14617
16456
  "planning",
14618
16457
  "designs"
@@ -14652,7 +16491,7 @@ async function design(target = null, options = {}, projectPath = process.cwd())
14652
16491
  break;
14653
16492
  }
14654
16493
  const designFileName = `${designType}-${designTarget.toLowerCase().replace(/\s+/g, "-")}.md`;
14655
- const designFilePath = path35.join(designsPath, designFileName);
16494
+ const designFilePath = path38.join(designsPath, designFileName);
14656
16495
  await file_helper_exports.writeFile(designFilePath, designContent);
14657
16496
  await memoryService.log(projectPath, "design_created", {
14658
16497
  type: designType,
@@ -14676,7 +16515,7 @@ var init_design = __esm({
14676
16515
  });
14677
16516
 
14678
16517
  // core/commands/snapshots.ts
14679
- import path36 from "path";
16518
+ import path39 from "path";
14680
16519
  async function recover(projectPath = process.cwd()) {
14681
16520
  try {
14682
16521
  const projectId = await config_manager_default.getProjectId(projectPath);
@@ -14728,14 +16567,14 @@ async function undo(projectPath = process.cwd()) {
14728
16567
  output_default.fail("no project ID");
14729
16568
  return { success: false, error: "No project ID found" };
14730
16569
  }
14731
- const snapshotsPath = path36.join(
16570
+ const snapshotsPath = path39.join(
14732
16571
  path_manager_default.getGlobalProjectPath(projectId),
14733
16572
  "snapshots"
14734
16573
  );
14735
16574
  await file_helper_exports.ensureDir(snapshotsPath);
14736
- const { execSync: execSync4 } = await import("child_process");
16575
+ const { execSync: execSync5 } = await import("child_process");
14737
16576
  try {
14738
- const status = execSync4("git status --porcelain", {
16577
+ const status = execSync5("git status --porcelain", {
14739
16578
  cwd: projectPath,
14740
16579
  encoding: "utf-8"
14741
16580
  }).trim();
@@ -14745,11 +16584,11 @@ async function undo(projectPath = process.cwd()) {
14745
16584
  }
14746
16585
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
14747
16586
  const stashMessage = `prjct-undo-${timestamp}`;
14748
- execSync4(`git stash push -m "${stashMessage}"`, {
16587
+ execSync5(`git stash push -m "${stashMessage}"`, {
14749
16588
  cwd: projectPath,
14750
16589
  encoding: "utf-8"
14751
16590
  });
14752
- const snapshotFile = path36.join(snapshotsPath, "history.json");
16591
+ const snapshotFile = path39.join(snapshotsPath, "history.json");
14753
16592
  let history2 = { snapshots: [], current: -1 };
14754
16593
  try {
14755
16594
  const content = await file_helper_exports.readFile(snapshotFile);
@@ -14789,11 +16628,11 @@ async function redo(projectPath = process.cwd()) {
14789
16628
  output_default.fail("no project ID");
14790
16629
  return { success: false, error: "No project ID found" };
14791
16630
  }
14792
- const snapshotsPath = path36.join(
16631
+ const snapshotsPath = path39.join(
14793
16632
  path_manager_default.getGlobalProjectPath(projectId),
14794
16633
  "snapshots"
14795
16634
  );
14796
- const snapshotFile = path36.join(snapshotsPath, "history.json");
16635
+ const snapshotFile = path39.join(snapshotsPath, "history.json");
14797
16636
  let history2;
14798
16637
  try {
14799
16638
  const content = await file_helper_exports.readFile(snapshotFile);
@@ -14809,9 +16648,9 @@ async function redo(projectPath = process.cwd()) {
14809
16648
  output_default.warn("nothing to redo");
14810
16649
  return { success: false, message: "Nothing to redo" };
14811
16650
  }
14812
- const { execSync: execSync4 } = await import("child_process");
16651
+ const { execSync: execSync5 } = await import("child_process");
14813
16652
  try {
14814
- const stashList = execSync4("git stash list", {
16653
+ const stashList = execSync5("git stash list", {
14815
16654
  cwd: projectPath,
14816
16655
  encoding: "utf-8"
14817
16656
  }).trim();
@@ -14824,7 +16663,7 @@ async function redo(projectPath = process.cwd()) {
14824
16663
  output_default.warn("no prjct undo point found");
14825
16664
  return { success: false, message: "No prjct undo point found" };
14826
16665
  }
14827
- execSync4("git stash pop", {
16666
+ execSync5("git stash pop", {
14828
16667
  cwd: projectPath,
14829
16668
  encoding: "utf-8"
14830
16669
  });
@@ -14852,11 +16691,11 @@ async function history(projectPath = process.cwd()) {
14852
16691
  output_default.fail("no project ID");
14853
16692
  return { success: false, error: "No project ID found" };
14854
16693
  }
14855
- const snapshotsPath = path36.join(
16694
+ const snapshotsPath = path39.join(
14856
16695
  path_manager_default.getGlobalProjectPath(projectId),
14857
16696
  "snapshots"
14858
16697
  );
14859
- const snapshotFile = path36.join(snapshotsPath, "history.json");
16698
+ const snapshotFile = path39.join(snapshotsPath, "history.json");
14860
16699
  let snapshotHistory;
14861
16700
  try {
14862
16701
  const content = await file_helper_exports.readFile(snapshotFile);
@@ -14962,9 +16801,9 @@ var init_maintenance = __esm({
14962
16801
  });
14963
16802
 
14964
16803
  // core/commands/setup.ts
14965
- import path37 from "path";
14966
- import fs30 from "fs";
14967
- import chalk4 from "chalk";
16804
+ import path40 from "path";
16805
+ import fs33 from "fs";
16806
+ import chalk6 from "chalk";
14968
16807
  var SetupCommands;
14969
16808
  var init_setup2 = __esm({
14970
16809
  "core/commands/setup.ts"() {
@@ -15084,7 +16923,7 @@ Please install it first:
15084
16923
  try {
15085
16924
  const claudeDir = path_manager_default.getClaudeDir();
15086
16925
  const settingsPath = path_manager_default.getClaudeSettingsPath();
15087
- const statusLinePath = path37.join(claudeDir, "prjct-statusline.sh");
16926
+ const statusLinePath = path40.join(claudeDir, "prjct-statusline.sh");
15088
16927
  const scriptContent = `#!/bin/bash
15089
16928
  # prjct Status Line for Claude Code
15090
16929
  # Shows version update notifications and current task
@@ -15142,11 +16981,11 @@ fi
15142
16981
  # Default: show prjct branding
15143
16982
  echo "\u26A1 prjct"
15144
16983
  `;
15145
- fs30.writeFileSync(statusLinePath, scriptContent, { mode: 493 });
16984
+ fs33.writeFileSync(statusLinePath, scriptContent, { mode: 493 });
15146
16985
  let settings = {};
15147
- if (fs30.existsSync(settingsPath)) {
16986
+ if (fs33.existsSync(settingsPath)) {
15148
16987
  try {
15149
- settings = JSON.parse(fs30.readFileSync(settingsPath, "utf8"));
16988
+ settings = JSON.parse(fs33.readFileSync(settingsPath, "utf8"));
15150
16989
  } catch (_error) {
15151
16990
  }
15152
16991
  }
@@ -15154,7 +16993,7 @@ echo "\u26A1 prjct"
15154
16993
  type: "command",
15155
16994
  command: statusLinePath
15156
16995
  };
15157
- fs30.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
16996
+ fs33.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
15158
16997
  return { success: true };
15159
16998
  } catch (error) {
15160
16999
  return { success: false, error: error.message };
@@ -15164,41 +17003,41 @@ echo "\u26A1 prjct"
15164
17003
  * Show beautiful ASCII art with quick start
15165
17004
  */
15166
17005
  showAsciiArt() {
15167
- console.log(chalk4.cyan("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
17006
+ console.log(chalk6.cyan("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
15168
17007
  console.log("");
15169
- console.log(chalk4.bold.cyan(" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557"));
15170
- console.log(chalk4.bold.cyan(" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D"));
15171
- console.log(chalk4.bold.cyan(" \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551"));
15172
- console.log(chalk4.bold.cyan(" \u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551"));
15173
- console.log(chalk4.bold.cyan(" \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551"));
15174
- console.log(chalk4.bold.cyan(" \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D"));
17008
+ console.log(chalk6.bold.cyan(" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557"));
17009
+ console.log(chalk6.bold.cyan(" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D"));
17010
+ console.log(chalk6.bold.cyan(" \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551"));
17011
+ console.log(chalk6.bold.cyan(" \u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551"));
17012
+ console.log(chalk6.bold.cyan(" \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551"));
17013
+ console.log(chalk6.bold.cyan(" \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D"));
15175
17014
  console.log("");
15176
- console.log(` ${chalk4.bold.cyan("prjct")}${chalk4.magenta("/")}${chalk4.green("cli")} ${chalk4.dim.white("v" + VERSION + " installed")}`);
17015
+ console.log(` ${chalk6.bold.cyan("prjct")}${chalk6.magenta("/")}${chalk6.green("cli")} ${chalk6.dim.white("v" + VERSION + " installed")}`);
15177
17016
  console.log("");
15178
- console.log(` ${chalk4.yellow("\u26A1")} Ship faster with zero friction`);
15179
- console.log(` ${chalk4.green("\u{1F4DD}")} From idea to technical tasks in minutes`);
15180
- console.log(` ${chalk4.cyan("\u{1F916}")} Perfect context for AI agents`);
17017
+ console.log(` ${chalk6.yellow("\u26A1")} Ship faster with zero friction`);
17018
+ console.log(` ${chalk6.green("\u{1F4DD}")} From idea to technical tasks in minutes`);
17019
+ console.log(` ${chalk6.cyan("\u{1F916}")} Perfect context for AI agents`);
15181
17020
  console.log("");
15182
- console.log(chalk4.cyan("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
17021
+ console.log(chalk6.cyan("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
15183
17022
  console.log("");
15184
- console.log(chalk4.bold.cyan("\u{1F680} Quick Start"));
15185
- console.log(chalk4.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
17023
+ console.log(chalk6.bold.cyan("\u{1F680} Quick Start"));
17024
+ console.log(chalk6.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
15186
17025
  console.log("");
15187
- console.log(` ${chalk4.bold("1.")} Initialize your project:`);
15188
- console.log(` ${chalk4.green("cd your-project && prjct init")}`);
17026
+ console.log(` ${chalk6.bold("1.")} Initialize your project:`);
17027
+ console.log(` ${chalk6.green("cd your-project && prjct init")}`);
15189
17028
  console.log("");
15190
- console.log(` ${chalk4.bold("2.")} Start your first task:`);
15191
- console.log(` ${chalk4.green('prjct task "build auth"')}`);
17029
+ console.log(` ${chalk6.bold("2.")} Start your first task:`);
17030
+ console.log(` ${chalk6.green('prjct task "build auth"')}`);
15192
17031
  console.log("");
15193
- console.log(` ${chalk4.bold("3.")} Ship & celebrate:`);
15194
- console.log(` ${chalk4.green('prjct ship "user login"')}`);
17032
+ console.log(` ${chalk6.bold("3.")} Ship & celebrate:`);
17033
+ console.log(` ${chalk6.green('prjct ship "user login"')}`);
15195
17034
  console.log("");
15196
- console.log(chalk4.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
17035
+ console.log(chalk6.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
15197
17036
  console.log("");
15198
- console.log(` ${chalk4.dim("Documentation:")} ${chalk4.cyan("https://prjct.app")}`);
15199
- console.log(` ${chalk4.dim("Report issues:")} ${chalk4.cyan("https://github.com/jlopezlira/prjct-cli/issues")}`);
17037
+ console.log(` ${chalk6.dim("Documentation:")} ${chalk6.cyan("https://prjct.app")}`);
17038
+ console.log(` ${chalk6.dim("Report issues:")} ${chalk6.cyan("https://github.com/jlopezlira/prjct-cli/issues")}`);
15200
17039
  console.log("");
15201
- console.log(chalk4.bold.magenta("Happy shipping! \u{1F680}"));
17040
+ console.log(chalk6.bold.magenta("Happy shipping! \u{1F680}"));
15202
17041
  console.log("");
15203
17042
  }
15204
17043
  };
@@ -15206,8 +17045,8 @@ echo "\u26A1 prjct"
15206
17045
  });
15207
17046
 
15208
17047
  // core/commands/context.ts
15209
- import fs31 from "fs/promises";
15210
- import path38 from "path";
17048
+ import fs34 from "fs/promises";
17049
+ import path41 from "path";
15211
17050
  var ContextCommands, contextCommands;
15212
17051
  var init_context = __esm({
15213
17052
  "core/commands/context.ts"() {
@@ -15333,8 +17172,8 @@ var init_context = __esm({
15333
17172
  */
15334
17173
  async loadRepoAnalysis(globalPath) {
15335
17174
  try {
15336
- const analysisPath = path38.join(globalPath, "analysis", "repo-analysis.json");
15337
- const content = await fs31.readFile(analysisPath, "utf-8");
17175
+ const analysisPath = path41.join(globalPath, "analysis", "repo-analysis.json");
17176
+ const content = await fs34.readFile(analysisPath, "utf-8");
15338
17177
  const data = JSON.parse(content);
15339
17178
  return {
15340
17179
  ecosystem: data.ecosystem || "unknown",
@@ -15457,8 +17296,8 @@ var init_commands = __esm({
15457
17296
  async analyze(options = {}, projectPath = process.cwd()) {
15458
17297
  return this.analysis.analyze(options, projectPath);
15459
17298
  }
15460
- async sync(projectPath = process.cwd()) {
15461
- return this.analysis.sync(projectPath);
17299
+ async sync(projectPath = process.cwd(), options = {}) {
17300
+ return this.analysis.sync(projectPath, options);
15462
17301
  }
15463
17302
  // ========== Context Commands ==========
15464
17303
  async context(input = null, projectPath = process.cwd()) {
@@ -15848,7 +17687,7 @@ var workflow, planning, shipping, analytics, maintenance, analysis, setup, conte
15848
17687
  var init_register = __esm({
15849
17688
  "core/commands/register.ts"() {
15850
17689
  "use strict";
15851
- init_registry();
17690
+ init_registry2();
15852
17691
  init_command_data();
15853
17692
  init_workflow();
15854
17693
  init_planning();
@@ -15877,7 +17716,7 @@ var init_commands2 = __esm({
15877
17716
  "core/commands/index.ts"() {
15878
17717
  "use strict";
15879
17718
  init_commands();
15880
- init_registry();
17719
+ init_registry2();
15881
17720
  init_register();
15882
17721
  init_types2();
15883
17722
  }
@@ -15888,7 +17727,7 @@ var require_package = __commonJS({
15888
17727
  "package.json"(exports, module) {
15889
17728
  module.exports = {
15890
17729
  name: "prjct-cli",
15891
- version: "0.41.0",
17730
+ version: "0.43.0",
15892
17731
  description: "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
15893
17732
  main: "core/index.ts",
15894
17733
  bin: {
@@ -15993,9 +17832,9 @@ var require_package = __commonJS({
15993
17832
 
15994
17833
  // core/index.ts
15995
17834
  var core_exports = {};
15996
- import fs32 from "fs";
15997
- import path39 from "path";
15998
- import os12 from "os";
17835
+ import fs35 from "fs";
17836
+ import path42 from "path";
17837
+ import os14 from "os";
15999
17838
  async function main() {
16000
17839
  const [commandName, ...rawArgs] = process.argv.slice(2);
16001
17840
  if (["-v", "--version", "version"].includes(commandName)) {
@@ -16068,7 +17907,9 @@ Use 'prjct --help' to see available commands.`);
16068
17907
  redo: /* @__PURE__ */ __name(() => commands.redo(), "redo"),
16069
17908
  history: /* @__PURE__ */ __name(() => commands.history(), "history"),
16070
17909
  // Setup
16071
- sync: /* @__PURE__ */ __name(() => commands.sync(), "sync"),
17910
+ sync: /* @__PURE__ */ __name(() => commands.sync(process.cwd(), {
17911
+ aiTools: options.agents ? String(options.agents).split(",") : void 0
17912
+ }), "sync"),
16072
17913
  start: /* @__PURE__ */ __name(() => commands.start(), "start"),
16073
17914
  // Context (for Claude templates)
16074
17915
  context: /* @__PURE__ */ __name((p) => commands.context(p), "context")
@@ -16114,12 +17955,12 @@ function parseCommandArgs(cmd, rawArgs) {
16114
17955
  }
16115
17956
  function displayVersion(version) {
16116
17957
  const detection = detectAllProviders();
16117
- const claudeCommandPath = path39.join(os12.homedir(), ".claude", "commands", "p.md");
16118
- const geminiCommandPath = path39.join(os12.homedir(), ".gemini", "commands", "p.toml");
16119
- const claudeConfigured = fs32.existsSync(claudeCommandPath);
16120
- const geminiConfigured = fs32.existsSync(geminiCommandPath);
16121
- const cursorConfigured = fs32.existsSync(path39.join(process.cwd(), ".cursor", "commands", "sync.md"));
16122
- const cursorExists = fs32.existsSync(path39.join(process.cwd(), ".cursor"));
17958
+ const claudeCommandPath = path42.join(os14.homedir(), ".claude", "commands", "p.md");
17959
+ const geminiCommandPath = path42.join(os14.homedir(), ".gemini", "commands", "p.toml");
17960
+ const claudeConfigured = fs35.existsSync(claudeCommandPath);
17961
+ const geminiConfigured = fs35.existsSync(geminiCommandPath);
17962
+ const cursorConfigured = fs35.existsSync(path42.join(process.cwd(), ".cursor", "commands", "sync.md"));
17963
+ const cursorExists = fs35.existsSync(path42.join(process.cwd(), ".cursor"));
16123
17964
  console.log(`
16124
17965
  ${CYAN2}p/${RESET2} prjct v${version}
16125
17966
  ${DIM3}Context layer for AI coding agents${RESET2}
@@ -16217,7 +18058,7 @@ var init_core = __esm({
16217
18058
  "core/index.ts"() {
16218
18059
  "use strict";
16219
18060
  init_commands2();
16220
- init_registry();
18061
+ init_registry2();
16221
18062
  init_register();
16222
18063
  init_output();
16223
18064
  init_ai_provider();
@@ -16243,9 +18084,9 @@ var init_core = __esm({
16243
18084
  // bin/prjct.ts
16244
18085
  init_version();
16245
18086
  init_editors_config();
16246
- import fs33 from "fs";
16247
- import path40 from "path";
16248
- import os13 from "os";
18087
+ import fs36 from "fs";
18088
+ import path43 from "path";
18089
+ import os15 from "os";
16249
18090
 
16250
18091
  // core/server/server.ts
16251
18092
  import { Hono as Hono3 } from "hono";
@@ -16985,17 +18826,17 @@ __name(startServer, "startServer");
16985
18826
  init_config_manager();
16986
18827
  init_ai_provider();
16987
18828
  function checkRoutersInstalled() {
16988
- const home = os13.homedir();
18829
+ const home = os15.homedir();
16989
18830
  const detection = detectAllProviders();
16990
18831
  if (detection.claude.installed) {
16991
- const claudeRouter = path40.join(home, ".claude", "commands", "p.md");
16992
- if (!fs33.existsSync(claudeRouter)) {
18832
+ const claudeRouter = path43.join(home, ".claude", "commands", "p.md");
18833
+ if (!fs36.existsSync(claudeRouter)) {
16993
18834
  return false;
16994
18835
  }
16995
18836
  }
16996
18837
  if (detection.gemini.installed) {
16997
- const geminiRouter = path40.join(home, ".gemini", "commands", "p.toml");
16998
- if (!fs33.existsSync(geminiRouter)) {
18838
+ const geminiRouter = path43.join(home, ".gemini", "commands", "p.toml");
18839
+ if (!fs36.existsSync(geminiRouter)) {
16999
18840
  return false;
17000
18841
  }
17001
18842
  }
@@ -17041,7 +18882,7 @@ if (args[0] === "start" || args[0] === "setup") {
17041
18882
  console.error('No prjct project found. Run "prjct init" first.');
17042
18883
  process.exitCode = 1;
17043
18884
  } else {
17044
- const linearCliPath = path40.join(__dirname, "..", "core", "cli", "linear.ts");
18885
+ const linearCliPath = path43.join(__dirname, "..", "core", "cli", "linear.ts");
17045
18886
  const linearArgs = ["--project", projectId, ...args.slice(1)];
17046
18887
  const child = spawn("bun", [linearCliPath, ...linearArgs], {
17047
18888
  stdio: "inherit",
@@ -17053,14 +18894,14 @@ if (args[0] === "start" || args[0] === "setup") {
17053
18894
  }
17054
18895
  } else if (args[0] === "version" || args[0] === "-v" || args[0] === "--version") {
17055
18896
  const detection = detectAllProviders();
17056
- const home = os13.homedir();
18897
+ const home = os15.homedir();
17057
18898
  const cwd = process.cwd();
17058
- const claudeConfigured = fs33.existsSync(path40.join(home, ".claude", "commands", "p.md"));
17059
- const geminiConfigured = fs33.existsSync(path40.join(home, ".gemini", "commands", "p.toml"));
17060
- const cursorDetected = fs33.existsSync(path40.join(cwd, ".cursor"));
17061
- const cursorConfigured = fs33.existsSync(path40.join(cwd, ".cursor", "rules", "prjct.mdc"));
17062
- const windsurfDetected = fs33.existsSync(path40.join(cwd, ".windsurf"));
17063
- const windsurfConfigured = fs33.existsSync(path40.join(cwd, ".windsurf", "rules", "prjct.md"));
18899
+ const claudeConfigured = fs36.existsSync(path43.join(home, ".claude", "commands", "p.md"));
18900
+ const geminiConfigured = fs36.existsSync(path43.join(home, ".gemini", "commands", "p.toml"));
18901
+ const cursorDetected = fs36.existsSync(path43.join(cwd, ".cursor"));
18902
+ const cursorConfigured = fs36.existsSync(path43.join(cwd, ".cursor", "rules", "prjct.mdc"));
18903
+ const windsurfDetected = fs36.existsSync(path43.join(cwd, ".windsurf"));
18904
+ const windsurfConfigured = fs36.existsSync(path43.join(cwd, ".windsurf", "rules", "prjct.md"));
17064
18905
  const GREEN4 = "\x1B[32m";
17065
18906
  console.log(`
17066
18907
  ${CYAN3}p/${RESET3} prjct v${VERSION}
@@ -17099,9 +18940,9 @@ ${DIM4}Run 'prjct init' to configure (Cursor/Windsurf IDE)${RESET3}
17099
18940
  ${CYAN3}https://prjct.app${RESET3}
17100
18941
  `);
17101
18942
  } else {
17102
- const configPath = path40.join(os13.homedir(), ".prjct-cli", "config", "installed-editors.json");
18943
+ const configPath = path43.join(os15.homedir(), ".prjct-cli", "config", "installed-editors.json");
17103
18944
  const routersInstalled = checkRoutersInstalled();
17104
- if (!fs33.existsSync(configPath) || !routersInstalled) {
18945
+ if (!fs36.existsSync(configPath) || !routersInstalled) {
17105
18946
  console.log(`
17106
18947
  ${CYAN3}${BOLD2} Welcome to prjct!${RESET3}
17107
18948