metame-cli 1.5.11 → 1.5.13

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 (55) hide show
  1. package/index.js +64 -7
  2. package/package.json +3 -2
  3. package/scripts/daemon-agent-commands.js +6 -2
  4. package/scripts/daemon-bridges.js +23 -9
  5. package/scripts/daemon-claude-engine.js +81 -28
  6. package/scripts/daemon-command-router.js +16 -0
  7. package/scripts/daemon-command-session-route.js +3 -1
  8. package/scripts/daemon-engine-runtime.js +1 -5
  9. package/scripts/daemon-message-pipeline.js +113 -44
  10. package/scripts/daemon-reactive-lifecycle.js +405 -9
  11. package/scripts/daemon-session-commands.js +3 -2
  12. package/scripts/daemon-session-store.js +82 -27
  13. package/scripts/daemon-team-dispatch.js +21 -5
  14. package/scripts/daemon-utils.js +3 -1
  15. package/scripts/daemon.js +1 -0
  16. package/scripts/docs/file-transfer.md +1 -0
  17. package/scripts/hooks/intent-file-transfer.js +2 -1
  18. package/scripts/hooks/intent-perpetual.js +109 -0
  19. package/scripts/hooks/intent-research.js +112 -0
  20. package/scripts/intent-registry.js +4 -0
  21. package/scripts/ops-mission-queue.js +258 -0
  22. package/scripts/ops-verifier.js +197 -0
  23. package/skills/agent-browser/SKILL.md +153 -0
  24. package/skills/agent-reach/SKILL.md +66 -0
  25. package/skills/agent-reach/evolution.json +13 -0
  26. package/skills/deep-research/SKILL.md +77 -0
  27. package/skills/find-skills/SKILL.md +133 -0
  28. package/skills/heartbeat-task-manager/SKILL.md +63 -0
  29. package/skills/macos-local-orchestrator/SKILL.md +192 -0
  30. package/skills/macos-local-orchestrator/agents/openai.yaml +4 -0
  31. package/skills/macos-local-orchestrator/references/tooling-landscape.md +70 -0
  32. package/skills/macos-mail-calendar/SKILL.md +394 -0
  33. package/skills/mcp-installer/SKILL.md +138 -0
  34. package/skills/skill-creator/LICENSE.txt +202 -0
  35. package/skills/skill-creator/README.md +72 -0
  36. package/skills/skill-creator/SKILL.md +96 -0
  37. package/skills/skill-creator/evolution.json +6 -0
  38. package/skills/skill-creator/references/creation-guide.md +116 -0
  39. package/skills/skill-creator/references/evolution-guide.md +74 -0
  40. package/skills/skill-creator/references/output-patterns.md +82 -0
  41. package/skills/skill-creator/references/workflows.md +28 -0
  42. package/skills/skill-creator/scripts/align_all.py +32 -0
  43. package/skills/skill-creator/scripts/auto_evolve_hook.js +247 -0
  44. package/skills/skill-creator/scripts/init_skill.py +303 -0
  45. package/skills/skill-creator/scripts/merge_evolution.py +70 -0
  46. package/skills/skill-creator/scripts/package_skill.py +110 -0
  47. package/skills/skill-creator/scripts/quick_validate.py +103 -0
  48. package/skills/skill-creator/scripts/setup.py +141 -0
  49. package/skills/skill-creator/scripts/smart_stitch.py +82 -0
  50. package/skills/skill-manager/SKILL.md +112 -0
  51. package/skills/skill-manager/scripts/delete_skill.py +31 -0
  52. package/skills/skill-manager/scripts/list_skills.py +61 -0
  53. package/skills/skill-manager/scripts/scan_and_check.py +125 -0
  54. package/skills/skill-manager/scripts/sync_index.py +144 -0
  55. package/skills/skill-manager/scripts/update_helper.py +39 -0
@@ -0,0 +1,125 @@
1
+ import os
2
+ import sys
3
+ import yaml
4
+ import json
5
+ import subprocess
6
+ import concurrent.futures
7
+
8
+ def get_remote_hash(url):
9
+ """Fetch the latest commit hash from the remote repository."""
10
+ try:
11
+ # Using git ls-remote to avoid downloading the whole repo
12
+ # Asking for HEAD specifically
13
+ result = subprocess.run(
14
+ ['git', 'ls-remote', url, 'HEAD'],
15
+ capture_output=True,
16
+ text=True,
17
+ timeout=10
18
+ )
19
+ if result.returncode != 0:
20
+ return None
21
+ # Output format: <hash>\tHEAD
22
+ parts = result.stdout.split()
23
+ if parts:
24
+ return parts[0]
25
+ return None
26
+ except Exception:
27
+ return None
28
+
29
+ def scan_skills(skills_root):
30
+ """Scan all subdirectories for SKILL.md and extract metadata."""
31
+ skill_list = []
32
+
33
+ if not os.path.exists(skills_root):
34
+ print(f"Skills root not found: {skills_root}", file=sys.stderr)
35
+ return []
36
+
37
+ for item in os.listdir(skills_root):
38
+ skill_dir = os.path.join(skills_root, item)
39
+ if not os.path.isdir(skill_dir):
40
+ continue
41
+
42
+ skill_md = os.path.join(skill_dir, "SKILL.md")
43
+ if not os.path.exists(skill_md):
44
+ continue
45
+
46
+ # Parse Frontmatter
47
+ try:
48
+ with open(skill_md, 'r', encoding='utf-8') as f:
49
+ content = f.read()
50
+
51
+ # Extract YAML between first two ---
52
+ parts = content.split('---')
53
+ if len(parts) < 3:
54
+ continue # Invalid format
55
+
56
+ frontmatter = yaml.safe_load(parts[1])
57
+
58
+ # Check if managed by github-to-skills
59
+ if 'github_url' in frontmatter:
60
+ skill_list.append({
61
+ "name": frontmatter.get('name', item),
62
+ "dir": skill_dir,
63
+ "github_url": frontmatter['github_url'],
64
+ "local_hash": frontmatter.get('github_hash', 'unknown'),
65
+ "local_version": frontmatter.get('version', '0.0.0')
66
+ })
67
+ except Exception as e:
68
+ # print(f"Skipping {item}: {e}", file=sys.stderr)
69
+ pass
70
+
71
+ return skill_list
72
+
73
+ def check_updates(skills):
74
+ """Check for updates concurrently."""
75
+ results = []
76
+
77
+ with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
78
+ # Create a map of future -> skill
79
+ future_to_skill = {
80
+ executor.submit(get_remote_hash, skill['github_url']): skill
81
+ for skill in skills
82
+ }
83
+
84
+ for future in concurrent.futures.as_completed(future_to_skill):
85
+ skill = future_to_skill[future]
86
+ try:
87
+ remote_hash = future.result()
88
+ skill['remote_hash'] = remote_hash
89
+
90
+ if not remote_hash:
91
+ skill['status'] = 'error'
92
+ skill['message'] = 'Could not reach remote'
93
+ elif remote_hash != skill['local_hash']:
94
+ skill['status'] = 'outdated'
95
+ skill['message'] = 'New commits available'
96
+ else:
97
+ skill['status'] = 'current'
98
+ skill['message'] = 'Up to date'
99
+
100
+ results.append(skill)
101
+ except Exception as e:
102
+ skill['status'] = 'error'
103
+ skill['message'] = str(e)
104
+ results.append(skill)
105
+
106
+ return results
107
+
108
+ if __name__ == "__main__":
109
+ if len(sys.argv) < 2:
110
+ # Default to standard Claude skills path if not provided
111
+ # Trying to guess typical Windows path for this user context
112
+ default_path = os.path.expanduser(r"~\.claude\skills")
113
+ # But we are in a tool env, let's use the provided one or current dir
114
+ if os.path.exists(r"C:\Users\20515\.claude\skills"):
115
+ target_dir = r"C:\Users\20515\.claude\skills"
116
+ else:
117
+ print("Usage: python scan_and_check.py <skills_dir>")
118
+ sys.exit(1)
119
+ else:
120
+ target_dir = sys.argv[1]
121
+
122
+ skills = scan_skills(target_dir)
123
+ updates = check_updates(skills)
124
+
125
+ print(json.dumps(updates, indent=2))
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env python3
2
+ import argparse
3
+ import os
4
+ import re
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ import yaml
9
+
10
+ START_MARKER = "<!-- AUTO-SKILL-INDEX:START -->"
11
+ END_MARKER = "<!-- AUTO-SKILL-INDEX:END -->"
12
+
13
+
14
+ def parse_frontmatter(skill_md_path: Path):
15
+ try:
16
+ text = skill_md_path.read_text(encoding="utf-8")
17
+ except Exception:
18
+ return None, None
19
+
20
+ m = re.match(r"^---\n(.*?)\n---", text, re.DOTALL)
21
+ if not m:
22
+ return None, None
23
+
24
+ try:
25
+ meta = yaml.safe_load(m.group(1)) or {}
26
+ except Exception:
27
+ return None, None
28
+
29
+ name = str(meta.get("name") or "").strip()
30
+ desc = str(meta.get("description") or "").strip()
31
+ return name, desc
32
+
33
+
34
+ def short_desc(text: str, limit: int = 72):
35
+ clean = re.sub(r"\s+", " ", text).strip()
36
+ if len(clean) <= limit:
37
+ return clean
38
+ return clean[: limit - 3] + "..."
39
+
40
+
41
+ def table_escape(text: str):
42
+ return text.replace("|", "\\|")
43
+
44
+
45
+ def source_label(root: Path):
46
+ s = str(root)
47
+ if "/.claude/" in s:
48
+ return "claude"
49
+ if "/.opencode/" in s:
50
+ return "opencode"
51
+ return root.name or "local"
52
+
53
+
54
+ def collect_skills(roots):
55
+ skills = {}
56
+ for root in roots:
57
+ if not root.exists() or not root.is_dir():
58
+ continue
59
+ src = source_label(root)
60
+ for entry in sorted(root.iterdir(), key=lambda p: p.name.lower()):
61
+ if entry.name.startswith("."):
62
+ continue
63
+ if not entry.is_dir():
64
+ continue
65
+ skill_md = entry / "SKILL.md"
66
+ if not skill_md.exists():
67
+ continue
68
+ name, desc = parse_frontmatter(skill_md)
69
+ if not name:
70
+ name = entry.name
71
+ if name in skills:
72
+ continue
73
+ skills[name] = {
74
+ "source": src,
75
+ "desc": short_desc(desc or "No description"),
76
+ }
77
+ return skills
78
+
79
+
80
+ def render_table(skills):
81
+ lines = [
82
+ "| Skill 名 | 来源 | 描述摘要 |",
83
+ "|---|---|---|",
84
+ ]
85
+ for name in sorted(skills.keys(), key=str.lower):
86
+ item = skills[name]
87
+ lines.append(
88
+ f"| `{table_escape(name)}` | {table_escape(item['source'])} | {table_escape(item['desc'])} |"
89
+ )
90
+ return "\n".join(lines)
91
+
92
+
93
+ def inject_table(skill_md_path: Path, table_md: str):
94
+ text = skill_md_path.read_text(encoding="utf-8")
95
+ block = f"{START_MARKER}\n{table_md}\n{END_MARKER}"
96
+
97
+ if START_MARKER in text and END_MARKER in text:
98
+ pattern = re.compile(
99
+ re.escape(START_MARKER) + r".*?" + re.escape(END_MARKER),
100
+ re.DOTALL,
101
+ )
102
+ new_text = pattern.sub(block, text)
103
+ else:
104
+ append = (
105
+ "\n\n## 已注册技能清单(自动生成)\n\n"
106
+ "由 `scripts/sync_index.py` 维护,请勿手工编辑该区块。\n\n"
107
+ f"{block}\n"
108
+ )
109
+ new_text = text + append
110
+
111
+ skill_md_path.write_text(new_text, encoding="utf-8")
112
+
113
+
114
+ def main():
115
+ parser = argparse.ArgumentParser(
116
+ description="Scan skills folders and sync auto-generated skill index section in SKILL.md."
117
+ )
118
+ parser.add_argument(
119
+ "--skill-md",
120
+ required=True,
121
+ help="Path to skill-manager SKILL.md to update.",
122
+ )
123
+ parser.add_argument(
124
+ "roots",
125
+ nargs="+",
126
+ help="Skill root folders to scan (e.g. ~/.claude/skills ~/.opencode/skills).",
127
+ )
128
+ args = parser.parse_args()
129
+
130
+ skill_md_path = Path(os.path.expanduser(args.skill_md)).resolve()
131
+ roots = [Path(os.path.expanduser(r)).resolve() for r in args.roots]
132
+
133
+ if not skill_md_path.exists():
134
+ print(f"[ERROR] skill markdown not found: {skill_md_path}")
135
+ sys.exit(1)
136
+
137
+ skills = collect_skills(roots)
138
+ table_md = render_table(skills)
139
+ inject_table(skill_md_path, table_md)
140
+ print(f"[OK] synced {len(skills)} skills into {skill_md_path}")
141
+
142
+
143
+ if __name__ == "__main__":
144
+ main()
@@ -0,0 +1,39 @@
1
+ import shutil
2
+ import os
3
+ import sys
4
+ import datetime
5
+
6
+ def backup_skill(skill_path):
7
+ """
8
+ Backs up SKILL.md to SKILL.md.bak.<timestamp>
9
+ """
10
+ if not os.path.exists(skill_path):
11
+ return False, "Skill path does not exist"
12
+
13
+ skill_md = os.path.join(skill_path, "SKILL.md")
14
+ if not os.path.exists(skill_md):
15
+ return False, "SKILL.md not found"
16
+
17
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
18
+ backup_name = f"SKILL.md.bak.{timestamp}"
19
+ backup_path = os.path.join(skill_path, backup_name)
20
+
21
+ try:
22
+ shutil.copy2(skill_md, backup_path)
23
+ return True, backup_path
24
+ except Exception as e:
25
+ return False, str(e)
26
+
27
+ if __name__ == "__main__":
28
+ if len(sys.argv) < 2:
29
+ print("Usage: python update_helper.py <skill_dir>")
30
+ sys.exit(1)
31
+
32
+ skill_dir = sys.argv[1]
33
+ success, msg = backup_skill(skill_dir)
34
+
35
+ if success:
36
+ print(f"Backup created: {msg}")
37
+ else:
38
+ print(f"Backup failed: {msg}")
39
+ sys.exit(1)