devworkspace 0.3.0 → 0.3.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/CHANGELOG.md +24 -0
- package/README.md +5 -4
- package/dist/agents/claude/adapter.d.ts +3 -14
- package/dist/agents/claude/adapter.js +46 -49
- package/dist/agents/claude/adapter.js.map +1 -1
- package/dist/agents/codex/adapter.d.ts +2 -0
- package/dist/agents/codex/adapter.js +61 -0
- package/dist/agents/codex/adapter.js.map +1 -0
- package/dist/agents/copilot/adapter.d.ts +2 -0
- package/dist/agents/copilot/adapter.js +62 -0
- package/dist/agents/copilot/adapter.js.map +1 -0
- package/dist/agents/cursor/adapter.d.ts +2 -0
- package/dist/agents/cursor/adapter.js +127 -0
- package/dist/agents/cursor/adapter.js.map +1 -0
- package/dist/agents/index.d.ts +15 -0
- package/dist/agents/index.js +54 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/shared.d.ts +20 -0
- package/dist/agents/shared.js +76 -0
- package/dist/agents/shared.js.map +1 -0
- package/dist/agents/types.d.ts +16 -1
- package/dist/agents/types.js.map +1 -1
- package/dist/cli/help.js +12 -5
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/render.d.ts +2 -1
- package/dist/cli/render.js +21 -0
- package/dist/cli/render.js.map +1 -1
- package/dist/cli/run.js +17 -6
- package/dist/cli/run.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/adapters/claudePlugin.d.ts +12 -0
- package/dist/integrations/adapters/claudePlugin.js +37 -8
- package/dist/integrations/adapters/claudePlugin.js.map +1 -1
- package/dist/integrations/adapters/pluginSkill.js +5 -0
- package/dist/integrations/adapters/pluginSkill.js.map +1 -1
- package/dist/security/files.js +10 -2
- package/dist/security/files.js.map +1 -1
- package/dist/security/redact.js +1 -1
- package/dist/security/redact.js.map +1 -1
- package/dist/services/agent.d.ts +12 -2
- package/dist/services/agent.js +96 -14
- package/dist/services/agent.js.map +1 -1
- package/dist/services/analysis.js +3 -3
- package/dist/services/analysis.js.map +1 -1
- package/dist/services/doctor.js +31 -14
- package/dist/services/doctor.js.map +1 -1
- package/dist/services/reset.js +45 -5
- package/dist/services/reset.js.map +1 -1
- package/dist/services/status.d.ts +2 -0
- package/dist/services/status.js +62 -10
- package/dist/services/status.js.map +1 -1
- package/dist/services/tasks.js +6 -1
- package/dist/services/tasks.js.map +1 -1
- package/dist/tasks/routing.js +14 -10
- package/dist/tasks/routing.js.map +1 -1
- package/dist/tasks/task.js +6 -2
- package/dist/tasks/task.js.map +1 -1
- package/dist/verification/scope.js +1 -1
- package/dist/verification/scope.js.map +1 -1
- package/docs/agents.md +60 -0
- package/docs/architecture.md +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,30 @@ All notable changes are documented here. This project follows [Semantic Versioni
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
### Added
|
|
8
|
+
- **Agent-Agnostic Architecture & Multi-Agent Compatibility**:
|
|
9
|
+
- Introduced typed `AgentAdapter` interface and shared section-merging engine in `src/agents/` to decouple DevWorkspace from Claude-specific assumptions.
|
|
10
|
+
- Implemented typed adapters for **Claude Code** (`claude`), **OpenAI Codex / open agent standard** (`codex` / `agents` via `AGENTS.md`), **Cursor** (`cursor` via modern `.cursor/rules/devworkspace.mdc` and legacy `.cursorrules`), and **GitHub Copilot** (`copilot` via `.github/copilot-instructions.md`).
|
|
11
|
+
- Added `devworkspace agent install [--agent claude|codex|cursor|copilot|all]` with support for multi-agent installation (`--agent all`), conflict detection, and CRLF preservation.
|
|
12
|
+
- Added `devworkspace agent list` to inspect installed status, detected files, and capabilities (instructions, skills, plugins, CLI) for each supported coding agent.
|
|
13
|
+
- Decoupled `doctor` and `status` readiness checks from Claude Code; projects using Cursor, Copilot, or Codex achieve `Core workspace: READY` and verify instruction freshness against their configured agent.
|
|
14
|
+
- Enhanced `resetWorkspace({ includeAgentFiles: true })` to cleanly purge managed DevWorkspace blocks and files across all agent integrations while strictly preserving user content outside the managed sections.
|
|
15
|
+
- Updated verification scoping (`NOT_CODE`) to recognize `.cursor/` and `.github/` instruction and rule files (`.mdc`, `.md`) as non-code documents.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## [0.3.1] - 2026-10-14
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- **Installed Claude plugins were reported as NOT_INSTALLED** (Agent Skills, Ponytail) by `status --deep`, `integrations status` and `setup`. Real `claude plugin list --json` output is pretty-printed over many lines, and the 0.3.0 parser kept only a line starting with `[`, which was just `[`. The whole output is now parsed as JSON (a banner before the array is tolerated). Empty, malformed or unexpected output (not an array of plugins with `id`, `scope` and `enabled`) is an error, never an empty list.
|
|
22
|
+
- **A plugin listed at several scopes** is now reported by its enabled entry instead of whichever came first.
|
|
23
|
+
- **Path containment in `safeReadText()`**: Repository text reads now strictly enforce root containment, preventing relative path traversals (`..`), sibling-prefix escapes (`/repo-other` vs `/repo`), out-of-root absolute paths, and ancestor symlink escapes. Sensitive files remain protected across relative and resolved canonical paths.
|
|
24
|
+
- **Read-only task intent and completion enforcement**: Requests with explicit negative constraints (`no-fix`, `no-modify`, `plan-only`, such as "investigate without fixing") now consistently set `readOnly: true`, skip inapplicable verification command requirements, and block task completion at the task gate if files were modified during the task. Keyword routing precedence between release preparation, documentation, and defect types (like memory leaks) has been clarified.
|
|
25
|
+
- **Secret redaction pattern**: Updated Google API key detector regex to match variable 38–40 character lengths (`AIza` prefix with 34–36 characters) without false positives on adjacent identifiers.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
- When the plugin list cannot be read and the installed Claude Code supports `plugin list --json`, `setup` now stops with an explanation and installs nothing. Before, it fell back to installing portable skills through `npx`, so a parse failure alone could trigger an install. Older Claude Code versions still use the portable-skills fallback.
|
|
29
|
+
- `scripts/release-check.mjs`: Production source hygiene scanner distinguishes testing domain regex patterns containing `stub` from genuine unfinished implementation markers.
|
|
30
|
+
|
|
7
31
|
## [0.3.0] - 2026-10-13
|
|
8
32
|
|
|
9
33
|
### Added
|
package/README.md
CHANGED
|
@@ -94,7 +94,8 @@ devworkspace doctor # check workspace, integrations, security, portabilit
|
|
|
94
94
|
| `setup --integrations` | Install and verify Graphify, Agent Skills and Ponytail (`--dry-run`, `--yes`, `--scope`, `--only`) |
|
|
95
95
|
| `integrations` | `status` (observed, never assumed) and `repair` |
|
|
96
96
|
| `analyze` | Deterministic discovery; writes `discovery.json`, testing contract, onboarding prompt |
|
|
97
|
-
| `agent install` | Add
|
|
97
|
+
| `agent install` | Add managed instruction blocks for your coding agent (`--agent claude\|codex\|cursor\|copilot\|all`) |
|
|
98
|
+
| `agent list` | Show supported and detected coding agent integrations and their capabilities |
|
|
98
99
|
| `status` | Concise overview, read-only (`--deep` also runs the integration health checks; `--json` for tooling) |
|
|
99
100
|
| `doctor` | Health, security and portability checks (`--fix`, `--strict`, `--json`) |
|
|
100
101
|
| `refresh` | Incremental staleness check; writes a refresh prompt when AI re-validation is needed |
|
|
@@ -105,7 +106,7 @@ devworkspace doctor # check workspace, integrations, security, portabilit
|
|
|
105
106
|
| `decision` | Append-only, human-readable decision records |
|
|
106
107
|
| `session` | Save short session summaries (never transcripts) |
|
|
107
108
|
| `export` / `import` | Move portable knowledge between machines safely |
|
|
108
|
-
| `reset` | Delete `.devworkspace/` to start fresh (`--yes` to confirm, `--include-agent-files` to also remove
|
|
109
|
+
| `reset` | Delete `.devworkspace/` to start fresh (`--yes` to confirm, `--include-agent-files` to also remove managed agent instruction blocks/skills) |
|
|
109
110
|
| `migrate` | Upgrade workspace files to the current schema, with backups |
|
|
110
111
|
|
|
111
112
|
Global flags: `--json`, `--cwd <dir>`, `--dry-run` (where it applies), `--debug`, `--no-color`, `--ascii`.
|
|
@@ -127,7 +128,7 @@ Details: [docs/security.md](docs/security.md).
|
|
|
127
128
|
|
|
128
129
|
## Limitations (V1)
|
|
129
130
|
|
|
130
|
-
- Claude Code
|
|
131
|
+
- Multi-agent support includes typed adapters for Claude Code, OpenAI Codex (`AGENTS.md`), Cursor (`.cursor/rules` and `.cursorrules`), and GitHub Copilot (`.github/copilot-instructions.md`).
|
|
131
132
|
- Integration setup needs prerequisites (Python 3.10+ or uv for Graphify; the Claude Code CLI for native plugins, otherwise portable skills with fewer capabilities) and sometimes your approval. It was not run end to end against GitHub on macOS/Linux; see the verification notes in [docs/integrations.md](docs/integrations.md).
|
|
132
133
|
- DevWorkspace does not build Graphify's graph (that uses the model) or uninstall anything.
|
|
133
134
|
- Deep analysis is not attempted for any language; discovery covers manifests, structure and commands for Node, Python, Go, Rust, Java and PHP projects.
|
|
@@ -136,7 +137,7 @@ Details: [docs/security.md](docs/security.md).
|
|
|
136
137
|
|
|
137
138
|
## Documentation
|
|
138
139
|
|
|
139
|
-
[Getting started](docs/getting-started.md) · [Architecture](docs/architecture.md) · [Knowledge model](docs/knowledge.md) · [Testing contract](docs/testing.md) · [Security & privacy](docs/security.md) · [Integrations](docs/integrations.md) · [Claude](docs/claude.md) · [Routing](docs/routing.md) · [Portability](docs/portability.md) · [Benchmarking](docs/benchmarking.md) · [Troubleshooting](docs/troubleshooting.md) · [Contributing](docs/contributing.md)
|
|
140
|
+
[Getting started](docs/getting-started.md) · [Architecture](docs/architecture.md) · [Agents](docs/agents.md) · [Knowledge model](docs/knowledge.md) · [Testing contract](docs/testing.md) · [Security & privacy](docs/security.md) · [Integrations](docs/integrations.md) · [Claude](docs/claude.md) · [Routing](docs/routing.md) · [Portability](docs/portability.md) · [Benchmarking](docs/benchmarking.md) · [Troubleshooting](docs/troubleshooting.md) · [Contributing](docs/contributing.md)
|
|
140
141
|
|
|
141
142
|
## License
|
|
142
143
|
|
|
@@ -1,15 +1,4 @@
|
|
|
1
|
-
import type { AgentAdapter
|
|
2
|
-
import { SECTION_END, SECTION_START } from '
|
|
3
|
-
/** Merge the managed block into existing instructions, preserving everything the user wrote. */
|
|
4
|
-
export declare function mergeSection(existing: string | null, block: string, prefix?: string): {
|
|
5
|
-
content: string;
|
|
6
|
-
kind: PlannedChange['kind'];
|
|
7
|
-
note: string;
|
|
8
|
-
};
|
|
1
|
+
import type { AgentAdapter } from '../types.js';
|
|
2
|
+
import { SECTION_END, SECTION_START, SECTION_RE, mergeSection, removeManagedSection } from '../shared.js';
|
|
9
3
|
export declare const claudeAdapter: AgentAdapter;
|
|
10
|
-
|
|
11
|
-
export declare function removeManagedSection(text: string): {
|
|
12
|
-
content: string;
|
|
13
|
-
empty: boolean;
|
|
14
|
-
} | null;
|
|
15
|
-
export { SECTION_START, SECTION_END };
|
|
4
|
+
export { mergeSection, removeManagedSection, SECTION_START, SECTION_END, SECTION_RE };
|
|
@@ -1,46 +1,17 @@
|
|
|
1
|
-
import { promises as fs } from 'node:fs';
|
|
2
1
|
import path from 'node:path';
|
|
3
2
|
import { pathExists } from '../../utils/fs.js';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
return text.includes('\r\n') ? '\r\n' : '\n';
|
|
8
|
-
}
|
|
9
|
-
function withEol(text, eol) {
|
|
10
|
-
return text.replace(/\r\n/g, '\n').replace(/\n/g, eol);
|
|
11
|
-
}
|
|
12
|
-
async function readIfExists(file) {
|
|
13
|
-
try {
|
|
14
|
-
return await fs.readFile(file, 'utf8');
|
|
15
|
-
}
|
|
16
|
-
catch {
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
/** Merge the managed block into existing instructions, preserving everything the user wrote. */
|
|
21
|
-
export function mergeSection(existing, block, prefix = '') {
|
|
22
|
-
if (existing === null) {
|
|
23
|
-
return { content: `${prefix}${block}\n`, kind: 'create', note: 'create with managed section' };
|
|
24
|
-
}
|
|
25
|
-
const eol = detectEol(existing);
|
|
26
|
-
const blockEol = withEol(block, eol);
|
|
27
|
-
const hasStart = existing.includes('<!-- devworkspace:start');
|
|
28
|
-
const hasEnd = existing.includes(SECTION_END);
|
|
29
|
-
if (hasStart !== hasEnd) {
|
|
30
|
-
return { content: existing, kind: 'conflict', note: 'managed section markers are unbalanced; fix or remove the stray marker by hand' };
|
|
31
|
-
}
|
|
32
|
-
if (hasStart && hasEnd) {
|
|
33
|
-
const next = existing.replace(SECTION_RE, () => blockEol);
|
|
34
|
-
return next === existing
|
|
35
|
-
? { content: existing, kind: 'unchanged', note: 'managed section already current' }
|
|
36
|
-
: { content: next, kind: 'update', note: 'refresh managed section; content outside it is untouched' };
|
|
37
|
-
}
|
|
38
|
-
const sep = existing.endsWith('\n') ? eol : eol + eol;
|
|
39
|
-
return { content: `${existing}${sep}${blockEol}${eol}`, kind: 'update', note: 'append managed section; existing instructions preserved' };
|
|
40
|
-
}
|
|
3
|
+
import { which } from '../../utils/proc.js';
|
|
4
|
+
import { SECTION_END, SECTION_START, SECTION_RE, detectEol, withEol, readIfExists, mergeSection, removeManagedSection, } from '../shared.js';
|
|
5
|
+
import { SKILL_MARKER, SKILL_PATH, claudeSection, claudeSkill } from './content.js';
|
|
41
6
|
export const claudeAdapter = {
|
|
42
7
|
id: 'claude',
|
|
43
8
|
displayName: 'Claude Code',
|
|
9
|
+
capabilities: {
|
|
10
|
+
instructions: true,
|
|
11
|
+
skills: true,
|
|
12
|
+
plugins: true,
|
|
13
|
+
cli: true,
|
|
14
|
+
},
|
|
44
15
|
async detectExisting(root) {
|
|
45
16
|
const found = [];
|
|
46
17
|
for (const rel of ['CLAUDE.md', '.claude/CLAUDE.md', 'CLAUDE.local.md', 'AGENTS.md']) {
|
|
@@ -49,6 +20,11 @@ export const claudeAdapter = {
|
|
|
49
20
|
}
|
|
50
21
|
return found;
|
|
51
22
|
},
|
|
23
|
+
async detectAvailable(root) {
|
|
24
|
+
if (await which('claude'))
|
|
25
|
+
return true;
|
|
26
|
+
return pathExists(path.join(root, '.claude'));
|
|
27
|
+
},
|
|
52
28
|
async plan(ctx) {
|
|
53
29
|
const changes = [];
|
|
54
30
|
const root = ctx.root;
|
|
@@ -92,16 +68,37 @@ export const claudeAdapter = {
|
|
|
92
68
|
}
|
|
93
69
|
return changes;
|
|
94
70
|
},
|
|
71
|
+
async unplan(root) {
|
|
72
|
+
const changes = [];
|
|
73
|
+
for (const rel of ['CLAUDE.md', '.claude/CLAUDE.md']) {
|
|
74
|
+
const full = path.join(root, ...rel.split('/'));
|
|
75
|
+
const text = await readIfExists(full);
|
|
76
|
+
if (text === null)
|
|
77
|
+
continue;
|
|
78
|
+
const removed = removeManagedSection(text);
|
|
79
|
+
if (!removed)
|
|
80
|
+
continue;
|
|
81
|
+
changes.push({
|
|
82
|
+
path: rel,
|
|
83
|
+
kind: 'update',
|
|
84
|
+
mode: 'section',
|
|
85
|
+
content: removed.content,
|
|
86
|
+
note: removed.empty ? 'delete file (contained only DevWorkspace block)' : 'remove managed DevWorkspace block',
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
const skillAbs = path.join(root, ...SKILL_PATH.split('/'));
|
|
90
|
+
const skillText = await readIfExists(skillAbs);
|
|
91
|
+
if (skillText !== null && skillText.includes(SKILL_MARKER)) {
|
|
92
|
+
changes.push({
|
|
93
|
+
path: SKILL_PATH,
|
|
94
|
+
kind: 'update',
|
|
95
|
+
mode: 'owned',
|
|
96
|
+
content: '',
|
|
97
|
+
note: 'delete DevWorkspace skill',
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return changes;
|
|
101
|
+
},
|
|
95
102
|
};
|
|
96
|
-
|
|
97
|
-
export function removeManagedSection(text) {
|
|
98
|
-
if (!SECTION_RE.test(text))
|
|
99
|
-
return null;
|
|
100
|
-
const eol = detectEol(text);
|
|
101
|
-
const blank = eol === '\r\n' ? '(?:\\r\\n){3,}' : '\\n{3,}';
|
|
102
|
-
const next = text.replace(SECTION_RE, '').replace(new RegExp(blank, 'g'), eol + eol);
|
|
103
|
-
const trimmed = next.trim();
|
|
104
|
-
return { content: trimmed === '' ? '' : trimmed + eol, empty: trimmed === '' };
|
|
105
|
-
}
|
|
106
|
-
export { SECTION_START, SECTION_END };
|
|
103
|
+
export { mergeSection, removeManagedSection, SECTION_START, SECTION_END, SECTION_RE };
|
|
107
104
|
//# sourceMappingURL=adapter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../src/agents/claude/adapter.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport type { AgentAdapter, AgentContext, PlannedChange } from '../types.js';\nimport { pathExists } from '../../utils/fs.js';\nimport { SECTION_END, SECTION_START, SKILL_MARKER, SKILL_PATH, claudeSection, claudeSkill } from './content.js';\n\nconst SECTION_RE = /<!-- devworkspace:start[^>]*-->[\\s\\S]*?<!-- devworkspace:end -->/;\n\nfunction detectEol(text: string): '\\r\\n' | '\\n' {\n return text.includes('\\r\\n') ? '\\r\\n' : '\\n';\n}\n\nfunction withEol(text: string, eol: '\\r\\n' | '\\n'): string {\n return text.replace(/\\r\\n/g, '\\n').replace(/\\n/g, eol);\n}\n\nasync function readIfExists(file: string): Promise<string | null> {\n try {\n return await fs.readFile(file, 'utf8');\n } catch {\n return null;\n }\n}\n\n/** Merge the managed block into existing instructions, preserving everything the user wrote. */\nexport function mergeSection(existing: string | null, block: string, prefix = ''): { content: string; kind: PlannedChange['kind']; note: string } {\n if (existing === null) {\n return { content: `${prefix}${block}\\n`, kind: 'create', note: 'create with managed section' };\n }\n const eol = detectEol(existing);\n const blockEol = withEol(block, eol);\n const hasStart = existing.includes('<!-- devworkspace:start');\n const hasEnd = existing.includes(SECTION_END);\n if (hasStart !== hasEnd) {\n return { content: existing, kind: 'conflict', note: 'managed section markers are unbalanced; fix or remove the stray marker by hand' };\n }\n if (hasStart && hasEnd) {\n const next = existing.replace(SECTION_RE, () => blockEol);\n return next === existing\n ? { content: existing, kind: 'unchanged', note: 'managed section already current' }\n : { content: next, kind: 'update', note: 'refresh managed section; content outside it is untouched' };\n }\n const sep = existing.endsWith('\\n') ? eol : eol + eol;\n return { content: `${existing}${sep}${blockEol}${eol}`, kind: 'update', note: 'append managed section; existing instructions preserved' };\n}\n\nexport const claudeAdapter: AgentAdapter = {\n id: 'claude',\n displayName: 'Claude Code',\n\n async detectExisting(root: string): Promise<string[]> {\n const found: string[] = [];\n for (const rel of ['CLAUDE.md', '.claude/CLAUDE.md', 'CLAUDE.local.md', 'AGENTS.md']) {\n if (await pathExists(path.join(root, ...rel.split('/')))) found.push(rel);\n }\n return found;\n },\n\n async plan(ctx: AgentContext): Promise<PlannedChange[]> {\n const changes: PlannedChange[] = [];\n const root = ctx.root;\n const rootClaude = path.join(root, 'CLAUDE.md');\n const dotClaude = path.join(root, '.claude', 'CLAUDE.md');\n const rootText = await readIfExists(rootClaude);\n const dotText = await readIfExists(dotClaude);\n const hasAgents = await pathExists(path.join(root, 'AGENTS.md'));\n\n // Prefer the file the project already uses. Claude Code reads AGENTS.md only when no CLAUDE.md exists,\n // so a new CLAUDE.md must import it or the existing instructions would silently stop loading.\n let target = 'CLAUDE.md';\n let text = rootText;\n if (rootText === null && dotText !== null) {\n target = '.claude/CLAUDE.md';\n text = dotText;\n }\n const prefix = text === null && hasAgents ? '@AGENTS.md\\n\\n' : '';\n const merged = mergeSection(text, claudeSection(ctx.projectName), prefix);\n changes.push({\n path: target,\n kind: merged.kind,\n mode: 'section',\n content: merged.content,\n note: merged.note + (prefix ? ' (imports existing AGENTS.md)' : ''),\n });\n\n const skillAbs = path.join(root, ...SKILL_PATH.split('/'));\n const skillExisting = await readIfExists(skillAbs);\n const eol = skillExisting ? detectEol(skillExisting) : '\\n';\n const skill = withEol(claudeSkill(), eol);\n if (skillExisting === null) {\n changes.push({ path: SKILL_PATH, kind: 'create', mode: 'owned', content: skill, note: 'create DevWorkspace skill' });\n } else if (!skillExisting.includes(SKILL_MARKER)) {\n changes.push({ path: SKILL_PATH, kind: 'conflict', mode: 'owned', content: skillExisting, note: 'a user-authored skill already uses this path; left untouched' });\n } else if (skillExisting === skill) {\n changes.push({ path: SKILL_PATH, kind: 'unchanged', mode: 'owned', content: skill, note: 'skill already current' });\n } else {\n changes.push({ path: SKILL_PATH, kind: 'update', mode: 'owned', content: skill, note: 'refresh DevWorkspace skill' });\n }\n return changes;\n },\n};\n\n/** Remove the managed block (and nothing else). Returns null if there is no balanced block. */\nexport function removeManagedSection(text: string): { content: string; empty: boolean } | null {\n if (!SECTION_RE.test(text)) return null;\n const eol = detectEol(text);\n const blank = eol === '\\r\\n' ? '(?:\\\\r\\\\n){3,}' : '\\\\n{3,}';\n const next = text.replace(SECTION_RE, '').replace(new RegExp(blank, 'g'), eol + eol);\n const trimmed = next.trim();\n return { content: trimmed === '' ? '' : trimmed + eol, empty: trimmed === '' };\n}\n\nexport { SECTION_START, SECTION_END };\n"]}
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../src/agents/claude/adapter.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path';\nimport type { AgentAdapter, AgentContext, PlannedChange } from '../types.js';\nimport { pathExists } from '../../utils/fs.js';\nimport { which } from '../../utils/proc.js';\nimport {\n SECTION_END,\n SECTION_START,\n SECTION_RE,\n detectEol,\n withEol,\n readIfExists,\n mergeSection,\n removeManagedSection,\n} from '../shared.js';\nimport { SKILL_MARKER, SKILL_PATH, claudeSection, claudeSkill } from './content.js';\n\nexport const claudeAdapter: AgentAdapter = {\n id: 'claude',\n displayName: 'Claude Code',\n capabilities: {\n instructions: true,\n skills: true,\n plugins: true,\n cli: true,\n },\n\n async detectExisting(root: string): Promise<string[]> {\n const found: string[] = [];\n for (const rel of ['CLAUDE.md', '.claude/CLAUDE.md', 'CLAUDE.local.md', 'AGENTS.md']) {\n if (await pathExists(path.join(root, ...rel.split('/')))) found.push(rel);\n }\n return found;\n },\n\n async detectAvailable(root: string): Promise<boolean> {\n if (await which('claude')) return true;\n return pathExists(path.join(root, '.claude'));\n },\n\n async plan(ctx: AgentContext): Promise<PlannedChange[]> {\n const changes: PlannedChange[] = [];\n const root = ctx.root;\n const rootClaude = path.join(root, 'CLAUDE.md');\n const dotClaude = path.join(root, '.claude', 'CLAUDE.md');\n const rootText = await readIfExists(rootClaude);\n const dotText = await readIfExists(dotClaude);\n const hasAgents = await pathExists(path.join(root, 'AGENTS.md'));\n\n // Prefer the file the project already uses. Claude Code reads AGENTS.md only when no CLAUDE.md exists,\n // so a new CLAUDE.md must import it or the existing instructions would silently stop loading.\n let target = 'CLAUDE.md';\n let text = rootText;\n if (rootText === null && dotText !== null) {\n target = '.claude/CLAUDE.md';\n text = dotText;\n }\n const prefix = text === null && hasAgents ? '@AGENTS.md\\n\\n' : '';\n const merged = mergeSection(text, claudeSection(ctx.projectName), prefix);\n changes.push({\n path: target,\n kind: merged.kind,\n mode: 'section',\n content: merged.content,\n note: merged.note + (prefix ? ' (imports existing AGENTS.md)' : ''),\n });\n\n const skillAbs = path.join(root, ...SKILL_PATH.split('/'));\n const skillExisting = await readIfExists(skillAbs);\n const eol = skillExisting ? detectEol(skillExisting) : '\\n';\n const skill = withEol(claudeSkill(), eol);\n if (skillExisting === null) {\n changes.push({ path: SKILL_PATH, kind: 'create', mode: 'owned', content: skill, note: 'create DevWorkspace skill' });\n } else if (!skillExisting.includes(SKILL_MARKER)) {\n changes.push({ path: SKILL_PATH, kind: 'conflict', mode: 'owned', content: skillExisting, note: 'a user-authored skill already uses this path; left untouched' });\n } else if (skillExisting === skill) {\n changes.push({ path: SKILL_PATH, kind: 'unchanged', mode: 'owned', content: skill, note: 'skill already current' });\n } else {\n changes.push({ path: SKILL_PATH, kind: 'update', mode: 'owned', content: skill, note: 'refresh DevWorkspace skill' });\n }\n return changes;\n },\n\n async unplan(root: string): Promise<PlannedChange[]> {\n const changes: PlannedChange[] = [];\n for (const rel of ['CLAUDE.md', '.claude/CLAUDE.md']) {\n const full = path.join(root, ...rel.split('/'));\n const text = await readIfExists(full);\n if (text === null) continue;\n const removed = removeManagedSection(text);\n if (!removed) continue;\n changes.push({\n path: rel,\n kind: 'update',\n mode: 'section',\n content: removed.content,\n note: removed.empty ? 'delete file (contained only DevWorkspace block)' : 'remove managed DevWorkspace block',\n });\n }\n const skillAbs = path.join(root, ...SKILL_PATH.split('/'));\n const skillText = await readIfExists(skillAbs);\n if (skillText !== null && skillText.includes(SKILL_MARKER)) {\n changes.push({\n path: SKILL_PATH,\n kind: 'update',\n mode: 'owned',\n content: '',\n note: 'delete DevWorkspace skill',\n });\n }\n return changes;\n },\n};\n\nexport { mergeSection, removeManagedSection, SECTION_START, SECTION_END, SECTION_RE };\n"]}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { pathExists } from '../../utils/fs.js';
|
|
3
|
+
import { which } from '../../utils/proc.js';
|
|
4
|
+
import { buildDevWorkspaceSection, mergeSection, readIfExists, removeManagedSection, } from '../shared.js';
|
|
5
|
+
export const codexAdapter = {
|
|
6
|
+
id: 'codex',
|
|
7
|
+
displayName: 'OpenAI Codex / AGENTS.md',
|
|
8
|
+
capabilities: {
|
|
9
|
+
instructions: true,
|
|
10
|
+
skills: false,
|
|
11
|
+
plugins: false,
|
|
12
|
+
cli: false,
|
|
13
|
+
},
|
|
14
|
+
async detectExisting(root) {
|
|
15
|
+
const found = [];
|
|
16
|
+
if (await pathExists(path.join(root, 'AGENTS.md')))
|
|
17
|
+
found.push('AGENTS.md');
|
|
18
|
+
return found;
|
|
19
|
+
},
|
|
20
|
+
async detectAvailable(root) {
|
|
21
|
+
if (await pathExists(path.join(root, 'AGENTS.md')))
|
|
22
|
+
return true;
|
|
23
|
+
return (await which('codex')) !== null;
|
|
24
|
+
},
|
|
25
|
+
async plan(ctx) {
|
|
26
|
+
const target = 'AGENTS.md';
|
|
27
|
+
const full = path.join(ctx.root, target);
|
|
28
|
+
const existing = await readIfExists(full);
|
|
29
|
+
const block = buildDevWorkspaceSection(ctx.projectName, 'OpenAI Codex / AGENTS.md');
|
|
30
|
+
const merged = mergeSection(existing, block);
|
|
31
|
+
return [
|
|
32
|
+
{
|
|
33
|
+
path: target,
|
|
34
|
+
kind: merged.kind,
|
|
35
|
+
mode: 'section',
|
|
36
|
+
content: merged.content,
|
|
37
|
+
note: merged.note,
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
},
|
|
41
|
+
async unplan(root) {
|
|
42
|
+
const target = 'AGENTS.md';
|
|
43
|
+
const full = path.join(root, target);
|
|
44
|
+
const text = await readIfExists(full);
|
|
45
|
+
if (text === null)
|
|
46
|
+
return [];
|
|
47
|
+
const removed = removeManagedSection(text);
|
|
48
|
+
if (!removed)
|
|
49
|
+
return [];
|
|
50
|
+
return [
|
|
51
|
+
{
|
|
52
|
+
path: target,
|
|
53
|
+
kind: 'update',
|
|
54
|
+
mode: 'section',
|
|
55
|
+
content: removed.content,
|
|
56
|
+
note: removed.empty ? 'delete file (contained only DevWorkspace block)' : 'remove managed DevWorkspace block',
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../src/agents/codex/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EACL,wBAAwB,EACxB,YAAY,EACZ,YAAY,EACZ,oBAAoB,GACrB,MAAM,cAAc,CAAC;AAEtB,MAAM,CAAC,MAAM,YAAY,GAAiB;IACxC,EAAE,EAAE,OAAO;IACX,WAAW,EAAE,0BAA0B;IACvC,YAAY,EAAE;QACZ,YAAY,EAAE,IAAI;QAClB,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,KAAK;QACd,GAAG,EAAE,KAAK;KACX;IAED,KAAK,CAAC,cAAc,CAAC,IAAY;QAC/B,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,IAAI,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC5E,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,IAAY;QAChC,IAAI,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAChE,OAAO,CAAC,MAAM,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAiB;QAC1B,MAAM,MAAM,GAAG,WAAW,CAAC;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACzC,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,KAAK,GAAG,wBAAwB,CAAC,GAAG,CAAC,WAAW,EAAE,0BAA0B,CAAC,CAAC;QACpF,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAE7C,OAAO;YACL;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,IAAI,EAAE,MAAM,CAAC,IAAI;aAClB;SACF,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAY;QACvB,MAAM,MAAM,GAAG,WAAW,CAAC;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACrC,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAC;QAExB,OAAO;YACL;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,iDAAiD,CAAC,CAAC,CAAC,mCAAmC;aAC9G;SACF,CAAC;IACJ,CAAC;CACF,CAAC","sourcesContent":["import path from 'node:path';\nimport type { AgentAdapter, AgentContext, PlannedChange } from '../types.js';\nimport { pathExists } from '../../utils/fs.js';\nimport { which } from '../../utils/proc.js';\nimport {\n buildDevWorkspaceSection,\n mergeSection,\n readIfExists,\n removeManagedSection,\n} from '../shared.js';\n\nexport const codexAdapter: AgentAdapter = {\n id: 'codex',\n displayName: 'OpenAI Codex / AGENTS.md',\n capabilities: {\n instructions: true,\n skills: false,\n plugins: false,\n cli: false,\n },\n\n async detectExisting(root: string): Promise<string[]> {\n const found: string[] = [];\n if (await pathExists(path.join(root, 'AGENTS.md'))) found.push('AGENTS.md');\n return found;\n },\n\n async detectAvailable(root: string): Promise<boolean> {\n if (await pathExists(path.join(root, 'AGENTS.md'))) return true;\n return (await which('codex')) !== null;\n },\n\n async plan(ctx: AgentContext): Promise<PlannedChange[]> {\n const target = 'AGENTS.md';\n const full = path.join(ctx.root, target);\n const existing = await readIfExists(full);\n const block = buildDevWorkspaceSection(ctx.projectName, 'OpenAI Codex / AGENTS.md');\n const merged = mergeSection(existing, block);\n\n return [\n {\n path: target,\n kind: merged.kind,\n mode: 'section',\n content: merged.content,\n note: merged.note,\n },\n ];\n },\n\n async unplan(root: string): Promise<PlannedChange[]> {\n const target = 'AGENTS.md';\n const full = path.join(root, target);\n const text = await readIfExists(full);\n if (text === null) return [];\n const removed = removeManagedSection(text);\n if (!removed) return [];\n\n return [\n {\n path: target,\n kind: 'update',\n mode: 'section',\n content: removed.content,\n note: removed.empty ? 'delete file (contained only DevWorkspace block)' : 'remove managed DevWorkspace block',\n },\n ];\n },\n};\n\n"]}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { pathExists } from '../../utils/fs.js';
|
|
3
|
+
import { which } from '../../utils/proc.js';
|
|
4
|
+
import { buildDevWorkspaceSection, mergeSection, readIfExists, removeManagedSection, } from '../shared.js';
|
|
5
|
+
const COPILOT_PATH = '.github/copilot-instructions.md';
|
|
6
|
+
export const copilotAdapter = {
|
|
7
|
+
id: 'copilot',
|
|
8
|
+
displayName: 'GitHub Copilot',
|
|
9
|
+
capabilities: {
|
|
10
|
+
instructions: true,
|
|
11
|
+
skills: false,
|
|
12
|
+
plugins: false,
|
|
13
|
+
cli: false,
|
|
14
|
+
},
|
|
15
|
+
async detectExisting(root) {
|
|
16
|
+
const found = [];
|
|
17
|
+
if (await pathExists(path.join(root, ...COPILOT_PATH.split('/'))))
|
|
18
|
+
found.push(COPILOT_PATH);
|
|
19
|
+
return found;
|
|
20
|
+
},
|
|
21
|
+
async detectAvailable(root) {
|
|
22
|
+
if (await pathExists(path.join(root, ...COPILOT_PATH.split('/'))))
|
|
23
|
+
return true;
|
|
24
|
+
if (await pathExists(path.join(root, '.github')))
|
|
25
|
+
return true;
|
|
26
|
+
return (await which('gh')) !== null;
|
|
27
|
+
},
|
|
28
|
+
async plan(ctx) {
|
|
29
|
+
const full = path.join(ctx.root, ...COPILOT_PATH.split('/'));
|
|
30
|
+
const existing = await readIfExists(full);
|
|
31
|
+
const block = buildDevWorkspaceSection(ctx.projectName, 'GitHub Copilot');
|
|
32
|
+
const merged = mergeSection(existing, block);
|
|
33
|
+
return [
|
|
34
|
+
{
|
|
35
|
+
path: COPILOT_PATH,
|
|
36
|
+
kind: merged.kind,
|
|
37
|
+
mode: 'section',
|
|
38
|
+
content: merged.content,
|
|
39
|
+
note: `${merged.note} (${COPILOT_PATH})`,
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
},
|
|
43
|
+
async unplan(root) {
|
|
44
|
+
const full = path.join(root, ...COPILOT_PATH.split('/'));
|
|
45
|
+
const text = await readIfExists(full);
|
|
46
|
+
if (text === null)
|
|
47
|
+
return [];
|
|
48
|
+
const removed = removeManagedSection(text);
|
|
49
|
+
if (!removed)
|
|
50
|
+
return [];
|
|
51
|
+
return [
|
|
52
|
+
{
|
|
53
|
+
path: COPILOT_PATH,
|
|
54
|
+
kind: 'update',
|
|
55
|
+
mode: 'section',
|
|
56
|
+
content: removed.content,
|
|
57
|
+
note: removed.empty ? 'delete file (contained only DevWorkspace block)' : 'remove managed DevWorkspace block',
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../src/agents/copilot/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EACL,wBAAwB,EACxB,YAAY,EACZ,YAAY,EACZ,oBAAoB,GACrB,MAAM,cAAc,CAAC;AAEtB,MAAM,YAAY,GAAG,iCAAiC,CAAC;AAEvD,MAAM,CAAC,MAAM,cAAc,GAAiB;IAC1C,EAAE,EAAE,SAAS;IACb,WAAW,EAAE,gBAAgB;IAC7B,YAAY,EAAE;QACZ,YAAY,EAAE,IAAI;QAClB,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,KAAK;QACd,GAAG,EAAE,KAAK;KACX;IAED,KAAK,CAAC,cAAc,CAAC,IAAY;QAC/B,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,IAAI,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC5F,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,IAAY;QAChC,IAAI,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAC/E,IAAI,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAC9D,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAiB;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7D,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,KAAK,GAAG,wBAAwB,CAAC,GAAG,CAAC,WAAW,EAAE,gBAAgB,CAAC,CAAC;QAC1E,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAE7C,OAAO;YACL;gBACE,IAAI,EAAE,YAAY;gBAClB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,IAAI,EAAE,GAAG,MAAM,CAAC,IAAI,KAAK,YAAY,GAAG;aACzC;SACF,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAY;QACvB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;QACzD,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAC;QAExB,OAAO;YACL;gBACE,IAAI,EAAE,YAAY;gBAClB,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,iDAAiD,CAAC,CAAC,CAAC,mCAAmC;aAC9G;SACF,CAAC;IACJ,CAAC;CACF,CAAC","sourcesContent":["import path from 'node:path';\nimport type { AgentAdapter, AgentContext, PlannedChange } from '../types.js';\nimport { pathExists } from '../../utils/fs.js';\nimport { which } from '../../utils/proc.js';\nimport {\n buildDevWorkspaceSection,\n mergeSection,\n readIfExists,\n removeManagedSection,\n} from '../shared.js';\n\nconst COPILOT_PATH = '.github/copilot-instructions.md';\n\nexport const copilotAdapter: AgentAdapter = {\n id: 'copilot',\n displayName: 'GitHub Copilot',\n capabilities: {\n instructions: true,\n skills: false,\n plugins: false,\n cli: false,\n },\n\n async detectExisting(root: string): Promise<string[]> {\n const found: string[] = [];\n if (await pathExists(path.join(root, ...COPILOT_PATH.split('/')))) found.push(COPILOT_PATH);\n return found;\n },\n\n async detectAvailable(root: string): Promise<boolean> {\n if (await pathExists(path.join(root, ...COPILOT_PATH.split('/')))) return true;\n if (await pathExists(path.join(root, '.github'))) return true;\n return (await which('gh')) !== null;\n },\n\n async plan(ctx: AgentContext): Promise<PlannedChange[]> {\n const full = path.join(ctx.root, ...COPILOT_PATH.split('/'));\n const existing = await readIfExists(full);\n const block = buildDevWorkspaceSection(ctx.projectName, 'GitHub Copilot');\n const merged = mergeSection(existing, block);\n\n return [\n {\n path: COPILOT_PATH,\n kind: merged.kind,\n mode: 'section',\n content: merged.content,\n note: `${merged.note} (${COPILOT_PATH})`,\n },\n ];\n },\n\n async unplan(root: string): Promise<PlannedChange[]> {\n const full = path.join(root, ...COPILOT_PATH.split('/'));\n const text = await readIfExists(full);\n if (text === null) return [];\n const removed = removeManagedSection(text);\n if (!removed) return [];\n\n return [\n {\n path: COPILOT_PATH,\n kind: 'update',\n mode: 'section',\n content: removed.content,\n note: removed.empty ? 'delete file (contained only DevWorkspace block)' : 'remove managed DevWorkspace block',\n },\n ];\n },\n};\n\n"]}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { pathExists } from '../../utils/fs.js';
|
|
3
|
+
import { which } from '../../utils/proc.js';
|
|
4
|
+
import { buildDevWorkspaceSection, mergeSection, readIfExists, removeManagedSection, } from '../shared.js';
|
|
5
|
+
const CURSOR_MDC_PATH = '.cursor/rules/devworkspace.mdc';
|
|
6
|
+
export const cursorAdapter = {
|
|
7
|
+
id: 'cursor',
|
|
8
|
+
displayName: 'Cursor',
|
|
9
|
+
capabilities: {
|
|
10
|
+
instructions: true,
|
|
11
|
+
skills: false,
|
|
12
|
+
plugins: false,
|
|
13
|
+
cli: false,
|
|
14
|
+
},
|
|
15
|
+
async detectExisting(root) {
|
|
16
|
+
const found = [];
|
|
17
|
+
if (await pathExists(path.join(root, '.cursorrules')))
|
|
18
|
+
found.push('.cursorrules');
|
|
19
|
+
if (await pathExists(path.join(root, ...CURSOR_MDC_PATH.split('/'))))
|
|
20
|
+
found.push(CURSOR_MDC_PATH);
|
|
21
|
+
if (await pathExists(path.join(root, '.cursor', 'rules')))
|
|
22
|
+
found.push('.cursor/rules');
|
|
23
|
+
return found;
|
|
24
|
+
},
|
|
25
|
+
async detectAvailable(root) {
|
|
26
|
+
if (await pathExists(path.join(root, '.cursor')))
|
|
27
|
+
return true;
|
|
28
|
+
if (await pathExists(path.join(root, '.cursorrules')))
|
|
29
|
+
return true;
|
|
30
|
+
return (await which('cursor')) !== null;
|
|
31
|
+
},
|
|
32
|
+
async plan(ctx) {
|
|
33
|
+
const root = ctx.root;
|
|
34
|
+
const legacyRulesPath = path.join(root, '.cursorrules');
|
|
35
|
+
const hasLegacy = await pathExists(legacyRulesPath);
|
|
36
|
+
if (hasLegacy) {
|
|
37
|
+
// User or project has existing .cursorrules; merge the managed block into it
|
|
38
|
+
const existing = await readIfExists(legacyRulesPath);
|
|
39
|
+
const block = buildDevWorkspaceSection(ctx.projectName, 'Cursor');
|
|
40
|
+
const merged = mergeSection(existing, block);
|
|
41
|
+
return [
|
|
42
|
+
{
|
|
43
|
+
path: '.cursorrules',
|
|
44
|
+
kind: merged.kind,
|
|
45
|
+
mode: 'section',
|
|
46
|
+
content: merged.content,
|
|
47
|
+
note: `${merged.note} (.cursorrules)`,
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
}
|
|
51
|
+
// Modern Cursor rule format (.cursor/rules/devworkspace.mdc)
|
|
52
|
+
const mdcFull = path.join(root, ...CURSOR_MDC_PATH.split('/'));
|
|
53
|
+
const existingMdc = await readIfExists(mdcFull);
|
|
54
|
+
const mdcFrontmatter = [
|
|
55
|
+
'---',
|
|
56
|
+
'description: DevWorkspace development workflow, context, and verification rules',
|
|
57
|
+
'alwaysApply: true',
|
|
58
|
+
'---',
|
|
59
|
+
'',
|
|
60
|
+
].join('\n');
|
|
61
|
+
const block = buildDevWorkspaceSection(ctx.projectName, 'Cursor');
|
|
62
|
+
const fullContent = `${mdcFrontmatter}${block}\n`;
|
|
63
|
+
if (existingMdc === null) {
|
|
64
|
+
return [
|
|
65
|
+
{
|
|
66
|
+
path: CURSOR_MDC_PATH,
|
|
67
|
+
kind: 'create',
|
|
68
|
+
mode: 'owned',
|
|
69
|
+
content: fullContent,
|
|
70
|
+
note: 'create Cursor rule in .cursor/rules/devworkspace.mdc',
|
|
71
|
+
},
|
|
72
|
+
];
|
|
73
|
+
}
|
|
74
|
+
if (existingMdc === fullContent) {
|
|
75
|
+
return [
|
|
76
|
+
{
|
|
77
|
+
path: CURSOR_MDC_PATH,
|
|
78
|
+
kind: 'unchanged',
|
|
79
|
+
mode: 'owned',
|
|
80
|
+
content: fullContent,
|
|
81
|
+
note: 'Cursor rule already current',
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
}
|
|
85
|
+
// If existing MDC has custom user content outside devworkspace section, merge into it
|
|
86
|
+
const merged = mergeSection(existingMdc, block);
|
|
87
|
+
return [
|
|
88
|
+
{
|
|
89
|
+
path: CURSOR_MDC_PATH,
|
|
90
|
+
kind: merged.kind,
|
|
91
|
+
mode: 'owned',
|
|
92
|
+
content: merged.content,
|
|
93
|
+
note: `${merged.note} (.cursor/rules/devworkspace.mdc)`,
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
},
|
|
97
|
+
async unplan(root) {
|
|
98
|
+
const changes = [];
|
|
99
|
+
const legacyPath = path.join(root, '.cursorrules');
|
|
100
|
+
const legacyText = await readIfExists(legacyPath);
|
|
101
|
+
if (legacyText !== null) {
|
|
102
|
+
const removed = removeManagedSection(legacyText);
|
|
103
|
+
if (removed) {
|
|
104
|
+
changes.push({
|
|
105
|
+
path: '.cursorrules',
|
|
106
|
+
kind: 'update',
|
|
107
|
+
mode: 'section',
|
|
108
|
+
content: removed.content,
|
|
109
|
+
note: removed.empty ? 'delete file (contained only DevWorkspace block)' : 'remove managed DevWorkspace block',
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const mdcPath = path.join(root, ...CURSOR_MDC_PATH.split('/'));
|
|
114
|
+
const mdcText = await readIfExists(mdcPath);
|
|
115
|
+
if (mdcText !== null && mdcText.includes('devworkspace:start')) {
|
|
116
|
+
changes.push({
|
|
117
|
+
path: CURSOR_MDC_PATH,
|
|
118
|
+
kind: 'update',
|
|
119
|
+
mode: 'owned',
|
|
120
|
+
content: '',
|
|
121
|
+
note: 'delete DevWorkspace rule file',
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
return changes;
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../src/agents/cursor/adapter.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path';\nimport type { AgentAdapter, AgentContext, PlannedChange } from '../types.js';\nimport { pathExists } from '../../utils/fs.js';\nimport { which } from '../../utils/proc.js';\nimport {\n buildDevWorkspaceSection,\n mergeSection,\n readIfExists,\n removeManagedSection,\n} from '../shared.js';\n\nconst CURSOR_MDC_PATH = '.cursor/rules/devworkspace.mdc';\n\nexport const cursorAdapter: AgentAdapter = {\n id: 'cursor',\n displayName: 'Cursor',\n capabilities: {\n instructions: true,\n skills: false,\n plugins: false,\n cli: false,\n },\n\n async detectExisting(root: string): Promise<string[]> {\n const found: string[] = [];\n if (await pathExists(path.join(root, '.cursorrules'))) found.push('.cursorrules');\n if (await pathExists(path.join(root, ...CURSOR_MDC_PATH.split('/')))) found.push(CURSOR_MDC_PATH);\n if (await pathExists(path.join(root, '.cursor', 'rules'))) found.push('.cursor/rules');\n return found;\n },\n\n async detectAvailable(root: string): Promise<boolean> {\n if (await pathExists(path.join(root, '.cursor'))) return true;\n if (await pathExists(path.join(root, '.cursorrules'))) return true;\n return (await which('cursor')) !== null;\n },\n\n async plan(ctx: AgentContext): Promise<PlannedChange[]> {\n const root = ctx.root;\n const legacyRulesPath = path.join(root, '.cursorrules');\n const hasLegacy = await pathExists(legacyRulesPath);\n\n if (hasLegacy) {\n // User or project has existing .cursorrules; merge the managed block into it\n const existing = await readIfExists(legacyRulesPath);\n const block = buildDevWorkspaceSection(ctx.projectName, 'Cursor');\n const merged = mergeSection(existing, block);\n return [\n {\n path: '.cursorrules',\n kind: merged.kind,\n mode: 'section',\n content: merged.content,\n note: `${merged.note} (.cursorrules)`,\n },\n ];\n }\n\n // Modern Cursor rule format (.cursor/rules/devworkspace.mdc)\n const mdcFull = path.join(root, ...CURSOR_MDC_PATH.split('/'));\n const existingMdc = await readIfExists(mdcFull);\n const mdcFrontmatter = [\n '---',\n 'description: DevWorkspace development workflow, context, and verification rules',\n 'alwaysApply: true',\n '---',\n '',\n ].join('\\n');\n const block = buildDevWorkspaceSection(ctx.projectName, 'Cursor');\n const fullContent = `${mdcFrontmatter}${block}\\n`;\n\n if (existingMdc === null) {\n return [\n {\n path: CURSOR_MDC_PATH,\n kind: 'create',\n mode: 'owned',\n content: fullContent,\n note: 'create Cursor rule in .cursor/rules/devworkspace.mdc',\n },\n ];\n }\n\n if (existingMdc === fullContent) {\n return [\n {\n path: CURSOR_MDC_PATH,\n kind: 'unchanged',\n mode: 'owned',\n content: fullContent,\n note: 'Cursor rule already current',\n },\n ];\n }\n\n // If existing MDC has custom user content outside devworkspace section, merge into it\n const merged = mergeSection(existingMdc, block);\n return [\n {\n path: CURSOR_MDC_PATH,\n kind: merged.kind,\n mode: 'owned',\n content: merged.content,\n note: `${merged.note} (.cursor/rules/devworkspace.mdc)`,\n },\n ];\n },\n\n async unplan(root: string): Promise<PlannedChange[]> {\n const changes: PlannedChange[] = [];\n const legacyPath = path.join(root, '.cursorrules');\n const legacyText = await readIfExists(legacyPath);\n if (legacyText !== null) {\n const removed = removeManagedSection(legacyText);\n if (removed) {\n changes.push({\n path: '.cursorrules',\n kind: 'update',\n mode: 'section',\n content: removed.content,\n note: removed.empty ? 'delete file (contained only DevWorkspace block)' : 'remove managed DevWorkspace block',\n });\n }\n }\n\n const mdcPath = path.join(root, ...CURSOR_MDC_PATH.split('/'));\n const mdcText = await readIfExists(mdcPath);\n if (mdcText !== null && mdcText.includes('devworkspace:start')) {\n changes.push({\n path: CURSOR_MDC_PATH,\n kind: 'update',\n mode: 'owned',\n content: '',\n note: 'delete DevWorkspace rule file',\n });\n }\n\n return changes;\n },\n};\n\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AgentAdapter } from './types.js';
|
|
2
|
+
export * from './types.js';
|
|
3
|
+
export * from './shared.js';
|
|
4
|
+
export { claudeAdapter } from './claude/adapter.js';
|
|
5
|
+
export { codexAdapter } from './codex/adapter.js';
|
|
6
|
+
export { cursorAdapter } from './cursor/adapter.js';
|
|
7
|
+
export { copilotAdapter } from './copilot/adapter.js';
|
|
8
|
+
export declare const AGENT_ADAPTERS: AgentAdapter[];
|
|
9
|
+
export declare const AGENTS: Record<string, AgentAdapter>;
|
|
10
|
+
export declare function getAgentAdapter(id: string): AgentAdapter | undefined;
|
|
11
|
+
export declare function listAgentAdapters(): AgentAdapter[];
|
|
12
|
+
/** Detect any existing instruction files across all known agent integrations. */
|
|
13
|
+
export declare function detectAllAgentFiles(root: string): Promise<string[]>;
|
|
14
|
+
/** Detect which coding agents appear active or configured in this project. */
|
|
15
|
+
export declare function detectActiveAgents(root: string): Promise<AgentAdapter[]>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { claudeAdapter } from './claude/adapter.js';
|
|
2
|
+
import { codexAdapter } from './codex/adapter.js';
|
|
3
|
+
import { cursorAdapter } from './cursor/adapter.js';
|
|
4
|
+
import { copilotAdapter } from './copilot/adapter.js';
|
|
5
|
+
export * from './types.js';
|
|
6
|
+
export * from './shared.js';
|
|
7
|
+
export { claudeAdapter } from './claude/adapter.js';
|
|
8
|
+
export { codexAdapter } from './codex/adapter.js';
|
|
9
|
+
export { cursorAdapter } from './cursor/adapter.js';
|
|
10
|
+
export { copilotAdapter } from './copilot/adapter.js';
|
|
11
|
+
export const AGENT_ADAPTERS = [
|
|
12
|
+
claudeAdapter,
|
|
13
|
+
codexAdapter,
|
|
14
|
+
cursorAdapter,
|
|
15
|
+
copilotAdapter,
|
|
16
|
+
];
|
|
17
|
+
export const AGENTS = {
|
|
18
|
+
claude: claudeAdapter,
|
|
19
|
+
codex: codexAdapter,
|
|
20
|
+
agents: codexAdapter, // Alias for AGENTS.md open standard
|
|
21
|
+
cursor: cursorAdapter,
|
|
22
|
+
copilot: copilotAdapter,
|
|
23
|
+
};
|
|
24
|
+
export function getAgentAdapter(id) {
|
|
25
|
+
return AGENTS[id.toLowerCase()];
|
|
26
|
+
}
|
|
27
|
+
export function listAgentAdapters() {
|
|
28
|
+
return [claudeAdapter, codexAdapter, cursorAdapter, copilotAdapter];
|
|
29
|
+
}
|
|
30
|
+
/** Detect any existing instruction files across all known agent integrations. */
|
|
31
|
+
export async function detectAllAgentFiles(root) {
|
|
32
|
+
const all = [];
|
|
33
|
+
for (const adapter of listAgentAdapters()) {
|
|
34
|
+
const files = await adapter.detectExisting(root);
|
|
35
|
+
all.push(...files);
|
|
36
|
+
}
|
|
37
|
+
return [...new Set(all)].sort();
|
|
38
|
+
}
|
|
39
|
+
/** Detect which coding agents appear active or configured in this project. */
|
|
40
|
+
export async function detectActiveAgents(root) {
|
|
41
|
+
const active = [];
|
|
42
|
+
for (const adapter of listAgentAdapters()) {
|
|
43
|
+
const existing = await adapter.detectExisting(root);
|
|
44
|
+
if (existing.length > 0) {
|
|
45
|
+
active.push(adapter);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (adapter.detectAvailable && (await adapter.detectAvailable(root))) {
|
|
49
|
+
active.push(adapter);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return active;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=index.js.map
|