← Files MightShapeARCHIVED FILE

scripts/dc.py

13.1 KB · Sep 30, 2026 · 23:14 UTC

↓ Download file

#!/usr/bin/env python3
"""Command-line interface for MightShape project state."""

from __future__ import annotations

import argparse
import sys
from pathlib import Path

from dc_core import DesignCouncilError, json_output
from project_state import (
    ADAPTATION_SOURCES,
    FACILITATOR_LEVELS,
    PARTICIPATION_ACTIVITIES,
    PARTICIPATION_KINDS,
    PARTICIPATION_MODES,
    PROVENANCE,
    STARTING_POINT_BASES,
    STARTING_POINTS,
    add_assumption,
    add_evidence,
    add_participation_contribution,
    initialize_project,
    load_project,
    open_participation_prompt,
    participation_action,
    record_council_memory,
    record_gate_override,
    record_participation_guidance,
    record_visual_artifact,
    set_facilitator_level,
    set_mode,
    set_participation_mode,
    set_process_view,
    set_starting_point,
    start_participation,
    validate_state,
)
from score_build_gate import assess_build_gate, apply_gate_assessment
from session_summary import summarize_state


def parser() -> argparse.ArgumentParser:
    root = argparse.ArgumentParser(description="MightShape state and facilitation utilities")
    commands = root.add_subparsers(dest="command", required=True)

    init = commands.add_parser("init", help="Initialize versioned project state")
    init.add_argument("--project-root", default=".")
    init.add_argument("--name", required=True)
    init.add_argument("--prompt", required=True)
    init.add_argument("--proposed-solution")
    init.add_argument("--project-id")

    validate = commands.add_parser("validate", help="Validate project state")
    validate.add_argument("--project-root", default=".")

    show = commands.add_parser("show", help="Show a compact state summary")
    show.add_argument("--project-root", default=".")
    show.add_argument("--json", action="store_true")

    evidence = commands.add_parser("add-evidence", help="Add a provenance-safe evidence item")
    evidence.add_argument("--project-root", default=".")
    evidence.add_argument("--claim", required=True)
    evidence.add_argument("--provenance", choices=sorted(PROVENANCE), required=True)
    evidence.add_argument("--confidence", type=float, required=True)
    evidence.add_argument("--strength", type=int, required=True)
    evidence.add_argument("--source-ref", action="append", default=[])
    evidence.add_argument("--scope")
    evidence.add_argument("--study-id")
    evidence.add_argument("--participant-id")
    evidence.add_argument("--excerpt")
    evidence.add_argument("--consent-allows-quote", action="store_true")

    assumption = commands.add_parser("add-assumption", help="Add an assumption to burn down")
    assumption.add_argument("--project-root", default=".")
    assumption.add_argument("--statement", required=True)
    assumption.add_argument("--risk", choices=["LOW", "MEDIUM", "HIGH"], required=True)
    assumption.add_argument("--importance", choices=["LOW", "MEDIUM", "HIGH"], required=True)
    assumption.add_argument("--status")

    mode = commands.add_parser("mode", help="Record a design-mode transition")
    mode.add_argument("--project-root", default=".")
    mode.add_argument("--to", required=True)
    mode.add_argument("--reason", required=True)
    mode.add_argument("--evidence-id", action="append", default=[])

    memory = commands.add_parser("memory", help="Record one Council member's project memory")
    memory.add_argument("--project-root", default=".")
    memory.add_argument("--member", required=True)
    memory.add_argument("--kind", required=True)
    memory.add_argument("--statement", required=True)
    memory.add_argument("--confidence", type=float)
    memory.add_argument("--evidence-id", action="append", default=[])
    memory.add_argument("--changed-because", action="append", default=[])

    gate = commands.add_parser("gate", help="Assess the advisory Build Gate")
    gate.add_argument("--project-root", default=".")
    gate.add_argument("--apply", action="store_true")

    override = commands.add_parser("override", help="Record 'build it anyway' without erasing risk")
    override.add_argument("--project-root", default=".")
    override.add_argument("--note", required=True)

    view = commands.add_parser("view", help="Set COMPACT, VISIBLE, or WORKSHOP process presentation")
    view.add_argument("--project-root", default=".")
    view.add_argument("--mode", choices=["COMPACT", "VISIBLE", "WORKSHOP"], required=True)

    orient = commands.add_parser("orient", help="Record the project's starting point and current decision")
    orient.add_argument("--project-root", default=".")
    orient.add_argument("--starting-point", choices=sorted(STARTING_POINTS), required=True)
    orient.add_argument("--basis", choices=sorted(STARTING_POINT_BASES), required=True)
    orient_decision = orient.add_mutually_exclusive_group()
    orient_decision.add_argument("--current-decision")
    orient_decision.add_argument("--clear-current-decision", action="store_true")

    artifact = commands.add_parser("record-artifact", help="Record a rendered visual manifest in project history")
    artifact.add_argument("--project-root", default=".")
    artifact.add_argument("--manifest", required=True)

    participate_start = commands.add_parser("participate-start", help="Start an optional participatory exercise")
    participate_start.add_argument("--project-root", default=".")
    participate_start.add_argument("--mode", choices=sorted(PARTICIPATION_MODES), default="OBSERVE")
    participate_start.add_argument("--activity", choices=sorted(PARTICIPATION_ACTIVITIES), required=True)
    participate_start.add_argument("--facilitator-level", choices=sorted(FACILITATOR_LEVELS), default="NOVICE_ASSISTED")
    participate_start.add_argument("--round-id")
    participate_start.add_argument("--sealed-phase", choices=["NONE", "PRE_ROUND", "ROUND_A_OPEN", "POST_FREEZE"], default="NONE")

    participate_mode = commands.add_parser("participate-mode", help="Change participation or facilitator mode")
    participate_mode.add_argument("--project-root", default=".")
    participate_mode.add_argument("--session-id", required=True)
    mode_choice = participate_mode.add_mutually_exclusive_group(required=True)
    mode_choice.add_argument("--mode", choices=sorted(PARTICIPATION_MODES))
    mode_choice.add_argument("--facilitator-level", choices=sorted(FACILITATOR_LEVELS))
    participate_mode.add_argument("--change-reason", help="Why facilitator support is changing")
    participate_mode.add_argument(
        "--change-source", choices=sorted(ADAPTATION_SOURCES), default="USER_REQUEST"
    )

    participate_prompt = commands.add_parser("participate-prompt", help="Open one bounded participation prompt")
    participate_prompt.add_argument("--project-root", default=".")
    participate_prompt.add_argument("--session-id", required=True)
    participate_prompt.add_argument("--prompt", required=True)
    participate_prompt.add_argument("--purpose")
    participate_prompt.add_argument("--mindset")
    participate_prompt.add_argument("--example")

    participate_add = commands.add_parser("participate-add", help="Record a USER_PROVIDED workshop contribution")
    participate_add.add_argument("--project-root", default=".")
    participate_add.add_argument("--session-id", required=True)
    participate_add.add_argument("--kind", choices=sorted(PARTICIPATION_KINDS), required=True)
    participate_add.add_argument("--content", required=True)
    participate_add.add_argument("--board-change")
    participate_add.add_argument("--sealed-disposition", choices=["NONE", "COMMON_PACKET_NEXT_ROUND", "HOLD_UNTIL_POST_FREEZE"])

    participate_guide = commands.add_parser("participate-guide", help="Record point-of-use facilitator guidance")
    participate_guide.add_argument("--project-root", default=".")
    participate_guide.add_argument("--session-id", required=True)
    participate_guide.add_argument("--request", choices=["WHY", "EXAMPLE", "DEFINE", "SLOWER", "FASTER", "COACHING"], required=True)
    participate_guide.add_argument("--response", required=True)
    participate_guide.add_argument("--term")
    participate_guide.add_argument("--adaptation-reason", help="Why SLOWER or FASTER is appropriate")
    participate_guide.add_argument(
        "--adaptation-source", choices=sorted(ADAPTATION_SOURCES), default="USER_REQUEST"
    )

    participate_control = commands.add_parser("participate-action", help="Skip, pause, resume, supersede, hand back, exit, or complete")
    participate_control.add_argument("--project-root", default=".")
    participate_control.add_argument("--session-id", required=True)
    participate_control.add_argument("--action", choices=["SKIP", "PAUSE", "RESUME", "UNDO", "HAND_BACK", "EXIT", "COMPLETE"], required=True)
    participate_control.add_argument("--contribution-id")
    participate_control.add_argument("--replacement")
    return root


def run(args: argparse.Namespace) -> dict | str:
    project_root = Path(args.project_root)
    if args.command == "init":
        return initialize_project(project_root, args.name, args.prompt, args.proposed_solution, args.project_id)
    if args.command == "validate":
        return validate_state(load_project(project_root))
    if args.command == "show":
        state = load_project(project_root)
        return state if args.json else summarize_state(state)
    if args.command == "add-evidence":
        return add_evidence(
            project_root,
            args.claim,
            args.provenance,
            args.confidence,
            args.strength,
            args.source_ref,
            args.scope,
            args.study_id,
            args.participant_id,
            args.excerpt,
            True if args.consent_allows_quote else None,
        )
    if args.command == "add-assumption":
        return add_assumption(project_root, args.statement, args.risk, args.importance, args.status)
    if args.command == "mode":
        return set_mode(project_root, args.to, args.reason, args.evidence_id)
    if args.command == "memory":
        return record_council_memory(
            project_root,
            args.member,
            args.kind,
            args.statement,
            args.confidence,
            args.evidence_id,
            args.changed_because,
        )
    if args.command == "gate":
        state = load_project(project_root)
        assessment = assess_build_gate(state)
        if args.apply:
            return apply_gate_assessment(project_root, assessment)
        return assessment
    if args.command == "override":
        return record_gate_override(project_root, args.note)
    if args.command == "view":
        return set_process_view(project_root, args.mode)
    if args.command == "orient":
        if args.clear_current_decision:
            return set_starting_point(project_root, args.starting_point, args.basis, None)
        if args.current_decision is not None:
            return set_starting_point(
                project_root,
                args.starting_point,
                args.basis,
                args.current_decision,
            )
        return set_starting_point(project_root, args.starting_point, args.basis)
    if args.command == "record-artifact":
        return record_visual_artifact(project_root, args.manifest)
    if args.command == "participate-start":
        return start_participation(
            project_root,
            args.mode,
            args.activity,
            args.facilitator_level,
            args.round_id,
            args.sealed_phase,
        )
    if args.command == "participate-mode":
        if args.mode:
            return set_participation_mode(project_root, args.session_id, args.mode)
        return set_facilitator_level(
            project_root,
            args.session_id,
            args.facilitator_level,
            args.change_reason,
            args.change_source,
        )
    if args.command == "participate-prompt":
        return open_participation_prompt(
            project_root,
            args.session_id,
            args.prompt,
            args.purpose,
            args.mindset,
            args.example,
        )
    if args.command == "participate-add":
        return add_participation_contribution(
            project_root,
            args.session_id,
            args.kind,
            args.content,
            args.board_change,
            args.sealed_disposition,
        )
    if args.command == "participate-guide":
        return record_participation_guidance(
            project_root,
            args.session_id,
            args.request,
            args.response,
            args.term,
            args.adaptation_reason,
            args.adaptation_source,
        )
    if args.command == "participate-action":
        return participation_action(
            project_root,
            args.session_id,
            args.action,
            args.contribution_id,
            args.replacement,
        )
    raise DesignCouncilError(f"Unsupported command: {args.command}")


def main() -> int:
    args = parser().parse_args()
    try:
        result = run(args)
    except DesignCouncilError as exc:
        print(f"MightShape error: {exc}", file=sys.stderr)
        return 2
    if isinstance(result, str):
        print(result)
    else:
        json_output(result)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

SHA-256: 1c4b0da27604ee08d0b40a78241c34ae4204822d07726ca026baf2de556aa466