@itz4blitz/agentful 0.1.11 → 0.2.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.
@@ -9,6 +9,26 @@ tools: Read, Write, Edit, Glob, Grep
9
9
 
10
10
  This skill provides natural language processing capabilities for understanding user intent, managing conversation context, resolving references, and maintaining conversation history.
11
11
 
12
+ ## When to Use
13
+
14
+ This skill is invoked when:
15
+ - User runs `/agentful <text>` with natural language input
16
+ - Any command receives ambiguous or unclear input
17
+ - Need to resolve pronouns or references from conversation history
18
+ - Need to classify user intent before routing to handlers
19
+
20
+ **Entry Point**: The `/agentful` command delegates to this skill for all natural language processing.
21
+
22
+ ## Responsibilities
23
+
24
+ 1. **Intent Classification** - Determine what the user wants (feature, bug fix, status, etc.)
25
+ 2. **Reference Resolution** - Resolve "it", "that", "this" to actual feature names
26
+ 3. **Entity Extraction** - Extract features, domains, subtasks mentioned
27
+ 4. **Ambiguity Detection** - Identify unclear requests and ask clarifying questions
28
+ 5. **Context Management** - Track conversation state, detect context loss
29
+ 6. **Routing** - Route to appropriate handler (orchestrator, status, validate, etc.)
30
+ 7. **History Tracking** - Maintain conversation history for context
31
+
12
32
  ## Core Functions
13
33
 
14
34
  ### 1. Intent Classification
@@ -796,6 +816,221 @@ function get_recent_context(
796
816
  }
797
817
  ```
798
818
 
819
+ ## Routing Logic
820
+
821
+ ### Route to Handler
822
+
823
+ ```typescript
824
+ /**
825
+ * Determine which handler should process the classified intent
826
+ * @param intent - Classified intent from user message
827
+ * @param entities - Extracted entities (features, domains, etc.)
828
+ * @param state - Current conversation state
829
+ * @returns Routing decision with handler and context
830
+ */
831
+ function route_to_handler(
832
+ intent: IntentClassification,
833
+ entities: FeatureMention,
834
+ state: ConversationState
835
+ ): RoutingDecision {
836
+ const intentName = intent.intent;
837
+
838
+ // Feature development or bug fixes → orchestrator
839
+ if (intentName === 'feature_request' || intentName === 'bug_report') {
840
+ return {
841
+ handler: 'orchestrator',
842
+ skill: null,
843
+ context: {
844
+ intent: intentName,
845
+ message: entities.feature_name
846
+ ? `User wants to ${intentName === 'feature_request' ? 'build' : 'fix'} ${entities.feature_name}`
847
+ : 'User has a request that needs classification',
848
+ feature_id: entities.feature_id,
849
+ domain_id: entities.domain_id,
850
+ work_type: intentName === 'feature_request' ? 'FEATURE_DEVELOPMENT' : 'BUGFIX'
851
+ }
852
+ };
853
+ }
854
+
855
+ // Status inquiries → product-tracking skill
856
+ if (intentName === 'status_update') {
857
+ return {
858
+ handler: 'product-tracking',
859
+ skill: 'product-tracking',
860
+ context: {
861
+ intent: intentName,
862
+ feature_filter: entities.feature_id || null,
863
+ domain_filter: entities.domain_id || null
864
+ }
865
+ };
866
+ }
867
+
868
+ // Validation requests → validation skill
869
+ if (/test|validate|check/i.test(intentName)) {
870
+ return {
871
+ handler: 'validation',
872
+ skill: 'validation',
873
+ context: {
874
+ intent: intentName,
875
+ scope: entities.feature_id ? 'feature' : 'all'
876
+ }
877
+ };
878
+ }
879
+
880
+ // Decision handling → decision-handler
881
+ if (intentName === 'decision' || /decide|choice|option/i.test(intentName)) {
882
+ return {
883
+ handler: 'decision-handler',
884
+ skill: null,
885
+ context: {
886
+ intent: intentName
887
+ }
888
+ };
889
+ }
890
+
891
+ // Product planning → product-planning skill
892
+ if (/plan|requirements|spec|analyze/i.test(intentName)) {
893
+ return {
894
+ handler: 'product-planning',
895
+ skill: 'product-planning',
896
+ context: {
897
+ intent: intentName
898
+ }
899
+ };
900
+ }
901
+
902
+ // Approval/continue → orchestrator (resume current work)
903
+ if (intentName === 'approval' || intentName === 'continue') {
904
+ return {
905
+ handler: 'orchestrator',
906
+ skill: null,
907
+ context: {
908
+ intent: 'continue',
909
+ message: 'User approved or wants to continue current work',
910
+ resume_feature: state.current_feature
911
+ }
912
+ };
913
+ }
914
+
915
+ // Rejection/stop → update state, don't delegate
916
+ if (intentName === 'rejection' || intentName === 'pause') {
917
+ return {
918
+ handler: 'inline',
919
+ skill: null,
920
+ context: {
921
+ intent: intentName,
922
+ action: 'pause_work'
923
+ }
924
+ };
925
+ }
926
+
927
+ // Questions/clarifications → handle inline
928
+ if (intentName === 'question' || intentName === 'clarification') {
929
+ return {
930
+ handler: 'inline',
931
+ skill: null,
932
+ context: {
933
+ intent: intentName,
934
+ answer_from: ['conversation_history', 'product_spec', 'completion_status']
935
+ }
936
+ };
937
+ }
938
+
939
+ // Default: handle inline or ask for clarification
940
+ return {
941
+ handler: 'inline',
942
+ skill: null,
943
+ context: {
944
+ intent: intentName,
945
+ needs_clarification: true
946
+ }
947
+ };
948
+ }
949
+
950
+ interface RoutingDecision {
951
+ handler: 'orchestrator' | 'product-tracking' | 'validation' | 'decision-handler' | 'product-planning' | 'inline';
952
+ skill: string | null; // Skill name for Task delegation
953
+ context: {
954
+ intent: string;
955
+ message?: string;
956
+ feature_id?: string;
957
+ domain_id?: string;
958
+ work_type?: string;
959
+ [key: string]: any;
960
+ };
961
+ }
962
+ ```
963
+
964
+ ### Execute Routing
965
+
966
+ ```typescript
967
+ /**
968
+ * Execute the routing decision
969
+ * @param routing - Routing decision from route_to_handler
970
+ * @param userMessage - Original user message
971
+ * @param resolved - Resolved message with references expanded
972
+ */
973
+ function execute_routing(
974
+ routing: RoutingDecision,
975
+ userMessage: string,
976
+ resolved: ResolvedMessage
977
+ ): void {
978
+ switch (routing.handler) {
979
+ case 'orchestrator':
980
+ // Delegate to orchestrator agent
981
+ Task('orchestrator',
982
+ `${routing.context.message || userMessage}
983
+
984
+ Work Type: ${routing.context.work_type || 'FEATURE_DEVELOPMENT'}
985
+ ${routing.context.feature_id ? `Feature: ${routing.context.feature_id}` : ''}
986
+ ${routing.context.domain_id ? `Domain: ${routing.context.domain_id}` : ''}
987
+
988
+ Classify and execute appropriate workflow.`
989
+ );
990
+ break;
991
+
992
+ case 'product-tracking':
993
+ // Delegate to product-tracking skill
994
+ Task('product-tracking',
995
+ `Show status and progress.
996
+ ${routing.context.feature_filter ? `Filter: feature ${routing.context.feature_filter}` : ''}
997
+ ${routing.context.domain_filter ? `Filter: domain ${routing.context.domain_filter}` : ''}`
998
+ );
999
+ break;
1000
+
1001
+ case 'validation':
1002
+ // Delegate to validation skill
1003
+ Task('validation',
1004
+ `Run quality gates.
1005
+ Scope: ${routing.context.scope || 'all'}`
1006
+ );
1007
+ break;
1008
+
1009
+ case 'decision-handler':
1010
+ // For now, show how to use /agentful-decide
1011
+ return `You have pending decisions. Run \`/agentful-decide\` to review and resolve them.`;
1012
+ break;
1013
+
1014
+ case 'product-planning':
1015
+ // Delegate to product-planning skill
1016
+ Task('product-planning', userMessage);
1017
+ break;
1018
+
1019
+ case 'inline':
1020
+ // Handle inline (no delegation needed)
1021
+ if (routing.context.needs_clarification) {
1022
+ return generate_clarification_response(userMessage, routing.context);
1023
+ } else if (routing.context.action === 'pause_work') {
1024
+ pause_current_work();
1025
+ return 'Work paused. Run `/agentful` with your next request when ready to continue.';
1026
+ } else if (routing.context.intent === 'question') {
1027
+ return answer_question(userMessage, routing.context);
1028
+ }
1029
+ break;
1030
+ }
1031
+ }
1032
+ ```
1033
+
799
1034
  ## Integration with Orchestrator
800
1035
 
801
1036
  ### Delegation Interface
@@ -1037,9 +1272,9 @@ interface StaleContextResponse {
1037
1272
  // Complete conversation processing flow
1038
1273
  async function process_conversation(userMessage: string): Promise<ConversationResponse> {
1039
1274
  // 1. Load conversation state and history
1040
- const state = load_conversation_state();
1041
- const history = read_conversation_history();
1042
- const productSpec = load_product_spec();
1275
+ const state = load_conversation_state('.agentful/conversation-state.json');
1276
+ const history = read_conversation_history('.agentful/conversation-history.json');
1277
+ const productSpec = load_product_spec('.claude/product/');
1043
1278
 
1044
1279
  // 2. Check for context loss
1045
1280
  const contextRecovery = detect_context_loss(history.messages, state);
@@ -1079,8 +1314,8 @@ async function process_conversation(userMessage: string): Promise<ConversationRe
1079
1314
  };
1080
1315
  }
1081
1316
 
1082
- // 8. Determine delegation
1083
- const delegation = determine_delegation(intent, entities);
1317
+ // 8. Route to appropriate handler
1318
+ const routing = route_to_handler(intent, entities, state);
1084
1319
 
1085
1320
  // 9. Add user message to history
1086
1321
  add_message_to_history({
@@ -1089,24 +1324,66 @@ async function process_conversation(userMessage: string): Promise<ConversationRe
1089
1324
  intent: intent.intent,
1090
1325
  entities: entities,
1091
1326
  references_resolved: resolved.references
1092
- });
1093
-
1094
- // 10. Return delegation decision or response
1095
- if (delegation.should_delegate) {
1096
- return {
1097
- type: 'delegate',
1098
- target_skill: delegation.target_skill,
1099
- context: delegation.context
1327
+ }, '.agentful/conversation-history.json');
1328
+
1329
+ // 10. Execute routing
1330
+ execute_routing(routing, userMessage, resolved);
1331
+
1332
+ // 11. Update conversation state
1333
+ state.last_message_time = new Date().toISOString();
1334
+ state.message_count++;
1335
+ if (entities.feature_id) {
1336
+ state.current_feature = {
1337
+ feature_id: entities.feature_id,
1338
+ feature_name: entities.feature_name,
1339
+ domain_id: entities.domain_id
1100
1340
  };
1101
1341
  }
1342
+ save_conversation_state(state);
1102
1343
 
1103
1344
  return {
1104
- type: 'response',
1105
- message: generate_response(intent, entities, state)
1345
+ type: 'routed',
1346
+ handler: routing.handler,
1347
+ context: routing.context
1106
1348
  };
1107
1349
  }
1108
1350
  ```
1109
1351
 
1352
+ ### Complete Flow Diagram
1353
+
1354
+ ```
1355
+ User Input
1356
+
1357
+ Load State & History
1358
+
1359
+ Context Loss Check ──→ [STALE] ──→ Confirm & Resume
1360
+ ↓ [FRESH]
1361
+ Mind Change Detection ──→ [DETECTED] ──→ Reset Context
1362
+ ↓ [CONTINUE]
1363
+ Reference Resolution
1364
+
1365
+ Intent Classification
1366
+
1367
+ Entity Extraction
1368
+
1369
+ Ambiguity Detection ──→ [AMBIGUOUS] ──→ Ask Clarifying Question
1370
+ ↓ [CLEAR]
1371
+ Route to Handler
1372
+
1373
+ ├─→ [orchestrator] ──→ Task('orchestrator', context)
1374
+ ├─→ [product-tracking] ──→ Task('product-tracking', context)
1375
+ ├─→ [validation] ──→ Task('validation', context)
1376
+ ├─→ [decision-handler] ──→ Show /agentful-decide
1377
+ ├─→ [product-planning] ──→ Task('product-planning', context)
1378
+ └─→ [inline] ──→ Generate Response Directly
1379
+
1380
+ Add to History
1381
+
1382
+ Update State
1383
+
1384
+ Done
1385
+ ```
1386
+
1110
1387
  ## File Locations
1111
1388
 
1112
1389
  ```
package/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  # agentful
2
2
 
3
- Autonomous product development framework for Claude Code.
3
+ Human-in-the-loop development framework for Claude Code.
4
4
 
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
  [![npm version](https://badge.fury.io/js/%40itz4blitz%2Fagentful.svg)](https://www.npmjs.com/package/@itz4blitz/agentful)
7
7
 
8
8
  ## Overview
9
9
 
10
- agentful is a Claude Code configuration that provides structured autonomous development through specialized AI agents. It coordinates multiple agents to implement features, write tests, and validate code quality according to a defined product specification.
10
+ agentful is a Claude Code configuration that provides structured development through specialized AI agents. It coordinates multiple agents to implement features, write tests, and validate code quality according to a defined product specification, with human checkpoints for key decisions.
11
11
 
12
12
  ## Installation
13
13
 
@@ -53,7 +53,7 @@ Create your specification manually:
53
53
  claude # Start Claude Code
54
54
  ```
55
55
 
56
- Then use the `/agentful-start` command to begin autonomous development.
56
+ Then use the `/agentful-start` command to begin structured development.
57
57
 
58
58
  #### New Projects (No Existing Code)
59
59
 
@@ -135,7 +135,7 @@ Code changes are validated against:
135
135
 
136
136
  ### State Tracking
137
137
 
138
- Runtime state is stored in `.agentful/`:
138
+ Runtime state is stored in `.agentful/` (gitignored, managed by npm package):
139
139
 
140
140
  - `state.json` - Current task and phase
141
141
  - `completion.json` - Feature completion status
@@ -144,13 +144,28 @@ Runtime state is stored in `.agentful/`:
144
144
  - New projects: Starts with declared stack (`confidence: 0.4`)
145
145
  - Existing projects: Detected from code (`confidence: 0.8-1.0`)
146
146
  - Re-analyzed after first implementation in new projects
147
+ - `last-validation.json` - Latest test/lint results
148
+ - `conversation-history.json` - Session tracking
149
+
150
+ User configuration is stored in `.claude/` (version controlled):
151
+
152
+ - `agents/` - Agent definitions (core + custom + ephemeral)
153
+ - `commands/` - Slash commands
154
+ - `product/` - Product specifications
155
+ - `index.md` - Main product spec (user editable)
156
+ - `product-analysis.json` - Readiness analysis (generated by `/agentful-product`)
157
+ - `domains/` - Optional hierarchical structure
158
+ - `skills/` - Reusable skill modules
159
+ - `settings.json` - Project configuration
160
+
161
+ **See [ARCHITECTURE.md](./ARCHITECTURE.md) for detailed explanation of file organization.**
147
162
 
148
163
  ## Commands
149
164
 
150
165
  | Command | Description |
151
166
  |---------|-------------|
152
167
  | `/agentful-product` | Smart product planning: create, analyze, and refine requirements |
153
- | `/agentful-start` | Start or resume autonomous development |
168
+ | `/agentful-start` | Start or resume structured development |
154
169
  | `/agentful-status` | Display progress and current state |
155
170
  | `/agentful-validate` | Run all quality checks |
156
171
  | `/agentful-decide` | Answer pending decisions |
@@ -188,11 +203,13 @@ your-project/
188
203
  │ ├── commands/ # Slash commands
189
204
  │ ├── skills/ # Reusable skills
190
205
  │ └── settings.json # Configuration
191
- ├── .agentful/ # Runtime state
206
+ ├── .agentful/ # Runtime state (gitignored)
192
207
  │ ├── state.json
193
208
  │ ├── completion.json
194
209
  │ ├── decisions.json
195
- └── architecture.json
210
+ ├── architecture.json
211
+ │ ├── last-validation.json
212
+ │ └── conversation-history.json
196
213
  └── src/ # Source code
197
214
  ```
198
215
 
package/bin/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  /**
4
4
  * agentful CLI
5
- * An opinionated autonomous product development kit for Claude Code
5
+ * An opinionated human-in-the-loop product development kit for Claude Code
6
6
  */
7
7
 
8
8
  import fs from 'fs';
@@ -11,6 +11,7 @@ import { fileURLToPath } from 'url';
11
11
  import { analyzeProject, exportToArchitectureJson } from '../lib/project-analyzer.js';
12
12
  import AgentGenerator from '../lib/agent-generator.js';
13
13
  import DomainStructureGenerator from '../lib/domain-structure-generator.js';
14
+ import { detectTechStack } from '../lib/tech-stack-detector.js';
14
15
 
15
16
  const __filename = fileURLToPath(import.meta.url);
16
17
  const __dirname = path.dirname(__filename);
@@ -74,9 +75,9 @@ function showHelp() {
74
75
  console.log(` ${colors.dim}(Detection based on: # of domains, frameworks, monorepo status)${colors.reset}`);
75
76
  console.log(` 2. ${colors.bright}Edit your product specification${colors.reset}`);
76
77
  console.log(` 3. Run ${colors.bright}claude${colors.reset} to start Claude Code`);
77
- console.log(` 4. Type ${colors.bright}/agentful${colors.reset} for natural conversation or ${colors.bright}/agentful-start${colors.reset} for autonomous development`);
78
+ console.log(` 4. Type ${colors.bright}/agentful${colors.reset} for natural conversation or ${colors.bright}/agentful-start${colors.reset} for structured development`);
78
79
  console.log('');
79
- console.log('FOR 24/7 DEVELOPMENT:');
80
+ console.log('FOR EXTENDED DEVELOPMENT SESSIONS:');
80
81
  console.log(` ${colors.cyan}/ralph-loop "/agentful-start" --max-iterations 50 --completion-promise "AGENTFUL_COMPLETE"${colors.reset}`);
81
82
  console.log('');
82
83
  }
@@ -442,7 +443,7 @@ ${analysis && analysis.domains.length > 0 ? analysis.domains.map((d, i) => `${i
442
443
  }
443
444
 
444
445
  console.log(` 2. ${colors.cyan}Run: claude${colors.reset}`);
445
- console.log(` 3. ${colors.cyan}Type: /agentful${colors.reset} (natural) or ${colors.cyan}/agentful-start${colors.reset} (autonomous)`);
446
+ console.log(` 3. ${colors.cyan}Type: /agentful${colors.reset} (natural) or ${colors.cyan}/agentful-start${colors.reset} (structured)`);
446
447
  console.log('');
447
448
 
448
449
  if (usingHierarchical) {
@@ -453,7 +454,7 @@ ${analysis && analysis.domains.length > 0 ? analysis.domains.map((d, i) => `${i
453
454
  console.log('');
454
455
  }
455
456
 
456
- log(colors.dim, 'For autonomous 24/7 development:');
457
+ log(colors.dim, 'For extended development sessions with fewer interruptions:');
457
458
  log(colors.cyan, ` /ralph-loop "/agentful-start" --max-iterations 50 --completion-promise "AGENTFUL_COMPLETE"`);
458
459
  console.log('');
459
460
  }
@@ -582,87 +583,13 @@ function showStatus() {
582
583
  console.log('');
583
584
  }
584
585
 
585
- function detectTechStack() {
586
- const targetDir = process.cwd();
587
- const detected = {
588
- language: null,
589
- framework: null,
590
- dependencies: [],
591
- devDependencies: []
592
- };
593
-
594
- // Check for package.json (Node.js/JavaScript/TypeScript)
595
- const packageJsonPath = path.join(targetDir, 'package.json');
596
- if (fs.existsSync(packageJsonPath)) {
597
- try {
598
- const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
599
- detected.dependencies = Object.keys(pkg.dependencies || {});
600
- detected.devDependencies = Object.keys(pkg.devDependencies || {});
601
- detected.language = pkg.type === 'module' ? 'TypeScript/ESM' : 'JavaScript/TypeScript';
602
-
603
- // Detect framework
604
- if (detected.dependencies.includes('next')) {
605
- detected.framework = 'Next.js';
606
- } else if (detected.dependencies.includes('react')) {
607
- detected.framework = 'React';
608
- } else if (detected.dependencies.includes('vue')) {
609
- detected.framework = 'Vue';
610
- } else if (detected.dependencies.includes('express')) {
611
- detected.framework = 'Express';
612
- } else if (detected.dependencies.includes('nestjs')) {
613
- detected.framework = 'NestJS';
614
- }
615
- } catch (err) {
616
- log(colors.yellow, '⚠️ Could not parse package.json');
617
- }
618
- }
619
-
620
- // Check for requirements.txt or pyproject.toml (Python)
621
- const requirementsPath = path.join(targetDir, 'requirements.txt');
622
- const pyprojectPath = path.join(targetDir, 'pyproject.toml');
623
- if (fs.existsSync(requirementsPath) || fs.existsSync(pyprojectPath)) {
624
- detected.language = 'Python';
625
- const requirements = fs.existsSync(requirementsPath)
626
- ? fs.readFileSync(requirementsPath, 'utf-8')
627
- : '';
628
- if (requirements.includes('django')) detected.framework = 'Django';
629
- else if (requirements.includes('flask')) detected.framework = 'Flask';
630
- else if (requirements.includes('fastapi')) detected.framework = 'FastAPI';
631
- }
632
-
633
- // Check for go.mod (Go)
634
- if (fs.existsSync(path.join(targetDir, 'go.mod'))) {
635
- detected.language = 'Go';
636
- detected.framework = 'Standard Library';
637
- }
638
-
639
- // Check for Cargo.toml (Rust)
640
- if (fs.existsSync(path.join(targetDir, 'Cargo.toml'))) {
641
- detected.language = 'Rust';
642
- }
643
-
644
- // Check for .csproj or .fsproj (C#/.NET)
645
- const csprojFiles = fs.readdirSync(targetDir).filter(f => f.endsWith('.csproj'));
646
- if (csprojFiles.length > 0) {
647
- detected.language = 'C#';
648
- detected.framework = 'ASP.NET';
649
- }
650
-
651
- // Check for pom.xml (Java)
652
- if (fs.existsSync(path.join(targetDir, 'pom.xml'))) {
653
- detected.language = 'Java';
654
- detected.framework = 'Maven';
655
- }
656
-
657
- return detected;
658
- }
659
-
660
586
  function generateAgentPrompt(stack) {
661
587
  let prompt = `# Tech Stack Analysis\n\n`;
662
588
  prompt += `**Language**: ${stack.language || 'Unknown'}\n`;
663
- prompt += `**Framework**: ${stack.framework || 'None'}\n\n`;
589
+ const primaryFramework = stack.frameworks && stack.frameworks.length > 0 ? stack.frameworks[0] : null;
590
+ prompt += `**Framework**: ${primaryFramework || 'None'}\n\n`;
664
591
 
665
- if (stack.dependencies.length > 0) {
592
+ if (stack.dependencies && stack.dependencies.length > 0) {
666
593
  prompt += `**Key Dependencies**:\n`;
667
594
  stack.dependencies.slice(0, 10).forEach(dep => {
668
595
  prompt += `- ${dep}\n`;
@@ -724,11 +651,11 @@ async function generateAgents() {
724
651
  process.exit(1);
725
652
  }
726
653
 
727
- // Detect tech stack
654
+ // Detect tech stack using library function
728
655
  log(colors.dim, 'Analyzing tech stack...');
729
- const stack = detectTechStack();
656
+ const stack = await detectTechStack(process.cwd());
730
657
 
731
- if (!stack.language) {
658
+ if (!stack.language || stack.language === 'unknown') {
732
659
  log(colors.yellow, '⚠️ Could not detect language/framework');
733
660
  log(colors.dim, 'Supported: Node.js, Python, Go, Rust, C#, Java');
734
661
  console.log('');
@@ -739,7 +666,8 @@ async function generateAgents() {
739
666
  return;
740
667
  }
741
668
 
742
- log(colors.green, `✓ Detected: ${stack.language} ${stack.framework ? `(${stack.framework})` : ''}`);
669
+ const primaryFramework = stack.frameworks.length > 0 ? stack.frameworks[0] : null;
670
+ log(colors.green, `✓ Detected: ${stack.language} ${primaryFramework ? `(${primaryFramework})` : ''}`);
743
671
  log(colors.dim, ` Dependencies: ${stack.dependencies.length} packages`);
744
672
 
745
673
  // Update architecture.json
@@ -762,7 +690,7 @@ async function generateAgents() {
762
690
 
763
691
  log(colors.bright, 'Detected Stack:');
764
692
  if (stack.language) log(colors.cyan, ` Language: ${stack.language}`);
765
- if (stack.framework) log(colors.cyan, ` Framework: ${stack.framework}`);
693
+ if (primaryFramework) log(colors.cyan, ` Framework: ${primaryFramework}`);
766
694
  if (stack.dependencies.length > 0) {
767
695
  log(colors.cyan, ` Dependencies: ${stack.dependencies.length} packages`);
768
696
  }