granola-toolkit 0.52.0 → 0.54.0
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/cli.js +128 -3
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2767,6 +2767,7 @@ function defaultGranolaToolkitPersistenceLayout(options = {}) {
|
|
|
2767
2767
|
dataDirectory,
|
|
2768
2768
|
exportJobsFile: join(dataDirectory, "export-jobs.json"),
|
|
2769
2769
|
meetingIndexFile: join(dataDirectory, "meeting-index.json"),
|
|
2770
|
+
pkmTargetsFile: join(dataDirectory, "pkm-targets.json"),
|
|
2770
2771
|
searchIndexFile: join(dataDirectory, "search-index.json"),
|
|
2771
2772
|
sessionFile: join(dataDirectory, "session.json"),
|
|
2772
2773
|
sessionStoreKind: targetPlatform === "darwin" ? "keychain" : "file",
|
|
@@ -3079,7 +3080,7 @@ function normaliseArtefact(value) {
|
|
|
3079
3080
|
updatedAt
|
|
3080
3081
|
};
|
|
3081
3082
|
}
|
|
3082
|
-
function normaliseFile(parsed) {
|
|
3083
|
+
function normaliseFile$1(parsed) {
|
|
3083
3084
|
const record = asRecord(parsed);
|
|
3084
3085
|
if (!record || record.version !== AUTOMATION_ARTEFACTS_VERSION || !Array.isArray(record.artefacts)) return {
|
|
3085
3086
|
artefacts: [],
|
|
@@ -3102,7 +3103,7 @@ var FileAutomationArtefactStore = class {
|
|
|
3102
3103
|
}
|
|
3103
3104
|
async readArtefacts(options = {}) {
|
|
3104
3105
|
try {
|
|
3105
|
-
const filtered = sortArtefacts(normaliseFile(parseJsonString(await readFile(this.filePath, "utf8"))).artefacts).filter((artefact) => {
|
|
3106
|
+
const filtered = sortArtefacts(normaliseFile$1(parseJsonString(await readFile(this.filePath, "utf8"))).artefacts).filter((artefact) => {
|
|
3106
3107
|
if (options.kind && artefact.kind !== options.kind) return false;
|
|
3107
3108
|
if (options.meetingId && artefact.meetingId !== options.meetingId) return false;
|
|
3108
3109
|
if (options.status && artefact.status !== options.status) return false;
|
|
@@ -3757,6 +3758,7 @@ function cloneAction$1(action) {
|
|
|
3757
3758
|
};
|
|
3758
3759
|
case "export-notes":
|
|
3759
3760
|
case "export-transcript": return { ...action };
|
|
3761
|
+
case "pkm-sync": return { ...action };
|
|
3760
3762
|
case "slack-message": return { ...action };
|
|
3761
3763
|
case "webhook": return {
|
|
3762
3764
|
...action,
|
|
@@ -3771,6 +3773,7 @@ function automationActionName(action) {
|
|
|
3771
3773
|
function automationActionTrigger(action) {
|
|
3772
3774
|
switch (action.kind) {
|
|
3773
3775
|
case "command":
|
|
3776
|
+
case "pkm-sync":
|
|
3774
3777
|
case "slack-message":
|
|
3775
3778
|
case "webhook":
|
|
3776
3779
|
case "write-file": return action.trigger ?? "match";
|
|
@@ -3788,6 +3791,7 @@ function enabledAutomationActions(rule, options = {}) {
|
|
|
3788
3791
|
if (options.trigger !== "approval") return true;
|
|
3789
3792
|
switch (action.kind) {
|
|
3790
3793
|
case "command":
|
|
3794
|
+
case "pkm-sync":
|
|
3791
3795
|
case "slack-message":
|
|
3792
3796
|
case "webhook":
|
|
3793
3797
|
case "write-file": return !options.sourceActionId || action.sourceActionId === options.sourceActionId;
|
|
@@ -3809,7 +3813,7 @@ function baseRun(match, rule, action, startedAt, context, options = {}) {
|
|
|
3809
3813
|
matchedAt: match.matchedAt,
|
|
3810
3814
|
meetingId: match.meetingId,
|
|
3811
3815
|
meta: {
|
|
3812
|
-
sourceActionId: action.kind === "command" || action.kind === "slack-message" || action.kind === "webhook" || action.kind === "write-file" ? action.sourceActionId : void 0,
|
|
3816
|
+
sourceActionId: action.kind === "command" || action.kind === "pkm-sync" || action.kind === "slack-message" || action.kind === "webhook" || action.kind === "write-file" ? action.sourceActionId : void 0,
|
|
3813
3817
|
trigger: context.trigger
|
|
3814
3818
|
},
|
|
3815
3819
|
ruleId: rule.id,
|
|
@@ -3934,6 +3938,19 @@ async function executeAutomationAction(match, rule, action, handlers, options =
|
|
|
3934
3938
|
} catch (error) {
|
|
3935
3939
|
return failedRun(run, handlers.nowIso(), error);
|
|
3936
3940
|
}
|
|
3941
|
+
case "pkm-sync": try {
|
|
3942
|
+
const result = await handlers.runPkmSync(match, rule, action, context);
|
|
3943
|
+
return completedRun(run, handlers.nowIso(), {
|
|
3944
|
+
meta: {
|
|
3945
|
+
...run.meta ? structuredClone(run.meta) : {},
|
|
3946
|
+
filePath: result.filePath,
|
|
3947
|
+
targetId: result.targetId
|
|
3948
|
+
},
|
|
3949
|
+
result: `Synced PKM target ${result.targetId} to ${result.filePath}`
|
|
3950
|
+
});
|
|
3951
|
+
} catch (error) {
|
|
3952
|
+
return failedRun(run, handlers.nowIso(), error);
|
|
3953
|
+
}
|
|
3937
3954
|
case "webhook": try {
|
|
3938
3955
|
const result = await handlers.runWebhook(match, rule, action, context);
|
|
3939
3956
|
return completedRun(run, handlers.nowIso(), {
|
|
@@ -4196,6 +4213,7 @@ function cloneAction(action) {
|
|
|
4196
4213
|
};
|
|
4197
4214
|
case "export-notes":
|
|
4198
4215
|
case "export-transcript": return { ...action };
|
|
4216
|
+
case "pkm-sync": return { ...action };
|
|
4199
4217
|
case "slack-message": return { ...action };
|
|
4200
4218
|
case "webhook": return {
|
|
4201
4219
|
...action,
|
|
@@ -4316,6 +4334,19 @@ function parseAction(value, index) {
|
|
|
4316
4334
|
outputDir: typeof record.outputDir === "string" && record.outputDir.trim() ? record.outputDir.trim() : void 0,
|
|
4317
4335
|
scopedOutput: typeof record.scopedOutput === "boolean" ? record.scopedOutput : void 0
|
|
4318
4336
|
};
|
|
4337
|
+
case "pkm-sync": {
|
|
4338
|
+
const targetId = typeof record.targetId === "string" && record.targetId.trim() ? record.targetId.trim() : void 0;
|
|
4339
|
+
if (!id || !targetId) return;
|
|
4340
|
+
return {
|
|
4341
|
+
enabled,
|
|
4342
|
+
id,
|
|
4343
|
+
kind,
|
|
4344
|
+
name,
|
|
4345
|
+
sourceActionId: typeof record.sourceActionId === "string" && record.sourceActionId.trim() ? record.sourceActionId.trim() : void 0,
|
|
4346
|
+
targetId,
|
|
4347
|
+
trigger: parseTrigger(record.trigger)
|
|
4348
|
+
};
|
|
4349
|
+
}
|
|
4319
4350
|
case "slack-message":
|
|
4320
4351
|
if (!id) return;
|
|
4321
4352
|
return {
|
|
@@ -5303,6 +5334,55 @@ function createDefaultMeetingIndexStore() {
|
|
|
5303
5334
|
return new FileMeetingIndexStore();
|
|
5304
5335
|
}
|
|
5305
5336
|
//#endregion
|
|
5337
|
+
//#region src/pkm-targets.ts
|
|
5338
|
+
function cloneTarget(target) {
|
|
5339
|
+
return { ...target };
|
|
5340
|
+
}
|
|
5341
|
+
function normaliseTarget(value) {
|
|
5342
|
+
const record = asRecord(value);
|
|
5343
|
+
if (!record) return;
|
|
5344
|
+
const id = stringValue(record.id).trim();
|
|
5345
|
+
const outputDir = stringValue(record.outputDir).trim();
|
|
5346
|
+
const kind = stringValue(record.kind).trim();
|
|
5347
|
+
if (!id || !outputDir || kind !== "docs-folder" && kind !== "obsidian") return;
|
|
5348
|
+
return {
|
|
5349
|
+
filenameTemplate: stringValue(record.filenameTemplate).trim() || void 0,
|
|
5350
|
+
folderSubdirectories: typeof record.folderSubdirectories === "boolean" ? record.folderSubdirectories : void 0,
|
|
5351
|
+
frontmatter: typeof record.frontmatter === "boolean" ? record.frontmatter : void 0,
|
|
5352
|
+
id,
|
|
5353
|
+
kind,
|
|
5354
|
+
name: stringValue(record.name).trim() || void 0,
|
|
5355
|
+
outputDir
|
|
5356
|
+
};
|
|
5357
|
+
}
|
|
5358
|
+
function normaliseFile(parsed) {
|
|
5359
|
+
const record = asRecord(parsed);
|
|
5360
|
+
if (!record || !Array.isArray(record.targets)) return { targets: [] };
|
|
5361
|
+
return { targets: record.targets.map((target) => normaliseTarget(target)).filter((target) => Boolean(target)) };
|
|
5362
|
+
}
|
|
5363
|
+
var FilePkmTargetStore = class {
|
|
5364
|
+
constructor(filePath = defaultPkmTargetsFilePath()) {
|
|
5365
|
+
this.filePath = filePath;
|
|
5366
|
+
}
|
|
5367
|
+
async readTargets() {
|
|
5368
|
+
try {
|
|
5369
|
+
return normaliseFile(parseJsonString(await readFile(this.filePath, "utf8"))).targets.map((target) => cloneTarget(target));
|
|
5370
|
+
} catch {
|
|
5371
|
+
return [];
|
|
5372
|
+
}
|
|
5373
|
+
}
|
|
5374
|
+
async writeTargets(targets) {
|
|
5375
|
+
await mkdir(dirname(this.filePath), { recursive: true });
|
|
5376
|
+
await writeFile(this.filePath, `${JSON.stringify({ targets }, null, 2)}\n`, "utf8");
|
|
5377
|
+
}
|
|
5378
|
+
};
|
|
5379
|
+
function defaultPkmTargetsFilePath() {
|
|
5380
|
+
return defaultGranolaToolkitPersistenceLayout().pkmTargetsFile;
|
|
5381
|
+
}
|
|
5382
|
+
function createDefaultPkmTargetStore(filePath) {
|
|
5383
|
+
return new FilePkmTargetStore(filePath);
|
|
5384
|
+
}
|
|
5385
|
+
//#endregion
|
|
5306
5386
|
//#region src/sync-state.ts
|
|
5307
5387
|
const SYNC_STATE_VERSION = 1;
|
|
5308
5388
|
const MAX_STORED_CHANGES = 50;
|
|
@@ -6264,6 +6344,7 @@ var GranolaApp = class {
|
|
|
6264
6344
|
};
|
|
6265
6345
|
case "export-notes":
|
|
6266
6346
|
case "export-transcript": return { ...action };
|
|
6347
|
+
case "pkm-sync": return { ...action };
|
|
6267
6348
|
case "slack-message": return { ...action };
|
|
6268
6349
|
case "webhook": return {
|
|
6269
6350
|
...action,
|
|
@@ -6401,6 +6482,7 @@ var GranolaApp = class {
|
|
|
6401
6482
|
runCommand: async (nextMatch, nextRule, nextAction, context) => await this.runAutomationCommand(nextMatch, nextRule, nextAction, context),
|
|
6402
6483
|
runSlackMessage: async (nextMatch, nextRule, nextAction, context) => await this.runAutomationSlackMessage(nextMatch, nextRule, nextAction, context),
|
|
6403
6484
|
runWebhook: async (nextMatch, nextRule, nextAction, context) => await this.runAutomationWebhook(nextMatch, nextRule, nextAction, context),
|
|
6485
|
+
runPkmSync: async (nextMatch, nextRule, nextAction, context) => await this.runAutomationPkmSync(nextMatch, nextRule, nextAction, context),
|
|
6404
6486
|
writeFile: async (nextMatch, nextRule, nextAction, context) => await this.runAutomationWriteFile(nextMatch, nextRule, nextAction, context)
|
|
6405
6487
|
};
|
|
6406
6488
|
}
|
|
@@ -7170,6 +7252,46 @@ var GranolaApp = class {
|
|
|
7170
7252
|
format: action.format ?? "markdown"
|
|
7171
7253
|
};
|
|
7172
7254
|
}
|
|
7255
|
+
async readPkmTargets() {
|
|
7256
|
+
if (!this.deps.pkmTargetStore) return [];
|
|
7257
|
+
return (await this.deps.pkmTargetStore.readTargets()).map((target) => ({ ...target }));
|
|
7258
|
+
}
|
|
7259
|
+
pkmFrontmatterEnabled(target) {
|
|
7260
|
+
return target.frontmatter ?? target.kind === "obsidian";
|
|
7261
|
+
}
|
|
7262
|
+
buildPkmFrontmatter(target, artefact, match) {
|
|
7263
|
+
if (!this.pkmFrontmatterEnabled(target)) return "";
|
|
7264
|
+
return [
|
|
7265
|
+
"---",
|
|
7266
|
+
`title: ${quoteYamlString(artefact.structured.title)}`,
|
|
7267
|
+
`meetingId: ${quoteYamlString(match.meetingId)}`,
|
|
7268
|
+
`artefactId: ${quoteYamlString(artefact.id)}`,
|
|
7269
|
+
`artefactKind: ${quoteYamlString(artefact.kind)}`,
|
|
7270
|
+
`ruleId: ${quoteYamlString(artefact.ruleId)}`,
|
|
7271
|
+
`sourceActionId: ${quoteYamlString(artefact.actionId)}`,
|
|
7272
|
+
`provider: ${quoteYamlString(artefact.provider)}`,
|
|
7273
|
+
`model: ${quoteYamlString(artefact.model)}`,
|
|
7274
|
+
"tags:",
|
|
7275
|
+
...match.tags.map((tag) => ` - ${quoteYamlString(tag)}`),
|
|
7276
|
+
"folders:",
|
|
7277
|
+
...match.folders.map((folder) => ` - ${quoteYamlString(folder.name)}`),
|
|
7278
|
+
"---",
|
|
7279
|
+
""
|
|
7280
|
+
].join("\n");
|
|
7281
|
+
}
|
|
7282
|
+
async runAutomationPkmSync(match, rule, action, context) {
|
|
7283
|
+
if (!context.artefact) throw new Error(`automation PKM sync action ${action.id} requires an artefact`);
|
|
7284
|
+
const target = (await this.readPkmTargets()).find((candidate) => candidate.id === action.targetId);
|
|
7285
|
+
if (!target) throw new Error(`automation PKM target not found: ${action.targetId}`);
|
|
7286
|
+
const meetingTitle = match.title || context.artefact.structured.title;
|
|
7287
|
+
const folderName = match.folders[0]?.name;
|
|
7288
|
+
const filePath = join(target.folderSubdirectories && folderName ? join(target.outputDir, sanitiseFilename(folderName, "folder")) : target.outputDir, sanitiseFilename(target.filenameTemplate?.trim() ? target.filenameTemplate.replaceAll("{{meeting.title}}", meetingTitle).replaceAll("{{artefact.kind}}", context.artefact.kind).replaceAll("{{artefact.title}}", context.artefact.structured.title) : `${sanitiseFilename(`${meetingTitle}-${context.artefact.kind}`)}.md`, "meeting.md"));
|
|
7289
|
+
await writeTextFile(filePath, `${this.buildPkmFrontmatter(target, context.artefact, match)}${context.artefact.structured.markdown.trim()}\n`);
|
|
7290
|
+
return {
|
|
7291
|
+
filePath,
|
|
7292
|
+
targetId: target.id
|
|
7293
|
+
};
|
|
7294
|
+
}
|
|
7173
7295
|
async buildAutomationAgentAttempt(match, rule, action, bundle, harness) {
|
|
7174
7296
|
const harnessCwd = harness?.cwd;
|
|
7175
7297
|
const promptFile = await readOptionalActionFile(action.promptFile, action.cwd ?? harnessCwd);
|
|
@@ -7734,6 +7856,7 @@ async function createGranolaApp(config, options = {}) {
|
|
|
7734
7856
|
const exportJobs = await exportJobStore.readJobs();
|
|
7735
7857
|
const meetingIndexStore = createDefaultMeetingIndexStore();
|
|
7736
7858
|
const meetingIndex = await meetingIndexStore.readIndex();
|
|
7859
|
+
const pkmTargetStore = createDefaultPkmTargetStore(config.automation?.pkmTargetsFile);
|
|
7737
7860
|
const searchIndexStore = createDefaultSearchIndexStore();
|
|
7738
7861
|
const searchIndex = await searchIndexStore.readIndex();
|
|
7739
7862
|
const syncEventStore = createDefaultSyncEventStore();
|
|
@@ -7759,6 +7882,7 @@ async function createGranolaApp(config, options = {}) {
|
|
|
7759
7882
|
meetingIndex,
|
|
7760
7883
|
meetingIndexStore,
|
|
7761
7884
|
now: options.now,
|
|
7885
|
+
pkmTargetStore,
|
|
7762
7886
|
searchIndex,
|
|
7763
7887
|
searchIndexStore,
|
|
7764
7888
|
syncEventStore,
|
|
@@ -7835,6 +7959,7 @@ async function loadConfig(options) {
|
|
|
7835
7959
|
return {
|
|
7836
7960
|
automation: {
|
|
7837
7961
|
artefactsFile: pickString(env.GRANOLA_AUTOMATION_ARTEFACTS_FILE) ?? pickString(configValues["automation-artefacts-file"]) ?? pickString(configValues.automationArtefactsFile) ?? defaultGranolaToolkitPersistenceLayout().automationArtefactsFile,
|
|
7962
|
+
pkmTargetsFile: pickString(env.GRANOLA_PKM_TARGETS_FILE) ?? pickString(configValues["pkm-targets-file"]) ?? pickString(configValues.pkmTargetsFile) ?? defaultGranolaToolkitPersistenceLayout().pkmTargetsFile,
|
|
7838
7963
|
rulesFile: pickString(options.globalFlags.rules) ?? pickString(env.GRANOLA_AUTOMATION_RULES_FILE) ?? pickString(configValues["automation-rules-file"]) ?? pickString(configValues.automationRulesFile) ?? defaultGranolaToolkitPersistenceLayout().automationRulesFile
|
|
7839
7964
|
},
|
|
7840
7965
|
agents: {
|