issy 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/bin/issy +15 -43
  2. package/dist/cli.js +29 -2
  3. package/package.json +4 -3
package/bin/issy CHANGED
@@ -1,46 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
  import { dirname } from 'node:path'
3
- import { existsSync, mkdirSync, readdirSync, writeFileSync, readFileSync } from 'node:fs'
3
+ import {
4
+ existsSync,
5
+ mkdirSync,
6
+ readdirSync,
7
+ writeFileSync,
8
+ readFileSync
9
+ } from 'node:fs'
4
10
  import { join, resolve } from 'node:path'
5
11
  import { fileURLToPath } from 'node:url'
12
+ import updateNotifier from 'update-notifier'
6
13
 
7
14
  const args = process.argv.slice(2)
8
15
 
9
- /**
10
- * Check for newer version on npm and warn if outdated.
11
- * Runs async and doesn't block startup.
12
- */
13
- async function checkForUpdates() {
14
- try {
15
- const here = resolve(fileURLToPath(import.meta.url), '..')
16
- const pkgPath = resolve(here, '..', 'package.json')
17
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))
18
- const currentVersion = pkg.version
19
-
20
- const controller = new AbortController()
21
- const timeout = setTimeout(() => controller.abort(), 2000)
22
-
23
- const res = await fetch('https://registry.npmjs.org/issy/latest', {
24
- signal: controller.signal
25
- })
26
- clearTimeout(timeout)
27
-
28
- if (!res.ok) return
29
-
30
- const data = await res.json()
31
- const latestVersion = data.version
32
-
33
- if (currentVersion !== latestVersion) {
34
- console.error(`\x1b[33m⚠ You're running issy v${currentVersion}, but v${latestVersion} is available.\x1b[0m`)
35
- console.error(`\x1b[33m Run: npx issy@latest\x1b[0m\n`)
36
- }
37
- } catch {
38
- // Silently ignore - network issues, timeouts, etc.
39
- }
40
- }
16
+ // Check for updates (non-blocking, cached, shows on next run)
17
+ const here = resolve(fileURLToPath(import.meta.url), '..')
18
+ const pkgPath = resolve(here, '..', 'package.json')
19
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))
20
+ updateNotifier({ pkg, updateCheckInterval: 1000 * 60 * 60 }).notify() // 1 hour
41
21
 
42
- // Fire off update check (non-blocking)
43
- const updateCheck = checkForUpdates()
44
22
  const cliCommands = new Set([
45
23
  'list',
46
24
  'search',
@@ -55,11 +33,7 @@ const cliCommands = new Set([
55
33
 
56
34
  // Handle --version / -v flag
57
35
  if (args[0] === '--version' || args[0] === '-v') {
58
- const here = resolve(fileURLToPath(import.meta.url), '..')
59
- const pkgPath = resolve(here, '..', 'package.json')
60
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))
61
36
  console.log(`issy v${pkg.version}`)
62
- await updateCheck
63
37
  process.exit(0)
64
38
  }
65
39
 
@@ -141,7 +115,6 @@ if (cliCommands.has(args[0] || '')) {
141
115
  process.argv = [process.argv[0], process.argv[1], ...args]
142
116
  const cli = await import(entry)
143
117
  await cli.ready // Wait for main() to complete before exiting
144
- await updateCheck // Wait for version check to complete
145
118
  process.exit(0)
146
119
  }
147
120
 
@@ -158,7 +131,7 @@ if (shouldInitOnly) {
158
131
  if (!existsSync(issuesDir)) {
159
132
  mkdirSync(issuesDir, { recursive: true })
160
133
  }
161
-
134
+
162
135
  // Only seed with welcome issue if --seed flag is passed
163
136
  if (shouldSeed) {
164
137
  const hasIssues =
@@ -180,9 +153,8 @@ if (shouldInitOnly) {
180
153
  writeFileSync(join(issuesDir, '0001-welcome-to-issy.md'), welcome)
181
154
  }
182
155
  }
183
-
156
+
184
157
  console.log(`Initialized ${issuesDir}`)
185
- await updateCheck // Wait for version check to complete
186
158
  process.exit(0)
187
159
  }
188
160
 
package/dist/cli.js CHANGED
@@ -93,6 +93,9 @@ function generateFrontmatter(data) {
93
93
  lines.push(`title: ${data.title}`);
94
94
  lines.push(`description: ${data.description}`);
95
95
  lines.push(`priority: ${data.priority}`);
96
+ if (data.scope) {
97
+ lines.push(`scope: ${data.scope}`);
98
+ }
96
99
  lines.push(`type: ${data.type}`);
97
100
  if (data.labels) {
98
101
  lines.push(`labels: ${data.labels}`);
@@ -178,10 +181,14 @@ async function createIssue(input) {
178
181
  throw new Error("Title is required");
179
182
  }
180
183
  const priority = input.priority || "medium";
184
+ const scope = input.scope;
181
185
  const type = input.type || "improvement";
182
186
  if (!["high", "medium", "low"].includes(priority)) {
183
187
  throw new Error("Priority must be: high, medium, or low");
184
188
  }
189
+ if (scope && !["small", "medium", "large"].includes(scope)) {
190
+ throw new Error("Scope must be: small, medium, or large");
191
+ }
185
192
  if (!["bug", "improvement"].includes(type)) {
186
193
  throw new Error("Type must be: bug or improvement");
187
194
  }
@@ -192,6 +199,7 @@ async function createIssue(input) {
192
199
  title: input.title,
193
200
  description: input.description || input.title,
194
201
  priority,
202
+ scope: scope || undefined,
195
203
  type,
196
204
  labels: input.labels || undefined,
197
205
  status: "open",
@@ -227,6 +235,7 @@ async function updateIssue(id, input) {
227
235
  ...input.title && { title: input.title },
228
236
  ...input.description && { description: input.description },
229
237
  ...input.priority && { priority: input.priority },
238
+ ...input.scope && { scope: input.scope },
230
239
  ...input.type && { type: input.type },
231
240
  ...input.labels !== undefined && {
232
241
  labels: input.labels || undefined
@@ -249,6 +258,7 @@ async function closeIssue(id) {
249
258
  var SUPPORTED_QUALIFIERS = new Set([
250
259
  "is",
251
260
  "priority",
261
+ "scope",
252
262
  "type",
253
263
  "label",
254
264
  "sort"
@@ -1570,6 +1580,9 @@ function filterIssues(issues, filters) {
1570
1580
  if (filters.priority && issue.frontmatter.priority !== filters.priority) {
1571
1581
  return false;
1572
1582
  }
1583
+ if (filters.scope && issue.frontmatter.scope !== filters.scope) {
1584
+ return false;
1585
+ }
1573
1586
  if (filters.type && issue.frontmatter.type !== filters.type) {
1574
1587
  return false;
1575
1588
  }
@@ -1629,6 +1642,7 @@ async function listIssues(options) {
1629
1642
  const issues = filterAndSearchIssues(allIssues, {
1630
1643
  status: options.all ? undefined : "open",
1631
1644
  priority: options.priority,
1645
+ scope: options.scope,
1632
1646
  type: options.type,
1633
1647
  search: options.search
1634
1648
  });
@@ -1660,6 +1674,9 @@ ${"=".repeat(70)}`);
1660
1674
  console.log(` ID: ${issue.id}`);
1661
1675
  console.log(` Status: ${issue.frontmatter.status.toUpperCase()}`);
1662
1676
  console.log(` Priority: ${prioritySymbol(issue.frontmatter.priority)} ${issue.frontmatter.priority}`);
1677
+ if (issue.frontmatter.scope) {
1678
+ console.log(` Scope: ${issue.frontmatter.scope}`);
1679
+ }
1663
1680
  console.log(` Type: ${issue.frontmatter.type}`);
1664
1681
  if (issue.frontmatter.labels) {
1665
1682
  console.log(` Labels: ${issue.frontmatter.labels}`);
@@ -1716,6 +1733,7 @@ Create New Issue`);
1716
1733
  options.title = await prompt("Title: ");
1717
1734
  options.description = await prompt("Description: ");
1718
1735
  options.priority = await prompt("Priority (high/medium/low) [medium]: ");
1736
+ options.scope = await prompt("Scope (small/medium/large) []: ");
1719
1737
  options.type = await prompt("Type (bug/improvement) [improvement]: ");
1720
1738
  options.labels = await prompt("Labels (comma-separated) []: ");
1721
1739
  if (!options.priority)
@@ -1732,6 +1750,7 @@ Create New Issue`);
1732
1750
  title: options.title,
1733
1751
  description: options.description,
1734
1752
  priority: options.priority,
1753
+ scope: options.scope,
1735
1754
  type: options.type,
1736
1755
  labels: options.labels
1737
1756
  };
@@ -1749,6 +1768,7 @@ async function updateIssueCommand(id, options) {
1749
1768
  title: options.title,
1750
1769
  description: options.description,
1751
1770
  priority: options.priority,
1771
+ scope: options.scope,
1752
1772
  type: options.type,
1753
1773
  labels: options.labels,
1754
1774
  status: options.status
@@ -1785,6 +1805,7 @@ Commands:
1785
1805
  list List all open issues
1786
1806
  --all, -a Include closed issues
1787
1807
  --priority, -p <p> Filter by priority (high, medium, low)
1808
+ --scope <s> Filter by scope (small, medium, large)
1788
1809
  --type, -t <t> Filter by type (bug, improvement)
1789
1810
  --search, -s <q> Fuzzy search issues
1790
1811
 
@@ -1797,6 +1818,7 @@ Commands:
1797
1818
  --title, -t <t> Issue title
1798
1819
  --description, -d <d> Short description
1799
1820
  --priority, -p <p> Priority (high, medium, low)
1821
+ --scope <s> Scope (small, medium, large)
1800
1822
  --type <t> Type (bug, improvement)
1801
1823
  --labels, -l <l> Comma-separated labels
1802
1824
 
@@ -1804,6 +1826,7 @@ Commands:
1804
1826
  --title, -t <t> New title
1805
1827
  --description, -d <d> New description
1806
1828
  --priority, -p <p> New priority
1829
+ --scope <s> New scope
1807
1830
  --type <t> New type
1808
1831
  --labels, -l <l> New labels
1809
1832
  --status, -s <s> New status (open, closed)
@@ -1813,11 +1836,12 @@ Commands:
1813
1836
  Examples:
1814
1837
  issy list
1815
1838
  issy list --priority high --type bug
1839
+ issy list --scope large
1816
1840
  issy search "dashboard"
1817
1841
  issy search "k8s" --all
1818
1842
  issy read 0001
1819
- issy create --title "Fix login bug" --type bug --priority high
1820
- issy update 0001 --priority low
1843
+ issy create --title "Fix login bug" --type bug --priority high --scope small
1844
+ issy update 0001 --priority low --scope medium
1821
1845
  issy close 0001
1822
1846
  `);
1823
1847
  return;
@@ -1829,6 +1853,7 @@ Examples:
1829
1853
  options: {
1830
1854
  all: { type: "boolean", short: "a" },
1831
1855
  priority: { type: "string", short: "p" },
1856
+ scope: { type: "string" },
1832
1857
  type: { type: "string", short: "t" },
1833
1858
  search: { type: "string", short: "s" }
1834
1859
  },
@@ -1869,6 +1894,7 @@ Examples:
1869
1894
  title: { type: "string", short: "t" },
1870
1895
  description: { type: "string", short: "d" },
1871
1896
  priority: { type: "string", short: "p" },
1897
+ scope: { type: "string" },
1872
1898
  type: { type: "string" },
1873
1899
  labels: { type: "string", short: "l" }
1874
1900
  },
@@ -1889,6 +1915,7 @@ Examples:
1889
1915
  title: { type: "string", short: "t" },
1890
1916
  description: { type: "string", short: "d" },
1891
1917
  priority: { type: "string", short: "p" },
1918
+ scope: { type: "string" },
1892
1919
  type: { type: "string" },
1893
1920
  labels: { type: "string", short: "l" },
1894
1921
  status: { type: "string", short: "s" }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "issy",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "AI-native issue tracking. Markdown files in .issues/, managed by your coding assistant.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -34,7 +34,8 @@
34
34
  "lint": "biome check src bin"
35
35
  },
36
36
  "dependencies": {
37
- "@miketromba/issy-app": "^0.2.0",
38
- "@miketromba/issy-core": "^0.2.0"
37
+ "@miketromba/issy-app": "^0.3.0",
38
+ "@miketromba/issy-core": "^0.3.0",
39
+ "update-notifier": "^7.3.1"
39
40
  }
40
41
  }