prjct-cli 0.41.0 → 0.44.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/CHANGELOG.md +115 -0
- package/bin/prjct.ts +26 -0
- package/core/agentic/command-executor.ts +15 -5
- package/core/ai-tools/formatters.ts +302 -0
- package/core/ai-tools/generator.ts +124 -0
- package/core/ai-tools/index.ts +15 -0
- package/core/ai-tools/registry.ts +195 -0
- package/core/cli/linear.ts +440 -0
- package/core/commands/analysis.ts +36 -2
- package/core/commands/commands.ts +2 -2
- package/core/commands/planning.ts +8 -4
- package/core/commands/shipping.ts +8 -6
- package/core/commands/workflow.ts +67 -17
- package/core/index.ts +3 -1
- package/core/integrations/issue-tracker/types.ts +7 -1
- package/core/integrations/linear/client.ts +56 -24
- package/core/integrations/linear/index.ts +3 -0
- package/core/integrations/linear/sync.ts +313 -0
- package/core/schemas/index.ts +3 -0
- package/core/schemas/issues.ts +144 -0
- package/core/schemas/state.ts +3 -0
- package/core/services/sync-service.ts +71 -4
- package/core/utils/agent-stream.ts +138 -0
- package/core/utils/next-steps.ts +95 -0
- package/core/utils/output.ts +26 -0
- package/core/utils/project-credentials.ts +148 -0
- package/core/workflow/index.ts +6 -0
- package/core/workflow/state-machine.ts +185 -0
- package/dist/bin/prjct.mjs +2399 -540
- package/dist/core/infrastructure/setup.js +238 -192
- package/package.json +1 -1
- package/templates/_bases/tracker-base.md +11 -0
- package/templates/commands/done.md +18 -13
- package/templates/commands/enrich.md +152 -18
- package/templates/commands/linear.md +169 -135
- package/templates/commands/sync.md +17 -0
- package/templates/commands/task.md +20 -11
- package/templates/global/CLAUDE.md +58 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,120 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.44.0] - 2026-01-29
|
|
4
|
+
|
|
5
|
+
### Feature: Workflow State Machine (PRJ-139)
|
|
6
|
+
|
|
7
|
+
Explicit states with valid transitions for prjct workflow. Users always know what commands are available.
|
|
8
|
+
|
|
9
|
+
**States:** `idle` → `working` → `completed` → `shipped` (with `paused` branch)
|
|
10
|
+
|
|
11
|
+
**New: State Machine (`core/workflow/state-machine.ts`)**
|
|
12
|
+
- Explicit state definitions with valid transitions
|
|
13
|
+
- State info shown after each command (e.g., `📍 State: WORKING`)
|
|
14
|
+
- Next steps derived from current state
|
|
15
|
+
|
|
16
|
+
**Updated Commands**
|
|
17
|
+
- `task` — Shows state: WORKING after starting
|
|
18
|
+
- `done` — Shows state: COMPLETED
|
|
19
|
+
- `pause` — Shows state: PAUSED
|
|
20
|
+
- `resume` — Shows state: WORKING
|
|
21
|
+
|
|
22
|
+
### Feature: Auto-sync to Linear (PRJ-142 completion)
|
|
23
|
+
|
|
24
|
+
When starting/completing tasks with Linear IDs, prjct now auto-syncs status.
|
|
25
|
+
|
|
26
|
+
- `p. task PRJ-123` → Fetches issue title, marks as In Progress in Linear
|
|
27
|
+
- `p. done` → Marks issue as Done in Linear (if task has linearId)
|
|
28
|
+
|
|
29
|
+
### Feature: Sync Summary (PRJ-119)
|
|
30
|
+
|
|
31
|
+
After `p. sync`, shows a compact summary with key metrics:
|
|
32
|
+
- Stack detected
|
|
33
|
+
- Files analyzed → context files generated
|
|
34
|
+
- Agents count
|
|
35
|
+
- Time elapsed
|
|
36
|
+
|
|
37
|
+
### Improvement: Linear CLI filter by team
|
|
38
|
+
|
|
39
|
+
`list` command now uses configured team, showing only relevant issues.
|
|
40
|
+
|
|
41
|
+
### Improvement: Clean Terminal UX guidelines
|
|
42
|
+
|
|
43
|
+
Added guidelines to CLAUDE.md for user-friendly tool call descriptions.
|
|
44
|
+
|
|
45
|
+
## [0.43.0] - 2026-01-29
|
|
46
|
+
|
|
47
|
+
### Feature: Bidirectional Sync Linear ↔ prjct (PRJ-142)
|
|
48
|
+
|
|
49
|
+
When `integrations.linear.enabled === true`, prjct now syncs bidirectionally with Linear, using a local cache for offline access and reduced API calls.
|
|
50
|
+
|
|
51
|
+
**New: Local Issue Cache (`storage/issues.json`)**
|
|
52
|
+
- Full copy of assigned Linear issues stored locally
|
|
53
|
+
- 30-minute staleness threshold for automatic refresh
|
|
54
|
+
- Local-first reads: no API call if issue is cached and fresh
|
|
55
|
+
|
|
56
|
+
**New: Sync Layer (`core/integrations/linear/sync.ts`)**
|
|
57
|
+
- `pullAll()` — Fetch all assigned issues to local cache
|
|
58
|
+
- `getIssue()` — Local-first fetch with API fallback
|
|
59
|
+
- `getIssueLocal()` — Local-only fetch (no API call)
|
|
60
|
+
- `pushStatus()` — Push status changes to Linear + update cache
|
|
61
|
+
- `isStale()` — Check if cache needs refresh
|
|
62
|
+
|
|
63
|
+
**New CLI Commands**
|
|
64
|
+
- `sync` — Pull all assigned issues to local storage
|
|
65
|
+
- `sync-status` — Check local cache status (hasCache, issueCount, isStale)
|
|
66
|
+
- `get-local <id>` — Get issue from local cache without API call
|
|
67
|
+
|
|
68
|
+
**Updated State Schema**
|
|
69
|
+
- `currentTask.linearId` — Linear identifier (e.g., "PRJ-123")
|
|
70
|
+
- `currentTask.linearUuid` — Linear internal UUID for API calls
|
|
71
|
+
|
|
72
|
+
**Updated Templates**
|
|
73
|
+
- `sync.md` — Syncs Linear issues when enabled
|
|
74
|
+
- `task.md` — Uses local-first approach, tracks linearId/linearUuid
|
|
75
|
+
- `done.md` — Pushes status to Linear via sync layer
|
|
76
|
+
|
|
77
|
+
**New Files:**
|
|
78
|
+
- `core/integrations/linear/sync.ts` — Sync layer implementation
|
|
79
|
+
- `core/schemas/issues.ts` — Zod schema for issues.json
|
|
80
|
+
|
|
81
|
+
## [0.42.0] - 2026-01-29
|
|
82
|
+
|
|
83
|
+
### Feature: Linear SDK Integration with Per-Project Credentials
|
|
84
|
+
|
|
85
|
+
Linear integration now uses the native `@linear/sdk` with per-project credential storage, enabling different projects to use different Linear workspaces.
|
|
86
|
+
|
|
87
|
+
**New: Per-Project Credentials**
|
|
88
|
+
- Credentials stored at `~/.prjct-cli/projects/{projectId}/config/credentials.json`
|
|
89
|
+
- Fallback chain: project credentials → macOS keychain → environment variable
|
|
90
|
+
- Each project can connect to a different Linear workspace
|
|
91
|
+
|
|
92
|
+
**New: CLI Bridge (`core/cli/linear.ts`)**
|
|
93
|
+
- Direct access to Linear SDK from templates
|
|
94
|
+
- Commands: `setup`, `list`, `get`, `create`, `update`, `start`, `done`, `comment`, `teams`, `projects`, `status`
|
|
95
|
+
- JSON output for easy parsing by Claude
|
|
96
|
+
|
|
97
|
+
**New: `prjct linear <cmd>` Subcommand**
|
|
98
|
+
- Direct CLI access: `prjct linear status`, `prjct linear list`, etc.
|
|
99
|
+
- Auto-resolves project ID from current directory
|
|
100
|
+
|
|
101
|
+
**Updated Templates**
|
|
102
|
+
- `linear.md` — Natural language interpretation guide for Claude
|
|
103
|
+
- `enrich.md` — Uses CLI instead of MCP for ticket enrichment
|
|
104
|
+
|
|
105
|
+
**Breaking: Removed MCP-only Trackers**
|
|
106
|
+
- Removed JIRA, GitHub Issues, Monday.com support (no native SDK)
|
|
107
|
+
- Only Linear is supported (has native SDK)
|
|
108
|
+
|
|
109
|
+
**New Files:**
|
|
110
|
+
- `core/cli/linear.ts` — CLI bridge for Linear SDK
|
|
111
|
+
- `core/utils/project-credentials.ts` — Per-project credential storage
|
|
112
|
+
|
|
113
|
+
**Modified:**
|
|
114
|
+
- `bin/prjct.ts` — Added `prjct linear` subcommand
|
|
115
|
+
- `templates/commands/linear.md` — Updated with CLI execution pattern
|
|
116
|
+
- `templates/commands/enrich.md` — Linear-only, uses CLI
|
|
117
|
+
|
|
3
118
|
## [0.40.0] - 2026-01-28
|
|
4
119
|
|
|
5
120
|
### Feature: Enhanced Skill System
|
package/bin/prjct.ts
CHANGED
|
@@ -85,6 +85,32 @@ if (args[0] === 'start' || args[0] === 'setup') {
|
|
|
85
85
|
console.error('Server error:', (error as Error).message)
|
|
86
86
|
process.exitCode = 1
|
|
87
87
|
}
|
|
88
|
+
} else if (args[0] === 'linear') {
|
|
89
|
+
// Linear CLI subcommand - direct access to Linear SDK
|
|
90
|
+
const { spawn } = await import('child_process')
|
|
91
|
+
const projectPath = process.cwd()
|
|
92
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
93
|
+
|
|
94
|
+
if (!projectId) {
|
|
95
|
+
console.error('No prjct project found. Run "prjct init" first.')
|
|
96
|
+
process.exitCode = 1
|
|
97
|
+
} else {
|
|
98
|
+
// Get the path to the linear CLI
|
|
99
|
+
const linearCliPath = path.join(__dirname, '..', 'core', 'cli', 'linear.ts')
|
|
100
|
+
|
|
101
|
+
// Forward args to linear CLI, adding --project flag
|
|
102
|
+
const linearArgs = ['--project', projectId, ...args.slice(1)]
|
|
103
|
+
|
|
104
|
+
// Use bun to run the CLI
|
|
105
|
+
const child = spawn('bun', [linearCliPath, ...linearArgs], {
|
|
106
|
+
stdio: 'inherit',
|
|
107
|
+
cwd: projectPath,
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
child.on('close', (code) => {
|
|
111
|
+
process.exitCode = code || 0
|
|
112
|
+
})
|
|
113
|
+
}
|
|
88
114
|
} else if (args[0] === 'version' || args[0] === '-v' || args[0] === '--version') {
|
|
89
115
|
// Show version with provider status
|
|
90
116
|
const detection = detectAllProviders()
|
|
@@ -20,6 +20,7 @@ import groundTruth from './ground-truth'
|
|
|
20
20
|
import planMode from './plan-mode'
|
|
21
21
|
import templateExecutor from './template-executor'
|
|
22
22
|
import orchestratorExecutor from './orchestrator-executor'
|
|
23
|
+
import { agentStream } from '../utils/agent-stream'
|
|
23
24
|
|
|
24
25
|
import type {
|
|
25
26
|
OrchestratorContext,
|
|
@@ -183,12 +184,21 @@ export class CommandExecutor {
|
|
|
183
184
|
projectPath
|
|
184
185
|
)
|
|
185
186
|
|
|
186
|
-
//
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
187
|
+
// Show orchestration with agent stream
|
|
188
|
+
if (orchestratorContext.detectedDomains.length > 0) {
|
|
189
|
+
agentStream.orchestrate(orchestratorContext.detectedDomains)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Show each agent being activated
|
|
193
|
+
for (const agent of orchestratorContext.agents) {
|
|
194
|
+
const domain = agent.domain || agent.name.replace('.md', '')
|
|
195
|
+
agentStream.startAgent(agent.name, domain, `Loading ${domain} specialist...`)
|
|
196
|
+
agentStream.endAgent(true)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Show subtasks if fragmented
|
|
190
200
|
if (orchestratorContext.requiresFragmentation && orchestratorContext.subtasks) {
|
|
191
|
-
|
|
201
|
+
agentStream.status('📋', `${orchestratorContext.subtasks.length} subtasks planned`)
|
|
192
202
|
}
|
|
193
203
|
} catch (error) {
|
|
194
204
|
// Orchestration failed - log warning but continue without it
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Tool Formatters
|
|
3
|
+
*
|
|
4
|
+
* Each AI tool has different context preferences:
|
|
5
|
+
* - Claude Code: Detailed markdown with sections
|
|
6
|
+
* - Cursor: Concise rules format
|
|
7
|
+
* - Copilot: Minimal bullet points
|
|
8
|
+
* - Windsurf: Similar to Cursor
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { AIToolConfig } from './registry'
|
|
12
|
+
|
|
13
|
+
export interface ProjectContext {
|
|
14
|
+
projectId: string
|
|
15
|
+
name: string
|
|
16
|
+
version: string
|
|
17
|
+
ecosystem: string
|
|
18
|
+
projectType: string
|
|
19
|
+
languages: string[]
|
|
20
|
+
frameworks: string[]
|
|
21
|
+
repoPath: string
|
|
22
|
+
branch: string
|
|
23
|
+
fileCount: number
|
|
24
|
+
commits: number
|
|
25
|
+
hasChanges: boolean
|
|
26
|
+
commands: {
|
|
27
|
+
install: string
|
|
28
|
+
dev: string
|
|
29
|
+
test: string
|
|
30
|
+
build: string
|
|
31
|
+
lint: string
|
|
32
|
+
format: string
|
|
33
|
+
}
|
|
34
|
+
agents: {
|
|
35
|
+
workflow: string[]
|
|
36
|
+
domain: string[]
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Format context for Claude Code (CLAUDE.md)
|
|
42
|
+
* Detailed markdown with full context
|
|
43
|
+
*/
|
|
44
|
+
export function formatForClaude(ctx: ProjectContext, config: AIToolConfig): string {
|
|
45
|
+
return `# ${ctx.name} - Project Rules
|
|
46
|
+
<!-- projectId: ${ctx.projectId} -->
|
|
47
|
+
<!-- Generated: ${new Date().toISOString()} -->
|
|
48
|
+
<!-- Ecosystem: ${ctx.ecosystem} | Type: ${ctx.projectType} -->
|
|
49
|
+
|
|
50
|
+
## THIS PROJECT (${ctx.ecosystem})
|
|
51
|
+
|
|
52
|
+
**Type:** ${ctx.projectType}
|
|
53
|
+
**Path:** ${ctx.repoPath}
|
|
54
|
+
|
|
55
|
+
### Commands (USE THESE, NOT OTHERS)
|
|
56
|
+
|
|
57
|
+
| Action | Command |
|
|
58
|
+
|--------|---------|
|
|
59
|
+
| Install dependencies | \`${ctx.commands.install}\` |
|
|
60
|
+
| Run dev server | \`${ctx.commands.dev}\` |
|
|
61
|
+
| Run tests | \`${ctx.commands.test}\` |
|
|
62
|
+
| Build | \`${ctx.commands.build}\` |
|
|
63
|
+
| Lint | \`${ctx.commands.lint}\` |
|
|
64
|
+
| Format | \`${ctx.commands.format}\` |
|
|
65
|
+
|
|
66
|
+
### Code Conventions
|
|
67
|
+
|
|
68
|
+
- **Languages**: ${ctx.languages.join(', ') || 'Not detected'}
|
|
69
|
+
- **Frameworks**: ${ctx.frameworks.join(', ') || 'Not detected'}
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## PRJCT RULES
|
|
74
|
+
|
|
75
|
+
### Path Resolution
|
|
76
|
+
**ALL prjct writes go to**: \`~/.prjct-cli/projects/${ctx.projectId}/\`
|
|
77
|
+
- NEVER write to \`.prjct/\`
|
|
78
|
+
- NEVER write to \`./\` for prjct data
|
|
79
|
+
|
|
80
|
+
### Workflow
|
|
81
|
+
\`\`\`
|
|
82
|
+
p. sync → p. task "desc" → [work] → p. done → p. ship
|
|
83
|
+
\`\`\`
|
|
84
|
+
|
|
85
|
+
| Command | Action |
|
|
86
|
+
|---------|--------|
|
|
87
|
+
| \`p. sync\` | Re-analyze project |
|
|
88
|
+
| \`p. task X\` | Start task |
|
|
89
|
+
| \`p. done\` | Complete subtask |
|
|
90
|
+
| \`p. ship X\` | Ship feature |
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## PROJECT STATE
|
|
95
|
+
|
|
96
|
+
| Field | Value |
|
|
97
|
+
|-------|-------|
|
|
98
|
+
| Name | ${ctx.name} |
|
|
99
|
+
| Version | ${ctx.version} |
|
|
100
|
+
| Ecosystem | ${ctx.ecosystem} |
|
|
101
|
+
| Branch | ${ctx.branch} |
|
|
102
|
+
| Files | ~${ctx.fileCount} |
|
|
103
|
+
| Commits | ${ctx.commits} |
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## AGENTS
|
|
108
|
+
|
|
109
|
+
Load from \`~/.prjct-cli/projects/${ctx.projectId}/agents/\`:
|
|
110
|
+
|
|
111
|
+
**Workflow**: ${ctx.agents.workflow.join(', ')}
|
|
112
|
+
**Domain**: ${ctx.agents.domain.join(', ') || 'none'}
|
|
113
|
+
`
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Format context for Cursor (.cursorrules)
|
|
118
|
+
* Concise rules format, optimized for inline suggestions
|
|
119
|
+
*/
|
|
120
|
+
export function formatForCursor(ctx: ProjectContext, config: AIToolConfig): string {
|
|
121
|
+
const rules: string[] = []
|
|
122
|
+
|
|
123
|
+
// Project identity
|
|
124
|
+
rules.push(`You are working on ${ctx.name}, a ${ctx.projectType} ${ctx.ecosystem} project.`)
|
|
125
|
+
rules.push('')
|
|
126
|
+
|
|
127
|
+
// Tech stack
|
|
128
|
+
rules.push('## Tech Stack')
|
|
129
|
+
if (ctx.languages.length > 0) {
|
|
130
|
+
rules.push(`- Languages: ${ctx.languages.join(', ')}`)
|
|
131
|
+
}
|
|
132
|
+
if (ctx.frameworks.length > 0) {
|
|
133
|
+
rules.push(`- Frameworks: ${ctx.frameworks.join(', ')}`)
|
|
134
|
+
}
|
|
135
|
+
rules.push('')
|
|
136
|
+
|
|
137
|
+
// Commands
|
|
138
|
+
rules.push('## Commands')
|
|
139
|
+
rules.push(`- Install: \`${ctx.commands.install}\``)
|
|
140
|
+
rules.push(`- Dev: \`${ctx.commands.dev}\``)
|
|
141
|
+
rules.push(`- Test: \`${ctx.commands.test}\``)
|
|
142
|
+
rules.push(`- Build: \`${ctx.commands.build}\``)
|
|
143
|
+
rules.push('')
|
|
144
|
+
|
|
145
|
+
// Code style - language agnostic
|
|
146
|
+
rules.push('## Code Style')
|
|
147
|
+
rules.push(`- Follow ${ctx.ecosystem} conventions`)
|
|
148
|
+
rules.push('- Match existing code patterns in this project')
|
|
149
|
+
rules.push('- Use idiomatic constructs for the language')
|
|
150
|
+
rules.push('')
|
|
151
|
+
|
|
152
|
+
// Best practices
|
|
153
|
+
rules.push('## Best Practices')
|
|
154
|
+
rules.push('- Write clean, readable code')
|
|
155
|
+
rules.push('- Add comments only for complex logic')
|
|
156
|
+
rules.push('- Keep functions small and focused')
|
|
157
|
+
rules.push('- Handle errors appropriately')
|
|
158
|
+
rules.push('- Write tests for new functionality')
|
|
159
|
+
|
|
160
|
+
return rules.join('\n')
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Format context for GitHub Copilot (.github/copilot-instructions.md)
|
|
165
|
+
* Minimal bullet points
|
|
166
|
+
*/
|
|
167
|
+
export function formatForCopilot(ctx: ProjectContext, config: AIToolConfig): string {
|
|
168
|
+
const lines: string[] = []
|
|
169
|
+
|
|
170
|
+
lines.push('# Copilot Instructions')
|
|
171
|
+
lines.push('')
|
|
172
|
+
lines.push(`This is ${ctx.name}, a ${ctx.ecosystem} project.`)
|
|
173
|
+
lines.push('')
|
|
174
|
+
|
|
175
|
+
// Key info
|
|
176
|
+
lines.push('## Project Info')
|
|
177
|
+
lines.push(`- Type: ${ctx.projectType}`)
|
|
178
|
+
lines.push(`- Stack: ${ctx.frameworks.join(', ') || ctx.ecosystem}`)
|
|
179
|
+
lines.push('')
|
|
180
|
+
|
|
181
|
+
// Conventions
|
|
182
|
+
lines.push('## Conventions')
|
|
183
|
+
lines.push(`- Follow ${ctx.ecosystem} conventions`)
|
|
184
|
+
lines.push('- Match existing code patterns')
|
|
185
|
+
lines.push('- Keep code clean and readable')
|
|
186
|
+
lines.push('')
|
|
187
|
+
|
|
188
|
+
// Commands
|
|
189
|
+
lines.push('## Commands')
|
|
190
|
+
lines.push(`- Test: \`${ctx.commands.test}\``)
|
|
191
|
+
lines.push(`- Build: \`${ctx.commands.build}\``)
|
|
192
|
+
|
|
193
|
+
return lines.join('\n')
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Format context for Windsurf (.windsurfrules)
|
|
198
|
+
* Optimized for Cascade AI with flow-based suggestions
|
|
199
|
+
*/
|
|
200
|
+
export function formatForWindsurf(ctx: ProjectContext, config: AIToolConfig): string {
|
|
201
|
+
const rules: string[] = []
|
|
202
|
+
|
|
203
|
+
// Project identity
|
|
204
|
+
rules.push(`# ${ctx.name}`)
|
|
205
|
+
rules.push('')
|
|
206
|
+
rules.push(`${ctx.projectType} project using ${ctx.ecosystem}.`)
|
|
207
|
+
rules.push('')
|
|
208
|
+
|
|
209
|
+
// Tech stack (concise)
|
|
210
|
+
rules.push('## Stack')
|
|
211
|
+
rules.push(`- ${ctx.languages.join(', ')}`)
|
|
212
|
+
if (ctx.frameworks.length > 0) {
|
|
213
|
+
rules.push(`- ${ctx.frameworks.join(', ')}`)
|
|
214
|
+
}
|
|
215
|
+
rules.push('')
|
|
216
|
+
|
|
217
|
+
// Commands (essential only)
|
|
218
|
+
rules.push('## Commands')
|
|
219
|
+
rules.push(`\`\`\`bash`)
|
|
220
|
+
rules.push(`# Install`)
|
|
221
|
+
rules.push(ctx.commands.install)
|
|
222
|
+
rules.push(`# Dev`)
|
|
223
|
+
rules.push(ctx.commands.dev)
|
|
224
|
+
rules.push(`# Test`)
|
|
225
|
+
rules.push(ctx.commands.test)
|
|
226
|
+
rules.push(`# Build`)
|
|
227
|
+
rules.push(ctx.commands.build)
|
|
228
|
+
rules.push(`\`\`\``)
|
|
229
|
+
rules.push('')
|
|
230
|
+
|
|
231
|
+
// Code style - language agnostic
|
|
232
|
+
rules.push('## Rules')
|
|
233
|
+
rules.push(`- Follow ${ctx.ecosystem} conventions`)
|
|
234
|
+
rules.push('- Match existing project patterns')
|
|
235
|
+
rules.push('- Clean code, minimal comments')
|
|
236
|
+
rules.push('- Test new functionality')
|
|
237
|
+
|
|
238
|
+
return rules.join('\n')
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Format context for Continue.dev (.continue/config.json)
|
|
243
|
+
* JSON config with system message and context providers
|
|
244
|
+
*/
|
|
245
|
+
export function formatForContinue(ctx: ProjectContext, config: AIToolConfig): string {
|
|
246
|
+
const systemMessage = [
|
|
247
|
+
`You are working on ${ctx.name}, a ${ctx.projectType} ${ctx.ecosystem} project.`,
|
|
248
|
+
'',
|
|
249
|
+
`Stack: ${ctx.languages.join(', ')}${ctx.frameworks.length > 0 ? ` with ${ctx.frameworks.join(', ')}` : ''}`,
|
|
250
|
+
'',
|
|
251
|
+
'Commands:',
|
|
252
|
+
`- Install: ${ctx.commands.install}`,
|
|
253
|
+
`- Dev: ${ctx.commands.dev}`,
|
|
254
|
+
`- Test: ${ctx.commands.test}`,
|
|
255
|
+
`- Build: ${ctx.commands.build}`,
|
|
256
|
+
'',
|
|
257
|
+
`Follow ${ctx.ecosystem} conventions. Match existing code patterns.`,
|
|
258
|
+
].join('\n')
|
|
259
|
+
|
|
260
|
+
const continueConfig = {
|
|
261
|
+
systemMessage,
|
|
262
|
+
models: [],
|
|
263
|
+
contextProviders: [
|
|
264
|
+
{ name: 'code' },
|
|
265
|
+
{ name: 'docs' },
|
|
266
|
+
{ name: 'diff' },
|
|
267
|
+
{ name: 'terminal' },
|
|
268
|
+
{ name: 'problems' },
|
|
269
|
+
{ name: 'folder' },
|
|
270
|
+
{ name: 'codebase' },
|
|
271
|
+
],
|
|
272
|
+
slashCommands: [
|
|
273
|
+
{ name: 'edit', description: 'Edit selected code' },
|
|
274
|
+
{ name: 'comment', description: 'Add comments to code' },
|
|
275
|
+
{ name: 'share', description: 'Export conversation' },
|
|
276
|
+
{ name: 'cmd', description: 'Run terminal command' },
|
|
277
|
+
],
|
|
278
|
+
customCommands: [
|
|
279
|
+
{
|
|
280
|
+
name: 'test',
|
|
281
|
+
prompt: `Write tests for the selected code. Use the project's testing conventions. Test command: ${ctx.commands.test}`,
|
|
282
|
+
},
|
|
283
|
+
],
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return JSON.stringify(continueConfig, null, 2)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Get formatter function for a tool
|
|
291
|
+
*/
|
|
292
|
+
export function getFormatter(toolId: string): ((ctx: ProjectContext, config: AIToolConfig) => string) | null {
|
|
293
|
+
const formatters: Record<string, (ctx: ProjectContext, config: AIToolConfig) => string> = {
|
|
294
|
+
claude: formatForClaude,
|
|
295
|
+
cursor: formatForCursor,
|
|
296
|
+
copilot: formatForCopilot,
|
|
297
|
+
windsurf: formatForWindsurf,
|
|
298
|
+
continue: formatForContinue,
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return formatters[toolId] || null
|
|
302
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Tools Context Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates optimized context files for each AI coding tool.
|
|
5
|
+
* Each tool gets context in its preferred format.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs/promises'
|
|
9
|
+
import path from 'path'
|
|
10
|
+
import { AI_TOOLS, DEFAULT_AI_TOOLS, getAIToolConfig, type AIToolConfig } from './registry'
|
|
11
|
+
import { getFormatter, type ProjectContext } from './formatters'
|
|
12
|
+
|
|
13
|
+
export interface GenerateResult {
|
|
14
|
+
toolId: string
|
|
15
|
+
outputFile: string
|
|
16
|
+
outputPath: string
|
|
17
|
+
success: boolean
|
|
18
|
+
error?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Generate context files for specified AI tools
|
|
23
|
+
*/
|
|
24
|
+
export async function generateAIToolContexts(
|
|
25
|
+
context: ProjectContext,
|
|
26
|
+
globalPath: string,
|
|
27
|
+
repoPath: string,
|
|
28
|
+
toolIds: string[] = DEFAULT_AI_TOOLS
|
|
29
|
+
): Promise<GenerateResult[]> {
|
|
30
|
+
const results: GenerateResult[] = []
|
|
31
|
+
|
|
32
|
+
for (const toolId of toolIds) {
|
|
33
|
+
const config = getAIToolConfig(toolId)
|
|
34
|
+
if (!config) {
|
|
35
|
+
results.push({
|
|
36
|
+
toolId,
|
|
37
|
+
outputFile: '',
|
|
38
|
+
outputPath: '',
|
|
39
|
+
success: false,
|
|
40
|
+
error: `Unknown tool: ${toolId}`,
|
|
41
|
+
})
|
|
42
|
+
continue
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const result = await generateForTool(context, config, globalPath, repoPath)
|
|
46
|
+
results.push(result)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return results
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Generate context for a single AI tool
|
|
54
|
+
*/
|
|
55
|
+
async function generateForTool(
|
|
56
|
+
context: ProjectContext,
|
|
57
|
+
config: AIToolConfig,
|
|
58
|
+
globalPath: string,
|
|
59
|
+
repoPath: string
|
|
60
|
+
): Promise<GenerateResult> {
|
|
61
|
+
const formatter = getFormatter(config.id)
|
|
62
|
+
if (!formatter) {
|
|
63
|
+
return {
|
|
64
|
+
toolId: config.id,
|
|
65
|
+
outputFile: config.outputFile,
|
|
66
|
+
outputPath: '',
|
|
67
|
+
success: false,
|
|
68
|
+
error: `No formatter for: ${config.id}`,
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
// Generate content
|
|
74
|
+
const content = formatter(context, config)
|
|
75
|
+
|
|
76
|
+
// Determine output path
|
|
77
|
+
let outputPath: string
|
|
78
|
+
if (config.outputPath === 'repo') {
|
|
79
|
+
outputPath = path.join(repoPath, config.outputFile)
|
|
80
|
+
} else {
|
|
81
|
+
outputPath = path.join(globalPath, 'context', config.outputFile)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Ensure directory exists
|
|
85
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true })
|
|
86
|
+
|
|
87
|
+
// Write file
|
|
88
|
+
await fs.writeFile(outputPath, content, 'utf-8')
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
toolId: config.id,
|
|
92
|
+
outputFile: config.outputFile,
|
|
93
|
+
outputPath,
|
|
94
|
+
success: true,
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {
|
|
97
|
+
return {
|
|
98
|
+
toolId: config.id,
|
|
99
|
+
outputFile: config.outputFile,
|
|
100
|
+
outputPath: '',
|
|
101
|
+
success: false,
|
|
102
|
+
error: (error as Error).message,
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Get list of files that would be generated
|
|
109
|
+
*/
|
|
110
|
+
export function getOutputFiles(
|
|
111
|
+
toolIds: string[] = DEFAULT_AI_TOOLS
|
|
112
|
+
): { toolId: string; file: string; location: 'repo' | 'global' }[] {
|
|
113
|
+
return toolIds
|
|
114
|
+
.map(id => {
|
|
115
|
+
const config = AI_TOOLS[id]
|
|
116
|
+
if (!config) return null
|
|
117
|
+
return {
|
|
118
|
+
toolId: id,
|
|
119
|
+
file: config.outputFile,
|
|
120
|
+
location: config.outputPath,
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
.filter((item): item is NonNullable<typeof item> => item !== null)
|
|
124
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Tools Module
|
|
3
|
+
*
|
|
4
|
+
* Multi-agent context generation for AI coding tools.
|
|
5
|
+
* Supports: Claude Code, Cursor, Copilot, Windsurf, Continue.dev
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Auto-detection of installed tools
|
|
9
|
+
* - Language-agnostic formatters
|
|
10
|
+
* - Tool-specific output formats
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export * from './registry'
|
|
14
|
+
export * from './formatters'
|
|
15
|
+
export * from './generator'
|