@tenonhq/dovetail-core 0.0.92 → 0.0.93

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/commander.js CHANGED
@@ -15,6 +15,7 @@ const deleteRecordCommand_1 = require("./deleteRecordCommand");
15
15
  const migrateCommand_1 = require("./migrateCommand");
16
16
  const clickupCommands_1 = require("./clickupCommands");
17
17
  const loginCommand_1 = require("./loginCommand");
18
+ const knowledgeDiffCommand_1 = require("./knowledgeDiffCommand");
18
19
  const yargs_1 = __importDefault(require("yargs"));
19
20
  async function initCommands() {
20
21
  const sharedOptions = {
@@ -461,6 +462,35 @@ async function initCommands() {
461
462
  .demandCommand(1, "Please specify a task subcommand (e.g., clear)");
462
463
  }, function () {
463
464
  /* subcommands handle execution */
465
+ })
466
+ .command(["knowledge-diff"], "Report Dovetail releases this repo has not documented yet (stages pending events for /dovetail-features-sync)", function (cmdArgs) {
467
+ cmdArgs.options({
468
+ ...sharedOptions,
469
+ manifest: {
470
+ type: "string",
471
+ describe: "Path to the Dovetail release manifest (defaults to the bundled one)",
472
+ },
473
+ config: {
474
+ type: "string",
475
+ describe: "Path to the consumer knowledge-sync config.json",
476
+ },
477
+ ledger: {
478
+ type: "string",
479
+ describe: "Path to the documented-state ledger.json",
480
+ },
481
+ out: {
482
+ type: "string",
483
+ describe: "Directory to write pending release-event files",
484
+ },
485
+ json: {
486
+ type: "boolean",
487
+ default: false,
488
+ describe: "Print the diff as JSON instead of writing pending files",
489
+ },
490
+ });
491
+ return cmdArgs;
492
+ }, async function (args) {
493
+ await (0, knowledgeDiffCommand_1.knowledgeDiffCommand)(args);
464
494
  })
465
495
  .help().argv;
466
496
  }
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ // Pure, deterministic core for `dove knowledge-diff`. No fs, no network — unit-testable.
3
+ // Given a Dovetail release manifest (the publish feed) and a consumer repo's
4
+ // documented-state ledger + watch config, returns the release-events the
5
+ // consumer has not documented yet. knowledgeDiffCommand wraps this with I/O to
6
+ // stage pending events for the in-session /dovetail-features-sync skill.
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.semverGt = semverGt;
9
+ exports.eventFilename = eventFilename;
10
+ exports.computeDrift = computeDrift;
11
+ // Numeric semver compare for the x.y.z range Dovetail uses (no prerelease tags).
12
+ function semverGt(a, b) {
13
+ const pa = String(a)
14
+ .split(".")
15
+ .map(function (n) {
16
+ return parseInt(n, 10) || 0;
17
+ });
18
+ const pb = String(b)
19
+ .split(".")
20
+ .map(function (n) {
21
+ return parseInt(n, 10) || 0;
22
+ });
23
+ for (let i = 0; i < 3; i++) {
24
+ const da = pa[i] || 0;
25
+ const db = pb[i] || 0;
26
+ if (da > db)
27
+ return true;
28
+ if (da < db)
29
+ return false;
30
+ }
31
+ return false;
32
+ }
33
+ // Filesystem-safe pending filename: unscoped package name + version.
34
+ function eventFilename(ev) {
35
+ const unscoped = String(ev.package || "").replace(/^@[^/]+\//, "");
36
+ return unscoped + "@" + ev.version + ".json";
37
+ }
38
+ // Events newer than the ledger's documented_version, limited to watch_packages
39
+ // (when set) and excluding ignore_packages. Deterministically sorted so callers
40
+ // and tests get a stable order.
41
+ function computeDrift(manifest, ledger, config) {
42
+ const events = (manifest && manifest.events) || [];
43
+ const watch = (config && config.watch_packages) || [];
44
+ const ignore = (config && config.ignore_packages) || [];
45
+ const ledgerPackages = (ledger && ledger.packages) || {};
46
+ const drift = [];
47
+ for (const ev of events) {
48
+ if (!ev || !ev.package || !ev.version)
49
+ continue;
50
+ if (ignore.indexOf(ev.package) !== -1)
51
+ continue;
52
+ if (watch.length > 0 && watch.indexOf(ev.package) === -1)
53
+ continue;
54
+ const entry = ledgerPackages[ev.package];
55
+ const documented = entry && entry.documented_version;
56
+ if (documented && !semverGt(ev.version, documented))
57
+ continue;
58
+ drift.push(ev);
59
+ }
60
+ drift.sort(function (a, b) {
61
+ return (a.package + a.version).localeCompare(b.package + b.version);
62
+ });
63
+ return drift;
64
+ }
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.knowledgeDiffCommand = knowledgeDiffCommand;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const commands_1 = require("./commands");
40
+ const Logger_1 = require("./Logger");
41
+ const knowledgeDiff_1 = require("./knowledgeDiff");
42
+ function readJson(filePath) {
43
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
44
+ }
45
+ function resolveManifestPath(explicit) {
46
+ if (explicit)
47
+ return path.resolve(explicit);
48
+ // Bundled inside @tenonhq/dovetail-core at publish time (see publish-on-merge.js).
49
+ // __dirname is <pkg>/dist at runtime, so ".." is the installed package root.
50
+ return path.resolve(__dirname, "..", "release-manifest.json");
51
+ }
52
+ // `dove knowledge-diff` — pure, deterministic detector. Compares the Dovetail
53
+ // release manifest against this repo's documented-state ledger and stages the
54
+ // undocumented release-events for the in-session /dovetail-features-sync skill.
55
+ // No network, no LLM — safe to run at install or in CI.
56
+ async function knowledgeDiffCommand(args) {
57
+ (0, commands_1.setLogLevel)(args);
58
+ const cwd = process.cwd();
59
+ const configPath = args.config
60
+ ? path.resolve(args.config)
61
+ : path.join(cwd, "context", "dovetail-releases", "config.json");
62
+ let config = {};
63
+ if (fs.existsSync(configPath)) {
64
+ config = readJson(configPath);
65
+ }
66
+ else {
67
+ Logger_1.logger.warn("knowledge-diff: no config at " + configPath + "; watching all packages.");
68
+ }
69
+ const manifestPath = resolveManifestPath(args.manifest);
70
+ if (!fs.existsSync(manifestPath)) {
71
+ Logger_1.logger.warn("knowledge-diff: no release manifest at " +
72
+ manifestPath +
73
+ " — nothing to diff (Dovetail publish writes it).");
74
+ return;
75
+ }
76
+ const manifest = readJson(manifestPath);
77
+ let ledgerPath;
78
+ if (args.ledger) {
79
+ ledgerPath = path.resolve(args.ledger);
80
+ }
81
+ else if (config.ledger) {
82
+ ledgerPath = path.resolve(cwd, config.ledger);
83
+ }
84
+ else {
85
+ ledgerPath = path.join(cwd, "context", "dovetail-releases", "ledger.json");
86
+ }
87
+ const ledger = fs.existsSync(ledgerPath)
88
+ ? readJson(ledgerPath)
89
+ : { packages: {} };
90
+ const drift = (0, knowledgeDiff_1.computeDrift)(manifest, ledger, config);
91
+ if (args.json) {
92
+ process.stdout.write(JSON.stringify({ count: drift.length, events: drift }, null, 2) + "\n");
93
+ return;
94
+ }
95
+ if (drift.length === 0) {
96
+ Logger_1.logger.info("knowledge-diff: no new Dovetail releases to document.");
97
+ return;
98
+ }
99
+ let outDir;
100
+ if (args.out) {
101
+ outDir = path.resolve(args.out);
102
+ }
103
+ else if (config.pending_dir) {
104
+ outDir = path.resolve(cwd, config.pending_dir);
105
+ }
106
+ else {
107
+ outDir = path.join(cwd, "context", "dovetail-releases", "pending");
108
+ }
109
+ if (!fs.existsSync(outDir)) {
110
+ fs.mkdirSync(outDir, { recursive: true });
111
+ }
112
+ for (const ev of drift) {
113
+ const dest = path.join(outDir, (0, knowledgeDiff_1.eventFilename)(ev));
114
+ fs.writeFileSync(dest, JSON.stringify(ev, null, 2) + "\n");
115
+ }
116
+ Logger_1.logger.info("knowledge-diff: wrote " +
117
+ drift.length +
118
+ " pending release-event(s) to " +
119
+ path.relative(cwd, outDir) +
120
+ ". Run /dovetail-features-sync to document them.");
121
+ }
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const knowledgeDiff_1 = require("../knowledgeDiff");
4
+ describe("semverGt", function () {
5
+ it("compares x.y.z numerically (not lexically)", function () {
6
+ expect((0, knowledgeDiff_1.semverGt)("0.0.13", "0.0.12")).toBe(true);
7
+ expect((0, knowledgeDiff_1.semverGt)("0.0.12", "0.0.12")).toBe(false);
8
+ expect((0, knowledgeDiff_1.semverGt)("0.1.0", "0.0.99")).toBe(true);
9
+ expect((0, knowledgeDiff_1.semverGt)("1.0.0", "0.9.9")).toBe(true);
10
+ expect((0, knowledgeDiff_1.semverGt)("0.0.2", "0.0.10")).toBe(false);
11
+ });
12
+ });
13
+ describe("eventFilename", function () {
14
+ it("strips the npm scope and appends the version", function () {
15
+ expect((0, knowledgeDiff_1.eventFilename)({
16
+ event_id: "x",
17
+ package: "@tenonhq/dovetail-clickup",
18
+ version: "0.0.13",
19
+ prev_version: "0.0.12",
20
+ })).toBe("dovetail-clickup@0.0.13.json");
21
+ });
22
+ });
23
+ describe("computeDrift", function () {
24
+ const manifest = {
25
+ events: [
26
+ {
27
+ event_id: "dovetail-clickup@0.0.13",
28
+ package: "@tenonhq/dovetail-clickup",
29
+ version: "0.0.13",
30
+ prev_version: "0.0.12",
31
+ },
32
+ {
33
+ event_id: "dovetail-core@0.0.93",
34
+ package: "@tenonhq/dovetail-core",
35
+ version: "0.0.93",
36
+ prev_version: "0.0.92",
37
+ },
38
+ {
39
+ event_id: "dovetail-gmail@0.0.11",
40
+ package: "@tenonhq/dovetail-gmail",
41
+ version: "0.0.11",
42
+ prev_version: "0.0.10",
43
+ },
44
+ ],
45
+ };
46
+ const ledger = {
47
+ packages: {
48
+ "@tenonhq/dovetail-clickup": { documented_version: "0.0.12" },
49
+ "@tenonhq/dovetail-core": { documented_version: "0.0.93" },
50
+ },
51
+ };
52
+ it("returns events newer than the ledger; undocumented packages count as drift", function () {
53
+ const ids = (0, knowledgeDiff_1.computeDrift)(manifest, ledger, { watch_packages: [] }).map(function (e) {
54
+ return e.event_id;
55
+ });
56
+ expect(ids).toContain("dovetail-clickup@0.0.13"); // 0.0.13 > 0.0.12
57
+ expect(ids).toContain("dovetail-gmail@0.0.11"); // not in ledger yet
58
+ expect(ids).not.toContain("dovetail-core@0.0.93"); // equal to documented
59
+ });
60
+ it("limits to watch_packages when the set is non-empty", function () {
61
+ const drift = (0, knowledgeDiff_1.computeDrift)(manifest, ledger, {
62
+ watch_packages: ["@tenonhq/dovetail-clickup"],
63
+ });
64
+ expect(drift.length).toBe(1);
65
+ expect(drift[0].package).toBe("@tenonhq/dovetail-clickup");
66
+ });
67
+ it("excludes ignore_packages", function () {
68
+ const ids = (0, knowledgeDiff_1.computeDrift)(manifest, ledger, {
69
+ watch_packages: [],
70
+ ignore_packages: ["@tenonhq/dovetail-gmail"],
71
+ }).map(function (e) {
72
+ return e.event_id;
73
+ });
74
+ expect(ids).not.toContain("dovetail-gmail@0.0.11");
75
+ expect(ids).toContain("dovetail-clickup@0.0.13");
76
+ });
77
+ it("is deterministically sorted by package+version", function () {
78
+ const ids = (0, knowledgeDiff_1.computeDrift)(manifest, ledger, {}).map(function (e) {
79
+ return e.event_id;
80
+ });
81
+ expect(ids).toEqual(["dovetail-clickup@0.0.13", "dovetail-gmail@0.0.11"]);
82
+ });
83
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenonhq/dovetail-core",
3
- "version": "0.0.92",
3
+ "version": "0.0.93",
4
4
  "description": "Next-gen file syncer",
5
5
  "license": "GPL-3.0",
6
6
  "main": "./dist/index.js",
@@ -61,7 +61,8 @@
61
61
  "files": [
62
62
  "dist/**/*",
63
63
  "skills/**/*",
64
- "README.md"
64
+ "README.md",
65
+ "release-manifest.json"
65
66
  ],
66
67
  "publishConfig": {
67
68
  "access": "public"
@@ -0,0 +1,5 @@
1
+ {
2
+ "$schema": "dovetail-manifest/v1",
3
+ "generated_at": "2026-05-28T00:00:00Z",
4
+ "events": []
5
+ }