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.
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/PKG-INFO +1 -1
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/SKILL.md +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
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/scripts/update-index.py +3 -3
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-lifecycle-portability/SKILL.md +5 -5
- hermes_task_framework-1.2.1/hermes_task_framework/tests/test_task_root_resolution.py +122 -0
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework.egg-info/PKG-INFO +1 -1
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework.egg-info/SOURCES.txt +2 -1
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/pyproject.toml +1 -1
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/LICENSE +0 -0
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/README.md +0 -0
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/__init__.py +0 -0
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-external-repos-pattern/SKILL.md +0 -0
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/CHANGELOG.md +0 -0
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/docs/TASKS.md +0 -0
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/docs/architecture.dot +0 -0
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/docs/architecture.svg +0 -0
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/docs/product-requirements.md +0 -0
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/auto-runner.md +0 -0
- {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
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/composites/research.md +0 -0
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/references/research-workflow.md +0 -0
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/scripts/__init__.py +0 -0
- {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
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/scripts/task-runner.sh +0 -0
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/scripts/task_ref.py +0 -0
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/templates/CHANGELOG.md +0 -0
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/templates/TASK.md +0 -0
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-framework/templates/run.py +0 -0
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-lifecycle-edge-cases/SKILL.md +0 -0
- {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
- {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
- {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
- {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
- {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
- {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
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-timestamp-convention/SKILL.md +0 -0
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/skills/task-tracker/SKILL.md +0 -0
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework.egg-info/dependency_links.txt +0 -0
- {hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework.egg-info/top_level.txt +0 -0
- {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.
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
-
"""
|
|
50
|
-
# 1.
|
|
51
|
-
env = os.environ.get("
|
|
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.
|
|
80
|
-
return os.path.expanduser("
|
|
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
|
-
|
|
364
|
-
|
|
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
|
|
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
|
-
|
|
425
|
-
|
|
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:
|
|
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.
|
|
65
|
-
return os.path.expanduser("
|
|
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`
|
|
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()
|
{hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hermes-task-framework
|
|
3
|
-
Version: 1.2.
|
|
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.
|
|
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 = []
|
|
File without changes
|
|
File without changes
|
{hermes_task_framework-1.2.0 → hermes_task_framework-1.2.1}/hermes_task_framework/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|