cairn-work 0.2.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 +241 -0
- package/bin/cairn.js +79 -0
- package/bin/cairn.test.js +26 -0
- package/lib/agents/claude-code.js +91 -0
- package/lib/agents/clawdbot.js +85 -0
- package/lib/agents/cursor.js +107 -0
- package/lib/agents/detect.js +60 -0
- package/lib/agents/detect.test.js +108 -0
- package/lib/agents/generic.js +60 -0
- package/lib/commands/create.js +158 -0
- package/lib/commands/doctor.js +137 -0
- package/lib/commands/init.js +28 -0
- package/lib/commands/onboard.js +141 -0
- package/lib/commands/update-skill.js +69 -0
- package/lib/commands/update.js +84 -0
- package/lib/setup/workspace.js +147 -0
- package/lib/setup/workspace.test.js +123 -0
- package/package.json +55 -0
- package/skills/agent-skill.template.md +348 -0
package/README.md
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# Cairn 🏔️
|
|
2
|
+
|
|
3
|
+
AI-native project management where markdown files are the source of truth.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g cairn
|
|
9
|
+
cairn onboard
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
That's it! Cairn will detect your AI agent (Clawdbot, Claude Code, Cursor, etc.) and configure everything automatically.
|
|
13
|
+
|
|
14
|
+
## What is Cairn?
|
|
15
|
+
|
|
16
|
+
Cairn is a project management system designed for working with AI agents. Instead of databases and web UIs, Cairn uses simple markdown files that both you and your AI agent can read and edit.
|
|
17
|
+
|
|
18
|
+
**Key features:**
|
|
19
|
+
- **Files are the source of truth** - No database, just markdown
|
|
20
|
+
- **AI-native** - Designed for AI agents to understand and work with
|
|
21
|
+
- **Agent detection** - Auto-configures for Clawdbot, Claude Code, Cursor, Windsurf
|
|
22
|
+
- **Simple hierarchy** - Projects → Tasks
|
|
23
|
+
- **Status tracking** - pending, active, review, blocked, completed
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
### Global Install (Recommended)
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install -g cairn
|
|
31
|
+
# or
|
|
32
|
+
bun install -g cairn
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Test Locally
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
git clone https://github.com/gregoryehill/cairn-cli.git
|
|
39
|
+
cd cairn-cli
|
|
40
|
+
bun install
|
|
41
|
+
bun link
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Commands
|
|
45
|
+
|
|
46
|
+
### `cairn onboard`
|
|
47
|
+
|
|
48
|
+
Set up Cairn and configure your AI agent automatically.
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
cairn onboard # Auto-detect agent
|
|
52
|
+
cairn onboard --agent clawdbot # Specific agent
|
|
53
|
+
cairn onboard --force # Re-run onboarding
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### `cairn init`
|
|
57
|
+
|
|
58
|
+
Initialize workspace without agent configuration.
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
cairn init # Create workspace in current directory
|
|
62
|
+
cairn init --path /custom # Custom location
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### `cairn create`
|
|
66
|
+
|
|
67
|
+
Create projects and tasks.
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Create a project
|
|
71
|
+
cairn create project "Launch My App"
|
|
72
|
+
|
|
73
|
+
# Create a task
|
|
74
|
+
cairn create task "Set up database" \\
|
|
75
|
+
--project launch-my-app
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### `cairn doctor`
|
|
79
|
+
|
|
80
|
+
Check workspace health and fix issues.
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
cairn doctor
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### `cairn update-skill`
|
|
87
|
+
|
|
88
|
+
Update agent skill documentation.
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
cairn update-skill # Update all detected agents
|
|
92
|
+
cairn update-skill --agent cursor # Specific agent
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### `cairn update`
|
|
96
|
+
|
|
97
|
+
Check for and install CLI updates.
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
cairn update # Check npm for latest version and prompt to upgrade
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Supported Agents
|
|
104
|
+
|
|
105
|
+
Cairn auto-detects and configures:
|
|
106
|
+
|
|
107
|
+
- **[Clawdbot](https://clawd.bot)** - Full integration via skills system
|
|
108
|
+
- **Claude Code** - Workspace context integration
|
|
109
|
+
- **Cursor** - .cursorrules integration
|
|
110
|
+
- **Windsurf** - Workspace integration
|
|
111
|
+
- **Generic** - Manual setup instructions for any AI agent
|
|
112
|
+
|
|
113
|
+
## How It Works
|
|
114
|
+
|
|
115
|
+
### File Structure
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
~/cairn/
|
|
119
|
+
projects/
|
|
120
|
+
launch-my-app/
|
|
121
|
+
charter.md # Project overview
|
|
122
|
+
tasks/
|
|
123
|
+
setup-database.md # Individual task
|
|
124
|
+
deploy-api.md # Another task
|
|
125
|
+
inbox/ # Incoming ideas
|
|
126
|
+
_drafts/ # Work in progress
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Project → Task
|
|
130
|
+
|
|
131
|
+
**Project** - A goal or initiative (e.g., "Launch My App")
|
|
132
|
+
**Task** - An atomic piece of work (e.g., "Set up database")
|
|
133
|
+
|
|
134
|
+
### Status Workflow
|
|
135
|
+
|
|
136
|
+
`pending` → `active` → `review` → `completed`
|
|
137
|
+
|
|
138
|
+
Or if blocked: `active` → `blocked` → `active`
|
|
139
|
+
|
|
140
|
+
### Working with AI Agents
|
|
141
|
+
|
|
142
|
+
After onboarding, your agent understands how to:
|
|
143
|
+
- Create and update projects/tasks
|
|
144
|
+
- Follow status workflows
|
|
145
|
+
- Log work with timestamps
|
|
146
|
+
- Ask for input when blocked
|
|
147
|
+
|
|
148
|
+
**Example conversation:**
|
|
149
|
+
```
|
|
150
|
+
You: "Help me plan out my app launch"
|
|
151
|
+
Agent: "I'll create a project structure. What's your app called?"
|
|
152
|
+
You: "TaskMaster - a todo app"
|
|
153
|
+
Agent: *creates project with tasks for backend, frontend, deployment*
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Updates
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
cairn update
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Checks npm for the latest version and prompts to upgrade.
|
|
163
|
+
|
|
164
|
+
Or update manually:
|
|
165
|
+
```bash
|
|
166
|
+
npm update -g cairn
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Updates only affect the CLI and agent skills. Your workspace files are never touched.
|
|
170
|
+
|
|
171
|
+
## Configuration
|
|
172
|
+
|
|
173
|
+
Cairn uses sensible defaults:
|
|
174
|
+
- **Workspace:** Current directory or `~/cairn`
|
|
175
|
+
- **Agent detection:** Automatic
|
|
176
|
+
- **Files:** Plain markdown with YAML frontmatter
|
|
177
|
+
|
|
178
|
+
Override workspace location:
|
|
179
|
+
```bash
|
|
180
|
+
export CAIRN_WORKSPACE=/custom/path
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Philosophy
|
|
184
|
+
|
|
185
|
+
1. **Context is King** - Keep all context in one place
|
|
186
|
+
2. **Files > Databases** - Text files are portable and future-proof
|
|
187
|
+
3. **Simple Beats Complete** - Start simple, add complexity when needed
|
|
188
|
+
4. **AI-First** - Designed for human-AI collaboration
|
|
189
|
+
|
|
190
|
+
## Troubleshooting
|
|
191
|
+
|
|
192
|
+
### Agent not detected
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
cairn doctor # Check setup
|
|
196
|
+
cairn onboard --force # Re-run onboarding
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Workspace issues
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
cairn doctor # Auto-fix common issues
|
|
203
|
+
cairn init --path ~/cairn # Recreate structure
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Skill not updating
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
cairn update-skill # Refresh agent skill
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Development
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
git clone https://github.com/gregoryehill/cairn-cli.git
|
|
216
|
+
cd cairn-cli
|
|
217
|
+
bun install
|
|
218
|
+
bun link # Test locally
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Run tests:
|
|
222
|
+
```bash
|
|
223
|
+
bun test
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Contributing
|
|
227
|
+
|
|
228
|
+
Contributions welcome! Open an issue or PR on GitHub.
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
MIT © Gregory Hill
|
|
233
|
+
|
|
234
|
+
## Links
|
|
235
|
+
|
|
236
|
+
- **GitHub:** https://github.com/gregoryehill/cairn-cli
|
|
237
|
+
- **npm:** https://www.npmjs.com/package/cairn
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
Built with ❤️ for AI-human collaboration
|
package/bin/cairn.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { dirname, join } from 'path';
|
|
7
|
+
import { readFileSync } from 'fs';
|
|
8
|
+
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = dirname(__filename);
|
|
11
|
+
const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf8'));
|
|
12
|
+
|
|
13
|
+
const program = new Command();
|
|
14
|
+
|
|
15
|
+
program
|
|
16
|
+
.name('cairn')
|
|
17
|
+
.description('🏔️ AI-native project management')
|
|
18
|
+
.version(packageJson.version);
|
|
19
|
+
|
|
20
|
+
// Import commands
|
|
21
|
+
import onboard from '../lib/commands/onboard.js';
|
|
22
|
+
import init from '../lib/commands/init.js';
|
|
23
|
+
import create from '../lib/commands/create.js';
|
|
24
|
+
import doctor from '../lib/commands/doctor.js';
|
|
25
|
+
import updateSkill from '../lib/commands/update-skill.js';
|
|
26
|
+
import update from '../lib/commands/update.js';
|
|
27
|
+
|
|
28
|
+
// Onboard command - full setup with agent detection
|
|
29
|
+
program
|
|
30
|
+
.command('onboard')
|
|
31
|
+
.description('Set up Cairn and configure your AI agent')
|
|
32
|
+
.option('--force', 'Force re-onboarding even if already set up')
|
|
33
|
+
.option('--agent <type>', 'Specify agent type (clawdbot|claude-code|cursor|generic)')
|
|
34
|
+
.option('--path <path>', 'Custom workspace path')
|
|
35
|
+
.action(onboard);
|
|
36
|
+
|
|
37
|
+
// Init command - workspace only, no agent setup
|
|
38
|
+
program
|
|
39
|
+
.command('init')
|
|
40
|
+
.description('Initialize Cairn workspace (without agent configuration)')
|
|
41
|
+
.option('--path <path>', 'Custom workspace path (default: current directory)')
|
|
42
|
+
.action(init);
|
|
43
|
+
|
|
44
|
+
// Create command - create projects/tasks
|
|
45
|
+
program
|
|
46
|
+
.command('create <type> <name>')
|
|
47
|
+
.description('Create a project or task')
|
|
48
|
+
.option('--project <slug>', 'Parent project (required for tasks)')
|
|
49
|
+
.option('--assignee <name>', 'Assignee name', 'you')
|
|
50
|
+
.option('--status <status>', 'Initial status', 'pending')
|
|
51
|
+
.option('--due <date>', 'Due date (YYYY-MM-DD)')
|
|
52
|
+
.option('--description <text>', 'Short description')
|
|
53
|
+
.option('--objective <text>', 'Detailed objective')
|
|
54
|
+
.action(create);
|
|
55
|
+
|
|
56
|
+
// Doctor command - check workspace health
|
|
57
|
+
program
|
|
58
|
+
.command('doctor')
|
|
59
|
+
.description('Check workspace health and fix issues')
|
|
60
|
+
.action(doctor);
|
|
61
|
+
|
|
62
|
+
// Update-skill command - refresh agent skill
|
|
63
|
+
program
|
|
64
|
+
.command('update-skill')
|
|
65
|
+
.description('Update agent skill documentation')
|
|
66
|
+
.option('--agent <type>', 'Specific agent to update')
|
|
67
|
+
.action(updateSkill);
|
|
68
|
+
|
|
69
|
+
// Update command - check for and install updates
|
|
70
|
+
program
|
|
71
|
+
.command('update')
|
|
72
|
+
.description('Check for and install Cairn CLI updates')
|
|
73
|
+
.action(update);
|
|
74
|
+
|
|
75
|
+
// Parse and handle errors
|
|
76
|
+
program.parseAsync(process.argv).catch((error) => {
|
|
77
|
+
console.error(chalk.red('Error:'), error.message);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = dirname(__filename);
|
|
8
|
+
|
|
9
|
+
describe('CLI Executable', () => {
|
|
10
|
+
test('cairn.js exists and is executable', () => {
|
|
11
|
+
const cliPath = join(__dirname, 'cairn.js');
|
|
12
|
+
expect(existsSync(cliPath)).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('cairn.js has shebang', () => {
|
|
16
|
+
const cliPath = join(__dirname, 'cairn.js');
|
|
17
|
+
const content = require('fs').readFileSync(cliPath, 'utf-8');
|
|
18
|
+
expect(content.startsWith('#!/usr/bin/env node')).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('cairn.js imports commander', () => {
|
|
22
|
+
const cliPath = join(__dirname, 'cairn.js');
|
|
23
|
+
const content = require('fs').readFileSync(cliPath, 'utf-8');
|
|
24
|
+
expect(content).toContain('commander');
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = dirname(__filename);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Set up Cairn for Claude Code
|
|
11
|
+
* Adds agent-skill.md to workspace context
|
|
12
|
+
*/
|
|
13
|
+
export async function setupClaudeCode(workspacePath) {
|
|
14
|
+
const cwd = process.cwd();
|
|
15
|
+
const contextDir = join(cwd, '.claude-code');
|
|
16
|
+
const skillTemplate = join(__dirname, '../../skills/agent-skill.template.md');
|
|
17
|
+
const skillDest = join(contextDir, 'cairn-skill.md');
|
|
18
|
+
|
|
19
|
+
// Create context directory
|
|
20
|
+
if (!existsSync(contextDir)) {
|
|
21
|
+
mkdirSync(contextDir, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Read template and replace workspace placeholders
|
|
25
|
+
let skillContent = readFileSync(skillTemplate, 'utf-8');
|
|
26
|
+
const workspaceRoot = dirname(workspacePath);
|
|
27
|
+
|
|
28
|
+
skillContent = skillContent
|
|
29
|
+
.replace(/\{\{WORKSPACE_PATH\}\}/g, workspacePath)
|
|
30
|
+
.replace(/\{\{WORKSPACE_ROOT\}\}/g, workspaceRoot);
|
|
31
|
+
|
|
32
|
+
// Write skill to workspace
|
|
33
|
+
writeFileSync(skillDest, skillContent);
|
|
34
|
+
console.log(chalk.green('✓'), 'Cairn skill added to Claude Code workspace');
|
|
35
|
+
|
|
36
|
+
// Create instructions file
|
|
37
|
+
const instructionsPath = join(contextDir, 'cairn-instructions.md');
|
|
38
|
+
const instructions = `# Cairn Project Management
|
|
39
|
+
|
|
40
|
+
This workspace uses Cairn for project management.
|
|
41
|
+
|
|
42
|
+
**Workspace:** ${workspacePath}
|
|
43
|
+
|
|
44
|
+
**Skill documentation:** Read \`cairn-skill.md\` in this directory for the complete workflow.
|
|
45
|
+
|
|
46
|
+
**Key points:**
|
|
47
|
+
- All project files are in markdown format at ${workspacePath}
|
|
48
|
+
- Files are the source of truth (no database)
|
|
49
|
+
- Follow the project → task hierarchy
|
|
50
|
+
- Always update status before asking blocking questions
|
|
51
|
+
- Log all work in the Work Log section
|
|
52
|
+
|
|
53
|
+
Refer to cairn-skill.md for detailed instructions on working with Cairn files.
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
writeFileSync(instructionsPath, instructions);
|
|
57
|
+
console.log(chalk.green('✓'), 'Cairn instructions added');
|
|
58
|
+
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get setup instructions for Claude Code
|
|
64
|
+
*/
|
|
65
|
+
export function getClaudeCodeInstructions(workspacePath) {
|
|
66
|
+
return `
|
|
67
|
+
${chalk.bold('Claude Code Setup Complete!')}
|
|
68
|
+
|
|
69
|
+
Your Cairn workspace: ${chalk.cyan(workspacePath)}
|
|
70
|
+
Context added at: ${chalk.cyan('.claude-code/')}
|
|
71
|
+
|
|
72
|
+
${chalk.bold('Test it:')}
|
|
73
|
+
Ask Claude Code:
|
|
74
|
+
${chalk.yellow('"Read .claude-code/cairn-skill.md and help me create a project"')}
|
|
75
|
+
|
|
76
|
+
${chalk.dim('Note: You may need to reload the workspace for changes to take effect.')}
|
|
77
|
+
`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Verify Claude Code setup
|
|
82
|
+
*/
|
|
83
|
+
export function verifyClaudeCode() {
|
|
84
|
+
const contextPath = join(process.cwd(), '.claude-code', 'cairn-skill.md');
|
|
85
|
+
|
|
86
|
+
if (!existsSync(contextPath)) {
|
|
87
|
+
return { success: false, message: 'Cairn skill not found in workspace' };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return { success: true, message: 'Claude Code setup verified' };
|
|
91
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Set up Cairn for Clawdbot
|
|
12
|
+
* Installs skill file from template (agent detects its own identity)
|
|
13
|
+
*/
|
|
14
|
+
export async function setupClawdbot(workspacePath) {
|
|
15
|
+
const clawdbotSkillsDir = join(homedir(), '.clawdbot', 'skills', 'cairn');
|
|
16
|
+
const skillTemplate = join(__dirname, '../../skills/agent-skill.template.md');
|
|
17
|
+
const skillDest = join(clawdbotSkillsDir, 'SKILL.md');
|
|
18
|
+
|
|
19
|
+
// Create skills directory if it doesn't exist
|
|
20
|
+
if (!existsSync(clawdbotSkillsDir)) {
|
|
21
|
+
mkdirSync(clawdbotSkillsDir, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Read template and replace workspace placeholders only
|
|
25
|
+
let skillContent = readFileSync(skillTemplate, 'utf-8');
|
|
26
|
+
|
|
27
|
+
// Determine workspace root (parent of workspace path, e.g., /home/user from /home/user/cairn)
|
|
28
|
+
const workspaceRoot = dirname(workspacePath);
|
|
29
|
+
|
|
30
|
+
// Replace workspace placeholders (agent will detect its own name)
|
|
31
|
+
skillContent = skillContent
|
|
32
|
+
.replace(/\{\{WORKSPACE_PATH\}\}/g, workspacePath)
|
|
33
|
+
.replace(/\{\{WORKSPACE_ROOT\}\}/g, workspaceRoot);
|
|
34
|
+
|
|
35
|
+
// Write skill file
|
|
36
|
+
writeFileSync(skillDest, skillContent);
|
|
37
|
+
console.log(chalk.green('✓'), 'Cairn skill added to Clawdbot');
|
|
38
|
+
|
|
39
|
+
// Create a config file with workspace path
|
|
40
|
+
const configPath = join(clawdbotSkillsDir, 'config.json');
|
|
41
|
+
const config = {
|
|
42
|
+
workspacePath,
|
|
43
|
+
version: '0.1.0',
|
|
44
|
+
installedAt: new Date().toISOString()
|
|
45
|
+
};
|
|
46
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
47
|
+
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get setup instructions for Clawdbot
|
|
53
|
+
*/
|
|
54
|
+
export function getClawdbotInstructions(workspacePath) {
|
|
55
|
+
return `
|
|
56
|
+
${chalk.bold('Clawdbot Setup Complete!')}
|
|
57
|
+
|
|
58
|
+
Your Cairn workspace: ${chalk.cyan(workspacePath)}
|
|
59
|
+
Skill installed at: ${chalk.cyan('~/.clawdbot/skills/cairn/')}
|
|
60
|
+
|
|
61
|
+
${chalk.bold('Test it:')}
|
|
62
|
+
In your next Clawdbot session, try:
|
|
63
|
+
${chalk.yellow('"Help me create a project called Launch My App"')}
|
|
64
|
+
|
|
65
|
+
The skill will be automatically available in new sessions.
|
|
66
|
+
`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Verify Clawdbot setup
|
|
71
|
+
*/
|
|
72
|
+
export function verifyClawdbot() {
|
|
73
|
+
const clawdbotPath = join(homedir(), '.clawdbot');
|
|
74
|
+
const skillPath = join(clawdbotPath, 'skills', 'cairn', 'SKILL.md');
|
|
75
|
+
|
|
76
|
+
if (!existsSync(clawdbotPath)) {
|
|
77
|
+
return { success: false, message: 'Clawdbot directory not found' };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!existsSync(skillPath)) {
|
|
81
|
+
return { success: false, message: 'Cairn skill not installed' };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return { success: true, message: 'Clawdbot setup verified' };
|
|
85
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, copyFileSync, mkdirSync } from 'fs';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = dirname(__filename);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Set up Cairn for Cursor
|
|
11
|
+
* Adds to .cursorrules or creates .cursor/ directory with skill
|
|
12
|
+
*/
|
|
13
|
+
export async function setupCursor(workspacePath) {
|
|
14
|
+
const cwd = process.cwd();
|
|
15
|
+
const cursorRulesPath = join(cwd, '.cursorrules');
|
|
16
|
+
const skillTemplate = join(__dirname, '../../skills/agent-skill.template.md');
|
|
17
|
+
|
|
18
|
+
// Strategy 1: Add to .cursorrules if it exists
|
|
19
|
+
if (existsSync(cursorRulesPath)) {
|
|
20
|
+
const existing = readFileSync(cursorRulesPath, 'utf8');
|
|
21
|
+
const cairnRule = `
|
|
22
|
+
|
|
23
|
+
# Cairn Project Management
|
|
24
|
+
|
|
25
|
+
This project uses Cairn for project management.
|
|
26
|
+
|
|
27
|
+
**Workspace:** ${workspacePath}
|
|
28
|
+
|
|
29
|
+
When working with project management tasks:
|
|
30
|
+
1. All files are in markdown format at ${workspacePath}
|
|
31
|
+
2. Follow project → task hierarchy
|
|
32
|
+
3. Update status before asking for blocking information
|
|
33
|
+
4. Log all work with timestamps
|
|
34
|
+
|
|
35
|
+
See .cursor/cairn-skill.md for complete workflow documentation.
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
if (!existing.includes('Cairn Project Management')) {
|
|
39
|
+
writeFileSync(cursorRulesPath, existing + cairnRule);
|
|
40
|
+
console.log(chalk.green('✓'), 'Cairn rules added to .cursorrules');
|
|
41
|
+
} else {
|
|
42
|
+
console.log(chalk.yellow('⚠'), '.cursorrules already contains Cairn rules');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Strategy 2: Create .cursor/ directory with skill
|
|
47
|
+
const cursorDir = join(cwd, '.cursor');
|
|
48
|
+
if (!existsSync(cursorDir)) {
|
|
49
|
+
mkdirSync(cursorDir, { recursive: true });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Read template and replace workspace placeholders
|
|
53
|
+
let skillContent = readFileSync(skillTemplate, 'utf-8');
|
|
54
|
+
const workspaceRoot = dirname(workspacePath);
|
|
55
|
+
|
|
56
|
+
skillContent = skillContent
|
|
57
|
+
.replace(/\{\{WORKSPACE_PATH\}\}/g, workspacePath)
|
|
58
|
+
.replace(/\{\{WORKSPACE_ROOT\}\}/g, workspaceRoot);
|
|
59
|
+
|
|
60
|
+
const skillDest = join(cursorDir, 'cairn-skill.md');
|
|
61
|
+
writeFileSync(skillDest, skillContent);
|
|
62
|
+
console.log(chalk.green('✓'), 'Cairn skill added to .cursor/');
|
|
63
|
+
|
|
64
|
+
// Create config
|
|
65
|
+
const configPath = join(cursorDir, 'cairn-config.json');
|
|
66
|
+
const config = {
|
|
67
|
+
workspacePath,
|
|
68
|
+
version: '0.1.0',
|
|
69
|
+
installedAt: new Date().toISOString()
|
|
70
|
+
};
|
|
71
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
72
|
+
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get setup instructions for Cursor
|
|
78
|
+
*/
|
|
79
|
+
export function getCursorInstructions(workspacePath) {
|
|
80
|
+
return `
|
|
81
|
+
${chalk.bold('Cursor Setup Complete!')}
|
|
82
|
+
|
|
83
|
+
Your Cairn workspace: ${chalk.cyan(workspacePath)}
|
|
84
|
+
Configuration: ${chalk.cyan('.cursor/')} and ${chalk.cyan('.cursorrules')}
|
|
85
|
+
|
|
86
|
+
${chalk.bold('Test it:')}
|
|
87
|
+
Ask Cursor:
|
|
88
|
+
${chalk.yellow('"Read .cursor/cairn-skill.md and help me create a project"')}
|
|
89
|
+
|
|
90
|
+
${chalk.dim('Note: Cursor should automatically pick up .cursorrules on next prompt.')}
|
|
91
|
+
`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Verify Cursor setup
|
|
96
|
+
*/
|
|
97
|
+
export function verifyCursor() {
|
|
98
|
+
const cwd = process.cwd();
|
|
99
|
+
const rulesPath = join(cwd, '.cursorrules');
|
|
100
|
+
const skillPath = join(cwd, '.cursor', 'cairn-skill.md');
|
|
101
|
+
|
|
102
|
+
if (!existsSync(rulesPath) && !existsSync(skillPath)) {
|
|
103
|
+
return { success: false, message: 'Cairn not configured for Cursor' };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return { success: true, message: 'Cursor setup verified' };
|
|
107
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Detect which AI agent(s) are available in the environment
|
|
7
|
+
* @returns {Object} { primary: string, detected: string[] }
|
|
8
|
+
*/
|
|
9
|
+
export function detectAgents() {
|
|
10
|
+
const detected = [];
|
|
11
|
+
|
|
12
|
+
// Check for Clawdbot
|
|
13
|
+
const clawdbotPath = join(homedir(), '.clawdbot');
|
|
14
|
+
if (existsSync(clawdbotPath)) {
|
|
15
|
+
detected.push('clawdbot');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Check for Claude Code
|
|
19
|
+
// Claude Code typically sets environment variables or has workspace indicators
|
|
20
|
+
if (process.env.ANTHROPIC_API_KEY || process.env.CLAUDE_CODE_WORKSPACE) {
|
|
21
|
+
detected.push('claude-code');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Check for Cursor
|
|
25
|
+
// Cursor uses .cursorrules file or sets env variables
|
|
26
|
+
if (existsSync('.cursorrules') || process.env.CURSOR_WORKSPACE) {
|
|
27
|
+
detected.push('cursor');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Check for Windsurf
|
|
31
|
+
if (process.env.WINDSURF_WORKSPACE) {
|
|
32
|
+
detected.push('windsurf');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Determine primary agent (first detected, or generic)
|
|
36
|
+
const primary = detected[0] || 'generic';
|
|
37
|
+
|
|
38
|
+
return { primary, detected };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get agent display name
|
|
43
|
+
*/
|
|
44
|
+
export function getAgentName(type) {
|
|
45
|
+
const names = {
|
|
46
|
+
'clawdbot': 'Clawdbot',
|
|
47
|
+
'claude-code': 'Claude Code',
|
|
48
|
+
'cursor': 'Cursor',
|
|
49
|
+
'windsurf': 'Windsurf',
|
|
50
|
+
'generic': 'Generic AI Agent'
|
|
51
|
+
};
|
|
52
|
+
return names[type] || type;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Check if an agent type is supported
|
|
57
|
+
*/
|
|
58
|
+
export function isSupportedAgent(type) {
|
|
59
|
+
return ['clawdbot', 'claude-code', 'cursor', 'windsurf', 'generic'].includes(type);
|
|
60
|
+
}
|