@tenonhq/dovetail-core 0.0.106 → 0.0.107
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 +22 -16
- package/dist/snClient.js +10 -0
- package/dist/tests/updateSetConfig.test.js +126 -0
- package/dist/updateSetCommands.js +61 -0
- package/dist/updateSetConfig.js +55 -0
- package/package.json +3 -3
- package/release-manifest.json +190 -225
- package/skills/dove-manage-update-sets.md +6 -0
package/dist/appUtils.js
CHANGED
|
@@ -38,30 +38,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.createAndAssignUpdateSet = exports.swapScope = exports.buildFiles = exports.summarizeRecord = exports.pushFiles = exports.getAppFileList = exports.groupAppFiles = exports.processMissingFiles = exports.refreshAllFiles = exports.findMissingFiles = exports.syncManifest = exports.processManifest = exports.normalizeManifestKeys = exports.toSafeFolderName = exports.stampMetadataContent = void 0;
|
|
40
40
|
const path_1 = __importDefault(require("path"));
|
|
41
|
-
const fs_1 = __importDefault(require("fs"));
|
|
42
41
|
const progress_1 = __importDefault(require("progress"));
|
|
43
42
|
const fUtils = __importStar(require("./FileUtils"));
|
|
44
43
|
const ConfigManager = __importStar(require("./config"));
|
|
45
44
|
const constants_1 = require("./constants");
|
|
46
45
|
const PluginManager_1 = __importDefault(require("./PluginManager"));
|
|
47
46
|
const FileLogger_1 = require("./FileLogger");
|
|
48
|
-
const
|
|
47
|
+
const updateSetConfig_1 = require("./updateSetConfig");
|
|
49
48
|
const snClient_1 = require("./snClient");
|
|
50
49
|
const benchmark_1 = require("./benchmark");
|
|
51
50
|
const Logger_1 = require("./Logger");
|
|
52
51
|
const genericUtils_1 = require("./genericUtils");
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (fs_1.default.existsSync(configPath)) {
|
|
57
|
-
return JSON.parse(fs_1.default.readFileSync(configPath, "utf8"));
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
catch (e) {
|
|
61
|
-
Logger_1.logger.warn(`Failed to parse update set config at ${configPath}: ${e instanceof Error ? e.message : String(e)}`);
|
|
62
|
-
}
|
|
63
|
-
return {};
|
|
64
|
-
};
|
|
52
|
+
// Delegate to the shared reader so the push hot-path and the
|
|
53
|
+
// createUpdateSet/switchUpdateSet writers stay on one parse contract.
|
|
54
|
+
const getUpdateSetConfig = () => (0, updateSetConfig_1.readUpdateSetConfig)();
|
|
65
55
|
// Strip the ServiceNow instance host from an absolute _record_link, leaving the
|
|
66
56
|
// instance-relative path. The CADSO metadata endpoint returns an absolute URL
|
|
67
57
|
// (https://<instance>.service-now.com/<table>.do?sys_id=...); persisting that
|
|
@@ -762,9 +752,11 @@ const pushFiles = async (recs) => {
|
|
|
762
752
|
const hasUpdateSets = Object.keys(updateSetConfig).length > 0;
|
|
763
753
|
if (hasUpdateSets) {
|
|
764
754
|
const activeScopes = Object.entries(updateSetConfig)
|
|
765
|
-
.map(([scope, us]) => `${scope} -> ${us.name}`)
|
|
755
|
+
.map(([scope, us]) => `${scope} -> ${us.name} (${us.sys_id})`)
|
|
766
756
|
.join(", ");
|
|
767
|
-
|
|
757
|
+
// Name the source file and show the sys_id so the operator can see exactly
|
|
758
|
+
// where captures land — push routes by this file, not the active set (#182).
|
|
759
|
+
Logger_1.logger.info(`Update set routing (from .dove-update-sets.json): ${activeScopes}`);
|
|
768
760
|
}
|
|
769
761
|
// Pre-resolve read-only table sets for every scope present in this batch.
|
|
770
762
|
// Cheaper than re-resolving per record. Tables flagged in _readOnlyTables
|
|
@@ -971,6 +963,20 @@ const createAndAssignUpdateSet = async (updateSetName = "", scope) => {
|
|
|
971
963
|
else {
|
|
972
964
|
await client.createCurrentUpdateSetUserPref(updateSetSysId, userSysId);
|
|
973
965
|
}
|
|
966
|
+
// Route this scope's captures to the new set. pushFiles() routes by
|
|
967
|
+
// .dove-update-sets.json, not the active set, so without this a `dove push
|
|
968
|
+
// --updateSet` could create+activate a set yet capture into a stale one
|
|
969
|
+
// (TenonHQ/Dovetail#182). Only possible when the scope name is known.
|
|
970
|
+
if (scope) {
|
|
971
|
+
const routed = (0, updateSetConfig_1.writeUpdateSetRouting)({
|
|
972
|
+
scope,
|
|
973
|
+
sysId: updateSetSysId,
|
|
974
|
+
name: updateSetName,
|
|
975
|
+
});
|
|
976
|
+
if (routed) {
|
|
977
|
+
Logger_1.logger.info(`Push routing updated: ${scope} -> ${updateSetName} (${updateSetSysId})`);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
974
980
|
return {
|
|
975
981
|
name: updateSetName,
|
|
976
982
|
id: updateSetSysId,
|
package/dist/snClient.js
CHANGED
|
@@ -334,6 +334,15 @@ const snClient = (baseURL, username, password) => {
|
|
|
334
334
|
},
|
|
335
335
|
});
|
|
336
336
|
};
|
|
337
|
+
const getScopeById = (scopeSysId) => {
|
|
338
|
+
const endpoint = "api/now/table/sys_scope";
|
|
339
|
+
return client.get(endpoint, {
|
|
340
|
+
params: {
|
|
341
|
+
sysparm_query: `sys_id=${scopeSysId}`,
|
|
342
|
+
sysparm_fields: "scope",
|
|
343
|
+
},
|
|
344
|
+
});
|
|
345
|
+
};
|
|
337
346
|
const getUserSysId = (userName = process.env.SN_USER) => {
|
|
338
347
|
const endpoint = "api/now/table/sys_user";
|
|
339
348
|
return client.get(endpoint, {
|
|
@@ -504,6 +513,7 @@ const snClient = (baseURL, username, password) => {
|
|
|
504
513
|
getAppList,
|
|
505
514
|
updateRecord,
|
|
506
515
|
getScopeId,
|
|
516
|
+
getScopeById,
|
|
507
517
|
getUserSysId,
|
|
508
518
|
getCurrentAppUserPrefSysId,
|
|
509
519
|
updateCurrentAppUserPref,
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* TenonHQ/Dovetail#182: readUpdateSetConfig / writeUpdateSetRouting
|
|
4
|
+
*
|
|
5
|
+
* The push-routing file (.dove-update-sets.json) is the source of truth for
|
|
6
|
+
* `dove push`. These tests pin the read/merge/write contract that keeps it in
|
|
7
|
+
* sync with createUpdateSet / switchUpdateSet.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
var mockFsStore = {};
|
|
11
|
+
jest.mock("fs", function () {
|
|
12
|
+
return {
|
|
13
|
+
existsSync: jest.fn(function (p) {
|
|
14
|
+
return p in mockFsStore;
|
|
15
|
+
}),
|
|
16
|
+
readFileSync: jest.fn(function (p) {
|
|
17
|
+
if (p in mockFsStore)
|
|
18
|
+
return mockFsStore[p];
|
|
19
|
+
throw new Error("ENOENT: " + p);
|
|
20
|
+
}),
|
|
21
|
+
writeFileSync: jest.fn(function (p, data) {
|
|
22
|
+
mockFsStore[p] = data;
|
|
23
|
+
}),
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
var CONFIG_PATH = "/repo/.dove-update-sets.json";
|
|
27
|
+
jest.mock("../projectFiles", function () {
|
|
28
|
+
return {
|
|
29
|
+
getUpdateSetsConfigPath: jest.fn(function () {
|
|
30
|
+
return CONFIG_PATH;
|
|
31
|
+
}),
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
jest.mock("../Logger", function () {
|
|
35
|
+
return {
|
|
36
|
+
logger: {
|
|
37
|
+
info: jest.fn(),
|
|
38
|
+
error: jest.fn(),
|
|
39
|
+
warn: jest.fn(),
|
|
40
|
+
debug: jest.fn(),
|
|
41
|
+
success: jest.fn(),
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
const updateSetConfig_1 = require("../updateSetConfig");
|
|
46
|
+
const Logger_1 = require("../Logger");
|
|
47
|
+
describe("updateSetConfig", function () {
|
|
48
|
+
beforeEach(function () {
|
|
49
|
+
for (var k in mockFsStore)
|
|
50
|
+
delete mockFsStore[k];
|
|
51
|
+
jest.clearAllMocks();
|
|
52
|
+
});
|
|
53
|
+
describe("readUpdateSetConfig", function () {
|
|
54
|
+
test("returns {} when the file is absent", function () {
|
|
55
|
+
expect((0, updateSetConfig_1.readUpdateSetConfig)()).toEqual({});
|
|
56
|
+
});
|
|
57
|
+
test("parses an existing routing file", function () {
|
|
58
|
+
mockFsStore[CONFIG_PATH] = JSON.stringify({
|
|
59
|
+
x_cadso_core: { sys_id: "abc", name: "Set A" },
|
|
60
|
+
});
|
|
61
|
+
expect((0, updateSetConfig_1.readUpdateSetConfig)()).toEqual({
|
|
62
|
+
x_cadso_core: { sys_id: "abc", name: "Set A" },
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
test("returns {} and warns on unparseable JSON", function () {
|
|
66
|
+
mockFsStore[CONFIG_PATH] = "{ not json";
|
|
67
|
+
expect((0, updateSetConfig_1.readUpdateSetConfig)()).toEqual({});
|
|
68
|
+
expect(Logger_1.logger.warn).toHaveBeenCalled();
|
|
69
|
+
});
|
|
70
|
+
test("returns {} and warns when the file is a non-object (array)", function () {
|
|
71
|
+
mockFsStore[CONFIG_PATH] = JSON.stringify(["nope"]);
|
|
72
|
+
expect((0, updateSetConfig_1.readUpdateSetConfig)()).toEqual({});
|
|
73
|
+
expect(Logger_1.logger.warn).toHaveBeenCalled();
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe("writeUpdateSetRouting", function () {
|
|
77
|
+
test("writes a fresh routing entry and returns the path", function () {
|
|
78
|
+
var written = (0, updateSetConfig_1.writeUpdateSetRouting)({
|
|
79
|
+
scope: "x_cadso_core",
|
|
80
|
+
sysId: "new123",
|
|
81
|
+
name: "New Set",
|
|
82
|
+
});
|
|
83
|
+
expect(written).toBe(CONFIG_PATH);
|
|
84
|
+
expect(JSON.parse(mockFsStore[CONFIG_PATH])).toEqual({
|
|
85
|
+
x_cadso_core: { sys_id: "new123", name: "New Set" },
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
test("merges without clobbering other scopes", function () {
|
|
89
|
+
mockFsStore[CONFIG_PATH] = JSON.stringify({
|
|
90
|
+
x_cadso_automate: { sys_id: "keep", name: "Keep Me" },
|
|
91
|
+
});
|
|
92
|
+
(0, updateSetConfig_1.writeUpdateSetRouting)({
|
|
93
|
+
scope: "x_cadso_core",
|
|
94
|
+
sysId: "new123",
|
|
95
|
+
name: "New Set",
|
|
96
|
+
});
|
|
97
|
+
expect(JSON.parse(mockFsStore[CONFIG_PATH])).toEqual({
|
|
98
|
+
x_cadso_automate: { sys_id: "keep", name: "Keep Me" },
|
|
99
|
+
x_cadso_core: { sys_id: "new123", name: "New Set" },
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
test("overwrites the same scope's stale entry", function () {
|
|
103
|
+
mockFsStore[CONFIG_PATH] = JSON.stringify({
|
|
104
|
+
x_cadso_core: { sys_id: "old", name: "Old Set" },
|
|
105
|
+
});
|
|
106
|
+
(0, updateSetConfig_1.writeUpdateSetRouting)({
|
|
107
|
+
scope: "x_cadso_core",
|
|
108
|
+
sysId: "fresh",
|
|
109
|
+
name: "Fresh Set",
|
|
110
|
+
});
|
|
111
|
+
expect(JSON.parse(mockFsStore[CONFIG_PATH])).toEqual({
|
|
112
|
+
x_cadso_core: { sys_id: "fresh", name: "Fresh Set" },
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
test("returns null and writes nothing when scope is missing", function () {
|
|
116
|
+
var written = (0, updateSetConfig_1.writeUpdateSetRouting)({ sysId: "x", name: "y" });
|
|
117
|
+
expect(written).toBeNull();
|
|
118
|
+
expect(mockFsStore[CONFIG_PATH]).toBeUndefined();
|
|
119
|
+
});
|
|
120
|
+
test("returns null when sys_id or name is blank", function () {
|
|
121
|
+
expect((0, updateSetConfig_1.writeUpdateSetRouting)({ scope: "x_cadso_core", sysId: " ", name: "y" })).toBeNull();
|
|
122
|
+
expect((0, updateSetConfig_1.writeUpdateSetRouting)({ scope: "x_cadso_core", sysId: "x", name: "" })).toBeNull();
|
|
123
|
+
expect(mockFsStore[CONFIG_PATH]).toBeUndefined();
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -49,6 +49,7 @@ const commands_1 = require("./commands");
|
|
|
49
49
|
const chalk_1 = __importDefault(require("chalk"));
|
|
50
50
|
const dovetail_clickup_1 = require("@tenonhq/dovetail-clickup");
|
|
51
51
|
const clickupCommands_1 = require("./clickupCommands");
|
|
52
|
+
const updateSetConfig_1 = require("./updateSetConfig");
|
|
52
53
|
/**
|
|
53
54
|
* Shows the current scope
|
|
54
55
|
*/
|
|
@@ -233,6 +234,26 @@ async function createUpdateSetCommand(args) {
|
|
|
233
234
|
if (scope) {
|
|
234
235
|
Logger_1.logger.info(`Scope: ${scope}`);
|
|
235
236
|
}
|
|
237
|
+
// Keep .dove-update-sets.json in sync with the set we just activated.
|
|
238
|
+
// `dove push` routes captures by this file (per scope), not by the
|
|
239
|
+
// instance's active update set — so without this write the new set would
|
|
240
|
+
// stay empty and pushes would land in whatever stale set the file still
|
|
241
|
+
// points at (TenonHQ/Dovetail#182).
|
|
242
|
+
if (scope) {
|
|
243
|
+
const routed = (0, updateSetConfig_1.writeUpdateSetRouting)({
|
|
244
|
+
scope,
|
|
245
|
+
sysId: updateSetSysId,
|
|
246
|
+
name,
|
|
247
|
+
});
|
|
248
|
+
if (routed) {
|
|
249
|
+
Logger_1.logger.info(chalk_1.default.green(`✓ Push routing updated: ${scope} -> ${name} (${updateSetSysId})`));
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
Logger_1.logger.warn("No scope provided, so .dove-update-sets.json was not updated. " +
|
|
254
|
+
"`dove push` routes captures by scope via that file — pass -s <scope> " +
|
|
255
|
+
"so pushes target this set, or edit .dove-update-sets.json directly.");
|
|
256
|
+
}
|
|
236
257
|
// If created from a ClickUp task, offer to post a comment back
|
|
237
258
|
if (args._clickupTaskId) {
|
|
238
259
|
try {
|
|
@@ -292,6 +313,22 @@ async function switchUpdateSetCommand(args) {
|
|
|
292
313
|
if (applicationName) {
|
|
293
314
|
Logger_1.logger.info(`Scope: ${applicationName}`);
|
|
294
315
|
}
|
|
316
|
+
// Keep .dove-update-sets.json in sync so `dove push` routes captures to the
|
|
317
|
+
// set we just switched to (it routes by this file per scope, not by the
|
|
318
|
+
// instance's active set — TenonHQ/Dovetail#182). `applicationScope` is the
|
|
319
|
+
// scope's sys_id, so resolve it back to the scope name the file is keyed on.
|
|
320
|
+
const scopeName = args.scope || (await resolveScopeName(applicationScope));
|
|
321
|
+
if (scopeName) {
|
|
322
|
+
const routed = (0, updateSetConfig_1.writeUpdateSetRouting)({ scope: scopeName, sysId, name });
|
|
323
|
+
if (routed) {
|
|
324
|
+
Logger_1.logger.info(chalk_1.default.green(`✓ Push routing updated: ${scopeName} -> ${name} (${sysId})`));
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
Logger_1.logger.warn("Could not resolve the scope name, so .dove-update-sets.json was not " +
|
|
329
|
+
"updated. `dove push` routes captures by scope via that file — pass " +
|
|
330
|
+
"--scope <scope> so pushes target this set.");
|
|
331
|
+
}
|
|
295
332
|
}
|
|
296
333
|
catch (e) {
|
|
297
334
|
Logger_1.logger.error("Failed to switch update set");
|
|
@@ -489,6 +526,30 @@ async function getActiveUpdateSetName(client, scope) {
|
|
|
489
526
|
return null;
|
|
490
527
|
}
|
|
491
528
|
}
|
|
529
|
+
/**
|
|
530
|
+
* Resolves a scope's sys_id back to its scope name (e.g. "x_cadso_core"), used
|
|
531
|
+
* to key the .dove-update-sets.json push-routing entry. Returns undefined when
|
|
532
|
+
* the input is missing or the lookup fails so the caller can warn instead.
|
|
533
|
+
*/
|
|
534
|
+
async function resolveScopeName(applicationSysId) {
|
|
535
|
+
if (!applicationSysId || applicationSysId.trim() === "")
|
|
536
|
+
return undefined;
|
|
537
|
+
try {
|
|
538
|
+
const client = (0, snClient_1.defaultClient)();
|
|
539
|
+
const rows = await (0, snClient_1.unwrapSNResponse)(client.getScopeById(applicationSysId));
|
|
540
|
+
if (rows && rows.length > 0) {
|
|
541
|
+
const resolved = rows[0].scope;
|
|
542
|
+
if (resolved && typeof resolved === "string" && resolved.trim() !== "") {
|
|
543
|
+
return resolved;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
catch (e) {
|
|
548
|
+
Logger_1.logger.debug("Could not resolve scope name from application sys_id: " +
|
|
549
|
+
(e instanceof Error ? e.message : String(e)));
|
|
550
|
+
}
|
|
551
|
+
return undefined;
|
|
552
|
+
}
|
|
492
553
|
/**
|
|
493
554
|
* Helper function to switch to a scope
|
|
494
555
|
*/
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.readUpdateSetConfig = readUpdateSetConfig;
|
|
7
|
+
exports.writeUpdateSetRouting = writeUpdateSetRouting;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const projectFiles_1 = require("./projectFiles");
|
|
10
|
+
const Logger_1 = require("./Logger");
|
|
11
|
+
/**
|
|
12
|
+
* Read .dove-update-sets.json (or its legacy .sinc- sibling). Returns {} when
|
|
13
|
+
* the file is absent or unparseable, so callers never have to guard for null.
|
|
14
|
+
*/
|
|
15
|
+
function readUpdateSetConfig() {
|
|
16
|
+
const configPath = (0, projectFiles_1.getUpdateSetsConfigPath)();
|
|
17
|
+
try {
|
|
18
|
+
if (fs_1.default.existsSync(configPath)) {
|
|
19
|
+
const parsed = JSON.parse(fs_1.default.readFileSync(configPath, "utf8"));
|
|
20
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
21
|
+
return parsed;
|
|
22
|
+
}
|
|
23
|
+
Logger_1.logger.warn(`Update set config at ${configPath} is not an object; ignoring it.`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
Logger_1.logger.warn(`Failed to parse update set config at ${configPath}: ${e instanceof Error ? e.message : String(e)}`);
|
|
28
|
+
}
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Merge a single scope's update set into .dove-update-sets.json so that
|
|
33
|
+
* `dove push` routes that scope's records to the set the operator just created
|
|
34
|
+
* or switched to.
|
|
35
|
+
*
|
|
36
|
+
* Without this, `dove createUpdateSet` / `dove switchUpdateSet` only flip the
|
|
37
|
+
* instance's *active* update set, while `dove push` keeps routing via this file
|
|
38
|
+
* — so a freshly "activated" set can stay empty and the work lands in whatever
|
|
39
|
+
* stale set the file still points at (TenonHQ/Dovetail#182).
|
|
40
|
+
*
|
|
41
|
+
* Returns the path written, or null when any input is missing (no scope name,
|
|
42
|
+
* sys_id, or set name to route by) so the caller can warn instead.
|
|
43
|
+
*/
|
|
44
|
+
function writeUpdateSetRouting(opts) {
|
|
45
|
+
const scope = opts.scope ? opts.scope.trim() : "";
|
|
46
|
+
const sysId = opts.sysId ? opts.sysId.trim() : "";
|
|
47
|
+
const name = opts.name ? opts.name.trim() : "";
|
|
48
|
+
if (!scope || !sysId || !name)
|
|
49
|
+
return null;
|
|
50
|
+
const configPath = (0, projectFiles_1.getUpdateSetsConfigPath)();
|
|
51
|
+
const config = readUpdateSetConfig();
|
|
52
|
+
config[scope] = { sys_id: sysId, name };
|
|
53
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
54
|
+
return configPath;
|
|
55
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tenonhq/dovetail-core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.107",
|
|
4
4
|
"description": "Next-gen file syncer",
|
|
5
5
|
"license": "GPL-3.0",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"node": ">=22"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@tenonhq/dovetail-types": "~0.0.
|
|
25
|
+
"@tenonhq/dovetail-types": "~0.0.20",
|
|
26
26
|
"@types/dotenv": "^6.1.1",
|
|
27
27
|
"@types/inquirer": "^8.2.10",
|
|
28
28
|
"@types/jest": "^29.5.5",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@tenonhq/dovetail-clickup": "~0.0.12",
|
|
42
|
-
"@tenonhq/dovetail-schema": "~0.0.
|
|
42
|
+
"@tenonhq/dovetail-schema": "~0.0.12",
|
|
43
43
|
"axios": "^1.5.1",
|
|
44
44
|
"axios-cookiejar-support": "^4.0.7",
|
|
45
45
|
"axios-rate-limit": "^1.3.0",
|
package/release-manifest.json
CHANGED
|
@@ -1,7 +1,196 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "dovetail-manifest/v1",
|
|
3
|
-
"generated_at": "2026-06-
|
|
3
|
+
"generated_at": "2026-06-16T01:18:07.747Z",
|
|
4
4
|
"events": [
|
|
5
|
+
{
|
|
6
|
+
"$schema": "dovetail-release-event/v1",
|
|
7
|
+
"event_id": "dovetail-babel-plugin@0.0.11",
|
|
8
|
+
"package": "@tenonhq/dovetail-babel-plugin",
|
|
9
|
+
"version": "0.0.11",
|
|
10
|
+
"prev_version": "0.0.10",
|
|
11
|
+
"published_at": "2026-06-16T01:18:07.728Z",
|
|
12
|
+
"git": {
|
|
13
|
+
"sha": "edabc263bd89f08b5c7a075f270c2172e82da4da",
|
|
14
|
+
"tag": "dovetail-babel-plugin@0.0.11"
|
|
15
|
+
},
|
|
16
|
+
"semver_bump": "patch",
|
|
17
|
+
"commits": [
|
|
18
|
+
{
|
|
19
|
+
"sha": "edabc263b",
|
|
20
|
+
"subject": "chore(deps): bump @babel/core from 7.29.0 to 7.29.6 (#181)",
|
|
21
|
+
"pr": 181
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"sha": "de9d77971",
|
|
25
|
+
"subject": "chore(release): publish 14 packages [skip ci]"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"exports_delta": null,
|
|
29
|
+
"narrative": null
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"$schema": "dovetail-release-event/v1",
|
|
33
|
+
"event_id": "dovetail-babel-plugin-remove-modules@0.0.11",
|
|
34
|
+
"package": "@tenonhq/dovetail-babel-plugin-remove-modules",
|
|
35
|
+
"version": "0.0.11",
|
|
36
|
+
"prev_version": "0.0.10",
|
|
37
|
+
"published_at": "2026-06-16T01:18:07.728Z",
|
|
38
|
+
"git": {
|
|
39
|
+
"sha": "edabc263bd89f08b5c7a075f270c2172e82da4da",
|
|
40
|
+
"tag": "dovetail-babel-plugin-remove-modules@0.0.11"
|
|
41
|
+
},
|
|
42
|
+
"semver_bump": "patch",
|
|
43
|
+
"commits": [
|
|
44
|
+
{
|
|
45
|
+
"sha": "edabc263b",
|
|
46
|
+
"subject": "chore(deps): bump @babel/core from 7.29.0 to 7.29.6 (#181)",
|
|
47
|
+
"pr": 181
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"sha": "de9d77971",
|
|
51
|
+
"subject": "chore(release): publish 14 packages [skip ci]"
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"exports_delta": null,
|
|
55
|
+
"narrative": null
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"$schema": "dovetail-release-event/v1",
|
|
59
|
+
"event_id": "dovetail-babel-preset-servicenow@0.0.11",
|
|
60
|
+
"package": "@tenonhq/dovetail-babel-preset-servicenow",
|
|
61
|
+
"version": "0.0.11",
|
|
62
|
+
"prev_version": "0.0.10",
|
|
63
|
+
"published_at": "2026-06-16T01:18:07.728Z",
|
|
64
|
+
"git": {
|
|
65
|
+
"sha": "edabc263bd89f08b5c7a075f270c2172e82da4da",
|
|
66
|
+
"tag": "dovetail-babel-preset-servicenow@0.0.11"
|
|
67
|
+
},
|
|
68
|
+
"semver_bump": "patch",
|
|
69
|
+
"commits": [
|
|
70
|
+
{
|
|
71
|
+
"sha": "edabc263b",
|
|
72
|
+
"subject": "chore(deps): bump @babel/core from 7.29.0 to 7.29.6 (#181)",
|
|
73
|
+
"pr": 181
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"sha": "de9d77971",
|
|
77
|
+
"subject": "chore(release): publish 14 packages [skip ci]"
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
"exports_delta": null,
|
|
81
|
+
"narrative": null
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"$schema": "dovetail-release-event/v1",
|
|
85
|
+
"event_id": "dovetail-servicenow@0.0.21",
|
|
86
|
+
"package": "@tenonhq/dovetail-servicenow",
|
|
87
|
+
"version": "0.0.21",
|
|
88
|
+
"prev_version": "0.0.20",
|
|
89
|
+
"published_at": "2026-06-15T17:59:30.698Z",
|
|
90
|
+
"git": {
|
|
91
|
+
"sha": "51e4e4592d491cd337f5bc1aa93d04f582f555a1",
|
|
92
|
+
"tag": "dovetail-servicenow@0.0.21"
|
|
93
|
+
},
|
|
94
|
+
"semver_bump": "patch",
|
|
95
|
+
"commits": [
|
|
96
|
+
{
|
|
97
|
+
"sha": "51e4e4592",
|
|
98
|
+
"subject": "feat(mcp): per-call instance retarget for servicenow_query_table (#180)",
|
|
99
|
+
"pr": 180
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"sha": "c5d9106c4",
|
|
103
|
+
"subject": "chore(release): publish 2 packages [skip ci]"
|
|
104
|
+
}
|
|
105
|
+
],
|
|
106
|
+
"exports_delta": null,
|
|
107
|
+
"narrative": null
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"$schema": "dovetail-release-event/v1",
|
|
111
|
+
"event_id": "dovetail-mcp@0.0.16",
|
|
112
|
+
"package": "@tenonhq/dovetail-mcp",
|
|
113
|
+
"version": "0.0.16",
|
|
114
|
+
"prev_version": "0.0.15",
|
|
115
|
+
"published_at": "2026-06-15T17:59:30.698Z",
|
|
116
|
+
"git": {
|
|
117
|
+
"sha": "51e4e4592d491cd337f5bc1aa93d04f582f555a1",
|
|
118
|
+
"tag": "dovetail-mcp@0.0.16"
|
|
119
|
+
},
|
|
120
|
+
"semver_bump": "patch",
|
|
121
|
+
"commits": [
|
|
122
|
+
{
|
|
123
|
+
"sha": "51e4e4592",
|
|
124
|
+
"subject": "feat(mcp): per-call instance retarget for servicenow_query_table (#180)",
|
|
125
|
+
"pr": 180
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"sha": "c5d9106c4",
|
|
129
|
+
"subject": "chore(release): publish 2 packages [skip ci]"
|
|
130
|
+
}
|
|
131
|
+
],
|
|
132
|
+
"exports_delta": null,
|
|
133
|
+
"narrative": null
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"$schema": "dovetail-release-event/v1",
|
|
137
|
+
"event_id": "dovetail-servicenow@0.0.20",
|
|
138
|
+
"package": "@tenonhq/dovetail-servicenow",
|
|
139
|
+
"version": "0.0.20",
|
|
140
|
+
"prev_version": "0.0.19",
|
|
141
|
+
"published_at": "2026-06-14T00:08:21.397Z",
|
|
142
|
+
"git": {
|
|
143
|
+
"sha": "41295667a526f32eb7a5763ec1b416c19ef49bee",
|
|
144
|
+
"tag": "dovetail-servicenow@0.0.20"
|
|
145
|
+
},
|
|
146
|
+
"semver_bump": "patch",
|
|
147
|
+
"commits": [
|
|
148
|
+
{
|
|
149
|
+
"sha": "41295667a",
|
|
150
|
+
"subject": "chore(servicenow): release 0.0.20 — ship create-table fix to npm (#179)",
|
|
151
|
+
"pr": 179
|
|
152
|
+
}
|
|
153
|
+
],
|
|
154
|
+
"exports_delta": null,
|
|
155
|
+
"narrative": null
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"$schema": "dovetail-release-event/v1",
|
|
159
|
+
"event_id": "dovetail-mcp@0.0.15",
|
|
160
|
+
"package": "@tenonhq/dovetail-mcp",
|
|
161
|
+
"version": "0.0.15",
|
|
162
|
+
"prev_version": "0.0.14",
|
|
163
|
+
"published_at": "2026-06-14T00:08:21.397Z",
|
|
164
|
+
"git": {
|
|
165
|
+
"sha": "41295667a526f32eb7a5763ec1b416c19ef49bee",
|
|
166
|
+
"tag": "dovetail-mcp@0.0.15"
|
|
167
|
+
},
|
|
168
|
+
"semver_bump": "patch",
|
|
169
|
+
"commits": [],
|
|
170
|
+
"exports_delta": null,
|
|
171
|
+
"narrative": null
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"$schema": "dovetail-release-event/v1",
|
|
175
|
+
"event_id": "dovetail-eslint-plugin@0.0.13",
|
|
176
|
+
"package": "@tenonhq/dovetail-eslint-plugin",
|
|
177
|
+
"version": "0.0.13",
|
|
178
|
+
"prev_version": "0.0.12",
|
|
179
|
+
"published_at": "2026-06-14T00:02:00.641Z",
|
|
180
|
+
"git": {
|
|
181
|
+
"sha": "ff9d18fd2c1a7305862cefdc942bbbf35ee155e8",
|
|
182
|
+
"tag": "dovetail-eslint-plugin@0.0.13"
|
|
183
|
+
},
|
|
184
|
+
"semver_bump": "patch",
|
|
185
|
+
"commits": [
|
|
186
|
+
{
|
|
187
|
+
"sha": "ff9d18fd2",
|
|
188
|
+
"subject": "docs(eslint-plugin): tighten overview wording"
|
|
189
|
+
}
|
|
190
|
+
],
|
|
191
|
+
"exports_delta": null,
|
|
192
|
+
"narrative": null
|
|
193
|
+
},
|
|
5
194
|
{
|
|
6
195
|
"$schema": "dovetail-release-event/v1",
|
|
7
196
|
"event_id": "dovetail-claude-plans@0.0.23",
|
|
@@ -1183,230 +1372,6 @@
|
|
|
1183
1372
|
],
|
|
1184
1373
|
"exports_delta": null,
|
|
1185
1374
|
"narrative": null
|
|
1186
|
-
},
|
|
1187
|
-
{
|
|
1188
|
-
"$schema": "dovetail-release-event/v1",
|
|
1189
|
-
"event_id": "dovetail-schema@0.0.9",
|
|
1190
|
-
"package": "@tenonhq/dovetail-schema",
|
|
1191
|
-
"version": "0.0.9",
|
|
1192
|
-
"prev_version": "0.0.8",
|
|
1193
|
-
"published_at": "2026-05-29T21:30:43.647Z",
|
|
1194
|
-
"git": {
|
|
1195
|
-
"sha": "dac76cc7b8e6a346aa54a84c7b50c442323301fb",
|
|
1196
|
-
"tag": "dovetail-schema@0.0.9"
|
|
1197
|
-
},
|
|
1198
|
-
"semver_bump": "patch",
|
|
1199
|
-
"commits": [
|
|
1200
|
-
{
|
|
1201
|
-
"sha": "dac76cc7b",
|
|
1202
|
-
"subject": "feat(schema): snapshots + structured drift diff for dove schema (#122)",
|
|
1203
|
-
"pr": 122
|
|
1204
|
-
},
|
|
1205
|
-
{
|
|
1206
|
-
"sha": "9332fdf7a",
|
|
1207
|
-
"subject": "chore(release): publish 20 packages [skip ci]"
|
|
1208
|
-
},
|
|
1209
|
-
{
|
|
1210
|
-
"sha": "9046da774",
|
|
1211
|
-
"subject": "chore(release): publish 19 packages [skip ci]"
|
|
1212
|
-
}
|
|
1213
|
-
],
|
|
1214
|
-
"exports_delta": null,
|
|
1215
|
-
"narrative": null
|
|
1216
|
-
},
|
|
1217
|
-
{
|
|
1218
|
-
"$schema": "dovetail-release-event/v1",
|
|
1219
|
-
"event_id": "dovetail-core@0.0.95",
|
|
1220
|
-
"package": "@tenonhq/dovetail-core",
|
|
1221
|
-
"version": "0.0.95",
|
|
1222
|
-
"prev_version": "0.0.94",
|
|
1223
|
-
"published_at": "2026-05-29T21:30:43.647Z",
|
|
1224
|
-
"git": {
|
|
1225
|
-
"sha": "dac76cc7b8e6a346aa54a84c7b50c442323301fb",
|
|
1226
|
-
"tag": "dovetail-core@0.0.95"
|
|
1227
|
-
},
|
|
1228
|
-
"semver_bump": "patch",
|
|
1229
|
-
"commits": [
|
|
1230
|
-
{
|
|
1231
|
-
"sha": "dac76cc7b",
|
|
1232
|
-
"subject": "feat(schema): snapshots + structured drift diff for dove schema (#122)",
|
|
1233
|
-
"pr": 122
|
|
1234
|
-
},
|
|
1235
|
-
{
|
|
1236
|
-
"sha": "da705804d",
|
|
1237
|
-
"subject": "chore(release): @tenonhq/dovetail-dashboard@0.0.33 [skip ci]"
|
|
1238
|
-
},
|
|
1239
|
-
{
|
|
1240
|
-
"sha": "92213e1f8",
|
|
1241
|
-
"subject": "chore(release): publish 2 packages [skip ci]"
|
|
1242
|
-
},
|
|
1243
|
-
{
|
|
1244
|
-
"sha": "1d43f9122",
|
|
1245
|
-
"subject": "chore(release): @tenonhq/dovetail-core@0.0.94 [skip ci]"
|
|
1246
|
-
}
|
|
1247
|
-
],
|
|
1248
|
-
"exports_delta": null,
|
|
1249
|
-
"narrative": null
|
|
1250
|
-
},
|
|
1251
|
-
{
|
|
1252
|
-
"$schema": "dovetail-release-event/v1",
|
|
1253
|
-
"event_id": "dovetail-dashboard@0.0.33",
|
|
1254
|
-
"package": "@tenonhq/dovetail-dashboard",
|
|
1255
|
-
"version": "0.0.33",
|
|
1256
|
-
"prev_version": "0.0.32",
|
|
1257
|
-
"published_at": "2026-05-29T16:55:37.729Z",
|
|
1258
|
-
"git": {
|
|
1259
|
-
"sha": "3aaef1ba92364c119ece69369c5cc3379da0ed73",
|
|
1260
|
-
"tag": "dovetail-dashboard@0.0.33"
|
|
1261
|
-
},
|
|
1262
|
-
"semver_bump": "patch",
|
|
1263
|
-
"commits": [
|
|
1264
|
-
{
|
|
1265
|
-
"sha": "3aaef1ba9",
|
|
1266
|
-
"subject": "fix: legible inline code in Claude Plans dark mode (#120)",
|
|
1267
|
-
"pr": 120
|
|
1268
|
-
},
|
|
1269
|
-
{
|
|
1270
|
-
"sha": "92213e1f8",
|
|
1271
|
-
"subject": "chore(release): publish 2 packages [skip ci]"
|
|
1272
|
-
}
|
|
1273
|
-
],
|
|
1274
|
-
"exports_delta": null,
|
|
1275
|
-
"narrative": null
|
|
1276
|
-
},
|
|
1277
|
-
{
|
|
1278
|
-
"$schema": "dovetail-release-event/v1",
|
|
1279
|
-
"event_id": "dovetail-claude-plans@0.0.20",
|
|
1280
|
-
"package": "@tenonhq/dovetail-claude-plans",
|
|
1281
|
-
"version": "0.0.20",
|
|
1282
|
-
"prev_version": "0.0.19",
|
|
1283
|
-
"published_at": "2026-05-29T02:18:11.229Z",
|
|
1284
|
-
"git": {
|
|
1285
|
-
"sha": "6a12c10733b64732855a8125c84f0d4846ced256",
|
|
1286
|
-
"tag": "dovetail-claude-plans@0.0.20"
|
|
1287
|
-
},
|
|
1288
|
-
"semver_bump": "patch",
|
|
1289
|
-
"commits": [
|
|
1290
|
-
{
|
|
1291
|
-
"sha": "6a12c1073",
|
|
1292
|
-
"subject": "feat: claude-plans version history, feature score, selected-state + deep-link hardening (#118)",
|
|
1293
|
-
"pr": 118
|
|
1294
|
-
},
|
|
1295
|
-
{
|
|
1296
|
-
"sha": "578d6fb2a",
|
|
1297
|
-
"subject": "chore(release): publish 2 packages [skip ci]"
|
|
1298
|
-
}
|
|
1299
|
-
],
|
|
1300
|
-
"exports_delta": null,
|
|
1301
|
-
"narrative": null
|
|
1302
|
-
},
|
|
1303
|
-
{
|
|
1304
|
-
"$schema": "dovetail-release-event/v1",
|
|
1305
|
-
"event_id": "dovetail-dashboard@0.0.32",
|
|
1306
|
-
"package": "@tenonhq/dovetail-dashboard",
|
|
1307
|
-
"version": "0.0.32",
|
|
1308
|
-
"prev_version": "0.0.31",
|
|
1309
|
-
"published_at": "2026-05-29T02:18:11.229Z",
|
|
1310
|
-
"git": {
|
|
1311
|
-
"sha": "6a12c10733b64732855a8125c84f0d4846ced256",
|
|
1312
|
-
"tag": "dovetail-dashboard@0.0.32"
|
|
1313
|
-
},
|
|
1314
|
-
"semver_bump": "patch",
|
|
1315
|
-
"commits": [
|
|
1316
|
-
{
|
|
1317
|
-
"sha": "6a12c1073",
|
|
1318
|
-
"subject": "feat: claude-plans version history, feature score, selected-state + deep-link hardening (#118)",
|
|
1319
|
-
"pr": 118
|
|
1320
|
-
},
|
|
1321
|
-
{
|
|
1322
|
-
"sha": "578d6fb2a",
|
|
1323
|
-
"subject": "chore(release): publish 2 packages [skip ci]"
|
|
1324
|
-
}
|
|
1325
|
-
],
|
|
1326
|
-
"exports_delta": null,
|
|
1327
|
-
"narrative": null
|
|
1328
|
-
},
|
|
1329
|
-
{
|
|
1330
|
-
"$schema": "dovetail-release-event/v1",
|
|
1331
|
-
"event_id": "dovetail-core@0.0.94",
|
|
1332
|
-
"package": "@tenonhq/dovetail-core",
|
|
1333
|
-
"version": "0.0.94",
|
|
1334
|
-
"prev_version": "0.0.93",
|
|
1335
|
-
"published_at": "2026-05-28T23:46:03.175Z",
|
|
1336
|
-
"git": {
|
|
1337
|
-
"sha": "8a76deffc38892321c92ae538fee3bc6b8f90d76",
|
|
1338
|
-
"tag": "dovetail-core@0.0.94"
|
|
1339
|
-
},
|
|
1340
|
-
"semver_bump": "patch",
|
|
1341
|
-
"commits": [
|
|
1342
|
-
{
|
|
1343
|
-
"sha": "8a76deffc",
|
|
1344
|
-
"subject": "fix: filesystem-safe record folder names (sys_id fallback for Windows) (#117)",
|
|
1345
|
-
"pr": 117
|
|
1346
|
-
},
|
|
1347
|
-
{
|
|
1348
|
-
"sha": "578d6fb2a",
|
|
1349
|
-
"subject": "chore(release): publish 2 packages [skip ci]"
|
|
1350
|
-
},
|
|
1351
|
-
{
|
|
1352
|
-
"sha": "3faeec43f",
|
|
1353
|
-
"subject": "chore(release): @tenonhq/dovetail-core@0.0.93 [skip ci]"
|
|
1354
|
-
}
|
|
1355
|
-
],
|
|
1356
|
-
"exports_delta": null,
|
|
1357
|
-
"narrative": null
|
|
1358
|
-
},
|
|
1359
|
-
{
|
|
1360
|
-
"$schema": "dovetail-release-event/v1",
|
|
1361
|
-
"event_id": "dovetail-claude-plans@0.0.19",
|
|
1362
|
-
"package": "@tenonhq/dovetail-claude-plans",
|
|
1363
|
-
"version": "0.0.19",
|
|
1364
|
-
"prev_version": "0.0.18",
|
|
1365
|
-
"published_at": "2026-05-28T15:54:22.483Z",
|
|
1366
|
-
"git": {
|
|
1367
|
-
"sha": "0247ef6e75093c95359413725977df07bd5bff70",
|
|
1368
|
-
"tag": "dovetail-claude-plans@0.0.19"
|
|
1369
|
-
},
|
|
1370
|
-
"semver_bump": "patch",
|
|
1371
|
-
"commits": [
|
|
1372
|
-
{
|
|
1373
|
-
"sha": "0247ef6e7",
|
|
1374
|
-
"subject": "feat(dashboard): Prompt Lints filters + merged-PR filter for Claude Plans (#116)",
|
|
1375
|
-
"pr": 116
|
|
1376
|
-
},
|
|
1377
|
-
{
|
|
1378
|
-
"sha": "5f14a3421",
|
|
1379
|
-
"subject": "chore(release): @tenonhq/dovetail-claude-plans@0.0.18 [skip ci]"
|
|
1380
|
-
}
|
|
1381
|
-
],
|
|
1382
|
-
"exports_delta": null,
|
|
1383
|
-
"narrative": null
|
|
1384
|
-
},
|
|
1385
|
-
{
|
|
1386
|
-
"$schema": "dovetail-release-event/v1",
|
|
1387
|
-
"event_id": "dovetail-dashboard@0.0.31",
|
|
1388
|
-
"package": "@tenonhq/dovetail-dashboard",
|
|
1389
|
-
"version": "0.0.31",
|
|
1390
|
-
"prev_version": "0.0.30",
|
|
1391
|
-
"published_at": "2026-05-28T15:54:22.483Z",
|
|
1392
|
-
"git": {
|
|
1393
|
-
"sha": "0247ef6e75093c95359413725977df07bd5bff70",
|
|
1394
|
-
"tag": "dovetail-dashboard@0.0.31"
|
|
1395
|
-
},
|
|
1396
|
-
"semver_bump": "patch",
|
|
1397
|
-
"commits": [
|
|
1398
|
-
{
|
|
1399
|
-
"sha": "0247ef6e7",
|
|
1400
|
-
"subject": "feat(dashboard): Prompt Lints filters + merged-PR filter for Claude Plans (#116)",
|
|
1401
|
-
"pr": 116
|
|
1402
|
-
},
|
|
1403
|
-
{
|
|
1404
|
-
"sha": "c35d549c4",
|
|
1405
|
-
"subject": "chore(release): @tenonhq/dovetail-dashboard@0.0.30 [skip ci]"
|
|
1406
|
-
}
|
|
1407
|
-
],
|
|
1408
|
-
"exports_delta": null,
|
|
1409
|
-
"narrative": null
|
|
1410
1375
|
}
|
|
1411
1376
|
]
|
|
1412
1377
|
}
|
|
@@ -62,6 +62,12 @@ Launches at `http://localhost:3456` (configurable via `DASHBOARD_PORT` in `.env`
|
|
|
62
62
|
|
|
63
63
|
The dashboard reads scopes from `dove.config.js` and stores selections in `.dove-update-sets.json`.
|
|
64
64
|
|
|
65
|
+
### How `dove push` chooses an update set (precedence)
|
|
66
|
+
|
|
67
|
+
`dove push` routes each record's capture by the per-scope map in **`.dove-update-sets.json`**, keyed on the record's scope — **not** by the instance's currently-active update set. When a scope has an entry, the push captures into that set via `pushWithUpdateSet`; when it has none, the push falls back to a plain update that lands in whatever set is active on the instance.
|
|
68
|
+
|
|
69
|
+
`createUpdateSet`, `switchUpdateSet`, and `push --updateSet` now write the affected scope's entry into `.dove-update-sets.json` as part of activating the set, so the routing file, the active set, and the push destination stay consistent. Each prints the resolved destination, e.g. `Push routing updated: x_cadso_core -> FEAT-123 (sys_id)`, and every push prints `Update set routing (from .dove-update-sets.json): <scope> -> <name> (<sys_id>)` so you can see exactly where captures land. If you edit `.dove-update-sets.json` by hand, that entry wins until you switch sets again.
|
|
70
|
+
|
|
65
71
|
### Multi-Scope Update Set Monitoring
|
|
66
72
|
|
|
67
73
|
When using `npx dove watchAllScopes`, update set status is automatically checked every 2 minutes. It warns if any scope is using the DEFAULT update set (a common mistake that puts changes in the wrong place).
|