owflow 0.3.0 → 0.4.3
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/README.md +24 -2
- package/dist/agents/information-gatherer.md +11 -11
- package/dist/agents/research-planner.md +15 -12
- package/dist/agents/research-synthesizer.md +7 -5
- package/dist/agents/solution-brainstormer.md +4 -4
- package/dist/agents/solution-designer.md +6 -6
- package/dist/commands/work.md +9 -9
- package/dist/configuration/commands-config.js +57 -0
- package/dist/configuration/skills-config.js +1 -1
- package/dist/index.js +5 -2
- package/dist/skills/diagrams-mermaid/SKILL.md +1 -0
- package/dist/skills/flow-init/SKILL.md +1 -1
- package/dist/skills/goal-research/SKILL.md +118 -0
- package/dist/skills/html-renderer/SKILL.md +1 -0
- package/dist/skills/migration/SKILL.md +1 -0
- package/dist/skills/orchestrator-framework/references/command-namespacing.md +1 -1
- package/dist/skills/orchestrator-framework/references/dispatcher-handoff.md +8 -1
- package/dist/skills/orchestrator-framework/references/gate-contract.md +17 -9
- package/dist/skills/performance/SKILL.md +1 -0
- package/dist/skills/research/SKILL.md +130 -376
- package/dist/skills/research-brainstorm/SKILL.md +120 -0
- package/dist/skills/{research → research-brainstorm}/references/brainstorming-techniques.md +1 -1
- package/dist/skills/research-converge/SKILL.md +130 -0
- package/dist/skills/research-design/SKILL.md +146 -0
- package/dist/skills/{research → research-design}/references/design-techniques.md +1 -1
- package/dist/skills/research-finalize/SKILL.md +125 -0
- package/dist/skills/research-fork/SKILL.md +190 -0
- package/dist/skills/research-gather/SKILL.md +133 -0
- package/dist/skills/research-plan/SKILL.md +141 -0
- package/dist/skills/research-quick/SKILL.md +139 -0
- package/dist/skills/research-synthesize/SKILL.md +127 -0
- package/dist/skills/rule-reviewer/SKILL.md +1 -1
- package/dist/skills/standards-discover/SKILL.md +1 -0
- package/dist/templates/orchestrator-state-research.yml +19 -13
- package/dist/tools/fork_task.js +36 -0
- package/dist/tools/verify_template.js +10 -31
- package/dist/utils/file-util.js +1 -1
- package/dist/utils/fork-task.js +74 -0
- package/dist/utils/plugin-info.js +20 -0
- package/dist/utils/yaml-structure.js +32 -0
- package/package.json +1 -1
- package/dist/commands/agents-md-generator.md +0 -36
- package/dist/commands/dev-analyze.md +0 -33
- package/dist/commands/dev-bugfix.md +0 -37
- package/dist/commands/dev-finalize.md +0 -33
- package/dist/commands/dev-implement.md +0 -34
- package/dist/commands/dev-plan.md +0 -37
- package/dist/commands/dev-spec.md +0 -37
- package/dist/commands/dev-tdd-red.md +0 -33
- package/dist/commands/dev-verify.md +0 -33
- package/dist/commands/development.md +0 -36
- package/dist/commands/diagrams-mermaid.md +0 -36
- package/dist/commands/flow-init.md +0 -36
- package/dist/commands/goal-development.md +0 -32
- package/dist/commands/html-renderer.md +0 -36
- package/dist/commands/migration.md +0 -36
- package/dist/commands/performance.md +0 -36
- package/dist/commands/research.md +0 -36
- package/dist/commands/rule-reviewer.md +0 -38
- package/dist/commands/standards-discover.md +0 -36
- package/dist/commands/standards-update.md +0 -36
- package/dist/constants.js +0 -4
- /package/dist/skills/{research → research-plan}/references/research-methodologies.md +0 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: owflow:research-synthesize
|
|
3
|
+
description: Research skill — delegates synthesis and evidence-based reporting to the research-synthesizer agent, producing the synthesis document and the comprehensive research report with per-finding confidence. Closes the research foundation (synthesis-complete) at the boundary with the optional chain (user choice).
|
|
4
|
+
argument-hint: "[task-path-or-identifier]"
|
|
5
|
+
user-invocable: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Research Synthesize — Synthesis & Research Report (synthesis-complete)
|
|
9
|
+
|
|
10
|
+
Work phase of the research workflow. Delegates pattern synthesis and evidence-based reporting to the research-synthesizer agent (`analysis/synthesis.md` + `outputs/research-report.md`). This is the research-foundation boundary: the Exit Gate is the pause between the foundation and the optional-phase decision. State lives in `orchestrator-state.yml` — this skill reads it on entry and writes results on exit.
|
|
11
|
+
|
|
12
|
+
Related phases: `/owflow:research-gather` (produces the merged findings); the optional chain (`research-brainstorm` / `research-design`) and `/owflow:research-finalize` follow, per the user's choice.
|
|
13
|
+
|
|
14
|
+
## Entry Gate
|
|
15
|
+
|
|
16
|
+
Resolve the `task-path-or-identifier` argument BEFORE anything else (see [Gate Contract](../orchestrator-framework/references/gate-contract.md)):
|
|
17
|
+
|
|
18
|
+
- **Path** (absolute or project-relative) to the task directory — use as-is.
|
|
19
|
+
- **Identifier** — exact directory name inside `.owflow/tasks/research/` (e.g., `2026-09-26-my-research`); resolve to its path.
|
|
20
|
+
- If the argument is **missing**, the path does **not exist**, or matches **no identifier** → print the blocked block, then STOP (never guess or auto-pick a task):
|
|
21
|
+
1. Steps that must be completed first (in order), each with its command:
|
|
22
|
+
- Research brief & plan (`brief-written`, `plan-created`) → `/owflow:research-plan <task-path>`
|
|
23
|
+
- Parallel findings fan (`findings-gathered`) → `/owflow:research-gather <task-path>`
|
|
24
|
+
2. List available research-task identifiers (directories under `.owflow/tasks/research/`) to resume from, if any.
|
|
25
|
+
3. Hint: `Run /owflow:research <question> to start a task from scratch, or pass a task path/identifier to resume.`
|
|
26
|
+
|
|
27
|
+
### Prerequisites
|
|
28
|
+
|
|
29
|
+
| Required for this skill | Where verified | Produced by |
|
|
30
|
+
| ------------------------------------ | ------------------------------------------------------------------------------ | ------------------------------------ |
|
|
31
|
+
| State file exists | `<task-path>/orchestrator-state.yml` | `/owflow:research <question>` or `/owflow:research-quick` |
|
|
32
|
+
| Findings exist | `findings-gathered` in `completed_phases` + `analysis/findings/` has per-category files | `/owflow:research-gather <task-path>` (or the quick lane's condensed gather) |
|
|
33
|
+
| Research question recorded | `research_context.research_question` non-null in state | `/owflow:research-plan <task-path>` |
|
|
34
|
+
|
|
35
|
+
1. **Read `orchestrator-state.yml`** from the task path. If missing → mid-pipeline bootstrap ([Missing-state Bootstrap](../orchestrator-framework/references/gate-contract.md), starting slug `synthesis-complete`): `question` — create a fresh standard research task starting at this step, or decline → print `No research task found at <path>. Run /owflow:research <question> to start a task from scratch.` and STOP.
|
|
36
|
+
2. **Content check — findings present**: the per-category files under `analysis/findings/` must exist (artifacts before state — drop the `findings-gathered` entry and route back to `/owflow:research-gather <task-path>` when they do not).
|
|
37
|
+
3. **Skip/resume**: if `analysis/synthesis.md` AND `outputs/research-report.md` both exist, adopt them — append `synthesis-complete` if it is missing, backfill `research_outputs.synthesis` and `research_outputs.research_report` when those fields are still null, report the existing summaries, and route to the Exit Gate. Either file missing → re-run below (drop a stale `synthesis-complete` entry first).
|
|
38
|
+
|
|
39
|
+
## Input / Output Artifacts
|
|
40
|
+
|
|
41
|
+
| Artifact | Normal mode (full lane) | Quick mode (`research-quick`) |
|
|
42
|
+
| ---------------------------------------------- | ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
|
43
|
+
| Consumed: `analysis/findings/*.md` | all per-category files (the gather contract) | same — quick-lane findings feed the inline synthesis |
|
|
44
|
+
| Consumed: `research-synthesizer` agent (Task tool) | 1 delegated call | n/a — synthesis is ALWAYS inline in the quick lane (the ONLY exception to this delegation) |
|
|
45
|
+
| Produced: `analysis/synthesis.md` | delegated (patterns, cross-references, gaps) | written directly inline |
|
|
46
|
+
| Produced: `outputs/research-report.md` | delegated (report + per-finding confidence) | written directly inline |
|
|
47
|
+
| State write | `synthesis-complete`; `confidence_level`, `phase_summaries.synthesize`, `research_outputs.synthesis` + `.research_report` | same slug and fields, condensation-noted |
|
|
48
|
+
|
|
49
|
+
## Execute (delegated synthesis)
|
|
50
|
+
|
|
51
|
+
**Read first**: the [Delegation Rules](../orchestrator-framework/references/delegation-rules.md).
|
|
52
|
+
|
|
53
|
+
> **ANTI-PATTERN — never write synthesis.md or research-report.md yourself. "The findings are few" is NOT a reason to skip delegation. (The quick lane is the ONLY exception — research-quick synthesizes inline and skips this section.)**
|
|
54
|
+
|
|
55
|
+
1. **INVOKE NOW**: Task tool - `research-synthesizer` subagent (never the Skill tool — this is an agent). Pass (Pattern 7 — accumulated context): task_path, findings_directory_path, research_question, research_type, methodology (from state).
|
|
56
|
+
|
|
57
|
+
**Synthesizer produces**:
|
|
58
|
+
|
|
59
|
+
- Pattern analysis and cross-references → `analysis/synthesis.md`
|
|
60
|
+
- Comprehensive research report answering research question → `outputs/research-report.md`
|
|
61
|
+
- Confidence levels for each finding
|
|
62
|
+
- Documented gaps and uncertainties
|
|
63
|
+
|
|
64
|
+
> **SELF-CHECK on both artifacts**: after the Task tool returns, verify `analysis/synthesis.md` AND `outputs/research-report.md` exist (synthesis documents gaps; report carries confidence per finding). If missing: **STOP. Re-invoke the research-synthesizer with corrected context.** If the second attempt also fails, use `question` to report the failure and ask whether to retry (after targeted re-gathering if the gaps require it) or stop. There is no continue-without-synthesis path — every later step reads these artifacts. On stop, record the failure per the State Update Convention and print the resume command.
|
|
65
|
+
|
|
66
|
+
### Post-synthesis decision re-cap (direct, condensable)
|
|
67
|
+
|
|
68
|
+
The synthesizer's key outcomes are surfaced to the user here before the Exit Gate (decisions from subagents must go to the user — never silently skipped): overall `confidence_level`, top patterns, documented gaps. Extract a 1-2 sentence summary into `phase_summaries.synthesize`.
|
|
69
|
+
|
|
70
|
+
### Close (`synthesis-complete`)
|
|
71
|
+
|
|
72
|
+
1. **State write**: append `synthesis-complete` to `completed_phases`; update `research_context.confidence_level`, `phase_summaries.synthesize`; set `research_outputs.synthesis` and `research_outputs.research_report`; bump `orchestrator.updated`. On failure: append `synthesis-complete` to `failed_phases`, increment `auto_fix_attempts["synthesis-complete"]`. Then re-read state + run `verify_template` (see State Update Convention).
|
|
73
|
+
|
|
74
|
+
## State Update Convention (per step)
|
|
75
|
+
|
|
76
|
+
Apply after EVERY step above:
|
|
77
|
+
|
|
78
|
+
1. **Write immediately** — update `orchestrator-state.yml` as soon as the step completes, appending ONLY the step slug actually performed (`synthesis-complete`) plus that step's fields. Never batch multiple steps into one end-of-skill write.
|
|
79
|
+
2. **Timestamp** — set `orchestrator.updated` to the current UTC timestamp on every write.
|
|
80
|
+
3. **Failures** — if the step fails or its retries are abandoned, do NOT append to `completed_phases`; instead append the step's slug to `orchestrator.failed_phases` and increment `auto_fix_attempts["synthesis-complete"]`.
|
|
81
|
+
4. **Validate** — after every write, re-read the file to confirm values, then run the `verify_template` tool with `filePath: <task-path>/orchestrator-state.yml`, `templateName: orchestrator-state-research.yml`. Fix any reported issue immediately before proceeding.
|
|
82
|
+
5. **Final check** — before the Exit Gate, one consolidated re-read + `verify_template` run to confirm the full state matches everything performed in this session.
|
|
83
|
+
|
|
84
|
+
## Recovery
|
|
85
|
+
|
|
86
|
+
| Step | Max Attempts | Strategy |
|
|
87
|
+
| --------------------------------- | ------------ | ------------------------------------- |
|
|
88
|
+
| Synthesize (`synthesis-complete`) | 2 | Request targeted re-gathering for gaps |
|
|
89
|
+
|
|
90
|
+
## Exit Gate
|
|
91
|
+
|
|
92
|
+
Present results, get user confirmation, then hand off (see [Gate Contract](../orchestrator-framework/references/gate-contract.md)). Never auto-invoke the next skill.
|
|
93
|
+
|
|
94
|
+
### Results box
|
|
95
|
+
|
|
96
|
+
```markdown
|
|
97
|
+
## ✅ RESEARCH SYNTHESIZE COMPLETE — <research question>
|
|
98
|
+
|
|
99
|
+
**Type** — [research type]
|
|
100
|
+
**Confidence** — [confidence level from state]
|
|
101
|
+
**Key patterns** — [2-3 one-line pattern highlights from synthesis]
|
|
102
|
+
**Gaps** — [documented gaps and uncertainties, 1 line]
|
|
103
|
+
|
|
104
|
+
**Artifacts**
|
|
105
|
+
|
|
106
|
+
- `analysis/synthesis.md`
|
|
107
|
+
- `outputs/research-report.md`
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Results-acceptance question
|
|
111
|
+
|
|
112
|
+
Use `question` — "Are these results correct?" with options:
|
|
113
|
+
|
|
114
|
+
- **Accept** — synthesis and report are good; continue.
|
|
115
|
+
- **Adjust** — re-run the affected part with the user's corrections (targeted re-gathering for gaps, then re-synthesize), update state and artifacts, re-present the results box.
|
|
116
|
+
- **Discuss** — walk through specific findings or confidence levels in more depth; then re-ask.
|
|
117
|
+
- **Stop here** — print the resume command (`/owflow:research-synthesize <task-path>`) and end.
|
|
118
|
+
|
|
119
|
+
### Next steps (after Accept)
|
|
120
|
+
|
|
121
|
+
Research foundation complete (initialized, planned, gathered, synthesized). Continue to optional-phase evaluation?
|
|
122
|
+
|
|
123
|
+
- `→ /owflow:research-brainstorm <task-path>` — when the user wants solution alternatives brainstormed. Remaining after: converge → design (when wanted) → finalize.
|
|
124
|
+
- `→ /owflow:research-design <task-path>` — design-only branch (no brainstorming), seeded from the research report. Remaining after: finalize.
|
|
125
|
+
- `→ /owflow:research-finalize <task-path>` — when neither is wanted: inventories the outputs and completes the task. If the foundation alone was the goal, this is the route — the task stays resumable.
|
|
126
|
+
|
|
127
|
+
Then STOP.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: owflow:rule-reviewer
|
|
3
|
-
description: Score
|
|
3
|
+
description: "Score a rule-for-AI markdown file (CLAUDE.md, AGENTS.md, or any rules file) on 5 axes and return concrete actionable fixes"
|
|
4
4
|
argument-hint: "[path/to/rules.md]"
|
|
5
5
|
user-invocable: true
|
|
6
6
|
---
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
orchestrator:
|
|
2
2
|
started_phase: null
|
|
3
|
+
entry_point: null
|
|
3
4
|
completed_phases: []
|
|
4
5
|
failed_phases: []
|
|
5
6
|
auto_fix_attempts:
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
design_enabled: null
|
|
7
|
+
brief-written: 0
|
|
8
|
+
plan-created: 0
|
|
9
|
+
findings-gathered: 0
|
|
10
|
+
synthesis-complete: 0
|
|
11
|
+
alternatives-generated: 0
|
|
12
|
+
approaches-chosen: 0
|
|
13
|
+
design-generated: 0
|
|
14
|
+
research-completed: 0
|
|
15
15
|
created: "YYYY-MM-DDTHH:MM:SSZ"
|
|
16
16
|
updated: "YYYY-MM-DDTHH:MM:SSZ"
|
|
17
17
|
task_path: null
|
|
@@ -38,17 +38,22 @@ research_context:
|
|
|
38
38
|
categories: []
|
|
39
39
|
count: 0
|
|
40
40
|
source: "planner | default"
|
|
41
|
+
project_doc_paths: []
|
|
41
42
|
phase_summaries:
|
|
42
|
-
|
|
43
|
+
plan:
|
|
43
44
|
summary: null
|
|
44
45
|
steps_completed: []
|
|
45
|
-
|
|
46
|
+
gather:
|
|
46
47
|
summary: null
|
|
47
|
-
|
|
48
|
+
synthesize:
|
|
49
|
+
summary: null
|
|
50
|
+
brainstorm:
|
|
51
|
+
summary: null
|
|
52
|
+
converge:
|
|
48
53
|
summary: null
|
|
49
54
|
decision_areas: []
|
|
50
55
|
deferred_ideas: []
|
|
51
|
-
|
|
56
|
+
design:
|
|
52
57
|
summary: null
|
|
53
58
|
architecture_style: null
|
|
54
59
|
decisions_count: 0
|
|
@@ -59,3 +64,4 @@ research_outputs:
|
|
|
59
64
|
solution_exploration: null
|
|
60
65
|
high_level_design: null
|
|
61
66
|
decision_log: null
|
|
67
|
+
synthesis: null
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { tool } from "@opencode-ai/plugin";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { forkTask } from "../utils/fork-task.js";
|
|
4
|
+
export const fork_task = tool({
|
|
5
|
+
description: "Fork a research task: copy the whole source task directory into a new date-prefixed directory named from the slug, then trim the copied state so the fork continues from the chosen completed step (fork point) onwards. The source task is never modified.",
|
|
6
|
+
args: {
|
|
7
|
+
taskRoot: tool.schema.string().describe("Project-relative root of all tasks (typically '.owflow/tasks')"),
|
|
8
|
+
source: tool.schema.string().describe("Source task path or identifier under .owflow/tasks/research/"),
|
|
9
|
+
slug: tool.schema.string().describe("Lowercase-kebab-case slug for the fork name, 3-5 words, WITHOUT the date prefix (derived by the agent from a short descriptive name)"),
|
|
10
|
+
from: tool.schema.string().describe("Fork point: a completed step slug from the source task's completed_phases (e.g. 'synthesis-complete'); the fork continues from the step after it"),
|
|
11
|
+
intent: tool.schema.string().optional().describe("Optional one-line description of what the fork should explore; stamped into the fork's orchestrator.options.fork_information.intent"),
|
|
12
|
+
},
|
|
13
|
+
async execute(args, context) {
|
|
14
|
+
try {
|
|
15
|
+
const result = forkTask({
|
|
16
|
+
taskRoot: args.taskRoot,
|
|
17
|
+
source: args.source,
|
|
18
|
+
slug: args.slug,
|
|
19
|
+
from: args.from,
|
|
20
|
+
intent: args.intent,
|
|
21
|
+
cwd: context.directory,
|
|
22
|
+
});
|
|
23
|
+
return {
|
|
24
|
+
output: [
|
|
25
|
+
`FORK CREATED`,
|
|
26
|
+
`Fork path: ${path.relative(context.directory, result.forkPath).split(path.sep).join("/")}`,
|
|
27
|
+
`Fork point: ${result.forkPoint}`,
|
|
28
|
+
...(args.intent ? [`Intent: ${args.intent}`] : []),
|
|
29
|
+
].join("\n"),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
return { output: error.message };
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { tool } from "@opencode-ai/plugin";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import yaml from "yaml";
|
|
5
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { findStructuralErrors, parseYamlFile, } from "../utils/yaml-structure.js";
|
|
6
6
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
7
|
export const verify_template = tool({
|
|
8
8
|
description: "Check if file in the given path exists and follows correct YAML structure by checking against the template file from /templates.",
|
|
@@ -20,39 +20,18 @@ export const verify_template = tool({
|
|
|
20
20
|
return { output: `Template '${templateName}' not found. Hint: Verify the template name is correct and exists in the /templates folder (e.g., 'orchestrator-state-development.yml').` };
|
|
21
21
|
}
|
|
22
22
|
let targetYaml;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
catch (error) {
|
|
28
|
-
return { output: `YAML Syntax Error in ${filePath}: ${error.message}. Hint: Check the file content for invalid YAML formatting, such as incorrect indentation or unescaped strings, and fix them.` };
|
|
23
|
+
const parsedTarget = parseYamlFile(absolutePath);
|
|
24
|
+
if (!parsedTarget.ok) {
|
|
25
|
+
return { output: `YAML Syntax Error in ${filePath}: ${parsedTarget.error}. Hint: Check the file content for invalid YAML formatting, such as incorrect indentation or unescaped strings, and fix them.` };
|
|
29
26
|
}
|
|
27
|
+
targetYaml = parsedTarget.data;
|
|
30
28
|
let templateYaml;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
return { output: `Internal Error: Failed to parse reference template YAML '${templateName}': ${error.message}. Hint: The template file itself contains invalid YAML syntax.` };
|
|
37
|
-
}
|
|
38
|
-
function checkStructure(templateObj, targetObj, currentPath = "") {
|
|
39
|
-
let errors = [];
|
|
40
|
-
if (typeof templateObj !== 'object' || templateObj === null) {
|
|
41
|
-
return errors;
|
|
42
|
-
}
|
|
43
|
-
for (const key of Object.keys(templateObj)) {
|
|
44
|
-
const newPath = currentPath ? `${currentPath}.${key}` : key;
|
|
45
|
-
if (typeof targetObj !== 'object' || targetObj === null || !(key in targetObj)) {
|
|
46
|
-
errors.push(`Missing key: ${newPath}`);
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
if (typeof templateObj[key] === 'object' && templateObj[key] !== null && !Array.isArray(templateObj[key])) {
|
|
50
|
-
errors.push(...checkStructure(templateObj[key], targetObj[key], newPath));
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return errors;
|
|
29
|
+
const parsedTemplate = parseYamlFile(templatePath);
|
|
30
|
+
if (!parsedTemplate.ok) {
|
|
31
|
+
return { output: `Internal Error: Failed to parse reference template YAML '${templateName}': ${parsedTemplate.error}. Hint: The template file itself contains invalid YAML syntax.` };
|
|
54
32
|
}
|
|
55
|
-
|
|
33
|
+
templateYaml = parsedTemplate.data;
|
|
34
|
+
const errors = findStructuralErrors(templateYaml, targetYaml);
|
|
56
35
|
if (errors.length > 0) {
|
|
57
36
|
return { output: `YAML Structure Validation Failed. Your file is missing the following required keys:\n- ${errors.join("\n- ")}\n\nHint: Update the file at ${filePath} to include these missing keys so it matches the structure of '${templateName}'.` };
|
|
58
37
|
}
|
package/dist/utils/file-util.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import matter from "gray-matter";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { PLUGIN_ROOT } from "
|
|
4
|
+
import { PLUGIN_ROOT } from "./plugin-info.js";
|
|
5
5
|
export const loadMarkdownDir = (dirPath) => {
|
|
6
6
|
const fullPath = path.join(PLUGIN_ROOT, dirPath);
|
|
7
7
|
let files;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import yaml from "yaml";
|
|
4
|
+
import { parseYamlFile } from "./yaml-structure.js";
|
|
5
|
+
/** Lowercase kebab-case, exactly 3-5 words of [a-z0-9] (Task Name Generation convention). */
|
|
6
|
+
export const RESEARCH_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+){2,4}$/;
|
|
7
|
+
const FORK_TERMINAL_STEP = "research-completed";
|
|
8
|
+
/**
|
|
9
|
+
* Copies the whole source task directory into a new date-prefixed directory
|
|
10
|
+
* named from the slug, then trims the copied state so the fork continues
|
|
11
|
+
* from the fork point onwards. The source task is never modified.
|
|
12
|
+
*/
|
|
13
|
+
export const forkTask = (input) => {
|
|
14
|
+
const cwd = input.cwd ?? process.cwd();
|
|
15
|
+
if (!input.slug || !RESEARCH_SLUG_PATTERN.test(input.slug)) {
|
|
16
|
+
throw new Error("INVALID_NAME: slug must be lowercase kebab-case, 3-5 words of [a-z0-9] (e.g. 'compare-caching-strategies')");
|
|
17
|
+
}
|
|
18
|
+
const taskRoot = path.resolve(cwd, input.taskRoot);
|
|
19
|
+
const researchRoot = path.join(taskRoot, "research");
|
|
20
|
+
const sourceDir = [
|
|
21
|
+
path.resolve(cwd, input.source),
|
|
22
|
+
path.resolve(researchRoot, input.source),
|
|
23
|
+
].find((candidate) => fs.existsSync(candidate) && fs.statSync(candidate).isDirectory());
|
|
24
|
+
if (sourceDir === undefined) {
|
|
25
|
+
throw new Error(`INVALID_SOURCE: no task directory at ${input.source}. Hint: pass the task path or a directory name under .owflow/tasks/research/.`);
|
|
26
|
+
}
|
|
27
|
+
if (!sourceDir.startsWith(researchRoot + path.sep)) {
|
|
28
|
+
throw new Error(`INVALID_SOURCE: ${input.source} is not a research task directory under .owflow/tasks/research/.`);
|
|
29
|
+
}
|
|
30
|
+
const parsedSourceState = parseYamlFile(path.join(sourceDir, "orchestrator-state.yml"));
|
|
31
|
+
if (!parsedSourceState.ok || !parsedSourceState.data) {
|
|
32
|
+
throw new Error(`INVALID_SOURCE: ${parsedSourceState.error ?? `no orchestrator-state.yml in ${input.source}`}`);
|
|
33
|
+
}
|
|
34
|
+
const sourceState = parsedSourceState.data;
|
|
35
|
+
const completed = Array.isArray(sourceState?.orchestrator?.completed_phases)
|
|
36
|
+
? sourceState.orchestrator.completed_phases.filter((s) => typeof s === "string")
|
|
37
|
+
: [];
|
|
38
|
+
if (input.from === FORK_TERMINAL_STEP) {
|
|
39
|
+
throw new Error(`UNKNOWN_STEP: ${FORK_TERMINAL_STEP} is not a valid fork point — nothing diverges after completion.`);
|
|
40
|
+
}
|
|
41
|
+
if (!completed.includes(input.from)) {
|
|
42
|
+
throw new Error(`UNKNOWN_STEP: ${input.from} is not completed in the source task. Completed steps: ${completed.join(", ") || "none"}`);
|
|
43
|
+
}
|
|
44
|
+
const forkName = `${new Date().toISOString().slice(0, 10)}-${input.slug}`;
|
|
45
|
+
const forkPath = path.join(researchRoot, forkName);
|
|
46
|
+
if (fs.existsSync(forkPath)) {
|
|
47
|
+
throw new Error(`NAME_TAKEN: a research task directory "${forkName}" already exists. Hint: ask the user for another short descriptive name (3-5 words).`);
|
|
48
|
+
}
|
|
49
|
+
fs.cpSync(sourceDir, forkPath, { recursive: true });
|
|
50
|
+
const forkState = structuredClone(sourceState);
|
|
51
|
+
forkState.orchestrator.started_phase = null;
|
|
52
|
+
forkState.orchestrator.completed_phases = completed.slice(0, completed.indexOf(input.from) + 1);
|
|
53
|
+
forkState.orchestrator.entry_point = "research-fork";
|
|
54
|
+
forkState.orchestrator.task_path = path
|
|
55
|
+
.relative(cwd, forkPath)
|
|
56
|
+
.split(path.sep)
|
|
57
|
+
.join("/");
|
|
58
|
+
forkState.orchestrator.options = {
|
|
59
|
+
...(forkState.orchestrator.options ?? {}),
|
|
60
|
+
fork_information: {
|
|
61
|
+
forked_from: path.basename(sourceDir),
|
|
62
|
+
fork_point: input.from,
|
|
63
|
+
forked_at: new Date().toISOString(),
|
|
64
|
+
...(input.intent ? { intent: input.intent } : {}),
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
forkState.task = {
|
|
68
|
+
...forkState.task,
|
|
69
|
+
status: "in_progress",
|
|
70
|
+
title: `${forkState.task?.title ?? "Research task"} (fork of ${path.basename(sourceDir)})`,
|
|
71
|
+
};
|
|
72
|
+
fs.writeFileSync(path.join(forkPath, "orchestrator-state.yml"), yaml.stringify(forkState), "utf8");
|
|
73
|
+
return { forkPath, forkName, forkPoint: input.from };
|
|
74
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
export const PLUGIN_ROOT = path.join(__dirname, ".."); // Points to dist/ where skills/commands/agents are copied
|
|
6
|
+
const UNKNOWN_VERSION = "unknown";
|
|
7
|
+
export const readPluginVersion = (pluginRoot = PLUGIN_ROOT) => {
|
|
8
|
+
try {
|
|
9
|
+
const packageJsonPath = path.join(pluginRoot, "..", "package.json");
|
|
10
|
+
const raw = fs.readFileSync(packageJsonPath, "utf8");
|
|
11
|
+
const version = JSON.parse(raw).version;
|
|
12
|
+
return typeof version === "string" && version.length > 0
|
|
13
|
+
? version
|
|
14
|
+
: UNKNOWN_VERSION;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return UNKNOWN_VERSION;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
export const PLUGIN_VERSION = readPluginVersion();
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import yaml from "yaml";
|
|
3
|
+
export const parseYamlFile = (absolutePath) => {
|
|
4
|
+
try {
|
|
5
|
+
const fileContent = fs.readFileSync(absolutePath, "utf8");
|
|
6
|
+
return { ok: true, data: yaml.parse(fileContent) };
|
|
7
|
+
}
|
|
8
|
+
catch (error) {
|
|
9
|
+
return { ok: false, error: error.message };
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
export const findStructuralErrors = (templateObj, targetObj, currentPath = "") => {
|
|
13
|
+
const errors = [];
|
|
14
|
+
if (typeof templateObj !== "object" || templateObj === null) {
|
|
15
|
+
return errors;
|
|
16
|
+
}
|
|
17
|
+
for (const key of Object.keys(templateObj)) {
|
|
18
|
+
const newPath = currentPath ? `${currentPath}.${key}` : key;
|
|
19
|
+
if (typeof targetObj !== "object" ||
|
|
20
|
+
targetObj === null ||
|
|
21
|
+
!(key in targetObj)) {
|
|
22
|
+
errors.push(`Missing key: ${newPath}`);
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (typeof templateObj[key] === "object" &&
|
|
26
|
+
templateObj[key] !== null &&
|
|
27
|
+
!Array.isArray(templateObj[key])) {
|
|
28
|
+
errors.push(...findStructuralErrors(templateObj[key], targetObj[key], newPath));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return errors;
|
|
32
|
+
};
|
package/package.json
CHANGED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: owflow:agents-md-generator
|
|
3
|
-
description: Generate AGENTS.md onboarding document for AI coding agents
|
|
4
|
-
argument-hint: "[directory or file path]"
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
CRITICAL INSTRUCTION: You MUST invoke the owflow:agents-md-generator skill immediately as your FIRST action.
|
|
8
|
-
|
|
9
|
-
Use the Skill tool with these exact parameters:
|
|
10
|
-
name: "agents-md-generator"
|
|
11
|
-
prompt: "$ARGUMENTS"
|
|
12
|
-
|
|
13
|
-
DO NOT:
|
|
14
|
-
|
|
15
|
-
- Analyze the task before invoking the skill
|
|
16
|
-
- Decide the task is "straightforward" and skip the skill
|
|
17
|
-
- Substitute your own approach or workflow
|
|
18
|
-
- Execute any part of the workflow yourself
|
|
19
|
-
|
|
20
|
-
WHY: The user explicitly chose this workflow by using /owflow:agents-md-generator.
|
|
21
|
-
Invoke the skill now and let it orchestrate the complete workflow.
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## About This Workflow
|
|
26
|
-
|
|
27
|
-
Generates an `AGENTS.md` onboarding document for AI coding agents working in a repository. Inspects the repo (package manifest, README, scripts, lint/test config, layout, commit history) and writes a concise, reference-heavy contributor guide.
|
|
28
|
-
|
|
29
|
-
The skill handles:
|
|
30
|
-
|
|
31
|
-
- Repo-level vs directory-level scope detection
|
|
32
|
-
- Create path (fresh generation) and Update path (surgical edits)
|
|
33
|
-
- Quality guards enforcing length, specificity, and structure
|
|
34
|
-
- Parallel discovery via subagents for large repos
|
|
35
|
-
|
|
36
|
-
See `skills/agents-md-generator/SKILL.md` for complete workflow documentation.
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: owflow:dev-analyze
|
|
3
|
-
description: Development — codebase analysis, clarifications, gap analysis & scope decisions
|
|
4
|
-
argument-hint: "[task-path-or-identifier]"
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
CRITICAL INSTRUCTION: You MUST invoke the owflow:dev-analyze skill immediately as your FIRST action.
|
|
8
|
-
|
|
9
|
-
Use the Skill tool with these exact parameters:
|
|
10
|
-
name: "dev-analyze"
|
|
11
|
-
prompt: "$ARGUMENTS"
|
|
12
|
-
|
|
13
|
-
DO NOT:
|
|
14
|
-
|
|
15
|
-
- Analyze the task before invoking the skill
|
|
16
|
-
- Decide the task is "straightforward" and skip the skill
|
|
17
|
-
- Substitute your own approach or workflow
|
|
18
|
-
- Execute any part of the workflow yourself
|
|
19
|
-
|
|
20
|
-
WHY: The user explicitly chose this workflow phase by using /owflow:dev-analyze.
|
|
21
|
-
Invoke the skill now and let it execute the phase, update orchestrator state,
|
|
22
|
-
and present results with next-step suggestions.
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## About This Workflow
|
|
27
|
-
|
|
28
|
-
Part of the development workflow. See `skills/dev-analyze/SKILL.md` for complete phase documentation.
|
|
29
|
-
|
|
30
|
-
Entry points:
|
|
31
|
-
|
|
32
|
-
- `/owflow:development <description>` — start a new development task (assisted mode)
|
|
33
|
-
- `/owflow:goal-development <description>` — run all phases in one loop (orchestrated mode)
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: owflow:dev-bugfix
|
|
3
|
-
description: Quick bug fix with TDD red/green gates and complexity escalation — standard dev-task state, continuable by any dev-* subskill
|
|
4
|
-
argument-hint: "[bug description | task-path-or-identifier]"
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
CRITICAL INSTRUCTION: You MUST invoke the owflow:dev-bugfix skill immediately as your FIRST action.
|
|
8
|
-
|
|
9
|
-
Use the Skill tool with these exact parameters:
|
|
10
|
-
name: "dev-bugfix"
|
|
11
|
-
prompt: "$ARGUMENTS"
|
|
12
|
-
|
|
13
|
-
DO NOT:
|
|
14
|
-
|
|
15
|
-
- Analyze the task before invoking the skill
|
|
16
|
-
- Decide the task is "straightforward" and skip the skill
|
|
17
|
-
- Substitute your own approach or workflow
|
|
18
|
-
- Execute any part of the workflow yourself
|
|
19
|
-
|
|
20
|
-
WHY: The user explicitly chose this workflow by using /owflow:dev-bugfix.
|
|
21
|
-
Invoke the skill now and let it orchestrate the complete workflow.
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## About This Workflow
|
|
26
|
-
|
|
27
|
-
Quick bug fix with TDD red/green gates and complexity escalation. An alternative entry point into the dev-* workflow: bootstraps (or resumes) a standard development task with `orchestrator-state.yml`.
|
|
28
|
-
|
|
29
|
-
Pass a bug description for a fresh fix, or a task path/identifier (under `.owflow/tasks/development/`) to fix a newly emerging problem on an existing development task.
|
|
30
|
-
The skill handles:
|
|
31
|
-
|
|
32
|
-
- Task directory creation and state management
|
|
33
|
-
- Phase execution with interactive gates
|
|
34
|
-
- Subagent delegation for specialized work
|
|
35
|
-
- Pause/resume capability
|
|
36
|
-
|
|
37
|
-
See `skills/dev-bugfix/SKILL.md` for complete workflow documentation.
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: owflow:dev-finalize
|
|
3
|
-
description: Development — E2E testing, user documentation & finalization
|
|
4
|
-
argument-hint: "[task-path-or-identifier]"
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
CRITICAL INSTRUCTION: You MUST invoke the owflow:dev-finalize skill immediately as your FIRST action.
|
|
8
|
-
|
|
9
|
-
Use the Skill tool with these exact parameters:
|
|
10
|
-
name: "dev-finalize"
|
|
11
|
-
prompt: "$ARGUMENTS"
|
|
12
|
-
|
|
13
|
-
DO NOT:
|
|
14
|
-
|
|
15
|
-
- Analyze the task before invoking the skill
|
|
16
|
-
- Decide the task is "straightforward" and skip the skill
|
|
17
|
-
- Substitute your own approach or workflow
|
|
18
|
-
- Execute any part of the workflow yourself
|
|
19
|
-
|
|
20
|
-
WHY: The user explicitly chose this workflow phase by using /owflow:dev-finalize.
|
|
21
|
-
Invoke the skill now and let it execute the phase, update orchestrator state,
|
|
22
|
-
and present results with next-step suggestions.
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## About This Workflow
|
|
27
|
-
|
|
28
|
-
Part of the development workflow. See `skills/dev-finalize/SKILL.md` for complete phase documentation.
|
|
29
|
-
|
|
30
|
-
Entry points:
|
|
31
|
-
|
|
32
|
-
- `/owflow:development <description>` — start a new development task (assisted mode)
|
|
33
|
-
- `/owflow:goal-development <description>` — run all phases in one loop (orchestrated mode)
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: owflow:dev-implement
|
|
3
|
-
description: Development — execute implementation plan & TDD Green Gate. --quick bootstraps a condensed task when no state file exists.
|
|
4
|
-
argument-hint: "[task-path-or-identifier | \"description\"] [--quick]"
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
CRITICAL INSTRUCTION: You MUST invoke the owflow:dev-implement skill immediately as your FIRST action.
|
|
8
|
-
|
|
9
|
-
Use the Skill tool with these exact parameters:
|
|
10
|
-
name: "dev-implement"
|
|
11
|
-
prompt: "$ARGUMENTS"
|
|
12
|
-
|
|
13
|
-
DO NOT:
|
|
14
|
-
|
|
15
|
-
- Analyze the task before invoking the skill
|
|
16
|
-
- Decide the task is "straightforward" and skip the skill
|
|
17
|
-
- Substitute your own approach or workflow
|
|
18
|
-
- Execute any part of the workflow yourself
|
|
19
|
-
|
|
20
|
-
WHY: The user explicitly chose this workflow phase by using /owflow:dev-implement.
|
|
21
|
-
Invoke the skill now and let it execute the phase, update orchestrator state,
|
|
22
|
-
and present results with next-step suggestions.
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## About This Workflow
|
|
27
|
-
|
|
28
|
-
Part of the development workflow. See `skills/dev-implement/SKILL.md` for complete phase documentation.
|
|
29
|
-
|
|
30
|
-
Entry points:
|
|
31
|
-
|
|
32
|
-
- `/owflow:development <description>` — start a new development task (assisted mode)
|
|
33
|
-
- `/owflow:goal-development <description>` — run all phases in one loop (orchestrated mode)
|
|
34
|
-
- `/owflow:dev-implement --quick "<description>"` — condensed quick task: bootstraps a standard development task (state + condensed spec/plan) here, implements it directly with standards, then stops — continuable with `/owflow:dev-verify` or any dev-* subskill
|