schub 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1246 -783
- package/package.json +7 -5
- package/skills/create-tasks/SKILL.md +2 -0
- package/templates/create-proposal/proposal-template.md +2 -0
- package/templates/create-tasks/task-template.md +8 -5
- package/skills/update-roadmap/SKILL.md +0 -23
- package/src/commands/adr.test.ts +0 -63
- package/src/commands/adr.ts +0 -107
- package/src/commands/changes.test.ts +0 -286
- package/src/commands/changes.ts +0 -264
- package/src/commands/cookbook.test.ts +0 -62
- package/src/commands/cookbook.ts +0 -95
- package/src/commands/eject.test.ts +0 -74
- package/src/commands/eject.ts +0 -100
- package/src/commands/init.test.ts +0 -260
- package/src/commands/init.ts +0 -187
- package/src/commands/project.test.ts +0 -109
- package/src/commands/project.ts +0 -75
- package/src/commands/review.test.ts +0 -80
- package/src/commands/review.ts +0 -231
- package/src/commands/tasks-create.test.ts +0 -221
- package/src/commands/tasks-implement.test.ts +0 -253
- package/src/commands/tasks-implement.ts +0 -121
- package/src/commands/tasks-list.test.ts +0 -177
- package/src/commands/tasks-update.test.ts +0 -92
- package/src/commands/tasks.ts +0 -269
- package/src/features/changes/index.test.ts +0 -166
- package/src/features/changes/index.ts +0 -400
- package/src/features/frontmatter/index.test.ts +0 -19
- package/src/features/frontmatter/index.ts +0 -110
- package/src/features/project/index.ts +0 -69
- package/src/features/tasks/constants.ts +0 -35
- package/src/features/tasks/create.test.ts +0 -52
- package/src/features/tasks/create.ts +0 -168
- package/src/features/tasks/filesystem.test.ts +0 -345
- package/src/features/tasks/filesystem.ts +0 -348
- package/src/features/tasks/graph.ts +0 -106
- package/src/features/tasks/index.ts +0 -22
- package/src/features/tasks/sorting.ts +0 -14
- package/src/features/tasks/template.test.ts +0 -57
- package/src/features/tasks/template.ts +0 -19
- package/src/features/tasks/worktree.ts +0 -48
- package/src/features/templates/index.test.ts +0 -29
- package/src/features/templates/index.ts +0 -18
- package/src/index.test.ts +0 -59
- package/src/index.ts +0 -222
- package/src/init.test.ts +0 -43
- package/src/init.ts +0 -27
- package/src/opencode.test.ts +0 -221
- package/src/opencode.ts +0 -231
- package/src/schub-root.ts +0 -33
- package/src/tui/app.test.tsx +0 -483
- package/src/tui/app.tsx +0 -354
- package/src/tui/clipboard.ts +0 -5
- package/src/tui/components/change-list.tsx +0 -69
- package/src/tui/components/list-item.test.tsx +0 -68
- package/src/tui/components/list-item.tsx +0 -208
- package/src/tui/components/markdown-renderer.test.ts +0 -1
- package/src/tui/components/markdown-renderer.ts +0 -1
- package/src/tui/components/plan-view.test.tsx +0 -101
- package/src/tui/components/plan-view.tsx +0 -98
- package/src/tui/components/preview-page.test.tsx +0 -69
- package/src/tui/components/preview-page.tsx +0 -68
- package/src/tui/components/proposal-detail-view.test.tsx +0 -203
- package/src/tui/components/proposal-detail-view.tsx +0 -140
- package/src/tui/components/session-view.tsx +0 -102
- package/src/tui/components/status-color.ts +0 -17
- package/src/tui/components/status-show-all-tasks-view.tsx +0 -28
- package/src/tui/components/status-view-data.test.ts +0 -125
- package/src/tui/components/status-view-data.ts +0 -556
- package/src/tui/components/status-view-render.tsx +0 -126
- package/src/tui/components/status-view-session.test.tsx +0 -324
- package/src/tui/components/status-view.test.tsx +0 -1570
- package/src/tui/components/status-view.tsx +0 -481
- package/src/tui/components/task-list.tsx +0 -79
- package/src/tui/hooks/use-refresh-interval.ts +0 -29
- package/src/tui/hooks/use-status-view-input.ts +0 -320
- package/src/tui/index.ts +0 -16
- package/src/tui/prompts/confirm-prompt.tsx +0 -83
- package/src/tui/prompts/multi-select-prompt.tsx +0 -50
- package/src/tui/shared/detail-tasks.ts +0 -38
- package/src/tui/shared/input.test.ts +0 -25
- package/src/tui/shared/input.ts +0 -85
- package/src/tui/shared/selection.test.ts +0 -11
- package/src/tui/shared/selection.ts +0 -7
- package/src/tui/terminal.test.ts +0 -46
- package/src/tui/terminal.ts +0 -16
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
+
#!/usr/bin/env node
|
|
3
4
|
import { createRequire } from "node:module";
|
|
4
5
|
var __create = Object.create;
|
|
5
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
@@ -31356,7 +31357,7 @@ var require_core = __commonJS((exports, module) => {
|
|
|
31356
31357
|
return match && match.index === 0;
|
|
31357
31358
|
}
|
|
31358
31359
|
var BACKREF_RE = /\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;
|
|
31359
|
-
function
|
|
31360
|
+
function join18(regexps, separator = "|") {
|
|
31360
31361
|
let numCaptures = 0;
|
|
31361
31362
|
return regexps.map((regex2) => {
|
|
31362
31363
|
numCaptures += 1;
|
|
@@ -31639,7 +31640,7 @@ var require_core = __commonJS((exports, module) => {
|
|
|
31639
31640
|
this.exec = () => null;
|
|
31640
31641
|
}
|
|
31641
31642
|
const terminators = this.regexes.map((el) => el[1]);
|
|
31642
|
-
this.matcherRe = langRe(
|
|
31643
|
+
this.matcherRe = langRe(join18(terminators), true);
|
|
31643
31644
|
this.lastIndex = 0;
|
|
31644
31645
|
}
|
|
31645
31646
|
exec(s) {
|
|
@@ -89519,6 +89520,7 @@ var resolveTemplatePath = (schubDir, templatePath, bundledPath) => {
|
|
|
89519
89520
|
// src/features/changes/index.ts
|
|
89520
89521
|
var CHANGE_ID_PATTERN = /^(?:[Cc]\d+_)?[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
89521
89522
|
var SUMMARY_FALLBACK = "No summary provided.";
|
|
89523
|
+
var ARCHIVED_CHANGE_STATUSES = new Set(["archived", "rejected"]);
|
|
89522
89524
|
var isDirectory2 = (path) => {
|
|
89523
89525
|
try {
|
|
89524
89526
|
return statSync5(path).isDirectory();
|
|
@@ -89533,6 +89535,7 @@ var readFrontmatterString = (data, key) => {
|
|
|
89533
89535
|
}
|
|
89534
89536
|
return "";
|
|
89535
89537
|
};
|
|
89538
|
+
var normalizeStatusValue = (status) => status.trim().toLowerCase();
|
|
89536
89539
|
var parseProposal = (content, changeId) => {
|
|
89537
89540
|
const titleMatch = content.match(/^#\s+Proposal\s+-\s+(.*)$/m);
|
|
89538
89541
|
const { data } = readFrontmatter(content);
|
|
@@ -89570,6 +89573,22 @@ var normalizeChangeId = (value) => {
|
|
|
89570
89573
|
return trimmed;
|
|
89571
89574
|
};
|
|
89572
89575
|
var isValidChangeId = (value) => CHANGE_ID_PATTERN.test(value.trim());
|
|
89576
|
+
var resolveChangePaths = (schubDir, changeId) => {
|
|
89577
|
+
const changeDir = join4(schubDir, "changes", changeId);
|
|
89578
|
+
const proposalPath = join4(changeDir, "proposal.md");
|
|
89579
|
+
if (existsSync(proposalPath)) {
|
|
89580
|
+
return { changeDir, proposalPath, isArchived: false };
|
|
89581
|
+
}
|
|
89582
|
+
const archiveDir = join4(schubDir, "archive", "changes", changeId);
|
|
89583
|
+
const archiveProposalPath = join4(archiveDir, "proposal.md");
|
|
89584
|
+
if (existsSync(archiveProposalPath)) {
|
|
89585
|
+
return { changeDir: archiveDir, proposalPath: archiveProposalPath, isArchived: true };
|
|
89586
|
+
}
|
|
89587
|
+
throw new Error(`Required change files missing:
|
|
89588
|
+
- ${proposalPath}
|
|
89589
|
+
- ${archiveProposalPath}
|
|
89590
|
+
Create the change proposal before scaffolding docs.`);
|
|
89591
|
+
};
|
|
89573
89592
|
var readChangeSummary = (schubDir, changeId) => {
|
|
89574
89593
|
const trimmed = changeId.trim();
|
|
89575
89594
|
if (!trimmed) {
|
|
@@ -89579,20 +89598,15 @@ var readChangeSummary = (schubDir, changeId) => {
|
|
|
89579
89598
|
throw new Error(`Invalid change-id '${changeId}'. Use kebab-case or a C-prefixed id (e.g., C1_add-user-auth).`);
|
|
89580
89599
|
}
|
|
89581
89600
|
const normalized = normalizeChangeId(trimmed);
|
|
89582
|
-
const
|
|
89583
|
-
const
|
|
89584
|
-
if (!existsSync(proposalPath)) {
|
|
89585
|
-
throw new Error(`Required change files missing:
|
|
89586
|
-
- ${proposalPath}
|
|
89587
|
-
Create the change proposal before scaffolding docs.`);
|
|
89588
|
-
}
|
|
89589
|
-
const content = readFileSync4(proposalPath, "utf8");
|
|
89601
|
+
const resolved = resolveChangePaths(schubDir, normalized);
|
|
89602
|
+
const content = readFileSync4(resolved.proposalPath, "utf8");
|
|
89590
89603
|
const parsed = parseProposal(content, normalized);
|
|
89591
89604
|
return {
|
|
89592
89605
|
changeId: normalized,
|
|
89593
89606
|
changeTitle: parsed.title,
|
|
89594
|
-
changeDir,
|
|
89595
|
-
proposalPath
|
|
89607
|
+
changeDir: resolved.changeDir,
|
|
89608
|
+
proposalPath: resolved.proposalPath,
|
|
89609
|
+
isArchived: resolved.isArchived
|
|
89596
89610
|
};
|
|
89597
89611
|
};
|
|
89598
89612
|
var readChangeDetail = (schubDir, changeId) => {
|
|
@@ -89607,6 +89621,7 @@ var readChangeDetail = (schubDir, changeId) => {
|
|
|
89607
89621
|
summary: detail.summary
|
|
89608
89622
|
};
|
|
89609
89623
|
};
|
|
89624
|
+
var isArchivedStatus = (value) => ARCHIVED_CHANGE_STATUSES.has(normalizeStatusValue(value));
|
|
89610
89625
|
var updateChangeStatus = (schubDir, changeId, status) => {
|
|
89611
89626
|
const nextStatus = status.trim();
|
|
89612
89627
|
if (!nextStatus) {
|
|
@@ -89621,6 +89636,38 @@ var updateChangeStatus = (schubDir, changeId, status) => {
|
|
|
89621
89636
|
Add a 'status' field in frontmatter before updating status.`);
|
|
89622
89637
|
}
|
|
89623
89638
|
const updated = updateFrontmatterValue(content, "status", nextStatus);
|
|
89639
|
+
const shouldArchive = isArchivedStatus(nextStatus);
|
|
89640
|
+
const archiveRoot = join4(schubDir, "archive", "changes");
|
|
89641
|
+
const archivePath = join4(archiveRoot, summary.changeId);
|
|
89642
|
+
const activePath = join4(schubDir, "changes", summary.changeId);
|
|
89643
|
+
if (shouldArchive && !summary.isArchived) {
|
|
89644
|
+
if (existsSync(archivePath)) {
|
|
89645
|
+
throw new Error(`Archive already exists: ${archivePath}`);
|
|
89646
|
+
}
|
|
89647
|
+
mkdirSync(archiveRoot, { recursive: true });
|
|
89648
|
+
writeFileSync(summary.proposalPath, updated, "utf8");
|
|
89649
|
+
renameSync(summary.changeDir, archivePath);
|
|
89650
|
+
return {
|
|
89651
|
+
changeId: summary.changeId,
|
|
89652
|
+
proposalPath: join4(archivePath, "proposal.md"),
|
|
89653
|
+
previousStatus,
|
|
89654
|
+
status: nextStatus
|
|
89655
|
+
};
|
|
89656
|
+
}
|
|
89657
|
+
if (!shouldArchive && summary.isArchived) {
|
|
89658
|
+
if (existsSync(activePath)) {
|
|
89659
|
+
throw new Error(`Change already exists: ${activePath}`);
|
|
89660
|
+
}
|
|
89661
|
+
mkdirSync(join4(schubDir, "changes"), { recursive: true });
|
|
89662
|
+
writeFileSync(summary.proposalPath, updated, "utf8");
|
|
89663
|
+
renameSync(summary.changeDir, activePath);
|
|
89664
|
+
return {
|
|
89665
|
+
changeId: summary.changeId,
|
|
89666
|
+
proposalPath: join4(activePath, "proposal.md"),
|
|
89667
|
+
previousStatus,
|
|
89668
|
+
status: nextStatus
|
|
89669
|
+
};
|
|
89670
|
+
}
|
|
89624
89671
|
writeFileSync(summary.proposalPath, updated, "utf8");
|
|
89625
89672
|
return {
|
|
89626
89673
|
changeId: summary.changeId,
|
|
@@ -89631,19 +89678,16 @@ Add a 'status' field in frontmatter before updating status.`);
|
|
|
89631
89678
|
};
|
|
89632
89679
|
var archiveChange = (schubDir, changeId) => {
|
|
89633
89680
|
const summary = readChangeSummary(schubDir, changeId);
|
|
89634
|
-
const
|
|
89635
|
-
|
|
89636
|
-
if (existsSync(archivePath)) {
|
|
89681
|
+
const archivePath = join4(schubDir, "archive", "changes", summary.changeId);
|
|
89682
|
+
if (summary.isArchived) {
|
|
89637
89683
|
throw new Error(`Archive already exists: ${archivePath}`);
|
|
89638
89684
|
}
|
|
89639
|
-
|
|
89640
|
-
const updated = updateChangeStatus(schubDir, summary.changeId, "Archived");
|
|
89641
|
-
renameSync(summary.changeDir, archivePath);
|
|
89685
|
+
const archived = updateChangeStatus(schubDir, summary.changeId, "Archived");
|
|
89642
89686
|
return {
|
|
89643
|
-
changeId:
|
|
89644
|
-
previousStatus:
|
|
89645
|
-
status:
|
|
89646
|
-
proposalPath:
|
|
89687
|
+
changeId: archived.changeId,
|
|
89688
|
+
previousStatus: archived.previousStatus,
|
|
89689
|
+
status: archived.status,
|
|
89690
|
+
proposalPath: archived.proposalPath,
|
|
89647
89691
|
archivePath
|
|
89648
89692
|
};
|
|
89649
89693
|
};
|
|
@@ -89693,6 +89737,7 @@ var STATUS_GROUPS = [
|
|
|
89693
89737
|
{ label: "Accepted", match: "accepted" },
|
|
89694
89738
|
{ label: "Implementing", match: "implement" },
|
|
89695
89739
|
{ label: "Done", match: "done" },
|
|
89740
|
+
{ label: "Rejected", match: "reject" },
|
|
89696
89741
|
{ label: "Archived", match: "archiv" }
|
|
89697
89742
|
];
|
|
89698
89743
|
var normalizeStatusLabel = (status) => {
|
|
@@ -89938,8 +89983,54 @@ var runAdrCreate = (args, startDir) => {
|
|
|
89938
89983
|
`);
|
|
89939
89984
|
};
|
|
89940
89985
|
|
|
89986
|
+
// src/features/tasks/checklist.ts
|
|
89987
|
+
var CHECKLIST_PATTERN = /^(\s*-\s+\[)( |x|X)(\].*)$/;
|
|
89988
|
+
var toggleChecklistItem = (content, itemNumber) => {
|
|
89989
|
+
if (!Number.isInteger(itemNumber) || itemNumber < 1) {
|
|
89990
|
+
throw new Error("Checklist item number must be a positive integer.");
|
|
89991
|
+
}
|
|
89992
|
+
const lines = content.split(/\r?\n/);
|
|
89993
|
+
let inSteps = false;
|
|
89994
|
+
let sawSteps = false;
|
|
89995
|
+
let currentItem = 0;
|
|
89996
|
+
for (let index = 0;index < lines.length; index += 1) {
|
|
89997
|
+
const line = lines[index];
|
|
89998
|
+
const headingMatch = line.match(/^##\s+(.*)$/);
|
|
89999
|
+
if (headingMatch) {
|
|
90000
|
+
const heading = headingMatch[1].trim().toLowerCase();
|
|
90001
|
+
if (heading === "steps") {
|
|
90002
|
+
inSteps = true;
|
|
90003
|
+
sawSteps = true;
|
|
90004
|
+
} else if (inSteps) {
|
|
90005
|
+
break;
|
|
90006
|
+
}
|
|
90007
|
+
continue;
|
|
90008
|
+
}
|
|
90009
|
+
if (!inSteps) {
|
|
90010
|
+
continue;
|
|
90011
|
+
}
|
|
90012
|
+
const checklistMatch = line.match(CHECKLIST_PATTERN);
|
|
90013
|
+
if (!checklistMatch) {
|
|
90014
|
+
continue;
|
|
90015
|
+
}
|
|
90016
|
+
currentItem += 1;
|
|
90017
|
+
if (currentItem !== itemNumber) {
|
|
90018
|
+
continue;
|
|
90019
|
+
}
|
|
90020
|
+
const isChecked = checklistMatch[2].toLowerCase() === "x";
|
|
90021
|
+
const nextValue = isChecked ? " " : "x";
|
|
90022
|
+
lines[index] = `${checklistMatch[1]}${nextValue}${checklistMatch[3]}`;
|
|
90023
|
+
return lines.join(`
|
|
90024
|
+
`);
|
|
90025
|
+
}
|
|
90026
|
+
if (!sawSteps) {
|
|
90027
|
+
throw new Error("No Steps checklist found.");
|
|
90028
|
+
}
|
|
90029
|
+
throw new Error(`Checklist item ${itemNumber} not found.`);
|
|
90030
|
+
};
|
|
89941
90031
|
// src/features/tasks/constants.ts
|
|
89942
|
-
var TASK_STATUSES = ["backlog", "reviewed", "wip", "blocked", "done", "archived"];
|
|
90032
|
+
var TASK_STATUSES = ["backlog", "reviewed", "wip", "blocked", "done", "rejected", "archived"];
|
|
90033
|
+
var DEFAULT_TASK_STATUSES = ["backlog", "reviewed", "wip", "blocked", "done", "archived"];
|
|
89943
90034
|
// src/features/tasks/create.ts
|
|
89944
90035
|
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readFileSync as readFileSync6, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
89945
90036
|
import { join as join6 } from "node:path";
|
|
@@ -90182,7 +90273,7 @@ var parseTaskFile = (filePath, fallback) => {
|
|
|
90182
90273
|
...checklistCounts ?? {}
|
|
90183
90274
|
};
|
|
90184
90275
|
};
|
|
90185
|
-
var loadTaskFiles = (schubDir, statuses =
|
|
90276
|
+
var loadTaskFiles = (schubDir, statuses = DEFAULT_TASK_STATUSES) => {
|
|
90186
90277
|
const tasksRoot = join7(schubDir, "tasks");
|
|
90187
90278
|
if (!existsSync4(tasksRoot) || !isDirectory3(tasksRoot)) {
|
|
90188
90279
|
return [];
|
|
@@ -90220,7 +90311,7 @@ var loadTaskFiles = (schubDir, statuses = TASK_STATUSES) => {
|
|
|
90220
90311
|
}
|
|
90221
90312
|
return tasks;
|
|
90222
90313
|
};
|
|
90223
|
-
var listTasks = (schubDir, statuses =
|
|
90314
|
+
var listTasks = (schubDir, statuses = DEFAULT_TASK_STATUSES) => {
|
|
90224
90315
|
const tasks = loadTaskFiles(schubDir, statuses).map((task) => ({
|
|
90225
90316
|
id: task.id,
|
|
90226
90317
|
title: task.title,
|
|
@@ -90232,7 +90323,7 @@ var listTasks = (schubDir, statuses = TASK_STATUSES) => {
|
|
|
90232
90323
|
}));
|
|
90233
90324
|
return tasks.sort(compareTasks);
|
|
90234
90325
|
};
|
|
90235
|
-
var loadTaskDependencies = (schubDir, statuses =
|
|
90326
|
+
var loadTaskDependencies = (schubDir, statuses = DEFAULT_TASK_STATUSES) => {
|
|
90236
90327
|
const tasks = loadTaskFiles(schubDir, statuses).map((task) => {
|
|
90237
90328
|
const dependsOn = task.parsedFile.dependsOn.sort(compareTaskIds);
|
|
90238
90329
|
return {
|
|
@@ -90249,7 +90340,7 @@ var loadTaskDependencies = (schubDir, statuses = TASK_STATUSES) => {
|
|
|
90249
90340
|
});
|
|
90250
90341
|
return tasks.sort(compareTasks);
|
|
90251
90342
|
};
|
|
90252
|
-
var listTasksForChange = (schubDir, changeId, statuses =
|
|
90343
|
+
var listTasksForChange = (schubDir, changeId, statuses = DEFAULT_TASK_STATUSES) => {
|
|
90253
90344
|
const normalizedChangeId = changeId.trim();
|
|
90254
90345
|
const tasks = loadTaskFiles(schubDir, statuses).filter((task) => task.parsedFile.changeId === normalizedChangeId).map((task) => ({
|
|
90255
90346
|
id: task.id,
|
|
@@ -90281,7 +90372,7 @@ var updateTaskStatuses = (schubDir, taskIds, status) => {
|
|
|
90281
90372
|
};
|
|
90282
90373
|
});
|
|
90283
90374
|
};
|
|
90284
|
-
var ACTIVE_TASK_STATUSES = TASK_STATUSES.filter((status) => status !== "archived");
|
|
90375
|
+
var ACTIVE_TASK_STATUSES = TASK_STATUSES.filter((status) => status !== "archived" && status !== "rejected");
|
|
90285
90376
|
var assignTaskToWip = (schubDir, taskId) => {
|
|
90286
90377
|
const normalizedId = taskId.trim().toUpperCase();
|
|
90287
90378
|
if (!normalizedId) {
|
|
@@ -90339,43 +90430,6 @@ var archiveTasksForChange = (schubDir, changeId) => {
|
|
|
90339
90430
|
});
|
|
90340
90431
|
};
|
|
90341
90432
|
// src/features/tasks/graph.ts
|
|
90342
|
-
var compareText = (left2, right2) => left2.localeCompare(right2, undefined, { sensitivity: "base" });
|
|
90343
|
-
var taskSortTitle = (task) => {
|
|
90344
|
-
const title = trimTaskTitle(task.title);
|
|
90345
|
-
return title || task.id;
|
|
90346
|
-
};
|
|
90347
|
-
var compareTaskTitles = (left2, right2) => {
|
|
90348
|
-
const titleCompare = compareText(taskSortTitle(left2), taskSortTitle(right2));
|
|
90349
|
-
if (titleCompare !== 0) {
|
|
90350
|
-
return titleCompare;
|
|
90351
|
-
}
|
|
90352
|
-
return compareText(left2.id, right2.id);
|
|
90353
|
-
};
|
|
90354
|
-
var buildTaskGraph = (tasks) => {
|
|
90355
|
-
const tasksById = new Map(tasks.map((task) => [task.id, task]));
|
|
90356
|
-
const nodes = new Map;
|
|
90357
|
-
for (const task of tasks) {
|
|
90358
|
-
const uniqueDependencies = Array.from(new Set(task.dependsOn)).sort(compareTaskIds);
|
|
90359
|
-
const dependencyIds = uniqueDependencies.filter((dependency) => tasksById.has(dependency));
|
|
90360
|
-
nodes.set(task.id, {
|
|
90361
|
-
...task,
|
|
90362
|
-
dependencyIds
|
|
90363
|
-
});
|
|
90364
|
-
}
|
|
90365
|
-
const childrenById = new Map;
|
|
90366
|
-
for (const node of nodes.values()) {
|
|
90367
|
-
for (const dependencyId of node.dependencyIds) {
|
|
90368
|
-
const siblings = childrenById.get(dependencyId) ?? [];
|
|
90369
|
-
siblings.push(node);
|
|
90370
|
-
childrenById.set(dependencyId, siblings);
|
|
90371
|
-
}
|
|
90372
|
-
}
|
|
90373
|
-
for (const [id, children] of childrenById) {
|
|
90374
|
-
childrenById.set(id, children.sort(compareTaskTitles));
|
|
90375
|
-
}
|
|
90376
|
-
const roots = Array.from(nodes.values()).filter((node) => node.dependencyIds.length === 0).sort(compareTaskTitles);
|
|
90377
|
-
return { roots, childrenById };
|
|
90378
|
-
};
|
|
90379
90433
|
var trimTaskTitle = (title, maxLength = 40) => {
|
|
90380
90434
|
const trimmed = title.trim();
|
|
90381
90435
|
if (trimmed.length <= maxLength) {
|
|
@@ -90386,41 +90440,6 @@ var trimTaskTitle = (title, maxLength = 40) => {
|
|
|
90386
90440
|
}
|
|
90387
90441
|
return `${trimmed.slice(0, maxLength - 1).trimEnd()}…`;
|
|
90388
90442
|
};
|
|
90389
|
-
var renderTaskGraphLines = (graph) => {
|
|
90390
|
-
const lines = [];
|
|
90391
|
-
const renderedIds = new Set;
|
|
90392
|
-
const renderNode = (node, prefix, isLast, isRoot) => {
|
|
90393
|
-
const connector = isRoot ? "" : isLast ? "└─ " : "├─ ";
|
|
90394
|
-
if (renderedIds.has(node.id)) {
|
|
90395
|
-
lines.push({
|
|
90396
|
-
text: `${prefix}${connector}${node.id} ↩`,
|
|
90397
|
-
status: node.status,
|
|
90398
|
-
isDuplicate: true,
|
|
90399
|
-
checklistRemaining: node.checklistRemaining,
|
|
90400
|
-
checklistTotal: node.checklistTotal
|
|
90401
|
-
});
|
|
90402
|
-
return;
|
|
90403
|
-
}
|
|
90404
|
-
renderedIds.add(node.id);
|
|
90405
|
-
const title = trimTaskTitle(node.title);
|
|
90406
|
-
lines.push({
|
|
90407
|
-
text: `${prefix}${connector}${node.id} ${title}`,
|
|
90408
|
-
status: node.status,
|
|
90409
|
-
isDuplicate: false,
|
|
90410
|
-
checklistRemaining: node.checklistRemaining,
|
|
90411
|
-
checklistTotal: node.checklistTotal
|
|
90412
|
-
});
|
|
90413
|
-
const children = graph.childrenById.get(node.id) ?? [];
|
|
90414
|
-
const nextPrefix = isRoot ? `${prefix} ` : `${prefix}${isLast ? " " : "│ "}`;
|
|
90415
|
-
children.forEach((child, index) => {
|
|
90416
|
-
renderNode(child, nextPrefix, index === children.length - 1, false);
|
|
90417
|
-
});
|
|
90418
|
-
};
|
|
90419
|
-
graph.roots.forEach((root, index) => {
|
|
90420
|
-
renderNode(root, "", index === graph.roots.length - 1, true);
|
|
90421
|
-
});
|
|
90422
|
-
return lines;
|
|
90423
|
-
};
|
|
90424
90443
|
// src/features/tasks/template.ts
|
|
90425
90444
|
var TEMPLATE_MARKERS = [
|
|
90426
90445
|
"{{CHANGE_ID}}",
|
|
@@ -90430,7 +90449,8 @@ var TEMPLATE_MARKERS = [
|
|
|
90430
90449
|
"[no|yes]",
|
|
90431
90450
|
"[What this task accomplishes, in one paragraph.]",
|
|
90432
90451
|
"[Included work]",
|
|
90433
|
-
"[Explicitly excluded work]"
|
|
90452
|
+
"[Explicitly excluded work]",
|
|
90453
|
+
'[Describe documentation updates required or state "No documentation updates required."]'
|
|
90434
90454
|
];
|
|
90435
90455
|
var TEMPLATE_PATTERNS = [/\[Objective \d+ pass\/fail condition\]/, /^\s*-\s+\[\s\]\s+Step\s+\d+/m, /^\s*-\s+\.\.\./m];
|
|
90436
90456
|
var hasUnimplementedTemplateTokens = (content) => {
|
|
@@ -90668,11 +90688,16 @@ var parseChangeArchiveOptions = (args) => {
|
|
|
90668
90688
|
return options;
|
|
90669
90689
|
};
|
|
90670
90690
|
var parseChangeListOptions = (args) => {
|
|
90691
|
+
let showAll = false;
|
|
90671
90692
|
const unknown = [];
|
|
90672
90693
|
const rejectUnsupported = (flag) => {
|
|
90673
90694
|
throw new Error(`Unsupported option: ${flag}.`);
|
|
90674
90695
|
};
|
|
90675
90696
|
for (const arg of args) {
|
|
90697
|
+
if (arg === "--show-all") {
|
|
90698
|
+
showAll = true;
|
|
90699
|
+
continue;
|
|
90700
|
+
}
|
|
90676
90701
|
if (arg === "--schub-root" || arg === "--agent-root") {
|
|
90677
90702
|
rejectUnsupported(arg);
|
|
90678
90703
|
}
|
|
@@ -90687,6 +90712,37 @@ var parseChangeListOptions = (args) => {
|
|
|
90687
90712
|
if (unknown.length > 0) {
|
|
90688
90713
|
throw new Error(`Unknown option(s): ${unknown.join(", ")}`);
|
|
90689
90714
|
}
|
|
90715
|
+
return { showAll };
|
|
90716
|
+
};
|
|
90717
|
+
var compareChangeIds = (left2, right2) => {
|
|
90718
|
+
const leftNumber = Number(left2.id.match(/\d+/)?.[0] ?? Number.POSITIVE_INFINITY);
|
|
90719
|
+
const rightNumber = Number(right2.id.match(/\d+/)?.[0] ?? Number.POSITIVE_INFINITY);
|
|
90720
|
+
if (leftNumber !== rightNumber) {
|
|
90721
|
+
return leftNumber - rightNumber;
|
|
90722
|
+
}
|
|
90723
|
+
return left2.id.localeCompare(right2.id);
|
|
90724
|
+
};
|
|
90725
|
+
var groupChangesByStatus = (changes) => {
|
|
90726
|
+
const grouped = new Map;
|
|
90727
|
+
for (const change of changes) {
|
|
90728
|
+
const existing = grouped.get(change.statusLabel);
|
|
90729
|
+
if (existing) {
|
|
90730
|
+
existing.items.push(change);
|
|
90731
|
+
continue;
|
|
90732
|
+
}
|
|
90733
|
+
grouped.set(change.statusLabel, { label: change.statusLabel, order: change.statusOrder, items: [change] });
|
|
90734
|
+
}
|
|
90735
|
+
const sorted = Array.from(grouped.values()).sort((left2, right2) => {
|
|
90736
|
+
const orderCompare = left2.order - right2.order;
|
|
90737
|
+
if (orderCompare !== 0) {
|
|
90738
|
+
return orderCompare;
|
|
90739
|
+
}
|
|
90740
|
+
return left2.label.localeCompare(right2.label);
|
|
90741
|
+
});
|
|
90742
|
+
for (const group of sorted) {
|
|
90743
|
+
group.items.sort(compareChangeIds);
|
|
90744
|
+
}
|
|
90745
|
+
return sorted;
|
|
90690
90746
|
};
|
|
90691
90747
|
var runChangesCreate = (args, startDir) => {
|
|
90692
90748
|
const options = parseChangeCreateOptions(args);
|
|
@@ -90712,8 +90768,20 @@ var runChangesArchive = (args, startDir) => {
|
|
|
90712
90768
|
`);
|
|
90713
90769
|
};
|
|
90714
90770
|
var runChangesList = (args, startDir) => {
|
|
90715
|
-
parseChangeListOptions(args);
|
|
90771
|
+
const options = parseChangeListOptions(args);
|
|
90716
90772
|
const schubDir = resolveChangeRoot(startDir);
|
|
90773
|
+
if (options.showAll) {
|
|
90774
|
+
const changes2 = listChangeOverview(schubDir);
|
|
90775
|
+
const groups = groupChangesByStatus(changes2);
|
|
90776
|
+
const lines2 = groups.flatMap((group) => [
|
|
90777
|
+
group.label,
|
|
90778
|
+
...group.items.map((change) => `${change.id} ${change.title} (${change.statusLabel})`)
|
|
90779
|
+
]);
|
|
90780
|
+
process.stdout.write(`${lines2.join(`
|
|
90781
|
+
`)}
|
|
90782
|
+
`);
|
|
90783
|
+
return;
|
|
90784
|
+
}
|
|
90717
90785
|
const changes = listChanges(schubDir);
|
|
90718
90786
|
const lines = changes.map((change) => `${change.id} ${change.title} (${change.status})`);
|
|
90719
90787
|
process.stdout.write(`${lines.join(`
|
|
@@ -99534,12 +99602,153 @@ var runReviewComplete = (args, startDir) => {
|
|
|
99534
99602
|
`);
|
|
99535
99603
|
};
|
|
99536
99604
|
|
|
99605
|
+
// src/commands/tasks-check.ts
|
|
99606
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "node:fs";
|
|
99607
|
+
import { dirname as dirname10, join as join15 } from "node:path";
|
|
99608
|
+
var parseTaskCheckOptions = (args) => {
|
|
99609
|
+
let taskId;
|
|
99610
|
+
let itemValue;
|
|
99611
|
+
const unknown = [];
|
|
99612
|
+
const rejectUnsupported = (flag) => {
|
|
99613
|
+
throw new Error(`Unsupported option: ${flag}.`);
|
|
99614
|
+
};
|
|
99615
|
+
for (let index = 0;index < args.length; index += 1) {
|
|
99616
|
+
const arg = args[index];
|
|
99617
|
+
if (arg === "--id") {
|
|
99618
|
+
taskId = args[index + 1];
|
|
99619
|
+
if (!taskId) {
|
|
99620
|
+
throw new Error("Missing value for --id.");
|
|
99621
|
+
}
|
|
99622
|
+
index += 1;
|
|
99623
|
+
continue;
|
|
99624
|
+
}
|
|
99625
|
+
if (arg.startsWith("--id=")) {
|
|
99626
|
+
taskId = arg.slice("--id=".length);
|
|
99627
|
+
continue;
|
|
99628
|
+
}
|
|
99629
|
+
if (arg === "--item") {
|
|
99630
|
+
itemValue = args[index + 1];
|
|
99631
|
+
if (itemValue === undefined) {
|
|
99632
|
+
throw new Error("Missing value for --item.");
|
|
99633
|
+
}
|
|
99634
|
+
index += 1;
|
|
99635
|
+
continue;
|
|
99636
|
+
}
|
|
99637
|
+
if (arg.startsWith("--item=")) {
|
|
99638
|
+
itemValue = arg.slice("--item=".length);
|
|
99639
|
+
continue;
|
|
99640
|
+
}
|
|
99641
|
+
if (arg === "--schub-root" || arg === "--agent-root") {
|
|
99642
|
+
rejectUnsupported(arg);
|
|
99643
|
+
}
|
|
99644
|
+
if (arg.startsWith("--schub-root=")) {
|
|
99645
|
+
rejectUnsupported("--schub-root");
|
|
99646
|
+
}
|
|
99647
|
+
if (arg.startsWith("--agent-root=")) {
|
|
99648
|
+
rejectUnsupported("--agent-root");
|
|
99649
|
+
}
|
|
99650
|
+
unknown.push(arg);
|
|
99651
|
+
}
|
|
99652
|
+
if (unknown.length > 0) {
|
|
99653
|
+
throw new Error(`Unknown option(s): ${unknown.join(", ")}`);
|
|
99654
|
+
}
|
|
99655
|
+
if (!taskId) {
|
|
99656
|
+
throw new Error("Provide --id.");
|
|
99657
|
+
}
|
|
99658
|
+
if (itemValue === undefined || itemValue.trim() === "") {
|
|
99659
|
+
throw new Error("Provide --item.");
|
|
99660
|
+
}
|
|
99661
|
+
const item = Number(itemValue);
|
|
99662
|
+
if (!Number.isInteger(item) || item < 1) {
|
|
99663
|
+
throw new Error(`Invalid item '${itemValue}'. Use a positive integer.`);
|
|
99664
|
+
}
|
|
99665
|
+
const options = {
|
|
99666
|
+
taskId: taskId.trim().toUpperCase(),
|
|
99667
|
+
item
|
|
99668
|
+
};
|
|
99669
|
+
return options;
|
|
99670
|
+
};
|
|
99671
|
+
var findTaskById = (schubDir, taskId) => {
|
|
99672
|
+
const normalizedId = taskId.trim().toUpperCase();
|
|
99673
|
+
const tasks = listTasks(schubDir, TASK_STATUSES);
|
|
99674
|
+
const task = tasks.find((entry) => entry.id === normalizedId);
|
|
99675
|
+
if (!task) {
|
|
99676
|
+
throw new Error(`Task ${normalizedId} not found.`);
|
|
99677
|
+
}
|
|
99678
|
+
return task;
|
|
99679
|
+
};
|
|
99680
|
+
var runTasksCheck = (schubDir, args) => {
|
|
99681
|
+
if (!schubDir) {
|
|
99682
|
+
throw new Error("No .schub directory found.");
|
|
99683
|
+
}
|
|
99684
|
+
const options = parseTaskCheckOptions(args);
|
|
99685
|
+
const task = findTaskById(schubDir, options.taskId);
|
|
99686
|
+
const repoRoot = dirname10(schubDir);
|
|
99687
|
+
const taskPath = join15(repoRoot, task.path);
|
|
99688
|
+
const content = readFileSync12(taskPath, "utf8");
|
|
99689
|
+
const updated = toggleChecklistItem(content, options.item);
|
|
99690
|
+
writeFileSync7(taskPath, updated, "utf8");
|
|
99691
|
+
process.stdout.write(`[OK] Updated task ${task.id}: item ${options.item}
|
|
99692
|
+
`);
|
|
99693
|
+
};
|
|
99537
99694
|
// src/commands/tasks-implement.ts
|
|
99538
|
-
import { dirname as
|
|
99695
|
+
import { dirname as dirname11, join as join17 } from "node:path";
|
|
99696
|
+
|
|
99697
|
+
// src/features/context/prepare-implement-context.ts
|
|
99698
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync13 } from "node:fs";
|
|
99699
|
+
import { basename as basename5, join as join16 } from "node:path";
|
|
99700
|
+
var listMarkdownFiles = (root, relativeRoot = "") => {
|
|
99701
|
+
const entries = readdirSync7(join16(root, relativeRoot), { withFileTypes: true });
|
|
99702
|
+
const paths = [];
|
|
99703
|
+
for (const entry of entries) {
|
|
99704
|
+
if (entry.isDirectory()) {
|
|
99705
|
+
paths.push(...listMarkdownFiles(root, join16(relativeRoot, entry.name)));
|
|
99706
|
+
continue;
|
|
99707
|
+
}
|
|
99708
|
+
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
99709
|
+
paths.push(join16(relativeRoot, entry.name));
|
|
99710
|
+
}
|
|
99711
|
+
}
|
|
99712
|
+
return paths;
|
|
99713
|
+
};
|
|
99714
|
+
var readChangeMarkdown = (schubDir, changeId) => {
|
|
99715
|
+
const changeDir = join16(schubDir, "changes", changeId);
|
|
99716
|
+
const entries = listMarkdownFiles(changeDir);
|
|
99717
|
+
return entries.sort().map((relativePath) => {
|
|
99718
|
+
const filePath = join16(changeDir, relativePath);
|
|
99719
|
+
return {
|
|
99720
|
+
filename: basename5(relativePath),
|
|
99721
|
+
content: readFileSync13(filePath, "utf8")
|
|
99722
|
+
};
|
|
99723
|
+
});
|
|
99724
|
+
};
|
|
99725
|
+
var buildPrompt = (taskContent, changeMarkdown) => {
|
|
99726
|
+
const sections = [taskContent.trimEnd()];
|
|
99727
|
+
for (const entry of changeMarkdown) {
|
|
99728
|
+
const content = entry.content.trimEnd();
|
|
99729
|
+
sections.push(`# ${entry.filename}
|
|
99730
|
+
|
|
99731
|
+
${content}`);
|
|
99732
|
+
}
|
|
99733
|
+
return sections.join(`
|
|
99734
|
+
|
|
99735
|
+
`);
|
|
99736
|
+
};
|
|
99737
|
+
var prepareImplementContext = (schubDir, taskPath) => {
|
|
99738
|
+
const taskContent = readFileSync13(taskPath, "utf8");
|
|
99739
|
+
const { data } = readFrontmatter(taskContent);
|
|
99740
|
+
const changeIdValue = data.change_id;
|
|
99741
|
+
const changeId = typeof changeIdValue === "string" ? changeIdValue.trim() : "";
|
|
99742
|
+
if (!changeId) {
|
|
99743
|
+
return taskContent.trimEnd();
|
|
99744
|
+
}
|
|
99745
|
+
const changeMarkdown = readChangeMarkdown(schubDir, changeId);
|
|
99746
|
+
return buildPrompt(taskContent, changeMarkdown);
|
|
99747
|
+
};
|
|
99539
99748
|
|
|
99540
99749
|
// src/opencode.ts
|
|
99541
99750
|
import { spawn, spawnSync as spawnSync3 } from "node:child_process";
|
|
99542
|
-
import { basename as
|
|
99751
|
+
import { basename as basename6 } from "node:path";
|
|
99543
99752
|
var opencodeSpawnOptions = { stdio: "ignore", detached: true };
|
|
99544
99753
|
var spawnOpencode = (command2, args, spawner, overrides) => {
|
|
99545
99754
|
const child = spawner(command2, args, { ...opencodeSpawnOptions, ...overrides });
|
|
@@ -99556,7 +99765,7 @@ var runOpencodeCommand = (args) => {
|
|
|
99556
99765
|
var buildSessionTitle = (repoRoot, label, id, detail) => {
|
|
99557
99766
|
const trimmedDetail = detail?.trim();
|
|
99558
99767
|
const suffix = trimmedDetail ? ` ${trimmedDetail}` : "";
|
|
99559
|
-
return `(${
|
|
99768
|
+
return `(${basename6(repoRoot)}) ${label} ${id}${suffix}`;
|
|
99560
99769
|
};
|
|
99561
99770
|
var formatChangeId = (value) => {
|
|
99562
99771
|
const match = value.match(/^([Cc]\d+)_/);
|
|
@@ -99569,11 +99778,16 @@ var buildReviewCommand = (changeId, repoRoot, changeTitle) => {
|
|
|
99569
99778
|
args: ["--prompt", `review ${changeId}`, "--title", title]
|
|
99570
99779
|
};
|
|
99571
99780
|
};
|
|
99572
|
-
var buildImplementCommand = (taskId, repoRoot,
|
|
99573
|
-
const title = buildSessionTitle(repoRoot, "Implement", taskId,
|
|
99781
|
+
var buildImplementCommand = (taskId, repoRoot, options = {}) => {
|
|
99782
|
+
const title = buildSessionTitle(repoRoot, "Implement", taskId, options.title);
|
|
99783
|
+
const args = ["run", "implement", taskId];
|
|
99784
|
+
if (typeof options.prompt === "string" && options.prompt.trim() !== "") {
|
|
99785
|
+
args.push("--prompt", options.prompt);
|
|
99786
|
+
}
|
|
99787
|
+
args.push("--title", title);
|
|
99574
99788
|
return {
|
|
99575
99789
|
command: "opencode",
|
|
99576
|
-
args
|
|
99790
|
+
args
|
|
99577
99791
|
};
|
|
99578
99792
|
};
|
|
99579
99793
|
var buildCreateTasksCommand = (changeId, repoRoot) => {
|
|
@@ -99646,8 +99860,8 @@ var launchOpencodeReview = (changeId, repoRoot, overrides, spawner = spawn) => {
|
|
|
99646
99860
|
const { command: command2, args } = buildReviewCommand(changeId, repoRoot);
|
|
99647
99861
|
return spawnOpencode(command2, args, spawner, overrides);
|
|
99648
99862
|
};
|
|
99649
|
-
var launchOpencodeImplement = (taskId, repoRoot,
|
|
99650
|
-
const { command: command2, args } = buildImplementCommand(taskId, repoRoot,
|
|
99863
|
+
var launchOpencodeImplement = (taskId, repoRoot, options, overrides, spawner = spawn) => {
|
|
99864
|
+
const { command: command2, args } = buildImplementCommand(taskId, repoRoot, options);
|
|
99651
99865
|
return spawnOpencode(command2, args, spawner, overrides);
|
|
99652
99866
|
};
|
|
99653
99867
|
var launchOpencodeCreateTasks = (changeId, repoRoot, overrides, spawner = spawn) => {
|
|
@@ -99734,7 +99948,7 @@ var runTasksImplement = (schubDir, args) => {
|
|
|
99734
99948
|
}
|
|
99735
99949
|
const options = parseTaskImplementOptions(args);
|
|
99736
99950
|
const assigned = assignTaskToWip(schubDir, options.taskId);
|
|
99737
|
-
const repoRoot =
|
|
99951
|
+
const repoRoot = dirname11(schubDir);
|
|
99738
99952
|
let launchRoot = repoRoot;
|
|
99739
99953
|
if (options.mode === "worktree") {
|
|
99740
99954
|
const worktree = createTaskWorktree({
|
|
@@ -99746,7 +99960,9 @@ var runTasksImplement = (schubDir, args) => {
|
|
|
99746
99960
|
launchRoot = worktree.worktreePath;
|
|
99747
99961
|
}
|
|
99748
99962
|
}
|
|
99749
|
-
|
|
99963
|
+
const taskPath = join17(repoRoot, assigned.path);
|
|
99964
|
+
const prompt = prepareImplementContext(schubDir, taskPath);
|
|
99965
|
+
launchOpencodeImplement(options.taskId, repoRoot, { title: assigned.title, prompt }, {
|
|
99750
99966
|
env: { ...process.env, SCHUB_CWD: launchRoot }
|
|
99751
99967
|
});
|
|
99752
99968
|
process.stdout.write(`[OK] Assigned task ${assigned.id}: ${assigned.status}
|
|
@@ -99756,7 +99972,7 @@ var runTasksImplement = (schubDir, args) => {
|
|
|
99756
99972
|
// src/commands/tasks.ts
|
|
99757
99973
|
var parseStatusFilter = (value) => {
|
|
99758
99974
|
if (!value) {
|
|
99759
|
-
return [...
|
|
99975
|
+
return [...DEFAULT_TASK_STATUSES];
|
|
99760
99976
|
}
|
|
99761
99977
|
const normalized = value.split(",").map((status) => status.trim().toLowerCase()).filter(Boolean);
|
|
99762
99978
|
const allowed = new Set(TASK_STATUSES);
|
|
@@ -99867,7 +100083,7 @@ var parseTaskCreateOptions = (args) => {
|
|
|
99867
100083
|
const options = { changeId, status, titles, overwrite };
|
|
99868
100084
|
return options;
|
|
99869
100085
|
};
|
|
99870
|
-
var BACKLOG_UPDATE_STATUSES = ["reviewed", "archived"];
|
|
100086
|
+
var BACKLOG_UPDATE_STATUSES = ["reviewed", "rejected", "archived"];
|
|
99871
100087
|
var parseTaskUpdateOptions = (args) => {
|
|
99872
100088
|
let statusValue;
|
|
99873
100089
|
const taskIds = [];
|
|
@@ -99921,7 +100137,7 @@ var parseTaskUpdateOptions = (args) => {
|
|
|
99921
100137
|
}
|
|
99922
100138
|
const normalizedStatus = statusValue.trim().toLowerCase();
|
|
99923
100139
|
if (!BACKLOG_UPDATE_STATUSES.includes(normalizedStatus)) {
|
|
99924
|
-
throw new Error(`Invalid status '${statusValue}'. Use reviewed or archived.`);
|
|
100140
|
+
throw new Error(`Invalid status '${statusValue}'. Use reviewed, rejected, or archived.`);
|
|
99925
100141
|
}
|
|
99926
100142
|
const normalizedIds = taskIds.map((id) => id.trim()).filter(Boolean);
|
|
99927
100143
|
if (normalizedIds.length === 0) {
|
|
@@ -99973,35 +100189,37 @@ var runTasksCreate = (args, startDir) => {
|
|
|
99973
100189
|
}
|
|
99974
100190
|
};
|
|
99975
100191
|
// src/tui/index.ts
|
|
99976
|
-
var
|
|
100192
|
+
var import_react62 = __toESM(require_react(), 1);
|
|
99977
100193
|
|
|
99978
100194
|
// src/tui/app.tsx
|
|
99979
|
-
import { readFileSync as
|
|
100195
|
+
import { readFileSync as readFileSync15 } from "node:fs";
|
|
99980
100196
|
import { homedir as homedir2 } from "node:os";
|
|
99981
|
-
import { basename as
|
|
99982
|
-
var
|
|
100197
|
+
import { basename as basename7, dirname as dirname14, resolve as resolve13 } from "node:path";
|
|
100198
|
+
var import_react61 = __toESM(require_react(), 1);
|
|
99983
100199
|
// package.json
|
|
99984
100200
|
var package_default = {
|
|
99985
100201
|
name: "schub",
|
|
99986
|
-
version: "0.1.
|
|
100202
|
+
version: "0.1.7",
|
|
99987
100203
|
type: "module",
|
|
99988
100204
|
bin: {
|
|
99989
|
-
schub: "./
|
|
100205
|
+
schub: "./dist/index.js"
|
|
99990
100206
|
},
|
|
99991
100207
|
files: [
|
|
99992
100208
|
"dist",
|
|
99993
|
-
"src",
|
|
99994
100209
|
"skills",
|
|
99995
100210
|
"templates"
|
|
99996
100211
|
],
|
|
99997
100212
|
scripts: {
|
|
99998
100213
|
schub: "bun ./src/index.ts",
|
|
99999
|
-
prepublishOnly: "
|
|
100000
|
-
build:
|
|
100214
|
+
prepublishOnly: "bun run build",
|
|
100215
|
+
build: 'bun build ./src/index.ts --outfile dist/index.js --target node --banner "#!/usr/bin/env node"',
|
|
100001
100216
|
lint: "bunx @biomejs/biome lint .",
|
|
100002
100217
|
format: "bunx @biomejs/biome format --write .",
|
|
100003
100218
|
test: "bun test"
|
|
100004
100219
|
},
|
|
100220
|
+
engines: {
|
|
100221
|
+
node: ">=24"
|
|
100222
|
+
},
|
|
100005
100223
|
dependencies: {
|
|
100006
100224
|
"@inkjs/ui": "^2.0.0",
|
|
100007
100225
|
chalk: "^5.6.2",
|
|
@@ -100028,108 +100246,240 @@ var copyToClipboard = (value) => {
|
|
|
100028
100246
|
spawnSync4("pbcopy", [], { input: value });
|
|
100029
100247
|
};
|
|
100030
100248
|
|
|
100031
|
-
// src/tui/components/
|
|
100032
|
-
var
|
|
100033
|
-
|
|
100034
|
-
|
|
100035
|
-
|
|
100036
|
-
|
|
100037
|
-
|
|
100038
|
-
|
|
100039
|
-
|
|
100040
|
-
|
|
100041
|
-
|
|
100042
|
-
|
|
100043
|
-
|
|
100044
|
-
|
|
100045
|
-
|
|
100046
|
-
|
|
100047
|
-
|
|
100048
|
-
return () => {
|
|
100049
|
-
clearInterval(interval);
|
|
100050
|
-
};
|
|
100051
|
-
}, [enabled, refreshIntervalMs]);
|
|
100249
|
+
// src/tui/components/status-color.ts
|
|
100250
|
+
var STATUS_COLORS = {
|
|
100251
|
+
draft: "gray",
|
|
100252
|
+
"in-review": "yellow",
|
|
100253
|
+
accepted: "blue",
|
|
100254
|
+
"no-tasks": "gray",
|
|
100255
|
+
wip: "blue",
|
|
100256
|
+
done: "green",
|
|
100257
|
+
archived: "gray",
|
|
100258
|
+
backlog: "gray",
|
|
100259
|
+
reviewed: "yellow",
|
|
100260
|
+
ready: "yellow",
|
|
100261
|
+
blocked: "red",
|
|
100262
|
+
rejected: "red"
|
|
100263
|
+
};
|
|
100264
|
+
var statusColor = (status) => {
|
|
100265
|
+
return status ? STATUS_COLORS[status] : "gray";
|
|
100052
100266
|
};
|
|
100053
100267
|
|
|
100054
|
-
// src/tui/components/
|
|
100268
|
+
// src/tui/components/list-item.tsx
|
|
100055
100269
|
var jsx_dev_runtime3 = __toESM(require_jsx_dev_runtime(), 1);
|
|
100056
|
-
var
|
|
100057
|
-
var
|
|
100058
|
-
|
|
100059
|
-
|
|
100060
|
-
|
|
100270
|
+
var TITLE_WIDTH = 40;
|
|
100271
|
+
var STATUS_LABELS = {
|
|
100272
|
+
draft: "DRAFT",
|
|
100273
|
+
"in-review": "IN REVIEW",
|
|
100274
|
+
accepted: "ACCEPTED",
|
|
100275
|
+
"no-tasks": "NO TASKS",
|
|
100276
|
+
wip: "WIP",
|
|
100277
|
+
done: "DONE",
|
|
100278
|
+
archived: "ARCHIVED",
|
|
100279
|
+
backlog: "BACKLOG",
|
|
100280
|
+
reviewed: "REVIEWED",
|
|
100281
|
+
ready: "READY",
|
|
100282
|
+
blocked: "BLOCKED",
|
|
100283
|
+
rejected: "REJECTED"
|
|
100284
|
+
};
|
|
100285
|
+
var CHANGE_STATUS_OPTIONS = [
|
|
100286
|
+
{ label: "Draft", status: "Draft" },
|
|
100287
|
+
{ label: "Pending Review", status: "Pending Review" },
|
|
100288
|
+
{ label: "Accepted", status: "Accepted" },
|
|
100289
|
+
{ label: "Implementing", status: "Implementing" },
|
|
100290
|
+
{ label: "Done", status: "Done" },
|
|
100291
|
+
{ label: "Rejected", status: "Rejected" },
|
|
100292
|
+
{ label: "Archived", status: "Archived" }
|
|
100293
|
+
];
|
|
100294
|
+
var CHANGE_STATUS_VALUE = {
|
|
100295
|
+
draft: "Draft",
|
|
100296
|
+
"in-review": "Pending Review",
|
|
100297
|
+
accepted: "Accepted",
|
|
100298
|
+
"no-tasks": "Accepted",
|
|
100299
|
+
wip: "Implementing",
|
|
100300
|
+
done: "Done",
|
|
100301
|
+
rejected: "Rejected",
|
|
100302
|
+
archived: "Archived"
|
|
100303
|
+
};
|
|
100304
|
+
var TASK_STATUS_OPTIONS = {
|
|
100305
|
+
backlog: [
|
|
100306
|
+
{ label: "Reviewed", status: "reviewed" },
|
|
100307
|
+
{ label: "Archive", status: "archived" }
|
|
100308
|
+
],
|
|
100309
|
+
reviewed: [
|
|
100310
|
+
{ label: "Backlog", status: "backlog" },
|
|
100311
|
+
{ label: "Archive", status: "archived" }
|
|
100312
|
+
]
|
|
100313
|
+
};
|
|
100314
|
+
var isTaskListItem = (item) => ("sourceStatus" in item);
|
|
100315
|
+
var buildChangeStatusOptions = (status) => {
|
|
100316
|
+
const currentStatus = CHANGE_STATUS_VALUE[status];
|
|
100317
|
+
return CHANGE_STATUS_OPTIONS.filter((option) => option.status !== currentStatus);
|
|
100318
|
+
};
|
|
100319
|
+
var buildTaskStatusOptions = (item) => {
|
|
100320
|
+
if (item.status === "draft") {
|
|
100321
|
+
return null;
|
|
100322
|
+
}
|
|
100323
|
+
if (item.sourceStatus === "backlog") {
|
|
100324
|
+
return TASK_STATUS_OPTIONS.backlog;
|
|
100061
100325
|
}
|
|
100062
|
-
|
|
100063
|
-
|
|
100064
|
-
if (visibleTasks.length === 0) {
|
|
100065
|
-
return { visibleTasks, graphLines: [] };
|
|
100326
|
+
if (item.sourceStatus === "reviewed") {
|
|
100327
|
+
return TASK_STATUS_OPTIONS.reviewed;
|
|
100066
100328
|
}
|
|
100067
|
-
|
|
100068
|
-
const graphLines = renderTaskGraphLines(graph);
|
|
100069
|
-
return { visibleTasks, graphLines };
|
|
100329
|
+
return null;
|
|
100070
100330
|
};
|
|
100071
|
-
|
|
100072
|
-
|
|
100073
|
-
|
|
100074
|
-
|
|
100075
|
-
|
|
100076
|
-
|
|
100077
|
-
|
|
100331
|
+
var buildStatusModalState = (item) => {
|
|
100332
|
+
if (isTaskListItem(item)) {
|
|
100333
|
+
const options = buildTaskStatusOptions(item);
|
|
100334
|
+
if (!options) {
|
|
100335
|
+
return null;
|
|
100336
|
+
}
|
|
100337
|
+
return { itemId: item.id, itemKind: "task", selection: 0, options };
|
|
100338
|
+
}
|
|
100339
|
+
return {
|
|
100340
|
+
itemId: item.id,
|
|
100341
|
+
itemKind: "change",
|
|
100342
|
+
selection: 0,
|
|
100343
|
+
options: buildChangeStatusOptions(item.status)
|
|
100078
100344
|
};
|
|
100079
|
-
|
|
100080
|
-
|
|
100081
|
-
|
|
100082
|
-
|
|
100083
|
-
|
|
100084
|
-
|
|
100085
|
-
children: "No .schub directory found."
|
|
100086
|
-
}, undefined, false, undefined, this)
|
|
100087
|
-
}, undefined, false, undefined, this);
|
|
100345
|
+
};
|
|
100346
|
+
var formatStatusLabel = (status) => STATUS_LABELS[status] ?? status.toUpperCase();
|
|
100347
|
+
var formatTitle = (value) => {
|
|
100348
|
+
const trimmed = value.trim();
|
|
100349
|
+
if (!trimmed) {
|
|
100350
|
+
return "";
|
|
100088
100351
|
}
|
|
100089
|
-
if (
|
|
100090
|
-
return
|
|
100091
|
-
|
|
100092
|
-
|
|
100093
|
-
|
|
100094
|
-
children: "No tasks found in .schub"
|
|
100095
|
-
}, undefined, false, undefined, this)
|
|
100096
|
-
}, undefined, false, undefined, this);
|
|
100352
|
+
if (trimmed.length <= TITLE_WIDTH) {
|
|
100353
|
+
return trimmed;
|
|
100354
|
+
}
|
|
100355
|
+
if (TITLE_WIDTH <= 1) {
|
|
100356
|
+
return "…";
|
|
100097
100357
|
}
|
|
100358
|
+
return `${trimmed.slice(0, TITLE_WIDTH - 1).trimEnd()}…`;
|
|
100359
|
+
};
|
|
100360
|
+
function ListItem({ id, status, title, selected, progress, secondaryId, pending }) {
|
|
100361
|
+
const statusLabel = formatStatusLabel(status);
|
|
100362
|
+
const formattedTitle = formatTitle(title);
|
|
100363
|
+
const showProgress = status === "wip" && progress && progress.total > 0;
|
|
100364
|
+
const progressValue = showProgress ? Math.min(100, Math.max(0, Math.round(progress.completed / progress.total * 100))) : 0;
|
|
100365
|
+
const showPending = Boolean(pending);
|
|
100366
|
+
return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
100367
|
+
marginLeft: 1,
|
|
100368
|
+
flexDirection: "row",
|
|
100369
|
+
children: [
|
|
100370
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
100371
|
+
color: selected ? "blue" : "gray",
|
|
100372
|
+
children: selected ? "›" : " "
|
|
100373
|
+
}, undefined, false, undefined, this),
|
|
100374
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
100375
|
+
flexDirection: "row",
|
|
100376
|
+
gap: 1,
|
|
100377
|
+
children: [
|
|
100378
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Badge, {
|
|
100379
|
+
color: statusColor(status),
|
|
100380
|
+
children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
100381
|
+
color: "darkGray",
|
|
100382
|
+
backgroundColor: statusColor(status),
|
|
100383
|
+
children: statusLabel
|
|
100384
|
+
}, undefined, false, undefined, this)
|
|
100385
|
+
}, undefined, false, undefined, this),
|
|
100386
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Badge, {
|
|
100387
|
+
color: "whiteBright",
|
|
100388
|
+
children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
100389
|
+
backgroundColor: "whiteBright",
|
|
100390
|
+
children: id
|
|
100391
|
+
}, undefined, false, undefined, this)
|
|
100392
|
+
}, undefined, false, undefined, this),
|
|
100393
|
+
secondaryId ? /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Badge, {
|
|
100394
|
+
color: "grey",
|
|
100395
|
+
children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
100396
|
+
backgroundColor: "grey",
|
|
100397
|
+
children: secondaryId
|
|
100398
|
+
}, undefined, false, undefined, this)
|
|
100399
|
+
}, undefined, false, undefined, this) : null,
|
|
100400
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
100401
|
+
width: TITLE_WIDTH,
|
|
100402
|
+
children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
100403
|
+
color: "gray",
|
|
100404
|
+
bold: selected,
|
|
100405
|
+
children: formattedTitle
|
|
100406
|
+
}, undefined, false, undefined, this)
|
|
100407
|
+
}, undefined, false, undefined, this),
|
|
100408
|
+
showProgress ? /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
100409
|
+
marginLeft: 1,
|
|
100410
|
+
width: 6,
|
|
100411
|
+
children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(ProgressBar, {
|
|
100412
|
+
value: progressValue
|
|
100413
|
+
}, undefined, false, undefined, this)
|
|
100414
|
+
}, undefined, false, undefined, this) : null,
|
|
100415
|
+
showPending ? /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Spinner, {}, undefined, false, undefined, this) : null
|
|
100416
|
+
]
|
|
100417
|
+
}, undefined, true, undefined, this)
|
|
100418
|
+
]
|
|
100419
|
+
}, undefined, true, undefined, this);
|
|
100420
|
+
}
|
|
100421
|
+
var ListItemStatusModal = ({ statusModal }) => {
|
|
100098
100422
|
return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
100423
|
+
backgroundColor: "darkGray",
|
|
100099
100424
|
flexDirection: "column",
|
|
100100
|
-
|
|
100101
|
-
|
|
100102
|
-
|
|
100103
|
-
|
|
100104
|
-
|
|
100105
|
-
|
|
100106
|
-
|
|
100107
|
-
|
|
100108
|
-
children: "Dependency Plan"
|
|
100109
|
-
}, undefined, false, undefined, this)
|
|
100110
|
-
}, undefined, false, undefined, this),
|
|
100111
|
-
planData.graphLines.map((line, index) => {
|
|
100112
|
-
const hasProgress = line.status === "wip" && line.checklistTotal !== undefined && line.checklistTotal > 0 && line.checklistRemaining !== undefined;
|
|
100113
|
-
const progressValue = hasProgress ? Math.round((line.checklistTotal - line.checklistRemaining) / line.checklistTotal * 100) : 0;
|
|
100425
|
+
paddingX: 2,
|
|
100426
|
+
paddingY: 1,
|
|
100427
|
+
children: [
|
|
100428
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
100429
|
+
flexDirection: "column",
|
|
100430
|
+
marginBottom: 1,
|
|
100431
|
+
children: statusModal.options.map((option, index) => {
|
|
100432
|
+
const selected = index === statusModal.selection;
|
|
100114
100433
|
return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
100115
100434
|
children: [
|
|
100116
100435
|
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
100117
|
-
color: "
|
|
100118
|
-
children:
|
|
100436
|
+
color: selected ? "blue" : "gray",
|
|
100437
|
+
children: selected ? "›" : " "
|
|
100119
100438
|
}, undefined, false, undefined, this),
|
|
100120
|
-
|
|
100121
|
-
|
|
100122
|
-
children:
|
|
100123
|
-
|
|
100124
|
-
|
|
100125
|
-
|
|
100439
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
100440
|
+
color: "white",
|
|
100441
|
+
children: [
|
|
100442
|
+
" ",
|
|
100443
|
+
option.label
|
|
100444
|
+
]
|
|
100445
|
+
}, undefined, true, undefined, this)
|
|
100126
100446
|
]
|
|
100127
|
-
},
|
|
100447
|
+
}, option.status, true, undefined, this);
|
|
100128
100448
|
})
|
|
100129
|
-
|
|
100130
|
-
|
|
100131
|
-
|
|
100132
|
-
|
|
100449
|
+
}, undefined, false, undefined, this),
|
|
100450
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
100451
|
+
alignItems: "flex-end",
|
|
100452
|
+
children: [
|
|
100453
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
100454
|
+
marginRight: 2,
|
|
100455
|
+
children: [
|
|
100456
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
100457
|
+
color: "white",
|
|
100458
|
+
children: "enter"
|
|
100459
|
+
}, undefined, false, undefined, this),
|
|
100460
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
100461
|
+
color: "gray",
|
|
100462
|
+
children: " confirm"
|
|
100463
|
+
}, undefined, false, undefined, this)
|
|
100464
|
+
]
|
|
100465
|
+
}, undefined, true, undefined, this),
|
|
100466
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
100467
|
+
children: [
|
|
100468
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
100469
|
+
color: "white",
|
|
100470
|
+
children: "esc"
|
|
100471
|
+
}, undefined, false, undefined, this),
|
|
100472
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
100473
|
+
color: "gray",
|
|
100474
|
+
children: " cancel"
|
|
100475
|
+
}, undefined, false, undefined, this)
|
|
100476
|
+
]
|
|
100477
|
+
}, undefined, true, undefined, this)
|
|
100478
|
+
]
|
|
100479
|
+
}, undefined, true, undefined, this)
|
|
100480
|
+
]
|
|
100481
|
+
}, undefined, true, undefined, this);
|
|
100482
|
+
};
|
|
100133
100483
|
|
|
100134
100484
|
// ../../node_modules/.bun/marked@9.1.6/node_modules/marked/lib/marked.esm.js
|
|
100135
100485
|
function _getDefaults() {
|
|
@@ -102656,7 +103006,7 @@ function sanitizeTab(tab2, fallbackTab) {
|
|
|
102656
103006
|
}
|
|
102657
103007
|
|
|
102658
103008
|
// src/tui/components/preview-page.tsx
|
|
102659
|
-
var
|
|
103009
|
+
var import_react58 = __toESM(require_react(), 1);
|
|
102660
103010
|
|
|
102661
103011
|
// src/tui/shared/input.ts
|
|
102662
103012
|
var normalizeTypedValue = (input, keyName2, keySequence) => {
|
|
@@ -102689,10 +103039,10 @@ var buildInputState = (input, key) => {
|
|
|
102689
103039
|
const isEnter = key.return || key.enter || keyName2 === "return" || keyName2 === "enter" || keySequence === "\r" || keySequence === `
|
|
102690
103040
|
` || input === "\r" || input === `
|
|
102691
103041
|
`;
|
|
102692
|
-
const isDownArrow = key.downArrow || Boolean(keySequence?.includes("[B")) || input.includes("[B");
|
|
102693
|
-
const isUpArrow = key.upArrow || Boolean(keySequence?.includes("[A")) || input.includes("[A");
|
|
102694
|
-
const isLeftArrow = key.leftArrow || Boolean(keySequence?.includes("[D")) || input.includes("[D");
|
|
102695
|
-
const isRightArrow = key.rightArrow || Boolean(keySequence?.includes("[C")) || input.includes("[C");
|
|
103042
|
+
const isDownArrow = key.downArrow || keyName2 === "down" || keyName2 === "downArrow" || Boolean(keySequence?.includes("[B")) || Boolean(keySequence?.includes("OB")) || input.includes("[B") || input.includes("OB");
|
|
103043
|
+
const isUpArrow = key.upArrow || keyName2 === "up" || keyName2 === "upArrow" || Boolean(keySequence?.includes("[A")) || Boolean(keySequence?.includes("OA")) || input.includes("[A") || input.includes("OA");
|
|
103044
|
+
const isLeftArrow = key.leftArrow || keyName2 === "left" || keyName2 === "leftArrow" || Boolean(keySequence?.includes("[D")) || Boolean(keySequence?.includes("OD")) || input.includes("[D") || input.includes("OD");
|
|
103045
|
+
const isRightArrow = key.rightArrow || keyName2 === "right" || keyName2 === "rightArrow" || Boolean(keySequence?.includes("[C")) || Boolean(keySequence?.includes("OC")) || input.includes("[C") || input.includes("OC");
|
|
102696
103046
|
return {
|
|
102697
103047
|
input,
|
|
102698
103048
|
lowerInput,
|
|
@@ -102713,13 +103063,14 @@ var jsx_dev_runtime4 = __toESM(require_jsx_dev_runtime(), 1);
|
|
|
102713
103063
|
var HEADER_HEIGHT = 2;
|
|
102714
103064
|
function PreviewPage({ fileName, markdown, height, onClose }) {
|
|
102715
103065
|
const { stdout } = use_stdout_default();
|
|
102716
|
-
const
|
|
103066
|
+
const { body } = import_react58.default.useMemo(() => readFrontmatter(markdown), [markdown]);
|
|
103067
|
+
const lines = import_react58.default.useMemo(() => body.split(/\r?\n/), [body]);
|
|
102717
103068
|
const availableHeight = height ?? stdout.rows ?? 0;
|
|
102718
103069
|
const visibleLineCount = Math.max(0, availableHeight - HEADER_HEIGHT);
|
|
102719
103070
|
const maxOffset = visibleLineCount > 0 ? Math.max(0, lines.length - visibleLineCount) : 0;
|
|
102720
|
-
const [scrollOffset, setScrollOffset] =
|
|
103071
|
+
const [scrollOffset, setScrollOffset] = import_react58.default.useState(0);
|
|
102721
103072
|
const clampOffset = (value) => Math.min(maxOffset, Math.max(0, value));
|
|
102722
|
-
|
|
103073
|
+
import_react58.default.useEffect(() => {
|
|
102723
103074
|
setScrollOffset((current) => Math.min(maxOffset, Math.max(0, current)));
|
|
102724
103075
|
}, [maxOffset]);
|
|
102725
103076
|
use_input_default((input, key) => {
|
|
@@ -102739,7 +103090,7 @@ function PreviewPage({ fileName, markdown, height, onClose }) {
|
|
|
102739
103090
|
const visibleLines = lines.slice(scrollOffset, scrollOffset + visibleLineCount);
|
|
102740
103091
|
const visibleMarkdown = visibleLines.join(`
|
|
102741
103092
|
`);
|
|
102742
|
-
const renderedMarkdown =
|
|
103093
|
+
const renderedMarkdown = import_react58.default.useMemo(() => {
|
|
102743
103094
|
setOptions({ renderer: new marked_terminal_default });
|
|
102744
103095
|
return parse(visibleMarkdown).trim();
|
|
102745
103096
|
}, [visibleMarkdown]);
|
|
@@ -102772,21 +103123,22 @@ function PreviewPage({ fileName, markdown, height, onClose }) {
|
|
|
102772
103123
|
}
|
|
102773
103124
|
|
|
102774
103125
|
// src/tui/components/status-view-data.ts
|
|
102775
|
-
import { readFileSync as
|
|
102776
|
-
import { dirname as
|
|
103126
|
+
import { readFileSync as readFileSync14 } from "node:fs";
|
|
103127
|
+
import { dirname as dirname12, join as join18, normalize as normalize2, sep } from "node:path";
|
|
102777
103128
|
var TASK_STATUS_LABELS = {
|
|
102778
103129
|
backlog: "Backlog",
|
|
102779
103130
|
reviewed: "Reviewed",
|
|
102780
103131
|
wip: "WIP",
|
|
102781
103132
|
blocked: "Blocked",
|
|
102782
103133
|
done: "Done",
|
|
103134
|
+
rejected: "Rejected",
|
|
102783
103135
|
archived: "Archived"
|
|
102784
103136
|
};
|
|
102785
103137
|
var ACTIVE_TASK_STATUSES2 = ["blocked", "wip", "reviewed", "backlog"];
|
|
102786
103138
|
var CHANGE_TASK_STATUSES = [...ACTIVE_TASK_STATUSES2, "done"];
|
|
102787
103139
|
var READY_TO_IMPLEMENT_STATUSES = new Set(["reviewed"]);
|
|
102788
103140
|
var AUTO_MARK_STATUSES = new Set(["accepted", "wip"]);
|
|
102789
|
-
var
|
|
103141
|
+
var compareText = (left2, right2) => left2.localeCompare(right2, undefined, { sensitivity: "base" });
|
|
102790
103142
|
var idSortValue = (value) => {
|
|
102791
103143
|
const match = value.match(/\d+/);
|
|
102792
103144
|
return match ? Number(match[0]) : Number.POSITIVE_INFINITY;
|
|
@@ -102796,7 +103148,7 @@ var compareIds = (left2, right2) => {
|
|
|
102796
103148
|
if (numberDiff !== 0) {
|
|
102797
103149
|
return numberDiff;
|
|
102798
103150
|
}
|
|
102799
|
-
return
|
|
103151
|
+
return compareText(left2, right2);
|
|
102800
103152
|
};
|
|
102801
103153
|
var taskSortName = (task) => {
|
|
102802
103154
|
if (task.status === "blocked") {
|
|
@@ -102817,7 +103169,7 @@ var sortByTaskIdThenName = (left2, right2) => {
|
|
|
102817
103169
|
if (idCompare !== 0) {
|
|
102818
103170
|
return idCompare;
|
|
102819
103171
|
}
|
|
102820
|
-
return
|
|
103172
|
+
return compareText(taskSortName(left2), taskSortName(right2));
|
|
102821
103173
|
};
|
|
102822
103174
|
var formatChangeId2 = (value) => {
|
|
102823
103175
|
const match = value.match(/^([Cc]\d+)_/);
|
|
@@ -102839,7 +103191,7 @@ var sortByChangeIdThenName = (left2, right2) => {
|
|
|
102839
103191
|
if (idCompare !== 0) {
|
|
102840
103192
|
return idCompare;
|
|
102841
103193
|
}
|
|
102842
|
-
return
|
|
103194
|
+
return compareText(changeSortName(left2), changeSortName(right2));
|
|
102843
103195
|
};
|
|
102844
103196
|
var buildChecklistProgress = (task) => {
|
|
102845
103197
|
if (task.status !== "wip") {
|
|
@@ -102879,7 +103231,7 @@ var buildChangeTaskCounts = (tasks) => {
|
|
|
102879
103231
|
return changeTaskCounts;
|
|
102880
103232
|
};
|
|
102881
103233
|
var hasDraftTokens = (repoRoot, task) => {
|
|
102882
|
-
const content =
|
|
103234
|
+
const content = readFileSync14(join18(repoRoot, task.path), "utf8");
|
|
102883
103235
|
return hasUnimplementedTemplateTokens(content);
|
|
102884
103236
|
};
|
|
102885
103237
|
var isReadyTask = (task, tasksById) => {
|
|
@@ -102905,6 +103257,9 @@ var deriveChangeListStatus = (change, counts) => {
|
|
|
102905
103257
|
if (normalized.includes("done")) {
|
|
102906
103258
|
return "done";
|
|
102907
103259
|
}
|
|
103260
|
+
if (normalized.includes("reject")) {
|
|
103261
|
+
return "rejected";
|
|
103262
|
+
}
|
|
102908
103263
|
if (normalized.includes("review")) {
|
|
102909
103264
|
return "in-review";
|
|
102910
103265
|
}
|
|
@@ -102925,7 +103280,7 @@ var deriveChangeListStatus = (change, counts) => {
|
|
|
102925
103280
|
}
|
|
102926
103281
|
return "accepted";
|
|
102927
103282
|
};
|
|
102928
|
-
var
|
|
103283
|
+
var groupChangesByStatus2 = (changes) => {
|
|
102929
103284
|
const grouped = new Map;
|
|
102930
103285
|
for (const change of changes) {
|
|
102931
103286
|
const key = `${change.statusOrder}-${change.statusLabel}`;
|
|
@@ -102942,7 +103297,7 @@ var groupChangesByStatus = (changes) => {
|
|
|
102942
103297
|
if (orderCompare !== 0) {
|
|
102943
103298
|
return orderCompare;
|
|
102944
103299
|
}
|
|
102945
|
-
return
|
|
103300
|
+
return compareText(left2.label, right2.label);
|
|
102946
103301
|
});
|
|
102947
103302
|
for (const group of sorted) {
|
|
102948
103303
|
group.items.sort(sortByChangeIdThenName);
|
|
@@ -102956,7 +103311,7 @@ var buildShowAllGroups = (schubDir) => {
|
|
|
102956
103311
|
const allTasks = loadTaskDependencies(schubDir, CHANGE_TASK_STATUSES);
|
|
102957
103312
|
const changeTaskCounts = buildChangeTaskCounts(allTasks);
|
|
102958
103313
|
const changes = listChangeOverview(schubDir).map((change) => addChangeProgress(change, changeTaskCounts.get(change.id)));
|
|
102959
|
-
return
|
|
103314
|
+
return groupChangesByStatus2(changes);
|
|
102960
103315
|
};
|
|
102961
103316
|
var buildTaskStatusGroups = (tasks) => {
|
|
102962
103317
|
const groups = new Map;
|
|
@@ -102984,13 +103339,13 @@ var buildShowAllTaskGroups = (schubDir) => {
|
|
|
102984
103339
|
}
|
|
102985
103340
|
return buildTaskStatusGroups(listTasks(schubDir, TASK_STATUSES));
|
|
102986
103341
|
};
|
|
102987
|
-
var buildTaskListItems = (schubDir, statuses =
|
|
103342
|
+
var buildTaskListItems = (schubDir, statuses = DEFAULT_TASK_STATUSES) => {
|
|
102988
103343
|
if (!schubDir) {
|
|
102989
103344
|
return [];
|
|
102990
103345
|
}
|
|
102991
103346
|
const allTasks = loadTaskDependencies(schubDir, TASK_STATUSES);
|
|
102992
103347
|
const tasksById = new Map(allTasks.map((task) => [task.id, task]));
|
|
102993
|
-
const repoRoot =
|
|
103348
|
+
const repoRoot = dirname12(schubDir);
|
|
102994
103349
|
const allowed = new Set(statuses);
|
|
102995
103350
|
return allTasks.filter((task) => allowed.has(task.status)).map((task) => ({
|
|
102996
103351
|
id: task.id,
|
|
@@ -103133,236 +103488,8 @@ var buildStatusData = (schubDir) => {
|
|
|
103133
103488
|
};
|
|
103134
103489
|
};
|
|
103135
103490
|
|
|
103136
|
-
// src/tui/components/status-color.ts
|
|
103137
|
-
var STATUS_COLORS = {
|
|
103138
|
-
draft: "gray",
|
|
103139
|
-
"in-review": "yellow",
|
|
103140
|
-
accepted: "blue",
|
|
103141
|
-
"no-tasks": "gray",
|
|
103142
|
-
wip: "blue",
|
|
103143
|
-
done: "green",
|
|
103144
|
-
archived: "gray",
|
|
103145
|
-
backlog: "gray",
|
|
103146
|
-
reviewed: "yellow",
|
|
103147
|
-
ready: "yellow",
|
|
103148
|
-
blocked: "red"
|
|
103149
|
-
};
|
|
103150
|
-
var statusColor = (status) => {
|
|
103151
|
-
return status ? STATUS_COLORS[status] : "gray";
|
|
103152
|
-
};
|
|
103153
|
-
|
|
103154
|
-
// src/tui/components/list-item.tsx
|
|
103155
|
-
var jsx_dev_runtime5 = __toESM(require_jsx_dev_runtime(), 1);
|
|
103156
|
-
var TITLE_WIDTH = 40;
|
|
103157
|
-
var STATUS_LABELS = {
|
|
103158
|
-
draft: "DRAFT",
|
|
103159
|
-
"in-review": "IN REVIEW",
|
|
103160
|
-
accepted: "ACCEPTED",
|
|
103161
|
-
"no-tasks": "NO TASKS",
|
|
103162
|
-
wip: "WIP",
|
|
103163
|
-
done: "DONE",
|
|
103164
|
-
archived: "ARCHIVED",
|
|
103165
|
-
backlog: "BACKLOG",
|
|
103166
|
-
reviewed: "REVIEWED",
|
|
103167
|
-
ready: "READY",
|
|
103168
|
-
blocked: "BLOCKED"
|
|
103169
|
-
};
|
|
103170
|
-
var CHANGE_STATUS_OPTIONS = [
|
|
103171
|
-
{ label: "Draft", status: "Draft" },
|
|
103172
|
-
{ label: "Pending Review", status: "Pending Review" },
|
|
103173
|
-
{ label: "Accepted", status: "Accepted" },
|
|
103174
|
-
{ label: "Implementing", status: "Implementing" },
|
|
103175
|
-
{ label: "Done", status: "Done" },
|
|
103176
|
-
{ label: "Archived", status: "Archived" }
|
|
103177
|
-
];
|
|
103178
|
-
var CHANGE_STATUS_VALUE = {
|
|
103179
|
-
draft: "Draft",
|
|
103180
|
-
"in-review": "Pending Review",
|
|
103181
|
-
accepted: "Accepted",
|
|
103182
|
-
"no-tasks": "Accepted",
|
|
103183
|
-
wip: "Implementing",
|
|
103184
|
-
done: "Done",
|
|
103185
|
-
archived: "Archived"
|
|
103186
|
-
};
|
|
103187
|
-
var TASK_STATUS_OPTIONS = {
|
|
103188
|
-
backlog: [
|
|
103189
|
-
{ label: "Reviewed", status: "reviewed" },
|
|
103190
|
-
{ label: "Archive", status: "archived" }
|
|
103191
|
-
],
|
|
103192
|
-
reviewed: [
|
|
103193
|
-
{ label: "Backlog", status: "backlog" },
|
|
103194
|
-
{ label: "Archive", status: "archived" }
|
|
103195
|
-
]
|
|
103196
|
-
};
|
|
103197
|
-
var isTaskListItem = (item) => ("sourceStatus" in item);
|
|
103198
|
-
var buildChangeStatusOptions = (status) => {
|
|
103199
|
-
const currentStatus = CHANGE_STATUS_VALUE[status];
|
|
103200
|
-
return CHANGE_STATUS_OPTIONS.filter((option) => option.status !== currentStatus);
|
|
103201
|
-
};
|
|
103202
|
-
var buildTaskStatusOptions = (item) => {
|
|
103203
|
-
if (item.sourceStatus === "backlog") {
|
|
103204
|
-
return TASK_STATUS_OPTIONS.backlog;
|
|
103205
|
-
}
|
|
103206
|
-
if (item.sourceStatus === "reviewed") {
|
|
103207
|
-
return TASK_STATUS_OPTIONS.reviewed;
|
|
103208
|
-
}
|
|
103209
|
-
return null;
|
|
103210
|
-
};
|
|
103211
|
-
var buildStatusModalState = (item) => {
|
|
103212
|
-
if (isTaskListItem(item)) {
|
|
103213
|
-
const options2 = buildTaskStatusOptions(item);
|
|
103214
|
-
if (!options2) {
|
|
103215
|
-
return null;
|
|
103216
|
-
}
|
|
103217
|
-
return { itemId: item.id, itemKind: "task", selection: 0, options: options2 };
|
|
103218
|
-
}
|
|
103219
|
-
return {
|
|
103220
|
-
itemId: item.id,
|
|
103221
|
-
itemKind: "change",
|
|
103222
|
-
selection: 0,
|
|
103223
|
-
options: buildChangeStatusOptions(item.status)
|
|
103224
|
-
};
|
|
103225
|
-
};
|
|
103226
|
-
var formatStatusLabel = (status) => STATUS_LABELS[status] ?? status.toUpperCase();
|
|
103227
|
-
var formatTitle = (value) => {
|
|
103228
|
-
const trimmed = value.trim();
|
|
103229
|
-
if (!trimmed) {
|
|
103230
|
-
return "";
|
|
103231
|
-
}
|
|
103232
|
-
if (trimmed.length <= TITLE_WIDTH) {
|
|
103233
|
-
return trimmed;
|
|
103234
|
-
}
|
|
103235
|
-
if (TITLE_WIDTH <= 1) {
|
|
103236
|
-
return "…";
|
|
103237
|
-
}
|
|
103238
|
-
return `${trimmed.slice(0, TITLE_WIDTH - 1).trimEnd()}…`;
|
|
103239
|
-
};
|
|
103240
|
-
function ListItem({ id, status, title, selected, progress, secondaryId, pending }) {
|
|
103241
|
-
const statusLabel = formatStatusLabel(status);
|
|
103242
|
-
const formattedTitle = formatTitle(title);
|
|
103243
|
-
const showProgress = status === "wip" && progress && progress.total > 0;
|
|
103244
|
-
const progressValue = showProgress ? Math.min(100, Math.max(0, Math.round(progress.completed / progress.total * 100))) : 0;
|
|
103245
|
-
const showPending = Boolean(pending);
|
|
103246
|
-
return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
103247
|
-
marginLeft: 1,
|
|
103248
|
-
flexDirection: "row",
|
|
103249
|
-
children: [
|
|
103250
|
-
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
103251
|
-
color: selected ? "blue" : "gray",
|
|
103252
|
-
children: selected ? "›" : " "
|
|
103253
|
-
}, undefined, false, undefined, this),
|
|
103254
|
-
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
103255
|
-
flexDirection: "row",
|
|
103256
|
-
gap: 1,
|
|
103257
|
-
children: [
|
|
103258
|
-
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Badge, {
|
|
103259
|
-
color: statusColor(status),
|
|
103260
|
-
children: /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
103261
|
-
color: "darkGray",
|
|
103262
|
-
backgroundColor: statusColor(status),
|
|
103263
|
-
children: statusLabel
|
|
103264
|
-
}, undefined, false, undefined, this)
|
|
103265
|
-
}, undefined, false, undefined, this),
|
|
103266
|
-
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Badge, {
|
|
103267
|
-
color: "whiteBright",
|
|
103268
|
-
children: /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
103269
|
-
backgroundColor: "whiteBright",
|
|
103270
|
-
children: id
|
|
103271
|
-
}, undefined, false, undefined, this)
|
|
103272
|
-
}, undefined, false, undefined, this),
|
|
103273
|
-
secondaryId ? /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Badge, {
|
|
103274
|
-
color: "grey",
|
|
103275
|
-
children: /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
103276
|
-
backgroundColor: "grey",
|
|
103277
|
-
children: secondaryId
|
|
103278
|
-
}, undefined, false, undefined, this)
|
|
103279
|
-
}, undefined, false, undefined, this) : null,
|
|
103280
|
-
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
103281
|
-
width: TITLE_WIDTH,
|
|
103282
|
-
children: /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
103283
|
-
color: "gray",
|
|
103284
|
-
bold: selected,
|
|
103285
|
-
children: formattedTitle
|
|
103286
|
-
}, undefined, false, undefined, this)
|
|
103287
|
-
}, undefined, false, undefined, this),
|
|
103288
|
-
showProgress ? /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
103289
|
-
marginLeft: 1,
|
|
103290
|
-
width: 6,
|
|
103291
|
-
children: /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(ProgressBar, {
|
|
103292
|
-
value: progressValue
|
|
103293
|
-
}, undefined, false, undefined, this)
|
|
103294
|
-
}, undefined, false, undefined, this) : null,
|
|
103295
|
-
showPending ? /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Spinner, {}, undefined, false, undefined, this) : null
|
|
103296
|
-
]
|
|
103297
|
-
}, undefined, true, undefined, this)
|
|
103298
|
-
]
|
|
103299
|
-
}, undefined, true, undefined, this);
|
|
103300
|
-
}
|
|
103301
|
-
var ListItemStatusModal = ({ statusModal }) => {
|
|
103302
|
-
return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
103303
|
-
backgroundColor: "darkGray",
|
|
103304
|
-
flexDirection: "column",
|
|
103305
|
-
paddingX: 2,
|
|
103306
|
-
paddingY: 1,
|
|
103307
|
-
children: [
|
|
103308
|
-
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
103309
|
-
flexDirection: "column",
|
|
103310
|
-
marginBottom: 1,
|
|
103311
|
-
children: statusModal.options.map((option, index) => {
|
|
103312
|
-
const selected = index === statusModal.selection;
|
|
103313
|
-
return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
103314
|
-
children: [
|
|
103315
|
-
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
103316
|
-
color: selected ? "blue" : "gray",
|
|
103317
|
-
children: selected ? "›" : " "
|
|
103318
|
-
}, undefined, false, undefined, this),
|
|
103319
|
-
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
103320
|
-
color: "white",
|
|
103321
|
-
children: [
|
|
103322
|
-
" ",
|
|
103323
|
-
option.label
|
|
103324
|
-
]
|
|
103325
|
-
}, undefined, true, undefined, this)
|
|
103326
|
-
]
|
|
103327
|
-
}, option.status, true, undefined, this);
|
|
103328
|
-
})
|
|
103329
|
-
}, undefined, false, undefined, this),
|
|
103330
|
-
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
103331
|
-
alignItems: "flex-end",
|
|
103332
|
-
children: [
|
|
103333
|
-
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
103334
|
-
marginRight: 2,
|
|
103335
|
-
children: [
|
|
103336
|
-
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
103337
|
-
color: "white",
|
|
103338
|
-
children: "enter"
|
|
103339
|
-
}, undefined, false, undefined, this),
|
|
103340
|
-
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
103341
|
-
color: "gray",
|
|
103342
|
-
children: " confirm"
|
|
103343
|
-
}, undefined, false, undefined, this)
|
|
103344
|
-
]
|
|
103345
|
-
}, undefined, true, undefined, this),
|
|
103346
|
-
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
103347
|
-
children: [
|
|
103348
|
-
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
103349
|
-
color: "white",
|
|
103350
|
-
children: "esc"
|
|
103351
|
-
}, undefined, false, undefined, this),
|
|
103352
|
-
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
103353
|
-
color: "gray",
|
|
103354
|
-
children: " cancel"
|
|
103355
|
-
}, undefined, false, undefined, this)
|
|
103356
|
-
]
|
|
103357
|
-
}, undefined, true, undefined, this)
|
|
103358
|
-
]
|
|
103359
|
-
}, undefined, true, undefined, this)
|
|
103360
|
-
]
|
|
103361
|
-
}, undefined, true, undefined, this);
|
|
103362
|
-
};
|
|
103363
|
-
|
|
103364
103491
|
// src/tui/components/task-list.tsx
|
|
103365
|
-
var
|
|
103492
|
+
var jsx_dev_runtime5 = __toESM(require_jsx_dev_runtime(), 1);
|
|
103366
103493
|
var EMPTY_PENDING_IDS = new Set;
|
|
103367
103494
|
var resolveTaskTitle = (item) => {
|
|
103368
103495
|
if (item.status === "blocked") {
|
|
@@ -103382,12 +103509,12 @@ function TaskList({
|
|
|
103382
103509
|
if (!section2.emptyState) {
|
|
103383
103510
|
return null;
|
|
103384
103511
|
}
|
|
103385
|
-
return /* @__PURE__ */
|
|
103512
|
+
return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
103386
103513
|
flexDirection: "column",
|
|
103387
103514
|
marginBottom: 1,
|
|
103388
|
-
children: /* @__PURE__ */
|
|
103515
|
+
children: /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
103389
103516
|
marginLeft: 1,
|
|
103390
|
-
children: /* @__PURE__ */
|
|
103517
|
+
children: /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
103391
103518
|
color: "gray",
|
|
103392
103519
|
children: section2.emptyState
|
|
103393
103520
|
}, undefined, false, undefined, this)
|
|
@@ -103399,8 +103526,8 @@ function TaskList({
|
|
|
103399
103526
|
const selected = selection === sectionStartIndex + index;
|
|
103400
103527
|
const title = resolveTaskTitle(item);
|
|
103401
103528
|
const changeBadge = item.changeId ? formatChangeId2(item.changeId) : undefined;
|
|
103402
|
-
const pending = pendingIds.has(item.id);
|
|
103403
|
-
return /* @__PURE__ */
|
|
103529
|
+
const pending = pendingIds.has(item.id) || item.status === "draft";
|
|
103530
|
+
return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(ListItem, {
|
|
103404
103531
|
id: item.id,
|
|
103405
103532
|
status: item.status,
|
|
103406
103533
|
title,
|
|
@@ -103411,22 +103538,22 @@ function TaskList({
|
|
|
103411
103538
|
}, item.id, false, undefined, this);
|
|
103412
103539
|
});
|
|
103413
103540
|
currentIndex += section2.items.length;
|
|
103414
|
-
return /* @__PURE__ */
|
|
103541
|
+
return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
103415
103542
|
flexDirection: "column",
|
|
103416
103543
|
marginBottom: 1,
|
|
103417
103544
|
children: rows
|
|
103418
103545
|
}, section2.title, false, undefined, this);
|
|
103419
103546
|
};
|
|
103420
|
-
return /* @__PURE__ */
|
|
103547
|
+
return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
103421
103548
|
flexDirection: "column",
|
|
103422
103549
|
children: sections.map((section2) => renderSection(section2))
|
|
103423
103550
|
}, undefined, false, undefined, this);
|
|
103424
103551
|
}
|
|
103425
103552
|
|
|
103426
103553
|
// src/tui/components/proposal-detail-view.tsx
|
|
103427
|
-
var
|
|
103554
|
+
var jsx_dev_runtime6 = __toESM(require_jsx_dev_runtime(), 1);
|
|
103428
103555
|
var SUMMARY_FALLBACK2 = "No summary provided.";
|
|
103429
|
-
var
|
|
103556
|
+
var compareText2 = (left2, right2) => left2.localeCompare(right2, undefined, { sensitivity: "base" });
|
|
103430
103557
|
var idSortValue2 = (value) => {
|
|
103431
103558
|
const match = value.match(/\d+/);
|
|
103432
103559
|
return match ? Number(match[0]) : Number.POSITIVE_INFINITY;
|
|
@@ -103436,7 +103563,7 @@ var compareIds2 = (left2, right2) => {
|
|
|
103436
103563
|
if (numberDiff !== 0) {
|
|
103437
103564
|
return numberDiff;
|
|
103438
103565
|
}
|
|
103439
|
-
return
|
|
103566
|
+
return compareText2(left2, right2);
|
|
103440
103567
|
};
|
|
103441
103568
|
var resolveTaskName = (task) => {
|
|
103442
103569
|
if (task.status === "blocked") {
|
|
@@ -103452,7 +103579,7 @@ var sortTaskItems = (tasks) => tasks.sort((left2, right2) => {
|
|
|
103452
103579
|
if (idCompare !== 0) {
|
|
103453
103580
|
return idCompare;
|
|
103454
103581
|
}
|
|
103455
|
-
return
|
|
103582
|
+
return compareText2(resolveTaskName(left2), resolveTaskName(right2));
|
|
103456
103583
|
});
|
|
103457
103584
|
var TASK_STATUS_LABELS2 = {
|
|
103458
103585
|
backlog: "Backlog",
|
|
@@ -103460,6 +103587,7 @@ var TASK_STATUS_LABELS2 = {
|
|
|
103460
103587
|
wip: "WIP",
|
|
103461
103588
|
blocked: "Blocked",
|
|
103462
103589
|
done: "Done",
|
|
103590
|
+
rejected: "Rejected",
|
|
103463
103591
|
archived: "Archived"
|
|
103464
103592
|
};
|
|
103465
103593
|
var buildTaskSections = (tasks) => {
|
|
@@ -103485,28 +103613,28 @@ function ProposalDetailView({ changeId, selection = 0, startDir }) {
|
|
|
103485
103613
|
const resolvedStartDir = startDir ?? process.env.SCHUB_CWD ?? process.cwd();
|
|
103486
103614
|
const schubDir = findSchubRoot(resolvedStartDir);
|
|
103487
103615
|
if (!schubDir) {
|
|
103488
|
-
return /* @__PURE__ */
|
|
103616
|
+
return /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
|
|
103489
103617
|
flexDirection: "column",
|
|
103490
|
-
children: /* @__PURE__ */
|
|
103618
|
+
children: /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
|
|
103491
103619
|
color: "red",
|
|
103492
103620
|
children: "No .schub directory found."
|
|
103493
103621
|
}, undefined, false, undefined, this)
|
|
103494
103622
|
}, undefined, false, undefined, this);
|
|
103495
103623
|
}
|
|
103496
103624
|
const detail = readChangeDetail(schubDir, changeId);
|
|
103497
|
-
const tasks = buildTaskListItems(schubDir).filter((task) => task.changeId === detail.changeId);
|
|
103625
|
+
const tasks = buildTaskListItems(schubDir, TASK_STATUSES).filter((task) => task.changeId === detail.changeId);
|
|
103498
103626
|
const summary = detail.summary.trim() || SUMMARY_FALLBACK2;
|
|
103499
103627
|
const taskSections = buildTaskSections(tasks);
|
|
103500
|
-
const renderMetadataRow = (label, value) => /* @__PURE__ */
|
|
103628
|
+
const renderMetadataRow = (label, value) => /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
|
|
103501
103629
|
children: [
|
|
103502
|
-
/* @__PURE__ */
|
|
103630
|
+
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
|
|
103503
103631
|
color: "white",
|
|
103504
103632
|
children: [
|
|
103505
103633
|
label,
|
|
103506
103634
|
":"
|
|
103507
103635
|
]
|
|
103508
103636
|
}, undefined, true, undefined, this),
|
|
103509
|
-
/* @__PURE__ */
|
|
103637
|
+
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
|
|
103510
103638
|
color: "gray",
|
|
103511
103639
|
children: [
|
|
103512
103640
|
" ",
|
|
@@ -103515,18 +103643,24 @@ function ProposalDetailView({ changeId, selection = 0, startDir }) {
|
|
|
103515
103643
|
}, undefined, true, undefined, this)
|
|
103516
103644
|
]
|
|
103517
103645
|
}, undefined, true, undefined, this);
|
|
103518
|
-
return /* @__PURE__ */
|
|
103646
|
+
return /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
|
|
103519
103647
|
flexDirection: "column",
|
|
103520
103648
|
children: [
|
|
103521
|
-
/* @__PURE__ */
|
|
103649
|
+
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
|
|
103522
103650
|
marginBottom: 1,
|
|
103523
|
-
children:
|
|
103524
|
-
|
|
103525
|
-
|
|
103526
|
-
|
|
103527
|
-
|
|
103528
|
-
|
|
103529
|
-
|
|
103651
|
+
children: [
|
|
103652
|
+
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
|
|
103653
|
+
bold: true,
|
|
103654
|
+
color: "white",
|
|
103655
|
+
children: "Proposal Detail"
|
|
103656
|
+
}, undefined, false, undefined, this),
|
|
103657
|
+
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
|
|
103658
|
+
color: "gray",
|
|
103659
|
+
children: " · esc to return"
|
|
103660
|
+
}, undefined, false, undefined, this)
|
|
103661
|
+
]
|
|
103662
|
+
}, undefined, true, undefined, this),
|
|
103663
|
+
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
|
|
103530
103664
|
flexDirection: "column",
|
|
103531
103665
|
marginBottom: 1,
|
|
103532
103666
|
children: [
|
|
@@ -103536,41 +103670,41 @@ function ProposalDetailView({ changeId, selection = 0, startDir }) {
|
|
|
103536
103670
|
renderMetadataRow("Input", detail.input)
|
|
103537
103671
|
]
|
|
103538
103672
|
}, undefined, true, undefined, this),
|
|
103539
|
-
/* @__PURE__ */
|
|
103673
|
+
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
|
|
103540
103674
|
flexDirection: "column",
|
|
103541
103675
|
marginBottom: 1,
|
|
103542
103676
|
children: [
|
|
103543
|
-
/* @__PURE__ */
|
|
103677
|
+
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
|
|
103544
103678
|
color: "white",
|
|
103545
103679
|
children: "Summary"
|
|
103546
103680
|
}, undefined, false, undefined, this),
|
|
103547
|
-
/* @__PURE__ */
|
|
103681
|
+
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
|
|
103548
103682
|
marginLeft: 1,
|
|
103549
|
-
children: /* @__PURE__ */
|
|
103683
|
+
children: /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
|
|
103550
103684
|
color: "gray",
|
|
103551
103685
|
children: summary
|
|
103552
103686
|
}, undefined, false, undefined, this)
|
|
103553
103687
|
}, undefined, false, undefined, this)
|
|
103554
103688
|
]
|
|
103555
103689
|
}, undefined, true, undefined, this),
|
|
103556
|
-
/* @__PURE__ */
|
|
103690
|
+
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
|
|
103557
103691
|
flexDirection: "column",
|
|
103558
103692
|
children: [
|
|
103559
|
-
/* @__PURE__ */
|
|
103693
|
+
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
|
|
103560
103694
|
marginBottom: 1,
|
|
103561
|
-
children: /* @__PURE__ */
|
|
103695
|
+
children: /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
|
|
103562
103696
|
bold: true,
|
|
103563
103697
|
color: "white",
|
|
103564
103698
|
children: "Tasks"
|
|
103565
103699
|
}, undefined, false, undefined, this)
|
|
103566
103700
|
}, undefined, false, undefined, this),
|
|
103567
|
-
taskSections.length === 0 ? /* @__PURE__ */
|
|
103701
|
+
taskSections.length === 0 ? /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
|
|
103568
103702
|
marginLeft: 1,
|
|
103569
|
-
children: /* @__PURE__ */
|
|
103703
|
+
children: /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
|
|
103570
103704
|
color: "gray",
|
|
103571
103705
|
children: "No tasks found."
|
|
103572
103706
|
}, undefined, false, undefined, this)
|
|
103573
|
-
}, undefined, false, undefined, this) : /* @__PURE__ */
|
|
103707
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(TaskList, {
|
|
103574
103708
|
selection,
|
|
103575
103709
|
sections: taskSections
|
|
103576
103710
|
}, undefined, false, undefined, this)
|
|
@@ -103581,28 +103715,28 @@ function ProposalDetailView({ changeId, selection = 0, startDir }) {
|
|
|
103581
103715
|
}
|
|
103582
103716
|
|
|
103583
103717
|
// src/tui/components/session-view.tsx
|
|
103584
|
-
var
|
|
103718
|
+
var jsx_dev_runtime7 = __toESM(require_jsx_dev_runtime(), 1);
|
|
103585
103719
|
var EMPTY_MESSAGE = "No session selected.";
|
|
103586
103720
|
var MESSAGE_FALLBACK = "No messages found.";
|
|
103587
103721
|
var ERROR_FALLBACK = "Unable to load session.";
|
|
103588
103722
|
function SessionView({ state }) {
|
|
103589
103723
|
if (state.status === "error") {
|
|
103590
|
-
return /* @__PURE__ */
|
|
103724
|
+
return /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
|
|
103591
103725
|
flexDirection: "column",
|
|
103592
103726
|
children: [
|
|
103593
|
-
/* @__PURE__ */
|
|
103727
|
+
/* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
|
|
103594
103728
|
marginBottom: 1,
|
|
103595
|
-
children: /* @__PURE__ */
|
|
103729
|
+
children: /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
|
|
103596
103730
|
bold: true,
|
|
103597
103731
|
color: "white",
|
|
103598
103732
|
children: "Session Detail"
|
|
103599
103733
|
}, undefined, false, undefined, this)
|
|
103600
103734
|
}, undefined, false, undefined, this),
|
|
103601
|
-
/* @__PURE__ */
|
|
103735
|
+
/* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
|
|
103602
103736
|
color: "red",
|
|
103603
103737
|
children: ERROR_FALLBACK
|
|
103604
103738
|
}, undefined, false, undefined, this),
|
|
103605
|
-
/* @__PURE__ */
|
|
103739
|
+
/* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
|
|
103606
103740
|
color: "gray",
|
|
103607
103741
|
children: state.message
|
|
103608
103742
|
}, undefined, false, undefined, this)
|
|
@@ -103610,18 +103744,18 @@ function SessionView({ state }) {
|
|
|
103610
103744
|
}, undefined, true, undefined, this);
|
|
103611
103745
|
}
|
|
103612
103746
|
if (state.status === "empty") {
|
|
103613
|
-
return /* @__PURE__ */
|
|
103747
|
+
return /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
|
|
103614
103748
|
flexDirection: "column",
|
|
103615
103749
|
children: [
|
|
103616
|
-
/* @__PURE__ */
|
|
103750
|
+
/* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
|
|
103617
103751
|
marginBottom: 1,
|
|
103618
|
-
children: /* @__PURE__ */
|
|
103752
|
+
children: /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
|
|
103619
103753
|
bold: true,
|
|
103620
103754
|
color: "white",
|
|
103621
103755
|
children: "Session Detail"
|
|
103622
103756
|
}, undefined, false, undefined, this)
|
|
103623
103757
|
}, undefined, false, undefined, this),
|
|
103624
|
-
/* @__PURE__ */
|
|
103758
|
+
/* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
|
|
103625
103759
|
color: "gray",
|
|
103626
103760
|
children: EMPTY_MESSAGE
|
|
103627
103761
|
}, undefined, false, undefined, this)
|
|
@@ -103630,16 +103764,16 @@ function SessionView({ state }) {
|
|
|
103630
103764
|
}
|
|
103631
103765
|
const { summary } = state;
|
|
103632
103766
|
const messages = summary.messages;
|
|
103633
|
-
const renderMetadataRow = (label, value) => /* @__PURE__ */
|
|
103767
|
+
const renderMetadataRow = (label, value) => /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
|
|
103634
103768
|
children: [
|
|
103635
|
-
/* @__PURE__ */
|
|
103769
|
+
/* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
|
|
103636
103770
|
color: "white",
|
|
103637
103771
|
children: [
|
|
103638
103772
|
label,
|
|
103639
103773
|
":"
|
|
103640
103774
|
]
|
|
103641
103775
|
}, undefined, true, undefined, this),
|
|
103642
|
-
/* @__PURE__ */
|
|
103776
|
+
/* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
|
|
103643
103777
|
color: "gray",
|
|
103644
103778
|
children: [
|
|
103645
103779
|
" ",
|
|
@@ -103648,14 +103782,14 @@ function SessionView({ state }) {
|
|
|
103648
103782
|
}, undefined, true, undefined, this)
|
|
103649
103783
|
]
|
|
103650
103784
|
}, undefined, true, undefined, this);
|
|
103651
|
-
const renderMessage = (message, index) => /* @__PURE__ */
|
|
103785
|
+
const renderMessage = (message, index) => /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
|
|
103652
103786
|
marginLeft: 1,
|
|
103653
103787
|
children: [
|
|
103654
|
-
/* @__PURE__ */
|
|
103788
|
+
/* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
|
|
103655
103789
|
color: "white",
|
|
103656
103790
|
children: message.role
|
|
103657
103791
|
}, undefined, false, undefined, this),
|
|
103658
|
-
/* @__PURE__ */
|
|
103792
|
+
/* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
|
|
103659
103793
|
color: "gray",
|
|
103660
103794
|
children: [
|
|
103661
103795
|
" ",
|
|
@@ -103664,18 +103798,18 @@ function SessionView({ state }) {
|
|
|
103664
103798
|
}, undefined, true, undefined, this)
|
|
103665
103799
|
]
|
|
103666
103800
|
}, `${message.role}-${index}`, true, undefined, this);
|
|
103667
|
-
return /* @__PURE__ */
|
|
103801
|
+
return /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
|
|
103668
103802
|
flexDirection: "column",
|
|
103669
103803
|
children: [
|
|
103670
|
-
/* @__PURE__ */
|
|
103804
|
+
/* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
|
|
103671
103805
|
marginBottom: 1,
|
|
103672
|
-
children: /* @__PURE__ */
|
|
103806
|
+
children: /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
|
|
103673
103807
|
bold: true,
|
|
103674
103808
|
color: "white",
|
|
103675
103809
|
children: "Session Detail"
|
|
103676
103810
|
}, undefined, false, undefined, this)
|
|
103677
103811
|
}, undefined, false, undefined, this),
|
|
103678
|
-
/* @__PURE__ */
|
|
103812
|
+
/* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
|
|
103679
103813
|
flexDirection: "column",
|
|
103680
103814
|
marginBottom: 1,
|
|
103681
103815
|
children: [
|
|
@@ -103685,20 +103819,20 @@ function SessionView({ state }) {
|
|
|
103685
103819
|
renderMetadataRow("Updated", summary.updatedAt)
|
|
103686
103820
|
]
|
|
103687
103821
|
}, undefined, true, undefined, this),
|
|
103688
|
-
/* @__PURE__ */
|
|
103822
|
+
/* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
|
|
103689
103823
|
flexDirection: "column",
|
|
103690
103824
|
children: [
|
|
103691
|
-
/* @__PURE__ */
|
|
103825
|
+
/* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
|
|
103692
103826
|
marginBottom: 1,
|
|
103693
|
-
children: /* @__PURE__ */
|
|
103827
|
+
children: /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
|
|
103694
103828
|
bold: true,
|
|
103695
103829
|
color: "white",
|
|
103696
103830
|
children: "Messages"
|
|
103697
103831
|
}, undefined, false, undefined, this)
|
|
103698
103832
|
}, undefined, false, undefined, this),
|
|
103699
|
-
messages.length === 0 ? /* @__PURE__ */
|
|
103833
|
+
messages.length === 0 ? /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
|
|
103700
103834
|
marginLeft: 1,
|
|
103701
|
-
children: /* @__PURE__ */
|
|
103835
|
+
children: /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
|
|
103702
103836
|
color: "gray",
|
|
103703
103837
|
children: MESSAGE_FALLBACK
|
|
103704
103838
|
}, undefined, false, undefined, this)
|
|
@@ -103710,8 +103844,98 @@ function SessionView({ state }) {
|
|
|
103710
103844
|
}
|
|
103711
103845
|
|
|
103712
103846
|
// src/tui/components/status-view.tsx
|
|
103713
|
-
import { dirname as
|
|
103714
|
-
var
|
|
103847
|
+
import { dirname as dirname13, resolve as resolve12 } from "node:path";
|
|
103848
|
+
var import_react60 = __toESM(require_react(), 1);
|
|
103849
|
+
|
|
103850
|
+
// src/tui/hooks/use-refresh-interval.ts
|
|
103851
|
+
var import_react59 = __toESM(require_react(), 1);
|
|
103852
|
+
var useRefreshInterval = ({ enabled = true, refreshIntervalMs, onRefresh }) => {
|
|
103853
|
+
const refreshRef = import_react59.default.useRef(onRefresh);
|
|
103854
|
+
import_react59.default.useEffect(() => {
|
|
103855
|
+
refreshRef.current = onRefresh;
|
|
103856
|
+
}, [onRefresh]);
|
|
103857
|
+
import_react59.default.useEffect(() => {
|
|
103858
|
+
if (!enabled) {
|
|
103859
|
+
return;
|
|
103860
|
+
}
|
|
103861
|
+
const interval = setInterval(() => {
|
|
103862
|
+
refreshRef.current();
|
|
103863
|
+
}, refreshIntervalMs);
|
|
103864
|
+
return () => {
|
|
103865
|
+
clearInterval(interval);
|
|
103866
|
+
};
|
|
103867
|
+
}, [enabled, refreshIntervalMs]);
|
|
103868
|
+
};
|
|
103869
|
+
|
|
103870
|
+
// src/tui/components/implement-mode-modal.tsx
|
|
103871
|
+
var jsx_dev_runtime8 = __toESM(require_jsx_dev_runtime(), 1);
|
|
103872
|
+
var IMPLEMENT_MODES = ["none", "worktree"];
|
|
103873
|
+
var IMPLEMENT_MODE_LABELS = {
|
|
103874
|
+
none: "None",
|
|
103875
|
+
worktree: "Worktree"
|
|
103876
|
+
};
|
|
103877
|
+
var ImplementModeModal = ({ modal }) => {
|
|
103878
|
+
return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
|
|
103879
|
+
backgroundColor: "darkGray",
|
|
103880
|
+
flexDirection: "column",
|
|
103881
|
+
paddingX: 2,
|
|
103882
|
+
paddingY: 1,
|
|
103883
|
+
children: [
|
|
103884
|
+
/* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
|
|
103885
|
+
flexDirection: "column",
|
|
103886
|
+
marginBottom: 1,
|
|
103887
|
+
children: IMPLEMENT_MODES.map((mode, index) => {
|
|
103888
|
+
const selected = index === modal.selection;
|
|
103889
|
+
return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
|
|
103890
|
+
children: [
|
|
103891
|
+
/* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
|
|
103892
|
+
color: selected ? "blue" : "gray",
|
|
103893
|
+
children: selected ? "›" : " "
|
|
103894
|
+
}, undefined, false, undefined, this),
|
|
103895
|
+
/* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
|
|
103896
|
+
color: "white",
|
|
103897
|
+
children: [
|
|
103898
|
+
" ",
|
|
103899
|
+
IMPLEMENT_MODE_LABELS[mode]
|
|
103900
|
+
]
|
|
103901
|
+
}, undefined, true, undefined, this)
|
|
103902
|
+
]
|
|
103903
|
+
}, mode, true, undefined, this);
|
|
103904
|
+
})
|
|
103905
|
+
}, undefined, false, undefined, this),
|
|
103906
|
+
/* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
|
|
103907
|
+
alignItems: "flex-end",
|
|
103908
|
+
children: [
|
|
103909
|
+
/* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
|
|
103910
|
+
marginRight: 2,
|
|
103911
|
+
children: [
|
|
103912
|
+
/* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
|
|
103913
|
+
color: "white",
|
|
103914
|
+
children: "enter"
|
|
103915
|
+
}, undefined, false, undefined, this),
|
|
103916
|
+
/* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
|
|
103917
|
+
color: "gray",
|
|
103918
|
+
children: " confirm"
|
|
103919
|
+
}, undefined, false, undefined, this)
|
|
103920
|
+
]
|
|
103921
|
+
}, undefined, true, undefined, this),
|
|
103922
|
+
/* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
|
|
103923
|
+
children: [
|
|
103924
|
+
/* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
|
|
103925
|
+
color: "white",
|
|
103926
|
+
children: "esc"
|
|
103927
|
+
}, undefined, false, undefined, this),
|
|
103928
|
+
/* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
|
|
103929
|
+
color: "gray",
|
|
103930
|
+
children: " cancel"
|
|
103931
|
+
}, undefined, false, undefined, this)
|
|
103932
|
+
]
|
|
103933
|
+
}, undefined, true, undefined, this)
|
|
103934
|
+
]
|
|
103935
|
+
}, undefined, true, undefined, this)
|
|
103936
|
+
]
|
|
103937
|
+
}, undefined, true, undefined, this);
|
|
103938
|
+
};
|
|
103715
103939
|
|
|
103716
103940
|
// src/tui/components/change-list.tsx
|
|
103717
103941
|
var jsx_dev_runtime9 = __toESM(require_jsx_dev_runtime(), 1);
|
|
@@ -103839,7 +104063,7 @@ var StatusMainView = ({
|
|
|
103839
104063
|
marginLeft: 1,
|
|
103840
104064
|
children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
|
|
103841
104065
|
color: "gray",
|
|
103842
|
-
children: "No
|
|
104066
|
+
children: "No pending proposals."
|
|
103843
104067
|
}, undefined, false, undefined, this)
|
|
103844
104068
|
}, undefined, false, undefined, this),
|
|
103845
104069
|
renderShowAllRow(showAllRow, selection === showAllIndex)
|
|
@@ -103866,7 +104090,7 @@ var StatusMainView = ({
|
|
|
103866
104090
|
marginLeft: 1,
|
|
103867
104091
|
children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
|
|
103868
104092
|
color: "gray",
|
|
103869
|
-
children: "No
|
|
104093
|
+
children: "No pending tasks."
|
|
103870
104094
|
}, undefined, false, undefined, this)
|
|
103871
104095
|
}, undefined, false, undefined, this) : null,
|
|
103872
104096
|
renderShowAllRow(showAllTasksRow, selection === showAllTasksIndex)
|
|
@@ -103932,6 +104156,20 @@ var updateStatusModalWithRef = (nextModal, setStatusModal, statusModalRef) => {
|
|
|
103932
104156
|
statusModalRef.current = nextModal;
|
|
103933
104157
|
setStatusModal(nextModal);
|
|
103934
104158
|
};
|
|
104159
|
+
var updateImplementModeSelection = (current, delta) => {
|
|
104160
|
+
if (!current) {
|
|
104161
|
+
return current;
|
|
104162
|
+
}
|
|
104163
|
+
const nextSelection = clampSelection(current.selection + delta, IMPLEMENT_MODES.length);
|
|
104164
|
+
if (nextSelection === current.selection) {
|
|
104165
|
+
return current;
|
|
104166
|
+
}
|
|
104167
|
+
return { ...current, selection: nextSelection };
|
|
104168
|
+
};
|
|
104169
|
+
var updateImplementModeModalWithRef = (nextModal, setImplementModeModal, implementModeModalRef) => {
|
|
104170
|
+
implementModeModalRef.current = nextModal;
|
|
104171
|
+
setImplementModeModal(nextModal);
|
|
104172
|
+
};
|
|
103935
104173
|
var applyStatusUpdate = (schubDir, modal) => {
|
|
103936
104174
|
const option = modal.options[modal.selection];
|
|
103937
104175
|
if (!option) {
|
|
@@ -103951,6 +104189,7 @@ var useStatusViewInput = ({
|
|
|
103951
104189
|
repoRoot,
|
|
103952
104190
|
showAllShortcutRef,
|
|
103953
104191
|
statusModalRef,
|
|
104192
|
+
implementModeModalRef,
|
|
103954
104193
|
currentItemsRef,
|
|
103955
104194
|
selectionRef,
|
|
103956
104195
|
viewRef,
|
|
@@ -103961,6 +104200,7 @@ var useStatusViewInput = ({
|
|
|
103961
104200
|
pendingOpencodeActionIdsRef,
|
|
103962
104201
|
setSelection,
|
|
103963
104202
|
setStatusModal,
|
|
104203
|
+
setImplementModeModal,
|
|
103964
104204
|
setRefreshTick,
|
|
103965
104205
|
updateView,
|
|
103966
104206
|
onCopyId,
|
|
@@ -103978,6 +104218,29 @@ var useStatusViewInput = ({
|
|
|
103978
104218
|
const keyName2 = inputState.keyName;
|
|
103979
104219
|
const keySequence = inputState.keySequence;
|
|
103980
104220
|
const isCtrlX = key.ctrl && (lowerInput === "x" || keyName2 === "x" || keySequence === "\x18") || input === "\x18";
|
|
104221
|
+
const activeImplementModeModal = implementModeModalRef.current;
|
|
104222
|
+
if (activeImplementModeModal) {
|
|
104223
|
+
if (isEscape || lowerInput === "q") {
|
|
104224
|
+
updateImplementModeModalWithRef(null, setImplementModeModal, implementModeModalRef);
|
|
104225
|
+
return;
|
|
104226
|
+
}
|
|
104227
|
+
if (isUpArrow || isLeftArrow) {
|
|
104228
|
+
const nextModal = updateImplementModeSelection(activeImplementModeModal, -1);
|
|
104229
|
+
updateImplementModeModalWithRef(nextModal, setImplementModeModal, implementModeModalRef);
|
|
104230
|
+
return;
|
|
104231
|
+
}
|
|
104232
|
+
if (isDownArrow || isRightArrow) {
|
|
104233
|
+
const nextModal = updateImplementModeSelection(activeImplementModeModal, 1);
|
|
104234
|
+
updateImplementModeModalWithRef(nextModal, setImplementModeModal, implementModeModalRef);
|
|
104235
|
+
return;
|
|
104236
|
+
}
|
|
104237
|
+
if (isEnter) {
|
|
104238
|
+
const mode = IMPLEMENT_MODES[activeImplementModeModal.selection] ?? "none";
|
|
104239
|
+
onImplement(activeImplementModeModal.taskId, repoRoot, mode);
|
|
104240
|
+
updateImplementModeModalWithRef(null, setImplementModeModal, implementModeModalRef);
|
|
104241
|
+
}
|
|
104242
|
+
return;
|
|
104243
|
+
}
|
|
103981
104244
|
const activeStatusModal = statusModalRef.current;
|
|
103982
104245
|
if (activeStatusModal) {
|
|
103983
104246
|
if (isEscape || lowerInput === "q") {
|
|
@@ -104051,19 +104314,28 @@ var useStatusViewInput = ({
|
|
|
104051
104314
|
selectionRef.current = nextSelection;
|
|
104052
104315
|
setSelection(nextSelection);
|
|
104053
104316
|
}
|
|
104054
|
-
if (isEnter && activeItem && !isShowAllRow(activeItem)
|
|
104317
|
+
if (isEnter && activeItem && !isShowAllRow(activeItem)) {
|
|
104318
|
+
if (isTaskItem(activeItem)) {
|
|
104319
|
+
onOpen?.(repoRoot, activeItem.path);
|
|
104320
|
+
return;
|
|
104321
|
+
}
|
|
104055
104322
|
onOpenDetail?.(activeItem.id);
|
|
104056
104323
|
return;
|
|
104057
104324
|
}
|
|
104058
|
-
if (
|
|
104059
|
-
|
|
104060
|
-
|
|
104061
|
-
|
|
104062
|
-
|
|
104063
|
-
|
|
104064
|
-
|
|
104065
|
-
|
|
104066
|
-
|
|
104325
|
+
if (lowerInput === "n" && activeItem && !isShowAllRow(activeItem) && isTaskItem(activeItem) && activeItem.status === "wip" && onOpenSession && (activeView === "main" || activeView === "show-all-tasks")) {
|
|
104326
|
+
try {
|
|
104327
|
+
const sessions = listOpencodeSessions();
|
|
104328
|
+
const match = selectLatestSessionMatch(Array.isArray(sessions) ? sessions : [], activeItem.id);
|
|
104329
|
+
if (match) {
|
|
104330
|
+
const exported = exportOpencodeSession(match.id);
|
|
104331
|
+
const summary = mapOpencodeSessionSummary(exported);
|
|
104332
|
+
onOpenSession({ status: "ready", summary });
|
|
104333
|
+
} else {
|
|
104334
|
+
onOpenSession({ status: "empty" });
|
|
104335
|
+
}
|
|
104336
|
+
} catch (error) {
|
|
104337
|
+
const message = error instanceof Error ? error.message : "Unknown opencode error.";
|
|
104338
|
+
onOpenSession({ status: "error", message });
|
|
104067
104339
|
}
|
|
104068
104340
|
return;
|
|
104069
104341
|
}
|
|
@@ -104071,9 +104343,6 @@ var useStatusViewInput = ({
|
|
|
104071
104343
|
updateView(isShowAllTasksRow(activeItem) ? "show-all-tasks" : "show-all");
|
|
104072
104344
|
return;
|
|
104073
104345
|
}
|
|
104074
|
-
if (input === "o" && activeItem && !isShowAllRow(activeItem)) {
|
|
104075
|
-
onOpen?.(repoRoot, activeItem.path);
|
|
104076
|
-
}
|
|
104077
104346
|
if (input === "c" && activeItem && !isShowAllRow(activeItem)) {
|
|
104078
104347
|
onCopyId(activeItem.id);
|
|
104079
104348
|
}
|
|
@@ -104099,13 +104368,13 @@ var useStatusViewInput = ({
|
|
|
104099
104368
|
const readyIndex = activeSelection - start;
|
|
104100
104369
|
const taskId = readyToImplementIdsRef.current[readyIndex];
|
|
104101
104370
|
if (taskId && !pendingOpencodeActionIdsRef.current.has(taskId)) {
|
|
104102
|
-
|
|
104371
|
+
updateImplementModeModalWithRef({ taskId, selection: 0 }, setImplementModeModal, implementModeModalRef);
|
|
104103
104372
|
}
|
|
104104
104373
|
}
|
|
104105
104374
|
return;
|
|
104106
104375
|
}
|
|
104107
104376
|
if (activeView === "show-all-tasks" && activeItem && !isShowAllRow(activeItem) && isTaskItem(activeItem) && readyToImplementIdsRef.current.includes(activeItem.id) && !pendingOpencodeActionIdsRef.current.has(activeItem.id)) {
|
|
104108
|
-
|
|
104377
|
+
updateImplementModeModalWithRef({ taskId: activeItem.id, selection: 0 }, setImplementModeModal, implementModeModalRef);
|
|
104109
104378
|
}
|
|
104110
104379
|
}
|
|
104111
104380
|
}, { isActive });
|
|
@@ -104149,13 +104418,32 @@ var StatusShowAllTasksView = ({ selection, sections, pendingIds }) => {
|
|
|
104149
104418
|
|
|
104150
104419
|
// src/tui/components/status-view.tsx
|
|
104151
104420
|
var jsx_dev_runtime12 = __toESM(require_jsx_dev_runtime(), 1);
|
|
104152
|
-
var
|
|
104153
|
-
var OPEN_SHORTCUT = { keyLabel: "o", label: "preview" };
|
|
104421
|
+
var DEFAULT_REFRESH_INTERVAL_MS = 1000;
|
|
104154
104422
|
var COPY_SHORTCUT = { keyLabel: "c", label: "copy id" };
|
|
104423
|
+
var SESSION_SHORTCUT = { keyLabel: "n", label: "session" };
|
|
104155
104424
|
var CREATE_TASKS_SHORTCUT = { keyLabel: "a", label: "create tasks" };
|
|
104156
104425
|
var REVIEW_SHORTCUT = { keyLabel: "r", label: "review" };
|
|
104157
104426
|
var IMPLEMENT_SHORTCUT = { keyLabel: "i", label: "implement" };
|
|
104158
104427
|
var STATUS_SHORTCUT = { keyLabel: "s", label: "status" };
|
|
104428
|
+
var defaultImplementLauncher = (id, repoRoot, mode, prompt) => {
|
|
104429
|
+
const options2 = prompt ? { prompt } : undefined;
|
|
104430
|
+
if (mode !== "worktree") {
|
|
104431
|
+
return launchOpencodeImplement(id, repoRoot, options2);
|
|
104432
|
+
}
|
|
104433
|
+
try {
|
|
104434
|
+
const worktree = createTaskWorktree({ repoRoot, taskId: id });
|
|
104435
|
+
const worktreePath = worktree?.worktreePath;
|
|
104436
|
+
if (!worktreePath) {
|
|
104437
|
+
throw new Error("Unable to resolve git root for worktree.");
|
|
104438
|
+
}
|
|
104439
|
+
return launchOpencodeImplement(id, repoRoot, options2, {
|
|
104440
|
+
env: { ...process.env, SCHUB_CWD: worktreePath },
|
|
104441
|
+
cwd: worktreePath
|
|
104442
|
+
});
|
|
104443
|
+
} catch {
|
|
104444
|
+
return;
|
|
104445
|
+
}
|
|
104446
|
+
};
|
|
104159
104447
|
var SHOW_ALL_ROW = {
|
|
104160
104448
|
kind: "show-all",
|
|
104161
104449
|
id: "show-all-proposals",
|
|
@@ -104170,7 +104458,7 @@ var isShowAllTasksRow = (row) => row.id === SHOW_ALL_TASKS_ROW.id;
|
|
|
104170
104458
|
var isChangeListItem = (item) => Boolean(item);
|
|
104171
104459
|
var isTaskListItem2 = (item) => Boolean(item);
|
|
104172
104460
|
function StatusView({
|
|
104173
|
-
refreshIntervalMs =
|
|
104461
|
+
refreshIntervalMs = DEFAULT_REFRESH_INTERVAL_MS,
|
|
104174
104462
|
startDir,
|
|
104175
104463
|
isActive = true,
|
|
104176
104464
|
onCopyId,
|
|
@@ -104178,13 +104466,13 @@ function StatusView({
|
|
|
104178
104466
|
onOpenDetail,
|
|
104179
104467
|
onOpenSession,
|
|
104180
104468
|
onCreateTasks = launchOpencodeCreateTasks,
|
|
104181
|
-
onImplement =
|
|
104469
|
+
onImplement = defaultImplementLauncher,
|
|
104182
104470
|
onReview = launchOpencodeReview,
|
|
104183
104471
|
onShortcutsChange,
|
|
104184
104472
|
onViewChange
|
|
104185
104473
|
}) {
|
|
104186
104474
|
const schubDir = findSchubRoot(startDir);
|
|
104187
|
-
const [, setRefreshTick] =
|
|
104475
|
+
const [, setRefreshTick] = import_react60.default.useState(0);
|
|
104188
104476
|
const {
|
|
104189
104477
|
pendingReview,
|
|
104190
104478
|
pendingImplementation,
|
|
@@ -104196,11 +104484,13 @@ function StatusView({
|
|
|
104196
104484
|
reviewed,
|
|
104197
104485
|
backlog
|
|
104198
104486
|
} = buildStatusData(schubDir);
|
|
104199
|
-
const repoRoot = schubDir ?
|
|
104487
|
+
const repoRoot = schubDir ? dirname13(schubDir) : "";
|
|
104200
104488
|
const changeListItems = buildChangeListItems(schubDir);
|
|
104201
104489
|
const taskListItems = buildTaskListItems(schubDir);
|
|
104490
|
+
const showAllTaskListItems = buildTaskListItems(schubDir, TASK_STATUSES);
|
|
104202
104491
|
const changeListById = new Map(changeListItems.map((item) => [item.id, item]));
|
|
104203
104492
|
const taskListById = new Map(taskListItems.map((item) => [item.id, item]));
|
|
104493
|
+
const showAllTaskListById = new Map(showAllTaskListItems.map((item) => [item.id, item]));
|
|
104204
104494
|
const resolveChangeItems = (items) => items.map((item) => changeListById.get(item.id)).filter(isChangeListItem);
|
|
104205
104495
|
const resolveTaskItems = (items) => items.map((item) => taskListById.get(item.id)).filter(isTaskListItem2);
|
|
104206
104496
|
const pendingReviewItems = resolveChangeItems(pendingReview);
|
|
@@ -104236,30 +104526,32 @@ function StatusView({
|
|
|
104236
104526
|
const showAllTaskGroups = buildShowAllTaskGroups(schubDir);
|
|
104237
104527
|
const showAllTaskSections = showAllTaskGroups.map((group) => ({
|
|
104238
104528
|
title: group.label,
|
|
104239
|
-
items: group.items.map((item) =>
|
|
104529
|
+
items: group.items.map((item) => showAllTaskListById.get(item.id)).filter(isTaskListItem2)
|
|
104240
104530
|
}));
|
|
104241
104531
|
const showAllTaskItems = showAllTaskSections.flatMap((section2) => section2.items);
|
|
104242
104532
|
const changeItems = changeSections.flatMap((section2) => section2.items);
|
|
104243
104533
|
const taskItems = taskSections.flatMap((section2) => section2.items);
|
|
104244
104534
|
const mainItems = [...changeItems, SHOW_ALL_ROW, ...taskItems, SHOW_ALL_TASKS_ROW];
|
|
104245
104535
|
const mainSelectionFloor = changeItems.length === 0 && taskItems.length > 0 ? 1 : 0;
|
|
104246
|
-
const [view, setView] =
|
|
104247
|
-
const [mainSelection, setMainSelection] =
|
|
104248
|
-
const [showAllSelection, setShowAllSelection] =
|
|
104249
|
-
const [showAllTasksSelection, setShowAllTasksSelection] =
|
|
104250
|
-
const [statusModal, setStatusModal] =
|
|
104251
|
-
const [
|
|
104252
|
-
const
|
|
104253
|
-
const
|
|
104254
|
-
const
|
|
104255
|
-
const
|
|
104536
|
+
const [view, setView] = import_react60.default.useState("main");
|
|
104537
|
+
const [mainSelection, setMainSelection] = import_react60.default.useState(() => mainSelectionFloor);
|
|
104538
|
+
const [showAllSelection, setShowAllSelection] = import_react60.default.useState(0);
|
|
104539
|
+
const [showAllTasksSelection, setShowAllTasksSelection] = import_react60.default.useState(0);
|
|
104540
|
+
const [statusModal, setStatusModal] = import_react60.default.useState(null);
|
|
104541
|
+
const [implementModeModal, setImplementModeModal] = import_react60.default.useState(null);
|
|
104542
|
+
const [pendingOpencodeActionIds, setPendingOpencodeActionIds] = import_react60.default.useState(() => new Set);
|
|
104543
|
+
const showAllShortcutRef = import_react60.default.useRef(false);
|
|
104544
|
+
const statusModalRef = import_react60.default.useRef(null);
|
|
104545
|
+
const implementModeModalRef = import_react60.default.useRef(null);
|
|
104546
|
+
const pendingOpencodeActionIdsRef = import_react60.default.useRef(pendingOpencodeActionIds);
|
|
104547
|
+
const updatePendingOpencodeActionIds = import_react60.default.useCallback((updater) => {
|
|
104256
104548
|
setPendingOpencodeActionIds((current) => {
|
|
104257
104549
|
const next = updater(current);
|
|
104258
104550
|
pendingOpencodeActionIdsRef.current = next;
|
|
104259
104551
|
return next;
|
|
104260
104552
|
});
|
|
104261
104553
|
}, []);
|
|
104262
|
-
const addPendingOpencodeAction =
|
|
104554
|
+
const addPendingOpencodeAction = import_react60.default.useCallback((id) => {
|
|
104263
104555
|
updatePendingOpencodeActionIds((current) => {
|
|
104264
104556
|
if (current.has(id)) {
|
|
104265
104557
|
return current;
|
|
@@ -104269,7 +104561,7 @@ function StatusView({
|
|
|
104269
104561
|
return next;
|
|
104270
104562
|
});
|
|
104271
104563
|
}, [updatePendingOpencodeActionIds]);
|
|
104272
|
-
const clearPendingOpencodeAction =
|
|
104564
|
+
const clearPendingOpencodeAction = import_react60.default.useCallback((id) => {
|
|
104273
104565
|
updatePendingOpencodeActionIds((current) => {
|
|
104274
104566
|
if (!current.has(id)) {
|
|
104275
104567
|
return current;
|
|
@@ -104279,7 +104571,7 @@ function StatusView({
|
|
|
104279
104571
|
return next;
|
|
104280
104572
|
});
|
|
104281
104573
|
}, [updatePendingOpencodeActionIds]);
|
|
104282
|
-
const trackPendingOpencodeAction =
|
|
104574
|
+
const trackPendingOpencodeAction = import_react60.default.useCallback((id, launcher) => {
|
|
104283
104575
|
if (pendingOpencodeActionIdsRef.current.has(id)) {
|
|
104284
104576
|
return;
|
|
104285
104577
|
}
|
|
@@ -104305,9 +104597,11 @@ function StatusView({
|
|
|
104305
104597
|
const selectedItem = totalItems > 0 ? currentItems[selection] : null;
|
|
104306
104598
|
const hasOpenableSelection = Boolean(selectedItem && !isShowAllRow(selectedItem));
|
|
104307
104599
|
const selectedTaskItem = selectedItem && !isShowAllRow(selectedItem) && isTaskItem(selectedItem) ? selectedItem : null;
|
|
104600
|
+
const canOpenSession = Boolean(selectedTaskItem && selectedTaskItem.status === "wip" && (view === "main" || view === "show-all-tasks"));
|
|
104308
104601
|
const statusModalCandidate = selectedItem && !isShowAllRow(selectedItem) ? buildStatusModalState(selectedItem) : null;
|
|
104309
104602
|
const canUpdateStatus = Boolean(statusModalCandidate);
|
|
104310
|
-
const
|
|
104603
|
+
const taskStartIndex = changeItems.length + 1;
|
|
104604
|
+
const readyToImplementStart = taskStartIndex + wipItems.length + blockedItems.length;
|
|
104311
104605
|
const readyToImplementEnd = readyToImplementStart + readyItems.length;
|
|
104312
104606
|
const readyToImplementIds = readyToImplement.map((task) => task.id);
|
|
104313
104607
|
const pendingImplementationNoTaskIds = pendingImplementationNoTasks.map((change) => change.id);
|
|
@@ -104318,15 +104612,25 @@ function StatusView({
|
|
|
104318
104612
|
const canCreateTasks = Boolean(selectedCreateTasksChangeId && !pendingOpencodeActionIds.has(selectedCreateTasksChangeId));
|
|
104319
104613
|
const canReview = Boolean(selectedReviewChangeId && !pendingOpencodeActionIds.has(selectedReviewChangeId));
|
|
104320
104614
|
const canImplement = Boolean(selectedReadyTaskId && !pendingOpencodeActionIds.has(selectedReadyTaskId));
|
|
104321
|
-
const
|
|
104322
|
-
|
|
104323
|
-
|
|
104324
|
-
|
|
104325
|
-
|
|
104326
|
-
|
|
104327
|
-
|
|
104328
|
-
|
|
104329
|
-
|
|
104615
|
+
const buildImplementPrompt = import_react60.default.useCallback((taskId) => {
|
|
104616
|
+
if (!schubDir) {
|
|
104617
|
+
return;
|
|
104618
|
+
}
|
|
104619
|
+
const taskPath = taskListById.get(taskId)?.path ?? showAllTaskListById.get(taskId)?.path;
|
|
104620
|
+
if (!taskPath) {
|
|
104621
|
+
return;
|
|
104622
|
+
}
|
|
104623
|
+
return prepareImplementContext(schubDir, resolve12(repoRoot, taskPath));
|
|
104624
|
+
}, [repoRoot, schubDir, showAllTaskListById, taskListById]);
|
|
104625
|
+
const lastShortcutRef = import_react60.default.useRef(null);
|
|
104626
|
+
const readyToImplementIdsRef = import_react60.default.useRef(readyToImplementIds);
|
|
104627
|
+
const readyToImplementRangeRef = import_react60.default.useRef({ start: readyToImplementStart, end: readyToImplementEnd });
|
|
104628
|
+
const pendingImplementationNoTaskIdsRef = import_react60.default.useRef(pendingImplementationNoTaskIds);
|
|
104629
|
+
const pendingReviewIdsRef = import_react60.default.useRef(pendingReviewIds);
|
|
104630
|
+
const currentItemsRef = import_react60.default.useRef(currentItems);
|
|
104631
|
+
const selectionRef = import_react60.default.useRef(selection);
|
|
104632
|
+
const viewRef = import_react60.default.useRef(view);
|
|
104633
|
+
import_react60.default.useEffect(() => {
|
|
104330
104634
|
currentItemsRef.current = currentItems;
|
|
104331
104635
|
selectionRef.current = selection;
|
|
104332
104636
|
viewRef.current = view;
|
|
@@ -104346,16 +104650,16 @@ function StatusView({
|
|
|
104346
104650
|
selection,
|
|
104347
104651
|
view
|
|
104348
104652
|
]);
|
|
104349
|
-
|
|
104350
|
-
statusModalRef.current = statusModal;
|
|
104351
|
-
}, [statusModal]);
|
|
104352
|
-
import_react61.default.useLayoutEffect(() => {
|
|
104653
|
+
import_react60.default.useLayoutEffect(() => {
|
|
104353
104654
|
if (!onShortcutsChange) {
|
|
104354
104655
|
return;
|
|
104355
104656
|
}
|
|
104356
104657
|
const shortcuts = [];
|
|
104357
104658
|
if (hasOpenableSelection) {
|
|
104358
|
-
shortcuts.push(
|
|
104659
|
+
shortcuts.push(COPY_SHORTCUT);
|
|
104660
|
+
}
|
|
104661
|
+
if (canOpenSession) {
|
|
104662
|
+
shortcuts.push(SESSION_SHORTCUT);
|
|
104359
104663
|
}
|
|
104360
104664
|
if (canCreateTasks) {
|
|
104361
104665
|
shortcuts.push(CREATE_TASKS_SHORTCUT);
|
|
@@ -104375,7 +104679,15 @@ function StatusView({
|
|
|
104375
104679
|
}
|
|
104376
104680
|
lastShortcutRef.current = shortcutKey;
|
|
104377
104681
|
onShortcutsChange(shortcuts);
|
|
104378
|
-
}, [
|
|
104682
|
+
}, [
|
|
104683
|
+
canCreateTasks,
|
|
104684
|
+
canImplement,
|
|
104685
|
+
canOpenSession,
|
|
104686
|
+
canReview,
|
|
104687
|
+
canUpdateStatus,
|
|
104688
|
+
hasOpenableSelection,
|
|
104689
|
+
onShortcutsChange
|
|
104690
|
+
]);
|
|
104379
104691
|
const refresh = () => {
|
|
104380
104692
|
if (!schubDir) {
|
|
104381
104693
|
return;
|
|
@@ -104384,7 +104696,7 @@ function StatusView({
|
|
|
104384
104696
|
setRefreshTick((current) => current + 1);
|
|
104385
104697
|
};
|
|
104386
104698
|
useRefreshInterval({ enabled: Boolean(schubDir), refreshIntervalMs, onRefresh: refresh });
|
|
104387
|
-
|
|
104699
|
+
import_react60.default.useEffect(() => {
|
|
104388
104700
|
if (mainItems.length === 0) {
|
|
104389
104701
|
setMainSelection(0);
|
|
104390
104702
|
return;
|
|
@@ -104394,40 +104706,42 @@ function StatusView({
|
|
|
104394
104706
|
return clamped === 0 ? mainSelectionFloor : clamped;
|
|
104395
104707
|
});
|
|
104396
104708
|
}, [mainItems.length, mainSelectionFloor]);
|
|
104397
|
-
|
|
104709
|
+
import_react60.default.useEffect(() => {
|
|
104398
104710
|
if (showAllItems.length === 0) {
|
|
104399
104711
|
setShowAllSelection(0);
|
|
104400
104712
|
return;
|
|
104401
104713
|
}
|
|
104402
104714
|
setShowAllSelection((current) => clampSelection(current, showAllItems.length));
|
|
104403
104715
|
}, [showAllItems.length]);
|
|
104404
|
-
|
|
104716
|
+
import_react60.default.useEffect(() => {
|
|
104405
104717
|
if (showAllTaskItems.length === 0) {
|
|
104406
104718
|
setShowAllTasksSelection(0);
|
|
104407
104719
|
return;
|
|
104408
104720
|
}
|
|
104409
104721
|
setShowAllTasksSelection((current) => clampSelection(current, showAllTaskItems.length));
|
|
104410
104722
|
}, [showAllTaskItems.length]);
|
|
104411
|
-
|
|
104723
|
+
import_react60.default.useEffect(() => {
|
|
104412
104724
|
if (view) {
|
|
104413
104725
|
showAllShortcutRef.current = false;
|
|
104414
104726
|
}
|
|
104415
104727
|
}, [view]);
|
|
104416
|
-
const handleCreateTasks =
|
|
104728
|
+
const handleCreateTasks = import_react60.default.useCallback((changeId, root) => {
|
|
104417
104729
|
trackPendingOpencodeAction(changeId, () => onCreateTasks(changeId, root));
|
|
104418
104730
|
}, [onCreateTasks, trackPendingOpencodeAction]);
|
|
104419
|
-
const handleReview =
|
|
104731
|
+
const handleReview = import_react60.default.useCallback((changeId, root) => {
|
|
104420
104732
|
trackPendingOpencodeAction(changeId, () => onReview(changeId, root));
|
|
104421
104733
|
}, [onReview, trackPendingOpencodeAction]);
|
|
104422
|
-
const handleImplement =
|
|
104423
|
-
|
|
104424
|
-
|
|
104734
|
+
const handleImplement = import_react60.default.useCallback((taskId, root, mode) => {
|
|
104735
|
+
const prompt = buildImplementPrompt(taskId);
|
|
104736
|
+
trackPendingOpencodeAction(taskId, () => onImplement(taskId, root, mode, prompt));
|
|
104737
|
+
}, [buildImplementPrompt, onImplement, trackPendingOpencodeAction]);
|
|
104425
104738
|
useStatusViewInput({
|
|
104426
104739
|
isActive,
|
|
104427
104740
|
schubDir,
|
|
104428
104741
|
repoRoot,
|
|
104429
104742
|
showAllShortcutRef,
|
|
104430
104743
|
statusModalRef,
|
|
104744
|
+
implementModeModalRef,
|
|
104431
104745
|
currentItemsRef,
|
|
104432
104746
|
selectionRef,
|
|
104433
104747
|
viewRef,
|
|
@@ -104438,6 +104752,7 @@ function StatusView({
|
|
|
104438
104752
|
pendingOpencodeActionIdsRef,
|
|
104439
104753
|
setSelection,
|
|
104440
104754
|
setStatusModal,
|
|
104755
|
+
setImplementModeModal,
|
|
104441
104756
|
setRefreshTick,
|
|
104442
104757
|
updateView,
|
|
104443
104758
|
onCopyId,
|
|
@@ -104464,14 +104779,11 @@ function StatusView({
|
|
|
104464
104779
|
]
|
|
104465
104780
|
}, undefined, true, undefined, this);
|
|
104466
104781
|
}
|
|
104467
|
-
|
|
104468
|
-
|
|
104469
|
-
|
|
104470
|
-
|
|
104471
|
-
|
|
104472
|
-
}, undefined, false, undefined, this);
|
|
104473
|
-
}
|
|
104474
|
-
const content = view === "show-all-tasks" ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(StatusShowAllTasksView, {
|
|
104782
|
+
const content = view === "show-all" ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(StatusShowAllView, {
|
|
104783
|
+
selection,
|
|
104784
|
+
sections: showAllSections,
|
|
104785
|
+
pendingIds: pendingOpencodeActionIds
|
|
104786
|
+
}, undefined, false, undefined, this) : view === "show-all-tasks" ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(StatusShowAllTasksView, {
|
|
104475
104787
|
selection,
|
|
104476
104788
|
sections: showAllTaskSections,
|
|
104477
104789
|
pendingIds: pendingOpencodeActionIds
|
|
@@ -104487,6 +104799,9 @@ function StatusView({
|
|
|
104487
104799
|
flexDirection: "column",
|
|
104488
104800
|
children: [
|
|
104489
104801
|
content,
|
|
104802
|
+
implementModeModal ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(ImplementModeModal, {
|
|
104803
|
+
modal: implementModeModal
|
|
104804
|
+
}, undefined, false, undefined, this) : null,
|
|
104490
104805
|
statusModal ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(ListItemStatusModal, {
|
|
104491
104806
|
statusModal
|
|
104492
104807
|
}, undefined, false, undefined, this) : null
|
|
@@ -104495,7 +104810,7 @@ function StatusView({
|
|
|
104495
104810
|
}
|
|
104496
104811
|
|
|
104497
104812
|
// src/tui/shared/detail-tasks.ts
|
|
104498
|
-
var
|
|
104813
|
+
var compareText3 = (left2, right2) => left2.localeCompare(right2, undefined, { sensitivity: "base" });
|
|
104499
104814
|
var idSortValue3 = (value) => {
|
|
104500
104815
|
const match = value.match(/\d+/);
|
|
104501
104816
|
return match ? Number(match[0]) : Number.POSITIVE_INFINITY;
|
|
@@ -104505,7 +104820,7 @@ var compareIds3 = (left2, right2) => {
|
|
|
104505
104820
|
if (numberDiff !== 0) {
|
|
104506
104821
|
return numberDiff;
|
|
104507
104822
|
}
|
|
104508
|
-
return
|
|
104823
|
+
return compareText3(left2, right2);
|
|
104509
104824
|
};
|
|
104510
104825
|
var resolveTaskName2 = (task) => {
|
|
104511
104826
|
if (task.status === "blocked") {
|
|
@@ -104521,38 +104836,93 @@ var sortDetailTasks = (tasks) => [...tasks].sort((left2, right2) => {
|
|
|
104521
104836
|
if (idCompare !== 0) {
|
|
104522
104837
|
return idCompare;
|
|
104523
104838
|
}
|
|
104524
|
-
return
|
|
104839
|
+
return compareText3(resolveTaskName2(left2), resolveTaskName2(right2));
|
|
104525
104840
|
});
|
|
104526
104841
|
var buildDetailTasks = (tasks) => TASK_STATUSES.flatMap((status) => sortDetailTasks(tasks.filter((task) => task.status === status)));
|
|
104527
104842
|
|
|
104528
104843
|
// src/tui/app.tsx
|
|
104529
104844
|
var jsx_dev_runtime13 = __toESM(require_jsx_dev_runtime(), 1);
|
|
104530
|
-
var
|
|
104531
|
-
{ id: "status", label: "Overview" },
|
|
104532
|
-
{ id: "plan", label: "Plan" }
|
|
104533
|
-
];
|
|
104845
|
+
var TAB_LABEL = "Overview";
|
|
104534
104846
|
var COPY_BANNER_TEXT = "Copied to clipboard !";
|
|
104535
104847
|
var COPY_BANNER_TIMEOUT_MS = 1500;
|
|
104536
|
-
var OPEN_SHORTCUT2 = { keyLabel: "o", label: "preview" };
|
|
104537
104848
|
var COPY_SHORTCUT2 = { keyLabel: "c", label: "copy id" };
|
|
104849
|
+
var STATUS_SHORTCUT2 = { keyLabel: "s", label: "status" };
|
|
104850
|
+
var ImplementErrorModal = ({ message }) => {
|
|
104851
|
+
return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
104852
|
+
backgroundColor: "darkGray",
|
|
104853
|
+
flexDirection: "column",
|
|
104854
|
+
paddingX: 2,
|
|
104855
|
+
paddingY: 1,
|
|
104856
|
+
children: [
|
|
104857
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
104858
|
+
marginBottom: 1,
|
|
104859
|
+
children: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
104860
|
+
color: "red",
|
|
104861
|
+
children: "Implement failed"
|
|
104862
|
+
}, undefined, false, undefined, this)
|
|
104863
|
+
}, undefined, false, undefined, this),
|
|
104864
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
104865
|
+
color: "white",
|
|
104866
|
+
children: message
|
|
104867
|
+
}, undefined, false, undefined, this),
|
|
104868
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
104869
|
+
alignItems: "flex-end",
|
|
104870
|
+
marginTop: 1,
|
|
104871
|
+
children: [
|
|
104872
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
104873
|
+
color: "white",
|
|
104874
|
+
children: "esc"
|
|
104875
|
+
}, undefined, false, undefined, this),
|
|
104876
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
104877
|
+
color: "gray",
|
|
104878
|
+
children: " dismiss"
|
|
104879
|
+
}, undefined, false, undefined, this)
|
|
104880
|
+
]
|
|
104881
|
+
}, undefined, true, undefined, this)
|
|
104882
|
+
]
|
|
104883
|
+
}, undefined, true, undefined, this);
|
|
104884
|
+
};
|
|
104885
|
+
var updateDetailStatusModalSelection = (current, delta) => {
|
|
104886
|
+
if (!current) {
|
|
104887
|
+
return current;
|
|
104888
|
+
}
|
|
104889
|
+
const nextSelection = clampSelection(current.selection + delta, current.options.length);
|
|
104890
|
+
if (nextSelection === current.selection) {
|
|
104891
|
+
return current;
|
|
104892
|
+
}
|
|
104893
|
+
return { ...current, selection: nextSelection };
|
|
104894
|
+
};
|
|
104895
|
+
var applyDetailStatusUpdate = (schubDir, modal) => {
|
|
104896
|
+
const option = modal.options[modal.selection];
|
|
104897
|
+
if (!option) {
|
|
104898
|
+
return;
|
|
104899
|
+
}
|
|
104900
|
+
if (modal.itemKind !== "task") {
|
|
104901
|
+
return;
|
|
104902
|
+
}
|
|
104903
|
+
if (option.status === "backlog" || option.status === "reviewed" || option.status === "archived") {
|
|
104904
|
+
updateTaskStatuses(schubDir, [modal.itemId], option.status);
|
|
104905
|
+
}
|
|
104906
|
+
};
|
|
104538
104907
|
function App2({
|
|
104539
104908
|
copyToClipboard: copyToClipboard2 = copyToClipboard,
|
|
104540
104909
|
initialSessionView = null,
|
|
104541
104910
|
onImplement,
|
|
104542
104911
|
startDir
|
|
104543
104912
|
}) {
|
|
104544
|
-
const [mode, setMode] = import_react62.default.useState("status");
|
|
104545
104913
|
const { stdout } = use_stdout_default();
|
|
104546
|
-
const [dimensions, setDimensions] =
|
|
104914
|
+
const [dimensions, setDimensions] = import_react61.default.useState(() => ({
|
|
104547
104915
|
columns: stdout.columns,
|
|
104548
104916
|
rows: stdout.rows
|
|
104549
104917
|
}));
|
|
104550
|
-
const [copyBanner, setCopyBanner] =
|
|
104551
|
-
const [statusShortcuts, setStatusShortcuts] =
|
|
104552
|
-
const [statusView, setStatusView] =
|
|
104553
|
-
const [preview, setPreview] =
|
|
104554
|
-
const [proposalDetail, setProposalDetail] =
|
|
104555
|
-
const [
|
|
104918
|
+
const [copyBanner, setCopyBanner] = import_react61.default.useState(null);
|
|
104919
|
+
const [statusShortcuts, setStatusShortcuts] = import_react61.default.useState([]);
|
|
104920
|
+
const [statusView, setStatusView] = import_react61.default.useState("main");
|
|
104921
|
+
const [preview, setPreview] = import_react61.default.useState(null);
|
|
104922
|
+
const [proposalDetail, setProposalDetail] = import_react61.default.useState(null);
|
|
104923
|
+
const [detailStatusModal, setDetailStatusModal] = import_react61.default.useState(null);
|
|
104924
|
+
const [implementError, setImplementError] = import_react61.default.useState(null);
|
|
104925
|
+
const [sessionView, setSessionView] = import_react61.default.useState(initialSessionView);
|
|
104556
104926
|
const versionLabel = `${package_default.version}`;
|
|
104557
104927
|
const homeDir = homedir2();
|
|
104558
104928
|
const resolvedStartDir = startDir ?? process.env.SCHUB_CWD ?? process.cwd();
|
|
@@ -104567,15 +104937,27 @@ function App2({
|
|
|
104567
104937
|
}
|
|
104568
104938
|
return targetDir;
|
|
104569
104939
|
})();
|
|
104570
|
-
const repoRoot = schubDir ?
|
|
104940
|
+
const repoRoot = schubDir ? dirname14(schubDir) : "";
|
|
104571
104941
|
const isPreviewActive = Boolean(preview);
|
|
104572
104942
|
const isDetailActive = Boolean(proposalDetail);
|
|
104573
|
-
const isSessionActive =
|
|
104943
|
+
const isSessionActive = Boolean(sessionView);
|
|
104944
|
+
const isImplementErrorActive = Boolean(implementError);
|
|
104574
104945
|
const detailSelection = proposalDetail?.selection ?? 0;
|
|
104575
104946
|
const detailTasks = proposalDetail && schubDir ? buildDetailTasks(listTasksForChange(schubDir, proposalDetail.changeId)) : [];
|
|
104576
104947
|
const selectedDetailTask = detailTasks[detailSelection] ?? null;
|
|
104577
|
-
const
|
|
104578
|
-
|
|
104948
|
+
const detailStatusItem = selectedDetailTask ? {
|
|
104949
|
+
id: selectedDetailTask.id,
|
|
104950
|
+
title: selectedDetailTask.title,
|
|
104951
|
+
status: selectedDetailTask.status,
|
|
104952
|
+
sourceStatus: selectedDetailTask.status,
|
|
104953
|
+
path: selectedDetailTask.path,
|
|
104954
|
+
changeId: selectedDetailTask.changeId,
|
|
104955
|
+
blockedReason: selectedDetailTask.blockedReason
|
|
104956
|
+
} : null;
|
|
104957
|
+
const detailStatusModalCandidate = detailStatusItem ? buildStatusModalState(detailStatusItem) : null;
|
|
104958
|
+
const canUpdateDetailStatus = Boolean(detailStatusModalCandidate);
|
|
104959
|
+
const detailShortcuts = isDetailActive && detailTasks.length > 0 ? [COPY_SHORTCUT2, ...canUpdateDetailStatus ? [STATUS_SHORTCUT2] : []] : [];
|
|
104960
|
+
import_react61.default.useEffect(() => {
|
|
104579
104961
|
if (!copyBanner) {
|
|
104580
104962
|
return;
|
|
104581
104963
|
}
|
|
@@ -104586,7 +104968,7 @@ function App2({
|
|
|
104586
104968
|
clearTimeout(timeout);
|
|
104587
104969
|
};
|
|
104588
104970
|
}, [copyBanner]);
|
|
104589
|
-
|
|
104971
|
+
import_react61.default.useEffect(() => {
|
|
104590
104972
|
const handleResize = () => {
|
|
104591
104973
|
setDimensions({ columns: stdout.columns, rows: stdout.rows });
|
|
104592
104974
|
};
|
|
@@ -104595,39 +104977,27 @@ function App2({
|
|
|
104595
104977
|
stdout.off("resize", handleResize);
|
|
104596
104978
|
};
|
|
104597
104979
|
}, [stdout]);
|
|
104598
|
-
const showTabs =
|
|
104980
|
+
const showTabs = statusView === "main" && !isPreviewActive && !isDetailActive && !isSessionActive;
|
|
104599
104981
|
const showHeader = showTabs || Boolean(copyBanner);
|
|
104600
104982
|
const previewHeight = Math.max(10, dimensions.rows || 0);
|
|
104601
|
-
use_input_default((_input, key) => {
|
|
104602
|
-
if (key.tab) {
|
|
104603
|
-
setMode((current) => {
|
|
104604
|
-
const currentIndex = tabs.findIndex((tab2) => tab2.id === current);
|
|
104605
|
-
const nextIndex = currentIndex < 0 ? 0 : (currentIndex + 1) % tabs.length;
|
|
104606
|
-
return tabs[nextIndex]?.id ?? "status";
|
|
104607
|
-
});
|
|
104608
|
-
}
|
|
104609
|
-
}, { isActive: showTabs });
|
|
104610
|
-
import_react62.default.useEffect(() => {
|
|
104611
|
-
if (mode !== "status") {
|
|
104612
|
-
setStatusShortcuts([]);
|
|
104613
|
-
}
|
|
104614
|
-
}, [mode]);
|
|
104615
104983
|
const handleCopyId = (value) => {
|
|
104616
104984
|
copyToClipboard2(value);
|
|
104617
104985
|
setCopyBanner(COPY_BANNER_TEXT);
|
|
104618
104986
|
};
|
|
104619
104987
|
const handleOpenPreview = (repoRoot2, relativePath) => {
|
|
104620
|
-
const targetPath =
|
|
104621
|
-
const markdown =
|
|
104622
|
-
setPreview({ fileName:
|
|
104988
|
+
const targetPath = resolve13(repoRoot2, relativePath);
|
|
104989
|
+
const markdown = readFileSync15(targetPath, "utf8");
|
|
104990
|
+
setPreview({ fileName: basename7(relativePath), markdown });
|
|
104623
104991
|
};
|
|
104624
104992
|
const handleClosePreview = () => {
|
|
104625
104993
|
setPreview(null);
|
|
104626
104994
|
};
|
|
104627
104995
|
const handleOpenDetail = (changeId) => {
|
|
104996
|
+
setDetailStatusModal(null);
|
|
104628
104997
|
setProposalDetail({ changeId, selection: 0 });
|
|
104629
104998
|
};
|
|
104630
104999
|
const handleCloseDetail = () => {
|
|
105000
|
+
setDetailStatusModal(null);
|
|
104631
105001
|
setProposalDetail(null);
|
|
104632
105002
|
};
|
|
104633
105003
|
const handleOpenSession = (state) => {
|
|
@@ -104636,8 +105006,53 @@ function App2({
|
|
|
104636
105006
|
const handleCloseSession = () => {
|
|
104637
105007
|
setSessionView(null);
|
|
104638
105008
|
};
|
|
105009
|
+
const handleImplement = import_react61.default.useCallback((taskId, root, selectedMode, prompt) => {
|
|
105010
|
+
if (onImplement) {
|
|
105011
|
+
onImplement(taskId, root, selectedMode, prompt);
|
|
105012
|
+
return;
|
|
105013
|
+
}
|
|
105014
|
+
const options2 = prompt ? { prompt } : undefined;
|
|
105015
|
+
if (selectedMode === "worktree") {
|
|
105016
|
+
try {
|
|
105017
|
+
const worktree = createTaskWorktree({ repoRoot: root, taskId });
|
|
105018
|
+
const worktreePath = worktree?.worktreePath;
|
|
105019
|
+
if (!worktreePath) {
|
|
105020
|
+
throw new Error("Unable to resolve git root for worktree.");
|
|
105021
|
+
}
|
|
105022
|
+
return launchOpencodeImplement(taskId, root, options2, {
|
|
105023
|
+
env: { ...process.env, SCHUB_CWD: worktreePath },
|
|
105024
|
+
cwd: worktreePath
|
|
105025
|
+
});
|
|
105026
|
+
} catch (error) {
|
|
105027
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
105028
|
+
setImplementError({ message });
|
|
105029
|
+
return;
|
|
105030
|
+
}
|
|
105031
|
+
}
|
|
105032
|
+
return launchOpencodeImplement(taskId, root, options2);
|
|
105033
|
+
}, [onImplement]);
|
|
104639
105034
|
use_input_default((input, key) => {
|
|
104640
|
-
const
|
|
105035
|
+
const inputState = buildInputState(input, key);
|
|
105036
|
+
const { lowerInput, isEscape, isEnter, isDownArrow, isUpArrow, isLeftArrow, isRightArrow } = inputState;
|
|
105037
|
+
if (detailStatusModal) {
|
|
105038
|
+
if (isEscape || lowerInput === "q") {
|
|
105039
|
+
setDetailStatusModal(null);
|
|
105040
|
+
return;
|
|
105041
|
+
}
|
|
105042
|
+
if (isUpArrow || isLeftArrow) {
|
|
105043
|
+
setDetailStatusModal((current) => updateDetailStatusModalSelection(current, -1));
|
|
105044
|
+
return;
|
|
105045
|
+
}
|
|
105046
|
+
if (isDownArrow || isRightArrow) {
|
|
105047
|
+
setDetailStatusModal((current) => updateDetailStatusModalSelection(current, 1));
|
|
105048
|
+
return;
|
|
105049
|
+
}
|
|
105050
|
+
if (isEnter && schubDir) {
|
|
105051
|
+
applyDetailStatusUpdate(schubDir, detailStatusModal);
|
|
105052
|
+
setDetailStatusModal(null);
|
|
105053
|
+
}
|
|
105054
|
+
return;
|
|
105055
|
+
}
|
|
104641
105056
|
if (isEscape) {
|
|
104642
105057
|
handleCloseDetail();
|
|
104643
105058
|
return;
|
|
@@ -104674,23 +105089,34 @@ function App2({
|
|
|
104674
105089
|
if (!selectedDetailTask) {
|
|
104675
105090
|
return;
|
|
104676
105091
|
}
|
|
104677
|
-
if (
|
|
105092
|
+
if (lowerInput === "s" && detailStatusModalCandidate) {
|
|
105093
|
+
setDetailStatusModal(detailStatusModalCandidate);
|
|
105094
|
+
return;
|
|
105095
|
+
}
|
|
105096
|
+
if (isEnter) {
|
|
104678
105097
|
handleOpenPreview(repoRoot, selectedDetailTask.path);
|
|
104679
105098
|
return;
|
|
104680
105099
|
}
|
|
104681
105100
|
if (input === "c") {
|
|
104682
105101
|
handleCopyId(selectedDetailTask.id);
|
|
104683
105102
|
}
|
|
104684
|
-
}, { isActive: isDetailActive && !isPreviewActive && !isSessionActive });
|
|
105103
|
+
}, { isActive: isDetailActive && !isPreviewActive && !isSessionActive && !isImplementErrorActive });
|
|
104685
105104
|
use_input_default((input, key) => {
|
|
104686
105105
|
const { isEscape } = buildInputState(input, key);
|
|
104687
105106
|
if (isEscape) {
|
|
104688
105107
|
handleCloseSession();
|
|
104689
105108
|
}
|
|
104690
|
-
}, { isActive: isSessionActive && !isPreviewActive });
|
|
104691
|
-
|
|
104692
|
-
|
|
104693
|
-
|
|
105109
|
+
}, { isActive: isSessionActive && !isPreviewActive && !isImplementErrorActive });
|
|
105110
|
+
use_input_default((input, key) => {
|
|
105111
|
+
const { lowerInput, isEscape } = buildInputState(input, key);
|
|
105112
|
+
if (isEscape || lowerInput === "q") {
|
|
105113
|
+
setImplementError(null);
|
|
105114
|
+
}
|
|
105115
|
+
}, { isActive: isImplementErrorActive });
|
|
105116
|
+
const shortcuts = !isPreviewActive && !isSessionActive && !isImplementErrorActive ? isDetailActive ? detailShortcuts : statusShortcuts : [];
|
|
105117
|
+
const isStatusListVisible = !isPreviewActive && !isDetailActive && !isSessionActive;
|
|
105118
|
+
const isStatusListActive = isStatusListVisible && !isImplementErrorActive;
|
|
105119
|
+
const isDetailVisible = isDetailActive && !isPreviewActive && !isSessionActive && !isImplementErrorActive;
|
|
104694
105120
|
return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
104695
105121
|
backgroundColor: "black",
|
|
104696
105122
|
flexDirection: "column",
|
|
@@ -104709,26 +105135,23 @@ function App2({
|
|
|
104709
105135
|
children: [
|
|
104710
105136
|
showTabs ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
104711
105137
|
flexDirection: "row",
|
|
104712
|
-
children:
|
|
104713
|
-
|
|
104714
|
-
|
|
104715
|
-
|
|
104716
|
-
|
|
104717
|
-
|
|
104718
|
-
|
|
104719
|
-
|
|
104720
|
-
|
|
104721
|
-
|
|
104722
|
-
|
|
104723
|
-
|
|
104724
|
-
|
|
104725
|
-
|
|
104726
|
-
|
|
104727
|
-
|
|
104728
|
-
|
|
104729
|
-
}, undefined, false, undefined, this)
|
|
104730
|
-
}, tab2.id, false, undefined, this);
|
|
104731
|
-
})
|
|
105138
|
+
children: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
105139
|
+
marginRight: 4,
|
|
105140
|
+
borderStyle: "bold",
|
|
105141
|
+
borderLeft: true,
|
|
105142
|
+
borderTop: false,
|
|
105143
|
+
borderRight: false,
|
|
105144
|
+
borderBottom: false,
|
|
105145
|
+
borderLeftColor: "blueBright",
|
|
105146
|
+
flexDirection: "row",
|
|
105147
|
+
alignItems: "center",
|
|
105148
|
+
paddingLeft: 1,
|
|
105149
|
+
children: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
105150
|
+
color: "white",
|
|
105151
|
+
bold: true,
|
|
105152
|
+
children: TAB_LABEL
|
|
105153
|
+
}, undefined, false, undefined, this)
|
|
105154
|
+
}, undefined, false, undefined, this)
|
|
104732
105155
|
}, undefined, false, undefined, this) : null,
|
|
104733
105156
|
copyBanner ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
104734
105157
|
backgroundColor: "green",
|
|
@@ -104747,42 +105170,49 @@ function App2({
|
|
|
104747
105170
|
paddingY: 1,
|
|
104748
105171
|
flexGrow: 1,
|
|
104749
105172
|
flexShrink: 1,
|
|
104750
|
-
children:
|
|
104751
|
-
|
|
104752
|
-
|
|
104753
|
-
|
|
104754
|
-
|
|
104755
|
-
|
|
104756
|
-
|
|
104757
|
-
|
|
104758
|
-
|
|
104759
|
-
|
|
104760
|
-
|
|
104761
|
-
|
|
104762
|
-
|
|
104763
|
-
|
|
104764
|
-
|
|
104765
|
-
|
|
104766
|
-
|
|
104767
|
-
|
|
104768
|
-
|
|
104769
|
-
|
|
104770
|
-
|
|
104771
|
-
|
|
104772
|
-
|
|
104773
|
-
|
|
104774
|
-
|
|
104775
|
-
|
|
104776
|
-
|
|
104777
|
-
|
|
104778
|
-
|
|
104779
|
-
|
|
104780
|
-
|
|
104781
|
-
|
|
104782
|
-
|
|
104783
|
-
|
|
104784
|
-
|
|
104785
|
-
|
|
105173
|
+
children: [
|
|
105174
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
105175
|
+
display: isStatusListVisible ? "flex" : "none",
|
|
105176
|
+
flexDirection: "column",
|
|
105177
|
+
children: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(StatusView, {
|
|
105178
|
+
startDir: resolvedStartDir,
|
|
105179
|
+
isActive: isStatusListActive,
|
|
105180
|
+
onCopyId: handleCopyId,
|
|
105181
|
+
onImplement: handleImplement,
|
|
105182
|
+
onOpen: handleOpenPreview,
|
|
105183
|
+
onOpenDetail: handleOpenDetail,
|
|
105184
|
+
onOpenSession: handleOpenSession,
|
|
105185
|
+
onShortcutsChange: setStatusShortcuts,
|
|
105186
|
+
onViewChange: setStatusView
|
|
105187
|
+
}, undefined, false, undefined, this)
|
|
105188
|
+
}, undefined, false, undefined, this),
|
|
105189
|
+
implementError ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(ImplementErrorModal, {
|
|
105190
|
+
message: implementError.message
|
|
105191
|
+
}, undefined, false, undefined, this) : null,
|
|
105192
|
+
isSessionActive && sessionView ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(SessionView, {
|
|
105193
|
+
state: sessionView
|
|
105194
|
+
}, undefined, false, undefined, this) : null,
|
|
105195
|
+
isDetailVisible && proposalDetail ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
105196
|
+
flexDirection: "column",
|
|
105197
|
+
children: [
|
|
105198
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(ProposalDetailView, {
|
|
105199
|
+
changeId: proposalDetail.changeId,
|
|
105200
|
+
selection: detailSelection,
|
|
105201
|
+
startDir: resolvedStartDir
|
|
105202
|
+
}, undefined, false, undefined, this),
|
|
105203
|
+
detailStatusModal ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(ListItemStatusModal, {
|
|
105204
|
+
statusModal: detailStatusModal
|
|
105205
|
+
}, undefined, false, undefined, this) : null
|
|
105206
|
+
]
|
|
105207
|
+
}, undefined, true, undefined, this) : null,
|
|
105208
|
+
isPreviewActive && preview ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(PreviewPage, {
|
|
105209
|
+
fileName: preview.fileName,
|
|
105210
|
+
markdown: preview.markdown,
|
|
105211
|
+
height: previewHeight,
|
|
105212
|
+
onClose: handleClosePreview
|
|
105213
|
+
}, undefined, false, undefined, this) : null
|
|
105214
|
+
]
|
|
105215
|
+
}, undefined, true, undefined, this),
|
|
104786
105216
|
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
104787
105217
|
flexDirection: "column",
|
|
104788
105218
|
paddingX: 2,
|
|
@@ -104791,44 +105221,31 @@ function App2({
|
|
|
104791
105221
|
children: [
|
|
104792
105222
|
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
104793
105223
|
justifyContent: "space-between",
|
|
104794
|
-
children:
|
|
104795
|
-
|
|
104796
|
-
|
|
104797
|
-
|
|
104798
|
-
marginRight: 2,
|
|
104799
|
-
children: [
|
|
104800
|
-
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
104801
|
-
color: "gray",
|
|
104802
|
-
children: "["
|
|
104803
|
-
}, undefined, false, undefined, this),
|
|
104804
|
-
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
104805
|
-
color: "white",
|
|
104806
|
-
children: shortcut.keyLabel
|
|
104807
|
-
}, undefined, false, undefined, this),
|
|
104808
|
-
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
104809
|
-
color: "gray",
|
|
104810
|
-
children: [
|
|
104811
|
-
" ",
|
|
104812
|
-
shortcut.label,
|
|
104813
|
-
"]"
|
|
104814
|
-
]
|
|
104815
|
-
}, undefined, true, undefined, this)
|
|
104816
|
-
]
|
|
104817
|
-
}, shortcut.keyLabel, true, undefined, this))
|
|
104818
|
-
}, undefined, false, undefined, this),
|
|
104819
|
-
showTabs ? /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
105224
|
+
children: /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
105225
|
+
flexDirection: "row",
|
|
105226
|
+
children: shortcuts.map((shortcut) => /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
105227
|
+
marginRight: 2,
|
|
104820
105228
|
children: [
|
|
104821
105229
|
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
104822
|
-
|
|
105230
|
+
color: "gray",
|
|
105231
|
+
children: "["
|
|
105232
|
+
}, undefined, false, undefined, this),
|
|
105233
|
+
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
105234
|
+
color: "white",
|
|
105235
|
+
children: shortcut.keyLabel
|
|
104823
105236
|
}, undefined, false, undefined, this),
|
|
104824
105237
|
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
|
|
104825
105238
|
color: "gray",
|
|
104826
|
-
children:
|
|
104827
|
-
|
|
105239
|
+
children: [
|
|
105240
|
+
" ",
|
|
105241
|
+
shortcut.label,
|
|
105242
|
+
"]"
|
|
105243
|
+
]
|
|
105244
|
+
}, undefined, true, undefined, this)
|
|
104828
105245
|
]
|
|
104829
|
-
},
|
|
104830
|
-
|
|
104831
|
-
}, undefined,
|
|
105246
|
+
}, shortcut.keyLabel, true, undefined, this))
|
|
105247
|
+
}, undefined, false, undefined, this)
|
|
105248
|
+
}, undefined, false, undefined, this),
|
|
104832
105249
|
/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Box_default, {
|
|
104833
105250
|
justifyContent: "space-between",
|
|
104834
105251
|
marginTop: 1,
|
|
@@ -104873,15 +105290,16 @@ var registerTerminalReset = () => {
|
|
|
104873
105290
|
var runTui = () => {
|
|
104874
105291
|
applyTerminalPrelude();
|
|
104875
105292
|
registerTerminalReset();
|
|
104876
|
-
render_default(
|
|
105293
|
+
render_default(import_react62.default.createElement(App2));
|
|
104877
105294
|
};
|
|
104878
105295
|
|
|
104879
105296
|
// src/features/changes/index.ts
|
|
104880
|
-
import { existsSync as existsSync12, mkdirSync as mkdirSync12, readdirSync as
|
|
104881
|
-
import { dirname as
|
|
105297
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync12, readdirSync as readdirSync8, readFileSync as readFileSync16, renameSync as renameSync3, statSync as statSync9, writeFileSync as writeFileSync8 } from "node:fs";
|
|
105298
|
+
import { dirname as dirname15, join as join19, relative as relative4 } from "node:path";
|
|
104882
105299
|
import { fileURLToPath as fileURLToPath10 } from "node:url";
|
|
104883
105300
|
var CHANGE_ID_PATTERN2 = /^(?:[Cc]\d+_)?[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
104884
105301
|
var SUMMARY_FALLBACK3 = "No summary provided.";
|
|
105302
|
+
var ARCHIVED_CHANGE_STATUSES2 = new Set(["archived", "rejected"]);
|
|
104885
105303
|
var isDirectory6 = (path) => {
|
|
104886
105304
|
try {
|
|
104887
105305
|
return statSync9(path).isDirectory();
|
|
@@ -104896,6 +105314,7 @@ var readFrontmatterString2 = (data, key) => {
|
|
|
104896
105314
|
}
|
|
104897
105315
|
return "";
|
|
104898
105316
|
};
|
|
105317
|
+
var normalizeStatusValue2 = (status) => status.trim().toLowerCase();
|
|
104899
105318
|
var parseProposal2 = (content, changeId) => {
|
|
104900
105319
|
const titleMatch = content.match(/^#\s+Proposal\s+-\s+(.*)$/m);
|
|
104901
105320
|
const { data } = readFrontmatter(content);
|
|
@@ -104933,6 +105352,22 @@ var normalizeChangeId2 = (value) => {
|
|
|
104933
105352
|
return trimmed;
|
|
104934
105353
|
};
|
|
104935
105354
|
var isValidChangeId2 = (value) => CHANGE_ID_PATTERN2.test(value.trim());
|
|
105355
|
+
var resolveChangePaths2 = (schubDir, changeId) => {
|
|
105356
|
+
const changeDir = join19(schubDir, "changes", changeId);
|
|
105357
|
+
const proposalPath = join19(changeDir, "proposal.md");
|
|
105358
|
+
if (existsSync12(proposalPath)) {
|
|
105359
|
+
return { changeDir, proposalPath, isArchived: false };
|
|
105360
|
+
}
|
|
105361
|
+
const archiveDir = join19(schubDir, "archive", "changes", changeId);
|
|
105362
|
+
const archiveProposalPath = join19(archiveDir, "proposal.md");
|
|
105363
|
+
if (existsSync12(archiveProposalPath)) {
|
|
105364
|
+
return { changeDir: archiveDir, proposalPath: archiveProposalPath, isArchived: true };
|
|
105365
|
+
}
|
|
105366
|
+
throw new Error(`Required change files missing:
|
|
105367
|
+
- ${proposalPath}
|
|
105368
|
+
- ${archiveProposalPath}
|
|
105369
|
+
Create the change proposal before scaffolding docs.`);
|
|
105370
|
+
};
|
|
104936
105371
|
var readChangeSummary2 = (schubDir, changeId) => {
|
|
104937
105372
|
const trimmed = changeId.trim();
|
|
104938
105373
|
if (!trimmed) {
|
|
@@ -104942,25 +105377,20 @@ var readChangeSummary2 = (schubDir, changeId) => {
|
|
|
104942
105377
|
throw new Error(`Invalid change-id '${changeId}'. Use kebab-case or a C-prefixed id (e.g., C1_add-user-auth).`);
|
|
104943
105378
|
}
|
|
104944
105379
|
const normalized = normalizeChangeId2(trimmed);
|
|
104945
|
-
const
|
|
104946
|
-
const
|
|
104947
|
-
if (!existsSync12(proposalPath)) {
|
|
104948
|
-
throw new Error(`Required change files missing:
|
|
104949
|
-
- ${proposalPath}
|
|
104950
|
-
Create the change proposal before scaffolding docs.`);
|
|
104951
|
-
}
|
|
104952
|
-
const content = readFileSync14(proposalPath, "utf8");
|
|
105380
|
+
const resolved = resolveChangePaths2(schubDir, normalized);
|
|
105381
|
+
const content = readFileSync16(resolved.proposalPath, "utf8");
|
|
104953
105382
|
const parsed = parseProposal2(content, normalized);
|
|
104954
105383
|
return {
|
|
104955
105384
|
changeId: normalized,
|
|
104956
105385
|
changeTitle: parsed.title,
|
|
104957
|
-
changeDir,
|
|
104958
|
-
proposalPath
|
|
105386
|
+
changeDir: resolved.changeDir,
|
|
105387
|
+
proposalPath: resolved.proposalPath,
|
|
105388
|
+
isArchived: resolved.isArchived
|
|
104959
105389
|
};
|
|
104960
105390
|
};
|
|
104961
105391
|
var readChangeDetail2 = (schubDir, changeId) => {
|
|
104962
105392
|
const summary = readChangeSummary2(schubDir, changeId);
|
|
104963
|
-
const content =
|
|
105393
|
+
const content = readFileSync16(summary.proposalPath, "utf8");
|
|
104964
105394
|
const detail = parseProposalDetail2(content, summary.changeId);
|
|
104965
105395
|
return {
|
|
104966
105396
|
changeId: detail.changeId,
|
|
@@ -104970,13 +105400,14 @@ var readChangeDetail2 = (schubDir, changeId) => {
|
|
|
104970
105400
|
summary: detail.summary
|
|
104971
105401
|
};
|
|
104972
105402
|
};
|
|
105403
|
+
var isArchivedStatus2 = (value) => ARCHIVED_CHANGE_STATUSES2.has(normalizeStatusValue2(value));
|
|
104973
105404
|
var updateChangeStatus2 = (schubDir, changeId, status) => {
|
|
104974
105405
|
const nextStatus = status.trim();
|
|
104975
105406
|
if (!nextStatus) {
|
|
104976
105407
|
throw new Error("Provide --status.");
|
|
104977
105408
|
}
|
|
104978
105409
|
const summary = readChangeSummary2(schubDir, changeId);
|
|
104979
|
-
const content =
|
|
105410
|
+
const content = readFileSync16(summary.proposalPath, "utf8");
|
|
104980
105411
|
const { data } = readFrontmatter(content);
|
|
104981
105412
|
const previousStatus = readFrontmatterString2(data, "status");
|
|
104982
105413
|
if (!previousStatus) {
|
|
@@ -104984,7 +105415,39 @@ var updateChangeStatus2 = (schubDir, changeId, status) => {
|
|
|
104984
105415
|
Add a 'status' field in frontmatter before updating status.`);
|
|
104985
105416
|
}
|
|
104986
105417
|
const updated = updateFrontmatterValue(content, "status", nextStatus);
|
|
104987
|
-
|
|
105418
|
+
const shouldArchive = isArchivedStatus2(nextStatus);
|
|
105419
|
+
const archiveRoot = join19(schubDir, "archive", "changes");
|
|
105420
|
+
const archivePath = join19(archiveRoot, summary.changeId);
|
|
105421
|
+
const activePath = join19(schubDir, "changes", summary.changeId);
|
|
105422
|
+
if (shouldArchive && !summary.isArchived) {
|
|
105423
|
+
if (existsSync12(archivePath)) {
|
|
105424
|
+
throw new Error(`Archive already exists: ${archivePath}`);
|
|
105425
|
+
}
|
|
105426
|
+
mkdirSync12(archiveRoot, { recursive: true });
|
|
105427
|
+
writeFileSync8(summary.proposalPath, updated, "utf8");
|
|
105428
|
+
renameSync3(summary.changeDir, archivePath);
|
|
105429
|
+
return {
|
|
105430
|
+
changeId: summary.changeId,
|
|
105431
|
+
proposalPath: join19(archivePath, "proposal.md"),
|
|
105432
|
+
previousStatus,
|
|
105433
|
+
status: nextStatus
|
|
105434
|
+
};
|
|
105435
|
+
}
|
|
105436
|
+
if (!shouldArchive && summary.isArchived) {
|
|
105437
|
+
if (existsSync12(activePath)) {
|
|
105438
|
+
throw new Error(`Change already exists: ${activePath}`);
|
|
105439
|
+
}
|
|
105440
|
+
mkdirSync12(join19(schubDir, "changes"), { recursive: true });
|
|
105441
|
+
writeFileSync8(summary.proposalPath, updated, "utf8");
|
|
105442
|
+
renameSync3(summary.changeDir, activePath);
|
|
105443
|
+
return {
|
|
105444
|
+
changeId: summary.changeId,
|
|
105445
|
+
proposalPath: join19(activePath, "proposal.md"),
|
|
105446
|
+
previousStatus,
|
|
105447
|
+
status: nextStatus
|
|
105448
|
+
};
|
|
105449
|
+
}
|
|
105450
|
+
writeFileSync8(summary.proposalPath, updated, "utf8");
|
|
104988
105451
|
return {
|
|
104989
105452
|
changeId: summary.changeId,
|
|
104990
105453
|
proposalPath: summary.proposalPath,
|
|
@@ -104994,19 +105457,16 @@ Add a 'status' field in frontmatter before updating status.`);
|
|
|
104994
105457
|
};
|
|
104995
105458
|
var archiveChange2 = (schubDir, changeId) => {
|
|
104996
105459
|
const summary = readChangeSummary2(schubDir, changeId);
|
|
104997
|
-
const
|
|
104998
|
-
|
|
104999
|
-
if (existsSync12(archivePath)) {
|
|
105460
|
+
const archivePath = join19(schubDir, "archive", "changes", summary.changeId);
|
|
105461
|
+
if (summary.isArchived) {
|
|
105000
105462
|
throw new Error(`Archive already exists: ${archivePath}`);
|
|
105001
105463
|
}
|
|
105002
|
-
|
|
105003
|
-
const updated = updateChangeStatus2(schubDir, summary.changeId, "Archived");
|
|
105004
|
-
renameSync3(summary.changeDir, archivePath);
|
|
105464
|
+
const archived = updateChangeStatus2(schubDir, summary.changeId, "Archived");
|
|
105005
105465
|
return {
|
|
105006
|
-
changeId:
|
|
105007
|
-
previousStatus:
|
|
105008
|
-
status:
|
|
105009
|
-
proposalPath:
|
|
105466
|
+
changeId: archived.changeId,
|
|
105467
|
+
previousStatus: archived.previousStatus,
|
|
105468
|
+
status: archived.status,
|
|
105469
|
+
proposalPath: archived.proposalPath,
|
|
105010
105470
|
archivePath
|
|
105011
105471
|
};
|
|
105012
105472
|
};
|
|
@@ -105018,17 +105478,17 @@ var readChangesFromRoot2 = (changesRoot, repoRoot) => {
|
|
|
105018
105478
|
if (!existsSync12(changesRoot) || !isDirectory6(changesRoot)) {
|
|
105019
105479
|
return [];
|
|
105020
105480
|
}
|
|
105021
|
-
const entries =
|
|
105481
|
+
const entries = readdirSync8(changesRoot, { withFileTypes: true });
|
|
105022
105482
|
const changes = [];
|
|
105023
105483
|
for (const entry of entries) {
|
|
105024
105484
|
if (!entry.isDirectory()) {
|
|
105025
105485
|
continue;
|
|
105026
105486
|
}
|
|
105027
|
-
const proposalPath =
|
|
105487
|
+
const proposalPath = join19(changesRoot, entry.name, "proposal.md");
|
|
105028
105488
|
if (!existsSync12(proposalPath)) {
|
|
105029
105489
|
continue;
|
|
105030
105490
|
}
|
|
105031
|
-
const content =
|
|
105491
|
+
const content = readFileSync16(proposalPath, "utf8");
|
|
105032
105492
|
const parsed = parseProposal2(content, entry.name);
|
|
105033
105493
|
changes.push({
|
|
105034
105494
|
id: entry.name,
|
|
@@ -105040,8 +105500,8 @@ var readChangesFromRoot2 = (changesRoot, repoRoot) => {
|
|
|
105040
105500
|
return changes;
|
|
105041
105501
|
};
|
|
105042
105502
|
var listChanges2 = (schubDir) => {
|
|
105043
|
-
const repoRoot =
|
|
105044
|
-
const changes = readChangesFromRoot2(
|
|
105503
|
+
const repoRoot = dirname15(schubDir);
|
|
105504
|
+
const changes = readChangesFromRoot2(join19(schubDir, "changes"), repoRoot);
|
|
105045
105505
|
return changes.sort((left2, right2) => {
|
|
105046
105506
|
const numberDiff = changeNumber2(left2.id) - changeNumber2(right2.id);
|
|
105047
105507
|
if (numberDiff !== 0) {
|
|
@@ -105056,6 +105516,7 @@ var STATUS_GROUPS2 = [
|
|
|
105056
105516
|
{ label: "Accepted", match: "accepted" },
|
|
105057
105517
|
{ label: "Implementing", match: "implement" },
|
|
105058
105518
|
{ label: "Done", match: "done" },
|
|
105519
|
+
{ label: "Rejected", match: "reject" },
|
|
105059
105520
|
{ label: "Archived", match: "archiv" }
|
|
105060
105521
|
];
|
|
105061
105522
|
var normalizeStatusLabel2 = (status) => {
|
|
@@ -105070,9 +105531,9 @@ var normalizeStatusLabel2 = (status) => {
|
|
|
105070
105531
|
return { label: label || "Unknown", order: STATUS_GROUPS2.length };
|
|
105071
105532
|
};
|
|
105072
105533
|
var listChangeOverview2 = (schubDir) => {
|
|
105073
|
-
const repoRoot =
|
|
105534
|
+
const repoRoot = dirname15(schubDir);
|
|
105074
105535
|
const combined = new Map;
|
|
105075
|
-
for (const root of [
|
|
105536
|
+
for (const root of [join19(schubDir, "changes"), join19(schubDir, "archive", "changes")]) {
|
|
105076
105537
|
for (const change of readChangesFromRoot2(root, repoRoot)) {
|
|
105077
105538
|
if (!combined.has(change.id)) {
|
|
105078
105539
|
combined.set(change.id, change);
|
|
@@ -105102,13 +105563,13 @@ var splitPrefixedChangeId2 = (changeId) => {
|
|
|
105102
105563
|
return { prefix: null, slug: changeId };
|
|
105103
105564
|
};
|
|
105104
105565
|
var nextChangePrefix2 = (schubDir) => {
|
|
105105
|
-
const changesRoot =
|
|
105106
|
-
const archiveRoot =
|
|
105566
|
+
const changesRoot = join19(schubDir, "changes");
|
|
105567
|
+
const archiveRoot = join19(schubDir, "archive", "changes");
|
|
105107
105568
|
const prefixes = [];
|
|
105108
105569
|
const scan = (root) => {
|
|
105109
105570
|
if (!existsSync12(root) || !isDirectory6(root))
|
|
105110
105571
|
return;
|
|
105111
|
-
for (const entry of
|
|
105572
|
+
for (const entry of readdirSync8(root, { withFileTypes: true })) {
|
|
105112
105573
|
if (!entry.isDirectory()) {
|
|
105113
105574
|
continue;
|
|
105114
105575
|
}
|
|
@@ -105126,24 +105587,24 @@ var nextChangePrefix2 = (schubDir) => {
|
|
|
105126
105587
|
var CHANGE_PREFIX2 = "C";
|
|
105127
105588
|
var BUNDLED_PROPOSAL_TEMPLATE_PATH2 = fileURLToPath10(new URL("../../../templates/create-proposal/proposal-template.md", import.meta.url));
|
|
105128
105589
|
var readProposalTemplate2 = (schubDir) => {
|
|
105129
|
-
const templatePath = resolveTemplatePath(schubDir,
|
|
105590
|
+
const templatePath = resolveTemplatePath(schubDir, join19("create-proposal", "proposal-template.md"), BUNDLED_PROPOSAL_TEMPLATE_PATH2);
|
|
105130
105591
|
try {
|
|
105131
|
-
return
|
|
105592
|
+
return readFileSync16(templatePath, "utf8");
|
|
105132
105593
|
} catch {
|
|
105133
105594
|
throw new Error(`[ERROR] Template not found: ${templatePath}`);
|
|
105134
105595
|
}
|
|
105135
105596
|
};
|
|
105136
105597
|
var isValidSlug2 = (value) => /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value);
|
|
105137
105598
|
var changeExists2 = (schubDir, changeId) => {
|
|
105138
|
-
const active =
|
|
105599
|
+
const active = join19(schubDir, "changes", changeId);
|
|
105139
105600
|
if (existsSync12(active)) {
|
|
105140
105601
|
return true;
|
|
105141
105602
|
}
|
|
105142
|
-
const archiveRoot =
|
|
105603
|
+
const archiveRoot = join19(schubDir, "archive", "changes");
|
|
105143
105604
|
if (!existsSync12(archiveRoot) || !isDirectory6(archiveRoot)) {
|
|
105144
105605
|
return false;
|
|
105145
105606
|
}
|
|
105146
|
-
for (const entry of
|
|
105607
|
+
for (const entry of readdirSync8(archiveRoot, { withFileTypes: true })) {
|
|
105147
105608
|
if (entry.isDirectory() && entry.name.includes(changeId)) {
|
|
105148
105609
|
return true;
|
|
105149
105610
|
}
|
|
@@ -105152,10 +105613,10 @@ var changeExists2 = (schubDir, changeId) => {
|
|
|
105152
105613
|
};
|
|
105153
105614
|
var findChangeByPrefix2 = (schubDir, prefix) => {
|
|
105154
105615
|
const normalizedPrefix = prefix.toUpperCase();
|
|
105155
|
-
for (const root of [
|
|
105616
|
+
for (const root of [join19(schubDir, "changes"), join19(schubDir, "archive", "changes")]) {
|
|
105156
105617
|
if (!existsSync12(root) || !isDirectory6(root))
|
|
105157
105618
|
continue;
|
|
105158
|
-
for (const entry of
|
|
105619
|
+
for (const entry of readdirSync8(root, { withFileTypes: true })) {
|
|
105159
105620
|
if (entry.isDirectory() && entry.name.toUpperCase().startsWith(`${normalizedPrefix}_`)) {
|
|
105160
105621
|
return entry.name;
|
|
105161
105622
|
}
|
|
@@ -105204,8 +105665,8 @@ var createChange2 = (schubDir, options2) => {
|
|
|
105204
105665
|
throw new Error(`Change prefix '${prefix}' already exists as '${existing}'. Choose a new prefix or omit it to auto-generate.`);
|
|
105205
105666
|
}
|
|
105206
105667
|
}
|
|
105207
|
-
const changeDir =
|
|
105208
|
-
const proposalPath =
|
|
105668
|
+
const changeDir = join19(schubDir, "changes", changeId);
|
|
105669
|
+
const proposalPath = join19(changeDir, "proposal.md");
|
|
105209
105670
|
if (changeExists2(schubDir, changeId) && !options2.overwrite) {
|
|
105210
105671
|
throw new Error(`Change '${changeId}' already exists under ${schubDir}. Choose a unique id or pass --overwrite.`);
|
|
105211
105672
|
}
|
|
@@ -105216,12 +105677,12 @@ var createChange2 = (schubDir, options2) => {
|
|
|
105216
105677
|
throw new Error(`Refusing to overwrite existing file: ${proposalPath}`);
|
|
105217
105678
|
}
|
|
105218
105679
|
mkdirSync12(changeDir, { recursive: true });
|
|
105219
|
-
|
|
105680
|
+
writeFileSync8(proposalPath, rendered, "utf8");
|
|
105220
105681
|
return proposalPath;
|
|
105221
105682
|
};
|
|
105222
105683
|
// src/features/project/index.ts
|
|
105223
|
-
import { existsSync as existsSync13, mkdirSync as mkdirSync13, readFileSync as
|
|
105224
|
-
import { basename as
|
|
105684
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync13, readFileSync as readFileSync17, writeFileSync as writeFileSync9 } from "node:fs";
|
|
105685
|
+
import { basename as basename8, dirname as dirname16, join as join20, resolve as resolve14 } from "node:path";
|
|
105225
105686
|
import { fileURLToPath as fileURLToPath11 } from "node:url";
|
|
105226
105687
|
var TEMPLATE_FILES2 = {
|
|
105227
105688
|
"project-overview.md": "project-overview-template.md",
|
|
@@ -105231,7 +105692,7 @@ var TEMPLATE_FILES2 = {
|
|
|
105231
105692
|
var TEMPLATES_ROOT2 = fileURLToPath11(new URL("../../../templates/setup-project", import.meta.url));
|
|
105232
105693
|
var readTemplate2 = (path) => {
|
|
105233
105694
|
try {
|
|
105234
|
-
return
|
|
105695
|
+
return readFileSync17(path, "utf8");
|
|
105235
105696
|
} catch {
|
|
105236
105697
|
throw new Error(`[ERROR] Template not found: ${path}`);
|
|
105237
105698
|
}
|
|
@@ -105240,18 +105701,18 @@ var writeOutput2 = (path, content, overwrite) => {
|
|
|
105240
105701
|
if (existsSync13(path) && !overwrite) {
|
|
105241
105702
|
throw new Error(`[ERROR] Refusing to overwrite existing file: ${path}`);
|
|
105242
105703
|
}
|
|
105243
|
-
mkdirSync13(
|
|
105244
|
-
|
|
105704
|
+
mkdirSync13(dirname16(path), { recursive: true });
|
|
105705
|
+
writeFileSync9(path, content, "utf8");
|
|
105245
105706
|
};
|
|
105246
105707
|
var deriveProjectName2 = (repoRoot) => {
|
|
105247
|
-
const name =
|
|
105708
|
+
const name = basename8(repoRoot).trim();
|
|
105248
105709
|
return name || "Project";
|
|
105249
105710
|
};
|
|
105250
105711
|
var resolveRepoRoot2 = (startDir, schubRoot, repoRoot) => {
|
|
105251
105712
|
if (repoRoot) {
|
|
105252
|
-
return
|
|
105713
|
+
return resolve14(startDir, repoRoot);
|
|
105253
105714
|
}
|
|
105254
|
-
return
|
|
105715
|
+
return resolve14(schubRoot, "..");
|
|
105255
105716
|
};
|
|
105256
105717
|
var createProject2 = (startDir, options2) => {
|
|
105257
105718
|
const schubRoot = resolveSchubRoot(startDir);
|
|
@@ -105260,11 +105721,11 @@ var createProject2 = (startDir, options2) => {
|
|
|
105260
105721
|
const overwrite = options2.overwrite ?? false;
|
|
105261
105722
|
const created = [];
|
|
105262
105723
|
for (const [outputName, templateName] of Object.entries(TEMPLATE_FILES2)) {
|
|
105263
|
-
const bundledPath =
|
|
105264
|
-
const templatePath = resolveTemplatePath(schubRoot,
|
|
105724
|
+
const bundledPath = join20(TEMPLATES_ROOT2, templateName);
|
|
105725
|
+
const templatePath = resolveTemplatePath(schubRoot, join20("setup-project", templateName), bundledPath);
|
|
105265
105726
|
const template = readTemplate2(templatePath);
|
|
105266
105727
|
const rendered = template.split("[Project Name]").join(projectName);
|
|
105267
|
-
const outputPath =
|
|
105728
|
+
const outputPath = join20(schubRoot, outputName);
|
|
105268
105729
|
writeOutput2(outputPath, rendered, overwrite);
|
|
105269
105730
|
created.push(outputPath);
|
|
105270
105731
|
}
|
|
@@ -105296,6 +105757,8 @@ var runCommand = async () => {
|
|
|
105296
105757
|
runTasksCreate(rawArgs.slice(2), startDir);
|
|
105297
105758
|
}).command("list [args..]", "List tasks", () => {}, () => {
|
|
105298
105759
|
runTasksList(resolveSchubDir(), rawArgs.slice(2));
|
|
105760
|
+
}).command("check [args..]", "Update task checklist item", () => {}, () => {
|
|
105761
|
+
runTasksCheck(resolveSchubDir(), rawArgs.slice(2));
|
|
105299
105762
|
}).command("update [args..]", "Update backlog task status", () => {}, () => {
|
|
105300
105763
|
runTasksUpdate(resolveSchubDir(), rawArgs.slice(2));
|
|
105301
105764
|
}).command("implement [args..]", "Assign a task for implementation", () => {}, () => {
|