jioogen 0.2.1 → 0.2.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.
package/dist/index.js CHANGED
@@ -363,9 +363,9 @@ var require_utils = __commonJS({
363
363
  fpath.split(self.sep).forEach(function(name) {
364
364
  if (!name || name.substr(-1, 1) === ":") return;
365
365
  resolvedPath += self.sep + name;
366
- var stat;
366
+ var stat2;
367
367
  try {
368
- stat = self.fs.statSync(resolvedPath);
368
+ stat2 = self.fs.statSync(resolvedPath);
369
369
  } catch (e) {
370
370
  if (e.message && e.message.startsWith("ENOENT")) {
371
371
  self.fs.mkdirSync(resolvedPath);
@@ -373,7 +373,7 @@ var require_utils = __commonJS({
373
373
  throw e;
374
374
  }
375
375
  }
376
- if (stat && stat.isFile()) throw Errors.FILE_IN_THE_WAY(`"${resolvedPath}"`);
376
+ if (stat2 && stat2.isFile()) throw Errors.FILE_IN_THE_WAY(`"${resolvedPath}"`);
377
377
  });
378
378
  }
379
379
  mkdirSync(folder);
@@ -382,8 +382,8 @@ var require_utils = __commonJS({
382
382
  const self = this;
383
383
  if (self.fs.existsSync(path)) {
384
384
  if (!overwrite) return false;
385
- var stat = self.fs.statSync(path);
386
- if (stat.isDirectory()) {
385
+ var stat2 = self.fs.statSync(path);
386
+ if (stat2.isDirectory()) {
387
387
  return false;
388
388
  }
389
389
  }
@@ -416,8 +416,8 @@ var require_utils = __commonJS({
416
416
  const self = this;
417
417
  self.fs.exists(path, function(exist) {
418
418
  if (exist && !overwrite) return callback(false);
419
- self.fs.stat(path, function(err, stat) {
420
- if (exist && stat && stat.isDirectory()) {
419
+ self.fs.stat(path, function(err, stat2) {
420
+ if (exist && stat2 && stat2.isDirectory()) {
421
421
  return callback(false);
422
422
  }
423
423
  var folder = pth.dirname(path);
@@ -469,13 +469,13 @@ var require_utils = __commonJS({
469
469
  for (const part of rel.split(pth.sep)) {
470
470
  if (!part || part === ".") continue;
471
471
  cur = pth.join(cur, part);
472
- let stat;
472
+ let stat2;
473
473
  try {
474
- stat = self.fs.lstatSync(cur);
474
+ stat2 = self.fs.lstatSync(cur);
475
475
  } catch (e) {
476
476
  break;
477
477
  }
478
- if (stat.isSymbolicLink()) throw Errors.FILE_IN_THE_WAY(`"${cur}"`);
478
+ if (stat2.isSymbolicLink()) throw Errors.FILE_IN_THE_WAY(`"${cur}"`);
479
479
  }
480
480
  };
481
481
  Utils.prototype.findFiles = function(path) {
@@ -502,11 +502,11 @@ var require_utils = __commonJS({
502
502
  self.fs.readdirSync(dir).forEach(function(file) {
503
503
  const path2 = pth.join(dir, file);
504
504
  if (escapesRoot(path2)) return;
505
- const stat = self.fs.statSync(path2);
505
+ const stat2 = self.fs.statSync(path2);
506
506
  if (!pattern || pattern.test(path2)) {
507
- files.push(pth.normalize(path2) + (stat.isDirectory() ? self.sep : ""));
507
+ files.push(pth.normalize(path2) + (stat2.isDirectory() ? self.sep : ""));
508
508
  }
509
- if (stat.isDirectory() && recursive) {
509
+ if (stat2.isDirectory() && recursive) {
510
510
  const realDir = self.fs.realpathSync(path2);
511
511
  if (!visited.has(realDir)) {
512
512
  visited.add(realDir);
@@ -553,14 +553,14 @@ var require_utils = __commonJS({
553
553
  if (!--pending) done();
554
554
  return;
555
555
  }
556
- self.fs.stat(file, function(err3, stat) {
556
+ self.fs.stat(file, function(err3, stat2) {
557
557
  if (err3) return done(err3);
558
- if (!stat) {
558
+ if (!stat2) {
559
559
  if (!--pending) done();
560
560
  return;
561
561
  }
562
- results.push(pth.normalize(file) + (stat.isDirectory() ? self.sep : ""));
563
- if (!stat.isDirectory()) {
562
+ results.push(pth.normalize(file) + (stat2.isDirectory() ? self.sep : ""));
563
+ if (!stat2.isDirectory()) {
564
564
  if (!--pending) done();
565
565
  return;
566
566
  }
@@ -8433,8 +8433,11 @@ function envValue(name) {
8433
8433
  const value = process.env[name]?.trim();
8434
8434
  return value !== void 0 && value.length > 0 ? value : void 0;
8435
8435
  }
8436
+ function configDirOverride() {
8437
+ return envValue(CONFIG_DIR_ENV) ?? envValue(LEGACY_CONFIG_DIR_ENV);
8438
+ }
8436
8439
  function configDir() {
8437
- const override = envValue(CONFIG_DIR_ENV) ?? envValue(LEGACY_CONFIG_DIR_ENV);
8440
+ const override = configDirOverride();
8438
8441
  return override !== void 0 ? resolve(override) : join2(homedir(), ".config", "jioogen");
8439
8442
  }
8440
8443
  function legacyConfigDir() {
@@ -8474,6 +8477,7 @@ async function readCredentialsFile(path) {
8474
8477
  async function readCredentials() {
8475
8478
  const primary = await readCredentialsFile(credentialsPath());
8476
8479
  if (primary !== null) return primary;
8480
+ if (configDirOverride() !== void 0) return null;
8477
8481
  const legacyPath = join2(legacyConfigDir(), CREDENTIALS_FILE);
8478
8482
  if (legacyPath === credentialsPath()) return null;
8479
8483
  return readCredentialsFile(legacyPath);
@@ -8558,6 +8562,10 @@ function isMissingFile(error) {
8558
8562
  return isRecord(error) && error.code === "ENOENT";
8559
8563
  }
8560
8564
 
8565
+ // src/errors.ts
8566
+ var UsageError = class extends Error {
8567
+ };
8568
+
8561
8569
  // src/output.ts
8562
8570
  function formatTable(headers, rows) {
8563
8571
  const widths = headers.map(
@@ -8569,11 +8577,52 @@ function formatTable(headers, rows) {
8569
8577
  }
8570
8578
 
8571
8579
  // src/sidecar.ts
8572
- import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
8580
+ import { randomUUID } from "node:crypto";
8581
+ import { mkdir as mkdir2, open, readFile as readFile3, rename, rm as rm2, stat, writeFile as writeFile2 } from "node:fs/promises";
8573
8582
  import { dirname as dirname2, join as join3 } from "node:path";
8583
+ var LOCK_RETRY_MS = 25;
8584
+ var LOCK_TIMEOUT_MS = 3e3;
8585
+ var LOCK_STALE_MS = 1e4;
8574
8586
  function sidecarPath(bundleDir) {
8575
8587
  return join3(bundleDir, EXECUTION_SIDECAR_PATH);
8576
8588
  }
8589
+ function lockPath(bundleDir) {
8590
+ return `${sidecarPath(bundleDir)}.lock`;
8591
+ }
8592
+ async function withStateLock(bundleDir, fn) {
8593
+ const target = lockPath(bundleDir);
8594
+ await mkdir2(dirname2(target), { recursive: true });
8595
+ const deadline = Date.now() + LOCK_TIMEOUT_MS;
8596
+ let handle = null;
8597
+ let staleRemoved = false;
8598
+ while (handle === null) {
8599
+ try {
8600
+ handle = await open(target, "wx");
8601
+ } catch (error) {
8602
+ if (!isLockBusy(error)) throw error;
8603
+ if (!staleRemoved && await removeStaleLock(target)) {
8604
+ staleRemoved = true;
8605
+ continue;
8606
+ }
8607
+ if (Date.now() >= deadline) {
8608
+ throw new Error(
8609
+ `Gagal mengambil lock sidecar setelah ${LOCK_TIMEOUT_MS / 1e3} detik: ${target}. Kemungkinan proses lain masih menulis; hapus file lock itu bila proses tersebut sudah mati.`
8610
+ );
8611
+ }
8612
+ await delay(LOCK_RETRY_MS);
8613
+ }
8614
+ }
8615
+ try {
8616
+ await handle.writeFile(`${process.pid}
8617
+ `, "utf8");
8618
+ return await fn();
8619
+ } finally {
8620
+ await handle.close().catch(() => {
8621
+ });
8622
+ await rm2(target, { force: true }).catch(() => {
8623
+ });
8624
+ }
8625
+ }
8577
8626
  async function readState(bundleDir) {
8578
8627
  let raw;
8579
8628
  try {
@@ -8662,8 +8711,16 @@ async function writeState(bundleDir, state) {
8662
8711
  const normalized = rebuild(state, state.tasks, state.current);
8663
8712
  const target = sidecarPath(bundleDir);
8664
8713
  await mkdir2(dirname2(target), { recursive: true });
8665
- await writeFile2(target, `${JSON.stringify(normalized, null, 2)}
8714
+ const temp = `${target}.${process.pid}.${randomUUID()}.tmp`;
8715
+ try {
8716
+ await writeFile2(temp, `${JSON.stringify(normalized, null, 2)}
8666
8717
  `, "utf8");
8718
+ await rename(temp, target);
8719
+ } catch (error) {
8720
+ await rm2(temp, { force: true }).catch(() => {
8721
+ });
8722
+ throw error;
8723
+ }
8667
8724
  return normalized;
8668
8725
  }
8669
8726
  function rebuild(state, tasks, current, now = /* @__PURE__ */ new Date()) {
@@ -8690,6 +8747,23 @@ function toTaskState(task) {
8690
8747
  function isMissingFile2(error) {
8691
8748
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
8692
8749
  }
8750
+ function isLockBusy(error) {
8751
+ return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
8752
+ }
8753
+ async function removeStaleLock(target) {
8754
+ try {
8755
+ const info = await stat(target);
8756
+ if (Date.now() - info.mtimeMs < LOCK_STALE_MS) return false;
8757
+ await rm2(target, { force: true });
8758
+ return true;
8759
+ } catch (error) {
8760
+ if (isMissingFile2(error)) return true;
8761
+ throw error;
8762
+ }
8763
+ }
8764
+ function delay(ms) {
8765
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
8766
+ }
8693
8767
 
8694
8768
  // src/skills.ts
8695
8769
  import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
@@ -9106,6 +9180,10 @@ async function extractBundle(bytes, targetDir) {
9106
9180
  }
9107
9181
 
9108
9182
  // src/commands.ts
9183
+ var PROJECT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
9184
+ function isValidProjectId(projectId) {
9185
+ return PROJECT_ID_RE.test(projectId.trim());
9186
+ }
9109
9187
  async function loginCommand(options) {
9110
9188
  const token = options.token ?? envFirst("JIOOGEN_TOKEN", "JIOOCODE_TOKEN");
9111
9189
  if (token !== void 0 && token.length > 0) {
@@ -9186,9 +9264,15 @@ async function projectsCommand(options) {
9186
9264
  );
9187
9265
  }
9188
9266
  async function pullCommand(projectId, options) {
9267
+ if (!isValidProjectId(projectId)) {
9268
+ throw new UsageError(
9269
+ `Project id tidak valid: ${projectId}. Gunakan UUID dari \`jioogen projects\`.`
9270
+ );
9271
+ }
9272
+ await assertPullDefaultDir(process.cwd(), options.dir);
9189
9273
  const { apiUrl, client } = await resolveApi(options);
9190
9274
  const target = resolve4(options.dir ?? projectId);
9191
- await assertPullTarget(target, projectId);
9275
+ await assertPullTarget(target, projectId, options.force === true);
9192
9276
  const bytes = await client.getBytes(projectPath(projectId, "/bundle"));
9193
9277
  const written = await extractBundle(bytes, target);
9194
9278
  const skills = await installAgentSkills(target);
@@ -9264,9 +9348,15 @@ async function handoffCommand(options) {
9264
9348
  }
9265
9349
  }
9266
9350
  async function taskNextCommand(options) {
9267
- const { bundleDir, state, definitions, projectId } = await loadLocalState(options);
9268
- const written = await writeState(bundleDir, state);
9269
- const next = selectNext(definitions, written.tasks);
9351
+ const { projectId, definitions, written, next } = await withLocalState(options, async (local) => {
9352
+ const written2 = await writeState(local.bundleDir, local.state);
9353
+ return {
9354
+ projectId: local.projectId,
9355
+ definitions: local.definitions,
9356
+ written: written2,
9357
+ next: selectNext(local.definitions, written2.tasks)
9358
+ };
9359
+ });
9270
9360
  if (next === null) {
9271
9361
  if (options.json === true) {
9272
9362
  console.log(JSON.stringify({ done: true, projectId }, null, 2));
@@ -9315,62 +9405,71 @@ Status: ${next.state.status} \u2014 silakan ulangi.`);
9315
9405
  Progress: ${written.counts?.done ?? 0}/${definitions.length} task selesai.`);
9316
9406
  }
9317
9407
  async function taskStartCommand(ref, options) {
9318
- const { bundleDir, state } = await loadLocalState(options);
9319
- const updated = withStatus(state, ref, "in_progress", {
9320
- awaitingCheckpoint: false,
9321
- note: ""
9408
+ const task = await withLocalState(options, async (local) => {
9409
+ assertStartDependencies(ref, local.definitions, local.state.tasks);
9410
+ const updated = withStatus(local.state, ref, "in_progress", {
9411
+ awaitingCheckpoint: false,
9412
+ note: ""
9413
+ });
9414
+ await writeState(local.bundleDir, updated);
9415
+ return updated.tasks.find((entry) => entry.ref.toUpperCase() === ref.toUpperCase());
9322
9416
  });
9323
- await writeState(bundleDir, updated);
9324
- const task = updated.tasks.find((entry) => entry.ref.toUpperCase() === ref.toUpperCase());
9325
9417
  console.log(`Started ${task?.ref ?? ref} (attempt ${task?.attempt ?? 1}).`);
9326
9418
  }
9327
9419
  async function taskCompleteCommand(ref, options) {
9328
- const { bundleDir, state, definitions } = await loadLocalState(options);
9329
- const completed = withStatus(state, ref, "done", {
9330
- note: options.note ?? "",
9331
- awaitingCheckpoint: false
9332
- });
9333
- const completedTask = completed.tasks.find(
9334
- (entry) => entry.ref.toUpperCase() === ref.toUpperCase()
9335
- );
9336
- const next = selectNext(definitions, completed.tasks);
9337
- if (completedTask !== void 0 && next !== null && isBoundary(completedTask, next.task)) {
9338
- const withCheckpoint = setCurrent(completed, {
9339
- ref: next.task.id,
9340
- ...next.task.layer !== void 0 ? { layer: next.task.layer } : {},
9341
- phase: next.task.phase,
9342
- awaitingCheckpoint: true
9420
+ const { completedTask, next, checkpoint } = await withLocalState(options, async (local) => {
9421
+ assertCompleteDependencies(ref, local.definitions, local.state.tasks, options.force === true);
9422
+ const completed = withStatus(local.state, ref, "done", {
9423
+ note: options.note ?? "",
9424
+ awaitingCheckpoint: false
9343
9425
  });
9344
- await writeState(bundleDir, withCheckpoint);
9345
- console.log(`Done ${completedTask.ref}.`);
9426
+ const completedTask2 = completed.tasks.find(
9427
+ (entry) => entry.ref.toUpperCase() === ref.toUpperCase()
9428
+ );
9429
+ const next2 = selectNext(local.definitions, completed.tasks);
9430
+ if (completedTask2 !== void 0 && next2 !== null && isBoundary(completedTask2, next2.task)) {
9431
+ const withCheckpoint = setCurrent(completed, {
9432
+ ref: next2.task.id,
9433
+ ...next2.task.layer !== void 0 ? { layer: next2.task.layer } : {},
9434
+ phase: next2.task.phase,
9435
+ awaitingCheckpoint: true
9436
+ });
9437
+ await writeState(local.bundleDir, withCheckpoint);
9438
+ return { completedTask: completedTask2, next: next2, checkpoint: true };
9439
+ }
9440
+ await writeState(local.bundleDir, completed);
9441
+ return { completedTask: completedTask2, next: next2, checkpoint: false };
9442
+ });
9443
+ console.log(`Done ${completedTask?.ref ?? ref}.`);
9444
+ if (checkpoint && next !== null) {
9346
9445
  console.log(
9347
9446
  `Checkpoint berikutnya: ${next.task.id} (${next.task.layer ?? "-"} / phase ${next.task.phase}).`
9348
9447
  );
9349
9448
  console.log("Berhenti dan minta konfirmasi user sebelum task start berikutnya.");
9350
9449
  return;
9351
9450
  }
9352
- await writeState(bundleDir, completed);
9353
- console.log(`Done ${completedTask?.ref ?? ref}.`);
9354
9451
  if (next !== null) console.log(`Berikutnya: ${next.task.id} \u2014 ${next.task.title}`);
9355
9452
  }
9356
9453
  async function taskFailCommand(ref, reasonParts, options) {
9357
9454
  const reason = reasonParts.join(" ").trim() || options.note || "";
9358
- const { bundleDir, state } = await loadLocalState(options);
9359
- const updated = withStatus(state, ref, "failed", {
9360
- note: reason,
9361
- awaitingCheckpoint: false
9455
+ await withLocalState(options, async (local) => {
9456
+ const updated = withStatus(local.state, ref, "failed", {
9457
+ note: reason,
9458
+ awaitingCheckpoint: false
9459
+ });
9460
+ await writeState(local.bundleDir, updated);
9362
9461
  });
9363
- await writeState(bundleDir, updated);
9364
9462
  console.log(`Failed ${ref}${reason.length > 0 ? `: ${reason}` : "."}`);
9365
9463
  }
9366
9464
  async function taskResetCommand(ref, reasonParts, options) {
9367
9465
  const reason = reasonParts.join(" ").trim() || options.note || "";
9368
- const { bundleDir, state } = await loadLocalState(options);
9369
- const updated = withStatus(state, ref, "todo", {
9370
- note: reason,
9371
- awaitingCheckpoint: false
9466
+ await withLocalState(options, async (local) => {
9467
+ const updated = withStatus(local.state, ref, "todo", {
9468
+ note: reason,
9469
+ awaitingCheckpoint: false
9470
+ });
9471
+ await writeState(local.bundleDir, updated);
9372
9472
  });
9373
- await writeState(bundleDir, updated);
9374
9473
  console.log(`Reset ${ref} ke todo${reason.length > 0 ? `: ${reason}` : "."}`);
9375
9474
  }
9376
9475
  async function statusCommand(options) {
@@ -9381,6 +9480,23 @@ async function statusCommand(options) {
9381
9480
  const { bundleDir, projectId } = await resolveLocalBundle(options);
9382
9481
  const existing = await readState(bundleDir);
9383
9482
  if (existing === null) {
9483
+ if (options.json === true) {
9484
+ console.log(
9485
+ JSON.stringify(
9486
+ {
9487
+ projectId,
9488
+ hasExecution: false,
9489
+ tasks: [],
9490
+ counts: summarizeExecutionCounts([]),
9491
+ current: null,
9492
+ updatedAt: null
9493
+ },
9494
+ null,
9495
+ 2
9496
+ )
9497
+ );
9498
+ return;
9499
+ }
9384
9500
  console.log(`Project ${projectId}: belum ada sidecar. Jalankan \`jioogen task next\`.`);
9385
9501
  return;
9386
9502
  }
@@ -9487,14 +9603,48 @@ async function resolveBundle(options) {
9487
9603
  const api = await resolveApi({ api: options.api ?? local.linkedApiUrl });
9488
9604
  return { ...api, ...local };
9489
9605
  }
9490
- async function loadLocalState(options) {
9606
+ async function withLocalState(options, fn) {
9491
9607
  const context = await resolveLocalBundle(options);
9492
- const definitions = await loadTasks(context.bundleDir);
9493
- const existing = await readState(context.bundleDir);
9494
- const state = existing === null ? createState(context.projectId, definitions) : mergeTasks(existing, definitions);
9495
- return { ...context, definitions, state };
9608
+ return withStateLock(context.bundleDir, async () => {
9609
+ const definitions = await loadTasks(context.bundleDir);
9610
+ const existing = await readState(context.bundleDir);
9611
+ const state = existing === null ? createState(context.projectId, definitions) : mergeTasks(existing, definitions);
9612
+ return fn({ ...context, definitions, state });
9613
+ });
9614
+ }
9615
+ function unmetDependencies(ref, definitions, states) {
9616
+ const key = ref.toUpperCase();
9617
+ const definition = definitions.find((task) => task.id.toUpperCase() === key);
9618
+ if (definition === void 0) return [];
9619
+ const byRef = new Map(states.map((state) => [state.ref.toUpperCase(), state]));
9620
+ return definition.dependsOn.filter(
9621
+ (dependency) => byRef.get(dependency.toUpperCase())?.status !== "done"
9622
+ );
9623
+ }
9624
+ function assertStartDependencies(ref, definitions, states) {
9625
+ const missing = unmetDependencies(ref, definitions, states);
9626
+ if (missing.length === 0) return;
9627
+ throw new Error(
9628
+ `Tidak bisa memulai ${ref}: dependency belum selesai (${missing.join(", ")}). Selesaikan dependency itu lebih dulu.`
9629
+ );
9630
+ }
9631
+ function assertCompleteDependencies(ref, definitions, states, force) {
9632
+ if (force) return;
9633
+ const missing = unmetDependencies(ref, definitions, states);
9634
+ if (missing.length === 0) return;
9635
+ throw new Error(
9636
+ `Tidak bisa menyelesaikan ${ref}: dependency belum selesai (${missing.join(", ")}). Selesaikan dependency itu lebih dulu, atau tambahkan --force bila memang disengaja.`
9637
+ );
9496
9638
  }
9497
- async function assertPullTarget(target, projectId) {
9639
+ async function assertPullDefaultDir(cwd, dirOption) {
9640
+ if (dirOption) return;
9641
+ if (await hasBundleMarkers(cwd)) {
9642
+ throw new Error(
9643
+ `Direktori ${cwd} adalah bundle JiooGen. Gunakan \`jioogen link <projectId>\` untuk mengikat folder ini, atau \`--dir <dir>\` eksplisit untuk mengunduh ke folder lain.`
9644
+ );
9645
+ }
9646
+ }
9647
+ async function assertPullTarget(target, projectId, force = false) {
9498
9648
  let entries;
9499
9649
  try {
9500
9650
  entries = await readdir3(target);
@@ -9510,6 +9660,11 @@ async function assertPullTarget(target, projectId) {
9510
9660
  if (link.projectId !== projectId) {
9511
9661
  throw new Error(`Direktori ${target} terikat ke project ${link.projectId}.`);
9512
9662
  }
9663
+ if (!force) {
9664
+ throw new Error(
9665
+ `Direktori ${target} sudah berisi bundle project ini; pull ulang akan menimpa edit lokal. Jalankan \`jioogen link ${projectId}\` bila hanya ingin mengikat, atau ulangi dengan --force.`
9666
+ );
9667
+ }
9513
9668
  }
9514
9669
  async function assertLinkTarget(target, projectId) {
9515
9670
  if (!await hasBundleMarkers(target)) {
@@ -9605,7 +9760,7 @@ function isMissingFile4(error) {
9605
9760
  }
9606
9761
 
9607
9762
  // src/index.ts
9608
- var VERSION = "0.2.1";
9763
+ var VERSION = "0.2.3";
9609
9764
  var USAGE = `jioogen \u2014 CLI JiooGen (task selection lokal, server read-only)
9610
9765
 
9611
9766
  Usage:
@@ -9614,14 +9769,14 @@ Usage:
9614
9769
  jioogen logout
9615
9770
  jioogen whoami
9616
9771
  jioogen projects
9617
- jioogen pull <projectId> [--dir <dir>]
9772
+ jioogen pull <projectId> [--dir <dir>] [--force]
9618
9773
  jioogen link <projectId> [--dir <dir>]
9619
9774
  jioogen skills install [--dir <dir>]
9620
9775
  jioogen check [--dir <dir>] [--json]
9621
9776
  jioogen handoff [--project <id>] [--json]
9622
9777
  jioogen task next [--json]
9623
9778
  jioogen task start <ref>
9624
- jioogen task complete <ref> [--note <text>]
9779
+ jioogen task complete <ref> [--note <text>] [--force]
9625
9780
  jioogen task fail <ref> [reason...] [--note <text>]
9626
9781
  jioogen task reset <ref> [reason...] [--note <text>]
9627
9782
  jioogen status [--remote] [--json]
@@ -9637,8 +9792,6 @@ Global options:
9637
9792
  -h, --help Tampilkan bantuan
9638
9793
  -v, --version Tampilkan versi
9639
9794
  `;
9640
- var UsageError = class extends Error {
9641
- };
9642
9795
  async function main() {
9643
9796
  let raw = {};
9644
9797
  let positionals = [];
@@ -9655,6 +9808,7 @@ async function main() {
9655
9808
  project: { type: "string" },
9656
9809
  dir: { type: "string" },
9657
9810
  remote: { type: "boolean" },
9811
+ force: { type: "boolean" },
9658
9812
  files: { type: "string" },
9659
9813
  note: { type: "string" },
9660
9814
  json: { type: "boolean" },
@@ -9680,6 +9834,7 @@ async function main() {
9680
9834
  project: text(raw.project),
9681
9835
  dir: text(raw.dir),
9682
9836
  remote: flag(raw.remote),
9837
+ force: flag(raw.force),
9683
9838
  files: text(raw.files),
9684
9839
  note: text(raw.note),
9685
9840
  json: flag(raw.json)