@basementuniverse/kanbn 2.0.0 → 2.1.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 (87) hide show
  1. package/README.md +2 -1
  2. package/docs/advanced-configuration.md +23 -0
  3. package/docs/commands/add.txt +9 -0
  4. package/docs/commands/archive.txt +5 -0
  5. package/docs/commands/board.txt +5 -0
  6. package/docs/commands/boards.txt +34 -0
  7. package/docs/commands/burndown.txt +6 -0
  8. package/docs/commands/comment.txt +5 -0
  9. package/docs/commands/edit.txt +5 -0
  10. package/docs/commands/find.txt +11 -0
  11. package/docs/commands/gantt.txt +5 -0
  12. package/docs/commands/help.txt +1 -0
  13. package/docs/commands/history.txt +5 -0
  14. package/docs/commands/init.txt +13 -0
  15. package/docs/commands/move.txt +9 -0
  16. package/docs/commands/remove.txt +12 -1
  17. package/docs/commands/rename.txt +5 -0
  18. package/docs/commands/restore.txt +6 -0
  19. package/docs/commands/sort.txt +5 -0
  20. package/docs/commands/sprint.txt +9 -0
  21. package/docs/commands/status.txt +10 -1
  22. package/docs/commands/task.txt +5 -0
  23. package/docs/commands/validate.txt +15 -0
  24. package/docs/index-structure.md +26 -0
  25. package/docs/index.md +3 -1
  26. package/docs/multiple-boards.md +258 -0
  27. package/docs/quick-start.md +21 -1
  28. package/docs/task-structure.md +22 -1
  29. package/example/README.md +23 -0
  30. package/example/boards/.kanbn/design.md +31 -0
  31. package/example/boards/.kanbn/index.md +44 -0
  32. package/example/boards/.kanbn/tasks/add-usage-alert-emails.md +19 -0
  33. package/example/boards/.kanbn/tasks/build-tenant-settings-page.md +43 -0
  34. package/example/boards/.kanbn/tasks/create-organization-switcher.md +44 -0
  35. package/example/boards/.kanbn/tasks/design-onboarding-checklist.md +22 -0
  36. package/example/boards/.kanbn/tasks/refresh-marketing-site.md +28 -0
  37. package/example/boards/.kanbn/tasks/ship-billing-portal.md +29 -0
  38. package/package.json +9 -7
  39. package/routes/add.json +35 -11
  40. package/routes/archive.json +10 -2
  41. package/routes/board.json +11 -3
  42. package/routes/boards.json +30 -0
  43. package/routes/burndown.json +23 -7
  44. package/routes/comment.json +14 -4
  45. package/routes/edit.json +32 -10
  46. package/routes/find.json +37 -12
  47. package/routes/gantt.json +20 -6
  48. package/routes/history.json +39 -25
  49. package/routes/init.json +3 -1
  50. package/routes/move.json +22 -6
  51. package/routes/remove.json +15 -4
  52. package/routes/rename.json +11 -3
  53. package/routes/restore.json +8 -2
  54. package/routes/sort.json +35 -11
  55. package/routes/sprint.json +14 -4
  56. package/routes/status.json +23 -7
  57. package/routes/task.json +10 -2
  58. package/routes/validate.json +18 -5
  59. package/skills/kanbn-plan/SKILL.md +10 -1
  60. package/skills/kanbn-replan/SKILL.md +6 -1
  61. package/src/board.js +1 -1
  62. package/src/controller/add.js +52 -46
  63. package/src/controller/archive.js +8 -4
  64. package/src/controller/board.js +8 -9
  65. package/src/controller/boards.js +140 -0
  66. package/src/controller/burndown.js +9 -5
  67. package/src/controller/comment.js +9 -5
  68. package/src/controller/edit.js +9 -5
  69. package/src/controller/find.js +12 -9
  70. package/src/controller/gantt.js +8 -4
  71. package/src/controller/history.js +8 -4
  72. package/src/controller/init.js +39 -4
  73. package/src/controller/move.js +69 -15
  74. package/src/controller/remove.js +34 -11
  75. package/src/controller/rename.js +8 -4
  76. package/src/controller/restore.js +23 -8
  77. package/src/controller/sort.js +19 -3
  78. package/src/controller/sprint.js +31 -7
  79. package/src/controller/status.js +8 -4
  80. package/src/controller/task.js +22 -9
  81. package/src/controller/validate.js +60 -7
  82. package/src/git-user-name.js +19 -0
  83. package/src/main.d.ts +184 -6
  84. package/src/main.js +1333 -63
  85. package/src/parse-index.js +14 -0
  86. package/src/parse-task.js +16 -0
  87. package/src/utility.js +140 -0
package/routes/task.json CHANGED
@@ -9,8 +9,16 @@
9
9
  "json"
10
10
  ],
11
11
  "alias": {
12
- "json": ["j"]
13
- }
12
+ "json": [
13
+ "j"
14
+ ],
15
+ "board": [
16
+ "b"
17
+ ]
18
+ },
19
+ "string": [
20
+ "board"
21
+ ]
14
22
  },
15
23
  "controller": "./src/controller/task",
16
24
  "help": "./docs/commands/task.txt"
@@ -7,13 +7,26 @@
7
7
  "boolean": [
8
8
  "json",
9
9
  "save",
10
- "fix"
10
+ "fix",
11
+ "all-boards"
11
12
  ],
12
13
  "alias": {
13
- "json": ["j"],
14
- "save": ["s"],
15
- "fix": ["f"]
16
- }
14
+ "json": [
15
+ "j"
16
+ ],
17
+ "save": [
18
+ "s"
19
+ ],
20
+ "fix": [
21
+ "f"
22
+ ],
23
+ "board": [
24
+ "b"
25
+ ]
26
+ },
27
+ "string": [
28
+ "board"
29
+ ]
17
30
  },
18
31
  "controller": "./src/controller/validate",
19
32
  "help": "./docs/commands/validate.txt"
@@ -13,9 +13,18 @@ Do not use this skill to implement the project, edit source code, scaffold files
13
13
 
14
14
  Your output must stay inside the Kanbn planning surface:
15
15
 
16
- - `.kanbn/index.md`
16
+ - `.kanbn/index.md` — the main board
17
+ - `.kanbn/{board-slug}.md` — any secondary boards, in exactly the same format as the index
17
18
  - `.kanbn/tasks/*.md`
18
19
 
20
+ A workspace may hold several boards over one shared pool of tasks. Every board file is a sibling of
21
+ `index.md` inside `.kanbn/`, and boards own membership: a task can be referenced by several boards,
22
+ in a different column on each. Task files never record which boards they belong to.
23
+
24
+ Unless the user asks for more than one board, plan onto `.kanbn/index.md` alone. When they do want
25
+ several, write each board file yourself in the same format, and make sure any task referenced by a
26
+ board has a file in `.kanbn/tasks/`.
27
+
19
28
  Do not create application code, tests, CI files, or documentation unrelated to the Kanbn board.
20
29
 
21
30
  If the user asks for planning plus implementation, complete the planning portion in Kanbn format first and keep the planning files cleanly separated from any later work.
@@ -13,9 +13,14 @@ Do not use this skill to redesign scope, rewrite completed tasks, or invent new
13
13
 
14
14
  Your output must stay inside the Kanbn task surface:
15
15
 
16
- - `.kanbn/index.md`
16
+ - `.kanbn/index.md` — the main board
17
+ - `.kanbn/{board-slug}.md` — any secondary boards, in exactly the same format as the index
17
18
  - `.kanbn/tasks/*.md`
18
19
 
20
+ A workspace may hold several boards over one shared pool of tasks. Rescheduling changes task
21
+ metadata dates, which are shared, so it applies to every board at once — you do not need to touch
22
+ board files unless a task's column changes.
23
+
19
24
  You may read the current board and gantt output, but the end result should be a revised schedule for existing tasks, not a new plan.
20
25
 
21
26
  Do not modify completed tasks unless the user explicitly asks to reopen them. Leave completed task descriptions, relations, and completion history intact.
package/src/board.js CHANGED
@@ -178,7 +178,7 @@ module.exports = (() => {
178
178
  borderChars: 'lightRounded',
179
179
  borderAttr: { color: 'grey' },
180
180
  textAttr: { color: 'white', bgColor: 'default' },
181
- width: term.width,
181
+ width: utility.terminalWidth(term.width),
182
182
  fit: true
183
183
  }
184
184
  );
@@ -1,9 +1,13 @@
1
- const kanbn = require('../main');
1
+ const workspace = require('../main');
2
2
  const utility = require('../utility');
3
3
  const inquirer = require('inquirer');
4
4
  const fuzzy = require('fuzzy');
5
5
  const chrono = require('chrono-node');
6
- const getGitUsername = require('git-user-name');
6
+ const getGitUsername = require('../git-user-name');
7
+
8
+ // The boards this command targets - set from --board before anything else runs
9
+ let kanbn = workspace;
10
+ let boardTargets = [];
7
11
 
8
12
  inquirer.registerPrompt('datepicker', require('inquirer-datepicker'));
9
13
  inquirer.registerPrompt('recursive', require('inquirer-recursive'));
@@ -235,15 +239,24 @@ async function interactiveAddUntrackedTasks(untrackedTasks, columnName, columnNa
235
239
  * @param {object} taskData
236
240
  * @param {string} columnName
237
241
  */
238
- function createTask(taskData, columnName) {
239
- kanbn
240
- .createTask(taskData, columnName)
241
- .then(taskId => {
242
- console.log(`Created task "${taskId}" in column "${columnName}"`);
243
- })
244
- .catch(error => {
242
+ async function createTask(taskData, columnName) {
243
+ try {
244
+
245
+ // The task is created once, on the first target board, and then added to the others - one task
246
+ // file, referenced by however many boards were named
247
+ const [first, ...rest] = boardTargets;
248
+ const taskId = await first.kanbn.createTask(taskData, columnName);
249
+ console.log(
250
+ `Created task "${taskId}" in column "${columnName}"` +
251
+ (boardTargets.length > 1 || !first.main ? ` on board "${first.slug}"` : '')
252
+ );
253
+ for (const target of rest) {
254
+ await target.kanbn.addTaskToBoard(taskId, target.columnName);
255
+ console.log(`Added task "${taskId}" to column "${target.columnName}" on board "${target.slug}"`);
256
+ }
257
+ } catch (error) {
245
258
  utility.error(error);
246
- });
259
+ }
247
260
  }
248
261
 
249
262
  /**
@@ -252,50 +265,44 @@ function createTask(taskData, columnName) {
252
265
  * @param {string} columnName
253
266
  */
254
267
  async function addUntrackedTasks(untrackedTasks, columnName) {
255
- for (let untrackedTask of untrackedTasks) {
256
- try {
257
- await kanbn.addUntrackedTaskToIndex(untrackedTask, columnName);
258
- } catch (error) {
259
- utility.error(error);
260
- return;
268
+ for (const target of boardTargets) {
269
+
270
+ // The column chosen interactively or on the command line applies to the first board; the others
271
+ // use whatever resolveBoardTargets() paired them with
272
+ const targetColumn = target === boardTargets[0] ? columnName : target.columnName;
273
+ for (let untrackedTask of untrackedTasks) {
274
+ try {
275
+ await target.kanbn.addUntrackedTaskToIndex(untrackedTask, targetColumn);
276
+ } catch (error) {
277
+ utility.error(error);
278
+ return;
279
+ }
261
280
  }
281
+ console.log(
282
+ `Added ${untrackedTasks.length} task${untrackedTasks.length !== 1 ? 's' : ''} ` +
283
+ `to column "${targetColumn}"` + (boardTargets.length > 1 || !target.main ? ` on board "${target.slug}"` : '')
284
+ );
262
285
  }
263
- console.log(
264
- `Added ${untrackedTasks.length} task${untrackedTasks.length !== 1 ? 's' : ''} to column "${columnName}"`
265
- );
266
286
  }
267
287
 
268
288
  module.exports = async args => {
269
289
 
270
- // Make sure kanbn has been initialised
271
- if (!await kanbn.initialised()) {
272
- utility.error('Kanbn has not been initialised in this folder\nTry running: {b}kanbn init{b}');
290
+ // Work out which boards this command targets, and which column to use on each. -b is repeatable,
291
+ // so one task can be created and placed on several boards at once
292
+ const targets = await utility.resolveBoardTargets(workspace, args);
293
+ if (targets === null) {
273
294
  return;
274
295
  }
275
-
276
- // Get the index and make sure it has some columns
277
- let index;
278
- try {
279
- index = await kanbn.getIndex();
280
- } catch (error) {
281
- utility.error(error);
282
- return;
283
- }
284
- const columnNames = Object.keys(index.columns);
285
- if (!columnNames.length) {
286
- utility.error('No columns defined in the index\nTry running {b}kanbn init -c "column name"{b}');
287
- return;
296
+ boardTargets = targets;
297
+ kanbn = targets[0].kanbn;
298
+ for (const target of boardTargets) {
299
+ target.main = await target.kanbn.isMainBoard();
288
300
  }
301
+ utility.showColumnFallbackNotices(boardTargets, kanbn.isVerbose(targets[0].index));
289
302
 
290
- // Get column name if specified, otherwise default to the first available column
291
- let columnName = columnNames[0];
292
- if (args.column) {
293
- columnName = utility.strArg(args.column);
294
- if (columnNames.indexOf(columnName) === -1) {
295
- utility.error(`Column "${columnName}" doesn't exist`);
296
- return;
297
- }
298
- }
303
+ const index = targets[0].index;
304
+ const columnNames = Object.keys(index.columns);
305
+ const columnName = targets[0].columnName;
299
306
 
300
307
  // Add untracked file(s)
301
308
  if (args.untracked) {
@@ -534,8 +541,7 @@ module.exports = async args => {
534
541
  type: relation.type
535
542
  }));
536
543
  }
537
- columnName = answers.column;
538
- createTask(taskData, columnName);
544
+ createTask(taskData, answers.column);
539
545
  })
540
546
  .catch(error => {
541
547
  utility.error(error);
@@ -1,6 +1,9 @@
1
- const kanbn = require('../main');
1
+ const workspace = require('../main');
2
2
  const utility = require('../utility');
3
3
 
4
+ // The board this command targets - set from --board before anything else runs
5
+ let kanbn = workspace;
6
+
4
7
  /**
5
8
  * Archive a task
6
9
  * @param {string} taskId
@@ -32,11 +35,12 @@ function listArchivedTasks() {
32
35
 
33
36
  module.exports = async args => {
34
37
 
35
- // Make sure kanbn has been initialised
36
- if (!await kanbn.initialised()) {
37
- utility.error('Kanbn has not been initialised in this folder\nTry running: {b}kanbn init{b}');
38
+ // Work out which board this command targets
39
+ const scoped = await utility.resolveBoard(workspace, args);
40
+ if (scoped === null) {
38
41
  return;
39
42
  }
43
+ kanbn = scoped;
40
44
 
41
45
  // Check if we're listing archived tasks
42
46
  if (args.list) {
@@ -1,12 +1,12 @@
1
- const kanbn = require('../main');
1
+ const workspace = require('../main');
2
2
  const utility = require('../utility');
3
3
  const board = require('../board');
4
4
 
5
5
  module.exports = async args => {
6
6
 
7
- // Make sure kanbn has been initialised
8
- if (!await kanbn.initialised()) {
9
- utility.error('Kanbn has not been initialised in this folder\nTry running: {b}kanbn init{b}');
7
+ // Work out which board we're showing
8
+ const kanbn = await utility.resolveBoard(workspace, args);
9
+ if (kanbn === null) {
10
10
  return;
11
11
  }
12
12
 
@@ -32,11 +32,10 @@ module.exports = async args => {
32
32
  // Show the board
33
33
  board
34
34
  .show(index, tasks, args.view, args.json)
35
- .then(() => {
36
- utility.showDateDriftHint(
37
- kanbn.getDateDrift(index, tasks),
38
- !args.json && kanbn.isVerbose(index)
39
- );
35
+ .then(async () => {
36
+ const verbose = !args.json && kanbn.isVerbose(index);
37
+ utility.showDateDriftHint(kanbn.getDateDrift(index, tasks), verbose);
38
+ utility.showBoardCountHint(await workspace.listBoards(), verbose);
40
39
  })
41
40
  .catch(error => {
42
41
  utility.error(error);
@@ -0,0 +1,140 @@
1
+ const kanbn = require('../main');
2
+ const utility = require('../utility');
3
+
4
+ /**
5
+ * Show the list of boards in this workspace
6
+ * @param {object[]} boards
7
+ * @param {boolean} json
8
+ */
9
+ function showBoards(boards, json) {
10
+ if (json) {
11
+ console.log(JSON.stringify(boards, null, 2));
12
+ return;
13
+ }
14
+ console.log(`${boards.length} ${boards.length === 1 ? 'board' : 'boards'}:`);
15
+ for (const board of boards) {
16
+ console.log(utility.replaceTags(`\n{b}${board.slug}{b}${board.main ? ' (main board)' : ''}`));
17
+ console.log(` ${board.name}`);
18
+ if (board.description) {
19
+ console.log(` ${board.description.split('\n')[0]}`);
20
+ }
21
+ console.log(
22
+ ` ${board.columns} ${board.columns === 1 ? 'column' : 'columns'}, ` +
23
+ `${board.tasks} ${board.tasks === 1 ? 'task' : 'tasks'}, ` +
24
+ `${board.completedPercentage}% complete` +
25
+ (board.modified ? `, last modified ${board.modified.toISOString()}` : '')
26
+ );
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Show which boards each task appears on
32
+ * @param {object[]} tasks
33
+ * @param {boolean} all True if single-board tasks are included
34
+ * @param {boolean} json
35
+ */
36
+ function showCrossBoardTasks(tasks, all, json) {
37
+ if (json) {
38
+ console.log(JSON.stringify(tasks, null, 2));
39
+ return;
40
+ }
41
+ if (!tasks.length) {
42
+ console.log(all ? 'No tracked tasks found' : 'No tasks appear on more than one board');
43
+ return;
44
+ }
45
+ console.log(
46
+ `${tasks.length} ${tasks.length === 1 ? 'task' : 'tasks'}` +
47
+ (all ? ':' : ' on more than one board:')
48
+ );
49
+ for (const task of tasks) {
50
+ console.log(utility.replaceTags(`\n{b}${task.id}{b}`));
51
+ for (const [slug, columnName] of Object.entries(task.boards)) {
52
+ console.log(` ${slug}: ${columnName}`);
53
+ }
54
+ }
55
+ }
56
+
57
+ module.exports = async args => {
58
+
59
+ // Make sure kanbn has been initialised
60
+ if (!await kanbn.workspaceInitialised()) {
61
+ utility.error('Kanbn has not been initialised in this folder\nTry running: {b}kanbn init{b}');
62
+ return;
63
+ }
64
+
65
+ // Delete a board
66
+ if (args.delete) {
67
+ const slug = utility.strArg(args.delete);
68
+ let orphaned;
69
+ try {
70
+ orphaned = await kanbn.findOrphanedTasks(slug);
71
+ } catch (error) {
72
+ utility.error(error);
73
+ return;
74
+ }
75
+
76
+ // Deleting a board never deletes task files, but it can leave tasks with no board at all
77
+ if (orphaned.length && !args.force) {
78
+ utility.error(
79
+ `Deleting board "${slug}" would leave ${orphaned.length} ` +
80
+ `${orphaned.length === 1 ? 'task' : 'tasks'} untracked:\n` +
81
+ orphaned.map(taskId => ` ${taskId}`).join('\n') +
82
+ '\nUse {b}-f{b} to delete the board anyway'
83
+ );
84
+ return;
85
+ }
86
+ try {
87
+ await kanbn.deleteBoard(slug);
88
+ } catch (error) {
89
+ utility.error(error);
90
+ return;
91
+ }
92
+ console.log(
93
+ `Deleted board "${slug}"` +
94
+ (orphaned.length
95
+ ? `\n${orphaned.length} ${orphaned.length === 1 ? 'task is' : 'tasks are'} now untracked:\n` +
96
+ orphaned.map(taskId => ` ${taskId}`).join('\n')
97
+ : '')
98
+ );
99
+ return;
100
+ }
101
+
102
+ // Rename a board, i.e. kanbn boards --rename design ux --name "UX Pipeline"
103
+ if (args.rename) {
104
+ const slug = utility.strArg(args.rename);
105
+ const newSlug = args._[1] || slug;
106
+ const newName = args.name ? utility.strArg(args.name) : null;
107
+ if (newSlug === slug && newName === null) {
108
+ utility.error('No new board slug or name specified\nTry running {b}kanbn boards --rename "board-slug" "new-slug"{b}');
109
+ return;
110
+ }
111
+ try {
112
+ const renamedSlug = await kanbn.renameBoard(slug, newSlug, newName);
113
+ console.log(
114
+ renamedSlug === slug
115
+ ? `Renamed board "${slug}" to "${newName}"`
116
+ : `Renamed board "${slug}" to "${renamedSlug}"`
117
+ );
118
+ } catch (error) {
119
+ utility.error(error);
120
+ }
121
+ return;
122
+ }
123
+
124
+ // Show which boards each task appears on
125
+ if (args.tasks) {
126
+ try {
127
+ showCrossBoardTasks(await kanbn.getCrossBoardTasks(args.all), args.all, args.json);
128
+ } catch (error) {
129
+ utility.error(error);
130
+ }
131
+ return;
132
+ }
133
+
134
+ // List boards
135
+ try {
136
+ showBoards(await kanbn.getBoardsSummary(), args.json);
137
+ } catch (error) {
138
+ utility.error(error);
139
+ }
140
+ };
@@ -1,10 +1,13 @@
1
- const kanbn = require('../main');
1
+ const workspace = require('../main');
2
2
  const utility = require('../utility');
3
3
  const asciichart = require('asciichart');
4
4
  const term = require('terminal-kit').terminal;
5
5
  const chrono = require('chrono-node');
6
6
  const formatDate = require('dateformat');
7
7
 
8
+ // The board this command targets - set from --board before anything else runs
9
+ let kanbn = workspace;
10
+
8
11
  const getLabelPlacements = (from, to, width, dateFormat, maxLabels) => {
9
12
  for (let count = maxLabels; count >= 1; count--) {
10
13
  const placements = [];
@@ -62,11 +65,12 @@ const renderXAxisLabels = (from, to, width, dateFormat, leftPadding) => {
62
65
 
63
66
  module.exports = async args => {
64
67
 
65
- // Make sure kanbn has been initialised
66
- if (!await kanbn.initialised()) {
67
- utility.error('Kanbn has not been initialised in this folder\nTry running: {b}kanbn init{b}');
68
+ // Work out which board this command targets
69
+ const scoped = await utility.resolveBoard(workspace, args);
70
+ if (scoped === null) {
68
71
  return;
69
72
  }
73
+ kanbn = scoped;
70
74
  const index = await kanbn.getIndex();
71
75
 
72
76
  // Get sprint numbers or names
@@ -127,7 +131,7 @@ module.exports = async args => {
127
131
 
128
132
  // Render chart
129
133
  const PADDING = ' ';
130
- const width = Math.max(1, term.width - (PADDING.length + 1));
134
+ const width = Math.max(1, utility.terminalWidth(term.width) - (PADDING.length + 1));
131
135
 
132
136
  const plots = [];
133
137
  for (const s of data.series) {
@@ -1,7 +1,10 @@
1
- const kanbn = require('../main');
1
+ const workspace = require('../main');
2
2
  const utility = require('../utility');
3
3
  const inquirer = require('inquirer');
4
- const getGitUsername = require('git-user-name');
4
+ const getGitUsername = require('../git-user-name');
5
+
6
+ // The board this command targets - set from --board before anything else runs
7
+ let kanbn = workspace;
5
8
 
6
9
  /**
7
10
  * Add a comment interactively
@@ -51,11 +54,12 @@ function addComment(taskId, text, author) {
51
54
 
52
55
  module.exports = async args => {
53
56
 
54
- // Make sure kanbn has been initialised
55
- if (!await kanbn.initialised()) {
56
- utility.error('Kanbn has not been initialised in this folder\nTry running: {b}kanbn init{b}');
57
+ // Work out which board this command targets
58
+ const scoped = await utility.resolveBoard(workspace, args);
59
+ if (scoped === null) {
57
60
  return;
58
61
  }
62
+ kanbn = scoped;
59
63
 
60
64
  // Get the task that we're add a comment to
61
65
  const taskId = args._[1];
@@ -1,9 +1,12 @@
1
- const kanbn = require('../main');
1
+ const workspace = require('../main');
2
2
  const utility = require('../utility');
3
3
  const inquirer = require('inquirer');
4
4
  const fuzzy = require('fuzzy');
5
5
  const chrono = require('chrono-node');
6
- const getGitUsername = require('git-user-name');
6
+ const getGitUsername = require('../git-user-name');
7
+
8
+ // The board this command targets - set from --board before anything else runs
9
+ let kanbn = workspace;
7
10
 
8
11
  inquirer.registerPrompt('datepicker', require('inquirer-datepicker'));
9
12
  inquirer.registerPrompt('recursive', require('inquirer-recursive'));
@@ -455,11 +458,12 @@ function updateTask(taskId, taskData, columnName, unsetFields = []) {
455
458
 
456
459
  module.exports = async args => {
457
460
 
458
- // Make sure kanbn has been initialised
459
- if (!await kanbn.initialised()) {
460
- utility.error('Kanbn has not been initialised in this folder\nTry running: {b}kanbn init{b}');
461
+ // Work out which board this command targets
462
+ const scoped = await utility.resolveBoard(workspace, args);
463
+ if (scoped === null) {
461
464
  return;
462
465
  }
466
+ kanbn = scoped;
463
467
 
464
468
  // Get the task that we're editing
465
469
  const taskId = args._[1];
@@ -1,9 +1,12 @@
1
- const kanbn = require('../main');
1
+ const workspace = require('../main');
2
2
  const utility = require('../utility');
3
3
  const inquirer = require('inquirer');
4
4
  const chrono = require('chrono-node');
5
5
  const yaml = require('yamljs');
6
6
 
7
+ // The board this command targets - set from --board before anything else runs
8
+ let kanbn = workspace;
9
+
7
10
  inquirer.registerPrompt('recursive', require('inquirer-recursive'));
8
11
 
9
12
  const searchFields = [
@@ -213,12 +216,11 @@ async function interactive() {
213
216
  * @param {boolean} quiet
214
217
  * @param {boolean} json
215
218
  */
216
- function findTasks(filters, quiet, json) {
219
+ function findTasks(filters, quiet, json, allBoards = false) {
217
220
  const removeEmptyProperties = o => Object.fromEntries(Object.entries(o).filter(
218
221
  ([k, v]) => !(Array.isArray(v) && v.length == 0) && !!v
219
222
  ));
220
- kanbn
221
- .search(filters, quiet)
223
+ (allBoards ? kanbn.searchAllBoards(filters, quiet) : kanbn.search(filters, quiet))
222
224
  .then(results => {
223
225
  if (quiet) {
224
226
  console.log(json ? JSON.stringify(results, null, 2) : results.join('\n'));
@@ -309,11 +311,12 @@ function convertDateFilters(filters, filterName) {
309
311
 
310
312
  module.exports = async args => {
311
313
 
312
- // Make sure kanbn has been initialised
313
- if (!await kanbn.initialised()) {
314
- utility.error('Kanbn has not been initialised in this folder\nTry running: {b}kanbn init{b}');
314
+ // Work out which board this command targets
315
+ const scoped = await utility.resolveBoard(workspace, args);
316
+ if (scoped === null) {
315
317
  return;
316
318
  }
319
+ kanbn = scoped;
317
320
 
318
321
  // Get the index and make sure it has some columns
319
322
  let index;
@@ -487,7 +490,7 @@ module.exports = async args => {
487
490
  filter.value
488
491
  );
489
492
  }
490
- findTasks(filters, !otherAnswers.nonquiet, otherAnswers.json);
493
+ findTasks(filters, !otherAnswers.nonquiet, otherAnswers.json, !!args['all-boards']);
491
494
  })
492
495
  .catch(error => {
493
496
  utility.error(error);
@@ -499,6 +502,6 @@ module.exports = async args => {
499
502
 
500
503
  // Otherwise create task non-interactively
501
504
  } else {
502
- findTasks(filters, args.quiet, args.json);
505
+ findTasks(filters, args.quiet, args.json, !!args['all-boards']);
503
506
  }
504
507
  };
@@ -1,8 +1,11 @@
1
- const kanbn = require('../main');
1
+ const workspace = require('../main');
2
2
  const utility = require('../utility');
3
3
  const formatDate = require('dateformat');
4
4
  const chrono = require('chrono-node');
5
5
 
6
+ // The board this command targets - set from --board before anything else runs
7
+ let kanbn = workspace;
8
+
6
9
  const NOW_MARKER = '┆';
7
10
  const ANSI_RESET = '\x1b[0m';
8
11
  const ANSI_FG_LINE = '\x1b[97m';
@@ -313,11 +316,12 @@ function truncateName(name, maxWidth) {
313
316
 
314
317
  module.exports = async args => {
315
318
 
316
- // Make sure kanbn has been initialised
317
- if (!await kanbn.initialised()) {
318
- utility.error('Kanbn has not been initialised in this folder\nTry running: {b}kanbn init{b}');
319
+ // Work out which board this command targets
320
+ const scoped = await utility.resolveBoard(workspace, args);
321
+ if (scoped === null) {
319
322
  return;
320
323
  }
324
+ kanbn = scoped;
321
325
  const index = await kanbn.getIndex();
322
326
 
323
327
  // Get assigned
@@ -1,8 +1,11 @@
1
- const kanbn = require('../main');
1
+ const workspace = require('../main');
2
2
  const utility = require('../utility');
3
3
  const chrono = require('chrono-node');
4
4
  const formatDate = require('dateformat');
5
5
 
6
+ // The board this command targets - set from --board before anything else runs
7
+ let kanbn = workspace;
8
+
6
9
  function formatHistoryEvent(event) {
7
10
  const details = [];
8
11
  if ('column' in event.event) {
@@ -29,11 +32,12 @@ function formatMetadataEvent(event, dateFormat) {
29
32
 
30
33
  module.exports = async args => {
31
34
 
32
- // Make sure kanbn has been initialised
33
- if (!await kanbn.initialised()) {
34
- utility.error('Kanbn has not been initialised in this folder\nTry running: {b}kanbn init{b}');
35
+ // Work out which board this command targets
36
+ const scoped = await utility.resolveBoard(workspace, args);
37
+ if (scoped === null) {
35
38
  return;
36
39
  }
40
+ kanbn = scoped;
37
41
  const index = await kanbn.getIndex();
38
42
 
39
43
  // Get sprint numbers or names