@kitelev/exocortex-cli 15.148.3 → 15.148.5
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 +172 -0
- package/dist/index.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -298,6 +298,171 @@ exocortex command schedule "tasks/feature.md" --date "2025-12-15" --vault ~/vaul
|
|
|
298
298
|
exocortex command set-deadline "tasks/feature.md" --date "2025-12-31" --vault ~/vault
|
|
299
299
|
```
|
|
300
300
|
|
|
301
|
+
### Dynamic Commands (RFC-009)
|
|
302
|
+
|
|
303
|
+
Execute **vault-defined commands** through a single generic interpreter. Any
|
|
304
|
+
`exocmd:Command` asset in the vault — defined entirely in RDF (precondition +
|
|
305
|
+
grounding + bindings) — can be invoked from CLI, Telegram bot, plugin button,
|
|
306
|
+
or `cron`, without writing TypeScript.
|
|
307
|
+
|
|
308
|
+
This is the **killer feature** of Exocortex: a uniform runtime for vault-defined
|
|
309
|
+
commands across plugin / CLI / scripts / Claude sessions. Source of truth is
|
|
310
|
+
the RDF graph in your vault, not hardcoded TS logic. Architectural contract:
|
|
311
|
+
RFC-009 (Dynamic Command System) and the umbrella RFC
|
|
312
|
+
`94e520da-c6f7-48af-944c-51298d68da45` for the production-ready roadmap.
|
|
313
|
+
Working command definitions live in
|
|
314
|
+
[`docs/examples/rfc-009/`](../../docs/examples/rfc-009/).
|
|
315
|
+
|
|
316
|
+
#### Subcommands
|
|
317
|
+
|
|
318
|
+
| Subcommand | Purpose |
|
|
319
|
+
|------------|---------|
|
|
320
|
+
| `dyncommand list` | Discover all `exocmd:Command` assets in vault |
|
|
321
|
+
| `dyncommand show <uid>` | Show full details: precondition, grounding, bindings |
|
|
322
|
+
| `dyncommand exec <uid> --target <path>` | Execute the command on a target asset |
|
|
323
|
+
| `dyncommand validate` | Validate all command definitions; surface duplicate UIDs |
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
# 1. Discover what commands the vault defines
|
|
327
|
+
exocortex dyncommand list --vault ~/vault
|
|
328
|
+
|
|
329
|
+
# 2. Filter to commands applicable to a specific asset
|
|
330
|
+
exocortex dyncommand list --target obsidian://vault/tasks/abc-123.md --vault ~/vault
|
|
331
|
+
|
|
332
|
+
# 3. Inspect a command (precondition SPARQL ASK + grounding)
|
|
333
|
+
exocortex dyncommand show 6e050240-58e9-4695-9dce-d73fc32cc1d7 --vault ~/vault
|
|
334
|
+
|
|
335
|
+
# 4. Execute (precondition is evaluated; non-passing ASK aborts before grounding)
|
|
336
|
+
exocortex dyncommand exec 6e050240-58e9-4695-9dce-d73fc32cc1d7 \
|
|
337
|
+
--target tasks/abc-123.md \
|
|
338
|
+
--vault ~/vault
|
|
339
|
+
|
|
340
|
+
# 5. Preview without writing
|
|
341
|
+
exocortex dyncommand exec <uid> --target tasks/abc-123.md --dry-run --vault ~/vault
|
|
342
|
+
|
|
343
|
+
# 6. Pass userInput to a service_call grounding
|
|
344
|
+
exocortex dyncommand exec <uid> \
|
|
345
|
+
--target daily/2026-05-02.md \
|
|
346
|
+
--input '{"label":"Lunch — vegetable soup"}' \
|
|
347
|
+
--vault ~/vault
|
|
348
|
+
|
|
349
|
+
# 7. Validate vault-wide command definitions
|
|
350
|
+
exocortex dyncommand validate --vault ~/vault --output json
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
**Common options for all subcommands:**
|
|
354
|
+
|
|
355
|
+
- `--vault <path>` — Path to Obsidian vault (default: current directory)
|
|
356
|
+
- `--output <text|json>` — Response format (default: `text`)
|
|
357
|
+
|
|
358
|
+
**Additional options for `exec`:**
|
|
359
|
+
|
|
360
|
+
- `--target <filepath>` — Path to target asset, relative to vault **[required]**
|
|
361
|
+
- `--dry-run` — Evaluate precondition + print grounding plan; do not mutate files
|
|
362
|
+
- `--input <json>` — JSON object forwarded to `service_call` groundings as `userInput`
|
|
363
|
+
|
|
364
|
+
**Exit codes (exec / validate):**
|
|
365
|
+
|
|
366
|
+
- `0` — Success (executed or precondition passed in dry-run)
|
|
367
|
+
- `1` — Operation failed (precondition not satisfied, grounding error)
|
|
368
|
+
- `2` — File not found (target or vault)
|
|
369
|
+
- `3` — Invalid arguments (missing `--target`, malformed `--input`)
|
|
370
|
+
|
|
371
|
+
#### Example 1 — Telegram bot: complete a task by UID
|
|
372
|
+
|
|
373
|
+
A Telegram bot routes the user phrase «закрой задачу abc-123» to a single
|
|
374
|
+
`dyncommand exec` invocation. The bot does **not** know what "complete" means —
|
|
375
|
+
the semantics live entirely in the vault command asset (e.g.
|
|
376
|
+
`ems__Effort_status` transition `Doing → Done` plus `endTimestamp`):
|
|
377
|
+
|
|
378
|
+
```bash
|
|
379
|
+
TASK_UID="abc-123"
|
|
380
|
+
COMPLETE_CMD_UID="6e050240-58e9-4695-9dce-d73fc32cc1d7" # from dyncommand list
|
|
381
|
+
|
|
382
|
+
exocortex dyncommand exec "$COMPLETE_CMD_UID" \
|
|
383
|
+
--target "tasks/${TASK_UID}.md" \
|
|
384
|
+
--vault ~/vault \
|
|
385
|
+
--output json
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
Why this matters: adding a new vault-defined command (e.g. «отправить в backlog»,
|
|
389
|
+
«rollback to ToDo») requires **zero bot code changes** — only a new
|
|
390
|
+
`exocmd:Command` asset in the vault. This is Homoiconicity Q1 in practice
|
|
391
|
+
(see RFC `c78cc5c8-076c-4509-9e22-6f0257c51df4`).
|
|
392
|
+
|
|
393
|
+
#### Example 2 — CLI / Claude session: bulk-apply a command across a class
|
|
394
|
+
|
|
395
|
+
A Claude session needs to mark every `ems__Task` resolved in 2025 as archived.
|
|
396
|
+
Discover the command UID once, then loop over targets selected by SPARQL:
|
|
397
|
+
|
|
398
|
+
```bash
|
|
399
|
+
ARCHIVE_CMD_UID=$(exocortex dyncommand list --vault ~/vault --output json \
|
|
400
|
+
| jq -r '.data[] | select(.label == "Archive Asset") | .uid')
|
|
401
|
+
|
|
402
|
+
exocortex sparql query \
|
|
403
|
+
"PREFIX ems: <https://exocortex.my/ontology/ems#>
|
|
404
|
+
SELECT ?path WHERE {
|
|
405
|
+
?s a ems:Task ;
|
|
406
|
+
ems:Effort_status ems:EffortStatusDone ;
|
|
407
|
+
ems:Effort_endTimestamp ?ts .
|
|
408
|
+
FILTER(STRSTARTS(STR(?ts), '2025'))
|
|
409
|
+
BIND(STRAFTER(STR(?s), 'obsidian://vault/') AS ?path)
|
|
410
|
+
}" \
|
|
411
|
+
--vault ~/vault --format json \
|
|
412
|
+
| jq -r '.results.bindings[].path.value' \
|
|
413
|
+
| while read -r path; do
|
|
414
|
+
exocortex dyncommand exec "$ARCHIVE_CMD_UID" \
|
|
415
|
+
--target "$path" \
|
|
416
|
+
--vault ~/vault \
|
|
417
|
+
--output json
|
|
418
|
+
done
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
The same `ARCHIVE_CMD_UID` works from a UI button (plugin), Telegram message,
|
|
422
|
+
or scheduled job — one definition, many runtimes.
|
|
423
|
+
|
|
424
|
+
#### Example 3 — `cron`: daily Lunch instance from a prototype
|
|
425
|
+
|
|
426
|
+
Production server runs `lunch-tracker.sh` at 13:00 every day. The script
|
|
427
|
+
materializes a fresh `Lunch` instance from a vault prototype using the generic
|
|
428
|
+
`Create Instance` command (a `create_instance` grounding):
|
|
429
|
+
|
|
430
|
+
```bash
|
|
431
|
+
#!/usr/bin/env bash
|
|
432
|
+
# lunch-tracker.sh — cron entry: 0 13 * * * /opt/exocortex/lunch-tracker.sh
|
|
433
|
+
set -euo pipefail
|
|
434
|
+
|
|
435
|
+
VAULT="$HOME/vault"
|
|
436
|
+
DAILY="daily/$(date +%Y-%m-%d).md"
|
|
437
|
+
CREATE_INSTANCE_CMD="<create-instance-command-uid>"
|
|
438
|
+
|
|
439
|
+
npx @kitelev/exocortex-cli dyncommand exec "$CREATE_INSTANCE_CMD" \
|
|
440
|
+
--target "$DAILY" \
|
|
441
|
+
--input "{\"label\":\"Lunch — $(date +%Y-%m-%d)\"}" \
|
|
442
|
+
--vault "$VAULT" \
|
|
443
|
+
--output json
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
If the command, its precondition, or its grounding ever change in the vault,
|
|
447
|
+
the cron job picks up the new behavior automatically — no redeploy.
|
|
448
|
+
|
|
449
|
+
#### Troubleshooting
|
|
450
|
+
|
|
451
|
+
- **`Precondition not satisfied`** — run `dyncommand show <uid>` to read the
|
|
452
|
+
SPARQL ASK; verify with `sparql query` that the target asset matches the
|
|
453
|
+
pattern. Common cause: target IRI mismatch (CLI ↔ plugin parity, RFC-027 in
|
|
454
|
+
progress) or an unset frontmatter property the precondition requires.
|
|
455
|
+
- **`Command with UID "..." not found`** — `dyncommand list` returns 0 on
|
|
456
|
+
UUID-wikilink-only vaults if the discovery index is stale; rebuild the
|
|
457
|
+
vault cache or pass `--target <iri>` to bypass class filtering.
|
|
458
|
+
- **Service `…` not implemented in CLI runtime** — the grounding uses a
|
|
459
|
+
`service_call` that is plugin-only (Phase 1 hard-fails such calls instead
|
|
460
|
+
of silently succeeding). Run from the plugin, or implement the service in
|
|
461
|
+
`@kitelev/exocortex-services`.
|
|
462
|
+
- **`dyncommand show` and `exec` disagree** — almost always a duplicate
|
|
463
|
+
`exo__Asset_uid`; run `dyncommand validate --output json` and grep for
|
|
464
|
+
`duplicateUids`. RCA: `docs/RCA_DYNCOMMAND_SHOW_VS_EXEC.md`.
|
|
465
|
+
|
|
301
466
|
## Workflow Examples
|
|
302
467
|
|
|
303
468
|
### Morning Planning
|
|
@@ -751,6 +916,13 @@ ems__Effort_status: "[[ems__EffortStatusDraft]]"
|
|
|
751
916
|
- `exocortex command schedule` - Set planned start date
|
|
752
917
|
- `exocortex command set-deadline` - Set planned end date
|
|
753
918
|
|
|
919
|
+
**Dynamic Commands (RFC-009):**
|
|
920
|
+
|
|
921
|
+
- `exocortex dyncommand list` - Discover vault-defined `exocmd:Command` assets
|
|
922
|
+
- `exocortex dyncommand show` - Show precondition / grounding / bindings for a command
|
|
923
|
+
- `exocortex dyncommand exec` - Execute vault-defined command on a target asset
|
|
924
|
+
- `exocortex dyncommand validate` - Validate command definitions; flag duplicate UIDs
|
|
925
|
+
|
|
754
926
|
**Batch Operations:**
|
|
755
927
|
|
|
756
928
|
- `exocortex batch` - Execute multiple operations in single invocation
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// @kitelev/exocortex-cli v15.148.
|
|
2
|
+
// @kitelev/exocortex-cli v15.148.5
|
|
3
3
|
// CLI tool for Exocortex knowledge management system - SPARQL queries, task management, and more
|
|
4
4
|
// License: MIT
|
|
5
5
|
|
|
@@ -794,7 +794,7 @@ exo__BacklinksTableBlock_columns:${f==="[]"?" []":f}
|
|
|
794
794
|
--- END PREVIEW ---
|
|
795
795
|
`)}o(xK,"printDryRunReport");function CK(n,e){let r=[`--- MIGRATION ${e.apply?"APPLY":"DRY RUN"} ---`,`Migrated pairs: ${n.pairs.length}`,`Skipped configs: ${n.skipped.length}`];e.apply&&(r.push(`Files written to: ${e.outDir}/ (relative to vault root)`),r.push(`Total files written: ${n.pairs.length*2} (${n.pairs.length} Layout + ${n.pairs.length} Block)`));for(let i of n.skipped)r.push(` SKIPPED: ${i.sourcePath} \u2014 ${i.reason}`);return r.push(`--- END SUMMARY ---
|
|
796
796
|
`),r.join(`
|
|
797
|
-
`)}o(CK,"formatSummary");function IK(n,e){return{mode:e.apply?"apply":"dry-run",outDir:e.outDir,pairsCount:n.pairs.length,skippedCount:n.skipped.length,pairs:n.pairs.map(t=>({sourceUid:t.sourceUid,sourcePath:t.sourcePath,layoutUid:t.layout.uid,blockUid:t.block.uid,warnings:t.warnings})),skipped:n.skipped}}o(IK,"summariseResult");function $D(n){n.addCommand(pO()),n.addCommand(_O()),n.addCommand(SO())}o($D,"addQuerySubcommands");function BD(n){let e=new xe;e.name("exocortex").description("CLI tool for Exocortex knowledge management system").version(n??"15.148.
|
|
797
|
+
`)}o(CK,"formatSummary");function IK(n,e){return{mode:e.apply?"apply":"dry-run",outDir:e.outDir,pairsCount:n.pairs.length,skippedCount:n.skipped.length,pairs:n.pairs.map(t=>({sourceUid:t.sourceUid,sourcePath:t.sourcePath,layoutUid:t.layout.uid,blockUid:t.block.uid,warnings:t.warnings})),skipped:n.skipped}}o(IK,"summariseResult");function $D(n){n.addCommand(pO()),n.addCommand(_O()),n.addCommand(SO())}o($D,"addQuerySubcommands");function BD(n){let e=new xe;e.name("exocortex").description("CLI tool for Exocortex knowledge management system").version(n??"15.148.5");let t=e.command("exoql").description("ExoQL query execution and cache management");$D(t);let r=e.command("sparql").description("(deprecated) Use 'exoql' instead");return $D(r),r.hook("preAction",()=>{console.error('\u26A0\uFE0F "sparql" is deprecated. Use "exoql" instead.')}),e.addCommand(CF()),e.addCommand(PF()),e.addCommand(OF()),e.addCommand(DF()),e.addCommand(NF()),e.addCommand($F()),e.addCommand(qF()),e.addCommand(rD()),e.addCommand(sD()),e.addCommand(uD()),e.addCommand(hD()),e.addCommand(pD()),e.addCommand(gD()),e.addCommand(CD()),e.addCommand(OD()),e.addCommand(jD()),e}o(BD,"createProgram");BD().parse();0&&(module.exports={createProgram});
|
|
798
798
|
/*! Bundled license information:
|
|
799
799
|
|
|
800
800
|
reflect-metadata/Reflect.js:
|
package/package.json
CHANGED