diff --git a/README.md b/README.md index a33936ace..a872e29e6 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ OpenWiki is a CLI that writes and maintains a wiki for your codebase or your per **OpenWiki gives you:** - **Agent-written docs** that stay accurate, generated by a [Deep Agents](https://github.com/langchain-ai/deepagentsjs) documentation agent. -- **Two modes:** a `code` wiki for a repository, or a `personal` wiki for your own knowledge. +- **Three modes:** a `code` wiki for a repository, a `company` brain for organizational knowledge, or a `personal` wiki for your own knowledge. - **Thirteen model providers** out of the box, from OpenAI and Anthropic to Bedrock, Gemini, and any OpenAI-compatible gateway. - **Coding-agent integrations** for Codex and Claude Code, using the host's model and repository tools. - **Grounded Claims** that track material facts back to versioned source evidence and surface them when the evidence changes. @@ -102,14 +102,15 @@ Before an update, OpenWiki checks those evidence versions. If source lines chang Grounded Claims currently apply to repository code wikis and repository evidence. Connector-derived facts, including LangSmith-only observations, are not claimed. -## Two modes +## Three modes -OpenWiki runs in one of two modes. Bare `openwiki`, `openwiki --init`, and `openwiki --update` default to **code** mode; add the `personal` positional (or `--mode personal`) for the personal brain. +Stratiki runs in one of three modes. Bare `stratiki`, `stratiki --init`, and `stratiki --update` default to **code** mode; add the `company` or `personal` positional (or `--mode company` / `--mode personal`) for the company or personal brain. | Mode | Documents | Writes to | Get started | | -------------------- | ---------------------- | ----------------------- | -------------------------- | -| **Code** _(default)_ | The current repository | `openwiki/` in the repo | `openwiki --init` | -| **Personal** | Your connected sources | `~/.openwiki/wiki` | `openwiki personal --init` | +| **Code** _(default)_ | The current repository | `openwiki/` in the repo | `stratiki --init` | +| **Company** | Company knowledge | `~/.stratiki/wiki` | `stratiki company --init` | +| **Personal** | Your connected sources | `~/.openwiki/wiki` | `stratiki personal --init` | By default the CLI stays open after a run so you can send follow-up messages. Add `-p` / `--print` for a one-shot, non-interactive run that prints the final output and exits. `--init` and `--update` auto-exit on success in an interactive terminal, so the same command works one-shot or interactively. diff --git a/package.json b/package.json index 492693efa..ac7962f23 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,7 @@ } }, "devDependencies": { + "@changesets/cli": "^3.0.1", "@eslint/js": "^10.0.1", "@types/better-sqlite3": "^7.6.12", "@types/jsdom": "^28.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 44a7b741d..81fa593dd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -91,6 +91,9 @@ importers: specifier: ^4.4.3 version: 4.4.3 devDependencies: + '@changesets/cli': + specifier: ^3.0.1 + version: 3.0.1 '@eslint/js': specifier: ^10.0.1 version: 10.0.1(eslint@10.6.0) @@ -292,9 +295,78 @@ packages: '@cfworker/json-schema@4.1.1': resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==} + '@changesets/apply-release-plan@8.0.0': + resolution: {integrity: sha512-kUd2pbf1w5/AYmBMb0Tt+rkIPCjFJdT0SZMrkOjJT/WV/QbtmvkyB5jkV0oaNPheavprZk+SfUiozUti7TIL2w==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/assemble-release-plan@7.0.0': + resolution: {integrity: sha512-oEW8BxdA604kGGtDSCiHr5w9Tv4UWe9I2k61IBNZzCOE1kbYaJj4v+lFQNgcEZFkUc2pV/+hASErGDvpJOZCTg==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/changelog-git@1.0.0': + resolution: {integrity: sha512-3Dst2Ime2Op5nd4XmWJLPIgp11ZFqJqSkVug9izK6TDcIV4YlhPS4ECbEVR+eGI0bk0r1ItogD4j2Oli87bJrA==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/cli@3.0.1': + resolution: {integrity: sha512-3IVpRSgiKDj2aRbBHKtWTXSRH2/iR3bWxau9iQUoEsZjDhRIj9nhl90k7FWMxZAJvLgJBbgMq8+qS0xQsenYsQ==} + engines: {node: ^22.11 || ^24 || >=26, npm: '>=10.9.0', pnpm: '>=10.0.0', yarn: '>=4.5.2'} + hasBin: true + + '@changesets/config@4.0.0': + resolution: {integrity: sha512-mw95/YrkOuhZZxfnVAA4bSXOFUi+KlhzOBTM8C4x777NhUU6HWIl9Z+K+nME+E4PVsv5NQVQwTfiHihAS1A/ow==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/errors@1.0.0': + resolution: {integrity: sha512-ElN/mEzn6zmETgjwf5MclCMa9ef59sAR0lfO8VSYIsiRvbC2FbLB/92EoYw10Sl0kGixxHFiJZUSv7dA+YpR8g==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/format@0.1.2': + resolution: {integrity: sha512-Caez5XtNXCFS/G5bwyav3wuXL0tMxVd2ZGbaumWbzN08tyzO21asCw7JZhNtVsAZDCvDRUzZN+Iit9SyRITSYA==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/get-dependents-graph@3.0.0': + resolution: {integrity: sha512-ji/t5wFA1zREKXRUePE6Qi+Qu2UgxCeSSGQrphezwvQZrp49B7sJ+8+wvM0tA7zPeSxYKCojDy3WWgrl+s+awg==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/git@4.0.0': + resolution: {integrity: sha512-uIEswpPUgzBBqrC0qg13byNaPorzhf05LE2T+gRizEKCXvMsJC6NPJp5iNDSV/gYj4Viqh09UiOF5E7XWeH5lQ==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/parse@1.0.0': + resolution: {integrity: sha512-P0iaMb9p9CRYZiTgAllEIF9AUMQHIy1G72tKlcIqJp61icZSsKQNiOPdxAMZG8m/DvZwt/oz5xEbTpike//dWg==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/pre@3.0.0': + resolution: {integrity: sha512-Zm/6YliV/a2oeWTqHJf6KxLrQwgcK1i/BRDl2m0EKZvbnxV5fG9QRhwJJGshjsZTUTS6dkfURQ2K6aAvgNw/3Q==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/read@1.0.0': + resolution: {integrity: sha512-8TdE2PwG6yArPt5Ozej83Z6iHz1G8BDBKvIEKZ455MccR4K3GNbLR2XqjBxa+5yLFnEd3xAOPXYboBg1pt8stQ==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/should-skip-package@1.0.0': + resolution: {integrity: sha512-pwqoJmbONn1XgXmZXPEExgAaT+HdZLjALFTDgIm+PnS5KeO2nLtzA2/Q+4aMFY14kFMuXKG30MObhvDzWgzDgg==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/types@7.0.0': + resolution: {integrity: sha512-c5GoiQyt3pxiXjrWSNoP8/GRf4kG+VnKzovx1OQM8dYYALlSwgedmkPmJ+ZqGxqwg9D3Bkj85Uo4KLd5BN3A0w==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/write@1.0.1': + resolution: {integrity: sha512-q/ThtP9gcnEP6xlv7LrY26C2mHqdGBti/MmOVngt/M/oIGYkssmQGxPK9WzBNt2juVcH/vml2WQ+ra8LXYOTaA==} + engines: {node: ^22.11 || ^24 || >=26} + '@chevrotain/types@11.1.2': resolution: {integrity: sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==} + '@clack/core@1.4.3': + resolution: {integrity: sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ==} + engines: {node: '>= 20.12.0'} + + '@clack/prompts@1.7.0': + resolution: {integrity: sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==} + engines: {node: '>= 20.12.0'} + '@csstools/color-helpers@6.1.0': resolution: {integrity: sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==} engines: {node: '>=20.19.0'} @@ -673,6 +745,18 @@ packages: peerDependencies: '@langchain/core': ^1.0.0 + '@manypkg/find-root@3.1.0': + resolution: {integrity: sha512-BcSqCyKhBVZ5YkSzOiheMCV41kqAFptW6xGqYSTjkVTl9XQpr+pqHhwgGCOHQtjDCv7Is6EFyA14Sm5GVbVABA==} + engines: {node: '>=20.0.0'} + + '@manypkg/get-packages@3.1.0': + resolution: {integrity: sha512-0TbBVyvPrP7xGYBI/cP8UP+yl/z+HtbTttAD7FMAJgn/kXOTwh5/60TsqP9ZYY710forNfyV0N8P/IE/ujGZJg==} + engines: {node: '>=20.0.0'} + + '@manypkg/tools@2.1.2': + resolution: {integrity: sha512-6QEf6yqFbETdwGITKq57aYoPfX/3K8XFNwsAlx0C1M7o8cb79sv1M3w+tWuWvIcSbNqrLF7OD7YpZMVVz335hQ==} + engines: {node: '>=20.0.0'} + '@mermaid-js/parser@1.2.0': resolution: {integrity: sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==} @@ -708,6 +792,10 @@ packages: '@oxc-project/types@0.139.0': resolution: {integrity: sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==} + '@pnpm/deps.graph-sequencer@1100.0.1': + resolution: {integrity: sha512-pOr5+q1fLYKwFN3LAJuGZEnfXDcQ73zqgDHMtGy+K+uIoUqyY+6MeDCWFwfu+4EFuq76I5EPFofoNAI+Bmmq4A==} + engines: {node: '>=22.13'} + '@posthog/core@1.40.0': resolution: {integrity: sha512-oGDbIwlTquNwdHbEL5ZLEkuW4UFkkEanfx3QAxDgyVbISv+OAA6YGQwrvo0JD3MUJEbJZvyh8XsX+WYDGw9XHw==} @@ -1180,6 +1268,10 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} + cac@7.0.0: + resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} + engines: {node: '>=20.19.0'} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -1662,9 +1754,18 @@ packages: fast-sha256@1.3.0: resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + fast-uri@3.1.5: resolution: {integrity: sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==} + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} @@ -1807,6 +1908,10 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} + human-id@4.2.1: + resolution: {integrity: sha512-zPGsiS+dWoTZtZ4AtpA9Y+BdSFSNWvnouNlWNoUFyAM6xHOHmdCvqO3k8AIbdamCOv4gUFUVNPf6rJFfc4UiJw==} + hasBin: true + iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -1930,6 +2035,9 @@ packages: resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} engines: {node: '>=8'} + jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + jose@6.2.3: resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} @@ -1973,6 +2081,9 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + jwa@2.0.1: resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} @@ -2015,6 +2126,9 @@ packages: ws: optional: true + launch-editor@2.14.1: + resolution: {integrity: sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==} + layout-base@1.0.2: resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} @@ -2498,6 +2612,10 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + shell-quote@1.10.0: + resolution: {integrity: sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==} + engines: {node: '>= 0.4'} + side-channel-list@1.0.1: resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} engines: {node: '>= 0.4'} @@ -2526,6 +2644,9 @@ packages: simple-get@4.0.1: resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + slice-ansi@5.0.0: resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} engines: {node: '>=12'} @@ -3140,8 +3261,124 @@ snapshots: '@cfworker/json-schema@4.1.1': {} + '@changesets/apply-release-plan@8.0.0': + dependencies: + '@changesets/config': 4.0.0 + '@changesets/format': 0.1.2 + '@changesets/git': 4.0.0 + '@changesets/should-skip-package': 1.0.0 + '@changesets/types': 7.0.0 + import-meta-resolve: 4.2.0 + jsonc-parser: 3.3.1 + semver: 7.8.5 + + '@changesets/assemble-release-plan@7.0.0': + dependencies: + '@changesets/errors': 1.0.0 + '@changesets/get-dependents-graph': 3.0.0 + '@changesets/should-skip-package': 1.0.0 + '@changesets/types': 7.0.0 + semver: 7.8.5 + + '@changesets/changelog-git@1.0.0': + dependencies: + '@changesets/types': 7.0.0 + + '@changesets/cli@3.0.1': + dependencies: + '@changesets/apply-release-plan': 8.0.0 + '@changesets/assemble-release-plan': 7.0.0 + '@changesets/changelog-git': 1.0.0 + '@changesets/config': 4.0.0 + '@changesets/errors': 1.0.0 + '@changesets/get-dependents-graph': 3.0.0 + '@changesets/git': 4.0.0 + '@changesets/pre': 3.0.0 + '@changesets/read': 1.0.0 + '@changesets/should-skip-package': 1.0.0 + '@changesets/types': 7.0.0 + '@changesets/write': 1.0.1 + '@clack/prompts': 1.7.0 + '@manypkg/get-packages': 3.1.0 + '@pnpm/deps.graph-sequencer': 1100.0.1 + cac: 7.0.0 + import-meta-resolve: 4.2.0 + launch-editor: 2.14.1 + package-manager-detector: 1.8.0 + semver: 7.8.5 + tinyexec: 1.3.0 + + '@changesets/config@4.0.0': + dependencies: + '@changesets/get-dependents-graph': 3.0.0 + '@changesets/should-skip-package': 1.0.0 + '@changesets/types': 7.0.0 + '@manypkg/get-packages': 3.1.0 + picomatch: 4.0.5 + + '@changesets/errors@1.0.0': {} + + '@changesets/format@0.1.2': + dependencies: + package-manager-detector: 1.8.0 + tinyexec: 1.3.0 + + '@changesets/get-dependents-graph@3.0.0': + dependencies: + '@changesets/types': 7.0.0 + semver: 7.8.5 + + '@changesets/git@4.0.0': + dependencies: + '@changesets/errors': 1.0.0 + '@changesets/types': 7.0.0 + '@manypkg/get-packages': 3.1.0 + picomatch: 4.0.5 + tinyexec: 1.3.0 + + '@changesets/parse@1.0.0': + dependencies: + '@changesets/types': 7.0.0 + yaml: 2.9.0 + + '@changesets/pre@3.0.0': + dependencies: + '@changesets/errors': 1.0.0 + '@changesets/types': 7.0.0 + '@manypkg/get-packages': 3.1.0 + + '@changesets/read@1.0.0': + dependencies: + '@changesets/git': 4.0.0 + '@changesets/parse': 1.0.0 + '@changesets/types': 7.0.0 + + '@changesets/should-skip-package@1.0.0': + dependencies: + '@changesets/types': 7.0.0 + + '@changesets/types@7.0.0': {} + + '@changesets/write@1.0.1': + dependencies: + '@changesets/format': 0.1.2 + '@changesets/types': 7.0.0 + human-id: 4.2.1 + '@chevrotain/types@11.1.2': {} + '@clack/core@1.4.3': + dependencies: + fast-wrap-ansi: 0.2.2 + sisteransi: 1.0.5 + + '@clack/prompts@1.7.0': + dependencies: + '@clack/core': 1.4.3 + fast-string-width: 3.0.2 + fast-wrap-ansi: 0.2.2 + sisteransi: 1.0.5 + '@csstools/color-helpers@6.1.0': {} '@csstools/css-calc@3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': @@ -3450,6 +3687,21 @@ snapshots: '@langchain/core': 1.2.4(openai@6.45.0(@aws-sdk/credential-provider-node@3.972.63)(@smithy/signature-v4@5.6.2)(ws@8.21.0)(zod@4.4.3))(ws@8.21.0) zod: 4.4.3 + '@manypkg/find-root@3.1.0': + dependencies: + '@manypkg/tools': 2.1.2 + + '@manypkg/get-packages@3.1.0': + dependencies: + '@manypkg/find-root': 3.1.0 + '@manypkg/tools': 2.1.2 + + '@manypkg/tools@2.1.2': + dependencies: + jju: 1.4.0 + tinyglobby: 0.2.17 + yaml: 2.9.0 + '@mermaid-js/parser@1.2.0': dependencies: '@chevrotain/types': 11.1.2 @@ -3499,6 +3751,8 @@ snapshots: '@oxc-project/types@0.139.0': {} + '@pnpm/deps.graph-sequencer@1100.0.1': {} + '@posthog/core@1.40.0': dependencies: '@posthog/types': 1.393.0 @@ -4015,6 +4269,8 @@ snapshots: bytes@3.1.2: {} + cac@7.0.0: {} + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -4548,8 +4804,18 @@ snapshots: fast-sha256@1.3.0: {} + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + fast-uri@3.1.5: {} + fast-wrap-ansi@0.2.2: + dependencies: + fast-string-width: 3.0.2 + fastq@1.20.1: dependencies: reusify: 1.1.0 @@ -4707,6 +4973,8 @@ snapshots: transitivePeerDependencies: - supports-color + human-id@4.2.1: {} + iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 @@ -4813,6 +5081,8 @@ snapshots: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 + jju@1.4.0: {} + jose@6.2.3: {} js-tiktoken@1.0.21: @@ -4868,6 +5138,8 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} + jsonc-parser@3.3.1: {} + jwa@2.0.1: dependencies: buffer-equal-constant-time: 1.0.1 @@ -4914,6 +5186,11 @@ snapshots: openai: 6.45.0(@aws-sdk/credential-provider-node@3.972.63)(@smithy/signature-v4@5.6.2)(ws@8.21.0)(zod@4.4.3) ws: 8.21.0 + launch-editor@2.14.1: + dependencies: + picocolors: 1.1.1 + shell-quote: 1.10.0 + layout-base@1.0.2: {} layout-base@2.0.1: {} @@ -5366,6 +5643,8 @@ snapshots: shebang-regex@3.0.0: {} + shell-quote@1.10.0: {} + side-channel-list@1.0.1: dependencies: es-errors: 1.3.0 @@ -5406,6 +5685,8 @@ snapshots: once: 1.4.0 simple-concat: 1.0.1 + sisteransi@1.0.5: {} + slice-ansi@5.0.0: dependencies: ansi-styles: 6.2.3 diff --git a/src/book/lease.ts b/src/book/lease.ts index 8d188ff83..e0269d566 100644 --- a/src/book/lease.ts +++ b/src/book/lease.ts @@ -88,7 +88,10 @@ function isLeaseContents(value: unknown): value is LeaseContents { ); } -async function mkdirAndWrite(filePath: string, contents: LeaseContents): Promise { +async function mkdirAndWrite( + filePath: string, + contents: LeaseContents, +): Promise { await mkdir(path.dirname(filePath), { recursive: true }); await writeFile(filePath, `${JSON.stringify(contents, null, 2)}\n`, "utf8"); } diff --git a/src/book/packet.ts b/src/book/packet.ts index ad306bd51..35376920b 100644 --- a/src/book/packet.ts +++ b/src/book/packet.ts @@ -134,7 +134,9 @@ function toRelativeDisplayPath(wikiDir: string, filePath: string): string { } function extractTitle(raw: string, filePath: string): string { - const frontMatterTitle = raw.match(/^---[\s\S]*?^title:\s*(.+)$/mu)?.[1]?.trim(); + const frontMatterTitle = raw + .match(/^---[\s\S]*?^title:\s*(.+)$/mu)?.[1] + ?.trim(); if (frontMatterTitle !== undefined && frontMatterTitle.length > 0) { return stripQuotes(frontMatterTitle); } diff --git a/src/cli/cli.tsx b/src/cli/cli.tsx index 3e0791331..4d781573e 100644 --- a/src/cli/cli.tsx +++ b/src/cli/cli.tsx @@ -90,7 +90,7 @@ async function runStandardCommand( } else if (command.kind === "cron") { await runCronCommand(command); } else if (command.kind === "book") { - await runBookCommand(command); + await runBookCommand(command, command.mode); } else if (command.kind === "ingest") { await runIngestCommand(command); } else if (command.kind === "visualize") { diff --git a/src/cli/commands.ts b/src/cli/commands.ts index af0baed18..5837322d6 100644 --- a/src/cli/commands.ts +++ b/src/cli/commands.ts @@ -23,7 +23,7 @@ export type HelpRow = { description: string; }; -export type OpenWikiRunMode = "personal" | "code"; +export type OpenWikiRunMode = "personal" | "code" | "company"; type CronTarget = Extract; export type HelpContent = { @@ -147,6 +147,7 @@ export type CliCommand = action: "context" | "init" | "refresh" | "status"; exitCode: 0; force: boolean; + mode: OpenWikiRunMode; name: string | null; query: string | null; } @@ -719,6 +720,7 @@ function parseMcpCommand(argv: string[]): CliCommand { /** * Parses `stratiki book` subcommands: init, status, refresh, context. + * Accepts --mode to specify company mode (defaults to code mode for repo book). */ function parseBookCommand(argv: string[]): CliCommand { const action = argv[0]; @@ -741,7 +743,9 @@ function parseBookCommand(argv: string[]): CliCommand { let force = false; let name: string | null = null; let query: string | null = null; + let mode: OpenWikiRunMode = "code"; let sawName = false; + let sawMode = false; for (let index = 1; index < argv.length; index += 1) { const arg = argv[index]; @@ -781,6 +785,36 @@ function parseBookCommand(argv: string[]): CliCommand { continue; } + if (arg === "--mode" || arg.startsWith("--mode=")) { + if (sawMode) { + return { + kind: "error", + exitCode: 1, + message: "--mode may only be specified once.", + }; + } + const value = + arg === "--mode" ? argv[index + 1] : arg.slice("--mode=".length); + if (!value || value.startsWith("-")) { + return { + kind: "error", + exitCode: 1, + message: "--mode requires a mode (personal, code, or company).", + }; + } + if (!isOpenWikiRunMode(value)) { + return { + kind: "error", + exitCode: 1, + message: `Invalid mode: ${value}. Expected personal, code, or company.`, + }; + } + mode = value; + sawMode = true; + if (arg === "--mode") index += 1; + continue; + } + if (action === "context" && !arg.startsWith("-")) { query = query === null ? arg : `${query} ${arg}`; continue; @@ -795,7 +829,7 @@ function parseBookCommand(argv: string[]): CliCommand { }; } - return { action, exitCode: 0, force, kind: "book", name, query }; + return { action, exitCode: 0, force, kind: "book", mode, name, query }; } /** @@ -912,7 +946,7 @@ function parseRunCommand( return { kind: "error", exitCode: 1, - message: "--mode requires personal or code.", + message: "--mode requires personal, code, or company.", }; } @@ -920,7 +954,7 @@ function parseRunCommand( return { kind: "error", exitCode: 1, - message: `Invalid mode: ${nextArg}. Expected personal or code.`, + message: `Invalid mode: ${nextArg}. Expected personal, code, or company.`, }; } @@ -942,7 +976,7 @@ function parseRunCommand( return { kind: "error", exitCode: 1, - message: `Invalid mode: ${rawMode}. Expected personal or code.`, + message: `Invalid mode: ${rawMode}. Expected personal, code, or company.`, }; } @@ -1112,7 +1146,7 @@ function resolveExplicitMode( function isOpenWikiRunMode( value: string | undefined, ): value is OpenWikiRunMode { - return value === "personal" || value === "code"; + return value === "personal" || value === "code" || value === "company"; } /** @@ -1178,7 +1212,8 @@ export const helpContent: HelpContent = { "stratiki [--init|--update] [message]", "stratiki code [--init|--update] [message]", "stratiki personal [--init|--update] [message]", - "stratiki --mode [--init|--update] [message]", + "stratiki company [--init|--update] [message]", + "stratiki --mode [--init|--update] [message]", "stratiki [--language ] [--init|--update] [message]", "stratiki [--modelId ]", "stratiki [--modelId ] [message]", @@ -1187,7 +1222,7 @@ export const helpContent: HelpContent = { "stratiki auth configure [--force]", "stratiki auth tools ", "stratiki ingest [--scheduled] [--print] [--modelId ]", - "stratiki book init [--name ] [--force]", + "stratiki book init [--name ] [--force] [--mode ]", "stratiki cron list", "stratiki cron pause all", "stratiki cron resume all", @@ -1204,6 +1239,10 @@ export const helpContent: HelpContent = { description: "Run OpenWiki for the current repository, writing docs under repo openwiki/ and using GitHub Actions for recurrence.", }, + { + label: "stratiki company", + description: `Run Stratiki as your company brain over configured sources, writing to the Stratiki home.`, + }, { label: "stratiki personal", description: `Run OpenWiki as your local personal brain over configured sources, writing to ${openWikiLocalWikiDisplayPath}.`, @@ -1233,9 +1272,10 @@ export const helpContent: HelpContent = { "Run ingestion and wiki update runs for one connector, one source instance, or all configured sources.", }, { - label: "stratiki book init [--name ] [--force]", + label: + "stratiki book init [--name ] [--force] [--mode ]", description: - "Seed the workspace System Book manifest (openwiki/book.config.json) with the U1-U7 coverage ontology.", + "Seed the workspace System Book manifest with the U1-U7 coverage ontology. Defaults to code mode (repo openwiki/); use --mode company for Stratiki home.", }, { label: "stratiki cron list", @@ -1294,9 +1334,9 @@ export const helpContent: HelpContent = { "Update existing Stratiki documentation. Defaults to code mode; use personal to update the local personal brain.", }, { - label: "--mode ", + label: "--mode ", description: - "Choose the personal brain (local, over configured sources) or the code brain (repository docs).", + "Choose the personal brain (local, over configured sources), the code brain (repository docs), or the company brain (company knowledge).", }, { label: "-l, --language ", @@ -1350,10 +1390,11 @@ export const helpContent: HelpContent = { examples: [ "stratiki", "stratiki --init", + "stratiki company --init", "stratiki personal --init", "stratiki code --init", "stratiki --update", - "stratiki --update --mode personal", + "stratiki --update --mode company", 'openwiki "What can you do?"', 'openwiki -p "Summarize what OpenWiki can do"', "stratiki --modelId gpt-5.5", diff --git a/src/cli/run-mode.ts b/src/cli/run-mode.ts index 4b0330be5..6494fbb50 100644 --- a/src/cli/run-mode.ts +++ b/src/cli/run-mode.ts @@ -1,5 +1,8 @@ import type { OpenWikiOutputMode } from "../agent/types.js"; -import { openWikiLocalWikiDir } from "../config/openwiki-home.js"; +import { + openWikiLocalWikiDir, + getStratikiCompanyWikiDir, +} from "../config/openwiki-home.js"; import type { CliCommand, OpenWikiRunMode } from "./commands.js"; /** @@ -30,7 +33,8 @@ export function shouldPrintStartupError( /** * Resolves the working directory for a run: the code runtime's cwd in `code` - * mode, otherwise the local wiki directory. + * mode, otherwise the local wiki directory for `personal` or company wiki for + * `company`. * * @param codeRuntimeCwd Working directory used in `code` mode. * @@ -41,7 +45,9 @@ export function getRunModeCwd( mode: OpenWikiRunMode, codeRuntimeCwd = process.cwd(), ): string { - return mode === "code" ? codeRuntimeCwd : openWikiLocalWikiDir; + if (mode === "code") return codeRuntimeCwd; + if (mode === "company") return getStratikiCompanyWikiDir(); + return openWikiLocalWikiDir; } /** diff --git a/src/cli/runners.ts b/src/cli/runners.ts index 6316ede7b..c86d402d0 100644 --- a/src/cli/runners.ts +++ b/src/cli/runners.ts @@ -21,6 +21,8 @@ import { CONNECTOR_IDS, isConnectorId } from "../connectors/registry.js"; import { openWikiBookDbPath, openWikiHomeDir, + getStratikiHomeDir, + getStratikiBookDbPath, } from "../config/openwiki-home.js"; import { resolveConfiguredProvider } from "../config/constants.js"; import { @@ -46,7 +48,7 @@ import { } from "../telemetry/index.js"; import { exportStaticVisualizer } from "../visualize/static-export.js"; import { runVisualizeServer } from "../visualize/server.js"; -import type { CliCommand } from "./commands.js"; +import type { CliCommand, OpenWikiRunMode } from "./commands.js"; import { isDebugMode } from "./debug.js"; import { getAuthFix, getAuthFixSteps } from "./diagnostics/auth-fix.js"; import { getErrorDiagnostics } from "./diagnostics/error-diagnostics.js"; @@ -390,18 +392,25 @@ export function writePrintErrorDiagnostics(error: unknown): void { /** * Dispatches `stratiki book` subcommands: init, status, refresh, context. + * Company mode uses the Stratiki home; other modes default to repo openwiki/. */ export async function runBookCommand( command: Extract, + mode: OpenWikiRunMode = "code", ): Promise { - const bookDir = path.join(process.cwd(), "openwiki"); + const bookDir = + mode === "company" + ? path.join(getStratikiHomeDir(), "openwiki") + : path.join(process.cwd(), "openwiki"); + const bookDbPath = + mode === "company" ? getStratikiBookDbPath() : openWikiBookDbPath; if (command.action === "init") { await initBookManifest(bookDir, command); return; } if (command.action === "status") { - await printBookStatus(bookDir); + await printBookStatus(bookDir, bookDbPath); return; } if (command.action === "context") { @@ -409,7 +418,7 @@ export async function runBookCommand( return; } - await refreshBook(bookDir); + await refreshBook(bookDir, bookDbPath); } async function initBookManifest( @@ -450,9 +459,12 @@ async function initBookManifest( * Prints episode-store counts plus per-source tier/staleness derived from * the workspace manifest. */ -async function printBookStatus(bookDir: string): Promise { +async function printBookStatus( + bookDir: string, + bookDbPath: string, +): Promise { const manifest = await loadManifestOrThrow(bookDir); - const store = await EpisodeStore.open(openWikiBookDbPath); + const store = await EpisodeStore.open(bookDbPath); try { process.stdout.write(`Workspace: ${manifest.name}\n`); @@ -491,7 +503,7 @@ async function printBookStatus(bookDir: string): Promise { * sources under a single-writer lease so concurrent refreshes cannot * interleave wiki edits. */ -async function refreshBook(bookDir: string): Promise { +async function refreshBook(bookDir: string, bookDbPath: string): Promise { const leasePath = path.join(openWikiHomeDir, "refresh.lock"); const lease = BookLease.at(leasePath); const acquired = await lease.acquire(); @@ -507,7 +519,7 @@ async function refreshBook(bookDir: string): Promise { try { const manifest = await loadManifestOrThrow(bookDir); const latestByConnector = new Map(); - const store = await EpisodeStore.open(openWikiBookDbPath); + const store = await EpisodeStore.open(bookDbPath); try { for (const episode of store.listRecent(1000)) { if (!latestByConnector.has(episode.connectorId)) { diff --git a/src/config/openwiki-home.ts b/src/config/openwiki-home.ts index 5849441c9..7a9a24adc 100644 --- a/src/config/openwiki-home.ts +++ b/src/config/openwiki-home.ts @@ -4,6 +4,7 @@ import path from "node:path"; import { restrictDirToCurrentUser } from "../platform/windows-acl.js"; export const OPENWIKI_CONFIG_DIR_ENV_KEY = "OPENWIKI_CONFIG_DIR"; +export const STRATIKI_HOME_ENV_KEY = "STRATIKI_HOME"; export function resolveOpenWikiHomeDir( environment: NodeJS.ProcessEnv = process.env, @@ -36,6 +37,34 @@ export function getOpenWikiHomeDisplayPath( : "~/.openwiki"; } +export function resolveStratikiHomeDir( + environment: NodeJS.ProcessEnv = process.env, +): string { + const configuredDir = environment[STRATIKI_HOME_ENV_KEY]?.trim(); + + if (!configuredDir) { + return path.join(os.homedir(), ".stratiki"); + } + + if (configuredDir === "~") { + return path.join(os.homedir()); + } + + if (configuredDir.startsWith("~/") || configuredDir.startsWith("~\\")) { + return path.resolve(os.homedir(), configuredDir.slice(2)); + } + + return path.resolve(configuredDir); +} + +export function getStratikiHomeDisplayPath( + environment: NodeJS.ProcessEnv = process.env, +): string { + return environment[STRATIKI_HOME_ENV_KEY]?.trim() + ? resolveStratikiHomeDir(environment) + : "~/.stratiki"; +} + export const openWikiHomeDir = resolveOpenWikiHomeDir(); export const openWikiHomeDisplayPath = getOpenWikiHomeDisplayPath(); export const openWikiConnectorsDir = path.join(openWikiHomeDir, "connectors"); @@ -51,6 +80,32 @@ export const openWikiLocalWikiDisplayPath = `${openWikiHomeDisplayPath}/wiki`; export const openWikiSkillsDisplayPath = `${openWikiHomeDisplayPath}/skills`; export const openWikiEnvDisplayPath = `${openWikiHomeDisplayPath}/.env`; +// Stratiki getters - lazy to avoid homedir() during module init +let _stratikiHomeDir: string | undefined; +let _stratikiCompanyWikiDir: string | undefined; +let _stratikiBookDbPath: string | undefined; + +export function getStratikiHomeDir(): string { + if (_stratikiHomeDir === undefined) { + _stratikiHomeDir = resolveStratikiHomeDir(); + } + return _stratikiHomeDir; +} + +export function getStratikiCompanyWikiDir(): string { + if (_stratikiCompanyWikiDir === undefined) { + _stratikiCompanyWikiDir = path.join(getStratikiHomeDir(), "wiki"); + } + return _stratikiCompanyWikiDir; +} + +export function getStratikiBookDbPath(): string { + if (_stratikiBookDbPath === undefined) { + _stratikiBookDbPath = path.join(getStratikiHomeDir(), "book.db"); + } + return _stratikiBookDbPath; +} + export function getConnectorDir(connectorId: string): string { return path.join(openWikiConnectorsDir, connectorId); } @@ -81,6 +136,15 @@ export async function ensureOpenWikiHome(): Promise { await mkdir(openWikiSkillsDir, { recursive: true, mode: 0o700 }); } +export async function ensureStratikiHome(): Promise { + const homeDir = getStratikiHomeDir(); + const wikiDir = getStratikiCompanyWikiDir(); + await mkdir(homeDir, { recursive: true, mode: 0o700 }); + await chmodIfExists(homeDir, 0o700); + await restrictDirToCurrentUser(homeDir); + await mkdir(wikiDir, { recursive: true, mode: 0o700 }); +} + export async function ensureConnectorHome(connectorId: string): Promise { assertSafeConnectorId(connectorId); await ensureOpenWikiHome(); diff --git a/test/book/lease.test.ts b/test/book/lease.test.ts index 0fa781ede..dd56a8744 100644 --- a/test/book/lease.test.ts +++ b/test/book/lease.test.ts @@ -81,7 +81,10 @@ describe("BookLease", () => { // Simulate a foreign holder by writing its lease directly. await writeFile( leasePath, - JSON.stringify({ acquiredAtIso: new Date().toISOString(), owner: "other" }), + JSON.stringify({ + acquiredAtIso: new Date().toISOString(), + owner: "other", + }), "utf8", ); const impostor = BookLease.at(leasePath, "impostor"); diff --git a/test/book/manifest.test.ts b/test/book/manifest.test.ts index 5fd3d9b39..9d52eb0b8 100644 --- a/test/book/manifest.test.ts +++ b/test/book/manifest.test.ts @@ -179,4 +179,42 @@ describe("book init name bound", () => { const command = parseCommand(["book", "init", "--name", "x".repeat(120)]); expect(command.kind).toBe("book"); }); + + test("book init accepts --mode company", async () => { + const { parseCommand } = await import("../../src/cli/commands.js"); + const command = parseCommand(["book", "init", "--mode", "company"]); + expect(command).toMatchObject({ + kind: "book", + action: "init", + mode: "company", + }); + }); + + test("book status defaults to code mode", async () => { + const { parseCommand } = await import("../../src/cli/commands.js"); + const command = parseCommand(["book", "status"]); + expect(command).toMatchObject({ + kind: "book", + action: "status", + mode: "code", + }); + }); + + test("book context accepts query and --mode company", async () => { + const { parseCommand } = await import("../../src/cli/commands.js"); + const command = parseCommand([ + "book", + "context", + "--mode", + "company", + "test", + "query", + ]); + expect(command).toMatchObject({ + kind: "book", + action: "context", + mode: "company", + query: "test query", + }); + }); }); diff --git a/test/book/packet.test.ts b/test/book/packet.test.ts index 34e3262c2..bf59b2994 100644 --- a/test/book/packet.test.ts +++ b/test/book/packet.test.ts @@ -27,8 +27,10 @@ afterEach(async () => { describe("ContextIndex", () => { test("indexes nested markdown and ranks matching pages", async () => { const wikiDir = await createWiki({ - "architecture/overview.md": "---\ntitle: Architecture\n---\nThe deploy pipeline uses blue-green releases.", - "quickstart.md": "# Quickstart\nRun stratiki init to generate the book. The deploy pipeline is documented elsewhere.", + "architecture/overview.md": + "---\ntitle: Architecture\n---\nThe deploy pipeline uses blue-green releases.", + "quickstart.md": + "# Quickstart\nRun stratiki init to generate the book. The deploy pipeline is documented elsewhere.", "claims/notes.md": "Unrelated content about coffee preferences.", }); const index = await ContextIndex.buildFromDirectory(wikiDir); @@ -39,9 +41,13 @@ describe("ContextIndex", () => { expect(entries.length).toBeGreaterThanOrEqual(2); expect(entries.map((entry) => entry.path)).toContain("/quickstart.md"); // Titles come from front matter or the first heading. - const architecture = entries.find((entry) => entry.path === "/architecture/overview.md"); + const architecture = entries.find( + (entry) => entry.path === "/architecture/overview.md", + ); expect(architecture?.title).toBe("Architecture"); - const quickstart = entries.find((entry) => entry.path === "/quickstart.md"); + const quickstart = entries.find( + (entry) => entry.path === "/quickstart.md", + ); expect(quickstart?.title).toBe("Quickstart"); } finally { index.close(); @@ -50,16 +56,17 @@ describe("ContextIndex", () => { test("skips dot-directories like .claims", async () => { const wikiDir = await createWiki({ - ".claims/secret-page.md": "Internal claim metadata should never be searchable.", + ".claims/secret-page.md": + "Internal claim metadata should never be searchable.", "public.md": "# Public page\nVisible knowledge.", }); const index = await ContextIndex.buildFromDirectory(wikiDir); try { expect(index.search("claim metadata")).toEqual([]); - expect(index.search("visible knowledge").map((entry) => entry.path)).toEqual([ - "/public.md", - ]); + expect( + index.search("visible knowledge").map((entry) => entry.path), + ).toEqual(["/public.md"]); } finally { index.close(); } @@ -81,7 +88,11 @@ describe("ContextIndex", () => { describe("renderPacket", () => { test("renders provenance-first markdown", () => { const packet = renderPacket("deploy pipeline", [ - { excerpt: ">>blue-green<< releases", path: "/arch.md", title: "Architecture" }, + { + excerpt: ">>blue-green<< releases", + path: "/arch.md", + title: "Architecture", + }, ]); expect(packet).toContain("# Context packet"); diff --git a/test/book/refresh-planner.test.ts b/test/book/refresh-planner.test.ts index 9d023b8e0..de1a1e5f2 100644 --- a/test/book/refresh-planner.test.ts +++ b/test/book/refresh-planner.test.ts @@ -40,14 +40,20 @@ describe("planRefresh", () => { NOW, ); - expect(due.map((decision) => decision.entry.connectorId)).toEqual(["github"]); - expect(deferred.map((decision) => decision.entry.connectorId)).toEqual(["linear"]); + expect(due.map((decision) => decision.entry.connectorId)).toEqual([ + "github", + ]); + expect(deferred.map((decision) => decision.entry.connectorId)).toEqual([ + "linear", + ]); // 168h window minus 0.5h elapsed rounds up to a whole remaining hour. expect(deferred[0]?.hoursRemaining).toBe(168); }); test("a source past its tier window is due with the elapsed reason", () => { - const oldIngest = new Date(NOW.getTime() - 25 * 60 * 60 * 1000).toISOString(); + const oldIngest = new Date( + NOW.getTime() - 25 * 60 * 60 * 1000, + ).toISOString(); const { due } = planRefresh( { hackernews: "daily" }, new Map([["hackernews", episode(oldIngest, "hackernews")]]), @@ -61,7 +67,11 @@ describe("planRefresh", () => { test("an unparseable last-ingest timestamp defers to due, never silently stalls", () => { const broken = episode("not-a-timestamp"); - const { due, deferred } = planRefresh({ rss: "cold" }, new Map([["rss", broken]]), NOW); + const { due, deferred } = planRefresh( + { rss: "cold" }, + new Map([["rss", broken]]), + NOW, + ); expect(deferred).toEqual([]); expect(due).toHaveLength(1); diff --git a/test/cli/commands.test.ts b/test/cli/commands.test.ts index 6715810d9..f67d51617 100644 --- a/test/cli/commands.test.ts +++ b/test/cli/commands.test.ts @@ -816,7 +816,9 @@ describe("parseCommand — --mode option forms and conflicts", () => { expect(result.kind).toBe("error"); if (result.kind === "error") { - expect(result.message).toMatch(/--mode requires personal or code/u); + expect(result.message).toMatch( + /--mode requires personal, code, or company/u, + ); } });