cangming-ai-dev-kit 0.1.0

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 (42) hide show
  1. checksums.yaml +7 -0
  2. data/adapters/claude-code/.claude-plugin/plugin.json +20 -0
  3. data/adapters/claude-code/agents/coder.md +22 -0
  4. data/adapters/claude-code/agents/planner.md +24 -0
  5. data/adapters/claude-code/agents/reviewer.md +23 -0
  6. data/adapters/claude-code/hooks/hooks.json +16 -0
  7. data/adapters/codex/.codex-plugin/plugin.json +26 -0
  8. data/adapters/codex/AGENTS.md.template +45 -0
  9. data/core/references/review_checklist.md +48 -0
  10. data/core/references/verify_rules.md +48 -0
  11. data/core/references/workflow.md +65 -0
  12. data/core/scripts/detect_project_type.sh +49 -0
  13. data/core/scripts/summarize_context.sh +55 -0
  14. data/core/scripts/verify_project.sh +61 -0
  15. data/core/skills/code-review/SKILL.md +71 -0
  16. data/core/skills/plan-first/SKILL.md +56 -0
  17. data/core/skills/safe-code-change/SKILL.md +48 -0
  18. data/core/skills/verify-before-done/SKILL.md +71 -0
  19. data/domains/harmony/references/arkts_syntax.md +90 -0
  20. data/domains/harmony/references/arkui_rules.md +81 -0
  21. data/domains/harmony/references/common_compile_errors.md +40 -0
  22. data/domains/harmony/references/harmony_project_structure.md +56 -0
  23. data/domains/harmony/references/hvigor_ohpm_hdc_commands.md +40 -0
  24. data/domains/harmony/scripts/build_hap.sh +39 -0
  25. data/domains/harmony/scripts/lint_harmony.sh +39 -0
  26. data/domains/harmony/scripts/test_harmony.sh +32 -0
  27. data/domains/harmony/scripts/verify_harmony.sh +92 -0
  28. data/domains/harmony/skills/harmony-code/SKILL.md +73 -0
  29. data/domains/harmony/skills/harmony-plan/SKILL.md +68 -0
  30. data/domains/harmony/skills/harmony-verify/SKILL.md +68 -0
  31. data/exe/cangming-dev-kit +6 -0
  32. data/lib/cangming_ai_dev_kit/adapter.rb +68 -0
  33. data/lib/cangming_ai_dev_kit/cli.rb +88 -0
  34. data/lib/cangming_ai_dev_kit/commands/init.rb +53 -0
  35. data/lib/cangming_ai_dev_kit/commands/sync.rb +25 -0
  36. data/lib/cangming_ai_dev_kit/commands/verify.rb +32 -0
  37. data/lib/cangming_ai_dev_kit/syncer.rb +102 -0
  38. data/lib/cangming_ai_dev_kit/verifier.rb +134 -0
  39. data/lib/cangming_ai_dev_kit/version.rb +5 -0
  40. data/lib/cangming_ai_dev_kit.rb +7 -0
  41. data/marketplace/.claude-plugin/marketplace.json +21 -0
  42. metadata +88 -0
@@ -0,0 +1,73 @@
1
+ # harmony-code — HarmonyOS 代码编写
2
+
3
+ ## description
4
+ 在纯血 HarmonyOS 项目中使用 ArkTS / ArkUI 编写高质量代码。
5
+
6
+ ## when to use
7
+ - 编写 HarmonyOS 页面/组件
8
+ - 实现 HarmonyOS 业务逻辑
9
+ - 重构 HarmonyOS 代码
10
+
11
+ ## 核心规则
12
+
13
+ ### 1. 只写 HarmonyOS 代码
14
+ - **不写** Flutter / Android / iOS 代码
15
+ - 使用 ArkTS(兼容 TypeScript 语法子集)
16
+ - 使用 ArkUI 声明式 UI
17
+
18
+ ### 2. 分层架构
19
+
20
+ ```
21
+ page/ — 页面层(组合组件、管理页面状态)
22
+ components/ — 可复用组件层
23
+ model/ — 数据模型层
24
+ service/ — 网络请求/数据处理层
25
+ utils/ — 工具函数层
26
+ ```
27
+
28
+ ### 3. 不把复杂逻辑塞进 build()
29
+ ❌ 错误:
30
+ ```typescript
31
+ build() {
32
+ // 100 行复杂逻辑
33
+ let result = complexCalculation(); // 不要!
34
+ this.dataList = transformData(rawData); // 不要!
35
+ }
36
+ ```
37
+
38
+ ✅ 正确:
39
+ ```typescript
40
+ aboutToAppear() {
41
+ this.initData();
42
+ }
43
+
44
+ build() {
45
+ Column() {
46
+ TitleBar({ title: this.title });
47
+ ContentList({ data: this.dataList });
48
+ }
49
+ }
50
+
51
+ private initData(): void {
52
+ this.dataList = this.transformData(rawData);
53
+ }
54
+ ```
55
+
56
+ ### 4. 状态管理
57
+ - 页面级状态用 @State + @Link
58
+ - 跨页面状态用 AppStorage
59
+ - 组件级状态用 @Prop
60
+ - 深层传递用 @Provide + @Consume
61
+
62
+ ### 5. 修改后验证
63
+ 每次修改后必须执行:
64
+ ```bash
65
+ bash domains/harmony/scripts/verify_harmony.sh
66
+ ```
67
+
68
+ ## done criteria
69
+ - [ ] 符合 HarmonyOS 分层架构
70
+ - [ ] build() 中无复杂逻辑
71
+ - [ ] 状态管理使用正确
72
+ - [ ] 无跨平台代码混入
73
+ - [ ] 验证脚本通过
@@ -0,0 +1,68 @@
1
+ # harmony-plan — HarmonyOS 项目计划
2
+
3
+ ## description
4
+ 针对纯血 HarmonyOS(鸿蒙原生)项目的计划制定。覆盖 ArkTS、ArkUI、路由、状态管理等关键环节。
5
+
6
+ ## when to use
7
+ - 启动新的 HarmonyOS 项目
8
+ - 为 HarmonyOS 项目添加新功能
9
+ - HarmonyOS 项目重构
10
+
11
+ ## workflow
12
+ 1. 确认是纯血 HarmonyOS 项目(非 Android 套壳)
13
+ 2. 阅读项目结构(src/ets 目录结构)
14
+ 3. 页面拆解和路由设计
15
+ 4. 状态管理层设计
16
+ 5. 权限声明检查
17
+ 6. 本地存储方案确定
18
+ 7. 服务层设计
19
+ 8. 测试与构建方案
20
+ 9. 输出计划
21
+
22
+ ## 检查清单
23
+
24
+ ### 页面拆解
25
+ - [ ] 列出所有页面及导航关系
26
+ - [ ] 每个页面的输入和输出定义
27
+ - [ ] 公共组件复用拆分
28
+
29
+ ### 路由
30
+ - [ ] router.pushUrl / router.replaceUrl 使用
31
+ - [ ] 路由参数类型定义
32
+ - [ ] 页面返回逻辑
33
+
34
+ ### 状态管理
35
+ - [ ] @State / @Prop / @Link / @Provide + @Consume
36
+ - [ ] AppStorage / LocalStorage 使用
37
+ - [ ] 全局状态 vs 局部状态
38
+
39
+ ### 权限
40
+ - [ ] oh-permission 声明
41
+ - [ ] 动态权限申请
42
+ - [ ] 权限被拒绝的处理
43
+
44
+ ### 本地存储
45
+ - [ ] Preferences 使用
46
+ - [ ] 数据库(关系型/RDB)使用
47
+ - [ ] 文件存储
48
+
49
+ ### 服务层
50
+ - [ ] http 请求封装
51
+ - [ ] 数据模型定义
52
+ - [ ] 错误处理
53
+
54
+ ### 测试与构建
55
+ - [ ] ohpm install 依赖
56
+ - [ ] hvigorw assembleHap 构建
57
+ - [ ] hdc 安装部署
58
+
59
+ ## output format
60
+ 遵循 core/skills/plan-first/SKILL.md 的输出格式,加上 HarmonyOS 专项检查结果。
61
+
62
+ ## done criteria
63
+ - [ ] 页面拆解完成
64
+ - [ ] 路由方案确定
65
+ - [ ] 状态管理方案确定
66
+ - [ ] 权限列表明确
67
+ - [ ] 存储方案确定
68
+ - [ ] 构建验证方案确定
@@ -0,0 +1,68 @@
1
+ # harmony-verify — HarmonyOS 项目验证
2
+
3
+ ## description
4
+ 对 HarmonyOS 项目执行完整的验证流程:依赖安装、lint、测试、构建。
5
+
6
+ ## when to use
7
+ - HarmonyOS 代码修改完成后
8
+ - 构建部署前
9
+ - PR 提交前
10
+
11
+ ## workflow
12
+ 1. 确认当前目录是 HarmonyOS 项目
13
+ 2. 执行 `ohpm install`(如果 ohpm 可用)
14
+ 3. 执行 lint 检查
15
+ 4. 执行测试
16
+ 5. 执行 `hvigorw assembleHap` 构建
17
+ 6. 记录所有结果
18
+
19
+ ## 验证步骤
20
+
21
+ ### 1. 依赖安装
22
+ ```bash
23
+ ohpm install
24
+ ```
25
+ - 如果 ohpm 不存在:输出 SKIP,原因是命令行工具未安装
26
+ - 如果执行失败:输出 FAIL,需要修复依赖
27
+
28
+ ### 2. Lint
29
+ 使用 `hvigorw lint` 或等效命令。
30
+ - 如果命令不存在:输出 SKIP
31
+ - 不伪造结果
32
+
33
+ ### 3. 测试
34
+ ```bash
35
+ ./hvigorw test
36
+ ```
37
+ - 如果命令不存在:输出 SKIP
38
+ - 不伪造结果
39
+
40
+ ### 4. 构建
41
+ ```bash
42
+ ./hvigorw assembleHap
43
+ ```
44
+ - 如果 hvigorw 不存在:输出 SKIP
45
+ - 不伪造结果
46
+
47
+ ## output format
48
+
49
+ ```markdown
50
+ ## HarmonyOS 验证报告
51
+
52
+ | 步骤 | 命令 | 结果 | 说明 |
53
+ |------|------|------|------|
54
+ | install | ohpm install | PASS | / |
55
+ | lint | hvigorw lint | SKIP | 命令不存在 |
56
+ | test | hvigorw test | SKIP | 命令不存在 |
57
+ | build | hvigorw assembleHap | PASS | / |
58
+
59
+ 最终结果:PASS
60
+ ```
61
+
62
+ ## done criteria
63
+ - [ ] ohpm install 已完成(或明确跳过)
64
+ - [ ] lint 已执行(或明确跳过)
65
+ - [ ] 测试已执行(或明确跳过)
66
+ - [ ] 构建已完成(或明确跳过)
67
+ - [ ] 无伪造的 PASS
68
+ - [ ] 报告已输出
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/cangming_ai_dev_kit"
5
+
6
+ CangmingAiDevKit::CLI.start(ARGV)
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CangmingAiDevKit
4
+ class Adapter
5
+ attr_reader :name, :relative_root, :plugin_dir_name, :plugin_file
6
+
7
+ def initialize(name, plugin_dir_name:)
8
+ @name = name
9
+ @plugin_dir_name = plugin_dir_name
10
+ @relative_root = "adapters/#{name}"
11
+ @plugin_file = "#{plugin_dir_name}/plugin.json"
12
+ end
13
+
14
+ def plugin_file_relative
15
+ "#{relative_root}/#{plugin_file}"
16
+ end
17
+
18
+ def skill_paths(data_root)
19
+ paths = []
20
+ skills_dir = File.join(data_root, relative_root, plugin_dir_name)
21
+ return paths unless Dir.exist?(skills_dir)
22
+ Dir.glob("#{skills_dir}/skills/*")
23
+ paths
24
+ end
25
+
26
+ def adapter_full_path(target_dir)
27
+ File.join(target_dir, "adapters", name)
28
+ end
29
+
30
+ SCAFFOLD_DIRS = %w[
31
+ skills
32
+ ].freeze
33
+
34
+ SCAFFOLD_FILES = {
35
+ "claude-code" => {
36
+ ".claude-plugin/plugin.json" => "adapters/claude-code/.claude-plugin/plugin.json",
37
+ "hooks/hooks.json" => "adapters/claude-code/hooks/hooks.json",
38
+ "agents/planner.md" => "adapters/claude-code/agents/planner.md",
39
+ "agents/coder.md" => "adapters/claude-code/agents/coder.md",
40
+ "agents/reviewer.md" => "adapters/claude-code/agents/reviewer.md",
41
+ },
42
+ "codex" => {
43
+ ".codex-plugin/plugin.json" => "adapters/codex/.codex-plugin/plugin.json",
44
+ "AGENTS.md.template" => "adapters/codex/AGENTS.md.template",
45
+ },
46
+ }.freeze
47
+
48
+ def scaffold_files(data_root)
49
+ SCAFFOLD_FILES.fetch(@name, {})
50
+ end
51
+
52
+ SKILL_SOURCE_PATTERNS = [
53
+ "core/skills/*", # → skills/core/<name>/SKILL.md
54
+ "domains/*/skills/*", # → skills/<domain>/<name>/SKILL.md
55
+ ].freeze
56
+
57
+ def self.available
58
+ @available ||= {
59
+ "claude-code" => new("claude-code", plugin_dir_name: ".claude-plugin"),
60
+ "codex" => new("codex", plugin_dir_name: ".codex-plugin"),
61
+ }
62
+ end
63
+
64
+ def self.resolve(name)
65
+ available[name] || available["claude-code"]
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+
5
+ require_relative "commands/init"
6
+ require_relative "commands/sync"
7
+ require_relative "commands/verify"
8
+
9
+ module CangmingAiDevKit
10
+ class CLI
11
+ def self.start(argv)
12
+ new.run(argv)
13
+ end
14
+
15
+ def run(argv)
16
+ command = argv.empty? ? "help" : argv.shift
17
+
18
+ case command
19
+ when "init"
20
+ do_init(argv)
21
+ when "sync"
22
+ do_sync(argv)
23
+ when "verify"
24
+ do_verify(argv)
25
+ when "version", "--version", "-v"
26
+ puts "cangming-ai-dev-kit #{VERSION}"
27
+ else
28
+ help
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def do_init(argv)
35
+ adapter_name = argv.shift || "claude-code"
36
+ target_dir = argv.shift || "."
37
+ with_skills = argv.include?("--with-skills")
38
+ force = argv.include?("--force")
39
+
40
+ Commands::Init.new(target_dir, adapter_name, with_skills: with_skills, force: force).run
41
+ end
42
+
43
+ def do_sync(argv)
44
+ target_dir = "."
45
+ adapter = nil
46
+ dry_run = false
47
+
48
+ parser = OptionParser.new do |opts|
49
+ opts.banner = "Usage: cangming-dev-kit sync [DIR] [options]"
50
+
51
+ opts.on("--adapter ADAPTER", "Sync to specific adapter (claude-code, codex)") { |v| adapter = v }
52
+ opts.on("--dry-run", "Preview only") { |v| dry_run = v }
53
+ end
54
+
55
+ parser.parse!(argv)
56
+ # 如果还有剩余参数, 视为 target_dir
57
+ target_dir = argv.shift if argv.first && !argv.first.start_with?("-")
58
+
59
+ Commands::Sync.new(target_dir, adapter: adapter, dry_run: dry_run).run
60
+ end
61
+
62
+ def do_verify(argv)
63
+ target_dir = argv.shift || "."
64
+ ok = Commands::Verify.new(target_dir).run
65
+ exit(1) unless ok
66
+ end
67
+
68
+ def help
69
+ puts "cangming-ai-dev-kit #{VERSION}"
70
+ puts ""
71
+ puts "Usage: cangming-dev-kit COMMAND [options]"
72
+ puts ""
73
+ puts "Commands:"
74
+ puts " init ADAPTER [DIR] Bootstrap adapter skeleton"
75
+ puts " --with-skills Also sync skills"
76
+ puts " --force Overwrite existing files"
77
+ puts ""
78
+ puts " sync [DIR] Sync skills to adapter directory"
79
+ puts " --adapter ADAPTER Sync to specific adapter only"
80
+ puts " --dry-run Preview mode"
81
+ puts ""
82
+ puts " verify [DIR] Validate project structure"
83
+ puts " version Print version"
84
+ puts ""
85
+ puts "Adapters: claude-code, codex"
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+
5
+ module CangmingAiDevKit
6
+ module Commands
7
+ class Init
8
+ def initialize(target_dir, adapter_name, with_skills: false, force: false)
9
+ @target_dir = target_dir
10
+ @adapter_name = adapter_name
11
+ @with_skills = with_skills
12
+ @force = force
13
+ end
14
+
15
+ def run
16
+ data_root = Syncer.data_root
17
+ adapter = Adapter.resolve(@adapter_name)
18
+ dest_root = adapter.adapter_full_path(@target_dir)
19
+
20
+ FileUtils.mkdir_p(dest_root)
21
+
22
+ files = adapter.scaffold_files(data_root)
23
+ files.each do |relative_dest, source_relative|
24
+ source = File.join(data_root, source_relative)
25
+ dest = File.join(dest_root, relative_dest)
26
+
27
+ if File.exist?(dest) && !@force
28
+ puts " SKIP #{relative_dest} (已存在,使用 --force 覆盖)"
29
+ next
30
+ end
31
+
32
+ FileUtils.mkdir_p(File.dirname(dest))
33
+ FileUtils.cp(source, dest)
34
+ puts " #{@adapter_name}/#{relative_dest}"
35
+ end
36
+
37
+ # 创建 skills 占位目录
38
+ skills_dir = File.join(dest_root, "skills")
39
+ FileUtils.mkdir_p(skills_dir)
40
+
41
+ if @with_skills
42
+ puts ""
43
+ puts "--- 同步 skills ---"
44
+ syncer = Syncer.new
45
+ syncer.sync(@target_dir, adapter: @adapter_name, dry_run: false)
46
+ end
47
+
48
+ puts ""
49
+ puts "#{@adapter_name} adapter 已就绪: #{dest_root}"
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CangmingAiDevKit
4
+ module Commands
5
+ class Sync
6
+ def initialize(target_dir, adapter: nil, dry_run: false)
7
+ @target_dir = target_dir
8
+ @adapter = adapter
9
+ @dry_run = dry_run
10
+ end
11
+
12
+ def run
13
+ syncer = Syncer.new
14
+ stats = syncer.sync(@target_dir, adapter: @adapter, dry_run: @dry_run)
15
+
16
+ puts ""
17
+ stats.each do |adp, info|
18
+ status = @dry_run ? "[dry-run]" : "[synced]"
19
+ puts "#{status} #{adp}: #{info[:count]} skills"
20
+ info[:skills].each { |s| puts " - #{s}" }
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CangmingAiDevKit
4
+ module Commands
5
+ class Verify
6
+ def initialize(target_dir)
7
+ @target_dir = target_dir
8
+ end
9
+
10
+ def run
11
+ verifier = Verifier.new(@target_dir)
12
+ result = verifier.verify
13
+
14
+ puts ""
15
+ puts "===== 验证汇总 ====="
16
+ puts " PASS: #{result.pass}"
17
+ puts " FAIL: #{result.fail}"
18
+ puts " WARN: #{result.warn}"
19
+
20
+ if result.fail.zero?
21
+ puts ""
22
+ puts " MVP_VERIFY_PASS"
23
+ return true
24
+ else
25
+ puts ""
26
+ puts " MVP_VERIFY_FAIL (#{result.fail} 项失败)"
27
+ return false
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+
5
+ module CangmingAiDevKit
6
+ class Syncer
7
+ # 返回 gem 安装后的项目根目录
8
+ def self.gem_data_root
9
+ @gem_data_root ||= begin
10
+ spec = Gem::Specification.find_by_name("cangming-ai-dev-kit")
11
+ spec.gem_dir
12
+ end
13
+ rescue Gem::LoadError
14
+ # 开发模式:从 lib/ 往上找到项目根目录
15
+ File.expand_path("../..", __dir__)
16
+ end
17
+
18
+ def self.data_root
19
+ @data_root ||= gem_data_root
20
+ end
21
+
22
+ # 收集所有 skill 源
23
+ # 返回 [{ category:, name:, path: }]
24
+ def self.skill_sources
25
+ sources = []
26
+
27
+ root = data_root
28
+ return sources unless Dir.exist?(root)
29
+
30
+ # core/skills/*
31
+ core_skills = File.join(root, "core", "skills")
32
+ if Dir.exist?(core_skills)
33
+ Dir.glob("#{core_skills}/*").each do |dir|
34
+ next unless File.directory?(dir)
35
+ sources << { category: "core", name: File.basename(dir), path: dir }
36
+ end
37
+ end
38
+
39
+ # domains/*/skills/*
40
+ domains_dir = File.join(root, "domains")
41
+ if Dir.exist?(domains_dir)
42
+ Dir.glob("#{domains_dir}/*/skills/*").each do |dir|
43
+ next unless File.directory?(dir)
44
+ parts = dir.split("/")
45
+ domain = parts[-3]
46
+ name = parts[-1]
47
+ sources << { category: domain, name: name, path: dir }
48
+ end
49
+ end
50
+
51
+ sources
52
+ end
53
+
54
+ # 列出所有可用 adapter 的名称
55
+ def self.adapters_available
56
+ @adapters_available ||= ["claude-code", "codex"]
57
+ end
58
+
59
+ # 同步 skills 到指定 adapter(s)
60
+ # target_dir: 目标根目录(包含 adapters/<name>/skills/)
61
+ # adapter: 可选,仅同步指定 adapter
62
+ # dry_run: 仅预览,不实际复制
63
+ # 返回同步统计 { adapter => { count:, skills: [] } }
64
+ def sync(target_dir, adapter: nil, dry_run: false)
65
+ adapters = adapter ? [adapter] : self.class.adapters_available
66
+ sources = self.class.skill_sources
67
+ stats = {}
68
+
69
+ adapters.each do |adp|
70
+ skills_root = File.join(target_dir, "adapters", adp, "skills")
71
+ copied = []
72
+
73
+ if dry_run
74
+ puts "[#{adp}] 目标: #{skills_root}"
75
+ puts "[#{adp}] 将清空 skills/ 目录并重新同步"
76
+ elsif Dir.exist?(skills_root)
77
+ FileUtils.rm_rf(Dir.glob("#{skills_root}/*"))
78
+ else
79
+ FileUtils.mkdir_p(skills_root)
80
+ end
81
+
82
+ sources.each do |src|
83
+ dest_dir = File.join(skills_root, src[:category], src[:name])
84
+ dest_file = File.join(dest_dir, "SKILL.md")
85
+ src_file = File.join(src[:path], "SKILL.md")
86
+
87
+ if dry_run
88
+ puts " #{src[:category]}/#{src[:name]} → #{dest_file}"
89
+ else
90
+ FileUtils.mkdir_p(dest_dir)
91
+ FileUtils.cp(src_file, dest_file)
92
+ end
93
+ copied << "#{src[:category]}/#{src[:name]}"
94
+ end
95
+
96
+ stats[adp] = { count: copied.size, skills: copied }
97
+ end
98
+
99
+ stats
100
+ end
101
+ end
102
+ end