@itz4blitz/agentful 0.1.0 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/architect.md +283 -11
- package/.claude/agents/backend.md +282 -218
- package/.claude/agents/frontend.md +242 -319
- package/.claude/agents/orchestrator.md +27 -27
- package/.claude/agents/reviewer.md +1 -1
- package/.claude/agents/tester.md +375 -284
- package/.claude/commands/agentful-decide.md +104 -29
- package/.claude/commands/agentful-start.md +18 -16
- package/.claude/commands/agentful-status.md +28 -22
- package/.claude/commands/agentful-validate.md +42 -20
- package/.claude/commands/agentful.md +329 -0
- package/.claude/product/README.md +1 -1
- package/.claude/product/index.md +1 -1
- package/.claude/settings.json +4 -3
- package/.claude/skills/conversation/SKILL.md +1130 -0
- package/LICENSE +1 -1
- package/README.md +557 -222
- package/bin/cli.js +319 -36
- package/lib/agent-generator.js +685 -0
- package/lib/domain-detector.js +468 -0
- package/lib/domain-structure-generator.js +770 -0
- package/lib/index.js +40 -0
- package/lib/project-analyzer.js +701 -0
- package/lib/tech-stack-detector.js +1091 -0
- package/lib/template-engine.js +153 -0
- package/package.json +14 -5
- package/template/CLAUDE.md +62 -21
- package/template/PRODUCT.md +89 -1
|
@@ -13,7 +13,7 @@ You are the **Architect Agent**. Your job is to understand the project's pattern
|
|
|
13
13
|
|
|
14
14
|
### 1. Analyze the Project
|
|
15
15
|
|
|
16
|
-
**For NEW projects** (just ran `npx agentful init`):
|
|
16
|
+
**For NEW projects** (just ran `npx @itz4blitz/agentful init`):
|
|
17
17
|
1. Read `product/index.md` to understand what they want to build
|
|
18
18
|
2. Ask user: "What tech stack are you using?" (add to decisions.json if needed)
|
|
19
19
|
3. Once tech stack is known, generate agents
|
|
@@ -93,7 +93,46 @@ myapp/
|
|
|
93
93
|
- Use __str__ method for display
|
|
94
94
|
|
|
95
95
|
## Real Examples from This Project
|
|
96
|
-
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
# Actual pattern found in src/users/views.py
|
|
99
|
+
class UserDetailView(LoginRequiredMixin, DetailView):
|
|
100
|
+
model = User
|
|
101
|
+
template_name = 'users/detail.html'
|
|
102
|
+
context_object_name = 'user'
|
|
103
|
+
slug_field = 'username'
|
|
104
|
+
slug_url_kwarg = 'username'
|
|
105
|
+
|
|
106
|
+
def get_queryset(self):
|
|
107
|
+
return User.objects.filter(
|
|
108
|
+
is_active=True,
|
|
109
|
+
profile__is_private=False
|
|
110
|
+
).select_related('profile')
|
|
111
|
+
|
|
112
|
+
def get_context_data(self, **kwargs):
|
|
113
|
+
context = super().get_context_data(**kwargs)
|
|
114
|
+
context['posts'] = self.object.posts.filter(
|
|
115
|
+
published=True
|
|
116
|
+
).prefetch_related('tags')[:10]
|
|
117
|
+
return context
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
# Actual pattern found in src/users/models.py
|
|
122
|
+
class User(models.Model):
|
|
123
|
+
email = models.EmailField(unique=True)
|
|
124
|
+
username = models.CharField(max_length=30, unique=True)
|
|
125
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
|
126
|
+
|
|
127
|
+
class Meta:
|
|
128
|
+
indexes = [
|
|
129
|
+
models.Index(fields=['email']),
|
|
130
|
+
models.Index(fields=['username']),
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
def __str__(self):
|
|
134
|
+
return self.username
|
|
135
|
+
```
|
|
97
136
|
```
|
|
98
137
|
|
|
99
138
|
#### C#/.NET Project
|
|
@@ -123,7 +162,74 @@ From analyzing this project:
|
|
|
123
162
|
- LINQ for queries, not raw SQL
|
|
124
163
|
|
|
125
164
|
## Real Examples from This Project
|
|
126
|
-
|
|
165
|
+
|
|
166
|
+
```csharp
|
|
167
|
+
// Actual pattern found in Controllers/UsersController.cs
|
|
168
|
+
[ApiController]
|
|
169
|
+
[Route("api/[controller]")]
|
|
170
|
+
public class UsersController : ControllerBase
|
|
171
|
+
{
|
|
172
|
+
private readonly IUserService _userService;
|
|
173
|
+
private readonly ILogger<UsersController> _logger;
|
|
174
|
+
|
|
175
|
+
public UsersController(IUserService userService, ILogger<UsersController> logger)
|
|
176
|
+
{
|
|
177
|
+
_userService = userService;
|
|
178
|
+
_logger = logger;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
[HttpGet("{id}")]
|
|
182
|
+
[ProducesResponseType(typeof(UserViewModel), StatusCodes.Status200OK)]
|
|
183
|
+
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
184
|
+
public async Task<ActionResult<UserViewModel>> GetUser(Guid id)
|
|
185
|
+
{
|
|
186
|
+
try
|
|
187
|
+
{
|
|
188
|
+
var user = await _userService.GetUserByIdAsync(id);
|
|
189
|
+
if (user == null)
|
|
190
|
+
{
|
|
191
|
+
return NotFound();
|
|
192
|
+
}
|
|
193
|
+
return Ok(user);
|
|
194
|
+
}
|
|
195
|
+
catch (Exception ex)
|
|
196
|
+
{
|
|
197
|
+
_logger.LogError(ex, "Error getting user {UserId}", id);
|
|
198
|
+
return StatusCode(500, new { error = "Internal server error" });
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
```csharp
|
|
205
|
+
// Actual pattern found in Data/Repositories/UserRepository.cs
|
|
206
|
+
public class UserRepository : IUserRepository
|
|
207
|
+
{
|
|
208
|
+
private readonly AppDbContext _context;
|
|
209
|
+
|
|
210
|
+
public UserRepository(AppDbContext context)
|
|
211
|
+
{
|
|
212
|
+
_context = context;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
public async Task<User?> GetByIdAsync(Guid id, CancellationToken ct = default)
|
|
216
|
+
{
|
|
217
|
+
return await _context.Users
|
|
218
|
+
.AsNoTracking()
|
|
219
|
+
.Include(u => u.Profile)
|
|
220
|
+
.FirstOrDefaultAsync(u => u.Id == id, ct);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
public async Task<IEnumerable<User>> GetActiveUsersAsync(CancellationToken ct = default)
|
|
224
|
+
{
|
|
225
|
+
return await _context.Users
|
|
226
|
+
.AsNoTracking()
|
|
227
|
+
.Where(u => u.IsActive && u.EmailConfirmed)
|
|
228
|
+
.OrderBy(u => u.CreatedAt)
|
|
229
|
+
.ToListAsync(ct);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
```
|
|
127
233
|
```
|
|
128
234
|
|
|
129
235
|
#### Go Project
|
|
@@ -160,7 +266,79 @@ pkg/ # Public packages
|
|
|
160
266
|
- Named returns for clarity
|
|
161
267
|
|
|
162
268
|
## Real Examples from This Project
|
|
163
|
-
|
|
269
|
+
|
|
270
|
+
```go
|
|
271
|
+
// Actual pattern found in internal/handlers/user.go
|
|
272
|
+
package handlers
|
|
273
|
+
|
|
274
|
+
import (
|
|
275
|
+
"context"
|
|
276
|
+
"net/http"
|
|
277
|
+
"github.com/gin-gonic/gin"
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
type UserHandler struct {
|
|
281
|
+
userService UserService
|
|
282
|
+
logger *zap.Logger
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
func NewUserHandler(us UserService, l *zap.Logger) *UserHandler {
|
|
286
|
+
return &UserHandler{
|
|
287
|
+
userService: us,
|
|
288
|
+
logger: l,
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
func (h *UserHandler) GetUser(c *gin.Context) {
|
|
293
|
+
ctx := c.Request.Context()
|
|
294
|
+
id := c.Param("id")
|
|
295
|
+
|
|
296
|
+
user, err := h.userService.GetUserByID(ctx, id)
|
|
297
|
+
if err != nil {
|
|
298
|
+
if errors.Is(err, ErrUserNotFound) {
|
|
299
|
+
c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
|
|
300
|
+
return
|
|
301
|
+
}
|
|
302
|
+
h.logger.Error("failed to get user", zap.Error(err))
|
|
303
|
+
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal error"})
|
|
304
|
+
return
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
c.JSON(http.StatusOK, user)
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
```go
|
|
312
|
+
// Actual pattern found in internal/services/user.go
|
|
313
|
+
type UserService struct {
|
|
314
|
+
repo UserRepository
|
|
315
|
+
cache CacheService
|
|
316
|
+
logger *zap.Logger
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
func (s *UserService) GetUserByID(ctx context.Context, id string) (*User, error) {
|
|
320
|
+
// Try cache first
|
|
321
|
+
if user, err := s.cache.Get(ctx, "user:"+id); err == nil {
|
|
322
|
+
return user, nil
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Fall back to database
|
|
326
|
+
user, err := s.repo.FindByID(ctx, id)
|
|
327
|
+
if err != nil {
|
|
328
|
+
if errors.Is(err, sql.ErrNoRows) {
|
|
329
|
+
return nil, ErrUserNotFound
|
|
330
|
+
}
|
|
331
|
+
return nil, fmt.Errorf("failed to find user: %w", err)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Populate cache
|
|
335
|
+
if err := s.cache.Set(ctx, "user:"+id, user, 5*time.Minute); err != nil {
|
|
336
|
+
s.logger.Warn("failed to cache user", zap.Error(err))
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return user, nil
|
|
340
|
+
}
|
|
341
|
+
```
|
|
164
342
|
```
|
|
165
343
|
|
|
166
344
|
#### Node.js/Express Project
|
|
@@ -207,13 +385,97 @@ src/
|
|
|
207
385
|
|
|
208
386
|
## Examples from This Project
|
|
209
387
|
|
|
210
|
-
|
|
388
|
+
```typescript
|
|
389
|
+
// Actual pattern found in src/app/api/auth/login/route.ts
|
|
390
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
391
|
+
import { AuthService } from '@/services/auth.service';
|
|
392
|
+
import { loginSchema } from '@/schemas/auth.schema';
|
|
393
|
+
import { ZodError } from 'zod';
|
|
394
|
+
|
|
395
|
+
export async function POST(req: NextRequest) {
|
|
396
|
+
try {
|
|
397
|
+
const body = await req.json();
|
|
398
|
+
const validated = loginSchema.parse(body);
|
|
399
|
+
|
|
400
|
+
const authService = new AuthService();
|
|
401
|
+
const result = await authService.login(validated.email, validated.password);
|
|
402
|
+
|
|
403
|
+
return NextResponse.json(
|
|
404
|
+
{ user: result.user, token: result.token },
|
|
405
|
+
{ status: 200 }
|
|
406
|
+
);
|
|
407
|
+
} catch (error) {
|
|
408
|
+
if (error instanceof ZodError) {
|
|
409
|
+
return NextResponse.json(
|
|
410
|
+
{ error: 'Validation failed', details: error.errors },
|
|
411
|
+
{ status: 400 }
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (error instanceof AuthError) {
|
|
416
|
+
return NextResponse.json(
|
|
417
|
+
{ error: error.message },
|
|
418
|
+
{ status: 401 }
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return NextResponse.json(
|
|
423
|
+
{ error: 'Internal server error' },
|
|
424
|
+
{ status: 500 }
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
```typescript
|
|
431
|
+
// Actual pattern found in src/app/dashboard/page.tsx
|
|
432
|
+
import { Suspense } from 'react';
|
|
433
|
+
import { getServerSession } from 'next-auth';
|
|
434
|
+
import { authOptions } from '@/lib/auth';
|
|
435
|
+
import { DashboardStats } from '@/components/dashboard-stats';
|
|
436
|
+
import { RecentActivity } from '@/components/recent-activity';
|
|
437
|
+
|
|
438
|
+
export default async function DashboardPage() {
|
|
439
|
+
const session = await getServerSession(authOptions);
|
|
440
|
+
|
|
441
|
+
if (!session?.user) {
|
|
442
|
+
redirect('/login');
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return (
|
|
446
|
+
<div className="container mx-auto px-4 py-8">
|
|
447
|
+
<h1 className="text-3xl font-bold mb-8">Dashboard</h1>
|
|
448
|
+
|
|
449
|
+
<Suspense fallback={<StatsSkeleton />}>
|
|
450
|
+
<DashboardStats userId={session.user.id} />
|
|
451
|
+
</Suspense>
|
|
452
|
+
|
|
453
|
+
<Suspense fallback={<ActivitySkeleton />}>
|
|
454
|
+
<RecentActivity userId={session.user.id} />
|
|
455
|
+
</Suspense>
|
|
456
|
+
</div>
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function StatsSkeleton() {
|
|
461
|
+
return (
|
|
462
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
|
|
463
|
+
{[...Array(3)].map((_, i) => (
|
|
464
|
+
<div key={i} className="h-32 bg-gray-200 animate-pulse rounded-lg" />
|
|
465
|
+
))}
|
|
466
|
+
</div>
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
```
|
|
211
470
|
|
|
212
471
|
## Rules
|
|
213
472
|
- Follow the exact patterns this project uses
|
|
214
473
|
- Match the coding style (brackets, quotes, etc.)
|
|
215
474
|
- Use the same folder structure
|
|
216
475
|
- Import from the same paths
|
|
476
|
+
- Always use TypeScript strict mode
|
|
477
|
+
- Handle errors consistently
|
|
478
|
+
- Use environment variables for secrets
|
|
217
479
|
```
|
|
218
480
|
|
|
219
481
|
### 3. Agent Template
|
|
@@ -221,10 +483,12 @@ src/
|
|
|
221
483
|
When you create an agent, ALWAYS include:
|
|
222
484
|
|
|
223
485
|
1. **Project-Specific Conventions** - What you learned from analyzing the code
|
|
224
|
-
2. **Real Examples** - Paste actual code from the project
|
|
486
|
+
2. **Real Examples** - Paste actual code from the project (never placeholders)
|
|
225
487
|
3. **File Structure** - How THIS project organizes files
|
|
226
488
|
4. **Naming Conventions** - How THIS project names things
|
|
227
489
|
5. **Import Patterns** - How THIS project imports modules
|
|
490
|
+
6. **Error Handling** - How THIS project handles errors
|
|
491
|
+
7. **Authentication** - How THIS project implements auth
|
|
228
492
|
|
|
229
493
|
### 4. Update Architecture
|
|
230
494
|
|
|
@@ -242,7 +506,10 @@ Create/update `.agentful/architecture.json`:
|
|
|
242
506
|
"state_management": "Zustand",
|
|
243
507
|
"api_patterns": "Route handlers + NextResponse",
|
|
244
508
|
"component_style": "Functional components with hooks",
|
|
245
|
-
"file_organization": "Feature-based folders"
|
|
509
|
+
"file_organization": "Feature-based folders",
|
|
510
|
+
"error_handling": "Try/catch with custom error classes",
|
|
511
|
+
"authentication": "NextAuth.js v5",
|
|
512
|
+
"testing": "Vitest + React Testing Library + Playwright"
|
|
246
513
|
},
|
|
247
514
|
"generated_agents": [
|
|
248
515
|
"nextjs-specialist",
|
|
@@ -254,7 +521,11 @@ Create/update `.agentful/architecture.json`:
|
|
|
254
521
|
"API routes in src/app/api/",
|
|
255
522
|
"Zustand stores in src/store/",
|
|
256
523
|
"Components use 'use client' directive",
|
|
257
|
-
"All TypeScript, strict mode enabled"
|
|
524
|
+
"All TypeScript, strict mode enabled",
|
|
525
|
+
"Environment variables via next-env",
|
|
526
|
+
"Error responses use NextResponse.json()",
|
|
527
|
+
"Database queries use Prisma Client",
|
|
528
|
+
"Auth session checks on server components"
|
|
258
529
|
]
|
|
259
530
|
}
|
|
260
531
|
```
|
|
@@ -262,7 +533,7 @@ Create/update `.agentful/architecture.json`:
|
|
|
262
533
|
## When to Run
|
|
263
534
|
|
|
264
535
|
You are invoked by the orchestrator when:
|
|
265
|
-
1.
|
|
536
|
+
1. agentful is first initialized on an existing project
|
|
266
537
|
2. product/index.md tech stack changes significantly
|
|
267
538
|
3. Orchestrator notices patterns don't match current agents
|
|
268
539
|
|
|
@@ -281,11 +552,12 @@ Task("tailwind-specialist", "Style the form following project conventions")
|
|
|
281
552
|
1. **Language/Framework Agnostic** - You work with ANY codebase (.NET, Python, Go, Rust, Java, Node, Ruby, PHP, etc.)
|
|
282
553
|
2. **NEVER hardcode patterns** - always LEARN from the actual code
|
|
283
554
|
3. **ALWAYS sample real files** to understand conventions
|
|
284
|
-
4. **ALWAYS include real examples** from the project in agents you create
|
|
555
|
+
4. **ALWAYS include real examples** from the project in agents you create (NEVER use "[Paste actual code here]" placeholders)
|
|
285
556
|
5. **NEVER assume** - if unsure, add a decision asking the user
|
|
286
557
|
6. **Generated agents are marked** `auto-generated/` so users know they can customize
|
|
287
558
|
7. **ALWAYS respect existing patterns** - don't introduce new conventions
|
|
288
559
|
8. **Adapt to the project** - if it's Flask, learn Flask patterns. If it's ASP.NET, learn ASP.NET patterns
|
|
560
|
+
9. **NEVER use placeholder code** - always show REAL examples from the codebase
|
|
289
561
|
|
|
290
562
|
## Language Detection Guide
|
|
291
563
|
|
|
@@ -443,4 +715,4 @@ Unlike static tools, you can:
|
|
|
443
715
|
- **Handle edge cases** - Every project is unique
|
|
444
716
|
- **Adapt over time** - Re-analyze as project evolves
|
|
445
717
|
|
|
446
|
-
This is what makes
|
|
718
|
+
This is what makes agentful special - we use Claude's intelligence, not hardcoded rules!
|