md-task-viewer 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +147 -13
- package/dist/cli.js.map +1 -1
- package/dist/client/assets/index-BcWwd56a.js +59 -0
- package/dist/client/assets/{index-BZUug9Ej.css → index-LYvJrXuE.css} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server.js +145 -11
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- package/dist/client/assets/index-CkOXAlkL.js +0 -59
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{color-scheme:dark;--bg: #0a0e14;--bg-strong: #0d1117;--panel: #131920;--panel-border: #21262d;--text: #e6edf3;--muted: #7d8590;--accent: #3fb950;--accent-strong: #2ea043;--danger: #f85149;--shadow: 0 2px 8px rgba(0, 0, 0, .4);font-family:JetBrains Mono,IBM Plex Mono,Fira Code,SF Mono,Cascadia Code,monospace;line-height:1.6;color:var(--text);background:var(--bg)}*{box-sizing:border-box}body{margin:0;height:100vh;overflow:hidden}#root{height:100vh;overflow:hidden}button,input,select,textarea{font:inherit}button{cursor:pointer}.app-shell{display:flex;flex-direction:column;height:100vh;padding:12px 16px 16px;gap:12px;overflow:hidden}.app-header{display:flex;justify-content:space-between;align-items:center;gap:16px;flex-shrink:0;padding-bottom:12px;border-bottom:1px solid var(--panel-border)}.app-header-left{display:flex;align-items:baseline;gap:12px;min-width:0}.app-header h1{margin:0;font-family:inherit;font-size:1.1rem;font-weight:700;line-height:1.2;letter-spacing:-.02em;white-space:nowrap;color:var(--accent)}.app-header h1:before{content:"> ";color:var(--muted)}.eyebrow{margin:0;color:var(--muted);text-transform:uppercase;letter-spacing:.12em;font-size:.65rem;font-weight:600;white-space:nowrap}.layout-grid{display:grid;grid-template-columns:minmax(260px,320px) minmax(0,1fr);gap:1px;flex:1;min-height:0;background:var(--panel-border)}.panel{display:flex;flex-direction:column;background:var(--panel);border:none;border-radius:0;padding:14px 16px;min-height:0;overflow:hidden}.panel-header{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-bottom:12px;padding-bottom:8px;border-bottom:1px solid var(--panel-border);flex-shrink:0}.panel-header h2{margin:0;font-family:inherit;font-size:.85rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--muted)}.panel-header span{font-size:.75rem;color:var(--muted)}.panel-header-right{display:flex;align-items:center;gap:12px}.filter-toggle{display:flex;align-items:center;gap:4px;cursor:pointer;-webkit-user-select:none;user-select:none}.filter-toggle input[type=checkbox]{accent-color:var(--accent);cursor:pointer}.sidebar-scroll{flex:1;overflow-y:auto;min-height:0;margin:0 -4px;padding:0 4px}.sidebar-scroll::-webkit-scrollbar{width:4px}.sidebar-scroll::-webkit-scrollbar-track{background:transparent}.sidebar-scroll::-webkit-scrollbar-thumb{background:var(--panel-border);border-radius:0}.task-list{display:grid;gap:2px}.empty-list{margin:0;padding:14px;border-radius:0;background:#ffffff08;border:1px dashed var(--panel-border);color:var(--muted);font-size:.82rem}.task-row{display:grid;gap:3px;width:100%;text-align:left;padding:8px 10px;border-radius:0;border:1px solid transparent;border-left:2px solid transparent;background:#ffffff05;transition:background .1s ease,border-color .1s ease}.task-row:hover{background:#ffffff0d;border-left-color:var(--muted)}.task-row-selected{background:#3fb9500f;border-color:#3fb95026;border-left-color:var(--accent)}.task-row-badges{display:flex;gap:6px;align-items:center}.task-row strong{font-size:.82rem;font-weight:600;line-height:1.3}.task-row small{color:var(--muted);font-size:.72rem}.badge{display:inline-flex;justify-content:center;width:fit-content;padding:1px 6px;border-radius:0;font-size:.6rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;border:1px solid}.badge-must{background:#f851491a;color:#f85149;border-color:#f851494d}.badge-want{background:#3fb9501a;color:#3fb950;border-color:#3fb9504d}.badge-todo{background:#58a6ff1a;color:#58a6ff;border-color:#58a6ff4d}.badge-wip{background:#d299221a;color:#d29922;border-color:#d299224d}.badge-done{background:#3fb95014;color:#2ea043;border-color:#2ea0434d}.editor-panel{overflow:hidden}.editor-scroll{flex:1;overflow-y:auto;min-height:0;margin:0 -4px;padding:0 4px}.editor-scroll::-webkit-scrollbar{width:4px}.editor-scroll::-webkit-scrollbar-track{background:transparent}.editor-scroll::-webkit-scrollbar-thumb{background:var(--panel-border);border-radius:0}.task-form{display:flex;flex-direction:column;gap:12px;height:100%}.task-form label{display:grid;gap:4px}.task-form span{font-size:.72rem;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.08em}.task-form input,.task-form select,.task-form textarea{width:100%;padding:7px 10px;border:1px solid var(--panel-border);border-radius:0;background:var(--bg);color:var(--text);font-size:.88rem;transition:border-color .1s ease}.task-form input:focus,.task-form select:focus,.task-form textarea:focus{outline:none;border-color:var(--accent)}.task-form textarea{resize:none;flex:1;min-height:120px}.editor-label{flex:1;display:flex!important;flex-direction:column;min-height:0}.field-row{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.field-row-top{padding-bottom:12px;border-bottom:1px solid var(--panel-border)}.meta-strip{display:flex;gap:16px;color:var(--muted);font-size:.72rem}.form-actions{display:flex;gap:8px;flex-shrink:0;padding-top:4px}.primary-button,.danger-button,.ghost-button{border:1px solid;border-radius:0;padding:6px 14px;font-size:.8rem;font-weight:700;letter-spacing:.03em;text-transform:uppercase;transition:background .1s ease,color .1s ease}.primary-button{color:var(--bg);background:var(--accent);border-color:var(--accent)}.primary-button:hover{background:var(--accent-strong);border-color:var(--accent-strong)}.danger-button{color:var(--danger);background:#f851491a;border-color:#f8514966}.danger-button:hover{background:#f8514933}.ghost-button{color:var(--muted);background:transparent;border-color:var(--panel-border)}.ghost-button:hover{color:var(--text);background:#ffffff0d;border-color:var(--muted)}.primary-button:active,.danger-button:active,.ghost-button:active{transform:scale(.98)}.primary-button:disabled,.danger-button:disabled{opacity:.4;cursor:not-allowed}.dirty-state{color:var(--accent);font-weight:700;font-size:.72rem;white-space:nowrap}.notice{margin:0;padding-top:6px;color:var(--accent);font-size:.8rem;height:1.5rem;flex-shrink:0}.empty-editor{display:grid;place-items:center;flex:1;color:var(--muted);font-size:.85rem}.empty-editor p{margin:0}.error-panel{margin-top:12px;padding:10px 12px;border-radius:0;background:#f8514914;border:1px solid rgba(248,81,73,.2);flex-shrink:0}.error-panel h3{margin:0;font-family:inherit;font-size:.82rem;color:var(--danger)}.error-panel p{margin:8px 0 0;display:grid;font-size:.78rem}.app-header-actions{display:flex;gap:8px;align-items:center}.settings-button{display:inline-flex;align-items:center;justify-content:center;padding:6px;color:var(--muted);border:1px solid transparent;background:transparent;transition:color .1s ease,border-color .1s ease}.settings-button:hover{color:var(--text);border-color:var(--panel-border)}.settings-overlay{position:fixed;top:0;right:0;bottom:0;left:0;z-index:100;display:grid;place-items:center;background:#0009}.settings-modal{background:var(--panel);border:1px solid var(--panel-border);border-radius:0;box-shadow:var(--shadow);padding:20px;width:90%;max-width:480px;display:flex;flex-direction:column;gap:16px}.settings-body{display:flex;flex-direction:column;gap:10px}.settings-label{font-size:.75rem;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.08em}.settings-hint{display:block;font-size:.72rem;color:var(--muted);margin-top:2px}.settings-dir-list{display:flex;flex-direction:column;gap:6px}.settings-dir-row{display:flex;gap:6px;align-items:center}.settings-dir-row input{flex:1;padding:7px 10px;border:1px solid var(--panel-border);border-radius:0;background:var(--bg);color:var(--text);font-size:.88rem;transition:border-color .1s ease}.settings-dir-row input:focus{outline:none;border-color:var(--accent)}.settings-remove-button{padding:6px!important;color:var(--muted);flex-shrink:0}.settings-remove-button:disabled{opacity:.3;cursor:not-allowed}@media(max-width:900px){.app-shell{padding:10px;height:auto;min-height:100vh;overflow:auto}body,#root{height:auto;overflow:auto}.layout-grid{grid-template-columns:1fr;flex:none}.panel{min-height:auto;max-height:none;overflow:visible}.sidebar-scroll,.editor-scroll{overflow:visible;max-height:none}.app-header{flex-direction:column;align-items:stretch;gap:10px}.app-header-left{flex-direction:column;gap:4px}.field-row{grid-template-columns:1fr}.task-form textarea{min-height:200px;flex:none}}
|
|
1
|
+
:root{color-scheme:dark;--bg: #0a0e14;--bg-strong: #0d1117;--panel: #131920;--panel-border: #21262d;--text: #e6edf3;--muted: #7d8590;--accent: #3fb950;--accent-strong: #2ea043;--danger: #f85149;--shadow: 0 2px 8px rgba(0, 0, 0, .4);font-family:JetBrains Mono,IBM Plex Mono,Fira Code,SF Mono,Cascadia Code,monospace;line-height:1.6;color:var(--text);background:var(--bg)}*{box-sizing:border-box}body{margin:0;height:100vh;overflow:hidden}#root{height:100vh;overflow:hidden}button,input,select,textarea{font:inherit}button{cursor:pointer}.app-shell{display:flex;flex-direction:column;height:100vh;padding:12px 16px 16px;gap:12px;overflow:hidden}.app-header{display:flex;justify-content:space-between;align-items:center;gap:16px;flex-shrink:0;padding-bottom:12px;border-bottom:1px solid var(--panel-border)}.app-header-left{display:flex;align-items:baseline;gap:12px;min-width:0}.app-header h1{margin:0;font-family:inherit;font-size:1.1rem;font-weight:700;line-height:1.2;letter-spacing:-.02em;white-space:nowrap;color:var(--accent)}.app-header h1:before{content:"> ";color:var(--muted)}.eyebrow{margin:0;color:var(--muted);text-transform:uppercase;letter-spacing:.12em;font-size:.65rem;font-weight:600;white-space:nowrap}.layout-grid{display:grid;grid-template-columns:minmax(260px,320px) minmax(0,1fr);gap:1px;flex:1;min-height:0;background:var(--panel-border)}.panel{display:flex;flex-direction:column;background:var(--panel);border:none;border-radius:0;padding:14px 16px;min-height:0;overflow:hidden}.panel-header{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-bottom:12px;padding-bottom:8px;border-bottom:1px solid var(--panel-border);flex-shrink:0}.panel-header h2{margin:0;font-family:inherit;font-size:.85rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--muted)}.panel-header span{font-size:.75rem;color:var(--muted)}.panel-header-right{display:flex;align-items:center;gap:12px}.filter-toggle{display:flex;align-items:center;gap:4px;cursor:pointer;-webkit-user-select:none;user-select:none}.filter-toggle input[type=checkbox]{accent-color:var(--accent);cursor:pointer}.sidebar-scroll{flex:1;overflow-y:auto;min-height:0;margin:0 -4px;padding:0 4px}.sidebar-scroll::-webkit-scrollbar{width:4px}.sidebar-scroll::-webkit-scrollbar-track{background:transparent}.sidebar-scroll::-webkit-scrollbar-thumb{background:var(--panel-border);border-radius:0}.task-list{display:grid;gap:2px}.empty-list{margin:0;padding:14px;border-radius:0;background:#ffffff08;border:1px dashed var(--panel-border);color:var(--muted);font-size:.82rem}.task-row{display:grid;gap:3px;width:100%;text-align:left;padding:8px 10px;border-radius:0;border:1px solid transparent;border-left:2px solid transparent;background:#ffffff05;transition:background .1s ease,border-color .1s ease}.task-row:hover{background:#ffffff0d;border-left-color:var(--muted)}.task-row-selected{background:#3fb9500f;border-color:#3fb95026;border-left-color:var(--accent)}.task-row-badges{display:flex;gap:6px;align-items:center}.task-row strong{font-size:.82rem;font-weight:600;line-height:1.3}.task-row small{color:var(--muted);font-size:.72rem}.badge{display:inline-flex;justify-content:center;width:fit-content;padding:1px 6px;border-radius:0;font-size:.6rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;border:1px solid}.badge-must{background:#f851491a;color:#f85149;border-color:#f851494d}.badge-want{background:#3fb9501a;color:#3fb950;border-color:#3fb9504d}.badge-todo{background:#58a6ff1a;color:#58a6ff;border-color:#58a6ff4d}.badge-wip{background:#d299221a;color:#d29922;border-color:#d299224d}.badge-done{background:#3fb95014;color:#2ea043;border-color:#2ea0434d}.editor-panel{overflow:hidden}.editor-scroll{flex:1;overflow-y:auto;min-height:0;margin:0 -4px;padding:0 4px}.editor-scroll::-webkit-scrollbar{width:4px}.editor-scroll::-webkit-scrollbar-track{background:transparent}.editor-scroll::-webkit-scrollbar-thumb{background:var(--panel-border);border-radius:0}.task-form{display:flex;flex-direction:column;gap:12px;height:100%}.task-form label{display:grid;gap:4px}.task-form span{font-size:.72rem;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.08em}.task-form input,.task-form select,.task-form textarea{width:100%;padding:7px 10px;border:1px solid var(--panel-border);border-radius:0;background:var(--bg);color:var(--text);font-size:.88rem;transition:border-color .1s ease}.task-form input:focus,.task-form select:focus,.task-form textarea:focus{outline:none;border-color:var(--accent)}.task-form textarea{resize:none;flex:1;min-height:120px}.editor-label{flex:1;display:flex!important;flex-direction:column;min-height:0}.field-row{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.field-row-top{padding-bottom:12px;border-bottom:1px solid var(--panel-border)}.meta-strip{display:flex;gap:16px;color:var(--muted);font-size:.72rem}.form-actions{display:flex;gap:8px;flex-shrink:0;padding-top:4px}.primary-button,.danger-button,.ghost-button{border:1px solid;border-radius:0;padding:6px 14px;font-size:.8rem;font-weight:700;letter-spacing:.03em;text-transform:uppercase;transition:background .1s ease,color .1s ease}.primary-button{color:var(--bg);background:var(--accent);border-color:var(--accent)}.primary-button:hover{background:var(--accent-strong);border-color:var(--accent-strong)}.danger-button{color:var(--danger);background:#f851491a;border-color:#f8514966}.danger-button:hover{background:#f8514933}.ghost-button{color:var(--muted);background:transparent;border-color:var(--panel-border)}.ghost-button:hover{color:var(--text);background:#ffffff0d;border-color:var(--muted)}.primary-button:active,.danger-button:active,.ghost-button:active{transform:scale(.98)}.primary-button:disabled,.danger-button:disabled{opacity:.4;cursor:not-allowed}.dirty-state{color:var(--accent);font-weight:700;font-size:.72rem;white-space:nowrap}.notice{margin:0;padding-top:6px;color:var(--accent);font-size:.8rem;height:1.5rem;flex-shrink:0}.empty-editor{display:grid;place-items:center;flex:1;color:var(--muted);font-size:.85rem}.empty-editor p{margin:0}.error-panel{margin-top:12px;padding:10px 12px;border-radius:0;background:#f8514914;border:1px solid rgba(248,81,73,.2);flex-shrink:0}.error-panel h3{margin:0;font-family:inherit;font-size:.82rem;color:var(--danger)}.error-panel p{margin:8px 0 0;display:grid;font-size:.78rem}.app-header-actions{display:flex;gap:8px;align-items:center}.settings-button{display:inline-flex;align-items:center;justify-content:center;padding:6px;color:var(--muted);border:1px solid transparent;background:transparent;transition:color .1s ease,border-color .1s ease}.settings-button:hover{color:var(--text);border-color:var(--panel-border)}.settings-overlay{position:fixed;top:0;right:0;bottom:0;left:0;z-index:100;display:grid;place-items:center;background:#0009}.settings-modal{background:var(--panel);border:1px solid var(--panel-border);border-radius:0;box-shadow:var(--shadow);padding:20px;width:90%;max-width:480px;display:flex;flex-direction:column;gap:16px}.settings-body{display:flex;flex-direction:column;gap:10px}.settings-label{font-size:.75rem;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.08em}.settings-hint{display:block;font-size:.72rem;color:var(--muted);margin-top:2px}.settings-dir-list{display:flex;flex-direction:column;gap:6px}.settings-dir-row{display:flex;gap:6px;align-items:center}.settings-dir-row input{flex:1;padding:7px 10px;border:1px solid var(--panel-border);border-radius:0;background:var(--bg);color:var(--text);font-size:.88rem;transition:border-color .1s ease}.settings-dir-row input:focus{outline:none;border-color:var(--accent)}.settings-remove-button{padding:6px!important;color:var(--muted);flex-shrink:0}.settings-remove-button:disabled{opacity:.3;cursor:not-allowed}.tab-bar{display:flex;gap:0;flex-shrink:0;margin-bottom:12px;border-bottom:1px solid var(--panel-border)}.tab-button{padding:6px 16px;border:none;border-bottom:2px solid transparent;border-radius:0;background:transparent;color:var(--muted);font-size:.75rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;cursor:pointer;transition:color .1s ease,border-color .1s ease}.tab-button:hover{color:var(--text)}.tab-button.active{color:var(--accent);border-bottom-color:var(--accent)}.execute-panel{display:flex;flex-direction:column;gap:12px;height:100%}.execute-commands{display:flex;flex-direction:column;gap:6px}.execute-command-item{display:flex;align-items:center;gap:8px;padding:6px 10px;background:var(--bg);border:1px solid var(--panel-border);font-size:.82rem}.execute-command-item .command-index{color:var(--muted);font-size:.72rem;font-weight:700;flex-shrink:0}.execute-command-item code{flex:1;word-break:break-all}.execute-command-item .pass-body-badge{font-size:.6rem;font-weight:700;color:var(--accent);border:1px solid rgba(63,185,80,.3);padding:1px 4px;flex-shrink:0;text-transform:uppercase}.execute-no-commands{color:var(--muted);font-size:.82rem;padding:14px;border:1px dashed var(--panel-border)}.execution-result{flex:1;display:flex;flex-direction:column;gap:8px;min-height:0}.execution-result-header{display:flex;justify-content:space-between;align-items:center;flex-shrink:0}.execution-result-meta{display:flex;gap:12px;font-size:.72rem;color:var(--muted)}.execution-result-meta .exit-code-error{color:var(--danger)}.execution-result pre{flex:1;margin:0;padding:10px 12px;background:var(--bg);border:1px solid var(--panel-border);color:var(--text);font-size:.82rem;line-height:1.5;overflow:auto;white-space:pre-wrap;word-break:break-all;min-height:80px}.execution-stderr{border-color:#f851494d!important;color:var(--danger)!important}.copy-button{padding:4px 10px!important;font-size:.7rem!important}.collapsible-header{display:flex;align-items:center;gap:6px;padding:6px 0;border:none;background:transparent;color:var(--muted);font-size:.75rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;cursor:pointer;width:100%;text-align:left}.collapsible-header:hover{color:var(--text)}.collapsible-chevron{display:inline-block;transition:transform .15s ease;font-size:.6rem}.collapsible-chevron.open{transform:rotate(90deg)}.collapsible-body{display:flex;flex-direction:column;gap:8px;padding:8px 0}.command-step-row{display:flex;gap:6px;align-items:center}.command-step-row input,.task-form .command-step-row input{flex:1;width:auto;min-width:0;padding:7px 10px;border:1px solid var(--panel-border);border-radius:0;background:var(--bg);color:var(--text);font-size:.82rem;transition:border-color .1s ease}.command-step-row input:focus,.task-form .command-step-row input:focus{outline:none;border-color:var(--accent)}.command-step-row select,.task-form .command-step-row select{width:auto;min-width:0;padding:7px 6px;border:1px solid var(--panel-border);border-radius:0;background:var(--bg);color:var(--text);font-size:.75rem;flex-shrink:0}.command-step-row select:focus,.task-form .command-step-row select:focus{outline:none;border-color:var(--accent)}@media(max-width:900px){.app-shell{padding:10px;height:auto;min-height:100vh;overflow:auto}body,#root{height:auto;overflow:auto}.layout-grid{grid-template-columns:1fr;flex:none}.panel{min-height:auto;max-height:none;overflow:visible}.sidebar-scroll,.editor-scroll{overflow:visible;max-height:none}.app-header{flex-direction:column;align-items:stretch;gap:10px}.app-header-left{flex-direction:column;gap:4px}.field-row{grid-template-columns:1fr}.task-form textarea{min-height:200px;flex:none}}
|
package/dist/client/index.html
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Markdown Task Viewer</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-BcWwd56a.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-LYvJrXuE.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
package/dist/server.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import Fastify from "fastify";
|
|
3
3
|
import fastifyStatic from "@fastify/static";
|
|
4
4
|
import chokidar from "chokidar";
|
|
5
|
-
import
|
|
5
|
+
import path3 from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
|
|
8
8
|
// src/taskStore.ts
|
|
@@ -154,7 +154,8 @@ async function readConfig(rootDir) {
|
|
|
154
154
|
const taskDirs = Array.isArray(parsed.taskDirs) ? parsed.taskDirs.filter((item) => typeof item === "string") : ["."];
|
|
155
155
|
const ignorePaths = Array.isArray(parsed.ignorePaths) ? parsed.ignorePaths.filter((item) => typeof item === "string") : [];
|
|
156
156
|
const order = Array.isArray(parsed.order) ? parsed.order.filter((item) => typeof item === "string") : [];
|
|
157
|
-
|
|
157
|
+
const commands = Array.isArray(parsed.commands) ? parsed.commands : void 0;
|
|
158
|
+
return { version: parsed.version ?? 1, taskDirs, ignorePaths, order, commands };
|
|
158
159
|
} catch (error) {
|
|
159
160
|
const maybeError = error;
|
|
160
161
|
if (maybeError.code !== "ENOENT") {
|
|
@@ -183,11 +184,11 @@ async function saveOrder(rootDir, order) {
|
|
|
183
184
|
)
|
|
184
185
|
);
|
|
185
186
|
const existing = await readConfig(rootDir);
|
|
186
|
-
const payload = { version: 1, taskDirs: existing.taskDirs, ignorePaths: existing.ignorePaths, order: normalized };
|
|
187
|
+
const payload = { version: 1, taskDirs: existing.taskDirs, ignorePaths: existing.ignorePaths, order: normalized, commands: existing.commands };
|
|
187
188
|
await fs.writeFile(path.join(rootDir, CONFIG_FILE_NAME), `${JSON.stringify(payload, null, 2)}
|
|
188
189
|
`, "utf8");
|
|
189
190
|
}
|
|
190
|
-
async function saveConfig(rootDir, taskDirs, ignorePaths) {
|
|
191
|
+
async function saveConfig(rootDir, taskDirs, ignorePaths, commands) {
|
|
191
192
|
const validated = taskDirs.map((dir) => {
|
|
192
193
|
const normalized = dir.trim().replace(/\\/g, "/").replace(/\/+$/, "") || ".";
|
|
193
194
|
if (normalized.startsWith("../") || normalized.includes("/../")) {
|
|
@@ -200,7 +201,8 @@ async function saveConfig(rootDir, taskDirs, ignorePaths) {
|
|
|
200
201
|
}
|
|
201
202
|
const existing = await readConfig(rootDir);
|
|
202
203
|
const validatedIgnorePaths = ignorePaths ?? existing.ignorePaths;
|
|
203
|
-
const
|
|
204
|
+
const validatedCommands = commands !== void 0 ? commands : existing.commands;
|
|
205
|
+
const payload = { version: 1, taskDirs: validated, ignorePaths: validatedIgnorePaths, order: existing.order, commands: validatedCommands };
|
|
204
206
|
await fs.writeFile(path.join(rootDir, CONFIG_FILE_NAME), `${JSON.stringify(payload, null, 2)}
|
|
205
207
|
`, "utf8");
|
|
206
208
|
return payload;
|
|
@@ -412,9 +414,97 @@ function parseOrderPayload(input) {
|
|
|
412
414
|
return input.map((item) => ensureMarkdownExtension(normalizeRelativePath(String(item))));
|
|
413
415
|
}
|
|
414
416
|
|
|
417
|
+
// src/commandExecutor.ts
|
|
418
|
+
import { spawn } from "child_process";
|
|
419
|
+
import path2 from "path";
|
|
420
|
+
var TIMEOUT_MS = 3e4;
|
|
421
|
+
var VARIABLE_PATTERN = /\$\{?(TASK_TITLE|TASK_FILEPATH|TASK_BODY)\}?/g;
|
|
422
|
+
function substituteVariables(command, vars) {
|
|
423
|
+
return command.replace(VARIABLE_PATTERN, (_match, name) => vars[name] ?? "");
|
|
424
|
+
}
|
|
425
|
+
async function executeCommandPipeline(rootDir, steps, task) {
|
|
426
|
+
if (steps.length === 0) {
|
|
427
|
+
return { stdout: "", stderr: "", exitCode: 0, duration: 0 };
|
|
428
|
+
}
|
|
429
|
+
const absoluteFilePath = path2.resolve(rootDir, task.path);
|
|
430
|
+
const vars = {
|
|
431
|
+
TASK_TITLE: task.frontmatter.title,
|
|
432
|
+
TASK_FILEPATH: absoluteFilePath,
|
|
433
|
+
TASK_BODY: task.content
|
|
434
|
+
};
|
|
435
|
+
const startTime = Date.now();
|
|
436
|
+
return new Promise((resolve) => {
|
|
437
|
+
const resolvedCommands = steps.map((step, index) => {
|
|
438
|
+
let cmd = substituteVariables(step.command, vars);
|
|
439
|
+
if (index === 0 && step.passBody === "arg") {
|
|
440
|
+
const escaped = task.content.replace(/'/g, "'\\''");
|
|
441
|
+
cmd = `${cmd} '${escaped}'`;
|
|
442
|
+
}
|
|
443
|
+
return cmd;
|
|
444
|
+
});
|
|
445
|
+
const processes = [];
|
|
446
|
+
for (let index = 0; index < resolvedCommands.length; index++) {
|
|
447
|
+
const proc = spawn(resolvedCommands[index], {
|
|
448
|
+
shell: true,
|
|
449
|
+
cwd: rootDir,
|
|
450
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
451
|
+
});
|
|
452
|
+
if (index > 0) {
|
|
453
|
+
const prev = processes[index - 1];
|
|
454
|
+
if (prev?.stdout) {
|
|
455
|
+
prev.stdout.pipe(proc.stdin);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
processes.push(proc);
|
|
459
|
+
}
|
|
460
|
+
const firstStep = steps[0];
|
|
461
|
+
const firstProc = processes[0];
|
|
462
|
+
if (firstStep.passBody === "stdin" && firstProc.stdin) {
|
|
463
|
+
firstProc.stdin.write(task.content);
|
|
464
|
+
}
|
|
465
|
+
firstProc.stdin?.end();
|
|
466
|
+
const lastProc = processes[processes.length - 1];
|
|
467
|
+
const stdoutChunks = [];
|
|
468
|
+
const stderrChunks = [];
|
|
469
|
+
lastProc.stdout?.on("data", (chunk) => stdoutChunks.push(chunk));
|
|
470
|
+
lastProc.stderr?.on("data", (chunk) => stderrChunks.push(chunk));
|
|
471
|
+
for (let i = 0; i < processes.length - 1; i++) {
|
|
472
|
+
processes[i].stderr?.on("data", (chunk) => stderrChunks.push(chunk));
|
|
473
|
+
}
|
|
474
|
+
const timeout = setTimeout(() => {
|
|
475
|
+
for (const proc of processes) {
|
|
476
|
+
proc.kill("SIGTERM");
|
|
477
|
+
}
|
|
478
|
+
}, TIMEOUT_MS);
|
|
479
|
+
lastProc.on("close", (code) => {
|
|
480
|
+
clearTimeout(timeout);
|
|
481
|
+
resolve({
|
|
482
|
+
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
483
|
+
stderr: Buffer.concat(stderrChunks).toString("utf8"),
|
|
484
|
+
exitCode: code ?? 1,
|
|
485
|
+
duration: Date.now() - startTime
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
lastProc.on("error", (err) => {
|
|
489
|
+
clearTimeout(timeout);
|
|
490
|
+
resolve({
|
|
491
|
+
stdout: "",
|
|
492
|
+
stderr: err.message,
|
|
493
|
+
exitCode: 1,
|
|
494
|
+
duration: Date.now() - startTime
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
for (let i = 0; i < processes.length - 1; i++) {
|
|
498
|
+
processes[i].on("error", (err) => {
|
|
499
|
+
stderrChunks.push(Buffer.from(err.message));
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
|
|
415
505
|
// src/server.ts
|
|
416
506
|
var __filename = fileURLToPath(import.meta.url);
|
|
417
|
-
var __dirname =
|
|
507
|
+
var __dirname = path3.dirname(__filename);
|
|
418
508
|
function resolveClientDir(explicitClientDir) {
|
|
419
509
|
if (explicitClientDir === null) {
|
|
420
510
|
return null;
|
|
@@ -422,7 +512,7 @@ function resolveClientDir(explicitClientDir) {
|
|
|
422
512
|
if (explicitClientDir) {
|
|
423
513
|
return explicitClientDir;
|
|
424
514
|
}
|
|
425
|
-
return
|
|
515
|
+
return path3.resolve(__dirname, "client");
|
|
426
516
|
}
|
|
427
517
|
function sendJsonError(reply, error) {
|
|
428
518
|
if (error instanceof ValidationError) {
|
|
@@ -510,12 +600,56 @@ async function createServer(options) {
|
|
|
510
600
|
}
|
|
511
601
|
ignorePaths = body.ignorePaths;
|
|
512
602
|
}
|
|
513
|
-
|
|
603
|
+
let commands;
|
|
604
|
+
if (body?.commands !== void 0) {
|
|
605
|
+
if (!Array.isArray(body.commands)) {
|
|
606
|
+
throw new ValidationError("commands must be an array.");
|
|
607
|
+
}
|
|
608
|
+
commands = body.commands;
|
|
609
|
+
}
|
|
610
|
+
const config = await saveConfig(options.rootDir, taskDirs, ignorePaths, commands);
|
|
514
611
|
return reply.send(config);
|
|
515
612
|
} catch (error) {
|
|
516
613
|
sendJsonError(reply, error);
|
|
517
614
|
}
|
|
518
615
|
});
|
|
616
|
+
app.post("/api/execute", async (request, reply) => {
|
|
617
|
+
try {
|
|
618
|
+
const body = request.body;
|
|
619
|
+
const taskPath = body?.taskPath;
|
|
620
|
+
if (!taskPath || typeof taskPath !== "string") {
|
|
621
|
+
throw new ValidationError("taskPath is required.");
|
|
622
|
+
}
|
|
623
|
+
let task;
|
|
624
|
+
try {
|
|
625
|
+
task = await parseTask(options.rootDir, taskPath);
|
|
626
|
+
} catch (error) {
|
|
627
|
+
const maybeError = error;
|
|
628
|
+
if (maybeError.code === "ENOENT") {
|
|
629
|
+
return reply.code(404).send({ error: "Task not found." });
|
|
630
|
+
}
|
|
631
|
+
throw error;
|
|
632
|
+
}
|
|
633
|
+
let commands = body?.commands;
|
|
634
|
+
if (!commands || commands.length === 0) {
|
|
635
|
+
const taskCommands = task.extraFrontmatter.commands;
|
|
636
|
+
if (Array.isArray(taskCommands) && taskCommands.length > 0) {
|
|
637
|
+
commands = taskCommands;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
if (!commands || commands.length === 0) {
|
|
641
|
+
const config = await readConfig(options.rootDir);
|
|
642
|
+
commands = config.commands;
|
|
643
|
+
}
|
|
644
|
+
if (!commands || commands.length === 0) {
|
|
645
|
+
throw new ValidationError("No commands configured.");
|
|
646
|
+
}
|
|
647
|
+
const result = await executeCommandPipeline(options.rootDir, commands, task);
|
|
648
|
+
return reply.send(result);
|
|
649
|
+
} catch (error) {
|
|
650
|
+
sendJsonError(reply, error);
|
|
651
|
+
}
|
|
652
|
+
});
|
|
519
653
|
app.get("/api/events", async (_request, reply) => {
|
|
520
654
|
reply.raw.writeHead(200, {
|
|
521
655
|
"Content-Type": "text/event-stream",
|
|
@@ -541,18 +675,18 @@ async function createServer(options) {
|
|
|
541
675
|
});
|
|
542
676
|
const watcher = chokidar.watch(options.rootDir, {
|
|
543
677
|
ignoreInitial: true,
|
|
544
|
-
ignored: (watchPath) => watchPath.includes(`${
|
|
678
|
+
ignored: (watchPath) => watchPath.includes(`${path3.sep}.git`) || watchPath.includes(`${path3.sep}node_modules`)
|
|
545
679
|
});
|
|
546
680
|
watcher.on("all", (eventName, changedPath) => {
|
|
547
681
|
const isMarkdown = changedPath.endsWith(".md") || changedPath.endsWith(".markdown");
|
|
548
|
-
const isConfigFile =
|
|
682
|
+
const isConfigFile = path3.basename(changedPath) === ".md-task-viewer.json";
|
|
549
683
|
if (!isMarkdown && !isConfigFile) {
|
|
550
684
|
return;
|
|
551
685
|
}
|
|
552
686
|
const payload = JSON.stringify({
|
|
553
687
|
type: "tasks-changed",
|
|
554
688
|
eventName,
|
|
555
|
-
path:
|
|
689
|
+
path: path3.relative(options.rootDir, changedPath)
|
|
556
690
|
});
|
|
557
691
|
for (const listener of listeners) {
|
|
558
692
|
listener.send(payload);
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server.ts","../src/taskStore.ts","../src/types.ts","../src/slugify.ts"],"sourcesContent":["import Fastify, { type FastifyInstance } from \"fastify\";\nimport fastifyStatic from \"@fastify/static\";\nimport chokidar from \"chokidar\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n ConflictError,\n ValidationError,\n createTask,\n deleteTask,\n listTasks,\n parseOrderPayload,\n patchTaskFields,\n readConfig,\n saveConfig,\n saveOrder,\n updateTask\n} from \"./taskStore.js\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nexport interface CreateServerOptions {\n rootDir: string;\n clientDir?: string | null;\n}\n\nfunction resolveClientDir(explicitClientDir?: string | null): string | null {\n if (explicitClientDir === null) {\n return null;\n }\n if (explicitClientDir) {\n return explicitClientDir;\n }\n return path.resolve(__dirname, \"client\");\n}\n\nfunction sendJsonError(reply: { code: (statusCode: number) => { send: (payload: unknown) => void } }, error: unknown): void {\n if (error instanceof ValidationError) {\n reply.code(400).send({ error: error.message });\n return;\n }\n if (error instanceof ConflictError) {\n reply.code(409).send({ error: error.message });\n return;\n }\n reply.code(500).send({ error: error instanceof Error ? error.message : \"Internal server error\" });\n}\n\nexport async function createServer(options: CreateServerOptions): Promise<FastifyInstance> {\n const app = Fastify({ logger: false });\n const listeners = new Set<{ send: (payload: string) => void; close: () => void }>();\n const clientDir = resolveClientDir(options.clientDir);\n\n app.addHook(\"onClose\", async () => {\n for (const listener of listeners) {\n listener.close();\n }\n });\n\n app.get(\"/api/tasks\", async () => listTasks(options.rootDir));\n\n app.post(\"/api/tasks\", async (request, reply) => {\n try {\n const task = await createTask(options.rootDir, (request.body ?? {}) as never);\n return reply.code(201).send(task);\n } catch (error) {\n sendJsonError(reply, error);\n }\n });\n\n app.patch(\"/api/tasks/*\", async (request, reply) => {\n const currentPath = decodeURIComponent((request.params as { \"*\": string })[\"*\"] ?? \"\");\n try {\n const task = await updateTask(options.rootDir, currentPath, (request.body ?? {}) as never);\n return reply.send(task);\n } catch (error) {\n sendJsonError(reply, error);\n }\n });\n\n app.delete(\"/api/tasks/*\", async (request, reply) => {\n const currentPath = decodeURIComponent((request.params as { \"*\": string })[\"*\"] ?? \"\");\n try {\n await deleteTask(options.rootDir, currentPath);\n return reply.code(204).send();\n } catch (error) {\n sendJsonError(reply, error);\n }\n });\n\n app.patch(\"/api/task-fields/*\", async (request, reply) => {\n const currentPath = decodeURIComponent((request.params as { \"*\": string })[\"*\"] ?? \"\");\n try {\n const task = await patchTaskFields(options.rootDir, currentPath, (request.body ?? {}) as never);\n return reply.send(task);\n } catch (error) {\n sendJsonError(reply, error);\n }\n });\n\n app.put(\"/api/order\", async (request, reply) => {\n try {\n const order = parseOrderPayload((request.body as { order?: unknown } | null)?.order ?? []);\n await saveOrder(options.rootDir, order);\n return reply.code(204).send();\n } catch (error) {\n sendJsonError(reply, error);\n }\n });\n\n app.get(\"/api/config\", async () => {\n try {\n return await readConfig(options.rootDir);\n } catch (error) {\n return { version: 1, taskDirs: [\".\"], order: [] };\n }\n });\n\n app.put(\"/api/config\", async (request, reply) => {\n try {\n const body = request.body as { taskDirs?: unknown; ignorePaths?: unknown } | null;\n const taskDirs = body?.taskDirs;\n if (!Array.isArray(taskDirs) || taskDirs.some((item) => typeof item !== \"string\")) {\n throw new ValidationError(\"taskDirs must be an array of strings.\");\n }\n let ignorePaths: string[] | undefined;\n if (body?.ignorePaths !== undefined) {\n if (!Array.isArray(body.ignorePaths) || body.ignorePaths.some((item) => typeof item !== \"string\")) {\n throw new ValidationError(\"ignorePaths must be an array of strings.\");\n }\n ignorePaths = body.ignorePaths as string[];\n }\n const config = await saveConfig(options.rootDir, taskDirs as string[], ignorePaths);\n return reply.send(config);\n } catch (error) {\n sendJsonError(reply, error);\n }\n });\n\n app.get(\"/api/events\", async (_request, reply) => {\n reply.raw.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache, no-transform\",\n Connection: \"keep-alive\"\n });\n reply.raw.write(\"\\n\");\n\n const listener = {\n send(payload: string) {\n reply.raw.write(`data: ${payload}\\n\\n`);\n },\n close() {\n reply.raw.end();\n }\n };\n\n listeners.add(listener);\n reply.raw.on(\"close\", () => {\n listeners.delete(listener);\n });\n\n return reply.hijack();\n });\n\n const watcher = chokidar.watch(options.rootDir, {\n ignoreInitial: true,\n ignored: (watchPath) => watchPath.includes(`${path.sep}.git`) || watchPath.includes(`${path.sep}node_modules`)\n });\n\n watcher.on(\"all\", (eventName, changedPath) => {\n const isMarkdown = changedPath.endsWith(\".md\") || changedPath.endsWith(\".markdown\");\n const isConfigFile = path.basename(changedPath) === \".md-task-viewer.json\";\n if (!isMarkdown && !isConfigFile) {\n return;\n }\n\n const payload = JSON.stringify({\n type: \"tasks-changed\",\n eventName,\n path: path.relative(options.rootDir, changedPath)\n });\n\n for (const listener of listeners) {\n listener.send(payload);\n }\n });\n\n app.addHook(\"onClose\", async () => {\n await watcher.close();\n });\n\n if (clientDir) {\n await app.register(fastifyStatic, {\n root: clientDir,\n prefix: \"/\"\n });\n\n app.setNotFoundHandler(async (request, reply) => {\n if (request.raw.url?.startsWith(\"/api/\")) {\n return reply.code(404).send({ error: \"Not found\" });\n }\n return reply.sendFile(\"index.html\");\n });\n }\n\n return app;\n}\n","import matter from \"gray-matter\";\nimport picomatch from \"picomatch\";\nimport path from \"node:path\";\nimport { promises as fs } from \"node:fs\";\nimport {\n CONFIG_FILE_NAME,\n type ConfigFile,\n type CreateTaskInput,\n type PatchTaskFieldsInput,\n type TaskFrontmatter,\n type TaskListResponse,\n type TaskParseError,\n type TaskPriority,\n type TaskRecord,\n type TaskStatus,\n type UpdateTaskInput\n} from \"./types.js\";\nimport { slugify } from \"./slugify.js\";\n\nconst MARKDOWN_EXTENSIONS = new Set([\".md\", \".markdown\"]);\nconst REQUIRED_PRIORITY: TaskPriority[] = [\"MUST\", \"WANT\"];\nconst REQUIRED_STATUS: TaskStatus[] = [\"TODO\", \"WIP\", \"DONE\"];\n\nexport class ConflictError extends Error {}\nexport class ValidationError extends Error {}\n\nfunction toPosixPath(filePath: string): string {\n return filePath.split(path.sep).join(\"/\");\n}\n\nfunction normalizeRelativePath(candidate: string): string {\n const normalized = toPosixPath(path.posix.normalize(candidate.trim()));\n if (!normalized || normalized === \".\" || normalized.startsWith(\"../\") || normalized.includes(\"/../\")) {\n throw new ValidationError(\"Path must stay within the workspace root.\");\n }\n\n return normalized.replace(/^\\.\\/+/, \"\");\n}\n\nfunction ensureMarkdownExtension(filePath: string): string {\n return path.posix.extname(filePath) ? filePath : `${filePath}.md`;\n}\n\nfunction asUtcISOString(date: Date): string {\n return date.toISOString();\n}\n\nfunction buildDefaults(filePath: string, stats: { birthtime: Date; mtime: Date }): TaskFrontmatter {\n const basename = path.basename(filePath, path.extname(filePath));\n const title = basename.replace(/[-_]+/g, \" \").replace(/\\b\\w/g, (char) => char.toUpperCase());\n\n return {\n title,\n priority: \"WANT\",\n status: \"TODO\",\n createdAt: asUtcISOString(stats.birthtime),\n updatedAt: asUtcISOString(stats.mtime)\n };\n}\n\nfunction splitFrontmatter(data: Record<string, unknown>, statsDefaults: TaskFrontmatter): {\n frontmatter: TaskFrontmatter;\n extraFrontmatter: Record<string, unknown>;\n normalized: boolean;\n} {\n const extraFrontmatter: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(data)) {\n if (![\"title\", \"priority\", \"status\", \"createdAt\", \"updatedAt\"].includes(key)) {\n extraFrontmatter[key] = value;\n }\n }\n\n const title = typeof data.title === \"string\" && data.title.trim() ? data.title : statsDefaults.title;\n const priority = REQUIRED_PRIORITY.includes(data.priority as TaskPriority)\n ? (data.priority as TaskPriority)\n : statsDefaults.priority;\n const status = REQUIRED_STATUS.includes(data.status as TaskStatus)\n ? (data.status as TaskStatus)\n : statsDefaults.status;\n const createdAt =\n typeof data.createdAt === \"string\" && !Number.isNaN(Date.parse(data.createdAt))\n ? new Date(data.createdAt).toISOString()\n : statsDefaults.createdAt;\n const updatedAt =\n typeof data.updatedAt === \"string\" && !Number.isNaN(Date.parse(data.updatedAt))\n ? new Date(data.updatedAt).toISOString()\n : statsDefaults.updatedAt;\n\n const normalized =\n title !== data.title ||\n priority !== data.priority ||\n status !== data.status ||\n createdAt !== data.createdAt ||\n updatedAt !== data.updatedAt;\n\n return {\n frontmatter: { title, priority, status, createdAt, updatedAt },\n extraFrontmatter,\n normalized\n };\n}\n\nexport function serializeTask(record: TaskRecord): string {\n const data = {\n ...record.extraFrontmatter,\n title: record.frontmatter.title,\n priority: record.frontmatter.priority,\n status: record.frontmatter.status,\n createdAt: record.frontmatter.createdAt,\n updatedAt: record.frontmatter.updatedAt\n };\n\n return matter.stringify(record.content, data);\n}\n\nasync function readDirectoryRecursive(rootDir: string, currentDir: string, results: string[]): Promise<void> {\n const entries = await fs.readdir(currentDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.name === \".git\" || entry.name === \"node_modules\") {\n continue;\n }\n\n const absolutePath = path.join(currentDir, entry.name);\n if (entry.isDirectory()) {\n await readDirectoryRecursive(rootDir, absolutePath, results);\n continue;\n }\n\n if (entry.name === CONFIG_FILE_NAME) {\n continue;\n }\n\n if (!MARKDOWN_EXTENSIONS.has(path.extname(entry.name).toLowerCase())) {\n continue;\n }\n\n results.push(toPosixPath(path.relative(rootDir, absolutePath)));\n }\n}\n\nasync function listMarkdownFiles(rootDir: string, taskDirs: string[], ignorePaths: string[]): Promise<string[]> {\n const results: string[] = [];\n const seen = new Set<string>();\n\n const isIgnored = ignorePaths.length > 0 ? picomatch(ignorePaths) : null;\n\n for (const taskDir of taskDirs) {\n const scanDir = path.resolve(rootDir, taskDir);\n try {\n await fs.access(scanDir);\n } catch {\n continue;\n }\n const dirResults: string[] = [];\n await readDirectoryRecursive(rootDir, scanDir, dirResults);\n for (const filePath of dirResults) {\n if (!seen.has(filePath)) {\n seen.add(filePath);\n if (isIgnored && isIgnored(filePath)) {\n continue;\n }\n results.push(filePath);\n }\n }\n }\n\n return results.sort();\n}\n\nexport async function parseTask(rootDir: string, relativePath: string): Promise<TaskRecord> {\n const absolutePath = path.join(rootDir, relativePath);\n const raw = await fs.readFile(absolutePath, \"utf8\");\n const stats = await fs.stat(absolutePath);\n const parsed = matter(raw);\n const defaults = buildDefaults(relativePath, stats);\n const { frontmatter, extraFrontmatter, normalized } = splitFrontmatter(parsed.data, defaults);\n\n return {\n path: toPosixPath(relativePath),\n content: parsed.content,\n frontmatter,\n extraFrontmatter,\n raw,\n normalized\n };\n}\n\nexport async function readConfig(rootDir: string): Promise<ConfigFile> {\n const configFilePath = path.join(rootDir, CONFIG_FILE_NAME);\n\n try {\n const raw = await fs.readFile(configFilePath, \"utf8\");\n const parsed = JSON.parse(raw) as Partial<ConfigFile>;\n const taskDirs = Array.isArray(parsed.taskDirs)\n ? parsed.taskDirs.filter((item): item is string => typeof item === \"string\")\n : [\".\"];\n const ignorePaths = Array.isArray(parsed.ignorePaths)\n ? parsed.ignorePaths.filter((item): item is string => typeof item === \"string\")\n : [];\n const order = Array.isArray(parsed.order)\n ? parsed.order.filter((item): item is string => typeof item === \"string\")\n : [];\n return { version: parsed.version ?? 1, taskDirs, ignorePaths, order };\n } catch (error) {\n const maybeError = error as NodeJS.ErrnoException;\n if (maybeError.code !== \"ENOENT\") {\n throw error;\n }\n return { version: 1, taskDirs: [\".\"], ignorePaths: [], order: [] };\n }\n}\n\nasync function reconcileOrder(rootDir: string, taskPaths: string[]): Promise<{ order: string[]; changed: boolean }> {\n const config = await readConfig(rootDir);\n const order = config.order;\n\n const known = new Set(taskPaths);\n const nextOrder = order.filter((item) => known.has(item));\n for (const taskPath of taskPaths) {\n if (!nextOrder.includes(taskPath)) {\n nextOrder.push(taskPath);\n }\n }\n\n const changed = nextOrder.length !== order.length || nextOrder.some((item, index) => item !== order[index]);\n return { order: nextOrder, changed };\n}\n\nexport async function saveOrder(rootDir: string, order: string[]): Promise<void> {\n const normalized = Array.from(\n new Set(\n order.map((item) => ensureMarkdownExtension(normalizeRelativePath(item)))\n )\n );\n const existing = await readConfig(rootDir);\n const payload: ConfigFile = { version: 1, taskDirs: existing.taskDirs, ignorePaths: existing.ignorePaths, order: normalized };\n await fs.writeFile(path.join(rootDir, CONFIG_FILE_NAME), `${JSON.stringify(payload, null, 2)}\\n`, \"utf8\");\n}\n\nexport async function saveConfig(rootDir: string, taskDirs: string[], ignorePaths?: string[]): Promise<ConfigFile> {\n const validated = taskDirs.map((dir) => {\n const normalized = dir.trim().replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\") || \".\";\n if (normalized.startsWith(\"../\") || normalized.includes(\"/../\")) {\n throw new ValidationError(\"taskDirs must stay within the workspace root.\");\n }\n return normalized;\n });\n if (validated.length === 0) {\n throw new ValidationError(\"taskDirs must contain at least one directory.\");\n }\n const existing = await readConfig(rootDir);\n const validatedIgnorePaths = ignorePaths ?? existing.ignorePaths;\n const payload: ConfigFile = { version: 1, taskDirs: validated, ignorePaths: validatedIgnorePaths, order: existing.order };\n await fs.writeFile(path.join(rootDir, CONFIG_FILE_NAME), `${JSON.stringify(payload, null, 2)}\\n`, \"utf8\");\n return payload;\n}\n\nexport async function listTasks(rootDir: string): Promise<TaskListResponse> {\n const config = await readConfig(rootDir);\n const files = await listMarkdownFiles(rootDir, config.taskDirs, config.ignorePaths);\n const errors: TaskParseError[] = [];\n const tasks = await Promise.all(\n files.map(async (relativePath) => {\n try {\n return await parseTask(rootDir, relativePath);\n } catch (error) {\n errors.push({\n path: relativePath,\n message: error instanceof Error ? error.message : \"Unknown parse error\"\n });\n return null;\n }\n })\n );\n\n const taskRecords = tasks.filter((task): task is TaskRecord => task !== null);\n const { order, changed } = await reconcileOrder(\n rootDir,\n taskRecords.map((task) => task.path)\n );\n\n if (changed) {\n await saveOrder(rootDir, order);\n }\n\n const orderIndex = new Map(order.map((item, index) => [item, index]));\n taskRecords.sort((left, right) => {\n const leftIndex = orderIndex.get(left.path) ?? Number.MAX_SAFE_INTEGER;\n const rightIndex = orderIndex.get(right.path) ?? Number.MAX_SAFE_INTEGER;\n return leftIndex - rightIndex || left.path.localeCompare(right.path);\n });\n\n return { tasks: taskRecords, errors };\n}\n\nasync function ensureDirectoryForFile(rootDir: string, relativeFilePath: string): Promise<string> {\n const normalized = ensureMarkdownExtension(normalizeRelativePath(relativeFilePath));\n const absolutePath = path.join(rootDir, normalized);\n const directory = path.dirname(absolutePath);\n await fs.mkdir(directory, { recursive: true });\n return normalized;\n}\n\nasync function nextAvailablePath(rootDir: string, directory: string, title: string): Promise<string> {\n const safeDirectory = directory ? normalizeRelativePath(directory) : \"\";\n const slug = slugify(title);\n const base = safeDirectory ? `${safeDirectory}/${slug}` : slug;\n\n let attempt = 0;\n while (true) {\n const candidate = ensureMarkdownExtension(attempt === 0 ? base : `${base}-${attempt + 1}`);\n try {\n await fs.access(path.join(rootDir, candidate));\n attempt += 1;\n } catch {\n return candidate;\n }\n }\n}\n\nexport async function createTask(rootDir: string, input: CreateTaskInput): Promise<TaskRecord> {\n if (!input.title.trim()) {\n throw new ValidationError(\"Title is required.\");\n }\n\n const now = asUtcISOString(new Date());\n const relativePath = input.path?.trim()\n ? await ensureDirectoryForFile(rootDir, input.path)\n : await nextAvailablePath(rootDir, input.directory ?? \"\", input.title);\n const absolutePath = path.join(rootDir, relativePath);\n\n try {\n await fs.access(absolutePath);\n } catch (error) {\n const maybeError = error as NodeJS.ErrnoException;\n if (maybeError.code === \"ENOENT\") {\n // The target path is available.\n } else if (maybeError.code) {\n throw error;\n } else {\n throw new ValidationError(\"A task already exists at that path.\");\n }\n }\n\n const record: TaskRecord = {\n path: relativePath,\n content: input.content ?? \"\",\n raw: \"\",\n normalized: false,\n extraFrontmatter: input.extraFrontmatter ?? {},\n frontmatter: {\n title: input.title.trim(),\n priority: input.priority ?? \"MUST\",\n status: input.status ?? \"TODO\",\n createdAt: now,\n updatedAt: now\n }\n };\n\n await fs.mkdir(path.dirname(absolutePath), { recursive: true });\n await fs.writeFile(absolutePath, serializeTask(record), \"utf8\");\n\n const current = await listTasks(rootDir);\n await saveOrder(rootDir, current.tasks.map((task) => task.path).concat(relativePath));\n return parseTask(rootDir, relativePath);\n}\n\nexport async function updateTask(rootDir: string, currentPath: string, input: UpdateTaskInput): Promise<TaskRecord> {\n const normalizedCurrentPath = ensureMarkdownExtension(normalizeRelativePath(currentPath));\n const absoluteCurrentPath = path.join(rootDir, normalizedCurrentPath);\n\n let existing: TaskRecord;\n try {\n existing = await parseTask(rootDir, normalizedCurrentPath);\n } catch (error) {\n const maybeError = error as NodeJS.ErrnoException;\n if (maybeError.code === \"ENOENT\") {\n throw new ConflictError(\"The task no longer exists.\");\n }\n throw error;\n }\n\n if (input.baseUpdatedAt && existing.frontmatter.updatedAt !== input.baseUpdatedAt) {\n throw new ConflictError(\"The task changed on disk. Reload before saving.\");\n }\n\n const nextPath = input.path?.trim()\n ? await ensureDirectoryForFile(rootDir, input.path)\n : normalizedCurrentPath;\n const absoluteNextPath = path.join(rootDir, nextPath);\n\n if (nextPath !== normalizedCurrentPath) {\n try {\n await fs.access(absoluteNextPath);\n } catch (error) {\n const maybeError = error as NodeJS.ErrnoException;\n if (maybeError.code === \"ENOENT\") {\n // The target path is available.\n } else if (maybeError.code) {\n throw error;\n } else {\n throw new ValidationError(\"A task already exists at the target path.\");\n }\n }\n }\n\n const record: TaskRecord = {\n path: nextPath,\n raw: existing.raw,\n normalized: false,\n content: input.content,\n extraFrontmatter: input.extraFrontmatter ?? existing.extraFrontmatter,\n frontmatter: {\n title: input.title.trim(),\n priority: input.priority,\n status: input.status,\n createdAt: existing.frontmatter.createdAt,\n updatedAt: asUtcISOString(new Date())\n }\n };\n\n await fs.writeFile(absoluteCurrentPath, serializeTask(record), \"utf8\");\n if (nextPath !== normalizedCurrentPath) {\n await fs.mkdir(path.dirname(absoluteNextPath), { recursive: true });\n await fs.rename(absoluteCurrentPath, absoluteNextPath);\n }\n\n const current = await listTasks(rootDir);\n const updatedOrder = current.tasks.map((task) => task.path);\n await saveOrder(rootDir, updatedOrder);\n return parseTask(rootDir, nextPath);\n}\n\nexport async function deleteTask(rootDir: string, relativePath: string): Promise<void> {\n const normalizedPath = ensureMarkdownExtension(normalizeRelativePath(relativePath));\n const absolutePath = path.join(rootDir, normalizedPath);\n\n try {\n await fs.unlink(absolutePath);\n } catch (error) {\n const maybeError = error as NodeJS.ErrnoException;\n if (maybeError.code === \"ENOENT\") {\n throw new ConflictError(\"The task no longer exists.\");\n }\n throw error;\n }\n\n const current = await listTasks(rootDir);\n await saveOrder(\n rootDir,\n current.tasks.map((task) => task.path)\n );\n}\n\nexport async function patchTaskFields(rootDir: string, currentPath: string, input: PatchTaskFieldsInput): Promise<TaskRecord> {\n const normalizedCurrentPath = ensureMarkdownExtension(normalizeRelativePath(currentPath));\n const absoluteCurrentPath = path.join(rootDir, normalizedCurrentPath);\n\n let existing: TaskRecord;\n try {\n existing = await parseTask(rootDir, normalizedCurrentPath);\n } catch (error) {\n const maybeError = error as NodeJS.ErrnoException;\n if (maybeError.code === \"ENOENT\") {\n throw new ConflictError(\"The task no longer exists.\");\n }\n throw error;\n }\n\n const priority = input.priority && REQUIRED_PRIORITY.includes(input.priority) ? input.priority : existing.frontmatter.priority;\n const status = input.status && REQUIRED_STATUS.includes(input.status) ? input.status : existing.frontmatter.status;\n\n if (priority === existing.frontmatter.priority && status === existing.frontmatter.status) {\n return existing;\n }\n\n const record: TaskRecord = {\n path: normalizedCurrentPath,\n raw: existing.raw,\n normalized: false,\n content: existing.content,\n extraFrontmatter: existing.extraFrontmatter,\n frontmatter: {\n ...existing.frontmatter,\n priority,\n status,\n updatedAt: asUtcISOString(new Date())\n }\n };\n\n await fs.writeFile(absoluteCurrentPath, serializeTask(record), \"utf8\");\n return parseTask(rootDir, normalizedCurrentPath);\n}\n\nexport function parseOrderPayload(input: unknown): string[] {\n if (!Array.isArray(input)) {\n throw new ValidationError(\"Order payload must be an array.\");\n }\n\n return input.map((item) => ensureMarkdownExtension(normalizeRelativePath(String(item))));\n}\n\nexport async function readOrder(rootDir: string): Promise<ConfigFile> {\n const config = await readConfig(rootDir);\n const { order } = await reconcileOrder(\n rootDir,\n (await listTasks(rootDir)).tasks.map((task) => task.path)\n );\n return { version: 1, taskDirs: config.taskDirs, ignorePaths: config.ignorePaths, order };\n}\n\nexport const taskStoreUtils = {\n slugify,\n normalizeRelativePath,\n ensureMarkdownExtension,\n splitFrontmatter,\n buildDefaults\n};\n","export const CONFIG_FILE_NAME = \".md-task-viewer.json\";\n\nexport type TaskPriority = \"MUST\" | \"WANT\";\nexport type TaskStatus = \"TODO\" | \"WIP\" | \"DONE\";\n\nexport interface TaskFrontmatter {\n title: string;\n priority: TaskPriority;\n status: TaskStatus;\n createdAt: string;\n updatedAt: string;\n [key: string]: unknown;\n}\n\nexport interface TaskRecord {\n path: string;\n content: string;\n frontmatter: TaskFrontmatter;\n extraFrontmatter: Record<string, unknown>;\n raw: string;\n normalized: boolean;\n}\n\nexport interface TaskParseError {\n path: string;\n message: string;\n}\n\nexport interface TaskListResponse {\n tasks: TaskRecord[];\n errors: TaskParseError[];\n}\n\nexport interface CreateTaskInput {\n title: string;\n priority?: TaskPriority;\n status?: TaskStatus;\n content?: string;\n directory?: string;\n path?: string;\n extraFrontmatter?: Record<string, unknown>;\n}\n\nexport interface UpdateTaskInput {\n path?: string;\n title: string;\n priority: TaskPriority;\n status: TaskStatus;\n content: string;\n extraFrontmatter?: Record<string, unknown>;\n baseUpdatedAt?: string;\n}\n\nexport interface PatchTaskFieldsInput {\n priority?: TaskPriority;\n status?: TaskStatus;\n}\n\nexport interface ConfigFile {\n version: number;\n taskDirs: string[];\n ignorePaths: string[];\n order: string[];\n}\n","export function slugify(value: string): string {\n const slug = value\n // Unicode結合文字列を正規合成形に統一(例: か+濁点 → が)\n .normalize(\"NFC\")\n // 半角・全角スペース(U+3000)など空白文字をハイフンに変換。近年では \\s は U+3000 にもマッチするが、明示的に記載\n .replace(/[\\s\\u3000]+/g, \"-\")\n // Unicode文字(L)・数字(N)・ハイフン以外を除去(記号や句読点など)\n .replace(/[^\\p{L}\\p{N}-]+/gu, \"\")\n // 先頭・末尾の余分なハイフンを除去\n .replace(/^-+|-+$/g, \"\");\n return slug || \"untitled-task\";\n}\n"],"mappings":"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","names":["path","path"]}
|
|
1
|
+
{"version":3,"sources":["../src/server.ts","../src/taskStore.ts","../src/types.ts","../src/slugify.ts","../src/commandExecutor.ts"],"sourcesContent":["import Fastify, { type FastifyInstance } from \"fastify\";\nimport fastifyStatic from \"@fastify/static\";\nimport chokidar from \"chokidar\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n ConflictError,\n ValidationError,\n createTask,\n deleteTask,\n listTasks,\n parseOrderPayload,\n parseTask,\n patchTaskFields,\n readConfig,\n saveConfig,\n saveOrder,\n updateTask\n} from \"./taskStore.js\";\nimport type { CommandStep } from \"./types.js\";\nimport { executeCommandPipeline } from \"./commandExecutor.js\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nexport interface CreateServerOptions {\n rootDir: string;\n clientDir?: string | null;\n}\n\nfunction resolveClientDir(explicitClientDir?: string | null): string | null {\n if (explicitClientDir === null) {\n return null;\n }\n if (explicitClientDir) {\n return explicitClientDir;\n }\n return path.resolve(__dirname, \"client\");\n}\n\nfunction sendJsonError(reply: { code: (statusCode: number) => { send: (payload: unknown) => void } }, error: unknown): void {\n if (error instanceof ValidationError) {\n reply.code(400).send({ error: error.message });\n return;\n }\n if (error instanceof ConflictError) {\n reply.code(409).send({ error: error.message });\n return;\n }\n reply.code(500).send({ error: error instanceof Error ? error.message : \"Internal server error\" });\n}\n\nexport async function createServer(options: CreateServerOptions): Promise<FastifyInstance> {\n const app = Fastify({ logger: false });\n const listeners = new Set<{ send: (payload: string) => void; close: () => void }>();\n const clientDir = resolveClientDir(options.clientDir);\n\n app.addHook(\"onClose\", async () => {\n for (const listener of listeners) {\n listener.close();\n }\n });\n\n app.get(\"/api/tasks\", async () => listTasks(options.rootDir));\n\n app.post(\"/api/tasks\", async (request, reply) => {\n try {\n const task = await createTask(options.rootDir, (request.body ?? {}) as never);\n return reply.code(201).send(task);\n } catch (error) {\n sendJsonError(reply, error);\n }\n });\n\n app.patch(\"/api/tasks/*\", async (request, reply) => {\n const currentPath = decodeURIComponent((request.params as { \"*\": string })[\"*\"] ?? \"\");\n try {\n const task = await updateTask(options.rootDir, currentPath, (request.body ?? {}) as never);\n return reply.send(task);\n } catch (error) {\n sendJsonError(reply, error);\n }\n });\n\n app.delete(\"/api/tasks/*\", async (request, reply) => {\n const currentPath = decodeURIComponent((request.params as { \"*\": string })[\"*\"] ?? \"\");\n try {\n await deleteTask(options.rootDir, currentPath);\n return reply.code(204).send();\n } catch (error) {\n sendJsonError(reply, error);\n }\n });\n\n app.patch(\"/api/task-fields/*\", async (request, reply) => {\n const currentPath = decodeURIComponent((request.params as { \"*\": string })[\"*\"] ?? \"\");\n try {\n const task = await patchTaskFields(options.rootDir, currentPath, (request.body ?? {}) as never);\n return reply.send(task);\n } catch (error) {\n sendJsonError(reply, error);\n }\n });\n\n app.put(\"/api/order\", async (request, reply) => {\n try {\n const order = parseOrderPayload((request.body as { order?: unknown } | null)?.order ?? []);\n await saveOrder(options.rootDir, order);\n return reply.code(204).send();\n } catch (error) {\n sendJsonError(reply, error);\n }\n });\n\n app.get(\"/api/config\", async () => {\n try {\n return await readConfig(options.rootDir);\n } catch (error) {\n return { version: 1, taskDirs: [\".\"], order: [] };\n }\n });\n\n app.put(\"/api/config\", async (request, reply) => {\n try {\n const body = request.body as { taskDirs?: unknown; ignorePaths?: unknown; commands?: unknown } | null;\n const taskDirs = body?.taskDirs;\n if (!Array.isArray(taskDirs) || taskDirs.some((item) => typeof item !== \"string\")) {\n throw new ValidationError(\"taskDirs must be an array of strings.\");\n }\n let ignorePaths: string[] | undefined;\n if (body?.ignorePaths !== undefined) {\n if (!Array.isArray(body.ignorePaths) || body.ignorePaths.some((item) => typeof item !== \"string\")) {\n throw new ValidationError(\"ignorePaths must be an array of strings.\");\n }\n ignorePaths = body.ignorePaths as string[];\n }\n let commands: CommandStep[] | undefined;\n if (body?.commands !== undefined) {\n if (!Array.isArray(body.commands)) {\n throw new ValidationError(\"commands must be an array.\");\n }\n commands = body.commands as CommandStep[];\n }\n const config = await saveConfig(options.rootDir, taskDirs as string[], ignorePaths, commands);\n return reply.send(config);\n } catch (error) {\n sendJsonError(reply, error);\n }\n });\n\n app.post(\"/api/execute\", async (request, reply) => {\n try {\n const body = request.body as { taskPath?: string; commands?: CommandStep[] } | null;\n const taskPath = body?.taskPath;\n if (!taskPath || typeof taskPath !== \"string\") {\n throw new ValidationError(\"taskPath is required.\");\n }\n\n let task;\n try {\n task = await parseTask(options.rootDir, taskPath);\n } catch (error) {\n const maybeError = error as NodeJS.ErrnoException;\n if (maybeError.code === \"ENOENT\") {\n return reply.code(404).send({ error: \"Task not found.\" });\n }\n throw error;\n }\n\n // Resolve commands: request body > task extraFrontmatter > global config\n let commands = body?.commands;\n if (!commands || commands.length === 0) {\n const taskCommands = task.extraFrontmatter.commands;\n if (Array.isArray(taskCommands) && taskCommands.length > 0) {\n commands = taskCommands as CommandStep[];\n }\n }\n if (!commands || commands.length === 0) {\n const config = await readConfig(options.rootDir);\n commands = config.commands;\n }\n if (!commands || commands.length === 0) {\n throw new ValidationError(\"No commands configured.\");\n }\n\n const result = await executeCommandPipeline(options.rootDir, commands, task);\n return reply.send(result);\n } catch (error) {\n sendJsonError(reply, error);\n }\n });\n\n app.get(\"/api/events\", async (_request, reply) => {\n reply.raw.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache, no-transform\",\n Connection: \"keep-alive\"\n });\n reply.raw.write(\"\\n\");\n\n const listener = {\n send(payload: string) {\n reply.raw.write(`data: ${payload}\\n\\n`);\n },\n close() {\n reply.raw.end();\n }\n };\n\n listeners.add(listener);\n reply.raw.on(\"close\", () => {\n listeners.delete(listener);\n });\n\n return reply.hijack();\n });\n\n const watcher = chokidar.watch(options.rootDir, {\n ignoreInitial: true,\n ignored: (watchPath) => watchPath.includes(`${path.sep}.git`) || watchPath.includes(`${path.sep}node_modules`)\n });\n\n watcher.on(\"all\", (eventName, changedPath) => {\n const isMarkdown = changedPath.endsWith(\".md\") || changedPath.endsWith(\".markdown\");\n const isConfigFile = path.basename(changedPath) === \".md-task-viewer.json\";\n if (!isMarkdown && !isConfigFile) {\n return;\n }\n\n const payload = JSON.stringify({\n type: \"tasks-changed\",\n eventName,\n path: path.relative(options.rootDir, changedPath)\n });\n\n for (const listener of listeners) {\n listener.send(payload);\n }\n });\n\n app.addHook(\"onClose\", async () => {\n await watcher.close();\n });\n\n if (clientDir) {\n await app.register(fastifyStatic, {\n root: clientDir,\n prefix: \"/\"\n });\n\n app.setNotFoundHandler(async (request, reply) => {\n if (request.raw.url?.startsWith(\"/api/\")) {\n return reply.code(404).send({ error: \"Not found\" });\n }\n return reply.sendFile(\"index.html\");\n });\n }\n\n return app;\n}\n","import matter from \"gray-matter\";\nimport picomatch from \"picomatch\";\nimport path from \"node:path\";\nimport { promises as fs } from \"node:fs\";\nimport {\n CONFIG_FILE_NAME,\n type CommandStep,\n type ConfigFile,\n type CreateTaskInput,\n type PatchTaskFieldsInput,\n type TaskFrontmatter,\n type TaskListResponse,\n type TaskParseError,\n type TaskPriority,\n type TaskRecord,\n type TaskStatus,\n type UpdateTaskInput\n} from \"./types.js\";\nimport { slugify } from \"./slugify.js\";\n\nconst MARKDOWN_EXTENSIONS = new Set([\".md\", \".markdown\"]);\nconst REQUIRED_PRIORITY: TaskPriority[] = [\"MUST\", \"WANT\"];\nconst REQUIRED_STATUS: TaskStatus[] = [\"TODO\", \"WIP\", \"DONE\"];\n\nexport class ConflictError extends Error {}\nexport class ValidationError extends Error {}\n\nfunction toPosixPath(filePath: string): string {\n return filePath.split(path.sep).join(\"/\");\n}\n\nfunction normalizeRelativePath(candidate: string): string {\n const normalized = toPosixPath(path.posix.normalize(candidate.trim()));\n if (!normalized || normalized === \".\" || normalized.startsWith(\"../\") || normalized.includes(\"/../\")) {\n throw new ValidationError(\"Path must stay within the workspace root.\");\n }\n\n return normalized.replace(/^\\.\\/+/, \"\");\n}\n\nfunction ensureMarkdownExtension(filePath: string): string {\n return path.posix.extname(filePath) ? filePath : `${filePath}.md`;\n}\n\nfunction asUtcISOString(date: Date): string {\n return date.toISOString();\n}\n\nfunction buildDefaults(filePath: string, stats: { birthtime: Date; mtime: Date }): TaskFrontmatter {\n const basename = path.basename(filePath, path.extname(filePath));\n const title = basename.replace(/[-_]+/g, \" \").replace(/\\b\\w/g, (char) => char.toUpperCase());\n\n return {\n title,\n priority: \"WANT\",\n status: \"TODO\",\n createdAt: asUtcISOString(stats.birthtime),\n updatedAt: asUtcISOString(stats.mtime)\n };\n}\n\nfunction splitFrontmatter(data: Record<string, unknown>, statsDefaults: TaskFrontmatter): {\n frontmatter: TaskFrontmatter;\n extraFrontmatter: Record<string, unknown>;\n normalized: boolean;\n} {\n const extraFrontmatter: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(data)) {\n if (![\"title\", \"priority\", \"status\", \"createdAt\", \"updatedAt\"].includes(key)) {\n extraFrontmatter[key] = value;\n }\n }\n\n const title = typeof data.title === \"string\" && data.title.trim() ? data.title : statsDefaults.title;\n const priority = REQUIRED_PRIORITY.includes(data.priority as TaskPriority)\n ? (data.priority as TaskPriority)\n : statsDefaults.priority;\n const status = REQUIRED_STATUS.includes(data.status as TaskStatus)\n ? (data.status as TaskStatus)\n : statsDefaults.status;\n const createdAt =\n typeof data.createdAt === \"string\" && !Number.isNaN(Date.parse(data.createdAt))\n ? new Date(data.createdAt).toISOString()\n : statsDefaults.createdAt;\n const updatedAt =\n typeof data.updatedAt === \"string\" && !Number.isNaN(Date.parse(data.updatedAt))\n ? new Date(data.updatedAt).toISOString()\n : statsDefaults.updatedAt;\n\n const normalized =\n title !== data.title ||\n priority !== data.priority ||\n status !== data.status ||\n createdAt !== data.createdAt ||\n updatedAt !== data.updatedAt;\n\n return {\n frontmatter: { title, priority, status, createdAt, updatedAt },\n extraFrontmatter,\n normalized\n };\n}\n\nexport function serializeTask(record: TaskRecord): string {\n const data = {\n ...record.extraFrontmatter,\n title: record.frontmatter.title,\n priority: record.frontmatter.priority,\n status: record.frontmatter.status,\n createdAt: record.frontmatter.createdAt,\n updatedAt: record.frontmatter.updatedAt\n };\n\n return matter.stringify(record.content, data);\n}\n\nasync function readDirectoryRecursive(rootDir: string, currentDir: string, results: string[]): Promise<void> {\n const entries = await fs.readdir(currentDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.name === \".git\" || entry.name === \"node_modules\") {\n continue;\n }\n\n const absolutePath = path.join(currentDir, entry.name);\n if (entry.isDirectory()) {\n await readDirectoryRecursive(rootDir, absolutePath, results);\n continue;\n }\n\n if (entry.name === CONFIG_FILE_NAME) {\n continue;\n }\n\n if (!MARKDOWN_EXTENSIONS.has(path.extname(entry.name).toLowerCase())) {\n continue;\n }\n\n results.push(toPosixPath(path.relative(rootDir, absolutePath)));\n }\n}\n\nasync function listMarkdownFiles(rootDir: string, taskDirs: string[], ignorePaths: string[]): Promise<string[]> {\n const results: string[] = [];\n const seen = new Set<string>();\n\n const isIgnored = ignorePaths.length > 0 ? picomatch(ignorePaths) : null;\n\n for (const taskDir of taskDirs) {\n const scanDir = path.resolve(rootDir, taskDir);\n try {\n await fs.access(scanDir);\n } catch {\n continue;\n }\n const dirResults: string[] = [];\n await readDirectoryRecursive(rootDir, scanDir, dirResults);\n for (const filePath of dirResults) {\n if (!seen.has(filePath)) {\n seen.add(filePath);\n if (isIgnored && isIgnored(filePath)) {\n continue;\n }\n results.push(filePath);\n }\n }\n }\n\n return results.sort();\n}\n\nexport async function parseTask(rootDir: string, relativePath: string): Promise<TaskRecord> {\n const absolutePath = path.join(rootDir, relativePath);\n const raw = await fs.readFile(absolutePath, \"utf8\");\n const stats = await fs.stat(absolutePath);\n const parsed = matter(raw);\n const defaults = buildDefaults(relativePath, stats);\n const { frontmatter, extraFrontmatter, normalized } = splitFrontmatter(parsed.data, defaults);\n\n return {\n path: toPosixPath(relativePath),\n content: parsed.content,\n frontmatter,\n extraFrontmatter,\n raw,\n normalized\n };\n}\n\nexport async function readConfig(rootDir: string): Promise<ConfigFile> {\n const configFilePath = path.join(rootDir, CONFIG_FILE_NAME);\n\n try {\n const raw = await fs.readFile(configFilePath, \"utf8\");\n const parsed = JSON.parse(raw) as Partial<ConfigFile>;\n const taskDirs = Array.isArray(parsed.taskDirs)\n ? parsed.taskDirs.filter((item): item is string => typeof item === \"string\")\n : [\".\"];\n const ignorePaths = Array.isArray(parsed.ignorePaths)\n ? parsed.ignorePaths.filter((item): item is string => typeof item === \"string\")\n : [];\n const order = Array.isArray(parsed.order)\n ? parsed.order.filter((item): item is string => typeof item === \"string\")\n : [];\n const commands = Array.isArray(parsed.commands)\n ? (parsed.commands as CommandStep[])\n : undefined;\n return { version: parsed.version ?? 1, taskDirs, ignorePaths, order, commands };\n } catch (error) {\n const maybeError = error as NodeJS.ErrnoException;\n if (maybeError.code !== \"ENOENT\") {\n throw error;\n }\n return { version: 1, taskDirs: [\".\"], ignorePaths: [], order: [] };\n }\n}\n\nasync function reconcileOrder(rootDir: string, taskPaths: string[]): Promise<{ order: string[]; changed: boolean }> {\n const config = await readConfig(rootDir);\n const order = config.order;\n\n const known = new Set(taskPaths);\n const nextOrder = order.filter((item) => known.has(item));\n for (const taskPath of taskPaths) {\n if (!nextOrder.includes(taskPath)) {\n nextOrder.push(taskPath);\n }\n }\n\n const changed = nextOrder.length !== order.length || nextOrder.some((item, index) => item !== order[index]);\n return { order: nextOrder, changed };\n}\n\nexport async function saveOrder(rootDir: string, order: string[]): Promise<void> {\n const normalized = Array.from(\n new Set(\n order.map((item) => ensureMarkdownExtension(normalizeRelativePath(item)))\n )\n );\n const existing = await readConfig(rootDir);\n const payload: ConfigFile = { version: 1, taskDirs: existing.taskDirs, ignorePaths: existing.ignorePaths, order: normalized, commands: existing.commands };\n await fs.writeFile(path.join(rootDir, CONFIG_FILE_NAME), `${JSON.stringify(payload, null, 2)}\\n`, \"utf8\");\n}\n\nexport async function saveConfig(rootDir: string, taskDirs: string[], ignorePaths?: string[], commands?: CommandStep[]): Promise<ConfigFile> {\n const validated = taskDirs.map((dir) => {\n const normalized = dir.trim().replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\") || \".\";\n if (normalized.startsWith(\"../\") || normalized.includes(\"/../\")) {\n throw new ValidationError(\"taskDirs must stay within the workspace root.\");\n }\n return normalized;\n });\n if (validated.length === 0) {\n throw new ValidationError(\"taskDirs must contain at least one directory.\");\n }\n const existing = await readConfig(rootDir);\n const validatedIgnorePaths = ignorePaths ?? existing.ignorePaths;\n const validatedCommands = commands !== undefined ? commands : existing.commands;\n const payload: ConfigFile = { version: 1, taskDirs: validated, ignorePaths: validatedIgnorePaths, order: existing.order, commands: validatedCommands };\n await fs.writeFile(path.join(rootDir, CONFIG_FILE_NAME), `${JSON.stringify(payload, null, 2)}\\n`, \"utf8\");\n return payload;\n}\n\nexport async function listTasks(rootDir: string): Promise<TaskListResponse> {\n const config = await readConfig(rootDir);\n const files = await listMarkdownFiles(rootDir, config.taskDirs, config.ignorePaths);\n const errors: TaskParseError[] = [];\n const tasks = await Promise.all(\n files.map(async (relativePath) => {\n try {\n return await parseTask(rootDir, relativePath);\n } catch (error) {\n errors.push({\n path: relativePath,\n message: error instanceof Error ? error.message : \"Unknown parse error\"\n });\n return null;\n }\n })\n );\n\n const taskRecords = tasks.filter((task): task is TaskRecord => task !== null);\n const { order, changed } = await reconcileOrder(\n rootDir,\n taskRecords.map((task) => task.path)\n );\n\n if (changed) {\n await saveOrder(rootDir, order);\n }\n\n const orderIndex = new Map(order.map((item, index) => [item, index]));\n taskRecords.sort((left, right) => {\n const leftIndex = orderIndex.get(left.path) ?? Number.MAX_SAFE_INTEGER;\n const rightIndex = orderIndex.get(right.path) ?? Number.MAX_SAFE_INTEGER;\n return leftIndex - rightIndex || left.path.localeCompare(right.path);\n });\n\n return { tasks: taskRecords, errors };\n}\n\nasync function ensureDirectoryForFile(rootDir: string, relativeFilePath: string): Promise<string> {\n const normalized = ensureMarkdownExtension(normalizeRelativePath(relativeFilePath));\n const absolutePath = path.join(rootDir, normalized);\n const directory = path.dirname(absolutePath);\n await fs.mkdir(directory, { recursive: true });\n return normalized;\n}\n\nasync function nextAvailablePath(rootDir: string, directory: string, title: string): Promise<string> {\n const safeDirectory = directory ? normalizeRelativePath(directory) : \"\";\n const slug = slugify(title);\n const base = safeDirectory ? `${safeDirectory}/${slug}` : slug;\n\n let attempt = 0;\n while (true) {\n const candidate = ensureMarkdownExtension(attempt === 0 ? base : `${base}-${attempt + 1}`);\n try {\n await fs.access(path.join(rootDir, candidate));\n attempt += 1;\n } catch {\n return candidate;\n }\n }\n}\n\nexport async function createTask(rootDir: string, input: CreateTaskInput): Promise<TaskRecord> {\n if (!input.title.trim()) {\n throw new ValidationError(\"Title is required.\");\n }\n\n const now = asUtcISOString(new Date());\n const relativePath = input.path?.trim()\n ? await ensureDirectoryForFile(rootDir, input.path)\n : await nextAvailablePath(rootDir, input.directory ?? \"\", input.title);\n const absolutePath = path.join(rootDir, relativePath);\n\n try {\n await fs.access(absolutePath);\n } catch (error) {\n const maybeError = error as NodeJS.ErrnoException;\n if (maybeError.code === \"ENOENT\") {\n // The target path is available.\n } else if (maybeError.code) {\n throw error;\n } else {\n throw new ValidationError(\"A task already exists at that path.\");\n }\n }\n\n const record: TaskRecord = {\n path: relativePath,\n content: input.content ?? \"\",\n raw: \"\",\n normalized: false,\n extraFrontmatter: input.extraFrontmatter ?? {},\n frontmatter: {\n title: input.title.trim(),\n priority: input.priority ?? \"MUST\",\n status: input.status ?? \"TODO\",\n createdAt: now,\n updatedAt: now\n }\n };\n\n await fs.mkdir(path.dirname(absolutePath), { recursive: true });\n await fs.writeFile(absolutePath, serializeTask(record), \"utf8\");\n\n const current = await listTasks(rootDir);\n await saveOrder(rootDir, current.tasks.map((task) => task.path).concat(relativePath));\n return parseTask(rootDir, relativePath);\n}\n\nexport async function updateTask(rootDir: string, currentPath: string, input: UpdateTaskInput): Promise<TaskRecord> {\n const normalizedCurrentPath = ensureMarkdownExtension(normalizeRelativePath(currentPath));\n const absoluteCurrentPath = path.join(rootDir, normalizedCurrentPath);\n\n let existing: TaskRecord;\n try {\n existing = await parseTask(rootDir, normalizedCurrentPath);\n } catch (error) {\n const maybeError = error as NodeJS.ErrnoException;\n if (maybeError.code === \"ENOENT\") {\n throw new ConflictError(\"The task no longer exists.\");\n }\n throw error;\n }\n\n if (input.baseUpdatedAt && existing.frontmatter.updatedAt !== input.baseUpdatedAt) {\n throw new ConflictError(\"The task changed on disk. Reload before saving.\");\n }\n\n const nextPath = input.path?.trim()\n ? await ensureDirectoryForFile(rootDir, input.path)\n : normalizedCurrentPath;\n const absoluteNextPath = path.join(rootDir, nextPath);\n\n if (nextPath !== normalizedCurrentPath) {\n try {\n await fs.access(absoluteNextPath);\n } catch (error) {\n const maybeError = error as NodeJS.ErrnoException;\n if (maybeError.code === \"ENOENT\") {\n // The target path is available.\n } else if (maybeError.code) {\n throw error;\n } else {\n throw new ValidationError(\"A task already exists at the target path.\");\n }\n }\n }\n\n const record: TaskRecord = {\n path: nextPath,\n raw: existing.raw,\n normalized: false,\n content: input.content,\n extraFrontmatter: input.extraFrontmatter ?? existing.extraFrontmatter,\n frontmatter: {\n title: input.title.trim(),\n priority: input.priority,\n status: input.status,\n createdAt: existing.frontmatter.createdAt,\n updatedAt: asUtcISOString(new Date())\n }\n };\n\n await fs.writeFile(absoluteCurrentPath, serializeTask(record), \"utf8\");\n if (nextPath !== normalizedCurrentPath) {\n await fs.mkdir(path.dirname(absoluteNextPath), { recursive: true });\n await fs.rename(absoluteCurrentPath, absoluteNextPath);\n }\n\n const current = await listTasks(rootDir);\n const updatedOrder = current.tasks.map((task) => task.path);\n await saveOrder(rootDir, updatedOrder);\n return parseTask(rootDir, nextPath);\n}\n\nexport async function deleteTask(rootDir: string, relativePath: string): Promise<void> {\n const normalizedPath = ensureMarkdownExtension(normalizeRelativePath(relativePath));\n const absolutePath = path.join(rootDir, normalizedPath);\n\n try {\n await fs.unlink(absolutePath);\n } catch (error) {\n const maybeError = error as NodeJS.ErrnoException;\n if (maybeError.code === \"ENOENT\") {\n throw new ConflictError(\"The task no longer exists.\");\n }\n throw error;\n }\n\n const current = await listTasks(rootDir);\n await saveOrder(\n rootDir,\n current.tasks.map((task) => task.path)\n );\n}\n\nexport async function patchTaskFields(rootDir: string, currentPath: string, input: PatchTaskFieldsInput): Promise<TaskRecord> {\n const normalizedCurrentPath = ensureMarkdownExtension(normalizeRelativePath(currentPath));\n const absoluteCurrentPath = path.join(rootDir, normalizedCurrentPath);\n\n let existing: TaskRecord;\n try {\n existing = await parseTask(rootDir, normalizedCurrentPath);\n } catch (error) {\n const maybeError = error as NodeJS.ErrnoException;\n if (maybeError.code === \"ENOENT\") {\n throw new ConflictError(\"The task no longer exists.\");\n }\n throw error;\n }\n\n const priority = input.priority && REQUIRED_PRIORITY.includes(input.priority) ? input.priority : existing.frontmatter.priority;\n const status = input.status && REQUIRED_STATUS.includes(input.status) ? input.status : existing.frontmatter.status;\n\n if (priority === existing.frontmatter.priority && status === existing.frontmatter.status) {\n return existing;\n }\n\n const record: TaskRecord = {\n path: normalizedCurrentPath,\n raw: existing.raw,\n normalized: false,\n content: existing.content,\n extraFrontmatter: existing.extraFrontmatter,\n frontmatter: {\n ...existing.frontmatter,\n priority,\n status,\n updatedAt: asUtcISOString(new Date())\n }\n };\n\n await fs.writeFile(absoluteCurrentPath, serializeTask(record), \"utf8\");\n return parseTask(rootDir, normalizedCurrentPath);\n}\n\nexport function parseOrderPayload(input: unknown): string[] {\n if (!Array.isArray(input)) {\n throw new ValidationError(\"Order payload must be an array.\");\n }\n\n return input.map((item) => ensureMarkdownExtension(normalizeRelativePath(String(item))));\n}\n\nexport async function readOrder(rootDir: string): Promise<ConfigFile> {\n const config = await readConfig(rootDir);\n const { order } = await reconcileOrder(\n rootDir,\n (await listTasks(rootDir)).tasks.map((task) => task.path)\n );\n return { version: 1, taskDirs: config.taskDirs, ignorePaths: config.ignorePaths, order, commands: config.commands };\n}\n\nexport const taskStoreUtils = {\n slugify,\n normalizeRelativePath,\n ensureMarkdownExtension,\n splitFrontmatter,\n buildDefaults\n};\n","export const CONFIG_FILE_NAME = \".md-task-viewer.json\";\n\nexport type TaskPriority = \"MUST\" | \"WANT\";\nexport type TaskStatus = \"TODO\" | \"WIP\" | \"DONE\";\n\nexport interface TaskFrontmatter {\n title: string;\n priority: TaskPriority;\n status: TaskStatus;\n createdAt: string;\n updatedAt: string;\n [key: string]: unknown;\n}\n\nexport interface TaskRecord {\n path: string;\n content: string;\n frontmatter: TaskFrontmatter;\n extraFrontmatter: Record<string, unknown>;\n raw: string;\n normalized: boolean;\n}\n\nexport interface TaskParseError {\n path: string;\n message: string;\n}\n\nexport interface TaskListResponse {\n tasks: TaskRecord[];\n errors: TaskParseError[];\n}\n\nexport interface CreateTaskInput {\n title: string;\n priority?: TaskPriority;\n status?: TaskStatus;\n content?: string;\n directory?: string;\n path?: string;\n extraFrontmatter?: Record<string, unknown>;\n}\n\nexport interface UpdateTaskInput {\n path?: string;\n title: string;\n priority: TaskPriority;\n status: TaskStatus;\n content: string;\n extraFrontmatter?: Record<string, unknown>;\n baseUpdatedAt?: string;\n}\n\nexport interface PatchTaskFieldsInput {\n priority?: TaskPriority;\n status?: TaskStatus;\n}\n\nexport interface CommandStep {\n command: string;\n passBody?: \"arg\" | \"stdin\" | false;\n}\n\nexport interface CommandExecutionResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n duration: number;\n}\n\nexport interface ConfigFile {\n version: number;\n taskDirs: string[];\n ignorePaths: string[];\n order: string[];\n commands?: CommandStep[];\n}\n","export function slugify(value: string): string {\n const slug = value\n // Unicode結合文字列を正規合成形に統一(例: か+濁点 → が)\n .normalize(\"NFC\")\n // 半角・全角スペース(U+3000)など空白文字をハイフンに変換。近年では \\s は U+3000 にもマッチするが、明示的に記載\n .replace(/[\\s\\u3000]+/g, \"-\")\n // Unicode文字(L)・数字(N)・ハイフン以外を除去(記号や句読点など)\n .replace(/[^\\p{L}\\p{N}-]+/gu, \"\")\n // 先頭・末尾の余分なハイフンを除去\n .replace(/^-+|-+$/g, \"\");\n return slug || \"untitled-task\";\n}\n","import { spawn } from \"node:child_process\";\nimport path from \"node:path\";\nimport type { CommandStep, CommandExecutionResult, TaskRecord } from \"./types.js\";\n\nconst TIMEOUT_MS = 30_000;\n\nconst VARIABLE_PATTERN = /\\$\\{?(TASK_TITLE|TASK_FILEPATH|TASK_BODY)\\}?/g;\n\nexport function substituteVariables(command: string, vars: Record<string, string>): string {\n return command.replace(VARIABLE_PATTERN, (_match, name: string) => vars[name] ?? \"\");\n}\n\nexport async function executeCommandPipeline(\n rootDir: string,\n steps: CommandStep[],\n task: TaskRecord\n): Promise<CommandExecutionResult> {\n if (steps.length === 0) {\n return { stdout: \"\", stderr: \"\", exitCode: 0, duration: 0 };\n }\n\n const absoluteFilePath = path.resolve(rootDir, task.path);\n const vars: Record<string, string> = {\n TASK_TITLE: task.frontmatter.title,\n TASK_FILEPATH: absoluteFilePath,\n TASK_BODY: task.content\n };\n\n const startTime = Date.now();\n\n return new Promise<CommandExecutionResult>((resolve) => {\n const resolvedCommands = steps.map((step, index) => {\n let cmd = substituteVariables(step.command, vars);\n if (index === 0 && step.passBody === \"arg\") {\n const escaped = task.content.replace(/'/g, \"'\\\\''\");\n cmd = `${cmd} '${escaped}'`;\n }\n return cmd;\n });\n\n const processes: ReturnType<typeof spawn>[] = [];\n for (let index = 0; index < resolvedCommands.length; index++) {\n const proc = spawn(resolvedCommands[index], {\n shell: true,\n cwd: rootDir,\n stdio: [\"pipe\", \"pipe\", \"pipe\"]\n });\n\n // pipe stdout of previous process to stdin of current\n if (index > 0) {\n const prev = processes[index - 1];\n if (prev?.stdout) {\n prev.stdout.pipe(proc.stdin!);\n }\n }\n\n processes.push(proc);\n }\n\n // Handle first command's passBody option\n const firstStep = steps[0];\n const firstProc = processes[0];\n if (firstStep.passBody === \"stdin\" && firstProc.stdin) {\n firstProc.stdin.write(task.content);\n }\n // Always close first process stdin (body written above if passBody is stdin)\n firstProc.stdin?.end();\n\n const lastProc = processes[processes.length - 1];\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n\n lastProc.stdout?.on(\"data\", (chunk: Buffer) => stdoutChunks.push(chunk));\n lastProc.stderr?.on(\"data\", (chunk: Buffer) => stderrChunks.push(chunk));\n\n // Collect stderr from all intermediate processes too\n for (let i = 0; i < processes.length - 1; i++) {\n processes[i].stderr?.on(\"data\", (chunk: Buffer) => stderrChunks.push(chunk));\n }\n\n const timeout = setTimeout(() => {\n for (const proc of processes) {\n proc.kill(\"SIGTERM\");\n }\n }, TIMEOUT_MS);\n\n lastProc.on(\"close\", (code) => {\n clearTimeout(timeout);\n resolve({\n stdout: Buffer.concat(stdoutChunks).toString(\"utf8\"),\n stderr: Buffer.concat(stderrChunks).toString(\"utf8\"),\n exitCode: code ?? 1,\n duration: Date.now() - startTime\n });\n });\n\n lastProc.on(\"error\", (err) => {\n clearTimeout(timeout);\n resolve({\n stdout: \"\",\n stderr: err.message,\n exitCode: 1,\n duration: Date.now() - startTime\n });\n });\n\n // Handle errors on intermediate processes\n for (let i = 0; i < processes.length - 1; i++) {\n processes[i].on(\"error\", (err) => {\n stderrChunks.push(Buffer.from(err.message));\n });\n }\n });\n}\n"],"mappings":"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","names":["path","path","path"]}
|