harveyz-skill 0.13.0 → 0.14.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/CHANGELOG.md +21 -0
- package/bin/cli.js +28 -1
- package/lib/installer.js +15 -0
- package/package.json +3 -1
- package/skills/creative/add-todo/SKILL.md +52 -3
- package/skills/meta/npm-release/SKILL.md +15 -3
- package/skills/research/article-fetcher/references/__pycache__/article_utils.cpython-314.pyc +0 -0
- package/skills/research/youtube-learner/SKILL.md +12 -3
- package/skills/writing/doc-forge/scripts/__pycache__/md_to_pdf.cpython-314.pyc +0 -0
- package/skills-index.json +11 -1
- package/tools/hub/hub/__init__.py +0 -0
- package/tools/hub/hub/__main__.py +24 -0
- package/tools/hub/hub/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tools/hub/hub/__pycache__/__main__.cpython-314.pyc +0 -0
- package/tools/hub/hub/cli/__init__.py +14 -0
- package/tools/hub/hub/cli/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tools/hub/hub/cli/__pycache__/projects.cpython-314.pyc +0 -0
- package/tools/hub/hub/cli/__pycache__/tasks.cpython-314.pyc +0 -0
- package/tools/hub/hub/cli/projects.py +100 -0
- package/tools/hub/hub/cli/tasks.py +114 -0
- package/tools/hub/hub/core/__init__.py +0 -0
- package/tools/hub/hub/core/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tools/hub/hub/core/__pycache__/db.cpython-314.pyc +0 -0
- package/tools/hub/hub/core/__pycache__/migrate.cpython-314.pyc +0 -0
- package/tools/hub/hub/core/__pycache__/projects.cpython-314.pyc +0 -0
- package/tools/hub/hub/core/__pycache__/tasks.cpython-314.pyc +0 -0
- package/tools/hub/hub/core/db.py +46 -0
- package/tools/hub/hub/core/migrate.py +49 -0
- package/tools/hub/hub/core/projects.py +65 -0
- package/tools/hub/hub/core/tasks.py +86 -0
- package/tools/hub/hub/tui/__init__.py +0 -0
- package/tools/hub/hub/tui/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tools/hub/hub/tui/__pycache__/app.cpython-314.pyc +0 -0
- package/tools/hub/hub/tui/__pycache__/git.cpython-314.pyc +0 -0
- package/tools/hub/hub/tui/app.py +78 -0
- package/tools/hub/hub/tui/git.py +300 -0
- package/tools/hub/hub/tui/panels/__init__.py +0 -0
- package/tools/hub/hub/tui/panels/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tools/hub/hub/tui/panels/__pycache__/git.cpython-314.pyc +0 -0
- package/tools/hub/hub/tui/panels/__pycache__/projects.cpython-314.pyc +0 -0
- package/tools/hub/hub/tui/panels/__pycache__/tasks.cpython-314.pyc +0 -0
- package/tools/hub/hub/tui/panels/git.py +118 -0
- package/tools/hub/hub/tui/panels/projects.py +58 -0
- package/tools/hub/hub/tui/panels/tasks.py +139 -0
- package/tools/hub/hub.sh +12 -0
- package/tools/hub/pyproject.toml +25 -0
- package/tools/hub/tests/__init__.py +0 -0
- package/tools/hub/tests/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tools/hub/tests/__pycache__/test_cli.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/hub/tests/__pycache__/test_db.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/hub/tests/__pycache__/test_migrate.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/hub/tests/__pycache__/test_projects.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/hub/tests/__pycache__/test_tasks.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/hub/tests/__pycache__/test_tui_app.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/hub/tests/__pycache__/test_tui_git.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/hub/tests/__pycache__/test_tui_git_panel.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/hub/tests/__pycache__/test_tui_projects_panel.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/hub/tests/__pycache__/test_tui_tasks_panel.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/hub/tests/test_cli.py +93 -0
- package/tools/hub/tests/test_db.py +35 -0
- package/tools/hub/tests/test_migrate.py +102 -0
- package/tools/hub/tests/test_projects.py +50 -0
- package/tools/hub/tests/test_tasks.py +74 -0
- package/tools/hub/tests/test_tui_app.py +66 -0
- package/tools/hub/tests/test_tui_git.py +58 -0
- package/tools/hub/tests/test_tui_git_panel.py +56 -0
- package/tools/hub/tests/test_tui_projects_panel.py +63 -0
- package/tools/hub/tests/test_tui_tasks_panel.py +136 -0
- package/tools/hub/tool.json +8 -0
- package/tools/p-launch/__pycache__/p_launch.cpython-314.pyc +0 -0
- package/tools/p-launch/p_launch.py +126 -3
- package/tools/p-launch/tests/__pycache__/test_p_launch.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/p-launch/tests/__pycache__/test_p_launch.cpython-314-pytest-9.1.0.pyc +0 -0
- package/tools/p-launch/tests/test_p_launch.py +139 -0
- package/tools/todo-tool/frontend/README.md +73 -0
- package/tools/todo-tool/frontend/components.json +25 -0
- package/tools/todo-tool/frontend/eslint.config.js +22 -0
- package/tools/todo-tool/frontend/index.html +13 -0
- package/tools/todo-tool/frontend/package-lock.json +6566 -0
- package/tools/todo-tool/frontend/package.json +42 -0
- package/tools/todo-tool/frontend/postcss.config.js +6 -0
- package/tools/todo-tool/frontend/public/favicon.svg +1 -0
- package/tools/todo-tool/frontend/public/icons.svg +24 -0
- package/tools/todo-tool/frontend/src/App.css +184 -0
- package/tools/todo-tool/frontend/src/App.tsx +78 -0
- package/tools/todo-tool/frontend/src/assets/hero.png +0 -0
- package/tools/todo-tool/frontend/src/assets/react.svg +1 -0
- package/tools/todo-tool/frontend/src/assets/vite.svg +1 -0
- package/tools/todo-tool/frontend/src/components/AddTaskDialog.tsx +92 -0
- package/tools/todo-tool/frontend/src/components/FilterBar.tsx +74 -0
- package/tools/todo-tool/frontend/src/components/TaskList.tsx +35 -0
- package/tools/todo-tool/frontend/src/components/TaskRow.tsx +44 -0
- package/tools/todo-tool/frontend/src/components/ui/badge.tsx +52 -0
- package/tools/todo-tool/frontend/src/components/ui/button.tsx +58 -0
- package/tools/todo-tool/frontend/src/components/ui/dialog.tsx +159 -0
- package/tools/todo-tool/frontend/src/components/ui/input.tsx +20 -0
- package/tools/todo-tool/frontend/src/components/ui/select.tsx +197 -0
- package/tools/todo-tool/frontend/src/index.css +90 -0
- package/tools/todo-tool/frontend/src/lib/api.ts +42 -0
- package/tools/todo-tool/frontend/src/lib/utils.ts +6 -0
- package/tools/todo-tool/frontend/src/main.tsx +10 -0
- package/tools/todo-tool/frontend/tailwind.config.js +66 -0
- package/tools/todo-tool/frontend/tsconfig.app.json +32 -0
- package/tools/todo-tool/frontend/tsconfig.json +7 -0
- package/tools/todo-tool/frontend/tsconfig.node.json +24 -0
- package/tools/todo-tool/frontend/vite.config.ts +17 -0
- package/tools/todo-tool/pyproject.toml +31 -0
- package/tools/todo-tool/tests/__init__.py +0 -0
- package/tools/todo-tool/tests/__pycache__/__init__.cpython-311.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_api.cpython-311-pytest-9.0.2.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_api.cpython-311-pytest-9.0.3.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_api.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_cli.cpython-311-pytest-9.0.2.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_cli.cpython-311-pytest-9.0.3.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_cli.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_cli.cpython-314.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_db.cpython-311-pytest-9.0.2.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_db.cpython-311-pytest-9.0.3.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_db.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_models.cpython-311-pytest-9.0.2.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_models.cpython-311-pytest-9.0.3.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_models.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_parser.cpython-311-pytest-9.0.3.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_parser.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_projects_index.cpython-311-pytest-9.0.3.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_sync.cpython-311-pytest-9.0.3.pyc +0 -0
- package/tools/todo-tool/tests/__pycache__/test_sync.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tools/todo-tool/tests/test_api.py +229 -0
- package/tools/todo-tool/tests/test_cli.py +214 -0
- package/tools/todo-tool/tests/test_db.py +214 -0
- package/tools/todo-tool/tests/test_models.py +24 -0
- package/tools/todo-tool/tests/test_parser.py +120 -0
- package/tools/todo-tool/tests/test_projects_index.py +185 -0
- package/tools/todo-tool/tests/test_sync.py +101 -0
- package/tools/todo-tool/todo/__init__.py +0 -0
- package/tools/todo-tool/todo/__pycache__/__init__.cpython-311.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/cli.cpython-311.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/cli.cpython-314.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/db.cpython-311.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/db.cpython-314.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/models.cpython-311.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/models.cpython-314.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/parser.cpython-311.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/parser.cpython-314.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/projects_index.cpython-311.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/projects_index.cpython-314.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/server.cpython-311.pyc +0 -0
- package/tools/todo-tool/todo/__pycache__/server.cpython-314.pyc +0 -0
- package/tools/todo-tool/todo/cli.py +208 -0
- package/tools/todo-tool/todo/db.py +289 -0
- package/tools/todo-tool/todo/models.py +41 -0
- package/tools/todo-tool/todo/parser.py +122 -0
- package/tools/todo-tool/todo/projects_index.py +152 -0
- package/tools/todo-tool/todo/server.py +98 -0
- package/tools/todo-tool/todo/todo_format.yaml +29 -0
- package/tools/todo-tool/todo-tool.sh +23 -0
- package/tools/todo-tool/tool.json +12 -0
- package/tools/todo-tool/zshrc.snippet +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.14.1] - 2026-06-14
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- `hub` 安装失败(`source_not_found`):新增 `hub.sh` 启动脚本,安装器现在能正确找到 hub 入口
|
|
14
|
+
- `hub` tool.json `extraPaths` 路径错误(`core/cli` 不在工具根目录),修正为 `["hub", "pyproject.toml"]`
|
|
15
|
+
- `hub` 卸载后遗留 `~/.hskill/tools/hub/` Python 源码目录,将其加入 `uninstallPaths`
|
|
16
|
+
- hskill 交互流程次级 fzf 页面(action/target/scope)缺少 preview 面板,视觉风格与主列表页不一致
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- `docs/how-to/use-hub.md`:hub 人用操作指南(安装、TUI、projects/tasks 常用命令)
|
|
20
|
+
- `docs/reference/hub-reference.md`:hub CLI 完整参考(供 agent 调用,含 JSON 输出格式)
|
|
21
|
+
|
|
22
|
+
## [0.14.0] - 2026-06-14
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
- `hub` tool(`tools` bundle):个人开发者 OS,整合项目管理、git 状态追踪、任务系统,替代 p-launch + todo-tool
|
|
26
|
+
- Phase 1:core 库(SQLite DB、projects CRUD、tasks CRUD)+ CLI(`hub project`、`hub task` 子命令,支持 `--json` 输出)
|
|
27
|
+
- Phase 2:三栏 Textual TUI(ProjectsPanel | GitPanel | TasksPanel),支持键盘导航、git fetch、任务增删改、项目切换联动
|
|
28
|
+
- 首次启动自动从 todo-tool DB 迁移数据(`migrate.py`)
|
|
29
|
+
- 62 个测试覆盖 core、CLI、TUI 各层
|
|
30
|
+
|
|
10
31
|
## [0.13.0] - 2026-06-12
|
|
11
32
|
|
|
12
33
|
### Fixed
|
package/bin/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { select, input } from '@inquirer/prompts'
|
|
3
3
|
import chalk from 'chalk'
|
|
4
4
|
import { execSync, spawnSync } from 'child_process'
|
|
5
|
-
import { existsSync } from 'fs'
|
|
5
|
+
import { existsSync, writeFileSync, unlinkSync } from 'fs'
|
|
6
6
|
import { createRequire } from 'module'
|
|
7
7
|
import os from 'os'
|
|
8
8
|
import path from 'path'
|
|
@@ -629,6 +629,8 @@ function selectTargetThenScope() {
|
|
|
629
629
|
'--layout=reverse',
|
|
630
630
|
'--border=rounded',
|
|
631
631
|
'--color=header:italic:dim,prompt:cyan,pointer:cyan,hl:cyan,hl+:cyan:bold',
|
|
632
|
+
'--preview=t=$(echo {} | awk \'{print $1}\'); if [ "$t" = "all" ]; then echo ""; echo " 安装到所有平台:"; echo ""; for p in claude cursor codex openclaw hermes opencode; do printf " ~/.%s/skills/\\n" "$p"; done; else p="${HOME}/.${t}/skills/"; echo ""; echo " 安装路径:"; echo ""; printf " \\033[36m%s\\033[0m\\n" "$p"; echo ""; if [ -d "$p" ]; then echo " ✓ 目录已存在"; else echo " · 将自动新建"; fi; fi',
|
|
633
|
+
'--preview-window=right:45%:wrap',
|
|
632
634
|
], {
|
|
633
635
|
input: targetInput,
|
|
634
636
|
encoding: 'utf8',
|
|
@@ -651,6 +653,8 @@ function selectTargetThenScope() {
|
|
|
651
653
|
'--layout=reverse',
|
|
652
654
|
'--border=rounded',
|
|
653
655
|
'--color=header:italic:dim,prompt:cyan,pointer:cyan,hl:cyan,hl+:cyan:bold',
|
|
656
|
+
'--preview=s=$(echo {} | awk \'{print $1}\'); if [ "$s" = "user" ]; then echo ""; echo " ~/.{target}/skills/"; echo ""; echo " 所有项目共享,适合常用 skill"; echo ""; echo " ✓ 推荐选项"; else echo ""; echo " .{target}/skills/"; echo ""; echo " 仅当前项目可见"; echo ""; echo " 适合项目专属 skill"; fi',
|
|
657
|
+
'--preview-window=right:45%:wrap',
|
|
654
658
|
], {
|
|
655
659
|
input: 'user — 所有项目共享 (~/.{target}/skills/)\nproject — 仅当前项目 (.{target}/skills/)',
|
|
656
660
|
encoding: 'utf8',
|
|
@@ -898,17 +902,30 @@ try {
|
|
|
898
902
|
})
|
|
899
903
|
|
|
900
904
|
if (anyInstalled) {
|
|
905
|
+
const actionPreviewFile = `/tmp/hskill-action-preview-${process.pid}.txt`
|
|
906
|
+
const actionPreviewLines = [
|
|
907
|
+
'',
|
|
908
|
+
' 已选中:',
|
|
909
|
+
'',
|
|
910
|
+
...skillItems.map(s => ` skill ${s.skillName}`),
|
|
911
|
+
...toolItems.map(t => ` tool ${t.toolName}`),
|
|
912
|
+
...hookItems.map(h => ` hook ${h.name}`),
|
|
913
|
+
]
|
|
914
|
+
writeFileSync(actionPreviewFile, actionPreviewLines.join('\n'))
|
|
901
915
|
const actionResult = spawnSync('fzf', [
|
|
902
916
|
'--prompt= › ',
|
|
903
917
|
'--header= Action · enter 确认 · esc 取消',
|
|
904
918
|
'--layout=reverse',
|
|
905
919
|
'--border=rounded',
|
|
906
920
|
'--color=header:italic:dim,prompt:cyan,pointer:cyan,hl:cyan,hl+:cyan:bold',
|
|
921
|
+
`--preview=cat ${actionPreviewFile}`,
|
|
922
|
+
'--preview-window=right:45%:wrap',
|
|
907
923
|
], {
|
|
908
924
|
input: `install 安装 / 重新安装\nuninstall 卸载并清理文件`,
|
|
909
925
|
encoding: 'utf8',
|
|
910
926
|
stdio: ['pipe', 'pipe', 'inherit'],
|
|
911
927
|
})
|
|
928
|
+
try { unlinkSync(actionPreviewFile) } catch { /* ignore */ }
|
|
912
929
|
if (!actionResult.stdout.trim()) {
|
|
913
930
|
console.log(chalk.dim(' · Cancelled'))
|
|
914
931
|
break
|
|
@@ -964,6 +981,8 @@ try {
|
|
|
964
981
|
'--layout=reverse',
|
|
965
982
|
'--border=rounded',
|
|
966
983
|
'--color=header:italic:dim,prompt:cyan,pointer:cyan,hl:cyan,hl+:cyan:bold',
|
|
984
|
+
'--preview=s=$(echo {} | awk \'{print $1}\'); if [ "$s" = "user" ]; then echo ""; echo " ~/.claude/hooks/"; echo " ~/.codex/hooks/"; echo ""; echo " 所有项目共享"; else echo ""; echo " .claude/hooks/"; echo " .codex/hooks/"; echo ""; echo " 仅当前项目"; fi',
|
|
985
|
+
'--preview-window=right:45%:wrap',
|
|
967
986
|
], {
|
|
968
987
|
input: `user — ~/.{claude,codex}/hooks/ (所有项目共享)\nproject — .{claude,codex}/hooks/ (仅当前项目)`,
|
|
969
988
|
encoding: 'utf8',
|
|
@@ -983,6 +1002,8 @@ try {
|
|
|
983
1002
|
'--layout=reverse',
|
|
984
1003
|
'--border=rounded',
|
|
985
1004
|
'--color=header:italic:dim,prompt:cyan,pointer:cyan,hl:cyan,hl+:cyan:bold',
|
|
1005
|
+
'--preview=t=$(echo {} | awk \'{print $1}\'); if [ "$t" = "all" ]; then echo ""; echo " ~/.claude/hooks/"; echo " ~/.codex/hooks/"; else echo ""; printf " ~/.%s/hooks/\\n" "$t"; fi',
|
|
1006
|
+
'--preview-window=right:45%:wrap',
|
|
986
1007
|
], {
|
|
987
1008
|
input: `claude — ~/.claude/hooks/\ncodex — ~/.codex/hooks/\nall — claude + codex`,
|
|
988
1009
|
encoding: 'utf8',
|
|
@@ -1033,6 +1054,8 @@ try {
|
|
|
1033
1054
|
'--header= 从哪里卸载 · tab 多选 · enter 确认 · esc 取消',
|
|
1034
1055
|
'--layout=reverse', '--border=rounded',
|
|
1035
1056
|
'--color=header:italic:dim,prompt:cyan,pointer:cyan,hl:cyan,hl+:cyan:bold',
|
|
1057
|
+
'--preview=t=$(echo {} | awk \'{print $1}\'); if [ "$t" = "all" ]; then echo ""; echo " 从所有平台卸载:"; echo ""; for p in claude cursor codex openclaw hermes opencode; do printf " ~/.%s/skills/\\n" "$p"; done; else echo ""; printf " ~/.%s/skills/\\n" "$t"; fi',
|
|
1058
|
+
'--preview-window=right:45%:wrap',
|
|
1036
1059
|
], {
|
|
1037
1060
|
input: targetChoices2.map(c => c.name).join('\n') + '\nall — all targets',
|
|
1038
1061
|
encoding: 'utf8', stdio: ['pipe', 'pipe', 'inherit'],
|
|
@@ -1046,6 +1069,8 @@ try {
|
|
|
1046
1069
|
'--header= 卸载范围 · enter 确认 · esc 取消',
|
|
1047
1070
|
'--layout=reverse', '--border=rounded',
|
|
1048
1071
|
'--color=header:italic:dim,prompt:cyan,pointer:cyan,hl:cyan,hl+:cyan:bold',
|
|
1072
|
+
'--preview=s=$(echo {} | awk \'{print $1}\'); if [ "$s" = "user" ]; then echo ""; echo " 从 user 级卸载:"; echo " ~/.{target}/skills/"; else echo ""; echo " 从 project 级卸载:"; echo " .{target}/skills/"; fi',
|
|
1073
|
+
'--preview-window=right:45%:wrap',
|
|
1049
1074
|
], {
|
|
1050
1075
|
input: 'user — 所有项目 (~/.{target}/skills/)\nproject — 仅当前项目 (.{target}/skills/)',
|
|
1051
1076
|
encoding: 'utf8', stdio: ['pipe', 'pipe', 'inherit'],
|
|
@@ -1065,6 +1090,8 @@ try {
|
|
|
1065
1090
|
'--header= Hook scope · enter 确认',
|
|
1066
1091
|
'--layout=reverse', '--border=rounded',
|
|
1067
1092
|
'--color=header:italic:dim,prompt:cyan,pointer:cyan,hl:cyan,hl+:cyan:bold',
|
|
1093
|
+
'--preview=s=$(echo {} | awk \'{print $1}\'); if [ "$s" = "user" ]; then echo ""; echo " 从 user 级卸载 hook:"; echo " ~/.claude/hooks/"; else echo ""; echo " 从 project 级卸载 hook:"; echo " .claude/hooks/"; fi',
|
|
1094
|
+
'--preview-window=right:45%:wrap',
|
|
1068
1095
|
], {
|
|
1069
1096
|
input: `user — ~/.claude/hooks/\nproject — .claude/hooks/`,
|
|
1070
1097
|
encoding: 'utf8', stdio: ['pipe', 'pipe', 'inherit'],
|
package/lib/installer.js
CHANGED
|
@@ -146,6 +146,21 @@ export async function installTools(tools, targetDir, force = false) {
|
|
|
146
146
|
console.error(chalk.dim(` · Installed ${toolName}.py`))
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
// Copy extra paths (files or dirs) declared in tool.json `extraPaths`
|
|
150
|
+
// Each entry is a relative path from the tool source; copied to ~/.hskill/tools/{toolName}/
|
|
151
|
+
try {
|
|
152
|
+
const toolMeta = await fs.readJson(path.join(srcPath, 'tool.json'))
|
|
153
|
+
for (const rel of (toolMeta.extraPaths ?? [])) {
|
|
154
|
+
const src = path.join(srcPath, rel)
|
|
155
|
+
const dest = path.join(dataDir, toolName, rel)
|
|
156
|
+
if (await fs.pathExists(src)) {
|
|
157
|
+
await fs.ensureDir(path.dirname(dest))
|
|
158
|
+
await fs.copy(src, dest, { overwrite: true })
|
|
159
|
+
console.error(chalk.dim(` · Installed ${toolName}/${rel}`))
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
} catch { /* tool.json missing or no extraPaths — skip silently */ }
|
|
163
|
+
|
|
149
164
|
installed.push(toolName)
|
|
150
165
|
await _patchZshrc(srcPath, toolName)
|
|
151
166
|
} catch (err) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harveyz-skill",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.1",
|
|
4
4
|
"description": "Skill manager for Claude Code, Cursor, and Codex",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -41,6 +41,8 @@
|
|
|
41
41
|
"skills/meta/npm-release/",
|
|
42
42
|
"skills/meta/opencode-runner/",
|
|
43
43
|
"tools/p-launch/",
|
|
44
|
+
"tools/todo-tool/",
|
|
45
|
+
"tools/hub/",
|
|
44
46
|
"hooks/check-similar-branch/"
|
|
45
47
|
],
|
|
46
48
|
"engines": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: add-todo
|
|
3
|
-
version: "4.
|
|
3
|
+
version: "4.5.0"
|
|
4
4
|
user_invocable: true
|
|
5
5
|
description: "Add a new requirement, task, or feature request to any project's TODO.md — from any working directory. Triggers whenever the user wants to capture a new need — even phrased casually like 'we should do X later', 'add this to the backlog', 'note this down', 'remember to build X', 'we need to do Y at some point', or 'record this for later'."
|
|
6
6
|
---
|
|
@@ -88,6 +88,36 @@ description: "Add a new requirement, task, or feature request to any project's T
|
|
|
88
88
|
todo project set-path [项目名] [本地路径]
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
+
### 切换到 chore/todo 分支
|
|
92
|
+
|
|
93
|
+
在写入前,在项目目录操作 git:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
cd {local_path}
|
|
97
|
+
|
|
98
|
+
# 1. 记录当前分支
|
|
99
|
+
ORIGINAL_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
|
100
|
+
|
|
101
|
+
# 2. 确定 staging 基准分支(用于创建 chore/todo 及事后合并目标)
|
|
102
|
+
if git show-ref --verify --quiet refs/heads/staging; then
|
|
103
|
+
BASE_BRANCH=staging
|
|
104
|
+
else
|
|
105
|
+
BASE_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null \
|
|
106
|
+
| sed 's@^refs/remotes/origin/@@' || echo "main")
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
# 3. 切换到 chore/todo(不存在则从 BASE_BRANCH 创建)
|
|
110
|
+
if git show-ref --verify --quiet refs/heads/chore/todo; then
|
|
111
|
+
git checkout chore/todo
|
|
112
|
+
else
|
|
113
|
+
git checkout -b chore/todo "$BASE_BRANCH"
|
|
114
|
+
fi
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**非 git 仓库**:若 `local_path` 不在 git 仓库中,跳过此步骤,直接写入文件。
|
|
118
|
+
|
|
119
|
+
**BRANCH_GUARD hook**:执行 `git checkout -b chore/todo` 时,BRANCH_GUARD hook 可能会提示与其他分支语义相似。`chore/todo` 是永久追踪分支,不是功能分支,**直接确认新建,忽略相似分支警告**,继续执行后续步骤。
|
|
120
|
+
|
|
91
121
|
### 重复检查
|
|
92
122
|
|
|
93
123
|
加载文件,扫描是否有语义重叠的已有条目。若有:
|
|
@@ -118,6 +148,25 @@ todo project set-path [项目名] [本地路径]
|
|
|
118
148
|
## ✅ 已完成
|
|
119
149
|
```
|
|
120
150
|
|
|
121
|
-
|
|
151
|
+
### 提交、合并到 staging、切回原分支
|
|
152
|
+
|
|
153
|
+
写入后在项目目录执行:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
cd {local_path}
|
|
157
|
+
|
|
158
|
+
# 1. 在 chore/todo 上提交
|
|
159
|
+
git add TODO.md
|
|
160
|
+
git commit -m "todo: add [任务标题]"
|
|
161
|
+
|
|
162
|
+
# 2. 合并到 staging(或主分支)
|
|
163
|
+
git checkout "$BASE_BRANCH"
|
|
164
|
+
git merge --no-ff chore/todo -m "Merge chore/todo: add [任务标题]"
|
|
165
|
+
|
|
166
|
+
# 3. 切回原分支
|
|
167
|
+
git checkout "$ORIGINAL_BRANCH"
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
完成后确认:
|
|
122
171
|
|
|
123
|
-
> "✅ 已将 **[任务标题]** 写入 `{local_path}/TODO.md`。"
|
|
172
|
+
> "✅ 已将 **[任务标题]** 写入 `{local_path}/TODO.md`,提交到 `chore/todo`,合并到 `$BASE_BRANCH`,当前回到 `$ORIGINAL_BRANCH`。"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: npm-release
|
|
3
3
|
description: "Complete npm publish workflow for harveyz-skill: bump version, update CHANGELOG, create release branch, merge to staging then main, tag, and publish to npm. Use this skill whenever the user wants to release, publish, cut a version, bump version, ship to npm, or deploy a new package version."
|
|
4
4
|
user_invocable: true
|
|
5
|
-
version: "1.
|
|
5
|
+
version: "1.2.0"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# npm-release
|
|
@@ -100,14 +100,26 @@ npm version <new-version> --no-git-tag-version
|
|
|
100
100
|
|
|
101
101
|
---
|
|
102
102
|
|
|
103
|
+
## Step 3.5 — 同步 .npmignore 和 package.json files 字段
|
|
104
|
+
|
|
105
|
+
运行生成脚本,确保 `.npmignore` 和 `package.json` 中的 `files` 字段与当前 `skills-index.json` 保持一致:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
node scripts/generate-npmignore.js
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
检查输出有无报错。此步骤必须在提交前完成,否则新增或重命名的 skill 路径不会被正确包含/排除。
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
103
115
|
## Step 4 — 创建 release 分支并提交
|
|
104
116
|
|
|
105
117
|
```bash
|
|
106
118
|
# 从当前 staging 切出 release 分支
|
|
107
119
|
git checkout -b release/<new-version>
|
|
108
120
|
|
|
109
|
-
#
|
|
110
|
-
git add CHANGELOG.md package.json package-lock.json
|
|
121
|
+
# 提交 CHANGELOG、package.json、package-lock.json 和 .npmignore
|
|
122
|
+
git add CHANGELOG.md package.json package-lock.json .npmignore
|
|
111
123
|
git commit -m "chore(release): bump version to <new-version>"
|
|
112
124
|
```
|
|
113
125
|
|
package/skills/research/article-fetcher/references/__pycache__/article_utils.cpython-314.pyc
CHANGED
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: youtube-learner
|
|
3
|
-
version: "1.3.
|
|
3
|
+
version: "1.3.1"
|
|
4
4
|
description: "Process a YouTube video using the vdl CLI: transcribe, generate article and summary. Triggers when the user provides a YouTube URL and wants to learn from, summarize, transcribe, or extract key points from the video — e.g. 'help me understand this talk', 'summarize this YouTube video', 'get the transcript', 'process this video', 'summarize it'."
|
|
5
5
|
user_invocable: true
|
|
6
6
|
---
|
|
@@ -35,7 +35,7 @@ npm link
|
|
|
35
35
|
| `url` | YouTube 链接 | 从用户消息提取 |
|
|
36
36
|
| `--focus` | 关注点(影响摘要内容) | 若用户未提供,主动询问:"你最想从这个视频中了解什么?(例如:核心论点、技术细节、行动项)" |
|
|
37
37
|
| `--mode` | 处理模式 | **必须询问用户**(见下方「模式选择」) |
|
|
38
|
-
| `--lang` | 输出语言 | 默认 `zh-CN`;若用户用英文交流则用 `en
|
|
38
|
+
| `--lang` | 输出语言 | 默认 `zh-CN`;若用户用英文交流则用 `en`;必须将解析结果赋给 `<LANG>` 占位符 |
|
|
39
39
|
|
|
40
40
|
### 模式选择
|
|
41
41
|
|
|
@@ -64,9 +64,18 @@ npm link
|
|
|
64
64
|
|
|
65
65
|
## 执行命令
|
|
66
66
|
|
|
67
|
+
将以下占位符全部替换后再执行:
|
|
68
|
+
|
|
69
|
+
| 占位符 | 替换为 |
|
|
70
|
+
|--------|--------|
|
|
71
|
+
| `<URL>` | YouTube 链接 |
|
|
72
|
+
| `<FOCUS>` | 用户填写的关注点 |
|
|
73
|
+
| `<MODE>` | `transcript` / `audio` / `media` / `full` |
|
|
74
|
+
| `<LANG>` | `zh-CN`(中文对话)或 `en`(英文对话) |
|
|
75
|
+
|
|
67
76
|
```bash
|
|
68
77
|
cd /Users/harveyzhang96/Projects/Video-Learner && \
|
|
69
|
-
vdl "<URL>" --focus "<FOCUS>" --mode <MODE> --lang
|
|
78
|
+
vdl "<URL>" --focus "<FOCUS>" --mode <MODE> --lang <LANG>
|
|
70
79
|
```
|
|
71
80
|
|
|
72
81
|
`vdl` 会自动:
|
|
Binary file
|
package/skills-index.json
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"toolBundleMeta": {
|
|
3
|
-
"shell-tools": "终端工具(p-launch
|
|
3
|
+
"shell-tools": "终端工具(p-launch 本地仓库管理器 + todo-tool 任务管理器)",
|
|
4
|
+
"tools": "开发者 OS 工具(hub — 项目管理 + git 状态 + 任务跟踪)"
|
|
4
5
|
},
|
|
5
6
|
"tools": [
|
|
6
7
|
{
|
|
7
8
|
"name": "p-launch",
|
|
8
9
|
"bundle": "shell-tools"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"name": "todo-tool",
|
|
13
|
+
"bundle": "shell-tools"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"name": "hub",
|
|
17
|
+
"path": "tools/hub",
|
|
18
|
+
"bundle": "tools"
|
|
9
19
|
}
|
|
10
20
|
],
|
|
11
21
|
"bundleMeta": {
|
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def main():
|
|
5
|
+
from hub.core.db import HubDB
|
|
6
|
+
from hub.core.migrate import needs_migration, run_migration
|
|
7
|
+
|
|
8
|
+
db = HubDB()
|
|
9
|
+
if needs_migration():
|
|
10
|
+
n = run_migration(db)
|
|
11
|
+
if n:
|
|
12
|
+
import typer
|
|
13
|
+
typer.echo(f"hub: migrated {n} tasks from todo-tool ✓")
|
|
14
|
+
|
|
15
|
+
if len(sys.argv) == 1:
|
|
16
|
+
from hub.tui.app import HubApp
|
|
17
|
+
HubApp().run()
|
|
18
|
+
return
|
|
19
|
+
from hub.cli import app
|
|
20
|
+
app()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
if __name__ == "__main__":
|
|
24
|
+
main()
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from hub.cli import projects as proj_cmd
|
|
6
|
+
from hub.cli import tasks as task_cmd
|
|
7
|
+
|
|
8
|
+
app = typer.Typer(
|
|
9
|
+
name="hub",
|
|
10
|
+
no_args_is_help=False,
|
|
11
|
+
add_completion=False,
|
|
12
|
+
)
|
|
13
|
+
app.add_typer(proj_cmd.app, name="projects")
|
|
14
|
+
app.add_typer(task_cmd.app, name="tasks")
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
from hub.core.db import HubDB
|
|
10
|
+
from hub.core import projects as proj
|
|
11
|
+
|
|
12
|
+
app = typer.Typer(no_args_is_help=True)
|
|
13
|
+
|
|
14
|
+
_DEFAULT_MD = Path.home() / ".hskill" / "public" / "PROJECTS.md"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _md_path() -> Path:
|
|
18
|
+
if env := os.environ.get("HUB_MD_PATH"):
|
|
19
|
+
return Path(env)
|
|
20
|
+
return _DEFAULT_MD
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _out(data, json_out: bool) -> None:
|
|
24
|
+
if json_out:
|
|
25
|
+
print(json.dumps({"ok": True, "data": data}))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _err(msg: str, json_out: bool) -> None:
|
|
29
|
+
if json_out:
|
|
30
|
+
print(json.dumps({"ok": False, "error": msg}))
|
|
31
|
+
else:
|
|
32
|
+
typer.echo(f"Error: {msg}", err=True)
|
|
33
|
+
raise SystemExit(1)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@app.command("list")
|
|
37
|
+
def projects_list(json_out: bool = typer.Option(False, "--json")):
|
|
38
|
+
"""List all registered projects."""
|
|
39
|
+
db = HubDB()
|
|
40
|
+
projects = proj.list_projects(db)
|
|
41
|
+
if json_out:
|
|
42
|
+
_out(projects, json_out)
|
|
43
|
+
else:
|
|
44
|
+
if not projects:
|
|
45
|
+
typer.echo("No projects. Use: hub projects add <name> --path <path>")
|
|
46
|
+
return
|
|
47
|
+
for p in projects:
|
|
48
|
+
typer.echo(f" {p['name']:<24} {p['path'] or ''}")
|
|
49
|
+
if p.get("description"):
|
|
50
|
+
typer.echo(f" {p['description']}")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@app.command("add")
|
|
54
|
+
def projects_add(
|
|
55
|
+
name: str = typer.Argument(..., help="Project name (GitHub repo name)"),
|
|
56
|
+
path: str = typer.Option("", "--path", help="Local directory path"),
|
|
57
|
+
description: str = typer.Option("", "--desc", help="Short description"),
|
|
58
|
+
json_out: bool = typer.Option(False, "--json"),
|
|
59
|
+
):
|
|
60
|
+
"""Register or update a project."""
|
|
61
|
+
db = HubDB()
|
|
62
|
+
result = proj.add_project(db, name, path=path, description=description, md_path=_md_path())
|
|
63
|
+
if json_out:
|
|
64
|
+
_out(result, json_out)
|
|
65
|
+
else:
|
|
66
|
+
typer.echo(f"✓ {name}")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@app.command("path")
|
|
70
|
+
def projects_path(
|
|
71
|
+
name: str = typer.Argument(...),
|
|
72
|
+
json_out: bool = typer.Option(False, "--json"),
|
|
73
|
+
):
|
|
74
|
+
"""Print the local path for a project (for shell cd / agent use)."""
|
|
75
|
+
db = HubDB()
|
|
76
|
+
p = proj.get_project_path(db, name)
|
|
77
|
+
if p is None:
|
|
78
|
+
_err(f"Project '{name}' not found", json_out)
|
|
79
|
+
if json_out:
|
|
80
|
+
_out(p, json_out)
|
|
81
|
+
else:
|
|
82
|
+
print(p)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@app.command("sync")
|
|
86
|
+
def projects_sync(json_out: bool = typer.Option(False, "--json")):
|
|
87
|
+
"""Re-scan configured dirs and update PROJECTS.md. (Requires p-launch config.)"""
|
|
88
|
+
try:
|
|
89
|
+
import sys as _sys
|
|
90
|
+
_sys.path.insert(0, str(Path(__file__).parents[4] / "p-launch"))
|
|
91
|
+
from p_launch import read_project_dirs, collect_repos, sync_to_index
|
|
92
|
+
dirs = read_project_dirs()
|
|
93
|
+
repos = collect_repos(dirs)
|
|
94
|
+
sync_to_index(repos, _md_path())
|
|
95
|
+
if json_out:
|
|
96
|
+
_out({"scanned": len(repos)}, json_out)
|
|
97
|
+
else:
|
|
98
|
+
typer.echo(f"✓ Scanned {len(repos)} repos")
|
|
99
|
+
except ImportError:
|
|
100
|
+
_err("p-launch not installed; cannot auto-scan", json_out)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from hub.core.db import HubDB
|
|
8
|
+
from hub.core import tasks as task
|
|
9
|
+
|
|
10
|
+
app = typer.Typer(no_args_is_help=True)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _out(data, json_out: bool) -> None:
|
|
14
|
+
if json_out:
|
|
15
|
+
print(json.dumps({"ok": True, "data": data}))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _err(msg: str, json_out: bool) -> None:
|
|
19
|
+
if json_out:
|
|
20
|
+
print(json.dumps({"ok": False, "error": msg}))
|
|
21
|
+
else:
|
|
22
|
+
typer.echo(f"Error: {msg}", err=True)
|
|
23
|
+
raise SystemExit(1)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@app.command("list")
|
|
27
|
+
def tasks_list(
|
|
28
|
+
project: Optional[str] = typer.Option(None, "--project", "-p"),
|
|
29
|
+
status: Optional[str] = typer.Option(None, "--status", "-s"),
|
|
30
|
+
priority: Optional[str] = typer.Option(None, "--priority", "-P"),
|
|
31
|
+
json_out: bool = typer.Option(False, "--json"),
|
|
32
|
+
):
|
|
33
|
+
"""List tasks, optionally filtered."""
|
|
34
|
+
db = HubDB()
|
|
35
|
+
tasks = task.list_tasks(db, project=project, status=status, priority=priority)
|
|
36
|
+
if json_out:
|
|
37
|
+
_out(tasks, json_out)
|
|
38
|
+
else:
|
|
39
|
+
if not tasks:
|
|
40
|
+
typer.echo("No tasks.")
|
|
41
|
+
return
|
|
42
|
+
for t in tasks:
|
|
43
|
+
check = "✓" if t["status"] == "done" else "○"
|
|
44
|
+
typer.echo(f" [{t['id']:>4}] {check} [{t['priority']}] {t['title']} ({t['project']})")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@app.command("add")
|
|
48
|
+
def tasks_add(
|
|
49
|
+
title: str = typer.Argument(...),
|
|
50
|
+
project: str = typer.Option(..., "--project", "-p"),
|
|
51
|
+
priority: str = typer.Option("P2", "--priority", "-P"),
|
|
52
|
+
json_out: bool = typer.Option(False, "--json"),
|
|
53
|
+
):
|
|
54
|
+
"""Add a new task."""
|
|
55
|
+
db = HubDB()
|
|
56
|
+
try:
|
|
57
|
+
t = task.add_task(db, title=title, project=project, priority=priority)
|
|
58
|
+
except ValueError as e:
|
|
59
|
+
_err(str(e), json_out)
|
|
60
|
+
if json_out:
|
|
61
|
+
_out(t, json_out)
|
|
62
|
+
else:
|
|
63
|
+
typer.echo(f"✓ [{t['id']}] {t['title']}")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@app.command("done")
|
|
67
|
+
def tasks_done(
|
|
68
|
+
task_id: int = typer.Argument(...),
|
|
69
|
+
json_out: bool = typer.Option(False, "--json"),
|
|
70
|
+
):
|
|
71
|
+
"""Mark a task as done."""
|
|
72
|
+
db = HubDB()
|
|
73
|
+
result = task.mark_done(db, task_id)
|
|
74
|
+
if result is None:
|
|
75
|
+
_err(f"Task {task_id} not found", json_out)
|
|
76
|
+
if json_out:
|
|
77
|
+
_out(result, json_out)
|
|
78
|
+
else:
|
|
79
|
+
typer.echo(f"✓ done: {result['title']}")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@app.command("update")
|
|
83
|
+
def tasks_update(
|
|
84
|
+
task_id: int = typer.Argument(...),
|
|
85
|
+
title: Optional[str] = typer.Option(None, "--title"),
|
|
86
|
+
priority: Optional[str] = typer.Option(None, "--priority"),
|
|
87
|
+
status: Optional[str] = typer.Option(None, "--status"),
|
|
88
|
+
json_out: bool = typer.Option(False, "--json"),
|
|
89
|
+
):
|
|
90
|
+
"""Update task fields."""
|
|
91
|
+
db = HubDB()
|
|
92
|
+
result = task.update_task(db, task_id, title=title, priority=priority, status=status)
|
|
93
|
+
if result is None:
|
|
94
|
+
_err(f"Task {task_id} not found", json_out)
|
|
95
|
+
if json_out:
|
|
96
|
+
_out(result, json_out)
|
|
97
|
+
else:
|
|
98
|
+
typer.echo(f"✓ updated [{task_id}]")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@app.command("rm")
|
|
102
|
+
def tasks_rm(
|
|
103
|
+
task_id: int = typer.Argument(...),
|
|
104
|
+
json_out: bool = typer.Option(False, "--json"),
|
|
105
|
+
):
|
|
106
|
+
"""Delete a task."""
|
|
107
|
+
db = HubDB()
|
|
108
|
+
deleted = task.delete_task(db, task_id)
|
|
109
|
+
if not deleted:
|
|
110
|
+
_err(f"Task {task_id} not found", json_out)
|
|
111
|
+
if json_out:
|
|
112
|
+
_out({"deleted": True}, json_out)
|
|
113
|
+
else:
|
|
114
|
+
typer.echo(f"✓ deleted [{task_id}]")
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|