ardfw 0.1.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.
- package/README.md +161 -0
- package/package.json +41 -0
- package/src/project_config/AGENTICS.md +19 -0
- package/src/project_config/agents/.gitkeep +0 -0
- package/src/project_config/commands/.gitkeep +0 -0
- package/src/project_config/settings.json +3 -0
- package/src/project_config/skills/.gitkeep +0 -0
- package/tools/ard-npx-wrapper.js +33 -0
- package/tools/cli/ard-cli.js +53 -0
- package/tools/cli/commands/install.js +81 -0
- package/tools/cli/lib/config.js +48 -0
- package/tools/cli/lib/file-ops.js +76 -0
- package/tools/cli/lib/installer.js +261 -0
- package/tools/cli/lib/ui.js +134 -0
package/README.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Agentics Framework
|
|
2
|
+
|
|
3
|
+
An AI agentics framework for building multi-agent systems focused on software engineering, datascience research, and personal knowledge management. Built for Claude Code and Claude Agent SDK, with support for other agentics platforms like OpenCode and Cursor.
|
|
4
|
+
|
|
5
|
+
## The Problem
|
|
6
|
+
|
|
7
|
+
Building robust multi-agent systems is challenging:
|
|
8
|
+
- **Inconsistent agent definitions** - Each agent defined differently across projects
|
|
9
|
+
- **Skill duplication** - Writing the same capabilities for different agents
|
|
10
|
+
- **Workflow complexity** - Orchestrating commands and agents is ad-hoc
|
|
11
|
+
- **Limited extensibility** - Hard to add custom skills and MCP servers
|
|
12
|
+
|
|
13
|
+
## The Solution
|
|
14
|
+
|
|
15
|
+
This framework provides a standardized system for:
|
|
16
|
+
- **Standardized Agent Definitions** - Consistent structure for defining specialized agents
|
|
17
|
+
- **Reusable Skill Library** - Build skills once, use across multiple agents
|
|
18
|
+
- **Command Workflow Automation** - Chain commands and agents for complex workflows
|
|
19
|
+
- **Custom Skills & MCP Servers** - Extend agents with custom capabilities and MCP integrations
|
|
20
|
+
|
|
21
|
+
## Architecture
|
|
22
|
+
|
|
23
|
+
The framework is built on the **ARD** design pattern:
|
|
24
|
+
|
|
25
|
+
- **Abstract** - Take existing methods and abstract them to understand underlying frameworks and context from user input
|
|
26
|
+
- **Refactor** - Modularize and reorganize ideas from abstraction
|
|
27
|
+
- **Derivative** - Derive new specialized variations from existing ideas
|
|
28
|
+
|
|
29
|
+
### Core Components
|
|
30
|
+
|
|
31
|
+
The framework has three essential building blocks that work together:
|
|
32
|
+
|
|
33
|
+
#### 1. **Agents** (`/agents`)
|
|
34
|
+
Specialized AI agents with defined roles and capabilities. Each agent is a prompt template with specific expertise (e.g., architect, developer, analyst, tech-writer, etc.). These are the primary abstractions users work with.
|
|
35
|
+
|
|
36
|
+
**Examples:**
|
|
37
|
+
- `architect.md` - Architecture and design decisions
|
|
38
|
+
- `dev.md` - Software development and coding
|
|
39
|
+
- `analyst.md` - Data analysis and research
|
|
40
|
+
- `tech-writer.md` - Documentation and technical writing
|
|
41
|
+
- `pm.md` - Project management
|
|
42
|
+
- `ux-designer.md` - UI/UX design
|
|
43
|
+
- `sm.md` - Scrum/agile management
|
|
44
|
+
- `agent-dev.md` - Building custom agents
|
|
45
|
+
|
|
46
|
+
#### 2. **Commands** (`/commands`)
|
|
47
|
+
Reusable Claude Code commands that orchestrate agents and define workflows. Commands are executed via Claude Code and can chain multiple agents together.
|
|
48
|
+
|
|
49
|
+
**Core commands:**
|
|
50
|
+
- `prime.md` - Understand a codebase quickly
|
|
51
|
+
- `quick-plan.md` - Create rapid implementation plans
|
|
52
|
+
- `build.md` - Execute implementations
|
|
53
|
+
- `meta-prompt.md` - Generate new command prompts
|
|
54
|
+
- `load_ai_docs.md` - Load documentation context
|
|
55
|
+
- `parallel_subagents.md` - Orchestrate multiple agents in parallel
|
|
56
|
+
|
|
57
|
+
**Expert commands** (`/commands/experts`)
|
|
58
|
+
- `plan.md` - Create detailed implementation plans
|
|
59
|
+
- `build.md` - Execute plans with expert guidance
|
|
60
|
+
- `plan-build-improve.md` - Complete workflow with continuous improvement
|
|
61
|
+
- `self-improve.md` - Update expertise based on learnings
|
|
62
|
+
|
|
63
|
+
#### 3. **Skills** (`/skills`)
|
|
64
|
+
Reusable capabilities that agents can use. Skills are MCP servers, tools, and utility functions that extend agent capabilities.
|
|
65
|
+
|
|
66
|
+
**Examples:**
|
|
67
|
+
- Custom MCP servers for domain-specific tasks
|
|
68
|
+
- Tool definitions for agent use
|
|
69
|
+
- Integration skills for connecting to external systems
|
|
70
|
+
|
|
71
|
+
### Project Structure
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
agentics/
|
|
75
|
+
├── agents/ # Agent definitions
|
|
76
|
+
│ ├── architect.md
|
|
77
|
+
│ ├── dev.md
|
|
78
|
+
│ ├── analyst.md
|
|
79
|
+
│ └── ... (other agents)
|
|
80
|
+
├── commands/ # Command workflows
|
|
81
|
+
│ ├── prime.md
|
|
82
|
+
│ ├── quick-plan.md
|
|
83
|
+
│ ├── build.md
|
|
84
|
+
│ └── experts/ # Expert command variants
|
|
85
|
+
└── skills/ # Reusable skills & MCP servers
|
|
86
|
+
└── meta-skills/
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Project-specific workflows and templates are built **outside** these core folders in your project directory.
|
|
90
|
+
|
|
91
|
+
## Getting Started
|
|
92
|
+
|
|
93
|
+
### Installation & Setup
|
|
94
|
+
|
|
95
|
+
1. **Clone this repository** to your `_ard` folder or relevant configuration directory:
|
|
96
|
+
```bash
|
|
97
|
+
git clone <repo> ~/_ard/agentics
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
2. **Symlink to your projects** - Link the framework into your project directories:
|
|
101
|
+
```bash
|
|
102
|
+
ln -s ~/_ard/agentics/agents ./project/_ard/agents
|
|
103
|
+
ln -s ~/_ard/agentics/commands ./project/_ard/commands
|
|
104
|
+
ln -s ~/_ard/agentics/skills ./project/_ard/skills
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
3. **Use in Claude Code** - Reference agents and commands in your Claude Code workflows
|
|
108
|
+
|
|
109
|
+
### Your First Agent
|
|
110
|
+
|
|
111
|
+
1. Start with the `prime` command to understand your codebase:
|
|
112
|
+
```
|
|
113
|
+
/prime
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
2. Create a plan using `quick-plan`:
|
|
117
|
+
```
|
|
118
|
+
/quick-plan
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
3. Execute with the appropriate agent (architect for design, dev for implementation, etc.)
|
|
122
|
+
|
|
123
|
+
### Creating Custom Agents
|
|
124
|
+
|
|
125
|
+
Use the `agent-dev.md` agent or `meta-prompt` command to create new specialized agents:
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
/meta-prompt "Create a [Role] agent that handles [Responsibility]"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Creating Custom Skills
|
|
132
|
+
|
|
133
|
+
Define custom skills in the `/skills` directory following the established patterns. Skills can be:
|
|
134
|
+
- MCP servers
|
|
135
|
+
- Tool definitions
|
|
136
|
+
- Integration wrappers
|
|
137
|
+
|
|
138
|
+
## Platform Compatibility
|
|
139
|
+
|
|
140
|
+
- **Primary**: Claude Code & Claude Agent SDK
|
|
141
|
+
- **Compatible**: OpenCode, Cursor, and other agentics platforms (with adaptations)
|
|
142
|
+
|
|
143
|
+
## Current Status
|
|
144
|
+
|
|
145
|
+
**Internal/Experimental** - This framework is actively being developed. It's suitable for internal use and early adopter feedback, but not yet production-ready for external distribution.
|
|
146
|
+
|
|
147
|
+
## Next Steps
|
|
148
|
+
|
|
149
|
+
- [ ] Define each agent's specific capabilities and use cases
|
|
150
|
+
- [ ] Build a comprehensive skill library with examples
|
|
151
|
+
- [ ] Create workflow templates for common scenarios
|
|
152
|
+
- [ ] Document best practices for extending the framework
|
|
153
|
+
- [ ] Add examples and sample projects
|
|
154
|
+
|
|
155
|
+
## Contributing
|
|
156
|
+
|
|
157
|
+
For internal development:
|
|
158
|
+
1. Create new agent definitions in `/agents`
|
|
159
|
+
2. Add commands in `/commands`
|
|
160
|
+
3. Extend skills in `/skills`
|
|
161
|
+
4. Follow the ARD pattern for consistency
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ardfw",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Agentics Framework CLI - Multi-agent system for Claude Code and OpenCode",
|
|
5
|
+
"bin": {
|
|
6
|
+
"ardfw": "tools/ard-npx-wrapper.js",
|
|
7
|
+
"ard": "tools/ard-npx-wrapper.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"tools/",
|
|
11
|
+
"src/project_config/"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "echo \"No tests yet\"",
|
|
15
|
+
"ard:install": "node tools/ard-npx-wrapper.js install"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"chalk": "^5.3.0",
|
|
19
|
+
"commander": "^12.0.0",
|
|
20
|
+
"fs-extra": "^11.2.0",
|
|
21
|
+
"inquirer": "^9.2.0",
|
|
22
|
+
"yaml": "^2.3.0"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18.0.0"
|
|
26
|
+
},
|
|
27
|
+
"type": "module",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/uechanx/agentics.git"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"agentics",
|
|
34
|
+
"claude-code",
|
|
35
|
+
"opencode",
|
|
36
|
+
"ai-agents",
|
|
37
|
+
"cli"
|
|
38
|
+
],
|
|
39
|
+
"author": "uechanx",
|
|
40
|
+
"license": "MIT"
|
|
41
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Project: {{PROJECT_NAME}}
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
<!-- Describe your project here -->
|
|
5
|
+
|
|
6
|
+
## Tech Stack
|
|
7
|
+
<!-- List key technologies, frameworks, and tools -->
|
|
8
|
+
|
|
9
|
+
## Project Structure
|
|
10
|
+
<!-- Describe directory layout and organization -->
|
|
11
|
+
|
|
12
|
+
## Conventions
|
|
13
|
+
<!-- Document coding conventions, naming patterns, etc. -->
|
|
14
|
+
|
|
15
|
+
## Development Commands
|
|
16
|
+
<!-- Common dev commands for building, testing, running -->
|
|
17
|
+
|
|
18
|
+
## Notes for AI Agents
|
|
19
|
+
<!-- Special instructions, constraints, or context for AI assistance -->
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ard CLI wrapper
|
|
4
|
+
* Handles npx execution context and delegates to main CLI
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { execSync } from 'node:child_process';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = path.dirname(__filename);
|
|
13
|
+
|
|
14
|
+
// Detect if running through npx (npm cache directory)
|
|
15
|
+
const isNpxExecution = __dirname.includes('_npx') || __dirname.includes('.npm');
|
|
16
|
+
|
|
17
|
+
if (isNpxExecution) {
|
|
18
|
+
// Running via npx - spawn child process to preserve CWD
|
|
19
|
+
const ardCliPath = path.join(__dirname, 'cli', 'ard-cli.js');
|
|
20
|
+
const args = process.argv.slice(2);
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
execSync(`node "${ardCliPath}" ${args.join(' ')}`, {
|
|
24
|
+
stdio: 'inherit',
|
|
25
|
+
cwd: process.cwd(), // Preserve user's working directory
|
|
26
|
+
});
|
|
27
|
+
} catch (error) {
|
|
28
|
+
process.exit(error.status || 1);
|
|
29
|
+
}
|
|
30
|
+
} else {
|
|
31
|
+
// Running locally - dynamic import
|
|
32
|
+
await import('./cli/ard-cli.js');
|
|
33
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ard CLI - Agentics Framework installer
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import fs from 'node:fs';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = path.dirname(__filename);
|
|
13
|
+
|
|
14
|
+
// Load package.json for version
|
|
15
|
+
const packageJsonPath = path.resolve(__dirname, '../../package.json');
|
|
16
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
17
|
+
|
|
18
|
+
const program = new Command();
|
|
19
|
+
|
|
20
|
+
program
|
|
21
|
+
.name('ard')
|
|
22
|
+
.description('Agentics Framework CLI - Multi-agent system for Claude Code and OpenCode')
|
|
23
|
+
.version(packageJson.version);
|
|
24
|
+
|
|
25
|
+
// Dynamically load commands from commands/ directory
|
|
26
|
+
const commandsDir = path.join(__dirname, 'commands');
|
|
27
|
+
|
|
28
|
+
if (fs.existsSync(commandsDir)) {
|
|
29
|
+
const commandFiles = fs.readdirSync(commandsDir).filter((f) => f.endsWith('.js'));
|
|
30
|
+
|
|
31
|
+
for (const file of commandFiles) {
|
|
32
|
+
const commandModule = await import(path.join(commandsDir, file));
|
|
33
|
+
const command = commandModule.default;
|
|
34
|
+
|
|
35
|
+
const cmd = program.command(command.command).description(command.description);
|
|
36
|
+
|
|
37
|
+
// Add options if defined
|
|
38
|
+
if (command.options) {
|
|
39
|
+
for (const opt of command.options) {
|
|
40
|
+
cmd.option(...opt);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
cmd.action(command.action);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Show help if no command provided
|
|
49
|
+
if (process.argv.length <= 2) {
|
|
50
|
+
program.help();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* install command - Sets up Agentics in a project
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import Installer from '../lib/installer.js';
|
|
6
|
+
import UI from '../lib/ui.js';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
command: 'install',
|
|
11
|
+
description: 'Install Agentics Framework in current project',
|
|
12
|
+
options: [
|
|
13
|
+
['--force', 'Overwrite existing installation'],
|
|
14
|
+
['--claude', 'Enable Claude Code integration'],
|
|
15
|
+
['--opencode', 'Enable OpenCode integration'],
|
|
16
|
+
['-y, --yes', 'Skip prompts, use defaults'],
|
|
17
|
+
],
|
|
18
|
+
|
|
19
|
+
async action(options) {
|
|
20
|
+
const ui = new UI();
|
|
21
|
+
const installer = new Installer();
|
|
22
|
+
|
|
23
|
+
console.log(chalk.blue('\n🤖 Agentics Framework Installer\n'));
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
// Check for existing installation
|
|
27
|
+
const existing = installer.checkExisting();
|
|
28
|
+
|
|
29
|
+
if (existing && !options.force) {
|
|
30
|
+
if (options.yes) {
|
|
31
|
+
// In non-interactive mode with existing install, just update
|
|
32
|
+
await installer.update();
|
|
33
|
+
console.log(chalk.green('\n✅ Agentics updated successfully!'));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const { action } = await ui.promptExistingInstall(existing);
|
|
38
|
+
|
|
39
|
+
if (action === 'cancel') {
|
|
40
|
+
console.log(chalk.yellow('Installation cancelled.'));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (action === 'update') {
|
|
45
|
+
await installer.update();
|
|
46
|
+
console.log(chalk.green('\n✅ Agentics updated successfully!'));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Gather installation options
|
|
52
|
+
let config;
|
|
53
|
+
if (options.yes) {
|
|
54
|
+
config = {
|
|
55
|
+
enableClaude: options.claude !== false,
|
|
56
|
+
enableOpenCode: options.opencode || false,
|
|
57
|
+
projectName: 'my-project',
|
|
58
|
+
};
|
|
59
|
+
} else {
|
|
60
|
+
config = await ui.promptInstall(existing);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (config.cancelled) {
|
|
64
|
+
console.log(chalk.yellow('Installation cancelled.'));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Run installation
|
|
69
|
+
await installer.install(config);
|
|
70
|
+
|
|
71
|
+
// Show success message
|
|
72
|
+
ui.showSuccess(config);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.error(chalk.red('\n❌ Installation failed:'), error.message);
|
|
75
|
+
if (process.env.DEBUG) {
|
|
76
|
+
console.error(error.stack);
|
|
77
|
+
}
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration constants for ARD CLI
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const PATHS = {
|
|
6
|
+
ARD_DIR: '_ard',
|
|
7
|
+
PROJECT_CONFIG: '_ard/project_config',
|
|
8
|
+
CONFIG_YAML: '_ard/config.yaml',
|
|
9
|
+
MANIFEST: '_ard/.manifest.json',
|
|
10
|
+
ARTIFACTS: '_ard/artifacts',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const DEFAULT_CONFIG = {
|
|
14
|
+
project_name: 'my-project',
|
|
15
|
+
user_name: 'User',
|
|
16
|
+
output_folder: '_ard/artifacts',
|
|
17
|
+
planning_artifacts: '_ard/artifacts/planning',
|
|
18
|
+
implementation_artifacts: '_ard/artifacts/implementation',
|
|
19
|
+
product_knowledge: 'docs',
|
|
20
|
+
communication_language: 'en',
|
|
21
|
+
document_output_language: 'en',
|
|
22
|
+
user_skill_level: 'intermediate',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const IDE_CONFIGS = {
|
|
26
|
+
claude: {
|
|
27
|
+
id: 'claude',
|
|
28
|
+
name: 'Claude Code',
|
|
29
|
+
linkName: '.claude',
|
|
30
|
+
contextFile: 'CLAUDE.md',
|
|
31
|
+
sourceContextFile: 'AGENTICS.md',
|
|
32
|
+
},
|
|
33
|
+
opencode: {
|
|
34
|
+
id: 'opencode',
|
|
35
|
+
name: 'OpenCode',
|
|
36
|
+
linkName: '.opencode',
|
|
37
|
+
contextFile: 'OPENCODE.md',
|
|
38
|
+
sourceContextFile: 'AGENTICS.md',
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const SYMLINK_MAPPINGS = [
|
|
43
|
+
{ source: 'AGENTICS.md', targets: { claude: 'CLAUDE.md', opencode: 'OPENCODE.md' } },
|
|
44
|
+
{ source: 'settings.json', targets: { claude: 'settings.json', opencode: 'settings.json' } },
|
|
45
|
+
{ source: 'agents', targets: { claude: 'agents', opencode: 'agents' }, isDir: true },
|
|
46
|
+
{ source: 'commands', targets: { claude: 'commands', opencode: 'commands' }, isDir: true },
|
|
47
|
+
{ source: 'skills', targets: { claude: 'skills', opencode: 'skills' }, isDir: true },
|
|
48
|
+
];
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File operations utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from 'fs-extra';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
|
|
8
|
+
export default class FileOps {
|
|
9
|
+
constructor(projectRoot) {
|
|
10
|
+
this.projectRoot = projectRoot;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
resolve(...paths) {
|
|
14
|
+
return path.join(this.projectRoot, ...paths);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async exists(filePath) {
|
|
18
|
+
return fs.pathExists(this.resolve(filePath));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async isSymlink(filePath) {
|
|
22
|
+
try {
|
|
23
|
+
const stat = await fs.lstat(this.resolve(filePath));
|
|
24
|
+
return stat.isSymbolicLink();
|
|
25
|
+
} catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async readSymlink(linkPath) {
|
|
31
|
+
return fs.readlink(this.resolve(linkPath));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async ensureDir(dirPath) {
|
|
35
|
+
return fs.ensureDir(this.resolve(dirPath));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async copy(src, dest, options = {}) {
|
|
39
|
+
return fs.copy(this.resolve(src), this.resolve(dest), options);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async writeFile(filePath, content) {
|
|
43
|
+
await fs.ensureDir(path.dirname(this.resolve(filePath)));
|
|
44
|
+
return fs.writeFile(this.resolve(filePath), content);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async readFile(filePath) {
|
|
48
|
+
return fs.readFile(this.resolve(filePath), 'utf8');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async remove(filePath) {
|
|
52
|
+
return fs.remove(this.resolve(filePath));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async symlink(target, linkPath, type = 'file') {
|
|
56
|
+
const fullLinkPath = this.resolve(linkPath);
|
|
57
|
+
await fs.ensureDir(path.dirname(fullLinkPath));
|
|
58
|
+
|
|
59
|
+
// Remove existing symlink if present
|
|
60
|
+
if (await this.isSymlink(linkPath)) {
|
|
61
|
+
await this.remove(linkPath);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Create relative symlink
|
|
65
|
+
return fs.symlink(target, fullLinkPath, type);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async writeJson(filePath, data, options = { spaces: 2 }) {
|
|
69
|
+
await fs.ensureDir(path.dirname(this.resolve(filePath)));
|
|
70
|
+
return fs.writeJson(this.resolve(filePath), data, options);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async readJson(filePath) {
|
|
74
|
+
return fs.readJson(this.resolve(filePath));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Installer - Core installation logic
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from 'fs-extra';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import * as yaml from 'yaml';
|
|
9
|
+
import FileOps from './file-ops.js';
|
|
10
|
+
import { PATHS, DEFAULT_CONFIG, IDE_CONFIGS, SYMLINK_MAPPINGS } from './config.js';
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = path.dirname(__filename);
|
|
14
|
+
|
|
15
|
+
export default class Installer {
|
|
16
|
+
constructor() {
|
|
17
|
+
this.projectRoot = process.cwd();
|
|
18
|
+
this.packageRoot = path.resolve(__dirname, '../../..');
|
|
19
|
+
this.fileOps = new FileOps(this.projectRoot);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Check for existing Agentics installation
|
|
24
|
+
*/
|
|
25
|
+
checkExisting() {
|
|
26
|
+
const ardPath = path.join(this.projectRoot, PATHS.ARD_DIR);
|
|
27
|
+
const configPath = path.join(this.projectRoot, PATHS.CONFIG_YAML);
|
|
28
|
+
|
|
29
|
+
if (!fs.existsSync(ardPath)) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
if (fs.existsSync(configPath)) {
|
|
35
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
36
|
+
const config = yaml.parse(content);
|
|
37
|
+
return {
|
|
38
|
+
path: ardPath,
|
|
39
|
+
version: config.ard_version || 'unknown',
|
|
40
|
+
config,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
} catch {
|
|
44
|
+
// Corrupted config
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return { path: ardPath, version: 'unknown', config: null };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get package version
|
|
52
|
+
*/
|
|
53
|
+
getVersion() {
|
|
54
|
+
const packageJsonPath = path.join(this.packageRoot, 'package.json');
|
|
55
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
56
|
+
return packageJson.version;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Main installation
|
|
61
|
+
*/
|
|
62
|
+
async install(options) {
|
|
63
|
+
const { enableClaude = true, enableOpenCode = false, projectName = 'my-project' } = options;
|
|
64
|
+
|
|
65
|
+
// 1. Create _ard directory structure
|
|
66
|
+
await this._createArdDirectory();
|
|
67
|
+
|
|
68
|
+
// 2. Copy project_config template
|
|
69
|
+
await this._copyProjectConfig();
|
|
70
|
+
|
|
71
|
+
// 3. Create config.yaml
|
|
72
|
+
await this._createConfigYaml(projectName, enableClaude, enableOpenCode);
|
|
73
|
+
|
|
74
|
+
// 4. Create artifacts directory
|
|
75
|
+
await this._createArtifactsDirectory();
|
|
76
|
+
|
|
77
|
+
// 5. Create IDE symlinks
|
|
78
|
+
if (enableClaude) {
|
|
79
|
+
await this._createIdeSymlinks('claude');
|
|
80
|
+
}
|
|
81
|
+
if (enableOpenCode) {
|
|
82
|
+
await this._createIdeSymlinks('opencode');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 6. Create manifest for tracking
|
|
86
|
+
await this._createManifest(options);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Update existing installation
|
|
91
|
+
*/
|
|
92
|
+
async update() {
|
|
93
|
+
// Read existing config
|
|
94
|
+
const existing = this.checkExisting();
|
|
95
|
+
if (!existing) {
|
|
96
|
+
throw new Error('No existing installation found');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Backup existing project_config
|
|
100
|
+
const projectConfigPath = path.join(this.projectRoot, PATHS.PROJECT_CONFIG);
|
|
101
|
+
const backupPath = path.join(this.projectRoot, '_ard/project_config.backup');
|
|
102
|
+
|
|
103
|
+
if (fs.existsSync(projectConfigPath)) {
|
|
104
|
+
await fs.copy(projectConfigPath, backupPath);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Copy new template, preserving user files
|
|
108
|
+
await this._copyProjectConfig({ merge: true });
|
|
109
|
+
|
|
110
|
+
// Update config.yaml version
|
|
111
|
+
await this._updateConfigVersion();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// === Private Methods ===
|
|
115
|
+
|
|
116
|
+
async _createArdDirectory() {
|
|
117
|
+
await this.fileOps.ensureDir(PATHS.ARD_DIR);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async _copyProjectConfig(options = {}) {
|
|
121
|
+
const srcPath = path.join(this.packageRoot, 'src/project_config');
|
|
122
|
+
const destPath = path.join(this.projectRoot, PATHS.PROJECT_CONFIG);
|
|
123
|
+
|
|
124
|
+
// Ensure source exists
|
|
125
|
+
if (!fs.existsSync(srcPath)) {
|
|
126
|
+
// Create minimal template if source doesn't exist yet
|
|
127
|
+
await this._createMinimalTemplate(destPath);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
await fs.copy(srcPath, destPath, {
|
|
132
|
+
overwrite: !options.merge, // Don't overwrite in merge mode
|
|
133
|
+
errorOnExist: false,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async _createMinimalTemplate(destPath) {
|
|
138
|
+
await fs.ensureDir(destPath);
|
|
139
|
+
await fs.ensureDir(path.join(destPath, 'agents'));
|
|
140
|
+
await fs.ensureDir(path.join(destPath, 'commands'));
|
|
141
|
+
await fs.ensureDir(path.join(destPath, 'skills'));
|
|
142
|
+
|
|
143
|
+
// Create AGENTICS.md
|
|
144
|
+
const agenticsContent = `# Project: ${path.basename(this.projectRoot)}
|
|
145
|
+
|
|
146
|
+
## Overview
|
|
147
|
+
<!-- Describe your project here -->
|
|
148
|
+
|
|
149
|
+
## Tech Stack
|
|
150
|
+
<!-- List key technologies -->
|
|
151
|
+
|
|
152
|
+
## Project Structure
|
|
153
|
+
<!-- Describe directory layout -->
|
|
154
|
+
|
|
155
|
+
## Conventions
|
|
156
|
+
<!-- Document coding conventions -->
|
|
157
|
+
|
|
158
|
+
## Development Commands
|
|
159
|
+
<!-- Common dev commands -->
|
|
160
|
+
|
|
161
|
+
## Notes for AI Agents
|
|
162
|
+
<!-- Special instructions -->
|
|
163
|
+
`;
|
|
164
|
+
await fs.writeFile(path.join(destPath, 'AGENTICS.md'), agenticsContent);
|
|
165
|
+
|
|
166
|
+
// Create settings.json
|
|
167
|
+
await fs.writeJson(path.join(destPath, 'settings.json'), { spinnerTipsEnabled: true }, { spaces: 2 });
|
|
168
|
+
|
|
169
|
+
// Create .gitkeep files
|
|
170
|
+
await fs.writeFile(path.join(destPath, 'agents/.gitkeep'), '');
|
|
171
|
+
await fs.writeFile(path.join(destPath, 'commands/.gitkeep'), '');
|
|
172
|
+
await fs.writeFile(path.join(destPath, 'skills/.gitkeep'), '');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async _createConfigYaml(projectName, enableClaude, enableOpenCode) {
|
|
176
|
+
const configPath = path.join(this.projectRoot, PATHS.CONFIG_YAML);
|
|
177
|
+
|
|
178
|
+
// Don't overwrite existing config
|
|
179
|
+
if (fs.existsSync(configPath)) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const config = {
|
|
184
|
+
...DEFAULT_CONFIG,
|
|
185
|
+
project_name: projectName,
|
|
186
|
+
ard_version: this.getVersion(),
|
|
187
|
+
ides: {
|
|
188
|
+
claude: enableClaude,
|
|
189
|
+
opencode: enableOpenCode,
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const yamlContent = yaml.stringify(config);
|
|
194
|
+
await fs.writeFile(configPath, `# Agentics Framework Configuration\n${yamlContent}`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async _createArtifactsDirectory() {
|
|
198
|
+
const dirs = [PATHS.ARTIFACTS, `${PATHS.ARTIFACTS}/planning`, `${PATHS.ARTIFACTS}/implementation`];
|
|
199
|
+
|
|
200
|
+
for (const dir of dirs) {
|
|
201
|
+
await this.fileOps.ensureDir(dir);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async _createIdeSymlinks(ideId) {
|
|
206
|
+
const ideConfig = IDE_CONFIGS[ideId];
|
|
207
|
+
const idePath = path.join(this.projectRoot, ideConfig.linkName);
|
|
208
|
+
|
|
209
|
+
// Check if IDE directory exists and is not a symlink
|
|
210
|
+
if (fs.existsSync(idePath)) {
|
|
211
|
+
const stat = fs.lstatSync(idePath);
|
|
212
|
+
if (!stat.isSymbolicLink()) {
|
|
213
|
+
throw new Error(
|
|
214
|
+
`${ideConfig.linkName} exists and is not a symlink. ` +
|
|
215
|
+
`Please remove or rename it before installing.`
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
// Remove existing symlink
|
|
219
|
+
await fs.remove(idePath);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Create IDE directory
|
|
223
|
+
await fs.ensureDir(idePath);
|
|
224
|
+
|
|
225
|
+
// Create individual symlinks for each file/directory
|
|
226
|
+
for (const mapping of SYMLINK_MAPPINGS) {
|
|
227
|
+
const sourcePath = path.join('..', PATHS.PROJECT_CONFIG, mapping.source);
|
|
228
|
+
const targetName = mapping.targets[ideId];
|
|
229
|
+
const targetPath = path.join(idePath, targetName);
|
|
230
|
+
|
|
231
|
+
// Create symlink
|
|
232
|
+
const symlinkType = mapping.isDir ? 'dir' : 'file';
|
|
233
|
+
await fs.symlink(sourcePath, targetPath, symlinkType);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async _createManifest(options) {
|
|
238
|
+
const manifestPath = path.join(this.projectRoot, PATHS.MANIFEST);
|
|
239
|
+
|
|
240
|
+
const manifest = {
|
|
241
|
+
installed_at: new Date().toISOString(),
|
|
242
|
+
version: this.getVersion(),
|
|
243
|
+
options: {
|
|
244
|
+
enableClaude: options.enableClaude,
|
|
245
|
+
enableOpenCode: options.enableOpenCode,
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
await fs.writeJson(manifestPath, manifest, { spaces: 2 });
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async _updateConfigVersion() {
|
|
253
|
+
const configPath = path.join(this.projectRoot, PATHS.CONFIG_YAML);
|
|
254
|
+
const content = await fs.readFile(configPath, 'utf8');
|
|
255
|
+
const config = yaml.parse(content);
|
|
256
|
+
|
|
257
|
+
config.ard_version = this.getVersion();
|
|
258
|
+
|
|
259
|
+
await fs.writeFile(configPath, yaml.stringify(config));
|
|
260
|
+
}
|
|
261
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI - Interactive prompts for installation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import inquirer from 'inquirer';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { IDE_CONFIGS } from './config.js';
|
|
9
|
+
|
|
10
|
+
export default class UI {
|
|
11
|
+
async promptExistingInstall(existing) {
|
|
12
|
+
const { action } = await inquirer.prompt([
|
|
13
|
+
{
|
|
14
|
+
type: 'list',
|
|
15
|
+
name: 'action',
|
|
16
|
+
message: `Agentics already installed (v${existing.version}). What would you like to do?`,
|
|
17
|
+
choices: [
|
|
18
|
+
{ name: 'Update to latest version', value: 'update' },
|
|
19
|
+
{ name: 'Reinstall (overwrites config)', value: 'reinstall' },
|
|
20
|
+
{ name: 'Cancel', value: 'cancel' },
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
return { action };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async promptInstall(existing) {
|
|
29
|
+
const answers = {};
|
|
30
|
+
|
|
31
|
+
// IDE Selection
|
|
32
|
+
const { ides } = await inquirer.prompt([
|
|
33
|
+
{
|
|
34
|
+
type: 'checkbox',
|
|
35
|
+
name: 'ides',
|
|
36
|
+
message: 'Select IDE integrations:',
|
|
37
|
+
choices: [
|
|
38
|
+
{
|
|
39
|
+
name: `${IDE_CONFIGS.claude.name} (${IDE_CONFIGS.claude.linkName}/)`,
|
|
40
|
+
value: 'claude',
|
|
41
|
+
checked: true,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: `${IDE_CONFIGS.opencode.name} (${IDE_CONFIGS.opencode.linkName}/)`,
|
|
45
|
+
value: 'opencode',
|
|
46
|
+
checked: false,
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
validate: (input) => {
|
|
50
|
+
if (input.length === 0) {
|
|
51
|
+
return 'Please select at least one IDE integration.';
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
]);
|
|
57
|
+
|
|
58
|
+
answers.enableClaude = ides.includes('claude');
|
|
59
|
+
answers.enableOpenCode = ides.includes('opencode');
|
|
60
|
+
|
|
61
|
+
// Project name (optional)
|
|
62
|
+
const { projectName } = await inquirer.prompt([
|
|
63
|
+
{
|
|
64
|
+
type: 'input',
|
|
65
|
+
name: 'projectName',
|
|
66
|
+
message: 'Project name (for config):',
|
|
67
|
+
default: this._inferProjectName(),
|
|
68
|
+
},
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
answers.projectName = projectName;
|
|
72
|
+
|
|
73
|
+
// Confirmation
|
|
74
|
+
console.log(chalk.dim('\nInstallation summary:'));
|
|
75
|
+
console.log(chalk.dim(' - Create _ard/ directory'));
|
|
76
|
+
console.log(chalk.dim(' - Copy project_config template'));
|
|
77
|
+
if (answers.enableClaude) {
|
|
78
|
+
console.log(chalk.dim(` - Symlink ${IDE_CONFIGS.claude.linkName}/ -> _ard/project_config/`));
|
|
79
|
+
}
|
|
80
|
+
if (answers.enableOpenCode) {
|
|
81
|
+
console.log(chalk.dim(` - Symlink ${IDE_CONFIGS.opencode.linkName}/ -> _ard/project_config/`));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const { confirm } = await inquirer.prompt([
|
|
85
|
+
{
|
|
86
|
+
type: 'confirm',
|
|
87
|
+
name: 'confirm',
|
|
88
|
+
message: 'Proceed with installation?',
|
|
89
|
+
default: true,
|
|
90
|
+
},
|
|
91
|
+
]);
|
|
92
|
+
|
|
93
|
+
if (!confirm) {
|
|
94
|
+
answers.cancelled = true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return answers;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
showSuccess(config) {
|
|
101
|
+
console.log(chalk.green('\n✅ Agentics Framework installed successfully!\n'));
|
|
102
|
+
|
|
103
|
+
console.log(chalk.bold('Created:'));
|
|
104
|
+
console.log(` ${chalk.cyan('_ard/')}`);
|
|
105
|
+
console.log(' ├── config.yaml');
|
|
106
|
+
console.log(' ├── project_config/');
|
|
107
|
+
console.log(' │ ├── AGENTICS.md');
|
|
108
|
+
console.log(' │ ├── settings.json');
|
|
109
|
+
console.log(' │ ├── agents/');
|
|
110
|
+
console.log(' │ ├── commands/');
|
|
111
|
+
console.log(' │ └── skills/');
|
|
112
|
+
console.log(' └── artifacts/');
|
|
113
|
+
|
|
114
|
+
if (config.enableClaude) {
|
|
115
|
+
console.log(` ${chalk.cyan('.claude/')} (symlinked to _ard/project_config/)`);
|
|
116
|
+
console.log(' └── CLAUDE.md -> AGENTICS.md');
|
|
117
|
+
}
|
|
118
|
+
if (config.enableOpenCode) {
|
|
119
|
+
console.log(` ${chalk.cyan('.opencode/')} (symlinked to _ard/project_config/)`);
|
|
120
|
+
console.log(' └── OPENCODE.md -> AGENTICS.md');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
console.log(chalk.bold('\nNext steps:'));
|
|
124
|
+
console.log(' 1. Edit _ard/project_config/AGENTICS.md with your project context');
|
|
125
|
+
console.log(' 2. Add project-specific commands to _ard/project_config/commands/');
|
|
126
|
+
console.log(' 3. Start Claude Code and use /prime to explore your codebase');
|
|
127
|
+
console.log('');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
_inferProjectName() {
|
|
131
|
+
const cwd = process.cwd();
|
|
132
|
+
return path.basename(cwd) || 'my-project';
|
|
133
|
+
}
|
|
134
|
+
}
|