@ziamana/bruine 0.1.1 → 0.1.2

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 (3) hide show
  1. package/README.md +1 -1
  2. package/dist/bin.js +94 -30
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
  [![License: MIT](https://img.shields.io/badge/license-MIT-b4a7ff)](LICENSE)
13
13
  [![Node 22+](https://img.shields.io/badge/node-22%2B-7dcfff)](https://nodejs.org)
14
14
  ![Windows · macOS · Linux](https://img.shields.io/badge/Windows%20%C2%B7%20macOS%20%C2%B7%20Linux-supported-8fe3a3)
15
- [![Changelog](https://img.shields.io/badge/version-0.1.1-ff9ed2)](CHANGELOG.md)
15
+ [![Changelog](https://img.shields.io/badge/version-0.1.2-ff9ed2)](CHANGELOG.md)
16
16
 
17
17
  <a href="docs/media/bruine-film.mp4"><img src="docs/media/hero.webp" alt="The bruine film: the word bruine becomes the logo in the rain, then the effort climbs from low to max and the rain turns into a storm" width="100%"></a>
18
18
 
package/dist/bin.js CHANGED
@@ -1180,6 +1180,48 @@ var init_skills = __esm({
1180
1180
  }
1181
1181
  });
1182
1182
 
1183
+ // src/setup/default-skills.ts
1184
+ import { existsSync as existsSync6 } from "fs";
1185
+ import { readFile as readFile4 } from "fs/promises";
1186
+ import { homedir as homedir3 } from "os";
1187
+ import { join as join4 } from "path";
1188
+ function defaultSkills(bundled) {
1189
+ return bundled.map((s) => s.name).filter((name) => !NOT_BY_DEFAULT.includes(name));
1190
+ }
1191
+ async function ensureDefaultSkills(opts) {
1192
+ const homeSkillsDir = join4(opts.dshHome, "skills");
1193
+ let doc = {};
1194
+ try {
1195
+ doc = JSON.parse(await readFile4(configReadPath(opts.dshHome), "utf8"));
1196
+ } catch {
1197
+ doc = {};
1198
+ }
1199
+ const nothing = { installed: [], kept: [] };
1200
+ if (Array.isArray(doc.skills)) return nothing;
1201
+ if (existsSync6(manifestReadPath(homeSkillsDir))) return nothing;
1202
+ const bundled = await readBundledSkills(opts.bundledRoot);
1203
+ if (bundled.length === 0) return nothing;
1204
+ const shipped = new Set(bundled.map((s) => s.name));
1205
+ const home = opts.home ?? homedir3();
1206
+ const mine = (await scanFoundSkills(home)).filter((s) => s.source === "agents" && !shipped.has(s.name));
1207
+ const chosen = [...defaultSkills(bundled), ...mine.map((s) => s.name)];
1208
+ const result = await syncSkills({ homeSkillsDir, bundledRoot: opts.bundledRoot, chosen, home });
1209
+ doc.skills = chosen;
1210
+ await writeAtomic2(configWritePath(opts.dshHome), `${JSON.stringify(doc, null, 2)}
1211
+ `);
1212
+ return { installed: result.installed, kept: mine.map((s) => s.name).filter((n) => result.installed.includes(n)) };
1213
+ }
1214
+ var NOT_BY_DEFAULT;
1215
+ var init_default_skills = __esm({
1216
+ "src/setup/default-skills.ts"() {
1217
+ "use strict";
1218
+ init_compat();
1219
+ init_simple();
1220
+ init_skills();
1221
+ NOT_BY_DEFAULT = ["remotion", "remotion-best-practices"];
1222
+ }
1223
+ });
1224
+
1183
1225
  // src/render/chars.ts
1184
1226
  function iconsFor(env = process.env, platform = process.platform) {
1185
1227
  if (appEnv("ASCII", env) === "1") return ASCII_ICONS;
@@ -1518,7 +1560,7 @@ var init_theme = __esm({
1518
1560
  // src/update.ts
1519
1561
  import { spawn } from "child_process";
1520
1562
  import { realpathSync } from "fs";
1521
- import { mkdir as mkdir4, readFile as readFile4, rename as rename4, writeFile as writeFile4 } from "fs/promises";
1563
+ import { mkdir as mkdir4, readFile as readFile5, rename as rename4, writeFile as writeFile4 } from "fs/promises";
1522
1564
  import path5 from "path";
1523
1565
  function updateCachePath(dshHome, pathMod = path5) {
1524
1566
  return pathMod.join(dshHome, UPDATE_CACHE_FILE);
@@ -1533,7 +1575,7 @@ function updateCheckEnabled(opts) {
1533
1575
  async function readUpdateCache(dshHome, pathMod = path5) {
1534
1576
  try {
1535
1577
  const doc = JSON.parse(
1536
- await readFile4(updateCachePath(dshHome, pathMod), "utf8")
1578
+ await readFile5(updateCachePath(dshHome, pathMod), "utf8")
1537
1579
  );
1538
1580
  if (typeof doc.checkedAt !== "number" || typeof doc.latest !== "string") return void 0;
1539
1581
  return { checkedAt: doc.checkedAt, latest: doc.latest };
@@ -1552,7 +1594,7 @@ async function writeUpdateCache(dshHome, cache, pathMod) {
1552
1594
  async function readBruineJsonDoc(dshHome) {
1553
1595
  try {
1554
1596
  return JSON.parse(
1555
- await readFile4(configReadPath(dshHome), "utf8")
1597
+ await readFile5(configReadPath(dshHome), "utf8")
1556
1598
  );
1557
1599
  } catch {
1558
1600
  return {};
@@ -1761,8 +1803,8 @@ var init_keys = __esm({
1761
1803
  });
1762
1804
 
1763
1805
  // src/setup/flow.ts
1764
- import { chmod as chmod2, mkdir as mkdir5, readFile as readFile5, rename as rename5, writeFile as writeFile5 } from "fs/promises";
1765
- import { dirname as dirname3, join as join5 } from "path";
1806
+ import { chmod as chmod2, mkdir as mkdir5, readFile as readFile6, rename as rename5, writeFile as writeFile5 } from "fs/promises";
1807
+ import { dirname as dirname3, join as join6 } from "path";
1766
1808
  function roleFromModelRef(ref) {
1767
1809
  if (ref.provider === "deepseek-official") return { cloud: "deepseek-official", model: ref.model };
1768
1810
  if (typeof ref.baseUrl === "string" && ref.baseUrl !== "") {
@@ -1811,10 +1853,10 @@ function usesLocalServer(answers) {
1811
1853
  }
1812
1854
  async function commitPlan(dshHome, plan) {
1813
1855
  if (plan.env.some(([name]) => name === void 0)) throw new Error("bad env entry");
1814
- await writeAtom(join5(dshHome, "settings.yaml"), plan.settingsYaml, 384);
1856
+ await writeAtom(join6(dshHome, "settings.yaml"), plan.settingsYaml, 384);
1815
1857
  let bruineJson = plan.bruineJson;
1816
1858
  try {
1817
- const previous = JSON.parse(await readFile5(configReadPath(dshHome), "utf8"));
1859
+ const previous = JSON.parse(await readFile6(configReadPath(dshHome), "utf8"));
1818
1860
  if (previous.reasoningEffort !== void 0) {
1819
1861
  const doc = JSON.parse(bruineJson);
1820
1862
  doc.reasoningEffort = previous.reasoningEffort;
@@ -1825,7 +1867,7 @@ async function commitPlan(dshHome, plan) {
1825
1867
  }
1826
1868
  await writeAtom(configWritePath(dshHome), bruineJson, 384);
1827
1869
  for (const [name, value] of plan.env) {
1828
- await writeEnvVar(join5(dshHome, ".env"), name, value);
1870
+ await writeEnvVar(join6(dshHome, ".env"), name, value);
1829
1871
  }
1830
1872
  await syncSkills(plan.skills);
1831
1873
  }
@@ -2141,7 +2183,7 @@ var init_flow = __esm({
2141
2183
  `,
2142
2184
  env,
2143
2185
  skills: {
2144
- homeSkillsDir: join5(opts.dshHome, "skills"),
2186
+ homeSkillsDir: join6(opts.dshHome, "skills"),
2145
2187
  bundledRoot: opts.bundledSkillsRoot,
2146
2188
  chosen: a.skills
2147
2189
  }
@@ -3103,10 +3145,10 @@ import {
3103
3145
  isKeyRelease as isKeyRelease4,
3104
3146
  matchesKey as matchesKey4
3105
3147
  } from "@earendil-works/pi-tui";
3106
- import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
3148
+ import { existsSync as existsSync7, readFileSync as readFileSync4 } from "fs";
3107
3149
  import { mkdir as mkdir6, rename as rename6, writeFile as writeFile6 } from "fs/promises";
3108
- import { homedir as homedir3 } from "os";
3109
- import { dirname as dirname4, join as join6, resolve } from "path";
3150
+ import { homedir as homedir4 } from "os";
3151
+ import { dirname as dirname4, join as join7, resolve } from "path";
3110
3152
  import { fileURLToPath } from "url";
3111
3153
  import { parse as parseYaml3 } from "yaml";
3112
3154
  async function setSuggestionsChoice(dshHome, value) {
@@ -3123,7 +3165,7 @@ function bundledSkillsRoot() {
3123
3165
  const dir = dirname4(fileURLToPath(import.meta.url));
3124
3166
  for (const up of [1, 2, 3]) {
3125
3167
  const candidate = resolve(dir, "..".repeat(up), "skills");
3126
- if (existsSync6(join6(candidate, "git-workflow", "SKILL.md"))) return candidate;
3168
+ if (existsSync7(join7(candidate, "git-workflow", "SKILL.md"))) return candidate;
3127
3169
  }
3128
3170
  return resolve(dir, "..", "skills");
3129
3171
  }
@@ -3144,7 +3186,7 @@ function isExistingInstall(prefill) {
3144
3186
  function readEnvFile(dshHome) {
3145
3187
  const out = {};
3146
3188
  try {
3147
- for (const line of readFileSync4(join6(dshHome, ".env"), "utf8").split(/\r?\n/)) {
3189
+ for (const line of readFileSync4(join7(dshHome, ".env"), "utf8").split(/\r?\n/)) {
3148
3190
  const m = /^([A-Z][A-Z0-9_]*)=(.*)$/.exec(line);
3149
3191
  if (m !== null && m[1] !== void 0 && m[2] !== void 0) out[m[1]] = m[2];
3150
3192
  }
@@ -3161,7 +3203,7 @@ function loadPrefill(dshHome) {
3161
3203
  }
3162
3204
  let settingsDoc;
3163
3205
  try {
3164
- settingsDoc = parseSettingsYaml(readFileSync4(join6(dshHome, "settings.yaml"), "utf8"));
3206
+ settingsDoc = parseSettingsYaml(readFileSync4(join7(dshHome, "settings.yaml"), "utf8"));
3165
3207
  } catch {
3166
3208
  settingsDoc = void 0;
3167
3209
  }
@@ -3379,7 +3421,7 @@ function savedSkillsList(dshHome) {
3379
3421
  }
3380
3422
  function installedSkillsList(dshHome) {
3381
3423
  try {
3382
- const raw = JSON.parse(readFileSync4(manifestReadPath(join6(dshHome, "skills")), "utf8"));
3424
+ const raw = JSON.parse(readFileSync4(manifestReadPath(join7(dshHome, "skills")), "utf8"));
3383
3425
  const names = Object.keys(raw).filter((n) => n !== "" && typeof raw[n] !== "undefined");
3384
3426
  return names.length > 0 ? names.sort((a, b) => a.localeCompare(b)) : [];
3385
3427
  } catch {
@@ -3392,8 +3434,10 @@ function initialSkillChecks(items, saved, found) {
3392
3434
  if (item.disabled === true) return;
3393
3435
  if (saved !== void 0) {
3394
3436
  if (saved.includes(item.value)) checked.add(i);
3395
- } else if (item.value !== "remotion" && item.value !== "remotion-best-practices" && (RECOMMENDED_SKILLS.includes(item.value) || found.some((f) => f.name === item.value && f.source === "agents"))) {
3396
- checked.add(i);
3437
+ } else if (!NOT_BY_DEFAULT.includes(item.value)) {
3438
+ const foundRow = found.find((f) => f.name === item.value);
3439
+ const shipped = !item.value.startsWith("#") && foundRow === void 0;
3440
+ if (shipped || RECOMMENDED_SKILLS.includes(item.value) || foundRow?.source === "agents") checked.add(i);
3397
3441
  }
3398
3442
  });
3399
3443
  return checked;
@@ -3588,6 +3632,9 @@ async function runFullSetup(dshHome, opts = {}) {
3588
3632
  async function drive() {
3589
3633
  const bundledRoot = opts.bundledSkillsRoot ?? bundledSkillsRoot();
3590
3634
  const bundled = await readBundledSkills(bundledRoot);
3635
+ if (flow.answers.skills.length === 0 && savedSkillsList(dshHome) === void 0 && installedSkillsList(dshHome) === void 0) {
3636
+ flow.answers.skills = defaultSkills(bundled);
3637
+ }
3591
3638
  const showWelcome = async () => {
3592
3639
  keyHelp = "Enter continue \xB7 Ctrl+C quit";
3593
3640
  status = "Bruine setup";
@@ -4208,7 +4255,7 @@ async function runFullSetup(dshHome, opts = {}) {
4208
4255
  }
4209
4256
  async function stepSkills(bundled) {
4210
4257
  displayStep = "skills";
4211
- const allFound = await scanFoundSkills(homedir3());
4258
+ const allFound = await scanFoundSkills(homedir4());
4212
4259
  const shippedNames = new Set(bundled.map((s) => s.name));
4213
4260
  const found = allFound.filter((s) => !shippedNames.has(s.name));
4214
4261
  const project2 = await scanProjectSkills(process.cwd());
@@ -4232,7 +4279,7 @@ async function runFullSetup(dshHome, opts = {}) {
4232
4279
  const saved = skillsSubmitted ? flow.answers.skills : savedSkillsList(dshHome);
4233
4280
  const checked = initialSkillChecks(items, saved, found);
4234
4281
  const sel = await checkStep(
4235
- saved !== void 0 && saved.length > 0 ? `Skills: Space toggles, Enter continues, s skips (keeps ${String(saved.length)} enabled)` : "Skills: Space toggles, Enter continues, s skips (enables none)",
4282
+ saved !== void 0 && saved.length > 0 ? `Skills: Space toggles, Enter continues, s skips (keeps ${String(saved.length)} enabled)` : "Skills: Space toggles, Enter continues, s skips (keeps the recommended ones)",
4236
4283
  items,
4237
4284
  checked
4238
4285
  );
@@ -4457,6 +4504,7 @@ var init_full = __esm({
4457
4504
  init_cloud();
4458
4505
  init_spacebunny();
4459
4506
  init_discover();
4507
+ init_default_skills();
4460
4508
  init_skills();
4461
4509
  init_simple();
4462
4510
  init_update();
@@ -4730,10 +4778,10 @@ var init_approve = __esm({
4730
4778
  // src/bin.ts
4731
4779
  init_compat();
4732
4780
  import { spawn as spawn2, spawnSync as spawnSync3 } from "child_process";
4733
- import { existsSync as existsSync7, readFileSync as readFileSync7, realpathSync as realpathSync2 } from "fs";
4781
+ import { existsSync as existsSync8, readFileSync as readFileSync7, realpathSync as realpathSync2 } from "fs";
4734
4782
  import { createInterface } from "readline";
4735
4783
  import os from "os";
4736
- import { dirname as dirname5, join as join7, resolve as resolve2 } from "path";
4784
+ import { dirname as dirname5, join as join8, resolve as resolve2 } from "path";
4737
4785
  import { fileURLToPath as fileURLToPath2 } from "url";
4738
4786
  import { createRequire as createRequire3 } from "module";
4739
4787
 
@@ -4962,6 +5010,7 @@ function resolveDshBaseDir() {
4962
5010
  // src/bin.ts
4963
5011
  init_simple();
4964
5012
  init_skills();
5013
+ init_default_skills();
4965
5014
  init_discover();
4966
5015
  init_theme();
4967
5016
 
@@ -5075,15 +5124,15 @@ var DSH_MISSING = "bruine: could not launch dsh (missing or broken install). Rei
5075
5124
  function selfPackageRoot() {
5076
5125
  try {
5077
5126
  const root = resolve2(dirname5(fileURLToPath2(import.meta.url)), "..");
5078
- const manifest = JSON.parse(readFileSync7(join7(root, "package.json"), "utf8"));
5127
+ const manifest = JSON.parse(readFileSync7(join8(root, "package.json"), "utf8"));
5079
5128
  return manifest.name === pkg2.name ? root : void 0;
5080
5129
  } catch {
5081
5130
  return void 0;
5082
5131
  }
5083
5132
  }
5084
5133
  function ensureBundleInstalled(profileDir, dshEntry, env, headless = false) {
5085
- const marker = join7(profileDir, "node_modules", pkg2.name, "package.json");
5086
- if (existsSync7(marker)) return;
5134
+ const marker = join8(profileDir, "node_modules", pkg2.name, "package.json");
5135
+ if (existsSync8(marker)) return;
5087
5136
  const root = selfPackageRoot();
5088
5137
  if (!headless) console.log("Setting up bruine (one time)\u2026");
5089
5138
  const dshBase = resolveDshBaseDir();
@@ -5120,12 +5169,12 @@ function readBruineJson(dshHome) {
5120
5169
  }
5121
5170
  async function printSkills(dshHome) {
5122
5171
  const { readInstalledSkills: readInstalledSkills2 } = await Promise.resolve().then(() => (init_skills(), skills_exports));
5123
- const skills = await readInstalledSkills2(join7(dshHome, "skills"));
5172
+ const skills = await readInstalledSkills2(join8(dshHome, "skills"));
5124
5173
  if (skills.length === 0) {
5125
5174
  console.log("No skills enabled. Run `bruine setup` to pick skills.");
5126
5175
  return;
5127
5176
  }
5128
- console.log(`Skills enabled in ${join7(dshHome, "skills")}:`);
5177
+ console.log(`Skills enabled in ${join8(dshHome, "skills")}:`);
5129
5178
  for (const s of skills) {
5130
5179
  const kind = s.copied === true ? "linked-copy" : s.kind;
5131
5180
  console.log(` ${s.name} ${kind}${s.source !== "" ? ` ${s.source}` : ""}`);
@@ -5169,7 +5218,7 @@ async function runUpdate() {
5169
5218
  let version = "unknown";
5170
5219
  try {
5171
5220
  const manifest = JSON.parse(
5172
- readFileSync7(join7(dirname5(real), "..", "package.json"), "utf8")
5221
+ readFileSync7(join8(dirname5(real), "..", "package.json"), "utf8")
5173
5222
  );
5174
5223
  version = manifest.version ?? "unknown";
5175
5224
  } catch {
@@ -5303,7 +5352,7 @@ async function main() {
5303
5352
  console.error("bruine: --continue cannot be combined with -p");
5304
5353
  process.exit(2);
5305
5354
  }
5306
- if (!existsSync7(join7(dshHome, "settings.yaml"))) {
5355
+ if (!existsSync8(join8(dshHome, "settings.yaml"))) {
5307
5356
  if (headless) {
5308
5357
  console.error("bruine: run `bruine setup` before using -p");
5309
5358
  process.exit(2);
@@ -5337,7 +5386,22 @@ async function main() {
5337
5386
  });
5338
5387
  const { dir } = await ensureProfile(dshHome);
5339
5388
  ensureBundleInstalled(dir, dshEntry, env, headless);
5340
- const migrated = await migrateAgentsSkills({ homeSkillsDir: join7(dshHome, "skills") });
5389
+ const defaults = await ensureDefaultSkills({
5390
+ dshHome,
5391
+ bundledRoot: resolve2(dirname5(fileURLToPath2(import.meta.url)), "..", "skills")
5392
+ });
5393
+ const shipped = defaults.installed.filter((name) => !defaults.kept.includes(name));
5394
+ const sayDefault = (said) => {
5395
+ const line = process.stdout.isTTY === true ? ansi.dim(said) : said;
5396
+ if (headless) console.error(line);
5397
+ else console.log(line);
5398
+ };
5399
+ if (shipped.length > 0) sayDefault(`bruine: ${String(shipped.length)} skills are on (turn any off with bruine setup)`);
5400
+ if (defaults.kept.length > 0) {
5401
+ const k = String(defaults.kept.length);
5402
+ sayDefault(`bruine: kept your ${k} skill${defaults.kept.length === 1 ? "" : "s"} from .agents/skills (manage them with bruine setup)`);
5403
+ }
5404
+ const migrated = await migrateAgentsSkills({ homeSkillsDir: join8(dshHome, "skills") });
5341
5405
  if (migrated.linked.length > 0) {
5342
5406
  const n = String(migrated.linked.length);
5343
5407
  const kept = `bruine: kept your ${n} skill${migrated.linked.length === 1 ? "" : "s"} from .agents/skills (manage them with bruine setup)`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziamana/bruine",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Interactive terminal coding agent that runs your own models, local or OpenAI-compatible",
5
5
  "type": "module",
6
6
  "license": "MIT",