@tenonhq/dovetail-core 0.0.111 → 0.0.113

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/README.md CHANGED
@@ -26,7 +26,7 @@ Quick reference:
26
26
  ```bash
27
27
  npx dove watch # multi-scope watch + dashboard
28
28
  npx dove push # build + push current files
29
- npx dove refresh # pull manifest + new files
29
+ npx dove refresh # pull manifest + new files (aliases: pull, r)
30
30
  npx dove build # local build only
31
31
  npx dove deploy # deploy built artifacts
32
32
  npx dove dashboard # update-set dashboard web UI
package/dist/commander.js CHANGED
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.configureCli = configureCli;
6
7
  exports.initCommands = initCommands;
7
8
  const commands_1 = require("./commands");
8
9
  const allScopesCommands_1 = require("./allScopesCommands");
@@ -18,15 +19,26 @@ const clickupCommands_1 = require("./clickupCommands");
18
19
  const loginCommand_1 = require("./loginCommand");
19
20
  const knowledgeDiffCommand_1 = require("./knowledgeDiffCommand");
20
21
  const yargs_1 = __importDefault(require("yargs"));
21
- async function initCommands() {
22
+ /**
23
+ * @description Registers every `dove` command, the global options, and the
24
+ * parser-strictness rules on a yargs instance. Split out from initCommands so
25
+ * tests can drive a fresh, non-exiting parser (see tests/commanderStrict.test.ts).
26
+ * @param {Argv} cli - A yargs instance to configure.
27
+ * @returns {Argv} The same instance, with all commands and options registered.
28
+ */
29
+ function configureCli(cli) {
22
30
  const sharedOptions = {
23
31
  logLevel: {
24
32
  default: "info",
25
33
  },
26
34
  };
27
- yargs_1.default
35
+ return cli
36
+ .scriptName("dove")
28
37
  .option("env", {
29
- alias: "e",
38
+ // `--env-file` / `--envFile` are long-documented spellings and are parsed
39
+ // from raw argv in envArg.ts. Declaring them as aliases keeps `--help` and
40
+ // the parser honest about what is actually accepted.
41
+ alias: ["e", "env-file", "envFile"],
30
42
  type: "string",
31
43
  describe: "Path to a .env file to load for this command (default: .env in the project root). Lets one checkout target multiple instances.",
32
44
  })
@@ -65,7 +77,10 @@ async function initCommands() {
65
77
  }, async (args) => {
66
78
  await (0, allScopesCommands_1.watchAllScopesCommand)(args);
67
79
  })
68
- .command(["refresh", "r"], "Pull latest manifest and file contents from the ServiceNow instance", (cmdArgs) => {
80
+ .command(
81
+ // `pull` is the verb people reach for ("pull before push") and it appears
82
+ // in downstream docs and scripts — alias it rather than silently no-op.
83
+ ["refresh", "r", "pull"], "Pull latest manifest and file contents from the ServiceNow instance", (cmdArgs) => {
69
84
  cmdArgs.options({
70
85
  ...sharedOptions,
71
86
  force: {
@@ -568,5 +583,17 @@ async function initCommands() {
568
583
  }, async function (args) {
569
584
  await (0, knowledgeDiffCommand_1.knowledgeDiffCommand)(args);
570
585
  })
571
- .help().argv;
586
+ // An unrecognized command used to fall through the parser and exit 0, doing
587
+ // nothing — so `dove pull` (and any typo) looked like it had succeeded, which
588
+ // is the exact false confidence "pull before push" exists to prevent.
589
+ // strictCommands (not strict) is deliberate: it rejects unknown *commands*
590
+ // while still tolerating undeclared flags, so documented option spellings
591
+ // keep working. demandCommand turns a bare `dove` into help + exit 1.
592
+ .demandCommand(1, "Please specify a command (run `dove --help` to see them all)")
593
+ .strictCommands()
594
+ .recommendCommands()
595
+ .help();
596
+ }
597
+ async function initCommands() {
598
+ configureCli(yargs_1.default).argv;
572
599
  }
@@ -0,0 +1,152 @@
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
+ // --- Mock setup (must be before imports) ---
7
+ //
8
+ // Every command module is mocked: these tests exercise the parser (routing +
9
+ // strictness), not the commands themselves, and the real handlers would hit
10
+ // ServiceNow.
11
+ jest.mock("../commands", () => ({
12
+ refreshCommand: jest.fn(),
13
+ pushCommand: jest.fn(),
14
+ downloadCommand: jest.fn(),
15
+ initCommand: jest.fn(),
16
+ buildCommand: jest.fn(),
17
+ deployCommand: jest.fn(),
18
+ statusCommand: jest.fn(),
19
+ taskClearCommand: jest.fn(),
20
+ }));
21
+ jest.mock("../allScopesCommands", () => ({
22
+ initScopesCommand: jest.fn(),
23
+ watchAllScopesCommand: jest.fn(),
24
+ }));
25
+ jest.mock("../updateSetCommands", () => ({
26
+ createUpdateSetCommand: jest.fn(),
27
+ switchUpdateSetCommand: jest.fn(),
28
+ listUpdateSetsCommand: jest.fn(),
29
+ showCurrentUpdateSetCommand: jest.fn(),
30
+ changeScopeCommand: jest.fn(),
31
+ showCurrentScopeCommand: jest.fn(),
32
+ }));
33
+ jest.mock("../dashboardCommand", () => ({ dashboardCommand: jest.fn() }));
34
+ jest.mock("../schemaCommand", () => ({
35
+ schemaPullCommand: jest.fn(),
36
+ schemaDiffCommand: jest.fn(),
37
+ schemaSnapshotsCommand: jest.fn(),
38
+ }));
39
+ jest.mock("../claudeCommand", () => ({ initClaudeCommand: jest.fn() }));
40
+ jest.mock("../createRecordCommand", () => ({ createRecordCommand: jest.fn() }));
41
+ jest.mock("../deleteRecordCommand", () => ({ deleteRecordCommand: jest.fn() }));
42
+ jest.mock("../reconcileCommand", () => ({ reconcileCommand: jest.fn() }));
43
+ jest.mock("../migrateCommand", () => ({ migrateCommand: jest.fn() }));
44
+ jest.mock("../loginCommand", () => ({ loginCommand: jest.fn() }));
45
+ jest.mock("../knowledgeDiffCommand", () => ({ knowledgeDiffCommand: jest.fn() }));
46
+ jest.mock("../clickupCommands", () => ({
47
+ clickupTasksCommand: jest.fn(),
48
+ clickupTaskCommand: jest.fn(),
49
+ clickupCreateCommand: jest.fn(),
50
+ clickupUpdateCommand: jest.fn(),
51
+ clickupCommentCommand: jest.fn(),
52
+ clickupSetupCommand: jest.fn(),
53
+ clickupTeamsCommand: jest.fn(),
54
+ clickupSpacesCommand: jest.fn(),
55
+ clickupListsCommand: jest.fn(),
56
+ }));
57
+ // --- Imports (after mocks) ---
58
+ const yargs_1 = __importDefault(require("yargs/yargs"));
59
+ const commander_1 = require("../commander");
60
+ const commands_1 = require("../commands");
61
+ /**
62
+ * @description Runs a fake `dove` invocation through a fresh, non-exiting parser.
63
+ * @param {string[]} args - Argv as the user would type it, minus the binary name.
64
+ * @returns {Promise<ParseResult>} The parse error (if any), yargs' captured output, and the parsed argv.
65
+ */
66
+ async function run(args) {
67
+ const cli = (0, commander_1.configureCli)((0, yargs_1.default)([])).exitProcess(false);
68
+ const result = await new Promise(function (resolve) {
69
+ cli.parse(args, {}, function (error, argv, output) {
70
+ resolve({
71
+ error: error === null ? undefined : error,
72
+ output: output || "",
73
+ argv: (argv || {}),
74
+ });
75
+ });
76
+ });
77
+ // Command handlers are async; let their microtasks flush before asserting.
78
+ await new Promise(function (resolve) {
79
+ setImmediate(resolve);
80
+ });
81
+ return result;
82
+ }
83
+ beforeEach(function () {
84
+ jest.clearAllMocks();
85
+ });
86
+ describe("dove pull", function () {
87
+ it("routes `pull` to the refresh command", async function () {
88
+ const { error } = await run(["pull"]);
89
+ expect(error).toBeUndefined();
90
+ expect(commands_1.refreshCommand).toHaveBeenCalledTimes(1);
91
+ });
92
+ it("still routes the canonical `refresh` and its `r` alias", async function () {
93
+ await run(["refresh"]);
94
+ expect(commands_1.refreshCommand).toHaveBeenCalledTimes(1);
95
+ await run(["r"]);
96
+ expect(commands_1.refreshCommand).toHaveBeenCalledTimes(2);
97
+ });
98
+ it("passes refresh flags through the pull alias", async function () {
99
+ const { error, argv } = await run(["pull", "--scope", "x_cadso_core", "--force"]);
100
+ expect(error).toBeUndefined();
101
+ expect(argv.scope).toBe("x_cadso_core");
102
+ expect(argv.force).toBe(true);
103
+ expect(commands_1.refreshCommand).toHaveBeenCalledTimes(1);
104
+ });
105
+ });
106
+ describe("unknown commands", function () {
107
+ it("errors instead of silently succeeding", async function () {
108
+ const { error } = await run(["psuh"]);
109
+ expect(error).toBeDefined();
110
+ expect(String(error)).toContain("Unknown command");
111
+ expect(commands_1.pushCommand).not.toHaveBeenCalled();
112
+ });
113
+ it("suggests the closest real command on a typo", async function () {
114
+ const { error, output } = await run(["refesh"]);
115
+ expect(error).toBeDefined();
116
+ expect(output + String(error)).toContain("Did you mean refresh?");
117
+ expect(commands_1.refreshCommand).not.toHaveBeenCalled();
118
+ });
119
+ it("errors when no command is given at all", async function () {
120
+ const { error } = await run([]);
121
+ expect(error).toBeDefined();
122
+ expect(String(error)).toContain("Please specify a command");
123
+ });
124
+ });
125
+ describe("existing invocations keep working", function () {
126
+ it("accepts a known command", async function () {
127
+ const { error } = await run(["status"]);
128
+ expect(error).toBeUndefined();
129
+ expect(commands_1.statusCommand).toHaveBeenCalledTimes(1);
130
+ });
131
+ it("accepts push with its --diff flag and optional target positional", async function () {
132
+ const { error, argv } = await run(["push", "--diff", "main"]);
133
+ expect(error).toBeUndefined();
134
+ expect(argv.diff).toBe("main");
135
+ expect(commands_1.pushCommand).toHaveBeenCalledTimes(1);
136
+ });
137
+ it("accepts the documented --env-file spelling (not just --env)", async function () {
138
+ // strictCommands, not strict: undeclared flags stay tolerated and the
139
+ // documented env aliases all resolve to `env`. See envArg.ts.
140
+ const { error, argv } = await run(["status", "--env-file", ".env.prod"]);
141
+ expect(error).toBeUndefined();
142
+ expect(argv.env).toBe(".env.prod");
143
+ });
144
+ it("still routes nested subcommands", async function () {
145
+ const { error } = await run(["schema", "pull"]);
146
+ expect(error).toBeUndefined();
147
+ });
148
+ it("rejects an unknown nested subcommand", async function () {
149
+ const { error } = await run(["clickup", "taks"]);
150
+ expect(error).toBeDefined();
151
+ });
152
+ });
@@ -3,6 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ // --- Mock setup (must be before imports) ---
7
+ //
8
+ // These tests assert on credential loading only. init() also boots the CLI
9
+ // parser, which under Jest sees jest's own argv, finds no `dove` command, and
10
+ // now exits 1 (the parser rejects unknown commands instead of silently
11
+ // succeeding). Stub the parser so the credential assertions are what's tested.
12
+ jest.mock("../commander", () => ({ initCommands: jest.fn() }));
13
+ // --- Imports (after mocks) ---
6
14
  const fs_1 = __importDefault(require("fs"));
7
15
  const path_1 = __importDefault(require("path"));
8
16
  const bootstrap_1 = require("../bootstrap");
@@ -0,0 +1,232 @@
1
+ "use strict";
2
+ /**
3
+ * Tests the SERVER half of the sync engine — servicenow/dovetail/sys_script_include/
4
+ * DovetailUtilsMS.js — against the record-name collision that silently dropped records.
5
+ *
6
+ * Before the fix, `records` was keyed by display name, so two records sharing a name
7
+ * collapsed into one entry: one vanished from the manifest with no warning, and because
8
+ * the survivor kept the shared folder, a later push to that folder wrote to the WRONG
9
+ * record.
10
+ *
11
+ * This loads the real script include into a sandbox with minimal ServiceNow stubs
12
+ * (Class / GlideRecord / gs) rather than re-implementing the logic, so the test fails if
13
+ * the deployed source regresses. There is no ATF/SN harness in this repo, and this code
14
+ * is deployed to a shared instance — so this is the only gate it gets before deploy.
15
+ *
16
+ * getFileMap() short-circuits when `includes[table]` carries explicit field overrides,
17
+ * which is why no sys_dictionary / TableUtils stub is needed here.
18
+ */
19
+ var __importDefault = (this && this.__importDefault) || function (mod) {
20
+ return (mod && mod.__esModule) ? mod : { "default": mod };
21
+ };
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ const fs_1 = __importDefault(require("fs"));
24
+ const path_1 = __importDefault(require("path"));
25
+ const vm_1 = __importDefault(require("vm"));
26
+ const SOURCE = path_1.default.join(__dirname, "../../../../servicenow/dovetail/sys_script_include/DovetailUtilsMS.js");
27
+ /** Minimal GlideRecord over a fixture list. Only what buildTableMap and processMissingFiles touch. */
28
+ function makeGlideRecord(rows) {
29
+ return function GlideRecordStub(_table) {
30
+ var i = -1;
31
+ this.get = function (sysId) {
32
+ i = rows.findIndex(function (r) {
33
+ return r.sys_id === sysId;
34
+ });
35
+ return i !== -1;
36
+ };
37
+ this.addQuery = function () {
38
+ return { addOrCondition: function () { } };
39
+ };
40
+ this.addEncodedQuery = function () { };
41
+ this.query = function () {
42
+ i = -1;
43
+ };
44
+ this.next = function () {
45
+ i += 1;
46
+ return i < rows.length;
47
+ };
48
+ this.getValue = function (field) {
49
+ return rows[i][field];
50
+ };
51
+ this.getDisplayValue = function (field) {
52
+ // No arg → the record's display value (the name). This is what
53
+ // generateRecordName() uses, and it is where the collision comes from.
54
+ return field ? rows[i][field] : rows[i].name;
55
+ };
56
+ this.getElements = function () {
57
+ return null; // metaData capture is skipped; getContents is false below
58
+ };
59
+ };
60
+ }
61
+ function loadUtils(rows) {
62
+ const warnings = [];
63
+ const sandbox = {
64
+ Class: {
65
+ create: function () {
66
+ return function () {
67
+ if (this.initialize)
68
+ this.initialize.apply(this, arguments);
69
+ };
70
+ },
71
+ },
72
+ GlideRecord: makeGlideRecord(rows),
73
+ gs: {
74
+ warn: function (msg) {
75
+ warnings.push(msg);
76
+ },
77
+ getProperty: function () {
78
+ return "https://example.service-now.com/";
79
+ },
80
+ },
81
+ };
82
+ vm_1.default.createContext(sandbox);
83
+ vm_1.default.runInContext(fs_1.default.readFileSync(SOURCE, "utf8"), sandbox);
84
+ return { utils: new sandbox.DovetailUtilsMS(), warnings: warnings };
85
+ }
86
+ function buildMap(rows) {
87
+ const loaded = loadUtils(rows);
88
+ const result = loaded.utils.buildTableMap({
89
+ tableName: "x_cadso_journey_action",
90
+ scopeId: "scope1",
91
+ getContents: false,
92
+ // Explicit field override → getFileMap short-circuits (no sys_dictionary stub).
93
+ includes: { x_cadso_journey_action: { script: { type: "js" } } },
94
+ excludes: {},
95
+ tableOptions: {},
96
+ });
97
+ return { records: result.records, warnings: loaded.warnings };
98
+ }
99
+ describe("DovetailUtilsMS.buildTableMap — duplicate display names", () => {
100
+ it("keeps BOTH records when two share a display name (regression: one was silently dropped)", () => {
101
+ const out = buildMap([
102
+ { sys_id: "1607d7f0c373f2d0d4ddf1db05013101", name: "Blueprint" },
103
+ { sys_id: "46ae2decc33f32d0d4ddf1db050131d9", name: "Blueprint" },
104
+ ]);
105
+ const keys = Object.keys(out.records);
106
+ expect(keys).toHaveLength(2); // was 1 — the second overwrote the first
107
+ const sysIds = keys.map((k) => out.records[k].sys_id).sort();
108
+ expect(sysIds).toEqual([
109
+ "1607d7f0c373f2d0d4ddf1db05013101",
110
+ "46ae2decc33f32d0d4ddf1db050131d9",
111
+ ]);
112
+ });
113
+ it("suffixes EVERY member of a colliding set, so no record arbitrarily keeps the bare name", () => {
114
+ // If only the later duplicate were suffixed, which record kept "Blueprint" would
115
+ // depend on query order — and folder names would churn between refreshes.
116
+ const out = buildMap([
117
+ { sys_id: "1607d7f0c373f2d0d4ddf1db05013101", name: "Blueprint" },
118
+ { sys_id: "46ae2decc33f32d0d4ddf1db050131d9", name: "Blueprint" },
119
+ ]);
120
+ expect(Object.keys(out.records).sort()).toEqual([
121
+ "Blueprint (1607d7f0)",
122
+ "Blueprint (46ae2dec)",
123
+ ]);
124
+ expect(out.records["Blueprint"]).toBeUndefined();
125
+ });
126
+ it("keeps record.name identical to the map key (writers build the folder path from it)", () => {
127
+ const out = buildMap([
128
+ { sys_id: "1607d7f0c373f2d0d4ddf1db05013101", name: "Blueprint" },
129
+ { sys_id: "46ae2decc33f32d0d4ddf1db050131d9", name: "Blueprint" },
130
+ ]);
131
+ Object.keys(out.records).forEach((key) => {
132
+ expect(out.records[key].name).toBe(key);
133
+ });
134
+ });
135
+ it("warns on a collision instead of dropping silently", () => {
136
+ const out = buildMap([
137
+ { sys_id: "1607d7f0c373f2d0d4ddf1db05013101", name: "Blueprint" },
138
+ { sys_id: "46ae2decc33f32d0d4ddf1db050131d9", name: "Blueprint" },
139
+ ]);
140
+ expect(out.warnings).toHaveLength(2);
141
+ expect(out.warnings[0]).toContain("duplicate display name");
142
+ expect(out.warnings[0]).toContain("Blueprint");
143
+ });
144
+ it("leaves unique names completely untouched (no suffix, no warning)", () => {
145
+ const out = buildMap([
146
+ { sys_id: "dc2ef56133a0e2507b18bc534d5c7bf5", name: "Send Email" },
147
+ { sys_id: "0d4626f933e826507b18bc534d5c7b37", name: "Send Text" },
148
+ ]);
149
+ expect(Object.keys(out.records).sort()).toEqual(["Send Email", "Send Text"]);
150
+ expect(out.warnings).toHaveLength(0);
151
+ });
152
+ it("handles three-way collisions", () => {
153
+ const out = buildMap([
154
+ { sys_id: "aaaaaaaa11111111", name: "Dup" },
155
+ { sys_id: "bbbbbbbb22222222", name: "Dup" },
156
+ { sys_id: "cccccccc33333333", name: "Dup" },
157
+ ]);
158
+ expect(Object.keys(out.records).sort()).toEqual([
159
+ "Dup (aaaaaaaa)",
160
+ "Dup (bbbbbbbb)",
161
+ "Dup (cccccccc)",
162
+ ]);
163
+ });
164
+ it("disambiguates only the colliding set, not the whole table", () => {
165
+ const out = buildMap([
166
+ { sys_id: "aaaaaaaa11111111", name: "Blueprint" },
167
+ { sys_id: "bbbbbbbb22222222", name: "Blueprint" },
168
+ { sys_id: "cccccccc33333333", name: "Send Email" },
169
+ ]);
170
+ expect(Object.keys(out.records).sort()).toEqual([
171
+ "Blueprint (aaaaaaaa)",
172
+ "Blueprint (bbbbbbbb)",
173
+ "Send Email", // untouched — this is why differentiatorField was the wrong tool
174
+ ]);
175
+ });
176
+ });
177
+ /**
178
+ * bulkDownload keys its response by display name too — the same collapse dropped the
179
+ * file CONTENT for one of a colliding pair even once the manifest listed both.
180
+ */
181
+ function processMissing(rows) {
182
+ const loaded = loadUtils(rows);
183
+ const recordMap = {};
184
+ rows.forEach((r) => {
185
+ recordMap[r.sys_id] = [{ name: "script", type: "js" }];
186
+ });
187
+ const result = loaded.utils.processMissingFiles({ x_cadso_journey_action: recordMap }, {});
188
+ return {
189
+ records: result.x_cadso_journey_action.records,
190
+ warnings: loaded.warnings,
191
+ };
192
+ }
193
+ describe("DovetailUtilsMS.processMissingFiles — duplicate display names", () => {
194
+ it("keeps BOTH records' content when two share a display name (regression: one payload was silently dropped)", () => {
195
+ const out = processMissing([
196
+ { sys_id: "1607d7f0c373f2d0d4ddf1db05013101", name: "Blueprint", script: "var a = 1;" },
197
+ { sys_id: "46ae2decc33f32d0d4ddf1db050131d9", name: "Blueprint", script: "var b = 2;" },
198
+ ]);
199
+ const keys = Object.keys(out.records).sort();
200
+ expect(keys).toEqual(["Blueprint (1607d7f0)", "Blueprint (46ae2dec)"]);
201
+ expect(out.records["Blueprint"]).toBeUndefined();
202
+ // The actual bulkDownload payload — each record keeps its OWN file content.
203
+ expect(out.records["Blueprint (1607d7f0)"].files[0].content).toBe("var a = 1;");
204
+ expect(out.records["Blueprint (46ae2dec)"].files[0].content).toBe("var b = 2;");
205
+ });
206
+ it("keeps record.name identical to the map key (writers build the folder path from it)", () => {
207
+ const out = processMissing([
208
+ { sys_id: "1607d7f0c373f2d0d4ddf1db05013101", name: "Blueprint", script: "" },
209
+ { sys_id: "46ae2decc33f32d0d4ddf1db050131d9", name: "Blueprint", script: "" },
210
+ ]);
211
+ Object.keys(out.records).forEach((key) => {
212
+ expect(out.records[key].name).toBe(key);
213
+ });
214
+ });
215
+ it("warns on a collision instead of staying silent (parity with buildTableMap)", () => {
216
+ const out = processMissing([
217
+ { sys_id: "1607d7f0c373f2d0d4ddf1db05013101", name: "Blueprint", script: "" },
218
+ { sys_id: "46ae2decc33f32d0d4ddf1db050131d9", name: "Blueprint", script: "" },
219
+ ]);
220
+ expect(out.warnings).toHaveLength(2);
221
+ expect(out.warnings[0]).toContain("duplicate display name");
222
+ expect(out.warnings[0]).toContain("bulkDownload");
223
+ });
224
+ it("leaves unique names completely untouched (no suffix, no warning)", () => {
225
+ const out = processMissing([
226
+ { sys_id: "dc2ef56133a0e2507b18bc534d5c7bf5", name: "Send Email", script: "" },
227
+ { sys_id: "0d4626f933e826507b18bc534d5c7b37", name: "Send Text", script: "" },
228
+ ]);
229
+ expect(Object.keys(out.records).sort()).toEqual(["Send Email", "Send Text"]);
230
+ expect(out.warnings).toHaveLength(0);
231
+ });
232
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenonhq/dovetail-core",
3
- "version": "0.0.111",
3
+ "version": "0.0.113",
4
4
  "description": "Next-gen file syncer",
5
5
  "license": "GPL-3.0",
6
6
  "main": "./dist/index.js",
@@ -1,7 +1,71 @@
1
1
  {
2
2
  "$schema": "dovetail-manifest/v1",
3
- "generated_at": "2026-07-10T18:41:37.097Z",
3
+ "generated_at": "2026-07-14T19:40:43.710Z",
4
4
  "events": [
5
+ {
6
+ "$schema": "dovetail-release-event/v1",
7
+ "event_id": "dovetail-core@0.0.112",
8
+ "package": "@tenonhq/dovetail-core",
9
+ "version": "0.0.112",
10
+ "prev_version": "0.0.111",
11
+ "published_at": "2026-07-14T19:40:43.702Z",
12
+ "git": {
13
+ "sha": "a2777edcbd46ee69de13e73173132f9cd18aa618",
14
+ "tag": "dovetail-core@0.0.112"
15
+ },
16
+ "semver_bump": "patch",
17
+ "commits": [
18
+ {
19
+ "sha": "a2777edcb",
20
+ "subject": "fix: reject unknown dove commands and alias pull to refresh (#219) (#221)",
21
+ "pr": 219
22
+ },
23
+ {
24
+ "sha": "6a42ef546",
25
+ "subject": "chore(release): @tenonhq/dovetail-core@0.0.111 [skip ci]"
26
+ }
27
+ ],
28
+ "exports_delta": null,
29
+ "narrative": null
30
+ },
31
+ {
32
+ "$schema": "dovetail-release-event/v1",
33
+ "event_id": "dovetail-core@0.0.111",
34
+ "package": "@tenonhq/dovetail-core",
35
+ "version": "0.0.111",
36
+ "prev_version": "0.0.110",
37
+ "published_at": "2026-07-14T18:00:31.240Z",
38
+ "git": {
39
+ "sha": "c017d194ec18d0293a0a512f835dafcf49f16560",
40
+ "tag": "dovetail-core@0.0.111"
41
+ },
42
+ "semver_bump": "patch",
43
+ "commits": [
44
+ {
45
+ "sha": "c017d194e",
46
+ "subject": "feat(core): add --table filter to dove refresh (#218)",
47
+ "pr": 218
48
+ },
49
+ {
50
+ "sha": "34c5ae1ba",
51
+ "subject": "chore(release): @tenonhq/dovetail-deploy@0.0.2 [skip ci]"
52
+ },
53
+ {
54
+ "sha": "ef9e73505",
55
+ "subject": "chore(release): publish 2 packages [skip ci]"
56
+ },
57
+ {
58
+ "sha": "619cf397b",
59
+ "subject": "chore(release): @tenonhq/dovetail-dashboard@0.0.48 [skip ci]"
60
+ },
61
+ {
62
+ "sha": "534f517e4",
63
+ "subject": "chore(release): publish 3 packages [skip ci]"
64
+ }
65
+ ],
66
+ "exports_delta": null,
67
+ "narrative": null
68
+ },
5
69
  {
6
70
  "$schema": "dovetail-release-event/v1",
7
71
  "event_id": "dovetail-deploy@0.0.2",
@@ -1213,62 +1277,6 @@
1213
1277
  ],
1214
1278
  "exports_delta": null,
1215
1279
  "narrative": null
1216
- },
1217
- {
1218
- "$schema": "dovetail-release-event/v1",
1219
- "event_id": "dovetail-mcp@0.0.9",
1220
- "package": "@tenonhq/dovetail-mcp",
1221
- "version": "0.0.9",
1222
- "prev_version": "0.0.8",
1223
- "published_at": "2026-06-05T06:43:18.252Z",
1224
- "git": {
1225
- "sha": "71bdf9bba8bd5e3849a212a18d9ebc9241232b32",
1226
- "tag": "dovetail-mcp@0.0.9"
1227
- },
1228
- "semver_bump": "patch",
1229
- "commits": [
1230
- {
1231
- "sha": "71bdf9bba",
1232
- "subject": "fix: float internal dep ranges to ~0.0.x so consumers need no overrides (#148)",
1233
- "pr": 148
1234
- },
1235
- {
1236
- "sha": "af1417796",
1237
- "subject": "chore(release): publish 3 packages [skip ci]"
1238
- }
1239
- ],
1240
- "exports_delta": null,
1241
- "narrative": null
1242
- },
1243
- {
1244
- "$schema": "dovetail-release-event/v1",
1245
- "event_id": "dovetail-prettier-plugin@0.0.11",
1246
- "package": "@tenonhq/dovetail-prettier-plugin",
1247
- "version": "0.0.11",
1248
- "prev_version": "0.0.10",
1249
- "published_at": "2026-06-05T06:43:18.252Z",
1250
- "git": {
1251
- "sha": "71bdf9bba8bd5e3849a212a18d9ebc9241232b32",
1252
- "tag": "dovetail-prettier-plugin@0.0.11"
1253
- },
1254
- "semver_bump": "patch",
1255
- "commits": [
1256
- {
1257
- "sha": "71bdf9bba",
1258
- "subject": "fix: float internal dep ranges to ~0.0.x so consumers need no overrides (#148)",
1259
- "pr": 148
1260
- },
1261
- {
1262
- "sha": "9332fdf7a",
1263
- "subject": "chore(release): publish 20 packages [skip ci]"
1264
- },
1265
- {
1266
- "sha": "9046da774",
1267
- "subject": "chore(release): publish 19 packages [skip ci]"
1268
- }
1269
- ],
1270
- "exports_delta": null,
1271
- "narrative": null
1272
1280
  }
1273
1281
  ]
1274
1282
  }