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.
Files changed (63) hide show
  1. package/README.md +18 -17
  2. package/README.zh-CN.md +19 -18
  3. package/dist/commands/doctor.d.ts +1 -0
  4. package/dist/commands/doctor.js +54 -23
  5. package/dist/commands/doctor.js.map +1 -1
  6. package/dist/commands/editors/adapters/claude.d.ts +5 -0
  7. package/dist/commands/editors/adapters/claude.js +79 -0
  8. package/dist/commands/editors/adapters/claude.js.map +1 -0
  9. package/dist/commands/editors/adapters/cline.d.ts +15 -0
  10. package/dist/commands/editors/adapters/cline.js +63 -0
  11. package/dist/commands/editors/adapters/cline.js.map +1 -0
  12. package/dist/commands/editors/adapters/cursor.d.ts +15 -0
  13. package/dist/commands/editors/adapters/cursor.js +79 -0
  14. package/dist/commands/editors/adapters/cursor.js.map +1 -0
  15. package/dist/commands/editors/adapters/dsh.d.ts +27 -0
  16. package/dist/commands/editors/adapters/dsh.js +67 -0
  17. package/dist/commands/editors/adapters/dsh.js.map +1 -0
  18. package/dist/commands/editors/adapters/omp.d.ts +24 -0
  19. package/dist/commands/editors/adapters/omp.js +73 -0
  20. package/dist/commands/editors/adapters/omp.js.map +1 -0
  21. package/dist/commands/editors/adapters/opencode.d.ts +8 -0
  22. package/dist/commands/editors/adapters/opencode.js +133 -0
  23. package/dist/commands/editors/adapters/opencode.js.map +1 -0
  24. package/dist/commands/editors/adapters/pi.d.ts +27 -0
  25. package/dist/commands/editors/adapters/pi.js +64 -0
  26. package/dist/commands/editors/adapters/pi.js.map +1 -0
  27. package/dist/commands/editors/project-rules.d.ts +69 -0
  28. package/dist/commands/editors/project-rules.js +246 -0
  29. package/dist/commands/editors/project-rules.js.map +1 -0
  30. package/dist/commands/editors/registry.d.ts +12 -0
  31. package/dist/commands/editors/registry.js +59 -0
  32. package/dist/commands/editors/registry.js.map +1 -0
  33. package/dist/commands/editors/shared.d.ts +37 -0
  34. package/dist/commands/editors/shared.js +92 -0
  35. package/dist/commands/editors/shared.js.map +1 -0
  36. package/dist/commands/editors/tool-selection.d.ts +17 -0
  37. package/dist/commands/editors/tool-selection.js +60 -0
  38. package/dist/commands/editors/tool-selection.js.map +1 -0
  39. package/dist/commands/editors/types.d.ts +82 -0
  40. package/dist/commands/editors/types.js +41 -0
  41. package/dist/commands/editors/types.js.map +1 -0
  42. package/dist/commands/editors.d.ts +32 -309
  43. package/dist/commands/editors.js +38 -960
  44. package/dist/commands/editors.js.map +1 -1
  45. package/dist/commands/explore.d.ts +2 -0
  46. package/dist/commands/explore.js +2 -2
  47. package/dist/commands/explore.js.map +1 -1
  48. package/dist/commands/init.js +15 -25
  49. package/dist/commands/init.js.map +1 -1
  50. package/dist/commands/uninstall.js +4 -2
  51. package/dist/commands/uninstall.js.map +1 -1
  52. package/dist/commands/update.d.ts +8 -0
  53. package/dist/commands/update.js +12 -8
  54. package/dist/commands/update.js.map +1 -1
  55. package/dist/shared/index.d.ts +1 -1
  56. package/dist/shared/index.js +1 -1
  57. package/dist/shared/index.js.map +1 -1
  58. package/dist/shared/mcp.d.ts +5 -2
  59. package/dist/shared/mcp.js +15 -7
  60. package/dist/shared/mcp.js.map +1 -1
  61. package/employee-standards.md +2 -0
  62. package/package.json +5 -5
  63. 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;