用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/mudrii/hom --skill hom-workflow-authoring命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | hom-workflow-authoring |
| description | Use when creating, modifying, or debugging YAML workflow definitions or the workflow engine |
Invoke this skill when:
workflows/crates/hom-workflow/src/parser.rscrates/hom-workflow/src/dag.rscrates/hom-workflow/src/executor.rscrates/hom-workflow/src/condition.rscrates/hom-workflow/src/checkpoint.rsEvery workflow file lives in workflows/ or ~/.config/hom/workflows/ and must follow this schema:
name: <unique-kebab-case-name>
description: <one-line description>
variables:
<key>: <default-value-or-empty-string>
steps:
- id: <unique-step-id>
harness: <harness-name> # Must match HarnessType::from_str_loose()
model: <optional-model-name>
prompt: |
<minijinja template — can reference top-level vars like {{ task }} and step outputs like {{ steps.plan.output }}>
depends_on: [<step-id>, ...] # Optional — steps with no deps run first
timeout: <duration> # Optional — e.g., "300s", "5m", or bare seconds like "300"
condition: '<expression>' # Optional — evaluated before execution
retry: # Optional
max_attempts: <n>
backoff: exponential|linear|fixed
on_failure: skip # Optional — default is abort
# Or:
# on_failure:
# fallback: <step-id>
The parser in parser.rs and dag.rs enforce:
iddepends_on reference must point to an existing step idpetgraph::algo::toposort must succeed; cycles are rejectedharness field must parse via HarnessType::from_str_loose()parse_timeout() accepts 300s, 5m, or bare seconds like 300Prompts are minijinja templates. Available variables:
{{ task }} — Runtime variables from --var key=value are injected at the top level by name{{ steps.<step-id>.output }} — Output captured from a completed stepGood prompt:
prompt: |
Review this codebase and create a detailed implementation plan for:
{{ task }}
Output a numbered list of steps with file paths and changes needed.
Bad prompt:
prompt: "do the thing" # Too vague — harness won't know what to do
Think of the DAG as a data flow graph. Each step should:
depends_on)plan ──→ implement ──→ validate ──→ security-review
↑
(condition: PASS)
Conditions are evaluated before a step runs. If false, the step is skipped.
Supported operators:
steps.<id>.output contains "<substring>"steps.<id>.status == "completed"steps.<id>.status != "failed"expr1 && expr2expr1 || expr2Notes:
&& binds tighter than ||| Strategy | Behavior |
|---|---|
abort (default) | Stop entire workflow, report failure |
skip | Mark step as skipped, continue to dependents |
on_failure: { fallback: <step-id> } | Run an alternative step instead |
#[test]
fn test_parse_valid_workflow() {
let yaml = r#"
name: test-workflow
description: A test
steps:
- id: step1
harness: claude-code
prompt: "hello"
"#;
let def = WorkflowDef::from_yaml(yaml).unwrap();
assert_eq!(def.name, "test-workflow");
assert_eq!(def.steps.len(), 1);
}
#[test]
fn test_reject_duplicate_step_ids() {
let yaml = r#"
name: bad
steps:
- id: dup
harness: claude-code
prompt: "a"
- id: dup
harness: codex
prompt: "b"
"#;
let def = WorkflowDef::from_yaml(yaml).unwrap();
assert!(def.validate().is_err());
}
#[test]
fn test_cycle_detection() {
let steps = vec![
StepDef {
id: "a".into(),
harness: "claude".into(),
model: None,
prompt: "a".into(),
depends_on: vec!["b".into()],
timeout: None,
condition: None,
retry: None,
on_failure: None,
},
StepDef {
id: "b".into(),
harness: "codex".into(),
model: None,
prompt: "b".into(),
depends_on: vec!["a".into()],
timeout: None,
condition: None,
retry: None,
on_failure: None,
},
];
assert!(WorkflowDag::from_steps(&steps).is_err());
}
#[test]
fn test_topo_order() {
let steps = vec![
StepDef {
id: "plan".into(),
harness: .(),
model: ,
prompt: .(),
depends_on: [],
timeout: ,
condition: ,
retry: ,
on_failure: ,
},
StepDef {
id: .(),
harness: .(),
model: ,
prompt: .(),
depends_on: [.()],
timeout: ,
condition: ,
retry: ,
on_failure: ,
},
];
= WorkflowDag::(&steps).();
= dag.().();
(order, [.(), .()]);
}
#[test]
fn test_contains_condition() {
let mut outputs = HashMap::new();
outputs.insert("test".to_string(), "All 42 tests PASS".to_string());
assert!(evaluate_condition(
r#"steps.test.output contains "PASS""#,
&outputs, &HashMap::new()
));
}
WorkflowDef::from_yaml()def.validate()WorkflowDag::from_steps()HarnessType::from_str_loose()variables: sectioncargo test -p hom-workflow passes