openspec-playwright 0.3.73 → 0.3.74

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.
Files changed (43) hide show
  1. package/dist/commands/editors/adapters/claude.d.ts +5 -0
  2. package/dist/commands/editors/adapters/claude.js +79 -0
  3. package/dist/commands/editors/adapters/claude.js.map +1 -0
  4. package/dist/commands/editors/adapters/cline.d.ts +15 -0
  5. package/dist/commands/editors/adapters/cline.js +63 -0
  6. package/dist/commands/editors/adapters/cline.js.map +1 -0
  7. package/dist/commands/editors/adapters/cursor.d.ts +15 -0
  8. package/dist/commands/editors/adapters/cursor.js +79 -0
  9. package/dist/commands/editors/adapters/cursor.js.map +1 -0
  10. package/dist/commands/editors/adapters/dsh.d.ts +27 -0
  11. package/dist/commands/editors/adapters/dsh.js +67 -0
  12. package/dist/commands/editors/adapters/dsh.js.map +1 -0
  13. package/dist/commands/editors/adapters/omp.d.ts +24 -0
  14. package/dist/commands/editors/adapters/omp.js +73 -0
  15. package/dist/commands/editors/adapters/omp.js.map +1 -0
  16. package/dist/commands/editors/adapters/opencode.d.ts +8 -0
  17. package/dist/commands/editors/adapters/opencode.js +133 -0
  18. package/dist/commands/editors/adapters/opencode.js.map +1 -0
  19. package/dist/commands/editors/adapters/pi.d.ts +27 -0
  20. package/dist/commands/editors/adapters/pi.js +64 -0
  21. package/dist/commands/editors/adapters/pi.js.map +1 -0
  22. package/dist/commands/editors/project-rules.d.ts +69 -0
  23. package/dist/commands/editors/project-rules.js +246 -0
  24. package/dist/commands/editors/project-rules.js.map +1 -0
  25. package/dist/commands/editors/registry.d.ts +12 -0
  26. package/dist/commands/editors/registry.js +59 -0
  27. package/dist/commands/editors/registry.js.map +1 -0
  28. package/dist/commands/editors/shared.d.ts +37 -0
  29. package/dist/commands/editors/shared.js +92 -0
  30. package/dist/commands/editors/shared.js.map +1 -0
  31. package/dist/commands/editors/tool-selection.d.ts +17 -0
  32. package/dist/commands/editors/tool-selection.js +60 -0
  33. package/dist/commands/editors/tool-selection.js.map +1 -0
  34. package/dist/commands/editors/types.d.ts +82 -0
  35. package/dist/commands/editors/types.js +41 -0
  36. package/dist/commands/editors/types.js.map +1 -0
  37. package/dist/commands/editors.d.ts +32 -309
  38. package/dist/commands/editors.js +38 -960
  39. package/dist/commands/editors.js.map +1 -1
  40. package/dist/commands/update.d.ts +8 -0
  41. package/dist/commands/update.js +1 -1
  42. package/dist/commands/update.js.map +1 -1
  43. package/package.json +5 -5
@@ -0,0 +1,5 @@
1
+ import { type EditorAdapter, type CommandMeta } from "../types.js";
2
+ export declare function formatClaudeCommand(meta: CommandMeta): string;
3
+ export declare function getClaudeCommandPath(id: string): string;
4
+ export declare function hasClaudeCode(projectRoot: string): boolean;
5
+ export declare const claudeAdapter: EditorAdapter;
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Claude Code adapter: `.claude/commands/opsx/<id>.md` with full
3
+ * frontmatter; reads CLAUDE.md directly (thin wrapper installed by
4
+ * project-rules). Playwright MCP installs at project scope — written to
5
+ * the project-root `.mcp.json` via `claude mcp add --scope project`.
6
+ */
7
+ import { existsSync, readFileSync } from "fs";
8
+ import { execFileSync } from "node:child_process";
9
+ import { join } from "path";
10
+ import { TIMEOUT } from "../../../shared/constants.js";
11
+ import { needsShell } from "../../../shared/platform.js";
12
+ import { defineAdapter } from "../types.js";
13
+ import { escapeYamlValue, formatTagsArray } from "../shared.js";
14
+ import { registerAdapter } from "../registry.js";
15
+ // ─── Claude Code adapter ─────────────────────────────────────────────────
16
+ export function formatClaudeCommand(meta) {
17
+ return `---
18
+ name: ${escapeYamlValue(meta.name)}
19
+ description: ${escapeYamlValue(meta.description)}
20
+ category: ${escapeYamlValue(meta.category)}
21
+ tags: ${formatTagsArray(meta.tags)}
22
+ ---
23
+
24
+ ${meta.body}
25
+ `;
26
+ }
27
+ export function getClaudeCommandPath(id) {
28
+ return join(".claude", "commands", "opsx", `${id}.md`);
29
+ }
30
+ export function hasClaudeCode(projectRoot) {
31
+ return existsSync(join(projectRoot, ".claude"));
32
+ }
33
+ export const claudeAdapter = defineAdapter({
34
+ id: "claude",
35
+ label: "claude",
36
+ displayName: "Claude Code",
37
+ detect: hasClaudeCode,
38
+ commandFilePath: getClaudeCommandPath,
39
+ formatCommand: formatClaudeCommand,
40
+ // Claude Code reads CLAUDE.md directly — override the AGENTS.md default.
41
+ projectRulesPath: (root) => join(root, "CLAUDE.md"),
42
+ isMcpInstalled(root, serverName) {
43
+ // Project scope: Playwright MCP lives in the project-root .mcp.json.
44
+ // Read the file directly instead of `claude mcp list` — zero CLI
45
+ // dependency, testable, and never confuses a global user-scope entry
46
+ // with a project-scoped one.
47
+ try {
48
+ const mcpJson = readFileSync(join(root, ".mcp.json"), "utf-8");
49
+ const parsed = JSON.parse(mcpJson);
50
+ return (typeof parsed === "object" &&
51
+ parsed !== null &&
52
+ typeof parsed.mcpServers === "object" &&
53
+ parsed.mcpServers !== null &&
54
+ Object.hasOwn(parsed.mcpServers, serverName));
55
+ }
56
+ catch {
57
+ // Missing or unparseable file — server not installed
58
+ return false;
59
+ }
60
+ },
61
+ installMcp(_root, serverName, command) {
62
+ execFileSync("claude", ["mcp", "add", "--scope", "project", serverName, ...command], {
63
+ encoding: "utf-8",
64
+ timeout: TIMEOUT.MCP_LIST,
65
+ stdio: ["pipe", "pipe", "pipe"],
66
+ shell: needsShell,
67
+ });
68
+ },
69
+ removeMcp(_root, serverName) {
70
+ execFileSync("claude", ["mcp", "remove", "--scope", "project", serverName], {
71
+ encoding: "utf-8",
72
+ timeout: TIMEOUT.MCP_LIST,
73
+ stdio: ["pipe", "pipe", "pipe"],
74
+ shell: needsShell,
75
+ });
76
+ },
77
+ });
78
+ registerAdapter(claudeAdapter);
79
+ //# sourceMappingURL=claude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.js","sourceRoot":"","sources":["../../../../src/commands/editors/adapters/claude.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { type EditorAdapter, type CommandMeta } from "../types.js";
2
+ /**
3
+ * Cline stores project-level config in `.cline/` (skills/, rules/, mcp.json).
4
+ * `.clinerules/` is the legacy rules-only directory, still auto-detected.
5
+ *
6
+ * Conventions follow the Cline documentation (2026):
7
+ * - Skills: `.cline/skills/<name>/SKILL.md` with YAML frontmatter
8
+ * (name + description). Triggered via `/<name>` slash command.
9
+ * - MCP: `.cline/mcp.json` with `{ "mcpServers": { ... } }` structure.
10
+ * - Rules: Cline auto-detects `AGENTS.md` — no wrapper file needed.
11
+ */
12
+ export declare function formatClineCommand(meta: CommandMeta): string;
13
+ export declare function getClineCommandPath(id: string): string;
14
+ export declare function hasCline(projectRoot: string): boolean;
15
+ export declare const clineAdapter: EditorAdapter;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Cline adapter: `.cline/skills/opsx-<id>/SKILL.md` with name+description
3
+ * frontmatter; MCP lives in `.cline/mcp.json`. Cline auto-detects
4
+ * AGENTS.md as project rules — no wrapper file needed.
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
+ // ─── Cline adapter ───────────────────────────────────────────────────────
12
+ /**
13
+ * Cline stores project-level config in `.cline/` (skills/, rules/, mcp.json).
14
+ * `.clinerules/` is the legacy rules-only directory, still auto-detected.
15
+ *
16
+ * Conventions follow the Cline documentation (2026):
17
+ * - Skills: `.cline/skills/<name>/SKILL.md` with YAML frontmatter
18
+ * (name + description). Triggered via `/<name>` slash command.
19
+ * - MCP: `.cline/mcp.json` with `{ "mcpServers": { ... } }` structure.
20
+ * - Rules: Cline auto-detects `AGENTS.md` — no wrapper file needed.
21
+ */
22
+ export function formatClineCommand(meta) {
23
+ const body = transformToHyphenCommands(meta.body);
24
+ const skillName = `opsx-${meta.id}`;
25
+ return `---
26
+ name: ${escapeYamlValue(skillName)}
27
+ description: ${escapeYamlValue(meta.description)}
28
+ ---
29
+
30
+ ${body}
31
+ `;
32
+ }
33
+ export function getClineCommandPath(id) {
34
+ return join(".cline", "skills", `opsx-${id}`, "SKILL.md");
35
+ }
36
+ export function hasCline(projectRoot) {
37
+ return (existsSync(join(projectRoot, ".cline")) ||
38
+ existsSync(join(projectRoot, ".clinerules")));
39
+ }
40
+ export const clineAdapter = defineAdapter({
41
+ id: "cline",
42
+ label: "cline",
43
+ displayName: "Cline",
44
+ detect: hasCline,
45
+ commandFilePath: getClineCommandPath,
46
+ formatCommand: formatClineCommand,
47
+ // Cline auto-detects AGENTS.md — that's the default.
48
+ isMcpInstalled(projectRoot, serverName) {
49
+ return isMcpServerInFile(clineMcpPath(projectRoot), serverName);
50
+ },
51
+ installMcp(projectRoot, serverName, command) {
52
+ installMcpServerInFile(clineMcpPath(projectRoot), serverName, command);
53
+ },
54
+ removeMcp(projectRoot, serverName) {
55
+ removeMcpServerFromFile(clineMcpPath(projectRoot), serverName);
56
+ },
57
+ });
58
+ /** Path to the project-level MCP config file for Cline. */
59
+ function clineMcpPath(projectRoot) {
60
+ return join(projectRoot, ".cline", "mcp.json");
61
+ }
62
+ registerAdapter(clineAdapter);
63
+ //# sourceMappingURL=cline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cline.js","sourceRoot":"","sources":["../../../../src/commands/editors/adapters/cline.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;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAiB;IAClD,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,mBAAmB,CAAC,EAAU;IAC5C,OAAO,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,WAAmB;IAC1C,OAAO,CACL,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;QACvC,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC,CAC7C,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAkB,aAAa,CAAC;IACvD,EAAE,EAAE,OAAO;IACX,KAAK,EAAE,OAAO;IACd,WAAW,EAAE,OAAO;IACpB,MAAM,EAAE,QAAQ;IAChB,eAAe,EAAE,mBAAmB;IACpC,aAAa,EAAE,kBAAkB;IACjC,qDAAqD;IACrD,cAAc,CAAC,WAAW,EAAE,UAAU;QACpC,OAAO,iBAAiB,CAAC,YAAY,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC,CAAC;IAClE,CAAC;IACD,UAAU,CAAC,WAAW,EAAE,UAAU,EAAE,OAAO;QACzC,sBAAsB,CAAC,YAAY,CAAC,WAAW,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;IACzE,CAAC;IACD,SAAS,CAAC,WAAW,EAAE,UAAU;QAC/B,uBAAuB,CAAC,YAAY,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC,CAAC;IACjE,CAAC;CACF,CAAC,CAAC;AAEH,2DAA2D;AAC3D,SAAS,YAAY,CAAC,WAAmB;IACvC,OAAO,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;AACjD,CAAC;AAED,eAAe,CAAC,YAAY,CAAC,CAAC"}
@@ -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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"}