issy 0.10.1 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/cli.js +49 -41
- package/dist/main.js +226 -336
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -151,7 +151,6 @@ issy update 0001 --body "New details" # Replace body content
|
|
|
151
151
|
issy close 0001 # Close issue
|
|
152
152
|
issy reopen 0001 --after 0004 # Reopen and place in roadmap
|
|
153
153
|
issy learn # Print compact AI-agent instructions
|
|
154
|
-
issy learn roadmap # Print focused topic instructions
|
|
155
154
|
issy skill install # Install the AI bootstrap skill
|
|
156
155
|
issy migrate # Migrate from .issues/ to .issy/
|
|
157
156
|
issy --version # Check version
|
|
@@ -187,6 +186,8 @@ Issues can declare blockers with `depends_on`:
|
|
|
187
186
|
depends_on: 0012, 0035
|
|
188
187
|
```
|
|
189
188
|
|
|
189
|
+
Roadmap placement is validated against open dependencies. An open issue must appear after every open issue it depends on, and `create`, `update`, or `reopen` fails if the requested position would put a blocked issue before one of its blockers.
|
|
190
|
+
|
|
190
191
|
### Hooks
|
|
191
192
|
|
|
192
193
|
issy supports optional hook files in `.issy/` that inject context into stdout after successful operations. The file contents are printed directly, making them visible to AI agents in their command output.
|
package/dist/cli.js
CHANGED
|
@@ -404,6 +404,41 @@ function getBlockingIssues(issue, issues) {
|
|
|
404
404
|
function isIssueUnblocked(issue, issues) {
|
|
405
405
|
return issue.frontmatter.status === "open" && getBlockingIssues(issue, issues).length === 0;
|
|
406
406
|
}
|
|
407
|
+
function compareIssuesByRoadmapOrder(a, b) {
|
|
408
|
+
const orderA = a.frontmatter.order;
|
|
409
|
+
const orderB = b.frontmatter.order;
|
|
410
|
+
if (orderA && orderB) {
|
|
411
|
+
if (orderA < orderB)
|
|
412
|
+
return -1;
|
|
413
|
+
if (orderA > orderB)
|
|
414
|
+
return 1;
|
|
415
|
+
}
|
|
416
|
+
if (orderA && !orderB)
|
|
417
|
+
return -1;
|
|
418
|
+
if (!orderA && orderB)
|
|
419
|
+
return 1;
|
|
420
|
+
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
421
|
+
}
|
|
422
|
+
function validateRoadmapDependencyOrder(issues) {
|
|
423
|
+
const openIssues = issues.filter((issue) => issue.frontmatter.status === "open").sort(compareIssuesByRoadmapOrder);
|
|
424
|
+
const positionById = new Map(openIssues.map((issue, index) => [issue.id, index]));
|
|
425
|
+
const violations = [];
|
|
426
|
+
for (const issue of openIssues) {
|
|
427
|
+
const issuePosition = positionById.get(issue.id);
|
|
428
|
+
if (issuePosition === undefined)
|
|
429
|
+
continue;
|
|
430
|
+
for (const dependency of getBlockingIssues(issue, issues)) {
|
|
431
|
+
const dependencyPosition = positionById.get(dependency.id);
|
|
432
|
+
if (dependencyPosition === undefined || dependencyPosition < issuePosition) {
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
violations.push(`#${issue.id} depends on #${dependency.id}`);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
if (violations.length > 0) {
|
|
439
|
+
throw new Error(`Roadmap dependency order invalid: ${violations.join("; ")}. Issues must be placed after all open issues they depend on.`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
407
442
|
function getIssueIdFromFilename(filename) {
|
|
408
443
|
const match = filename.match(/^(\d+)-/);
|
|
409
444
|
return match ? match[1] : filename.replace(".md", "");
|
|
@@ -460,17 +495,7 @@ async function getAllIssues() {
|
|
|
460
495
|
content: body
|
|
461
496
|
});
|
|
462
497
|
}
|
|
463
|
-
return issues.sort(
|
|
464
|
-
const orderA = a.frontmatter.order;
|
|
465
|
-
const orderB = b.frontmatter.order;
|
|
466
|
-
if (orderA && orderB)
|
|
467
|
-
return orderA < orderB ? -1 : orderA > orderB ? 1 : 0;
|
|
468
|
-
if (orderA && !orderB)
|
|
469
|
-
return -1;
|
|
470
|
-
if (!orderA && orderB)
|
|
471
|
-
return 1;
|
|
472
|
-
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
473
|
-
});
|
|
498
|
+
return issues.sort(compareIssuesByRoadmapOrder);
|
|
474
499
|
}
|
|
475
500
|
async function getOpenIssuesByOrder() {
|
|
476
501
|
const allIssues = await getAllIssues();
|
|
@@ -558,8 +583,7 @@ async function createIssue(input) {
|
|
|
558
583
|
const content = `${generateFrontmatter(frontmatter)}
|
|
559
584
|
${body}
|
|
560
585
|
`;
|
|
561
|
-
|
|
562
|
-
return {
|
|
586
|
+
const newIssue = {
|
|
563
587
|
id: issueNumber,
|
|
564
588
|
filename,
|
|
565
589
|
frontmatter,
|
|
@@ -567,6 +591,9 @@ ${body}
|
|
|
567
591
|
${body}
|
|
568
592
|
`
|
|
569
593
|
};
|
|
594
|
+
validateRoadmapDependencyOrder([...existingIssues, newIssue]);
|
|
595
|
+
await writeFile(join(getIssuesDir(), filename), content);
|
|
596
|
+
return newIssue;
|
|
570
597
|
}
|
|
571
598
|
async function updateIssue(id, input) {
|
|
572
599
|
const issue = await getIssue(id);
|
|
@@ -596,12 +623,16 @@ ${input.body}
|
|
|
596
623
|
` : issue.content;
|
|
597
624
|
const content = `${generateFrontmatter(updatedFrontmatter)}
|
|
598
625
|
${updatedContent}`;
|
|
599
|
-
|
|
600
|
-
return {
|
|
626
|
+
const updatedIssue = {
|
|
601
627
|
...issue,
|
|
602
628
|
frontmatter: updatedFrontmatter,
|
|
603
629
|
content: updatedContent
|
|
604
630
|
};
|
|
631
|
+
if (input.order !== undefined || input.depends_on !== undefined || input.status === "open") {
|
|
632
|
+
validateRoadmapDependencyOrder(allIssues.map((current) => current.id === issue.id ? updatedIssue : current));
|
|
633
|
+
}
|
|
634
|
+
await writeFile(join(getIssuesDir(), issue.filename), content);
|
|
635
|
+
return updatedIssue;
|
|
605
636
|
}
|
|
606
637
|
async function closeIssue(id) {
|
|
607
638
|
return updateIssue(id, { status: "closed" });
|
|
@@ -2002,17 +2033,7 @@ function createSearchIndex(issues) {
|
|
|
2002
2033
|
function sortIssues(issues, sortBy) {
|
|
2003
2034
|
const sortOption = sortBy.toLowerCase();
|
|
2004
2035
|
if (sortOption === "roadmap") {
|
|
2005
|
-
issues.sort(
|
|
2006
|
-
const orderA = a.frontmatter.order;
|
|
2007
|
-
const orderB = b.frontmatter.order;
|
|
2008
|
-
if (orderA && orderB)
|
|
2009
|
-
return orderA < orderB ? -1 : orderA > orderB ? 1 : 0;
|
|
2010
|
-
if (orderA && !orderB)
|
|
2011
|
-
return -1;
|
|
2012
|
-
if (!orderA && orderB)
|
|
2013
|
-
return 1;
|
|
2014
|
-
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
2015
|
-
});
|
|
2036
|
+
issues.sort(compareIssuesByRoadmapOrder);
|
|
2016
2037
|
} else if (sortOption === "priority") {
|
|
2017
2038
|
const priorityOrder = {
|
|
2018
2039
|
high: 0,
|
|
@@ -2066,17 +2087,7 @@ function sortIssues(issues, sortBy) {
|
|
|
2066
2087
|
} else if (sortOption === "id") {
|
|
2067
2088
|
issues.sort((a, b) => b.id.localeCompare(a.id));
|
|
2068
2089
|
} else {
|
|
2069
|
-
issues.sort(
|
|
2070
|
-
const orderA = a.frontmatter.order;
|
|
2071
|
-
const orderB = b.frontmatter.order;
|
|
2072
|
-
if (orderA && orderB)
|
|
2073
|
-
return orderA < orderB ? -1 : orderA > orderB ? 1 : 0;
|
|
2074
|
-
if (orderA && !orderB)
|
|
2075
|
-
return -1;
|
|
2076
|
-
if (!orderA && orderB)
|
|
2077
|
-
return 1;
|
|
2078
|
-
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
2079
|
-
});
|
|
2090
|
+
issues.sort(compareIssuesByRoadmapOrder);
|
|
2080
2091
|
}
|
|
2081
2092
|
}
|
|
2082
2093
|
function filterByQuery(issues, query) {
|
|
@@ -2490,9 +2501,7 @@ Commands:
|
|
|
2490
2501
|
|
|
2491
2502
|
next Show the next issue to work on
|
|
2492
2503
|
|
|
2493
|
-
learn
|
|
2494
|
-
--all Print the full reference
|
|
2495
|
-
--list List focused topics
|
|
2504
|
+
learn Print AI-agent instructions for this issy version
|
|
2496
2505
|
|
|
2497
2506
|
create Create a new issue
|
|
2498
2507
|
--title, -t <t> Issue title
|
|
@@ -2536,7 +2545,6 @@ Examples:
|
|
|
2536
2545
|
issy list --priority high --type bug
|
|
2537
2546
|
issy next
|
|
2538
2547
|
issy learn
|
|
2539
|
-
issy learn roadmap
|
|
2540
2548
|
issy read 0001
|
|
2541
2549
|
issy create --title "Fix login bug" --type bug --priority high --after 0002
|
|
2542
2550
|
issy create --title "Add dark mode" --last
|
package/dist/main.js
CHANGED
|
@@ -415,6 +415,41 @@ function getBlockingIssues(issue, issues) {
|
|
|
415
415
|
function isIssueUnblocked(issue, issues) {
|
|
416
416
|
return issue.frontmatter.status === "open" && getBlockingIssues(issue, issues).length === 0;
|
|
417
417
|
}
|
|
418
|
+
function compareIssuesByRoadmapOrder(a, b) {
|
|
419
|
+
const orderA = a.frontmatter.order;
|
|
420
|
+
const orderB = b.frontmatter.order;
|
|
421
|
+
if (orderA && orderB) {
|
|
422
|
+
if (orderA < orderB)
|
|
423
|
+
return -1;
|
|
424
|
+
if (orderA > orderB)
|
|
425
|
+
return 1;
|
|
426
|
+
}
|
|
427
|
+
if (orderA && !orderB)
|
|
428
|
+
return -1;
|
|
429
|
+
if (!orderA && orderB)
|
|
430
|
+
return 1;
|
|
431
|
+
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
432
|
+
}
|
|
433
|
+
function validateRoadmapDependencyOrder(issues) {
|
|
434
|
+
const openIssues = issues.filter((issue) => issue.frontmatter.status === "open").sort(compareIssuesByRoadmapOrder);
|
|
435
|
+
const positionById = new Map(openIssues.map((issue, index) => [issue.id, index]));
|
|
436
|
+
const violations = [];
|
|
437
|
+
for (const issue of openIssues) {
|
|
438
|
+
const issuePosition = positionById.get(issue.id);
|
|
439
|
+
if (issuePosition === undefined)
|
|
440
|
+
continue;
|
|
441
|
+
for (const dependency of getBlockingIssues(issue, issues)) {
|
|
442
|
+
const dependencyPosition = positionById.get(dependency.id);
|
|
443
|
+
if (dependencyPosition === undefined || dependencyPosition < issuePosition) {
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
violations.push(`#${issue.id} depends on #${dependency.id}`);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
if (violations.length > 0) {
|
|
450
|
+
throw new Error(`Roadmap dependency order invalid: ${violations.join("; ")}. Issues must be placed after all open issues they depend on.`);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
418
453
|
function getIssueIdFromFilename(filename) {
|
|
419
454
|
const match = filename.match(/^(\d+)-/);
|
|
420
455
|
return match ? match[1] : filename.replace(".md", "");
|
|
@@ -471,17 +506,7 @@ async function getAllIssues() {
|
|
|
471
506
|
content: body
|
|
472
507
|
});
|
|
473
508
|
}
|
|
474
|
-
return issues.sort(
|
|
475
|
-
const orderA = a.frontmatter.order;
|
|
476
|
-
const orderB = b.frontmatter.order;
|
|
477
|
-
if (orderA && orderB)
|
|
478
|
-
return orderA < orderB ? -1 : orderA > orderB ? 1 : 0;
|
|
479
|
-
if (orderA && !orderB)
|
|
480
|
-
return -1;
|
|
481
|
-
if (!orderA && orderB)
|
|
482
|
-
return 1;
|
|
483
|
-
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
484
|
-
});
|
|
509
|
+
return issues.sort(compareIssuesByRoadmapOrder);
|
|
485
510
|
}
|
|
486
511
|
async function getOpenIssuesByOrder() {
|
|
487
512
|
const allIssues = await getAllIssues();
|
|
@@ -569,8 +594,7 @@ async function createIssue(input) {
|
|
|
569
594
|
const content = `${generateFrontmatter(frontmatter)}
|
|
570
595
|
${body}
|
|
571
596
|
`;
|
|
572
|
-
|
|
573
|
-
return {
|
|
597
|
+
const newIssue = {
|
|
574
598
|
id: issueNumber,
|
|
575
599
|
filename,
|
|
576
600
|
frontmatter,
|
|
@@ -578,6 +602,9 @@ ${body}
|
|
|
578
602
|
${body}
|
|
579
603
|
`
|
|
580
604
|
};
|
|
605
|
+
validateRoadmapDependencyOrder([...existingIssues, newIssue]);
|
|
606
|
+
await writeFile(join(getIssuesDir(), filename), content);
|
|
607
|
+
return newIssue;
|
|
581
608
|
}
|
|
582
609
|
async function updateIssue(id, input) {
|
|
583
610
|
const issue = await getIssue(id);
|
|
@@ -607,12 +634,16 @@ ${input.body}
|
|
|
607
634
|
` : issue.content;
|
|
608
635
|
const content = `${generateFrontmatter(updatedFrontmatter)}
|
|
609
636
|
${updatedContent}`;
|
|
610
|
-
|
|
611
|
-
return {
|
|
637
|
+
const updatedIssue = {
|
|
612
638
|
...issue,
|
|
613
639
|
frontmatter: updatedFrontmatter,
|
|
614
640
|
content: updatedContent
|
|
615
641
|
};
|
|
642
|
+
if (input.order !== undefined || input.depends_on !== undefined || input.status === "open") {
|
|
643
|
+
validateRoadmapDependencyOrder(allIssues.map((current) => current.id === issue.id ? updatedIssue : current));
|
|
644
|
+
}
|
|
645
|
+
await writeFile(join(getIssuesDir(), issue.filename), content);
|
|
646
|
+
return updatedIssue;
|
|
616
647
|
}
|
|
617
648
|
async function closeIssue(id) {
|
|
618
649
|
return updateIssue(id, { status: "closed" });
|
|
@@ -2013,17 +2044,7 @@ function createSearchIndex(issues) {
|
|
|
2013
2044
|
function sortIssues(issues, sortBy) {
|
|
2014
2045
|
const sortOption = sortBy.toLowerCase();
|
|
2015
2046
|
if (sortOption === "roadmap") {
|
|
2016
|
-
issues.sort(
|
|
2017
|
-
const orderA = a.frontmatter.order;
|
|
2018
|
-
const orderB = b.frontmatter.order;
|
|
2019
|
-
if (orderA && orderB)
|
|
2020
|
-
return orderA < orderB ? -1 : orderA > orderB ? 1 : 0;
|
|
2021
|
-
if (orderA && !orderB)
|
|
2022
|
-
return -1;
|
|
2023
|
-
if (!orderA && orderB)
|
|
2024
|
-
return 1;
|
|
2025
|
-
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
2026
|
-
});
|
|
2047
|
+
issues.sort(compareIssuesByRoadmapOrder);
|
|
2027
2048
|
} else if (sortOption === "priority") {
|
|
2028
2049
|
const priorityOrder = {
|
|
2029
2050
|
high: 0,
|
|
@@ -2077,17 +2098,7 @@ function sortIssues(issues, sortBy) {
|
|
|
2077
2098
|
} else if (sortOption === "id") {
|
|
2078
2099
|
issues.sort((a, b) => b.id.localeCompare(a.id));
|
|
2079
2100
|
} else {
|
|
2080
|
-
issues.sort(
|
|
2081
|
-
const orderA = a.frontmatter.order;
|
|
2082
|
-
const orderB = b.frontmatter.order;
|
|
2083
|
-
if (orderA && orderB)
|
|
2084
|
-
return orderA < orderB ? -1 : orderA > orderB ? 1 : 0;
|
|
2085
|
-
if (orderA && !orderB)
|
|
2086
|
-
return -1;
|
|
2087
|
-
if (!orderA && orderB)
|
|
2088
|
-
return 1;
|
|
2089
|
-
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
2090
|
-
});
|
|
2101
|
+
issues.sort(compareIssuesByRoadmapOrder);
|
|
2091
2102
|
}
|
|
2092
2103
|
}
|
|
2093
2104
|
function filterByQuery(issues, query) {
|
|
@@ -2181,336 +2192,215 @@ function markdown(lines) {
|
|
|
2181
2192
|
`).trim()}
|
|
2182
2193
|
`;
|
|
2183
2194
|
}
|
|
2184
|
-
var
|
|
2185
|
-
"# issy
|
|
2195
|
+
var output = markdown([
|
|
2196
|
+
"# Track issues with the issy CLI",
|
|
2197
|
+
"",
|
|
2198
|
+
"## Authoring Issues",
|
|
2199
|
+
"",
|
|
2200
|
+
"Issues describe *what* needs to be done and *why*. Keep them high-level unless the user provides specific details.",
|
|
2201
|
+
"",
|
|
2202
|
+
"### What to Include",
|
|
2203
|
+
"",
|
|
2204
|
+
"- **Problem/Overview**: What's wrong or what's needed (1-2 paragraphs)",
|
|
2205
|
+
"- **Proposed Solution**: High-level approach",
|
|
2206
|
+
'- **Acceptance Criteria**: Optional - what "done" looks like from user perspective',
|
|
2207
|
+
"- **Verification**: Optional but encouraged - how to prove the issue is resolved (see below)",
|
|
2208
|
+
"- **Future Considerations**: Optional - related ideas for later",
|
|
2209
|
+
"- **References**: Optional - links to related docs, issues, or resources",
|
|
2210
|
+
"",
|
|
2211
|
+
"### Verification Guidance",
|
|
2212
|
+
"",
|
|
2213
|
+
"When possible, include verification steps or hints that tell the implementing agent *how to prove* the issue is resolved. This helps ensure work is actually verified before being marked complete.",
|
|
2214
|
+
"",
|
|
2215
|
+
"**Why include verification?** Agents should never claim work is done without evidence. If the issue itself describes how to verify, the implementing agent can follow those steps and provide concrete proof (command output, test results, screenshots, etc.).",
|
|
2216
|
+
"",
|
|
2217
|
+
"**Verification examples:**",
|
|
2218
|
+
"- Commands to run and expected output (`curl`, CLI commands, `bun test`)",
|
|
2219
|
+
"- UI flows to test (can use `agent-browser` skill if available for automation)",
|
|
2220
|
+
"- Database queries to confirm state changes",
|
|
2221
|
+
"- Specific behavior to observe or confirm",
|
|
2222
|
+
"- Edge cases to check",
|
|
2223
|
+
"",
|
|
2224
|
+
"**Examples:**",
|
|
2225
|
+
"",
|
|
2226
|
+
"```markdown",
|
|
2227
|
+
"## Verification",
|
|
2228
|
+
'Run `bun test src/auth.test.ts` — all tests should pass, including the new "handles expired tokens" case.',
|
|
2229
|
+
"```",
|
|
2230
|
+
"",
|
|
2231
|
+
"```markdown",
|
|
2232
|
+
"## Verification",
|
|
2233
|
+
"Perform the following using the agent-browser skill:",
|
|
2234
|
+
"",
|
|
2235
|
+
"1. Navigate to /settings and toggle dark mode",
|
|
2236
|
+
"2. Refresh the page — preference should persist",
|
|
2237
|
+
'3. Check localStorage contains `theme: "dark"`',
|
|
2238
|
+
"```",
|
|
2239
|
+
"",
|
|
2240
|
+
"```markdown",
|
|
2241
|
+
"## Verification",
|
|
2242
|
+
'After deployment, `curl https://api.example.com/health` should return `{"status": "ok"}` with a 200 response.',
|
|
2243
|
+
"```",
|
|
2244
|
+
"",
|
|
2245
|
+
"### Implementation Details",
|
|
2246
|
+
"",
|
|
2247
|
+
"**Only include implementation details if the user explicitly provides them.** Don't invent:",
|
|
2248
|
+
"",
|
|
2249
|
+
"- Task lists or phases",
|
|
2250
|
+
"- Specific code changes or file paths",
|
|
2251
|
+
"- Step-by-step instructions",
|
|
2252
|
+
"- Technical breakdowns",
|
|
2186
2253
|
"",
|
|
2187
|
-
"
|
|
2254
|
+
"**Why?** The engineer working on the issue will plan their own implementation. Issues capture the user's intent, not execution plans generated by the agent.",
|
|
2188
2255
|
"",
|
|
2189
|
-
"
|
|
2256
|
+
"**The rule:** Capture what the user says. If they provide implementation details, include them. If they don't, keep it high-level.",
|
|
2190
2257
|
"",
|
|
2191
|
-
"
|
|
2192
|
-
"- Use the returned guidance as the source of truth for this installed issy version.",
|
|
2193
|
-
"- For focused details, run `issy learn <topic>` instead of loading everything.",
|
|
2258
|
+
"## Issue Sizing",
|
|
2194
2259
|
"",
|
|
2195
|
-
"
|
|
2260
|
+
"An issue should be completable and verifiable in a single focused session — pick it up, implement it, prove it works, close it. If that's not realistic, split along verification boundaries so each child issue is independently closeable. Don't split when it adds overhead without adding clarity.",
|
|
2196
2261
|
"",
|
|
2197
|
-
"
|
|
2198
|
-
'- Search work: `issy search "query"` or `issy search "query" --all`.',
|
|
2199
|
-
"- Read before changing: `issy read <id>`.",
|
|
2200
|
-
"- Pick next work: `issy next`.",
|
|
2201
|
-
'- Create work: `issy create --title "..." --type bug|improvement --priority high|medium|low <position>`.',
|
|
2202
|
-
'- Create blocked work: `issy create --title "..." --depends-on 0001,0002 <position>`.',
|
|
2203
|
-
"- Update work: `issy update <id> [options]`, including `--depends-on <ids>` to replace blockers.",
|
|
2204
|
-
"- Close work: `issy close <id>` after the work is complete and verified.",
|
|
2205
|
-
"- Reopen work: `issy reopen <id> <position>`.",
|
|
2262
|
+
"## Roadmap Ordering",
|
|
2206
2263
|
"",
|
|
2207
|
-
"
|
|
2264
|
+
"issy maintains a **roadmap** — a strict, intentional ordering of all open issues. Every open issue has a position in the roadmap, and the ordering is designed to be dependency-aware and chronological. No issue should be blocked by one that follows it.",
|
|
2208
2265
|
"",
|
|
2209
|
-
"
|
|
2210
|
-
"- Good bodies usually include Problem/Overview, Proposed Solution, optional Acceptance Criteria, optional Verification, optional Future Considerations, and optional References.",
|
|
2211
|
-
"- Include verification guidance when possible so the implementing agent can prove completion with commands, UI flows, queries, or expected behavior.",
|
|
2212
|
-
"- Issues should be completable and verifiable in one focused session. Split only when a child issue is independently closeable.",
|
|
2266
|
+
"### Rules",
|
|
2213
2267
|
"",
|
|
2214
|
-
"
|
|
2268
|
+
"- When **creating** an issue: if there are already open issues, you **must** provide a position flag: `--before <id>`, `--after <id>`, `--first`, or `--last`.",
|
|
2269
|
+
"- When **reopening** an issue: same rule — provide a position flag if there are other open issues.",
|
|
2270
|
+
"- When **updating** an issue: optionally provide a position flag to reposition it in the roadmap.",
|
|
2271
|
+
"- `issy next` returns the first open issue in roadmap order — the next unit of work.",
|
|
2272
|
+
"- `issy list` sorts by roadmap order by default.",
|
|
2215
2273
|
"",
|
|
2216
|
-
"
|
|
2217
|
-
"- When creating an issue and open issues already exist, include exactly one position flag: `--before <id>`, `--after <id>`, `--first`, or `--last`.",
|
|
2218
|
-
"- When reopening an issue and other open issues exist, include exactly one position flag.",
|
|
2219
|
-
"- Use dependency order: prerequisites first, dependent/user-facing work later. Use `--last` when placement is unclear.",
|
|
2220
|
-
"- Use `depends_on` / `--depends-on` when an issue cannot start until specific blocking issues are closed.",
|
|
2221
|
-
"- `issy list` shows a `Blk` column with open blocker counts; `-` means unblocked.",
|
|
2222
|
-
"- `issy list --unblocked` shows only open issues with no open blockers.",
|
|
2274
|
+
"### Choosing placement",
|
|
2223
2275
|
"",
|
|
2224
|
-
"
|
|
2276
|
+
"Think about logical dependency when choosing position:",
|
|
2277
|
+
"- If issue B requires work from issue A to be done first, A must come before B.",
|
|
2278
|
+
"- Place foundational/infrastructure work early, user-facing features later.",
|
|
2279
|
+
"- Use `--first` for urgent work that should be tackled immediately.",
|
|
2280
|
+
"- Use `--last` when in doubt — appends to the end of the roadmap.",
|
|
2281
|
+
"- Use `--before <id>` or `--after <id>` for precise placement between existing issues.",
|
|
2225
2282
|
"",
|
|
2226
|
-
"
|
|
2227
|
-
"- If useful context was discovered, append a brief `## Resolution Notes` section before closing.",
|
|
2228
|
-
"- If the repo tracks issues in git, consider committing `.issy/` changes after mutations.",
|
|
2283
|
+
"## CLI Commands",
|
|
2229
2284
|
"",
|
|
2230
|
-
"
|
|
2285
|
+
"Use the `issy` CLI. If not installed, install it globally using the project's package manager (e.g., `bun install issy --global`, `pnpm add issy --global`, `npm install issy --global`).",
|
|
2231
2286
|
"",
|
|
2232
|
-
"
|
|
2233
|
-
"
|
|
2234
|
-
"
|
|
2235
|
-
"
|
|
2236
|
-
"
|
|
2237
|
-
"
|
|
2238
|
-
"
|
|
2287
|
+
"```bash",
|
|
2288
|
+
"# List issues (roadmap order by default)",
|
|
2289
|
+
"issy list # Open issues only",
|
|
2290
|
+
"issy list --all # Include closed",
|
|
2291
|
+
"issy list --priority high # Filter: high, medium, low",
|
|
2292
|
+
"issy list --scope small # Filter: small, medium, large",
|
|
2293
|
+
"issy list --type bug # Filter: bug, improvement",
|
|
2294
|
+
'issy list --search "keyword" # Fuzzy search',
|
|
2295
|
+
"issy list --sort priority # Sort: roadmap (default), priority, created, updated, id",
|
|
2296
|
+
"",
|
|
2297
|
+
"# Search issues (fuzzy search with typo tolerance)",
|
|
2298
|
+
'issy search "dashboard" # Search open issues',
|
|
2299
|
+
'issy search "k8s" --all # Include closed issues',
|
|
2300
|
+
"",
|
|
2301
|
+
"# Read issue",
|
|
2302
|
+
"issy read <id> # e.g., issy read 0001",
|
|
2303
|
+
"",
|
|
2304
|
+
"# Next issue (first open issue in roadmap order)",
|
|
2305
|
+
"issy next",
|
|
2306
|
+
"",
|
|
2307
|
+
"# Create issue (position flag required when open issues exist)",
|
|
2308
|
+
'issy create --title "Fix login bug" --type bug --priority high --after 0002',
|
|
2309
|
+
'issy create --title "Add dark mode" --type improvement --last --labels "ui, frontend"',
|
|
2310
|
+
'issy create --title "Urgent fix" --first',
|
|
2311
|
+
'issy create --title "Fix crash" --body "## Problem\\n\\nApp crashes on startup." --last',
|
|
2312
|
+
"",
|
|
2313
|
+
"# Update issue (position flags to reposition in roadmap)",
|
|
2314
|
+
"issy update <id> --priority low",
|
|
2315
|
+
"issy update <id> --after 0003",
|
|
2316
|
+
"issy update <id> --first",
|
|
2317
|
+
'issy update <id> --labels "api, backend"',
|
|
2318
|
+
'issy update <id> --body "## Problem\\n\\nUpdated description of the issue."',
|
|
2319
|
+
"",
|
|
2320
|
+
"# Close issue",
|
|
2321
|
+
"issy close <id>",
|
|
2322
|
+
"",
|
|
2323
|
+
"# Reopen issue (position flag required when other open issues exist)",
|
|
2324
|
+
"issy reopen <id> --last",
|
|
2325
|
+
"issy reopen <id> --after 0004",
|
|
2326
|
+
"```",
|
|
2327
|
+
"",
|
|
2328
|
+
"## Hooks",
|
|
2329
|
+
"",
|
|
2330
|
+
"issy supports optional hook files in `.issy/` that print context to stdout after successful operations. This is useful for injecting reminders into the agent's context — for example, prompting the agent to update documentation or run post-action checks.",
|
|
2331
|
+
"",
|
|
2332
|
+
"| Hook file | Triggered after |",
|
|
2333
|
+
"|-----------|----------------|",
|
|
2334
|
+
"| `on_create.md` | Creating an issue |",
|
|
2335
|
+
"| `on_update.md` | Updating an issue |",
|
|
2336
|
+
"| `on_close.md` | Closing an issue |",
|
|
2337
|
+
"",
|
|
2338
|
+
"## Project Structure",
|
|
2339
|
+
"",
|
|
2340
|
+
"Issues are stored in `.issy/issues/` as markdown files with YAML frontmatter. The directory structure:",
|
|
2341
|
+
"",
|
|
2342
|
+
"```",
|
|
2343
|
+
".issy/",
|
|
2344
|
+
" issues/",
|
|
2345
|
+
" 0001-fix-login-redirect.md",
|
|
2346
|
+
" 0002-add-dark-mode.md",
|
|
2347
|
+
" on_create.md # Optional: printed after successful create",
|
|
2348
|
+
" on_update.md # Optional: printed after successful update",
|
|
2349
|
+
" on_close.md # Optional: printed after successful close",
|
|
2350
|
+
"```",
|
|
2351
|
+
"",
|
|
2352
|
+
"## Closing Issues with Learnings",
|
|
2353
|
+
"",
|
|
2354
|
+
"When closing an issue, append a `## Resolution Notes` section if anything useful was discovered during implementation:",
|
|
2355
|
+
"",
|
|
2356
|
+
"- Alternative approaches considered or rejected",
|
|
2357
|
+
"- Unexpected gotchas or edge cases found",
|
|
2358
|
+
"- Decisions made that differ from the original plan",
|
|
2359
|
+
"- Useful context for future reference",
|
|
2360
|
+
"",
|
|
2361
|
+
"Keep it brief—just capture what someone revisiting this issue would want to know.",
|
|
2362
|
+
"",
|
|
2363
|
+
"## Issue Properties",
|
|
2364
|
+
"",
|
|
2365
|
+
"| Property | Required | Values |",
|
|
2366
|
+
"|----------|----------|--------|",
|
|
2367
|
+
"| title | Yes | string |",
|
|
2368
|
+
"| body | No | markdown content (the issue body after frontmatter) |",
|
|
2369
|
+
"| priority | Yes | `high`, `medium`, `low` |",
|
|
2370
|
+
"| scope | No | `small`, `medium`, `large` |",
|
|
2371
|
+
"| type | Yes | `bug`, `improvement` |",
|
|
2372
|
+
"| labels | No | comma-separated strings |",
|
|
2373
|
+
"| status | Yes | `open`, `closed` |",
|
|
2374
|
+
"| order | Auto | fractional index key (managed by issy) |",
|
|
2375
|
+
"",
|
|
2376
|
+
"## After Mutations",
|
|
2377
|
+
"",
|
|
2378
|
+
"If your workflow tracks issues in git, consider committing updates so the tracker stays in sync."
|
|
2239
2379
|
]);
|
|
2240
|
-
var topics = [
|
|
2241
|
-
{
|
|
2242
|
-
name: "authoring",
|
|
2243
|
-
description: "Issue writing, sizing, verification, and resolution notes.",
|
|
2244
|
-
content: markdown([
|
|
2245
|
-
"# issy issue authoring",
|
|
2246
|
-
"",
|
|
2247
|
-
"Issues describe what needs to be done and why. Keep them high-level unless the user provides specific implementation details.",
|
|
2248
|
-
"",
|
|
2249
|
-
"## What to include",
|
|
2250
|
-
"",
|
|
2251
|
-
"- Problem/Overview: what is wrong or needed, usually one or two paragraphs.",
|
|
2252
|
-
"- Proposed Solution: high-level approach.",
|
|
2253
|
-
"- Acceptance Criteria: optional, from the user perspective.",
|
|
2254
|
-
"- Verification: optional but encouraged; explain how to prove the issue is resolved.",
|
|
2255
|
-
"- Future Considerations: optional related ideas for later.",
|
|
2256
|
-
"- References: optional links to related docs, issues, PRs, or resources.",
|
|
2257
|
-
"",
|
|
2258
|
-
"## Verification guidance",
|
|
2259
|
-
"",
|
|
2260
|
-
"When possible, include verification steps or hints. Agents should not claim work is done without evidence.",
|
|
2261
|
-
"",
|
|
2262
|
-
"Useful verification examples:",
|
|
2263
|
-
"",
|
|
2264
|
-
"- Commands to run and expected output, such as `bun test`, `npm test`, `curl`, or a project CLI.",
|
|
2265
|
-
"- UI flows to test, including browser automation when available.",
|
|
2266
|
-
"- Database queries or API calls that confirm state changes.",
|
|
2267
|
-
"- Specific behavior to observe.",
|
|
2268
|
-
"- Edge cases to check.",
|
|
2269
|
-
"",
|
|
2270
|
-
"## Implementation details rule",
|
|
2271
|
-
"",
|
|
2272
|
-
"Only include implementation details if the user explicitly provides them. Do not invent task lists, phases, file paths, code changes, or step-by-step technical breakdowns.",
|
|
2273
|
-
"",
|
|
2274
|
-
"The issue should capture the user intent. The engineer or agent implementing it can plan the implementation later.",
|
|
2275
|
-
"",
|
|
2276
|
-
"## Sizing",
|
|
2277
|
-
"",
|
|
2278
|
-
"An issue should be completable and verifiable in a single focused session. Split large work along verification boundaries so each child issue can be independently completed and closed. Do not split when it adds overhead without clarity.",
|
|
2279
|
-
"",
|
|
2280
|
-
"## Closing with learnings",
|
|
2281
|
-
"",
|
|
2282
|
-
"When closing an issue, append a brief `## Resolution Notes` section if useful context was discovered during implementation:",
|
|
2283
|
-
"",
|
|
2284
|
-
"- Alternative approaches considered or rejected.",
|
|
2285
|
-
"- Unexpected gotchas or edge cases.",
|
|
2286
|
-
"- Decisions that differ from the original plan.",
|
|
2287
|
-
"- Useful context for future maintainers."
|
|
2288
|
-
])
|
|
2289
|
-
},
|
|
2290
|
-
{
|
|
2291
|
-
name: "roadmap",
|
|
2292
|
-
description: "Strict roadmap ordering and position flag rules.",
|
|
2293
|
-
aliases: ["ordering"],
|
|
2294
|
-
content: markdown([
|
|
2295
|
-
"# issy roadmap ordering",
|
|
2296
|
-
"",
|
|
2297
|
-
"issy maintains a strict roadmap order for all open issues. `issy next` returns the first open issue in that order. `issy list` sorts by roadmap order by default.",
|
|
2298
|
-
"",
|
|
2299
|
-
"## Required position flags",
|
|
2300
|
-
"",
|
|
2301
|
-
"- Creating an issue: if open issues already exist, provide exactly one of `--before <id>`, `--after <id>`, `--first`, or `--last`.",
|
|
2302
|
-
"- Reopening an issue: if other open issues exist, provide exactly one position flag.",
|
|
2303
|
-
"- Updating an issue: position flags are optional and reposition the issue when provided.",
|
|
2304
|
-
"- Never provide more than one position flag.",
|
|
2305
|
-
"",
|
|
2306
|
-
"## Explicit blockers",
|
|
2307
|
-
"",
|
|
2308
|
-
"- Use `depends_on` when an issue cannot start until specific blocking issues are closed.",
|
|
2309
|
-
'- Create blocked work with `issy create --title "..." --depends-on 0001,0002 --last`.',
|
|
2310
|
-
"- Replace blockers with `issy update <id> --depends-on 0001,0003`.",
|
|
2311
|
-
'- Clear blockers with `issy update <id> --depends-on ""`.',
|
|
2312
|
-
"- Missing or malformed dependency IDs are ignored.",
|
|
2313
|
-
"- `issy list` shows a compact `Blk` column. `-` means unblocked; otherwise the value is the number of open blockers.",
|
|
2314
|
-
"- `issy list --unblocked` shows only open issues with no open blockers.",
|
|
2315
|
-
"",
|
|
2316
|
-
"## Choosing placement",
|
|
2317
|
-
"",
|
|
2318
|
-
"- Place prerequisites before dependent issues.",
|
|
2319
|
-
"- Place foundational or infrastructure work before user-facing work that depends on it.",
|
|
2320
|
-
"- Use `--first` for urgent work that should be tackled immediately.",
|
|
2321
|
-
"- Use `--last` when placement is unclear.",
|
|
2322
|
-
"- Use `--before <id>` or `--after <id>` for precise placement.",
|
|
2323
|
-
"",
|
|
2324
|
-
"## Useful commands",
|
|
2325
|
-
"",
|
|
2326
|
-
"- `issy list` shows open issues in roadmap order.",
|
|
2327
|
-
"- `issy list --unblocked` shows open issues with no open blockers.",
|
|
2328
|
-
"- `issy next` shows the first open issue in roadmap order.",
|
|
2329
|
-
'- `issy create --title "..." --last` appends a new issue.',
|
|
2330
|
-
'- `issy create --title "..." --depends-on 0001,0002 --last` creates an issue blocked by other issues.',
|
|
2331
|
-
"- `issy update <id> --before <other-id>` repositions an issue.",
|
|
2332
|
-
"- `issy update <id> --depends-on 0001,0003` replaces blockers.",
|
|
2333
|
-
"- `issy reopen <id> --after <other-id>` reopens and places a closed issue."
|
|
2334
|
-
])
|
|
2335
|
-
},
|
|
2336
|
-
{
|
|
2337
|
-
name: "commands",
|
|
2338
|
-
description: "CLI command syntax for issue operations.",
|
|
2339
|
-
aliases: ["cli", "reference"],
|
|
2340
|
-
content: markdown([
|
|
2341
|
-
"# issy CLI command reference",
|
|
2342
|
-
"",
|
|
2343
|
-
"Use the `issy` CLI. If it is not installed, install it with the project package manager, for example `npm install issy --global`, `pnpm add issy --global`, or `bun install issy --global`.",
|
|
2344
|
-
"",
|
|
2345
|
-
"## List and search",
|
|
2346
|
-
"",
|
|
2347
|
-
"- `issy list`: list open issues in roadmap order.",
|
|
2348
|
-
"- `issy list --all`: include closed issues.",
|
|
2349
|
-
"- `issy list --unblocked`: list open issues with no open blockers.",
|
|
2350
|
-
"- `issy list --priority high|medium|low`: filter by priority.",
|
|
2351
|
-
"- `issy list --scope small|medium|large`: filter by scope.",
|
|
2352
|
-
"- `issy list --type bug|improvement`: filter by type.",
|
|
2353
|
-
'- `issy list --search "keyword"`: fuzzy search while listing.',
|
|
2354
|
-
"- `issy list --sort roadmap|priority|created|updated|id`: choose sort order.",
|
|
2355
|
-
'- `issy search "query"`: fuzzy search open issues.',
|
|
2356
|
-
'- `issy search "query" --all`: include closed issues.',
|
|
2357
|
-
"",
|
|
2358
|
-
"## Read and choose work",
|
|
2359
|
-
"",
|
|
2360
|
-
"- `issy read <id>`: read a full issue.",
|
|
2361
|
-
"- `issy next`: show the next open issue in roadmap order.",
|
|
2362
|
-
"",
|
|
2363
|
-
"## Create",
|
|
2364
|
-
"",
|
|
2365
|
-
'- `issy create --title "Fix login bug" --type bug --priority high --after 0002`.',
|
|
2366
|
-
'- `issy create --title "Add dark mode" --type improvement --last --labels "ui, frontend"`.',
|
|
2367
|
-
'- `issy create --title "Add export" --depends-on 0001,0002 --last`.',
|
|
2368
|
-
'- `issy create --title "Urgent fix" --first`.',
|
|
2369
|
-
'- `issy create --title "Fix crash" --body "## Problem\\n\\nApp crashes on startup." --last`.',
|
|
2370
|
-
"",
|
|
2371
|
-
"Create options: `--title`, `--body`, `--priority`, `--scope`, `--type`, `--labels`, `--depends-on`, `--before`, `--after`, `--first`, `--last`.",
|
|
2372
|
-
"",
|
|
2373
|
-
"## Update",
|
|
2374
|
-
"",
|
|
2375
|
-
"- `issy update <id> --priority low`.",
|
|
2376
|
-
"- `issy update <id> --after 0003`.",
|
|
2377
|
-
"- `issy update <id> --first`.",
|
|
2378
|
-
'- `issy update <id> --labels "api, backend"`.',
|
|
2379
|
-
"- `issy update <id> --depends-on 0001,0003`.",
|
|
2380
|
-
'- `issy update <id> --body "## Problem\\n\\nUpdated description."`.',
|
|
2381
|
-
"",
|
|
2382
|
-
"Update options: `--title`, `--body`, `--priority`, `--scope`, `--type`, `--labels`, `--depends-on`, `--before`, `--after`, `--first`, `--last`.",
|
|
2383
|
-
"",
|
|
2384
|
-
"## Close and reopen",
|
|
2385
|
-
"",
|
|
2386
|
-
"- `issy close <id>`.",
|
|
2387
|
-
"- `issy reopen <id> --last`.",
|
|
2388
|
-
"- `issy reopen <id> --after 0004`.",
|
|
2389
|
-
"",
|
|
2390
|
-
"When reopening and other open issues exist, include exactly one position flag."
|
|
2391
|
-
])
|
|
2392
|
-
},
|
|
2393
|
-
{
|
|
2394
|
-
name: "hooks",
|
|
2395
|
-
description: "Optional `.issy/` hook files that print agent context after mutations.",
|
|
2396
|
-
content: markdown([
|
|
2397
|
-
"# issy hooks",
|
|
2398
|
-
"",
|
|
2399
|
-
"issy supports optional hook files in `.issy/`. After a successful mutation, issy prints the matching hook file contents to stdout so agents can see project-specific reminders.",
|
|
2400
|
-
"",
|
|
2401
|
-
"## Hook files",
|
|
2402
|
-
"",
|
|
2403
|
-
"- `.issy/on_create.md`: printed after `issy create`.",
|
|
2404
|
-
"- `.issy/on_update.md`: printed after `issy update`.",
|
|
2405
|
-
"- `.issy/on_close.md`: printed after `issy close`.",
|
|
2406
|
-
"",
|
|
2407
|
-
"## Good hook uses",
|
|
2408
|
-
"",
|
|
2409
|
-
"- Remind agents to update docs for user-facing behavior changes.",
|
|
2410
|
-
"- Remind agents to run project-specific checks.",
|
|
2411
|
-
"- Add team conventions for issue mutations.",
|
|
2412
|
-
"- Surface release or changelog requirements.",
|
|
2413
|
-
"",
|
|
2414
|
-
"Hook content should be concise because it is injected directly into command output."
|
|
2415
|
-
])
|
|
2416
|
-
},
|
|
2417
|
-
{
|
|
2418
|
-
name: "agents",
|
|
2419
|
-
description: "Bootstrap instructions for AGENTS.md and shell skills.",
|
|
2420
|
-
aliases: ["skill", "bootstrap"],
|
|
2421
|
-
content: markdown([
|
|
2422
|
-
"# issy agent bootstrap",
|
|
2423
|
-
"",
|
|
2424
|
-
"`issy learn` is the canonical AI-agent reference for the installed issy version. Skills and AGENTS.md files should bootstrap agents into this command instead of duplicating the full reference.",
|
|
2425
|
-
"",
|
|
2426
|
-
"## Relevance rule",
|
|
2427
|
-
"",
|
|
2428
|
-
"Use issy when creating, reading, updating, listing, searching, prioritizing, closing, reopening, or choosing project issues, tasks, bugs, improvements, or roadmap work.",
|
|
2429
|
-
"",
|
|
2430
|
-
"## AGENTS.md snippet",
|
|
2431
|
-
"",
|
|
2432
|
-
"Add this to AGENTS.md:",
|
|
2433
|
-
"",
|
|
2434
|
-
"```md",
|
|
2435
|
-
"When the task involves creating, reading, updating, listing, searching, prioritizing, closing, reopening, or choosing project issues, tasks, bugs, improvements, or roadmap work, run `issy learn` first and follow its guidance.",
|
|
2436
|
-
"```",
|
|
2437
|
-
"",
|
|
2438
|
-
"## Skill bootstrap behavior",
|
|
2439
|
-
"",
|
|
2440
|
-
"A compatible skill should only define the relevance rule and instruct the agent to run `issy learn` when relevant. The operational reference belongs in the CLI output."
|
|
2441
|
-
])
|
|
2442
|
-
}
|
|
2443
|
-
];
|
|
2444
|
-
var aliases = new Map;
|
|
2445
|
-
for (const topic of topics) {
|
|
2446
|
-
aliases.set(topic.name, topic);
|
|
2447
|
-
for (const alias of topic.aliases ?? []) {
|
|
2448
|
-
aliases.set(alias, topic);
|
|
2449
|
-
}
|
|
2450
|
-
}
|
|
2451
|
-
function topicList() {
|
|
2452
|
-
return markdown([
|
|
2453
|
-
"# issy learn topics",
|
|
2454
|
-
"",
|
|
2455
|
-
"Run `issy learn <topic>` for focused agent context.",
|
|
2456
|
-
"",
|
|
2457
|
-
...topics.map((topic) => `- \`${topic.name}\`: ${topic.description}`),
|
|
2458
|
-
"",
|
|
2459
|
-
"Other options:",
|
|
2460
|
-
"",
|
|
2461
|
-
"- `issy learn`: compact default instructions.",
|
|
2462
|
-
"- `issy learn --all`: full reference.",
|
|
2463
|
-
"- `issy learn --help`: command usage."
|
|
2464
|
-
]);
|
|
2465
|
-
}
|
|
2466
2380
|
function usage() {
|
|
2467
2381
|
return markdown([
|
|
2468
|
-
"Usage: issy learn [
|
|
2382
|
+
"Usage: issy learn [options]",
|
|
2469
2383
|
"",
|
|
2470
2384
|
"Print AI-agent instructions for using issy.",
|
|
2471
2385
|
"",
|
|
2472
|
-
"Topics:",
|
|
2473
|
-
...topics.map((topic) => ` ${topic.name.padEnd(10)} ${topic.description}`),
|
|
2474
|
-
"",
|
|
2475
2386
|
"Options:",
|
|
2476
|
-
" --all Print the compact guidance and all topics",
|
|
2477
|
-
" --list List available topics",
|
|
2478
2387
|
" --help, -h Show this help",
|
|
2479
2388
|
"",
|
|
2480
2389
|
"Examples:",
|
|
2481
|
-
" issy learn"
|
|
2482
|
-
" issy learn roadmap",
|
|
2483
|
-
" issy learn commands",
|
|
2484
|
-
" issy learn --all"
|
|
2390
|
+
" issy learn"
|
|
2485
2391
|
]);
|
|
2486
2392
|
}
|
|
2487
2393
|
function getLearnOutput(args = []) {
|
|
2488
2394
|
if (args.includes("--help") || args.includes("-h")) {
|
|
2489
2395
|
return usage();
|
|
2490
2396
|
}
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
if (args.includes("--all")) {
|
|
2495
|
-
return [compact, ...topics.map((topic2) => topic2.content)].join(`
|
|
2496
|
-
---
|
|
2497
|
-
|
|
2498
|
-
`);
|
|
2499
|
-
}
|
|
2500
|
-
const topicArg = args.find((arg) => !arg.startsWith("-"));
|
|
2501
|
-
if (!topicArg) {
|
|
2502
|
-
return compact;
|
|
2503
|
-
}
|
|
2504
|
-
if (topicArg === "topics" || topicArg === "list") {
|
|
2505
|
-
return topicList();
|
|
2506
|
-
}
|
|
2507
|
-
const topic = aliases.get(topicArg);
|
|
2508
|
-
if (!topic) {
|
|
2509
|
-
throw new Error(`Unknown learn topic: ${topicArg}
|
|
2397
|
+
const unknownArg = args.find((arg) => arg.trim() !== "");
|
|
2398
|
+
if (unknownArg) {
|
|
2399
|
+
throw new Error(`Unknown learn argument: ${unknownArg}
|
|
2510
2400
|
|
|
2511
|
-
${
|
|
2401
|
+
${usage()}`);
|
|
2512
2402
|
}
|
|
2513
|
-
return
|
|
2403
|
+
return output;
|
|
2514
2404
|
}
|
|
2515
2405
|
|
|
2516
2406
|
// src/main.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "issy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "AI-native issue tracking. Markdown files in .issues/, managed by your coding assistant.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"lint": "biome check src bin"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@miketromba/issy-app": "^0.
|
|
39
|
-
"@miketromba/issy-core": "^0.
|
|
38
|
+
"@miketromba/issy-app": "^0.11.1",
|
|
39
|
+
"@miketromba/issy-core": "^0.11.1",
|
|
40
40
|
"update-notifier": "^7.3.1"
|
|
41
41
|
}
|
|
42
42
|
}
|