cadence-skill-installer 0.2.5 → 0.2.6
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 +1 -1
- package/skill/SKILL.md +23 -1
- package/skill/agents/openai.yaml +1 -1
- package/skill/assets/AGENTS.md +4 -0
- package/skill/assets/cadence.json +2 -1
- package/skill/config/commit-conventions.json +120 -0
- package/skill/scripts/check-project-repo-status.py +190 -0
- package/skill/scripts/configure-cadence-gitignore.py +79 -0
- package/skill/scripts/finalize-skill-checkpoint.py +420 -0
- package/skill/scripts/git-checkpoint.py +302 -0
- package/skill/scripts/scaffold-project.sh +2 -1
- package/skill/scripts/workflow_state.py +5 -0
- package/skill/skills/ideation-updater/SKILL.md +16 -13
- package/skill/skills/ideation-updater/agents/openai.yaml +1 -1
- package/skill/skills/ideator/SKILL.md +16 -13
- package/skill/skills/ideator/agents/openai.yaml +1 -1
- package/skill/skills/prerequisite-gate/SKILL.md +4 -1
- package/skill/skills/prerequisite-gate/agents/openai.yaml +1 -1
- package/skill/skills/project-progress/SKILL.md +5 -3
- package/skill/skills/project-progress/agents/openai.yaml +1 -1
- package/skill/skills/scaffold/SKILL.md +24 -4
- package/skill/skills/scaffold/agents/openai.yaml +1 -1
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -9,6 +9,28 @@ description: Structured project operating system for end-to-end greenfield or br
|
|
|
9
9
|
1. Keep this root skill as an orchestrator.
|
|
10
10
|
2. Delegate concrete execution to focused subskills.
|
|
11
11
|
|
|
12
|
+
## Response Hygiene
|
|
13
|
+
1. Keep user-facing replies outcome-focused and concise.
|
|
14
|
+
2. Do not expose internal execution details unless the user explicitly asks for them:
|
|
15
|
+
- skill routing chains
|
|
16
|
+
- gate-by-gate status narration
|
|
17
|
+
- raw commands, terminal traces, or timing metadata
|
|
18
|
+
|
|
19
|
+
## Repo Status Gate
|
|
20
|
+
1. At the start of every Cadence turn, run `python3 scripts/check-project-repo-status.py` (resolve this relative path from this skill directory).
|
|
21
|
+
2. Read `repo_enabled` from script output and treat it as the authoritative push mode.
|
|
22
|
+
3. If `repo_enabled` is false, continue with local commits only until a GitHub remote is configured.
|
|
23
|
+
|
|
24
|
+
## Git Checkpoints
|
|
25
|
+
1. Enforce Cadence commit convention from `config/commit-conventions.json`.
|
|
26
|
+
2. At the end of each successful subskill conversation, run `scripts/finalize-skill-checkpoint.py` with configured `--scope` and `--checkpoint` values.
|
|
27
|
+
3. `finalize-skill-checkpoint.py` must check git diff first, split changes into atomic semantic batches, and create multiple small checkpoint commits when needed before push.
|
|
28
|
+
4. `finalize-skill-checkpoint.py` must use repo status output so commits are local-only when `repo_enabled` is false.
|
|
29
|
+
5. Use `--paths .` unless a narrower path is explicitly required, so commits include any files changed by the skill.
|
|
30
|
+
6. Treat checkpoint or push failures as blocking; surface the exact script error to the user.
|
|
31
|
+
7. If finalization returns `status=no_changes`, continue without failure.
|
|
32
|
+
8. Do not gate checkpoint commits on test results yet; add that gate only when explicitly introduced later.
|
|
33
|
+
|
|
12
34
|
## Scaffold Gate (Mandatory On First Turn)
|
|
13
35
|
1. Check for `.cadence` in the project root.
|
|
14
36
|
2. If `.cadence` is missing, invoke `skills/scaffold/SKILL.md`.
|
|
@@ -26,7 +48,7 @@ description: Structured project operating system for end-to-end greenfield or br
|
|
|
26
48
|
## Ideation Flow
|
|
27
49
|
1. Do not switch to `skills/ideator/SKILL.md` inside this conversation.
|
|
28
50
|
2. After scaffold and prerequisite gates pass for a net-new project, hand off to a fresh chat so context resets cleanly.
|
|
29
|
-
3. Tell the user
|
|
51
|
+
3. Tell the user: `Start a new chat and either say "help me define my project" or share your project brief.`
|
|
30
52
|
4. Stop here and wait for the user to continue in the new chat.
|
|
31
53
|
|
|
32
54
|
## Ideation Update Flow
|
package/skill/agents/openai.yaml
CHANGED
|
@@ -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). 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:
|
|
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."
|
package/skill/assets/AGENTS.md
CHANGED
|
@@ -36,6 +36,10 @@
|
|
|
36
36
|
- Zero context switching required from the user
|
|
37
37
|
- Go fix failing CI tests without being told how
|
|
38
38
|
|
|
39
|
+
### 7. User-Facing Hygiene
|
|
40
|
+
- Keep user-facing messages outcome-focused
|
|
41
|
+
- Do not expose internal routing, command traces, terminal transcripts, or timing metadata unless the user explicitly asks
|
|
42
|
+
|
|
39
43
|
## Task Management
|
|
40
44
|
|
|
41
45
|
1. **Plan First**: Write plan to `.cadence/tasks/todo.md` with checkable items
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": 1,
|
|
3
|
+
"commit_type": "cadence",
|
|
4
|
+
"subject_max_length": 72,
|
|
5
|
+
"default_remote": "origin",
|
|
6
|
+
"atomic": {
|
|
7
|
+
"max_files_per_commit": 4,
|
|
8
|
+
"group_order": [
|
|
9
|
+
"cadence-state",
|
|
10
|
+
"skill-instructions",
|
|
11
|
+
"docs",
|
|
12
|
+
"scripts",
|
|
13
|
+
"tests",
|
|
14
|
+
"config",
|
|
15
|
+
"source"
|
|
16
|
+
],
|
|
17
|
+
"groups": {
|
|
18
|
+
"cadence-state": {
|
|
19
|
+
"tag": "state",
|
|
20
|
+
"label": "cadence state",
|
|
21
|
+
"patterns": [
|
|
22
|
+
".cadence/**"
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
"skill-instructions": {
|
|
26
|
+
"tag": "skills",
|
|
27
|
+
"label": "skill instructions",
|
|
28
|
+
"patterns": [
|
|
29
|
+
"**/SKILL.md",
|
|
30
|
+
"**/AGENTS.md"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
"docs": {
|
|
34
|
+
"tag": "docs",
|
|
35
|
+
"label": "documentation",
|
|
36
|
+
"patterns": [
|
|
37
|
+
"docs/**",
|
|
38
|
+
"**/*.md"
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"tag": "scripts",
|
|
43
|
+
"label": "automation scripts",
|
|
44
|
+
"patterns": [
|
|
45
|
+
"scripts/**",
|
|
46
|
+
"**/*.py",
|
|
47
|
+
"**/*.sh"
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
"tests": {
|
|
51
|
+
"tag": "tests",
|
|
52
|
+
"label": "test updates",
|
|
53
|
+
"patterns": [
|
|
54
|
+
"tests/**",
|
|
55
|
+
"**/*test*",
|
|
56
|
+
"**/*.spec.*"
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
"config": {
|
|
60
|
+
"tag": "config",
|
|
61
|
+
"label": "configuration",
|
|
62
|
+
"patterns": [
|
|
63
|
+
".gitignore",
|
|
64
|
+
"**/.gitignore",
|
|
65
|
+
"package.json",
|
|
66
|
+
"package-lock.json",
|
|
67
|
+
"pnpm-lock.yaml",
|
|
68
|
+
"yarn.lock",
|
|
69
|
+
"**/*.json",
|
|
70
|
+
"**/*.yaml",
|
|
71
|
+
"**/*.yml",
|
|
72
|
+
"**/*.toml",
|
|
73
|
+
"**/*.ini"
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
"source": {
|
|
77
|
+
"tag": "src",
|
|
78
|
+
"label": "source changes",
|
|
79
|
+
"patterns": [
|
|
80
|
+
"src/**",
|
|
81
|
+
"app/**",
|
|
82
|
+
"lib/**"
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"scopes": {
|
|
88
|
+
"scaffold": {
|
|
89
|
+
"description": "Scaffold and repository visibility policy",
|
|
90
|
+
"checkpoints": {
|
|
91
|
+
"cadence-tracked": "track .cadence and persist scaffold state",
|
|
92
|
+
"cadence-ignored": "ignore .cadence and persist scaffold state"
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
"prerequisite-gate": {
|
|
96
|
+
"description": "Prerequisite gate state transitions",
|
|
97
|
+
"checkpoints": {
|
|
98
|
+
"prerequisites-passed": "persist prerequisite gate pass state"
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"ideator": {
|
|
102
|
+
"description": "Initial ideation persistence",
|
|
103
|
+
"checkpoints": {
|
|
104
|
+
"ideation-completed": "persist finalized ideation"
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
"ideation-updater": {
|
|
108
|
+
"description": "Iterative ideation updates",
|
|
109
|
+
"checkpoints": {
|
|
110
|
+
"ideation-updated": "persist ideation updates"
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
"project-progress": {
|
|
114
|
+
"description": "Project progress routing checks",
|
|
115
|
+
"checkpoints": {
|
|
116
|
+
"progress-checked": "check progress and route next action"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Check GitHub repo status and persist state.repo-enabled when .cadence exists."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import copy
|
|
8
|
+
import json
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from workflow_state import default_data, reconcile_workflow_state
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
CADENCE_DIR = Path(".cadence")
|
|
18
|
+
CADENCE_JSON_PATH = CADENCE_DIR / "cadence.json"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def run_command(command: list[str], cwd: Path) -> subprocess.CompletedProcess[str]:
|
|
22
|
+
return subprocess.run(
|
|
23
|
+
command,
|
|
24
|
+
cwd=str(cwd),
|
|
25
|
+
capture_output=True,
|
|
26
|
+
text=True,
|
|
27
|
+
check=False,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def parse_args() -> argparse.Namespace:
|
|
32
|
+
parser = argparse.ArgumentParser(
|
|
33
|
+
description="Check project repo readiness and persist Cadence repo-enabled state.",
|
|
34
|
+
)
|
|
35
|
+
parser.add_argument(
|
|
36
|
+
"--project-root",
|
|
37
|
+
default=".",
|
|
38
|
+
help="Project root where git and .cadence state should be evaluated.",
|
|
39
|
+
)
|
|
40
|
+
parser.add_argument(
|
|
41
|
+
"--set-local-only",
|
|
42
|
+
action="store_true",
|
|
43
|
+
help="Persist local-only mode (repo-enabled=false) when no GitHub remote is configured.",
|
|
44
|
+
)
|
|
45
|
+
return parser.parse_args()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def load_cadence_data(project_root: Path) -> dict[str, Any] | None:
|
|
49
|
+
cadence_path = project_root / CADENCE_JSON_PATH
|
|
50
|
+
cadence_dir = project_root / CADENCE_DIR
|
|
51
|
+
if not cadence_path.exists():
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
with cadence_path.open("r", encoding="utf-8") as file:
|
|
56
|
+
raw = json.load(file)
|
|
57
|
+
except json.JSONDecodeError as exc:
|
|
58
|
+
print(f"INVALID_CADENCE_JSON: {exc}", file=sys.stderr)
|
|
59
|
+
raise SystemExit(1)
|
|
60
|
+
|
|
61
|
+
return reconcile_workflow_state(raw, cadence_dir_exists=cadence_dir.exists())
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def save_cadence_data(project_root: Path, data: dict[str, Any]) -> None:
|
|
65
|
+
cadence_path = project_root / CADENCE_JSON_PATH
|
|
66
|
+
cadence_path.parent.mkdir(parents=True, exist_ok=True)
|
|
67
|
+
with cadence_path.open("w", encoding="utf-8") as file:
|
|
68
|
+
json.dump(data, file, indent=4)
|
|
69
|
+
file.write("\n")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def parse_remotes(remote_text: str) -> list[dict[str, str]]:
|
|
73
|
+
remotes: list[dict[str, str]] = []
|
|
74
|
+
seen: set[tuple[str, str]] = set()
|
|
75
|
+
|
|
76
|
+
for line in remote_text.splitlines():
|
|
77
|
+
parts = line.split()
|
|
78
|
+
if len(parts) < 2:
|
|
79
|
+
continue
|
|
80
|
+
name = parts[0].strip()
|
|
81
|
+
url = parts[1].strip()
|
|
82
|
+
key = (name, url)
|
|
83
|
+
if not name or not url or key in seen:
|
|
84
|
+
continue
|
|
85
|
+
seen.add(key)
|
|
86
|
+
remotes.append({"name": name, "url": url})
|
|
87
|
+
|
|
88
|
+
return remotes
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def detect_git_repo(project_root: Path) -> dict[str, Any]:
|
|
92
|
+
inside_result = run_command(["git", "rev-parse", "--is-inside-work-tree"], project_root)
|
|
93
|
+
git_initialized = inside_result.returncode == 0 and inside_result.stdout.strip() == "true"
|
|
94
|
+
|
|
95
|
+
repo_root = ""
|
|
96
|
+
if git_initialized:
|
|
97
|
+
root_result = run_command(["git", "rev-parse", "--show-toplevel"], project_root)
|
|
98
|
+
if root_result.returncode == 0:
|
|
99
|
+
repo_root = root_result.stdout.strip()
|
|
100
|
+
|
|
101
|
+
remotes: list[dict[str, str]] = []
|
|
102
|
+
github_remote_name = ""
|
|
103
|
+
github_remote_url = ""
|
|
104
|
+
if git_initialized:
|
|
105
|
+
remote_result = run_command(["git", "remote", "-v"], project_root)
|
|
106
|
+
if remote_result.returncode == 0:
|
|
107
|
+
remotes = parse_remotes(remote_result.stdout)
|
|
108
|
+
|
|
109
|
+
for remote in remotes:
|
|
110
|
+
url = remote.get("url", "")
|
|
111
|
+
if "github.com" in url.lower() or "github." in url.lower():
|
|
112
|
+
github_remote_name = remote.get("name", "")
|
|
113
|
+
github_remote_url = url
|
|
114
|
+
break
|
|
115
|
+
|
|
116
|
+
github_remote_configured = bool(github_remote_name and github_remote_url)
|
|
117
|
+
repo_enabled_detected = bool(git_initialized and github_remote_configured)
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
"git_initialized": git_initialized,
|
|
121
|
+
"repo_root": repo_root,
|
|
122
|
+
"remotes": remotes,
|
|
123
|
+
"github_remote_configured": github_remote_configured,
|
|
124
|
+
"github_remote_name": github_remote_name,
|
|
125
|
+
"github_remote_url": github_remote_url,
|
|
126
|
+
"repo_enabled_detected": repo_enabled_detected,
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def ensure_default_state(data: dict[str, Any]) -> dict[str, Any]:
|
|
131
|
+
reconciled = reconcile_workflow_state(data, cadence_dir_exists=True)
|
|
132
|
+
state = reconciled.setdefault("state", {})
|
|
133
|
+
if not isinstance(state, dict):
|
|
134
|
+
state = {}
|
|
135
|
+
reconciled["state"] = state
|
|
136
|
+
state["repo-enabled"] = bool(state.get("repo-enabled", False))
|
|
137
|
+
return reconciled
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def main() -> int:
|
|
141
|
+
args = parse_args()
|
|
142
|
+
project_root = Path(args.project_root).resolve()
|
|
143
|
+
|
|
144
|
+
repo_status = detect_git_repo(project_root)
|
|
145
|
+
cadence_exists = (project_root / CADENCE_JSON_PATH).exists()
|
|
146
|
+
state_updated = False
|
|
147
|
+
|
|
148
|
+
data = load_cadence_data(project_root)
|
|
149
|
+
if data is None and cadence_exists:
|
|
150
|
+
# Safety fallback for race conditions between exists check and file read.
|
|
151
|
+
data = default_data()
|
|
152
|
+
|
|
153
|
+
repo_enabled_state = bool(repo_status["repo_enabled_detected"])
|
|
154
|
+
if data is not None:
|
|
155
|
+
original = copy.deepcopy(data)
|
|
156
|
+
data = ensure_default_state(data)
|
|
157
|
+
state = data["state"]
|
|
158
|
+
|
|
159
|
+
if repo_status["repo_enabled_detected"]:
|
|
160
|
+
state["repo-enabled"] = True
|
|
161
|
+
elif args.set_local_only:
|
|
162
|
+
state["repo-enabled"] = False
|
|
163
|
+
else:
|
|
164
|
+
state["repo-enabled"] = False
|
|
165
|
+
|
|
166
|
+
repo_enabled_state = bool(state.get("repo-enabled", False))
|
|
167
|
+
if data != original:
|
|
168
|
+
save_cadence_data(project_root, data)
|
|
169
|
+
state_updated = True
|
|
170
|
+
|
|
171
|
+
response = {
|
|
172
|
+
"status": "ok",
|
|
173
|
+
"project_root": str(project_root),
|
|
174
|
+
"cadence_state_path": str(project_root / CADENCE_JSON_PATH),
|
|
175
|
+
"cadence_state_available": data is not None,
|
|
176
|
+
"state_updated": state_updated,
|
|
177
|
+
"repo_enabled": repo_enabled_state,
|
|
178
|
+
"repo_enabled_detected": bool(repo_status["repo_enabled_detected"]),
|
|
179
|
+
"git_initialized": bool(repo_status["git_initialized"]),
|
|
180
|
+
"github_remote_configured": bool(repo_status["github_remote_configured"]),
|
|
181
|
+
"github_remote_name": repo_status.get("github_remote_name", ""),
|
|
182
|
+
"github_remote_url": repo_status.get("github_remote_url", ""),
|
|
183
|
+
"set_local_only": bool(args.set_local_only),
|
|
184
|
+
}
|
|
185
|
+
print(json.dumps(response))
|
|
186
|
+
return 0
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
if __name__ == "__main__":
|
|
190
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Update project .gitignore policy for .cadence visibility."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import json
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
TARGET_PATTERNS = {".cadence", ".cadence/"}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def parse_args() -> argparse.Namespace:
|
|
15
|
+
parser = argparse.ArgumentParser(
|
|
16
|
+
description="Configure whether .cadence should be tracked or ignored by git.",
|
|
17
|
+
)
|
|
18
|
+
parser.add_argument(
|
|
19
|
+
"--mode",
|
|
20
|
+
choices=["track", "ignore"],
|
|
21
|
+
required=True,
|
|
22
|
+
help="track removes .cadence ignore entries; ignore adds .cadence/ entry",
|
|
23
|
+
)
|
|
24
|
+
parser.add_argument(
|
|
25
|
+
"--gitignore-path",
|
|
26
|
+
default=".gitignore",
|
|
27
|
+
help="Path to .gitignore file",
|
|
28
|
+
)
|
|
29
|
+
return parser.parse_args()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def normalize_lines(text: str) -> list[str]:
|
|
33
|
+
return text.splitlines()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def apply_mode(lines: list[str], mode: str) -> list[str]:
|
|
37
|
+
filtered = [line for line in lines if line.strip() not in TARGET_PATTERNS]
|
|
38
|
+
if mode == "ignore":
|
|
39
|
+
filtered.append(".cadence/")
|
|
40
|
+
return filtered
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def render_lines(lines: list[str]) -> str:
|
|
44
|
+
if not lines:
|
|
45
|
+
return ""
|
|
46
|
+
return "\n".join(lines) + "\n"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def main() -> int:
|
|
50
|
+
args = parse_args()
|
|
51
|
+
gitignore_path = Path(args.gitignore_path)
|
|
52
|
+
|
|
53
|
+
original_text = ""
|
|
54
|
+
if gitignore_path.exists():
|
|
55
|
+
original_text = gitignore_path.read_text(encoding="utf-8")
|
|
56
|
+
|
|
57
|
+
original_lines = normalize_lines(original_text)
|
|
58
|
+
updated_lines = apply_mode(original_lines, args.mode)
|
|
59
|
+
updated_text = render_lines(updated_lines)
|
|
60
|
+
changed = updated_text != original_text
|
|
61
|
+
|
|
62
|
+
if changed:
|
|
63
|
+
gitignore_path.write_text(updated_text, encoding="utf-8")
|
|
64
|
+
|
|
65
|
+
print(
|
|
66
|
+
json.dumps(
|
|
67
|
+
{
|
|
68
|
+
"status": "ok",
|
|
69
|
+
"mode": args.mode,
|
|
70
|
+
"path": str(gitignore_path),
|
|
71
|
+
"changed": changed,
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
return 0
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
if __name__ == "__main__":
|
|
79
|
+
raise SystemExit(main())
|