@tenonhq/dovetail-core 0.0.113 → 0.0.114

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/appUtils.js CHANGED
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.createAndAssignUpdateSet = exports.swapScope = exports.buildFiles = exports.summarizeRecord = exports.pushFiles = exports.getAppFileList = exports.groupAppFiles = exports.processMissingFiles = exports.refreshAllFiles = exports.findMissingFiles = exports.syncManifest = exports.narrowManifestToTables = exports.processManifest = exports.normalizeManifestKeys = exports.toSafeFolderName = exports.stampMetadataContent = void 0;
39
+ exports.createAndAssignUpdateSet = exports.swapScope = exports.buildFiles = exports.summarizeRecord = exports.pushFiles = exports.getAppFileList = exports.groupAppFiles = exports.processMissingFiles = exports.refreshAllFiles = exports.findMissingFiles = exports.syncManifest = exports.narrowManifestToRecord = exports.narrowManifestToTables = exports.processManifest = exports.normalizeManifestKeys = exports.toSafeFolderName = exports.stampMetadataContent = void 0;
40
40
  const path_1 = __importDefault(require("path"));
41
41
  const progress_1 = __importDefault(require("progress"));
42
42
  const fUtils = __importStar(require("./FileUtils"));
@@ -298,6 +298,44 @@ const narrowManifestToTables = (manifest, tables) => {
298
298
  return Object.assign({}, manifest, { tables: wantedTables });
299
299
  };
300
300
  exports.narrowManifestToTables = narrowManifestToTables;
301
+ /**
302
+ * Narrow a manifest's table map to a SINGLE record (matched by `table` +
303
+ * `sysId`), for the FILE refresh only.
304
+ *
305
+ * Returns a NEW manifest and NEVER mutates its input — same safety contract as
306
+ * `narrowManifestToTables`: the caller keeps writing the original, full manifest
307
+ * to disk, so the on-disk `dove.manifest.<scope>.json` never loses its other
308
+ * tables/records. Only the file download is scoped to this one record.
309
+ *
310
+ * If the table or a record with that sys_id is absent from the manifest, the
311
+ * returned manifest has an empty table map — refreshAllFiles no-ops on it, so no
312
+ * files are written (the create flow warns and points the user at `dove refresh`).
313
+ */
314
+ const narrowManifestToRecord = (manifest, table, sysId) => {
315
+ var empty = Object.assign({}, manifest, { tables: {} });
316
+ var tables = manifest.tables || {};
317
+ var tableEntry = tables[table];
318
+ if (!tableEntry || !tableEntry.records)
319
+ return empty;
320
+ var records = tableEntry.records;
321
+ var recKeys = Object.keys(records);
322
+ var wantedRecords = {};
323
+ // sys_id is unique per table, so stop at the first match — the record set for
324
+ // a large table can be sizable and this is the exact scenario the feature targets.
325
+ for (var i = 0; i < recKeys.length; i++) {
326
+ var rec = records[recKeys[i]];
327
+ if (rec && rec.sys_id === sysId) {
328
+ wantedRecords[recKeys[i]] = rec;
329
+ break;
330
+ }
331
+ }
332
+ if (Object.keys(wantedRecords).length === 0)
333
+ return empty;
334
+ var narrowedTables = {};
335
+ narrowedTables[table] = Object.assign({}, tableEntry, { records: wantedRecords });
336
+ return Object.assign({}, manifest, { tables: narrowedTables });
337
+ };
338
+ exports.narrowManifestToRecord = narrowManifestToRecord;
301
339
  const syncManifest = async (scope, options = {}) => {
302
340
  // Top-level entry owns the collector lifecycle. Recursive calls (all-scopes
303
341
  // → per-scope) inherit the collector via options._benchmarkCollector.
@@ -368,6 +406,23 @@ const syncManifest = async (scope, options = {}) => {
368
406
  // so intersecting the manifest is enough. `newManifest` is left intact —
369
407
  // see narrowManifestToTables.
370
408
  var refreshManifest = (0, exports.narrowManifestToTables)(newManifest, options.tables);
409
+ // Single-record narrowing (dove create round-trip). Applied after the
410
+ // --table narrowing so the two compose; the full manifest was already
411
+ // written above, so only the file download is scoped to the one record.
412
+ if (options.record) {
413
+ refreshManifest = (0, exports.narrowManifestToRecord)(refreshManifest, options.record.table, options.record.sysId);
414
+ if (Object.keys(refreshManifest.tables).length === 0) {
415
+ Logger_1.logger.warn("Created record " + options.record.sysId + " (" + options.record.table +
416
+ ") not found in the refreshed scope manifest — local files not written. " +
417
+ "If '" + options.record.table + "' is missing from this scope's `_tables` " +
418
+ "whitelist in dove.config.js, add it there first; otherwise run " +
419
+ "'npx dove refresh' to pull it.");
420
+ }
421
+ else {
422
+ FileLogger_1.fileLogger.debug("syncManifest: narrowed file refresh to single record " +
423
+ options.record.sysId + " (" + options.record.table + ")");
424
+ }
425
+ }
371
426
  if (options.tables && options.tables.length > 0) {
372
427
  var wantedNames = Object.keys(refreshManifest.tables);
373
428
  if (wantedNames.length === 0) {
package/dist/commander.js CHANGED
@@ -228,6 +228,11 @@ function configureCli(cli) {
228
228
  default: false,
229
229
  describe: "Skip interactive prompts",
230
230
  },
231
+ refresh: {
232
+ type: "boolean",
233
+ default: true,
234
+ describe: "Pull the created record's files to disk afterward (default: true; --no-refresh skips the local sync)",
235
+ },
231
236
  });
232
237
  return cmdArgs;
233
238
  }, createRecordCommand_1.createRecordCommand)
@@ -281,20 +281,33 @@ async function createRecordCommand(args) {
281
281
  " (" +
282
282
  newSysId +
283
283
  ")");
284
- // 7. Full round-trip: pull the record back locally
285
- Logger_1.logger.info("Syncing record to local files...");
286
- FileLogger_1.fileLogger.debug("Starting manifest sync for scope:", scope);
287
- try {
288
- await AppUtils.syncManifest(scope);
289
- var sourcePath = ConfigManager.getSourcePath();
290
- var localPath = path_1.default.join(sourcePath, table, recordName);
291
- Logger_1.logger.success(chalk_1.default.green("Local files created at: ") + localPath);
284
+ // 7. Round-trip: pull ONLY the new record's files back locally. Scoped to
285
+ // { table, sysId } so creating one record no longer refreshes the entire
286
+ // scope (the whole-scope-churn footgun). --no-refresh skips it entirely.
287
+ if (typedArgs.refresh === false) {
288
+ Logger_1.logger.info("Skipping local sync (--no-refresh). Run 'npx dove refresh' to pull the record locally.");
292
289
  }
293
- catch (syncErr) {
294
- Logger_1.logger.warn("Record created on instance but local sync failed.");
295
- Logger_1.logger.warn("Run 'npx dove refresh' to pull the record locally.");
296
- if (syncErr instanceof Error) {
297
- FileLogger_1.fileLogger.error("Sync error:", syncErr.message);
290
+ else {
291
+ Logger_1.logger.info("Syncing record to local files...");
292
+ FileLogger_1.fileLogger.debug("Starting single-record sync for scope:", scope);
293
+ try {
294
+ await AppUtils.syncManifest(scope, {
295
+ record: { table: table, sysId: newSysId },
296
+ });
297
+ // Resolve the scope's own source directory — syncManifest writes into
298
+ // getSourcePathForScope(scope), which a scope config can override. Using
299
+ // the top-level getSourcePath() here would print a wrong path in
300
+ // multi-scope setups even though the files landed correctly.
301
+ var sourcePath = ConfigManager.getSourcePathForScope(scope);
302
+ var localPath = path_1.default.join(sourcePath, table, recordName);
303
+ Logger_1.logger.success(chalk_1.default.green("Local files created at: ") + localPath);
304
+ }
305
+ catch (syncErr) {
306
+ Logger_1.logger.warn("Record created on instance but local sync failed.");
307
+ Logger_1.logger.warn("Run 'npx dove refresh' to pull the record locally.");
308
+ if (syncErr instanceof Error) {
309
+ FileLogger_1.fileLogger.error("Sync error:", syncErr.message);
310
+ }
298
311
  }
299
312
  }
300
313
  }
@@ -0,0 +1,234 @@
1
+ "use strict";
2
+ /**
3
+ * Tests for the single-record narrowing of syncManifest — the `record` option
4
+ * used by `dove create`'s post-create round-trip.
5
+ *
6
+ * Regression target: `dove create <table>` previously called syncManifest(scope)
7
+ * with no narrowing, which re-downloaded and rewrote EVERY record in the scope.
8
+ * Creating one record churned ~1,892 files in a scope like x_cadso_text_spoke.
9
+ * The fix scopes the file refresh to just the created record.
10
+ *
11
+ * These tests assert:
12
+ * 1. narrowManifestToRecord returns only the matching record, never mutating input
13
+ * 2. narrowManifestToRecord returns an empty table map when the record is absent
14
+ * 3. syncManifest({ record }) fetches + writes ONLY the target record's files
15
+ * 4. sibling records already on disk are left untouched (never re-fetched)
16
+ * 5. a record absent from the fetched manifest → warn, no fetch, no throw
17
+ */
18
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ var desc = Object.getOwnPropertyDescriptor(m, k);
21
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
22
+ desc = { enumerable: true, get: function() { return m[k]; } };
23
+ }
24
+ Object.defineProperty(o, k2, desc);
25
+ }) : (function(o, m, k, k2) {
26
+ if (k2 === undefined) k2 = k;
27
+ o[k2] = m[k];
28
+ }));
29
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
30
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
31
+ }) : function(o, v) {
32
+ o["default"] = v;
33
+ });
34
+ var __importStar = (this && this.__importStar) || (function () {
35
+ var ownKeys = function(o) {
36
+ ownKeys = Object.getOwnPropertyNames || function (o) {
37
+ var ar = [];
38
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
39
+ return ar;
40
+ };
41
+ return ownKeys(o);
42
+ };
43
+ return function (mod) {
44
+ if (mod && mod.__esModule) return mod;
45
+ var result = {};
46
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
47
+ __setModuleDefault(result, mod);
48
+ return result;
49
+ };
50
+ })();
51
+ Object.defineProperty(exports, "__esModule", { value: true });
52
+ const fs = __importStar(require("fs"));
53
+ const os = __importStar(require("os"));
54
+ const path = __importStar(require("path"));
55
+ // ---------- mocks (must come before importing the module under test) ----------
56
+ var mockLogger = {
57
+ info: jest.fn(),
58
+ debug: jest.fn(),
59
+ warn: jest.fn(),
60
+ error: jest.fn(),
61
+ success: jest.fn(),
62
+ getLogLevel: function () { return "warn"; },
63
+ };
64
+ var mockFileLogger = {
65
+ debug: jest.fn(),
66
+ info: jest.fn(),
67
+ warn: jest.fn(),
68
+ error: jest.fn(),
69
+ };
70
+ var mockClient = {
71
+ getManifest: jest.fn(),
72
+ getMissingFiles: jest.fn(),
73
+ };
74
+ jest.mock("../Logger", function () { return { logger: mockLogger }; });
75
+ jest.mock("../FileLogger", function () { return { fileLogger: mockFileLogger }; });
76
+ jest.mock("../snClient", function () {
77
+ return {
78
+ defaultClient: function () { return mockClient; },
79
+ unwrapSNResponse: function (p) { return Promise.resolve(p).then(function (r) { return r; }); },
80
+ processPushResponse: jest.fn(),
81
+ retryOnErr: jest.fn(),
82
+ retryOnHttpErr: jest.fn(),
83
+ unwrapTableAPIFirstItem: jest.fn(),
84
+ };
85
+ });
86
+ var mockConfig = {
87
+ getConfig: jest.fn().mockReturnValue({
88
+ scopes: { x_cadso_core: {} },
89
+ tableOptions: {},
90
+ }),
91
+ getManifest: jest.fn().mockResolvedValue({
92
+ x_cadso_core: { scope: "x_cadso_core", tables: {} },
93
+ }),
94
+ getSourcePathForScope: jest.fn(),
95
+ getSourcePath: jest.fn(),
96
+ getManifestPath: jest.fn().mockReturnValue("/tmp/dove.manifest.json"),
97
+ resolveConfigForScope: jest.fn().mockImplementation(function () {
98
+ return {
99
+ tables: ["sys_script_include"],
100
+ fieldOverrides: {},
101
+ apiIncludes: {},
102
+ apiExcludes: {},
103
+ };
104
+ }),
105
+ isMultiScopeManifest: jest.fn().mockReturnValue(true),
106
+ updateManifest: jest.fn(),
107
+ };
108
+ jest.mock("../config", function () { return mockConfig; });
109
+ jest.mock("progress", function () {
110
+ return jest.fn().mockImplementation(function () {
111
+ return { tick: jest.fn() };
112
+ });
113
+ });
114
+ // Stub writeScopeManifest so the test never writes a manifest file. Leave the
115
+ // other FileUtils exports real so we can assert against the file system.
116
+ jest.mock("../FileUtils", function () {
117
+ var actual = jest.requireActual("../FileUtils");
118
+ return Object.assign({}, actual, {
119
+ writeScopeManifest: jest.fn().mockResolvedValue(undefined),
120
+ });
121
+ });
122
+ const AppUtils = __importStar(require("../appUtils"));
123
+ // ---------- fixtures ----------
124
+ // A three-record scope manifest, all in sys_script_include, matching the shape
125
+ // client.getManifest returns (files carry name+type only; no content).
126
+ function threeRecordManifest() {
127
+ return {
128
+ scope: "x_cadso_core",
129
+ tables: {
130
+ sys_script_include: {
131
+ records: {
132
+ New: { name: "New", sys_id: "sysid_New", files: [{ name: "script", type: "js" }] },
133
+ Sib1: { name: "Sib1", sys_id: "sysid_Sib1", files: [{ name: "script", type: "js" }] },
134
+ Sib2: { name: "Sib2", sys_id: "sysid_Sib2", files: [{ name: "script", type: "js" }] },
135
+ },
136
+ },
137
+ },
138
+ };
139
+ }
140
+ var tmpRoot;
141
+ function writeLocal(record, name, type, content) {
142
+ var recDir = path.join(tmpRoot, "sys_script_include", record);
143
+ fs.mkdirSync(recDir, { recursive: true });
144
+ fs.writeFileSync(path.join(recDir, name + "." + type), content);
145
+ }
146
+ function readLocal(record, name, type) {
147
+ var p = path.join(tmpRoot, "sys_script_include", record, name + "." + type);
148
+ try {
149
+ return fs.readFileSync(p, "utf8");
150
+ }
151
+ catch (e) {
152
+ return null;
153
+ }
154
+ }
155
+ // ---------- pure helper ----------
156
+ describe("narrowManifestToRecord", function () {
157
+ test("returns only the matching record and does NOT mutate the input", function () {
158
+ var manifest = threeRecordManifest();
159
+ var out = AppUtils.narrowManifestToRecord(manifest, "sys_script_include", "sysid_New");
160
+ // input untouched — all three records still present
161
+ expect(Object.keys(manifest.tables.sys_script_include.records).sort())
162
+ .toEqual(["New", "Sib1", "Sib2"]);
163
+ // output holds exactly the one record
164
+ expect(Object.keys(out.tables)).toEqual(["sys_script_include"]);
165
+ expect(Object.keys(out.tables.sys_script_include.records)).toEqual(["New"]);
166
+ expect(out.tables.sys_script_include.records.New.sys_id).toBe("sysid_New");
167
+ });
168
+ test("returns an empty table map when the sys_id is absent", function () {
169
+ var manifest = threeRecordManifest();
170
+ var out = AppUtils.narrowManifestToRecord(manifest, "sys_script_include", "sysid_Missing");
171
+ expect(Object.keys(out.tables)).toEqual([]);
172
+ });
173
+ test("returns an empty table map when the table is absent", function () {
174
+ var manifest = threeRecordManifest();
175
+ var out = AppUtils.narrowManifestToRecord(manifest, "sys_ui_action", "sysid_New");
176
+ expect(Object.keys(out.tables)).toEqual([]);
177
+ });
178
+ });
179
+ // ---------- syncManifest({ record }) ----------
180
+ describe("syncManifest — single-record round-trip (dove create)", function () {
181
+ beforeEach(function () {
182
+ jest.clearAllMocks();
183
+ tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "sinc-record-test-"));
184
+ mockConfig.getSourcePathForScope.mockReturnValue(tmpRoot);
185
+ mockConfig.getSourcePath.mockReturnValue(tmpRoot);
186
+ });
187
+ afterEach(function () {
188
+ try {
189
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
190
+ }
191
+ catch (e) { }
192
+ });
193
+ test("fetches + writes ONLY the target record; siblings are never re-fetched", async function () {
194
+ // A stale sibling file already on disk — it must be left exactly as-is.
195
+ writeLocal("Sib1", "script", "js", "STALE SIBLING — must not change");
196
+ mockClient.getManifest.mockResolvedValue(threeRecordManifest());
197
+ // getMissingFiles should be asked for the new record ONLY; return its content.
198
+ mockClient.getMissingFiles.mockResolvedValue({
199
+ sys_script_include: {
200
+ records: {
201
+ New: {
202
+ name: "New", sys_id: "sysid_New",
203
+ files: [{ name: "script", type: "js", content: "// fresh created record" }],
204
+ },
205
+ },
206
+ },
207
+ });
208
+ await AppUtils.syncManifest("x_cadso_core", {
209
+ record: { table: "sys_script_include", sysId: "sysid_New" },
210
+ });
211
+ // exactly one content fetch, scoped to the one record
212
+ expect(mockClient.getMissingFiles).toHaveBeenCalledTimes(1);
213
+ var missingArg = mockClient.getMissingFiles.mock.calls[0][0];
214
+ expect(Object.keys(missingArg.sys_script_include)).toEqual(["sysid_New"]);
215
+ // the new record landed
216
+ expect(readLocal("New", "script", "js")).toBe("// fresh created record");
217
+ // siblings untouched
218
+ expect(readLocal("Sib1", "script", "js")).toBe("STALE SIBLING — must not change");
219
+ expect(readLocal("Sib2", "script", "js")).toBeNull();
220
+ });
221
+ test("record absent from the fetched manifest → warn, no fetch, no throw", async function () {
222
+ mockClient.getManifest.mockResolvedValue(threeRecordManifest());
223
+ await AppUtils.syncManifest("x_cadso_core", {
224
+ record: { table: "sys_script_include", sysId: "sysid_DoesNotExist" },
225
+ });
226
+ // empty narrowed manifest → refreshAllFiles no-ops, so no content fetch
227
+ expect(mockClient.getMissingFiles).not.toHaveBeenCalled();
228
+ // and the user is told to run a full refresh
229
+ var warned = mockLogger.warn.mock.calls.some(function (c) {
230
+ return typeof c[0] === "string" && c[0].indexOf("sysid_DoesNotExist") !== -1;
231
+ });
232
+ expect(warned).toBe(true);
233
+ });
234
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenonhq/dovetail-core",
3
- "version": "0.0.113",
3
+ "version": "0.0.114",
4
4
  "description": "Next-gen file syncer",
5
5
  "license": "GPL-3.0",
6
6
  "main": "./dist/index.js",
@@ -1,7 +1,221 @@
1
1
  {
2
2
  "$schema": "dovetail-manifest/v1",
3
- "generated_at": "2026-07-14T19:40:43.710Z",
3
+ "generated_at": "2026-07-15T02:38:50.750Z",
4
4
  "events": [
5
+ {
6
+ "$schema": "dovetail-release-event/v1",
7
+ "event_id": "dovetail-servicenow@0.0.30",
8
+ "package": "@tenonhq/dovetail-servicenow",
9
+ "version": "0.0.30",
10
+ "prev_version": "0.0.29",
11
+ "published_at": "2026-07-15T02:38:50.737Z",
12
+ "git": {
13
+ "sha": "2977ae7b00bf62482f12d7a6ccf7b87babdbae21",
14
+ "tag": "dovetail-servicenow@0.0.30"
15
+ },
16
+ "semver_bump": "patch",
17
+ "commits": [
18
+ {
19
+ "sha": "2977ae7b0",
20
+ "subject": "feat(servicenow): set-field/create-record accept --from-json for large fields (#224)",
21
+ "pr": 224
22
+ },
23
+ {
24
+ "sha": "e0c350259",
25
+ "subject": "chore(release): publish 2 packages [skip ci]"
26
+ }
27
+ ],
28
+ "exports_delta": null,
29
+ "narrative": null
30
+ },
31
+ {
32
+ "$schema": "dovetail-release-event/v1",
33
+ "event_id": "dovetail-mcp@0.0.27",
34
+ "package": "@tenonhq/dovetail-mcp",
35
+ "version": "0.0.27",
36
+ "prev_version": "0.0.26",
37
+ "published_at": "2026-07-15T02:38:50.737Z",
38
+ "git": {
39
+ "sha": "2977ae7b00bf62482f12d7a6ccf7b87babdbae21",
40
+ "tag": "dovetail-mcp@0.0.27"
41
+ },
42
+ "semver_bump": "patch",
43
+ "commits": [
44
+ {
45
+ "sha": "e0c350259",
46
+ "subject": "chore(release): publish 2 packages [skip ci]"
47
+ }
48
+ ],
49
+ "exports_delta": null,
50
+ "narrative": null
51
+ },
52
+ {
53
+ "$schema": "dovetail-release-event/v1",
54
+ "event_id": "dovetail-servicenow@0.0.29",
55
+ "package": "@tenonhq/dovetail-servicenow",
56
+ "version": "0.0.29",
57
+ "prev_version": "0.0.28",
58
+ "published_at": "2026-07-15T01:16:10.133Z",
59
+ "git": {
60
+ "sha": "6d7309949cc82ece927f9a4f61614ef4a23cc5ee",
61
+ "tag": "dovetail-servicenow@0.0.29"
62
+ },
63
+ "semver_bump": "patch",
64
+ "commits": [
65
+ {
66
+ "sha": "6d7309949",
67
+ "subject": "feat(servicenow): set-column verb + set_column MCP tool (DEV-509) (#217)",
68
+ "pr": 217
69
+ },
70
+ {
71
+ "sha": "6a4081d25",
72
+ "subject": "chore(release): publish 2 packages [skip ci]"
73
+ }
74
+ ],
75
+ "exports_delta": null,
76
+ "narrative": null
77
+ },
78
+ {
79
+ "$schema": "dovetail-release-event/v1",
80
+ "event_id": "dovetail-mcp@0.0.26",
81
+ "package": "@tenonhq/dovetail-mcp",
82
+ "version": "0.0.26",
83
+ "prev_version": "0.0.25",
84
+ "published_at": "2026-07-15T01:16:10.133Z",
85
+ "git": {
86
+ "sha": "6d7309949cc82ece927f9a4f61614ef4a23cc5ee",
87
+ "tag": "dovetail-mcp@0.0.26"
88
+ },
89
+ "semver_bump": "patch",
90
+ "commits": [
91
+ {
92
+ "sha": "6a4081d25",
93
+ "subject": "chore(release): publish 2 packages [skip ci]"
94
+ }
95
+ ],
96
+ "exports_delta": null,
97
+ "narrative": null
98
+ },
99
+ {
100
+ "$schema": "dovetail-release-event/v1",
101
+ "event_id": "dovetail-servicenow@0.0.28",
102
+ "package": "@tenonhq/dovetail-servicenow",
103
+ "version": "0.0.28",
104
+ "prev_version": "0.0.27",
105
+ "published_at": "2026-07-14T20:25:03.138Z",
106
+ "git": {
107
+ "sha": "a5aeaa3c3f56c0bb24de80f12808689c5987e457",
108
+ "tag": "dovetail-servicenow@0.0.28"
109
+ },
110
+ "semver_bump": "patch",
111
+ "commits": [
112
+ {
113
+ "sha": "a5aeaa3c3",
114
+ "subject": "feat(servicenow): edit a published action type's steps in one publish (#216) (#223)",
115
+ "pr": 216
116
+ },
117
+ {
118
+ "sha": "b8967c79d",
119
+ "subject": "chore(release): publish 2 packages [skip ci]"
120
+ }
121
+ ],
122
+ "exports_delta": null,
123
+ "narrative": null
124
+ },
125
+ {
126
+ "$schema": "dovetail-release-event/v1",
127
+ "event_id": "dovetail-mcp@0.0.25",
128
+ "package": "@tenonhq/dovetail-mcp",
129
+ "version": "0.0.25",
130
+ "prev_version": "0.0.24",
131
+ "published_at": "2026-07-14T20:25:03.138Z",
132
+ "git": {
133
+ "sha": "a5aeaa3c3f56c0bb24de80f12808689c5987e457",
134
+ "tag": "dovetail-mcp@0.0.25"
135
+ },
136
+ "semver_bump": "patch",
137
+ "commits": [
138
+ {
139
+ "sha": "b8967c79d",
140
+ "subject": "chore(release): publish 2 packages [skip ci]"
141
+ }
142
+ ],
143
+ "exports_delta": null,
144
+ "narrative": null
145
+ },
146
+ {
147
+ "$schema": "dovetail-release-event/v1",
148
+ "event_id": "dovetail-servicenow@0.0.27",
149
+ "package": "@tenonhq/dovetail-servicenow",
150
+ "version": "0.0.27",
151
+ "prev_version": "0.0.26",
152
+ "published_at": "2026-07-14T20:15:42.598Z",
153
+ "git": {
154
+ "sha": "43d6c5124bec7e79537d8ad6364749f36c601d9c",
155
+ "tag": "dovetail-servicenow@0.0.27"
156
+ },
157
+ "semver_bump": "patch",
158
+ "commits": [
159
+ {
160
+ "sha": "43d6c5124",
161
+ "subject": "feat(servicenow): add-column mandatory/default via scope-aware insert (#213)",
162
+ "pr": 213
163
+ },
164
+ {
165
+ "sha": "ef9e73505",
166
+ "subject": "chore(release): publish 2 packages [skip ci]"
167
+ }
168
+ ],
169
+ "exports_delta": null,
170
+ "narrative": null
171
+ },
172
+ {
173
+ "$schema": "dovetail-release-event/v1",
174
+ "event_id": "dovetail-mcp@0.0.24",
175
+ "package": "@tenonhq/dovetail-mcp",
176
+ "version": "0.0.24",
177
+ "prev_version": "0.0.23",
178
+ "published_at": "2026-07-14T20:15:42.598Z",
179
+ "git": {
180
+ "sha": "43d6c5124bec7e79537d8ad6364749f36c601d9c",
181
+ "tag": "dovetail-mcp@0.0.24"
182
+ },
183
+ "semver_bump": "patch",
184
+ "commits": [
185
+ {
186
+ "sha": "ef9e73505",
187
+ "subject": "chore(release): publish 2 packages [skip ci]"
188
+ }
189
+ ],
190
+ "exports_delta": null,
191
+ "narrative": null
192
+ },
193
+ {
194
+ "$schema": "dovetail-release-event/v1",
195
+ "event_id": "dovetail-core@0.0.113",
196
+ "package": "@tenonhq/dovetail-core",
197
+ "version": "0.0.113",
198
+ "prev_version": "0.0.112",
199
+ "published_at": "2026-07-14T20:02:38.069Z",
200
+ "git": {
201
+ "sha": "99f064bf76f0fa4a2e049debff52be8303ebed24",
202
+ "tag": "dovetail-core@0.0.113"
203
+ },
204
+ "semver_bump": "patch",
205
+ "commits": [
206
+ {
207
+ "sha": "99f064bf7",
208
+ "subject": "fix(servicenow): stop getManifest silently dropping same-named records (#222)",
209
+ "pr": 222
210
+ },
211
+ {
212
+ "sha": "38ffe1e27",
213
+ "subject": "chore(release): @tenonhq/dovetail-core@0.0.112 [skip ci]"
214
+ }
215
+ ],
216
+ "exports_delta": null,
217
+ "narrative": null
218
+ },
5
219
  {
6
220
  "$schema": "dovetail-release-event/v1",
7
221
  "event_id": "dovetail-core@0.0.112",
@@ -1011,272 +1225,6 @@
1011
1225
  ],
1012
1226
  "exports_delta": null,
1013
1227
  "narrative": null
1014
- },
1015
- {
1016
- "$schema": "dovetail-release-event/v1",
1017
- "event_id": "dovetail-babel-plugin@0.0.10",
1018
- "package": "@tenonhq/dovetail-babel-plugin",
1019
- "version": "0.0.10",
1020
- "prev_version": "0.0.9",
1021
- "published_at": "2026-06-05T06:43:18.252Z",
1022
- "git": {
1023
- "sha": "71bdf9bba8bd5e3849a212a18d9ebc9241232b32",
1024
- "tag": "dovetail-babel-plugin@0.0.10"
1025
- },
1026
- "semver_bump": "patch",
1027
- "commits": [
1028
- {
1029
- "sha": "71bdf9bba",
1030
- "subject": "fix: float internal dep ranges to ~0.0.x so consumers need no overrides (#148)",
1031
- "pr": 148
1032
- },
1033
- {
1034
- "sha": "9332fdf7a",
1035
- "subject": "chore(release): publish 20 packages [skip ci]"
1036
- },
1037
- {
1038
- "sha": "9046da774",
1039
- "subject": "chore(release): publish 19 packages [skip ci]"
1040
- }
1041
- ],
1042
- "exports_delta": null,
1043
- "narrative": null
1044
- },
1045
- {
1046
- "$schema": "dovetail-release-event/v1",
1047
- "event_id": "dovetail-babel-plugin-remove-modules@0.0.10",
1048
- "package": "@tenonhq/dovetail-babel-plugin-remove-modules",
1049
- "version": "0.0.10",
1050
- "prev_version": "0.0.9",
1051
- "published_at": "2026-06-05T06:43:18.252Z",
1052
- "git": {
1053
- "sha": "71bdf9bba8bd5e3849a212a18d9ebc9241232b32",
1054
- "tag": "dovetail-babel-plugin-remove-modules@0.0.10"
1055
- },
1056
- "semver_bump": "patch",
1057
- "commits": [
1058
- {
1059
- "sha": "71bdf9bba",
1060
- "subject": "fix: float internal dep ranges to ~0.0.x so consumers need no overrides (#148)",
1061
- "pr": 148
1062
- },
1063
- {
1064
- "sha": "9332fdf7a",
1065
- "subject": "chore(release): publish 20 packages [skip ci]"
1066
- },
1067
- {
1068
- "sha": "9046da774",
1069
- "subject": "chore(release): publish 19 packages [skip ci]"
1070
- }
1071
- ],
1072
- "exports_delta": null,
1073
- "narrative": null
1074
- },
1075
- {
1076
- "$schema": "dovetail-release-event/v1",
1077
- "event_id": "dovetail-babel-preset-servicenow@0.0.10",
1078
- "package": "@tenonhq/dovetail-babel-preset-servicenow",
1079
- "version": "0.0.10",
1080
- "prev_version": "0.0.9",
1081
- "published_at": "2026-06-05T06:43:18.252Z",
1082
- "git": {
1083
- "sha": "71bdf9bba8bd5e3849a212a18d9ebc9241232b32",
1084
- "tag": "dovetail-babel-preset-servicenow@0.0.10"
1085
- },
1086
- "semver_bump": "patch",
1087
- "commits": [
1088
- {
1089
- "sha": "71bdf9bba",
1090
- "subject": "fix: float internal dep ranges to ~0.0.x so consumers need no overrides (#148)",
1091
- "pr": 148
1092
- },
1093
- {
1094
- "sha": "9332fdf7a",
1095
- "subject": "chore(release): publish 20 packages [skip ci]"
1096
- },
1097
- {
1098
- "sha": "9046da774",
1099
- "subject": "chore(release): publish 19 packages [skip ci]"
1100
- }
1101
- ],
1102
- "exports_delta": null,
1103
- "narrative": null
1104
- },
1105
- {
1106
- "$schema": "dovetail-release-event/v1",
1107
- "event_id": "dovetail-schema@0.0.11",
1108
- "package": "@tenonhq/dovetail-schema",
1109
- "version": "0.0.11",
1110
- "prev_version": "0.0.10",
1111
- "published_at": "2026-06-05T06:43:18.252Z",
1112
- "git": {
1113
- "sha": "71bdf9bba8bd5e3849a212a18d9ebc9241232b32",
1114
- "tag": "dovetail-schema@0.0.11"
1115
- },
1116
- "semver_bump": "patch",
1117
- "commits": [
1118
- {
1119
- "sha": "71bdf9bba",
1120
- "subject": "fix: float internal dep ranges to ~0.0.x so consumers need no overrides (#148)",
1121
- "pr": 148
1122
- },
1123
- {
1124
- "sha": "a8922d9d5",
1125
- "subject": "chore(release): @tenonhq/dovetail-schema@0.0.10 [skip ci]"
1126
- }
1127
- ],
1128
- "exports_delta": null,
1129
- "narrative": null
1130
- },
1131
- {
1132
- "$schema": "dovetail-release-event/v1",
1133
- "event_id": "dovetail-core@0.0.103",
1134
- "package": "@tenonhq/dovetail-core",
1135
- "version": "0.0.103",
1136
- "prev_version": "0.0.102",
1137
- "published_at": "2026-06-05T06:43:18.252Z",
1138
- "git": {
1139
- "sha": "71bdf9bba8bd5e3849a212a18d9ebc9241232b32",
1140
- "tag": "dovetail-core@0.0.103"
1141
- },
1142
- "semver_bump": "patch",
1143
- "commits": [
1144
- {
1145
- "sha": "71bdf9bba",
1146
- "subject": "fix: float internal dep ranges to ~0.0.x so consumers need no overrides (#148)",
1147
- "pr": 148
1148
- },
1149
- {
1150
- "sha": "d5e064760",
1151
- "subject": "chore(release): @tenonhq/dovetail-servicenow@0.0.15 [skip ci]"
1152
- },
1153
- {
1154
- "sha": "af1417796",
1155
- "subject": "chore(release): publish 3 packages [skip ci]"
1156
- },
1157
- {
1158
- "sha": "e25ae74c2",
1159
- "subject": "chore(release): @tenonhq/dovetail-core@0.0.102 [skip ci]"
1160
- }
1161
- ],
1162
- "exports_delta": null,
1163
- "narrative": null
1164
- },
1165
- {
1166
- "$schema": "dovetail-release-event/v1",
1167
- "event_id": "dovetail-dashboard@0.0.37",
1168
- "package": "@tenonhq/dovetail-dashboard",
1169
- "version": "0.0.37",
1170
- "prev_version": "0.0.36",
1171
- "published_at": "2026-06-05T06:43:18.252Z",
1172
- "git": {
1173
- "sha": "71bdf9bba8bd5e3849a212a18d9ebc9241232b32",
1174
- "tag": "dovetail-dashboard@0.0.37"
1175
- },
1176
- "semver_bump": "patch",
1177
- "commits": [
1178
- {
1179
- "sha": "71bdf9bba",
1180
- "subject": "fix: float internal dep ranges to ~0.0.x so consumers need no overrides (#148)",
1181
- "pr": 148
1182
- },
1183
- {
1184
- "sha": "ba1c67775",
1185
- "subject": "chore(release): @tenonhq/dovetail-dashboard@0.0.36 [skip ci]"
1186
- }
1187
- ],
1188
- "exports_delta": null,
1189
- "narrative": null
1190
- },
1191
- {
1192
- "$schema": "dovetail-release-event/v1",
1193
- "event_id": "dovetail-eslint-plugin@0.0.10",
1194
- "package": "@tenonhq/dovetail-eslint-plugin",
1195
- "version": "0.0.10",
1196
- "prev_version": "0.0.9",
1197
- "published_at": "2026-06-05T06:43:18.252Z",
1198
- "git": {
1199
- "sha": "71bdf9bba8bd5e3849a212a18d9ebc9241232b32",
1200
- "tag": "dovetail-eslint-plugin@0.0.10"
1201
- },
1202
- "semver_bump": "patch",
1203
- "commits": [
1204
- {
1205
- "sha": "71bdf9bba",
1206
- "subject": "fix: float internal dep ranges to ~0.0.x so consumers need no overrides (#148)",
1207
- "pr": 148
1208
- },
1209
- {
1210
- "sha": "9332fdf7a",
1211
- "subject": "chore(release): publish 20 packages [skip ci]"
1212
- },
1213
- {
1214
- "sha": "9046da774",
1215
- "subject": "chore(release): publish 19 packages [skip ci]"
1216
- }
1217
- ],
1218
- "exports_delta": null,
1219
- "narrative": null
1220
- },
1221
- {
1222
- "$schema": "dovetail-release-event/v1",
1223
- "event_id": "dovetail-gmail@0.0.11",
1224
- "package": "@tenonhq/dovetail-gmail",
1225
- "version": "0.0.11",
1226
- "prev_version": "0.0.10",
1227
- "published_at": "2026-06-05T06:43:18.252Z",
1228
- "git": {
1229
- "sha": "71bdf9bba8bd5e3849a212a18d9ebc9241232b32",
1230
- "tag": "dovetail-gmail@0.0.11"
1231
- },
1232
- "semver_bump": "patch",
1233
- "commits": [
1234
- {
1235
- "sha": "71bdf9bba",
1236
- "subject": "fix: float internal dep ranges to ~0.0.x so consumers need no overrides (#148)",
1237
- "pr": 148
1238
- },
1239
- {
1240
- "sha": "9332fdf7a",
1241
- "subject": "chore(release): publish 20 packages [skip ci]"
1242
- },
1243
- {
1244
- "sha": "9046da774",
1245
- "subject": "chore(release): publish 19 packages [skip ci]"
1246
- }
1247
- ],
1248
- "exports_delta": null,
1249
- "narrative": null
1250
- },
1251
- {
1252
- "$schema": "dovetail-release-event/v1",
1253
- "event_id": "dovetail-google-calendar@0.0.11",
1254
- "package": "@tenonhq/dovetail-google-calendar",
1255
- "version": "0.0.11",
1256
- "prev_version": "0.0.10",
1257
- "published_at": "2026-06-05T06:43:18.252Z",
1258
- "git": {
1259
- "sha": "71bdf9bba8bd5e3849a212a18d9ebc9241232b32",
1260
- "tag": "dovetail-google-calendar@0.0.11"
1261
- },
1262
- "semver_bump": "patch",
1263
- "commits": [
1264
- {
1265
- "sha": "71bdf9bba",
1266
- "subject": "fix: float internal dep ranges to ~0.0.x so consumers need no overrides (#148)",
1267
- "pr": 148
1268
- },
1269
- {
1270
- "sha": "9332fdf7a",
1271
- "subject": "chore(release): publish 20 packages [skip ci]"
1272
- },
1273
- {
1274
- "sha": "9046da774",
1275
- "subject": "chore(release): publish 19 packages [skip ci]"
1276
- }
1277
- ],
1278
- "exports_delta": null,
1279
- "narrative": null
1280
1228
  }
1281
1229
  ]
1282
1230
  }