pmflow-cli 0.1.1__tar.gz → 0.1.3__tar.gz

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.
Files changed (29) hide show
  1. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/PKG-INFO +3 -3
  2. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/pyproject.toml +3 -3
  3. pmflow_cli-0.1.3/src/pmflow_cli/commands/skills.py +198 -0
  4. pmflow_cli-0.1.1/src/pmflow_cli/commands/skills.py +0 -100
  5. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/.claude-plugin/plugin.json +0 -0
  6. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/.gitignore +0 -0
  7. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/README.md +0 -0
  8. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/skills/plan-space/SKILL.md +0 -0
  9. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/skills/plan-task/SKILL.md +0 -0
  10. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/skills/prd/SKILL.md +0 -0
  11. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/skills/prdfix/SKILL.md +0 -0
  12. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/skills/snapshot-space/SKILL.md +0 -0
  13. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/src/pmflow_cli/__init__.py +0 -0
  14. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/src/pmflow_cli/client.py +0 -0
  15. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/src/pmflow_cli/commands/__init__.py +0 -0
  16. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/src/pmflow_cli/commands/ai.py +0 -0
  17. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/src/pmflow_cli/commands/auth.py +0 -0
  18. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/src/pmflow_cli/commands/context.py +0 -0
  19. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/src/pmflow_cli/commands/issue.py +0 -0
  20. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/src/pmflow_cli/commands/workspace.py +0 -0
  21. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/src/pmflow_cli/config.py +0 -0
  22. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/src/pmflow_cli/main.py +0 -0
  23. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/src/pmflow_cli/output.py +0 -0
  24. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/src/pmflow_cli/skills/plan-space/SKILL.md +0 -0
  25. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/src/pmflow_cli/skills/plan-task/SKILL.md +0 -0
  26. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/src/pmflow_cli/skills/prd/SKILL.md +0 -0
  27. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/src/pmflow_cli/skills/prdfix/SKILL.md +0 -0
  28. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/src/pmflow_cli/skills/snapshot-space/SKILL.md +0 -0
  29. {pmflow_cli-0.1.1 → pmflow_cli-0.1.3}/src/pmflow_cli/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pmflow-cli
3
- Version: 0.1.1
3
+ Version: 0.1.3
4
4
  Summary: CLI for pmflow — project management for AI-assisted development
5
5
  Project-URL: Repository, https://github.com/zhanglaixian/pmflow
6
6
  Author-email: zhanglaixian <zhanglaixian@zhuanzhuan.com>
@@ -10,8 +10,8 @@ Classifier: Environment :: Console
10
10
  Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Requires-Python: >=3.12
13
- Requires-Dist: httpx>=0.27
14
- Requires-Dist: pmflow-shared>=0.1.1
13
+ Requires-Dist: httpx[socks]>=0.27
14
+ Requires-Dist: pmflow-shared>=0.1.2
15
15
  Requires-Dist: rich>=13.0
16
16
  Requires-Dist: tomli-w>=1.0
17
17
  Requires-Dist: tomli>=2.0; python_version < '3.11'
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pmflow-cli"
3
- version = "0.1.1"
3
+ version = "0.1.3"
4
4
  description = "CLI for pmflow — project management for AI-assisted development"
5
5
  requires-python = ">=3.12"
6
6
  readme = "README.md"
@@ -13,9 +13,9 @@ classifiers = [
13
13
  "Environment :: Console",
14
14
  ]
15
15
  dependencies = [
16
- "pmflow-shared>=0.1.1",
16
+ "pmflow-shared>=0.1.2",
17
17
  "typer>=0.12",
18
- "httpx>=0.27",
18
+ "httpx[socks]>=0.27",
19
19
  "rich>=13.0",
20
20
  "tomli>=2.0; python_version < '3.11'",
21
21
  "tomli-w>=1.0",
@@ -0,0 +1,198 @@
1
+ """Install/uninstall pmflow Claude Code skills and Codex prompts."""
2
+
3
+ import re
4
+ import shutil
5
+ from pathlib import Path
6
+
7
+ import typer
8
+ from rich.table import Table
9
+
10
+ from pmflow_cli.output import console
11
+
12
+ app = typer.Typer(help="Manage Claude Code skills and Codex prompts")
13
+
14
+ SKILLS_SOURCE = Path(__file__).parent.parent / "skills"
15
+
16
+ # Claude Code target
17
+ COMMANDS_TARGET = Path.home() / ".claude" / "commands" / "pmflow"
18
+
19
+ # Codex target
20
+ CODEX_TARGET = Path.home() / ".codex" / "prompts"
21
+
22
+ SKILL_NAMES = ["plan-space", "plan-task", "prd", "prdfix", "snapshot-space"]
23
+
24
+ # Argument hints for Codex prompts (derived from each skill's usage)
25
+ _ARGUMENT_HINTS: dict[str, str] = {
26
+ "plan-space": "{slug}",
27
+ "plan-task": "{slug}-{seq_num}",
28
+ "prd": "功能描述",
29
+ "prdfix": "{slug}-{seq_num}",
30
+ "snapshot-space": "{slug}",
31
+ }
32
+
33
+ # Legacy install path (pre-v0.2), cleaned up on install/uninstall
34
+ _LEGACY_TARGET = Path.home() / ".claude" / "skills"
35
+
36
+
37
+ def _clean_legacy():
38
+ """Remove skills installed to the old ~/.claude/skills/ location."""
39
+ for name in SKILL_NAMES:
40
+ legacy = _LEGACY_TARGET / f"pmflow-{name}"
41
+ if legacy.exists():
42
+ shutil.rmtree(legacy)
43
+
44
+
45
+ def _convert_to_codex_prompt(skill_md: str, name: str) -> str:
46
+ """Convert a Claude Code SKILL.md to a Codex-compatible prompt.
47
+
48
+ Replaces the Claude Code frontmatter (name/description/user-invocable)
49
+ with Codex frontmatter (description/argument-hint), keeping the body as-is.
50
+ """
51
+ # Split frontmatter and body
52
+ match = re.match(r"^---\n(.*?)\n---\n(.*)", skill_md, re.DOTALL)
53
+ if not match:
54
+ return skill_md
55
+
56
+ frontmatter_text = match.group(1)
57
+ body = match.group(2)
58
+
59
+ # Extract description from original frontmatter
60
+ desc_match = re.search(r"^description:\s*(.+)$", frontmatter_text, re.MULTILINE)
61
+ # Use first sentence of description (before "用法:")
62
+ description = desc_match.group(1).strip() if desc_match else name
63
+ description = re.split(r"[。.]?用法[::]", description)[0].rstrip("。. ")
64
+
65
+ hint = _ARGUMENT_HINTS.get(name, "")
66
+
67
+ lines = [
68
+ "---",
69
+ f"description: {description}",
70
+ ]
71
+ if hint:
72
+ lines.append(f"argument-hint: {hint}")
73
+ lines.append("---")
74
+
75
+ return "\n".join(lines) + "\n" + body
76
+
77
+
78
+ @app.command()
79
+ def install(
80
+ force: bool = typer.Option(False, "--force", "-f", help="Overwrite existing skills"),
81
+ ):
82
+ """Install pmflow skills into Claude Code and Codex."""
83
+ if not SKILLS_SOURCE.exists():
84
+ console.print("[red]Error:[/red] Skills source directory not found. Package may be corrupted.")
85
+ raise typer.Exit(1)
86
+
87
+ _clean_legacy()
88
+ COMMANDS_TARGET.mkdir(parents=True, exist_ok=True)
89
+ CODEX_TARGET.mkdir(parents=True, exist_ok=True)
90
+
91
+ installed_cc: list[str] = []
92
+ installed_codex: list[str] = []
93
+ skipped_cc: list[str] = []
94
+ skipped_codex: list[str] = []
95
+
96
+ for name in SKILL_NAMES:
97
+ src = SKILLS_SOURCE / name / "SKILL.md"
98
+ if not src.exists():
99
+ continue
100
+
101
+ # --- Claude Code ---
102
+ cc_dst = COMMANDS_TARGET / f"{name}.md"
103
+ if cc_dst.exists() and not force:
104
+ skipped_cc.append(name)
105
+ else:
106
+ shutil.copy2(src, cc_dst)
107
+ installed_cc.append(name)
108
+
109
+ # --- Codex ---
110
+ codex_dst = CODEX_TARGET / f"pmflow-{name}.md"
111
+ if codex_dst.exists() and not force:
112
+ skipped_codex.append(name)
113
+ else:
114
+ skill_content = src.read_text(encoding="utf-8")
115
+ codex_content = _convert_to_codex_prompt(skill_content, name)
116
+ codex_dst.write_text(codex_content, encoding="utf-8")
117
+ installed_codex.append(name)
118
+
119
+ # Report
120
+ if installed_cc:
121
+ console.print(f"[green]Claude Code — installed {len(installed_cc)} skill(s):[/green] {', '.join(installed_cc)}")
122
+ if skipped_cc:
123
+ console.print(f"[yellow]Claude Code — skipped {len(skipped_cc)} (already exist):[/yellow] {', '.join(skipped_cc)}")
124
+
125
+ if installed_codex:
126
+ console.print(f"[green]Codex — installed {len(installed_codex)} prompt(s):[/green] {', '.join(installed_codex)}")
127
+ if skipped_codex:
128
+ console.print(f"[yellow]Codex — skipped {len(skipped_codex)} (already exist):[/yellow] {', '.join(skipped_codex)}")
129
+
130
+ if skipped_cc or skipped_codex:
131
+ console.print("Use [bold]--force[/bold] to overwrite.")
132
+
133
+ if installed_cc or installed_codex:
134
+ console.print(
135
+ "\n[dim]Claude Code: /pmflow:plan-space, /pmflow:plan-task, /pmflow:prd, /pmflow:prdfix, /pmflow:snapshot-space\n"
136
+ "Codex: /prompts:pmflow-plan-space, /prompts:pmflow-plan-task, /prompts:pmflow-prd, /prompts:pmflow-prdfix, /prompts:pmflow-snapshot-space[/dim]"
137
+ )
138
+
139
+
140
+ @app.command()
141
+ def uninstall():
142
+ """Remove pmflow skills from Claude Code and Codex."""
143
+ _clean_legacy()
144
+
145
+ removed_cc: list[str] = []
146
+ removed_codex: list[str] = []
147
+
148
+ for name in SKILL_NAMES:
149
+ # Claude Code
150
+ cc_dst = COMMANDS_TARGET / f"{name}.md"
151
+ if cc_dst.exists():
152
+ cc_dst.unlink()
153
+ removed_cc.append(name)
154
+
155
+ # Codex
156
+ codex_dst = CODEX_TARGET / f"pmflow-{name}.md"
157
+ if codex_dst.exists():
158
+ codex_dst.unlink()
159
+ removed_codex.append(name)
160
+
161
+ # Remove empty directories
162
+ if COMMANDS_TARGET.exists() and not any(COMMANDS_TARGET.iterdir()):
163
+ COMMANDS_TARGET.rmdir()
164
+
165
+ if removed_cc:
166
+ console.print(f"[green]Claude Code — removed {len(removed_cc)} skill(s):[/green] {', '.join(removed_cc)}")
167
+ if removed_codex:
168
+ console.print(f"[green]Codex — removed {len(removed_codex)} prompt(s):[/green] {', '.join(removed_codex)}")
169
+ if not removed_cc and not removed_codex:
170
+ console.print("[dim]No pmflow skills or prompts found to remove.[/dim]")
171
+
172
+
173
+ @app.command(name="list")
174
+ def list_skills():
175
+ """List pmflow skills and their install status."""
176
+ table = Table(title="pmflow Skills & Prompts")
177
+ table.add_column("Skill", style="bold")
178
+ table.add_column("Claude Code")
179
+ table.add_column("CC Status")
180
+ table.add_column("Codex")
181
+ table.add_column("Codex Status")
182
+
183
+ for name in SKILL_NAMES:
184
+ cc_dst = COMMANDS_TARGET / f"{name}.md"
185
+ cc_status = "[green]installed[/green]" if cc_dst.exists() else "[dim]not installed[/dim]"
186
+
187
+ codex_dst = CODEX_TARGET / f"pmflow-{name}.md"
188
+ codex_status = "[green]installed[/green]" if codex_dst.exists() else "[dim]not installed[/dim]"
189
+
190
+ table.add_row(
191
+ name,
192
+ f"/pmflow:{name}",
193
+ cc_status,
194
+ f"/prompts:pmflow-{name}",
195
+ codex_status,
196
+ )
197
+
198
+ console.print(table)
@@ -1,100 +0,0 @@
1
- """Install/uninstall pmflow Claude Code skills to ~/.claude/commands/pmflow/."""
2
-
3
- import shutil
4
- from pathlib import Path
5
-
6
- import typer
7
- from rich.table import Table
8
-
9
- from pmflow_cli.output import console
10
-
11
- app = typer.Typer(help="Manage Claude Code skills")
12
-
13
- SKILLS_SOURCE = Path(__file__).parent.parent / "skills"
14
- COMMANDS_TARGET = Path.home() / ".claude" / "commands" / "pmflow"
15
- SKILL_NAMES = ["plan-space", "plan-task", "prd", "prdfix", "snapshot-space"]
16
-
17
- # Legacy install path (pre-v0.2), cleaned up on install/uninstall
18
- _LEGACY_TARGET = Path.home() / ".claude" / "skills"
19
-
20
-
21
- def _clean_legacy():
22
- """Remove skills installed to the old ~/.claude/skills/ location."""
23
- for name in SKILL_NAMES:
24
- legacy = _LEGACY_TARGET / f"pmflow-{name}"
25
- if legacy.exists():
26
- shutil.rmtree(legacy)
27
-
28
-
29
- @app.command()
30
- def install(
31
- force: bool = typer.Option(False, "--force", "-f", help="Overwrite existing skills"),
32
- ):
33
- """Install pmflow skills into ~/.claude/commands/pmflow/ for Claude Code."""
34
- if not SKILLS_SOURCE.exists():
35
- console.print("[red]Error:[/red] Skills source directory not found. Package may be corrupted.")
36
- raise typer.Exit(1)
37
-
38
- _clean_legacy()
39
- COMMANDS_TARGET.mkdir(parents=True, exist_ok=True)
40
-
41
- installed = []
42
- skipped = []
43
-
44
- for name in SKILL_NAMES:
45
- src = SKILLS_SOURCE / name / "SKILL.md"
46
- dst = COMMANDS_TARGET / f"{name}.md"
47
-
48
- if dst.exists() and not force:
49
- skipped.append(name)
50
- continue
51
-
52
- shutil.copy2(src, dst)
53
- installed.append(name)
54
-
55
- if installed:
56
- console.print(f"[green]Installed {len(installed)} skill(s):[/green] {', '.join(installed)}")
57
- if skipped:
58
- console.print(f"[yellow]Skipped {len(skipped)} (already exist):[/yellow] {', '.join(skipped)}")
59
- console.print("Use [bold]--force[/bold] to overwrite.")
60
-
61
- if installed:
62
- console.print("\n[dim]Skills are now available as /pmflow:plan-space, /pmflow:plan-task, /pmflow:prd, /pmflow:prdfix, /pmflow:snapshot-space in Claude Code.[/dim]")
63
-
64
-
65
- @app.command()
66
- def uninstall():
67
- """Remove pmflow skills from ~/.claude/commands/pmflow/."""
68
- _clean_legacy()
69
-
70
- removed = []
71
- for name in SKILL_NAMES:
72
- dst = COMMANDS_TARGET / f"{name}.md"
73
- if dst.exists():
74
- dst.unlink()
75
- removed.append(name)
76
-
77
- # Remove the pmflow directory if empty
78
- if COMMANDS_TARGET.exists() and not any(COMMANDS_TARGET.iterdir()):
79
- COMMANDS_TARGET.rmdir()
80
-
81
- if removed:
82
- console.print(f"[green]Removed {len(removed)} skill(s):[/green] {', '.join(removed)}")
83
- else:
84
- console.print("[dim]No pmflow skills found to remove.[/dim]")
85
-
86
-
87
- @app.command(name="list")
88
- def list_skills():
89
- """List pmflow skills and their install status."""
90
- table = Table(title="pmflow Claude Code Skills")
91
- table.add_column("Skill", style="bold")
92
- table.add_column("Command")
93
- table.add_column("Status")
94
-
95
- for name in SKILL_NAMES:
96
- dst = COMMANDS_TARGET / f"{name}.md"
97
- status = "[green]installed[/green]" if dst.exists() else "[dim]not installed[/dim]"
98
- table.add_row(name, f"/pmflow:{name}", status)
99
-
100
- console.print(table)
File without changes
File without changes