@itz4blitz/agentful 0.3.0 → 0.5.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.
- package/README.md +139 -10
- package/bin/cli.js +1032 -48
- package/bin/hooks/README.md +338 -82
- package/bin/hooks/analyze-trigger.js +69 -0
- package/bin/hooks/block-random-docs.js +77 -0
- package/bin/hooks/health-check.js +153 -0
- package/bin/hooks/post-agent.js +101 -0
- package/bin/hooks/post-feature.js +227 -0
- package/bin/hooks/pre-agent.js +118 -0
- package/bin/hooks/pre-feature.js +138 -0
- package/lib/VALIDATION_README.md +455 -0
- package/lib/atomic.js +350 -0
- package/lib/ci/claude-action-integration.js +641 -0
- package/lib/ci/index.js +10 -0
- package/lib/core/CLAUDE_EXECUTOR.md +371 -0
- package/lib/core/README.md +321 -0
- package/lib/core/analyzer.js +497 -0
- package/lib/core/claude-executor.example.js +210 -0
- package/lib/core/claude-executor.js +1046 -0
- package/lib/core/cli.js +141 -0
- package/lib/core/detectors/conventions.js +342 -0
- package/lib/core/detectors/framework.js +276 -0
- package/lib/core/detectors/index.js +15 -0
- package/lib/core/detectors/language.js +199 -0
- package/lib/core/detectors/patterns.js +356 -0
- package/lib/core/generator.js +626 -0
- package/lib/core/index.js +9 -0
- package/lib/core/output-parser.example.js +250 -0
- package/lib/core/output-parser.js +458 -0
- package/lib/core/storage.js +515 -0
- package/lib/core/templates.js +556 -0
- package/lib/index.js +32 -0
- package/lib/init.js +497 -25
- package/lib/pipeline/cli.js +423 -0
- package/lib/pipeline/engine.js +928 -0
- package/lib/pipeline/executor.js +440 -0
- package/lib/pipeline/index.js +33 -0
- package/lib/pipeline/integrations.js +559 -0
- package/lib/pipeline/schemas.js +288 -0
- package/lib/presets.js +207 -0
- package/lib/remote/client.js +361 -0
- package/lib/server/auth.js +286 -0
- package/lib/server/client-example.js +190 -0
- package/lib/server/executor.js +426 -0
- package/lib/server/index.js +469 -0
- package/lib/update-helpers.js +505 -0
- package/lib/validation.js +460 -0
- package/package.json +19 -2
- package/template/.claude/agents/architect.md +260 -0
- package/template/.claude/agents/backend.md +203 -0
- package/template/.claude/agents/fixer.md +244 -0
- package/template/.claude/agents/frontend.md +232 -0
- package/template/.claude/agents/orchestrator.md +528 -0
- package/template/.claude/agents/product-analyzer.md +1130 -0
- package/template/.claude/agents/reviewer.md +229 -0
- package/template/.claude/agents/tester.md +242 -0
- package/{.claude → template/.claude}/commands/agentful-analyze.md +151 -43
- package/template/.claude/commands/agentful-decide.md +470 -0
- package/{.claude → template/.claude}/commands/agentful-product.md +92 -8
- package/template/.claude/commands/agentful-start.md +432 -0
- package/{.claude → template/.claude}/commands/agentful-status.md +88 -3
- package/template/.claude/commands/agentful-update.md +402 -0
- package/template/.claude/commands/agentful-validate.md +369 -0
- package/{.claude → template/.claude}/commands/agentful.md +111 -195
- package/template/.claude/product/EXAMPLES.md +167 -0
- package/{.claude → template/.claude}/settings.json +9 -13
- package/{.claude → template/.claude}/skills/conversation/SKILL.md +13 -7
- package/template/.claude/skills/deployment/SKILL.md +116 -0
- package/template/.claude/skills/product-planning/SKILL.md +463 -0
- package/{.claude → template/.claude}/skills/product-tracking/SKILL.md +10 -21
- package/template/.claude/skills/testing/SKILL.md +228 -0
- package/template/.claude/skills/validation/SKILL.md +650 -0
- package/template/CLAUDE.md +84 -16
- package/template/bin/hooks/block-random-docs.js +121 -0
- package/version.json +1 -1
- package/.claude/agents/architect.md +0 -524
- package/.claude/agents/backend.md +0 -315
- package/.claude/agents/fixer.md +0 -263
- package/.claude/agents/frontend.md +0 -274
- package/.claude/agents/orchestrator.md +0 -283
- package/.claude/agents/product-analyzer.md +0 -799
- package/.claude/agents/reviewer.md +0 -332
- package/.claude/agents/tester.md +0 -410
- package/.claude/commands/agentful-decide.md +0 -214
- package/.claude/commands/agentful-start.md +0 -182
- package/.claude/commands/agentful-validate.md +0 -127
- package/.claude/product/EXAMPLES.md +0 -610
- package/.claude/product/README.md +0 -344
- package/.claude/skills/validation/SKILL.md +0 -271
- package/bin/hooks/analyze-trigger.sh +0 -57
- package/bin/hooks/health-check.sh +0 -36
- package/template/PRODUCT.md +0 -584
- /package/{.claude → template/.claude}/commands/agentful-generate.md +0 -0
- /package/{.claude → template/.claude}/product/index.md +0 -0
package/lib/init.js
CHANGED
|
@@ -1,35 +1,72 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
|
+
import {
|
|
5
|
+
initializeMetadata,
|
|
6
|
+
recordFileMetadata,
|
|
7
|
+
computeFileHash
|
|
8
|
+
} from './update-helpers.js';
|
|
9
|
+
import { generateHooksConfig } from './presets.js';
|
|
4
10
|
|
|
5
11
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
12
|
const TEMPLATE_DIR = path.join(__dirname, '..', 'template');
|
|
7
|
-
const
|
|
13
|
+
const CLAUDE_TEMPLATE_DIR = path.join(__dirname, '..', 'template', '.claude');
|
|
14
|
+
const HOOKS_DIR = path.join(__dirname, '..', 'bin', 'hooks');
|
|
15
|
+
const PACKAGE_ROOT = path.join(__dirname, '..');
|
|
8
16
|
|
|
9
17
|
/**
|
|
10
18
|
* Initialize agentful in a target project directory
|
|
11
19
|
* @param {string} targetDir - Target project directory
|
|
12
|
-
* @param {Object}
|
|
13
|
-
* @param {
|
|
20
|
+
* @param {Object} config - Configuration (optional)
|
|
21
|
+
* @param {string[]} config.agents - Array of agent names to install
|
|
22
|
+
* @param {string[]} config.skills - Array of skill names to install
|
|
23
|
+
* @param {string[]} config.hooks - Array of hook identifiers to configure
|
|
24
|
+
* @param {string[]} config.gates - Array of quality gate identifiers
|
|
25
|
+
* @param {Object} config.techStack - Tech stack configuration
|
|
14
26
|
* @returns {Promise<{success: boolean, files: string[]}>}
|
|
15
27
|
*/
|
|
16
|
-
export async function initProject(targetDir,
|
|
17
|
-
const { includeProduct = false } = options;
|
|
28
|
+
export async function initProject(targetDir, config = null) {
|
|
18
29
|
const createdFiles = [];
|
|
19
30
|
|
|
20
31
|
try {
|
|
21
32
|
// Ensure target directory exists
|
|
22
33
|
await fs.access(targetDir);
|
|
23
34
|
|
|
24
|
-
//
|
|
35
|
+
// Read package version for metadata tracking
|
|
36
|
+
const packageJsonPath = path.join(PACKAGE_ROOT, 'package.json');
|
|
37
|
+
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
|
|
38
|
+
const version = packageJson.version;
|
|
39
|
+
|
|
40
|
+
// Initialize metadata tracking
|
|
41
|
+
await initializeMetadata(targetDir, version);
|
|
42
|
+
|
|
43
|
+
// 1. Copy .claude/ directory (agents, skills, commands) from template
|
|
25
44
|
const claudeTargetDir = path.join(targetDir, '.claude');
|
|
26
45
|
|
|
27
46
|
try {
|
|
28
|
-
await fs.access(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
47
|
+
await fs.access(CLAUDE_TEMPLATE_DIR);
|
|
48
|
+
|
|
49
|
+
if (config) {
|
|
50
|
+
// Selective installation based on config
|
|
51
|
+
await copySelectiveComponents(CLAUDE_TEMPLATE_DIR, claudeTargetDir, targetDir, config, version);
|
|
52
|
+
createdFiles.push('.claude/ (selective)');
|
|
53
|
+
} else {
|
|
54
|
+
// Full installation (backward compatible)
|
|
55
|
+
await copyDirectoryWithTracking(CLAUDE_TEMPLATE_DIR, claudeTargetDir, targetDir, '.claude', version);
|
|
56
|
+
createdFiles.push('.claude/');
|
|
57
|
+
}
|
|
58
|
+
} catch {
|
|
59
|
+
// .claude template directory doesn't exist in package, skip
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Copy hook scripts
|
|
63
|
+
try {
|
|
64
|
+
await fs.access(HOOKS_DIR);
|
|
65
|
+
const targetHooksDir = path.join(targetDir, 'bin', 'hooks');
|
|
66
|
+
await copyDirectoryWithTracking(HOOKS_DIR, targetHooksDir, targetDir, 'bin/hooks', version);
|
|
67
|
+
createdFiles.push('bin/hooks/');
|
|
68
|
+
} catch {
|
|
69
|
+
console.log('Warning: Hook scripts not found, skipping');
|
|
33
70
|
}
|
|
34
71
|
|
|
35
72
|
// 2. Copy CLAUDE.md template
|
|
@@ -39,8 +76,13 @@ export async function initProject(targetDir, options = {}) {
|
|
|
39
76
|
try {
|
|
40
77
|
await fs.access(claudeMdSource);
|
|
41
78
|
await fs.copyFile(claudeMdSource, claudeMdTarget);
|
|
79
|
+
|
|
80
|
+
// Track CLAUDE.md
|
|
81
|
+
const hash = await computeFileHash(claudeMdTarget);
|
|
82
|
+
await recordFileMetadata(targetDir, 'CLAUDE.md', hash, version);
|
|
83
|
+
|
|
42
84
|
createdFiles.push('CLAUDE.md');
|
|
43
|
-
} catch
|
|
85
|
+
} catch {
|
|
44
86
|
// CLAUDE.md template doesn't exist, skip
|
|
45
87
|
}
|
|
46
88
|
|
|
@@ -79,19 +121,311 @@ export async function initProject(targetDir, options = {}) {
|
|
|
79
121
|
await fs.writeFile(decisionsFile, JSON.stringify(initialDecisions, null, 2));
|
|
80
122
|
createdFiles.push('.agentful/decisions.json');
|
|
81
123
|
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
124
|
+
// Conversation state for natural language interface
|
|
125
|
+
const conversationState = {
|
|
126
|
+
current_phase: 'idle',
|
|
127
|
+
last_message_time: null,
|
|
128
|
+
active_feature: null,
|
|
129
|
+
unresolved_references: [],
|
|
130
|
+
context_history: []
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
await fs.writeFile(
|
|
134
|
+
path.join(agentfulDir, 'conversation-state.json'),
|
|
135
|
+
JSON.stringify(conversationState, null, 2)
|
|
136
|
+
);
|
|
137
|
+
createdFiles.push('.agentful/conversation-state.json');
|
|
138
|
+
|
|
139
|
+
// Conversation history
|
|
140
|
+
const conversationHistory = {
|
|
141
|
+
messages: [],
|
|
142
|
+
created_at: new Date().toISOString()
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
await fs.writeFile(
|
|
146
|
+
path.join(agentfulDir, 'conversation-history.json'),
|
|
147
|
+
JSON.stringify(conversationHistory, null, 2)
|
|
148
|
+
);
|
|
149
|
+
createdFiles.push('.agentful/conversation-history.json');
|
|
150
|
+
|
|
151
|
+
// Agent metrics for lifecycle hooks
|
|
152
|
+
const agentMetrics = {
|
|
153
|
+
invocations: {},
|
|
154
|
+
last_invocation: null,
|
|
155
|
+
feature_hooks: []
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
await fs.writeFile(
|
|
159
|
+
path.join(agentfulDir, 'agent-metrics.json'),
|
|
160
|
+
JSON.stringify(agentMetrics, null, 2)
|
|
161
|
+
);
|
|
162
|
+
createdFiles.push('.agentful/agent-metrics.json');
|
|
163
|
+
|
|
164
|
+
// 4. Create .claude/product/ hierarchical structure
|
|
165
|
+
const productDir = path.join(targetDir, '.claude', 'product');
|
|
166
|
+
await fs.mkdir(productDir, { recursive: true });
|
|
167
|
+
|
|
168
|
+
// Create basic index.md template
|
|
169
|
+
const indexMdContent = `# Product Specification
|
|
170
|
+
|
|
171
|
+
## Overview
|
|
172
|
+
|
|
173
|
+
[Describe what you're building in 2-3 sentences]
|
|
174
|
+
|
|
175
|
+
Example:
|
|
176
|
+
> A task management application that helps teams collaborate on projects. Users can create projects, add tasks with deadlines, assign team members, and track progress with real-time updates.
|
|
177
|
+
|
|
178
|
+
## Goals
|
|
179
|
+
|
|
180
|
+
- [ ] [Primary goal 1]
|
|
181
|
+
- [ ] [Primary goal 2]
|
|
182
|
+
- [ ] [Primary goal 3]
|
|
183
|
+
|
|
184
|
+
## Tech Stack
|
|
185
|
+
|
|
186
|
+
### Frontend
|
|
187
|
+
- **Framework**: [Next.js 14 / React + Vite / Vue + Nuxt / SvelteKit]
|
|
188
|
+
- **Language**: [TypeScript / JavaScript]
|
|
189
|
+
- **Styling**: [Tailwind CSS / CSS Modules / styled-components / shadcn/ui]
|
|
190
|
+
- **State Management**: [Zustand / Context API / Redux / Jotai]
|
|
191
|
+
|
|
192
|
+
### Backend
|
|
193
|
+
- **Runtime**: [Node.js / Bun / Deno]
|
|
194
|
+
- **Framework**: [Next.js API Routes / Express / Fastify / NestJS / Hono]
|
|
195
|
+
- **Language**: [TypeScript / JavaScript]
|
|
196
|
+
|
|
197
|
+
### Database
|
|
198
|
+
- **Database**: [PostgreSQL / MySQL / SQLite / MongoDB / PlanetScale]
|
|
199
|
+
- **ORM**: [Prisma / Drizzle / TypeORM / Mongoose]
|
|
200
|
+
|
|
201
|
+
### Authentication
|
|
202
|
+
- **Method**: [JWT / NextAuth / Clerk / Auth0 / Lucia]
|
|
203
|
+
|
|
204
|
+
### Testing
|
|
205
|
+
- **Unit**: [Vitest / Jest]
|
|
206
|
+
- **E2E**: [Playwright / Cypress]
|
|
207
|
+
|
|
208
|
+
### Deployment
|
|
209
|
+
- **Hosting**: [Vercel / Netlify / Railway / Fly.io]
|
|
210
|
+
|
|
211
|
+
## Domains
|
|
212
|
+
|
|
213
|
+
Create domain-specific subdirectories under \`.claude/product/domains/\` to organize your features:
|
|
214
|
+
|
|
215
|
+
\`\`\`
|
|
216
|
+
.claude/product/
|
|
217
|
+
├── index.md (this file)
|
|
218
|
+
├── README.md
|
|
219
|
+
└── domains/
|
|
220
|
+
├── authentication/
|
|
221
|
+
│ ├── index.md
|
|
222
|
+
│ └── features/
|
|
223
|
+
│ ├── login.md
|
|
224
|
+
│ └── register.md
|
|
225
|
+
└── user-management/
|
|
226
|
+
├── index.md
|
|
227
|
+
└── features/
|
|
228
|
+
└── profile.md
|
|
229
|
+
\`\`\`
|
|
230
|
+
|
|
231
|
+
See \`.claude/product/README.md\` for detailed structure guidance.
|
|
232
|
+
|
|
233
|
+
## Architecture Notes
|
|
234
|
+
|
|
235
|
+
### Design Patterns
|
|
236
|
+
|
|
237
|
+
- [Any specific patterns to use]
|
|
238
|
+
- [Any patterns to avoid]
|
|
239
|
+
|
|
240
|
+
### Constraints
|
|
241
|
+
|
|
242
|
+
- [Performance requirements]
|
|
243
|
+
- [Accessibility requirements]
|
|
244
|
+
- [Browser support requirements]
|
|
245
|
+
|
|
246
|
+
## Third-Party Integrations (Optional)
|
|
247
|
+
|
|
248
|
+
- [API 1]: [Purpose]
|
|
249
|
+
- [API 2]: [Purpose]
|
|
250
|
+
|
|
251
|
+
## Out of Scope (for MVP)
|
|
252
|
+
|
|
253
|
+
List what you're explicitly NOT building:
|
|
254
|
+
|
|
255
|
+
- [Feature X] - Will add in v2
|
|
256
|
+
- [Feature Y] - Out of scope
|
|
257
|
+
- [Feature Z] - Not needed
|
|
258
|
+
|
|
259
|
+
## Success Criteria
|
|
260
|
+
|
|
261
|
+
The product is complete when:
|
|
262
|
+
|
|
263
|
+
1. [All critical features implemented and tested]
|
|
264
|
+
2. [All tests passing with 80%+ coverage]
|
|
265
|
+
3. [No TypeScript errors]
|
|
266
|
+
4. [No security vulnerabilities]
|
|
267
|
+
5. [Deployed to production]
|
|
268
|
+
|
|
269
|
+
## Notes
|
|
270
|
+
|
|
271
|
+
[Any additional context, links, or documentation]
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
**Tip**: The more detailed your product specification, the better agentful can understand what to build. Include:
|
|
276
|
+
- Clear acceptance criteria
|
|
277
|
+
- User stories for context
|
|
278
|
+
- Technical constraints
|
|
279
|
+
- Examples when helpful
|
|
280
|
+
`;
|
|
281
|
+
|
|
282
|
+
const indexMdPath = path.join(productDir, 'index.md');
|
|
283
|
+
await fs.writeFile(indexMdPath, indexMdContent);
|
|
284
|
+
createdFiles.push('.claude/product/index.md');
|
|
285
|
+
|
|
286
|
+
// Create README.md explaining the structure
|
|
287
|
+
const readmeMdContent = `# Product Structure Guide
|
|
288
|
+
|
|
289
|
+
agentful uses a hierarchical product structure to organize features by domain.
|
|
290
|
+
|
|
291
|
+
## Structure
|
|
292
|
+
|
|
293
|
+
\`\`\`
|
|
294
|
+
.claude/product/
|
|
295
|
+
├── index.md # Product overview (name, description, tech stack)
|
|
296
|
+
├── README.md # This file - documentation about the structure
|
|
297
|
+
└── domains/ # Business domains (create as needed)
|
|
298
|
+
├── authentication/
|
|
299
|
+
│ ├── index.md # Domain overview
|
|
300
|
+
│ └── features/
|
|
301
|
+
│ ├── login.md
|
|
302
|
+
│ └── register.md
|
|
303
|
+
└── user-management/
|
|
304
|
+
├── index.md
|
|
305
|
+
└── features/
|
|
306
|
+
└── profile.md
|
|
307
|
+
\`\`\`
|
|
308
|
+
|
|
309
|
+
## Files Explained
|
|
310
|
+
|
|
311
|
+
### \`index.md\` (Product Level)
|
|
312
|
+
- Product name and description
|
|
313
|
+
- Tech stack
|
|
314
|
+
- High-level goals
|
|
315
|
+
- List of domains
|
|
316
|
+
- Architecture notes
|
|
317
|
+
- Success criteria
|
|
318
|
+
|
|
319
|
+
### \`domains/{domain-name}/index.md\` (Domain Level)
|
|
320
|
+
- Domain description and purpose
|
|
321
|
+
- List of features in this domain
|
|
322
|
+
- Domain-specific constraints
|
|
323
|
+
- Dependencies on other domains
|
|
324
|
+
|
|
325
|
+
### \`domains/{domain-name}/features/{feature-name}.md\` (Feature Level)
|
|
326
|
+
- Feature description
|
|
327
|
+
- Priority (CRITICAL, HIGH, MEDIUM, LOW)
|
|
328
|
+
- Acceptance criteria (checkboxes)
|
|
329
|
+
- User stories
|
|
330
|
+
- Technical notes
|
|
331
|
+
- Subtasks breakdown
|
|
332
|
+
|
|
333
|
+
## When to Create a Domain
|
|
334
|
+
|
|
335
|
+
Create a new domain when you have:
|
|
336
|
+
- A distinct business area (e.g., authentication, billing, analytics)
|
|
337
|
+
- Multiple related features (e.g., login, register, password reset)
|
|
338
|
+
- Shared logic or data models
|
|
339
|
+
- Clear boundaries from other domains
|
|
340
|
+
|
|
341
|
+
**Example domains:**
|
|
342
|
+
- \`authentication\` - Login, registration, password reset
|
|
343
|
+
- \`user-management\` - Profile, settings, preferences
|
|
344
|
+
- \`purchasing\` - Cart, checkout, orders, payments
|
|
345
|
+
- \`analytics\` - Dashboards, reports, metrics
|
|
346
|
+
- \`content\` - Posts, comments, media uploads
|
|
347
|
+
|
|
348
|
+
## Example Feature File
|
|
349
|
+
|
|
350
|
+
\`\`\`markdown
|
|
351
|
+
<!-- .claude/product/domains/authentication/features/login.md -->
|
|
352
|
+
|
|
353
|
+
# Feature: User Login
|
|
354
|
+
|
|
355
|
+
**Priority**: CRITICAL
|
|
356
|
+
|
|
357
|
+
**Description**: Allow existing users to authenticate with email and password.
|
|
358
|
+
|
|
359
|
+
## Acceptance Criteria
|
|
360
|
+
|
|
361
|
+
- [ ] Login form with email and password fields
|
|
362
|
+
- [ ] Client-side validation before submission
|
|
363
|
+
- [ ] API endpoint POST /api/auth/login
|
|
364
|
+
- [ ] Returns JWT token on success
|
|
365
|
+
- [ ] Sets httpOnly cookie with token
|
|
366
|
+
- [ ] Returns 401 for invalid credentials
|
|
367
|
+
- [ ] Rate limited to 10 requests per minute
|
|
368
|
+
- [ ] Account lockout after 5 failed attempts
|
|
369
|
+
|
|
370
|
+
## User Stories
|
|
371
|
+
|
|
372
|
+
- As a returning user, I want to log in with my credentials so that I can access my account
|
|
373
|
+
|
|
374
|
+
## Subtasks
|
|
375
|
+
|
|
376
|
+
### 1. Create login form UI
|
|
377
|
+
**Status**: pending
|
|
378
|
+
|
|
379
|
+
- [ ] Email and password input fields
|
|
380
|
+
- [ ] "Remember me" checkbox
|
|
381
|
+
- [ ] "Forgot password" link
|
|
382
|
+
- [ ] Loading state during authentication
|
|
383
|
+
- [ ] Error message display
|
|
384
|
+
|
|
385
|
+
### 2. Implement login API endpoint
|
|
386
|
+
**Status**: pending
|
|
387
|
+
|
|
388
|
+
- [ ] Verify email exists in database
|
|
389
|
+
- [ ] Compare hashed password using bcrypt
|
|
390
|
+
- [ ] Generate JWT token with 7-day expiration
|
|
391
|
+
- [ ] Set secure httpOnly cookie
|
|
392
|
+
- [ ] Implement rate limiting
|
|
393
|
+
- [ ] Track failed login attempts
|
|
394
|
+
|
|
395
|
+
## Technical Notes
|
|
396
|
+
|
|
397
|
+
- Use jose or jsonwebtoken for JWT
|
|
398
|
+
- Store failed attempts in Redis
|
|
399
|
+
- Set cookie flags: httpOnly, secure, sameSite=strict
|
|
400
|
+
\`\`\`
|
|
401
|
+
|
|
402
|
+
## Priority Levels
|
|
403
|
+
|
|
404
|
+
- **CRITICAL** - Must have for MVP, blocks other features
|
|
405
|
+
- **HIGH** - Important for MVP, should include
|
|
406
|
+
- **MEDIUM** - Nice to have if time permits
|
|
407
|
+
- **LOW** - Future enhancement, not for MVP
|
|
408
|
+
|
|
409
|
+
## Status Tracking
|
|
410
|
+
|
|
411
|
+
- \`pending\` - Not started
|
|
412
|
+
- \`in-progress\` - Currently being worked on
|
|
413
|
+
- \`complete\` - Done and tested
|
|
414
|
+
- \`blocked\` - Waiting for decision or dependency
|
|
415
|
+
|
|
416
|
+
## Getting Started
|
|
417
|
+
|
|
418
|
+
1. Edit \`index.md\` to describe your product
|
|
419
|
+
2. Create domains under \`domains/\` for major functional areas
|
|
420
|
+
3. Add features under each domain's \`features/\` directory
|
|
421
|
+
4. Run \`/agentful-start\` in Claude Code to begin development
|
|
422
|
+
|
|
423
|
+
For more information, see the agentful documentation.
|
|
424
|
+
`;
|
|
425
|
+
|
|
426
|
+
const readmeMdPath = path.join(productDir, 'README.md');
|
|
427
|
+
await fs.writeFile(readmeMdPath, readmeMdContent);
|
|
428
|
+
createdFiles.push('.claude/product/README.md');
|
|
95
429
|
|
|
96
430
|
return {
|
|
97
431
|
success: true,
|
|
@@ -102,6 +436,109 @@ export async function initProject(targetDir, options = {}) {
|
|
|
102
436
|
}
|
|
103
437
|
}
|
|
104
438
|
|
|
439
|
+
/**
|
|
440
|
+
* Selectively copy components based on configuration
|
|
441
|
+
* @param {string} src - Source directory
|
|
442
|
+
* @param {string} dest - Destination directory
|
|
443
|
+
* @param {string} targetDir - Project root directory
|
|
444
|
+
* @param {Object} config - Configuration
|
|
445
|
+
* @param {string} version - Package version
|
|
446
|
+
*/
|
|
447
|
+
async function copySelectiveComponents(src, dest, targetDir, config, version) {
|
|
448
|
+
// Ensure orchestrator is always included
|
|
449
|
+
if (!config.agents.includes('orchestrator')) {
|
|
450
|
+
config.agents.unshift('orchestrator');
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Create base .claude directory
|
|
454
|
+
await fs.mkdir(dest, { recursive: true });
|
|
455
|
+
|
|
456
|
+
// 1. Copy selected agents
|
|
457
|
+
const agentsSourceDir = path.join(src, 'agents');
|
|
458
|
+
const agentsTargetDir = path.join(dest, 'agents');
|
|
459
|
+
await fs.mkdir(agentsTargetDir, { recursive: true });
|
|
460
|
+
|
|
461
|
+
for (const agentName of config.agents) {
|
|
462
|
+
const agentFile = `${agentName}.md`;
|
|
463
|
+
const sourcePath = path.join(agentsSourceDir, agentFile);
|
|
464
|
+
const targetPath = path.join(agentsTargetDir, agentFile);
|
|
465
|
+
|
|
466
|
+
try {
|
|
467
|
+
await fs.copyFile(sourcePath, targetPath);
|
|
468
|
+
const hash = await computeFileHash(targetPath);
|
|
469
|
+
await recordFileMetadata(targetDir, `.claude/agents/${agentFile}`, hash, version);
|
|
470
|
+
} catch (error) {
|
|
471
|
+
console.warn(`Warning: Agent ${agentName} not found, skipping`);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// 2. Copy selected skills
|
|
476
|
+
const skillsSourceDir = path.join(src, 'skills');
|
|
477
|
+
const skillsTargetDir = path.join(dest, 'skills');
|
|
478
|
+
await fs.mkdir(skillsTargetDir, { recursive: true });
|
|
479
|
+
|
|
480
|
+
for (const skillName of config.skills) {
|
|
481
|
+
const skillDir = path.join(skillsSourceDir, skillName);
|
|
482
|
+
const targetSkillDir = path.join(skillsTargetDir, skillName);
|
|
483
|
+
|
|
484
|
+
try {
|
|
485
|
+
await copyDirectoryWithTracking(
|
|
486
|
+
skillDir,
|
|
487
|
+
targetSkillDir,
|
|
488
|
+
targetDir,
|
|
489
|
+
`.claude/skills/${skillName}`,
|
|
490
|
+
version
|
|
491
|
+
);
|
|
492
|
+
} catch (error) {
|
|
493
|
+
console.warn(`Warning: Skill ${skillName} not found, skipping`);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// 3. Copy all commands (always include core commands)
|
|
498
|
+
const commandsSourceDir = path.join(src, 'commands');
|
|
499
|
+
const commandsTargetDir = path.join(dest, 'commands');
|
|
500
|
+
|
|
501
|
+
try {
|
|
502
|
+
await copyDirectoryWithTracking(
|
|
503
|
+
commandsSourceDir,
|
|
504
|
+
commandsTargetDir,
|
|
505
|
+
targetDir,
|
|
506
|
+
'.claude/commands',
|
|
507
|
+
version
|
|
508
|
+
);
|
|
509
|
+
} catch (error) {
|
|
510
|
+
console.warn('Warning: Commands directory not found, skipping');
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// 4. Generate settings.json with selected hooks
|
|
514
|
+
const settingsPath = path.join(dest, 'settings.json');
|
|
515
|
+
|
|
516
|
+
const settings = {
|
|
517
|
+
includeCoAuthoredBy: false,
|
|
518
|
+
env: {
|
|
519
|
+
ENABLE_TOOL_SEARCH: 'true'
|
|
520
|
+
},
|
|
521
|
+
hooks: generateHooksConfig(config.hooks || []),
|
|
522
|
+
permissions: {
|
|
523
|
+
deny: [
|
|
524
|
+
'Bash(rm -rf /)',
|
|
525
|
+
'Bash(rm -rf ~/.*)',
|
|
526
|
+
'Bash(rm -rf /.*)',
|
|
527
|
+
'Bash(dd:*)',
|
|
528
|
+
'Bash(mkfs:*)'
|
|
529
|
+
]
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2));
|
|
534
|
+
const hash = await computeFileHash(settingsPath);
|
|
535
|
+
await recordFileMetadata(targetDir, '.claude/settings.json', hash, version);
|
|
536
|
+
|
|
537
|
+
// 5. Create product directory (always included)
|
|
538
|
+
const productDir = path.join(dest, 'product');
|
|
539
|
+
await fs.mkdir(productDir, { recursive: true });
|
|
540
|
+
}
|
|
541
|
+
|
|
105
542
|
/**
|
|
106
543
|
* Recursively copy a directory
|
|
107
544
|
* @param {string} src - Source directory
|
|
@@ -128,6 +565,41 @@ export async function copyDirectory(src, dest) {
|
|
|
128
565
|
}
|
|
129
566
|
}
|
|
130
567
|
|
|
568
|
+
/**
|
|
569
|
+
* Recursively copy a directory with metadata tracking
|
|
570
|
+
* @param {string} src - Source directory
|
|
571
|
+
* @param {string} dest - Destination directory
|
|
572
|
+
* @param {string} targetDir - Project root directory
|
|
573
|
+
* @param {string} baseRelativePath - Base relative path for tracking (e.g., '.claude', 'bin/hooks')
|
|
574
|
+
* @param {string} version - Package version
|
|
575
|
+
*/
|
|
576
|
+
export async function copyDirectoryWithTracking(src, dest, targetDir, baseRelativePath, version) {
|
|
577
|
+
// Create destination directory
|
|
578
|
+
await fs.mkdir(dest, { recursive: true });
|
|
579
|
+
|
|
580
|
+
// Read source directory
|
|
581
|
+
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
582
|
+
|
|
583
|
+
for (const entry of entries) {
|
|
584
|
+
const srcPath = path.join(src, entry.name);
|
|
585
|
+
const destPath = path.join(dest, entry.name);
|
|
586
|
+
|
|
587
|
+
if (entry.isDirectory()) {
|
|
588
|
+
// Recursively copy subdirectory
|
|
589
|
+
const newBaseRelativePath = path.join(baseRelativePath, entry.name);
|
|
590
|
+
await copyDirectoryWithTracking(srcPath, destPath, targetDir, newBaseRelativePath, version);
|
|
591
|
+
} else {
|
|
592
|
+
// Copy file
|
|
593
|
+
await fs.copyFile(srcPath, destPath);
|
|
594
|
+
|
|
595
|
+
// Compute hash and track file
|
|
596
|
+
const relativePath = path.join(baseRelativePath, entry.name);
|
|
597
|
+
const hash = await computeFileHash(destPath);
|
|
598
|
+
await recordFileMetadata(targetDir, relativePath, hash, version);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
131
603
|
/**
|
|
132
604
|
* Check if project is already initialized
|
|
133
605
|
* @param {string} targetDir - Target project directory
|