@takemo101/mikan 0.0.1 → 0.0.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 (3) hide show
  1. package/README.md +59 -3
  2. package/dist/bin.js +1904 -1073
  3. package/package.json +13 -1
package/dist/bin.js CHANGED
@@ -34460,11 +34460,11 @@ import { Buffer as Buffer2 } from "buffer";
34460
34460
  import { Buffer as Buffer3 } from "buffer";
34461
34461
  import { EventEmitter as EventEmitter2 } from "events";
34462
34462
  import { mkdir, writeFile as writeFileNode } from "fs/promises";
34463
- import { dirname as dirname6, isAbsolute, resolve as resolve4 } from "path";
34463
+ import { dirname as dirname7, isAbsolute, resolve as resolve4 } from "path";
34464
34464
  import { fileURLToPath } from "url";
34465
34465
  import { resolve as resolve22, isAbsolute as isAbsolute2, parse as parse7 } from "path";
34466
- import { existsSync as existsSync6 } from "fs";
34467
- import { basename as basename3, join as join7 } from "path";
34466
+ import { existsSync as existsSync8 } from "fs";
34467
+ import { basename as basename4, join as join9 } from "path";
34468
34468
  import os4 from "os";
34469
34469
  import path4 from "path";
34470
34470
  import { EventEmitter as EventEmitter3 } from "events";
@@ -38016,7 +38016,7 @@ async function resolveBundledFilePath(loadBundledFile, fallbackPath, metaUrl) {
38016
38016
  if (isAbsolute(loadedPath)) {
38017
38017
  return loadedPath;
38018
38018
  }
38019
- return resolve4(dirname6(fileURLToPath(metaUrl)), loadedPath);
38019
+ return resolve4(dirname7(fileURLToPath(metaUrl)), loadedPath);
38020
38020
  }
38021
38021
  function standardSleep(msOrDate) {
38022
38022
  const ms = msOrDate instanceof Date ? msOrDate.getTime() - Date.now() : msOrDate;
@@ -38025,7 +38025,7 @@ function standardSleep(msOrDate) {
38025
38025
  async function writeFilePortable(destination, data, options) {
38026
38026
  const destinationPath = destination instanceof URL ? fileURLToPath(destination) : destination;
38027
38027
  if (options?.createPath) {
38028
- await mkdir(dirname6(destinationPath), { recursive: true });
38028
+ await mkdir(dirname7(destinationPath), { recursive: true });
38029
38029
  }
38030
38030
  const bytes = typeof data === "string" ? TEXT_ENCODER.encode(data) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
38031
38031
  await writeFileNode(destinationPath, bytes, { mode: options?.mode });
@@ -38115,7 +38115,7 @@ function getBunfsRootPath() {
38115
38115
  return process.platform === "win32" ? "B:\\~BUN\\root" : "/$bunfs/root";
38116
38116
  }
38117
38117
  function normalizeBunfsPath(fileName) {
38118
- return join7(getBunfsRootPath(), basename3(fileName));
38118
+ return join9(getBunfsRootPath(), basename4(fileName));
38119
38119
  }
38120
38120
 
38121
38121
  class UnsupportedWorker {
@@ -50485,7 +50485,7 @@ var init_index_081xws23 = __esm(async () => {
50485
50485
  return this.options.workerPath;
50486
50486
  }
50487
50487
  let workerPath = new URL("./parser.worker.js", import.meta.url).href;
50488
- if (!existsSync6(resolve22(import.meta.dirname, "parser.worker.js"))) {
50488
+ if (!existsSync8(resolve22(import.meta.dirname, "parser.worker.js"))) {
50489
50489
  workerPath = new URL("./parser.worker.ts", import.meta.url).href;
50490
50490
  }
50491
50491
  return workerPath;
@@ -70949,7 +70949,7 @@ var init_chunk_2mx7fq49 = () => {};
70949
70949
 
70950
70950
  // ../../node_modules/.bun/react@19.2.6/node_modules/react/cjs/react-jsx-dev-runtime.development.js
70951
70951
  var require_react_jsx_dev_runtime_development = __commonJS((exports) => {
70952
- var React = __toESM(require_react());
70952
+ var React4 = __toESM(require_react());
70953
70953
  (function() {
70954
70954
  function getComponentNameFromType(type) {
70955
70955
  if (type == null)
@@ -71141,17 +71141,17 @@ React keys must be passed directly to JSX without using spread:
71141
71141
  function isValidElement(object2) {
71142
71142
  return typeof object2 === "object" && object2 !== null && object2.$$typeof === REACT_ELEMENT_TYPE;
71143
71143
  }
71144
- var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"), REACT_PORTAL_TYPE = Symbol.for("react.portal"), REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"), REACT_STRICT_MODE_TYPE = Symbol.for("react.strict_mode"), REACT_PROFILER_TYPE = Symbol.for("react.profiler"), REACT_CONSUMER_TYPE = Symbol.for("react.consumer"), REACT_CONTEXT_TYPE = Symbol.for("react.context"), REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref"), REACT_SUSPENSE_TYPE = Symbol.for("react.suspense"), REACT_SUSPENSE_LIST_TYPE = Symbol.for("react.suspense_list"), REACT_MEMO_TYPE = Symbol.for("react.memo"), REACT_LAZY_TYPE = Symbol.for("react.lazy"), REACT_ACTIVITY_TYPE = Symbol.for("react.activity"), REACT_CLIENT_REFERENCE = Symbol.for("react.client.reference"), ReactSharedInternals = React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, hasOwnProperty = Object.prototype.hasOwnProperty, isArrayImpl = Array.isArray, createTask = console.createTask ? console.createTask : function() {
71144
+ var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"), REACT_PORTAL_TYPE = Symbol.for("react.portal"), REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"), REACT_STRICT_MODE_TYPE = Symbol.for("react.strict_mode"), REACT_PROFILER_TYPE = Symbol.for("react.profiler"), REACT_CONSUMER_TYPE = Symbol.for("react.consumer"), REACT_CONTEXT_TYPE = Symbol.for("react.context"), REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref"), REACT_SUSPENSE_TYPE = Symbol.for("react.suspense"), REACT_SUSPENSE_LIST_TYPE = Symbol.for("react.suspense_list"), REACT_MEMO_TYPE = Symbol.for("react.memo"), REACT_LAZY_TYPE = Symbol.for("react.lazy"), REACT_ACTIVITY_TYPE = Symbol.for("react.activity"), REACT_CLIENT_REFERENCE = Symbol.for("react.client.reference"), ReactSharedInternals = React4.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, hasOwnProperty = Object.prototype.hasOwnProperty, isArrayImpl = Array.isArray, createTask = console.createTask ? console.createTask : function() {
71145
71145
  return null;
71146
71146
  };
71147
- React = {
71147
+ React4 = {
71148
71148
  react_stack_bottom_frame: function(callStackForError) {
71149
71149
  return callStackForError();
71150
71150
  }
71151
71151
  };
71152
71152
  var specialPropKeyWarningShown;
71153
71153
  var didWarnAboutElementRef = {};
71154
- var unknownOwnerDebugStack = React.react_stack_bottom_frame.bind(React, UnknownOwner)();
71154
+ var unknownOwnerDebugStack = React4.react_stack_bottom_frame.bind(React4, UnknownOwner)();
71155
71155
  var unknownOwnerDebugTask = createTask(getTaskName(UnknownOwner));
71156
71156
  var didWarnAboutKeySpread = {};
71157
71157
  exports.Fragment = REACT_FRAGMENT_TYPE;
@@ -71435,7 +71435,7 @@ var require_scheduler = __commonJS((exports, module) => {
71435
71435
 
71436
71436
  // ../../node_modules/.bun/react-reconciler@0.33.0+d86b59289c1a13ae/node_modules/react-reconciler/cjs/react-reconciler.development.js
71437
71437
  var require_react_reconciler_development = __commonJS((exports, module) => {
71438
- var React = __toESM(require_react());
71438
+ var React4 = __toESM(require_react());
71439
71439
  var Scheduler = __toESM(require_scheduler());
71440
71440
  module.exports = function($$$config) {
71441
71441
  function findHook(fiber, id) {
@@ -80057,7 +80057,7 @@ Check the render method of %s.`, getComponentNameFromFiber(current) || "Unknown"
80057
80057
  Symbol.for("react.tracing_marker");
80058
80058
  var REACT_MEMO_CACHE_SENTINEL = Symbol.for("react.memo_cache_sentinel");
80059
80059
  Symbol.for("react.view_transition");
80060
- var MAYBE_ITERATOR_SYMBOL = Symbol.iterator, REACT_CLIENT_REFERENCE = Symbol.for("react.client.reference"), isArrayImpl = Array.isArray, ReactSharedInternals = React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, rendererVersion = $$$config.rendererVersion, rendererPackageName = $$$config.rendererPackageName, extraDevToolsConfig = $$$config.extraDevToolsConfig, getPublicInstance = $$$config.getPublicInstance, getRootHostContext = $$$config.getRootHostContext, getChildHostContext = $$$config.getChildHostContext, prepareForCommit = $$$config.prepareForCommit, resetAfterCommit = $$$config.resetAfterCommit, createInstance = $$$config.createInstance;
80060
+ var MAYBE_ITERATOR_SYMBOL = Symbol.iterator, REACT_CLIENT_REFERENCE = Symbol.for("react.client.reference"), isArrayImpl = Array.isArray, ReactSharedInternals = React4.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, rendererVersion = $$$config.rendererVersion, rendererPackageName = $$$config.rendererPackageName, extraDevToolsConfig = $$$config.extraDevToolsConfig, getPublicInstance = $$$config.getPublicInstance, getRootHostContext = $$$config.getRootHostContext, getChildHostContext = $$$config.getChildHostContext, prepareForCommit = $$$config.prepareForCommit, resetAfterCommit = $$$config.resetAfterCommit, createInstance = $$$config.createInstance;
80061
80061
  $$$config.cloneMutableInstance;
80062
80062
  var { appendInitialChild, finalizeInitialChildren, shouldSetTextContent, createTextInstance } = $$$config;
80063
80063
  $$$config.cloneMutableTextInstance;
@@ -81926,7 +81926,7 @@ var require_backend = __commonJS((exports, module) => {
81926
81926
  return o2 && typeof Symbol == "function" && o2.constructor === Symbol && o2 !== Symbol.prototype ? "symbol" : typeof o2;
81927
81927
  }, _typeof(o);
81928
81928
  }
81929
- var ErrorStackParser = __webpack_require__2(206), React = __webpack_require__2(189), assign = Object.assign, ReactSharedInternals = React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, REACT_CONTEXT_TYPE = Symbol.for("react.context"), REACT_MEMO_CACHE_SENTINEL = Symbol.for("react.memo_cache_sentinel"), hasOwnProperty = Object.prototype.hasOwnProperty, hookLog = [], primitiveStackCache = null;
81929
+ var ErrorStackParser = __webpack_require__2(206), React4 = __webpack_require__2(189), assign = Object.assign, ReactSharedInternals = React4.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, REACT_CONTEXT_TYPE = Symbol.for("react.context"), REACT_MEMO_CACHE_SENTINEL = Symbol.for("react.memo_cache_sentinel"), hasOwnProperty = Object.prototype.hasOwnProperty, hookLog = [], primitiveStackCache = null;
81930
81930
  function getPrimitiveStackCache() {
81931
81931
  if (primitiveStackCache === null) {
81932
81932
  var cache = new Map;
@@ -98861,13 +98861,13 @@ function createRoot(renderer) {
98861
98861
  return {
98862
98862
  render: (node) => {
98863
98863
  engine.attach(renderer);
98864
- container = _render(import_react2.default.createElement(AppContext.Provider, { value: { keyHandler: renderer.keyInput, renderer } }, import_react2.default.createElement(ErrorBoundary, null, node)), renderer.root);
98864
+ container = _render(import_react5.default.createElement(AppContext.Provider, { value: { keyHandler: renderer.keyInput, renderer } }, import_react5.default.createElement(ErrorBoundary, null, node)), renderer.root);
98865
98865
  },
98866
98866
  unmount: cleanup
98867
98867
  };
98868
98868
  }
98869
- var import_react, import_react2, import_react3, import_jsx_dev_runtime, import_react_reconciler, import_constants, import_react4, import_constants2, textNodeKeys, SpanRenderable, TextModifierRenderable, BoldSpanRenderable, ItalicSpanRenderable, UnderlineSpanRenderable, LineBreakRenderable, LinkRenderable, baseComponents, componentCatalogue, AppContext, useAppContext = () => {
98870
- return import_react.useContext(AppContext);
98869
+ var import_react4, import_react5, import_react6, import_jsx_dev_runtime, import_react_reconciler, import_constants, import_react7, import_constants2, textNodeKeys, SpanRenderable, TextModifierRenderable, BoldSpanRenderable, ItalicSpanRenderable, UnderlineSpanRenderable, LineBreakRenderable, LinkRenderable, baseComponents, componentCatalogue, AppContext, useAppContext = () => {
98870
+ return import_react4.useContext(AppContext);
98871
98871
  }, ErrorBoundary, package_default2, idCounter, currentUpdatePriority, hostConfig, reconciler, _r, flushSync, createPortal;
98872
98872
  var init_chunk_w83f8rxy = __esm(async () => {
98873
98873
  init_chunk_2mx7fq49();
@@ -98878,13 +98878,13 @@ var init_chunk_w83f8rxy = __esm(async () => {
98878
98878
  init_core3(),
98879
98879
  init_core3()
98880
98880
  ]);
98881
- import_react = __toESM(require_react(), 1);
98882
- import_react2 = __toESM(require_react(), 1);
98883
- import_react3 = __toESM(require_react(), 1);
98881
+ import_react4 = __toESM(require_react(), 1);
98882
+ import_react5 = __toESM(require_react(), 1);
98883
+ import_react6 = __toESM(require_react(), 1);
98884
98884
  import_jsx_dev_runtime = __toESM(require_jsx_dev_runtime(), 1);
98885
98885
  import_react_reconciler = __toESM(require_react_reconciler(), 1);
98886
98886
  import_constants = __toESM(require_constants(), 1);
98887
- import_react4 = __toESM(require_react(), 1);
98887
+ import_react7 = __toESM(require_react(), 1);
98888
98888
  import_constants2 = __toESM(require_constants(), 1);
98889
98889
  textNodeKeys = ["span", "b", "strong", "i", "em", "u", "br", "a"];
98890
98890
  SpanRenderable = class SpanRenderable extends TextNodeRenderable {
@@ -98963,11 +98963,11 @@ var init_chunk_w83f8rxy = __esm(async () => {
98963
98963
  a: LinkRenderable
98964
98964
  };
98965
98965
  componentCatalogue = { ...baseComponents };
98966
- AppContext = import_react.createContext({
98966
+ AppContext = import_react4.createContext({
98967
98967
  keyHandler: null,
98968
98968
  renderer: null
98969
98969
  });
98970
- ErrorBoundary = class ErrorBoundary extends import_react3.default.Component {
98970
+ ErrorBoundary = class ErrorBoundary extends import_react6.default.Component {
98971
98971
  constructor(props) {
98972
98972
  super(props);
98973
98973
  this.state = { hasError: false, error: null };
@@ -99183,7 +99183,7 @@ var init_chunk_w83f8rxy = __esm(async () => {
99183
99183
  return false;
99184
99184
  },
99185
99185
  NotPendingTransition: null,
99186
- HostTransitionContext: import_react4.createContext(null),
99186
+ HostTransitionContext: import_react7.createContext(null),
99187
99187
  resetFormInstance() {},
99188
99188
  requestPostPaintCallback() {},
99189
99189
  trackSchedulerEvent() {},
@@ -99269,7 +99269,7 @@ __export(exports_react, {
99269
99269
  createRoot: () => createRoot,
99270
99270
  createReactSlotRegistry: () => createReactSlotRegistry,
99271
99271
  createPortal: () => createPortal,
99272
- createElement: () => import_react16.createElement,
99272
+ createElement: () => import_react19.createElement,
99273
99273
  componentCatalogue: () => componentCatalogue,
99274
99274
  baseComponents: () => baseComponents,
99275
99275
  TimeToFirstDraw: () => TimeToFirstDraw,
@@ -99277,11 +99277,11 @@ __export(exports_react, {
99277
99277
  AppContext: () => AppContext
99278
99278
  });
99279
99279
  function useEffectEvent(handler) {
99280
- const handlerRef = import_react6.useRef(handler);
99281
- import_react6.useLayoutEffect(() => {
99280
+ const handlerRef = import_react9.useRef(handler);
99281
+ import_react9.useLayoutEffect(() => {
99282
99282
  handlerRef.current = handler;
99283
99283
  });
99284
- return import_react6.useCallback((...args) => {
99284
+ return import_react9.useCallback((...args) => {
99285
99285
  const fn = handlerRef.current;
99286
99286
  return fn(...args);
99287
99287
  }, []);
@@ -99327,17 +99327,17 @@ function createSlot(registry2, options = {}) {
99327
99327
  };
99328
99328
  }
99329
99329
  function Slot(props) {
99330
- const [version2, setVersion] = import_react14.useState(0);
99330
+ const [version2, setVersion] = import_react17.useState(0);
99331
99331
  const registry2 = props.registry;
99332
99332
  const slotName = String(props.name);
99333
- const renderFailuresByPluginRef = import_react14.useRef(new Map);
99334
- const pendingRenderReportsRef = import_react14.useRef(new Map);
99335
- import_react14.useEffect(() => {
99333
+ const renderFailuresByPluginRef = import_react17.useRef(new Map);
99334
+ const pendingRenderReportsRef = import_react17.useRef(new Map);
99335
+ import_react17.useEffect(() => {
99336
99336
  return registry2.subscribe(() => {
99337
99337
  setVersion((current) => current + 1);
99338
99338
  });
99339
99339
  }, [registry2]);
99340
- import_react14.useEffect(() => {
99340
+ import_react17.useEffect(() => {
99341
99341
  if (pendingRenderReportsRef.current.size === 0) {
99342
99342
  return;
99343
99343
  }
@@ -99354,7 +99354,7 @@ function Slot(props) {
99354
99354
  renderFailuresByPluginRef.current.set(`${report.slot}:${report.pluginId}:render`, failure);
99355
99355
  }
99356
99356
  });
99357
- const entries = import_react14.useMemo(() => registry2.resolveEntries(props.name), [registry2, props.name, version2]);
99357
+ const entries = import_react17.useMemo(() => registry2.resolveEntries(props.name), [registry2, props.name, version2]);
99358
99358
  const slotProps = getSlotProps(props);
99359
99359
  const renderEntry = (entry, fallbackOnFailure) => {
99360
99360
  const key = `${slotName}:${entry.id}`;
@@ -99399,7 +99399,7 @@ function Slot(props) {
99399
99399
  if (placeholder === null || placeholder === undefined || placeholder === false) {
99400
99400
  return fallbackOnFailure ?? null;
99401
99401
  }
99402
- return /* @__PURE__ */ import_jsx_dev_runtime.jsxDEV(import_react14.Fragment, {
99402
+ return /* @__PURE__ */ import_jsx_dev_runtime.jsxDEV(import_react17.Fragment, {
99403
99403
  children: placeholder
99404
99404
  }, key, false, undefined, this);
99405
99405
  }
@@ -99456,7 +99456,7 @@ function Slot(props) {
99456
99456
  ]
99457
99457
  }, undefined, true, undefined, this);
99458
99458
  }
99459
- var import_react5, import_react6, import_react7, import_react8, import_react9, import_react10, import_react11, import_react12, import_react13, import_react14, import_react15, import_react16, useRenderer = () => {
99459
+ var import_react8, import_react9, import_react10, import_react11, import_react12, import_react13, import_react14, import_react15, import_react16, import_react17, import_react18, import_react19, useRenderer = () => {
99460
99460
  const { renderer } = useAppContext();
99461
99461
  if (!renderer) {
99462
99462
  throw new Error("Renderer not found.");
@@ -99465,7 +99465,7 @@ var import_react5, import_react6, import_react7, import_react8, import_react9, i
99465
99465
  }, useBlur = (handler) => {
99466
99466
  const renderer = useRenderer();
99467
99467
  const stableHandler = useEffectEvent(handler);
99468
- import_react5.useEffect(() => {
99468
+ import_react8.useEffect(() => {
99469
99469
  renderer.on("blur", stableHandler);
99470
99470
  return () => {
99471
99471
  renderer.off("blur", stableHandler);
@@ -99474,7 +99474,7 @@ var import_react5, import_react6, import_react7, import_react8, import_react9, i
99474
99474
  }, useFocus = (handler) => {
99475
99475
  const renderer = useRenderer();
99476
99476
  const stableHandler = useEffectEvent(handler);
99477
- import_react7.useEffect(() => {
99477
+ import_react10.useEffect(() => {
99478
99478
  renderer.on("focus", stableHandler);
99479
99479
  return () => {
99480
99480
  renderer.off("focus", stableHandler);
@@ -99483,7 +99483,7 @@ var import_react5, import_react6, import_react7, import_react8, import_react9, i
99483
99483
  }, useKeyboard = (handler, options = { release: false }) => {
99484
99484
  const { keyHandler } = useAppContext();
99485
99485
  const stableHandler = useEffectEvent(handler);
99486
- import_react8.useEffect(() => {
99486
+ import_react11.useEffect(() => {
99487
99487
  keyHandler?.on("keypress", stableHandler);
99488
99488
  if (options?.release) {
99489
99489
  keyHandler?.on("keyrelease", stableHandler);
@@ -99498,7 +99498,7 @@ var import_react5, import_react6, import_react7, import_react8, import_react9, i
99498
99498
  }, usePaste = (handler) => {
99499
99499
  const { keyHandler } = useAppContext();
99500
99500
  const stableHandler = useEffectEvent(handler);
99501
- import_react9.useEffect(() => {
99501
+ import_react12.useEffect(() => {
99502
99502
  keyHandler?.on("paste", stableHandler);
99503
99503
  return () => {
99504
99504
  keyHandler?.off("paste", stableHandler);
@@ -99507,7 +99507,7 @@ var import_react5, import_react6, import_react7, import_react8, import_react9, i
99507
99507
  }, useOnResize = (callback) => {
99508
99508
  const renderer = useRenderer();
99509
99509
  const stableCallback = useEffectEvent(callback);
99510
- import_react10.useEffect(() => {
99510
+ import_react13.useEffect(() => {
99511
99511
  renderer.on("resize", stableCallback);
99512
99512
  return () => {
99513
99513
  renderer.off("resize", stableCallback);
@@ -99517,7 +99517,7 @@ var import_react5, import_react6, import_react7, import_react8, import_react9, i
99517
99517
  }, useSelectionHandler = (handler) => {
99518
99518
  const renderer = useRenderer();
99519
99519
  const stableHandler = useEffectEvent(handler);
99520
- import_react11.useEffect(() => {
99520
+ import_react14.useEffect(() => {
99521
99521
  renderer.on("selection", stableHandler);
99522
99522
  return () => {
99523
99523
  renderer.off("selection", stableHandler);
@@ -99525,7 +99525,7 @@ var import_react5, import_react6, import_react7, import_react8, import_react9, i
99525
99525
  }, [renderer]);
99526
99526
  }, useTerminalDimensions = () => {
99527
99527
  const renderer = useRenderer();
99528
- const [dimensions, setDimensions] = import_react12.useState({
99528
+ const [dimensions, setDimensions] = import_react15.useState({
99529
99529
  width: renderer.width,
99530
99530
  height: renderer.height
99531
99531
  });
@@ -99536,7 +99536,7 @@ var import_react5, import_react6, import_react7, import_react8, import_react9, i
99536
99536
  return dimensions;
99537
99537
  }, useTimeline = (options = {}) => {
99538
99538
  const timeline = new Timeline(options);
99539
- import_react13.useEffect(() => {
99539
+ import_react16.useEffect(() => {
99540
99540
  if (!options.autoplay) {
99541
99541
  timeline.play();
99542
99542
  }
@@ -99548,7 +99548,7 @@ var import_react5, import_react6, import_react7, import_react8, import_react9, i
99548
99548
  }, []);
99549
99549
  return timeline;
99550
99550
  }, PluginErrorBoundary, TimeToFirstDraw = (props) => {
99551
- return import_react15.createElement("time-to-first-draw", props);
99551
+ return import_react18.createElement("time-to-first-draw", props);
99552
99552
  };
99553
99553
  var init_react = __esm(async () => {
99554
99554
  init_chunk_2mx7fq49();
@@ -99558,9 +99558,6 @@ var init_react = __esm(async () => {
99558
99558
  init_core3(),
99559
99559
  init_core3()
99560
99560
  ]);
99561
- import_react5 = __toESM(require_react(), 1);
99562
- import_react6 = __toESM(require_react(), 1);
99563
- import_react7 = __toESM(require_react(), 1);
99564
99561
  import_react8 = __toESM(require_react(), 1);
99565
99562
  import_react9 = __toESM(require_react(), 1);
99566
99563
  import_react10 = __toESM(require_react(), 1);
@@ -99570,7 +99567,10 @@ var init_react = __esm(async () => {
99570
99567
  import_react14 = __toESM(require_react(), 1);
99571
99568
  import_react15 = __toESM(require_react(), 1);
99572
99569
  import_react16 = __toESM(require_react(), 1);
99573
- PluginErrorBoundary = class PluginErrorBoundary extends import_react14.default.Component {
99570
+ import_react17 = __toESM(require_react(), 1);
99571
+ import_react18 = __toESM(require_react(), 1);
99572
+ import_react19 = __toESM(require_react(), 1);
99573
+ PluginErrorBoundary = class PluginErrorBoundary extends import_react17.default.Component {
99574
99574
  constructor(props) {
99575
99575
  super(props);
99576
99576
  this.state = { failure: null };
@@ -99604,22 +99604,88 @@ var init_react = __esm(async () => {
99604
99604
  extend2({ "time-to-first-draw": TimeToFirstDrawRenderable });
99605
99605
  });
99606
99606
 
99607
- // src/index.ts
99608
- import { readFileSync as readFileSync9 } from "fs";
99609
- import { basename as basename4, join as join9 } from "path";
99607
+ // ../mcp/src/index.ts
99608
+ import { readFileSync as readFileSync7 } from "fs";
99610
99609
 
99611
- // ../core/src/index.ts
99612
- import {
99613
- existsSync,
99614
- mkdirSync,
99615
- readdirSync,
99616
- readFileSync,
99617
- renameSync,
99618
- rmSync,
99619
- statSync,
99620
- writeFileSync
99621
- } from "fs";
99622
- import { basename, dirname, join } from "path";
99610
+ // ../core/src/board-scan.ts
99611
+ import { existsSync, readdirSync, readFileSync, statSync } from "fs";
99612
+ import { join } from "path";
99613
+
99614
+ // ../core/src/dependency.ts
99615
+ function deriveDependencyState(byId, warnings) {
99616
+ for (const item of [...byId.values()].flat()) {
99617
+ const issueId = String(item.issue.id);
99618
+ const unmet = new Map;
99619
+ for (const dependency of item.issue.dependencies) {
99620
+ const dependencyId = String(dependency);
99621
+ if (dependencyId === issueId) {
99622
+ unmet.set(dependencyId, dependency);
99623
+ warnings.push({
99624
+ kind: "dependency_self",
99625
+ message: `${issueId} depends on itself`,
99626
+ issueId,
99627
+ path: item.path
99628
+ });
99629
+ continue;
99630
+ }
99631
+ const matches = byId.get(dependencyId) ?? [];
99632
+ const target = matches[0];
99633
+ if (!target) {
99634
+ unmet.set(dependencyId, dependency);
99635
+ warnings.push({
99636
+ kind: "dependency_missing",
99637
+ message: `${issueId} depends on missing Issue ${dependencyId}`,
99638
+ issueId,
99639
+ path: item.path
99640
+ });
99641
+ continue;
99642
+ }
99643
+ if (hasDependencyPath(dependencyId, issueId, byId)) {
99644
+ unmet.set(dependencyId, dependency);
99645
+ warnings.push({
99646
+ kind: "dependency_cycle",
99647
+ message: `${issueId} has cyclic dependency through ${dependencyId}`,
99648
+ issueId,
99649
+ path: item.path
99650
+ });
99651
+ continue;
99652
+ }
99653
+ const targetStatus = String(target.status);
99654
+ if (targetStatus === "archived") {
99655
+ unmet.set(dependencyId, dependency);
99656
+ warnings.push({
99657
+ kind: "dependency_archived",
99658
+ message: `${issueId} depends on archived Issue ${dependencyId}`,
99659
+ issueId,
99660
+ path: item.path
99661
+ });
99662
+ continue;
99663
+ }
99664
+ if (targetStatus !== "completed") {
99665
+ unmet.set(dependencyId, dependency);
99666
+ warnings.push({
99667
+ kind: "dependency_incomplete",
99668
+ message: `${issueId} depends on incomplete Issue ${dependencyId}`,
99669
+ issueId,
99670
+ path: item.path
99671
+ });
99672
+ }
99673
+ }
99674
+ item.unmetDependencies = [...unmet.values()];
99675
+ item.dependencyStatus = item.unmetDependencies.length > 0 ? "blocked" : "ready";
99676
+ }
99677
+ }
99678
+ function hasDependencyPath(fromId, toId, byId, seen = new Set) {
99679
+ if (fromId === toId)
99680
+ return true;
99681
+ if (seen.has(fromId))
99682
+ return false;
99683
+ seen.add(fromId);
99684
+ const [item] = byId.get(fromId) ?? [];
99685
+ if (!item)
99686
+ return false;
99687
+ return item.issue.dependencies.some((dependency) => hasDependencyPath(String(dependency), toId, byId, seen));
99688
+ }
99623
99689
 
99624
99690
  // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/index.js
99625
99691
  var composer = require_composer();
@@ -99671,18 +99737,11 @@ var $visitAsync = visit.visitAsync;
99671
99737
  init_external();
99672
99738
  init_external();
99673
99739
 
99674
- // ../core/src/index.ts
99740
+ // ../core/src/primitives.ts
99675
99741
  var issueIdPattern = /^[A-Z][A-Z0-9]*-\d{3,}$/;
99676
99742
  var statusOrLabelPattern = /^[a-z][a-z0-9-]*$/;
99677
99743
  var projectKeyPattern = /^[A-Z][A-Z0-9]*$/;
99678
99744
  var utcTimestampPattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/;
99679
- var frontmatterSchema = exports_external.object({
99680
- id: exports_external.string().min(1),
99681
- title: exports_external.string().min(1),
99682
- labels: exports_external.array(exports_external.string()).optional().default([]),
99683
- created_at: exports_external.string().min(1),
99684
- updated_at: exports_external.string().min(1)
99685
- }).passthrough();
99686
99745
  function parseIssueId(input) {
99687
99746
  if (!issueIdPattern.test(input)) {
99688
99747
  return invalidFrontmatter(`id must look like MIK-001: ${input}`);
@@ -99715,12 +99774,124 @@ function parseUtcTimestamp(input) {
99715
99774
  }
99716
99775
  return { ok: true, value: input };
99717
99776
  }
99777
+ function invalidFrontmatter(message) {
99778
+ return invalidFrontmatterResult(message);
99779
+ }
99780
+ function invalidFrontmatterResult(message) {
99781
+ return {
99782
+ ok: false,
99783
+ error: {
99784
+ kind: "invalid_frontmatter",
99785
+ message
99786
+ }
99787
+ };
99788
+ }
99789
+
99790
+ // ../core/src/issue-markdown.ts
99791
+ var frontmatterSchema = exports_external.object({
99792
+ id: exports_external.string().min(1),
99793
+ title: exports_external.string().min(1),
99794
+ labels: exports_external.array(exports_external.string()).optional().default([]),
99795
+ depends_on: exports_external.array(exports_external.string()).optional().default([]),
99796
+ created_at: exports_external.string().min(1),
99797
+ updated_at: exports_external.string().min(1)
99798
+ }).passthrough();
99718
99799
  function parseIssueMarkdown(markdown) {
99719
99800
  const parsed = parseIssueDocument(markdown);
99720
99801
  if (!parsed.ok)
99721
99802
  return parsed;
99722
99803
  return { ok: true, value: parsed.value.issue };
99723
99804
  }
99805
+ function parseIssueDocument(markdown) {
99806
+ const frontmatter = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(markdown);
99807
+ if (!frontmatter) {
99808
+ return {
99809
+ ok: false,
99810
+ error: {
99811
+ kind: "missing_frontmatter",
99812
+ message: "Issue Markdown must start with YAML frontmatter"
99813
+ }
99814
+ };
99815
+ }
99816
+ const frontmatterSource = frontmatter[1] ?? "";
99817
+ let rawFrontmatter;
99818
+ try {
99819
+ rawFrontmatter = $parse(frontmatterSource);
99820
+ } catch (error51) {
99821
+ return invalidFrontmatterResult(error51 instanceof Error ? error51.message : String(error51));
99822
+ }
99823
+ const parsedFrontmatter = frontmatterSchema.safeParse(rawFrontmatter);
99824
+ if (!parsedFrontmatter.success) {
99825
+ return invalidFrontmatterResult(parsedFrontmatter.error.issues.map(formatZodIssue).join("; "));
99826
+ }
99827
+ const errors4 = [];
99828
+ let issueId;
99829
+ const id = parseIssueId(parsedFrontmatter.data.id);
99830
+ if (id.ok)
99831
+ issueId = id.value;
99832
+ else
99833
+ errors4.push(id.error.message);
99834
+ const labels = [];
99835
+ for (const rawLabel of parsedFrontmatter.data.labels) {
99836
+ const label = parseLabelId(rawLabel);
99837
+ if (label.ok)
99838
+ labels.push(label.value);
99839
+ else
99840
+ errors4.push(label.error.message);
99841
+ }
99842
+ const dependencies = [];
99843
+ for (const rawDependency of parsedFrontmatter.data.depends_on) {
99844
+ const dependency = parseIssueId(rawDependency);
99845
+ if (dependency.ok)
99846
+ dependencies.push(dependency.value);
99847
+ else
99848
+ errors4.push(`depends_on: ${dependency.error.message}`);
99849
+ }
99850
+ let createdAt;
99851
+ const parsedCreatedAt = parseUtcTimestamp(parsedFrontmatter.data.created_at);
99852
+ if (parsedCreatedAt.ok)
99853
+ createdAt = parsedCreatedAt.value;
99854
+ else
99855
+ errors4.push(`created_at: ${parsedCreatedAt.error.message}`);
99856
+ let updatedAt;
99857
+ const parsedUpdatedAt = parseUtcTimestamp(parsedFrontmatter.data.updated_at);
99858
+ if (parsedUpdatedAt.ok)
99859
+ updatedAt = parsedUpdatedAt.value;
99860
+ else
99861
+ errors4.push(`updated_at: ${parsedUpdatedAt.error.message}`);
99862
+ if (errors4.length > 0 || !issueId || !createdAt || !updatedAt) {
99863
+ return invalidFrontmatterResult(errors4.join("; "));
99864
+ }
99865
+ return {
99866
+ ok: true,
99867
+ value: {
99868
+ frontmatter: parsedFrontmatter.data,
99869
+ issue: {
99870
+ id: issueId,
99871
+ title: parsedFrontmatter.data.title,
99872
+ labels,
99873
+ dependencies,
99874
+ createdAt,
99875
+ updatedAt,
99876
+ body: markdown.slice(frontmatter[0].length)
99877
+ }
99878
+ }
99879
+ };
99880
+ }
99881
+ function serializeIssue(input) {
99882
+ return `---
99883
+ ${$stringify(input.frontmatter).trim()}
99884
+ ---
99885
+ ${input.body.startsWith(`
99886
+ `) ? "" : `
99887
+ `}${input.body}`;
99888
+ }
99889
+ function formatZodIssue(issue2) {
99890
+ const path = issue2.path.length > 0 ? `${issue2.path.join(".")}: ` : "";
99891
+ return `${path}${issue2.message}`;
99892
+ }
99893
+
99894
+ // ../core/src/board-scan.ts
99724
99895
  function scanBoard(options) {
99725
99896
  const mikanRoot = join(options.projectRoot, ".mikan");
99726
99897
  const configuredStatuses = options.config.board.columns.map((column) => column.id);
@@ -99755,7 +99926,9 @@ function scanBoard(options) {
99755
99926
  const item = {
99756
99927
  issue: parsed.value,
99757
99928
  status: status.value,
99758
- path
99929
+ path,
99930
+ unmetDependencies: [],
99931
+ dependencyStatus: "ready"
99759
99932
  };
99760
99933
  const id = String(parsed.value.id);
99761
99934
  const matches = byId.get(id) ?? [];
@@ -99784,6 +99957,7 @@ function scanBoard(options) {
99784
99957
  });
99785
99958
  }
99786
99959
  }
99960
+ deriveDependencyState(byId, warnings);
99787
99961
  if (existsSync(mikanRoot)) {
99788
99962
  const configured = new Set(configuredStatuses.concat([".state", "templates"]));
99789
99963
  for (const entry of readdirSync(mikanRoot, { withFileTypes: true })) {
@@ -99864,6 +100038,101 @@ function findIssueById(options) {
99864
100038
  }
99865
100039
  return { ok: true, value: matches[0] };
99866
100040
  }
100041
+ function sortedMarkdownFiles(directory) {
100042
+ return readdirSync(directory).filter((entry) => entry.endsWith(".md")).filter((entry) => statSync(join(directory, entry)).isFile()).sort();
100043
+ }
100044
+ function readHookFailureWarnings(projectRoot) {
100045
+ const path = join(projectRoot, ".mikan", ".state", "hook-log.ndjson");
100046
+ if (!existsSync(path))
100047
+ return [];
100048
+ return readFileSync(path, "utf8").split(`
100049
+ `).filter((line) => line.trim().length > 0).flatMap((line) => {
100050
+ let entry;
100051
+ try {
100052
+ entry = JSON.parse(line);
100053
+ } catch {
100054
+ return [];
100055
+ }
100056
+ if (!isHookFailureEntry(entry))
100057
+ return [];
100058
+ const issueId = entry.issue_id;
100059
+ const command = entry.command;
100060
+ const exitCode = entry.exit_code;
100061
+ const detail = entry.error ? `: ${entry.error}` : "";
100062
+ return [
100063
+ {
100064
+ kind: "hook_failure",
100065
+ message: `Hook failed for ${issueId}: ${command} exited ${exitCode}${detail}`,
100066
+ path,
100067
+ issueId
100068
+ }
100069
+ ];
100070
+ });
100071
+ }
100072
+ function isHookFailureEntry(entry) {
100073
+ if (!entry || typeof entry !== "object")
100074
+ return false;
100075
+ const value = entry;
100076
+ return typeof value.issue_id === "string" && typeof value.command === "string" && typeof value.exit_code === "number" && (value.error === undefined || typeof value.error === "string");
100077
+ }
100078
+ // ../core/src/issue-mutations.ts
100079
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync as renameSync2 } from "fs";
100080
+ import { basename as basename2, dirname as dirname2, join as join3 } from "path";
100081
+
100082
+ // ../core/src/write-lock.ts
100083
+ import {
100084
+ existsSync as existsSync2,
100085
+ mkdirSync,
100086
+ renameSync,
100087
+ rmSync,
100088
+ writeFileSync
100089
+ } from "fs";
100090
+ import { basename, dirname, join as join2 } from "path";
100091
+ function isWriteLocked(projectRoot) {
100092
+ return existsSync2(lockPath(projectRoot));
100093
+ }
100094
+ function withWriteLock(projectRoot, operation) {
100095
+ const path = lockPath(projectRoot);
100096
+ if (existsSync2(path)) {
100097
+ return {
100098
+ ok: false,
100099
+ error: { kind: "lock_held", message: "mikan write lock is held", path }
100100
+ };
100101
+ }
100102
+ mkdirSync(dirname(path), { recursive: true });
100103
+ try {
100104
+ writeFileSync(path, String(process.pid), { flag: "wx" });
100105
+ } catch {
100106
+ return {
100107
+ ok: false,
100108
+ error: { kind: "lock_held", message: "mikan write lock is held", path }
100109
+ };
100110
+ }
100111
+ try {
100112
+ return operation();
100113
+ } catch (error51) {
100114
+ return {
100115
+ ok: false,
100116
+ error: {
100117
+ kind: "io_error",
100118
+ message: error51 instanceof Error ? error51.message : String(error51)
100119
+ }
100120
+ };
100121
+ } finally {
100122
+ rmSync(path, { force: true });
100123
+ }
100124
+ }
100125
+ function atomicWriteFile(path, content) {
100126
+ mkdirSync(dirname(path), { recursive: true });
100127
+ const tmp = join2(dirname(path), `.${basename(path)}.${process.pid}.${Date.now()}.tmp`);
100128
+ writeFileSync(tmp, content);
100129
+ renameSync(tmp, path);
100130
+ }
100131
+ function lockPath(projectRoot) {
100132
+ return join2(projectRoot, ".mikan", ".state", "write.lock");
100133
+ }
100134
+
100135
+ // ../core/src/issue-mutations.ts
99867
100136
  function createIssue(options) {
99868
100137
  const status = options.status ?? "backlog";
99869
100138
  const statusValidation = validateStatus(options.config, status);
@@ -99872,6 +100141,9 @@ function createIssue(options) {
99872
100141
  const labelsValidation = validateLabels(options.config, options.labels ?? []);
99873
100142
  if (!labelsValidation.ok)
99874
100143
  return labelsValidation;
100144
+ const dependenciesValidation = validateDependencies(options.dependencies ?? []);
100145
+ if (!dependenciesValidation.ok)
100146
+ return dependenciesValidation;
99875
100147
  const projectKey = options.config.project?.key ?? "MIK";
99876
100148
  const parsedProjectKey = parseProjectKey(projectKey);
99877
100149
  if (!parsedProjectKey.ok) {
@@ -99915,13 +100187,14 @@ function createIssue(options) {
99915
100187
  id,
99916
100188
  title: options.title,
99917
100189
  labels: options.labels ?? [],
100190
+ depends_on: dependenciesValidation.value.map(String),
99918
100191
  created_at: now,
99919
100192
  updated_at: now
99920
100193
  },
99921
100194
  body
99922
100195
  });
99923
- const path = join(options.projectRoot, ".mikan", status, `${id}.md`);
99924
- if (existsSync(path)) {
100196
+ const path = join3(options.projectRoot, ".mikan", status, `${id}.md`);
100197
+ if (existsSync3(path)) {
99925
100198
  return {
99926
100199
  ok: false,
99927
100200
  error: {
@@ -99939,10 +100212,11 @@ function createIssue(options) {
99939
100212
  error: { kind: "malformed_issue", message: parsed.error.message, path }
99940
100213
  };
99941
100214
  }
99942
- return {
99943
- ok: true,
99944
- value: { issue: parsed.value, status: statusValidation.value, path }
99945
- };
100215
+ return findIssueById({
100216
+ projectRoot: options.projectRoot,
100217
+ config: options.config,
100218
+ id
100219
+ });
99946
100220
  });
99947
100221
  }
99948
100222
  function updateIssue(options) {
@@ -99954,6 +100228,9 @@ function updateIssue(options) {
99954
100228
  const labelsValidation = validateLabels(options.config, labels);
99955
100229
  if (!labelsValidation.ok)
99956
100230
  return labelsValidation;
100231
+ const dependenciesValidation = options.dependencies ? validateDependencies(options.dependencies) : undefined;
100232
+ if (dependenciesValidation && !dependenciesValidation.ok)
100233
+ return dependenciesValidation;
99957
100234
  const document2 = readIssueDocument(target.value.path);
99958
100235
  if (!document2.ok)
99959
100236
  return document2;
@@ -99962,6 +100239,7 @@ function updateIssue(options) {
99962
100239
  ...document2.value.frontmatter,
99963
100240
  title: options.title ?? target.value.issue.title,
99964
100241
  labels,
100242
+ ...dependenciesValidation ? { depends_on: dependenciesValidation.value.map(String) } : {},
99965
100243
  updated_at: utcNow(options.now)
99966
100244
  },
99967
100245
  body: options.body ?? target.value.issue.body
@@ -99978,7 +100256,7 @@ function updateIssue(options) {
99978
100256
  }
99979
100257
  };
99980
100258
  }
99981
- return { ok: true, value: { ...target.value, issue: parsed.value } };
100259
+ return findIssueById(options);
99982
100260
  });
99983
100261
  }
99984
100262
  function moveIssue(options) {
@@ -99992,8 +100270,8 @@ function moveIssue(options) {
99992
100270
  const existingLabels = validateLabels(options.config, target.value.issue.labels.map(String));
99993
100271
  if (!existingLabels.ok)
99994
100272
  return existingLabels;
99995
- const destination = join(options.projectRoot, ".mikan", options.status, basename(target.value.path));
99996
- if (destination !== target.value.path && existsSync(destination)) {
100273
+ const destination = join3(options.projectRoot, ".mikan", options.status, basename2(target.value.path));
100274
+ if (destination !== target.value.path && existsSync3(destination)) {
99997
100275
  return {
99998
100276
  ok: false,
99999
100277
  error: {
@@ -100003,7 +100281,7 @@ function moveIssue(options) {
100003
100281
  }
100004
100282
  };
100005
100283
  }
100006
- mkdirSync(dirname(destination), { recursive: true });
100284
+ mkdirSync2(dirname2(destination), { recursive: true });
100007
100285
  const document2 = readIssueDocument(target.value.path);
100008
100286
  if (!document2.ok)
100009
100287
  return document2;
@@ -100020,8 +100298,8 @@ function moveIssue(options) {
100020
100298
  });
100021
100299
  atomicWriteFile(target.value.path, updated);
100022
100300
  if (destination !== target.value.path)
100023
- renameSync(target.value.path, destination);
100024
- const parsed = parseIssueMarkdown(readFileSync(destination, "utf8"));
100301
+ renameSync2(target.value.path, destination);
100302
+ const parsed = parseIssueMarkdown(readFileSync2(destination, "utf8"));
100025
100303
  if (!parsed.ok) {
100026
100304
  return {
100027
100305
  ok: false,
@@ -100032,14 +100310,11 @@ function moveIssue(options) {
100032
100310
  }
100033
100311
  };
100034
100312
  }
100035
- return {
100036
- ok: true,
100037
- value: {
100038
- issue: parsed.value,
100039
- status: statusValidation.value,
100040
- path: destination
100041
- }
100042
- };
100313
+ return findIssueById({
100314
+ projectRoot: options.projectRoot,
100315
+ config: options.config,
100316
+ id: options.id
100317
+ });
100043
100318
  });
100044
100319
  }
100045
100320
  function appendIssue(options) {
@@ -100073,7 +100348,7 @@ function appendIssue(options) {
100073
100348
  }
100074
100349
  };
100075
100350
  }
100076
- return { ok: true, value: { ...target.value, issue: parsed.value } };
100351
+ return findIssueById(options);
100077
100352
  });
100078
100353
  }
100079
100354
  function appendToSection(body, section, entry) {
@@ -100106,78 +100381,8 @@ ${entry.trim()}
100106
100381
  ${after ? `
100107
100382
  ${after}` : ""}`;
100108
100383
  }
100109
- function isWriteLocked(projectRoot) {
100110
- return existsSync(lockPath(projectRoot));
100111
- }
100112
- function parseIssueDocument(markdown) {
100113
- const frontmatter = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(markdown);
100114
- if (!frontmatter) {
100115
- return {
100116
- ok: false,
100117
- error: {
100118
- kind: "missing_frontmatter",
100119
- message: "Issue Markdown must start with YAML frontmatter"
100120
- }
100121
- };
100122
- }
100123
- const frontmatterSource = frontmatter[1] ?? "";
100124
- let rawFrontmatter;
100125
- try {
100126
- rawFrontmatter = $parse(frontmatterSource);
100127
- } catch (error51) {
100128
- return invalidFrontmatterResult(error51 instanceof Error ? error51.message : String(error51));
100129
- }
100130
- const parsedFrontmatter = frontmatterSchema.safeParse(rawFrontmatter);
100131
- if (!parsedFrontmatter.success) {
100132
- return invalidFrontmatterResult(parsedFrontmatter.error.issues.map(formatZodIssue).join("; "));
100133
- }
100134
- const errors4 = [];
100135
- let issueId;
100136
- const id = parseIssueId(parsedFrontmatter.data.id);
100137
- if (id.ok)
100138
- issueId = id.value;
100139
- else
100140
- errors4.push(id.error.message);
100141
- const labels = [];
100142
- for (const rawLabel of parsedFrontmatter.data.labels) {
100143
- const label = parseLabelId(rawLabel);
100144
- if (label.ok)
100145
- labels.push(label.value);
100146
- else
100147
- errors4.push(label.error.message);
100148
- }
100149
- let createdAt;
100150
- const parsedCreatedAt = parseUtcTimestamp(parsedFrontmatter.data.created_at);
100151
- if (parsedCreatedAt.ok)
100152
- createdAt = parsedCreatedAt.value;
100153
- else
100154
- errors4.push(`created_at: ${parsedCreatedAt.error.message}`);
100155
- let updatedAt;
100156
- const parsedUpdatedAt = parseUtcTimestamp(parsedFrontmatter.data.updated_at);
100157
- if (parsedUpdatedAt.ok)
100158
- updatedAt = parsedUpdatedAt.value;
100159
- else
100160
- errors4.push(`updated_at: ${parsedUpdatedAt.error.message}`);
100161
- if (errors4.length > 0 || !issueId || !createdAt || !updatedAt) {
100162
- return invalidFrontmatterResult(errors4.join("; "));
100163
- }
100164
- return {
100165
- ok: true,
100166
- value: {
100167
- frontmatter: parsedFrontmatter.data,
100168
- issue: {
100169
- id: issueId,
100170
- title: parsedFrontmatter.data.title,
100171
- labels,
100172
- createdAt,
100173
- updatedAt,
100174
- body: markdown.slice(frontmatter[0].length)
100175
- }
100176
- }
100177
- };
100178
- }
100179
100384
  function readIssueDocument(path) {
100180
- const parsed = parseIssueDocument(readFileSync(path, "utf8"));
100385
+ const parsed = parseIssueDocument(readFileSync2(path, "utf8"));
100181
100386
  if (!parsed.ok) {
100182
100387
  return {
100183
100388
  ok: false,
@@ -100186,14 +100391,6 @@ function readIssueDocument(path) {
100186
100391
  }
100187
100392
  return parsed;
100188
100393
  }
100189
- function serializeIssue(input) {
100190
- return `---
100191
- ${$stringify(input.frontmatter).trim()}
100192
- ---
100193
- ${input.body.startsWith(`
100194
- `) ? "" : `
100195
- `}${input.body}`;
100196
- }
100197
100394
  function validateStatus(config2, status) {
100198
100395
  if (!config2.board.columns.some((column) => column.id === status)) {
100199
100396
  return {
@@ -100230,79 +100427,19 @@ function validateLabels(config2, labels) {
100230
100427
  }
100231
100428
  return { ok: true, value: parsed };
100232
100429
  }
100233
- function withWriteLock(projectRoot, operation) {
100234
- const path = lockPath(projectRoot);
100235
- if (existsSync(path)) {
100236
- return {
100237
- ok: false,
100238
- error: { kind: "lock_held", message: "mikan write lock is held", path }
100239
- };
100240
- }
100241
- mkdirSync(dirname(path), { recursive: true });
100242
- try {
100243
- writeFileSync(path, String(process.pid), { flag: "wx" });
100244
- } catch {
100245
- return {
100246
- ok: false,
100247
- error: { kind: "lock_held", message: "mikan write lock is held", path }
100248
- };
100249
- }
100250
- try {
100251
- return operation();
100252
- } catch (error51) {
100253
- return {
100254
- ok: false,
100255
- error: {
100256
- kind: "io_error",
100257
- message: error51 instanceof Error ? error51.message : String(error51)
100258
- }
100259
- };
100260
- } finally {
100261
- rmSync(path, { force: true });
100262
- }
100263
- }
100264
- function atomicWriteFile(path, content) {
100265
- mkdirSync(dirname(path), { recursive: true });
100266
- const tmp = join(dirname(path), `.${basename(path)}.${process.pid}.${Date.now()}.tmp`);
100267
- writeFileSync(tmp, content);
100268
- renameSync(tmp, path);
100269
- }
100270
- function sortedMarkdownFiles(directory) {
100271
- return readdirSync(directory).filter((entry) => entry.endsWith(".md")).filter((entry) => statSync(join(directory, entry)).isFile()).sort();
100272
- }
100273
- function readHookFailureWarnings(projectRoot) {
100274
- const path = join(projectRoot, ".mikan", ".state", "hook-log.ndjson");
100275
- if (!existsSync(path))
100276
- return [];
100277
- return readFileSync(path, "utf8").split(`
100278
- `).filter((line) => line.trim().length > 0).flatMap((line) => {
100279
- let entry;
100280
- try {
100281
- entry = JSON.parse(line);
100282
- } catch {
100283
- return [];
100430
+ function validateDependencies(dependencies) {
100431
+ const parsed = [];
100432
+ for (const dependency of dependencies) {
100433
+ const dependencyId = parseIssueId(dependency);
100434
+ if (!dependencyId.ok) {
100435
+ return {
100436
+ ok: false,
100437
+ error: { kind: "malformed_issue", message: dependencyId.error.message }
100438
+ };
100284
100439
  }
100285
- if (!isHookFailureEntry(entry))
100286
- return [];
100287
- const issueId = entry.issue_id;
100288
- const command = entry.command;
100289
- const exitCode = entry.exit_code;
100290
- const detail = entry.error ? `: ${entry.error}` : "";
100291
- return [
100292
- {
100293
- kind: "hook_failure",
100294
- message: `Hook failed for ${issueId}: ${command} exited ${exitCode}${detail}`,
100295
- path,
100296
- issueId
100297
- }
100298
- ];
100299
- });
100300
- }
100301
- function isHookFailureEntry(entry) {
100302
- if (!entry || typeof entry !== "object")
100303
- return false;
100304
- const value = entry;
100305
- return typeof value.issue_id === "string" && typeof value.command === "string" && typeof value.exit_code === "number" && (value.error === undefined || typeof value.error === "string");
100440
+ parsed.push(dependencyId.value);
100441
+ }
100442
+ return { ok: true, value: parsed };
100306
100443
  }
100307
100444
  function defaultIssueBody(title) {
100308
100445
  return `# ${title}
@@ -100334,38 +100471,15 @@ ${message}`;
100334
100471
  function utcNow(now) {
100335
100472
  return (now?.() ?? new Date).toISOString().replace(/\.\d{3}Z$/, "Z");
100336
100473
  }
100337
- function lockPath(projectRoot) {
100338
- return join(projectRoot, ".mikan", ".state", "write.lock");
100339
- }
100340
- function invalidFrontmatter(message) {
100341
- return invalidFrontmatterResult(message);
100342
- }
100343
- function invalidFrontmatterResult(message) {
100344
- return {
100345
- ok: false,
100346
- error: {
100347
- kind: "invalid_frontmatter",
100348
- message
100349
- }
100350
- };
100351
- }
100352
- function formatZodIssue(issue2) {
100353
- const path = issue2.path.length > 0 ? `${issue2.path.join(".")}: ` : "";
100354
- return `${path}${issue2.message}`;
100355
- }
100356
-
100357
- // ../mcp/src/index.ts
100358
- import { readFileSync as readFileSync6 } from "fs";
100359
-
100360
100474
  // ../project-config/src/index.ts
100361
100475
  import {
100362
- existsSync as existsSync2,
100363
- mkdirSync as mkdirSync2,
100364
- readFileSync as readFileSync2,
100476
+ existsSync as existsSync4,
100477
+ mkdirSync as mkdirSync3,
100478
+ readFileSync as readFileSync3,
100365
100479
  statSync as statSync2,
100366
100480
  writeFileSync as writeFileSync2
100367
100481
  } from "fs";
100368
- import { dirname as dirname2, join as join2 } from "path";
100482
+ import { dirname as dirname3, join as join4 } from "path";
100369
100483
  var DEFAULT_COLUMNS = [
100370
100484
  { id: "backlog", title: "Backlog" },
100371
100485
  { id: "ready", title: "Ready" },
@@ -100452,11 +100566,11 @@ var projectConfigSchema = exports_external.object({
100452
100566
  function findProjectConfig(startDir = process.cwd()) {
100453
100567
  let current = normalizeStartDirectory(startDir);
100454
100568
  while (true) {
100455
- const configPath = join2(current, ".mikan", "config.yaml");
100456
- if (existsSync2(configPath)) {
100569
+ const configPath = join4(current, ".mikan", "config.yaml");
100570
+ if (existsSync4(configPath)) {
100457
100571
  return { ok: true, value: { projectRoot: current, configPath } };
100458
100572
  }
100459
- const parent = dirname2(current);
100573
+ const parent = dirname3(current);
100460
100574
  if (parent === current) {
100461
100575
  return {
100462
100576
  ok: false,
@@ -100475,7 +100589,7 @@ function loadProjectConfig(startDir = process.cwd()) {
100475
100589
  return found;
100476
100590
  let raw;
100477
100591
  try {
100478
- raw = readFileSync2(found.value.configPath, "utf8");
100592
+ raw = readFileSync3(found.value.configPath, "utf8");
100479
100593
  } catch (error51) {
100480
100594
  return {
100481
100595
  ok: false,
@@ -100519,8 +100633,8 @@ function loadProjectConfig(startDir = process.cwd()) {
100519
100633
  };
100520
100634
  }
100521
100635
  function initProject(projectRoot, options) {
100522
- const mikanRoot = join2(projectRoot, ".mikan");
100523
- const configPath = join2(mikanRoot, "config.yaml");
100636
+ const mikanRoot = join4(projectRoot, ".mikan");
100637
+ const configPath = join4(mikanRoot, "config.yaml");
100524
100638
  const configInput = {
100525
100639
  project: {
100526
100640
  key: options.key,
@@ -100544,13 +100658,13 @@ function initProject(projectRoot, options) {
100544
100658
  }
100545
100659
  const config2 = parsedConfig.data;
100546
100660
  try {
100547
- mkdirSync2(mikanRoot, { recursive: true });
100661
+ mkdirSync3(mikanRoot, { recursive: true });
100548
100662
  for (const column of DEFAULT_COLUMNS) {
100549
- mkdirSync2(join2(mikanRoot, column.id), { recursive: true });
100663
+ mkdirSync3(join4(mikanRoot, column.id), { recursive: true });
100550
100664
  }
100551
- mkdirSync2(join2(mikanRoot, ".state"), { recursive: true });
100552
- mkdirSync2(join2(mikanRoot, "templates"), { recursive: true });
100553
- writeFileSync2(join2(mikanRoot, "templates", "issue.md"), defaultIssueTemplate());
100665
+ mkdirSync3(join4(mikanRoot, ".state"), { recursive: true });
100666
+ mkdirSync3(join4(mikanRoot, "templates"), { recursive: true });
100667
+ writeFileSync2(join4(mikanRoot, "templates", "issue.md"), defaultIssueTemplate());
100554
100668
  writeFileSync2(configPath, $stringify(config2));
100555
100669
  } catch (error51) {
100556
100670
  return {
@@ -100582,9 +100696,9 @@ function formatConfigIssue(issue2) {
100582
100696
  return `${path}${issue2.message}`;
100583
100697
  }
100584
100698
  function normalizeStartDirectory(startDir) {
100585
- if (!existsSync2(startDir))
100699
+ if (!existsSync4(startDir))
100586
100700
  return startDir;
100587
- return statSync2(startDir).isDirectory() ? startDir : dirname2(startDir);
100701
+ return statSync2(startDir).isDirectory() ? startDir : dirname3(startDir);
100588
100702
  }
100589
100703
  function defaultIssueTemplate() {
100590
100704
  return `---
@@ -103656,9 +103770,9 @@ function resolveTypeName2(prop) {
103656
103770
 
103657
103771
  // ../../node_modules/.bun/incur@0.4.8/node_modules/incur/dist/SyncMcp.js
103658
103772
  import { execFile } from "child_process";
103659
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
103773
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
103660
103774
  import { homedir } from "os";
103661
- import { dirname as dirname3, join as join3 } from "path";
103775
+ import { dirname as dirname4, join as join5 } from "path";
103662
103776
  async function register2(name, options = {}) {
103663
103777
  const runner = detectRunner();
103664
103778
  const command = options.command ?? `${runner} ${detectPackageSpecifier(name)} --mcp`;
@@ -103684,11 +103798,11 @@ async function register2(name, options = {}) {
103684
103798
  return { command, agents };
103685
103799
  }
103686
103800
  function registerAmp(name, command) {
103687
- const configPath = join3(homedir(), ".config", "amp", "settings.json");
103801
+ const configPath = join5(homedir(), ".config", "amp", "settings.json");
103688
103802
  let config2 = {};
103689
- if (existsSync3(configPath)) {
103803
+ if (existsSync5(configPath)) {
103690
103804
  try {
103691
- config2 = JSON.parse(readFileSync3(configPath, "utf-8"));
103805
+ config2 = JSON.parse(readFileSync4(configPath, "utf-8"));
103692
103806
  } catch {
103693
103807
  return false;
103694
103808
  }
@@ -103699,9 +103813,9 @@ function registerAmp(name, command) {
103699
103813
  const servers = config2["amp.mcpServers"] ?? {};
103700
103814
  servers[name] = { command: cmd, args };
103701
103815
  config2["amp.mcpServers"] = servers;
103702
- const dir = dirname3(configPath);
103703
- if (!existsSync3(dir))
103704
- mkdirSync3(dir, { recursive: true });
103816
+ const dir = dirname4(configPath);
103817
+ if (!existsSync5(dir))
103818
+ mkdirSync4(dir, { recursive: true });
103705
103819
  writeFileSync3(configPath, JSON.stringify(config2, null, 2) + `
103706
103820
  `);
103707
103821
  return true;
@@ -103714,7 +103828,7 @@ function detectPackageSpecifier(name) {
103714
103828
  if (!match)
103715
103829
  return name;
103716
103830
  try {
103717
- const pkg = JSON.parse(readFileSync3(join3(match[1], "package.json"), "utf-8"));
103831
+ const pkg = JSON.parse(readFileSync4(join5(match[1], "package.json"), "utf-8"));
103718
103832
  const deps = pkg.dependencies ?? {};
103719
103833
  const spec = deps[name];
103720
103834
  if (!spec || Object.keys(deps).length !== 1)
@@ -106311,116 +106425,85 @@ function resolveDisplayName(name, aliases) {
106311
106425
  const bin = process.argv[1];
106312
106426
  if (!bin)
106313
106427
  return name;
106314
- const basename3 = path3.basename(bin);
106315
- if (basename3 === name)
106428
+ const basename4 = path3.basename(bin);
106429
+ if (basename4 === name)
106316
106430
  return name;
106317
- if (aliases?.includes(basename3))
106318
- return basename3;
106431
+ if (aliases?.includes(basename4))
106432
+ return basename4;
106319
106433
  return name;
106320
106434
  }
106321
- // ../mcp/src/installers.ts
106435
+ // ../mcp/src/installers/shared.ts
106322
106436
  import {
106323
106437
  chmodSync,
106324
- existsSync as existsSync5,
106325
- mkdirSync as mkdirSync5,
106326
- readFileSync as readFileSync5,
106327
- renameSync as renameSync2,
106438
+ existsSync as existsSync7,
106439
+ mkdirSync as mkdirSync6,
106440
+ readFileSync as readFileSync6,
106441
+ renameSync as renameSync3,
106328
106442
  statSync as statSync3,
106329
106443
  writeFileSync as writeFileSync4
106330
106444
  } from "fs";
106331
106445
  import { homedir as homedir4 } from "os";
106332
- import { dirname as dirname5, join as join6, resolve as resolve3 } from "path";
106446
+ import { dirname as dirname6, join as join8, resolve as resolve3 } from "path";
106333
106447
  var defaultServerName = "mikan";
106334
106448
  var defaultCommand = "mikan";
106335
106449
  var defaultArgs = ["mcp"];
106336
- var mcpAgentInstallers = [
106337
- { agent: "pi", install: installPiMcpServer },
106338
- { agent: "antigravity", install: installAntigravityMcpServer },
106339
- { agent: "jcode", install: installJcodeMcpServer }
106340
- ];
106341
- function installMcpServerForAgent(agent, options = {}) {
106342
- const installer = mcpAgentInstallers.find((entry) => entry.agent === agent);
106343
- if (!installer) {
106344
- throw new Error(`Unsupported MCP agent: ${agent}. Supported agents: ${mcpAgentInstallers.map((entry) => entry.agent).join(", ")}`);
106345
- }
106346
- return installer.install(options);
106450
+ function isGlobalScope(options) {
106451
+ return options.global !== false;
106347
106452
  }
106348
- function installPiMcpServer(options = {}) {
106349
- const global2 = options.global !== false;
106350
- const path4 = global2 ? join6(options.home ?? homedir4(), ".config", "mcp", "mcp.json") : resolve3(options.cwd ?? process.cwd(), ".mcp.json");
106351
- const config2 = readJsonObject(path4);
106352
- const servers = objectProperty(config2, "mcpServers");
106353
- const serverName = options.serverName ?? defaultServerName;
106354
- servers[serverName] = {
106355
- command: options.command ?? defaultCommand,
106356
- args: options.args ?? defaultArgs
106357
- };
106358
- config2.mcpServers = servers;
106359
- writeJsonObject(path4, config2);
106360
- return {
106361
- agent: "pi",
106362
- path: path4,
106363
- serverName,
106364
- scope: global2 ? "global" : "workspace"
106365
- };
106453
+ function workspacePath(options, ...segments) {
106454
+ return resolve3(options.cwd ?? process.cwd(), ...segments);
106455
+ }
106456
+ function homePath(options, ...segments) {
106457
+ return join8(options.home ?? homedir4(), ...segments);
106366
106458
  }
106367
- function installAntigravityMcpServer(options = {}) {
106368
- const global2 = options.global !== false;
106369
- const path4 = global2 ? join6(options.home ?? homedir4(), ".gemini", "antigravity-cli", "mcp_config.json") : resolve3(options.cwd ?? process.cwd(), ".agents", "mcp_config.json");
106370
- const config2 = readJsonObject(path4);
106371
- const servers = objectProperty(config2, "mcpServers");
106372
- const serverName = options.serverName ?? defaultServerName;
106373
- servers[serverName] = {
106459
+ function resolveServerName(options) {
106460
+ return options.serverName ?? defaultServerName;
106461
+ }
106462
+ function buildServerSpec(options) {
106463
+ return {
106374
106464
  command: options.command ?? defaultCommand,
106375
- args: options.args ?? defaultArgs,
106465
+ args: options.args ?? [...defaultArgs],
106376
106466
  env: options.env ?? {}
106377
106467
  };
106378
- config2.mcpServers = servers;
106379
- writeJsonObject(path4, config2);
106380
- return {
106381
- agent: "antigravity",
106382
- path: path4,
106383
- serverName,
106384
- scope: global2 ? "cli-global" : "workspace"
106385
- };
106386
106468
  }
106387
- function installJcodeMcpServer(options = {}) {
106388
- const global2 = options.global !== false;
106389
- const path4 = global2 ? join6(options.home ?? homedir4(), ".jcode", "mcp.json") : resolve3(options.cwd ?? process.cwd(), ".jcode", "mcp.json");
106390
- const config2 = readJsonObject(path4);
106391
- const servers = objectProperty(config2, "servers");
106392
- const serverName = options.serverName ?? defaultServerName;
106393
- servers[serverName] = {
106394
- command: options.command ?? defaultCommand,
106395
- args: options.args ?? defaultArgs,
106396
- env: options.env ?? {},
106397
- shared: true
106398
- };
106399
- config2.servers = servers;
106400
- writeJsonObject(path4, config2);
106469
+ function createInstaller(adapter) {
106401
106470
  return {
106402
- agent: "jcode",
106403
- path: path4,
106404
- serverName,
106405
- scope: global2 ? "global" : "workspace"
106471
+ agent: adapter.agent,
106472
+ install: (options = {}) => {
106473
+ const { path: path4, scope } = adapter.resolveTarget(options);
106474
+ const serverName = resolveServerName(options);
106475
+ const spec = buildServerSpec(options);
106476
+ const config2 = readJsonObject(path4);
106477
+ const servers = objectProperty(config2, adapter.serversKey);
106478
+ servers[serverName] = adapter.buildEntry(spec);
106479
+ config2[adapter.serversKey] = servers;
106480
+ writeJsonObject(path4, config2);
106481
+ return { agent: adapter.agent, path: path4, serverName, scope };
106482
+ }
106406
106483
  };
106407
106484
  }
106408
106485
  function readJsonObject(path4) {
106409
- if (!existsSync5(path4))
106486
+ if (!existsSync7(path4))
106410
106487
  return {};
106411
- const parsed = JSON.parse(readFileSync5(path4, "utf8"));
106488
+ const parsed = JSON.parse(readFileSync6(path4, "utf8"));
106412
106489
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
106413
106490
  return {};
106414
106491
  return parsed;
106415
106492
  }
106416
- function writeJsonObject(path4, config2) {
106417
- mkdirSync5(dirname5(path4), { recursive: true });
106493
+ function writeTextFileAtomic(path4, contents) {
106494
+ mkdirSync6(dirname6(path4), { recursive: true });
106418
106495
  const tmpPath = `${path4}.${process.pid}.tmp`;
106419
- const mode = existsSync5(path4) ? statSync3(path4).mode & 511 : 384;
106420
- writeFileSync4(tmpPath, `${JSON.stringify(config2, null, 2)}
106421
- `, "utf8");
106496
+ const mode = existsSync7(path4) ? statSync3(path4).mode & 511 : 384;
106497
+ writeFileSync4(tmpPath, contents, "utf8");
106422
106498
  chmodSync(tmpPath, mode);
106423
- renameSync2(tmpPath, path4);
106499
+ renameSync3(tmpPath, path4);
106500
+ }
106501
+ function writeJsonObject(path4, config2) {
106502
+ writeTextFileAtomic(path4, `${JSON.stringify(config2, null, 2)}
106503
+ `);
106504
+ }
106505
+ function readTextFile(path4) {
106506
+ return existsSync7(path4) ? readFileSync6(path4, "utf8") : "";
106424
106507
  }
106425
106508
  function objectProperty(config2, key) {
106426
106509
  const value = config2[key];
@@ -106429,6 +106512,308 @@ function objectProperty(config2, key) {
106429
106512
  return value;
106430
106513
  }
106431
106514
 
106515
+ // ../mcp/src/installers/antigravity.ts
106516
+ var antigravityAdapter = {
106517
+ agent: "antigravity",
106518
+ serversKey: "mcpServers",
106519
+ resolveTarget: (options) => isGlobalScope(options) ? {
106520
+ path: homePath(options, ".gemini", "antigravity-cli", "mcp_config.json"),
106521
+ scope: "cli-global"
106522
+ } : {
106523
+ path: workspacePath(options, ".agents", "mcp_config.json"),
106524
+ scope: "workspace"
106525
+ },
106526
+ buildEntry: (spec) => ({
106527
+ command: spec.command,
106528
+ args: spec.args,
106529
+ env: spec.env
106530
+ })
106531
+ };
106532
+ var antigravityInstaller = createInstaller(antigravityAdapter);
106533
+
106534
+ // ../mcp/src/installers/claude-code.ts
106535
+ var claudeCodeAdapter = {
106536
+ agent: "claude-code",
106537
+ serversKey: "mcpServers",
106538
+ resolveTarget: (options) => isGlobalScope(options) ? { path: homePath(options, ".claude.json"), scope: "global" } : { path: workspacePath(options, ".mcp.json"), scope: "workspace" },
106539
+ buildEntry: (spec) => ({ command: spec.command, args: spec.args })
106540
+ };
106541
+ var claudeCodeInstaller = createInstaller(claudeCodeAdapter);
106542
+
106543
+ // ../mcp/src/installers/codex.ts
106544
+ function tomlBasicString(value) {
106545
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
106546
+ }
106547
+ function tomlKey(key) {
106548
+ return /^[A-Za-z0-9_-]+$/.test(key) ? key : tomlBasicString(key);
106549
+ }
106550
+ function tableHeader(serverName) {
106551
+ return `[mcp_servers.${tomlKey(serverName)}]`;
106552
+ }
106553
+ function regexEscape(value) {
106554
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
106555
+ }
106556
+ function hasParentFormDefinition(existing, serverName) {
106557
+ const name = regexEscape(serverName);
106558
+ if (new RegExp(`^\\s*mcp_servers\\s*\\.\\s*${name}\\b`, "m").test(existing)) {
106559
+ return true;
106560
+ }
106561
+ const assignment = new RegExp(`^\\s*${name}\\s*[.=]`);
106562
+ let inParentTable = false;
106563
+ for (const line of existing.split(`
106564
+ `)) {
106565
+ const trimmed = line.trim();
106566
+ if (trimmed.startsWith("[")) {
106567
+ inParentTable = trimmed === "[mcp_servers]";
106568
+ continue;
106569
+ }
106570
+ if (inParentTable && assignment.test(line))
106571
+ return true;
106572
+ }
106573
+ return false;
106574
+ }
106575
+ function renderServerTable(serverName, spec) {
106576
+ const lines = [tableHeader(serverName)];
106577
+ lines.push(`command = ${tomlBasicString(spec.command)}`);
106578
+ lines.push(`args = [${spec.args.map(tomlBasicString).join(", ")}]`);
106579
+ const envEntries2 = Object.entries(spec.env);
106580
+ if (envEntries2.length > 0) {
106581
+ const inline = envEntries2.map(([key, value]) => `${tomlKey(key)} = ${tomlBasicString(value)}`).join(", ");
106582
+ lines.push(`env = { ${inline} }`);
106583
+ }
106584
+ return lines.join(`
106585
+ `);
106586
+ }
106587
+ function upsertServerTable(existing, serverName, tableText) {
106588
+ const header = tableHeader(serverName);
106589
+ const lines = existing.split(`
106590
+ `);
106591
+ const startIdx = lines.findIndex((line) => line.trim() === header);
106592
+ if (startIdx === -1) {
106593
+ const trimmed = existing.replace(/\s*$/, "");
106594
+ return trimmed.length > 0 ? `${trimmed}
106595
+
106596
+ ${tableText}
106597
+ ` : `${tableText}
106598
+ `;
106599
+ }
106600
+ let endIdx = lines.length;
106601
+ for (let i = startIdx + 1;i < lines.length; i++) {
106602
+ if ((lines[i] ?? "").trim().startsWith("[")) {
106603
+ endIdx = i;
106604
+ break;
106605
+ }
106606
+ }
106607
+ const merged = [
106608
+ ...lines.slice(0, startIdx),
106609
+ ...tableText.split(`
106610
+ `),
106611
+ ...lines.slice(endIdx)
106612
+ ];
106613
+ const result = merged.join(`
106614
+ `);
106615
+ return result.endsWith(`
106616
+ `) ? result : `${result}
106617
+ `;
106618
+ }
106619
+ function installCodexMcpServer(options = {}) {
106620
+ if (!isGlobalScope(options)) {
106621
+ throw new Error("Codex MCP configuration is global-only; it has no workspace-local " + "scope. Re-run `mikan mcp add --agent codex` without --no-global to " + "register the server in ~/.codex/config.toml.");
106622
+ }
106623
+ const path4 = homePath(options, ".codex", "config.toml");
106624
+ const serverName = resolveServerName(options);
106625
+ const spec = buildServerSpec(options);
106626
+ const existing = readTextFile(path4);
106627
+ const hasCanonicalTable = existing.split(`
106628
+ `).some((line) => line.trim() === tableHeader(serverName));
106629
+ if (!hasCanonicalTable && hasParentFormDefinition(existing, serverName)) {
106630
+ throw new Error(`mikan found an existing '${serverName}' MCP server in ${path4} defined ` + "under a [mcp_servers] table form it cannot safely merge. Edit that " + "entry manually or use `codex mcp add` instead.");
106631
+ }
106632
+ const table2 = renderServerTable(serverName, spec);
106633
+ writeTextFileAtomic(path4, upsertServerTable(existing, serverName, table2));
106634
+ return { agent: "codex", path: path4, serverName, scope: "global" };
106635
+ }
106636
+ var codexInstaller = {
106637
+ agent: "codex",
106638
+ install: installCodexMcpServer
106639
+ };
106640
+
106641
+ // ../mcp/src/installers/jcode.ts
106642
+ var jcodeAdapter = {
106643
+ agent: "jcode",
106644
+ serversKey: "servers",
106645
+ resolveTarget: (options) => isGlobalScope(options) ? { path: homePath(options, ".jcode", "mcp.json"), scope: "global" } : {
106646
+ path: workspacePath(options, ".jcode", "mcp.json"),
106647
+ scope: "workspace"
106648
+ },
106649
+ buildEntry: (spec) => ({
106650
+ command: spec.command,
106651
+ args: spec.args,
106652
+ env: spec.env,
106653
+ shared: true
106654
+ })
106655
+ };
106656
+ var jcodeInstaller = createInstaller(jcodeAdapter);
106657
+
106658
+ // ../mcp/src/installers/opencode.ts
106659
+ var opencodeAdapter = {
106660
+ agent: "opencode",
106661
+ serversKey: "mcp",
106662
+ resolveTarget: (options) => isGlobalScope(options) ? {
106663
+ path: homePath(options, ".config", "opencode", "opencode.json"),
106664
+ scope: "global"
106665
+ } : {
106666
+ path: workspacePath(options, "opencode.json"),
106667
+ scope: "workspace"
106668
+ },
106669
+ buildEntry: (spec) => ({
106670
+ type: "local",
106671
+ command: [spec.command, ...spec.args],
106672
+ enabled: true,
106673
+ environment: spec.env
106674
+ })
106675
+ };
106676
+ var opencodeInstaller = createInstaller(opencodeAdapter);
106677
+
106678
+ // ../mcp/src/installers/pi.ts
106679
+ var piAdapter = {
106680
+ agent: "pi",
106681
+ serversKey: "mcpServers",
106682
+ resolveTarget: (options) => isGlobalScope(options) ? {
106683
+ path: homePath(options, ".config", "mcp", "mcp.json"),
106684
+ scope: "global"
106685
+ } : { path: workspacePath(options, ".mcp.json"), scope: "workspace" },
106686
+ buildEntry: (spec) => ({ command: spec.command, args: spec.args })
106687
+ };
106688
+ var piInstaller = createInstaller(piAdapter);
106689
+
106690
+ // ../mcp/src/installers/index.ts
106691
+ var mcpAgentInstallers = [
106692
+ piInstaller,
106693
+ antigravityInstaller,
106694
+ jcodeInstaller,
106695
+ claudeCodeInstaller,
106696
+ opencodeInstaller,
106697
+ codexInstaller
106698
+ ];
106699
+ function installMcpServerForAgent(agent, options = {}) {
106700
+ const installer = mcpAgentInstallers.find((entry) => entry.agent === agent);
106701
+ if (!installer) {
106702
+ throw new Error(`Unsupported MCP agent: ${agent}. Supported agents: ${mcpAgentInstallers.map((entry) => entry.agent).join(", ")}`);
106703
+ }
106704
+ return installer.install(options);
106705
+ }
106706
+ // ../mcp/src/skills/shared.ts
106707
+ var skillDocument = `---
106708
+ name: mikan
106709
+ description: mikan is a local-first Issue board for AI-assisted development. Use it to read the board and to create, update, move, and append to Issues through the mikan MCP tools. Trigger when the user wants to see the board, add or change an Issue, move an Issue to another Status, record a Report or Note, or decide what to work on next.
106710
+ ---
106711
+
106712
+ # mikan
106713
+
106714
+ mikan is a tiny, local-first, Markdown-backed Issue board. Each Issue has a
106715
+ stable Issue ID such as \`MIK-001\`, one current Status (the board Column it
106716
+ lives in), optional Labels, and a body that can hold Reports and Notes.
106717
+
106718
+ Drive mikan through its MCP tools rather than editing the Markdown files
106719
+ directly:
106720
+
106721
+ - \`get_board\`, \`list_issues\`, \`get_issue\` \u2014 read the board and individual Issues.
106722
+ - \`create_issue\` \u2014 create an Issue (title, optional body, status, labels, depends_on).
106723
+ - \`update_issue\` \u2014 update an Issue's title, labels, dependencies, or body.
106724
+ - \`move_issue\` \u2014 move an Issue to another Status, including \`blocked\` and \`completed\`.
106725
+ - \`append_issue\` \u2014 append a Report (with a source) or a Note to an Issue.
106726
+
106727
+ ## Statuses
106728
+
106729
+ The standard Statuses are \`backlog\`, \`ready\`, \`active\`, \`blocked\`,
106730
+ \`completed\`, and \`archived\`. An Issue's Status is the board Column it sits in;
106731
+ change it with \`move_issue\`.
106732
+
106733
+ ## Dependencies are advisory
106734
+
106735
+ An Issue may declare \`depends_on\` (prerequisite Issue IDs). Read tools also
106736
+ return \`unmet_dependencies\` and \`dependency_status\` (\`ready\` or \`blocked\`).
106737
+ These are advisory read-model data to help humans and agents pick an order.
106738
+ mikan does not schedule, auto-move, or block Issues on dependencies.
106739
+
106740
+ ## Vocabulary
106741
+
106742
+ Use Issue, Issue ID, Status, Column, Label, Report, Note, and Dependency. Avoid
106743
+ Task, ticket, profile, and role.
106744
+ `;
106745
+ function globalSkillPath(options, ...segments) {
106746
+ return homePath(options, ...segments);
106747
+ }
106748
+ function workspaceSkillPath(options, ...segments) {
106749
+ return workspacePath(options, ...segments);
106750
+ }
106751
+ function createSkillInstaller(adapter) {
106752
+ return {
106753
+ agent: adapter.agent,
106754
+ install: (options = {}) => {
106755
+ const { path: path4, scope } = adapter.resolveTarget(options);
106756
+ writeTextFileAtomic(path4, skillDocument);
106757
+ return { agent: adapter.agent, path: path4, scope };
106758
+ }
106759
+ };
106760
+ }
106761
+
106762
+ // ../mcp/src/skills/claude-code.ts
106763
+ var claudeCodeAdapter2 = {
106764
+ agent: "claude-code",
106765
+ resolveTarget: (options) => isGlobalScope(options) ? {
106766
+ path: globalSkillPath(options, ".claude", "skills", "mikan", "SKILL.md"),
106767
+ scope: "global"
106768
+ } : {
106769
+ path: workspaceSkillPath(options, ".claude", "skills", "mikan", "SKILL.md"),
106770
+ scope: "workspace"
106771
+ }
106772
+ };
106773
+ var claudeCodeSkillInstaller = createSkillInstaller(claudeCodeAdapter2);
106774
+
106775
+ // ../mcp/src/skills/codex.ts
106776
+ var codexAdapter = {
106777
+ agent: "codex",
106778
+ resolveTarget: (options) => {
106779
+ if (!isGlobalScope(options)) {
106780
+ throw new Error("Codex skills are global-only; Codex has no workspace-local skill " + "directory. Re-run `mikan skills add --agent codex` without " + "--no-global to install into ~/.codex/skills/.");
106781
+ }
106782
+ return {
106783
+ path: globalSkillPath(options, ".codex", "skills", "mikan", "SKILL.md"),
106784
+ scope: "global"
106785
+ };
106786
+ }
106787
+ };
106788
+ var codexSkillInstaller = createSkillInstaller(codexAdapter);
106789
+
106790
+ // ../mcp/src/skills/opencode.ts
106791
+ var opencodeAdapter2 = {
106792
+ agent: "opencode",
106793
+ resolveTarget: (options) => isGlobalScope(options) ? {
106794
+ path: globalSkillPath(options, ".config", "opencode", "skills", "mikan", "SKILL.md"),
106795
+ scope: "global"
106796
+ } : {
106797
+ path: workspaceSkillPath(options, ".opencode", "skills", "mikan", "SKILL.md"),
106798
+ scope: "workspace"
106799
+ }
106800
+ };
106801
+ var opencodeSkillInstaller = createSkillInstaller(opencodeAdapter2);
106802
+
106803
+ // ../mcp/src/skills/index.ts
106804
+ var skillAgentInstallers = [
106805
+ claudeCodeSkillInstaller,
106806
+ opencodeSkillInstaller,
106807
+ codexSkillInstaller
106808
+ ];
106809
+ function installSkillForAgent(agent, options = {}) {
106810
+ const installer = skillAgentInstallers.find((entry) => entry.agent === agent);
106811
+ if (!installer) {
106812
+ throw new Error(`Unsupported skill agent: ${agent}. Supported agents: ${skillAgentInstallers.map((entry) => entry.agent).join(", ")}`);
106813
+ }
106814
+ return installer.install(options);
106815
+ }
106816
+
106432
106817
  // ../mcp/src/index.ts
106433
106818
  function getBoardTool(args, runtime = {}) {
106434
106819
  const loaded = load(runtime.cwd);
@@ -106486,7 +106871,7 @@ function getIssueTool(args, runtime = {}) {
106486
106871
  return coreError(found.error.kind, found.error.message);
106487
106872
  return ok({
106488
106873
  ...formatIssue2(found.value, board.value.warnings),
106489
- markdown: readFileSync6(found.value.path, "utf8")
106874
+ markdown: readFileSync7(found.value.path, "utf8")
106490
106875
  });
106491
106876
  }
106492
106877
  function createIssueTool(args, runtime = {}) {
@@ -106500,6 +106885,7 @@ function createIssueTool(args, runtime = {}) {
106500
106885
  body: args.body,
106501
106886
  status: args.status,
106502
106887
  labels: args.labels,
106888
+ dependencies: args.depends_on,
106503
106889
  now: runtime.now
106504
106890
  });
106505
106891
  if (!result.ok)
@@ -106517,6 +106903,7 @@ function updateIssueTool(args, runtime = {}) {
106517
106903
  title: args.title,
106518
106904
  labels: args.labels,
106519
106905
  body: args.body,
106906
+ dependencies: args.depends_on,
106520
106907
  now: runtime.now
106521
106908
  });
106522
106909
  if (!result.ok)
@@ -106580,16 +106967,18 @@ function createMikanMcpCli(runtime = {}) {
106580
106967
  title: exports_external.string(),
106581
106968
  body: exports_external.string().optional(),
106582
106969
  status: exports_external.string().optional(),
106583
- labels: exports_external.array(exports_external.string()).optional()
106970
+ labels: exports_external.array(exports_external.string()).optional(),
106971
+ depends_on: exports_external.array(exports_external.string()).optional()
106584
106972
  }),
106585
106973
  run: (context) => forIncur(context, createIssueTool(context.args, runtime))
106586
106974
  }).command("update_issue", {
106587
- description: "Update title, labels, or body through the core update primitive.",
106975
+ description: "Update title, labels, dependencies, or body through the core update primitive.",
106588
106976
  args: exports_external.object({
106589
106977
  id: exports_external.string(),
106590
106978
  title: exports_external.string().optional(),
106591
106979
  labels: exports_external.array(exports_external.string()).optional(),
106592
- body: exports_external.string().optional()
106980
+ body: exports_external.string().optional(),
106981
+ depends_on: exports_external.array(exports_external.string()).optional()
106593
106982
  }),
106594
106983
  run: (context) => forIncur(context, updateIssueTool(context.args, runtime))
106595
106984
  }).command("move_issue", {
@@ -106614,6 +107003,16 @@ function createMikanMcpCli(runtime = {}) {
106614
107003
  async function startMcpServer(runtime = {}) {
106615
107004
  await createMikanMcpCli(runtime).serve(["--mcp"]);
106616
107005
  }
107006
+ async function getMcpManifest(runtime = {}, options = {}) {
107007
+ let manifest = "";
107008
+ await createMikanMcpCli(runtime).serve([options.full ? "--llms-full" : "--llms"], {
107009
+ stdout: (chunk) => {
107010
+ manifest += chunk;
107011
+ },
107012
+ exit: () => {}
107013
+ });
107014
+ return manifest;
107015
+ }
106617
107016
  function formatColumn(column) {
106618
107017
  return {
106619
107018
  ...column,
@@ -106630,6 +107029,9 @@ function formatIssue2(issue2, warnings) {
106630
107029
  body: issue2.issue.body,
106631
107030
  status: String(issue2.status),
106632
107031
  path: issue2.path,
107032
+ depends_on: issue2.issue.dependencies.map(String),
107033
+ unmet_dependencies: issue2.unmetDependencies.map(String),
107034
+ dependency_status: issue2.dependencyStatus,
106633
107035
  warnings: warnings.filter((warning) => warning.issueId === String(issue2.issue.id) || warning.path === issue2.path)
106634
107036
  };
106635
107037
  }
@@ -106657,12 +107059,11 @@ function coreError(code, message) {
106657
107059
  }
106658
107060
 
106659
107061
  // ../tui/src/index.ts
106660
- import { readFileSync as readFileSync7 } from "fs";
106661
- var import_react17 = __toESM(require_react(), 1);
107062
+ var import_react20 = __toESM(require_react(), 1);
106662
107063
  // ../tui/package.json
106663
107064
  var package_default = {
106664
107065
  name: "@mikan/tui",
106665
- version: "0.0.1",
107066
+ version: "0.0.3",
106666
107067
  private: true,
106667
107068
  type: "module",
106668
107069
  exports: {
@@ -106682,6 +107083,10 @@ var package_default = {
106682
107083
  }
106683
107084
  };
106684
107085
 
107086
+ // ../tui/src/board-view.ts
107087
+ await init_core3();
107088
+ var import_react = __toESM(require_react(), 1);
107089
+
106685
107090
  // ../tui/src/formatting.ts
106686
107091
  function formatLabels(labels) {
106687
107092
  return labels.map((label) => `#${label}`).join(" ");
@@ -106697,42 +107102,111 @@ function formatLineRange(options) {
106697
107102
  ].filter(Boolean).join(" | ");
106698
107103
  }
106699
107104
  function visibleCardCountForViewport(viewportHeight) {
106700
- return Math.max(1, Math.floor((viewportHeight - 10) / 3));
107105
+ return Math.max(1, viewportHeight - 6);
107106
+ }
107107
+ var MIN_COLUMN_WIDTH = 40;
107108
+ var MIN_VISIBLE_COLUMNS = 2;
107109
+ var MAX_VISIBLE_COLUMNS = 5;
107110
+ function visibleColumnCountForViewport(viewportWidth) {
107111
+ const fitted = Math.floor(viewportWidth / MIN_COLUMN_WIDTH);
107112
+ return Math.min(MAX_VISIBLE_COLUMNS, Math.max(MIN_VISIBLE_COLUMNS, fitted));
106701
107113
  }
106702
107114
  function visibleDetailLineCount(viewportHeight) {
106703
- return Math.max(1, viewportHeight - 11);
107115
+ return Math.max(1, viewportHeight - 8);
106704
107116
  }
106705
107117
  function footerText(mode) {
106706
- if (mode === "modal")
106707
- return "Modal | enter confirm | esc cancel";
107118
+ if (mode === "modal") {
107119
+ return "Modal | enter confirm | esc cancel | ? keys";
107120
+ }
106708
107121
  if (mode === "detail") {
106709
- return "Detail | j/k scroll | esc board | n note | a archive | r reload | q quit";
107122
+ return "Detail | \u2191\u2193 scroll | esc board | ? keys";
106710
107123
  }
106711
- return "Board | j/k card | h/l column | enter detail | H/L move | n note | a archive | r reload | q quit";
107124
+ return "Board | \u2191\u2193 card | \u2190\u2192 column | enter detail | ? keys";
106712
107125
  }
106713
107126
 
106714
- // ../tui/src/index.ts
106715
- var TUI_VERSION = package_default.version;
106716
- function buildTuiTheme() {
107127
+ // ../tui/src/selection.ts
107128
+ function clamp(value, min, max) {
107129
+ return Math.min(max, Math.max(min, value));
107130
+ }
107131
+ function clampSelection(model, selection) {
107132
+ const columnIndex = clamp(selection.columnIndex, 0, Math.max(0, model.columns.length - 1));
107133
+ const maxCardIndex = Math.max(0, (model.columns[columnIndex]?.cards.length ?? 1) - 1);
106717
107134
  return {
106718
- base: {
106719
- canvas: "#1f1a14",
106720
- surface: "#2a2118",
106721
- text: "#eadfce",
106722
- muted: "#9c8870"
106723
- },
106724
- interactive: {
106725
- accent: "#f0a04b",
106726
- focus: "#f6c177",
106727
- selectedSurface: "#3a2a1d"
106728
- },
106729
- feedback: {
106730
- warning: "#f6c177",
106731
- error: "#d66a4a",
106732
- success: "#8faa5f"
107135
+ ...selection,
107136
+ columnIndex,
107137
+ cardIndex: clamp(selection.cardIndex, 0, maxCardIndex)
107138
+ };
107139
+ }
107140
+ function findSelectionByCardId(model, cardId) {
107141
+ for (const [columnIndex, column] of model.columns.entries()) {
107142
+ const cardIndex = column.cards.findIndex((card) => card.id === cardId);
107143
+ if (cardIndex !== -1) {
107144
+ return { columnIndex, cardIndex, detailOpen: false };
106733
107145
  }
107146
+ }
107147
+ return;
107148
+ }
107149
+
107150
+ // ../tui/src/board-view-model.ts
107151
+ function buildBoardViewModel(model, selection, options = {}) {
107152
+ const visibleCardCount = Math.max(1, options.visibleCardCount ?? (options.viewportHeight ? visibleCardCountForViewport(options.viewportHeight) : 6));
107153
+ const columns = model.columns.map((column, columnIndex) => {
107154
+ const cards = column.cards.map((card, cardIndex) => ({
107155
+ ...card,
107156
+ selected: columnIndex === selection.columnIndex && cardIndex === selection.cardIndex,
107157
+ labelsText: card.labels.join(", ")
107158
+ }));
107159
+ const maxCardStart = Math.max(0, cards.length - visibleCardCount);
107160
+ const cardStart = cards.length <= visibleCardCount ? 0 : columnIndex === selection.columnIndex ? clamp(selection.cardIndex - Math.floor(visibleCardCount / 2), 0, maxCardStart) : 0;
107161
+ const visibleCards = cards.slice(cardStart, cardStart + visibleCardCount);
107162
+ const cardEnd = cardStart + visibleCards.length;
107163
+ return {
107164
+ id: column.id,
107165
+ title: column.title,
107166
+ count: column.cards.length,
107167
+ active: columnIndex === selection.columnIndex,
107168
+ empty: column.cards.length === 0,
107169
+ emptyText: "No Issues",
107170
+ cards,
107171
+ visibleCards,
107172
+ laneFillLineCount: Math.max(0, visibleCardCount - visibleCards.length),
107173
+ hiddenCardsBefore: cardStart,
107174
+ hiddenCardsAfter: Math.max(0, cards.length - cardEnd),
107175
+ cardRangeText: cards.length > visibleCardCount ? `${cardStart + 1}-${cardEnd}/${cards.length} | \u2191${cardStart} | \u2193${Math.max(0, cards.length - cardEnd)}` : ""
107176
+ };
107177
+ });
107178
+ const visibleColumnCount = Math.max(1, options.visibleColumnCount ?? (options.viewportWidth ? visibleColumnCountForViewport(options.viewportWidth) : 3));
107179
+ const maxStart = Math.max(0, columns.length - visibleColumnCount);
107180
+ const start = clamp(selection.columnIndex - (visibleColumnCount - 1), 0, maxStart);
107181
+ const visibleColumns = columns.slice(start, start + visibleColumnCount);
107182
+ const hasColumnsBefore = start > 0;
107183
+ const hasColumnsAfter = start + visibleColumnCount < columns.length;
107184
+ return {
107185
+ columns,
107186
+ visibleColumns,
107187
+ groups: [{ columns: visibleColumns }],
107188
+ hasColumnsBefore,
107189
+ hasColumnsAfter,
107190
+ columnViewportText: `Columns: ${hasColumnsBefore ? "\u25C0 " : ""}${visibleColumns.map((column) => column.title).join(" / ")}${hasColumnsAfter ? " \u25B6" : ""}`
106734
107191
  };
106735
107192
  }
107193
+ function columnWidthPercent(index2, count) {
107194
+ const safeCount = Math.max(1, count);
107195
+ const base = Math.floor(100 / safeCount);
107196
+ const remainder = 100 - base * safeCount;
107197
+ const extraStart = Math.floor((safeCount - remainder) / 2);
107198
+ const width = index2 >= extraStart && index2 < extraStart + remainder ? base + 1 : base;
107199
+ return `${width}%`;
107200
+ }
107201
+ function formatWarningSummary(warnings) {
107202
+ if (warnings.length === 0)
107203
+ return "";
107204
+ const kinds = [...new Set(warnings.map((warning) => warning.split(":")[0]))].filter(Boolean).join(", ");
107205
+ return `Warnings: ${warnings.length}${kinds ? ` ${kinds}` : ""} | w details`;
107206
+ }
107207
+
107208
+ // ../tui/src/model.ts
107209
+ import { readFileSync as readFileSync8 } from "fs";
106736
107210
  function loadTuiModel(cwd = process.cwd()) {
106737
107211
  const loaded = loadProjectConfig(cwd);
106738
107212
  if (!loaded.ok)
@@ -106752,89 +107226,27 @@ function buildTuiModel(board) {
106752
107226
  title: column.title,
106753
107227
  cards: column.issues.map(formatCard)
106754
107228
  })),
106755
- warnings: board.warnings.map((warning) => `${warning.kind}: ${warning.message}`)
107229
+ warnings: board.warnings.map(formatWarning),
107230
+ ...board.warnings.length > 0 ? { warningDetails: board.warnings.map(formatTuiWarning) } : {}
106756
107231
  };
106757
107232
  }
106758
- function moveSelection(model, selection, direction, options = {}) {
106759
- if (direction === "enter") {
106760
- return { ...selection, detailOpen: true, detailScrollOffset: 0 };
106761
- }
106762
- if (selection.detailOpen && !selection.moveOpen && !selection.noteOpen && (direction === "up" || direction === "down")) {
106763
- return {
106764
- ...selection,
106765
- detailScrollOffset: clamp((selection.detailScrollOffset ?? 0) + (direction === "down" ? 1 : -1), 0, detailScrollMax(model, selection, options))
106766
- };
106767
- }
106768
- if (direction === "escape") {
106769
- if (selection.archiveOpen) {
106770
- return { ...selection, archiveOpen: false };
106771
- }
106772
- return {
106773
- ...selection,
106774
- detailOpen: false,
106775
- moveOpen: false,
106776
- noteOpen: false
106777
- };
106778
- }
106779
- if (direction === "move") {
106780
- return {
106781
- ...selection,
106782
- archiveOpen: false,
106783
- detailOpen: false,
106784
- noteOpen: false,
106785
- moveOpen: true,
106786
- moveTargetIndex: 0
106787
- };
106788
- }
106789
- if (direction === "append-note") {
106790
- return {
106791
- ...selection,
106792
- archiveOpen: false,
106793
- detailOpen: false,
106794
- moveOpen: false,
106795
- noteOpen: true
106796
- };
106797
- }
106798
- if (direction === "archive") {
106799
- return {
106800
- ...selection,
106801
- archiveOpen: true,
106802
- moveOpen: false,
106803
- noteOpen: false
106804
- };
106805
- }
106806
- const columnIndex = clamp(selection.columnIndex + (direction === "right" ? 1 : direction === "left" ? -1 : 0), 0, Math.max(0, model.columns.length - 1));
106807
- const maxCardIndex = Math.max(0, (model.columns[columnIndex]?.cards.length ?? 1) - 1);
106808
- const cardIndex = clamp(direction === "up" ? selection.cardIndex - 1 : direction === "down" ? selection.cardIndex + 1 : Math.min(selection.cardIndex, maxCardIndex), 0, maxCardIndex);
106809
- return { ...selection, columnIndex, cardIndex };
107233
+ function formatWarning(warning) {
107234
+ return `${warning.kind}: ${warning.message}`;
106810
107235
  }
106811
- function refreshTuiModel(options) {
106812
- const selectedCard = options.model.columns[options.selection.columnIndex]?.cards[options.selection.cardIndex];
106813
- const model = loadTuiModel(options.cwd);
106814
- const foundSelection = selectedCard ? findSelectionByCardId(model, selectedCard.id) : undefined;
106815
- const selection = foundSelection ?? clampSelection(model, options.selection);
106816
- const stillSelected = Boolean(foundSelection);
107236
+ function formatTuiWarning(warning) {
106817
107237
  return {
106818
- model,
106819
- selection: {
106820
- ...selection,
106821
- detailOpen: stillSelected ? options.selection.detailOpen : false,
106822
- detailScrollOffset: stillSelected ? options.selection.detailScrollOffset : undefined,
106823
- detailScrollMax: stillSelected ? options.selection.detailScrollMax : undefined,
106824
- moveOpen: stillSelected ? options.selection.moveOpen : false,
106825
- moveTargetIndex: stillSelected ? options.selection.moveTargetIndex : undefined,
106826
- noteOpen: stillSelected ? options.selection.noteOpen : false,
106827
- noteDraft: stillSelected ? options.selection.noteDraft : undefined,
106828
- message: options.selection.message,
106829
- archiveOpen: stillSelected ? options.selection.archiveOpen : false
106830
- }
107238
+ text: formatWarning(warning),
107239
+ kind: warning.kind,
107240
+ message: warning.message,
107241
+ issueId: warning.issueId,
107242
+ path: warning.path
106831
107243
  };
106832
107244
  }
106833
107245
  function getSelectedDetails(model, selection) {
106834
107246
  const card = model.columns[selection.columnIndex]?.cards[selection.cardIndex];
106835
107247
  if (!card)
106836
107248
  return;
106837
- const markdown = readFileSync7(card.path, "utf8");
107249
+ const markdown = readFileSync8(card.path, "utf8");
106838
107250
  return {
106839
107251
  card,
106840
107252
  markdown,
@@ -106845,162 +107257,128 @@ function getSelectedDetails(model, selection) {
106845
107257
  herdr: extractSection(markdown, "Herdr") || extractSection(markdown, "herdr")
106846
107258
  };
106847
107259
  }
106848
- function createTuiAppElement(props) {
106849
- return import_react17.default.createElement(TuiAppView, props);
107260
+ function cardDependsOn(card) {
107261
+ return card.dependsOn ?? [];
106850
107262
  }
106851
- function TuiAppView({
106852
- model,
106853
- selection,
106854
- theme = buildTuiTheme(),
106855
- viewportHeight
106856
- }) {
106857
- const details = selection.detailOpen ? getSelectedDetails(model, selection) : undefined;
106858
- return import_react17.default.createElement("box", {
106859
- id: "mikan-app",
106860
- style: {
106861
- backgroundColor: theme.base.canvas,
106862
- color: theme.base.text,
106863
- flexDirection: "column",
106864
- height: "100%"
106865
- }
106866
- }, import_react17.default.createElement(Header, { theme }), import_react17.default.createElement("box", {
106867
- id: "mikan-main",
106868
- style: { flexDirection: "column", flexGrow: 1, minHeight: 0 }
106869
- }, details ? import_react17.default.createElement(DetailPage, {
106870
- model,
106871
- selection,
106872
- theme,
106873
- viewportHeight
106874
- }) : import_react17.default.createElement(BoardView, {
106875
- model,
106876
- selection,
106877
- theme,
106878
- viewportHeight
106879
- })), selection.moveOpen ? import_react17.default.createElement(MovePrompt, { model, selection, theme }) : undefined, selection.noteOpen ? import_react17.default.createElement(NotePrompt, { model, selection, theme }) : undefined, selection.archiveOpen ? import_react17.default.createElement(ArchivePrompt, { model, selection, theme }) : undefined, import_react17.default.createElement(Footer, {
106880
- message: selection.message,
106881
- mode: footerMode(selection),
106882
- theme
106883
- }));
107263
+ function cardUnmetDependencies(card) {
107264
+ return card.unmetDependencies ?? [];
106884
107265
  }
106885
- function Header(props) {
106886
- const theme = props.theme ?? buildTuiTheme();
106887
- return import_react17.default.createElement("text", {
106888
- id: "mikan-header",
106889
- style: { color: theme.interactive.accent },
106890
- content: `\uD83C\uDF4A mikan v${TUI_VERSION}`
106891
- });
107266
+ function cardDependencyStatus(card) {
107267
+ return card.dependencyStatus ?? "ready";
106892
107268
  }
106893
- function buildDetailPageViewModel(model, selection, options = {}) {
106894
- const details = getSelectedDetails(model, selection);
106895
- if (!details)
106896
- return;
106897
- const markdown = stripFrontmatter(details.markdown).trimEnd();
106898
- const markdownLines = markdown.split(`
106899
- `);
106900
- const visibleLineCount = options.visibleLineCount ?? (options.viewportHeight ? visibleDetailLineCount(options.viewportHeight) : 40);
106901
- const offset = clamp(selection.detailScrollOffset ?? 0, 0, Math.max(0, markdownLines.length - visibleLineCount));
106902
- const visibleMarkdownLines = markdownLines.slice(offset, offset + visibleLineCount);
106903
- const lineEnd = offset + visibleMarkdownLines.length;
107269
+ function formatCard(issue2) {
106904
107270
  return {
106905
- id: details.card.id,
106906
- title: details.card.title,
106907
- status: details.card.status,
106908
- labelsText: details.card.labels.join(", "),
106909
- markdown,
106910
- visibleMarkdownLines,
106911
- hiddenLinesBefore: offset,
106912
- hiddenLinesAfter: Math.max(0, markdownLines.length - lineEnd),
106913
- lineRangeText: formatLineRange({
106914
- start: offset + 1,
106915
- end: lineEnd,
106916
- total: markdownLines.length,
106917
- hiddenBefore: offset,
106918
- hiddenAfter: Math.max(0, markdownLines.length - lineEnd)
106919
- })
107271
+ id: String(issue2.issue.id),
107272
+ title: issue2.issue.title,
107273
+ labels: issue2.issue.labels.map(String),
107274
+ status: String(issue2.status),
107275
+ path: issue2.path,
107276
+ dependsOn: issue2.issue.dependencies.map(String),
107277
+ unmetDependencies: issue2.unmetDependencies.map(String),
107278
+ dependencyStatus: issue2.dependencyStatus
106920
107279
  };
106921
107280
  }
106922
- function buildBoardViewModel(model, selection, options = {}) {
106923
- const visibleCardCount = Math.max(1, options.visibleCardCount ?? (options.viewportHeight ? visibleCardCountForViewport(options.viewportHeight) : 6));
106924
- const columns = model.columns.map((column, columnIndex) => {
106925
- const cards = column.cards.map((card, cardIndex) => ({
106926
- ...card,
106927
- selected: columnIndex === selection.columnIndex && cardIndex === selection.cardIndex,
106928
- labelsText: card.labels.join(", ")
106929
- }));
106930
- const maxCardStart = Math.max(0, cards.length - visibleCardCount);
106931
- const cardStart = cards.length <= visibleCardCount ? 0 : columnIndex === selection.columnIndex ? clamp(selection.cardIndex - Math.floor(visibleCardCount / 2), 0, maxCardStart) : 0;
106932
- const visibleCards = cards.slice(cardStart, cardStart + visibleCardCount);
106933
- const cardEnd = cardStart + visibleCards.length;
106934
- return {
106935
- id: column.id,
106936
- title: column.title,
106937
- count: column.cards.length,
106938
- active: columnIndex === selection.columnIndex,
106939
- empty: column.cards.length === 0,
106940
- emptyText: "No Issues",
106941
- cards,
106942
- visibleCards,
106943
- hiddenCardsBefore: cardStart,
106944
- hiddenCardsAfter: Math.max(0, cards.length - cardEnd),
106945
- cardRangeText: cards.length > visibleCardCount ? `${cardStart + 1}-${cardEnd}/${cards.length} | \u2191${cardStart} | \u2193${Math.max(0, cards.length - cardEnd)}` : ""
106946
- };
106947
- });
106948
- const visibleColumnCount = Math.max(1, options.visibleColumnCount ?? 3);
106949
- const maxStart = Math.max(0, columns.length - visibleColumnCount);
106950
- const start = clamp(selection.columnIndex - (visibleColumnCount - 1), 0, maxStart);
106951
- const visibleColumns = columns.slice(start, start + visibleColumnCount);
106952
- const hasColumnsBefore = start > 0;
106953
- const hasColumnsAfter = start + visibleColumnCount < columns.length;
107281
+ function stripFrontmatter(markdown) {
107282
+ if (!markdown.startsWith(`---
107283
+ `))
107284
+ return markdown;
107285
+ const end = markdown.indexOf(`
107286
+ ---
107287
+ `, 4);
107288
+ if (end === -1)
107289
+ return markdown;
107290
+ return markdown.slice(end + `
107291
+ ---
107292
+ `.length).trimStart();
107293
+ }
107294
+ function extractSection(markdown, section) {
107295
+ const lines = markdown.split(`
107296
+ `);
107297
+ const start = lines.findIndex((line) => line.trim().toLowerCase() === `## ${section}`.toLowerCase());
107298
+ if (start === -1)
107299
+ return "";
107300
+ let end = lines.length;
107301
+ for (let index2 = start + 1;index2 < lines.length; index2++) {
107302
+ if (/^##\s+/.test(lines[index2] ?? "")) {
107303
+ end = index2;
107304
+ break;
107305
+ }
107306
+ }
107307
+ return lines.slice(start + 1, end).join(`
107308
+ `).trim();
107309
+ }
107310
+
107311
+ // ../tui/src/theme.ts
107312
+ function buildTuiTheme() {
106954
107313
  return {
106955
- columns,
106956
- visibleColumns,
106957
- groups: [{ columns: visibleColumns }],
106958
- hasColumnsBefore,
106959
- hasColumnsAfter,
106960
- columnViewportText: `Columns: ${hasColumnsBefore ? "\u25C0 " : ""}${visibleColumns.map((column) => column.title).join(" / ")}${hasColumnsAfter ? " \u25B6" : ""}`
107314
+ base: {
107315
+ canvas: "#1f1a14",
107316
+ surface: "#2a2118",
107317
+ text: "#eadfce",
107318
+ muted: "#9c8870"
107319
+ },
107320
+ interactive: {
107321
+ accent: "#f0a04b",
107322
+ focus: "#f6c177",
107323
+ selectedSurface: "#3a2a1d"
107324
+ },
107325
+ feedback: {
107326
+ warning: "#f6c177",
107327
+ error: "#d66a4a",
107328
+ success: "#8faa5f"
107329
+ }
106961
107330
  };
106962
107331
  }
107332
+
107333
+ // ../tui/src/board-view.ts
106963
107334
  function BoardView({
106964
107335
  model,
106965
107336
  selection,
106966
107337
  theme = buildTuiTheme(),
106967
- viewportHeight
107338
+ viewportHeight,
107339
+ viewportWidth,
107340
+ columns
106968
107341
  }) {
106969
- const view = buildBoardViewModel(model, selection, { viewportHeight });
106970
- return import_react17.default.createElement("box", {
107342
+ const view = buildBoardViewModel(model, selection, {
107343
+ viewportHeight,
107344
+ ...typeof columns === "number" ? { visibleColumnCount: columns } : { viewportWidth }
107345
+ });
107346
+ return import_react.default.createElement("box", {
106971
107347
  id: "mikan-board",
106972
107348
  style: { flexDirection: "column", flexGrow: 1, minHeight: 0 }
106973
- }, import_react17.default.createElement("text", { content: view.columnViewportText }), ...view.groups.map((group, groupIndex) => import_react17.default.createElement("box", {
107349
+ }, model.warnings.length > 0 ? import_react.default.createElement("text", {
107350
+ content: formatWarningSummary(model.warnings),
107351
+ style: { color: theme.feedback.warning }
107352
+ }) : undefined, import_react.default.createElement("text", { content: view.columnViewportText }), ...view.groups.map((group, groupIndex) => import_react.default.createElement("box", {
106974
107353
  key: `group-${groupIndex}`,
106975
107354
  id: `board-row-${groupIndex}`,
106976
107355
  style: { flexDirection: "row", flexGrow: 1, minHeight: 0 }
106977
- }, ...group.columns.map((column) => import_react17.default.createElement(ColumnPane, {
107356
+ }, ...group.columns.map((column, columnIndex) => import_react.default.createElement(ColumnPane, {
106978
107357
  key: column.id,
106979
107358
  column,
106980
107359
  theme,
106981
- width: `${(100 / group.columns.length).toFixed(2)}%`
106982
- })))), model.warnings.length > 0 ? import_react17.default.createElement("text", {
106983
- content: [
106984
- `Warnings: ${model.warnings.length}`,
106985
- ...model.warnings
106986
- ].join(`
106987
- `)
106988
- }) : undefined);
107360
+ width: columnWidthPercent(columnIndex, group.columns.length)
107361
+ })))));
106989
107362
  }
106990
107363
  function ColumnPane(props) {
106991
107364
  const theme = props.theme ?? buildTuiTheme();
106992
- const children = [
106993
- props.column.cardRangeText ? import_react17.default.createElement("text", { content: props.column.cardRangeText }) : undefined,
106994
- ...props.column.empty ? [import_react17.default.createElement("text", { content: props.column.emptyText })] : props.column.visibleCards.map((card) => import_react17.default.createElement(IssueCard, {
106995
- key: card.id,
106996
- card,
106997
- selected: card.selected,
106998
- theme
106999
- }))
107000
- ];
107001
- return import_react17.default.createElement("box", {
107365
+ const cardChildren = props.column.empty ? [
107366
+ import_react.default.createElement("text", {
107367
+ id: `column-${props.column.id}-empty`,
107368
+ content: props.column.emptyText
107369
+ })
107370
+ ] : props.column.visibleCards.map((card) => import_react.default.createElement(IssueCard, {
107371
+ key: card.id,
107372
+ card,
107373
+ selected: card.selected,
107374
+ theme
107375
+ }));
107376
+ const children = cardChildren;
107377
+ return import_react.default.createElement("box", {
107002
107378
  id: `column-${props.column.id}`,
107003
107379
  title: `${props.column.active ? "\u25B6 " : ""}${props.column.title} (${props.column.count})`,
107380
+ bottomTitle: props.column.cardRangeText || undefined,
107381
+ bottomTitleAlignment: "center",
107004
107382
  border: true,
107005
107383
  focused: props.column.active,
107006
107384
  style: {
@@ -107012,31 +107390,141 @@ function ColumnPane(props) {
107012
107390
  }
107013
107391
  }, ...children);
107014
107392
  }
107393
+ function issueCardContent(card, selected, theme) {
107394
+ const chunks = [];
107395
+ if (selected)
107396
+ chunks.push(fg(theme.interactive.focus)("\u25B6 "));
107397
+ chunks.push(fg(theme.interactive.accent)(card.id));
107398
+ if (cardDependencyStatus(card) === "blocked") {
107399
+ chunks.push(fg(theme.base.text)(" "));
107400
+ chunks.push(fg(theme.feedback.warning)("deps!"));
107401
+ }
107402
+ chunks.push(fg(theme.base.muted)(" \u2502 "));
107403
+ chunks.push(fg(theme.base.text)(card.title));
107404
+ if (card.labels.length > 0) {
107405
+ chunks.push(fg(theme.base.text)(" "));
107406
+ chunks.push(fg(theme.base.muted)(formatLabels(card.labels)));
107407
+ }
107408
+ return new StyledText(chunks);
107409
+ }
107015
107410
  function IssueCard(props) {
107016
107411
  const theme = props.theme ?? buildTuiTheme();
107017
- return import_react17.default.createElement("box", {
107412
+ return import_react.default.createElement("box", {
107018
107413
  id: `card-${props.card.id}`,
107019
- border: true,
107414
+ border: false,
107020
107415
  focused: props.selected,
107021
107416
  style: {
107022
107417
  backgroundColor: props.selected ? theme.interactive.selectedSurface : theme.base.surface,
107023
- borderColor: props.selected ? theme.interactive.focus : theme.base.muted,
107024
- color: props.selected ? theme.interactive.focus : theme.base.text,
107025
107418
  flexDirection: "column",
107026
- height: 3
107419
+ height: 1
107027
107420
  }
107028
- }, import_react17.default.createElement("text", {
107029
- content: `${props.selected ? "\u25B6 " : ""}${props.card.id} ${props.card.title}${props.card.labels.length > 0 ? ` ${formatLabels(props.card.labels)}` : ""}`
107421
+ }, import_react.default.createElement("text", {
107422
+ content: issueCardContent(props.card, props.selected, theme)
107030
107423
  }));
107031
107424
  }
107425
+ function Footer(props) {
107426
+ const theme = props.theme ?? buildTuiTheme();
107427
+ return import_react.default.createElement("text", {
107428
+ id: "mikan-footer",
107429
+ style: { color: theme.base.muted, marginTop: "auto" },
107430
+ content: [footerText(props.mode ?? "board"), props.message].filter(Boolean).join(" ")
107431
+ });
107432
+ }
107433
+
107434
+ // ../tui/src/detail-view.ts
107435
+ await init_core3();
107436
+ var import_react2 = __toESM(require_react(), 1);
107437
+
107438
+ // ../tui/src/detail-view-model.ts
107439
+ function buildDetailPageViewModel(model, selection, options = {}) {
107440
+ const details = getSelectedDetails(model, selection);
107441
+ if (!details)
107442
+ return;
107443
+ const markdown = stripFrontmatter(details.markdown).trimEnd();
107444
+ const markdownLines = markdown.split(`
107445
+ `);
107446
+ const visibleLineCount = options.visibleLineCount ?? (options.viewportHeight ? visibleDetailLineCount(options.viewportHeight) : 40);
107447
+ const offset = clamp(selection.detailScrollOffset ?? 0, 0, Math.max(0, markdownLines.length - visibleLineCount));
107448
+ const visibleMarkdownLines = markdownLines.slice(offset, offset + visibleLineCount);
107449
+ const lineEnd = offset + visibleMarkdownLines.length;
107450
+ return {
107451
+ id: details.card.id,
107452
+ title: details.card.title,
107453
+ status: details.card.status,
107454
+ labelsText: details.card.labels.join(", "),
107455
+ dependsOnText: cardDependsOn(details.card).join(", "),
107456
+ unmetDependenciesText: cardUnmetDependencies(details.card).join(", "),
107457
+ dependencyStatus: cardDependencyStatus(details.card),
107458
+ warningCount: warningCountForCard(model.warningDetails, details.card),
107459
+ markdown,
107460
+ visibleMarkdownLines,
107461
+ hiddenLinesBefore: offset,
107462
+ hiddenLinesAfter: Math.max(0, markdownLines.length - lineEnd),
107463
+ lineRangeText: formatLineRange({
107464
+ start: offset + 1,
107465
+ end: lineEnd,
107466
+ total: markdownLines.length,
107467
+ hiddenBefore: offset,
107468
+ hiddenAfter: Math.max(0, markdownLines.length - lineEnd)
107469
+ })
107470
+ };
107471
+ }
107472
+ function warningCountForCard(warnings, card) {
107473
+ return (warnings ?? []).filter((warning) => warning.issueId === card.id || warning.path === card.path).length;
107474
+ }
107475
+
107476
+ // ../tui/src/detail-view.ts
107477
+ function detailLabelsText(page) {
107478
+ return page.labelsText ? formatLabels(page.labelsText.split(", ").filter(Boolean)) : "none";
107479
+ }
107480
+ function detailDependencyText(page) {
107481
+ if (page.unmetDependenciesText)
107482
+ return `deps unmet ${page.unmetDependenciesText}`;
107483
+ return page.dependsOnText ? `deps ${page.dependencyStatus}` : "";
107484
+ }
107485
+ function detailStatusColor(status, theme) {
107486
+ if (status === "completed")
107487
+ return theme.feedback.success;
107488
+ if (status === "blocked")
107489
+ return theme.feedback.warning;
107490
+ if (status === "active")
107491
+ return theme.interactive.accent;
107492
+ return theme.base.muted;
107493
+ }
107494
+ function detailTitleContent(page, theme) {
107495
+ const chunks = [
107496
+ fg(theme.interactive.accent)(page.id),
107497
+ fg(theme.base.muted)(" \u2502 "),
107498
+ fg(theme.base.text)(page.title)
107499
+ ];
107500
+ if (page.lineRangeText) {
107501
+ chunks.push(fg(theme.base.muted)(" \u2502 "));
107502
+ chunks.push(fg(theme.base.muted)(page.lineRangeText));
107503
+ }
107504
+ return new StyledText(chunks);
107505
+ }
107506
+ function detailMetaContent(page, theme) {
107507
+ const chunks = [
107508
+ fg(detailStatusColor(page.status, theme))(page.status),
107509
+ fg(theme.base.muted)(" \xB7 labels "),
107510
+ fg(theme.base.muted)(detailLabelsText(page))
107511
+ ];
107512
+ const dependency = detailDependencyText(page);
107513
+ if (dependency)
107514
+ chunks.push(fg(theme.feedback.warning)(` \xB7 ${dependency}`));
107515
+ if (page.warningCount > 0) {
107516
+ chunks.push(fg(theme.feedback.warning)(` \xB7 warnings ${page.warningCount}`));
107517
+ }
107518
+ return new StyledText(chunks);
107519
+ }
107032
107520
  function DetailPage(props) {
107033
107521
  const page = buildDetailPageViewModel(props.model, props.selection, {
107034
107522
  viewportHeight: props.viewportHeight
107035
107523
  });
107036
107524
  const theme = props.theme ?? buildTuiTheme();
107037
107525
  if (!page)
107038
- return import_react17.default.createElement("text", { content: "No Issue selected" });
107039
- return import_react17.default.createElement("box", {
107526
+ return import_react2.default.createElement("text", { content: "No Issue selected" });
107527
+ return import_react2.default.createElement("box", {
107040
107528
  id: "detail-page",
107041
107529
  title: "Detail",
107042
107530
  border: true,
@@ -107047,20 +107535,18 @@ function DetailPage(props) {
107047
107535
  flexGrow: 1,
107048
107536
  overflow: "hidden"
107049
107537
  }
107050
- }, import_react17.default.createElement("box", {
107538
+ }, import_react2.default.createElement("box", {
107051
107539
  id: "detail-header",
107052
107540
  style: {
107053
107541
  backgroundColor: theme.base.surface,
107054
107542
  flexDirection: "column",
107055
107543
  flexShrink: 0
107056
107544
  }
107057
- }, import_react17.default.createElement("text", {
107058
- content: `${page.id} ${page.title}`
107059
- }), import_react17.default.createElement("text", {
107060
- content: `Status: ${page.status} | Labels: ${page.labelsText ? formatLabels(page.labelsText.split(", ").filter(Boolean)) : "none"}${page.lineRangeText ? ` | ${page.lineRangeText}` : ""}`
107061
- }), import_react17.default.createElement("text", {
107062
- content: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
107063
- })), import_react17.default.createElement("box", {
107545
+ }, import_react2.default.createElement("text", {
107546
+ content: detailTitleContent(page, theme)
107547
+ }), import_react2.default.createElement("text", {
107548
+ content: detailMetaContent(page, theme)
107549
+ })), import_react2.default.createElement("box", {
107064
107550
  id: "detail-markdown-body",
107065
107551
  style: {
107066
107552
  flexDirection: "column",
@@ -107068,7 +107554,7 @@ function DetailPage(props) {
107068
107554
  minHeight: 0,
107069
107555
  overflow: "hidden"
107070
107556
  }
107071
- }, import_react17.default.createElement("markdown", {
107557
+ }, import_react2.default.createElement("markdown", {
107072
107558
  id: "detail-markdown",
107073
107559
  content: page.visibleMarkdownLines.join(`
107074
107560
  `),
@@ -107079,47 +107565,256 @@ function DetailPage(props) {
107079
107565
  }
107080
107566
  })));
107081
107567
  }
107568
+
107569
+ // ../tui/src/modals.ts
107570
+ var import_react3 = __toESM(require_react(), 1);
107571
+
107572
+ // ../tui/src/navigation.ts
107573
+ function moveSelection(model, selection, direction, options = {}) {
107574
+ if (direction === "enter") {
107575
+ const card = model.columns[selection.columnIndex]?.cards[selection.cardIndex];
107576
+ if (!card) {
107577
+ return { ...selection, detailOpen: false, message: "No Issue selected" };
107578
+ }
107579
+ return {
107580
+ ...selection,
107581
+ detailOpen: true,
107582
+ detailScrollOffset: 0,
107583
+ message: undefined
107584
+ };
107585
+ }
107586
+ if (selection.detailOpen && !selection.moveOpen && !selection.noteOpen) {
107587
+ if (direction === "up" || direction === "down") {
107588
+ return {
107589
+ ...selection,
107590
+ detailScrollOffset: clamp((selection.detailScrollOffset ?? 0) + (direction === "down" ? 1 : -1), 0, detailScrollMax(model, selection, options))
107591
+ };
107592
+ }
107593
+ if (direction === "left" || direction === "right") {
107594
+ return selection;
107595
+ }
107596
+ }
107597
+ if (direction === "escape") {
107598
+ if (selection.helpOpen) {
107599
+ return { ...selection, helpOpen: false };
107600
+ }
107601
+ if (selection.archiveOpen) {
107602
+ return { ...selection, archiveOpen: false };
107603
+ }
107604
+ if (selection.warningsOpen) {
107605
+ return { ...selection, warningsOpen: false };
107606
+ }
107607
+ return {
107608
+ ...selection,
107609
+ detailOpen: false,
107610
+ moveOpen: false,
107611
+ noteOpen: false
107612
+ };
107613
+ }
107614
+ if (direction === "move") {
107615
+ return {
107616
+ ...selection,
107617
+ archiveOpen: false,
107618
+ detailOpen: false,
107619
+ noteOpen: false,
107620
+ moveOpen: true,
107621
+ moveTargetIndex: 0
107622
+ };
107623
+ }
107624
+ if (direction === "append-note") {
107625
+ return {
107626
+ ...selection,
107627
+ archiveOpen: false,
107628
+ detailOpen: false,
107629
+ moveOpen: false,
107630
+ noteOpen: true
107631
+ };
107632
+ }
107633
+ if (direction === "archive") {
107634
+ return {
107635
+ ...selection,
107636
+ archiveOpen: true,
107637
+ moveOpen: false,
107638
+ noteOpen: false
107639
+ };
107640
+ }
107641
+ if (direction === "warnings") {
107642
+ return model.warnings.length > 0 ? { ...selection, warningsOpen: !selection.warningsOpen } : { ...selection, message: "No warnings" };
107643
+ }
107644
+ if (direction === "help") {
107645
+ return { ...selection, helpOpen: !selection.helpOpen };
107646
+ }
107647
+ const columnIndex = clamp(selection.columnIndex + (direction === "right" ? 1 : direction === "left" ? -1 : 0), 0, Math.max(0, model.columns.length - 1));
107648
+ const maxCardIndex = Math.max(0, (model.columns[columnIndex]?.cards.length ?? 1) - 1);
107649
+ const cardIndex = clamp(direction === "up" ? selection.cardIndex - 1 : direction === "down" ? selection.cardIndex + 1 : Math.min(selection.cardIndex, maxCardIndex), 0, maxCardIndex);
107650
+ return { ...selection, columnIndex, cardIndex };
107651
+ }
107652
+ function getMoveTargets(model, selection) {
107653
+ const currentStatus = model.columns[selection.columnIndex]?.id;
107654
+ return model.columns.filter((column) => column.id !== currentStatus).map((column) => ({ id: column.id, title: column.title }));
107655
+ }
107656
+ function getAdjacentMoveTarget(model, selection, direction) {
107657
+ const offset = direction === "left" ? -1 : 1;
107658
+ const column = model.columns[selection.columnIndex + offset];
107659
+ return column ? { id: column.id, title: column.title } : undefined;
107660
+ }
107661
+ function applyNoteInput(selection, keyName2, shift = false) {
107662
+ if (!selection.noteOpen || !keyName2)
107663
+ return selection;
107664
+ if (keyName2 === "backspace") {
107665
+ return {
107666
+ ...selection,
107667
+ noteDraft: (selection.noteDraft ?? "").slice(0, -1)
107668
+ };
107669
+ }
107670
+ const character = keyName2 === "space" ? " " : keyName2;
107671
+ if (character.length !== 1)
107672
+ return selection;
107673
+ const value = shift && /[a-z]/.test(character) ? character.toUpperCase() : character;
107674
+ return { ...selection, noteDraft: `${selection.noteDraft ?? ""}${value}` };
107675
+ }
107676
+ function footerMode(selection) {
107677
+ if (selection.moveOpen || selection.noteOpen || selection.archiveOpen) {
107678
+ return "modal";
107679
+ }
107680
+ return selection.detailOpen ? "detail" : "board";
107681
+ }
107682
+ function keyToTuiAction(keyName2, shift = false) {
107683
+ if (shift && keyName2 === "h")
107684
+ return "move-left";
107685
+ if (shift && keyName2 === "l")
107686
+ return "move-right";
107687
+ switch (keyName2) {
107688
+ case "left":
107689
+ case "right":
107690
+ case "up":
107691
+ case "down":
107692
+ case "enter":
107693
+ case "escape":
107694
+ return keyName2;
107695
+ case "h":
107696
+ return "left";
107697
+ case "l":
107698
+ return "right";
107699
+ case "j":
107700
+ return "down";
107701
+ case "k":
107702
+ return "up";
107703
+ case "return":
107704
+ return "enter";
107705
+ case "H":
107706
+ return "move-left";
107707
+ case "L":
107708
+ return "move-right";
107709
+ case "r":
107710
+ return "reload";
107711
+ case "m":
107712
+ return "move";
107713
+ case "n":
107714
+ return "append-note";
107715
+ case "a":
107716
+ return "archive";
107717
+ case "w":
107718
+ return "warnings";
107719
+ case "?":
107720
+ return "help";
107721
+ case "q":
107722
+ return "quit";
107723
+ default:
107724
+ return;
107725
+ }
107726
+ }
107727
+ function detailScrollMax(model, selection, options = {}) {
107728
+ const details = getSelectedDetails(model, selection);
107729
+ if (!details)
107730
+ return 0;
107731
+ const visibleLineCount = options.viewportHeight ? visibleDetailLineCount(options.viewportHeight) : 40;
107732
+ return Math.max(0, stripFrontmatter(details.markdown).trimEnd().split(`
107733
+ `).length - visibleLineCount);
107734
+ }
107735
+
107736
+ // ../tui/src/prompt-view-model.ts
107737
+ function buildMovePromptViewModel(model, selection) {
107738
+ const card = model.columns[selection.columnIndex]?.cards[selection.cardIndex];
107739
+ if (!card)
107740
+ return;
107741
+ const targetIndex = selection.moveTargetIndex ?? 0;
107742
+ return {
107743
+ title: `Move ${card.id}`,
107744
+ focused: Boolean(selection.moveOpen),
107745
+ targets: getMoveTargets(model, selection).map((target, index2) => ({
107746
+ ...target,
107747
+ selected: index2 === targetIndex
107748
+ })),
107749
+ hint: "enter move esc cancel"
107750
+ };
107751
+ }
107752
+ function buildNotePromptViewModel(model, selection) {
107753
+ const card = model.columns[selection.columnIndex]?.cards[selection.cardIndex];
107754
+ if (!card)
107755
+ return;
107756
+ return {
107757
+ title: `Append note to ${card.id}`,
107758
+ focused: Boolean(selection.noteOpen),
107759
+ draft: selection.noteDraft ?? "",
107760
+ hint: "enter append esc cancel"
107761
+ };
107762
+ }
107763
+ function buildArchivePromptViewModel(model, selection) {
107764
+ const card = model.columns[selection.columnIndex]?.cards[selection.cardIndex];
107765
+ if (!card)
107766
+ return;
107767
+ return {
107768
+ title: `Archive ${card.id}?`,
107769
+ focused: Boolean(selection.archiveOpen),
107770
+ body: `${card.title}
107771
+ Move to archived. It will disappear from the default board.`,
107772
+ hint: "enter archive esc cancel"
107773
+ };
107774
+ }
107775
+
107776
+ // ../tui/src/modals.ts
107082
107777
  function MovePrompt(props) {
107083
107778
  const theme = props.theme ?? buildTuiTheme();
107084
- return import_react17.default.createElement("box", {
107779
+ return import_react3.default.createElement("box", {
107085
107780
  id: "move-modal-backdrop",
107086
107781
  style: modalBackdropStyle(theme)
107087
- }, import_react17.default.createElement("box", {
107782
+ }, import_react3.default.createElement("box", {
107088
107783
  id: "move-prompt",
107089
107784
  title: "Move Issue",
107090
107785
  border: true,
107091
107786
  style: modalStyle(theme)
107092
- }, import_react17.default.createElement("text", {
107787
+ }, import_react3.default.createElement("text", {
107093
107788
  content: renderMoveInteraction(props.model, props.selection).join(`
107094
107789
  `)
107095
107790
  })));
107096
107791
  }
107097
107792
  function NotePrompt(props) {
107098
107793
  const theme = props.theme ?? buildTuiTheme();
107099
- return import_react17.default.createElement("box", {
107794
+ return import_react3.default.createElement("box", {
107100
107795
  id: "note-modal-backdrop",
107101
107796
  style: modalBackdropStyle(theme)
107102
- }, import_react17.default.createElement("box", {
107797
+ }, import_react3.default.createElement("box", {
107103
107798
  id: "note-prompt",
107104
107799
  title: "Append Note",
107105
107800
  border: true,
107106
107801
  style: modalStyle(theme)
107107
- }, import_react17.default.createElement("text", {
107802
+ }, import_react3.default.createElement("text", {
107108
107803
  content: renderNoteInteraction(props.model, props.selection).join(`
107109
107804
  `)
107110
107805
  })));
107111
107806
  }
107112
107807
  function ArchivePrompt(props) {
107113
107808
  const theme = props.theme ?? buildTuiTheme();
107114
- return import_react17.default.createElement("box", {
107809
+ return import_react3.default.createElement("box", {
107115
107810
  id: "archive-modal-backdrop",
107116
107811
  style: modalBackdropStyle(theme)
107117
- }, import_react17.default.createElement("box", {
107812
+ }, import_react3.default.createElement("box", {
107118
107813
  id: "archive-prompt",
107119
107814
  title: "Archive Issue",
107120
107815
  border: true,
107121
107816
  style: modalStyle(theme)
107122
- }, import_react17.default.createElement("text", {
107817
+ }, import_react3.default.createElement("text", {
107123
107818
  content: renderArchiveInteraction(props.model, props.selection).join(`
107124
107819
  `)
107125
107820
  })));
@@ -107146,43 +107841,103 @@ function modalStyle(theme) {
107146
107841
  width: "70%"
107147
107842
  };
107148
107843
  }
107149
- function Footer(props) {
107844
+ function HelpPanel(props) {
107150
107845
  const theme = props.theme ?? buildTuiTheme();
107151
- return import_react17.default.createElement("text", {
107152
- id: "mikan-footer",
107153
- style: { color: theme.base.muted, marginTop: "auto" },
107154
- content: [footerText(props.mode ?? "board"), props.message].filter(Boolean).join(" | ")
107155
- });
107846
+ return import_react3.default.createElement("box", {
107847
+ id: "help-panel-backdrop",
107848
+ style: modalBackdropStyle(theme)
107849
+ }, import_react3.default.createElement("box", {
107850
+ id: "help-panel",
107851
+ title: "Key help",
107852
+ border: true,
107853
+ style: modalStyle(theme)
107854
+ }, import_react3.default.createElement("text", { content: renderKeyHelp().join(`
107855
+ `) })));
107156
107856
  }
107157
- function footerMode(selection) {
107158
- if (selection.moveOpen || selection.noteOpen || selection.archiveOpen) {
107159
- return "modal";
107160
- }
107161
- return selection.detailOpen ? "detail" : "board";
107857
+ function WarningPanel(props) {
107858
+ const theme = props.theme ?? buildTuiTheme();
107859
+ return import_react3.default.createElement("box", {
107860
+ id: "warning-panel",
107861
+ title: "Warning details",
107862
+ border: true,
107863
+ style: {
107864
+ backgroundColor: theme.base.surface,
107865
+ borderColor: theme.feedback.warning,
107866
+ flexDirection: "column"
107867
+ }
107868
+ }, import_react3.default.createElement("text", {
107869
+ content: props.model.warnings.length > 0 ? props.model.warnings.map((warning) => `! ${warning}`).join(`
107870
+ `) : "No warnings"
107871
+ }));
107162
107872
  }
107163
- function getMoveTargets(model, selection) {
107164
- const currentStatus = model.columns[selection.columnIndex]?.id;
107165
- return model.columns.filter((column) => column.id !== currentStatus).map((column) => ({ id: column.id, title: column.title }));
107873
+ function renderMoveInteraction(model, selection) {
107874
+ const view = buildMovePromptViewModel(model, selection);
107875
+ if (!view)
107876
+ return ["Move", "No Issue selected"];
107877
+ return [
107878
+ `${view.title} to Status`,
107879
+ ...view.targets.map((target) => `${target.selected ? ">" : " "} ${target.id} (${target.title})`),
107880
+ view.hint
107881
+ ];
107166
107882
  }
107167
- function getAdjacentMoveTarget(model, selection, direction) {
107168
- const offset = direction === "left" ? -1 : 1;
107169
- const column = model.columns[selection.columnIndex + offset];
107170
- return column ? { id: column.id, title: column.title } : undefined;
107883
+ function renderNoteInteraction(model, selection) {
107884
+ const view = buildNotePromptViewModel(model, selection);
107885
+ if (!view)
107886
+ return ["Append note", "No Issue selected"];
107887
+ return [
107888
+ view.title,
107889
+ `Note: ${view.draft}`,
107890
+ ...view.feedback ? [view.feedback] : [],
107891
+ view.hint
107892
+ ];
107171
107893
  }
107172
- function applyNoteInput(selection, keyName2, shift = false) {
107173
- if (!selection.noteOpen || !keyName2)
107174
- return selection;
107175
- if (keyName2 === "backspace") {
107176
- return {
107894
+ function renderArchiveInteraction(model, selection) {
107895
+ const view = buildArchivePromptViewModel(model, selection);
107896
+ if (!view)
107897
+ return ["Archive", "No Issue selected"];
107898
+ return [view.title, view.body, view.hint];
107899
+ }
107900
+ function renderKeyHelp() {
107901
+ return [
107902
+ "Key help",
107903
+ "\u2191/\u2193 or j/k card/scroll",
107904
+ "\u2190/\u2192 or h/l column",
107905
+ "enter detail/confirm",
107906
+ "esc back/cancel",
107907
+ "H/L move Issue",
107908
+ "m move menu",
107909
+ "n append Note",
107910
+ "a archive Issue",
107911
+ "w warning details",
107912
+ "r reload",
107913
+ "q quit"
107914
+ ];
107915
+ }
107916
+
107917
+ // ../tui/src/mutations.ts
107918
+ function refreshTuiModel(options) {
107919
+ const selectedCard = options.model.columns[options.selection.columnIndex]?.cards[options.selection.cardIndex];
107920
+ const model = loadTuiModel(options.cwd);
107921
+ const foundSelection = selectedCard ? findSelectionByCardId(model, selectedCard.id) : undefined;
107922
+ const selection = foundSelection ?? clampSelection(model, options.selection);
107923
+ const stillSelected = Boolean(foundSelection);
107924
+ return {
107925
+ model,
107926
+ selection: {
107177
107927
  ...selection,
107178
- noteDraft: (selection.noteDraft ?? "").slice(0, -1)
107179
- };
107180
- }
107181
- const character = keyName2 === "space" ? " " : keyName2;
107182
- if (character.length !== 1)
107183
- return selection;
107184
- const value = shift && /[a-z]/.test(character) ? character.toUpperCase() : character;
107185
- return { ...selection, noteDraft: `${selection.noteDraft ?? ""}${value}` };
107928
+ detailOpen: stillSelected ? options.selection.detailOpen : false,
107929
+ detailScrollOffset: stillSelected ? options.selection.detailScrollOffset : undefined,
107930
+ detailScrollMax: stillSelected ? options.selection.detailScrollMax : undefined,
107931
+ moveOpen: stillSelected ? options.selection.moveOpen : false,
107932
+ moveTargetIndex: stillSelected ? options.selection.moveTargetIndex : undefined,
107933
+ noteOpen: stillSelected ? options.selection.noteOpen : false,
107934
+ noteDraft: stillSelected ? options.selection.noteDraft : undefined,
107935
+ message: options.selection.message,
107936
+ archiveOpen: stillSelected ? options.selection.archiveOpen : false,
107937
+ warningsOpen: options.selection.warningsOpen,
107938
+ helpOpen: options.selection.helpOpen
107939
+ }
107940
+ };
107186
107941
  }
107187
107942
  function moveSelectedIssueByDirection(options) {
107188
107943
  const target = getAdjacentMoveTarget(options.model, options.selection, options.direction);
@@ -107325,70 +108080,57 @@ function appendSelectedIssueNote(options) {
107325
108080
  message: `${card.id} note appended`
107326
108081
  };
107327
108082
  }
107328
- function buildMovePromptViewModel(model, selection) {
107329
- const card = model.columns[selection.columnIndex]?.cards[selection.cardIndex];
107330
- if (!card)
107331
- return;
107332
- const targetIndex = selection.moveTargetIndex ?? 0;
107333
- return {
107334
- title: `Move ${card.id}`,
107335
- focused: Boolean(selection.moveOpen),
107336
- targets: getMoveTargets(model, selection).map((target, index2) => ({
107337
- ...target,
107338
- selected: index2 === targetIndex
107339
- })),
107340
- hint: "enter move esc cancel"
107341
- };
107342
- }
107343
- function buildNotePromptViewModel(model, selection) {
107344
- const card = model.columns[selection.columnIndex]?.cards[selection.cardIndex];
107345
- if (!card)
107346
- return;
107347
- return {
107348
- title: `Append note to ${card.id}`,
107349
- focused: Boolean(selection.noteOpen),
107350
- draft: selection.noteDraft ?? "",
107351
- hint: "enter append esc cancel"
107352
- };
107353
- }
107354
- function buildArchivePromptViewModel(model, selection) {
107355
- const card = model.columns[selection.columnIndex]?.cards[selection.cardIndex];
107356
- if (!card)
107357
- return;
107358
- return {
107359
- title: `Archive ${card.id}?`,
107360
- focused: Boolean(selection.archiveOpen),
107361
- body: `${card.title}
107362
- Move to archived. It will disappear from the default board.`,
107363
- hint: "enter archive esc cancel"
107364
- };
107365
- }
107366
- function renderMoveInteraction(model, selection) {
107367
- const view = buildMovePromptViewModel(model, selection);
107368
- if (!view)
107369
- return ["Move", "No Issue selected"];
107370
- return [
107371
- `${view.title} to Status`,
107372
- ...view.targets.map((target) => `${target.selected ? ">" : " "} ${target.id} (${target.title})`),
107373
- view.hint
107374
- ];
108083
+
108084
+ // ../tui/src/index.ts
108085
+ var TUI_VERSION = package_default.version;
108086
+ function createTuiAppElement(props) {
108087
+ return import_react20.default.createElement(TuiAppView, props);
107375
108088
  }
107376
- function renderNoteInteraction(model, selection) {
107377
- const view = buildNotePromptViewModel(model, selection);
107378
- if (!view)
107379
- return ["Append note", "No Issue selected"];
107380
- return [
107381
- view.title,
107382
- `Note: ${view.draft}`,
107383
- ...view.feedback ? [view.feedback] : [],
107384
- view.hint
107385
- ];
108089
+ function TuiAppView({
108090
+ model,
108091
+ selection,
108092
+ theme = buildTuiTheme(),
108093
+ viewportHeight,
108094
+ viewportWidth,
108095
+ columns
108096
+ }) {
108097
+ const details = selection.detailOpen ? getSelectedDetails(model, selection) : undefined;
108098
+ return import_react20.default.createElement("box", {
108099
+ id: "mikan-app",
108100
+ style: {
108101
+ backgroundColor: theme.base.canvas,
108102
+ color: theme.base.text,
108103
+ flexDirection: "column",
108104
+ height: "100%"
108105
+ }
108106
+ }, import_react20.default.createElement(Header, { theme }), import_react20.default.createElement("box", {
108107
+ id: "mikan-main",
108108
+ style: { flexDirection: "column", flexGrow: 1, minHeight: 0 }
108109
+ }, details ? import_react20.default.createElement(DetailPage, {
108110
+ model,
108111
+ selection,
108112
+ theme,
108113
+ viewportHeight
108114
+ }) : import_react20.default.createElement(BoardView, {
108115
+ model,
108116
+ selection,
108117
+ theme,
108118
+ viewportHeight,
108119
+ viewportWidth,
108120
+ columns
108121
+ })), selection.moveOpen ? import_react20.default.createElement(MovePrompt, { model, selection, theme }) : undefined, selection.noteOpen ? import_react20.default.createElement(NotePrompt, { model, selection, theme }) : undefined, selection.archiveOpen ? import_react20.default.createElement(ArchivePrompt, { model, selection, theme }) : undefined, selection.warningsOpen ? import_react20.default.createElement(WarningPanel, { model, theme }) : undefined, selection.helpOpen ? import_react20.default.createElement(HelpPanel, { theme }) : undefined, import_react20.default.createElement(Footer, {
108122
+ message: selection.message,
108123
+ mode: footerMode(selection),
108124
+ theme
108125
+ }));
107386
108126
  }
107387
- function renderArchiveInteraction(model, selection) {
107388
- const view = buildArchivePromptViewModel(model, selection);
107389
- if (!view)
107390
- return ["Archive", "No Issue selected"];
107391
- return [view.title, view.body, view.hint];
108127
+ function Header(props) {
108128
+ const theme = props.theme ?? buildTuiTheme();
108129
+ return import_react20.default.createElement("text", {
108130
+ id: "mikan-header",
108131
+ style: { color: theme.interactive.accent },
108132
+ content: `\uD83C\uDF4A mikan v${TUI_VERSION}`
108133
+ });
107392
108134
  }
107393
108135
  async function launchTui(options = {}) {
107394
108136
  const { createCliRenderer: createCliRenderer2 } = await init_core3().then(() => exports_core3);
@@ -107401,17 +108143,17 @@ async function launchTui(options = {}) {
107401
108143
  renderer.destroy();
107402
108144
  };
107403
108145
  function App() {
107404
- const [model, setModel] = import_react17.default.useState(() => loadTuiModel(options.cwd));
107405
- const [selection, setSelection] = import_react17.default.useState({
108146
+ const [model, setModel] = import_react20.default.useState(() => loadTuiModel(options.cwd));
108147
+ const [selection, setSelection] = import_react20.default.useState({
107406
108148
  columnIndex: 0,
107407
108149
  cardIndex: 0,
107408
108150
  detailOpen: false
107409
108151
  });
107410
- const modelRef = import_react17.default.useRef(model);
107411
- const selectionRef = import_react17.default.useRef(selection);
108152
+ const modelRef = import_react20.default.useRef(model);
108153
+ const selectionRef = import_react20.default.useRef(selection);
107412
108154
  modelRef.current = model;
107413
108155
  selectionRef.current = selection;
107414
- import_react17.default.useEffect(() => {
108156
+ import_react20.default.useEffect(() => {
107415
108157
  const interval = setInterval(() => {
107416
108158
  const refreshed = refreshTuiModel({
107417
108159
  cwd: options.cwd,
@@ -107427,7 +108169,17 @@ async function launchTui(options = {}) {
107427
108169
  }, []);
107428
108170
  useKeyboard2((key) => {
107429
108171
  const action = keyToTuiAction(key.name, key.shift);
108172
+ if (selection.helpOpen) {
108173
+ if (action === "escape" || action === "help") {
108174
+ setSelection((current) => moveSelection(model, current, action));
108175
+ }
108176
+ return;
108177
+ }
107430
108178
  if (selection.noteOpen) {
108179
+ if (action === "help") {
108180
+ setSelection((current) => moveSelection(model, current, action));
108181
+ return;
108182
+ }
107431
108183
  if (action === "escape") {
107432
108184
  setSelection((current) => moveSelection(model, current, action));
107433
108185
  return;
@@ -107447,6 +108199,10 @@ async function launchTui(options = {}) {
107447
108199
  return;
107448
108200
  }
107449
108201
  if (selection.archiveOpen) {
108202
+ if (action === "help") {
108203
+ setSelection((current) => moveSelection(model, current, action));
108204
+ return;
108205
+ }
107450
108206
  if (action === "escape") {
107451
108207
  setSelection((current) => moveSelection(model, current, action));
107452
108208
  return;
@@ -107512,138 +108268,165 @@ async function launchTui(options = {}) {
107512
108268
  setSelection((current) => moveSelection(model, current, action));
107513
108269
  return;
107514
108270
  }
107515
- setSelection((current) => moveSelection(model, current, action, {
107516
- viewportHeight: renderer.height
107517
- }));
107518
- });
107519
- return createTuiAppElement({
107520
- model,
107521
- selection,
107522
- viewportHeight: renderer.height
107523
- });
107524
- }
107525
- root.render(import_react17.default.createElement(App));
107526
- process.once("SIGINT", stop);
107527
- }
107528
- function keyToTuiAction(keyName2, shift = false) {
107529
- if (shift && keyName2 === "h")
107530
- return "move-left";
107531
- if (shift && keyName2 === "l")
107532
- return "move-right";
107533
- switch (keyName2) {
107534
- case "left":
107535
- case "right":
107536
- case "up":
107537
- case "down":
107538
- case "enter":
107539
- case "escape":
107540
- return keyName2;
107541
- case "h":
107542
- return "left";
107543
- case "l":
107544
- return "right";
107545
- case "j":
107546
- return "down";
107547
- case "k":
107548
- return "up";
107549
- case "return":
107550
- return "enter";
107551
- case "H":
107552
- return "move-left";
107553
- case "L":
107554
- return "move-right";
107555
- case "r":
107556
- return "reload";
107557
- case "m":
107558
- return "move";
107559
- case "n":
107560
- return "append-note";
107561
- case "a":
107562
- return "archive";
107563
- case "q":
107564
- return "quit";
107565
- default:
107566
- return;
108271
+ setSelection((current) => moveSelection(model, current, action, {
108272
+ viewportHeight: renderer.height
108273
+ }));
108274
+ });
108275
+ return createTuiAppElement({
108276
+ model,
108277
+ selection,
108278
+ viewportHeight: renderer.height,
108279
+ viewportWidth: renderer.width,
108280
+ columns: options.columns
108281
+ });
108282
+ }
108283
+ root.render(import_react20.default.createElement(App));
108284
+ process.once("SIGINT", stop);
108285
+ }
108286
+
108287
+ // src/args.ts
108288
+ var commands = [
108289
+ "init",
108290
+ "add",
108291
+ "list",
108292
+ "show",
108293
+ "update",
108294
+ "move",
108295
+ "append",
108296
+ "mcp",
108297
+ "skills",
108298
+ "tui",
108299
+ "watch"
108300
+ ];
108301
+ var commandOptions = {
108302
+ init: [
108303
+ { name: "key", short: "k", value: true },
108304
+ { name: "name", short: "n", value: true }
108305
+ ],
108306
+ add: [
108307
+ { name: "status", short: "s", value: true },
108308
+ { name: "label", short: "l", value: true },
108309
+ { name: "depends-on", value: true }
108310
+ ],
108311
+ list: [
108312
+ { name: "status", short: "s", value: true },
108313
+ { name: "include-archived", short: "a", value: false }
108314
+ ],
108315
+ show: [],
108316
+ update: [
108317
+ { name: "title", short: "t", value: true },
108318
+ { name: "label", short: "l", value: true },
108319
+ { name: "depends-on", value: true },
108320
+ { name: "body", short: "b", value: true }
108321
+ ],
108322
+ move: [{ name: "log", short: "l", value: true }],
108323
+ append: [
108324
+ { name: "section", short: "S", value: true },
108325
+ { name: "body", short: "b", value: true },
108326
+ { name: "source", short: "s", value: true }
108327
+ ],
108328
+ mcp: [
108329
+ { name: "agent", short: "a", value: true },
108330
+ { name: "no-global", value: false },
108331
+ { name: "full", value: false }
108332
+ ],
108333
+ skills: [
108334
+ { name: "agent", short: "a", value: true },
108335
+ { name: "no-global", value: false }
108336
+ ],
108337
+ tui: [{ name: "columns", short: "c", value: true }],
108338
+ watch: [{ name: "quiet", short: "q", value: false }]
108339
+ };
108340
+ function parseArgs(args, command) {
108341
+ const positionals = [];
108342
+ const flags = new Map;
108343
+ for (let index2 = 0;index2 < args.length; index2++) {
108344
+ const arg = args[index2];
108345
+ if (!arg)
108346
+ continue;
108347
+ if (!arg.startsWith("-")) {
108348
+ positionals.push(arg);
108349
+ continue;
108350
+ }
108351
+ const parsed = parseOptionToken(arg, command);
108352
+ if (!parsed.ok)
108353
+ return parsed;
108354
+ const { spec, inlineValue, displayName } = parsed.value;
108355
+ const values = flags.get(spec.name) ?? [];
108356
+ if (!spec.value) {
108357
+ if (inlineValue !== undefined) {
108358
+ return {
108359
+ ok: false,
108360
+ error: `${displayName} does not take a value`
108361
+ };
108362
+ }
108363
+ values.push("true");
108364
+ flags.set(spec.name, values);
108365
+ continue;
108366
+ }
108367
+ const value = inlineValue ?? args[index2 + 1];
108368
+ if (!value || value.startsWith("-")) {
108369
+ return { ok: false, error: `Missing value for ${displayName}` };
108370
+ }
108371
+ if (inlineValue === undefined)
108372
+ index2++;
108373
+ values.push(value);
108374
+ flags.set(spec.name, values);
107567
108375
  }
108376
+ return { ok: true, value: { positionals, flags } };
107568
108377
  }
107569
- function clampSelection(model, selection) {
107570
- const columnIndex = clamp(selection.columnIndex, 0, Math.max(0, model.columns.length - 1));
107571
- const maxCardIndex = Math.max(0, (model.columns[columnIndex]?.cards.length ?? 1) - 1);
107572
- return {
107573
- ...selection,
107574
- columnIndex,
107575
- cardIndex: clamp(selection.cardIndex, 0, maxCardIndex)
107576
- };
108378
+ function parseOptionToken(token, command) {
108379
+ const equalsIndex = token.indexOf("=");
108380
+ const raw = equalsIndex === -1 ? token : token.slice(0, equalsIndex);
108381
+ const inlineValue = equalsIndex === -1 ? undefined : token.slice(equalsIndex + 1);
108382
+ const spec = optionSpecFor(command, raw);
108383
+ if (!spec)
108384
+ return { ok: false, error: `Unknown option: ${raw}` };
108385
+ return { ok: true, value: { spec, inlineValue, displayName: raw } };
107577
108386
  }
107578
- function findSelectionByCardId(model, cardId) {
107579
- for (const [columnIndex, column] of model.columns.entries()) {
107580
- const cardIndex = column.cards.findIndex((card) => card.id === cardId);
107581
- if (cardIndex !== -1) {
107582
- return { columnIndex, cardIndex, detailOpen: false };
107583
- }
108387
+ function optionSpecFor(command, raw) {
108388
+ if (isHelpFlag(raw))
108389
+ return { name: "help", short: "h", value: false };
108390
+ if (raw.startsWith("--")) {
108391
+ const name = raw.slice(2);
108392
+ return commandOptions[command].find((option) => option.name === name);
108393
+ }
108394
+ if (raw.startsWith("-") && raw.length === 2) {
108395
+ const short = raw.slice(1);
108396
+ return commandOptions[command].find((option) => option.short === short);
107584
108397
  }
107585
108398
  return;
107586
108399
  }
107587
- function formatCard(issue2) {
107588
- return {
107589
- id: String(issue2.issue.id),
107590
- title: issue2.issue.title,
107591
- labels: issue2.issue.labels.map(String),
107592
- status: String(issue2.status),
107593
- path: issue2.path
107594
- };
107595
- }
107596
- function detailScrollMax(model, selection, options = {}) {
107597
- const details = getSelectedDetails(model, selection);
107598
- if (!details)
107599
- return 0;
107600
- const visibleLineCount = options.viewportHeight ? visibleDetailLineCount(options.viewportHeight) : 40;
107601
- return Math.max(0, stripFrontmatter(details.markdown).trimEnd().split(`
107602
- `).length - visibleLineCount);
108400
+ function isHelpFlag(input) {
108401
+ return input === "-h" || input === "--help";
107603
108402
  }
107604
- function stripFrontmatter(markdown) {
107605
- if (!markdown.startsWith(`---
107606
- `))
107607
- return markdown;
107608
- const end = markdown.indexOf(`
107609
- ---
107610
- `, 4);
107611
- if (end === -1)
107612
- return markdown;
107613
- return markdown.slice(end + `
107614
- ---
107615
- `.length).trimStart();
108403
+ function isCommandName(input) {
108404
+ return commands.includes(input);
107616
108405
  }
107617
- function extractSection(markdown, section) {
107618
- const lines = markdown.split(`
107619
- `);
107620
- const start = lines.findIndex((line) => line.trim().toLowerCase() === `## ${section}`.toLowerCase());
107621
- if (start === -1)
107622
- return "";
107623
- let end = lines.length;
107624
- for (let index2 = start + 1;index2 < lines.length; index2++) {
107625
- if (/^##\s+/.test(lines[index2] ?? "")) {
107626
- end = index2;
107627
- break;
107628
- }
107629
- }
107630
- return lines.slice(start + 1, end).join(`
107631
- `).trim();
108406
+
108407
+ // src/cli-output.ts
108408
+ function ok2(stdout) {
108409
+ return { exitCode: 0, stdout, stderr: "" };
107632
108410
  }
107633
- function clamp(value, min, max) {
107634
- return Math.min(max, Math.max(min, value));
108411
+ function fail(stderr) {
108412
+ return { exitCode: 1, stdout: "", stderr: `${stderr}
108413
+ ` };
107635
108414
  }
107636
108415
 
108416
+ // src/commands.ts
108417
+ import { readFileSync as readFileSync10 } from "fs";
108418
+ import { basename as basename5, join as join11 } from "path";
108419
+
107637
108420
  // src/watch.ts
107638
108421
  import {
107639
108422
  appendFileSync,
107640
- existsSync as existsSync7,
107641
- mkdirSync as mkdirSync6,
107642
- readFileSync as readFileSync8,
107643
- renameSync as renameSync3,
108423
+ existsSync as existsSync9,
108424
+ mkdirSync as mkdirSync7,
108425
+ readFileSync as readFileSync9,
108426
+ renameSync as renameSync4,
107644
108427
  writeFileSync as writeFileSync6
107645
108428
  } from "fs";
107646
- import { dirname as dirname7, join as join8 } from "path";
108429
+ import { dirname as dirname8, join as join10 } from "path";
107647
108430
  function runWatchOnce(options = {}) {
107648
108431
  const loaded = loadProjectConfig(options.cwd ?? process.cwd());
107649
108432
  if (!loaded.ok)
@@ -107717,11 +108500,11 @@ function appendPlaceholderStatusLog(loaded, issue2, fromStatus, now) {
107717
108500
  }
107718
108501
  function fireHooks(loaded, issue2, fromStatus, toStatus, options) {
107719
108502
  const hooks = loaded.config.hooks;
107720
- const commands = [
108503
+ const commands2 = [
107721
108504
  ...hooks?.on_enter?.[toStatus] ?? [],
107722
108505
  ...hooks?.on_transition?.[`${fromStatus}->${toStatus}`] ?? []
107723
108506
  ];
107724
- for (const command of commands) {
108507
+ for (const command of commands2) {
107725
108508
  const rendered = renderHookCommand(command, {
107726
108509
  project_root: loaded.projectRoot,
107727
108510
  issue_path: issue2.path,
@@ -107781,122 +108564,34 @@ function renderHookCommand(command, values) {
107781
108564
  return command.replace(/{{\s*([a-z_]+)\s*}}/g, (match, key) => values[key] ?? match);
107782
108565
  }
107783
108566
  function appendHookFailure(projectRoot, entry) {
107784
- const path5 = join8(projectRoot, ".mikan", ".state", "hook-log.ndjson");
107785
- mkdirSync6(dirname7(path5), { recursive: true });
108567
+ const path5 = join10(projectRoot, ".mikan", ".state", "hook-log.ndjson");
108568
+ mkdirSync7(dirname8(path5), { recursive: true });
107786
108569
  appendFileSync(path5, `${JSON.stringify(entry)}
107787
108570
  `);
107788
108571
  }
107789
108572
  function readSnapshot(path5) {
107790
- if (!existsSync7(path5))
108573
+ if (!existsSync9(path5))
107791
108574
  return;
107792
108575
  try {
107793
- return JSON.parse(readFileSync8(path5, "utf8"));
108576
+ return JSON.parse(readFileSync9(path5, "utf8"));
107794
108577
  } catch {
107795
108578
  return;
107796
108579
  }
107797
108580
  }
107798
108581
  function writeSnapshot(path5, snapshot) {
107799
- mkdirSync6(dirname7(path5), { recursive: true });
108582
+ mkdirSync7(dirname8(path5), { recursive: true });
107800
108583
  const tmp = `${path5}.${process.pid}.${Date.now()}.tmp`;
107801
108584
  writeFileSync6(tmp, JSON.stringify(snapshot, null, 2));
107802
- renameSync3(tmp, path5);
108585
+ renameSync4(tmp, path5);
107803
108586
  }
107804
108587
  function watcherSnapshotPath(projectRoot) {
107805
- return join8(projectRoot, ".mikan", ".state", "watcher-snapshot.json");
108588
+ return join10(projectRoot, ".mikan", ".state", "watcher-snapshot.json");
107806
108589
  }
107807
108590
  function utcNow2(now) {
107808
108591
  return (now?.() ?? new Date).toISOString().replace(".000Z", "Z");
107809
108592
  }
107810
108593
 
107811
- // src/index.ts
107812
- async function runCli(argv = process.argv.slice(2), options = {}) {
107813
- const cwd = options.cwd ?? process.cwd();
107814
- const command = argv[0];
107815
- if (!command || isHelpFlag(command))
107816
- return ok2(helpText());
107817
- if (command === "help") {
107818
- const topic = argv[1];
107819
- return topic ? commandHelp(topic) : ok2(helpText());
107820
- }
107821
- if (!isCommandName(command)) {
107822
- return fail(`Unknown command: ${command}
107823
-
107824
- Run \`mikan help\` to see available commands.`);
107825
- }
107826
- if (argv.slice(1).some(isHelpFlag))
107827
- return commandHelp(command);
107828
- const parsed = parseArgs(argv.slice(1), command);
107829
- if (!parsed.ok) {
107830
- return fail(`${parsed.error}
107831
-
107832
- Run \`mikan help ${command}\` for usage.`);
107833
- }
107834
- try {
107835
- switch (command) {
107836
- case "init":
107837
- return runInit(cwd, parsed.value);
107838
- case "add":
107839
- return runAdd(cwd, parsed.value, options);
107840
- case "list":
107841
- return runList(cwd, parsed.value);
107842
- case "show":
107843
- return runShow(cwd, parsed.value);
107844
- case "update":
107845
- return runUpdate(cwd, parsed.value, options);
107846
- case "move":
107847
- return runMove(cwd, parsed.value, options);
107848
- case "append":
107849
- return runAppend(cwd, parsed.value, options);
107850
- case "mcp":
107851
- return runMcp(cwd, parsed.value, options);
107852
- case "tui":
107853
- return ok2(`Starting mikan OpenTUI board
107854
- `);
107855
- case "watch":
107856
- return runWatch(cwd, parsed.value);
107857
- default:
107858
- return ok2(helpText());
107859
- }
107860
- } catch (error51) {
107861
- return fail(error51 instanceof Error ? error51.message : String(error51));
107862
- }
107863
- }
107864
- async function main(argv = process.argv.slice(2)) {
107865
- const result = await runInteractiveCommand(argv, { cwd: process.cwd() });
107866
- if (result.stdout)
107867
- process.stdout.write(result.stdout);
107868
- if (result.stderr)
107869
- process.stderr.write(result.stderr);
107870
- process.exitCode = result.exitCode;
107871
- }
107872
- async function runInteractiveCommand(argv = process.argv.slice(2), options = {}) {
107873
- const cwd = options.cwd ?? process.cwd();
107874
- if (!argv.some(isHelpFlag)) {
107875
- if (argv[0] === "mcp" && argv[1] !== "add") {
107876
- await (options.launchMcp ?? (() => startMcpServer({ cwd })))();
107877
- return ok2("");
107878
- }
107879
- if (argv[0] === "tui") {
107880
- const loaded = loadProjectConfig(cwd);
107881
- if (!loaded.ok)
107882
- return fail(loaded.error.message);
107883
- await (options.launchTui ?? (() => launchTui({ cwd })))();
107884
- return ok2("");
107885
- }
107886
- if (argv[0] === "watch") {
107887
- const parsed = parseArgs(argv.slice(1), "watch");
107888
- if (!parsed.ok) {
107889
- return fail(`${parsed.error}
107890
-
107891
- Run \`mikan help watch\` for usage.`);
107892
- }
107893
- const quiet = parsed.value.flags.has("quiet");
107894
- (options.launchWatch ?? (() => watchProject({ cwd, quiet })))();
107895
- return ok2("");
107896
- }
107897
- }
107898
- return runCli(argv, { cwd, home: options.home });
107899
- }
108594
+ // src/commands.ts
107900
108595
  function runMcp(cwd, parsed, options) {
107901
108596
  if (parsed.positionals[0] !== "add") {
107902
108597
  return ok2(`Starting mikan MCP server on stdio
@@ -107920,6 +108615,41 @@ ${hint}`);
107920
108615
  return fail(error51 instanceof Error ? error51.message : String(error51));
107921
108616
  }
107922
108617
  }
108618
+ async function runMcpLlms(cwd, args) {
108619
+ const parsed = parseArgs(args, "mcp");
108620
+ if (!parsed.ok) {
108621
+ return fail(`${parsed.error}
108622
+
108623
+ Run \`mikan help mcp\` for usage.`);
108624
+ }
108625
+ if (parsed.value.flags.has("agent")) {
108626
+ return fail(`incur-backed discovery only prints a manifest; it cannot install for a specific agent.
108627
+ ` + "Use `mikan mcp add --agent <agent>` for native MCP registration.");
108628
+ }
108629
+ const manifest = await getMcpManifest({ cwd }, { full: parsed.value.flags.has("full") });
108630
+ return ok2(manifest.endsWith(`
108631
+ `) ? manifest : `${manifest}
108632
+ `);
108633
+ }
108634
+ function runSkills(cwd, parsed, options) {
108635
+ if (parsed.positionals[0] !== "add") {
108636
+ return fail("Usage: mikan skills add --agent <agent>");
108637
+ }
108638
+ const agent = parsed.flags.get("agent")?.at(-1);
108639
+ if (!agent)
108640
+ return fail("Usage: mikan skills add --agent <agent>");
108641
+ try {
108642
+ const result = installSkillForAgent(agent, {
108643
+ cwd,
108644
+ home: options.home,
108645
+ global: !parsed.flags.has("no-global")
108646
+ });
108647
+ return ok2(`Installed mikan skill for ${result.agent} (${result.scope}): ${result.path}
108648
+ `);
108649
+ } catch (error51) {
108650
+ return fail(error51 instanceof Error ? error51.message : String(error51));
108651
+ }
108652
+ }
107923
108653
  function runWatch(cwd, parsed) {
107924
108654
  const lines = [];
107925
108655
  runWatchOnce({
@@ -107933,17 +108663,17 @@ function runWatch(cwd, parsed) {
107933
108663
  }
107934
108664
  function runInit(cwd, parsed) {
107935
108665
  const key = parsed.flags.get("key")?.at(-1) ?? "MIK";
107936
- const name = parsed.flags.get("name")?.at(-1) ?? basename4(cwd);
108666
+ const name = parsed.flags.get("name")?.at(-1) ?? basename5(cwd);
107937
108667
  const initialized = initProject(cwd, { key, name });
107938
108668
  if (!initialized.ok)
107939
108669
  return fail(initialized.error.message);
107940
- return ok2(`Initialized mikan project at ${join9(cwd, ".mikan")}
108670
+ return ok2(`Initialized mikan project at ${join11(cwd, ".mikan")}
107941
108671
  `);
107942
108672
  }
107943
108673
  function runAdd(cwd, parsed, options) {
107944
108674
  const title = parsed.positionals[0];
107945
108675
  if (!title)
107946
- return fail("Usage: mikan add <title> [--status status] [--label label]");
108676
+ return fail("Usage: mikan add <title> [--status status] [--label label] [--depends-on issue-id]");
107947
108677
  const loaded = loadProjectConfig(cwd);
107948
108678
  if (!loaded.ok)
107949
108679
  return fail(loaded.error.message);
@@ -107953,6 +108683,7 @@ function runAdd(cwd, parsed, options) {
107953
108683
  title,
107954
108684
  status: parsed.flags.get("status")?.at(-1),
107955
108685
  labels: parsed.flags.get("label") ?? [],
108686
+ dependencies: parsed.flags.get("depends-on") ?? [],
107956
108687
  now: options.now
107957
108688
  });
107958
108689
  if (!result.ok)
@@ -107997,12 +108728,16 @@ function runShow(cwd, parsed) {
107997
108728
  });
107998
108729
  if (!found.ok)
107999
108730
  return fail(found.error.message);
108000
- return ok2(readFileSync9(found.value.path, "utf8"));
108731
+ return {
108732
+ exitCode: 0,
108733
+ stdout: readFileSync10(found.value.path, "utf8"),
108734
+ stderr: formatDependencyShowInfo(found.value)
108735
+ };
108001
108736
  }
108002
108737
  function runUpdate(cwd, parsed, options) {
108003
108738
  const id = parsed.positionals[0];
108004
108739
  if (!id)
108005
- return fail("Usage: mikan update <issue-id> [--title title] [--label label] [--body body]");
108740
+ return fail("Usage: mikan update <issue-id> [--title title] [--label label] [--depends-on issue-id] [--body body]");
108006
108741
  const loaded = loadProjectConfig(cwd);
108007
108742
  if (!loaded.ok)
108008
108743
  return fail(loaded.error.message);
@@ -108012,6 +108747,7 @@ function runUpdate(cwd, parsed, options) {
108012
108747
  id,
108013
108748
  title: parsed.flags.get("title")?.at(-1),
108014
108749
  labels: parsed.flags.has("label") ? parsed.flags.get("label") ?? [] : undefined,
108750
+ dependencies: parsed.flags.has("depends-on") ? parsed.flags.get("depends-on") ?? [] : undefined,
108015
108751
  body: parsed.flags.get("body")?.at(-1),
108016
108752
  now: options.now
108017
108753
  });
@@ -108064,142 +108800,52 @@ function runAppend(cwd, parsed, options) {
108064
108800
  return ok2(`${String(result.value.issue.id)} appended ${section}
108065
108801
  `);
108066
108802
  }
108067
- var commands = [
108068
- "init",
108069
- "add",
108070
- "list",
108071
- "show",
108072
- "update",
108073
- "move",
108074
- "append",
108075
- "mcp",
108076
- "tui",
108077
- "watch"
108078
- ];
108079
- var commandOptions = {
108080
- init: [
108081
- { name: "key", short: "k", value: true },
108082
- { name: "name", short: "n", value: true }
108083
- ],
108084
- add: [
108085
- { name: "status", short: "s", value: true },
108086
- { name: "label", short: "l", value: true }
108087
- ],
108088
- list: [
108089
- { name: "status", short: "s", value: true },
108090
- { name: "include-archived", short: "a", value: false }
108091
- ],
108092
- show: [],
108093
- update: [
108094
- { name: "title", short: "t", value: true },
108095
- { name: "label", short: "l", value: true },
108096
- { name: "body", short: "b", value: true }
108097
- ],
108098
- move: [{ name: "log", short: "l", value: true }],
108099
- append: [
108100
- { name: "section", short: "S", value: true },
108101
- { name: "body", short: "b", value: true },
108102
- { name: "source", short: "s", value: true }
108103
- ],
108104
- mcp: [
108105
- { name: "agent", short: "a", value: true },
108106
- { name: "no-global", value: false }
108107
- ],
108108
- tui: [],
108109
- watch: [{ name: "quiet", short: "q", value: false }]
108110
- };
108111
- function parseArgs(args, command) {
108112
- const positionals = [];
108113
- const flags = new Map;
108114
- for (let index2 = 0;index2 < args.length; index2++) {
108115
- const arg = args[index2];
108116
- if (!arg)
108117
- continue;
108118
- if (!arg.startsWith("-")) {
108119
- positionals.push(arg);
108120
- continue;
108121
- }
108122
- const parsed = parseOptionToken(arg, command);
108123
- if (!parsed.ok)
108124
- return parsed;
108125
- const { spec, inlineValue, displayName } = parsed.value;
108126
- const values = flags.get(spec.name) ?? [];
108127
- if (!spec.value) {
108128
- if (inlineValue !== undefined) {
108129
- return {
108130
- ok: false,
108131
- error: `${displayName} does not take a value`
108132
- };
108133
- }
108134
- values.push("true");
108135
- flags.set(spec.name, values);
108136
- continue;
108137
- }
108138
- const value = inlineValue ?? args[index2 + 1];
108139
- if (!value || value.startsWith("-")) {
108140
- return { ok: false, error: `Missing value for ${displayName}` };
108141
- }
108142
- if (inlineValue === undefined)
108143
- index2++;
108144
- values.push(value);
108145
- flags.set(spec.name, values);
108146
- }
108147
- return { ok: true, value: { positionals, flags } };
108148
- }
108149
- function parseOptionToken(token, command) {
108150
- const equalsIndex = token.indexOf("=");
108151
- const raw = equalsIndex === -1 ? token : token.slice(0, equalsIndex);
108152
- const inlineValue = equalsIndex === -1 ? undefined : token.slice(equalsIndex + 1);
108153
- const spec = optionSpecFor(command, raw);
108154
- if (!spec)
108155
- return { ok: false, error: `Unknown option: ${raw}` };
108156
- return { ok: true, value: { spec, inlineValue, displayName: raw } };
108157
- }
108158
- function optionSpecFor(command, raw) {
108159
- if (isHelpFlag(raw))
108160
- return { name: "help", short: "h", value: false };
108161
- if (raw.startsWith("--")) {
108162
- const name = raw.slice(2);
108163
- return commandOptions[command].find((option) => option.name === name);
108164
- }
108165
- if (raw.startsWith("-") && raw.length === 2) {
108166
- const short = raw.slice(1);
108167
- return commandOptions[command].find((option) => option.short === short);
108168
- }
108169
- return;
108170
- }
108171
- function isHelpFlag(input) {
108172
- return input === "-h" || input === "--help";
108173
- }
108174
- function isCommandName(input) {
108175
- return commands.includes(input);
108176
- }
108177
108803
  function formatBoard(columns) {
108178
108804
  const lines = [];
108179
108805
  for (const column of columns) {
108180
108806
  lines.push(`${column.title}`);
108181
108807
  for (const item of column.issues) {
108182
108808
  const labels = item.issue.labels.length > 0 ? ` [${item.issue.labels.map(String).join(", ")}]` : "";
108183
- lines.push(` ${String(item.issue.id)} ${item.issue.title}${labels}`);
108809
+ lines.push(` ${String(item.issue.id)} ${item.issue.title}${labels}${formatDependencyListSuffix(item)}`);
108184
108810
  }
108185
108811
  }
108186
108812
  return `${lines.join(`
108187
108813
  `)}
108188
108814
  `;
108189
108815
  }
108816
+ function formatDependencyListSuffix(item) {
108817
+ const dependsOn = item.issue.dependencies.map(String);
108818
+ const unmet = item.unmetDependencies.map(String);
108819
+ if (dependsOn.length === 0 && unmet.length === 0)
108820
+ return "";
108821
+ const parts = [
108822
+ `depends_on=${dependsOn.length > 0 ? dependsOn.join(",") : "-"}`,
108823
+ `unmet_dependencies=${unmet.length > 0 ? unmet.join(",") : "-"}`,
108824
+ `dependency_status=${item.dependencyStatus}`
108825
+ ];
108826
+ return ` ${parts.join(" ")}`;
108827
+ }
108828
+ function formatDependencyShowInfo(item) {
108829
+ const dependsOn = item.issue.dependencies.map(String);
108830
+ const unmet = item.unmetDependencies.map(String);
108831
+ if (dependsOn.length === 0 && unmet.length === 0)
108832
+ return "";
108833
+ return [
108834
+ `Dependency Status: ${item.dependencyStatus}`,
108835
+ `Depends On: ${dependsOn.length > 0 ? dependsOn.join(", ") : "-"}`,
108836
+ `Unmet Dependencies: ${unmet.length > 0 ? unmet.join(", ") : "-"}`,
108837
+ ""
108838
+ ].join(`
108839
+ `);
108840
+ }
108190
108841
  function formatWarnings(warnings) {
108191
108842
  if (warnings.length === 0)
108192
108843
  return "";
108193
108844
  return warnings.map((warning) => `warning ${warning.kind}: ${warning.message}
108194
108845
  `).join("");
108195
108846
  }
108196
- function ok2(stdout) {
108197
- return { exitCode: 0, stdout, stderr: "" };
108198
- }
108199
- function fail(stderr) {
108200
- return { exitCode: 1, stdout: "", stderr: `${stderr}
108201
- ` };
108202
- }
108847
+
108848
+ // src/help.ts
108203
108849
  function commandHelp(topic) {
108204
108850
  if (!isCommandName(topic)) {
108205
108851
  return fail(`Unknown help topic: ${topic}
@@ -108225,6 +108871,7 @@ Commands:
108225
108871
  tui Open the read-only board
108226
108872
  watch Run the polling watcher
108227
108873
  mcp Start the stdio MCP server
108874
+ skills Install agent-facing mikan usage guidance
108228
108875
 
108229
108876
  Run \`mikan help <command>\` for command-specific options.
108230
108877
  `;
@@ -108251,6 +108898,7 @@ Usage:
108251
108898
  Options:
108252
108899
  -s, --status <status> Add to Status (default: backlog)
108253
108900
  -l, --label <label> Add label; repeat for multiple labels
108901
+ --depends-on <issue-id> Add dependency; repeat for multiple dependencies
108254
108902
  -h, --help Show this help
108255
108903
 
108256
108904
  Examples:
@@ -108285,6 +108933,7 @@ Usage:
108285
108933
  Options:
108286
108934
  -t, --title <title> Replace title
108287
108935
  -l, --label <label> Replace labels; repeat for multiple labels
108936
+ --depends-on <issue-id> Replace dependencies; repeat for multiple dependencies
108288
108937
  -b, --body <body> Replace body Markdown
108289
108938
  -h, --help Show this help
108290
108939
  `;
@@ -108314,10 +108963,22 @@ Options:
108314
108963
  return `Open the read-only board.
108315
108964
 
108316
108965
  Usage:
108317
- mikan tui
108966
+ mikan tui [options]
108318
108967
 
108319
108968
  Options:
108969
+ -c, --columns <auto|2|3|4|5> Preferred visible Column count (default: auto)
108320
108970
  -h, --help Show this help
108971
+
108972
+ Examples:
108973
+ mikan tui
108974
+ mikan tui --columns auto
108975
+ mikan tui --columns 3
108976
+ mikan tui -c 5
108977
+
108978
+ Notes:
108979
+ --columns sets how many Status Columns the board shows at once. auto
108980
+ derives 2..5 Columns from terminal width and keeps the sliding viewport;
108981
+ a fixed 2..5 pins that count. It never changes configured Statuses.
108321
108982
  `;
108322
108983
  case "watch":
108323
108984
  return `Run the polling watcher.
@@ -108330,24 +108991,194 @@ Options:
108330
108991
  -h, --help Show this help
108331
108992
  `;
108332
108993
  case "mcp":
108333
- return `Start the stdio MCP server or register it with an agent.
108994
+ return `Start the stdio MCP server, register it with an agent, or print its manifest.
108334
108995
 
108335
108996
  Usage:
108336
108997
  mikan mcp
108337
108998
  mikan mcp add --agent <agent> [--no-global]
108999
+ mikan mcp llms [--full]
108338
109000
 
108339
109001
  Options:
108340
- -a, --agent <agent> Agent to configure: pi, antigravity, jcode
109002
+ -a, --agent <agent> Agent to configure: pi, antigravity, jcode, claude-code, opencode, codex
108341
109003
  --no-global Write workspace-local config instead of global config
109004
+ --full With llms: print the full incur manifest
108342
109005
  -h, --help Show this help
108343
109006
 
109007
+ Notes:
109008
+ codex registers in global ~/.codex/config.toml only; --no-global is rejected.
109009
+ Use mikan mcp add for native per-agent registration. Use mikan mcp llms for
109010
+ incur-backed discovery: it prints a manifest for agents that read incur
109011
+ manifests directly and never installs anything. Passing --agent to llms is
109012
+ rejected; install with mikan mcp add --agent <agent> instead.
109013
+
108344
109014
  Examples:
108345
109015
  mikan mcp add --agent pi
108346
109016
  mikan mcp add --agent antigravity --no-global
108347
109017
  mikan mcp add -a jcode
109018
+ mikan mcp add --agent claude-code
109019
+ mikan mcp add --agent opencode --no-global
109020
+ mikan mcp add --agent codex
109021
+ mikan mcp llms
109022
+ mikan mcp llms --full
109023
+ `;
109024
+ case "skills":
109025
+ return `Install agent-facing mikan usage guidance.
109026
+
109027
+ mikan skills add installs lightweight instructions that teach an agent how to
109028
+ use mikan. It is separate from mikan mcp add, which registers the MCP tools;
109029
+ installing skills never changes MCP config.
109030
+
109031
+ Usage:
109032
+ mikan skills add --agent <agent> [--no-global]
109033
+
109034
+ Options:
109035
+ -a, --agent <agent> Agent to install the mikan skill for: claude-code, opencode, codex
109036
+ --no-global Install workspace-local guidance instead of global
109037
+ -h, --help Show this help
109038
+
109039
+ Notes:
109040
+ codex installs the skill globally only; --no-global is rejected for codex.
109041
+
109042
+ Examples:
109043
+ mikan skills add --agent claude-code
109044
+ mikan skills add --agent opencode --no-global
109045
+ mikan skills add -a codex
108348
109046
  `;
108349
109047
  }
108350
109048
  }
108351
109049
 
109050
+ // src/tui-options.ts
109051
+ var DEFAULT_TUI_COLUMNS = "auto";
109052
+ var NUMERIC_TUI_COLUMNS = /^[2-5]$/;
109053
+ function parseTuiColumnsOption(raw) {
109054
+ if (raw === undefined)
109055
+ return { ok: true, value: DEFAULT_TUI_COLUMNS };
109056
+ if (raw === "auto")
109057
+ return { ok: true, value: "auto" };
109058
+ if (NUMERIC_TUI_COLUMNS.test(raw)) {
109059
+ return { ok: true, value: Number(raw) };
109060
+ }
109061
+ return {
109062
+ ok: false,
109063
+ error: `Invalid --columns value: ${raw}. Expected auto, 2, 3, 4, or 5.`
109064
+ };
109065
+ }
109066
+
109067
+ // src/index.ts
109068
+ async function runCli(argv = process.argv.slice(2), options = {}) {
109069
+ const cwd = options.cwd ?? process.cwd();
109070
+ const command = argv[0];
109071
+ if (!command || isHelpFlag(command))
109072
+ return ok2(helpText());
109073
+ if (command === "help") {
109074
+ const topic = argv[1];
109075
+ return topic ? commandHelp(topic) : ok2(helpText());
109076
+ }
109077
+ if (!isCommandName(command)) {
109078
+ return fail(`Unknown command: ${command}
109079
+
109080
+ Run \`mikan help\` to see available commands.`);
109081
+ }
109082
+ if (argv.slice(1).some(isHelpFlag))
109083
+ return commandHelp(command);
109084
+ const parsed = parseArgs(argv.slice(1), command);
109085
+ if (!parsed.ok) {
109086
+ return fail(`${parsed.error}
109087
+
109088
+ Run \`mikan help ${command}\` for usage.`);
109089
+ }
109090
+ try {
109091
+ switch (command) {
109092
+ case "init":
109093
+ return runInit(cwd, parsed.value);
109094
+ case "add":
109095
+ return runAdd(cwd, parsed.value, options);
109096
+ case "list":
109097
+ return runList(cwd, parsed.value);
109098
+ case "show":
109099
+ return runShow(cwd, parsed.value);
109100
+ case "update":
109101
+ return runUpdate(cwd, parsed.value, options);
109102
+ case "move":
109103
+ return runMove(cwd, parsed.value, options);
109104
+ case "append":
109105
+ return runAppend(cwd, parsed.value, options);
109106
+ case "mcp":
109107
+ return runMcp(cwd, parsed.value, options);
109108
+ case "skills":
109109
+ return runSkills(cwd, parsed.value, options);
109110
+ case "tui": {
109111
+ const columns = parseTuiColumnsOption(parsed.value.flags.get("columns")?.at(-1));
109112
+ if (!columns.ok) {
109113
+ return fail(`${columns.error}
109114
+
109115
+ Run \`mikan help tui\` for usage.`);
109116
+ }
109117
+ return ok2(`Starting mikan OpenTUI board
109118
+ `);
109119
+ }
109120
+ case "watch":
109121
+ return runWatch(cwd, parsed.value);
109122
+ default:
109123
+ return ok2(helpText());
109124
+ }
109125
+ } catch (error51) {
109126
+ return fail(error51 instanceof Error ? error51.message : String(error51));
109127
+ }
109128
+ }
109129
+ async function main(argv = process.argv.slice(2)) {
109130
+ const result = await runInteractiveCommand(argv, { cwd: process.cwd() });
109131
+ if (result.stdout)
109132
+ process.stdout.write(result.stdout);
109133
+ if (result.stderr)
109134
+ process.stderr.write(result.stderr);
109135
+ process.exitCode = result.exitCode;
109136
+ }
109137
+ async function runInteractiveCommand(argv = process.argv.slice(2), options = {}) {
109138
+ const cwd = options.cwd ?? process.cwd();
109139
+ if (!argv.some(isHelpFlag)) {
109140
+ if (argv[0] === "mcp" && argv[1] === "llms") {
109141
+ return runMcpLlms(cwd, argv.slice(2));
109142
+ }
109143
+ if (argv[0] === "mcp" && argv[1] !== "add") {
109144
+ await (options.launchMcp ?? (() => startMcpServer({ cwd })))();
109145
+ return ok2("");
109146
+ }
109147
+ if (argv[0] === "tui") {
109148
+ const parsed = parseArgs(argv.slice(1), "tui");
109149
+ if (!parsed.ok) {
109150
+ return fail(`${parsed.error}
109151
+
109152
+ Run \`mikan help tui\` for usage.`);
109153
+ }
109154
+ const columns = parseTuiColumnsOption(parsed.value.flags.get("columns")?.at(-1));
109155
+ if (!columns.ok) {
109156
+ return fail(`${columns.error}
109157
+
109158
+ Run \`mikan help tui\` for usage.`);
109159
+ }
109160
+ const loaded = loadProjectConfig(cwd);
109161
+ if (!loaded.ok)
109162
+ return fail(loaded.error.message);
109163
+ await (options.launchTui ?? ((launchOptions) => launchTui({ cwd, columns: launchOptions.columns })))({
109164
+ columns: columns.value
109165
+ });
109166
+ return ok2("");
109167
+ }
109168
+ if (argv[0] === "watch") {
109169
+ const parsed = parseArgs(argv.slice(1), "watch");
109170
+ if (!parsed.ok) {
109171
+ return fail(`${parsed.error}
109172
+
109173
+ Run \`mikan help watch\` for usage.`);
109174
+ }
109175
+ const quiet = parsed.value.flags.has("quiet");
109176
+ (options.launchWatch ?? (() => watchProject({ cwd, quiet })))();
109177
+ return ok2("");
109178
+ }
109179
+ }
109180
+ return runCli(argv, { cwd, home: options.home });
109181
+ }
109182
+
108352
109183
  // src/bin.ts
108353
109184
  await main();