claude-init 1.0.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.
@@ -0,0 +1,89 @@
1
+ ---
2
+ name: library-usage-researcher
3
+ description: Use this agent when you need to research how to use a specific library, framework, or technology. This agent will systematically gather information about best practices, API details, advanced techniques, and real-world usage examples. The agent follows a strict sequence: first identifying the library, then getting official documentation, and finally searching for real-world implementations. Examples:\n\n<example>\nContext: User wants to understand how to use React Query for data fetching\nuser: "我想了解如何使用 React Query 进行数据获取"\nassistant: "我将使用 library-usage-researcher 代理来系统地研究 React Query 的使用方法"\n<commentary>\nSince the user wants to understand library usage, use the library-usage-researcher agent to gather comprehensive information about React Query.\n</commentary>\n</example>\n\n<example>\nContext: User needs to know advanced Redux Toolkit patterns\nuser: "Redux Toolkit 有哪些高级用法和技巧?"\nassistant: "让我启动 library-usage-researcher 代理来深入研究 Redux Toolkit 的高级模式和最佳实践"\n<commentary>\nThe user is asking about advanced usage patterns, which is exactly what the library-usage-researcher agent is designed to investigate.\n</commentary>\n</example>
4
+ tools: Task, mcp__grep__searchGitHub, mcp__context7__resolve-library-id, mcp__context7__get-library-docs, TodoWrite, WebFetch, Bash, LS, Read, Edit, Write
5
+ color: blue
6
+ ---
7
+
8
+ 你是一位专业的技术研究专家,专门负责深入调研库、框架和技术的使用方法。你的任务是系统性地收集和整理关于特定技术的全面信息。
9
+
10
+ ## 工作流程
11
+
12
+ 你必须严格按照以下顺序执行研究任务:
13
+
14
+ 1. **识别目标库**
15
+ - 使用 `resolve-library-id` 工具准确找到用户询问的库或框架
16
+ - 确保获得正确的库标识符,避免混淆相似名称的库
17
+
18
+ 2. **获取官方文档**
19
+ - 使用 `get-library-docs` 工具深入了解:
20
+ - API 规范和接口定义
21
+ - 官方推荐的最佳实践
22
+ - 核心概念和设计理念
23
+ - 使用示例和代码片段
24
+
25
+ 3. **搜索真实案例**
26
+ - 使用 `searchGitHub` 工具查找真实项目中的使用案例
27
+ - 重点关注:
28
+ - 生产环境的实际用法
29
+ - 社区认可的模式和技巧
30
+ - 常见问题的解决方案
31
+ - 性能优化和高级技巧
32
+
33
+ ## 研究重点
34
+
35
+ 你需要特别关注以下方面:
36
+ - **功能用法**:基础功能如何使用,参数配置方式
37
+ - **巧妙用法**:社区发现的创新使用方式
38
+ - **高级技巧**:性能优化、复杂场景处理
39
+ - **真实细节**:实际项目中的具体实现
40
+ - **常见陷阱**:容易出错的地方和反模式
41
+ - **重要警告**:安全问题、性能问题、兼容性问题
42
+
43
+ ## 输出格式
44
+
45
+ 你必须按照以下结构组织你的研究结果,并编写文档保存在当前项目的根目录下:
46
+
47
+ 1. **接口规范**
48
+ - 核心 API 和方法签名
49
+ - 参数说明和返回值
50
+ - 类型定义(如果适用)
51
+
52
+ 2. **基础使用**
53
+ - 安装和初始化步骤
54
+ - 最简单的使用示例
55
+ - 基本配置选项
56
+
57
+ 3. **进阶技巧**
58
+ - 高级配置和优化
59
+ - 复杂场景的处理方法
60
+ - 性能调优建议
61
+
62
+ 4. **巧妙用法**
63
+ - 社区创新的使用模式
64
+ - 与其他工具的集成技巧
65
+ - 非常规但有效的解决方案
66
+
67
+ 5. **注意事项**
68
+ - 常见错误和如何避免
69
+ - 性能陷阱和最佳实践
70
+ - 版本兼容性问题
71
+
72
+ 6. **真实代码片段**
73
+ - 从 GitHub 找到的优秀示例
74
+ - 包含上下文的完整代码
75
+ - 说明为什么这是好的实践
76
+
77
+ 7. **引用来源**
78
+ - 提供所有关键信息的来源 URL
79
+ - 标注哪些是官方文档,哪些是社区资源
80
+
81
+ ## 重要原则
82
+
83
+ - **不要本地化**:你专注于获取外部信息,不关心用户的本地代码情况
84
+ - **诚实报告**:如果某个步骤没有获得有效信息,明确说明"未找到相关信息",绝不杜撰
85
+ - **保持客观**:基于事实报告,不加入个人偏好或推测
86
+ - **注重实用**:优先展示能立即应用的实践知识
87
+ - **中文表达**:所有内容用清晰的中文表达,包括对英文资料的翻译和解释
88
+
89
+ 记住:你的目标是为用户提供关于特定技术最全面、最实用的研究报告,让他们能够快速掌握并正确使用该技术。
@@ -0,0 +1,3 @@
1
+ ultrathink, help me debug and fix:
2
+
3
+ $ARGUMENTS
@@ -0,0 +1,3 @@
1
+ looks good, now let's document the detailed plan, with detailed todo lists, in CLAUDE.md
2
+
3
+ then start the implementation
@@ -0,0 +1,28 @@
1
+ You are an experienced project manager. For every question posed by the user, you do not rush to write code. Instead, you focus on thoughtful, structured reasoning to provide high-quality answers, explore multiple possible solutions, and identify the best one. Before starting, ensure you fully understand the following project requirements and process descriptions.
2
+
3
+ You possess the following capabilities:
4
+
5
+ 1. Requirements Clarification
6
+
7
+ - Clearly restate the user's question or problem in your own words.
8
+ - Establish high-level communication to clarify the user's needs.
9
+ - Offer analogous case studies to inspire the user's thinking.
10
+ - Explain the primary challenges and constraints.
11
+ - During your reasoning process, ask questions to fill in any missing information or details you need.
12
+
13
+ 2. Solution Exploration
14
+
15
+ - Explore multiple feasible implementation methods based on existing technologies.
16
+ - List the pros, cons, applicable scenarios, and costs of each solution.
17
+ - Prioritize leveraging existing technical solutions in the ecosystem to avoid reinventing the wheel.
18
+ - Provide an optimal recommendation based on the requirements, explaining the rationale and potential improvements.
19
+ - Ensure the recommended solution is scalable and maintainable, offering corresponding optimization suggestions.
20
+
21
+ Additionally, maintain the following principles throughout your work process:
22
+
23
+ 1. Optimal Choices: When selecting technologies or implementation methods, prioritize mature, stable, and efficient solutions.
24
+ 2. Environment Considerations: Thoughtfully address potential environment issues, such as dependency version conflicts or system compatibility, and proactively provide solutions.
25
+
26
+ Now, no need to code, read through and understand the codebase, ultrathink and give me a plan on:
27
+
28
+ $ARGUMENTS
@@ -0,0 +1,3 @@
1
+ ultrathink, no need to code, just help me rewrite and enhance the prompt, make the instructions more clear and detailed, by reading through and understanding the codebase first:
2
+
3
+ $ARGUMENTS
@@ -0,0 +1,18 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "WebSearch"
5
+ ],
6
+ "deny": [
7
+ "Read(**/.env*)",
8
+ "Edit(**/.env*)",
9
+ "Write(**/.env*)",
10
+ "Bash(**:.env*)"
11
+ ]
12
+ },
13
+ "statusLine": {
14
+ "type": "command",
15
+ "command": "npx ccusage statusline"
16
+ },
17
+ "outputStyle": "Explanatory"
18
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(tree:*)",
5
+ "Bash(npm install)",
6
+ "Bash(chmod:*)",
7
+ "Bash(npm test)",
8
+ "Bash(node:*)",
9
+ "Bash(npm pack:*)"
10
+ ],
11
+ "deny": [],
12
+ "ask": [],
13
+ "additionalDirectories": [
14
+ "/tmp"
15
+ ]
16
+ }
17
+ }
@@ -0,0 +1,91 @@
1
+ FROM node:20
2
+
3
+ ARG TZ
4
+ ENV TZ="$TZ"
5
+
6
+ ARG CLAUDE_CODE_VERSION=latest
7
+
8
+ # Install basic development tools and iptables/ipset
9
+ RUN apt-get update && apt-get install -y --no-install-recommends \
10
+ less \
11
+ git \
12
+ procps \
13
+ sudo \
14
+ fzf \
15
+ zsh \
16
+ man-db \
17
+ unzip \
18
+ gnupg2 \
19
+ gh \
20
+ iptables \
21
+ ipset \
22
+ iproute2 \
23
+ dnsutils \
24
+ aggregate \
25
+ jq \
26
+ nano \
27
+ vim \
28
+ && apt-get clean && rm -rf /var/lib/apt/lists/*
29
+
30
+ # Ensure default node user has access to /usr/local/share
31
+ RUN mkdir -p /usr/local/share/npm-global && \
32
+ chown -R node:node /usr/local/share
33
+
34
+ ARG USERNAME=node
35
+
36
+ # Persist bash history.
37
+ RUN SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \
38
+ && mkdir /commandhistory \
39
+ && touch /commandhistory/.bash_history \
40
+ && chown -R $USERNAME /commandhistory
41
+
42
+ # Set `DEVCONTAINER` environment variable to help with orientation
43
+ ENV DEVCONTAINER=true
44
+
45
+ # Create workspace and config directories and set permissions
46
+ RUN mkdir -p /workspace /home/node/.claude && \
47
+ chown -R node:node /workspace /home/node/.claude
48
+
49
+ WORKDIR /workspace
50
+
51
+ ARG GIT_DELTA_VERSION=0.18.2
52
+ RUN ARCH=$(dpkg --print-architecture) && \
53
+ wget "https://github.com/dandavison/delta/releases/download/${GIT_DELTA_VERSION}/git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" && \
54
+ sudo dpkg -i "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" && \
55
+ rm "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb"
56
+
57
+ # Set up non-root user
58
+ USER node
59
+
60
+ # Install global packages
61
+ ENV NPM_CONFIG_PREFIX=/usr/local/share/npm-global
62
+ ENV PATH=$PATH:/usr/local/share/npm-global/bin
63
+
64
+ # Set the default shell to zsh rather than sh
65
+ ENV SHELL=/bin/zsh
66
+
67
+ # Set the default editor and visual
68
+ ENV EDITOR nano
69
+ ENV VISUAL nano
70
+
71
+ # Default powerline10k theme
72
+ ARG ZSH_IN_DOCKER_VERSION=1.2.0
73
+ RUN sh -c "$(wget -O- https://github.com/deluan/zsh-in-docker/releases/download/v${ZSH_IN_DOCKER_VERSION}/zsh-in-docker.sh)" -- \
74
+ -p git \
75
+ -p fzf \
76
+ -a "source /usr/share/doc/fzf/examples/key-bindings.zsh" \
77
+ -a "source /usr/share/doc/fzf/examples/completion.zsh" \
78
+ -a "export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \
79
+ -x
80
+
81
+ # Install Claude
82
+ RUN npm install -g @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}
83
+
84
+
85
+ # Copy and set up firewall script
86
+ COPY init-firewall.sh /usr/local/bin/
87
+ USER root
88
+ RUN chmod +x /usr/local/bin/init-firewall.sh && \
89
+ echo "node ALL=(root) NOPASSWD: /usr/local/bin/init-firewall.sh" > /etc/sudoers.d/node-firewall && \
90
+ chmod 0440 /etc/sudoers.d/node-firewall
91
+ USER node
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "Claude Code Sandbox",
3
+ "build": {
4
+ "dockerfile": "Dockerfile",
5
+ "args": {
6
+ "TZ": "${localEnv:TZ:America/Los_Angeles}",
7
+ "CLAUDE_CODE_VERSION": "latest",
8
+ "GIT_DELTA_VERSION": "0.18.2",
9
+ "ZSH_IN_DOCKER_VERSION": "1.2.0"
10
+ }
11
+ },
12
+ "runArgs": [
13
+ "--cap-add=NET_ADMIN",
14
+ "--cap-add=NET_RAW"
15
+ ],
16
+ "customizations": {
17
+ "vscode": {
18
+ "extensions": [
19
+ "dbaeumer.vscode-eslint",
20
+ "esbenp.prettier-vscode",
21
+ "eamodio.gitlens",
22
+ "anthropic.claude-code"
23
+ ],
24
+ "settings": {
25
+ "editor.formatOnSave": true,
26
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
27
+ "editor.codeActionsOnSave": {
28
+ "source.fixAll.eslint": "explicit"
29
+ },
30
+ "terminal.integrated.defaultProfile.linux": "zsh",
31
+ "terminal.integrated.profiles.linux": {
32
+ "bash": {
33
+ "path": "bash",
34
+ "icon": "terminal-bash"
35
+ },
36
+ "zsh": {
37
+ "path": "zsh"
38
+ }
39
+ }
40
+ }
41
+ }
42
+ },
43
+ "remoteUser": "node",
44
+ "mounts": [
45
+ "source=claude-code-bashhistory-${devcontainerId},target=/commandhistory,type=volume",
46
+ "source=claude-code-config-${devcontainerId},target=/home/node/.claude,type=volume"
47
+ ],
48
+ "containerEnv": {
49
+ "NODE_OPTIONS": "--max-old-space-size=4096",
50
+ "CLAUDE_CONFIG_DIR": "/home/node/.claude",
51
+ "POWERLEVEL9K_DISABLE_GITSTATUS": "true"
52
+ },
53
+ "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated",
54
+ "workspaceFolder": "/workspace",
55
+ "postCreateCommand": "sudo /usr/local/bin/init-firewall.sh"
56
+ }
@@ -0,0 +1,131 @@
1
+ #!/bin/bash
2
+ set -euo pipefail # Exit on error, undefined vars, and pipeline failures
3
+ IFS=$'\n\t' # Stricter word splitting
4
+
5
+ # 1. Extract Docker DNS info BEFORE any flushing
6
+ DOCKER_DNS_RULES=$(iptables-save -t nat | grep "127\.0\.0\.11" || true)
7
+
8
+ # Flush existing rules and delete existing ipsets
9
+ iptables -F
10
+ iptables -X
11
+ iptables -t nat -F
12
+ iptables -t nat -X
13
+ iptables -t mangle -F
14
+ iptables -t mangle -X
15
+ ipset destroy allowed-domains 2>/dev/null || true
16
+
17
+ # 2. Selectively restore ONLY internal Docker DNS resolution
18
+ if [ -n "$DOCKER_DNS_RULES" ]; then
19
+ echo "Restoring Docker DNS rules..."
20
+ iptables -t nat -N DOCKER_OUTPUT 2>/dev/null || true
21
+ iptables -t nat -N DOCKER_POSTROUTING 2>/dev/null || true
22
+ echo "$DOCKER_DNS_RULES" | xargs -L 1 iptables -t nat
23
+ else
24
+ echo "No Docker DNS rules to restore"
25
+ fi
26
+
27
+ # First allow DNS and localhost before any restrictions
28
+ # Allow outbound DNS
29
+ iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
30
+ # Allow inbound DNS responses
31
+ iptables -A INPUT -p udp --sport 53 -j ACCEPT
32
+ # Allow outbound SSH
33
+ iptables -A OUTPUT -p tcp --dport 22 -j ACCEPT
34
+ # Allow inbound SSH responses
35
+ iptables -A INPUT -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
36
+ # Allow localhost
37
+ iptables -A INPUT -i lo -j ACCEPT
38
+ iptables -A OUTPUT -o lo -j ACCEPT
39
+
40
+ # Create ipset with CIDR support
41
+ ipset create allowed-domains hash:net
42
+
43
+ # Fetch GitHub meta information and aggregate + add their IP ranges
44
+ echo "Fetching GitHub IP ranges..."
45
+ gh_ranges=$(curl -s https://api.github.com/meta)
46
+ if [ -z "$gh_ranges" ]; then
47
+ echo "ERROR: Failed to fetch GitHub IP ranges"
48
+ exit 1
49
+ fi
50
+
51
+ if ! echo "$gh_ranges" | jq -e '.web and .api and .git' >/dev/null; then
52
+ echo "ERROR: GitHub API response missing required fields"
53
+ exit 1
54
+ fi
55
+
56
+ echo "Processing GitHub IPs..."
57
+ while read -r cidr; do
58
+ if [[ ! "$cidr" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[0-9]{1,2}$ ]]; then
59
+ echo "ERROR: Invalid CIDR range from GitHub meta: $cidr"
60
+ exit 1
61
+ fi
62
+ echo "Adding GitHub range $cidr"
63
+ ipset add allowed-domains "$cidr"
64
+ done < <(echo "$gh_ranges" | jq -r '(.web + .api + .git)[]' | aggregate -q)
65
+
66
+ # Resolve and add other allowed domains
67
+ for domain in \
68
+ "registry.npmjs.org" \
69
+ "api.anthropic.com" \
70
+ "sentry.io" \
71
+ "statsig.anthropic.com" \
72
+ "statsig.com"; do
73
+ echo "Resolving $domain..."
74
+ ips=$(dig +noall +answer A "$domain" | awk '$4 == "A" {print $5}')
75
+ if [ -z "$ips" ]; then
76
+ echo "ERROR: Failed to resolve $domain"
77
+ exit 1
78
+ fi
79
+
80
+ while read -r ip; do
81
+ if [[ ! "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
82
+ echo "ERROR: Invalid IP from DNS for $domain: $ip"
83
+ exit 1
84
+ fi
85
+ echo "Adding $ip for $domain"
86
+ ipset add allowed-domains "$ip"
87
+ done < <(echo "$ips")
88
+ done
89
+
90
+ # Get host IP from default route
91
+ HOST_IP=$(ip route | grep default | cut -d" " -f3)
92
+ if [ -z "$HOST_IP" ]; then
93
+ echo "ERROR: Failed to detect host IP"
94
+ exit 1
95
+ fi
96
+
97
+ HOST_NETWORK=$(echo "$HOST_IP" | sed "s/\.[0-9]*$/.0\/24/")
98
+ echo "Host network detected as: $HOST_NETWORK"
99
+
100
+ # Set up remaining iptables rules
101
+ iptables -A INPUT -s "$HOST_NETWORK" -j ACCEPT
102
+ iptables -A OUTPUT -d "$HOST_NETWORK" -j ACCEPT
103
+
104
+ # Set default policies to DROP first
105
+ iptables -P INPUT DROP
106
+ iptables -P FORWARD DROP
107
+ iptables -P OUTPUT DROP
108
+
109
+ # First allow established connections for already approved traffic
110
+ iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
111
+ iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
112
+
113
+ # Then allow only specific outbound traffic to allowed domains
114
+ iptables -A OUTPUT -m set --match-set allowed-domains dst -j ACCEPT
115
+
116
+ echo "Firewall configuration complete"
117
+ echo "Verifying firewall rules..."
118
+ if curl --connect-timeout 5 https://example.com >/dev/null 2>&1; then
119
+ echo "ERROR: Firewall verification failed - was able to reach https://example.com"
120
+ exit 1
121
+ else
122
+ echo "Firewall verification passed - unable to reach https://example.com as expected"
123
+ fi
124
+
125
+ # Verify GitHub API access
126
+ if ! curl --connect-timeout 5 https://api.github.com/zen >/dev/null 2>&1; then
127
+ echo "ERROR: Firewall verification failed - unable to reach https://api.github.com"
128
+ exit 1
129
+ else
130
+ echo "Firewall verification passed - able to reach https://api.github.com as expected"
131
+ fi
package/CLAUDE.md ADDED
@@ -0,0 +1,4 @@
1
+ + During you interaction with the user, if you find anything reusable in this project (e.g. version of a library, model name), especially about a fix to a mistake you made or a correction you received, you should take note in the `Lessons` section in the `CLAUDE.md` file so you will not make the same mistake again.
2
+ + You should also use the `CLAUDE.md` file as a scratchpad to organize your thoughts. Especially when you receive a new task, you should first review the content of the scratchpad, clear old different task but keep lessons learned, then explain the task, and plan the steps you need to take to complete the task. You can use todo markers to indicate the progress, e.g.
3
+ [X] Task 1
4
+ [ ] Task 2
package/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # claude-init
2
+
3
+ A CLI tool to initialize Claude development environment with standardized configurations and templates.
4
+
5
+ ## Features
6
+
7
+ - **Smart Setup**: Automatically detects existing files and only creates/updates what's needed
8
+ - **Non-destructive**: Never overwrites existing files, only adds missing content
9
+ - **Progress Feedback**: Clear visual indicators of what's being created, updated, or skipped
10
+ - **Cross-platform**: Works on Windows, macOS, and Linux
11
+
12
+ ## Usage
13
+
14
+ ### Quick Start
15
+
16
+ ```bash
17
+ npx claude-init
18
+ ```
19
+
20
+ This will set up your current directory with:
21
+
22
+ - `CLAUDE.md` - Project instructions and scratchpad for Claude
23
+ - `.devcontainer/` - Development container configuration
24
+ - `.claude/settings.json` - Claude-specific settings
25
+ - `.claude/commands/` - Custom Claude commands
26
+ - `.claude/agents/` - Specialized agent configurations
27
+
28
+ ### What It Does
29
+
30
+ #### 📄 CLAUDE.md
31
+ - **If missing**: Creates new file with template content
32
+ - **If exists**: Appends template content under "# Claude Scratchpad Rules" heading
33
+ - **If already contains template**: Skips to preserve your content
34
+
35
+ #### 📁 .devcontainer
36
+ - **If missing**: Creates complete directory with Docker configuration
37
+ - **If exists**: Skips entirely to preserve your setup
38
+
39
+ #### ⚙️ .claude/settings.json
40
+ - **If missing**: Creates with default Claude settings
41
+ - **If exists**: Skips to preserve your custom settings
42
+
43
+ #### 📋 .claude/commands & .claude/agents
44
+ - **If missing**: Creates with all template files
45
+ - **If exists**: Only adds missing files, preserves existing ones
46
+
47
+ ## Example Output
48
+
49
+ ```
50
+ 🚀 Claude Environment Initializer
51
+ Initializing in: /path/to/your/project
52
+
53
+ ✓ Created new CLAUDE.md
54
+ ✓ Created .devcontainer directory
55
+ ✓ Created .claude/settings.json
56
+ ↻ Added 2 missing files to .claude/commands (2 files)
57
+ - All files in .claude/agents are up to date
58
+
59
+ 📊 Summary:
60
+ Created: 3 items
61
+ Updated: 1 items
62
+ Skipped: 1 items (already exist)
63
+ Files added: 2
64
+
65
+ ✨ Claude environment is ready!
66
+ ```
67
+
68
+ ## File Structure Created
69
+
70
+ ```
71
+ your-project/
72
+ ├── CLAUDE.md # Project instructions
73
+ ├── .devcontainer/
74
+ │ ├── devcontainer.json # VS Code dev container config
75
+ │ ├── Dockerfile # Container setup
76
+ │ └── init-firewall.sh # Initialization script
77
+ └── .claude/
78
+ ├── settings.json # Claude settings
79
+ ├── commands/ # Custom commands
80
+ │ ├── debug.md
81
+ │ ├── gogogo.md
82
+ │ ├── plan.md
83
+ │ └── prompt-enhancement.md
84
+ └── agents/ # Specialized agents
85
+ └── library-usage-researcher.md
86
+ ```
87
+
88
+ ## Development
89
+
90
+ ### Running Tests
91
+
92
+ ```bash
93
+ npm test
94
+ ```
95
+
96
+ ### Local Development
97
+
98
+ ```bash
99
+ npm run dev
100
+ ```
101
+
102
+ ## License
103
+
104
+ MIT
package/bin/index.js ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { cli } from '../src/cli.js';
4
+
5
+ // Entry point for npx claude-init
6
+ cli().catch((error) => {
7
+ console.error('❌ Error:', error.message);
8
+ process.exit(1);
9
+ });
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "claude-init",
3
+ "version": "1.0.0",
4
+ "description": "Initialize Claude development environment with configurations and templates",
5
+ "type": "module",
6
+ "bin": {
7
+ "claude-init": "./bin/index.js"
8
+ },
9
+ "main": "./src/cli.js",
10
+ "files": [
11
+ "bin/",
12
+ "src/",
13
+ "CLAUDE.md",
14
+ ".claude/",
15
+ ".devcontainer/"
16
+ ],
17
+ "scripts": {
18
+ "test": "node --test test/*.test.js",
19
+ "dev": "node bin/index.js",
20
+ "prepublishOnly": "npm test"
21
+ },
22
+ "keywords": [
23
+ "claude",
24
+ "ai",
25
+ "development",
26
+ "environment",
27
+ "cli",
28
+ "initialization",
29
+ "scaffolding"
30
+ ],
31
+ "author": "ChrisLinn <nanyishanshaoxiu@gmail.com>",
32
+ "license": "MIT",
33
+ "engines": {
34
+ "node": ">=16.0.0"
35
+ },
36
+ "dependencies": {
37
+ "chalk": "^5.3.0",
38
+ "fs-extra": "^11.2.0",
39
+ "ora": "^7.0.1"
40
+ },
41
+ "devDependencies": {
42
+ "tmp": "^0.2.1"
43
+ },
44
+ "repository": {
45
+ "type": "git",
46
+ "url": ""
47
+ },
48
+ "bugs": {
49
+ "url": ""
50
+ },
51
+ "homepage": ""
52
+ }
package/src/cli.js ADDED
@@ -0,0 +1,116 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import {
4
+ handleClaudeMarkdown,
5
+ handleDirectoryMirror,
6
+ handleSelectiveFileCopy,
7
+ handleSingleFile
8
+ } from './fileManager.js';
9
+
10
+ /**
11
+ * Format action result with appropriate colors and icons
12
+ */
13
+ function formatResult(result) {
14
+ const { action, details, filesAdded } = result;
15
+
16
+ switch (action) {
17
+ case 'created':
18
+ return chalk.green(`✓ ${details}`);
19
+ case 'updated':
20
+ const fileInfo = filesAdded !== undefined ? ` (${filesAdded} files)` : '';
21
+ return chalk.yellow(`↻ ${details}${fileInfo}`);
22
+ case 'skipped':
23
+ return chalk.gray(`- ${details}`);
24
+ default:
25
+ return details;
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Main CLI function
31
+ */
32
+ export async function cli() {
33
+ const targetDir = process.cwd();
34
+
35
+ console.log(chalk.blue.bold('🚀 Claude Environment Initializer'));
36
+ console.log(chalk.gray(`Initializing in: ${targetDir}`));
37
+ console.log();
38
+
39
+ const tasks = [
40
+ {
41
+ name: 'CLAUDE.md',
42
+ handler: () => handleClaudeMarkdown(targetDir)
43
+ },
44
+ {
45
+ name: '.devcontainer',
46
+ handler: () => handleDirectoryMirror(targetDir, '.devcontainer')
47
+ },
48
+ {
49
+ name: '.claude/settings.json',
50
+ handler: () => handleSingleFile(targetDir, '.claude/settings.json')
51
+ },
52
+ {
53
+ name: '.claude/commands',
54
+ handler: () => handleSelectiveFileCopy(targetDir, '.claude/commands')
55
+ },
56
+ {
57
+ name: '.claude/agents',
58
+ handler: () => handleSelectiveFileCopy(targetDir, '.claude/agents')
59
+ }
60
+ ];
61
+
62
+ let totalActions = { created: 0, updated: 0, skipped: 0, filesAdded: 0 };
63
+
64
+ for (const task of tasks) {
65
+ const spinner = ora(`Processing ${task.name}...`).start();
66
+
67
+ try {
68
+ const result = await task.handler();
69
+ spinner.succeed(formatResult(result));
70
+
71
+ // Track statistics
72
+ totalActions[result.action]++;
73
+ if (result.filesAdded) {
74
+ totalActions.filesAdded += result.filesAdded;
75
+ }
76
+
77
+ } catch (error) {
78
+ spinner.fail(`Failed to process ${task.name}: ${error.message}`);
79
+ throw error;
80
+ }
81
+ }
82
+
83
+ // Summary
84
+ console.log();
85
+ console.log(chalk.blue.bold('📊 Summary:'));
86
+
87
+ if (totalActions.created > 0) {
88
+ console.log(chalk.green(` Created: ${totalActions.created} items`));
89
+ }
90
+
91
+ if (totalActions.updated > 0) {
92
+ console.log(chalk.yellow(` Updated: ${totalActions.updated} items`));
93
+ }
94
+
95
+ if (totalActions.skipped > 0) {
96
+ console.log(chalk.gray(` Skipped: ${totalActions.skipped} items (already exist)`));
97
+ }
98
+
99
+ if (totalActions.filesAdded > 0) {
100
+ console.log(chalk.cyan(` Files added: ${totalActions.filesAdded}`));
101
+ }
102
+
103
+ console.log();
104
+
105
+ if (totalActions.created > 0 || totalActions.updated > 0) {
106
+ console.log(chalk.green.bold('✨ Claude environment is ready!'));
107
+ } else {
108
+ console.log(chalk.blue.bold('✅ Claude environment is already up to date!'));
109
+ }
110
+
111
+ console.log();
112
+ console.log(chalk.gray('Next steps:'));
113
+ console.log(chalk.gray('• Review CLAUDE.md for project-specific instructions'));
114
+ console.log(chalk.gray('• Customize .claude/settings.json if needed'));
115
+ console.log(chalk.gray('• Add custom commands to .claude/commands/'));
116
+ }
@@ -0,0 +1,124 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import {
4
+ getTemplatesPath,
5
+ createContentHash,
6
+ contentContains,
7
+ readTemplate,
8
+ getFilesInDirectory
9
+ } from './utils.js';
10
+
11
+ /**
12
+ * Handle CLAUDE.md file creation or updating
13
+ */
14
+ export async function handleClaudeMarkdown(targetDir) {
15
+ const claudeFile = path.join(targetDir, 'CLAUDE.md');
16
+ const templateContent = await readTemplate('CLAUDE.md');
17
+ const templateHash = createContentHash(templateContent);
18
+
19
+ if (!(await fs.pathExists(claudeFile))) {
20
+ // File doesn't exist, create it
21
+ await fs.writeFile(claudeFile, templateContent);
22
+ return { action: 'created', details: 'Created new CLAUDE.md' };
23
+ }
24
+
25
+ // File exists, check if it contains our template content
26
+ const existingContent = await fs.readFile(claudeFile, 'utf8');
27
+ const existingHash = createContentHash(existingContent);
28
+
29
+ // Check if template content is already present
30
+ if (existingHash === templateHash || contentContains(existingContent, templateContent.trim())) {
31
+ return { action: 'skipped', details: 'CLAUDE.md already contains template content' };
32
+ }
33
+
34
+ // Append template content under a heading
35
+ const heading = '\\n\\n# Claude Scratchpad Rules\\n\\n';
36
+ const updatedContent = existingContent + heading + templateContent;
37
+ await fs.writeFile(claudeFile, updatedContent);
38
+
39
+ return { action: 'updated', details: 'Appended template content to existing CLAUDE.md' };
40
+ }
41
+
42
+ /**
43
+ * Handle directory mirroring (create if not exists, skip if exists)
44
+ */
45
+ export async function handleDirectoryMirror(targetDir, relativePath) {
46
+ const targetPath = path.join(targetDir, relativePath);
47
+ const templatePath = path.join(getTemplatesPath(), relativePath);
48
+
49
+ if (await fs.pathExists(targetPath)) {
50
+ return { action: 'skipped', details: `Directory ${relativePath} already exists` };
51
+ }
52
+
53
+ // Copy entire directory
54
+ await fs.copy(templatePath, targetPath);
55
+ return { action: 'created', details: `Created ${relativePath} directory` };
56
+ }
57
+
58
+ /**
59
+ * Handle selective file copying (add missing files only)
60
+ */
61
+ export async function handleSelectiveFileCopy(targetDir, relativePath) {
62
+ const targetPath = path.join(targetDir, relativePath);
63
+ const templatePath = path.join(getTemplatesPath(), relativePath);
64
+
65
+ if (!(await fs.pathExists(targetPath))) {
66
+ // Directory doesn't exist, create it with all template files
67
+ await fs.copy(templatePath, targetPath);
68
+ const files = await getFilesInDirectory(templatePath);
69
+ return {
70
+ action: 'created',
71
+ details: `Created ${relativePath} with ${files.length} files`,
72
+ filesAdded: files.length
73
+ };
74
+ }
75
+
76
+ // Directory exists, check for missing files
77
+ const templateFiles = await getFilesInDirectory(templatePath);
78
+ const targetFiles = await getFilesInDirectory(targetPath);
79
+ const missingFiles = templateFiles.filter(file => !targetFiles.includes(file));
80
+
81
+ if (missingFiles.length === 0) {
82
+ return {
83
+ action: 'skipped',
84
+ details: `All files in ${relativePath} are up to date`,
85
+ filesAdded: 0
86
+ };
87
+ }
88
+
89
+ // Copy missing files
90
+ let copiedCount = 0;
91
+ for (const missingFile of missingFiles) {
92
+ const sourcePath = path.join(templatePath, missingFile);
93
+ const destPath = path.join(targetPath, missingFile);
94
+
95
+ // Ensure destination directory exists
96
+ await fs.ensureDir(path.dirname(destPath));
97
+ await fs.copy(sourcePath, destPath);
98
+ copiedCount++;
99
+ }
100
+
101
+ return {
102
+ action: 'updated',
103
+ details: `Added ${copiedCount} missing files to ${relativePath}`,
104
+ filesAdded: copiedCount
105
+ };
106
+ }
107
+
108
+ /**
109
+ * Handle single file copying (create if not exists, skip if exists)
110
+ */
111
+ export async function handleSingleFile(targetDir, relativePath) {
112
+ const targetPath = path.join(targetDir, relativePath);
113
+ const templatePath = path.join(getTemplatesPath(), relativePath);
114
+
115
+ if (await fs.pathExists(targetPath)) {
116
+ return { action: 'skipped', details: `File ${relativePath} already exists` };
117
+ }
118
+
119
+ // Ensure target directory exists
120
+ await fs.ensureDir(path.dirname(targetPath));
121
+ await fs.copy(templatePath, targetPath);
122
+
123
+ return { action: 'created', details: `Created ${relativePath}` };
124
+ }
package/src/utils.js ADDED
@@ -0,0 +1,65 @@
1
+ import { createHash } from 'crypto';
2
+ import fs from 'fs-extra';
3
+ import path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+
9
+ /**
10
+ * Get the absolute path to the templates directory
11
+ */
12
+ export function getTemplatesPath() {
13
+ // return path.join(__dirname, '..', 'templates');
14
+ return path.join(__dirname, '..');
15
+ }
16
+
17
+ /**
18
+ * Create a hash of file content for comparison
19
+ */
20
+ export function createContentHash(content) {
21
+ return createHash('md5').update(content.trim()).digest('hex');
22
+ }
23
+
24
+ /**
25
+ * Check if content contains a specific text (case-insensitive)
26
+ */
27
+ export function contentContains(content, searchText) {
28
+ return content.toLowerCase().includes(searchText.toLowerCase());
29
+ }
30
+
31
+ /**
32
+ * Read a template file
33
+ */
34
+ export async function readTemplate(relativePath) {
35
+ const templatePath = path.join(getTemplatesPath(), relativePath);
36
+ return await fs.readFile(templatePath, 'utf8');
37
+ }
38
+
39
+ /**
40
+ * Get all files in a directory recursively
41
+ */
42
+ export async function getFilesInDirectory(dirPath) {
43
+ const files = [];
44
+
45
+ try {
46
+ const entries = await fs.readdir(dirPath);
47
+
48
+ for (const entry of entries) {
49
+ const fullPath = path.join(dirPath, entry);
50
+ const stats = await fs.stat(fullPath);
51
+
52
+ if (stats.isFile()) {
53
+ files.push(entry);
54
+ } else if (stats.isDirectory()) {
55
+ const subFiles = await getFilesInDirectory(fullPath);
56
+ files.push(...subFiles.map(f => path.join(entry, f)));
57
+ }
58
+ }
59
+ } catch (error) {
60
+ // Directory doesn't exist
61
+ return [];
62
+ }
63
+
64
+ return files;
65
+ }