open-wadah 1.1.0 → 1.2.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 +6 -4
- package/cli.js +178 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,8 +27,9 @@ wadah complete <task-id>
|
|
|
27
27
|
| Command | Description |
|
|
28
28
|
|--------|-------------|
|
|
29
29
|
| **Auth** | `login`, `signup`, `logout`, `whoami` |
|
|
30
|
-
| **Tasks** | `open`, `list`, `requested`, `add`, `complete`, `reopen`, `view`, `update`, `move`, `assign`, `comment`, `delete` |
|
|
31
|
-
| **
|
|
30
|
+
| **Tasks** | `open`, `list`, `search`, `requested`, `add`, `complete`, `reopen`, `view`, `update`, `move`, `assign`, `comment`, `delete` |
|
|
31
|
+
| **Relationships** | `add --blocks <id>`, `add --blocked-by <id>`, `update --blocks <id>` |
|
|
32
|
+
| **Board** | `boards --json`, `buckets --json`, `assignees --json`; `board create/delete`, `bucket create/update/delete`, `assignee create/update/delete` |
|
|
32
33
|
| **Files** | `folders`, `files`, `folder create` / `mkdir`, `upload` |
|
|
33
34
|
| **Calendar** | `calendar` (list; `--assignee`, `--task`, `--from`, `--to`), `calendar add`, `calendar update <id>`, `calendar delete <id>` |
|
|
34
35
|
| **Docs** | `docs` (list), `doc create [title]`, `doc show <id>`, `doc update <id>`, `doc delete <id>` |
|
|
@@ -56,10 +57,11 @@ source ~/.zshrc
|
|
|
56
57
|
|
|
57
58
|
## Agent / AI use
|
|
58
59
|
|
|
59
|
-
- Set `TASK_MANAGER_TOKEN` (create a token
|
|
60
|
-
- Use `--json` for machine-readable output: `wadah open --json`, `wadah list --json`.
|
|
60
|
+
- Set `TASK_MANAGER_TOKEN` (create a token: `wadah agent-token create "My Agent"`).
|
|
61
|
+
- Use `--json` for machine-readable output: `wadah open --json`, `wadah list --json`, `wadah buckets --json`.
|
|
61
62
|
- Natural language: `wadah do "add a task to fix the bug"` (requires `OPENAI_API_KEY`).
|
|
62
63
|
- In Cursor/Claude/Kimi: see **WADAH_CLI.md** in the repo root for a short reference.
|
|
64
|
+
- For autonomous 24/7 agents with GitHub Actions: see **AGENTS.md** in the repo root.
|
|
63
65
|
|
|
64
66
|
## Global flags
|
|
65
67
|
|
package/cli.js
CHANGED
|
@@ -181,8 +181,8 @@ function printTaskList(tasks, state) {
|
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
function handleError(err) {
|
|
184
|
-
const msg
|
|
185
|
-
const code = err.code
|
|
184
|
+
const msg = err.message ?? 'Unexpected error'
|
|
185
|
+
const code = err.code ?? 'unknown_error'
|
|
186
186
|
const authLike =
|
|
187
187
|
code === 'auth_error' ||
|
|
188
188
|
msg.toLowerCase().includes('missing token') ||
|
|
@@ -195,6 +195,17 @@ function handleError(err) {
|
|
|
195
195
|
console.error(chalk.red('\n✗ Not authenticated.'))
|
|
196
196
|
console.error(chalk.gray(' Humans: wadah login'))
|
|
197
197
|
console.error(chalk.gray(' Agents: TASK_MANAGER_TOKEN=<token> wadah open\n'))
|
|
198
|
+
} else if (code === 'network_error') {
|
|
199
|
+
console.error(chalk.red('\n✗ Network error — could not reach the API.'))
|
|
200
|
+
console.error(chalk.gray(` Check your connection or API URL: ${getApiBase()}`))
|
|
201
|
+
console.error(chalk.gray(' Run: wadah doctor\n'))
|
|
202
|
+
} else if (code === 'server_error') {
|
|
203
|
+
console.error(chalk.red('\n✗ Server error — something went wrong on the API side.'))
|
|
204
|
+
console.error(chalk.gray(' Try again in a moment. If it persists, check api.openwadah.com status.\n'))
|
|
205
|
+
} else if (code === 'api_error' && msg.toLowerCase().includes('not found')) {
|
|
206
|
+
console.error(chalk.red('\n✗ Not found.'))
|
|
207
|
+
console.error(chalk.gray(' The resource may not exist, or this endpoint may not be available for agent tokens.'))
|
|
208
|
+
console.error(chalk.gray(' Run: wadah doctor\n'))
|
|
198
209
|
} else {
|
|
199
210
|
console.error(chalk.red(`\n✗ ${msg}\n`))
|
|
200
211
|
}
|
|
@@ -262,6 +273,13 @@ function isMachineOutput() {
|
|
|
262
273
|
return runtimeJsonOutput || runtimeQuietOutput || !process.stdout.isTTY
|
|
263
274
|
}
|
|
264
275
|
|
|
276
|
+
// ── session state cache ───────────────────────────────────────────────────────
|
|
277
|
+
let _cachedState = null
|
|
278
|
+
async function getState() {
|
|
279
|
+
if (!_cachedState) _cachedState = await api('/api/state')
|
|
280
|
+
return _cachedState
|
|
281
|
+
}
|
|
282
|
+
|
|
265
283
|
function sleep(ms) {
|
|
266
284
|
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
267
285
|
}
|
|
@@ -684,7 +702,7 @@ program
|
|
|
684
702
|
.option('--completed', 'Show completed tasks instead')
|
|
685
703
|
.action(async (opts) => {
|
|
686
704
|
try {
|
|
687
|
-
const state = await
|
|
705
|
+
const state = await getState()
|
|
688
706
|
let tasks = state.tasks.filter((t) => !!t.completed === !!opts.completed)
|
|
689
707
|
|
|
690
708
|
if (!opts.all && !opts.assignee) {
|
|
@@ -723,7 +741,7 @@ program
|
|
|
723
741
|
if (opts.open && opts.completed) {
|
|
724
742
|
return fail('Use either --open or --completed, not both.')
|
|
725
743
|
}
|
|
726
|
-
const state = await
|
|
744
|
+
const state = await getState()
|
|
727
745
|
let tasks = state.tasks
|
|
728
746
|
if (opts.open) tasks = tasks.filter((t) => !t.completed)
|
|
729
747
|
if (opts.completed) tasks = tasks.filter((t) => !!t.completed)
|
|
@@ -748,6 +766,48 @@ program
|
|
|
748
766
|
} catch (err) { handleError(err) }
|
|
749
767
|
})
|
|
750
768
|
|
|
769
|
+
// ── tm search ─────────────────────────────────────────────────────────────────
|
|
770
|
+
|
|
771
|
+
program
|
|
772
|
+
.command('search <query>')
|
|
773
|
+
.description('Search tasks by title, notes or tags')
|
|
774
|
+
.option('--board <name>', 'Limit search to a specific board')
|
|
775
|
+
.option('--assignee <name>', 'Limit search to a specific assignee')
|
|
776
|
+
.option('--open', 'Only open tasks (default)')
|
|
777
|
+
.option('--all', 'Include completed tasks')
|
|
778
|
+
.action(async (query, opts) => {
|
|
779
|
+
try {
|
|
780
|
+
const state = await getState()
|
|
781
|
+
const q = query.toLowerCase()
|
|
782
|
+
let tasks = state.tasks
|
|
783
|
+
|
|
784
|
+
if (!opts.all) tasks = tasks.filter((t) => !t.completed)
|
|
785
|
+
if (opts.board) {
|
|
786
|
+
const b = findBoard(state.boards, opts.board)
|
|
787
|
+
if (!b) return fail(`Board not found: ${opts.board}`)
|
|
788
|
+
tasks = tasks.filter((t) => t.boardId === b.id)
|
|
789
|
+
}
|
|
790
|
+
if (opts.assignee) {
|
|
791
|
+
const a = findAssignee(state.assignees, opts.assignee)
|
|
792
|
+
if (!a) return fail(`Assignee not found: ${opts.assignee}`)
|
|
793
|
+
tasks = tasks.filter((t) => t.assigneeId === a.id)
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
tasks = tasks.filter((t) =>
|
|
797
|
+
t.title.toLowerCase().includes(q) ||
|
|
798
|
+
(t.notes ?? '').toLowerCase().includes(q) ||
|
|
799
|
+
(t.description ?? '').toLowerCase().includes(q) ||
|
|
800
|
+
(t.tags ?? []).some((tag) => tag.toLowerCase().includes(q))
|
|
801
|
+
)
|
|
802
|
+
|
|
803
|
+
if (!isMachineOutput() && tasks.length === 0) {
|
|
804
|
+
console.log(chalk.gray(`\n No tasks matching "${query}".\n`))
|
|
805
|
+
return
|
|
806
|
+
}
|
|
807
|
+
printTaskList(tasks, state)
|
|
808
|
+
} catch (err) { handleError(err) }
|
|
809
|
+
})
|
|
810
|
+
|
|
751
811
|
// ── tm requested ──────────────────────────────────────────────────────────────
|
|
752
812
|
|
|
753
813
|
program
|
|
@@ -755,7 +815,7 @@ program
|
|
|
755
815
|
.description('List open tasks you requested (assigned to others / agents)')
|
|
756
816
|
.action(async () => {
|
|
757
817
|
try {
|
|
758
|
-
const state = await
|
|
818
|
+
const state = await getState()
|
|
759
819
|
const myId = await resolveMyAssigneeId(state)
|
|
760
820
|
const tasks = state.tasks.filter((t) => t.requestedById === myId && !t.completed)
|
|
761
821
|
printTaskList(tasks, state)
|
|
@@ -774,9 +834,11 @@ program
|
|
|
774
834
|
.option('--repo <owner/repo>','GitHub repo')
|
|
775
835
|
.option('--url <url>', 'Issue / PR URL')
|
|
776
836
|
.option('--notes <text>', 'Notes')
|
|
837
|
+
.option('--blocks <id>', 'This task blocks another task (provide task id)')
|
|
838
|
+
.option('--blocked-by <id>', 'This task is blocked by another task (provide task id)')
|
|
777
839
|
.action(async (title, opts) => {
|
|
778
840
|
try {
|
|
779
|
-
const state = await
|
|
841
|
+
const state = await getState()
|
|
780
842
|
const myId = await resolveMyAssigneeId(state)
|
|
781
843
|
|
|
782
844
|
let assigneeId = myId
|
|
@@ -789,14 +851,19 @@ program
|
|
|
789
851
|
requestedById = myId
|
|
790
852
|
}
|
|
791
853
|
|
|
792
|
-
const bucket = opts.bucket
|
|
793
|
-
? state.buckets.find((b) => b.title.toLowerCase().includes(opts.bucket.toLowerCase()))
|
|
794
|
-
: state.buckets[0]
|
|
795
|
-
|
|
796
854
|
const board = opts.board
|
|
797
855
|
? state.boards.find((b) => b.name.toLowerCase().includes(opts.board.toLowerCase()))
|
|
798
856
|
: state.boards.find((b) => b.id === state.currentBoardId) ?? state.boards[0]
|
|
799
857
|
|
|
858
|
+
if (!board && opts.board) return fail(`Board not found: ${opts.board}`)
|
|
859
|
+
if (!board) console.warn(chalk.yellow(` ⚠ No board specified — defaulting to "${state.boards[0]?.name}". Set a default with: wadah config --default-board "<name>"`))
|
|
860
|
+
|
|
861
|
+
const boardBuckets = board ? state.buckets.filter((b) => b.boardId === board.id) : state.buckets
|
|
862
|
+
const bucket = opts.bucket
|
|
863
|
+
? boardBuckets.find((b) => b.title.toLowerCase().includes(opts.bucket.toLowerCase()))
|
|
864
|
+
?? state.buckets.find((b) => b.title.toLowerCase().includes(opts.bucket.toLowerCase()))
|
|
865
|
+
: boardBuckets[0] ?? state.buckets[0]
|
|
866
|
+
|
|
800
867
|
const task = await api('/api/tasks', {
|
|
801
868
|
method: 'POST',
|
|
802
869
|
body: JSON.stringify({
|
|
@@ -809,9 +876,27 @@ program
|
|
|
809
876
|
repo: opts.repo ?? null,
|
|
810
877
|
contextUrl: opts.url ?? null,
|
|
811
878
|
notes: opts.notes ?? '',
|
|
879
|
+
tags: [
|
|
880
|
+
...(opts.blocks ? [`blocks:${opts.blocks.slice(0, 8)}`] : []),
|
|
881
|
+
...(opts.blockedBy ? [`blocked-by:${opts.blockedBy.slice(0, 8)}`] : []),
|
|
882
|
+
],
|
|
812
883
|
}),
|
|
813
884
|
})
|
|
814
885
|
|
|
886
|
+
// If this task blocks another, tag the other task back
|
|
887
|
+
if (opts.blocks) {
|
|
888
|
+
try {
|
|
889
|
+
const other = await api(`/api/tasks/${opts.blocks}`)
|
|
890
|
+
const existingTags = other.tags ?? []
|
|
891
|
+
if (!existingTags.some((t) => t.startsWith('blocked-by:'))) {
|
|
892
|
+
await api(`/api/tasks/${opts.blocks}`, {
|
|
893
|
+
method: 'PATCH',
|
|
894
|
+
body: JSON.stringify({ tags: [...existingTags, `blocked-by:${task.id.slice(0, 8)}`] }),
|
|
895
|
+
})
|
|
896
|
+
}
|
|
897
|
+
} catch {}
|
|
898
|
+
}
|
|
899
|
+
|
|
815
900
|
const assigneeName = state.assignees.find((a) => a.id === task.assigneeId)?.name ?? 'Unassigned'
|
|
816
901
|
console.log(chalk.green('\n✓ Created') + ` ${chalk.bold(task.title)}`)
|
|
817
902
|
console.log(chalk.gray(` ID: ${task.id} · Assigned to: ${assigneeName}\n`))
|
|
@@ -856,7 +941,7 @@ program
|
|
|
856
941
|
.requiredOption('--to <name>', 'Assignee name')
|
|
857
942
|
.action(async (id, opts) => {
|
|
858
943
|
try {
|
|
859
|
-
const state = await
|
|
944
|
+
const state = await getState()
|
|
860
945
|
const a = findAssignee(state.assignees, opts.to)
|
|
861
946
|
if (!a) return fail(`Assignee not found: ${opts.to}`)
|
|
862
947
|
|
|
@@ -874,14 +959,18 @@ program
|
|
|
874
959
|
.description('Move a task to another column')
|
|
875
960
|
.action(async (id, bucketQuery) => {
|
|
876
961
|
try {
|
|
877
|
-
const state = await
|
|
962
|
+
const state = await getState()
|
|
878
963
|
const bucket = findBucket(state.buckets, bucketQuery)
|
|
879
964
|
if (!bucket) return fail(`Bucket not found: ${bucketQuery}`)
|
|
965
|
+
const patch = { bucketId: bucket.id }
|
|
966
|
+
if (bucket.boardId) patch.boardId = bucket.boardId
|
|
880
967
|
const task = await api(`/api/tasks/${id}`, {
|
|
881
968
|
method: 'PATCH',
|
|
882
|
-
body: JSON.stringify(
|
|
969
|
+
body: JSON.stringify(patch),
|
|
883
970
|
})
|
|
884
|
-
|
|
971
|
+
const boardName = state.boards.find((b) => b.id === bucket.boardId)?.name
|
|
972
|
+
const dest = boardName ? `${boardName} / ${bucket.title}` : bucket.title
|
|
973
|
+
console.log(chalk.green('\n✓ Moved') + ` ${chalk.bold(task.title)} → ${dest}\n`)
|
|
885
974
|
} catch (err) { handleError(err) }
|
|
886
975
|
})
|
|
887
976
|
|
|
@@ -899,6 +988,8 @@ program
|
|
|
899
988
|
.option('--board <name>', 'Set board')
|
|
900
989
|
.option('--bucket <name>', 'Set bucket')
|
|
901
990
|
.option('--completed <true|false>', 'Set completion state')
|
|
991
|
+
.option('--blocks <id>', 'Mark this task as blocking another task')
|
|
992
|
+
.option('--blocked-by <id>', 'Mark this task as blocked by another task')
|
|
902
993
|
.action(async (id, opts) => {
|
|
903
994
|
try {
|
|
904
995
|
const patch = {}
|
|
@@ -923,7 +1014,8 @@ program
|
|
|
923
1014
|
if (opts.clearDue) patch.dueDate = null
|
|
924
1015
|
|
|
925
1016
|
const needsStateLookup = !!(opts.assignee || opts.board || opts.bucket)
|
|
926
|
-
const state = needsStateLookup ? await
|
|
1017
|
+
const state = needsStateLookup ? await getState() : null
|
|
1018
|
+
|
|
927
1019
|
|
|
928
1020
|
if (opts.assignee) {
|
|
929
1021
|
const a = findAssignee(state.assignees, opts.assignee)
|
|
@@ -936,12 +1028,32 @@ program
|
|
|
936
1028
|
patch.boardId = b.id
|
|
937
1029
|
}
|
|
938
1030
|
if (opts.bucket) {
|
|
939
|
-
const
|
|
1031
|
+
const targetBoardId = patch.boardId ?? null
|
|
1032
|
+
const scopedBuckets = targetBoardId
|
|
1033
|
+
? state.buckets.filter((b) => b.boardId === targetBoardId)
|
|
1034
|
+
: state.buckets
|
|
1035
|
+
const b = scopedBuckets.find((b) => b.id === opts.bucket || b.title.toLowerCase().includes(opts.bucket.toLowerCase()))
|
|
1036
|
+
?? state.buckets.find((b) => b.id === opts.bucket || b.title.toLowerCase().includes(opts.bucket.toLowerCase()))
|
|
940
1037
|
if (!b) return fail(`Bucket not found: ${opts.bucket}`)
|
|
941
1038
|
patch.bucketId = b.id
|
|
1039
|
+
if (!patch.boardId && b.boardId) patch.boardId = b.boardId
|
|
942
1040
|
}
|
|
943
1041
|
if (opts.completed !== undefined) patch.completed = parsedCompleted
|
|
944
1042
|
|
|
1043
|
+
// Handle relationship tags (blocks / blocked-by)
|
|
1044
|
+
if (opts.blocks || opts.blockedBy) {
|
|
1045
|
+
const currentTask = await api(`/api/tasks/${id}`)
|
|
1046
|
+
const currentTags = currentTask.tags ?? []
|
|
1047
|
+
const newTags = [...currentTags]
|
|
1048
|
+
if (opts.blocks && !newTags.some((t) => t.startsWith('blocks:'))) {
|
|
1049
|
+
newTags.push(`blocks:${opts.blocks.slice(0, 8)}`)
|
|
1050
|
+
}
|
|
1051
|
+
if (opts.blockedBy && !newTags.some((t) => t.startsWith('blocked-by:'))) {
|
|
1052
|
+
newTags.push(`blocked-by:${opts.blockedBy.slice(0, 8)}`)
|
|
1053
|
+
}
|
|
1054
|
+
patch.tags = newTags
|
|
1055
|
+
}
|
|
1056
|
+
|
|
945
1057
|
if (Object.keys(patch).length === 0) {
|
|
946
1058
|
return fail('Nothing to update. Run wadah update --help')
|
|
947
1059
|
}
|
|
@@ -989,7 +1101,17 @@ program
|
|
|
989
1101
|
if (task.dueDate) console.log(chalk.gray(` Due: ${formatDate(task.dueDate)}`))
|
|
990
1102
|
if (task.repo) console.log(chalk.gray(` Repo: ${task.repo}`))
|
|
991
1103
|
if (task.contextUrl) console.log(chalk.gray(` Link: ${task.contextUrl}`))
|
|
992
|
-
if (task.tags?.length)
|
|
1104
|
+
if (task.tags?.length) {
|
|
1105
|
+
const relTags = task.tags.filter((t) => t.startsWith('blocks:') || t.startsWith('blocked-by:'))
|
|
1106
|
+
const plainTags = task.tags.filter((t) => !t.startsWith('blocks:') && !t.startsWith('blocked-by:'))
|
|
1107
|
+
if (plainTags.length) console.log(chalk.gray(` Tags: #${plainTags.join(' #')}`))
|
|
1108
|
+
if (relTags.length) {
|
|
1109
|
+
const blocking = relTags.filter((t) => t.startsWith('blocks:')).map((t) => t.replace('blocks:', ''))
|
|
1110
|
+
const blockedBy = relTags.filter((t) => t.startsWith('blocked-by:')).map((t) => t.replace('blocked-by:', ''))
|
|
1111
|
+
if (blocking.length) console.log(chalk.red(` Blocks: ${blocking.join(', ')}`))
|
|
1112
|
+
if (blockedBy.length) console.log(chalk.yellow(` Blocked by: ${blockedBy.join(', ')}`))
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
993
1115
|
if (task.description) { console.log(); console.log(` ${task.description}`) }
|
|
994
1116
|
if (task.notes) { console.log(chalk.gray('\n Notes:')); console.log(` ${task.notes}`) }
|
|
995
1117
|
if (task.subtasks?.length) {
|
|
@@ -1032,7 +1154,17 @@ program
|
|
|
1032
1154
|
.description('List all boards')
|
|
1033
1155
|
.action(async () => {
|
|
1034
1156
|
try {
|
|
1035
|
-
const state = await
|
|
1157
|
+
const state = await getState()
|
|
1158
|
+
if (isMachineOutput()) {
|
|
1159
|
+
const result = state.boards.map((b) => ({
|
|
1160
|
+
id: b.id,
|
|
1161
|
+
name: b.name,
|
|
1162
|
+
openCount: state.tasks.filter((t) => t.boardId === b.id && !t.completed).length,
|
|
1163
|
+
doneCount: state.tasks.filter((t) => t.boardId === b.id && t.completed).length,
|
|
1164
|
+
}))
|
|
1165
|
+
console.log(JSON.stringify(result, null, runtimeJsonOutput ? 2 : 0))
|
|
1166
|
+
return
|
|
1167
|
+
}
|
|
1036
1168
|
console.log()
|
|
1037
1169
|
state.boards.forEach((b) => {
|
|
1038
1170
|
const open = state.tasks.filter((t) => t.boardId === b.id && !t.completed).length
|
|
@@ -1048,8 +1180,20 @@ program
|
|
|
1048
1180
|
.description('List all columns (buckets)')
|
|
1049
1181
|
.action(async () => {
|
|
1050
1182
|
try {
|
|
1051
|
-
const state = await
|
|
1183
|
+
const state = await getState()
|
|
1052
1184
|
const boardMap = Object.fromEntries(state.boards.map((b) => [b.id, b.name]))
|
|
1185
|
+
if (isMachineOutput()) {
|
|
1186
|
+
const result = state.buckets.map((b) => ({
|
|
1187
|
+
id: b.id,
|
|
1188
|
+
title: b.title,
|
|
1189
|
+
boardId: b.boardId,
|
|
1190
|
+
boardName: boardMap[b.boardId] ?? '—',
|
|
1191
|
+
openCount: state.tasks.filter((t) => t.bucketId === b.id && !t.completed).length,
|
|
1192
|
+
doneCount: state.tasks.filter((t) => t.bucketId === b.id && t.completed).length,
|
|
1193
|
+
}))
|
|
1194
|
+
console.log(JSON.stringify(result, null, runtimeJsonOutput ? 2 : 0))
|
|
1195
|
+
return
|
|
1196
|
+
}
|
|
1053
1197
|
console.log()
|
|
1054
1198
|
state.buckets.forEach((b) => {
|
|
1055
1199
|
const open = state.tasks.filter((t) => t.bucketId === b.id && !t.completed).length
|
|
@@ -1065,7 +1209,18 @@ program
|
|
|
1065
1209
|
.description('List all assignees')
|
|
1066
1210
|
.action(async () => {
|
|
1067
1211
|
try {
|
|
1068
|
-
const state = await
|
|
1212
|
+
const state = await getState()
|
|
1213
|
+
if (isMachineOutput()) {
|
|
1214
|
+
const result = state.assignees.map((a) => ({
|
|
1215
|
+
id: a.id,
|
|
1216
|
+
name: a.name,
|
|
1217
|
+
type: a.type ?? 'human',
|
|
1218
|
+
openCount: state.tasks.filter((t) => t.assigneeId === a.id && !t.completed).length,
|
|
1219
|
+
doneCount: state.tasks.filter((t) => t.assigneeId === a.id && t.completed).length,
|
|
1220
|
+
}))
|
|
1221
|
+
console.log(JSON.stringify(result, null, runtimeJsonOutput ? 2 : 0))
|
|
1222
|
+
return
|
|
1223
|
+
}
|
|
1069
1224
|
console.log()
|
|
1070
1225
|
state.assignees.forEach((a) => {
|
|
1071
1226
|
const open = state.tasks.filter((t) => t.assigneeId === a.id && !t.completed).length
|
|
@@ -1225,7 +1380,7 @@ program
|
|
|
1225
1380
|
.action(async function () {
|
|
1226
1381
|
const opts = this.opts()
|
|
1227
1382
|
try {
|
|
1228
|
-
const state = await
|
|
1383
|
+
const state = await getState()
|
|
1229
1384
|
let files = state.files ?? []
|
|
1230
1385
|
if (opts.folder) files = files.filter((f) => (f.folderId || f.folder_id) === opts.folder)
|
|
1231
1386
|
if (isMachineOutput()) {
|
|
@@ -1578,7 +1733,7 @@ program
|
|
|
1578
1733
|
.description('Print full workspace state as JSON')
|
|
1579
1734
|
.action(async () => {
|
|
1580
1735
|
try {
|
|
1581
|
-
const state = await
|
|
1736
|
+
const state = await getState()
|
|
1582
1737
|
console.log(JSON.stringify(state, null, 2))
|
|
1583
1738
|
} catch (err) { handleError(err) }
|
|
1584
1739
|
})
|
|
@@ -1739,7 +1894,7 @@ const CLI_COMMANDS = [
|
|
|
1739
1894
|
'add', 'agent-token', 'agent-tokens', 'assign', 'assignee', 'assignees', 'board', 'boards',
|
|
1740
1895
|
'bucket', 'buckets', 'calendar', 'comment', 'complete', 'config', 'delete', 'doc', 'docs',
|
|
1741
1896
|
'do', 'doctor', 'files', 'folder', 'folders', 'invite', 'list', 'login', 'members', 'mkdir',
|
|
1742
|
-
'move', 'open', 'reopen', 'requested', 'signup', 'state', 'update', 'upload', 'view', 'whoami',
|
|
1897
|
+
'move', 'open', 'reopen', 'requested', 'search', 'signup', 'state', 'update', 'upload', 'view', 'whoami',
|
|
1743
1898
|
]
|
|
1744
1899
|
|
|
1745
1900
|
program
|