@bx-h/meta-flow 0.1.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/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +172 -0
- package/bin/meta-flow.js +7 -0
- package/examples/sample-task/adjudication-report.json +14 -0
- package/examples/sample-task/final-report.md +26 -0
- package/examples/sample-task/goal-contract.json +44 -0
- package/examples/sample-task/milestone-plan.json +31 -0
- package/examples/sample-task/milestones/M1/direction-evaluation.json +16 -0
- package/examples/sample-task/milestones/M1/task-list.json +41 -0
- package/examples/sample-task/milestones/M1/tasks/T1/execution-report.json +25 -0
- package/examples/sample-task/milestones/M1/tasks/T1/task-spec.json +37 -0
- package/examples/sample-task/milestones/M1/tasks/T1/verification-report.json +23 -0
- package/examples/sample-task/proposal-summary.md +21 -0
- package/examples/sample-task/proposal.md +29 -0
- package/examples/sample-task/questioning-report.json +31 -0
- package/examples/sample-task/raw-request.md +1 -0
- package/examples/sample-task/review-aggregate.json +34 -0
- package/examples/sample-task/reviews/product-reviewer.json +16 -0
- package/examples/sample-task/reviews/risk-reviewer.json +16 -0
- package/examples/sample-task/reviews/technical-reviewer.json +16 -0
- package/examples/sample-task/reviews/verification-reviewer.json +15 -0
- package/examples/sample-task/state.json +35 -0
- package/marketplace/marketplace.json +20 -0
- package/package.json +50 -0
- package/plugin/.codex-plugin/plugin.json +36 -0
- package/plugin/agent-templates/adjudicator.toml +26 -0
- package/plugin/agent-templates/direction-evaluator.toml +18 -0
- package/plugin/agent-templates/executor.toml +21 -0
- package/plugin/agent-templates/final-summarizer.toml +17 -0
- package/plugin/agent-templates/planner.toml +18 -0
- package/plugin/agent-templates/product-reviewer.toml +15 -0
- package/plugin/agent-templates/proposal-summarizer.toml +17 -0
- package/plugin/agent-templates/questioner.toml +22 -0
- package/plugin/agent-templates/researcher-proposer.toml +18 -0
- package/plugin/agent-templates/result-verifier.toml +20 -0
- package/plugin/agent-templates/risk-reviewer.toml +14 -0
- package/plugin/agent-templates/task-decomposer.toml +19 -0
- package/plugin/agent-templates/technical-reviewer.toml +14 -0
- package/plugin/agent-templates/verification-reviewer.toml +15 -0
- package/plugin/assets/icon.svg +10 -0
- package/plugin/assets/screenshot-placeholder.md +3 -0
- package/plugin/scripts/_common.py +95 -0
- package/plugin/scripts/aggregate_reviews.py +99 -0
- package/plugin/scripts/new_task.py +78 -0
- package/plugin/scripts/status.py +95 -0
- package/plugin/scripts/validate_adjudication.py +47 -0
- package/plugin/scripts/validate_goal_contract.py +56 -0
- package/plugin/scripts/validate_milestone_plan.py +49 -0
- package/plugin/scripts/validate_task_list.py +59 -0
- package/plugin/scripts/validate_task_verification.py +49 -0
- package/plugin/skills/meta-flow/SKILL.md +159 -0
- package/plugin/skills/meta-flow/references/adjudication-policy.md +30 -0
- package/plugin/skills/meta-flow/references/direction-evaluation-policy.md +24 -0
- package/plugin/skills/meta-flow/references/execution-policy.md +35 -0
- package/plugin/skills/meta-flow/references/review-rubric.md +28 -0
- package/plugin/skills/meta-flow/references/role-contracts.md +29 -0
- package/plugin/templates/adjudication-report.json +11 -0
- package/plugin/templates/direction-evaluation.json +16 -0
- package/plugin/templates/final-report.md +11 -0
- package/plugin/templates/goal-contract.json +20 -0
- package/plugin/templates/milestone-plan.json +15 -0
- package/plugin/templates/proposal-summary.md +11 -0
- package/plugin/templates/proposal.md +17 -0
- package/plugin/templates/questioning-report.json +15 -0
- package/plugin/templates/raw-request.md +3 -0
- package/plugin/templates/review-aggregate.json +14 -0
- package/plugin/templates/reviewer-report.json +10 -0
- package/plugin/templates/state.json +23 -0
- package/plugin/templates/task-execution-report.json +13 -0
- package/plugin/templates/task-list.json +20 -0
- package/plugin/templates/task-spec.json +16 -0
- package/plugin/templates/task-verification-report.json +13 -0
- package/src/cli/commands/doctor.js +171 -0
- package/src/cli/commands/install.js +120 -0
- package/src/cli/commands/print_paths.js +20 -0
- package/src/cli/commands/uninstall.js +56 -0
- package/src/cli/commands/verify.js +197 -0
- package/src/cli/index.js +53 -0
- package/src/cli/lib/agents.js +89 -0
- package/src/cli/lib/args.js +49 -0
- package/src/cli/lib/codex_config.js +106 -0
- package/src/cli/lib/fs_safe.js +142 -0
- package/src/cli/lib/logger.js +23 -0
- package/src/cli/lib/marketplace.js +72 -0
- package/src/cli/lib/paths.js +37 -0
- package/src/cli/lib/plugin.js +57 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Execution Policy
|
|
2
|
+
|
|
3
|
+
Execution is concrete-task scoped.
|
|
4
|
+
|
|
5
|
+
## Planner
|
|
6
|
+
|
|
7
|
+
The planner writes milestone-level checkpoints with objective, scope, out-of-scope, acceptance checks, expected outputs, risk, and status. It does not create implementation steps.
|
|
8
|
+
|
|
9
|
+
## Task Decomposer
|
|
10
|
+
|
|
11
|
+
The task decomposer writes concrete tasks for one milestone. Each task must be independently executable and independently verifiable.
|
|
12
|
+
|
|
13
|
+
## Executor
|
|
14
|
+
|
|
15
|
+
The executor:
|
|
16
|
+
|
|
17
|
+
- handles one concrete task at a time
|
|
18
|
+
- changes only `allowed_files`
|
|
19
|
+
- avoids broad refactors
|
|
20
|
+
- records changed files, commands, evidence, discovered facts, and risk flags
|
|
21
|
+
- marks blocked instead of silently expanding scope
|
|
22
|
+
|
|
23
|
+
## Result Verifier
|
|
24
|
+
|
|
25
|
+
The verifier:
|
|
26
|
+
|
|
27
|
+
- checks one concrete task against `task-spec.json`
|
|
28
|
+
- may run tests, lint, typecheck, scripts, or diff review
|
|
29
|
+
- outputs pass/revise/block
|
|
30
|
+
- gives minimal repair instructions on failure
|
|
31
|
+
- does not modify implementation files
|
|
32
|
+
|
|
33
|
+
## Repair Limit
|
|
34
|
+
|
|
35
|
+
Each concrete task has at most 2 repair attempts. After that, mark blocked and route to adjudicator or user.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Review Rubric
|
|
2
|
+
|
|
3
|
+
Reviewer output must be a `reviewer-report.json` object with:
|
|
4
|
+
|
|
5
|
+
- `decision`: `pass`, `revise`, or `block`
|
|
6
|
+
- `confidence`: number from 0 to 1
|
|
7
|
+
- concrete evidence references
|
|
8
|
+
- blocking issues, suggested changes, and missing information
|
|
9
|
+
|
|
10
|
+
## Product Review
|
|
11
|
+
|
|
12
|
+
Pass only when the proposal directly addresses the refined goal, respects non-goals, and preserves user value. Revise if scope or value is blurry. Block if it solves the wrong problem.
|
|
13
|
+
|
|
14
|
+
## Technical Review
|
|
15
|
+
|
|
16
|
+
Pass only when the implementation route is feasible, simple enough, and compatible with the local architecture. Revise if complexity is avoidable. Block if the route is structurally unsafe or impossible under constraints.
|
|
17
|
+
|
|
18
|
+
## Risk Review
|
|
19
|
+
|
|
20
|
+
Pass only when safety, data, permission, destructive action, rollback, and operational risks are identified and controlled. Block high-risk work that lacks mitigation.
|
|
21
|
+
|
|
22
|
+
## Verification Review
|
|
23
|
+
|
|
24
|
+
Pass only when every acceptance criterion can be observed, repeated, and evidenced. Revise vague checks. Block when success cannot be verified.
|
|
25
|
+
|
|
26
|
+
## Duplication Rule
|
|
27
|
+
|
|
28
|
+
Reviewers may read prior reviewer reports to avoid repeating the same issue. They should cite agreement instead of restating generic feedback.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Meta-Flow Role Contracts
|
|
2
|
+
|
|
3
|
+
Each role has a narrow contract. Do not merge roles even when a task looks small.
|
|
4
|
+
|
|
5
|
+
## Proposal Roles
|
|
6
|
+
|
|
7
|
+
- `questioner`: turns ambiguity into a small set of high-value questions and a draft goal contract. It does not propose an implementation.
|
|
8
|
+
- `researcher_proposer`: researches and writes `proposal.md`. It does not change the goal contract.
|
|
9
|
+
- `product_reviewer`: checks whether the proposal solves the user's real goal.
|
|
10
|
+
- `technical_reviewer`: checks feasibility, complexity, dependencies, and maintainability.
|
|
11
|
+
- `risk_reviewer`: checks safety, data, permission, destructive, rollback, and operational risks.
|
|
12
|
+
- `verification_reviewer`: checks whether acceptance criteria are observable and repeatable.
|
|
13
|
+
- `adjudicator`: resolves reviewer conflicts and routes the workflow. It does not write code or edit the proposal directly.
|
|
14
|
+
- `proposal_summarizer`: compresses accepted proposal material for user confirmation without adding new claims.
|
|
15
|
+
- `user_confirmation`: a human gate, not an agent.
|
|
16
|
+
|
|
17
|
+
## Execution Roles
|
|
18
|
+
|
|
19
|
+
- `planner`: creates milestone-level plan only.
|
|
20
|
+
- `task_decomposer`: decomposes the current milestone into concrete tasks.
|
|
21
|
+
- `executor`: executes exactly one concrete task.
|
|
22
|
+
- `result_verifier`: verifies exactly one concrete task.
|
|
23
|
+
- `direction_evaluator`: checks whether the plan and goal remain valid after a milestone or major new finding.
|
|
24
|
+
- `final_summarizer`: reports completion, evidence, gaps, and residual risk.
|
|
25
|
+
- `user_final_confirmation`: a human gate, not an agent.
|
|
26
|
+
|
|
27
|
+
## Grain Rule
|
|
28
|
+
|
|
29
|
+
Milestones are stage checkpoints. Concrete tasks are the executor/verifier unit. Never ask executor or verifier to handle an entire milestone.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"task_id": "__TASK_ID__",
|
|
3
|
+
"source": "reviewers",
|
|
4
|
+
"decision": "revise_proposal",
|
|
5
|
+
"rationale": "string",
|
|
6
|
+
"resolved_conflicts": [],
|
|
7
|
+
"deduplicated_issues": [],
|
|
8
|
+
"instructions_for_next_agent": "string",
|
|
9
|
+
"next_phase": "PROPOSAL_REWORK",
|
|
10
|
+
"requires_user_confirmation": false
|
|
11
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"task_id": "__TASK_ID__",
|
|
3
|
+
"milestone_id": "M1",
|
|
4
|
+
"decision": "continue",
|
|
5
|
+
"summary": "string",
|
|
6
|
+
"new_findings": [],
|
|
7
|
+
"invalidated_assumptions": [],
|
|
8
|
+
"goal_drift_detected": false,
|
|
9
|
+
"proposal_patch_needed": false,
|
|
10
|
+
"contract_patch": {
|
|
11
|
+
"proposed_changes": [],
|
|
12
|
+
"reason": "string",
|
|
13
|
+
"requires_user_confirmation": true
|
|
14
|
+
},
|
|
15
|
+
"next_phase": "CONTINUE_NEXT_MILESTONE"
|
|
16
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"task_id": "__TASK_ID__",
|
|
3
|
+
"raw_user_request": "string",
|
|
4
|
+
"refined_goal": "string",
|
|
5
|
+
"problem_boundary": "string",
|
|
6
|
+
"non_goals": [],
|
|
7
|
+
"assumptions": [],
|
|
8
|
+
"constraints": [],
|
|
9
|
+
"success_definition": "string",
|
|
10
|
+
"acceptance_criteria": [
|
|
11
|
+
{
|
|
12
|
+
"id": "AC1",
|
|
13
|
+
"criterion": "string",
|
|
14
|
+
"verification_method": "manual",
|
|
15
|
+
"evidence_required": "string"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"risk_level": "medium",
|
|
19
|
+
"requires_user_confirmation": true
|
|
20
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Proposal
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Describe the refined goal from `goal-contract.json`.
|
|
6
|
+
|
|
7
|
+
## Recommended Approach
|
|
8
|
+
|
|
9
|
+
Explain the primary plan, tradeoffs, risk controls, and validation path.
|
|
10
|
+
|
|
11
|
+
## Alternatives
|
|
12
|
+
|
|
13
|
+
List viable alternatives and why they are not preferred.
|
|
14
|
+
|
|
15
|
+
## Open Issues
|
|
16
|
+
|
|
17
|
+
Record issues that need adjudication or user confirmation.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"task_id": "__TASK_ID__",
|
|
3
|
+
"raw_user_request": "string",
|
|
4
|
+
"known_information": [],
|
|
5
|
+
"missing_information": [],
|
|
6
|
+
"clarifying_questions": [
|
|
7
|
+
{
|
|
8
|
+
"question": "string",
|
|
9
|
+
"why_it_matters": "string",
|
|
10
|
+
"blocking": true
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"assumptions_if_user_does_not_answer": [],
|
|
14
|
+
"can_continue_without_user_answer": false
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"task_id": "__TASK_ID__",
|
|
3
|
+
"overall_mechanical_result": "revise",
|
|
4
|
+
"reviewers": [
|
|
5
|
+
{
|
|
6
|
+
"reviewer": "string",
|
|
7
|
+
"decision": "revise",
|
|
8
|
+
"confidence": 0.7
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"all_blocking_issues": [],
|
|
12
|
+
"all_suggested_changes": [],
|
|
13
|
+
"all_missing_information": []
|
|
14
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"task_id": "__TASK_ID__",
|
|
3
|
+
"phase": "INTAKE",
|
|
4
|
+
"created_at": "__CREATED_AT__",
|
|
5
|
+
"updated_at": "__UPDATED_AT__",
|
|
6
|
+
"proposal_review_round": 0,
|
|
7
|
+
"direction_adjustment_round": 0,
|
|
8
|
+
"current_milestone_id": null,
|
|
9
|
+
"current_task_id": null,
|
|
10
|
+
"max_proposal_rework_rounds": 3,
|
|
11
|
+
"max_task_repair_rounds": 2,
|
|
12
|
+
"max_direction_adjustment_rounds": 2,
|
|
13
|
+
"status": "active",
|
|
14
|
+
"last_route_decision": "Task initialized.",
|
|
15
|
+
"history": [
|
|
16
|
+
{
|
|
17
|
+
"at": "__CREATED_AT__",
|
|
18
|
+
"from_phase": "NONE",
|
|
19
|
+
"to_phase": "INTAKE",
|
|
20
|
+
"reason": "Raw request captured."
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"task_id": "__TASK_ID__",
|
|
3
|
+
"milestone_id": "M1",
|
|
4
|
+
"concrete_task_id": "T1",
|
|
5
|
+
"status": "done",
|
|
6
|
+
"summary": "string",
|
|
7
|
+
"changed_files": [],
|
|
8
|
+
"commands_run": [],
|
|
9
|
+
"evidence": [],
|
|
10
|
+
"discovered_facts": [],
|
|
11
|
+
"risk_flags": [],
|
|
12
|
+
"notes_for_verifier": "string"
|
|
13
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"task_id": "__TASK_ID__",
|
|
3
|
+
"milestone_id": "M1",
|
|
4
|
+
"tasks": [
|
|
5
|
+
{
|
|
6
|
+
"id": "T1",
|
|
7
|
+
"objective": "string",
|
|
8
|
+
"inputs": [],
|
|
9
|
+
"expected_outputs": [],
|
|
10
|
+
"allowed_files": [],
|
|
11
|
+
"forbidden_files": [],
|
|
12
|
+
"allowed_commands": [],
|
|
13
|
+
"acceptance_checks": [],
|
|
14
|
+
"dependencies": [],
|
|
15
|
+
"risk": "medium",
|
|
16
|
+
"repair_attempts": 0,
|
|
17
|
+
"status": "pending"
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"task_id": "__TASK_ID__",
|
|
3
|
+
"milestone_id": "M1",
|
|
4
|
+
"concrete_task_id": "T1",
|
|
5
|
+
"objective": "string",
|
|
6
|
+
"inputs": [],
|
|
7
|
+
"expected_outputs": [],
|
|
8
|
+
"allowed_files": [],
|
|
9
|
+
"forbidden_files": [],
|
|
10
|
+
"allowed_commands": [],
|
|
11
|
+
"acceptance_checks": [],
|
|
12
|
+
"dependencies": [],
|
|
13
|
+
"risk": "medium",
|
|
14
|
+
"repair_attempts": 0,
|
|
15
|
+
"status": "pending"
|
|
16
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"task_id": "__TASK_ID__",
|
|
3
|
+
"milestone_id": "M1",
|
|
4
|
+
"concrete_task_id": "T1",
|
|
5
|
+
"decision": "pass",
|
|
6
|
+
"checks_run": [],
|
|
7
|
+
"passed_checks": [],
|
|
8
|
+
"failed_checks": [],
|
|
9
|
+
"evidence_refs": [],
|
|
10
|
+
"minimal_repair_instructions": "",
|
|
11
|
+
"new_findings": [],
|
|
12
|
+
"should_trigger_direction_evaluation": false
|
|
13
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { parseOptions, helpRequested } from "../lib/args.js";
|
|
5
|
+
import { AGENT_FILES, validateAgentTemplate } from "../lib/agents.js";
|
|
6
|
+
import { inspectCodexConfig } from "../lib/codex_config.js";
|
|
7
|
+
import { pathExists, readJsonOrDefault } from "../lib/fs_safe.js";
|
|
8
|
+
import { createLogger } from "../lib/logger.js";
|
|
9
|
+
import { resolveTargets, sampleTaskRoot } from "../lib/paths.js";
|
|
10
|
+
|
|
11
|
+
export function doctorHelp() {
|
|
12
|
+
return `Usage: meta-flow doctor --scope repo|user [--target <path>] [--verbose]`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function runDoctor(argv = []) {
|
|
16
|
+
if (helpRequested(argv)) {
|
|
17
|
+
console.log(doctorHelp());
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
const { options } = parseOptions(argv, {
|
|
21
|
+
scope: "string",
|
|
22
|
+
target: "string"
|
|
23
|
+
});
|
|
24
|
+
const targets = resolveTargets({ scope: options.scope || "repo", target: options.target });
|
|
25
|
+
const logger = createLogger({ verbose: options.verbose });
|
|
26
|
+
const results = [];
|
|
27
|
+
|
|
28
|
+
results.push(checkCodexCli());
|
|
29
|
+
results.push(await checkPlugin(targets));
|
|
30
|
+
results.push(await checkMarketplace(targets));
|
|
31
|
+
results.push(await checkAgents(targets));
|
|
32
|
+
results.push(await checkConfig(targets));
|
|
33
|
+
results.push(await checkPythonScripts(targets));
|
|
34
|
+
results.push(await checkSampleTask(targets));
|
|
35
|
+
|
|
36
|
+
for (const result of results) {
|
|
37
|
+
logger[result.level.toLowerCase()](`${result.title}${result.message ? ` - ${result.message}` : ""}`);
|
|
38
|
+
}
|
|
39
|
+
const hasFail = results.some((result) => result.level === "FAIL");
|
|
40
|
+
return hasFail ? 1 : 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function checkCodexCli() {
|
|
44
|
+
const found = spawnSync("codex", ["--version"], { encoding: "utf8" });
|
|
45
|
+
if (found.status === 0) {
|
|
46
|
+
return { level: "PASS", title: "Codex CLI found" };
|
|
47
|
+
}
|
|
48
|
+
return { level: "WARN", title: "Codex CLI not found", message: "Install Codex CLI or use the Codex desktop app plugin loader." };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function checkPlugin(targets) {
|
|
52
|
+
const manifestPath = path.join(targets.pluginTarget, ".codex-plugin", "plugin.json");
|
|
53
|
+
const skillPath = path.join(targets.pluginTarget, "skills", "meta-flow", "SKILL.md");
|
|
54
|
+
if (!(await pathExists(manifestPath))) {
|
|
55
|
+
return { level: "FAIL", title: "plugin manifest missing", message: `Run meta-flow install --scope ${targets.scope}` };
|
|
56
|
+
}
|
|
57
|
+
const manifest = JSON.parse(await fs.readFile(manifestPath, "utf8"));
|
|
58
|
+
if (manifest.name !== "meta-flow") {
|
|
59
|
+
return { level: "FAIL", title: "plugin manifest invalid", message: "Expected name=meta-flow." };
|
|
60
|
+
}
|
|
61
|
+
if (!(await pathExists(skillPath))) {
|
|
62
|
+
return { level: "FAIL", title: "SKILL.md missing", message: "Re-run install." };
|
|
63
|
+
}
|
|
64
|
+
const skill = await fs.readFile(skillPath, "utf8");
|
|
65
|
+
if (!/^---\n[\s\S]*?name:\s*meta-flow[\s\S]*?---/m.test(skill)) {
|
|
66
|
+
return { level: "FAIL", title: "SKILL.md frontmatter invalid", message: "Reinstall plugin files." };
|
|
67
|
+
}
|
|
68
|
+
return { level: "PASS", title: "plugin and skill present" };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function checkMarketplace(targets) {
|
|
72
|
+
if (!(await pathExists(targets.marketplaceTarget))) {
|
|
73
|
+
return { level: "FAIL", title: "marketplace missing", message: "Run install to create marketplace.json." };
|
|
74
|
+
}
|
|
75
|
+
const marketplace = await readJsonOrDefault(targets.marketplaceTarget, {});
|
|
76
|
+
const entry = Array.isArray(marketplace.plugins)
|
|
77
|
+
? marketplace.plugins.find((plugin) => plugin?.name === "meta-flow")
|
|
78
|
+
: null;
|
|
79
|
+
if (!entry) {
|
|
80
|
+
return { level: "FAIL", title: "marketplace entry missing", message: "Run install to add meta-flow entry." };
|
|
81
|
+
}
|
|
82
|
+
return { level: "PASS", title: "marketplace entry present" };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function checkAgents(targets) {
|
|
86
|
+
const missing = [];
|
|
87
|
+
const invalid = [];
|
|
88
|
+
for (const fileName of AGENT_FILES) {
|
|
89
|
+
const filePath = path.join(targets.agentsTarget, fileName);
|
|
90
|
+
if (!(await pathExists(filePath))) {
|
|
91
|
+
missing.push(fileName);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const result = await validateAgentTemplate(filePath);
|
|
95
|
+
if (result.missing.length) {
|
|
96
|
+
invalid.push(`${fileName}:${result.missing.join(",")}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (missing.length || invalid.length) {
|
|
100
|
+
return {
|
|
101
|
+
level: "FAIL",
|
|
102
|
+
title: "custom agents incomplete",
|
|
103
|
+
message: `missing=[${missing.join(", ")}] invalid=[${invalid.join(", ")}]`
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return { level: "PASS", title: "custom agents present" };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function checkConfig(targets) {
|
|
110
|
+
const config = await inspectCodexConfig(targets.codexConfigTarget);
|
|
111
|
+
if (!config.exists) {
|
|
112
|
+
return { level: "FAIL", title: "Codex config missing", message: "Run install to create .codex/config.toml." };
|
|
113
|
+
}
|
|
114
|
+
if (!config.hasAgents || !config.hasMaxThreads || !config.hasMaxDepth) {
|
|
115
|
+
return { level: "WARN", title: "Codex agents config incomplete", message: "Run install --force to patch existing settings if desired." };
|
|
116
|
+
}
|
|
117
|
+
return { level: "PASS", title: "Codex agents config present" };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function checkPythonScripts(targets) {
|
|
121
|
+
const scriptsDir = path.join(targets.pluginTarget, "scripts");
|
|
122
|
+
const scriptPath = path.join(scriptsDir, "validate_goal_contract.py");
|
|
123
|
+
if (!(await pathExists(scriptPath))) {
|
|
124
|
+
return { level: "FAIL", title: "Python scripts missing", message: "Reinstall plugin files." };
|
|
125
|
+
}
|
|
126
|
+
const result = spawnSync(resolvePython(), [scriptPath, "--help"], pythonOptions());
|
|
127
|
+
if (result.status !== 0) {
|
|
128
|
+
return { level: "FAIL", title: "Python scripts not runnable", message: result.stderr || result.stdout };
|
|
129
|
+
}
|
|
130
|
+
return { level: "PASS", title: "Python scripts runnable" };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function checkSampleTask(targets) {
|
|
134
|
+
if (!(await pathExists(sampleTaskRoot))) {
|
|
135
|
+
return { level: "WARN", title: "sample task unavailable", message: "Package examples are not present." };
|
|
136
|
+
}
|
|
137
|
+
const python = resolvePython();
|
|
138
|
+
const scripts = path.join(targets.pluginTarget, "scripts");
|
|
139
|
+
const runs = [
|
|
140
|
+
[path.join(scripts, "validate_goal_contract.py"), path.join(sampleTaskRoot, "goal-contract.json")],
|
|
141
|
+
[path.join(scripts, "validate_adjudication.py"), path.join(sampleTaskRoot, "adjudication-report.json")],
|
|
142
|
+
[path.join(scripts, "validate_milestone_plan.py"), path.join(sampleTaskRoot, "milestone-plan.json")],
|
|
143
|
+
[path.join(scripts, "validate_task_list.py"), path.join(sampleTaskRoot, "milestones", "M1", "task-list.json")],
|
|
144
|
+
[
|
|
145
|
+
path.join(scripts, "validate_task_verification.py"),
|
|
146
|
+
path.join(sampleTaskRoot, "milestones", "M1", "tasks", "T1", "verification-report.json")
|
|
147
|
+
]
|
|
148
|
+
];
|
|
149
|
+
for (const args of runs) {
|
|
150
|
+
const result = spawnSync(python, args, pythonOptions());
|
|
151
|
+
if (result.status !== 0) {
|
|
152
|
+
return { level: "FAIL", title: "sample task validation failed", message: result.stderr || result.stdout };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return { level: "PASS", title: "sample task validation passed" };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function resolvePython() {
|
|
159
|
+
const python3 = spawnSync("python3", ["--version"], pythonOptions());
|
|
160
|
+
return python3.status === 0 ? "python3" : "python";
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function pythonOptions() {
|
|
164
|
+
return {
|
|
165
|
+
encoding: "utf8",
|
|
166
|
+
env: {
|
|
167
|
+
...process.env,
|
|
168
|
+
PYTHONDONTWRITEBYTECODE: "1"
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parseOptions, helpRequested } from "../lib/args.js";
|
|
4
|
+
import { installAgents } from "../lib/agents.js";
|
|
5
|
+
import { patchCodexConfig } from "../lib/codex_config.js";
|
|
6
|
+
import { backupIfExists } from "../lib/fs_safe.js";
|
|
7
|
+
import { createLogger } from "../lib/logger.js";
|
|
8
|
+
import { updateMarketplace } from "../lib/marketplace.js";
|
|
9
|
+
import { installPlugin } from "../lib/plugin.js";
|
|
10
|
+
import { resolveTargets } from "../lib/paths.js";
|
|
11
|
+
|
|
12
|
+
export function installHelp() {
|
|
13
|
+
return `Usage: meta-flow install --scope repo|user [options]
|
|
14
|
+
|
|
15
|
+
Options:
|
|
16
|
+
--target <path> Target repo path for repo scope. Defaults to cwd.
|
|
17
|
+
--force Overwrite conflicts after backing them up.
|
|
18
|
+
--dry-run Print planned actions without writing files.
|
|
19
|
+
--yes Skip confirmation prompts.
|
|
20
|
+
--no-agents Do not install custom agent TOML files.
|
|
21
|
+
--no-plugin Do not install the Codex plugin files.
|
|
22
|
+
--backup Backup existing managed files before update.
|
|
23
|
+
--verbose Print detailed actions.`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function runInstall(argv = []) {
|
|
27
|
+
if (helpRequested(argv)) {
|
|
28
|
+
console.log(installHelp());
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
const { options } = parseOptions(argv, {
|
|
32
|
+
scope: "string",
|
|
33
|
+
target: "string"
|
|
34
|
+
});
|
|
35
|
+
const scope = options.scope || "repo";
|
|
36
|
+
const targets = resolveTargets({ scope, target: options.target });
|
|
37
|
+
const logger = createLogger({ verbose: options.verbose });
|
|
38
|
+
const dryRun = Boolean(options.dryRun);
|
|
39
|
+
const force = Boolean(options.force);
|
|
40
|
+
const backup = Boolean(options.backup);
|
|
41
|
+
const installPluginEnabled = options.plugin !== false;
|
|
42
|
+
const installAgentsEnabled = options.agents !== false;
|
|
43
|
+
|
|
44
|
+
printInstallPlan(targets, {
|
|
45
|
+
installPluginEnabled,
|
|
46
|
+
installAgentsEnabled,
|
|
47
|
+
dryRun
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (backup && !dryRun) {
|
|
51
|
+
await fs.mkdir(path.dirname(targets.marketplaceTarget), { recursive: true });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (installPluginEnabled) {
|
|
55
|
+
if (backup) {
|
|
56
|
+
await backupIfExists(targets.pluginTarget, { dryRun, logger });
|
|
57
|
+
}
|
|
58
|
+
const result = await installPlugin(targets, { dryRun, force, logger });
|
|
59
|
+
if (result.conflict) {
|
|
60
|
+
logger.warn(`plugin target exists and is not a meta-flow plugin: ${result.conflict}. Re-run with --force to overwrite.`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
await updateMarketplace(targets, { dryRun, logger });
|
|
65
|
+
|
|
66
|
+
if (installAgentsEnabled) {
|
|
67
|
+
if (backup) {
|
|
68
|
+
await backupExistingAgents(targets, { dryRun, logger });
|
|
69
|
+
}
|
|
70
|
+
const result = await installAgents(targets, { dryRun, force, logger });
|
|
71
|
+
for (const conflict of result.conflicts) {
|
|
72
|
+
logger.warn(`agent file exists without meta-flow marker; not overwritten: ${conflict}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const configResult = await patchCodexConfig(targets.codexConfigTarget, {
|
|
77
|
+
dryRun,
|
|
78
|
+
force,
|
|
79
|
+
backup,
|
|
80
|
+
logger
|
|
81
|
+
});
|
|
82
|
+
for (const warning of configResult.warnings) {
|
|
83
|
+
logger.warn(warning);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
console.log("\nNext:");
|
|
87
|
+
console.log("1. Restart Codex.");
|
|
88
|
+
console.log(`2. Run: meta-flow doctor --scope ${scope}${scope === "repo" ? ` --target ${targets.target}` : ""}`);
|
|
89
|
+
console.log("3. In Codex, mention: $meta-flow");
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function printInstallPlan(targets, options) {
|
|
94
|
+
console.log("Meta Flow install plan:");
|
|
95
|
+
console.log(`- scope: ${targets.scope}`);
|
|
96
|
+
console.log(`- target: ${targets.target}`);
|
|
97
|
+
console.log(`- plugin: ${targets.pluginTarget}`);
|
|
98
|
+
console.log(`- marketplace: ${targets.marketplaceTarget}`);
|
|
99
|
+
console.log(`- agents: ${targets.agentsTarget}`);
|
|
100
|
+
console.log(`- config: ${targets.codexConfigTarget}`);
|
|
101
|
+
console.log("\nActions:");
|
|
102
|
+
if (options.installPluginEnabled) {
|
|
103
|
+
console.log("- copy plugin");
|
|
104
|
+
}
|
|
105
|
+
if (options.installAgentsEnabled) {
|
|
106
|
+
console.log("- install 14 agent templates");
|
|
107
|
+
}
|
|
108
|
+
console.log("- update marketplace");
|
|
109
|
+
console.log("- ensure Codex agent config");
|
|
110
|
+
if (options.dryRun) {
|
|
111
|
+
console.log("- dry-run only; no files will be written");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function backupExistingAgents(targets, options) {
|
|
116
|
+
const { AGENT_FILES } = await import("../lib/agents.js");
|
|
117
|
+
for (const fileName of AGENT_FILES) {
|
|
118
|
+
await backupIfExists(path.join(targets.agentsTarget, fileName), options);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { parseOptions, helpRequested } from "../lib/args.js";
|
|
2
|
+
import { resolveTargets } from "../lib/paths.js";
|
|
3
|
+
|
|
4
|
+
export function printPathsHelp() {
|
|
5
|
+
return `Usage: meta-flow print-paths --scope repo|user [--target <path>]`;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function runPrintPaths(argv = []) {
|
|
9
|
+
if (helpRequested(argv)) {
|
|
10
|
+
console.log(printPathsHelp());
|
|
11
|
+
return 0;
|
|
12
|
+
}
|
|
13
|
+
const { options } = parseOptions(argv, {
|
|
14
|
+
scope: "string",
|
|
15
|
+
target: "string"
|
|
16
|
+
});
|
|
17
|
+
const targets = resolveTargets({ scope: options.scope || "repo", target: options.target });
|
|
18
|
+
console.log(JSON.stringify(targets, null, 2));
|
|
19
|
+
return 0;
|
|
20
|
+
}
|