@se-studio/contentful-cms 1.0.19 → 1.0.21

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 (106) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +3 -13
  3. package/dist/bin/cms-edit.js +66 -9
  4. package/dist/bin/cms-edit.js.map +1 -1
  5. package/dist/commands/add.d.ts.map +1 -1
  6. package/dist/commands/add.js +8 -5
  7. package/dist/commands/add.js.map +1 -1
  8. package/dist/commands/asset.d.ts.map +1 -1
  9. package/dist/commands/asset.js +122 -16
  10. package/dist/commands/asset.js.map +1 -1
  11. package/dist/commands/backup.js.map +1 -1
  12. package/dist/commands/diff.d.ts.map +1 -1
  13. package/dist/commands/diff.js +119 -26
  14. package/dist/commands/diff.js.map +1 -1
  15. package/dist/commands/discard.d.ts.map +1 -1
  16. package/dist/commands/discard.js +26 -0
  17. package/dist/commands/discard.js.map +1 -1
  18. package/dist/commands/entry-search.d.ts.map +1 -1
  19. package/dist/commands/entry-search.js +10 -1
  20. package/dist/commands/entry-search.js.map +1 -1
  21. package/dist/commands/health.d.ts +4 -0
  22. package/dist/commands/health.d.ts.map +1 -0
  23. package/dist/commands/health.js +167 -0
  24. package/dist/commands/health.js.map +1 -0
  25. package/dist/commands/list.d.ts.map +1 -1
  26. package/dist/commands/list.js +7 -0
  27. package/dist/commands/list.js.map +1 -1
  28. package/dist/commands/nav.js.map +1 -1
  29. package/dist/commands/open.d.ts.map +1 -1
  30. package/dist/commands/open.js +3 -11
  31. package/dist/commands/open.js.map +1 -1
  32. package/dist/commands/peek.d.ts +4 -0
  33. package/dist/commands/peek.d.ts.map +1 -0
  34. package/dist/commands/peek.js +86 -0
  35. package/dist/commands/peek.js.map +1 -0
  36. package/dist/commands/restore.js +2 -2
  37. package/dist/commands/restore.js.map +1 -1
  38. package/dist/commands/revert.d.ts +4 -0
  39. package/dist/commands/revert.d.ts.map +1 -0
  40. package/dist/commands/revert.js +142 -0
  41. package/dist/commands/revert.js.map +1 -0
  42. package/dist/commands/rtf.d.ts +1 -0
  43. package/dist/commands/rtf.d.ts.map +1 -1
  44. package/dist/commands/rtf.js +308 -14
  45. package/dist/commands/rtf.js.map +1 -1
  46. package/dist/commands/run.d.ts +4 -0
  47. package/dist/commands/run.d.ts.map +1 -0
  48. package/dist/commands/run.js +290 -0
  49. package/dist/commands/run.js.map +1 -0
  50. package/dist/commands/schema.d.ts +4 -0
  51. package/dist/commands/schema.d.ts.map +1 -0
  52. package/dist/commands/schema.js +119 -0
  53. package/dist/commands/schema.js.map +1 -0
  54. package/dist/commands/screenshot.d.ts.map +1 -1
  55. package/dist/commands/screenshot.js +8 -0
  56. package/dist/commands/screenshot.js.map +1 -1
  57. package/dist/commands/sitemap.d.ts +4 -0
  58. package/dist/commands/sitemap.d.ts.map +1 -0
  59. package/dist/commands/sitemap.js +134 -0
  60. package/dist/commands/sitemap.js.map +1 -0
  61. package/dist/contentful/assets.d.ts +14 -1
  62. package/dist/contentful/assets.d.ts.map +1 -1
  63. package/dist/contentful/assets.js +24 -2
  64. package/dist/contentful/assets.js.map +1 -1
  65. package/dist/contentful/client.d.ts +16 -12
  66. package/dist/contentful/client.d.ts.map +1 -1
  67. package/dist/contentful/client.js +12 -21
  68. package/dist/contentful/client.js.map +1 -1
  69. package/dist/contentful/fetch.d.ts +5 -6
  70. package/dist/contentful/fetch.d.ts.map +1 -1
  71. package/dist/contentful/fetch.js +8 -5
  72. package/dist/contentful/fetch.js.map +1 -1
  73. package/dist/contentful/write.d.ts.map +1 -1
  74. package/dist/contentful/write.js +2 -3
  75. package/dist/contentful/write.js.map +1 -1
  76. package/dist/rtf/replacePlainText.d.ts +52 -9
  77. package/dist/rtf/replacePlainText.d.ts.map +1 -1
  78. package/dist/rtf/replacePlainText.js +169 -63
  79. package/dist/rtf/replacePlainText.js.map +1 -1
  80. package/dist/session/state.d.ts +7 -1
  81. package/dist/session/state.d.ts.map +1 -1
  82. package/dist/session/state.js +21 -0
  83. package/dist/session/state.js.map +1 -1
  84. package/dist/session/store.d.ts +10 -4
  85. package/dist/session/store.d.ts.map +1 -1
  86. package/dist/session/store.js +46 -11
  87. package/dist/session/store.js.map +1 -1
  88. package/dist/telemetry/index.d.ts +10 -0
  89. package/dist/telemetry/index.d.ts.map +1 -0
  90. package/dist/telemetry/index.js +11 -0
  91. package/dist/telemetry/index.js.map +1 -0
  92. package/dist/telemetry/local.d.ts +9 -0
  93. package/dist/telemetry/local.d.ts.map +1 -0
  94. package/dist/telemetry/local.js +21 -0
  95. package/dist/telemetry/local.js.map +1 -0
  96. package/dist/telemetry/types.d.ts +29 -0
  97. package/dist/telemetry/types.d.ts.map +1 -0
  98. package/dist/telemetry/types.js +2 -0
  99. package/dist/telemetry/types.js.map +1 -0
  100. package/package.json +4 -4
  101. package/skills/core/SKILL.md +84 -0
  102. package/skills/rich-text/SKILL.md +63 -0
  103. package/dist/commands/export-converted.d.ts +0 -8
  104. package/dist/commands/export-converted.d.ts.map +0 -1
  105. package/dist/commands/export-converted.js +0 -62
  106. package/dist/commands/export-converted.js.map +0 -1
@@ -2,8 +2,33 @@ import * as fs from 'node:fs';
2
2
  import * as os from 'node:os';
3
3
  import * as path from 'node:path';
4
4
  const SESSIONS_DIR = path.join(os.homedir(), '.contentful-cms', 'sessions');
5
- function getSessionPath(spaceId, rootEntryId) {
6
- return path.join(SESSIONS_DIR, `${spaceId}-${rootEntryId}.json`);
5
+ /**
6
+ * Returns the name of the active session from CLI options or the CONTENTFUL_CMS_SESSION env var.
7
+ * Used by all commands to support named, isolated sessions (--session <name>).
8
+ */
9
+ export function resolveSessionName(cliName) {
10
+ return cliName ?? process.env['CONTENTFUL_CMS_SESSION'] ?? undefined;
11
+ }
12
+ function getSessionPath(spaceId, rootEntryId, sessionName) {
13
+ const suffix = sessionName ? `-${sessionName}` : '';
14
+ return path.join(SESSIONS_DIR, `${spaceId}-${rootEntryId}${suffix}.json`);
15
+ }
16
+ /**
17
+ * Returns the path to the most recently modified session file whose name contains the given
18
+ * session name suffix, if any. Used when only --session is given without spaceId/rootEntryId.
19
+ */
20
+ function findNamedSessionPath(sessionName) {
21
+ if (!fs.existsSync(SESSIONS_DIR))
22
+ return null;
23
+ const suffix = `-${sessionName}.json`;
24
+ const files = fs.readdirSync(SESSIONS_DIR).filter((f) => f.endsWith(suffix));
25
+ if (files.length === 0)
26
+ return null;
27
+ const sorted = files
28
+ .map((f) => ({ file: f, mtime: fs.statSync(path.join(SESSIONS_DIR, f)).mtime }))
29
+ .sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
30
+ const latest = sorted[0];
31
+ return latest ? path.join(SESSIONS_DIR, latest.file) : null;
7
32
  }
8
33
  /** Returns the path to the most recently modified session file, if any. */
9
34
  function findLatestSessionPath() {
@@ -18,8 +43,13 @@ function findLatestSessionPath() {
18
43
  const latest = sorted[0];
19
44
  return latest ? path.join(SESSIONS_DIR, latest.file) : null;
20
45
  }
21
- export function loadSession(spaceId, rootEntryId) {
22
- const sessionPath = spaceId && rootEntryId ? getSessionPath(spaceId, rootEntryId) : findLatestSessionPath();
46
+ export function loadSession(spaceId, rootEntryId, sessionName) {
47
+ const resolvedName = resolveSessionName(sessionName);
48
+ const sessionPath = spaceId && rootEntryId
49
+ ? getSessionPath(spaceId, rootEntryId, resolvedName)
50
+ : resolvedName
51
+ ? findNamedSessionPath(resolvedName)
52
+ : findLatestSessionPath();
23
53
  if (!sessionPath || !fs.existsSync(sessionPath))
24
54
  return null;
25
55
  try {
@@ -29,13 +59,15 @@ export function loadSession(spaceId, rootEntryId) {
29
59
  return null;
30
60
  }
31
61
  }
32
- export function saveSession(state) {
62
+ export function saveSession(state, sessionName) {
33
63
  fs.mkdirSync(SESSIONS_DIR, { recursive: true });
34
- const sessionPath = getSessionPath(state.spaceId, state.rootEntryId);
64
+ const resolvedName = resolveSessionName(sessionName);
65
+ const sessionPath = getSessionPath(state.spaceId, state.rootEntryId, resolvedName);
35
66
  fs.writeFileSync(sessionPath, JSON.stringify(state, null, 2), 'utf-8');
36
67
  }
37
- export function clearSession(spaceId, rootEntryId) {
38
- const sessionPath = getSessionPath(spaceId, rootEntryId);
68
+ export function clearSession(spaceId, rootEntryId, sessionName) {
69
+ const resolvedName = resolveSessionName(sessionName);
70
+ const sessionPath = getSessionPath(spaceId, rootEntryId, resolvedName);
39
71
  if (fs.existsSync(sessionPath)) {
40
72
  fs.unlinkSync(sessionPath);
41
73
  }
@@ -51,11 +83,14 @@ export function clearAllSessions() {
51
83
  }
52
84
  /**
53
85
  * Loads the active session or throws a helpful error if none exists.
86
+ * Pass `sessionName` (or set CONTENTFUL_CMS_SESSION) to target a named session.
54
87
  */
55
- export function requireSession() {
56
- const session = loadSession();
88
+ export function requireSession(sessionName) {
89
+ const session = loadSession(undefined, undefined, sessionName);
57
90
  if (!session) {
58
- throw new Error("No active session. Run 'cms-edit open <slug>' or 'cms-edit open <id> --id' first.");
91
+ const nameHint = resolveSessionName(sessionName);
92
+ const hint = nameHint ? ` (session: ${nameHint})` : '';
93
+ throw new Error(`No active session${hint}. Run 'cms-edit open <slug>' or 'cms-edit open <id> --id' first.`);
59
94
  }
60
95
  return session;
61
96
  }
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/session/store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/session/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { TelemetryWriter } from './types.js';
2
+ export { LocalFileTelemetryWriter } from './local.js';
3
+ export type { TelemetryEvent, TelemetryWriter } from './types.js';
4
+ /**
5
+ * Returns the default telemetry writer for this environment.
6
+ * Currently always returns a LocalFileTelemetryWriter.
7
+ * Swap this factory to switch to a remote sink in the future.
8
+ */
9
+ export declare function createTelemetryWriter(): TelemetryWriter;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/telemetry/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AACtD,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElE;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,eAAe,CAEvD"}
@@ -0,0 +1,11 @@
1
+ import { LocalFileTelemetryWriter } from './local.js';
2
+ export { LocalFileTelemetryWriter } from './local.js';
3
+ /**
4
+ * Returns the default telemetry writer for this environment.
5
+ * Currently always returns a LocalFileTelemetryWriter.
6
+ * Swap this factory to switch to a remote sink in the future.
7
+ */
8
+ export function createTelemetryWriter() {
9
+ return new LocalFileTelemetryWriter();
10
+ }
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/telemetry/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAGtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAGtD;;;;GAIG;AACH,MAAM,UAAU,qBAAqB;IACnC,OAAO,IAAI,wBAAwB,EAAE,CAAC;AACxC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { TelemetryEvent, TelemetryWriter } from './types.js';
2
+ /**
3
+ * Appends telemetry events as newline-delimited JSON to ~/.contentful-cms/telemetry.log.
4
+ * Errors are silently swallowed so telemetry never affects CLI behaviour.
5
+ */
6
+ export declare class LocalFileTelemetryWriter implements TelemetryWriter {
7
+ record(event: TelemetryEvent): void;
8
+ }
9
+ //# sourceMappingURL=local.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/telemetry/local.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAIlE;;;GAGG;AACH,qBAAa,wBAAyB,YAAW,eAAe;IAC9D,MAAM,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;CASpC"}
@@ -0,0 +1,21 @@
1
+ import * as fs from 'node:fs';
2
+ import * as os from 'node:os';
3
+ import * as path from 'node:path';
4
+ const LOG_PATH = path.join(os.homedir(), '.contentful-cms', 'telemetry.log');
5
+ /**
6
+ * Appends telemetry events as newline-delimited JSON to ~/.contentful-cms/telemetry.log.
7
+ * Errors are silently swallowed so telemetry never affects CLI behaviour.
8
+ */
9
+ export class LocalFileTelemetryWriter {
10
+ record(event) {
11
+ try {
12
+ const dir = path.dirname(LOG_PATH);
13
+ fs.mkdirSync(dir, { recursive: true });
14
+ fs.appendFileSync(LOG_PATH, JSON.stringify(event) + '\n', 'utf-8');
15
+ }
16
+ catch {
17
+ // Intentionally swallowed — telemetry must not break the CLI
18
+ }
19
+ }
20
+ }
21
+ //# sourceMappingURL=local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.js","sourceRoot":"","sources":["../../src/telemetry/local.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAGlC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,iBAAiB,EAAE,eAAe,CAAC,CAAC;AAE7E;;;GAGG;AACH,MAAM,OAAO,wBAAwB;IACnC,MAAM,CAAC,KAAqB;QAC1B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YACnC,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACvC,EAAE,CAAC,cAAc,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;QACrE,CAAC;QAAC,MAAM,CAAC;YACP,6DAA6D;QAC/D,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * A single telemetry event recording one CLI command invocation.
3
+ * No PII is stored: no slugs, entry IDs, field values, query text, or replacement strings.
4
+ */
5
+ export interface TelemetryEvent {
6
+ /** ISO 8601 timestamp of when the command completed. */
7
+ timestamp: string;
8
+ /** Top-level command name (e.g. "rtf", "open", "save"). */
9
+ command: string;
10
+ /** Subcommand name when applicable (e.g. "replace", "embed", "headings"). */
11
+ subcommand?: string;
12
+ /** Whether the command succeeded, errored, or exited (process.exit called). */
13
+ outcome: 'success' | 'error' | 'exit';
14
+ /** Process exit code (populated for 'exit' outcome). */
15
+ exit_code?: number;
16
+ /** Error constructor name only — NOT the message, to avoid logging PII. */
17
+ error_name?: string;
18
+ /** Wall-clock duration from command start to completion in milliseconds. */
19
+ duration_ms: number;
20
+ }
21
+ /**
22
+ * Abstraction for telemetry sinks. Swap out LocalFileTelemetryWriter for a remote
23
+ * implementation in the future without changing call sites.
24
+ */
25
+ export interface TelemetryWriter {
26
+ record(event: TelemetryEvent): void;
27
+ flush?(): Promise<void>;
28
+ }
29
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/telemetry/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,OAAO,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;IACtC,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IACpC,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/telemetry/types.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@se-studio/contentful-cms",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
4
4
  "description": "CLI tool for AI agents to read and edit Contentful draft content without publish permissions",
5
5
  "repository": {
6
6
  "type": "git",
@@ -28,15 +28,15 @@
28
28
  "dependencies": {
29
29
  "@contentful/rich-text-types": "^17.2.5",
30
30
  "commander": "^14.0.3",
31
- "contentful-management": "^11.75.0",
31
+ "contentful-management": "^12.0.0",
32
32
  "dotenv": "^17.3.1",
33
33
  "picocolors": "^1.1.1"
34
34
  },
35
35
  "devDependencies": {
36
- "@biomejs/biome": "^2.4.8",
36
+ "@biomejs/biome": "^2.4.9",
37
37
  "@types/node": "^22.19.15",
38
38
  "typescript": "^6.0.2",
39
- "vitest": "^4.1.1"
39
+ "vitest": "^4.1.2"
40
40
  },
41
41
  "keywords": [
42
42
  "contentful",
@@ -48,6 +48,8 @@ cms-edit open 4xKj2abcDef --id
48
48
  cms-edit open /
49
49
  ```
50
50
 
51
+ **Slug `open`:** matches **page**, **article**, **articleType**, and **tag** by `fields.slug` (in that order; first match wins). For **template** or **navigation**, use `open <id> --id` or `cms-edit nav open`.
52
+
51
53
  ### Step 2: Read the snapshot
52
54
 
53
55
  ```bash
@@ -510,6 +512,88 @@ cms-edit screenshot @c0 --out after.png
510
512
  agent-browser diff screenshot --baseline before.png after.png
511
513
  ```
512
514
 
515
+ ## Revert
516
+
517
+ Field-level undo — restore fields to their original values from when the session was opened.
518
+
519
+ ```bash
520
+ cms-edit revert @c0 heading # Revert a single field to its original value
521
+ cms-edit revert @c0 # Revert all fields on an entry
522
+ cms-edit revert --all # Revert all modified entries (session stays open)
523
+ ```
524
+
525
+ Note: Entries created via `add` cannot be reverted — use `remove` instead.
526
+
527
+ ## Health Check
528
+
529
+ Validate config and connectivity before running a workflow.
530
+
531
+ ```bash
532
+ cms-edit health # Check config file, space config, Contentful connectivity, agent-browser
533
+ ```
534
+
535
+ ## Schema Inspection
536
+
537
+ Inspect field definitions for a content type. Prefer this over `cms-edit types <ct>` when you need full field info.
538
+
539
+ ```bash
540
+ cms-edit schema component # Show all fields, types, enum values, link targets
541
+ cms-edit schema component --json # Full JSON output
542
+ ```
543
+
544
+ ## Sitemap
545
+
546
+ Browse all pages in the space.
547
+
548
+ ```bash
549
+ cms-edit sitemap # All pages, sorted by slug
550
+ cms-edit sitemap --prefix /blog # Filter by slug prefix
551
+ cms-edit sitemap --status draft # Draft pages only
552
+ cms-edit sitemap --sort updated # Most recently updated first
553
+ cms-edit sitemap --tree # Tree rendering
554
+ cms-edit sitemap --include page,article # Include articles too
555
+ ```
556
+
557
+ ## Peek
558
+
559
+ Inspect a page without affecting your active session.
560
+
561
+ ```bash
562
+ cms-edit peek /pricing # Show snapshot of /pricing; your active session is unchanged
563
+ cms-edit peek <id> --id # Look up by entry ID
564
+ ```
565
+
566
+ ## Batch Operations (run)
567
+
568
+ Run a sequence of operations from a JSON file or stdin.
569
+
570
+ ```bash
571
+ cms-edit run --file ops.json # Run batch ops from file
572
+ echo '[...]' | cms-edit run # Pipe from stdin
573
+ cms-edit run --file ops.json --dry-run # Validate without saving
574
+ ```
575
+
576
+ Supported ops: `open`, `set`, `rtf`, `rtf-replace`, `save`.
577
+
578
+ Example `ops.json`:
579
+ ```json
580
+ [
581
+ { "cmd": "open", "args": ["/pricing"] },
582
+ { "cmd": "set", "args": ["@c0", "heading", "New Heading"] },
583
+ { "cmd": "rtf", "args": ["@c1", "body", "## Section\n\nContent here."] },
584
+ { "cmd": "save" }
585
+ ]
586
+ ```
587
+
588
+ ## Concurrent Sessions
589
+
590
+ Use `--session <name>` or `CONTENTFUL_CMS_SESSION=<name>` to isolate parallel workflows.
591
+
592
+ ```bash
593
+ cms-edit --session agent-1 open /pricing
594
+ cms-edit --session agent-2 open /blog
595
+ ```
596
+
513
597
  ## Related skills
514
598
 
515
599
  For templates see the **templates** skill; for navigation see the **navigation** skill; for rich text and embeds see the **rich-text** skill; for screenshots see the **screenshots** skill.
@@ -18,6 +18,69 @@ Use **set** for scalar and link fields; use **rtf** only for rich text fields.
18
18
 
19
19
  Editing or moving existing embeds is **not supported in v1**.
20
20
 
21
+ ## rtf replace — additional flags
22
+
23
+ These flags provide finer control when using `rtf replace`:
24
+
25
+ | Flag | Description |
26
+ |------|-------------|
27
+ | `--find-plain <text>` | Alias for `--find`; treats the search string as literal text (use when the string contains `*`) |
28
+ | `--ignore-marks` | Match across adjacent bold/plain splits within the same block node |
29
+ | `--protect-pattern <regex>` | Skip matches that fall inside spans matching this regex (e.g. existing quidget tokens) |
30
+ | `--dry-run` | Count matches without writing any changes |
31
+ | `--in-table-row <n>` | Restrict matches to row N (1-indexed; row 1 = header) |
32
+ | `--in-row-containing <text>` | Restrict to rows where any cell contains this text |
33
+ | `--in-col-containing <text>` | Restrict to the column whose header cell contains this text |
34
+
35
+ Example — replace a value only in the column whose header contains "CSP":
36
+ ```bash
37
+ cms-edit rtf replace @c1 body \
38
+ --find 'None' \
39
+ --replace-plain '{*credit_card_id*:*5048345*,*field*:*foreign_transaction_fee*,*api*:*cc*}' \
40
+ --in-col-containing 'CSP' \
41
+ --mode all
42
+ ```
43
+
44
+ ## rtf patch — JSON batch find/replace
45
+
46
+ Apply multiple find/replace operations in a single call. All ops are validated before any write.
47
+
48
+ ```bash
49
+ # From file
50
+ cms-edit rtf patch @c0 body --file patch.json
51
+
52
+ # From stdin
53
+ echo '[{"find":"old","replaceWith":"new","mode":"exactlyOne"}]' | cms-edit rtf patch @c0 body
54
+ ```
55
+
56
+ Each op in the JSON array supports:
57
+
58
+ | Key | Required | Description |
59
+ |-----|----------|-------------|
60
+ | `find` | yes | Search string (Markdown-interpreted) |
61
+ | `replaceWith` | yes | Replacement string (Markdown-interpreted unless `replaceIsMarkdown: false`) |
62
+ | `replaceIsMarkdown` | no | Set to `false` to treat `replaceWith` as plain text (like `--replace-plain`) |
63
+ | `mode` | no | `"exactlyOne"` (default), `"all"`, `"first"` |
64
+ | `ignoreMarks` | no | Match across adjacent bold/plain splits |
65
+ | `inTableCol` | no | Restrict to column N (1-indexed) |
66
+ | `inTableRow` | no | Restrict to row N (1-indexed) |
67
+ | `inRowContaining` | no | Restrict to rows where any cell contains this text |
68
+ | `inColContaining` | no | Restrict to the column whose header cell contains this text |
69
+ | `section` | no | Restrict to a document section |
70
+ | `protectPattern` | no | Skip matches inside spans matching this regex |
71
+
72
+ Add `--dry-run` to count matches without writing.
73
+
74
+ ## rtf edit — replace field via stdin
75
+
76
+ Replace an entire rich text field by piping Markdown from stdin. Avoids shell quoting issues for long content.
77
+
78
+ ```bash
79
+ echo "## New heading\n\nNew body text." | cms-edit rtf edit @c1 body
80
+ ```
81
+
82
+ In human (interactive) mode, the command prints the current Markdown to stdout first, then reads the replacement from stdin.
83
+
21
84
  ## Related skills
22
85
 
23
86
  See the **core** skill for workflow, refs, and all commands.
@@ -1,8 +0,0 @@
1
- /** biome-ignore-all lint/suspicious/noConsole: Console output is intentional in CLI */
2
- import type { Command } from 'commander';
3
- /**
4
- * Export a session ref's entry as converted (IBase*) JSON by calling the app's
5
- * convert API. Use the output with cms-edit screenshot --json-file for variants.
6
- */
7
- export declare function registerExportConvertedCommand(program: Command): void;
8
- //# sourceMappingURL=export-converted.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"export-converted.d.ts","sourceRoot":"","sources":["../../src/commands/export-converted.ts"],"names":[],"mappings":"AAAA,uFAAuF;AAIvF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQzC;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA4DrE"}
@@ -1,62 +0,0 @@
1
- /** biome-ignore-all lint/suspicious/noConsole: Console output is intentional in CLI */
2
- import * as fs from 'node:fs';
3
- import * as path from 'node:path';
4
- import { loadConfig, resolveSpaceConfig } from '../config/load.js';
5
- import { errorMsg, successMsg } from '../output/print.js';
6
- import { resolveRef } from '../session/refs.js';
7
- import { requireSession } from '../session/store.js';
8
- const DEFAULT_BASE_URL = 'http://localhost:3000';
9
- /**
10
- * Export a session ref's entry as converted (IBase*) JSON by calling the app's
11
- * convert API. Use the output with cms-edit screenshot --json-file for variants.
12
- */
13
- export function registerExportConvertedCommand(program) {
14
- program
15
- .command('export-converted <ref>')
16
- .description("Export a ref's entry as converted JSON (IBase*) via the app's convert API. " +
17
- 'Use with screenshot --json-file for custom variants. App must be running at devBaseUrl.')
18
- .option('--out <path>', 'Output file path (required)')
19
- .action(async (ref, opts, cmd) => {
20
- try {
21
- const session = requireSession();
22
- const parentOpts = cmd.parent?.opts() ?? {};
23
- const config = loadConfig(parentOpts.config);
24
- const { space } = resolveSpaceConfig(config, parentOpts.space);
25
- const baseUrl = space.devBaseUrl ?? DEFAULT_BASE_URL;
26
- if (!space.devBaseUrl) {
27
- errorMsg('No devBaseUrl set. Add "devBaseUrl" (e.g. "http://localhost:3010") to your space in .contentful-cms.json.');
28
- process.exit(1);
29
- }
30
- if (!opts.out) {
31
- errorMsg('Missing --out <path>. Example: cms-edit export-converted @c1 --out hero-base.json');
32
- process.exit(1);
33
- }
34
- const entry = resolveRef(session, ref);
35
- const entryId = entry.sys.id;
36
- const convertUrl = `${baseUrl}/api/cms-preview/convert?id=${entryId}`;
37
- const res = await fetch(convertUrl);
38
- if (!res.ok) {
39
- const text = await res.text();
40
- errorMsg(`Convert API failed (${res.status}). Ensure the app is running at ${baseUrl}. ${text.slice(0, 150)}`);
41
- process.exit(1);
42
- }
43
- const body = (await res.json());
44
- if (body.data === undefined || body.data === null) {
45
- errorMsg('Convert API returned no data.');
46
- process.exit(1);
47
- }
48
- const outPath = path.resolve(opts.out);
49
- const dir = path.dirname(outPath);
50
- if (dir && dir !== '.') {
51
- fs.mkdirSync(dir, { recursive: true });
52
- }
53
- fs.writeFileSync(outPath, JSON.stringify(body.data, null, 2), 'utf-8');
54
- successMsg(`Exported converted JSON to ${outPath}`);
55
- }
56
- catch (err) {
57
- errorMsg(err instanceof Error ? err.message : String(err));
58
- process.exit(1);
59
- }
60
- });
61
- }
62
- //# sourceMappingURL=export-converted.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"export-converted.js","sourceRoot":"","sources":["../../src/commands/export-converted.ts"],"names":[],"mappings":"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"}