fluncle 0.154.0 → 0.156.0

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 (2) hide show
  1. package/bin/fluncle.mjs +165 -3
  2. package/package.json +1 -1
package/bin/fluncle.mjs CHANGED
@@ -561,7 +561,7 @@ function parseVersion(version) {
561
561
  var currentVersion;
562
562
  var init_version = __esm(() => {
563
563
  init_output();
564
- currentVersion = "0.154.0".trim() ? "0.154.0".trim() : "0.1.0";
564
+ currentVersion = "0.156.0".trim() ? "0.156.0".trim() : "0.1.0";
565
565
  });
566
566
 
567
567
  // src/update-notifier.ts
@@ -2189,7 +2189,7 @@ function parseVersion2(version) {
2189
2189
  var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
2190
2190
  var init_version2 = __esm(() => {
2191
2191
  init_output();
2192
- currentVersion2 = "0.154.0".trim() ? "0.154.0".trim() : "0.1.0";
2192
+ currentVersion2 = "0.156.0".trim() ? "0.156.0".trim() : "0.1.0";
2193
2193
  });
2194
2194
 
2195
2195
  // ../../packages/registry/src/index.ts
@@ -3025,6 +3025,28 @@ var init_src = __esm(() => {
3025
3025
  probeConfig: { cadenceMs: 10 * MINUTE_MS, cronName: "fluncle-note", kind: "cron" },
3026
3026
  weights: { status: "hidden" }
3027
3027
  },
3028
+ {
3029
+ command: "fluncle admin artists describe --queue",
3030
+ exposedContent: [
3031
+ "auto-author the /artist/<slug> voiced bio, fill-empty-only (hybrid: one claude -p call)"
3032
+ ],
3033
+ kind: "cron",
3034
+ name: "cron.artist-bio",
3035
+ operatorNotes: "every 30m. Hybrid --no-agent; one claude -p authors the paragraph off best-effort Firecrawl facts + identity. Never clobbers an operator bio. Box activation operator-gated. Source: docs/agents/hermes/scripts/{entity-bio-sweep.ts,artist-bio-sweep.sh}.",
3036
+ probeConfig: { cadenceMs: 30 * MINUTE_MS, cronName: "fluncle-artist-bio", kind: "cron" },
3037
+ weights: { status: "hidden" }
3038
+ },
3039
+ {
3040
+ command: "fluncle admin labels describe --queue",
3041
+ exposedContent: [
3042
+ "auto-author the /label/<slug> voiced bio, fill-empty-only (hybrid: one claude -p call)"
3043
+ ],
3044
+ kind: "cron",
3045
+ name: "cron.label-bio",
3046
+ operatorNotes: "every 30m. Hybrid --no-agent; one claude -p authors the paragraph off best-effort Firecrawl facts + identity. Never clobbers an operator bio. Box activation operator-gated. Source: docs/agents/hermes/scripts/{entity-bio-sweep.ts,label-bio-sweep.sh}.",
3047
+ probeConfig: { cadenceMs: 30 * MINUTE_MS, cronName: "fluncle-label-bio", kind: "cron" },
3048
+ weights: { status: "hidden" }
3049
+ },
3028
3050
  {
3029
3051
  command: "fluncle admin submissions triage",
3030
3052
  exposedContent: [
@@ -5896,9 +5918,33 @@ var exports_admin_artists = {};
5896
5918
  __export(exports_admin_artists, {
5897
5919
  resolveArtistCommand: () => resolveArtistCommand,
5898
5920
  listArtistsCommand: () => listArtistsCommand,
5921
+ draftArtistBioCommand: () => draftArtistBioCommand,
5922
+ describeArtistCommand: () => describeArtistCommand,
5923
+ buildBioBody: () => buildBioBody,
5899
5924
  backfillArtistsCommand: () => backfillArtistsCommand,
5900
- backfillArtistImagesCommand: () => backfillArtistImagesCommand
5925
+ backfillArtistImagesCommand: () => backfillArtistImagesCommand,
5926
+ artistsBioQueueCommand: () => artistsBioQueueCommand
5901
5927
  });
5928
+ function buildBioBody(options) {
5929
+ const body = { bio: options.bio };
5930
+ if (options.dryRun) {
5931
+ body.dryRun = true;
5932
+ }
5933
+ if (typeof options.promptVersion === "number") {
5934
+ body.promptVersion = options.promptVersion;
5935
+ }
5936
+ return body;
5937
+ }
5938
+ async function describeArtistCommand(slug, options) {
5939
+ return adminApiPost(`/api/admin/artists/${encodeURIComponent(slug)}/bio`, buildBioBody(options));
5940
+ }
5941
+ async function draftArtistBioCommand(slug) {
5942
+ return adminApiGet(`/api/admin/artists/${encodeURIComponent(slug)}/bio-draft`);
5943
+ }
5944
+ async function artistsBioQueueCommand(limit) {
5945
+ const response = await adminApiGet(`/api/admin/artists/bio-queue?limit=${limit}`);
5946
+ return response.artists;
5947
+ }
5902
5948
  async function listArtistsCommand(limit, cursor) {
5903
5949
  const params = new URLSearchParams({ limit: String(limit) });
5904
5950
  if (cursor) {
@@ -5927,11 +5973,39 @@ var init_admin_artists = __esm(() => {
5927
5973
  init_api();
5928
5974
  });
5929
5975
 
5976
+ // src/commands/admin-artists.ts
5977
+ function buildBioBody2(options) {
5978
+ const body = { bio: options.bio };
5979
+ if (options.dryRun) {
5980
+ body.dryRun = true;
5981
+ }
5982
+ if (typeof options.promptVersion === "number") {
5983
+ body.promptVersion = options.promptVersion;
5984
+ }
5985
+ return body;
5986
+ }
5987
+ var init_admin_artists2 = __esm(() => {
5988
+ init_api();
5989
+ });
5990
+
5930
5991
  // src/commands/admin-labels.ts
5931
5992
  var exports_admin_labels = {};
5932
5993
  __export(exports_admin_labels, {
5994
+ labelsBioQueueCommand: () => labelsBioQueueCommand,
5995
+ draftLabelBioCommand: () => draftLabelBioCommand,
5996
+ describeLabelCommand: () => describeLabelCommand,
5933
5997
  backfillLabelImagesCommand: () => backfillLabelImagesCommand
5934
5998
  });
5999
+ async function describeLabelCommand(slug, options) {
6000
+ return adminApiPost(`/api/admin/labels/${encodeURIComponent(slug)}/bio`, buildBioBody2(options));
6001
+ }
6002
+ async function draftLabelBioCommand(slug) {
6003
+ return adminApiGet(`/api/admin/labels/${encodeURIComponent(slug)}/bio-draft`);
6004
+ }
6005
+ async function labelsBioQueueCommand(limit) {
6006
+ const response = await adminApiGet(`/api/admin/labels/bio-queue?limit=${limit}`);
6007
+ return response.labels;
6008
+ }
5935
6009
  async function backfillLabelImagesCommand(limit, dryRun, cursor) {
5936
6010
  const params = new URLSearchParams({ dryRun: String(dryRun), limit: String(limit) });
5937
6011
  if (cursor) {
@@ -5941,6 +6015,7 @@ async function backfillLabelImagesCommand(limit, dryRun, cursor) {
5941
6015
  }
5942
6016
  var init_admin_labels = __esm(() => {
5943
6017
  init_api();
6018
+ init_admin_artists2();
5944
6019
  });
5945
6020
 
5946
6021
  // src/commands/admin-covers.ts
@@ -9121,6 +9196,36 @@ function addAdminCommands(program2) {
9121
9196
  const { resolveArtistCommand: resolveArtistCommand2 } = await Promise.resolve().then(() => (init_admin_artists(), exports_admin_artists));
9122
9197
  await runArtistResolve(artistId, options, resolveArtistCommand2);
9123
9198
  });
9199
+ artists.command("describe").description("Author the voiced bio for an artist (fills an empty bio only)").argument("[slug]").option("--queue", "Show the bio worklist (artists with findings but no bio), oldest first", false).option("--limit <limit>", "Number of artists to show with --queue", "50").option("--bio <text>", "The voice-gated bio paragraph").option("--bio-file <file>", "Read the bio from a file").option("--prompt-version <n>", "The prompt-registry version that authored the bio (the sweep sends this; it is the bio's provenance)").option("--dry-run", "Run the voice gate and report the verdict without storing anything", false).option("--json", "Print JSON", false).allowExcessArguments().action(async (slug, options) => {
9200
+ if (options.queue) {
9201
+ const { artistsBioQueueCommand: artistsBioQueueCommand2 } = await Promise.resolve().then(() => (init_admin_artists(), exports_admin_artists));
9202
+ await runEntityBioQueue("artist", options, artistsBioQueueCommand2);
9203
+ return;
9204
+ }
9205
+ const { describeArtistCommand: describeArtistCommand2 } = await Promise.resolve().then(() => (init_admin_artists(), exports_admin_artists));
9206
+ await runEntityDescribe("artist", slug, options, describeArtistCommand2);
9207
+ });
9208
+ artists.command("draft-bio").description("Assemble the Worker-grounded bio prompt for an artist (the sweep's trigger)").argument("<slug>").option("--json", "Print JSON", false).allowExcessArguments().action(async (slug, options) => {
9209
+ const { draftArtistBioCommand: draftArtistBioCommand2 } = await Promise.resolve().then(() => (init_admin_artists(), exports_admin_artists));
9210
+ await runEntityBioDraft("artist", slug, options, draftArtistBioCommand2);
9211
+ });
9212
+ const labels = configureCommand(admin.command("labels").description("Label entity commands (the voiced bio)"));
9213
+ labels.action(() => {
9214
+ labels.outputHelp();
9215
+ });
9216
+ labels.command("describe").description("Author the voiced bio for a label (fills an empty bio only)").argument("[slug]").option("--queue", "Show the bio worklist (labels with findings but no bio), oldest first", false).option("--limit <limit>", "Number of labels to show with --queue", "50").option("--bio <text>", "The voice-gated bio paragraph").option("--bio-file <file>", "Read the bio from a file").option("--prompt-version <n>", "The prompt-registry version that authored the bio (the sweep sends this; it is the bio's provenance)").option("--dry-run", "Run the voice gate and report the verdict without storing anything", false).option("--json", "Print JSON", false).allowExcessArguments().action(async (slug, options) => {
9217
+ if (options.queue) {
9218
+ const { labelsBioQueueCommand: labelsBioQueueCommand2 } = await Promise.resolve().then(() => (init_admin_labels(), exports_admin_labels));
9219
+ await runEntityBioQueue("label", options, labelsBioQueueCommand2);
9220
+ return;
9221
+ }
9222
+ const { describeLabelCommand: describeLabelCommand2 } = await Promise.resolve().then(() => (init_admin_labels(), exports_admin_labels));
9223
+ await runEntityDescribe("label", slug, options, describeLabelCommand2);
9224
+ });
9225
+ labels.command("draft-bio").description("Assemble the Worker-grounded bio prompt for a label (the sweep's trigger)").argument("<slug>").option("--json", "Print JSON", false).allowExcessArguments().action(async (slug, options) => {
9226
+ const { draftLabelBioCommand: draftLabelBioCommand2 } = await Promise.resolve().then(() => (init_admin_labels(), exports_admin_labels));
9227
+ await runEntityBioDraft("label", slug, options, draftLabelBioCommand2);
9228
+ });
9124
9229
  const migrations = configureCommand(admin.command("migrations").description("One-off operator data migrations"));
9125
9230
  migrations.action(() => {
9126
9231
  migrations.outputHelp();
@@ -9242,6 +9347,61 @@ async function runTrackNote(idOrLogId, options, trackNoteCommand2) {
9242
9347
  console.log(`Authored the note for ${result.logId}:`);
9243
9348
  console.log(` ${result.note}`);
9244
9349
  }
9350
+ async function runEntityDescribe(kind, slug, options, describeCommand) {
9351
+ const bio = options.bioFile ? readFileSync7(options.bioFile, "utf8") : options.bio;
9352
+ if (!slug || !bio || !bio.trim()) {
9353
+ throw new Error(`Usage: fluncle admin ${kind}s describe <slug> (--bio <text> | --bio-file <file>) [--dry-run] [--json]`);
9354
+ }
9355
+ const result = await describeCommand(slug, {
9356
+ bio: bio.trim(),
9357
+ dryRun: options.dryRun,
9358
+ promptVersion: parsePromptVersion(options.promptVersion)
9359
+ });
9360
+ if (options.json) {
9361
+ printJson(result);
9362
+ return;
9363
+ }
9364
+ if (result.skipped) {
9365
+ console.log(`A bio is already on file for ${result.slug}. The operator's bio stands.`);
9366
+ return;
9367
+ }
9368
+ if (result.dryRun) {
9369
+ console.log(`The voice gate passed for ${result.slug}. Nothing was stored (dry run).`);
9370
+ console.log(` ${result.bio}`);
9371
+ return;
9372
+ }
9373
+ console.log(`Authored the bio for ${result.slug}:`);
9374
+ console.log(` ${result.bio}`);
9375
+ }
9376
+ async function runEntityBioQueue(kind, options, queueCommand2) {
9377
+ const limit = options.limit ? Number.parseInt(options.limit, 10) : 50;
9378
+ const items = await queueCommand2(Number.isFinite(limit) ? limit : 50);
9379
+ if (options.json) {
9380
+ printJson(items);
9381
+ return;
9382
+ }
9383
+ if (items.length === 0) {
9384
+ console.log(`No ${kind}s awaiting a bio.`);
9385
+ return;
9386
+ }
9387
+ const noun = items.length === 1 ? kind : `${kind}s`;
9388
+ console.log(`${items.length} ${noun} awaiting a bio, oldest first:`);
9389
+ for (const item of items) {
9390
+ console.log(` ${item.slug} ${item.name}`);
9391
+ }
9392
+ }
9393
+ async function runEntityBioDraft(kind, slug, options, draftCommand) {
9394
+ const draft = await draftCommand(slug);
9395
+ if (options.json) {
9396
+ printJson(draft);
9397
+ return;
9398
+ }
9399
+ if (!draft.found) {
9400
+ console.log(`No ${kind} with slug ${slug}.`);
9401
+ return;
9402
+ }
9403
+ console.log(`Drafted a bio prompt for ${draft.name} (${draft.findingCount} finding(s), facts: ${draft.hasFacts ? "yes" : "none"}, prompt v${draft.promptVersion}).`);
9404
+ }
9245
9405
  async function runTrackSimilar(idOrLogId, options, trackSimilarCommand2) {
9246
9406
  if (!idOrLogId) {
9247
9407
  throw new Error("Missing id. Usage: fluncle tracks similar <track_id|log_id> [--limit <n>] [--json]");
@@ -11423,6 +11583,8 @@ var stringOptions = new Set([
11423
11583
  "--analyzed-from",
11424
11584
  "--at",
11425
11585
  "--audio",
11586
+ "--bio",
11587
+ "--bio-file",
11426
11588
  "--body",
11427
11589
  "--body-file",
11428
11590
  "--bpm",
package/package.json CHANGED
@@ -31,5 +31,5 @@
31
31
  "url": "git+https://github.com/mauricekleine/fluncle.git"
32
32
  },
33
33
  "type": "module",
34
- "version": "0.154.0"
34
+ "version": "0.156.0"
35
35
  }