@karmaniverous/jeeves 0.5.9 → 0.5.11

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/index.js CHANGED
@@ -184,14 +184,14 @@ const PLATFORM_COMPONENTS = [
184
184
  * Core library version, inlined at build time.
185
185
  *
186
186
  * @remarks
187
- * The `0.5.8` placeholder is replaced by
187
+ * The `0.5.10` placeholder is replaced by
188
188
  * `@rollup/plugin-replace` during the build with the actual version
189
189
  * from `package.json`. This ensures the correct version survives
190
190
  * when consumers bundle core into their own dist (where runtime
191
191
  * `import.meta.url`-based resolution would find the wrong package.json).
192
192
  */
193
193
  /** The core library version from package.json (inlined at build time). */
194
- const CORE_VERSION = '0.5.8';
194
+ const CORE_VERSION = '0.5.10';
195
195
 
196
196
  /**
197
197
  * Workspace and config root initialization.
@@ -204,12 +204,26 @@ const CORE_VERSION = '0.5.8';
204
204
  * - `{configRoot}/jeeves-{name}/` for each component
205
205
  */
206
206
  let state;
207
+ const WINDOWS_DRIVE_RE = /^[a-zA-Z]:/;
208
+ /**
209
+ * Throw if a path looks like a Windows drive letter on a non-Windows platform.
210
+ *
211
+ * @param label - Human-readable name for the path (used in error messages).
212
+ * @param value - The raw path string to validate.
213
+ */
214
+ function rejectWindowsDrivePath(label, value) {
215
+ if (process.platform !== 'win32' && WINDOWS_DRIVE_RE.test(value)) {
216
+ throw new Error(`jeeves-core: ${label} "${value}" looks like a Windows drive-letter path and will not resolve correctly on this platform.`);
217
+ }
218
+ }
207
219
  /**
208
220
  * Initialize the core library with workspace and config root paths.
209
221
  *
210
222
  * @param options - Workspace and config root paths.
211
223
  */
212
224
  function init(options) {
225
+ rejectWindowsDrivePath('configRoot', options.configRoot);
226
+ rejectWindowsDrivePath('workspacePath', options.workspacePath);
213
227
  state = {
214
228
  workspacePath: options.workspacePath,
215
229
  configRoot: options.configRoot,
@@ -704,6 +718,52 @@ function checkNodeVersion() {
704
718
  }
705
719
  }
706
720
 
721
+ /**
722
+ * @packageDocumentation
723
+ *
724
+ * Deep-walks config objects and replaces `${VAR_NAME}` patterns with environment variable values.
725
+ * Canonical implementation in `@karmaniverous/jeeves` (jeeves-core).
726
+ */
727
+ const ENV_PATTERN = /\$\{([^}]+)\}/g;
728
+ /**
729
+ * Replace `${VAR_NAME}` patterns in a string with `process.env.VAR_NAME`.
730
+ *
731
+ * @param value - The string to process.
732
+ * @returns The string with resolved env vars; unresolvable expressions left untouched.
733
+ */
734
+ function substituteString(value) {
735
+ return value.replace(ENV_PATTERN, (match, varName) => {
736
+ const envValue = process.env[varName];
737
+ if (envValue === undefined)
738
+ return match;
739
+ return envValue;
740
+ });
741
+ }
742
+ /**
743
+ * Deep-walk a value and substitute `${VAR_NAME}` patterns in all string values.
744
+ *
745
+ * @param value - The value to walk (object, array, or primitive).
746
+ * @returns A new value with all env var references resolved.
747
+ */
748
+ function substituteEnvVars(value) {
749
+ if (typeof value === 'string') {
750
+ return substituteString(value);
751
+ }
752
+ if (Array.isArray(value)) {
753
+ return value.map((item) => substituteEnvVars(item));
754
+ }
755
+ if (value !== null &&
756
+ typeof value === 'object' &&
757
+ Object.getPrototypeOf(value) === Object.prototype) {
758
+ const result = {};
759
+ for (const [key, val] of Object.entries(value)) {
760
+ result[key] = substituteEnvVars(val);
761
+ }
762
+ return result;
763
+ }
764
+ return value;
765
+ }
766
+
707
767
  /**
708
768
  * Workspace-level shared configuration: `jeeves.config.json`.
709
769
  *
@@ -913,6 +973,10 @@ function resolveCliConfig(opts) {
913
973
  */
914
974
  function initFromOptions(opts) {
915
975
  const resolved = resolveCliConfig(opts);
976
+ // Validate raw values BEFORE resolve() — on Linux, resolve('j:/config')
977
+ // produces '/cwd/j:/config' which masks the drive-letter pattern.
978
+ rejectWindowsDrivePath('configRoot', resolved.core.configRoot.value);
979
+ rejectWindowsDrivePath('workspacePath', resolved.core.workspace.value);
916
980
  init({
917
981
  workspacePath: resolve(resolved.core.workspace.value),
918
982
  configRoot: resolve(resolved.core.configRoot.value),
@@ -979,31 +1043,31 @@ var hasRequiredExtraTypings;
979
1043
  function requireExtraTypings () {
980
1044
  if (hasRequiredExtraTypings) return extraTypings.exports;
981
1045
  hasRequiredExtraTypings = 1;
982
- (function (module, exports$1) {
1046
+ (function (module, exports) {
983
1047
  const commander = require$$0;
984
1048
 
985
- exports$1 = module.exports = {};
1049
+ exports = module.exports = {};
986
1050
 
987
1051
  // Return a different global program than commander,
988
1052
  // and don't also return it as default export.
989
- exports$1.program = new commander.Command();
1053
+ exports.program = new commander.Command();
990
1054
 
991
1055
  /**
992
1056
  * Expose classes. The FooT versions are just types, so return Commander original implementations!
993
1057
  */
994
1058
 
995
- exports$1.Argument = commander.Argument;
996
- exports$1.Command = commander.Command;
997
- exports$1.CommanderError = commander.CommanderError;
998
- exports$1.Help = commander.Help;
999
- exports$1.InvalidArgumentError = commander.InvalidArgumentError;
1000
- exports$1.InvalidOptionArgumentError = commander.InvalidArgumentError; // Deprecated
1001
- exports$1.Option = commander.Option;
1059
+ exports.Argument = commander.Argument;
1060
+ exports.Command = commander.Command;
1061
+ exports.CommanderError = commander.CommanderError;
1062
+ exports.Help = commander.Help;
1063
+ exports.InvalidArgumentError = commander.InvalidArgumentError;
1064
+ exports.InvalidOptionArgumentError = commander.InvalidArgumentError; // Deprecated
1065
+ exports.Option = commander.Option;
1002
1066
 
1003
- exports$1.createCommand = (name) => new commander.Command(name);
1004
- exports$1.createOption = (flags, description) =>
1067
+ exports.createCommand = (name) => new commander.Command(name);
1068
+ exports.createOption = (flags, description) =>
1005
1069
  new commander.Option(flags, description);
1006
- exports$1.createArgument = (name, description) =>
1070
+ exports.createArgument = (name, description) =>
1007
1071
  new commander.Argument(name, description);
1008
1072
  } (extraTypings, extraTypings.exports));
1009
1073
  return extraTypings.exports;
@@ -1507,7 +1571,158 @@ function buildWithSections(beforeContent, userContent, sections, markers, coreVe
1507
1571
  return parts.join('\n');
1508
1572
  }
1509
1573
 
1510
- var skillContent = `---
1574
+ var codingContent = `---
1575
+ name: coding
1576
+ description: Engineering standards for all code work. Use when writing code, reviewing PRs, spawning coding sub-agents, or making architectural decisions in any project (not just Jeeves). Covers design-first development, schema-first patterns, testing, STAN workflow, dependency management, and pre-PR checklist.
1577
+ ---
1578
+
1579
+ # Engineering Standards
1580
+
1581
+ These standards apply to ALL code work — whether done directly or via sub-agents.
1582
+ When spawning sub-agents for coding tasks, include the relevant rules in the task prompt.
1583
+ Sub-agents don't inherit your context — if you don't pass the rules, they don't exist.
1584
+
1585
+ ---
1586
+
1587
+ ## Design-First Development
1588
+
1589
+ 1. **Iterate on design until convergence** — Summarize requirements, propose approach, raise questions BEFORE writing code.
1590
+ 2. **Services-first architecture** — Core logic in services behind ports; adapters thin; side effects at boundaries.
1591
+ 3. **Schema-first** — Runtime schema (Zod) is source of truth; TypeScript types derived via \`z.infer<>\`; validation centralized. Plain TypeScript \`interface\` declarations for config surfaces are not acceptable.
1592
+ 4. **300 LOC hard limit** — If a file would exceed 300 lines, stop and decompose first. No exceptions.
1593
+ 5. **Avoid \`any\`** — Prefer \`unknown\` + narrowing; if unavoidable, narrowest scope + rationale.
1594
+ 6. **Test pairing** — Every non-trivial module gets a \`*.test.ts\`.
1595
+ 7. **Open-source first** — Prefer established deps over home-grown solutions. Search npm/GitHub before building anything non-trivial.
1596
+
1597
+ ## Module Design
1598
+
1599
+ - **Single Responsibility** applies to modules as well as functions.
1600
+ - Prefer many small modules over a few large ones.
1601
+ - Keep module boundaries explicit and cohesive; avoid "kitchen-sink" files.
1602
+ - Co-locate tests with modules for discoverability.
1603
+
1604
+ ## Config Surfaces
1605
+
1606
+ - Define config with **Zod schemas** — never bare TypeScript interfaces.
1607
+ - Derive types: \`type MyConfig = z.infer<typeof myConfigSchema>\`
1608
+ - Generate **JSON Schema** from Zod for IDE DX (\`\$schema\` pointer in config files).
1609
+ - Validate at load time — fail fast with clear error messages.
1610
+ - \`init\` commands generate config with \`\$schema\` pointer already in place.
1611
+
1612
+ ## Testing
1613
+
1614
+ - **Unit tests** for pure services (no fs/process/network).
1615
+ - **Integration tests** for adapters/seams (minimal end-to-end slices).
1616
+ - Exercise happy paths AND representative error paths.
1617
+ - Table-driven cases encouraged for exhaustive coverage.
1618
+ - Keep coverage meaningful — prefer covering branches/decisions over chasing 100% lines.
1619
+
1620
+ ## STAN-Enabled Repos
1621
+
1622
+ When working in a repo with \`.stan/\`:
1623
+ - Run \`stan run --sequential --no-archive\` **before** each commit. Scripts must pass before you commit. Sequential runs are preferred to limit side effects. Archives are not needed (you won't use them).
1624
+ - **Push after every commit.** Don't accumulate unpushed local commits. Jason needs to be able to see your work at any time.
1625
+ - All scripts must pass before claiming work is complete.
1626
+ - Read \`.stan/output/<script>.txt\` for evidence on failures.
1627
+ - When creating stan scripts, eliminate colorized output where possible (e.g. \`--no-color\`, \`NO_COLOR=1\`) to reduce noise in script output files.
1628
+
1629
+ ## Cross-Package Verification
1630
+
1631
+ When changes affect exports consumed by another repo:
1632
+ - Standalone scripts passing ≠ "ready for review."
1633
+ - Use \`npm link\` or equivalent to verify the consumer builds against your changes.
1634
+ - Only claim completion when BOTH repos pass.
1635
+
1636
+ ## Dependencies: Latest Versions Required (HARD GATE)
1637
+
1638
+ **NEVER use a superseded version of ANY dependency without direct human authorization.** When adding a new dependency — or creating a new project — ALWAYS check the latest stable version and use it. This applies to runtime deps, dev deps, and peer deps alike.
1639
+
1640
+ - Before \`npm install <package>\`: run \`npm view <package> version\` (or check npmjs.com) to confirm you're installing the current major.
1641
+ - Before spawning sub-agents that install packages: include the latest version in the task prompt, or instruct the sub-agent to verify latest before installing.
1642
+ - If the latest major has known breaking issues that block adoption, flag it to the human — don't silently pin an old major.
1643
+
1644
+ LLMs are trained on stale data. Your training cutoff means you will default to old versions of everything. **Assume your version knowledge is wrong** and verify before every install.
1645
+
1646
+ *Earned: 2026-05-12, created the jeeves-tools repo with Zod 3 despite Zod 4 being available since mid-2025. Shipped 84 commits on the old major before catching it.*
1647
+
1648
+ ## Dependencies: Local Over Global
1649
+
1650
+ - **Dev dependencies belong in the project, not the global environment.** Install with \`npm install --save-dev\`, not \`npm install -g\`.
1651
+ - This guarantees reproducibility across machines and CI. Global installs mask environment differences that cause "works on my machine" failures.
1652
+ - **Rare exceptions:** Tools that are genuinely machine-level utilities (e.g. \`stan-cli\`). If in doubt, install locally.
1653
+ ## Dependency Failures
1654
+
1655
+ When a third-party dependency is broken:
1656
+ 1. Summarize the failure concisely.
1657
+ 2. Enumerate options: switch dependency → fix upstream → temporary pin → shim (last resort).
1658
+ 3. Recommend with rationale.
1659
+ 4. Do NOT immediately code around the problem.
1660
+
1661
+ ## CHANGELOG
1662
+
1663
+ - **Do not manually update CHANGELOG.md** — it is generated as part of the release process (e.g. via \`standard-version\`, \`changesets\`, or equivalent). Conventional commit messages are the input; the tooling produces the output.
1664
+
1665
+ ## Pre-PR Checklist (HARD GATE)
1666
+
1667
+ **Before creating ANY PR, run the full verification sequence. No exceptions.**
1668
+
1669
+ 1. \`stan run --sequential --no-archive\` if \`.stan/\` exists — this is the canonical check suite
1670
+ 2. In monorepos: run checks **from each package directory**, not just the root. Root-level runs may mask package-level failures due to config resolution differences.
1671
+ 3. Exercise the release path: check \`release-it\` hooks (or equivalent) in each releasable package — run the same commands (\`lint\`, \`typecheck\`, \`test\`, \`build\`) from the same cwd the release process uses.
1672
+
1673
+ If any step fails, fix it before committing. Do NOT create the PR and "note" the failures. Do NOT claim pre-existing failures without having actually run the commands first. Skipping this sequence is how we ship broken code and fabricate diagnoses.
1674
+
1675
+ - **Compare against canonical template** (\`karmaniverous/npm-package-template-ts\`) before any npm package PR. If the project is behind the template, update it to conform. If the template is behind the project, raise the issue with Jason for template upkeep.
1676
+ - **Run \`ncu --peer\`** before any PR. Review the output. Update safe patches/minors. **Flag major version bumps for discussion** — never auto-apply \`ncu -u\` without reading what changed. Peer dep conflicts must be resolved, not ignored.
1677
+ - When spawning sub-agents, include \`ncu --peer\` in the quality gate commands: \`ncu --peer && npm run lint && npm run typecheck && npm run build && npm test\`. The sub-agent should report \`ncu\` output and only apply updates that don't involve major bumps or peer conflicts.
1678
+ - **Resolve ALL script warnings.** It is NOT acceptable to release code with outstanding warnings. They exist for a reason — fix them.
1679
+ - **Typecheck/lint rules apply to ALL authored code**, including configs at project root (\`eslint.config.ts\`, \`rollup.config.ts\`, \`vitest.config.ts\`, etc.). Only generated code (e.g. typedoc output) should be excepted from code quality checks.
1680
+ - **Never disable lint/typecheck rules** without surfacing it for discussion first. Disabled rules are a major code smell. If a rule must be disabled, document the rationale inline at the point of suppression.
1681
+ - **Multiple tsconfigs are a code smell.** Sometimes needed, but often they paper over poor configuration choices. Fix root causes rather than adding tsconfig variants.
1682
+ - **In a TS repo, all scripts should be authored in TS** (not JS). Prefer execution with \`tsx\`.
1683
+ - **Clean-room verify before claiming "all green."** Run \`rimraf node_modules && npm install && npm run build\` (or equivalent) to catch issues masked by cached state. If someone reports an error you can't reproduce, assume your cache is lying — not that they're wrong.
1684
+ - Verify build, test, and lint pass after updates.
1685
+
1686
+ ## Dev Workspace
1687
+
1688
+ - **Clone location:** \`D:\\repos\\{org-or-userid}\\{repo}\` (e.g. \`D:\\repos\\karmaniverous\\jeeves-watcher\`)
1689
+ - D drive is the dev workspace. Do not clone repos elsewhere.
1690
+ - Fresh clones are preferred over copying existing checkouts — \`npm install\` from registry is faster than disk-copying \`node_modules\`.
1691
+ - D drive is NOT indexed by jeeves-watcher. Dev work stays off the archive.
1692
+
1693
+ ## GitHub Auth (HARD GATE)
1694
+
1695
+ - **ALL GitHub operations use \`jgs-jeeves\` auth.** Set \`GH_TOKEN\` before any \`gh\` CLI command:
1696
+ \`\`\`powershell
1697
+ \$env:GH_TOKEN = (Get-Content "J:\\config\\credentials\\github\\jgs-jeeves.token" -Raw).Trim()
1698
+ \`\`\`
1699
+ - Never write to GitHub as \`karmaniverous\` — that's Jason's account.
1700
+ - If \`jgs-jeeves\` lacks permissions, **escalate to Jason** rather than falling back to \`karmaniverous\`.
1701
+
1702
+ ## Issue Hygiene
1703
+
1704
+ - **Always comment rationale when closing an issue without resolution** (duplicate, won't-fix, obsolete). The close action alone doesn't explain why.
1705
+ - Reference the replacement issue/PR when closing as duplicate.
1706
+
1707
+ ## Code Style
1708
+
1709
+ - Prettier is source of truth for formatting.
1710
+ - Keep imports sorted per repo tooling.
1711
+ - Avoid dead code.
1712
+ - TSDoc \`@module\` or \`@packageDocumentation\` on every non-test module.
1713
+ - First 160 chars of module doc should be high-signal: what it does, IO/side effects, traversal hints.
1714
+
1715
+ ## eslint-disable is a HARD GATE
1716
+
1717
+ Never disable lint/typecheck rules without surfacing it for discussion first. Fix the code, don't suppress the warning. For test mocks, use properly typed partial objects (\`Partial<RealType>\`, typed \`MockReply\` interfaces) instead of \`any\`. Tests are code.
1718
+
1719
+ ## Git Merge Policy
1720
+
1721
+ - **No squash merges.** Preserve commit history.
1722
+ - **PR reviewer:** When creating a PR under \`jgs-jeeves\` auth, always add \`karmaniverous\` (Jason) as a reviewer.
1723
+ `;
1724
+
1725
+ var jeevesContent = `---
1511
1726
  name: jeeves
1512
1727
  description: Jeeves platform architecture, data flow, component interaction, scripts repo, and coordination knowledge. Use when making architectural decisions, coordinating across components, checking platform health, managing service lifecycle, or working with the scripts repo.
1513
1728
  ---
@@ -1631,26 +1846,304 @@ When a workspace file exceeds the warning threshold, a \`## {filename}\` alert a
1631
1846
  Each file heading follows the same declined/active lifecycle as component headings. Users can decline alerts by changing the heading to \`## {filename}: declined\` (e.g., \`## AGENTS.md: declined\`).
1632
1847
  `;
1633
1848
 
1849
+ var operationsContent = `---
1850
+ name: operations
1851
+ description: Operational knowledge for a Jeeves installation. Covers date formatting utilities, email pipeline architecture, curation signal protocol, label taxonomy, and data flow patterns. Use when working with date formatting, email scripts, debugging email pipeline issues, or understanding how human email actions are interpreted.
1852
+ ---
1853
+
1854
+ # Operations
1855
+
1856
+ Operational knowledge for the Jeeves platform. Covers email pipeline architecture, curation protocols, and operational conventions.
1857
+
1858
+ ## Date Formatting
1859
+
1860
+ A \`date-fns\` wrapper lives at \`{configRoot}/jeeves-core/scripts/src/lib/dates.ts\`. It provides:
1861
+
1862
+ | Export | Purpose |
1863
+ |--------|---------|
1864
+ | \`dayOfWeek(dateStr)\` | Full weekday name for an ISO date string (e.g. \`'Monday'\`) |
1865
+ | \`formatDate(dateStr, fmt)\` | Format with any date-fns pattern |
1866
+ | \`relativeDays(dateStr, refStr?)\` | Human-friendly relative description (\`'today'\`, \`'tomorrow'\`, \`'3 days ago'\`) |
1867
+ | \`parseISO\` / \`format\` | Re-exported from date-fns for direct use |
1868
+
1869
+ ### Gateway session usage
1870
+
1871
+ From a gateway session, call via \`exec\`:
1872
+
1873
+ \`\`\`
1874
+ node -e "import { dayOfWeek } from './src/lib/dates.js'; console.log(dayOfWeek('2026-05-11'));"
1875
+ \`\`\`
1876
+
1877
+ with \`workdir: {configRoot}/jeeves-core/scripts\`.
1878
+
1879
+ Or use the simpler inline form when the full wrapper isn't needed:
1880
+
1881
+ \`\`\`
1882
+ node -e "import { format, parseISO } from 'date-fns'; console.log(format(parseISO('2026-05-11'), 'EEEE'));"
1883
+ \`\`\`
1884
+
1885
+ with \`workdir: {configRoot}/jeeves-core/scripts\` (so date-fns resolves from \`node_modules\`).
1886
+
1887
+ ### Hard gate
1888
+
1889
+ NEVER state a day of the week without computing it first. LLMs cannot do day-of-week arithmetic reliably.
1890
+
1891
+ ## Email Curation Signal Protocol
1892
+
1893
+ Defines how human email actions in Gmail are interpreted by Jeeves email processes.
1894
+
1895
+ ### Human Signals
1896
+
1897
+ | Signal | Meaning | Action |
1898
+ |--------|---------|--------|
1899
+ | Label added | Human is adjusting Jeeves classification | Update domain process inputs to reflect new classification |
1900
+ | Label removed | Human is adjusting Jeeves classification (removal) | Update domain process inputs to reflect removed classification |
1901
+ | Archived → Inbox | Human wants to keep this email in sight | Add \`watch\` label via update queue |
1902
+ | Starred / Flagged | Elevated attention — email is important in context | Domain processes should weight higher |
1903
+ | Moved to Spam | Confirmed spam — human classified as junk | Learn from classification for future triage |
1904
+ | Removed from Spam | False positive — human rescued from spam | Process as normal email, learn from false positive |
1905
+
1906
+ ### Watch Label
1907
+
1908
+ The \`watch\` label has special semantics:
1909
+ - When present, never auto-archive the email
1910
+ - When a watched email lands in archive (by anyone), remove the watch label
1911
+ - Added automatically when a human moves an archived email back to inbox
1912
+
1913
+ ### Label Taxonomy
1914
+
1915
+ Labels applied by Jeeves processes fall into two categories:
1916
+
1917
+ **Mechanical labels** (applied by domain extractors with high confidence):
1918
+ - \`meeting\` — meeting-related email (invite, notes, transcript)
1919
+ - \`finance\` — financial email (receipt, invoice, billing, statement)
1920
+
1921
+ **Reasoning labels** (applied by Update Email Meta, requiring cross-domain context):
1922
+ - \`project/<name>\` — associated with a known project
1923
+ - \`todo\` — requires action or work from the user
1924
+ - \`reply\` — someone is waiting on a response
1925
+ - \`alert\` — automated notification from a service
1926
+ - \`readme\` — newsletter or subscribed informational content
1927
+
1928
+ ### Labeling Principles
1929
+
1930
+ 1. Label at the earliest point where confidence is high enough
1931
+ 2. Domain extractors label what they know with certainty
1932
+ 3. Update Email Meta labels what requires cross-domain context
1933
+ 4. A thread can have multiple labels
1934
+ 5. Do not re-label threads that already have the label
1935
+ 6. **Prefer false negatives over false positives**
1936
+
1937
+ ### Poll Scope
1938
+
1939
+ Query: \`newer_than:1d in:anywhere\`
1940
+
1941
+ Must include spam and trash to detect human curation signals (e.g., moving to/from spam).
1942
+
1943
+ ## Email Pipeline Architecture
1944
+
1945
+ ### Directory Layout
1946
+
1947
+ \`\`\`
1948
+ {configRoot}/jeeves-core/email-config.json — pipeline configuration (accounts, buckets)
1949
+ {workspace}/../email/threads/{account}/ — canonical email archive (thread.json + per-message JSONs)
1950
+ \`\`\`
1951
+
1952
+ ### Data Flow
1953
+
1954
+ 1. **Poll** (\`email/poll.ts\`) — searches Gmail for recent threads, classifies, enqueues important ones for metadata fetch
1955
+ 2. **Fetch** (\`email/email-fetch.ts\`) — fetches full thread metadata from Gmail, creates/updates \`thread.json\` cache, enqueues for body download
1956
+ 3. **Download** (\`email/download.ts\`) — downloads full message bodies, writes per-message JSONs to \`threads/{account}/{threadId}/\`
1957
+ 4. **Drain Updates** (\`email/drain-updates.ts\`) — applies label changes and other queued updates back to Gmail
1958
+ 5. **Meta synthesis** — jeeves-meta synthesizes email archives into searchable summaries
1959
+
1960
+ ### thread.json (Cache Format)
1961
+
1962
+ Each \`threads/{account}/{threadId}/thread.json\` contains:
1963
+ - \`threadId\`, \`account\`, \`subject\`, \`participants\`
1964
+ - \`messages\` — record of \`{ messageId → { from, to, cc, date, internalDateMs, labels, snippet, attachments } }\`
1965
+ - \`provenance\` — label change history
1966
+ - \`cachedAt\`, \`updatedAt\`
1967
+
1968
+ ### Per-Message JSONs
1969
+
1970
+ Each \`threads/{account}/{threadId}/{messageId}.json\` contains full message data:
1971
+ - \`messageId\`, \`threadId\`, \`account\`, \`subject\`, \`from\`, \`to\`, \`cc\`
1972
+ - \`date\` (RFC 2822), \`internalDateMs\` (epoch ms)
1973
+ - \`labels\`, \`body\`, \`attachments\`, \`downloadedAt\`
1974
+ `;
1975
+
1976
+ var playbooksContent = `---
1977
+ name: playbooks
1978
+ description: >
1979
+ Reusable operational workflow patterns for the Jeeves platform. Use when asked to
1980
+ set up a daily briefing for a person or team, create standing meeting ops (notes +
1981
+ agenda generation), replicate an existing workflow pattern for a new context, or
1982
+ understand how recurring intelligence/ops workflows are structured. Covers the
1983
+ full stack: content directory, TASK files, standing orders, runner jobs, dispatcher
1984
+ scripts, Slack channel integration, and meta synthesis.
1985
+ ---
1986
+
1987
+ # Playbooks
1988
+
1989
+ Proven, replicable operational patterns. Each playbook describes what it does, what
1990
+ infrastructure it needs, and how to instantiate a new instance.
1991
+
1992
+ ## Common Infrastructure
1993
+
1994
+ All playbooks share these building blocks:
1995
+
1996
+ | Component | Purpose |
1997
+ |-----------|---------|
1998
+ | **Content directory** | \`{workspace}/../<silo>/<domain>/\` — stores output files, \`.meta/\`, standing orders |
1999
+ | **TASK file** | Markdown prompt that defines the LLM session's entire job |
2000
+ | **Standing orders** | \`standing-orders.md\` — append-only file for persistent stakeholder preferences |
2001
+ | **Dispatcher script** | TypeScript in \`{configRoot}/jeeves-core/scripts/src/\` — reads TASK, spawns worker |
2002
+ | **Runner job** | jeeves-runner job with cron schedule, timezone, and rrstack |
2003
+ | **Slack channel** | Delivery surface — summary posts, quick-link pins, feedback loop |
2004
+ | **Meta entity** | \`.meta/\` directory seeded so jeeves-meta synthesizes context over time |
2005
+
2006
+ ### Dispatcher Pattern
2007
+
2008
+ All dispatchers use \`taskFileDispatcher\` from \`dispatchers/lib/task-file-dispatcher.ts\`:
2009
+
2010
+ \`\`\`typescript
2011
+ import { taskFileDispatcher } from '../dispatchers/lib/task-file-dispatcher.js';
2012
+
2013
+ taskFileDispatcher({
2014
+ scriptName: '<silo>/<job-name>',
2015
+ jobId: '<runner-job-id>',
2016
+ taskFile: '<path-to-TASK.md>',
2017
+ timeout: 600,
2018
+ injectDateContext: true,
2019
+ dateTimezone: '<IANA timezone>',
2020
+ });
2021
+ \`\`\`
2022
+
2023
+ \`injectDateContext: true\` prepends an authoritative date line so the LLM knows today's date.
2024
+
2025
+ ### Standing Orders Convention
2026
+
2027
+ - Append-only — never modify existing entries
2028
+ - TASK files instruct the LLM to read standing orders at Step 0
2029
+ - TASK files instruct the LLM to append new persistent preferences from channel feedback
2030
+ - Include initial configuration section with participants, timezones, channel rules
2031
+
2032
+ ## Available Playbook Patterns
2033
+
2034
+ | Pattern | Description |
2035
+ |---------|-------------|
2036
+ | **Daily Briefing** | Recurring intelligence or action-item report for a stakeholder |
2037
+ | **Standing Meeting Ops** | Post-meeting notes + next-day agenda generation for a recurring meeting |
2038
+
2039
+ ## Instantiation Checklist
2040
+
2041
+ When creating a new playbook instance:
2042
+
2043
+ 1. Choose the appropriate pattern from the table above
2044
+ 2. Create the content directory with \`.meta/\` and \`standing-orders.md\`
2045
+ 3. Write the TASK file(s) — adapt from an existing instance. Ensure Step 0 reads feedback from the *delivery channel* (where output is posted), not only a DM
2046
+ 4. Write the dispatcher script(s) in \`{configRoot}/jeeves-core/scripts/src/<silo>/\`
2047
+ 5. Register the runner job(s) with appropriate cron, timezone, rrstack
2048
+ 6. Set up the Slack channel — pin a quick-links message if the pattern calls for it
2049
+ 7. Seed \`.meta/\` so meta synthesis begins
2050
+ 8. Test with \`--dry-run\` before going live
2051
+ `;
2052
+
2053
+ var slackBotProvisionerContent = `---
2054
+ name: slack-bot-provisioner
2055
+ description: Provision a new Slack bot identity for Clawdbot on a fresh server. Guides through Slack App creation steps, collects tokens, writes local config/env, and verifies connectivity.
2056
+ ---
2057
+
2058
+ # Slack bot provisioner (per-bot server)
2059
+
2060
+ Use this when you are setting up **a new Clawdbot instance** that should have **its own Slack bot identity** (one Slack App per bot), and you want a repeatable guided setup.
2061
+
2062
+ This skill assumes:
2063
+ - The user is a Slack workspace admin.
2064
+ - Each bot runs on its own server with its own Gateway config.
2065
+
2066
+ ## What can be automated vs not
2067
+
2068
+ **Automated (this skill):**
2069
+ - Create local folders.
2070
+ - Write a \`slack.env\` (bot token, signing secret).
2071
+ - Patch the Clawdbot gateway config to enable Slack for this instance (user approves).
2072
+ - Run a connectivity test (send a message to a channel).
2073
+
2074
+ **Not fully automatable (Slack-side):**
2075
+ - Creating/installing the Slack App and granting scopes (UI/OAuth).
2076
+ - Verifying event subscription URLs (requires public HTTPS endpoint).
2077
+
2078
+ ## Recommended mode
2079
+ Start with **outbound-only** (post messages) and expand to event subscriptions later.
2080
+
2081
+ ## Quick start
2082
+
2083
+ 1) Have the user do the Slack UI steps in \`references/slack-app-checklist.md\`.
2084
+ 2) Run the provisioning script:
2085
+
2086
+ - PowerShell:
2087
+ - \`powershell -NoProfile -ExecutionPolicy Bypass -File scripts/provision.ps1\`
2088
+
2089
+ The script will prompt for:
2090
+ - bot name
2091
+ - Slack bot token (\`xoxb-...\`)
2092
+ - Slack signing secret
2093
+ - (optional) test channel id
2094
+
2095
+ ## Files
2096
+ - Script: \`scripts/provision.ps1\`
2097
+ - Reference checklist: \`references/slack-app-checklist.md\`
2098
+ - Reference scopes: \`references/scopes.md\`
2099
+
2100
+ ## Secrets (best practices)
2101
+ - **Do not** store secrets inside the skill folder (skills are meant to be shareable/publishable).
2102
+ - Store secrets **per-instance** in the Clawdbot runtime directory (recommended):
2103
+ - \`C:\\Users\\Administrator\\.clawdbot\\credentials\\...\`
2104
+ - Prefer environment variables / local credential files loaded by the Gateway/service manager.
2105
+ - Never paste Slack secrets into public chats.
2106
+
2107
+ ## Safety notes
2108
+ - Store secrets per-instance. Do not reuse tokens across bot identities.
2109
+ - Avoid bot-to-bot loops: bots should ignore messages from other bots by default.
2110
+ `;
2111
+
1634
2112
  /**
1635
- * Skill seeding: write the `jeeves` workspace skill unconditionally.
2113
+ * Skill seeding: write all bundled platform skills to the workspace.
1636
2114
  *
1637
2115
  * @remarks
1638
- * The skill file is entirely generated — no user-authored content (Decision 48).
1639
- * Every installer (core CLI and component plugins) writes it unconditionally.
2116
+ * Skill files are entirely generated — no user-authored content (Decision 48).
2117
+ * Every installer (core CLI and component plugins) writes them unconditionally.
1640
2118
  * Content is inlined at build time via `rollup-plugin-md.ts`.
2119
+ *
2120
+ * @module
1641
2121
  */
2122
+ /** Map of skill directory name to inlined content. */
2123
+ const BUNDLED_SKILLS = {
2124
+ jeeves: jeevesContent,
2125
+ coding: codingContent,
2126
+ 'slack-bot-provisioner': slackBotProvisionerContent,
2127
+ operations: operationsContent,
2128
+ playbooks: playbooksContent,
2129
+ };
1642
2130
  /**
1643
- * Seed the jeeves workspace skill file.
2131
+ * Seed all bundled platform skills into the workspace.
2132
+ *
2133
+ * @remarks
2134
+ * Writes each skill to `{workspace}/skills/{name}/SKILL.md`, creating
2135
+ * directories as needed. Overwrites existing content unconditionally.
1644
2136
  *
1645
2137
  * @param workspacePath - Workspace root directory.
1646
2138
  */
1647
- function seedSkill(workspacePath) {
1648
- const skillDir = join(workspacePath, SKILLS_DIR, JEEVES_SKILL_DIR);
1649
- if (!existsSync(skillDir)) {
1650
- mkdirSync(skillDir, { recursive: true });
2139
+ function seedSkills(workspacePath) {
2140
+ for (const [name, content] of Object.entries(BUNDLED_SKILLS)) {
2141
+ const skillDir = join(workspacePath, SKILLS_DIR, name);
2142
+ if (!existsSync(skillDir)) {
2143
+ mkdirSync(skillDir, { recursive: true });
2144
+ }
2145
+ writeFileSync(join(skillDir, 'SKILL.md'), content, 'utf-8');
1651
2146
  }
1652
- const skillPath = join(skillDir, 'SKILL.md');
1653
- writeFileSync(skillPath, skillContent, 'utf-8');
1654
2147
  }
1655
2148
 
1656
2149
  /**
@@ -2876,11 +3369,11 @@ function createPluginCli(options) {
2876
3369
  console.log(' ⚠ Could not write HEARTBEAT entry');
2877
3370
  }
2878
3371
  try {
2879
- seedSkill(ws);
2880
- console.log(' ✓ Jeeves skill seeded');
3372
+ seedSkills(ws);
3373
+ console.log(' ✓ Platform skills seeded');
2881
3374
  }
2882
3375
  catch {
2883
- console.log(' ⚠ Could not seed Jeeves skill');
3376
+ console.log(' ⚠ Could not seed platform skills');
2884
3377
  }
2885
3378
  }
2886
3379
  }
@@ -3681,6 +4174,20 @@ When editing files outside the workspace, use the bridge pattern: copy in → ed
3681
4174
 
3682
4175
  **Cross-channel sends:** Use the \`message\` tool with an explicit \`target\` to send to a different channel or DM.
3683
4176
 
4177
+ ### Slack File Downloads
4178
+
4179
+ To download a Slack-hosted file, first try the \`message\` tool's \`download-file\` action. If that fails, fall back to a direct HTTP fetch using the bot token:
4180
+
4181
+ \`\`\`js
4182
+ fetch(url_private_download, {
4183
+ headers: { Authorization: 'Bearer ' + botToken },
4184
+ });
4185
+ \`\`\`
4186
+
4187
+ The bot token is at \`channels.slack.accounts.default.botToken\` in \`openclaw.json\`.
4188
+
4189
+ Never tell the user a file can't be downloaded until both methods have been tried.
4190
+
3684
4191
  ### Plugin Lifecycle
3685
4192
 
3686
4193
  \`\`\`bash
@@ -4981,8 +5488,26 @@ async function seedContent(options) {
4981
5488
  content: `- ${NOT_INSTALLED_ALERTS[name]}`,
4982
5489
  }));
4983
5490
  await writeHeartbeatSection(heartbeatPath, entries);
4984
- // Seed jeeves workspace skill (Decision 48: overwrite-on-install)
4985
- seedSkill(getWorkspacePath());
5491
+ // Seed all bundled platform skills (Decision 48: overwrite-on-install)
5492
+ seedSkills(getWorkspacePath());
5493
+ }
5494
+
5495
+ /**
5496
+ * Backward-compatible re-export of `seedSkills`.
5497
+ *
5498
+ * @remarks
5499
+ * Delegates to `seedSkills` which seeds all bundled platform skills.
5500
+ * Retained for API compatibility with existing component plugins.
5501
+ *
5502
+ * @module
5503
+ */
5504
+ /**
5505
+ * Seed all bundled platform skills into the workspace.
5506
+ *
5507
+ * @param workspacePath - Workspace root directory.
5508
+ */
5509
+ function seedSkill(workspacePath) {
5510
+ seedSkills(workspacePath);
4986
5511
  }
4987
5512
 
4988
5513
  /**
@@ -5359,4 +5884,4 @@ async function getChannelWorkspace(channelId, token, options) {
5359
5884
  return teamId;
5360
5885
  }
5361
5886
 
5362
- export { AGENTS_MARKERS, CLEANUP_FLAG, COMPONENT_CONFIG_PREFIX, COMPONENT_VERSIONS_FILE, CONFIG_FILE, CORE_CONFIG_DIR, CORE_VERSION, ComponentWriter, DEFAULT_BIND_ADDRESS, DEFAULT_CORE_VERSION, DEFAULT_PORTS, HEARTBEAT_HEADING, JEEVES_SKILL_DIR, MEMORY_HEARTBEAT_NAME, META_PORT, PLATFORM_COMPONENTS, REGISTRY_CACHE_FILE, RUNNER_PORT, SECTION_IDS, SECTION_ORDER, SERVER_PORT, SKILLS_DIR, SOUL_MARKERS, STALENESS_THRESHOLD_MS, STALE_LOCK_MS, TEMPLATES_DIR, TOOLS_MARKERS, VERSION_STAMP_PATTERN, WATCHER_PORT, WORKSPACE_CONFIG_DEFAULTS, WORKSPACE_CONFIG_FILE, WORKSPACE_FILES, analyzeMemory, appendJsonl, atomicWrite, buildEffectiveConfig, buildHeartbeatSection, checkMemoryHealth, checkNodeVersion, checkRegistryVersion, connectionFail, coreConfigSchema, createAsyncContentCache, createComponentWriter, createConfigApplyHandler, createConfigQueryHandler, createGoogleAuth, createPluginCli, createPluginToolset, createServiceCli, createServiceManager, createStatusHandler, ensureDir, fail, fetchJson, fetchWithTimeout, formatBeginMarker, formatEndMarker, generateJsonSchema, generateWorkspaceJsonSchema, getArg, getBindAddress, getChannelWorkspace, getComponentConfigDir, getComponentConfigPath, getConfigRoot, getCoreConfigDir, getCoreConfigFile, getEffectiveServiceName, getErrorMessage, getPackageRoot, getPackageVersion, getServiceState, getServiceUrl, getWorkspacePath, init, isPrime, isTransientError, jaccard, jeevesComponentDescriptorSchema, loadEnvFile, loadWorkspaceConfig, needsCleanup, nowIso, ok, orchestrateHeartbeat, parseArgs, parseHeartbeat, parseManaged, patchConfig, postJson, readComponentVersions, readJson, readJsonl, refreshPlatformContent, registerComponentConfigPath, removeComponentVersion, removeManagedSection, resetInit, resolveConfigPath, resolveConfigValue, resolveOpenClawHome, resolveOptionalPluginSetting, resolvePluginSetting, resolveWorkspacePath, run, runScript, runWithRetry, saveCache, seedContent, seedSkill, shingles, shouldWrite, sleepAsync, sleepMs, updateManagedSection, uuid, withFileLock, workspaceConfigSchema, writeComponentVersion, writeHeartbeatSection, writeJsonAtomic, writeJsonl };
5887
+ export { AGENTS_MARKERS, CLEANUP_FLAG, COMPONENT_CONFIG_PREFIX, COMPONENT_VERSIONS_FILE, CONFIG_FILE, CORE_CONFIG_DIR, CORE_VERSION, ComponentWriter, DEFAULT_BIND_ADDRESS, DEFAULT_CORE_VERSION, DEFAULT_PORTS, HEARTBEAT_HEADING, JEEVES_SKILL_DIR, MEMORY_HEARTBEAT_NAME, META_PORT, PLATFORM_COMPONENTS, REGISTRY_CACHE_FILE, RUNNER_PORT, SECTION_IDS, SECTION_ORDER, SERVER_PORT, SKILLS_DIR, SOUL_MARKERS, STALENESS_THRESHOLD_MS, STALE_LOCK_MS, TEMPLATES_DIR, TOOLS_MARKERS, VERSION_STAMP_PATTERN, WATCHER_PORT, WORKSPACE_CONFIG_DEFAULTS, WORKSPACE_CONFIG_FILE, WORKSPACE_FILES, analyzeMemory, appendJsonl, atomicWrite, buildEffectiveConfig, buildHeartbeatSection, checkMemoryHealth, checkNodeVersion, checkRegistryVersion, connectionFail, coreConfigSchema, createAsyncContentCache, createComponentWriter, createConfigApplyHandler, createConfigQueryHandler, createGoogleAuth, createPluginCli, createPluginToolset, createServiceCli, createServiceManager, createStatusHandler, ensureDir, fail, fetchJson, fetchWithTimeout, formatBeginMarker, formatEndMarker, generateJsonSchema, generateWorkspaceJsonSchema, getArg, getBindAddress, getChannelWorkspace, getComponentConfigDir, getComponentConfigPath, getConfigRoot, getCoreConfigDir, getCoreConfigFile, getEffectiveServiceName, getErrorMessage, getPackageRoot, getPackageVersion, getServiceState, getServiceUrl, getWorkspacePath, init, isPrime, isTransientError, jaccard, jeevesComponentDescriptorSchema, loadEnvFile, loadWorkspaceConfig, needsCleanup, nowIso, ok, orchestrateHeartbeat, parseArgs, parseHeartbeat, parseManaged, patchConfig, postJson, readComponentVersions, readJson, readJsonl, refreshPlatformContent, registerComponentConfigPath, rejectWindowsDrivePath, removeComponentVersion, removeManagedSection, resetInit, resolveConfigPath, resolveConfigValue, resolveOpenClawHome, resolveOptionalPluginSetting, resolvePluginSetting, resolveWorkspacePath, run, runScript, runWithRetry, saveCache, seedContent, seedSkill, seedSkills, shingles, shouldWrite, sleepAsync, sleepMs, substituteEnvVars, updateManagedSection, uuid, withFileLock, workspaceConfigSchema, writeComponentVersion, writeHeartbeatSection, writeJsonAtomic, writeJsonl };