@fdcn/sourcefed 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -32,13 +32,13 @@ import { createSourcefedMcp } from "@fdcn/sourcefed/mcp"
32
32
  OpenCode loads this package as both a server and a TUI plugin:
33
33
 
34
34
  ```json
35
- // opencode.json
36
- { "plugin": ["@fdcn/sourcefed@0.3.0"] }
35
+ // ~/.config/opencode/opencode.jsonc
36
+ { "plugins": ["@fdcn/sourcefed@0.3.1"] }
37
37
  ```
38
38
 
39
39
  ```json
40
- // tui.json
41
- { "plugin": ["@fdcn/sourcefed@0.3.0"] }
40
+ // ~/.config/opencode/cli.json
41
+ { "plugins": ["@fdcn/sourcefed@0.3.1"] }
42
42
  ```
43
43
 
44
44
  The package exposes `./server`, `./tui`, `./opencode`, `./opencode/tui`, and `./pi` entrypoints for hosts that load them directly.
package/dist/cli.js CHANGED
@@ -44184,7 +44184,9 @@ async function runMonitor(argv) {
44184
44184
 
44185
44185
  // packages/cli/src/utils/setup.ts
44186
44186
  import { execFile } from "node:child_process";
44187
- import { readFileSync } from "node:fs";
44187
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
44188
+ import os8 from "node:os";
44189
+ import path8 from "node:path";
44188
44190
  import { fileURLToPath } from "node:url";
44189
44191
  function setupSpec() {
44190
44192
  const manifest = JSON.parse(readFileSync(fileURLToPath(new URL("../package.json", import.meta.url)), "utf8"));
@@ -44196,23 +44198,64 @@ async function setupHost(name, args, spec) {
44196
44198
  try {
44197
44199
  await execFile(name, args);
44198
44200
  console.error(`[sourcefed] ${name} configured \u2014 restart ${name} to load the plugin`);
44201
+ return true;
44199
44202
  } catch (error2) {
44200
44203
  const code = error2 && typeof error2 === "object" && "code" in error2 ? String(error2.code) : "";
44201
44204
  if (code === "ENOENT") {
44202
44205
  console.error(`[sourcefed] ${name} is not on PATH \u2014 install it manually:`);
44203
- console.error(name === "opencode" ? ` add "${spec}" to the plugin array in ~/.config/opencode/opencode.jsonc and tui.json` : ` run: pi install ${spec}`);
44204
- return;
44206
+ console.error(name === "opencode" ? ` add "${spec}" to the plugins array in ~/.config/opencode/opencode.jsonc and cli.json` : ` run: pi install ${spec}`);
44207
+ return false;
44205
44208
  }
44206
44209
  throw error2;
44207
44210
  }
44208
44211
  }
44212
+ function configureOpenCodeCliPlugin(spec) {
44213
+ const file = openCodeCliConfigPath();
44214
+ const config2 = readOpenCodeCliConfig(file);
44215
+ const plugins = config2.plugins;
44216
+ if (plugins === void 0) {
44217
+ config2.plugins = [spec];
44218
+ } else if (!Array.isArray(plugins)) {
44219
+ throw new Error(`OpenCode CLI config has a non-array plugins setting: ${file}`);
44220
+ } else if (!plugins.some((plugin) => isPluginEntry(plugin, spec))) {
44221
+ config2.plugins = [...plugins, spec];
44222
+ } else {
44223
+ return;
44224
+ }
44225
+ mkdirSync(path8.dirname(file), { recursive: true });
44226
+ writeFileSync(file, `${JSON.stringify(config2, null, 2)}
44227
+ `);
44228
+ }
44229
+ function openCodeCliConfigPath() {
44230
+ const configHome = process.env.XDG_CONFIG_HOME ?? path8.join(os8.homedir(), ".config");
44231
+ return path8.join(configHome, "opencode", "cli.json");
44232
+ }
44233
+ function readOpenCodeCliConfig(file) {
44234
+ try {
44235
+ const value = JSON.parse(readFileSync(file, "utf8"));
44236
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
44237
+ throw new Error(`OpenCode CLI config must contain a JSON object: ${file}`);
44238
+ }
44239
+ return value;
44240
+ } catch (error2) {
44241
+ if (error2 instanceof Error && "code" in error2 && error2.code === "ENOENT") return {};
44242
+ if (error2 instanceof SyntaxError) throw new Error(`OpenCode CLI config is not valid JSON: ${file}`);
44243
+ throw error2;
44244
+ }
44245
+ }
44246
+ function isPluginEntry(value, spec) {
44247
+ if (value === spec) return true;
44248
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
44249
+ return "package" in value && value.package === spec;
44250
+ }
44209
44251
 
44210
44252
  // packages/cli/src/commands/setup.ts
44211
44253
  async function runSetup(args) {
44212
44254
  const spec = setupSpec();
44213
44255
  const requested = args[0] ?? "all";
44214
44256
  if (requested === "opencode" || requested === "all") {
44215
- await setupHost("opencode", ["plugin", spec, "--global"], spec);
44257
+ const installed = await setupHost("opencode", ["plugin", "add", spec], spec);
44258
+ if (installed) configureOpenCodeCliPlugin(spec);
44216
44259
  }
44217
44260
  if (requested === "pi" || requested === "all") {
44218
44261
  await setupHost("pi", ["install", `npm:${spec}`], `npm:${spec}`);
@@ -44221,12 +44264,12 @@ async function runSetup(args) {
44221
44264
 
44222
44265
  // packages/cli/src/skills/command.ts
44223
44266
  import { readFile as readFile7, readdir as readdir2, stat as stat2 } from "node:fs/promises";
44224
- import path9 from "node:path";
44267
+ import path10 from "node:path";
44225
44268
 
44226
44269
  // packages/cli/src/skills/discovery.ts
44227
44270
  import { statSync } from "node:fs";
44228
44271
  import { readdir, readFile as readFile6, stat } from "node:fs/promises";
44229
- import path8 from "node:path";
44272
+ import path9 from "node:path";
44230
44273
  import { fileURLToPath as fileURLToPath2 } from "node:url";
44231
44274
 
44232
44275
  // packages/cli/src/skills/frontmatter.ts
@@ -44274,7 +44317,7 @@ async function skillsDirs() {
44274
44317
  }
44275
44318
  const directories = [];
44276
44319
  for (const name of SKILL_DIRS) {
44277
- const candidate = path8.join(skillRoot(), name);
44320
+ const candidate = path9.join(skillRoot(), name);
44278
44321
  try {
44279
44322
  if ((await stat(candidate)).isDirectory()) directories.push(candidate);
44280
44323
  } catch {
@@ -44286,7 +44329,7 @@ async function discoverSkills() {
44286
44329
  const skills = [];
44287
44330
  for (const dir of await skillsDirs()) {
44288
44331
  for (const entry of await readdir(dir)) {
44289
- const skillDir = path8.join(dir, entry);
44332
+ const skillDir = path9.join(dir, entry);
44290
44333
  let entryStat;
44291
44334
  try {
44292
44335
  entryStat = await stat(skillDir);
@@ -44294,7 +44337,7 @@ async function discoverSkills() {
44294
44337
  continue;
44295
44338
  }
44296
44339
  if (!entryStat.isDirectory()) continue;
44297
- const skillFile = path8.join(skillDir, "SKILL.md");
44340
+ const skillFile = path9.join(skillDir, "SKILL.md");
44298
44341
  try {
44299
44342
  const content = await readFile6(skillFile, "utf8");
44300
44343
  const parsed = parseFrontmatter(content);
@@ -44307,15 +44350,15 @@ async function discoverSkills() {
44307
44350
  return skills;
44308
44351
  }
44309
44352
  function skillRoot() {
44310
- let dir = path8.dirname(fileURLToPath2(import.meta.url));
44353
+ let dir = path9.dirname(fileURLToPath2(import.meta.url));
44311
44354
  for (let depth = 0; depth < 6; depth++) {
44312
- const marker = path8.join(dir, "data");
44355
+ const marker = path9.join(dir, "data");
44313
44356
  if (statSync(marker, { throwIfNoEntry: false })?.isDirectory()) return dir;
44314
- const parent = path8.dirname(dir);
44357
+ const parent = path9.dirname(dir);
44315
44358
  if (parent === dir) break;
44316
44359
  dir = parent;
44317
44360
  }
44318
- return path8.dirname(fileURLToPath2(import.meta.url));
44361
+ return path9.dirname(fileURLToPath2(import.meta.url));
44319
44362
  }
44320
44363
 
44321
44364
  // packages/cli/src/skills/command.ts
@@ -44362,7 +44405,7 @@ async function runGet(args) {
44362
44405
  output += `# ${skill.name}
44363
44406
 
44364
44407
  `;
44365
- output += await readFile7(path9.join(skill.dir, "SKILL.md"), "utf8");
44408
+ output += await readFile7(path10.join(skill.dir, "SKILL.md"), "utf8");
44366
44409
  output += "\n";
44367
44410
  if (full) output += await supplementaryFiles(skill.dir);
44368
44411
  }
@@ -44385,7 +44428,7 @@ async function runPath(name) {
44385
44428
  async function supplementaryFiles(skillDir) {
44386
44429
  let output = "";
44387
44430
  for (const subdirName of ["references", "templates"]) {
44388
- const subdir = path9.join(skillDir, subdirName);
44431
+ const subdir = path10.join(skillDir, subdirName);
44389
44432
  try {
44390
44433
  if (!(await stat2(subdir)).isDirectory()) continue;
44391
44434
  } catch {
@@ -44393,7 +44436,7 @@ async function supplementaryFiles(skillDir) {
44393
44436
  }
44394
44437
  const entries = (await readdir2(subdir)).sort();
44395
44438
  for (const entry of entries) {
44396
- const filePath = path9.join(subdir, entry);
44439
+ const filePath = path10.join(subdir, entry);
44397
44440
  try {
44398
44441
  if (!(await stat2(filePath)).isFile()) continue;
44399
44442
  } catch {
@@ -177,6 +177,26 @@ var sourcefedTui = (ctx) => {
177
177
  sessionID
178
178
  })
179
179
  });
180
+ const disposeKeymapSlot = ctx.ui.slot({
181
+ append: "app",
182
+ render: () => _$createComponent2(KeymapRegistration, {
183
+ ctx,
184
+ currentSessionID,
185
+ getClient
186
+ })
187
+ });
188
+ return () => {
189
+ disposeSlot();
190
+ disposeKeymapSlot();
191
+ void client?.close();
192
+ };
193
+ };
194
+ function KeymapRegistration(props) {
195
+ const {
196
+ ctx,
197
+ currentSessionID,
198
+ getClient
199
+ } = props;
180
200
  ctx.keymap.layer(() => ({
181
201
  mode: "global",
182
202
  commands: [{
@@ -259,11 +279,8 @@ var sourcefedTui = (ctx) => {
259
279
  }
260
280
  }]
261
281
  }));
262
- return () => {
263
- disposeSlot();
264
- void client?.close();
265
- };
266
- };
282
+ return null;
283
+ }
267
284
  function MonitorDialog(props) {
268
285
  const palette = createMemo2(() => tone(props.ctx.theme));
269
286
  const active = props.monitors.filter((monitor) => monitor.enabled);
@@ -1,2 +1,3 @@
1
1
  export declare function setupSpec(): string;
2
- export declare function setupHost(name: string, args: string[], spec: string): Promise<void>;
2
+ export declare function setupHost(name: string, args: string[], spec: string): Promise<boolean>;
3
+ export declare function configureOpenCodeCliPlugin(spec: string): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fdcn/sourcefed",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Live Jira, GitHub, and Slack monitoring for agent hosts: CLI, daemon, MCP server, and host plugins.",
5
5
  "license": "MIT",
6
6
  "type": "module",