@tomkapa/tayto 0.8.2 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -140,6 +140,67 @@ Or from the Claude Code marketplace:
140
140
 
141
141
  ---
142
142
 
143
+ ## Analytics
144
+
145
+ Two read-only commands expose productivity metrics over a rolling time window. Designed to be consumed by a scheduled agent (e.g. Claude Cowork) at a set interval.
146
+
147
+ ### `tayto analytic summary`
148
+
149
+ Returns a JSON productivity summary for the given period.
150
+
151
+ ```
152
+ tayto analytic summary --period <day|week> [-p <project>]
153
+ ```
154
+
155
+ **Sample output:**
156
+
157
+ ```json
158
+ {
159
+ "ok": true,
160
+ "data": {
161
+ "period": "day",
162
+ "windowStart": "2026-04-22T06:00:00.000Z",
163
+ "windowEnd": "2026-04-23T06:00:00.000Z",
164
+ "completed": { "total": 3, "byType": { "story": 2, "bug": 1, "tech-debt": 0, "release": 0 } },
165
+ "created": { "total": 5, "byType": { "story": 4, "bug": 1, "tech-debt": 0, "release": 0 } },
166
+ "current": { "total": 12, "byStatus": { "backlog": 5, "todo": 3, "in-progress": 2, "review": 1, "done": 1, "cancelled": 0 }, "byType": { ... } },
167
+ "backlogDelta": 2,
168
+ "throughputPerDay": 3
169
+ }
170
+ }
171
+ ```
172
+
173
+ - `backlogDelta` = `created.total − completed.total` (positive means backlog is growing)
174
+ - `throughputPerDay` = `completed.total / periodDays`
175
+ - All `byType` and `byStatus` maps always include every key (zero-filled) for stable agent parsing
176
+
177
+ > **Caveat:** `completed` counts tasks whose `updated_at` falls within the window. Because any edit bumps `updated_at`, a done task edited later will reappear. A proper `completed_at` column is tracked as a separate tech-debt item.
178
+
179
+ ### `tayto analytic completed`
180
+
181
+ Returns the raw list of completed tasks within a rolling window.
182
+
183
+ ```
184
+ tayto analytic completed --since <duration> [-p <project>]
185
+ ```
186
+
187
+ Duration format: `<positive integer><unit>` where unit is `m` (minutes), `h` (hours), `d` (days), or `w` (weeks). Examples: `24h`, `7d`, `2w`. Maximum: `365d`.
188
+
189
+ **Sample output:**
190
+
191
+ ```json
192
+ {
193
+ "ok": true,
194
+ "data": [
195
+ { "id": "PROJ-12", "name": "Fix auth bug", "type": "bug", "status": "done", "updatedAt": "2026-04-23T05:12:00.000Z", ... }
196
+ ]
197
+ }
198
+ ```
199
+
200
+ Results are ordered by `updated_at DESC`. Invalid `--since` values return a VALIDATION error on stderr with exit code 1.
201
+
202
+ ---
203
+
143
204
  ## Configuration
144
205
 
145
206
  | Variable | Default | Description |
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // <define:__CHANGELOG_ENTRIES__>
4
- var define_CHANGELOG_ENTRIES_default = [{ version: "0.8.1", date: "2026-04-14", sections: [{ heading: "Changed", items: ["Lower minimum Node.js requirement from >=25 to >=22"] }] }, { version: "0.8.0", date: "2026-04-13", sections: [{ heading: "Added", items: ["What's new ticker and changelog dialog for post-upgrade visibility"] }] }, { version: "0.7.0", date: "2026-04-12", sections: [{ heading: "Added", items: ["Prompt to create project when unlinked git remote is detected on TUI startup", "Project edit support in TUI and `--git-remote` flag for CLI", "Git remote field to create project form", "Arrow key navigation and inline cursor movement in form fields"] }, { heading: "Changed", items: ["Introduce `GitRemote` value object to enforce URL normalization"] }, { heading: "Fixed", items: ["Restore terminal raw mode around external editor to prevent readonly mode"] }] }, { version: "0.6.0", date: "2026-04-12", sections: [{ heading: "Added", items: ["Auto-upgrade with npm registry version check and `tayto upgrade` command", "Contextual hints in TUI for discoverability", "Shift+Tab panel navigation in TUI"] }, { heading: "Changed", items: ["Improve TUI intuitiveness with better keyboard shortcut guidance"] }, { heading: "Chore", items: ["Add open-source project scaffolding and fix CI node version", "Add demo video to README"] }] }, { version: "0.5.0", date: "2026-04-10", sections: [{ heading: "Changed", items: ["Redesign TUI color theme around #9B9BA5 (logo helmet grey) for consistent palette", "Redesign header with 3-column layout: logo, product info, and shortcut hints", "Integrate Tayto pixel logo into TUI header bar"] }, { heading: "Improved", items: ["Consolidate logo assets into `src/tui/assets/` and `src/tui/components/Logo.tsx`", "Theme colors now import from logo PALETTE for single source of truth", "Memoize Logo component to avoid unnecessary re-renders"] }, { heading: "Removed", items: ["Standalone `tayto-logo/` directory (AGENT.md, preview PNGs)"] }] }, { version: "0.4.1", date: "2026-04-10", sections: [{ heading: "Fixed", items: ["Prevent rank collisions from floating-point precision collapse", "Scope `listTasks` and `searchTasks` to default project when none specified"] }, { heading: "Changed", items: ["Resolve project at CLI/TUI boundary; services now accept `Project` directly"] }] }, { version: "0.4.0", date: "2026-04-09", sections: [{ heading: "Added", items: ["Link projects to git remotes for automatic project selection", "Running `tayto` inside a git repo auto-selects the linked project"] }] }, { version: "0.3.0", date: null, sections: [{ heading: "Added", items: ["Dependency management (`tayto dep add/remove/list/graph`)", "Dependency types: `blocks`, `blocked-by`, `relates-to`, `duplicates`", "Task breakdown command to split tasks into subtasks"] }] }, { version: "0.2.0", date: null, sections: [{ heading: "Added", items: ["TUI (terminal UI) built with Ink/React", "Full-text search via SQLite FTS5", "Task export and import (JSON)", "OpenTelemetry tracing on every operation"] }] }, { version: "0.1.0", date: null, sections: [{ heading: "Added", items: ["Initial release", "CLI with Commander.js", "SQLite database backend with ULID identifiers", "Fractional ranking for O(1) reorders", "Task types: `story`, `bug`, `tech-debt`", "Task statuses: `backlog` \u2192 `todo` \u2192 `in-progress` \u2192 `review` \u2192 `done` / `cancelled`", "Project management with default project support", "Zod validation across all service layer inputs"] }] }];
4
+ var define_CHANGELOG_ENTRIES_default = [{ version: "0.9.0", date: "2026-04-26", sections: [{ heading: "Added", items: ["Analytic commands for agent productivity reporting (`tayto analytics`)", "Settings tab with tab bar navigation infrastructure", "Show hovered release in detail panel when release panel is focused", "Enter key on release panel opens full task detail view for that release"] }, { heading: "Changed", items: ['Renamed "epic" to "release" throughout codebase and UI', "Repository boundary with branded IDs and per-module error unions"] }, { heading: "Fixed", items: ["Show release name instead of raw task ID in detail panel parent field", "Highlight task name in detail panel with accent color across all wrapped lines", "Prevent task detail overflow from clobbering header and overlapping rows", "Align task table columns and truncate long names to prevent flickering", "Add `--detach-parent` flag to `task update` CLI command"] }] }, { version: "0.8.1", date: "2026-04-14", sections: [{ heading: "Changed", items: ["Lower minimum Node.js requirement from >=25 to >=22"] }] }, { version: "0.8.0", date: "2026-04-13", sections: [{ heading: "Added", items: ["What's new ticker and changelog dialog for post-upgrade visibility"] }] }, { version: "0.7.0", date: "2026-04-12", sections: [{ heading: "Added", items: ["Prompt to create project when unlinked git remote is detected on TUI startup", "Project edit support in TUI and `--git-remote` flag for CLI", "Git remote field to create project form", "Arrow key navigation and inline cursor movement in form fields"] }, { heading: "Changed", items: ["Introduce `GitRemote` value object to enforce URL normalization"] }, { heading: "Fixed", items: ["Restore terminal raw mode around external editor to prevent readonly mode"] }] }, { version: "0.6.0", date: "2026-04-12", sections: [{ heading: "Added", items: ["Auto-upgrade with npm registry version check and `tayto upgrade` command", "Contextual hints in TUI for discoverability", "Shift+Tab panel navigation in TUI"] }, { heading: "Changed", items: ["Improve TUI intuitiveness with better keyboard shortcut guidance"] }, { heading: "Chore", items: ["Add open-source project scaffolding and fix CI node version", "Add demo video to README"] }] }, { version: "0.5.0", date: "2026-04-10", sections: [{ heading: "Changed", items: ["Redesign TUI color theme around #9B9BA5 (logo helmet grey) for consistent palette", "Redesign header with 3-column layout: logo, product info, and shortcut hints", "Integrate Tayto pixel logo into TUI header bar"] }, { heading: "Improved", items: ["Consolidate logo assets into `src/tui/assets/` and `src/tui/components/Logo.tsx`", "Theme colors now import from logo PALETTE for single source of truth", "Memoize Logo component to avoid unnecessary re-renders"] }, { heading: "Removed", items: ["Standalone `tayto-logo/` directory (AGENT.md, preview PNGs)"] }] }, { version: "0.4.1", date: "2026-04-10", sections: [{ heading: "Fixed", items: ["Prevent rank collisions from floating-point precision collapse", "Scope `listTasks` and `searchTasks` to default project when none specified"] }, { heading: "Changed", items: ["Resolve project at CLI/TUI boundary; services now accept `Project` directly"] }] }, { version: "0.4.0", date: "2026-04-09", sections: [{ heading: "Added", items: ["Link projects to git remotes for automatic project selection", "Running `tayto` inside a git repo auto-selects the linked project"] }] }, { version: "0.3.0", date: null, sections: [{ heading: "Added", items: ["Dependency management (`tayto dep add/remove/list/graph`)", "Dependency types: `blocks`, `blocked-by`, `relates-to`, `duplicates`", "Task breakdown command to split tasks into subtasks"] }] }, { version: "0.2.0", date: null, sections: [{ heading: "Added", items: ["TUI (terminal UI) built with Ink/React", "Full-text search via SQLite FTS5", "Task export and import (JSON)", "OpenTelemetry tracing on every operation"] }] }, { version: "0.1.0", date: null, sections: [{ heading: "Added", items: ["Initial release", "CLI with Commander.js", "SQLite database backend with ULID identifiers", "Fractional ranking for O(1) reorders", "Task types: `story`, `bug`, `tech-debt`", "Task statuses: `backlog` \u2192 `todo` \u2192 `in-progress` \u2192 `review` \u2192 `done` / `cancelled`", "Project management with default project support", "Zod validation across all service layer inputs"] }] }];
5
5
 
6
6
  // src/logging/logger.ts
7
7
  import { appendFileSync, readdirSync, unlinkSync } from "fs";
@@ -92,62 +92,7 @@ var Logger = class {
92
92
  var logger = new Logger();
93
93
 
94
94
  // src/version.ts
95
- var APP_VERSION = true ? "0.8.2" : "0.0.0-dev";
96
-
97
- // src/types/enums.ts
98
- var TaskStatus = {
99
- Backlog: "backlog",
100
- Todo: "todo",
101
- InProgress: "in-progress",
102
- Review: "review",
103
- Done: "done",
104
- Cancelled: "cancelled"
105
- };
106
- var TaskType = {
107
- Epic: "epic",
108
- Story: "story",
109
- TechDebt: "tech-debt",
110
- Bug: "bug"
111
- };
112
- var TaskLevel = {
113
- Epic: 1,
114
- Work: 2
115
- };
116
- var TYPE_TO_LEVEL = {
117
- [TaskType.Epic]: TaskLevel.Epic,
118
- [TaskType.Story]: TaskLevel.Work,
119
- [TaskType.TechDebt]: TaskLevel.Work,
120
- [TaskType.Bug]: TaskLevel.Work
121
- };
122
- function getTaskLevel(type) {
123
- return TYPE_TO_LEVEL[type] ?? TaskLevel.Work;
124
- }
125
- var WORK_TYPES = /* @__PURE__ */ new Set([
126
- TaskType.Story,
127
- TaskType.TechDebt,
128
- TaskType.Bug
129
- ]);
130
- var DependencyType = {
131
- Blocks: "blocks",
132
- RelatesTo: "relates-to",
133
- Duplicates: "duplicates"
134
- };
135
- var UIDependencyType = {
136
- ...DependencyType,
137
- BlockedBy: "blocked-by"
138
- };
139
- var RANK_GAP = 1e3;
140
- function midpoint(a, b) {
141
- const m = (a + b) / 2;
142
- return m > a && m < b ? m : null;
143
- }
144
- var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
145
- TaskStatus.Done,
146
- TaskStatus.Cancelled
147
- ]);
148
- function isTerminalStatus(status) {
149
- return TERMINAL_STATUSES.has(status);
150
- }
95
+ var APP_VERSION = true ? "0.9.0" : "0.0.0-dev";
151
96
 
152
97
  // src/utils/git.ts
153
98
  import { spawnSync } from "child_process";
@@ -218,6 +163,61 @@ function detectGitRemote(cwd) {
218
163
  }
219
164
  }
220
165
 
166
+ // src/types/enums.ts
167
+ var TaskStatus = {
168
+ Backlog: "backlog",
169
+ Todo: "todo",
170
+ InProgress: "in-progress",
171
+ Review: "review",
172
+ Done: "done",
173
+ Cancelled: "cancelled"
174
+ };
175
+ var TaskType = {
176
+ Release: "release",
177
+ Story: "story",
178
+ TechDebt: "tech-debt",
179
+ Bug: "bug"
180
+ };
181
+ var TaskLevel = {
182
+ Release: 1,
183
+ Work: 2
184
+ };
185
+ var TYPE_TO_LEVEL = {
186
+ [TaskType.Release]: TaskLevel.Release,
187
+ [TaskType.Story]: TaskLevel.Work,
188
+ [TaskType.TechDebt]: TaskLevel.Work,
189
+ [TaskType.Bug]: TaskLevel.Work
190
+ };
191
+ function getTaskLevel(type) {
192
+ return TYPE_TO_LEVEL[type] ?? TaskLevel.Work;
193
+ }
194
+ var WORK_TYPES = /* @__PURE__ */ new Set([
195
+ TaskType.Story,
196
+ TaskType.TechDebt,
197
+ TaskType.Bug
198
+ ]);
199
+ var DependencyType = {
200
+ Blocks: "blocks",
201
+ RelatesTo: "relates-to",
202
+ Duplicates: "duplicates"
203
+ };
204
+ var UIDependencyType = {
205
+ ...DependencyType,
206
+ BlockedBy: "blocked-by"
207
+ };
208
+ var RANK_GAP = 1e3;
209
+ function midpoint(a, b) {
210
+ const m = (a + b) / 2;
211
+ return m > a && m < b ? m : null;
212
+ }
213
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
214
+ TaskStatus.Done,
215
+ TaskStatus.Cancelled
216
+ ]);
217
+ function isTerminalStatus(status) {
218
+ return TERMINAL_STATUSES.has(status);
219
+ }
220
+
221
221
  // src/utils/version.ts
222
222
  function isNewerVersion(a, b) {
223
223
  const parse = (v) => {
@@ -237,6 +237,7 @@ export {
237
237
  ok,
238
238
  err,
239
239
  GitRemote,
240
+ detectGitRemote,
240
241
  TaskStatus,
241
242
  TaskType,
242
243
  TaskLevel,
@@ -248,8 +249,7 @@ export {
248
249
  midpoint,
249
250
  TERMINAL_STATUSES,
250
251
  isTerminalStatus,
251
- detectGitRemote,
252
252
  isNewerVersion,
253
253
  APP_VERSION
254
254
  };
255
- //# sourceMappingURL=chunk-TJH7MKMN.js.map
255
+ //# sourceMappingURL=chunk-7SQR55R2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["<define:__CHANGELOG_ENTRIES__>","../src/logging/logger.ts","../src/version.ts","../src/utils/git.ts","../src/types/common.ts","../src/types/git-remote.ts","../src/types/enums.ts","../src/utils/version.ts"],"sourcesContent":["[{\"version\":\"0.9.0\",\"date\":\"2026-04-26\",\"sections\":[{\"heading\":\"Added\",\"items\":[\"Analytic commands for agent productivity reporting (`tayto analytics`)\",\"Settings tab with tab bar navigation infrastructure\",\"Show hovered release in detail panel when release panel is focused\",\"Enter key on release panel opens full task detail view for that release\"]},{\"heading\":\"Changed\",\"items\":[\"Renamed \\\"epic\\\" to \\\"release\\\" throughout codebase and UI\",\"Repository boundary with branded IDs and per-module error unions\"]},{\"heading\":\"Fixed\",\"items\":[\"Show release name instead of raw task ID in detail panel parent field\",\"Highlight task name in detail panel with accent color across all wrapped lines\",\"Prevent task detail overflow from clobbering header and overlapping rows\",\"Align task table columns and truncate long names to prevent flickering\",\"Add `--detach-parent` flag to `task update` CLI command\"]}]},{\"version\":\"0.8.1\",\"date\":\"2026-04-14\",\"sections\":[{\"heading\":\"Changed\",\"items\":[\"Lower minimum Node.js requirement from >=25 to >=22\"]}]},{\"version\":\"0.8.0\",\"date\":\"2026-04-13\",\"sections\":[{\"heading\":\"Added\",\"items\":[\"What's new ticker and changelog dialog for post-upgrade visibility\"]}]},{\"version\":\"0.7.0\",\"date\":\"2026-04-12\",\"sections\":[{\"heading\":\"Added\",\"items\":[\"Prompt to create project when unlinked git remote is detected on TUI startup\",\"Project edit support in TUI and `--git-remote` flag for CLI\",\"Git remote field to create project form\",\"Arrow key navigation and inline cursor movement in form fields\"]},{\"heading\":\"Changed\",\"items\":[\"Introduce `GitRemote` value object to enforce URL normalization\"]},{\"heading\":\"Fixed\",\"items\":[\"Restore terminal raw mode around external editor to prevent readonly mode\"]}]},{\"version\":\"0.6.0\",\"date\":\"2026-04-12\",\"sections\":[{\"heading\":\"Added\",\"items\":[\"Auto-upgrade with npm registry version check and `tayto upgrade` command\",\"Contextual hints in TUI for discoverability\",\"Shift+Tab panel navigation in TUI\"]},{\"heading\":\"Changed\",\"items\":[\"Improve TUI intuitiveness with better keyboard shortcut guidance\"]},{\"heading\":\"Chore\",\"items\":[\"Add open-source project scaffolding and fix CI node version\",\"Add demo video to README\"]}]},{\"version\":\"0.5.0\",\"date\":\"2026-04-10\",\"sections\":[{\"heading\":\"Changed\",\"items\":[\"Redesign TUI color theme around #9B9BA5 (logo helmet grey) for consistent palette\",\"Redesign header with 3-column layout: logo, product info, and shortcut hints\",\"Integrate Tayto pixel logo into TUI header bar\"]},{\"heading\":\"Improved\",\"items\":[\"Consolidate logo assets into `src/tui/assets/` and `src/tui/components/Logo.tsx`\",\"Theme colors now import from logo PALETTE for single source of truth\",\"Memoize Logo component to avoid unnecessary re-renders\"]},{\"heading\":\"Removed\",\"items\":[\"Standalone `tayto-logo/` directory (AGENT.md, preview PNGs)\"]}]},{\"version\":\"0.4.1\",\"date\":\"2026-04-10\",\"sections\":[{\"heading\":\"Fixed\",\"items\":[\"Prevent rank collisions from floating-point precision collapse\",\"Scope `listTasks` and `searchTasks` to default project when none specified\"]},{\"heading\":\"Changed\",\"items\":[\"Resolve project at CLI/TUI boundary; services now accept `Project` directly\"]}]},{\"version\":\"0.4.0\",\"date\":\"2026-04-09\",\"sections\":[{\"heading\":\"Added\",\"items\":[\"Link projects to git remotes for automatic project selection\",\"Running `tayto` inside a git repo auto-selects the linked project\"]}]},{\"version\":\"0.3.0\",\"date\":null,\"sections\":[{\"heading\":\"Added\",\"items\":[\"Dependency management (`tayto dep add/remove/list/graph`)\",\"Dependency types: `blocks`, `blocked-by`, `relates-to`, `duplicates`\",\"Task breakdown command to split tasks into subtasks\"]}]},{\"version\":\"0.2.0\",\"date\":null,\"sections\":[{\"heading\":\"Added\",\"items\":[\"TUI (terminal UI) built with Ink/React\",\"Full-text search via SQLite FTS5\",\"Task export and import (JSON)\",\"OpenTelemetry tracing on every operation\"]}]},{\"version\":\"0.1.0\",\"date\":null,\"sections\":[{\"heading\":\"Added\",\"items\":[\"Initial release\",\"CLI with Commander.js\",\"SQLite database backend with ULID identifiers\",\"Fractional ranking for O(1) reorders\",\"Task types: `story`, `bug`, `tech-debt`\",\"Task statuses: `backlog` → `todo` → `in-progress` → `review` → `done` / `cancelled`\",\"Project management with default project support\",\"Zod validation across all service layer inputs\"]}]}]","import { appendFileSync, readdirSync, unlinkSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { trace, type Span, SpanStatusCode } from '@opentelemetry/api';\n\nconst tracer = trace.getTracer('task');\nconst LOG_RETENTION_DAYS = 7;\n\nexport interface LogAttributes {\n [key: string]: string | number | boolean;\n}\n\ntype LogLevel = 'INFO' | 'WARN' | 'ERROR';\n\nfunction formatTimestamp(): string {\n return new Date().toISOString();\n}\n\nfunction formatAttrs(attrs?: LogAttributes): string {\n if (!attrs || Object.keys(attrs).length === 0) return '';\n return ' ' + JSON.stringify(attrs);\n}\n\nclass Logger {\n private logFilePath: string | null = null;\n\n init(logDir: string): void {\n const date = new Date().toISOString().slice(0, 10); // YYYY-MM-DD\n this.logFilePath = join(logDir, `task-${date}.log`);\n this.pruneOldLogs(logDir);\n }\n\n info(message: string, attrs?: LogAttributes): void {\n this.write('INFO', message, attrs);\n const span = trace.getActiveSpan();\n if (span) {\n span.addEvent(message, attrs);\n }\n }\n\n warn(message: string, attrs?: LogAttributes): void {\n this.write('WARN', message, attrs);\n const span = trace.getActiveSpan();\n if (span) {\n span.addEvent(`WARN: ${message}`, attrs);\n }\n }\n\n error(message: string, error?: unknown, attrs?: LogAttributes): void {\n const errorDetail = error instanceof Error ? ` | ${error.stack ?? error.message}` : '';\n this.write('ERROR', `${message}${errorDetail}`, attrs);\n const span = trace.getActiveSpan();\n if (span) {\n span.addEvent(`ERROR: ${message}`, attrs);\n if (error instanceof Error) {\n span.recordException(error);\n }\n span.setStatus({ code: SpanStatusCode.ERROR, message });\n }\n }\n\n startSpan<T>(name: string, fn: (span: Span) => T): T {\n return tracer.startActiveSpan(name, (span) => {\n try {\n const result = fn(span);\n span.end();\n return result;\n } catch (e) {\n if (e instanceof Error) {\n span.recordException(e);\n }\n span.setStatus({ code: SpanStatusCode.ERROR });\n span.end();\n throw e;\n }\n });\n }\n\n private write(level: LogLevel, message: string, attrs?: LogAttributes): void {\n if (!this.logFilePath) return;\n const line = `${formatTimestamp()} [${level}] ${message}${formatAttrs(attrs)}\\n`;\n try {\n appendFileSync(this.logFilePath, line);\n } catch {\n // Swallowing here is intentional: logging must never crash the app.\n // If the log file is unwritable, the OTel span still captures the event.\n }\n }\n\n private pruneOldLogs(logDir: string): void {\n try {\n const cutoff = Date.now() - LOG_RETENTION_DAYS * 24 * 60 * 60 * 1000;\n const files = readdirSync(logDir).filter((f) => f.startsWith('task-') && f.endsWith('.log'));\n for (const file of files) {\n const dateStr = file.slice('task-'.length, -'.log'.length);\n const fileDate = new Date(dateStr).getTime();\n if (!isNaN(fileDate) && fileDate < cutoff) {\n unlinkSync(join(logDir, file));\n }\n }\n } catch {\n // Best-effort cleanup — don't crash if pruning fails\n }\n }\n}\n\nexport const logger = new Logger();\n","declare const __APP_VERSION__: string;\n\nexport const APP_VERSION: string =\n typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : '0.0.0-dev';\n","import { spawnSync } from 'node:child_process';\nimport type { Result } from '../types/common.js';\nimport { ok } from '../types/common.js';\nimport { GitRemote } from '../types/git-remote.js';\nimport { logger } from '../logging/logger.js';\n\n/**\n * Detect the git origin remote URL from the given directory.\n * Returns ok(GitRemote) if found, ok(null) if no git repo or no origin remote.\n * Never returns err() — all git failures are silent fallbacks.\n */\nexport function detectGitRemote(cwd?: string): Result<GitRemote | null, never> {\n try {\n const result = spawnSync('git', ['remote', 'get-url', 'origin'], {\n cwd: cwd ?? process.cwd(),\n encoding: 'utf-8',\n timeout: 5000,\n });\n\n if (result.status !== 0 || result.error) {\n logger.info('detectGitRemote: no git remote found (non-zero exit or error)');\n return ok(null);\n }\n\n const raw = result.stdout.trim();\n if (!raw) {\n logger.info('detectGitRemote: empty stdout from git remote get-url');\n return ok(null);\n }\n\n const remote = GitRemote.parse(raw);\n logger.info(`detectGitRemote: found remote=${remote.value}`);\n return ok(remote);\n } catch (e: unknown) {\n logger.info(`detectGitRemote: exception during git detection: ${String(e)}`);\n return ok(null);\n }\n}\n","export type Result<T, E> = { ok: true; value: T } | { ok: false; error: E };\n\nexport function ok<T>(value: T): Result<T, never> {\n return { ok: true, value };\n}\n\nexport function err<E>(error: E): Result<never, E> {\n return { ok: false, error };\n}\n\nexport interface CLIOutput<T> {\n ok: boolean;\n data?: T;\n error?: { code: string; message: string };\n}\n\nexport type FetchFn = (url: string, init?: RequestInit) => Promise<Response>;\n","/**\n * Value object representing a normalized git remote URL.\n * Accepts any common format (SSH or HTTPS, with or without .git suffix)\n * and stores the canonical `host/owner/repo` form.\n */\nexport class GitRemote {\n readonly value: string;\n\n private constructor(normalized: string) {\n this.value = normalized;\n }\n\n static parse(raw: string): GitRemote {\n return new GitRemote(GitRemote.normalize(raw));\n }\n\n private static normalize(url: string): string {\n const trimmed = url.trim();\n\n // SSH format: git@github.com:owner/repo.git or git@github.com:owner/repo\n const sshMatch = trimmed.match(/^git@([^:]+):(.+?)(?:\\.git)?$/);\n if (sshMatch) {\n const [, host, path] = sshMatch;\n return `${host}/${path}`.toLowerCase();\n }\n\n // HTTPS/HTTP format: https://github.com/owner/repo.git or without .git\n try {\n const parsed = new URL(trimmed);\n const path = parsed.pathname.replace(/\\.git$/, '').replace(/^\\//, '');\n return `${parsed.host}/${path}`.toLowerCase();\n } catch {\n return trimmed.toLowerCase();\n }\n }\n\n equals(other: GitRemote): boolean {\n return this.value === other.value;\n }\n\n toString(): string {\n return this.value;\n }\n}\n","export const TaskStatus = {\n Backlog: 'backlog',\n Todo: 'todo',\n InProgress: 'in-progress',\n Review: 'review',\n Done: 'done',\n Cancelled: 'cancelled',\n} as const;\nexport type TaskStatus = (typeof TaskStatus)[keyof typeof TaskStatus];\n\nexport const TaskType = {\n Release: 'release',\n Story: 'story',\n TechDebt: 'tech-debt',\n Bug: 'bug',\n} as const;\nexport type TaskType = (typeof TaskType)[keyof typeof TaskType];\n\n/**\n * Task level derived from type.\n * Level 1: releases (grouping/delivery layer)\n * Level 2: stories, tech-debt, bugs (execution layer)\n */\nexport const TaskLevel = {\n Release: 1,\n Work: 2,\n} as const;\nexport type TaskLevel = (typeof TaskLevel)[keyof typeof TaskLevel];\n\nconst TYPE_TO_LEVEL: Record<string, TaskLevel> = {\n [TaskType.Release]: TaskLevel.Release,\n [TaskType.Story]: TaskLevel.Work,\n [TaskType.TechDebt]: TaskLevel.Work,\n [TaskType.Bug]: TaskLevel.Work,\n};\n\nexport function getTaskLevel(type: string): TaskLevel {\n return TYPE_TO_LEVEL[type] ?? TaskLevel.Work;\n}\n\n/** Types that belong to the work (level 2) execution layer. */\nexport const WORK_TYPES: ReadonlySet<string> = new Set([\n TaskType.Story,\n TaskType.TechDebt,\n TaskType.Bug,\n]);\n\n/** Types stored in the database. */\nexport const DependencyType = {\n Blocks: 'blocks',\n RelatesTo: 'relates-to',\n Duplicates: 'duplicates',\n} as const;\nexport type DependencyType = (typeof DependencyType)[keyof typeof DependencyType];\n\n/**\n * UI-level dependency types — includes BlockedBy which is a reverse-Blocks\n * relationship resolved before persisting to the database.\n */\nexport const UIDependencyType = {\n ...DependencyType,\n BlockedBy: 'blocked-by',\n} as const;\nexport type UIDependencyType = (typeof UIDependencyType)[keyof typeof UIDependencyType];\n\n/** Gap between consecutive rank values, used for insertion between neighbors. */\nexport const RANK_GAP = 1000.0;\n\n/**\n * Collapse-safe midpoint between two rank values. Returns `null` when\n * IEEE 754 double precision cannot represent a strictly-between value —\n * callers use this as a signal to rebalance the rank grid and retry.\n * Returning a number unconditionally would silently collide with an\n * endpoint and corrupt the task ordering.\n */\nexport function midpoint(a: number, b: number): number | null {\n const m = (a + b) / 2;\n return m > a && m < b ? m : null;\n}\n\n/** Statuses that represent terminal/completed task states. */\nexport const TERMINAL_STATUSES: ReadonlySet<string> = new Set([\n TaskStatus.Done,\n TaskStatus.Cancelled,\n]);\n\nexport function isTerminalStatus(status: string): boolean {\n return TERMINAL_STATUSES.has(status);\n}\n","/**\n * Compare two semver strings (major.minor.patch).\n * Returns true if `a` is strictly newer than `b`.\n */\nexport function isNewerVersion(a: string, b: string): boolean {\n const parse = (v: string): [number, number, number] => {\n const parts = v.replace(/^v/, '').split('.').map(Number);\n return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];\n };\n const [aMaj, aMin, aPatch] = parse(a);\n const [bMaj, bMin, bPatch] = parse(b);\n if (aMaj !== bMaj) return aMaj > bMaj;\n if (aMin !== bMin) return aMin > bMin;\n return aPatch > bPatch;\n}\n"],"mappings":"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","names":[]}