kimi-dev-workflow 0.1.0__py3-none-any.whl
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.
- kimi_dev_workflow/__init__.py +3 -0
- kimi_dev_workflow/cli.py +45 -0
- kimi_dev_workflow/commands/__init__.py +1 -0
- kimi_dev_workflow/commands/doctor.py +119 -0
- kimi_dev_workflow/commands/index.py +84 -0
- kimi_dev_workflow/commands/init.py +242 -0
- kimi_dev_workflow/commands/memory.py +47 -0
- kimi_dev_workflow/commands/wiki.py +75 -0
- kimi_dev_workflow/config.py +52 -0
- kimi_dev_workflow/detector.py +130 -0
- kimi_dev_workflow/renderer.py +107 -0
- kimi_dev_workflow/scripts/kimi-index +231 -0
- kimi_dev_workflow/scripts/kimi-wiki +294 -0
- kimi_dev_workflow/scripts/release.sh +71 -0
- kimi_dev_workflow/templates/.grepaiignore.template +97 -0
- kimi_dev_workflow/templates/AGENTS.md.template +109 -0
- kimi_dev_workflow/templates/kimi_dir/README.md +144 -0
- kimi_dev_workflow/templates/kimi_dir/memory.md.template +53 -0
- kimi_dev_workflow/templates/kimi_dir/prompts/01-search.md +24 -0
- kimi_dev_workflow/templates/kimi_dir/prompts/02-code.md +33 -0
- kimi_dev_workflow/templates/kimi_dir/prompts/03-refactor.md +29 -0
- kimi_dev_workflow/templates/kimi_dir/prompts/04-debug.md +34 -0
- kimi_dev_workflow/templates/kimi_dir/prompts/05-review.md +31 -0
- kimi_dev_workflow/templates/kimi_dir/prompts/06-wiki.md +22 -0
- kimi_dev_workflow/templates/kimi_dir/prompts/07-memory.md +32 -0
- kimi_dev_workflow/utils.py +43 -0
- kimi_dev_workflow-0.1.0.dist-info/METADATA +317 -0
- kimi_dev_workflow-0.1.0.dist-info/RECORD +31 -0
- kimi_dev_workflow-0.1.0.dist-info/WHEEL +4 -0
- kimi_dev_workflow-0.1.0.dist-info/entry_points.txt +2 -0
- kimi_dev_workflow-0.1.0.dist-info/licenses/LICENSE +21 -0
kimi_dev_workflow/cli.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Kimi Dev Workflow — Unified CLI entry point."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from .commands import doctor, index, init, memory, wiki
|
|
8
|
+
from .config import __version__
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@click.group(
|
|
12
|
+
context_settings={"help_option_names": ["-h", "--help"]},
|
|
13
|
+
invoke_without_command=True,
|
|
14
|
+
)
|
|
15
|
+
@click.version_option(version=__version__, prog_name="kimi-dev")
|
|
16
|
+
@click.pass_context
|
|
17
|
+
def main(ctx: click.Context) -> None:
|
|
18
|
+
"""Kimi Dev Workflow — 让 Kimi CLI 像 Cursor 一样理解你的代码库.
|
|
19
|
+
|
|
20
|
+
\b
|
|
21
|
+
快速开始:
|
|
22
|
+
cd your-project
|
|
23
|
+
kimi-dev init
|
|
24
|
+
kimi-dev index start
|
|
25
|
+
"""
|
|
26
|
+
if ctx.invoked_subcommand is None:
|
|
27
|
+
click.echo(ctx.get_help())
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# Register commands
|
|
31
|
+
main.add_command(init.init)
|
|
32
|
+
main.add_command(index.index)
|
|
33
|
+
main.add_command(wiki.wiki)
|
|
34
|
+
main.add_command(memory.memory)
|
|
35
|
+
main.add_command(doctor.doctor)
|
|
36
|
+
|
|
37
|
+
# Add version as a top-level command too
|
|
38
|
+
@main.command("version")
|
|
39
|
+
def version_cmd() -> None:
|
|
40
|
+
"""显示版本号."""
|
|
41
|
+
click.echo(f"kimi-dev {__version__}")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if __name__ == "__main__":
|
|
45
|
+
main()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Kimi Dev Workflow CLI commands."""
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""kimi-dev doctor — Environment diagnostics."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import shutil
|
|
6
|
+
import subprocess
|
|
7
|
+
from urllib.request import urlopen
|
|
8
|
+
|
|
9
|
+
import click
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from rich.table import Table
|
|
12
|
+
|
|
13
|
+
from ..config import OLLAMA_HOST
|
|
14
|
+
|
|
15
|
+
console = Console()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _check_cmd(name: str) -> tuple[bool, str]:
|
|
19
|
+
"""Check if a command exists and get its version."""
|
|
20
|
+
path = shutil.which(name)
|
|
21
|
+
if not path:
|
|
22
|
+
return False, "未安装"
|
|
23
|
+
|
|
24
|
+
version = "未知版本"
|
|
25
|
+
for flag in ["--version", "-v", "version"]:
|
|
26
|
+
try:
|
|
27
|
+
result = subprocess.run(
|
|
28
|
+
[name, flag],
|
|
29
|
+
capture_output=True,
|
|
30
|
+
text=True,
|
|
31
|
+
timeout=5,
|
|
32
|
+
)
|
|
33
|
+
if result.returncode == 0:
|
|
34
|
+
output = (result.stdout + result.stderr).strip().splitlines()[0]
|
|
35
|
+
version = output[:60]
|
|
36
|
+
break
|
|
37
|
+
except (subprocess.TimeoutExpired, Exception):
|
|
38
|
+
continue
|
|
39
|
+
|
|
40
|
+
return True, version
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _check_ollama() -> tuple[bool, str]:
|
|
44
|
+
"""Check if Ollama is running."""
|
|
45
|
+
try:
|
|
46
|
+
with urlopen(f"{OLLAMA_HOST}/api/tags", timeout=3) as resp:
|
|
47
|
+
if resp.status == 200:
|
|
48
|
+
return True, "运行中"
|
|
49
|
+
except Exception:
|
|
50
|
+
pass
|
|
51
|
+
return False, "未运行"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@click.command()
|
|
55
|
+
def doctor() -> None:
|
|
56
|
+
"""诊断环境依赖状态.
|
|
57
|
+
|
|
58
|
+
检查 kimi、grepai、ollama 等必要依赖是否安装并正常运行。
|
|
59
|
+
"""
|
|
60
|
+
console.print("")
|
|
61
|
+
console.print("[bold blue]🔍 环境诊断[/bold blue]")
|
|
62
|
+
console.print("")
|
|
63
|
+
|
|
64
|
+
table = Table(show_header=True, header_style="bold")
|
|
65
|
+
table.add_column("依赖")
|
|
66
|
+
table.add_column("状态")
|
|
67
|
+
table.add_column("版本/信息")
|
|
68
|
+
|
|
69
|
+
# Kimi CLI
|
|
70
|
+
ok, info = _check_cmd("kimi")
|
|
71
|
+
status = "[green]✓[/green]" if ok else "[red]✗[/red]"
|
|
72
|
+
table.add_row("Kimi CLI", status, info)
|
|
73
|
+
|
|
74
|
+
# grepai
|
|
75
|
+
ok, info = _check_cmd("grepai")
|
|
76
|
+
status = "[green]✓[/green]" if ok else "[red]✗[/red]"
|
|
77
|
+
table.add_row("grepai", status, info)
|
|
78
|
+
|
|
79
|
+
# Ollama service
|
|
80
|
+
ok, info = _check_ollama()
|
|
81
|
+
status = "[green]✓[/green]" if ok else "[red]✗[/red]"
|
|
82
|
+
table.add_row("Ollama", status, info)
|
|
83
|
+
|
|
84
|
+
# ollama binary
|
|
85
|
+
ok, info = _check_cmd("ollama")
|
|
86
|
+
status = "[green]✓[/green]" if ok else "[red]✗[/red]"
|
|
87
|
+
table.add_row("ollama CLI", status, info)
|
|
88
|
+
|
|
89
|
+
# git
|
|
90
|
+
ok, info = _check_cmd("git")
|
|
91
|
+
status = "[green]✓[/green]" if ok else "[red]✗[/red]"
|
|
92
|
+
table.add_row("git", status, info)
|
|
93
|
+
|
|
94
|
+
# Python
|
|
95
|
+
ok, info = _check_cmd("python3")
|
|
96
|
+
if not ok:
|
|
97
|
+
ok, info = _check_cmd("python")
|
|
98
|
+
status = "[green]✓[/green]" if ok else "[red]✗[/red]"
|
|
99
|
+
table.add_row("Python", status, info)
|
|
100
|
+
|
|
101
|
+
console.print(table)
|
|
102
|
+
console.print("")
|
|
103
|
+
|
|
104
|
+
# Recommendations
|
|
105
|
+
has_issues = False
|
|
106
|
+
if not shutil.which("kimi"):
|
|
107
|
+
console.print("[yellow]💡 Kimi CLI 未安装:[/yellow] curl -fsSL https://cli.moonshot.cn/install.sh | sh")
|
|
108
|
+
has_issues = True
|
|
109
|
+
if not shutil.which("grepai"):
|
|
110
|
+
console.print("[yellow]💡 grepai 未安装:[/yellow] curl -fsSL https://raw.githubusercontent.com/yoanbernabeu/grepai/main/install.sh | INSTALL_DIR=$HOME/.local/bin sh")
|
|
111
|
+
has_issues = True
|
|
112
|
+
if not _check_ollama()[0]:
|
|
113
|
+
console.print("[yellow]💡 Ollama 未运行:[/yellow] https://ollama.com/download")
|
|
114
|
+
has_issues = True
|
|
115
|
+
|
|
116
|
+
if not has_issues:
|
|
117
|
+
console.print("[bold green]✅ 所有依赖已就绪![/bold green]")
|
|
118
|
+
|
|
119
|
+
console.print("")
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""kimi-dev index — Code index management commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import subprocess
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
|
|
10
|
+
from ..config import GREPAI_BIN
|
|
11
|
+
from ..utils import find_script
|
|
12
|
+
|
|
13
|
+
console = Console()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _run_kimi_index_cmd(args: list[str]) -> None:
|
|
17
|
+
"""Run kimi-index script, finding it in various locations."""
|
|
18
|
+
script = find_script("kimi-index")
|
|
19
|
+
if script:
|
|
20
|
+
subprocess.run([script] + args, check=False)
|
|
21
|
+
else:
|
|
22
|
+
console.print("[red]✗[/red] kimi-index 未找到。请确保 kimi-dev-workflow 正确安装。")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@click.group("index", help="代码索引管理")
|
|
26
|
+
def index() -> None:
|
|
27
|
+
"""管理本地代码语义索引."""
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@index.command("start")
|
|
32
|
+
def start() -> None:
|
|
33
|
+
"""启动索引监听(后台运行)."""
|
|
34
|
+
console.print("[blue]ℹ[/blue] 启动代码索引...")
|
|
35
|
+
_run_kimi_index_cmd(["start"])
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@index.command("stop")
|
|
39
|
+
def stop() -> None:
|
|
40
|
+
"""停止索引监听."""
|
|
41
|
+
_run_kimi_index_cmd(["stop"])
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@index.command("status")
|
|
45
|
+
def status() -> None:
|
|
46
|
+
"""查看索引状态."""
|
|
47
|
+
_run_kimi_index_cmd(["status"])
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@index.command("search")
|
|
51
|
+
@click.argument("query", nargs=-1, required=True)
|
|
52
|
+
def search(query: tuple[str, ...]) -> None:
|
|
53
|
+
"""语义搜索代码库.
|
|
54
|
+
|
|
55
|
+
\b
|
|
56
|
+
示例:
|
|
57
|
+
kimi-dev index search "用户认证逻辑"
|
|
58
|
+
kimi-dev index search "JWT token 刷新"
|
|
59
|
+
"""
|
|
60
|
+
q = " ".join(query)
|
|
61
|
+
script = find_script("kimi-index")
|
|
62
|
+
if script:
|
|
63
|
+
subprocess.run([script, "search", q], check=False)
|
|
64
|
+
else:
|
|
65
|
+
# Fallback to direct grepai
|
|
66
|
+
try:
|
|
67
|
+
subprocess.run([GREPAI_BIN, "search", q], check=False)
|
|
68
|
+
except FileNotFoundError:
|
|
69
|
+
console.print("[red]✗[/red] grepai 未安装。")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@index.command("reindex")
|
|
73
|
+
def reindex() -> None:
|
|
74
|
+
"""重置并重新索引."""
|
|
75
|
+
if not click.confirm("确定要重置并重新索引吗? 这将删除现有索引数据。", default=False):
|
|
76
|
+
console.print("[yellow]已取消[/yellow]")
|
|
77
|
+
return
|
|
78
|
+
_run_kimi_index_cmd(["reindex"])
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@index.command("logs")
|
|
82
|
+
def logs() -> None:
|
|
83
|
+
"""查看索引日志."""
|
|
84
|
+
_run_kimi_index_cmd(["logs"])
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"""kimi-dev init — Initialize a project with kimi-dev-workflow templates."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib.resources as pkg_resources
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
|
|
13
|
+
from ..config import (
|
|
14
|
+
AGENTS_FILE,
|
|
15
|
+
GREPAIIGNORE_FILE,
|
|
16
|
+
INDEX_DIR,
|
|
17
|
+
KIMI_DIR,
|
|
18
|
+
MEMORY_FILE,
|
|
19
|
+
PACKAGE_NAME,
|
|
20
|
+
WIKI_DIR,
|
|
21
|
+
)
|
|
22
|
+
from ..detector import detect_project, detect_project_name
|
|
23
|
+
from ..renderer import write_agents
|
|
24
|
+
|
|
25
|
+
console = Console()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _get_resource_path(module: str, filename: str) -> Path:
|
|
29
|
+
"""Get a resource file path from the package."""
|
|
30
|
+
try:
|
|
31
|
+
return Path(str(pkg_resources.files(module).joinpath(filename)))
|
|
32
|
+
except (AttributeError, TypeError):
|
|
33
|
+
with pkg_resources.path(module, filename) as p:
|
|
34
|
+
return Path(str(p))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _copy_template(src_module: str, src_name: str, dst: Path) -> None:
|
|
38
|
+
"""Copy a template file from package resources to destination."""
|
|
39
|
+
src = _get_resource_path(src_module, src_name)
|
|
40
|
+
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
41
|
+
shutil.copy2(src, dst)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _copy_directory(src_module: str, dst: Path) -> None:
|
|
45
|
+
"""Copy all files from a package resource directory."""
|
|
46
|
+
try:
|
|
47
|
+
files = pkg_resources.files(src_module)
|
|
48
|
+
for item in files.iterdir():
|
|
49
|
+
if item.is_file():
|
|
50
|
+
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
51
|
+
shutil.copy2(str(item), dst.parent / item.name)
|
|
52
|
+
elif item.is_dir():
|
|
53
|
+
sub_dst = dst / item.name
|
|
54
|
+
sub_dst.mkdir(parents=True, exist_ok=True)
|
|
55
|
+
_copy_directory(f"{src_module}.{item.name}", sub_dst)
|
|
56
|
+
except (AttributeError, TypeError):
|
|
57
|
+
# Fallback: try to use path directly
|
|
58
|
+
with pkg_resources.path(src_module, "") as p:
|
|
59
|
+
src_path = Path(str(p))
|
|
60
|
+
if src_path.exists():
|
|
61
|
+
for item in src_path.iterdir():
|
|
62
|
+
if item.is_file():
|
|
63
|
+
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
64
|
+
shutil.copy2(item, dst.parent / item.name)
|
|
65
|
+
elif item.is_dir() and not item.name.startswith("__"):
|
|
66
|
+
sub_dst = dst / item.name
|
|
67
|
+
shutil.copytree(item, sub_dst, dirs_exist_ok=True)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@click.command()
|
|
71
|
+
@click.option(
|
|
72
|
+
"--wiki-url",
|
|
73
|
+
help="从远程克隆 Wiki 仓库地址",
|
|
74
|
+
)
|
|
75
|
+
@click.option(
|
|
76
|
+
"--project-type",
|
|
77
|
+
type=click.Choice(["react", "vue", "go", "python", "rust", "node", "unknown"]),
|
|
78
|
+
help="强制指定项目类型(自动检测失败时使用)",
|
|
79
|
+
)
|
|
80
|
+
@click.option(
|
|
81
|
+
"--yes / -y",
|
|
82
|
+
"non_interactive",
|
|
83
|
+
default=False,
|
|
84
|
+
help="跳过交互确认,自动填充默认值",
|
|
85
|
+
)
|
|
86
|
+
@click.option(
|
|
87
|
+
"--no-wiki",
|
|
88
|
+
is_flag=True,
|
|
89
|
+
default=False,
|
|
90
|
+
help="不初始化 Wiki 目录",
|
|
91
|
+
)
|
|
92
|
+
@click.pass_context
|
|
93
|
+
def init(
|
|
94
|
+
ctx: click.Context,
|
|
95
|
+
wiki_url: str | None,
|
|
96
|
+
project_type: str | None,
|
|
97
|
+
non_interactive: bool,
|
|
98
|
+
no_wiki: bool,
|
|
99
|
+
) -> None:
|
|
100
|
+
"""初始化当前项目,自动复制模板并配置环境.
|
|
101
|
+
|
|
102
|
+
\b
|
|
103
|
+
示例:
|
|
104
|
+
kimi-dev init
|
|
105
|
+
kimi-dev init --wiki-url https://github.com/user/repo.wiki.git
|
|
106
|
+
kimi-dev init --yes
|
|
107
|
+
"""
|
|
108
|
+
cwd = Path.cwd()
|
|
109
|
+
|
|
110
|
+
console.print("")
|
|
111
|
+
console.print("[bold blue]🚀 Kimi Dev Workflow 项目初始化[/bold blue]")
|
|
112
|
+
console.print("")
|
|
113
|
+
|
|
114
|
+
# 1. Detect project type
|
|
115
|
+
info = detect_project(cwd)
|
|
116
|
+
if project_type:
|
|
117
|
+
# Override if explicitly specified
|
|
118
|
+
from ..detector import ProjectInfo
|
|
119
|
+
info = ProjectInfo(project_type, info.framework, info.language, info.build_tool)
|
|
120
|
+
|
|
121
|
+
project_name = detect_project_name(cwd)
|
|
122
|
+
|
|
123
|
+
type_display = f"{info.framework} + {info.language}" if info.framework != info.language else info.framework
|
|
124
|
+
console.print(f"[blue]ℹ[/blue] 检测到项目类型: [bold]{type_display}[/bold]")
|
|
125
|
+
console.print(f"[blue]ℹ[/blue] 项目名称: [bold]{project_name}[/bold]")
|
|
126
|
+
console.print("")
|
|
127
|
+
|
|
128
|
+
if not non_interactive:
|
|
129
|
+
if not click.confirm("是否继续初始化?", default=True):
|
|
130
|
+
console.print("[yellow]已取消[/yellow]")
|
|
131
|
+
return
|
|
132
|
+
|
|
133
|
+
# 2. Generate AGENTS.md
|
|
134
|
+
agents_path = cwd / AGENTS_FILE
|
|
135
|
+
if agents_path.exists():
|
|
136
|
+
backup = agents_path.with_suffix(".md.backup")
|
|
137
|
+
shutil.copy2(agents_path, backup)
|
|
138
|
+
console.print(f"[yellow]⚠[/yellow] AGENTS.md 已存在,已备份到 {backup.name}")
|
|
139
|
+
|
|
140
|
+
write_agents(cwd, info, project_name)
|
|
141
|
+
console.print(f"[green]✓[/green] 已生成 {AGENTS_FILE}")
|
|
142
|
+
|
|
143
|
+
if not non_interactive:
|
|
144
|
+
console.print("[yellow]⚠[/yellow] 请检查 AGENTS.md 中的内容并根据项目实际情况修改")
|
|
145
|
+
|
|
146
|
+
# 3. Copy .kimi/ directory
|
|
147
|
+
kimi_dst = cwd / KIMI_DIR
|
|
148
|
+
kimi_src_module = f"{PACKAGE_NAME}.templates.kimi_dir"
|
|
149
|
+
|
|
150
|
+
# Copy memory.md.template
|
|
151
|
+
memory_template = _get_resource_path(kimi_src_module, "memory.md.template")
|
|
152
|
+
memory_dst = cwd / MEMORY_FILE
|
|
153
|
+
memory_dst.parent.mkdir(parents=True, exist_ok=True)
|
|
154
|
+
shutil.copy2(memory_template, memory_dst)
|
|
155
|
+
console.print(f"[green]✓[/green] 已创建 {MEMORY_FILE}")
|
|
156
|
+
|
|
157
|
+
# Copy prompts
|
|
158
|
+
prompts_src = f"{kimi_src_module}.prompts"
|
|
159
|
+
prompts_dst = cwd / KIMI_DIR / "prompts"
|
|
160
|
+
try:
|
|
161
|
+
pkg = pkg_resources.files(prompts_src)
|
|
162
|
+
prompts_dst.mkdir(parents=True, exist_ok=True)
|
|
163
|
+
for item in pkg.iterdir():
|
|
164
|
+
if item.is_file() and item.name.endswith(".md"):
|
|
165
|
+
shutil.copy2(str(item), prompts_dst / item.name)
|
|
166
|
+
except (AttributeError, TypeError):
|
|
167
|
+
with pkg_resources.path(prompts_src, "") as p:
|
|
168
|
+
src_path = Path(str(p))
|
|
169
|
+
if src_path.exists():
|
|
170
|
+
prompts_dst.mkdir(parents=True, exist_ok=True)
|
|
171
|
+
for f in src_path.glob("*.md"):
|
|
172
|
+
shutil.copy2(f, prompts_dst / f.name)
|
|
173
|
+
|
|
174
|
+
# Copy .kimi/README.md
|
|
175
|
+
readme_src = _get_resource_path(kimi_src_module, "README.md")
|
|
176
|
+
readme_dst = cwd / KIMI_DIR / "README.md"
|
|
177
|
+
shutil.copy2(readme_src, readme_dst)
|
|
178
|
+
console.print(f"[green]✓[/green] 已复制 prompt 模板到 {KIMI_DIR}/prompts/")
|
|
179
|
+
|
|
180
|
+
# 4. Copy .grepaiignore
|
|
181
|
+
grepaiignore_src = _get_resource_path(f"{PACKAGE_NAME}.templates", GREPAIIGNORE_FILE + ".template")
|
|
182
|
+
grepaiignore_dst = cwd / GREPAIIGNORE_FILE
|
|
183
|
+
shutil.copy2(grepaiignore_src, grepaiignore_dst)
|
|
184
|
+
console.print(f"[green]✓[/green] 已创建 {GREPAIIGNORE_FILE}")
|
|
185
|
+
|
|
186
|
+
# 5. Initialize wiki
|
|
187
|
+
if not no_wiki:
|
|
188
|
+
wiki_path = cwd / WIKI_DIR
|
|
189
|
+
if not wiki_path.exists():
|
|
190
|
+
wiki_path.mkdir(parents=True, exist_ok=True)
|
|
191
|
+
# Create example pages
|
|
192
|
+
(wiki_path / "01-getting-started.md").write_text(
|
|
193
|
+
"# 项目入门指南\n\n## 环境搭建\n\n## 快速开始\n\n## 常用命令\n",
|
|
194
|
+
encoding="utf-8",
|
|
195
|
+
)
|
|
196
|
+
(wiki_path / "02-architecture.md").write_text(
|
|
197
|
+
"# 架构说明\n\n## 系统架构图\n\n## 模块划分\n\n## 技术选型理由\n",
|
|
198
|
+
encoding="utf-8",
|
|
199
|
+
)
|
|
200
|
+
console.print(f"[green]✓[/green] 已创建 {WIKI_DIR}/ 目录及示例页面")
|
|
201
|
+
|
|
202
|
+
if wiki_url:
|
|
203
|
+
console.print(f"[blue]ℹ[/blue] 正在克隆远程 Wiki: {wiki_url}")
|
|
204
|
+
try:
|
|
205
|
+
if wiki_path.exists() and any(wiki_path.iterdir()):
|
|
206
|
+
backup = cwd / f"{WIKI_DIR}.backup"
|
|
207
|
+
shutil.move(str(wiki_path), str(backup))
|
|
208
|
+
console.print(f"[yellow]⚠[/yellow] 已备份旧 wiki 到 {backup.name}")
|
|
209
|
+
subprocess.run(["git", "clone", wiki_url, str(wiki_path)], check=True, capture_output=True)
|
|
210
|
+
(wiki_path / ".wiki-origin").write_text(wiki_url + "\n", encoding="utf-8")
|
|
211
|
+
console.print(f"[green]✓[/green] Wiki 克隆成功")
|
|
212
|
+
except subprocess.CalledProcessError:
|
|
213
|
+
console.print(f"[red]✗[/red] Wiki 克隆失败,请检查仓库地址")
|
|
214
|
+
|
|
215
|
+
# 6. Initialize grepai
|
|
216
|
+
if not (cwd / INDEX_DIR).exists():
|
|
217
|
+
try:
|
|
218
|
+
result = subprocess.run(
|
|
219
|
+
["grepai", "init"],
|
|
220
|
+
capture_output=True,
|
|
221
|
+
text=True,
|
|
222
|
+
cwd=cwd,
|
|
223
|
+
)
|
|
224
|
+
if result.returncode == 0:
|
|
225
|
+
console.print(f"[green]✓[/green] 已初始化 {INDEX_DIR}/")
|
|
226
|
+
else:
|
|
227
|
+
console.print(f"[yellow]⚠[/yellow] grepai init 输出: {result.stderr.strip() or result.stdout.strip()}")
|
|
228
|
+
except FileNotFoundError:
|
|
229
|
+
console.print(f"[yellow]⚠[/yellow] grepai 未安装,跳过索引初始化")
|
|
230
|
+
console.print(" 安装: curl -fsSL https://raw.githubusercontent.com/yoanbernabeu/grepai/main/install.sh | INSTALL_DIR=$HOME/.local/bin sh")
|
|
231
|
+
else:
|
|
232
|
+
console.print(f"[blue]ℹ[/blue] {INDEX_DIR}/ 已存在,跳过初始化")
|
|
233
|
+
|
|
234
|
+
# 7. Summary
|
|
235
|
+
console.print("")
|
|
236
|
+
console.print("[bold green]✅ 初始化完成![/bold green]")
|
|
237
|
+
console.print("")
|
|
238
|
+
console.print("[bold]下一步:[/bold]")
|
|
239
|
+
console.print(" 1. 编辑 AGENTS.md 完善项目信息")
|
|
240
|
+
console.print(" 2. 运行 [bold]kimi-dev index start[/bold] 启动索引")
|
|
241
|
+
console.print(" 3. 运行 [bold]kimi[/bold] 开始开发")
|
|
242
|
+
console.print("")
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""kimi-dev memory — Agent memory commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
|
|
10
|
+
from ..config import MEMORY_FILE
|
|
11
|
+
|
|
12
|
+
console = Console()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@click.group("memory", help="Agent 记忆管理")
|
|
16
|
+
def memory() -> None:
|
|
17
|
+
"""查看和管理 Agent 记忆文件."""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@memory.command("show")
|
|
22
|
+
def show() -> None:
|
|
23
|
+
"""查看记忆文件内容."""
|
|
24
|
+
path = Path.cwd() / MEMORY_FILE
|
|
25
|
+
if not path.exists():
|
|
26
|
+
console.print(f"[yellow]⚠[/yellow] 记忆文件不存在: {MEMORY_FILE}")
|
|
27
|
+
console.print(" 运行 [bold]kimi-dev init[/bold] 创建")
|
|
28
|
+
return
|
|
29
|
+
|
|
30
|
+
content = path.read_text(encoding="utf-8")
|
|
31
|
+
console.print(f"[bold blue]{MEMORY_FILE}[/bold blue]")
|
|
32
|
+
console.print("")
|
|
33
|
+
console.print(content)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@memory.command("edit")
|
|
37
|
+
def edit() -> None:
|
|
38
|
+
"""用默认编辑器打开记忆文件."""
|
|
39
|
+
path = Path.cwd() / MEMORY_FILE
|
|
40
|
+
if not path.exists():
|
|
41
|
+
console.print(f"[yellow]⚠[/yellow] 记忆文件不存在: {MEMORY_FILE}")
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
import os
|
|
45
|
+
|
|
46
|
+
editor = os.environ.get("EDITOR", "vim")
|
|
47
|
+
click.launch(str(path), wait=True, locate=False)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""kimi-dev wiki — Repo Wiki management commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import subprocess
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
|
|
10
|
+
from ..utils import find_script
|
|
11
|
+
|
|
12
|
+
console = Console()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _run_kimi_wiki_cmd(args: list[str]) -> None:
|
|
16
|
+
"""Run kimi-wiki script, finding it in various locations."""
|
|
17
|
+
script = find_script("kimi-wiki")
|
|
18
|
+
if script:
|
|
19
|
+
subprocess.run([script] + args, check=False)
|
|
20
|
+
else:
|
|
21
|
+
console.print("[red]✗[/red] kimi-wiki 未找到。请确保 kimi-dev-workflow 正确安装。")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@click.group("wiki", help="Repo Wiki 管理")
|
|
25
|
+
def wiki() -> None:
|
|
26
|
+
"""管理项目 Wiki 知识库."""
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@wiki.command("init")
|
|
31
|
+
@click.argument("url", required=False)
|
|
32
|
+
def wiki_init(url: str | None) -> None:
|
|
33
|
+
"""初始化 Wiki(本地或从远程克隆).
|
|
34
|
+
|
|
35
|
+
\b
|
|
36
|
+
示例:
|
|
37
|
+
kimi-dev wiki init
|
|
38
|
+
kimi-dev wiki init https://github.com/user/repo.wiki.git
|
|
39
|
+
"""
|
|
40
|
+
args = ["init"]
|
|
41
|
+
if url:
|
|
42
|
+
args.append(url)
|
|
43
|
+
_run_kimi_wiki_cmd(args)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@wiki.command("sync")
|
|
47
|
+
def wiki_sync() -> None:
|
|
48
|
+
"""同步远程 Wiki 更新."""
|
|
49
|
+
_run_kimi_wiki_cmd(["sync"])
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@wiki.command("list")
|
|
53
|
+
def wiki_list() -> None:
|
|
54
|
+
"""列出所有 Wiki 页面."""
|
|
55
|
+
_run_kimi_wiki_cmd(["list"])
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@wiki.command("search")
|
|
59
|
+
@click.argument("query", nargs=-1, required=True)
|
|
60
|
+
def wiki_search(query: tuple[str, ...]) -> None:
|
|
61
|
+
"""语义搜索 Wiki 内容.
|
|
62
|
+
|
|
63
|
+
\b
|
|
64
|
+
示例:
|
|
65
|
+
kimi-dev wiki search "订单状态机"
|
|
66
|
+
kimi-dev wiki search "API 约定"
|
|
67
|
+
"""
|
|
68
|
+
q = " ".join(query)
|
|
69
|
+
_run_kimi_wiki_cmd(["search", q])
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@wiki.command("status")
|
|
73
|
+
def wiki_status() -> None:
|
|
74
|
+
"""查看 Wiki 状态."""
|
|
75
|
+
_run_kimi_wiki_cmd(["status"])
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Configuration constants for kimi-dev-workflow."""
|
|
2
|
+
|
|
3
|
+
import importlib.resources as pkg_resources
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
__version__ = "0.1.0"
|
|
7
|
+
|
|
8
|
+
PACKAGE_NAME = "kimi_dev_workflow"
|
|
9
|
+
|
|
10
|
+
# Template paths (using importlib.resources for package-relative access)
|
|
11
|
+
TEMPLATES_MODULE = f"{PACKAGE_NAME}.templates"
|
|
12
|
+
KIMI_TEMPLATES_MODULE = f"{PACKAGE_NAME}.templates.kimi"
|
|
13
|
+
|
|
14
|
+
# Project-level paths
|
|
15
|
+
INDEX_DIR = ".grepai"
|
|
16
|
+
WIKI_DIR = "wiki"
|
|
17
|
+
MEMORY_FILE = ".kimi/memory.md"
|
|
18
|
+
AGENTS_FILE = "AGENTS.md"
|
|
19
|
+
GREPAIIGNORE_FILE = ".grepaiignore"
|
|
20
|
+
KIMI_DIR = ".kimi"
|
|
21
|
+
|
|
22
|
+
# External dependencies
|
|
23
|
+
OLLAMA_HOST = "http://localhost:11434"
|
|
24
|
+
GREPAI_BIN = "grepai"
|
|
25
|
+
|
|
26
|
+
# Supported project types
|
|
27
|
+
PROJECT_TYPES = ["react", "vue", "go", "python", "rust", "node", "unknown"]
|
|
28
|
+
|
|
29
|
+
# AGENTS.md template placeholders
|
|
30
|
+
AGENTS_PLACEHOLDERS = [
|
|
31
|
+
"{项目名}",
|
|
32
|
+
"{React/Vue/Svelte}",
|
|
33
|
+
"{版本}",
|
|
34
|
+
"{TypeScript/JavaScript}",
|
|
35
|
+
"{Vite/Webpack/Rollup}",
|
|
36
|
+
"{Zustand/Redux/Pinia}",
|
|
37
|
+
"{React Router/Vue Router/TanStack Router}",
|
|
38
|
+
"{Tailwind/Styled Components/SCSS/Less}",
|
|
39
|
+
"{Axios/Fetch/TanStack Query}",
|
|
40
|
+
"{Zod/Yup/Joi}",
|
|
41
|
+
"{react-hook-form/VeeValidate}",
|
|
42
|
+
"{store}",
|
|
43
|
+
"{Context/useState}",
|
|
44
|
+
"{ErrorClass}",
|
|
45
|
+
"{校验库}",
|
|
46
|
+
"{路由文件路径}",
|
|
47
|
+
"{store 目录路径}",
|
|
48
|
+
"{API 封装路径}",
|
|
49
|
+
"{认证相关文件}",
|
|
50
|
+
"{布局组件路径}",
|
|
51
|
+
"{路由文件}",
|
|
52
|
+
]
|