cadence-skill-installer 0.2.6 → 0.2.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cadence-skill-installer",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "description": "Install the Cadence skill into supported AI tool skill directories.",
5
5
  "repository": "https://github.com/snowdamiz/cadence",
6
6
  "private": false,
package/skill/SKILL.md CHANGED
@@ -45,6 +45,11 @@ description: Structured project operating system for end-to-end greenfield or br
45
45
  1. Invoke `skills/project-progress/SKILL.md` when the user asks to continue/resume or requests progress status (for example: "continue the project", "how far along are we?", "where did we leave off?").
46
46
  2. Use that skill's state-based routing result to continue from the correct next phase.
47
47
 
48
+ ## Manual Subskill Safety Gate
49
+ 1. If the user manually requests a Cadence subskill, run `python3 scripts/assert-workflow-route.py --skill-name <subskill>` before executing it.
50
+ 2. If route assertion fails, stop and surface the exact script error.
51
+ 3. Do not execute state-changing subskill steps when assertion fails.
52
+
48
53
  ## Ideation Flow
49
54
  1. Do not switch to `skills/ideator/SKILL.md` inside this conversation.
50
55
  2. After scaffold and prerequisite gates pass for a net-new project, hand off to a fresh chat so context resets cleanly.
@@ -1,4 +1,4 @@
1
1
  interface:
2
2
  display_name: "Cadence"
3
3
  short_description: "Lifecycle + delivery system for structured project execution"
4
- default_prompt: "Use Cadence to guide this project from lifecycle setup through phased execution, traceability, audit, and milestone completion. Always read and apply the active SOUL persona from .cadence/SOUL.json (fallback: SOUL.json). Keep user-facing responses concise and outcome-focused, and never expose internal skill-routing or command-execution traces unless the user explicitly asks. If user intent indicates resuming/continuing work or asking progress, invoke skills/project-progress/SKILL.md first, report current phase, then route to the next step. For net-new project starts, after scaffold and prerequisite gates pass, do not switch skills in-thread; tell the user: Start a new chat and either say \"help me define my project\" or share your project brief."
4
+ default_prompt: "Use Cadence to guide this project from lifecycle setup through phased execution, traceability, audit, and milestone completion. Always read and apply the active SOUL persona from .cadence/SOUL.json (fallback: SOUL.json). Keep user-facing responses concise and outcome-focused, and never expose internal skill-routing or command-execution traces unless the user explicitly asks. If user intent indicates resuming/continuing work or asking progress, invoke skills/project-progress/SKILL.md first, report current phase, then route to the next step. If the user manually requests a Cadence subskill, run scripts/assert-workflow-route.py with that skill name first and block state-changing execution on mismatch. For net-new project starts, after scaffold and prerequisite gates pass, do not switch skills in-thread; tell the user: Start a new chat and either say \"help me define my project\" or share your project brief."
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env python3
2
+ """Assert that a requested skill matches the current workflow route."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import copy
7
+ import json
8
+ import sys
9
+ from pathlib import Path
10
+
11
+ from workflow_state import default_data, reconcile_workflow_state
12
+
13
+
14
+ CADENCE_DIR = Path(".cadence")
15
+ CADENCE_JSON_PATH = CADENCE_DIR / "cadence.json"
16
+
17
+
18
+ def load_state() -> dict:
19
+ cadence_exists = CADENCE_DIR.exists()
20
+
21
+ if not CADENCE_JSON_PATH.exists():
22
+ data = default_data()
23
+ return reconcile_workflow_state(data, cadence_dir_exists=cadence_exists)
24
+
25
+ try:
26
+ with CADENCE_JSON_PATH.open("r", encoding="utf-8") as file:
27
+ original_data = json.load(file)
28
+ except json.JSONDecodeError as exc:
29
+ print(f"INVALID_CADENCE_JSON: {exc}", file=sys.stderr)
30
+ raise SystemExit(1)
31
+
32
+ return reconcile_workflow_state(copy.deepcopy(original_data), cadence_dir_exists=cadence_exists)
33
+
34
+
35
+ def parse_args(argv: list[str]) -> tuple[str, bool]:
36
+ if not argv or len(argv) > 3:
37
+ print("Usage: assert-workflow-route.py --skill-name <name> [--allow-complete]", file=sys.stderr)
38
+ raise SystemExit(2)
39
+
40
+ skill_name = ""
41
+ allow_complete = False
42
+ idx = 0
43
+ while idx < len(argv):
44
+ token = argv[idx]
45
+ if token == "--skill-name":
46
+ if idx + 1 >= len(argv):
47
+ print("MISSING_SKILL_NAME", file=sys.stderr)
48
+ raise SystemExit(2)
49
+ skill_name = str(argv[idx + 1]).strip()
50
+ idx += 2
51
+ continue
52
+ if token == "--allow-complete":
53
+ allow_complete = True
54
+ idx += 1
55
+ continue
56
+
57
+ print(f"UNKNOWN_ARGUMENT: {token}", file=sys.stderr)
58
+ raise SystemExit(2)
59
+
60
+ if not skill_name:
61
+ print("MISSING_SKILL_NAME", file=sys.stderr)
62
+ raise SystemExit(2)
63
+
64
+ return skill_name, allow_complete
65
+
66
+
67
+ def main() -> int:
68
+ requested_skill, allow_complete = parse_args(sys.argv[1:])
69
+ data = load_state()
70
+
71
+ workflow = data.get("workflow", {})
72
+ next_item = workflow.get("next_item", {})
73
+ next_route = workflow.get("next_route", {})
74
+
75
+ next_item_id = str(next_item.get("id", "complete")).strip() or "complete"
76
+ next_item_title = str(next_item.get("title", next_item_id)).strip() or next_item_id
77
+ expected_skill = str(next_route.get("skill_name", "")).strip()
78
+
79
+ if next_item_id == "complete" and not allow_complete:
80
+ print("WORKFLOW_ALREADY_COMPLETE", file=sys.stderr)
81
+ return 2
82
+
83
+ if next_item_id != "complete" and expected_skill != requested_skill:
84
+ expected = expected_skill or "none"
85
+ print(
86
+ (
87
+ "WORKFLOW_ROUTE_MISMATCH: "
88
+ f"expected={expected} "
89
+ f"requested={requested_skill} "
90
+ f"next_item={next_item_id}"
91
+ ),
92
+ file=sys.stderr,
93
+ )
94
+ return 2
95
+
96
+ print(
97
+ json.dumps(
98
+ {
99
+ "status": "ok",
100
+ "requested_skill": requested_skill,
101
+ "expected_skill": expected_skill,
102
+ "next_item_id": next_item_id,
103
+ "next_item_title": next_item_title,
104
+ "workflow_complete": next_item_id == "complete",
105
+ }
106
+ )
107
+ )
108
+ return 0
109
+
110
+
111
+ if __name__ == "__main__":
112
+ raise SystemExit(main())
@@ -3,6 +3,7 @@
3
3
 
4
4
  import argparse
5
5
  import json
6
+ import subprocess
6
7
  import sys
7
8
  from pathlib import Path
8
9
 
@@ -10,6 +11,19 @@ from workflow_state import default_data, reconcile_workflow_state
10
11
 
11
12
 
12
13
  CADENCE_JSON_PATH = Path(".cadence") / "cadence.json"
14
+ SCRIPT_DIR = Path(__file__).resolve().parent
15
+ ROUTE_GUARD_SCRIPT = SCRIPT_DIR / "assert-workflow-route.py"
16
+
17
+
18
+ def run_command(command):
19
+ return subprocess.run(command, capture_output=True, text=True, check=False)
20
+
21
+
22
+ def assert_ideator_route():
23
+ result = run_command([sys.executable, str(ROUTE_GUARD_SCRIPT), "--skill-name", "ideator"])
24
+ if result.returncode != 0:
25
+ stderr = result.stderr.strip() or result.stdout.strip() or "WORKFLOW_ROUTE_CHECK_FAILED"
26
+ raise ValueError(stderr)
13
27
 
14
28
 
15
29
  def load_cadence():
@@ -99,6 +113,8 @@ def main():
99
113
  args = parse_args()
100
114
 
101
115
  try:
116
+ if args.completion_state == "complete":
117
+ assert_ideator_route()
102
118
  data = load_cadence()
103
119
  payload, payload_file_path = parse_payload(args)
104
120
  except ValueError as exc:
@@ -10,12 +10,21 @@ from pathlib import Path
10
10
 
11
11
  SCRIPT_DIR = Path(__file__).resolve().parent
12
12
  RESOLVE_SCRIPT = SCRIPT_DIR / "resolve-project-scripts-dir.py"
13
+ ROUTE_GUARD_SCRIPT = SCRIPT_DIR / "assert-workflow-route.py"
13
14
 
14
15
 
15
16
  def run_command(command):
16
17
  return subprocess.run(command, capture_output=True, text=True, check=False)
17
18
 
18
19
 
20
+ def assert_expected_route():
21
+ result = run_command([sys.executable, str(ROUTE_GUARD_SCRIPT), "--skill-name", "prerequisite-gate"])
22
+ if result.returncode != 0:
23
+ stderr = result.stderr.strip() or result.stdout.strip() or "WORKFLOW_ROUTE_CHECK_FAILED"
24
+ print(stderr, file=sys.stderr)
25
+ raise SystemExit(result.returncode)
26
+
27
+
19
28
  def resolve_scripts_dir():
20
29
  result = run_command([sys.executable, str(RESOLVE_SCRIPT)])
21
30
  if result.returncode != 0:
@@ -51,6 +60,7 @@ def write_prerequisite_state(scripts_dir, pass_state):
51
60
 
52
61
 
53
62
  def main():
63
+ assert_expected_route()
54
64
  scripts_dir = resolve_scripts_dir()
55
65
  state = read_prerequisite_state(scripts_dir)
56
66
 
@@ -11,12 +11,21 @@ CADENCE_JSON_PATH = Path(".cadence") / "cadence.json"
11
11
  SCRIPT_DIR = Path(__file__).resolve().parent
12
12
  SCAFFOLD_SCRIPT = SCRIPT_DIR / "scaffold-project.sh"
13
13
  INIT_SCRIPT = SCRIPT_DIR / "init-cadence-scripts-dir.py"
14
+ ROUTE_GUARD_SCRIPT = SCRIPT_DIR / "assert-workflow-route.py"
14
15
 
15
16
 
16
17
  def run_command(command):
17
18
  return subprocess.run(command, capture_output=True, text=True, check=False)
18
19
 
19
20
 
21
+ def assert_expected_route():
22
+ result = run_command([sys.executable, str(ROUTE_GUARD_SCRIPT), "--skill-name", "scaffold"])
23
+ if result.returncode != 0:
24
+ stderr = result.stderr.strip() or result.stdout.strip() or "WORKFLOW_ROUTE_CHECK_FAILED"
25
+ print(stderr, file=sys.stderr)
26
+ raise SystemExit(result.returncode)
27
+
28
+
20
29
  def run_scaffold():
21
30
  result = run_command(["bash", str(SCAFFOLD_SCRIPT)])
22
31
  if result.returncode != 0:
@@ -60,6 +69,7 @@ def verify_expected_state():
60
69
 
61
70
 
62
71
  def main():
72
+ assert_expected_route()
63
73
  scaffold_status = run_scaffold()
64
74
  initialize_scripts_dir()
65
75
  scripts_dir = verify_expected_state()
@@ -6,39 +6,41 @@ description: Guide users from a rough concept to a fully defined project idea th
6
6
  # Ideator
7
7
 
8
8
  1. Keep user-facing responses focused on ideation content. Do not expose internal skill-routing, command output, or execution traces unless the user explicitly asks.
9
- 2. Start from the user's input and briefly restate your understanding.
10
- 3. Accept two input modes:
9
+ 2. Run `python3 ../../scripts/assert-workflow-route.py --skill-name ideator` and parse the JSON response.
10
+ 3. If route assertion fails, stop and surface the exact error to the user.
11
+ 4. Start from the user's input and briefly restate your understanding.
12
+ 5. Accept two input modes:
11
13
  - Conversational discovery from a rough idea.
12
14
  - Project brief or project document provided up front.
13
- 4. If the user provides a project brief or document:
15
+ 6. If the user provides a project brief or document:
14
16
  - Extract the explicit requirements into a working ideation object.
15
17
  - Judge whether the brief is execution-ready across the relevant dimensions below.
16
18
  - If information is sufficient, present the final ideation summary and ask for confirmation.
17
19
  - If information is missing or ambiguous, ask exactly one highest-leverage follow-up question.
18
- 5. Ask exactly one question at a time. Never ask a batch of questions in a single turn.
19
- 6. After each user answer:
20
+ 7. Ask exactly one question at a time. Never ask a batch of questions in a single turn.
21
+ 8. After each user answer:
20
22
  - Summarize what changed in one short sentence.
21
23
  - Decide the next highest-leverage unknown.
22
24
  - Ask one natural follow-up question.
23
- 7. Keep discovery domain-agnostic and adaptive:
25
+ 9. Keep discovery domain-agnostic and adaptive:
24
26
  - Derive the question path from the user's domain, any provided document, and prior answers.
25
27
  - Do not force fixed templates or hard-coded checklists during discovery.
26
28
  - Drill deep where ambiguity remains; move on when the topic is clear.
27
- 8. Build understanding until the idea is execution-ready. Cover the relevant dimensions for the domain, including:
29
+ 10. Build understanding until the idea is execution-ready. Cover the relevant dimensions for the domain, including:
28
30
  - objective and core outcome
29
31
  - target audience or user
30
32
  - core experience or structure (for example mechanics, flow, chapters, systems)
31
33
  - scope boundaries (in-scope vs out-of-scope)
32
34
  - implementation approach (for example tools, tech stack, process, platforms)
33
35
  - delivery shape (milestones, sequencing, constraints, risks, success signals)
34
- 9. Do not hard-code assumptions. If you infer something, label it explicitly and ask for confirmation.
35
- 10. When coverage is deep enough, present a final ideation summary and ask for confirmation.
36
- 11. Resolve helper scripts dir by running `python3 ../../scripts/resolve-project-scripts-dir.py` and store stdout in `CADENCE_SCRIPTS_DIR`.
37
- 12. After confirmation, persist ideation programmatically:
36
+ 11. Do not hard-code assumptions. If you infer something, label it explicitly and ask for confirmation.
37
+ 12. When coverage is deep enough, present a final ideation summary and ask for confirmation.
38
+ 13. Resolve helper scripts dir by running `python3 ../../scripts/resolve-project-scripts-dir.py` and store stdout in `CADENCE_SCRIPTS_DIR`.
39
+ 14. After confirmation, persist ideation programmatically:
38
40
  - Create a JSON payload file at `.cadence/ideation_payload.json`.
39
41
  - Write the full finalized ideation object to that file.
40
42
  - Run `python3 "$CADENCE_SCRIPTS_DIR/inject-ideation.py" --file .cadence/ideation_payload.json --completion-state complete` (this injects ideation and deletes `.cadence/ideation_payload.json` on success).
41
- 13. Verify persistence by running `python3 "$CADENCE_SCRIPTS_DIR/get-ideation.py"`.
42
- 14. At end of this successful skill conversation, run `python3 "$CADENCE_SCRIPTS_DIR/finalize-skill-checkpoint.py" --scope ideator --checkpoint ideation-completed --paths .`.
43
- 15. If `finalize-skill-checkpoint.py` returns `status=no_changes`, continue without failure.
44
- 16. If the user requests revisions later, regenerate the payload and rerun `inject-ideation.py`.
43
+ 15. Verify persistence by running `python3 "$CADENCE_SCRIPTS_DIR/get-ideation.py"`.
44
+ 16. At end of this successful skill conversation, run `python3 "$CADENCE_SCRIPTS_DIR/finalize-skill-checkpoint.py" --scope ideator --checkpoint ideation-completed --paths .`.
45
+ 17. If `finalize-skill-checkpoint.py` returns `status=no_changes`, continue without failure.
46
+ 18. If the user requests revisions later, regenerate the payload and rerun `inject-ideation.py`.
@@ -1,4 +1,4 @@
1
1
  interface:
2
2
  display_name: "Ideator"
3
3
  short_description: "Step-by-step domain-agnostic ideation"
4
- default_prompt: "Guide the user from a rough concept or provided project brief to a fully defined idea. If a project brief is provided, decide whether it is sufficient, then ask one follow-up question at a time only for missing critical details before persisting the final ideation payload. Keep user-facing responses focused on ideation outcomes and do not expose internal skill-routing or command traces unless explicitly requested."
4
+ default_prompt: "Guide the user from a rough concept or provided project brief to a fully defined idea. Before ideation steps, assert the workflow route with scripts/assert-workflow-route.py --skill-name ideator and stop on mismatch. If a project brief is provided, decide whether it is sufficient, then ask one follow-up question at a time only for missing critical details before persisting the final ideation payload. Keep user-facing responses focused on ideation outcomes and do not expose internal skill-routing or command traces unless explicitly requested."
@@ -6,10 +6,12 @@ description: Run and persist Cadence prerequisite checks for Python availability
6
6
  # Prerequisite Gate
7
7
 
8
8
  1. Run this only after scaffold routing from `skills/scaffold/SKILL.md`.
9
- 2. Run `python3 ../../scripts/run-prerequisite-gate.py` (resolve this relative path from this sub-skill directory).
10
- 3. If the script reports `MISSING_PYTHON3`, stop and ask the user for confirmation to install prerequisites.
11
- 4. Do not continue Cadence lifecycle or delivery execution while prerequisites are missing.
12
- 5. At end of this successful skill conversation, run `python3 ../../scripts/finalize-skill-checkpoint.py --scope prerequisite-gate --checkpoint prerequisites-passed --paths .`.
13
- 6. If `finalize-skill-checkpoint.py` returns `status=no_changes`, continue without failure.
14
- 7. Surface script failures verbatim instead of adding custom fallback logic.
15
- 8. In normal user-facing updates, share the prerequisite outcome without raw command traces or internal routing details unless explicitly requested.
9
+ 2. Run `python3 ../../scripts/assert-workflow-route.py --skill-name prerequisite-gate` and parse the JSON response.
10
+ 3. If route assertion fails, stop and surface the exact error to the user.
11
+ 4. Run `python3 ../../scripts/run-prerequisite-gate.py` (resolve this relative path from this sub-skill directory).
12
+ 5. If the script reports `MISSING_PYTHON3`, stop and ask the user for confirmation to install prerequisites.
13
+ 6. Do not continue Cadence lifecycle or delivery execution while prerequisites are missing.
14
+ 7. At end of this successful skill conversation, run `python3 ../../scripts/finalize-skill-checkpoint.py --scope prerequisite-gate --checkpoint prerequisites-passed --paths .`.
15
+ 8. If `finalize-skill-checkpoint.py` returns `status=no_changes`, continue without failure.
16
+ 9. Surface script failures verbatim instead of adding custom fallback logic.
17
+ 10. In normal user-facing updates, share the prerequisite outcome without raw command traces or internal routing details unless explicitly requested.
@@ -1,4 +1,4 @@
1
1
  interface:
2
2
  display_name: "Prerequisite Gate"
3
3
  short_description: "Run and persist Cadence prerequisite checks for Python availability"
4
- default_prompt: "Run Cadence prerequisite checks, verify Python availability, and persist pass state before lifecycle or delivery execution. Keep user-facing messages focused on prerequisite outcomes and avoid internal command/routing traces unless explicitly requested."
4
+ default_prompt: "Run Cadence prerequisite checks, verify Python availability, and persist pass state before lifecycle or delivery execution. Before running prerequisite actions, assert the workflow route with scripts/assert-workflow-route.py --skill-name prerequisite-gate and stop on mismatch. Keep user-facing messages focused on prerequisite outcomes and avoid internal command/routing traces unless explicitly requested."
@@ -6,27 +6,29 @@ description: Initialize Cadence project scaffolding for first-time setup. Use wh
6
6
  # Scaffold
7
7
 
8
8
  1. Run this only from the target project root.
9
- 2. Run `python3 ../../scripts/run-scaffold-gate.py` (resolve this relative path from this sub-skill directory) and parse the JSON response.
10
- 3. If the script errors, stop and surface the exact error to the user.
11
- 4. Run `python3 ../../scripts/check-project-repo-status.py` and parse the JSON output.
12
- 5. If `repo_enabled` is false, ask the user: `No GitHub remote is configured yet. Do you want to initialize a GitHub repo now? (yes/no)`.
13
- 6. If the user answers yes:
9
+ 2. Run `python3 ../../scripts/assert-workflow-route.py --skill-name scaffold` and parse the JSON response.
10
+ 3. If route assertion fails, stop and surface the exact error to the user.
11
+ 4. Run `python3 ../../scripts/run-scaffold-gate.py` (resolve this relative path from this sub-skill directory) and parse the JSON response.
12
+ 5. If the script errors, stop and surface the exact error to the user.
13
+ 6. Run `python3 ../../scripts/check-project-repo-status.py` and parse the JSON output.
14
+ 7. If `repo_enabled` is false, ask the user: `No GitHub remote is configured yet. Do you want to initialize a GitHub repo now? (yes/no)`.
15
+ 8. If the user answers yes:
14
16
  - If `git_initialized` is false, run `git init`.
15
17
  - Ask for repo name and visibility, then run `gh repo create <name> --source . --remote origin --<public|private>`.
16
18
  - Rerun `python3 ../../scripts/check-project-repo-status.py` and verify `repo_enabled` is true.
17
19
  - If repo setup still fails, stop and surface the exact failure to the user.
18
- 7. If the user answers no:
20
+ 9. If the user answers no:
19
21
  - If `git_initialized` is false, run `git init` so local commits can be stored.
20
22
  - Run `python3 ../../scripts/check-project-repo-status.py --set-local-only` to persist `state.repo-enabled=false`.
21
23
  - Continue in local-only commit mode until the user configures a GitHub repo later.
22
- 8. Ask the user: `Do you want .cadence tracked in git history? (yes/no)`.
23
- 9. If the user answers yes:
24
+ 10. Ask the user: `Do you want .cadence tracked in git history? (yes/no)`.
25
+ 11. If the user answers yes:
24
26
  - Run `python3 ../../scripts/configure-cadence-gitignore.py --mode track`.
25
27
  - At end of this skill conversation, run `python3 ../../scripts/finalize-skill-checkpoint.py --scope scaffold --checkpoint cadence-tracked --paths .`.
26
- 10. If the user answers no:
28
+ 12. If the user answers no:
27
29
  - Run `python3 ../../scripts/configure-cadence-gitignore.py --mode ignore`.
28
30
  - At end of this skill conversation, run `python3 ../../scripts/finalize-skill-checkpoint.py --scope scaffold --checkpoint cadence-ignored --paths .`.
29
- 11. If `finalize-skill-checkpoint.py` returns `status=no_changes`, continue without failure.
30
- 12. If `finalize-skill-checkpoint.py` reports an error, stop and surface it verbatim.
31
- 13. Execute scaffold actions serially. Do not run this flow in parallel with other setup gates.
32
- 14. In user-facing replies, summarize only the result. Do not expose internal command lines, skill chains, or execution traces unless explicitly requested.
31
+ 13. If `finalize-skill-checkpoint.py` returns `status=no_changes`, continue without failure.
32
+ 14. If `finalize-skill-checkpoint.py` reports an error, stop and surface it verbatim.
33
+ 15. Execute scaffold actions serially. Do not run this flow in parallel with other setup gates.
34
+ 16. In user-facing replies, summarize only the result. Do not expose internal command lines, skill chains, or execution traces unless explicitly requested.
@@ -1,4 +1,4 @@
1
1
  interface:
2
2
  display_name: "Scaffold"
3
3
  short_description: "Initialize Cadence project scaffolding for first-time setup"
4
- default_prompt: "Initialize Cadence project scaffolding in the target project root, creating .cadence state files only when they do not already exist. In user-facing replies, summarize outcome only and avoid internal command or routing traces unless explicitly requested."
4
+ default_prompt: "Initialize Cadence project scaffolding in the target project root, creating .cadence state files only when they do not already exist. Before running scaffold actions, assert the workflow route with scripts/assert-workflow-route.py --skill-name scaffold and stop on mismatch. In user-facing replies, summarize outcome only and avoid internal command or routing traces unless explicitly requested."