prjct-cli 1.11.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -23,13 +23,14 @@ import { getRecentFiles } from '../context-tools/recent-tool'
23
23
  import { extractSignatures } from '../context-tools/signatures-tool'
24
24
  import configManager from '../infrastructure/config-manager'
25
25
  import pathManager from '../infrastructure/path-manager'
26
- import { stateStorage } from '../storage'
26
+ import { analysisStorage, stateStorage } from '../storage'
27
27
  import type {
28
28
  LoadedAgent,
29
29
  LoadedSkill,
30
30
  OrchestratorContext,
31
31
  OrchestratorSubtask,
32
32
  RealCodebaseContext,
33
+ SealedAnalysisContext,
33
34
  } from '../types'
34
35
  import { getErrorMessage, isNotFoundError } from '../types/fs'
35
36
  import domainClassifier, { type ProjectContext } from './domain-classifier'
@@ -76,8 +77,11 @@ export class OrchestratorExecutor {
76
77
  // Step 5: Load skills from agent frontmatter
77
78
  const skills = await this.loadSkills(agents)
78
79
 
79
- // Step 6: Gather real codebase context proactively
80
- const realContext = await this.gatherRealContext(taskDescription, projectPath)
80
+ // Step 6: Gather real codebase context and sealed analysis in parallel
81
+ const [realContext, sealedAnalysis] = await Promise.all([
82
+ this.gatherRealContext(taskDescription, projectPath),
83
+ this.loadSealedAnalysis(projectId),
84
+ ])
81
85
 
82
86
  // Step 7: Determine if fragmentation is needed
83
87
  const requiresFragmentation = this.shouldFragment(domains, taskDescription)
@@ -101,6 +105,7 @@ export class OrchestratorExecutor {
101
105
  conventions: repoAnalysis?.conventions || [],
102
106
  },
103
107
  realContext,
108
+ sealedAnalysis,
104
109
  }
105
110
  }
106
111
 
@@ -197,6 +202,34 @@ export class OrchestratorExecutor {
197
202
  }
198
203
  }
199
204
 
205
+ /**
206
+ * Load sealed/active analysis from analysis storage (PRJ-260).
207
+ * Returns sealed if available, otherwise draft as fallback.
208
+ * Returns null if no analysis exists (graceful degradation).
209
+ */
210
+ private async loadSealedAnalysis(projectId: string): Promise<SealedAnalysisContext | null> {
211
+ try {
212
+ const analysis = await analysisStorage.getActive(projectId)
213
+ if (!analysis) return null
214
+
215
+ return {
216
+ languages: analysis.languages,
217
+ frameworks: analysis.frameworks,
218
+ packageManager: analysis.packageManager,
219
+ sourceDir: analysis.sourceDir,
220
+ testDir: analysis.testDir,
221
+ fileCount: analysis.fileCount,
222
+ patterns: analysis.patterns,
223
+ antiPatterns: analysis.antiPatterns,
224
+ status: analysis.status ?? 'draft',
225
+ commitHash: analysis.commitHash,
226
+ }
227
+ } catch {
228
+ // Graceful degradation — analysis is optional enhancement
229
+ return null
230
+ }
231
+ }
232
+
200
233
  /**
201
234
  * Load repo-analysis.json for project context
202
235
  */
@@ -542,10 +542,50 @@ class PromptBuilder {
542
542
  // =========================================================================
543
543
 
544
544
  if (orchestratorContext) {
545
+ const sa = orchestratorContext.sealedAnalysis
545
546
  parts.push('\n## PROJECT ANALYSIS (Sealed)\n')
546
547
  parts.push(`**Ecosystem**: ${orchestratorContext.project.ecosystem}\n`)
547
548
  parts.push(`**Primary Domain**: ${orchestratorContext.primaryDomain}\n`)
548
- parts.push(`**Domains**: ${orchestratorContext.detectedDomains.join(', ')}\n\n`)
549
+ parts.push(`**Domains**: ${orchestratorContext.detectedDomains.join(', ')}\n`)
550
+
551
+ // Inject sealed analysis data (PRJ-260)
552
+ if (sa) {
553
+ if (sa.languages.length > 0) {
554
+ parts.push(`**Languages**: ${sa.languages.join(', ')}\n`)
555
+ }
556
+ if (sa.frameworks.length > 0) {
557
+ parts.push(`**Frameworks**: ${sa.frameworks.join(', ')}\n`)
558
+ }
559
+ if (sa.packageManager) {
560
+ parts.push(`**Package Manager**: ${sa.packageManager}\n`)
561
+ }
562
+ if (sa.sourceDir) {
563
+ parts.push(`**Source Dir**: ${sa.sourceDir}\n`)
564
+ }
565
+ if (sa.testDir) {
566
+ parts.push(`**Test Dir**: ${sa.testDir}\n`)
567
+ }
568
+ parts.push(`**Files Analyzed**: ${sa.fileCount}\n`)
569
+ parts.push(
570
+ `**Analysis Status**: ${sa.status}${sa.commitHash ? ` (commit: ${sa.commitHash.slice(0, 8)})` : ''}\n`
571
+ )
572
+
573
+ if (sa.patterns.length > 0) {
574
+ parts.push('\n### Code Patterns (Follow These)\n')
575
+ for (const p of sa.patterns) {
576
+ parts.push(`- **${p.name}**: ${p.description}${p.location ? ` (${p.location})` : ''}\n`)
577
+ }
578
+ }
579
+
580
+ if (sa.antiPatterns.length > 0) {
581
+ parts.push('\n### Anti-Patterns (Avoid These)\n')
582
+ for (const ap of sa.antiPatterns) {
583
+ parts.push(`- **${ap.issue}** in \`${ap.file}\` — ${ap.suggestion}\n`)
584
+ }
585
+ }
586
+ }
587
+
588
+ parts.push('\n')
549
589
  }
550
590
 
551
591
  const needsPatterns = commandContext.patterns
@@ -648,6 +688,7 @@ class PromptBuilder {
648
688
  // =========================================================================
649
689
 
650
690
  if (projectPath) {
691
+ const sa = orchestratorContext?.sealedAnalysis
651
692
  const groundTruth: ProjectGroundTruth = {
652
693
  projectPath,
653
694
  language: orchestratorContext?.project?.ecosystem,
@@ -655,6 +696,10 @@ class PromptBuilder {
655
696
  domains: this.extractDomains(state),
656
697
  fileCount: context.files?.length || context.filteredSize || 0,
657
698
  availableAgents: orchestratorContext?.agents?.map((a) => a.name) || [],
699
+ // Inject sealed analysis data for enriched grounding (PRJ-260)
700
+ analysisLanguages: sa?.languages || [],
701
+ analysisFrameworks: sa?.frameworks || [],
702
+ analysisPackageManager: sa?.packageManager,
658
703
  }
659
704
  parts.push(`\n${buildAntiHallucinationBlock(groundTruth)}\n`)
660
705
  } else {
@@ -800,6 +845,24 @@ class PromptBuilder {
800
845
  if (currentSubtask.dependsOn.length > 0) {
801
846
  parts.push(`Dependencies: ${currentSubtask.dependsOn.join(', ')}\n`)
802
847
  }
848
+
849
+ // Inject previous subtask handoff for context continuity (PRJ-262)
850
+ if (currentSubtask.handoff) {
851
+ const h = currentSubtask.handoff
852
+ parts.push('\n### Previous Subtask Handoff\n\n')
853
+ parts.push(`**From:** ${h.fromSubtask}\n\n`)
854
+ parts.push('**What was done:**\n')
855
+ for (const item of h.whatWasDone) {
856
+ parts.push(`- ${item}\n`)
857
+ }
858
+ if (h.filesChanged.length > 0) {
859
+ parts.push('\n**Files changed:**\n')
860
+ for (const f of h.filesChanged) {
861
+ parts.push(`- \`${f.path}\` (${f.action})\n`)
862
+ }
863
+ }
864
+ parts.push(`\n**Context for this subtask:**\n${h.outputForNextAgent}\n`)
865
+ }
803
866
  }
804
867
  parts.push('\n')
805
868
  }
@@ -44,11 +44,18 @@ export const SubtaskSummarySchema = z.object({
44
44
  action: z.enum(['created', 'modified', 'deleted']),
45
45
  })
46
46
  ),
47
- whatWasDone: z.array(z.string()),
48
- outputForNextAgent: z.string().optional(),
47
+ whatWasDone: z.array(z.string()).min(1),
48
+ outputForNextAgent: z.string().min(1),
49
49
  notes: z.string().optional(),
50
50
  })
51
51
 
52
+ // Schema for validating completion data before persisting
53
+ // Used by completeSubtask() to enforce mandatory handoff
54
+ export const SubtaskCompletionDataSchema = z.object({
55
+ output: z.string().min(1, 'Subtask output is required'),
56
+ summary: SubtaskSummarySchema,
57
+ })
58
+
52
59
  // Subtask schema for task fragmentation
53
60
  export const SubtaskSchema = z.object({
54
61
  id: z.string(), // subtask-xxx
@@ -169,6 +176,7 @@ export type ActivityType = z.infer<typeof ActivityTypeSchema>
169
176
 
170
177
  export type Subtask = z.infer<typeof SubtaskSchema>
171
178
  export type SubtaskSummary = z.infer<typeof SubtaskSummarySchema>
179
+ export type SubtaskCompletionData = z.infer<typeof SubtaskCompletionDataSchema>
172
180
  export type SubtaskProgress = z.infer<typeof SubtaskProgressSchema>
173
181
 
174
182
  export type CurrentTask = z.infer<typeof CurrentTaskSchema>
@@ -197,6 +205,18 @@ export const parseQueue = (data: unknown): QueueJson => QueueJsonSchema.parse(da
197
205
  export const safeParseState = (data: unknown) => StateJsonSchema.safeParse(data)
198
206
  export const safeParseQueue = (data: unknown) => QueueJsonSchema.safeParse(data)
199
207
 
208
+ /** Validate subtask completion data — returns errors or null */
209
+ export const validateSubtaskCompletion = (
210
+ data: unknown
211
+ ): { success: true; data: SubtaskCompletionData } | { success: false; errors: string[] } => {
212
+ const result = SubtaskCompletionDataSchema.safeParse(data)
213
+ if (result.success) return { success: true, data: result.data }
214
+ return {
215
+ success: false,
216
+ errors: result.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`),
217
+ }
218
+ }
219
+
200
220
  // =============================================================================
201
221
  // Defaults
202
222
  // =============================================================================
@@ -14,9 +14,9 @@ import type {
14
14
  PreviousTask,
15
15
  StateJson,
16
16
  Subtask,
17
- SubtaskSummary,
17
+ SubtaskCompletionData,
18
18
  } from '../schemas/state'
19
- import { StateJsonSchema } from '../schemas/state'
19
+ import { StateJsonSchema, SubtaskCompletionDataSchema } from '../schemas/state'
20
20
  import { getTimestamp, toRelative } from '../utils/date-helper'
21
21
  import { md } from '../utils/markdown-builder'
22
22
  import type { WorkflowCommand } from '../workflow/state-machine'
@@ -497,14 +497,23 @@ class StateStorage extends StorageManager<StateJson> {
497
497
  }
498
498
 
499
499
  /**
500
- * Complete current subtask and advance to next
501
- * Returns the next subtask (or null if all complete)
500
+ * Complete current subtask and advance to next.
501
+ * Requires output and summary for mandatory handoff (PRJ-262).
502
+ * Returns the next subtask (or null if all complete).
502
503
  */
503
504
  async completeSubtask(
504
505
  projectId: string,
505
- output?: string,
506
- summary?: SubtaskSummary
506
+ completionData: SubtaskCompletionData
507
507
  ): Promise<Subtask | null> {
508
+ // Validate handoff data with Zod before persisting
509
+ const validation = SubtaskCompletionDataSchema.safeParse(completionData)
510
+ if (!validation.success) {
511
+ const errors = validation.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`)
512
+ throw new Error(`Subtask completion requires handoff data:\n${errors.join('\n')}`)
513
+ }
514
+
515
+ const { output, summary } = validation.data
516
+
508
517
  const state = await this.read(projectId)
509
518
  if (!state.currentTask?.subtasks) return null
510
519
 
@@ -512,7 +521,7 @@ class StateStorage extends StorageManager<StateJson> {
512
521
  const current = state.currentTask.subtasks[currentIndex]
513
522
  if (!current) return null
514
523
 
515
- // Mark current as completed
524
+ // Mark current as completed with mandatory handoff
516
525
  const updatedSubtasks = [...state.currentTask.subtasks]
517
526
  updatedSubtasks[currentIndex] = {
518
527
  ...current,
@@ -548,12 +557,14 @@ class StateStorage extends StorageManager<StateJson> {
548
557
  lastUpdated: getTimestamp(),
549
558
  }))
550
559
 
551
- // Publish event
560
+ // Publish event with handoff data
552
561
  await this.publishEvent(projectId, 'subtask.completed', {
553
562
  taskId: state.currentTask.id,
554
563
  subtaskId: current.id,
555
564
  description: current.description,
556
565
  output,
566
+ handoff: summary.outputForNextAgent,
567
+ filesChanged: summary.filesChanged.length,
557
568
  progress: { completed, total, percentage },
558
569
  })
559
570
 
@@ -592,6 +603,27 @@ class StateStorage extends StorageManager<StateJson> {
592
603
  return state.currentTask.subtasks[prevIndex] || null
593
604
  }
594
605
 
606
+ /**
607
+ * Get handoff from the most recently completed subtask (PRJ-262).
608
+ * Returns the summary.outputForNextAgent and related context,
609
+ * or null if no previous subtask or no handoff data.
610
+ */
611
+ async getPreviousHandoff(projectId: string): Promise<{
612
+ fromSubtask: string
613
+ outputForNextAgent: string
614
+ filesChanged: Array<{ path: string; action: string }>
615
+ whatWasDone: string[]
616
+ } | null> {
617
+ const prev = await this.getPreviousSubtask(projectId)
618
+ if (!prev?.summary?.outputForNextAgent) return null
619
+ return {
620
+ fromSubtask: prev.description,
621
+ outputForNextAgent: prev.summary.outputForNextAgent,
622
+ filesChanged: prev.summary.filesChanged,
623
+ whatWasDone: prev.summary.whatWasDone,
624
+ }
625
+ }
626
+
595
627
  /**
596
628
  * Get all subtasks
597
629
  */
@@ -673,6 +673,13 @@ export interface OrchestratorSubtask {
673
673
  status: 'pending' | 'in_progress' | 'completed' | 'failed'
674
674
  dependsOn: string[]
675
675
  order: number
676
+ /** Handoff from previous subtask — injected into prompt for context continuity (PRJ-262) */
677
+ handoff?: {
678
+ fromSubtask: string
679
+ outputForNextAgent: string
680
+ filesChanged: Array<{ path: string; action: string }>
681
+ whatWasDone: string[]
682
+ }
676
683
  }
677
684
 
678
685
  /**
@@ -717,4 +724,35 @@ export interface OrchestratorContext {
717
724
  }
718
725
  /** Real codebase context gathered proactively */
719
726
  realContext?: RealCodebaseContext
727
+ /** Sealed/active analysis from PRJ-263 storage — injected into prompt context (PRJ-260) */
728
+ sealedAnalysis?: SealedAnalysisContext | null
729
+ }
730
+
731
+ /**
732
+ * Subset of analysis data injected into prompt context.
733
+ * Extracted from AnalysisSchema to avoid coupling types to storage schema.
734
+ *
735
+ * @see PRJ-260
736
+ */
737
+ export interface SealedAnalysisContext {
738
+ /** Programming languages detected */
739
+ languages: string[]
740
+ /** Frameworks detected */
741
+ frameworks: string[]
742
+ /** Package manager (e.g., 'bun', 'npm', 'pnpm') */
743
+ packageManager?: string
744
+ /** Source directory */
745
+ sourceDir?: string
746
+ /** Test directory */
747
+ testDir?: string
748
+ /** Total files analyzed */
749
+ fileCount: number
750
+ /** Code patterns found */
751
+ patterns: Array<{ name: string; description: string; location?: string }>
752
+ /** Anti-patterns found */
753
+ antiPatterns: Array<{ issue: string; file: string; suggestion: string }>
754
+ /** Lifecycle status */
755
+ status: 'draft' | 'verified' | 'sealed'
756
+ /** Git commit hash when analysis was performed */
757
+ commitHash?: string
720
758
  }
@@ -81,6 +81,8 @@ export type {
81
81
  RealCodebaseContext,
82
82
  ReasoningResult,
83
83
  ReasoningStep,
84
+ // Sealed analysis context (PRJ-260)
85
+ SealedAnalysisContext,
84
86
  SimpleExecutionResult,
85
87
  SkillContext,
86
88
  SmartContextProjectState,
@@ -10212,7 +10212,7 @@ var init_shipped = __esm({
10212
10212
 
10213
10213
  // core/schemas/state.ts
10214
10214
  import { z as z14 } from "zod";
10215
- var PrioritySchema, TaskTypeSchema, TaskSectionSchema, TaskStatusSchema, ActivityTypeSchema, SubtaskSummarySchema, SubtaskSchema, SubtaskProgressSchema, CurrentTaskSchema, PreviousTaskSchema, StateJsonSchema, QueueTaskSchema, QueueJsonSchema, StatsSchema, RecentActivitySchema, StateSchemaFull;
10215
+ var PrioritySchema, TaskTypeSchema, TaskSectionSchema, TaskStatusSchema, ActivityTypeSchema, SubtaskSummarySchema, SubtaskCompletionDataSchema, SubtaskSchema, SubtaskProgressSchema, CurrentTaskSchema, PreviousTaskSchema, StateJsonSchema, QueueTaskSchema, QueueJsonSchema, StatsSchema, RecentActivitySchema, StateSchemaFull;
10216
10216
  var init_state = __esm({
10217
10217
  "core/schemas/state.ts"() {
10218
10218
  "use strict";
@@ -10244,10 +10244,14 @@ var init_state = __esm({
10244
10244
  action: z14.enum(["created", "modified", "deleted"])
10245
10245
  })
10246
10246
  ),
10247
- whatWasDone: z14.array(z14.string()),
10248
- outputForNextAgent: z14.string().optional(),
10247
+ whatWasDone: z14.array(z14.string()).min(1),
10248
+ outputForNextAgent: z14.string().min(1),
10249
10249
  notes: z14.string().optional()
10250
10250
  });
10251
+ SubtaskCompletionDataSchema = z14.object({
10252
+ output: z14.string().min(1, "Subtask output is required"),
10253
+ summary: SubtaskSummarySchema
10254
+ });
10251
10255
  SubtaskSchema = z14.object({
10252
10256
  id: z14.string(),
10253
10257
  // subtask-xxx
@@ -13259,10 +13263,18 @@ var init_state_storage = __esm({
13259
13263
  });
13260
13264
  }
13261
13265
  /**
13262
- * Complete current subtask and advance to next
13263
- * Returns the next subtask (or null if all complete)
13266
+ * Complete current subtask and advance to next.
13267
+ * Requires output and summary for mandatory handoff (PRJ-262).
13268
+ * Returns the next subtask (or null if all complete).
13264
13269
  */
13265
- async completeSubtask(projectId, output, summary) {
13270
+ async completeSubtask(projectId, completionData) {
13271
+ const validation = SubtaskCompletionDataSchema.safeParse(completionData);
13272
+ if (!validation.success) {
13273
+ const errors = validation.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`);
13274
+ throw new Error(`Subtask completion requires handoff data:
13275
+ ${errors.join("\n")}`);
13276
+ }
13277
+ const { output, summary } = validation.data;
13266
13278
  const state = await this.read(projectId);
13267
13279
  if (!state.currentTask?.subtasks) return null;
13268
13280
  const currentIndex = state.currentTask.currentSubtaskIndex || 0;
@@ -13302,6 +13314,8 @@ var init_state_storage = __esm({
13302
13314
  subtaskId: current.id,
13303
13315
  description: current.description,
13304
13316
  output,
13317
+ handoff: summary.outputForNextAgent,
13318
+ filesChanged: summary.filesChanged.length,
13305
13319
  progress: { completed, total, percentage }
13306
13320
  });
13307
13321
  return nextIndex < total ? updatedSubtasks[nextIndex] : null;
@@ -13334,6 +13348,21 @@ var init_state_storage = __esm({
13334
13348
  if (prevIndex < 0) return null;
13335
13349
  return state.currentTask.subtasks[prevIndex] || null;
13336
13350
  }
13351
+ /**
13352
+ * Get handoff from the most recently completed subtask (PRJ-262).
13353
+ * Returns the summary.outputForNextAgent and related context,
13354
+ * or null if no previous subtask or no handoff data.
13355
+ */
13356
+ async getPreviousHandoff(projectId) {
13357
+ const prev = await this.getPreviousSubtask(projectId);
13358
+ if (!prev?.summary?.outputForNextAgent) return null;
13359
+ return {
13360
+ fromSubtask: prev.description,
13361
+ outputForNextAgent: prev.summary.outputForNextAgent,
13362
+ filesChanged: prev.summary.filesChanged,
13363
+ whatWasDone: prev.summary.whatWasDone
13364
+ };
13365
+ }
13337
13366
  /**
13338
13367
  * Get all subtasks
13339
13368
  */
@@ -14174,7 +14203,10 @@ var init_orchestrator_executor = __esm({
14174
14203
  const { domains, primary } = await this.detectDomains(taskDescription, projectId, repoAnalysis);
14175
14204
  const agents = await this.loadAgents(domains, projectId);
14176
14205
  const skills = await this.loadSkills(agents);
14177
- const realContext = await this.gatherRealContext(taskDescription, projectPath);
14206
+ const [realContext, sealedAnalysis] = await Promise.all([
14207
+ this.gatherRealContext(taskDescription, projectPath),
14208
+ this.loadSealedAnalysis(projectId)
14209
+ ]);
14178
14210
  const requiresFragmentation = this.shouldFragment(domains, taskDescription);
14179
14211
  let subtasks = null;
14180
14212
  if (requiresFragmentation && command === "task") {
@@ -14192,7 +14224,8 @@ var init_orchestrator_executor = __esm({
14192
14224
  ecosystem: repoAnalysis?.ecosystem || "unknown",
14193
14225
  conventions: repoAnalysis?.conventions || []
14194
14226
  },
14195
- realContext
14227
+ realContext,
14228
+ sealedAnalysis
14196
14229
  };
14197
14230
  }
14198
14231
  /**
@@ -14272,6 +14305,31 @@ var init_orchestrator_executor = __esm({
14272
14305
  return { branch: "unknown", status: "git unavailable" };
14273
14306
  }
14274
14307
  }
14308
+ /**
14309
+ * Load sealed/active analysis from analysis storage (PRJ-260).
14310
+ * Returns sealed if available, otherwise draft as fallback.
14311
+ * Returns null if no analysis exists (graceful degradation).
14312
+ */
14313
+ async loadSealedAnalysis(projectId) {
14314
+ try {
14315
+ const analysis2 = await analysisStorage.getActive(projectId);
14316
+ if (!analysis2) return null;
14317
+ return {
14318
+ languages: analysis2.languages,
14319
+ frameworks: analysis2.frameworks,
14320
+ packageManager: analysis2.packageManager,
14321
+ sourceDir: analysis2.sourceDir,
14322
+ testDir: analysis2.testDir,
14323
+ fileCount: analysis2.fileCount,
14324
+ patterns: analysis2.patterns,
14325
+ antiPatterns: analysis2.antiPatterns,
14326
+ status: analysis2.status ?? "draft",
14327
+ commitHash: analysis2.commitHash
14328
+ };
14329
+ } catch {
14330
+ return null;
14331
+ }
14332
+ }
14275
14333
  /**
14276
14334
  * Load repo-analysis.json for project context
14277
14335
  */
@@ -15352,9 +15410,24 @@ function buildAntiHallucinationBlock(truth) {
15352
15410
  if (truth.framework) available.push(truth.framework);
15353
15411
  const techStack = truth.techStack ?? [];
15354
15412
  available.push(...techStack.filter((t) => t !== truth.framework));
15413
+ const analysisLangs = truth.analysisLanguages ?? [];
15414
+ const analysisFrameworks = truth.analysisFrameworks ?? [];
15415
+ for (const lang of analysisLangs) {
15416
+ if (!available.some((a) => a.toLowerCase() === lang.toLowerCase())) {
15417
+ available.push(lang);
15418
+ }
15419
+ }
15420
+ for (const fw of analysisFrameworks) {
15421
+ if (!available.some((a) => a.toLowerCase() === fw.toLowerCase())) {
15422
+ available.push(fw);
15423
+ }
15424
+ }
15355
15425
  if (available.length > 0) {
15356
15426
  parts.push(`AVAILABLE in this project: ${available.join(", ")}`);
15357
15427
  }
15428
+ if (truth.analysisPackageManager) {
15429
+ parts.push(`PACKAGE MANAGER: ${truth.analysisPackageManager}`);
15430
+ }
15358
15431
  if (truth.domains) {
15359
15432
  const absent = Object.entries(truth.domains).filter(([, hasIt]) => !hasIt).map(([key]) => DOMAIN_LABELS[key]).filter(Boolean);
15360
15433
  if (absent.length > 0) {
@@ -15401,7 +15474,13 @@ var init_anti_hallucination = __esm({
15401
15474
  /** Total files in project */
15402
15475
  fileCount: z15.number().optional(),
15403
15476
  /** Available agent names (e.g., ['backend', 'testing']) */
15404
- availableAgents: z15.array(z15.string()).default([])
15477
+ availableAgents: z15.array(z15.string()).default([]),
15478
+ /** Sealed analysis languages — used to ground available tech (PRJ-260) */
15479
+ analysisLanguages: z15.array(z15.string()).default([]),
15480
+ /** Sealed analysis frameworks — used to ground available tech (PRJ-260) */
15481
+ analysisFrameworks: z15.array(z15.string()).default([]),
15482
+ /** Package manager from sealed analysis (PRJ-260) */
15483
+ analysisPackageManager: z15.string().optional()
15405
15484
  });
15406
15485
  DOMAIN_LABELS = {
15407
15486
  hasFrontend: "Frontend (UI/components)",
@@ -16145,14 +16224,57 @@ ${envBlock}
16145
16224
  `);
16146
16225
  }
16147
16226
  if (orchestratorContext) {
16227
+ const sa = orchestratorContext.sealedAnalysis;
16148
16228
  parts.push("\n## PROJECT ANALYSIS (Sealed)\n");
16149
16229
  parts.push(`**Ecosystem**: ${orchestratorContext.project.ecosystem}
16150
16230
  `);
16151
16231
  parts.push(`**Primary Domain**: ${orchestratorContext.primaryDomain}
16152
16232
  `);
16153
16233
  parts.push(`**Domains**: ${orchestratorContext.detectedDomains.join(", ")}
16154
-
16155
16234
  `);
16235
+ if (sa) {
16236
+ if (sa.languages.length > 0) {
16237
+ parts.push(`**Languages**: ${sa.languages.join(", ")}
16238
+ `);
16239
+ }
16240
+ if (sa.frameworks.length > 0) {
16241
+ parts.push(`**Frameworks**: ${sa.frameworks.join(", ")}
16242
+ `);
16243
+ }
16244
+ if (sa.packageManager) {
16245
+ parts.push(`**Package Manager**: ${sa.packageManager}
16246
+ `);
16247
+ }
16248
+ if (sa.sourceDir) {
16249
+ parts.push(`**Source Dir**: ${sa.sourceDir}
16250
+ `);
16251
+ }
16252
+ if (sa.testDir) {
16253
+ parts.push(`**Test Dir**: ${sa.testDir}
16254
+ `);
16255
+ }
16256
+ parts.push(`**Files Analyzed**: ${sa.fileCount}
16257
+ `);
16258
+ parts.push(
16259
+ `**Analysis Status**: ${sa.status}${sa.commitHash ? ` (commit: ${sa.commitHash.slice(0, 8)})` : ""}
16260
+ `
16261
+ );
16262
+ if (sa.patterns.length > 0) {
16263
+ parts.push("\n### Code Patterns (Follow These)\n");
16264
+ for (const p of sa.patterns) {
16265
+ parts.push(`- **${p.name}**: ${p.description}${p.location ? ` (${p.location})` : ""}
16266
+ `);
16267
+ }
16268
+ }
16269
+ if (sa.antiPatterns.length > 0) {
16270
+ parts.push("\n### Anti-Patterns (Avoid These)\n");
16271
+ for (const ap of sa.antiPatterns) {
16272
+ parts.push(`- **${ap.issue}** in \`${ap.file}\` \u2014 ${ap.suggestion}
16273
+ `);
16274
+ }
16275
+ }
16276
+ }
16277
+ parts.push("\n");
16156
16278
  }
16157
16279
  const needsPatterns = commandContext.patterns;
16158
16280
  const codePatternsContent = state?.codePatterns || "";
@@ -16253,13 +16375,18 @@ Show changes, list affected files, ask for confirmation.
16253
16375
  );
16254
16376
  }
16255
16377
  if (projectPath) {
16378
+ const sa = orchestratorContext?.sealedAnalysis;
16256
16379
  const groundTruth2 = {
16257
16380
  projectPath,
16258
16381
  language: orchestratorContext?.project?.ecosystem,
16259
16382
  techStack: orchestratorContext?.project?.conventions || [],
16260
16383
  domains: this.extractDomains(state),
16261
16384
  fileCount: context2.files?.length || context2.filteredSize || 0,
16262
- availableAgents: orchestratorContext?.agents?.map((a) => a.name) || []
16385
+ availableAgents: orchestratorContext?.agents?.map((a) => a.name) || [],
16386
+ // Inject sealed analysis data for enriched grounding (PRJ-260)
16387
+ analysisLanguages: sa?.languages || [],
16388
+ analysisFrameworks: sa?.frameworks || [],
16389
+ analysisPackageManager: sa?.packageManager
16263
16390
  };
16264
16391
  parts.push(`
16265
16392
  ${buildAntiHallucinationBlock(groundTruth2)}
@@ -16386,6 +16513,29 @@ Read files before modifying.
16386
16513
  `);
16387
16514
  if (currentSubtask.dependsOn.length > 0) {
16388
16515
  parts.push(`Dependencies: ${currentSubtask.dependsOn.join(", ")}
16516
+ `);
16517
+ }
16518
+ if (currentSubtask.handoff) {
16519
+ const h = currentSubtask.handoff;
16520
+ parts.push("\n### Previous Subtask Handoff\n\n");
16521
+ parts.push(`**From:** ${h.fromSubtask}
16522
+
16523
+ `);
16524
+ parts.push("**What was done:**\n");
16525
+ for (const item of h.whatWasDone) {
16526
+ parts.push(`- ${item}
16527
+ `);
16528
+ }
16529
+ if (h.filesChanged.length > 0) {
16530
+ parts.push("\n**Files changed:**\n");
16531
+ for (const f of h.filesChanged) {
16532
+ parts.push(`- \`${f.path}\` (${f.action})
16533
+ `);
16534
+ }
16535
+ }
16536
+ parts.push(`
16537
+ **Context for this subtask:**
16538
+ ${h.outputForNextAgent}
16389
16539
  `);
16390
16540
  }
16391
16541
  }
@@ -31050,7 +31200,7 @@ var require_package = __commonJS({
31050
31200
  "package.json"(exports, module) {
31051
31201
  module.exports = {
31052
31202
  name: "prjct-cli",
31053
- version: "1.11.0",
31203
+ version: "1.13.0",
31054
31204
  description: "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
31055
31205
  main: "core/index.ts",
31056
31206
  bin: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prjct-cli",
3
- "version": "1.11.0",
3
+ "version": "1.13.0",
4
4
  "description": "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
5
5
  "main": "core/index.ts",
6
6
  "bin": {