hermes-task-framework 1.2.0__tar.gz → 1.2.1__tar.gz

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 (57) hide show
  1. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/PKG-INFO +1 -1
  2. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/SKILL.md +3 -3
  3. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/scripts/manage_task.py +33 -15
  4. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/scripts/update-index.py +3 -3
  5. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-lifecycle-portability/SKILL.md +5 -5
  6. hermes_task_framework-1.2.1/hermes_task_framework/tests/test_task_root_resolution.py +122 -0
  7. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework.egg-info/PKG-INFO +1 -1
  8. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework.egg-info/SOURCES.txt +2 -1
  9. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/pyproject.toml +1 -1
  10. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/LICENSE +0 -0
  11. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/README.md +0 -0
  12. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/__init__.py +0 -0
  13. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-external-repos-pattern/SKILL.md +0 -0
  14. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/CHANGELOG.md +0 -0
  15. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/docs/TASKS.md +0 -0
  16. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/docs/architecture.dot +0 -0
  17. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/docs/architecture.svg +0 -0
  18. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/docs/product-requirements.md +0 -0
  19. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/auto-runner.md +0 -0
  20. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/composites/code-review-session.md +0 -0
  21. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/composites/research.md +0 -0
  22. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/composites/software-dev.md +0 -0
  23. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/document-analysis-workflow.md +0 -0
  24. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/file-safety-lesson.md +0 -0
  25. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/generating-output-documents.md +0 -0
  26. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/operations/code-write.md +0 -0
  27. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/operations/document-write.md +0 -0
  28. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/operations/info-search.md +0 -0
  29. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/operations/web-research.md +0 -0
  30. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/policy-time-metadata.md +0 -0
  31. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/research-workflow.md +0 -0
  32. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/task-format-validation.md +0 -0
  33. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/task-hash-naming.md +0 -0
  34. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/task-lifecycle-example.md +0 -0
  35. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/task-overview-discovery.md +0 -0
  36. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/task-types/analysis.md +0 -0
  37. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/task-types/external-audit.md +0 -0
  38. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/task-types/video-production-pipeline.md +0 -0
  39. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/scripts/__init__.py +0 -0
  40. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/scripts/convert_md_to_pdf.py +0 -0
  41. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/scripts/task-runner.sh +0 -0
  42. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/scripts/task_ref.py +0 -0
  43. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/templates/CHANGELOG.md +0 -0
  44. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/templates/TASK.md +0 -0
  45. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/templates/run.py +0 -0
  46. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-lifecycle-edge-cases/SKILL.md +0 -0
  47. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-lifecycle-edge-cases/references/task-5d5a1a-recovery-example.md +0 -0
  48. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-lifecycle-portability/references/design-session-20260611.md +0 -0
  49. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-lifecycle-portability/references/output-model-design.md +0 -0
  50. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-lifecycle-portability/references/pipeline-output-transition.md +0 -0
  51. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-lifecycle-portability/references/task-recovery-5d5a1a-example.md +0 -0
  52. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-lifecycle-portability/references/task-recovery-procedure.md +0 -0
  53. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-timestamp-convention/SKILL.md +0 -0
  54. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-tracker/SKILL.md +0 -0
  55. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework.egg-info/dependency_links.txt +0 -0
  56. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework.egg-info/top_level.txt +0 -0
  57. {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hermes-task-framework
3
- Version: 1.2.0
3
+ Version: 1.2.1
4
4
  Summary: Structured task management skills for Hermes Agent — task creation, decomposition, stage gates
5
5
  Project-URL: Source, https://github.com/nuffin/hermes-task-framework-pip
6
6
  Requires-Python: >=3.11
@@ -33,7 +33,7 @@ tags:
33
33
  - operations
34
34
  - logging
35
35
  - pdf
36
- version: 1.1.0
36
+ version: 1.2.1
37
37
  ---
38
38
 
39
39
  ---
@@ -134,11 +134,11 @@ This skill uses environment variables for portability across machines. After ins
134
134
  source ~/.hermes/env.sh
135
135
  ```
136
136
 
137
- If the variable is unset, fall back to `~/.hermes/tasks` (the historical default).
137
+ Root resolution order is: `HERMES_TASKS_ROOT`, `HERMES_TASKS_DIR` compatibility alias, active-profile `tasks.data_dir`, global `tasks.data_dir`, then `~/studio/hermes/tasks`. The historical `~/.hermes/tasks` directory is compatibility-only: never create, mirror, populate, or symlink task directories there. When a custom `tasks.data_dir` is configured, it is the only task root used.
138
138
 
139
139
  ## Task Directory Structure
140
140
 
141
- The canonical tasks root is defined by `$HERMES_TASKS_ROOT` (default: `~/.hermes/tasks/`). All relative paths (e.g. `tasks/YYYYMMDD-*`) in documentation are relative to this root. When the user says "tasks" without qualification, this directory is the default reference — not the abstract concept of "任务". "创建一个任务" means creating a `YYYYMMDD-HHMMSS.<name>-<hash6>/` structure here using this skill.
141
+ The canonical tasks root is defined by `$HERMES_TASKS_ROOT` or configuration (default: `~/studio/hermes/tasks/`). All relative paths (e.g. `tasks/YYYYMMDD-*`) in documentation are relative to this root. When the user says "tasks" without qualification, this directory is the default reference — not the abstract concept of "任务". "创建一个任务" means creating a `YYYYMMDD-HHMMSS.<name>-<hash6>/` structure here using this skill. Task directories must be real directories; do not create task-root or task-directory symlinks.
142
142
 
143
143
  🔴 **Semantic disambiguation (important):** When the user says "任务" or "task", first determine whether they mean (a) a task-framework managed task (in `tasks/YY.../` directories) or (b) a generic concept. Clues: specific name/timestamp, operating on a task directory → (a); abstract discussion → (b). For (a), always use task-framework tools (task_create, task_set_status, etc.) — never raw `mv`/`cp`/`rm` on task directories. For (b), handle as normal conversation.
144
144
 
@@ -9,7 +9,7 @@ task-framework 任务生命周期管理工具
9
9
  1. HERMES_TASKS_ROOT 或 HERMES_TASKS_DIR 环境变量
10
10
  2. 当前 profile config.yaml: tasks.data_dir
11
11
  3. 全局 ~/.hermes/config.yaml: tasks.data_dir
12
- 4. Fallback: ~/.hermes/tasks
12
+ 4. Fallback: ~/studio/hermes/tasks (legacy ~/.hermes/tasks is never populated)
13
13
 
14
14
  Commands:
15
15
  create 创建新任务(目录 + hash + 元数据 + 模板 + 可选 inbox 移入)
@@ -22,12 +22,12 @@ Commands:
22
22
  reindex 重建索引文件(README.md + TASKS.md)
23
23
  init 为任务创建目录 + TASK.md + CHANGELOG.md + .hermes-task.json
24
24
  export 将任务打包为可移植 tar.gz
25
- import 从 tar.gz 恢复任务
26
- rebuild 按 hash 查找最近 tar.gz 并导入
25
+ import 从 tar.gz 或 zip 恢复任务
26
+ rebuild 按 hash 查找最近 tar.gz 或 zip 并导入
27
27
  migrate 一次性迁移:将旧存储目录下的文件迁移到统一目录
28
28
  ensure-all 全量注册所有现有任务
29
29
  """
30
- import os, sys, glob, json, shutil, re, tarfile, tempfile, hashlib, random, argparse, secrets
30
+ import os, sys, glob, json, shutil, re, tarfile, zipfile, tempfile, hashlib, random, argparse, secrets
31
31
  from datetime import datetime
32
32
  from pathlib import Path
33
33
 
@@ -46,9 +46,11 @@ def _read_config_yaml(config_path):
46
46
 
47
47
 
48
48
  def _resolve_tasks_root() -> str:
49
- """Priority chain: env → profile config → global config → fallback."""
50
- # 1. Env var
51
- env = os.environ.get("HERMES_TASKS_DIR", "").strip()
49
+ """Resolve the task root with ROOT as canonical and DIR as legacy fallback."""
50
+ # 1. Canonical env var, then the legacy pip-package compatibility alias.
51
+ env = os.environ.get("HERMES_TASKS_ROOT", "").strip()
52
+ if not env:
53
+ env = os.environ.get("HERMES_TASKS_DIR", "").strip()
52
54
  if env:
53
55
  return os.path.expanduser(env)
54
56
 
@@ -76,8 +78,8 @@ def _resolve_tasks_root() -> str:
76
78
  if val and isinstance(val, str):
77
79
  return os.path.expanduser(val)
78
80
 
79
- # 4. Fallback
80
- return os.path.expanduser("~/.hermes/tasks")
81
+ # 4. Canonical fallback. Never populate the legacy ~/.hermes/tasks root.
82
+ return os.path.expanduser("~/studio/hermes/tasks")
81
83
 
82
84
 
83
85
  TASKS_ROOT = _resolve_tasks_root()
@@ -360,8 +362,15 @@ def cmd_import(archive_path):
360
362
  print(f"Archive not found: {archive_path}")
361
363
  return False
362
364
  with tempfile.TemporaryDirectory(prefix="task-import-") as tmp:
363
- with tarfile.open(archive_path, 'r:gz') as tar:
364
- tar.extractall(path=tmp)
365
+ if tarfile.is_tarfile(archive_path):
366
+ with tarfile.open(archive_path, 'r:gz') as tar:
367
+ tar.extractall(path=tmp)
368
+ elif zipfile.is_zipfile(archive_path):
369
+ with zipfile.ZipFile(archive_path) as archive:
370
+ archive.extractall(path=tmp)
371
+ else:
372
+ print(f"Unsupported archive format: {archive_path}")
373
+ return False
365
374
  extracted = os.listdir(tmp)
366
375
  print(f"Extracted: {extracted}")
367
376
 
@@ -415,16 +424,25 @@ def cmd_import(archive_path):
415
424
 
416
425
 
417
426
  def cmd_rebuild(hash_or_archive):
418
- if tarfile.is_tarfile(hash_or_archive):
427
+ if os.path.isfile(hash_or_archive) and (
428
+ tarfile.is_tarfile(hash_or_archive) or zipfile.is_zipfile(hash_or_archive)
429
+ ):
419
430
  return cmd_import(hash_or_archive)
420
431
  h = hash_or_archive
421
432
  if not re.match(r'^[a-z0-9]{6}$', h):
422
433
  print(f"Invalid hash: {h}")
423
434
  return False
424
- pattern = os.path.join(TASKS_ROOT, f"*{h}*.tar.gz")
425
- matches = sorted(glob.glob(pattern), key=os.path.getmtime, reverse=True)
435
+ patterns = [
436
+ os.path.join(TASKS_ROOT, f"*{h}*.tar.gz"),
437
+ os.path.join(TASKS_ROOT, f"*{h}*.zip"),
438
+ ]
439
+ matches = sorted(
440
+ [match for pattern in patterns for match in glob.glob(pattern)],
441
+ key=os.path.getmtime,
442
+ reverse=True,
443
+ )
426
444
  if not matches:
427
- print(f"No tar.gz found for hash {h} in {TASKS_ROOT}")
445
+ print(f"No tar.gz or zip found for hash {h} in {TASKS_ROOT}")
428
446
  return False
429
447
  archive = matches[0]
430
448
  print(f"Found: {archive}")
@@ -7,7 +7,7 @@ Regenerates both:
7
7
  2. tasks/TASKS.md — aggregated checklist view (deep overview)
8
8
 
9
9
  Usage:
10
- python3 scripts/update-index.py # default: $HERMES_TASKS_ROOT (or ~/.hermes/tasks)
10
+ python3 scripts/update-index.py # default: configured root or ~/studio/hermes/tasks
11
11
  python3 scripts/update-index.py --tasks-dir /path # custom path
12
12
  """
13
13
 
@@ -61,8 +61,8 @@ def _resolve_tasks_root() -> str:
61
61
  if val and isinstance(val, str):
62
62
  return os.path.expanduser(val)
63
63
 
64
- # 4. Fallback
65
- return os.path.expanduser("~/.hermes/tasks")
64
+ # 4. Canonical fallback. Never populate the legacy ~/.hermes/tasks root.
65
+ return os.path.expanduser("~/studio/hermes/tasks")
66
66
 
67
67
 
68
68
  TASKS_ROOT = _resolve_tasks_root()
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  author: Hermes Agent
3
3
  category: software-development
4
- description: Task migration, export/import, and file-protection model. Covers tar.gz
4
+ description: Task migration, export/import, and file-protection model. Covers tar.gz and zip
5
5
  snapshots and manage.py lifecycle commands (export/import/rebuild).
6
6
  license: MIT
7
7
  metadata:
@@ -62,8 +62,8 @@ tasks/<ts>.<name>-<hash6>/
62
62
  | Command | What it does |
63
63
  |---------|-------------|
64
64
  | `export <hash>` | Package task directory as `tasks/<ts>.<name>-<hash>.tar.gz` (excludes `output/`, includes `input/` + metadata files) |
65
- | `import <tar.gz>` | Extract to `tasks/` directory, restore all files |
66
- | `rebuild <hash>` | Find latest `<hash>.tar.gz` in `tasks/`, extract. Generates semantic directory name from TASK.md title |
65
+ | `import <tar.gz-or-zip>` | Extract to `tasks/` directory, restore all files |
66
+ | `rebuild <hash>` | Find latest `<hash>.tar.gz` or `<hash>.zip` in `tasks/`, extract. Generates semantic directory name from TASK.md title |
67
67
 
68
68
  **Actual script path:**
69
69
  `scripts/manage_task.py # from the task-framework skill directory`
@@ -73,12 +73,12 @@ Usage: `python3 <path> <command> <arg>`
73
73
  **Export behavior:**
74
74
  - Archive contains actual file content (no symlinks involved)
75
75
  - Output dir is excluded (it's regenerable via pipeline)
76
- - `tar.gz` acts as a portable backup that can be committed to git
76
+ - `tar.gz` and `zip` act as portable backups that can be committed to git
77
77
 
78
78
  **Cross-machine strategy:**
79
79
  - No automatic merge — each machine's copy is an independent task
80
80
  - To combine work from two machines: create a new task referencing both via `ref:<hash>/...`
81
- - `tar.gz` in git repo acts as backup
81
+ - `tar.gz` or `zip` in git repo acts as backup
82
82
 
83
83
  ## Old Tasks (Migration)
84
84
 
@@ -0,0 +1,122 @@
1
+ import importlib.util
2
+ import os
3
+ from pathlib import Path
4
+ import subprocess
5
+ import sys
6
+ import tempfile
7
+ import unittest
8
+ import zipfile
9
+
10
+
11
+ SCRIPT = (
12
+ Path(__file__).resolve().parents[1]
13
+ / "skills"
14
+ / "task-framework"
15
+ / "scripts"
16
+ / "manage_task.py"
17
+ )
18
+
19
+
20
+ def load_manage_task():
21
+ spec = importlib.util.spec_from_file_location("manage_task_under_test", SCRIPT)
22
+ if spec is None or spec.loader is None:
23
+ raise RuntimeError(f"Cannot load {SCRIPT}")
24
+ module = importlib.util.module_from_spec(spec)
25
+ spec.loader.exec_module(module)
26
+ return module
27
+
28
+
29
+ class TaskRootResolutionTests(unittest.TestCase):
30
+ def setUp(self):
31
+ self.original = {
32
+ "HERMES_TASKS_ROOT": os.environ.get("HERMES_TASKS_ROOT"),
33
+ "HERMES_TASKS_DIR": os.environ.get("HERMES_TASKS_DIR"),
34
+ "HERMES_HOME": os.environ.get("HERMES_HOME"),
35
+ }
36
+ os.environ["HERMES_HOME"] = "/tmp/hermes-task-framework-test-profile"
37
+
38
+ def tearDown(self):
39
+ for key, value in self.original.items():
40
+ if value is None:
41
+ os.environ.pop(key, None)
42
+ else:
43
+ os.environ[key] = value
44
+
45
+ def test_root_is_canonical_when_both_variables_are_set(self):
46
+ os.environ["HERMES_TASKS_ROOT"] = "/tmp/canonical-tasks"
47
+ os.environ["HERMES_TASKS_DIR"] = "/tmp/legacy-tasks"
48
+ module = load_manage_task()
49
+ self.assertEqual(module.TASKS_ROOT, "/tmp/canonical-tasks")
50
+
51
+ def test_legacy_dir_is_used_as_fallback(self):
52
+ os.environ.pop("HERMES_TASKS_ROOT", None)
53
+ os.environ["HERMES_TASKS_DIR"] = "/tmp/legacy-tasks"
54
+ module = load_manage_task()
55
+ self.assertEqual(module.TASKS_ROOT, "/tmp/legacy-tasks")
56
+
57
+ def test_canonical_root_is_used_when_no_override_or_config_exists(self):
58
+ os.environ.pop("HERMES_TASKS_ROOT", None)
59
+ os.environ.pop("HERMES_TASKS_DIR", None)
60
+ module = load_manage_task()
61
+ self.assertEqual(module.TASKS_ROOT, str(Path.home() / "studio" / "hermes" / "tasks"))
62
+
63
+ def test_create_command_uses_root_without_legacy_alias(self):
64
+ with tempfile.TemporaryDirectory() as temp_dir:
65
+ env = os.environ.copy()
66
+ env["HERMES_TASKS_ROOT"] = temp_dir
67
+ env.pop("HERMES_TASKS_DIR", None)
68
+ result = subprocess.run(
69
+ [sys.executable, str(SCRIPT), "create", "root-only-test"],
70
+ cwd=SCRIPT.parent,
71
+ env=env,
72
+ capture_output=True,
73
+ text=True,
74
+ check=True,
75
+ )
76
+ self.assertIn(temp_dir, result.stdout)
77
+ created = list(Path(temp_dir).glob("*.root-only-test-*"))
78
+ self.assertEqual(len(created), 1)
79
+ self.assertTrue((created[0] / ".hermes-task.json").exists())
80
+
81
+ def test_import_accepts_zip_archive(self):
82
+ module = load_manage_task()
83
+ with tempfile.TemporaryDirectory() as temp_dir:
84
+ setattr(module, "TASKS_ROOT", temp_dir)
85
+ task_name = "20260813-120000.zip-import-a1b2c3"
86
+ task_dir = Path(temp_dir) / task_name
87
+ staging = Path(temp_dir) / "staging" / task_name
88
+ staging.mkdir(parents=True)
89
+ (staging / "TASK.md").write_text("# Task: ZIP import\n", encoding="utf-8")
90
+ (staging / ".hermes-task.json").write_text(
91
+ '{"hash": "a1b2c3", "name": "zip-import"}', encoding="utf-8"
92
+ )
93
+ archive_path = Path(temp_dir) / f"{task_name}.zip"
94
+ with zipfile.ZipFile(archive_path, "w") as archive:
95
+ for path in staging.rglob("*"):
96
+ archive.write(path, path.relative_to(staging.parent))
97
+
98
+ self.assertTrue(module.cmd_import(str(archive_path)))
99
+ self.assertTrue((task_dir / "TASK.md").exists())
100
+
101
+ def test_rebuild_discovers_latest_zip_archive(self):
102
+ module = load_manage_task()
103
+ with tempfile.TemporaryDirectory() as temp_dir:
104
+ setattr(module, "TASKS_ROOT", temp_dir)
105
+ task_name = "20260813-120000.zip-rebuild-d4e5f6"
106
+ archive_path = Path(temp_dir) / f"{task_name}.zip"
107
+ staging = Path(temp_dir) / "staging" / task_name
108
+ staging.mkdir(parents=True)
109
+ (staging / "TASK.md").write_text("# Task: ZIP rebuild\n", encoding="utf-8")
110
+ (staging / ".hermes-task.json").write_text(
111
+ '{"hash": "d4e5f6", "name": "zip-rebuild"}', encoding="utf-8"
112
+ )
113
+ with zipfile.ZipFile(archive_path, "w") as archive:
114
+ for path in staging.rglob("*"):
115
+ archive.write(path, path.relative_to(staging.parent))
116
+
117
+ self.assertTrue(module.cmd_rebuild("d4e5f6"))
118
+ self.assertTrue((Path(temp_dir) / task_name / "TASK.md").exists())
119
+
120
+
121
+ if __name__ == "__main__":
122
+ unittest.main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hermes-task-framework
3
- Version: 1.2.0
3
+ Version: 1.2.1
4
4
  Summary: Structured task management skills for Hermes Agent — task creation, decomposition, stage gates
5
5
  Project-URL: Source, https://github.com/nuffin/hermes-task-framework-pip
6
6
  Requires-Python: >=3.11
@@ -51,4 +51,5 @@ hermes_task_framework/skills/task-lifecycle-portability/references/pipeline-outp
51
51
  hermes_task_framework/skills/task-lifecycle-portability/references/task-recovery-5d5a1a-example.md
52
52
  hermes_task_framework/skills/task-lifecycle-portability/references/task-recovery-procedure.md
53
53
  hermes_task_framework/skills/task-timestamp-convention/SKILL.md
54
- hermes_task_framework/skills/task-tracker/SKILL.md
54
+ hermes_task_framework/skills/task-tracker/SKILL.md
55
+ hermes_task_framework/tests/test_task_root_resolution.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "hermes-task-framework"
7
- version = "1.2.0"
7
+ version = "1.2.1"
8
8
  description = "Structured task management skills for Hermes Agent — task creation, decomposition, stage gates"
9
9
  requires-python = ">=3.11"
10
10
  dependencies = []