openspec-playwright 0.3.73 → 0.3.75
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 +18 -17
- package/README.zh-CN.md +19 -18
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +54 -23
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/editors/adapters/claude.d.ts +5 -0
- package/dist/commands/editors/adapters/claude.js +79 -0
- package/dist/commands/editors/adapters/claude.js.map +1 -0
- package/dist/commands/editors/adapters/cline.d.ts +15 -0
- package/dist/commands/editors/adapters/cline.js +63 -0
- package/dist/commands/editors/adapters/cline.js.map +1 -0
- package/dist/commands/editors/adapters/cursor.d.ts +15 -0
- package/dist/commands/editors/adapters/cursor.js +79 -0
- package/dist/commands/editors/adapters/cursor.js.map +1 -0
- package/dist/commands/editors/adapters/dsh.d.ts +27 -0
- package/dist/commands/editors/adapters/dsh.js +67 -0
- package/dist/commands/editors/adapters/dsh.js.map +1 -0
- package/dist/commands/editors/adapters/omp.d.ts +24 -0
- package/dist/commands/editors/adapters/omp.js +73 -0
- package/dist/commands/editors/adapters/omp.js.map +1 -0
- package/dist/commands/editors/adapters/opencode.d.ts +8 -0
- package/dist/commands/editors/adapters/opencode.js +133 -0
- package/dist/commands/editors/adapters/opencode.js.map +1 -0
- package/dist/commands/editors/adapters/pi.d.ts +27 -0
- package/dist/commands/editors/adapters/pi.js +64 -0
- package/dist/commands/editors/adapters/pi.js.map +1 -0
- package/dist/commands/editors/project-rules.d.ts +69 -0
- package/dist/commands/editors/project-rules.js +246 -0
- package/dist/commands/editors/project-rules.js.map +1 -0
- package/dist/commands/editors/registry.d.ts +12 -0
- package/dist/commands/editors/registry.js +59 -0
- package/dist/commands/editors/registry.js.map +1 -0
- package/dist/commands/editors/shared.d.ts +37 -0
- package/dist/commands/editors/shared.js +92 -0
- package/dist/commands/editors/shared.js.map +1 -0
- package/dist/commands/editors/tool-selection.d.ts +17 -0
- package/dist/commands/editors/tool-selection.js +60 -0
- package/dist/commands/editors/tool-selection.js.map +1 -0
- package/dist/commands/editors/types.d.ts +82 -0
- package/dist/commands/editors/types.js +41 -0
- package/dist/commands/editors/types.js.map +1 -0
- package/dist/commands/editors.d.ts +32 -309
- package/dist/commands/editors.js +38 -960
- package/dist/commands/editors.js.map +1 -1
- package/dist/commands/explore.d.ts +2 -0
- package/dist/commands/explore.js +2 -2
- package/dist/commands/explore.js.map +1 -1
- package/dist/commands/init.js +15 -25
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/uninstall.js +4 -2
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.d.ts +8 -0
- package/dist/commands/update.js +12 -8
- package/dist/commands/update.js.map +1 -1
- package/dist/shared/index.d.ts +1 -1
- package/dist/shared/index.js +1 -1
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/mcp.d.ts +5 -2
- package/dist/shared/mcp.js +15 -7
- package/dist/shared/mcp.js.map +1 -1
- package/employee-standards.md +2 -0
- package/package.json +5 -5
- package/templates/e2e-command.md +6 -2
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type EditorAdapter, type CommandMeta } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Cursor slash commands are plain markdown (no frontmatter); the filename
|
|
4
|
+
* is the command name. `$1` is the change-name argument.
|
|
5
|
+
*/
|
|
6
|
+
export declare function formatCursorCommand(meta: CommandMeta): string;
|
|
7
|
+
export declare function getCursorCommandPath(id: string): string;
|
|
8
|
+
export declare function getCursorSkillPath(id: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Cursor Agent Skill — explicit invocation only (`disable-model-invocation`).
|
|
11
|
+
* No `$1` placeholders (those belong to the slash command file).
|
|
12
|
+
*/
|
|
13
|
+
export declare function formatCursorSkill(meta: CommandMeta): string;
|
|
14
|
+
export declare function hasCursor(projectRoot: string): boolean;
|
|
15
|
+
export declare const cursorAdapter: EditorAdapter;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cursor adapter: slash command at `.cursor/commands/opsx-<id>.md` (plain
|
|
3
|
+
* markdown) plus an Agent Skill at `.cursor/skills/opsx-<id>/SKILL.md`;
|
|
4
|
+
* MCP lives in `.cursor/mcp.json`. Cursor auto-detects AGENTS.md.
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync } from "fs";
|
|
7
|
+
import { join } from "path";
|
|
8
|
+
import { defineAdapter } from "../types.js";
|
|
9
|
+
import { escapeYamlValue, transformToHyphenCommands, isMcpServerInFile, installMcpServerInFile, removeMcpServerFromFile, } from "../shared.js";
|
|
10
|
+
import { registerAdapter } from "../registry.js";
|
|
11
|
+
// ─── Cursor adapter (format / paths) ─────────────────────────────────────
|
|
12
|
+
/**
|
|
13
|
+
* Cursor slash commands are plain markdown (no frontmatter); the filename
|
|
14
|
+
* is the command name. `$1` is the change-name argument.
|
|
15
|
+
*/
|
|
16
|
+
export function formatCursorCommand(meta) {
|
|
17
|
+
const body = transformToHyphenCommands(meta.body);
|
|
18
|
+
return `<!-- Change name: $1 (e.g. /opsx-${meta.id} my-change) -->
|
|
19
|
+
|
|
20
|
+
${body}
|
|
21
|
+
`;
|
|
22
|
+
}
|
|
23
|
+
export function getCursorCommandPath(id) {
|
|
24
|
+
return join(".cursor", "commands", `opsx-${id}.md`);
|
|
25
|
+
}
|
|
26
|
+
export function getCursorSkillPath(id) {
|
|
27
|
+
return join(".cursor", "skills", `opsx-${id}`, "SKILL.md");
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Cursor Agent Skill — explicit invocation only (`disable-model-invocation`).
|
|
31
|
+
* No `$1` placeholders (those belong to the slash command file).
|
|
32
|
+
*/
|
|
33
|
+
export function formatCursorSkill(meta) {
|
|
34
|
+
const body = transformToHyphenCommands(meta.body);
|
|
35
|
+
const skillName = `opsx-${meta.id}`;
|
|
36
|
+
return `---
|
|
37
|
+
name: ${escapeYamlValue(skillName)}
|
|
38
|
+
description: ${escapeYamlValue(meta.description)}
|
|
39
|
+
disable-model-invocation: true
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
${body}
|
|
43
|
+
`;
|
|
44
|
+
}
|
|
45
|
+
export function hasCursor(projectRoot) {
|
|
46
|
+
return existsSync(join(projectRoot, ".cursor"));
|
|
47
|
+
}
|
|
48
|
+
export const cursorAdapter = defineAdapter({
|
|
49
|
+
id: "cursor",
|
|
50
|
+
label: "cursor",
|
|
51
|
+
displayName: "Cursor",
|
|
52
|
+
detect: hasCursor,
|
|
53
|
+
commandFilePath: getCursorCommandPath,
|
|
54
|
+
formatCommand: formatCursorCommand,
|
|
55
|
+
// Cursor auto-detects AGENTS.md — that's the default.
|
|
56
|
+
isMcpInstalled(projectRoot, serverName) {
|
|
57
|
+
return isMcpServerInFile(cursorMcpPath(projectRoot), serverName);
|
|
58
|
+
},
|
|
59
|
+
installMcp(projectRoot, serverName, command) {
|
|
60
|
+
installMcpServerInFile(cursorMcpPath(projectRoot), serverName, command);
|
|
61
|
+
},
|
|
62
|
+
removeMcp(projectRoot, serverName) {
|
|
63
|
+
removeMcpServerFromFile(cursorMcpPath(projectRoot), serverName);
|
|
64
|
+
},
|
|
65
|
+
extraArtifacts(meta) {
|
|
66
|
+
return [
|
|
67
|
+
{
|
|
68
|
+
relativePath: getCursorSkillPath(meta.id),
|
|
69
|
+
contents: formatCursorSkill(meta),
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
/** Path to the project-level MCP config file for Cursor. */
|
|
75
|
+
function cursorMcpPath(projectRoot) {
|
|
76
|
+
return join(projectRoot, ".cursor", "mcp.json");
|
|
77
|
+
}
|
|
78
|
+
registerAdapter(cursorAdapter);
|
|
79
|
+
//# sourceMappingURL=cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../../../src/commands/editors/adapters/cursor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAwC,MAAM,aAAa,CAAC;AAClF,OAAO,EACL,eAAe,EACf,yBAAyB,EACzB,iBAAiB,EACjB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,4EAA4E;AAE5E;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAiB;IACnD,MAAM,IAAI,GAAG,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClD,OAAO,oCAAoC,IAAI,CAAC,EAAE;;EAElD,IAAI;CACL,CAAC;AACF,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,EAAU;IAC7C,OAAO,IAAI,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,EAAU;IAC3C,OAAO,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,CAAC,CAAC;AAC7D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAiB;IACjD,MAAM,IAAI,GAAG,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,QAAQ,IAAI,CAAC,EAAE,EAAE,CAAC;IACpC,OAAO;QACD,eAAe,CAAC,SAAS,CAAC;eACnB,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC;;;;EAI9C,IAAI;CACL,CAAC;AACF,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,WAAmB;IAC3C,OAAO,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAkB,aAAa,CAAC;IACxD,EAAE,EAAE,QAAQ;IACZ,KAAK,EAAE,QAAQ;IACf,WAAW,EAAE,QAAQ;IACrB,MAAM,EAAE,SAAS;IACjB,eAAe,EAAE,oBAAoB;IACrC,aAAa,EAAE,mBAAmB;IAClC,sDAAsD;IACtD,cAAc,CAAC,WAAW,EAAE,UAAU;QACpC,OAAO,iBAAiB,CAAC,aAAa,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC,CAAC;IACnE,CAAC;IACD,UAAU,CAAC,WAAW,EAAE,UAAU,EAAE,OAAO;QACzC,sBAAsB,CAAC,aAAa,CAAC,WAAW,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;IAC1E,CAAC;IACD,SAAS,CAAC,WAAW,EAAE,UAAU;QAC/B,uBAAuB,CAAC,aAAa,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC,CAAC;IAClE,CAAC;IACD,cAAc,CAAC,IAAI;QACjB,OAAO;YACL;gBACE,YAAY,EAAE,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzC,QAAQ,EAAE,iBAAiB,CAAC,IAAI,CAAC;aAClC;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC;AAEH,4DAA4D;AAC5D,SAAS,aAAa,CAAC,WAAmB;IACxC,OAAO,IAAI,CAAC,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;AAClD,CAAC;AAED,eAAe,CAAC,aAAa,CAAC,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type EditorAdapter, type CommandMeta } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* DeepSeek Harness (dsh) stores project skills in `.dsh/skills/` — the
|
|
4
|
+
* `project-dsh` local provider root, rank 100 (highest local priority).
|
|
5
|
+
* Skills are directory bundles (`<name>/SKILL.md`) with YAML frontmatter
|
|
6
|
+
* (name + description); the kebab-case name becomes the model-invocable
|
|
7
|
+
* skill name (`opsx-e2e`).
|
|
8
|
+
*
|
|
9
|
+
* Conventions follow the dsh skills subsystem (docs/subsystems/skills.md):
|
|
10
|
+
* - Skills: `.dsh/skills/<name>/SKILL.md`, invoked via the `skill` tool.
|
|
11
|
+
* - Rules: dsh reads `AGENTS.md` / `CLAUDE.md` natively (default
|
|
12
|
+
* instructionFileCandidates) — no wrapper file needed.
|
|
13
|
+
* - MCP: dsh configures MCP via `cordis.yml` plugin config
|
|
14
|
+
* (@deepseek-ai/dsh-mcp-client), not a simple mcpServers file —
|
|
15
|
+
* the adapter declares `supportsMcp: false` so shared MCP phases
|
|
16
|
+
* skip it (configure Playwright MCP manually in cordis.yml).
|
|
17
|
+
* - Detected via `.dsh/` or the global `~/.dsh/` (DSH_HOME).
|
|
18
|
+
*/
|
|
19
|
+
export declare function formatDshCommand(meta: CommandMeta): string;
|
|
20
|
+
export declare function getDshCommandPath(id: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* True when DeepSeek Harness is in use: a project `.dsh/` dir, or the global
|
|
23
|
+
* dsh home (`~/.dsh/`, DSH_HOME). The home signal lets `openspec-pw init`
|
|
24
|
+
* detect dsh in a fresh project that has no `.dsh/` yet.
|
|
25
|
+
*/
|
|
26
|
+
export declare function hasDsh(projectRoot: string, homeDir?: string): boolean;
|
|
27
|
+
export declare const dshAdapter: EditorAdapter;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSeek Harness adapter: skill bundle at `.dsh/skills/opsx-<id>/SKILL.md`
|
|
3
|
+
* (project-dsh root, rank 100). NO simple MCP file — cordis.yml plugin
|
|
4
|
+
* config instead, so `supportsMcp: false`. Detected via `.dsh/` or
|
|
5
|
+
* global `~/.dsh/` (DSH_HOME).
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync } from "fs";
|
|
8
|
+
import { homedir } from "os";
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
import { defineAdapter } from "../types.js";
|
|
11
|
+
import { escapeYamlValue, transformToHyphenCommands } from "../shared.js";
|
|
12
|
+
import { registerAdapter } from "../registry.js";
|
|
13
|
+
// ─── DeepSeek Harness adapter ────────────────────────────────────────────
|
|
14
|
+
/**
|
|
15
|
+
* DeepSeek Harness (dsh) stores project skills in `.dsh/skills/` — the
|
|
16
|
+
* `project-dsh` local provider root, rank 100 (highest local priority).
|
|
17
|
+
* Skills are directory bundles (`<name>/SKILL.md`) with YAML frontmatter
|
|
18
|
+
* (name + description); the kebab-case name becomes the model-invocable
|
|
19
|
+
* skill name (`opsx-e2e`).
|
|
20
|
+
*
|
|
21
|
+
* Conventions follow the dsh skills subsystem (docs/subsystems/skills.md):
|
|
22
|
+
* - Skills: `.dsh/skills/<name>/SKILL.md`, invoked via the `skill` tool.
|
|
23
|
+
* - Rules: dsh reads `AGENTS.md` / `CLAUDE.md` natively (default
|
|
24
|
+
* instructionFileCandidates) — no wrapper file needed.
|
|
25
|
+
* - MCP: dsh configures MCP via `cordis.yml` plugin config
|
|
26
|
+
* (@deepseek-ai/dsh-mcp-client), not a simple mcpServers file —
|
|
27
|
+
* the adapter declares `supportsMcp: false` so shared MCP phases
|
|
28
|
+
* skip it (configure Playwright MCP manually in cordis.yml).
|
|
29
|
+
* - Detected via `.dsh/` or the global `~/.dsh/` (DSH_HOME).
|
|
30
|
+
*/
|
|
31
|
+
export function formatDshCommand(meta) {
|
|
32
|
+
const body = transformToHyphenCommands(meta.body);
|
|
33
|
+
const skillName = `opsx-${meta.id}`;
|
|
34
|
+
return `---
|
|
35
|
+
name: ${escapeYamlValue(skillName)}
|
|
36
|
+
description: ${escapeYamlValue(meta.description)}
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
${body}
|
|
40
|
+
`;
|
|
41
|
+
}
|
|
42
|
+
export function getDshCommandPath(id) {
|
|
43
|
+
return join(".dsh", "skills", `opsx-${id}`, "SKILL.md");
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* True when DeepSeek Harness is in use: a project `.dsh/` dir, or the global
|
|
47
|
+
* dsh home (`~/.dsh/`, DSH_HOME). The home signal lets `openspec-pw init`
|
|
48
|
+
* detect dsh in a fresh project that has no `.dsh/` yet.
|
|
49
|
+
*/
|
|
50
|
+
export function hasDsh(projectRoot, homeDir = homedir()) {
|
|
51
|
+
return (existsSync(join(projectRoot, ".dsh")) ||
|
|
52
|
+
existsSync(join(homeDir, ".dsh")));
|
|
53
|
+
}
|
|
54
|
+
export const dshAdapter = defineAdapter({
|
|
55
|
+
id: "dsh",
|
|
56
|
+
label: "dsh",
|
|
57
|
+
displayName: "DeepSeek Harness",
|
|
58
|
+
detect: hasDsh,
|
|
59
|
+
// dsh configures MCP via cordis.yml plugin config, not a simple file —
|
|
60
|
+
// shared MCP phases skip this adapter (configure Playwright MCP manually).
|
|
61
|
+
supportsMcp: false,
|
|
62
|
+
commandFilePath: getDshCommandPath,
|
|
63
|
+
formatCommand: formatDshCommand,
|
|
64
|
+
// dsh reads AGENTS.md natively — that's the default.
|
|
65
|
+
});
|
|
66
|
+
registerAdapter(dshAdapter);
|
|
67
|
+
//# sourceMappingURL=dsh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dsh.js","sourceRoot":"","sources":["../../../../src/commands/editors/adapters/dsh.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAwC,MAAM,aAAa,CAAC;AAClF,OAAO,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,4EAA4E;AAE5E;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAiB;IAChD,MAAM,IAAI,GAAG,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,QAAQ,IAAI,CAAC,EAAE,EAAE,CAAC;IACpC,OAAO;QACD,eAAe,CAAC,SAAS,CAAC;eACnB,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC;;;EAG9C,IAAI;CACL,CAAC;AACF,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,EAAU;IAC1C,OAAO,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,CAAC,CAAC;AAC1D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,MAAM,CACpB,WAAmB,EACnB,OAAO,GAAG,OAAO,EAAE;IAEnB,OAAO,CACL,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QACrC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAClC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAkB,aAAa,CAAC;IACrD,EAAE,EAAE,KAAK;IACT,KAAK,EAAE,KAAK;IACZ,WAAW,EAAE,kBAAkB;IAC/B,MAAM,EAAE,MAAM;IACd,uEAAuE;IACvE,2EAA2E;IAC3E,WAAW,EAAE,KAAK;IAClB,eAAe,EAAE,iBAAiB;IAClC,aAAa,EAAE,gBAAgB;IAC/B,qDAAqD;CACtD,CAAC,CAAC;AAEH,eAAe,CAAC,UAAU,CAAC,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type EditorAdapter, type CommandMeta } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Oh My Pi (omp) stores project slash commands in `.omp/commands/*.md`
|
|
4
|
+
* (non-recursive scan, project before user). Native commands are parsed
|
|
5
|
+
* with FATAL frontmatter parsing, so the YAML must be valid; `name`
|
|
6
|
+
* overrides the filename and `description` feeds autocomplete. `$1` /
|
|
7
|
+
* `$ARGUMENTS` placeholders expand at prompt time.
|
|
8
|
+
*
|
|
9
|
+
* Conventions follow the omp docs (docs/slash-command-internals.md,
|
|
10
|
+
* docs/mcp-config.md):
|
|
11
|
+
* - Commands: `.omp/commands/*.md`, invoked via `/name`.
|
|
12
|
+
* - MCP: `.omp/mcp.json` with `{ "mcpServers": { ... } }` — same
|
|
13
|
+
* shape as Cline/Cursor. omp also inherits `.claude/` /
|
|
14
|
+
* `.cursor/` / opencode MCP configs when present.
|
|
15
|
+
* - Rules: omp reads `AGENTS.md` natively — no wrapper file needed.
|
|
16
|
+
*/
|
|
17
|
+
export declare function formatOmpCommand(meta: CommandMeta): string;
|
|
18
|
+
export declare function getOmpCommandPath(id: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* True when Oh My Pi is in use: a project `.omp/` dir, or a global omp
|
|
21
|
+
* config dir (`~/.omp/agent/`, created by omp on first run).
|
|
22
|
+
*/
|
|
23
|
+
export declare function hasOmp(projectRoot: string, homeDir?: string): boolean;
|
|
24
|
+
export declare const ompAdapter: EditorAdapter;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Oh My Pi adapter: native command at `.omp/commands/opsx-<id>.md`
|
|
3
|
+
* (FATAL frontmatter parsing — keep YAML valid); MCP in `.omp/mcp.json`.
|
|
4
|
+
* Detected via `.omp/` or global `~/.omp/agent/`.
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync } from "fs";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
import { defineAdapter } from "../types.js";
|
|
10
|
+
import { escapeYamlValue, transformToHyphenCommands, isMcpServerInFile, installMcpServerInFile, removeMcpServerFromFile, } from "../shared.js";
|
|
11
|
+
import { registerAdapter } from "../registry.js";
|
|
12
|
+
// ─── Oh My Pi adapter ────────────────────────────────────────────────────
|
|
13
|
+
/**
|
|
14
|
+
* Oh My Pi (omp) stores project slash commands in `.omp/commands/*.md`
|
|
15
|
+
* (non-recursive scan, project before user). Native commands are parsed
|
|
16
|
+
* with FATAL frontmatter parsing, so the YAML must be valid; `name`
|
|
17
|
+
* overrides the filename and `description` feeds autocomplete. `$1` /
|
|
18
|
+
* `$ARGUMENTS` placeholders expand at prompt time.
|
|
19
|
+
*
|
|
20
|
+
* Conventions follow the omp docs (docs/slash-command-internals.md,
|
|
21
|
+
* docs/mcp-config.md):
|
|
22
|
+
* - Commands: `.omp/commands/*.md`, invoked via `/name`.
|
|
23
|
+
* - MCP: `.omp/mcp.json` with `{ "mcpServers": { ... } }` — same
|
|
24
|
+
* shape as Cline/Cursor. omp also inherits `.claude/` /
|
|
25
|
+
* `.cursor/` / opencode MCP configs when present.
|
|
26
|
+
* - Rules: omp reads `AGENTS.md` natively — no wrapper file needed.
|
|
27
|
+
*/
|
|
28
|
+
export function formatOmpCommand(meta) {
|
|
29
|
+
const body = transformToHyphenCommands(meta.body);
|
|
30
|
+
const cmdName = `opsx-${meta.id}`;
|
|
31
|
+
return `---
|
|
32
|
+
name: ${escapeYamlValue(cmdName)}
|
|
33
|
+
description: ${escapeYamlValue(meta.description)}
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
${body}
|
|
37
|
+
`;
|
|
38
|
+
}
|
|
39
|
+
export function getOmpCommandPath(id) {
|
|
40
|
+
return join(".omp", "commands", `opsx-${id}.md`);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* True when Oh My Pi is in use: a project `.omp/` dir, or a global omp
|
|
44
|
+
* config dir (`~/.omp/agent/`, created by omp on first run).
|
|
45
|
+
*/
|
|
46
|
+
export function hasOmp(projectRoot, homeDir = homedir()) {
|
|
47
|
+
return (existsSync(join(projectRoot, ".omp")) ||
|
|
48
|
+
existsSync(join(homeDir, ".omp", "agent")));
|
|
49
|
+
}
|
|
50
|
+
export const ompAdapter = defineAdapter({
|
|
51
|
+
id: "omp",
|
|
52
|
+
label: "omp",
|
|
53
|
+
displayName: "Oh My Pi",
|
|
54
|
+
detect: hasOmp,
|
|
55
|
+
commandFilePath: getOmpCommandPath,
|
|
56
|
+
formatCommand: formatOmpCommand,
|
|
57
|
+
// omp reads AGENTS.md natively — that's the default.
|
|
58
|
+
isMcpInstalled(projectRoot, serverName) {
|
|
59
|
+
return isMcpServerInFile(ompMcpPath(projectRoot), serverName);
|
|
60
|
+
},
|
|
61
|
+
installMcp(projectRoot, serverName, command) {
|
|
62
|
+
installMcpServerInFile(ompMcpPath(projectRoot), serverName, command);
|
|
63
|
+
},
|
|
64
|
+
removeMcp(projectRoot, serverName) {
|
|
65
|
+
removeMcpServerFromFile(ompMcpPath(projectRoot), serverName);
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
/** Path to the project-level MCP config file for Oh My Pi. */
|
|
69
|
+
function ompMcpPath(projectRoot) {
|
|
70
|
+
return join(projectRoot, ".omp", "mcp.json");
|
|
71
|
+
}
|
|
72
|
+
registerAdapter(ompAdapter);
|
|
73
|
+
//# sourceMappingURL=omp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"omp.js","sourceRoot":"","sources":["../../../../src/commands/editors/adapters/omp.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAwC,MAAM,aAAa,CAAC;AAClF,OAAO,EACL,eAAe,EACf,yBAAyB,EACzB,iBAAiB,EACjB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,4EAA4E;AAE5E;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAiB;IAChD,MAAM,IAAI,GAAG,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,QAAQ,IAAI,CAAC,EAAE,EAAE,CAAC;IAClC,OAAO;QACD,eAAe,CAAC,OAAO,CAAC;eACjB,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC;;;EAG9C,IAAI;CACL,CAAC;AACF,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,EAAU;IAC1C,OAAO,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;AACnD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,MAAM,CACpB,WAAmB,EACnB,OAAO,GAAG,OAAO,EAAE;IAEnB,OAAO,CACL,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QACrC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAC3C,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAkB,aAAa,CAAC;IACrD,EAAE,EAAE,KAAK;IACT,KAAK,EAAE,KAAK;IACZ,WAAW,EAAE,UAAU;IACvB,MAAM,EAAE,MAAM;IACd,eAAe,EAAE,iBAAiB;IAClC,aAAa,EAAE,gBAAgB;IAC/B,qDAAqD;IACrD,cAAc,CAAC,WAAW,EAAE,UAAU;QACpC,OAAO,iBAAiB,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC,CAAC;IAChE,CAAC;IACD,UAAU,CAAC,WAAW,EAAE,UAAU,EAAE,OAAO;QACzC,sBAAsB,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;IACvE,CAAC;IACD,SAAS,CAAC,WAAW,EAAE,UAAU;QAC/B,uBAAuB,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC,CAAC;IAC/D,CAAC;CACF,CAAC,CAAC;AAEH,8DAA8D;AAC9D,SAAS,UAAU,CAAC,WAAmB;IACrC,OAAO,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;AAC/C,CAAC;AAED,eAAe,CAAC,UAAU,CAAC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type EditorAdapter, type CommandMeta } from "../types.js";
|
|
2
|
+
export declare function formatOpenCodeCommand(meta: CommandMeta): string;
|
|
3
|
+
export declare function getOpenCodeCommandPath(id: string): string;
|
|
4
|
+
export declare function hasOpenCode(projectRoot: string): boolean;
|
|
5
|
+
/** Read the current `instructions` array from opencode.json(c), or undefined. */
|
|
6
|
+
declare function readOpenCodeInstructions(projectRoot: string): string[] | undefined;
|
|
7
|
+
export declare const opencodeAdapter: EditorAdapter;
|
|
8
|
+
export { readOpenCodeInstructions };
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode adapter: `.opencode/commands/opsx-<id>.md`, description-only
|
|
3
|
+
* frontmatter; edits `opencode.json(c)` for both MCP (`mcp.playwright`)
|
|
4
|
+
* and the `instructions` list that routes AGENTS.md into context.
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
7
|
+
import { join } from "path";
|
|
8
|
+
import { modify, applyEdits, parseTree as parseJsonc, findNodeAtLocation, getNodeValue, } from "jsonc-parser";
|
|
9
|
+
import { defineAdapter } from "../types.js";
|
|
10
|
+
import { escapeYamlValue, transformToHyphenCommands } from "../shared.js";
|
|
11
|
+
import { registerAdapter } from "../registry.js";
|
|
12
|
+
// ─── OpenCode adapter ────────────────────────────────────────────────────
|
|
13
|
+
export function formatOpenCodeCommand(meta) {
|
|
14
|
+
const body = transformToHyphenCommands(meta.body);
|
|
15
|
+
return `---
|
|
16
|
+
description: ${escapeYamlValue(meta.description)}
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
${body}
|
|
20
|
+
`;
|
|
21
|
+
}
|
|
22
|
+
export function getOpenCodeCommandPath(id) {
|
|
23
|
+
return join(".opencode", "commands", `opsx-${id}.md`);
|
|
24
|
+
}
|
|
25
|
+
export function hasOpenCode(projectRoot) {
|
|
26
|
+
return existsSync(join(projectRoot, ".opencode"));
|
|
27
|
+
}
|
|
28
|
+
const JSONC_FORMAT = { tabSize: 2, insertSpaces: true };
|
|
29
|
+
/** Find the first existing opencode.json(c), or null. */
|
|
30
|
+
function findOpenCodeConfig(projectRoot) {
|
|
31
|
+
for (const name of ["opencode.jsonc", "opencode.json"]) {
|
|
32
|
+
const p = join(projectRoot, name);
|
|
33
|
+
if (existsSync(p))
|
|
34
|
+
return { path: p, text: readFileSync(p, "utf-8") };
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Set a value at a JSON path inside opencode.json(c), creating the file
|
|
40
|
+
* with a `$schema` scaffold if it doesn't exist. Replaces any existing
|
|
41
|
+
* value at the path (does not merge arrays).
|
|
42
|
+
*/
|
|
43
|
+
function setOpenCodeValue(projectRoot, keySegments, value) {
|
|
44
|
+
const existing = findOpenCodeConfig(projectRoot);
|
|
45
|
+
const targetPath = existing?.path ?? join(projectRoot, "opencode.jsonc");
|
|
46
|
+
if (!existing) {
|
|
47
|
+
const scaffold = {
|
|
48
|
+
$schema: "https://opencode.ai/config.json",
|
|
49
|
+
};
|
|
50
|
+
// Build nested scaffold for multi-segment keys (e.g. ["mcp", "playwright"])
|
|
51
|
+
let cursor = scaffold;
|
|
52
|
+
for (let i = 0; i < keySegments.length - 1; i++) {
|
|
53
|
+
const k = String(keySegments[i]);
|
|
54
|
+
cursor[k] = {};
|
|
55
|
+
cursor = cursor[k];
|
|
56
|
+
}
|
|
57
|
+
cursor[String(keySegments[keySegments.length - 1])] = value;
|
|
58
|
+
// ponytail: new file uses 2-space indent; modify branch preserves existing formatting.
|
|
59
|
+
writeFileSync(targetPath, JSON.stringify(scaffold, null, 2) + "\n");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const edits = modify(existing.text, keySegments, value, {
|
|
63
|
+
formattingOptions: JSONC_FORMAT,
|
|
64
|
+
});
|
|
65
|
+
writeFileSync(targetPath, applyEdits(existing.text, edits));
|
|
66
|
+
}
|
|
67
|
+
/** Read the current value at a JSON path (returns undefined if missing). */
|
|
68
|
+
function readOpenCodeValue(text, keySegments) {
|
|
69
|
+
try {
|
|
70
|
+
const tree = parseJsonc(text);
|
|
71
|
+
if (!tree)
|
|
72
|
+
return undefined;
|
|
73
|
+
const node = findNodeAtLocation(tree, keySegments);
|
|
74
|
+
return node ? getNodeValue(node) : undefined;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** Read the current `instructions` array from opencode.json(c), or undefined. */
|
|
81
|
+
function readOpenCodeInstructions(projectRoot) {
|
|
82
|
+
const config = findOpenCodeConfig(projectRoot);
|
|
83
|
+
if (!config)
|
|
84
|
+
return undefined;
|
|
85
|
+
const value = readOpenCodeValue(config.text, ["instructions"]);
|
|
86
|
+
if (Array.isArray(value) && value.every((v) => typeof v === "string")) {
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
export const opencodeAdapter = defineAdapter({
|
|
92
|
+
id: "opencode",
|
|
93
|
+
label: "opencode",
|
|
94
|
+
displayName: "OpenCode",
|
|
95
|
+
detect: hasOpenCode,
|
|
96
|
+
commandFilePath: getOpenCodeCommandPath,
|
|
97
|
+
formatCommand: formatOpenCodeCommand,
|
|
98
|
+
// AGENTS.md is the default; not declared explicitly.
|
|
99
|
+
isMcpInstalled(projectRoot, serverName) {
|
|
100
|
+
const config = findOpenCodeConfig(projectRoot);
|
|
101
|
+
if (!config)
|
|
102
|
+
return false;
|
|
103
|
+
const value = readOpenCodeValue(config.text, ["mcp", serverName]);
|
|
104
|
+
return value !== undefined;
|
|
105
|
+
},
|
|
106
|
+
installMcp(projectRoot, serverName, command) {
|
|
107
|
+
setOpenCodeValue(projectRoot, ["mcp", serverName], {
|
|
108
|
+
type: "local",
|
|
109
|
+
command,
|
|
110
|
+
});
|
|
111
|
+
},
|
|
112
|
+
removeMcp(projectRoot, serverName) {
|
|
113
|
+
// Read current mcp map, rebuild without this server, write back
|
|
114
|
+
const config = findOpenCodeConfig(projectRoot);
|
|
115
|
+
if (!config)
|
|
116
|
+
return;
|
|
117
|
+
const value = readOpenCodeValue(config.text, ["mcp", serverName]);
|
|
118
|
+
if (value === undefined)
|
|
119
|
+
return;
|
|
120
|
+
const current = readOpenCodeValue(config.text, ["mcp"]);
|
|
121
|
+
if (current && typeof current === "object") {
|
|
122
|
+
const next = { ...current };
|
|
123
|
+
delete next[serverName];
|
|
124
|
+
setOpenCodeValue(projectRoot, ["mcp"], next);
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
registerInstructions(projectRoot, instructions) {
|
|
128
|
+
setOpenCodeValue(projectRoot, ["instructions"], instructions);
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
export { readOpenCodeInstructions };
|
|
132
|
+
registerAdapter(opencodeAdapter);
|
|
133
|
+
//# sourceMappingURL=opencode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode.js","sourceRoot":"","sources":["../../../../src/commands/editors/adapters/opencode.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type EditorAdapter, type CommandMeta } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Pi stores project resources in `.pi/` (skills/, prompts/, extensions/).
|
|
4
|
+
* Prompt templates are Markdown with optional YAML frontmatter; the
|
|
5
|
+
* filename becomes the slash command name (`opsx-e2e.md` → `/opsx-e2e`),
|
|
6
|
+
* `description` feeds autocomplete, and `argument-hint` shows expected
|
|
7
|
+
* arguments. `$1` / `$ARGUMENTS` placeholders expand at prompt time.
|
|
8
|
+
*
|
|
9
|
+
* Conventions follow the Pi docs (packages/coding-agent/docs):
|
|
10
|
+
* - Prompts: `.pi/prompts/*.md`, invoked via `/name`.
|
|
11
|
+
* - Skills: `.pi/skills/` (root `.md` files or `SKILL.md` dirs),
|
|
12
|
+
* invoked via `/skill:name`.
|
|
13
|
+
* - Rules: Pi loads `AGENTS.md` (or `CLAUDE.md`) walking up from cwd
|
|
14
|
+
* natively — no wrapper file needed.
|
|
15
|
+
* - MCP: Pi has NO MCP client (built-in tools only). The adapter
|
|
16
|
+
* still implements the interface with no-ops and declares
|
|
17
|
+
* `supportsMcp: false` so shared MCP phases skip it.
|
|
18
|
+
*/
|
|
19
|
+
export declare function formatPiCommand(meta: CommandMeta): string;
|
|
20
|
+
export declare function getPiCommandPath(id: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* True when Pi is in use: a project `.pi/` dir, or a global Pi config dir
|
|
23
|
+
* (`~/.pi/agent/`, created by Pi on first run). The home signal lets
|
|
24
|
+
* `openspec-pw init` detect Pi in a fresh project that has no `.pi/` yet.
|
|
25
|
+
*/
|
|
26
|
+
export declare function hasPi(projectRoot: string, homeDir?: string): boolean;
|
|
27
|
+
export declare const piAdapter: EditorAdapter;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi adapter: prompt template at `.pi/prompts/opsx-<id>.md` (filename =
|
|
3
|
+
* slash command). NO MCP client — `supportsMcp: false` makes shared MCP
|
|
4
|
+
* phases skip it. Detected via `.pi/` or global `~/.pi/agent/`.
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync } from "fs";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
import { defineAdapter } from "../types.js";
|
|
10
|
+
import { escapeYamlValue, transformToHyphenCommands } from "../shared.js";
|
|
11
|
+
import { registerAdapter } from "../registry.js";
|
|
12
|
+
// ─── Pi adapter ──────────────────────────────────────────────────────────
|
|
13
|
+
/**
|
|
14
|
+
* Pi stores project resources in `.pi/` (skills/, prompts/, extensions/).
|
|
15
|
+
* Prompt templates are Markdown with optional YAML frontmatter; the
|
|
16
|
+
* filename becomes the slash command name (`opsx-e2e.md` → `/opsx-e2e`),
|
|
17
|
+
* `description` feeds autocomplete, and `argument-hint` shows expected
|
|
18
|
+
* arguments. `$1` / `$ARGUMENTS` placeholders expand at prompt time.
|
|
19
|
+
*
|
|
20
|
+
* Conventions follow the Pi docs (packages/coding-agent/docs):
|
|
21
|
+
* - Prompts: `.pi/prompts/*.md`, invoked via `/name`.
|
|
22
|
+
* - Skills: `.pi/skills/` (root `.md` files or `SKILL.md` dirs),
|
|
23
|
+
* invoked via `/skill:name`.
|
|
24
|
+
* - Rules: Pi loads `AGENTS.md` (or `CLAUDE.md`) walking up from cwd
|
|
25
|
+
* natively — no wrapper file needed.
|
|
26
|
+
* - MCP: Pi has NO MCP client (built-in tools only). The adapter
|
|
27
|
+
* still implements the interface with no-ops and declares
|
|
28
|
+
* `supportsMcp: false` so shared MCP phases skip it.
|
|
29
|
+
*/
|
|
30
|
+
export function formatPiCommand(meta) {
|
|
31
|
+
const body = transformToHyphenCommands(meta.body);
|
|
32
|
+
return `---
|
|
33
|
+
description: ${escapeYamlValue(meta.description)}
|
|
34
|
+
argument-hint: "<change-name>"
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
${body}
|
|
38
|
+
`;
|
|
39
|
+
}
|
|
40
|
+
export function getPiCommandPath(id) {
|
|
41
|
+
return join(".pi", "prompts", `opsx-${id}.md`);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* True when Pi is in use: a project `.pi/` dir, or a global Pi config dir
|
|
45
|
+
* (`~/.pi/agent/`, created by Pi on first run). The home signal lets
|
|
46
|
+
* `openspec-pw init` detect Pi in a fresh project that has no `.pi/` yet.
|
|
47
|
+
*/
|
|
48
|
+
export function hasPi(projectRoot, homeDir = homedir()) {
|
|
49
|
+
return (existsSync(join(projectRoot, ".pi")) ||
|
|
50
|
+
existsSync(join(homeDir, ".pi", "agent")));
|
|
51
|
+
}
|
|
52
|
+
export const piAdapter = defineAdapter({
|
|
53
|
+
id: "pi",
|
|
54
|
+
label: "pi",
|
|
55
|
+
displayName: "Pi",
|
|
56
|
+
detect: hasPi,
|
|
57
|
+
// Pi has no MCP client — shared MCP phases skip this adapter.
|
|
58
|
+
supportsMcp: false,
|
|
59
|
+
commandFilePath: getPiCommandPath,
|
|
60
|
+
formatCommand: formatPiCommand,
|
|
61
|
+
// Pi loads AGENTS.md natively (walking up from cwd) — that's the default.
|
|
62
|
+
});
|
|
63
|
+
registerAdapter(piAdapter);
|
|
64
|
+
//# sourceMappingURL=pi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pi.js","sourceRoot":"","sources":["../../../../src/commands/editors/adapters/pi.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAwC,MAAM,aAAa,CAAC;AAClF,OAAO,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,4EAA4E;AAE5E;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,eAAe,CAAC,IAAiB;IAC/C,MAAM,IAAI,GAAG,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClD,OAAO;eACM,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC;;;;EAI9C,IAAI;CACL,CAAC;AACF,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,EAAU;IACzC,OAAO,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;AACjD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,KAAK,CACnB,WAAmB,EACnB,OAAO,GAAG,OAAO,EAAE;IAEnB,OAAO,CACL,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;QACpC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAC1C,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,SAAS,GAAkB,aAAa,CAAC;IACpD,EAAE,EAAE,IAAI;IACR,KAAK,EAAE,IAAI;IACX,WAAW,EAAE,IAAI;IACjB,MAAM,EAAE,KAAK;IACb,8DAA8D;IAC9D,WAAW,EAAE,KAAK;IAClB,eAAe,EAAE,gBAAgB;IACjC,aAAa,EAAE,eAAe;IAC9B,0EAA0E;CAC3E,CAAC,CAAC;AAEH,eAAe,CAAC,SAAS,CAAC,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { EditorAdapter } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Read the OPENSPEC marker block from a rules file, or `null` when the file
|
|
4
|
+
* is missing / has no markers. Used by drift detection and update to decide
|
|
5
|
+
* whether a rules file needs rewriting.
|
|
6
|
+
*/
|
|
7
|
+
export declare function readOpenSpecBlock(projectRoot: string, adapter: EditorAdapter): string | null;
|
|
8
|
+
/**
|
|
9
|
+
* Whether a rules file's OPENSPEC block matches the expected content.
|
|
10
|
+
* A missing file or absent/truncated markers counts as "does not match"
|
|
11
|
+
* (the caller will rewrite it), which keeps update idempotent but safe.
|
|
12
|
+
*/
|
|
13
|
+
export declare function blockMatchesExpected(projectRoot: string, adapter: EditorAdapter, expected: string): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Install employee-grade standards into the editor's rules file
|
|
16
|
+
* (CLAUDE.md for Claude, AGENTS.md for OpenCode, Cline, and Cursor). Wraps content in
|
|
17
|
+
* `<!-- OPENSPEC:START -->` / `<!-- OPENSPEC:END -->` markers so future
|
|
18
|
+
* updates can replace the block without touching the rest of the file.
|
|
19
|
+
*/
|
|
20
|
+
export declare function installOpenSpecBlock(projectRoot: string, standardsContent: string, adapter?: EditorAdapter): void;
|
|
21
|
+
/**
|
|
22
|
+
* The expected OPENSPEC block content for a thin CLAUDE.md wrapper
|
|
23
|
+
* (CodeGraph-first guidance + workflow hint + `@AGENTS.md` import). Exported
|
|
24
|
+
* so drift detection / update can compare against it.
|
|
25
|
+
*
|
|
26
|
+
* The `@AGENTS.md` line is Claude Code's documented way to reuse AGENTS.md
|
|
27
|
+
* inside CLAUDE.md — AGENTS.md is NOT read by default ("Claude Code reads
|
|
28
|
+
* CLAUDE.md, not AGENTS.md"). Contract per
|
|
29
|
+
* https://code.claude.com/docs/en/memory.md:
|
|
30
|
+
* - Position is irrelevant — the doc says "@ ... anywhere in your CLAUDE.md"
|
|
31
|
+
* (examples even inline it mid-sentence or in a list item). The one real
|
|
32
|
+
* constraint: the `@` line must NOT sit inside a code span (backticks) or
|
|
33
|
+
* a fenced code block — the resolver skips those. This wrapper keeps the
|
|
34
|
+
* import at the end of the block as a bare line.
|
|
35
|
+
* - The path resolves relative to the importing CLAUDE.md; import recursion
|
|
36
|
+
* is capped at 4 hops.
|
|
37
|
+
* - Block-level HTML comments (`<!-- ... -->`) are stripped before context
|
|
38
|
+
* injection, so the OPENSPEC markers vanish while the live `@AGENTS.md`
|
|
39
|
+
* line inside them is still honored.
|
|
40
|
+
*/
|
|
41
|
+
export declare function claudeWrapperStandardsContent(): string;
|
|
42
|
+
/**
|
|
43
|
+
* Install a thin CLAUDE.md that imports AGENTS.md.
|
|
44
|
+
*
|
|
45
|
+
* Uses the same OPENSPEC:START/END markers as the full standards block so
|
|
46
|
+
* `cleanProjectRules` can remove it uniformly. The CodeGraph-first block is
|
|
47
|
+
* written directly into CLAUDE.md (before the @AGENTS.md import) so Claude
|
|
48
|
+
* Code picks it up without depending on the import.
|
|
49
|
+
*
|
|
50
|
+
* Also handles migration: if CLAUDE.md has an existing OPENSPEC:START block
|
|
51
|
+
* (old format that wrote standards directly to CLAUDE.md), calling
|
|
52
|
+
* `installOpenSpecBlock` replaces the content with the CodeGraph block +
|
|
53
|
+
* `@AGENTS.md` import.
|
|
54
|
+
*/
|
|
55
|
+
export declare function installClaudeWrapper(projectRoot: string): void;
|
|
56
|
+
/**
|
|
57
|
+
* Route employee-grade standards into project rules files.
|
|
58
|
+
*
|
|
59
|
+
* AGENTS.md is always the single source of truth, regardless of which
|
|
60
|
+
* editors are detected. If Claude is in use, a thin CLAUDE.md wrapper
|
|
61
|
+
* with `@AGENTS.md` import is created so Claude loads AGENTS.md as
|
|
62
|
+
* its project rules. Cline and Cursor auto-detect AGENTS.md natively — no
|
|
63
|
+
* wrapper needed.
|
|
64
|
+
*/
|
|
65
|
+
export declare function installProjectRules(projectRoot: string, standardsContent: string, detected: EditorAdapter[]): void;
|
|
66
|
+
/** Remove all OpenSpec marker blocks from AGENTS.md (always) and CLAUDE.md (for claude adapter). */
|
|
67
|
+
export declare function cleanProjectRules(adapter: EditorAdapter, projectRoot: string): void;
|
|
68
|
+
/** Read the employee-grade standards source file (empty string if missing). */
|
|
69
|
+
export declare function readEmployeeStandards(srcPath: string): string;
|