handoff-mcp-server 0.19.1 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +52 -11
- package/Cargo.toml +2 -1
- package/README.md +140 -4
- package/package.json +5 -2
- package/scripts/sync-plugin-skills.sh +23 -1
- package/scripts/sync-workflow-inline.sh +138 -0
- package/skills/handoff/SKILL.md +61 -0
- package/skills/handoff-docs/SKILL.md +233 -0
- package/skills/handoff-load/SKILL.md +10 -2
- package/skills/handoff-memory/SKILL.md +20 -0
- package/src/context/injection.rs +239 -0
- package/src/context/mod.rs +129 -0
- package/src/lib.rs +1 -0
- package/src/mcp/handlers/bulk_update.rs +30 -2
- package/src/mcp/handlers/docs.rs +1043 -0
- package/src/mcp/handlers/docs_query.rs +1335 -0
- package/src/mcp/handlers/get_task.rs +9 -0
- package/src/mcp/handlers/import_context.rs +120 -13
- package/src/mcp/handlers/memory.rs +33 -53
- package/src/mcp/handlers/mod.rs +13 -0
- package/src/mcp/handlers/update_task.rs +22 -4
- package/src/mcp/tools.rs +185 -13
- package/src/storage/docs/mod.rs +478 -0
- package/src/storage/docs/model.rs +537 -0
- package/src/storage/docs/reassemble.rs +167 -0
- package/src/storage/docs/split.rs +377 -0
- package/src/storage/mod.rs +1 -0
- package/src/storage/tasks.rs +197 -5
|
@@ -3,6 +3,7 @@ use chrono::Utc;
|
|
|
3
3
|
use serde_json::{json, Value};
|
|
4
4
|
|
|
5
5
|
use super::resolve_project_dir;
|
|
6
|
+
use crate::storage::config::read_config;
|
|
6
7
|
use crate::storage::ensure_handoff_exists;
|
|
7
8
|
use crate::storage::tasks::*;
|
|
8
9
|
|
|
@@ -11,6 +12,10 @@ pub fn handle(arguments: &Value) -> Result<String> {
|
|
|
11
12
|
let handoff = ensure_handoff_exists(&project_dir)?;
|
|
12
13
|
let tasks_dir = handoff.join("tasks");
|
|
13
14
|
|
|
15
|
+
let require_estimate_hours = read_config(&handoff.join("config.toml"))
|
|
16
|
+
.map(|c| c.settings.require_estimate_hours)
|
|
17
|
+
.unwrap_or(true);
|
|
18
|
+
|
|
14
19
|
let updates = arguments
|
|
15
20
|
.get("updates")
|
|
16
21
|
.and_then(|v| v.as_array())
|
|
@@ -28,7 +33,9 @@ pub fn handle(arguments: &Value) -> Result<String> {
|
|
|
28
33
|
}
|
|
29
34
|
};
|
|
30
35
|
|
|
31
|
-
|
|
36
|
+
// A rejected update is reported per task and the rest still land: that is
|
|
37
|
+
// this tool's established `applied` + `errors[]` contract.
|
|
38
|
+
if let Err(e) = apply_single_update(&tasks_dir, task_id, update, require_estimate_hours) {
|
|
32
39
|
errors.push(json!({"task_id": task_id, "error": e.to_string()}));
|
|
33
40
|
} else {
|
|
34
41
|
applied += 1;
|
|
@@ -43,7 +50,12 @@ pub fn handle(arguments: &Value) -> Result<String> {
|
|
|
43
50
|
serde_json::to_string_pretty(&result).map_err(Into::into)
|
|
44
51
|
}
|
|
45
52
|
|
|
46
|
-
fn apply_single_update(
|
|
53
|
+
fn apply_single_update(
|
|
54
|
+
tasks_dir: &std::path::Path,
|
|
55
|
+
task_id: &str,
|
|
56
|
+
update: &Value,
|
|
57
|
+
require_estimate_hours: bool,
|
|
58
|
+
) -> Result<()> {
|
|
47
59
|
let task_dir = find_task_dir_by_id(tasks_dir, task_id)?
|
|
48
60
|
.ok_or_else(|| anyhow::anyhow!("{}", suggest_task_id(tasks_dir, task_id)))?;
|
|
49
61
|
|
|
@@ -117,6 +129,22 @@ fn apply_single_update(tasks_dir: &std::path::Path, task_id: &str, update: &Valu
|
|
|
117
129
|
validate_skipped_transition(&task_dir, &data)?;
|
|
118
130
|
}
|
|
119
131
|
|
|
132
|
+
// Guard the same invariant handoff_update_task enforces, or a bulk patch
|
|
133
|
+
// becomes a way around it. The check is on the task as it would be written —
|
|
134
|
+
// status and schedule already merged — not on the patch: a date-only patch
|
|
135
|
+
// that leaves an estimateless leaf in `todo` is exactly the state
|
|
136
|
+
// update_task refuses to write. Parent tasks (with children) are exempt.
|
|
137
|
+
let has_children = task_has_children(&task_dir)?;
|
|
138
|
+
validate_estimate_required(
|
|
139
|
+
require_estimate_hours,
|
|
140
|
+
task_id,
|
|
141
|
+
&data.title,
|
|
142
|
+
new_status,
|
|
143
|
+
has_children,
|
|
144
|
+
false,
|
|
145
|
+
data.schedule.as_ref(),
|
|
146
|
+
)?;
|
|
147
|
+
|
|
120
148
|
data.updated_at = Some(Utc::now().to_rfc3339());
|
|
121
149
|
|
|
122
150
|
if let Some((old_path, _)) = find_task_file(&task_dir)? {
|