blacksmith-organization-system 0.4.2 → 1.0.0-dev.3

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.
Files changed (355) hide show
  1. package/.claude-plugin/marketplace.json +17 -0
  2. package/.claude-plugin/mcp.json +8 -0
  3. package/.claude-plugin/plugin.json +16 -0
  4. package/AGENTS.md +114 -0
  5. package/CLAUDE.md +1 -0
  6. package/LICENSE +1 -1
  7. package/README.md +74 -57
  8. package/bin/bos +3 -0
  9. package/hooks/hooks.json +40 -0
  10. package/package.json +82 -22
  11. package/resources/house/HOUSE.md +22 -0
  12. package/resources/house/bloodlines.json +35 -0
  13. package/resources/house/children/deepseeks-of-anubis.json +4 -0
  14. package/resources/house/children/eon.json +7 -0
  15. package/resources/house/children/hermetic-officer.json +1 -0
  16. package/resources/house/children/ifrit.json +1 -0
  17. package/resources/house/children/license-keepers.json +4 -0
  18. package/resources/house/children/sebas.json +10 -0
  19. package/resources/house/children/shakespeare.json +10 -0
  20. package/resources/house/orders/ravensarmy.json +6 -0
  21. package/resources/house/orders/seiaic-ucdi.json +5 -0
  22. package/resources/ravens/skryba.md +19 -0
  23. package/resources/workshop-root/AGENTS.md +92 -0
  24. package/resources/workshop-root/CLAUDE.md +1 -0
  25. package/resources/workshop.default.json +6 -0
  26. package/skills/bos-archive/SKILL.md +28 -0
  27. package/skills/bos-craftbook/SKILL.md +32 -0
  28. package/skills/bos-craftset/SKILL.md +36 -0
  29. package/skills/bos-devarmory/SKILL.md +34 -0
  30. package/skills/bos-exhibit/SKILL.md +28 -0
  31. package/skills/bos-forge/SKILL.md +28 -0
  32. package/skills/bos-house/SKILL.md +52 -0
  33. package/skills/bos-project/SKILL.md +27 -0
  34. package/skills/bos-sarcophag/SKILL.md +26 -0
  35. package/skills/bos-scope/SKILL.md +33 -0
  36. package/skills/bos-sheme/SKILL.md +38 -0
  37. package/skills/bos-throwbox/SKILL.md +27 -0
  38. package/skills/bos-workshop/SKILL.md +65 -0
  39. package/{config/README.md → skills/bos-workshop/assets/.gitkeep} +0 -0
  40. package/skills/bos-workshop/references/bos-codebase.md +116 -0
  41. package/skills/bos-workshop/references/bos-concept.md +121 -0
  42. package/skills/bos-workshop/scripts/find-workshop.sh +30 -0
  43. package/src/agent-hook.mjs +28 -0
  44. package/src/bridge/_index.mjs +24 -0
  45. package/src/bridge/bos-instances-link/_index.mjs +18 -0
  46. package/src/bridge/docker-host/_index.mjs +43 -0
  47. package/src/bridge/docker-host/container.mjs +32 -0
  48. package/src/bridge/docker-host/image.mjs +23 -0
  49. package/src/bridge/docker-host/volume.mjs +12 -0
  50. package/src/bridge/docker-publish/_index.mjs +24 -0
  51. package/src/bridge/git-client/_index.mjs +121 -0
  52. package/src/bridge/git-client/author.mjs +26 -0
  53. package/src/bridge/gitea-api/_index.mjs +62 -0
  54. package/src/bridge/github-api/_index.mjs +45 -0
  55. package/src/bridge/npm-publish/_index.mjs +19 -0
  56. package/src/bridge/ollama-link/_index.mjs +93 -0
  57. package/src/bridge/ollama-link/ravens.mjs +29 -0
  58. package/src/bridge/ssh-link/_index.mjs +33 -0
  59. package/src/bridge/subprocess-runner/_index.mjs +33 -0
  60. package/src/cli.mjs +29 -0
  61. package/src/commands/_index.mjs +51 -0
  62. package/src/commands/bootstrap/exec.mjs +7 -0
  63. package/src/commands/bootstrap/help.md +5 -0
  64. package/src/commands/bootstrap/index.json +7 -0
  65. package/src/commands/close/exec.mjs +4 -0
  66. package/src/commands/close/help.md +5 -0
  67. package/src/commands/close/index.json +7 -0
  68. package/src/commands/describe/exec.mjs +11 -0
  69. package/src/commands/describe/help.md +5 -0
  70. package/src/commands/describe/index.json +7 -0
  71. package/src/commands/help/exec.mjs +6 -0
  72. package/src/commands/help/help.md +3 -0
  73. package/src/commands/help/index.json +6 -0
  74. package/src/commands/hook/exec.mjs +3 -0
  75. package/src/commands/hook/help.md +4 -0
  76. package/src/commands/hook/index.json +7 -0
  77. package/src/commands/hooks/exec.mjs +5 -0
  78. package/src/commands/hooks/help.md +4 -0
  79. package/src/commands/hooks/index.json +7 -0
  80. package/src/commands/house/exec.mjs +8 -0
  81. package/src/commands/house/help.md +8 -0
  82. package/src/commands/house/index.json +7 -0
  83. package/src/commands/locate/exec.mjs +2 -0
  84. package/src/commands/locate/help.md +6 -0
  85. package/src/commands/locate/index.json +6 -0
  86. package/src/commands/mode/exec.mjs +17 -0
  87. package/src/commands/mode/help.md +14 -0
  88. package/src/commands/mode/index.json +8 -0
  89. package/src/commands/mode/repl.mjs +18 -0
  90. package/src/commands/open/exec.mjs +5 -0
  91. package/src/commands/open/help.md +4 -0
  92. package/src/commands/open/index.json +7 -0
  93. package/src/commands/promote/exec.mjs +5 -0
  94. package/src/commands/promote/help.md +6 -0
  95. package/src/commands/promote/index.json +7 -0
  96. package/src/commands/repl/exec.mjs +3 -0
  97. package/src/commands/repl/help.md +4 -0
  98. package/src/commands/repl/index.json +6 -0
  99. package/src/commands/seal/exec.mjs +8 -0
  100. package/src/commands/seal/help.md +4 -0
  101. package/src/commands/seal/index.json +7 -0
  102. package/src/commands/sink/exec.mjs +2 -0
  103. package/src/commands/sink/help.md +6 -0
  104. package/src/commands/sink/index.json +7 -0
  105. package/src/commands/skills/exec.mjs +5 -0
  106. package/src/commands/skills/help.md +5 -0
  107. package/src/commands/skills/index.json +7 -0
  108. package/src/commands/status/exec.mjs +2 -0
  109. package/src/commands/status/help.md +6 -0
  110. package/src/commands/status/index.json +7 -0
  111. package/src/commands/status/repl.mjs +14 -0
  112. package/src/commands/verify/exec.mjs +9 -0
  113. package/src/commands/verify/help.md +4 -0
  114. package/src/commands/verify/index.json +7 -0
  115. package/src/controllers/_index.mjs +5 -0
  116. package/src/controllers/skills-controll.mjs +13 -0
  117. package/src/controllers/workshop-controll.mjs +48 -0
  118. package/src/core/basic-bridge.mjs +29 -0
  119. package/src/core/basic-controll.mjs +26 -0
  120. package/src/core/basic-element.mjs +149 -0
  121. package/src/core/basic-model.mjs +7 -0
  122. package/src/core/basic-procedure.mjs +37 -0
  123. package/src/core/basic-submodule.mjs +21 -0
  124. package/src/core/basic-view.mjs +21 -0
  125. package/src/core/self.mjs +10 -0
  126. package/src/index.mjs +24 -0
  127. package/src/main.ts +82 -0
  128. package/src/mcp.mjs +8 -0
  129. package/src/models/_index.mjs +43 -0
  130. package/src/models/archive/class.mjs +9 -0
  131. package/src/models/craftbook/class.mjs +11 -0
  132. package/src/models/craftbook/hang.scrapnotes.mjs +10 -0
  133. package/src/models/craftset/class.mjs +14 -0
  134. package/src/models/devarmory/class.mjs +10 -0
  135. package/src/models/exhibit/class.mjs +8 -0
  136. package/src/models/forge/class.mjs +19 -0
  137. package/src/models/house/bloodlines.mjs +27 -0
  138. package/src/models/house/class.mjs +53 -0
  139. package/src/models/house/groups.mjs +38 -0
  140. package/src/models/project/anatomy.mjs +11 -0
  141. package/src/models/project/class.mjs +20 -0
  142. package/src/models/sarcophag/class.mjs +8 -0
  143. package/src/models/scope/class.mjs +19 -0
  144. package/src/models/sheme/class.mjs +8 -0
  145. package/src/models/throwbox/class.mjs +9 -0
  146. package/src/models/workshop/class.mjs +30 -0
  147. package/src/models/workshop/hang.data.mjs +10 -0
  148. package/src/models/workshop/hang.nests.mjs +14 -0
  149. package/src/models/workshop/hang.setup.mjs +10 -0
  150. package/src/models/workshop/hang.storylines.logs.mjs +10 -0
  151. package/src/models/workshop/hang.storylines.manifests.mjs +10 -0
  152. package/src/models/workshop/hang.storylines.mjs +39 -0
  153. package/src/models/workshop/hang.storylines.ttystories.mjs +10 -0
  154. package/src/models/workshop/hang.system.mjs +25 -0
  155. package/src/models/workshop/host-roles.mjs +9 -0
  156. package/src/models/workshop/locate.mjs +60 -0
  157. package/src/models/workshop/modes.mjs +31 -0
  158. package/src/models/workshop/taskfile.mjs +21 -0
  159. package/src/procedures/_index.mjs +32 -0
  160. package/src/procedures/agent-recording/_index.mjs +34 -0
  161. package/src/procedures/agent-recording/situate.mjs +11 -0
  162. package/src/procedures/bootstrapping/DESCRIPTION.md +6 -0
  163. package/src/procedures/bootstrapping/_index.mjs +13 -0
  164. package/src/procedures/bootstrapping/deploy-guides.mjs +15 -0
  165. package/src/procedures/bootstrapping/ensure-tree.mjs +4 -0
  166. package/src/procedures/closing/DESCRIPTION.md +6 -0
  167. package/src/procedures/closing/_index.mjs +11 -0
  168. package/src/procedures/closing/capture-ttystory.mjs +14 -0
  169. package/src/procedures/closing/record-closing.mjs +6 -0
  170. package/src/procedures/describing/DESCRIPTION.md +23 -0
  171. package/src/procedures/describing/_index.mjs +15 -0
  172. package/src/procedures/describing/ask-skryba.mjs +82 -0
  173. package/src/procedures/describing/compose-message.mjs +25 -0
  174. package/src/procedures/describing/read-staged.mjs +7 -0
  175. package/src/procedures/describing/scopes.mjs +38 -0
  176. package/src/procedures/describing/staged-diff.mjs +25 -0
  177. package/src/procedures/describing/write-message.mjs +15 -0
  178. package/src/procedures/gathering/DESCRIPTION.md +13 -0
  179. package/src/procedures/gathering/_index.mjs +13 -0
  180. package/src/procedures/gathering/list-served.mjs +9 -0
  181. package/src/procedures/gathering/merge-residents.mjs +31 -0
  182. package/src/procedures/gathering/read-registry.mjs +7 -0
  183. package/src/procedures/hooking/DESCRIPTION.md +6 -0
  184. package/src/procedures/hooking/_index.mjs +12 -0
  185. package/src/procedures/hooking/hooks.mjs +10 -0
  186. package/src/procedures/hooking/install-hooks.mjs +34 -0
  187. package/src/procedures/hooking/record-hook.mjs +13 -0
  188. package/src/procedures/inspecting/DESCRIPTION.md +3 -0
  189. package/src/procedures/inspecting/_index.mjs +9 -0
  190. package/src/procedures/loading/DESCRIPTION.md +4 -0
  191. package/src/procedures/loading/_index.mjs +15 -0
  192. package/src/procedures/loading/load-config.mjs +16 -0
  193. package/src/procedures/loading/validate-mode.mjs +8 -0
  194. package/src/procedures/loading/validate-role.mjs +8 -0
  195. package/src/procedures/locating/DESCRIPTION.md +6 -0
  196. package/src/procedures/locating/_index.mjs +13 -0
  197. package/src/procedures/locating/locate-self.mjs +7 -0
  198. package/src/procedures/locating/locate-workshop.mjs +16 -0
  199. package/src/procedures/locating/read-environment.mjs +12 -0
  200. package/src/procedures/mode-switching/DESCRIPTION.md +7 -0
  201. package/src/procedures/mode-switching/_index.mjs +10 -0
  202. package/src/procedures/mode-switching/record-mode.mjs +8 -0
  203. package/src/procedures/mode-switching/switch-mode.mjs +16 -0
  204. package/src/procedures/opening/DESCRIPTION.md +6 -0
  205. package/src/procedures/opening/_index.mjs +8 -0
  206. package/src/procedures/opening/record-opening.mjs +4 -0
  207. package/src/procedures/promoting/DESCRIPTION.md +8 -0
  208. package/src/procedures/promoting/_index.mjs +14 -0
  209. package/src/procedures/promoting/apply.mjs +28 -0
  210. package/src/procedures/promoting/branch-flow.mjs +23 -0
  211. package/src/procedures/promoting/plan.mjs +14 -0
  212. package/src/procedures/promoting/read-position.mjs +15 -0
  213. package/src/procedures/sinking/DESCRIPTION.md +8 -0
  214. package/src/procedures/sinking/_index.mjs +12 -0
  215. package/src/procedures/sinking/inventory-forge.mjs +18 -0
  216. package/src/procedures/sinking/inventory-tools.mjs +7 -0
  217. package/src/procedures/skilling/DESCRIPTION.md +11 -0
  218. package/src/procedures/skilling/_index.mjs +15 -0
  219. package/src/procedures/skilling/collect-models.mjs +15 -0
  220. package/src/procedures/skilling/compose-skill.mjs +49 -0
  221. package/src/procedures/skilling/glossary.mjs +23 -0
  222. package/src/procedures/skilling/register-skills.mjs +21 -0
  223. package/src/procedures/skilling/write-skills.mjs +19 -0
  224. package/src/views/_index.mjs +23 -0
  225. package/src/views/agent/_index.mjs +2 -0
  226. package/src/views/agent/brief.mjs +34 -0
  227. package/src/views/help/_index.mjs +41 -0
  228. package/src/views/house/_index.mjs +56 -0
  229. package/src/views/inventory/_index.mjs +27 -0
  230. package/src/views/mcp/_index.mjs +31 -0
  231. package/src/views/mcp/tools.mjs +87 -0
  232. package/src/views/mode/_index.mjs +34 -0
  233. package/src/views/mode/chooser.mjs +37 -0
  234. package/src/views/promotion/_index.mjs +20 -0
  235. package/src/views/repl/_index.mjs +30 -0
  236. package/src/views/skills/_index.mjs +24 -0
  237. package/src/views/status/_index.mjs +57 -0
  238. package/src/views/status/tree.mjs +19 -0
  239. package/src/views/terminal/paint.mjs +22 -0
  240. package/template/SKILL.md +16 -0
  241. package/docs/README.md +0 -15
  242. package/docs/devlog.md +0 -20
  243. package/invoke-api.js +0 -0
  244. package/invoke-cli.js +0 -105
  245. package/invoke-desktop.js +0 -0
  246. package/invoke-exec.js +0 -0
  247. package/invoke-webapp.js +0 -0
  248. package/main.js +0 -33
  249. package/shell-procedures/deploy-new-workshop.sh +0 -15
  250. package/shell-procedures/new-project.sh +0 -0
  251. package/shell-procedures/new-sheme.sh +0 -0
  252. package/shell-procedures/new-superproject.sh +0 -6
  253. package/shell-procedures/open-project.sh +0 -0
  254. package/shell-procedures/pack-backup.sh +0 -22
  255. package/shell-procedures/project-to-sarcophag.sh +0 -0
  256. package/shell-procedures/publicate.sh +0 -0
  257. package/shell-procedures/pull-archive.sh +0 -0
  258. package/shell-procedures/purge-workshop.sh +0 -4
  259. package/shell-procedures/push-to-bos-server.sh +0 -0
  260. package/shell-procedures/start-cli-interface.sh +0 -1
  261. package/shell-procedures/sweep-into-throwbox.sh +0 -0
  262. package/shell-procedures/synchronize.sh +0 -7
  263. package/system/bridges/README.md +0 -4
  264. package/system/bridges/main.js +0 -41
  265. package/system/commands/README.md +0 -0
  266. package/system/commands/debug.js +0 -20
  267. package/system/commands/exit.js +0 -19
  268. package/system/core/README.md +0 -0
  269. package/system/core/basic-subject-model.js +0 -200
  270. package/system/core/config-controll.js +0 -51
  271. package/system/core/contain-controll.js +0 -28
  272. package/system/core/event-controll.js +0 -12
  273. package/system/core/extending-mod.js +0 -145
  274. package/system/core/main.js +0 -184
  275. package/system/core/raport-controll.js +0 -10
  276. package/system/core/static-extender.js +0 -19
  277. package/system/factors/README.md +0 -0
  278. package/system/factors/bridge-factory.js +0 -5
  279. package/system/factors/command-factory.js +0 -45
  280. package/system/factors/config-factory.js +0 -6
  281. package/system/factors/controller-factory.js +0 -5
  282. package/system/factors/interface-factory.js +0 -70
  283. package/system/factors/model-factory.js +0 -0
  284. package/system/interfaces/README.md +0 -28
  285. package/system/interfaces/cli/index.js +0 -96
  286. package/system/interfaces/cli/init.js +0 -47
  287. package/system/interfaces/cli/loop.js +0 -7
  288. package/system/interfaces/cli/on-close.js +0 -17
  289. package/system/interfaces/cli/on-line.js +0 -24
  290. package/system/interfaces/cli/on-resume.js +0 -15
  291. package/system/interfaces/cli/setup.js +0 -8
  292. package/system/models/README.md +0 -47
  293. package/system/models/archive/README.md +0 -0
  294. package/system/models/archive/actions/README.md +0 -0
  295. package/system/models/archive/class.js +0 -23
  296. package/system/models/archive/events/README.md +0 -0
  297. package/system/models/archive/index.json +0 -18
  298. package/system/models/archive/listeners/README.md +0 -0
  299. package/system/models/archive/methods/README.md +0 -0
  300. package/system/models/forge/README.md +0 -0
  301. package/system/models/forge/actions/README.md +0 -0
  302. package/system/models/forge/class.js +0 -23
  303. package/system/models/forge/events/README.md +0 -0
  304. package/system/models/forge/index.json +0 -18
  305. package/system/models/forge/listeners/README.md +0 -0
  306. package/system/models/forge/methods/README.md +0 -0
  307. package/system/models/project/README.md +0 -0
  308. package/system/models/project/actions/README.md +0 -0
  309. package/system/models/project/class.js +0 -23
  310. package/system/models/project/events/README.md +0 -0
  311. package/system/models/project/index.json +0 -18
  312. package/system/models/project/listeners/README.md +0 -0
  313. package/system/models/project/methods/README.md +0 -0
  314. package/system/models/sarcophag/README.md +0 -0
  315. package/system/models/sarcophag/actions/README.md +0 -0
  316. package/system/models/sarcophag/class.js +0 -22
  317. package/system/models/sarcophag/events/README.md +0 -0
  318. package/system/models/sarcophag/index.json +0 -18
  319. package/system/models/sarcophag/listeners/README.md +0 -0
  320. package/system/models/sarcophag/methods/README.md +0 -0
  321. package/system/models/sheme/README.md +0 -0
  322. package/system/models/sheme/actions/README.md +0 -0
  323. package/system/models/sheme/class.js +0 -23
  324. package/system/models/sheme/events/README.md +0 -0
  325. package/system/models/sheme/index.json +0 -18
  326. package/system/models/sheme/listeners/README.md +0 -0
  327. package/system/models/sheme/methods/README.md +0 -0
  328. package/system/models/superproject/README.md +0 -0
  329. package/system/models/superproject/actions/README.md +0 -0
  330. package/system/models/superproject/class.js +0 -23
  331. package/system/models/superproject/events/README.md +0 -0
  332. package/system/models/superproject/index.json +0 -18
  333. package/system/models/superproject/listeners/README.md +0 -0
  334. package/system/models/superproject/methods/README.md +0 -0
  335. package/system/models/throwbox/README.md +0 -0
  336. package/system/models/throwbox/actions/README.md +0 -0
  337. package/system/models/throwbox/class.js +0 -23
  338. package/system/models/throwbox/events/README.md +0 -0
  339. package/system/models/throwbox/index.json +0 -18
  340. package/system/models/throwbox/listeners/README.md +0 -0
  341. package/system/models/throwbox/methods/README.md +0 -0
  342. package/system/models/workshop/README.md +0 -0
  343. package/system/models/workshop/actions/README.md +0 -0
  344. package/system/models/workshop/class.js +0 -22
  345. package/system/models/workshop/events/README.md +0 -0
  346. package/system/models/workshop/index.json +0 -18
  347. package/system/models/workshop/listeners/README.md +0 -0
  348. package/system/models/workshop/methods/README.md +0 -0
  349. package/system/resources/textarts/big-logo-title.txt +0 -24
  350. package/system/resources/textarts/big-shortcut-logo-title.txt +0 -10
  351. package/system/resources/textarts/medium-logo-title.txt +0 -13
  352. package/system/resources/textarts/minimenu-header.ptxml +0 -13
  353. package/system/resources/textarts/minimenu-header.txt +0 -11
  354. package/system/resources/textarts/small-logo-title.txt +0 -10
  355. package/system/resources/textarts/wellcome.txt +0 -10
@@ -0,0 +1,62 @@
1
+ // The self-hosted Gitea that carries private scope repositories and pre-public drafts.
2
+ // Address: options.url or $GITEA_URL (e.g. http://gitea.lan:3000); token: options.token or $GITEA_TOKEN.
3
+ import { BOS } from "../../main.ts";
4
+
5
+ export class GiteaApi extends BOS.Bridge {
6
+ static id = "gitea-api";
7
+
8
+ get url() {
9
+ const url = this.options.url ?? process.env.GITEA_URL;
10
+ if (!url) throw new Error("Gitea address unknown: set $GITEA_URL");
11
+ return url.replace(/\/+$/, "");
12
+ }
13
+
14
+ get token() {
15
+ return this.options.token ?? process.env.GITEA_TOKEN;
16
+ }
17
+
18
+ async request(method, path, body) {
19
+ const response = await fetch(`${this.url}/api/v1${path}`, {
20
+ method,
21
+ headers: {
22
+ "content-type": "application/json",
23
+ ...(this.token ? { authorization: `token ${this.token}` } : {}),
24
+ },
25
+ body: body && JSON.stringify(body),
26
+ });
27
+ if (!response.ok) throw new Error(`gitea ${method} ${path}: ${response.status} ${response.statusText}`);
28
+ return response.status === 204 ? null : response.json();
29
+ }
30
+
31
+ async available() {
32
+ try {
33
+ await this.request("GET", "/version");
34
+ return true;
35
+ } catch {
36
+ return false;
37
+ }
38
+ }
39
+
40
+ async me() {
41
+ return (await this.request("GET", "/user")).login;
42
+ }
43
+
44
+ async repos() {
45
+ return (await this.request("GET", "/user/repos")).map((r) => ({
46
+ name: r.full_name,
47
+ private: r.private,
48
+ url: r.clone_url,
49
+ }));
50
+ }
51
+
52
+ // scopes are private by default
53
+ createRepo(name, { private: isPrivate = true, description = "" } = {}) {
54
+ return this.request("POST", "/user/repos", { name, private: isPrivate, description, default_branch: "master" });
55
+ }
56
+
57
+ cloneUrl(owner, repo) {
58
+ return `${this.url}/${owner}/${repo}.git`;
59
+ }
60
+ }
61
+
62
+ export default GiteaApi;
@@ -0,0 +1,45 @@
1
+ // GitHub through octokit. Token: options.token, $GITHUB_TOKEN or $GH_TOKEN.
2
+ import { Octokit } from "octokit";
3
+
4
+ import { BOS } from "../../main.ts";
5
+
6
+ export class GithubApi extends BOS.Bridge {
7
+ static id = "github-api";
8
+
9
+ connect() {
10
+ return new Octokit({ auth: this.options.token ?? process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN });
11
+ }
12
+
13
+ async available() {
14
+ try {
15
+ await this.me();
16
+ return true;
17
+ } catch {
18
+ return false;
19
+ }
20
+ }
21
+
22
+ async me() {
23
+ return (await this.client.rest.users.getAuthenticated()).data.login;
24
+ }
25
+
26
+ async repos() {
27
+ const repos = await this.client.paginate(this.client.rest.repos.listForAuthenticatedUser, { per_page: 100 });
28
+ return repos.map((r) => ({ name: r.full_name, private: r.private, url: r.clone_url, branch: r.default_branch }));
29
+ }
30
+
31
+ async repo(owner, repo) {
32
+ return (await this.client.rest.repos.get({ owner, repo })).data;
33
+ }
34
+
35
+ async createRepo(name, { private: isPrivate = false, description = "" } = {}) {
36
+ return (await this.client.rest.repos.createForAuthenticatedUser({ name, private: isPrivate, description })).data;
37
+ }
38
+
39
+ // releasing → master lands in canon through a pull request
40
+ async pullRequest({ owner, repo, head, base = "master", title = `${head} → ${base}`, body = "" }) {
41
+ return (await this.client.rest.pulls.create({ owner, repo, head, base, title, body })).data;
42
+ }
43
+ }
44
+
45
+ export default GithubApi;
@@ -0,0 +1,19 @@
1
+ // Publishing a package to npm. Dry run unless told otherwise.
2
+ import { BOS } from "../../main.ts";
3
+ import SubprocessRunner from "../subprocess-runner/_index.mjs";
4
+
5
+ const npm = new SubprocessRunner({ command: "npm" });
6
+
7
+ export class NpmPublish extends BOS.Bridge {
8
+ static id = "npm-publish";
9
+
10
+ available() {
11
+ return npm.available();
12
+ }
13
+
14
+ publish(dir, { dryRun = true, tag = "latest" } = {}) {
15
+ return npm.run("npm", ["publish", "--tag", tag, ...(dryRun ? ["--dry-run"] : [])], { cwd: dir, inherit: true });
16
+ }
17
+ }
18
+
19
+ export default NpmPublish;
@@ -0,0 +1,93 @@
1
+ // Local language models through the ollama client (host: options.host, $OLLAMA_HOST
2
+ // or http://127.0.0.1:11434). Ravens (resources/ravens/) speak through it.
3
+ // Before any call the House (resources/house/) is asked: only active residents are called.
4
+ import { Ollama } from "ollama";
5
+
6
+ import { BOS } from "../../main.ts";
7
+ import House from "../../models/house/class.mjs";
8
+ import { listRavens, readRaven } from "./ravens.mjs";
9
+
10
+ export class OllamaLink extends BOS.Bridge {
11
+ static id = "ollama-link";
12
+
13
+ connect() {
14
+ return new Ollama({ host: this.options.host ?? process.env.OLLAMA_HOST ?? "http://127.0.0.1:11434" });
15
+ }
16
+
17
+ // give up after `ms`, abort the request, and answer null instead of throwing
18
+ async within(ms, work) {
19
+ let timer;
20
+ const timeout = new Promise((resolve) => {
21
+ timer = setTimeout(() => {
22
+ this.client.abort();
23
+ resolve(null);
24
+ }, ms);
25
+ });
26
+ try {
27
+ return await Promise.race([work(), timeout]);
28
+ } catch {
29
+ return null;
30
+ } finally {
31
+ clearTimeout(timer);
32
+ }
33
+ }
34
+
35
+ // names, architectures and parents of the served models: reading the list starts no model
36
+ async catalog() {
37
+ const listed = await this.within(2000, () => this.client.list());
38
+ return (
39
+ listed?.models.map((m) => ({
40
+ name: m.name,
41
+ family: m.details?.family ?? null,
42
+ parent: m.details?.parent_model || null,
43
+ })) ?? []
44
+ );
45
+ }
46
+
47
+ async models() {
48
+ return (await this.catalog()).map((entry) => entry.name);
49
+ }
50
+
51
+ get house() {
52
+ this._house ??= new House();
53
+ return this._house;
54
+ }
55
+
56
+ async available(model) {
57
+ if (model && !this.house.mayCall(model)) return false;
58
+ const models = await this.models();
59
+ return model ? models.some((name) => name === model || name === `${model}:latest`) : models.length > 0;
60
+ }
61
+
62
+ raven(name) {
63
+ return readRaven(name);
64
+ }
65
+ ravens() {
66
+ return listRavens();
67
+ }
68
+
69
+ // one question to one raven; `format` is a JSON schema for structured answers
70
+ // a cold model can take a minute to load; keep_alive keeps it warm for the next calls
71
+ async ask(ravenName, prompt, { format, timeout = 90000, model, keepAlive = "30m" } = {}) {
72
+ const raven = this.raven(ravenName);
73
+ const chosen = model ?? process.env[`BOS_${ravenName.toUpperCase()}_MODEL`] ?? raven.model;
74
+ const { state, reason } = this.house.stateOf(chosen);
75
+ if (state !== "active") throw new Error(`${chosen} is ${state} and is not called.${reason ? ` ${reason}` : ""}`);
76
+ const response = await this.within(timeout, () =>
77
+ this.client.chat({
78
+ model: chosen,
79
+ messages: [
80
+ { role: "system", content: raven.system },
81
+ { role: "user", content: prompt },
82
+ ],
83
+ format,
84
+ stream: false,
85
+ keep_alive: keepAlive,
86
+ options: { temperature: 0.2 },
87
+ }),
88
+ );
89
+ return response?.message?.content ?? null;
90
+ }
91
+ }
92
+
93
+ export default OllamaLink;
@@ -0,0 +1,29 @@
1
+ // Ravens: AI helpers of the workshop, one markdown file each in resources/ravens/.
2
+ // Frontmatter (model, duty) + the text below it as the raven's system prompt.
3
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
4
+ import { join } from "node:path";
5
+
6
+ import { RESOURCES } from "../../core/self.mjs";
7
+
8
+ const DIR = join(RESOURCES, "ravens");
9
+
10
+ export function readRaven(name) {
11
+ const path = join(DIR, `${name}.md`);
12
+ if (!existsSync(path)) throw new Error(`no raven "${name}" in resources/ravens/`);
13
+ const [, head = "", system = ""] = readFileSync(path, "utf8").match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/) ?? [];
14
+ const meta = Object.fromEntries(
15
+ head
16
+ .split("\n")
17
+ .map((line) => line.match(/^(\w+):\s*(.*)$/))
18
+ .filter(Boolean)
19
+ .map(([, k, v]) => [k, v.trim()]),
20
+ );
21
+ return { name, ...meta, system: system.trim() };
22
+ }
23
+
24
+ export const listRavens = () =>
25
+ existsSync(DIR)
26
+ ? readdirSync(DIR)
27
+ .filter((file) => file.endsWith(".md"))
28
+ .map((file) => readRaven(file.slice(0, -3)))
29
+ : [];
@@ -0,0 +1,33 @@
1
+ // Other hosts over ssh; aliases come from ~/.ssh/config (glossary: setup).
2
+ import { readFileSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
5
+
6
+ import { BOS } from "../../main.ts";
7
+ import SubprocessRunner from "../subprocess-runner/_index.mjs";
8
+
9
+ const ssh = new SubprocessRunner({ command: "ssh" });
10
+
11
+ export class SshLink extends BOS.Bridge {
12
+ static id = "ssh-link";
13
+
14
+ available() {
15
+ return Promise.resolve(ssh.try("ssh", ["-V"]) !== null);
16
+ }
17
+
18
+ aliases(config = join(homedir(), ".ssh", "config")) {
19
+ try {
20
+ return [...readFileSync(config, "utf8").matchAll(/^\s*Host\s+(.+)$/gim)]
21
+ .flatMap((match) => match[1].split(/\s+/))
22
+ .filter((alias) => !alias.includes("*"));
23
+ } catch {
24
+ return [];
25
+ }
26
+ }
27
+
28
+ run(host, command) {
29
+ return ssh.run("ssh", ["-o", "BatchMode=yes", host, command]);
30
+ }
31
+ }
32
+
33
+ export default SshLink;
@@ -0,0 +1,33 @@
1
+ // Any command-line tool as a bridge: run it, or ask whether it is installed.
2
+ import { execFileSync, spawnSync } from "node:child_process";
3
+
4
+ import { BOS } from "../../main.ts";
5
+
6
+ export class SubprocessRunner extends BOS.Bridge {
7
+ static id = "subprocess-runner";
8
+
9
+ async available(command = this.options.command) {
10
+ if (!command) return false;
11
+ return spawnSync(command, ["--version"], { stdio: "ignore" }).status === 0;
12
+ }
13
+
14
+ run(command, args = [], { cwd = process.cwd(), inherit = false } = {}) {
15
+ const output = execFileSync(command, args, {
16
+ cwd,
17
+ encoding: "utf8",
18
+ stdio: inherit ? "inherit" : ["ignore", "pipe", "pipe"],
19
+ });
20
+ return typeof output === "string" ? output.trim() : "";
21
+ }
22
+
23
+ // like run, but null instead of throwing
24
+ try(command, args, options) {
25
+ try {
26
+ return this.run(command, args, options);
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+ }
32
+
33
+ export default SubprocessRunner;
package/src/cli.mjs ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ // bos <command> [operands] [--json] [--dry-run] [--workshop=PATH]
3
+ import { VERSION } from "./core/self.mjs";
4
+ import { BOS } from "./main.ts";
5
+
6
+ const args = process.argv.slice(2);
7
+ const flags = Object.fromEntries(
8
+ args
9
+ .filter((arg) => arg.startsWith("--"))
10
+ .map((arg) => {
11
+ const [key, value = true] = arg.slice(2).split("=");
12
+ return [key.replace(/-(\w)/g, (_, c) => c.toUpperCase()), value];
13
+ }),
14
+ );
15
+ const [name = "help", ...operands] = args.filter((arg) => !arg.startsWith("--"));
16
+
17
+ if (flags.version) {
18
+ console.log(VERSION);
19
+ process.exit(0);
20
+ }
21
+
22
+ try {
23
+ const bos = await new BOS({ workshop: flags.workshop }).load();
24
+ const output = await bos.command(name, operands, flags);
25
+ if (output !== undefined) console.log(typeof output === "string" ? output : JSON.stringify(output, null, 2));
26
+ } catch (error) {
27
+ console.error(`bos ${name}: ${error.message}${error.procedure ? `\n in ${error.procedure}` : ""}`);
28
+ process.exitCode = 1;
29
+ }
@@ -0,0 +1,51 @@
1
+ /*
2
+ based on index.json in dirs generate commands
3
+ {
4
+ "path": "/status", # url path for apis
5
+ "info": "status information", # shortest explanation
6
+ "help": ["./help.md", "MD"], # detailed usage with render filetype handling
7
+ "inline": "./exec.mjs", # when called as oneline command with subcommand to return output to standard output, if it is streamable command then piping of input-output should be also handled there somewhere... it can be splitted to another file but have to be imported later
8
+ "repl": "./repl.mjs" # interactive cli menu that allows standard input of choice or more advanced nice-looking terminal gui based on universal ascii base
9
+ "mode": "conform" # mode of work the command serves (almanac, conform, smeltry, commandorate, provision); none: general
10
+ }
11
+ not all of these are required, most minimalistic now is "help" - only show universal help on general error or invoking help
12
+ should list commands and what they does
13
+ */
14
+
15
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
16
+ import { dirname, join } from "node:path";
17
+ import { fileURLToPath, pathToFileURL } from "node:url";
18
+
19
+ const HERE = dirname(fileURLToPath(import.meta.url));
20
+
21
+ // "./exec.mjs" → a function that imports the module on first call and runs its default export
22
+ const lazy = (dir, file) => async (context) => (await import(pathToFileURL(join(dir, file)))).default(context);
23
+
24
+ export function loadCommand(dir, name = dir.split("/").pop()) {
25
+ const index = JSON.parse(readFileSync(join(dir, "index.json"), "utf8"));
26
+ const [helpFile, helpType = "MD"] = [].concat(index.help ?? []);
27
+ return {
28
+ name,
29
+ path: index.path ?? `/${name}`,
30
+ info: index.info ?? "",
31
+ mode: index.mode ?? null, // the mode of work this command serves; null: any mode
32
+ help: () =>
33
+ helpFile && existsSync(join(dir, helpFile))
34
+ ? { text: readFileSync(join(dir, helpFile), "utf8"), type: helpType }
35
+ : null,
36
+ inline: index.inline
37
+ ? lazy(dir, index.inline)
38
+ : async () => {
39
+ throw new Error(`${name} has no inline form; see: bos help ${name}`);
40
+ },
41
+ repl: index.repl ? lazy(dir, index.repl) : null,
42
+ };
43
+ }
44
+
45
+ export async function loadCommands(root = HERE) {
46
+ return Object.fromEntries(
47
+ readdirSync(root, { withFileTypes: true })
48
+ .filter((entry) => entry.isDirectory() && existsSync(join(root, entry.name, "index.json")))
49
+ .map((entry) => [entry.name, loadCommand(join(root, entry.name), entry.name)]),
50
+ );
51
+ }
@@ -0,0 +1,7 @@
1
+ export default async ({ bos, flags }) =>
2
+ new bos.views.StatusView().render(
3
+ await bos.workshop.bootstrap({
4
+ dryRun: Boolean(flags.dryRun),
5
+ }),
6
+ flags.json ? "json" : "text",
7
+ );
@@ -0,0 +1,5 @@
1
+ # bootstrap
2
+
3
+ `bos bootstrap [--dry-run]` creates every missing area and descriptor of the
4
+ workshop, plus `AGENTS.md` and `CLAUDE.md` at its root. It never overwrites.
5
+ `--dry-run` lists what would be created.
@@ -0,0 +1,7 @@
1
+ {
2
+ "path": "/bootstrap",
3
+ "info": "create missing areas, descriptors and agent guides",
4
+ "mode": "conform",
5
+ "help": ["./help.md", "MD"],
6
+ "inline": "./exec.mjs"
7
+ }
@@ -0,0 +1,4 @@
1
+ export default async ({ bos, flags }) => {
2
+ const context = await bos.workshop.close();
3
+ return flags.json ? context.event : `closed · ttystory: ${context.ttystory ?? "no shell history found"}`;
4
+ };
@@ -0,0 +1,5 @@
1
+ # close
2
+
3
+ `bos close` copies the shell history to
4
+ `.BOS/storylines/ttystories/ttystory-<host>-<unixusat>.txt` and records the
5
+ closing. Run `history -a` first so the current shell is included.
@@ -0,0 +1,7 @@
1
+ {
2
+ "path": "/close",
3
+ "info": "keep shell history as a ttystory, record the closing",
4
+ "mode": "conform",
5
+ "help": ["./help.md", "MD"],
6
+ "inline": "./exec.mjs"
7
+ }
@@ -0,0 +1,11 @@
1
+ export default async ({ bos, operands: [file, source], flags }) => {
2
+ const context = await bos.workshop.describe({
3
+ ...(flags.hook ? { file, source } : {}),
4
+ ...(flags.skryba === "0" || flags.plain ? { skryba: false } : {}),
5
+ });
6
+ if (flags.hook) {
7
+ return undefined;
8
+ }
9
+ const note = context.skryba && !context.skryba.used ? `\n# Skryba: ${context.skryba.reason}` : "";
10
+ return (context.message ?? "nothing staged") + note;
11
+ };
@@ -0,0 +1,5 @@
1
+ # describe
2
+
3
+ `bos describe` prints a conventional-commit draft of what is staged.
4
+ `bos describe --hook <file> [source]` is what husky's `prepare-commit-msg`
5
+ calls: it writes the draft into the commit message file of a plain `git commit`.
@@ -0,0 +1,7 @@
1
+ {
2
+ "path": "/describe",
3
+ "info": "draft a conventional commit message from the staged changes",
4
+ "mode": "smeltry",
5
+ "help": ["./help.md", "MD"],
6
+ "inline": "./exec.mjs"
7
+ }
@@ -0,0 +1,6 @@
1
+ export default ({ bos, operands: [name], flags }) => {
2
+ const view = new bos.views.HelpView();
3
+ const state = { commands: bos.commands, command: name, help: name && bos.commands[name]?.help() };
4
+ if (name && !bos.commands[name]) throw new Error(`unknown command "${name}"`);
5
+ return view.render(state, flags.json ? "json" : "text");
6
+ };
@@ -0,0 +1,3 @@
1
+ # help
2
+
3
+ `bos help` lists every command. `bos help <command>` shows its help.
@@ -0,0 +1,6 @@
1
+ {
2
+ "path": "/help",
3
+ "info": "list commands, or show the help of one",
4
+ "help": ["./help.md", "MD"],
5
+ "inline": "./exec.mjs"
6
+ }
@@ -0,0 +1,3 @@
1
+ export default async ({ bos, operands: [name] }) => {
2
+ await bos.workshop.hook(name);
3
+ };
@@ -0,0 +1,4 @@
1
+ # hook
2
+
3
+ `bos hook <name>` is called by git hooks. It records the event, and the
4
+ repository's place in the workshop, in storylines.
@@ -0,0 +1,7 @@
1
+ {
2
+ "path": "/hook",
3
+ "info": "(called by git) record a hook in storylines",
4
+ "mode": "smeltry",
5
+ "help": ["./help.md", "MD"],
6
+ "inline": "./exec.mjs"
7
+ }
@@ -0,0 +1,5 @@
1
+ export default async ({ bos, operands: [action] }) => {
2
+ if (action !== "install") throw new Error("usage: bos hooks install");
3
+ const context = await bos.workshop.installHooks();
4
+ return { installed: context.changes, skipped: context.skipped };
5
+ };
@@ -0,0 +1,4 @@
1
+ # hooks
2
+
3
+ `bos hooks install`, inside a git repository, links its `post-commit`,
4
+ `post-checkout` and `post-merge` hooks to BOS. Existing hooks are left alone.
@@ -0,0 +1,7 @@
1
+ {
2
+ "path": "/hooks",
3
+ "info": "link this repository's git hooks to BOS",
4
+ "mode": "smeltry",
5
+ "help": ["./help.md", "MD"],
6
+ "inline": "./exec.mjs"
7
+ }
@@ -0,0 +1,8 @@
1
+ export default async ({ bos, flags }) =>
2
+ new bos.views.HouseView().render(
3
+ {
4
+ ...(await bos.workshop.gather()),
5
+ options: { all: Boolean(flags.all) },
6
+ },
7
+ flags.json ? "json" : "text",
8
+ );
@@ -0,0 +1,8 @@
1
+ # house
2
+
3
+ `bos house [--all]` shows the House of Anubis: each family of
4
+ `resources/house/families/` with its state (active, resting, frozen), its
5
+ residents, their duties, and whether ollama serves them now. `--all` also
6
+ lists the served models no family claims yet.
7
+
8
+ Only model names are read from ollama; no resident is started or prompted.
@@ -0,0 +1,7 @@
1
+ {
2
+ "path": "/house",
3
+ "info": "the House of Anubis: families, residents, states, duties",
4
+ "mode": "provision",
5
+ "help": ["./help.md", "MD"],
6
+ "inline": "./exec.mjs"
7
+ }
@@ -0,0 +1,2 @@
1
+ export default async ({ bos, flags }) =>
2
+ new bos.views.StatusView().render(await bos.workshop.locate(), flags.json ? "json" : "text");
@@ -0,0 +1,6 @@
1
+ # locate
2
+
3
+ `bos locate` prints the active workshop and the rule that found it:
4
+ `--workshop`, then `$BOS_WORKSHOP`, the nearest parent `__WORKSHOP`, the
5
+ workshop BOS is installed in, and `~/__WORKSHOP`. Also shows where BOS itself
6
+ sits, e.g. `forge / blacksmith-organization-system / bos-skillset`.
@@ -0,0 +1,6 @@
1
+ {
2
+ "path": "/locate",
3
+ "info": "active workshop, how it was found, where BOS sits",
4
+ "help": ["./help.md", "MD"],
5
+ "inline": "./exec.mjs"
6
+ }
@@ -0,0 +1,17 @@
1
+ import { modeByDigit } from "../../models/workshop/modes.mjs";
2
+
3
+ export default async ({ bos, operands: [asked], flags }) => {
4
+ const view = new bos.views.ModeView();
5
+ const format = flags.json ? "json" : "text";
6
+
7
+ if (asked === undefined) {
8
+ const { config } = await bos.workshop.status();
9
+ const digit = await bos.views.chooseDigit(`${view.menu(config.mode)}choose 0-5: `, 5);
10
+ if (digit === 0) {
11
+ return undefined;
12
+ }
13
+ asked = modeByDigit(digit);
14
+ }
15
+
16
+ return view.render(await bos.workshop.mode(asked), format);
17
+ };
@@ -0,0 +1,14 @@
1
+ # mode
2
+
3
+ `bos mode <name|digit>` switches the mode of work of this workshop.
4
+ `bos mode` shows the menu and waits for one digit (no Enter needed); `0` leaves it.
5
+
6
+ | digit | mode | covers |
7
+ | ----- | ---- | ------ |
8
+ | 1 | ALMANAC | emergency protocols: sinking |
9
+ | 2 | CONFORM | establishing the workshop, migrations, cleaning, verifying, syncing between fortified workshops |
10
+ | 3 | SMELTRY | code, development, active editing, publishing changes |
11
+ | 4 | COMMANDORATE | rulesets, privileges, roles, switching modes |
12
+ | 5 | PROVISION | presenting what a source of truth delivers, e.g. a repository on GitHub |
13
+
14
+ The mode is kept in `.BOS/workshop.json`, and every switch is recorded in storylines.
@@ -0,0 +1,8 @@
1
+ {
2
+ "path": "/mode",
3
+ "info": "show or switch the mode of work (1-5), or choose it by one digit",
4
+ "mode": "commandorate",
5
+ "help": ["./help.md", "MD"],
6
+ "inline": "./exec.mjs",
7
+ "repl": "./repl.mjs"
8
+ }
@@ -0,0 +1,18 @@
1
+ import { modeByDigit } from "../../models/workshop/modes.mjs";
2
+
3
+ // inside `bos repl` the prompt owns the input, so the digit is confirmed with Enter
4
+ export default async ({ bos, operands: [asked], io }) => {
5
+ const view = new bos.views.ModeView();
6
+
7
+ if (asked === undefined) {
8
+ const { config } = await bos.workshop.status();
9
+ console.log(view.menu(config.mode));
10
+ const digit = Number((await io.question("choose 0-5: ")).trim()[0]);
11
+ if (!digit || digit > 5) {
12
+ return undefined;
13
+ }
14
+ asked = modeByDigit(digit);
15
+ }
16
+
17
+ return view.text(await bos.workshop.mode(asked));
18
+ };
@@ -0,0 +1,5 @@
1
+ export default async ({ bos, flags }) =>
2
+ new bos.views.StatusView().render(
3
+ await bos.workshop.open({ dryRun: Boolean(flags.dryRun) }),
4
+ flags.json ? "json" : "text",
5
+ );