@khanhcan148/mk 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.
- package/README.md +266 -0
- package/bin/mk.js +53 -0
- package/package.json +38 -0
- package/src/commands/auth.js +143 -0
- package/src/commands/init.js +144 -0
- package/src/commands/remove.js +146 -0
- package/src/commands/update.js +221 -0
- package/src/lib/auth.js +211 -0
- package/src/lib/checksum.js +13 -0
- package/src/lib/config.js +72 -0
- package/src/lib/constants.js +37 -0
- package/src/lib/copy.js +130 -0
- package/src/lib/download.js +262 -0
- package/src/lib/manifest.js +115 -0
- package/src/lib/paths.js +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# Claude Code Skills, Agents & Workflows
|
|
2
|
+
|
|
3
|
+
[](https://github.com/khanhtran148/mk-kit/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@khanhcan148/mk)
|
|
5
|
+
|
|
6
|
+
**Built by** TRAN Le Khanh — developer and AI tooling author.
|
|
7
|
+
|
|
8
|
+
Modular packages that extend Claude Code with specialized knowledge, workflows, and tool integrations.
|
|
9
|
+
|
|
10
|
+
**Quick Navigation:** [Quick Reference](docs/QUICK-REFERENCE.md) | [Common Workflows](docs/COMMON-WORKFLOWS.md) | [Skill Index](docs/SKILL-INDEX.md)
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
### CLI Installation (Recommended)
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Install globally
|
|
18
|
+
npm install -g @khanhcan148/mk
|
|
19
|
+
|
|
20
|
+
# Install kit to current project (./.claude/)
|
|
21
|
+
mk init
|
|
22
|
+
|
|
23
|
+
# Install kit globally (~/.claude/ — available in all projects)
|
|
24
|
+
mk init --global
|
|
25
|
+
|
|
26
|
+
# Preview what would be installed without writing files
|
|
27
|
+
mk init --dry-run
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### CLI Commands
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
mk init Install kit files to current project (./.claude/)
|
|
34
|
+
mk init --global Install kit globally (~/.claude/)
|
|
35
|
+
mk init --dry-run Preview what would be installed
|
|
36
|
+
mk update Update kit files (preserves your edits by default)
|
|
37
|
+
mk update --force Update and overwrite user-modified files
|
|
38
|
+
mk remove Remove all kit files tracked by manifest
|
|
39
|
+
mk --version Show installed CLI version
|
|
40
|
+
mk --help Show help
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Manual Installation (Alternative)
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# Clone the repository
|
|
47
|
+
git clone https://github.com/khanhtran148/mk-kit.git
|
|
48
|
+
cd mk-kit
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Project-level usage** (skills available in one project):
|
|
52
|
+
```bash
|
|
53
|
+
cp -r .claude /path/to/your/project/
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Global-level usage** (skills available across all projects):
|
|
57
|
+
```bash
|
|
58
|
+
cp -r .claude ~/.claude/
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Usage
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Use a workflow command
|
|
65
|
+
/mk-init # Bootstrap new project
|
|
66
|
+
/mk-brainstorm # Explore options, debate trade-offs
|
|
67
|
+
/mk-plan # Create implementation plan
|
|
68
|
+
/mk-implement # End-to-end feature delivery
|
|
69
|
+
/mk-test # Run tests and validate
|
|
70
|
+
/mk-review # Code quality review
|
|
71
|
+
/mk-debug # Debug issues
|
|
72
|
+
/mk-security # Security scan and audit
|
|
73
|
+
/mk-db # Database operations
|
|
74
|
+
/mk-docs # Generate documentation
|
|
75
|
+
/mk-git # Git operations
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Structure
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
├── .claude/
|
|
82
|
+
│ ├── agents/ # 29 agents (5 primary + 24 utility: implementers, quality, docs, specialized, concerns)
|
|
83
|
+
│ ├── skills/ # 53 skill packages (SKILL.md + scripts/references/assets)
|
|
84
|
+
│ │ ├── mk-*/ # 14 workflow commands (/mk-brainstorm, /mk-selftest, etc.)
|
|
85
|
+
│ │ └── ... # Domain skills (frontend, backend, testing, etc.)
|
|
86
|
+
│ └── workflows/ # Development protocols
|
|
87
|
+
├── bin/ # CLI entry point (mk command)
|
|
88
|
+
├── src/ # CLI source code
|
|
89
|
+
├── docs/ # Project documentation
|
|
90
|
+
│ └── plans/ # Implementation plans (created during development)
|
|
91
|
+
├── package.json # npm package metadata
|
|
92
|
+
├── CLAUDE.md # Claude Code guidance
|
|
93
|
+
└── README.md # This file
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Primary Workflow
|
|
97
|
+
|
|
98
|
+
Orchestration is handled by `/mk-*` skills which spawn utility agents as needed. See [Common Workflows](docs/COMMON-WORKFLOWS.md) for practical examples.
|
|
99
|
+
|
|
100
|
+
**Architecture:**
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
User → /mk-* command (skill) → spawns utility agents → agents use knowledge skills
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Sequential chaining:**
|
|
107
|
+
```
|
|
108
|
+
/mk-plan → /mk-implement → /mk-test → /mk-review
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Entry point commands:**
|
|
112
|
+
|
|
113
|
+
| Command | Purpose |
|
|
114
|
+
|---------|---------|
|
|
115
|
+
| `/mk-init` | Full project bootstrap from conception to deployment; includes brownfield detection (Phase 0 gate) and adoption workflow (B1-B4.5 stages) |
|
|
116
|
+
| `/mk-brainstorm` | Ideation, requirements exploration, structured debate (analyzer opus agent); use `--domain` flag to extract and document domain knowledge |
|
|
117
|
+
| `/mk-research` | Deep multi-source research on technical topics |
|
|
118
|
+
| `/mk-plan` | Create implementation plans with phases and tasks (opus) |
|
|
119
|
+
| `/mk-design` | UI/UX wireframes, design systems, mockups |
|
|
120
|
+
| `/mk-implement` | End-to-end feature delivery (sonnet) |
|
|
121
|
+
| `/mk-test` | Run tests, analyze coverage, validate builds |
|
|
122
|
+
| `/mk-review` | Code review for quality, security, performance |
|
|
123
|
+
| `/mk-debug` | Debugging workflow: investigate, diagnose, fix, verify |
|
|
124
|
+
| `/mk-security` | Security-first workflow: dependency scan, secrets detection |
|
|
125
|
+
| `/mk-db` | Database operations: diagnose, optimize, design, migrate |
|
|
126
|
+
| `/mk-docs` | Generate and update project documentation |
|
|
127
|
+
| `/mk-git` | Git operations: branch, commit, push, PR, merge |
|
|
128
|
+
| `/mk-selftest` | Run self-validation checks on kit agents, skills, docs, and workflows |
|
|
129
|
+
|
|
130
|
+
## Primary Agents
|
|
131
|
+
|
|
132
|
+
Invoked directly via `/mk-*` skills:
|
|
133
|
+
|
|
134
|
+
| Agent | Purpose | Model |
|
|
135
|
+
|-------|---------|-------|
|
|
136
|
+
| **analyzer** | Deep research, decision matrix, architecture diagrams, adversarial debate (Phases 5-8 of /mk-brainstorm) | opus |
|
|
137
|
+
| **planner** | Implementation planning with phases, tasks, dependencies | opus |
|
|
138
|
+
| **implementer** | End-to-end feature implementation | sonnet |
|
|
139
|
+
| **database-admin** | Database operations: diagnose, optimize, design, migrate | sonnet |
|
|
140
|
+
| **git-manager** | Git operations: commit, push, PR, merge | haiku |
|
|
141
|
+
|
|
142
|
+
## Utility Agents
|
|
143
|
+
|
|
144
|
+
Spawned by primary agents for domain-specific work:
|
|
145
|
+
|
|
146
|
+
**Implementation & Quality (8)**
|
|
147
|
+
| Agent | Purpose |
|
|
148
|
+
|-------|---------|
|
|
149
|
+
| **backend-implementer** | API and domain logic implementation |
|
|
150
|
+
| **frontend-implementer** | UI components, consuming API contract |
|
|
151
|
+
| **tester** | Test execution and validation |
|
|
152
|
+
| **debugger** | Issue investigation and diagnosis |
|
|
153
|
+
| **refactorer** | Refactoring with TFD-first safety workflow |
|
|
154
|
+
|
|
155
|
+
**Docs & Project Management (3)**
|
|
156
|
+
| Agent | Purpose |
|
|
157
|
+
|-------|---------|
|
|
158
|
+
| **project-manager** | Progress tracking, roadmap updates |
|
|
159
|
+
| **docs-manager** | Documentation maintenance |
|
|
160
|
+
| **journal-writer** | Technical difficulty documentation |
|
|
161
|
+
|
|
162
|
+
**Research & Design (3)**
|
|
163
|
+
| Agent | Purpose |
|
|
164
|
+
|-------|---------|
|
|
165
|
+
| **researcher** | Technical research |
|
|
166
|
+
| **ui-ux-designer** | Frontend design |
|
|
167
|
+
| **copywriter** | Marketing copy and content |
|
|
168
|
+
|
|
169
|
+
**Utilities & Search (2)**
|
|
170
|
+
| Agent | Purpose |
|
|
171
|
+
|-------|---------|
|
|
172
|
+
| **scout** | Codebase file search (internal) |
|
|
173
|
+
| **scout-external** | Codebase search via external tools |
|
|
174
|
+
| **mcp-manager** | MCP server integrations |
|
|
175
|
+
|
|
176
|
+
**Parallel Concern Review Agents (10)**
|
|
177
|
+
| Agent | Purpose |
|
|
178
|
+
|-------|---------|
|
|
179
|
+
| **quality-reviewer** | Code quality, readability, maintainability (mk-review concern) |
|
|
180
|
+
| **security-reviewer** | Security vulnerabilities, threat analysis (mk-review concern) |
|
|
181
|
+
| **performance-reviewer** | Performance profiling, optimization opportunities (mk-review concern) |
|
|
182
|
+
| **tfd-reviewer** | Test-first development verification (mk-review concern) |
|
|
183
|
+
| **dep-scanner** | Dependency vulnerability scanning (mk-security concern) |
|
|
184
|
+
| **secret-scanner** | Secrets and credential detection (mk-security concern) |
|
|
185
|
+
| **code-security-reviewer** | Code-level security analysis (mk-security concern) |
|
|
186
|
+
| **infra-reviewer** | Infrastructure security review (mk-security concern) |
|
|
187
|
+
| **log-analyzer** | Log analysis and diagnostics (mk-debug concern) |
|
|
188
|
+
| **hypothesis-generator** | Root cause hypothesis generation (mk-debug concern) |
|
|
189
|
+
|
|
190
|
+
[Full agent details →](.claude/agents/README.md)
|
|
191
|
+
|
|
192
|
+
## Skills
|
|
193
|
+
|
|
194
|
+
See [Skill Index](docs/SKILL-INDEX.md) for a complete categorized list of all skills.
|
|
195
|
+
|
|
196
|
+
Each skill contains:
|
|
197
|
+
- `SKILL.md` (required) - Instructions (<100 lines)
|
|
198
|
+
- `scripts/` (optional) - Executable code with tests
|
|
199
|
+
- `references/` (optional) - Documentation chunks
|
|
200
|
+
- `assets/` (optional) - Templates, images
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
# Initialize new skill
|
|
204
|
+
.claude/skills/skill-creator/scripts/init_skill.py <skill-name> --path <output-directory>
|
|
205
|
+
|
|
206
|
+
# Package for distribution
|
|
207
|
+
.claude/skills/skill-creator/scripts/package_skill.py <path/to/skill-folder>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Documentation
|
|
211
|
+
|
|
212
|
+
| Document | Description |
|
|
213
|
+
|----------|-------------|
|
|
214
|
+
| [Skill Index](docs/SKILL-INDEX.md) | Categorized list of all skills and workflows |
|
|
215
|
+
| [mk-* Workflows & Agents](docs/mk-workflow-agents.md) | How each /mk-* command works and which agents it uses |
|
|
216
|
+
| [Project Overview & PDR](docs/project-overview-pdr.md) | Requirements, constraints, success metrics |
|
|
217
|
+
| [Project Roadmap](docs/project-roadmap.md) | Phases, milestones, and risk register |
|
|
218
|
+
| [Changelog](docs/changelog.md) | Version history and release notes |
|
|
219
|
+
| [Code Standards](docs/code-standards.md) | Conventions, naming, quality gates |
|
|
220
|
+
| [System Architecture](docs/system-architecture.md) | Component diagrams, data flow |
|
|
221
|
+
| [Product Domain Glossary](docs/product-domain-glossary.md) | Core domain concepts, terminology, and ecosystem definitions |
|
|
222
|
+
| [Quick Reference](docs/QUICK-REFERENCE.md) | Common commands and quick lookup |
|
|
223
|
+
| [Common Workflows](docs/COMMON-WORKFLOWS.md) | Practical workflow examples |
|
|
224
|
+
|
|
225
|
+
## Core Principles
|
|
226
|
+
|
|
227
|
+
- **YAGNI** - Don't add functionality until necessary
|
|
228
|
+
- **KISS** - Prefer simple solutions
|
|
229
|
+
- **DRY** - Extract common patterns
|
|
230
|
+
- **SOLID** - For OOP/enterprise projects
|
|
231
|
+
|
|
232
|
+
## Key Constraints
|
|
233
|
+
|
|
234
|
+
- SKILL.md must be under 100 lines
|
|
235
|
+
- Referenced markdown files also under 100 lines
|
|
236
|
+
- Scripts must include tests
|
|
237
|
+
- **Cross-platform compatibility (Windows + macOS/Linux) is mandatory**: Scripts use Python or Node.js only — no bash/shell scripts
|
|
238
|
+
- Token efficiency - minimize context usage
|
|
239
|
+
|
|
240
|
+
## Compatibility
|
|
241
|
+
|
|
242
|
+
This skill kit works in both Claude Code CLI and VSCode GitHub Copilot (1.108+).
|
|
243
|
+
|
|
244
|
+
## Cross-Platform Compatibility
|
|
245
|
+
|
|
246
|
+
This kit is designed to work on Windows, macOS, and Linux:
|
|
247
|
+
|
|
248
|
+
- **Scripts**: All executable scripts use Python or Node.js (no bash/shell scripts)
|
|
249
|
+
- **Claude Code Bash tool**: Provides a Unix-like shell on all platforms, including Windows — git commands and other Bash tool invocations work everywhere
|
|
250
|
+
- **External tool installs**: Reference files include install instructions for macOS (`brew`), Ubuntu/Debian (`apt-get`), and Windows (`winget`/`choco`) where applicable
|
|
251
|
+
- **Agent fallbacks**: Agents using CLI-only tools (TodoWrite, BashOutput, MultiEdit) document VSCode Copilot fallback behavior inline
|
|
252
|
+
|
|
253
|
+
| Feature | Claude Code CLI | VSCode Copilot (1.108+) |
|
|
254
|
+
|---------|----------------|------------------------|
|
|
255
|
+
| Skills (SKILL.md) | Full support | Full support |
|
|
256
|
+
| Agents (.md) | Full support | Full support |
|
|
257
|
+
| CLAUDE.md | Full support | Full support |
|
|
258
|
+
| Model field | Shorthand (opus/sonnet/haiku) | Shorthand (recommended) |
|
|
259
|
+
| Task tool | Native Task() syntax | Natural language delegation* |
|
|
260
|
+
| color | Supported | Ignored (harmless) |
|
|
261
|
+
| MCP tools | Full support | Limited |
|
|
262
|
+
| AskUserQuestion | Native UI | Natural language fallback |
|
|
263
|
+
|
|
264
|
+
*VSCode Copilot users: Use natural language to invoke agents (e.g., "Use the planner agent to create a plan"). The explicit Task() syntax is Claude Code CLI-specific.
|
|
265
|
+
|
|
266
|
+
**CLI-only tools**: TodoWrite, BashOutput, KillShell, MultiEdit are Claude Code-specific. Agents gracefully fall back to standard tools (Write, Bash, sequential Edit) when these are unavailable.
|
package/bin/mk.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { program } from 'commander';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { dirname, join } from 'node:path';
|
|
6
|
+
import { initAction } from '../src/commands/init.js';
|
|
7
|
+
import { updateAction } from '../src/commands/update.js';
|
|
8
|
+
import { removeAction } from '../src/commands/remove.js';
|
|
9
|
+
import { loginAction, logoutAction, statusAction } from '../src/commands/auth.js';
|
|
10
|
+
|
|
11
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
|
|
13
|
+
|
|
14
|
+
program
|
|
15
|
+
.name('mk')
|
|
16
|
+
.description('Install and manage MyClaudeKit (.claude/) in your projects')
|
|
17
|
+
.version(pkg.version);
|
|
18
|
+
|
|
19
|
+
program.command('init')
|
|
20
|
+
.description('Install .claude/ kit files to current project or globally')
|
|
21
|
+
.option('--global', 'Install to ~/.claude/ for all projects')
|
|
22
|
+
.option('--dry-run', 'Print planned changes without writing files')
|
|
23
|
+
.action((options) => initAction(options));
|
|
24
|
+
|
|
25
|
+
program.command('update')
|
|
26
|
+
.description('Update kit files to the latest version, preserving user modifications')
|
|
27
|
+
.option('--force', 'Overwrite user-modified files without warning')
|
|
28
|
+
.option('--global', 'Update global installation in ~/.claude/')
|
|
29
|
+
.action((options) => updateAction(options));
|
|
30
|
+
|
|
31
|
+
program.command('remove')
|
|
32
|
+
.description('Remove all kit files tracked by the manifest')
|
|
33
|
+
.option('--global', 'Remove global installation from ~/.claude/')
|
|
34
|
+
.action((options) => removeAction(options));
|
|
35
|
+
|
|
36
|
+
// Auth command group
|
|
37
|
+
const auth = program.command('auth')
|
|
38
|
+
.description('Manage GitHub authentication for kit downloads');
|
|
39
|
+
|
|
40
|
+
auth.command('login')
|
|
41
|
+
.description('Authenticate with GitHub via OAuth Device Flow')
|
|
42
|
+
.option('--token <token>', 'Use a Personal Access Token instead of device flow')
|
|
43
|
+
.action((options) => loginAction(options));
|
|
44
|
+
|
|
45
|
+
auth.command('logout')
|
|
46
|
+
.description('Remove stored GitHub authentication token')
|
|
47
|
+
.action(() => logoutAction());
|
|
48
|
+
|
|
49
|
+
auth.command('status')
|
|
50
|
+
.description('Show current authentication status')
|
|
51
|
+
.action(() => statusAction());
|
|
52
|
+
|
|
53
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@khanhcan148/mk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI to install and manage MyClaudeKit (.claude/) in your projects",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mk": "./bin/mk.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"src/"
|
|
12
|
+
],
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=18.0.0"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"test": "node --test test/lib/*.test.js test/commands/*.test.js test/integration/*.test.js",
|
|
18
|
+
"lint": "node --check src/**/*.js bin/**/*.js 2>/dev/null || true",
|
|
19
|
+
"selftest": "python3 .claude/skills/mk-selftest/scripts/validate_kit.py"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"commander": "^12.0.0",
|
|
23
|
+
"fs-extra": "^11.0.0",
|
|
24
|
+
"chalk": "^5.0.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {},
|
|
27
|
+
"keywords": ["claude", "claude-code", "skills", "agents", "cli"],
|
|
28
|
+
"author": "khanhcan148",
|
|
29
|
+
"license": "UNLICENSED",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/khanhtran148/mk-kit.git"
|
|
33
|
+
},
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/khanhtran148/mk-kit/issues"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/khanhtran148/mk-kit#readme"
|
|
38
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import {
|
|
3
|
+
resolveToken as _resolveToken,
|
|
4
|
+
tokenSource as _tokenSource,
|
|
5
|
+
validateToken as _validateToken,
|
|
6
|
+
checkRepoAccess as _checkRepoAccess,
|
|
7
|
+
startDeviceFlow as _startDeviceFlow
|
|
8
|
+
} from '../lib/auth.js';
|
|
9
|
+
import { writeToken as _writeToken, deleteToken as _deleteToken, readStoredToken as _readStoredToken } from '../lib/config.js';
|
|
10
|
+
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// loginAction
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Handle 'mk auth login'.
|
|
17
|
+
* Options: { token?: string } — if token provided, store it as PAT
|
|
18
|
+
*
|
|
19
|
+
* @param {{ token?: string }} options
|
|
20
|
+
* @param {object} [deps] - Injected dependencies (for testing)
|
|
21
|
+
*/
|
|
22
|
+
export async function loginAction(options = {}, deps = {}) {
|
|
23
|
+
const {
|
|
24
|
+
resolveToken = _resolveToken,
|
|
25
|
+
validateToken = _validateToken,
|
|
26
|
+
startDeviceFlow = (opts) => _startDeviceFlow(opts),
|
|
27
|
+
writeToken = _writeToken,
|
|
28
|
+
readStoredToken = _readStoredToken
|
|
29
|
+
} = deps;
|
|
30
|
+
|
|
31
|
+
// PAT mode: --token flag provided
|
|
32
|
+
if (options.token) {
|
|
33
|
+
process.stdout.write('Validating token...\n');
|
|
34
|
+
const validation = await validateToken(options.token);
|
|
35
|
+
if (!validation.valid) {
|
|
36
|
+
process.stderr.write(chalk.red('Error: Token is invalid or has expired. Please check and try again.\n'));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
writeToken(options.token);
|
|
40
|
+
process.stdout.write(chalk.green(`Authenticated as ${validation.username}\n`));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Check if already authenticated
|
|
45
|
+
const existing = resolveToken({ readStored: readStoredToken });
|
|
46
|
+
if (existing) {
|
|
47
|
+
const validation = await validateToken(existing);
|
|
48
|
+
if (validation.valid) {
|
|
49
|
+
process.stdout.write(chalk.green(`Already authenticated as ${validation.username}\n`));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
// Token invalid — clear it and re-auth
|
|
53
|
+
process.stdout.write(chalk.yellow('Stored token is no longer valid. Re-authenticating...\n'));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Device flow
|
|
57
|
+
process.stdout.write('Starting GitHub OAuth Device Flow...\n');
|
|
58
|
+
let newToken;
|
|
59
|
+
try {
|
|
60
|
+
newToken = await startDeviceFlow({
|
|
61
|
+
display: ({ userCode, verificationUri, expiresIn }) => {
|
|
62
|
+
process.stdout.write('\n');
|
|
63
|
+
process.stdout.write(chalk.bold('To authenticate, visit: ') + chalk.cyan(verificationUri) + '\n');
|
|
64
|
+
process.stdout.write(chalk.bold('And enter the code: ') + chalk.yellow(userCode) + '\n');
|
|
65
|
+
process.stdout.write(`(Code expires in ${Math.round(expiresIn / 60)} minutes)\n\n`);
|
|
66
|
+
process.stdout.write('Waiting for authorization...\n');
|
|
67
|
+
},
|
|
68
|
+
writeStored: writeToken
|
|
69
|
+
});
|
|
70
|
+
} catch (err) {
|
|
71
|
+
process.stderr.write(chalk.red(`Error: ${err.message}\n`));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const validation = await validateToken(newToken);
|
|
76
|
+
if (validation.valid) {
|
|
77
|
+
process.stdout.write(chalk.green(`\nAuthenticated as ${validation.username}\n`));
|
|
78
|
+
} else {
|
|
79
|
+
process.stdout.write(chalk.green('\nAuthentication successful.\n'));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// logoutAction
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Handle 'mk auth logout'.
|
|
89
|
+
*
|
|
90
|
+
* @param {object} [deps] - Injected dependencies (for testing)
|
|
91
|
+
*/
|
|
92
|
+
export async function logoutAction(deps = {}) {
|
|
93
|
+
const { deleteToken = _deleteToken } = deps;
|
|
94
|
+
deleteToken();
|
|
95
|
+
process.stdout.write(chalk.green('Logged out. Stored token removed.\n'));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
// statusAction
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Handle 'mk auth status'.
|
|
104
|
+
*
|
|
105
|
+
* @param {object} [deps] - Injected dependencies (for testing)
|
|
106
|
+
*/
|
|
107
|
+
export async function statusAction(deps = {}) {
|
|
108
|
+
const {
|
|
109
|
+
resolveToken = _resolveToken,
|
|
110
|
+
tokenSource = _tokenSource,
|
|
111
|
+
validateToken = _validateToken,
|
|
112
|
+
checkRepoAccess = _checkRepoAccess,
|
|
113
|
+
readStoredToken = _readStoredToken
|
|
114
|
+
} = deps;
|
|
115
|
+
|
|
116
|
+
const token = resolveToken({ readStored: readStoredToken });
|
|
117
|
+
|
|
118
|
+
if (!token) {
|
|
119
|
+
process.stdout.write(chalk.yellow('Not authenticated. Run \'mk auth login\' to authenticate.\n'));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const source = tokenSource(token, { readStored: readStoredToken });
|
|
124
|
+
process.stdout.write(`Token source: ${source}\n`);
|
|
125
|
+
|
|
126
|
+
const validation = await validateToken(token);
|
|
127
|
+
if (!validation.valid) {
|
|
128
|
+
process.stdout.write(chalk.red('Token: invalid or expired\n'));
|
|
129
|
+
process.stdout.write(chalk.yellow('Run \'mk auth login\' to re-authenticate.\n'));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
process.stdout.write(chalk.green(`Logged in as: ${validation.username}\n`));
|
|
134
|
+
process.stdout.write('Token: valid\n');
|
|
135
|
+
|
|
136
|
+
const access = await checkRepoAccess(token);
|
|
137
|
+
if (access.accessible) {
|
|
138
|
+
process.stdout.write(chalk.green('Repo access: granted\n'));
|
|
139
|
+
} else {
|
|
140
|
+
process.stdout.write(chalk.red('Repo access: denied\n'));
|
|
141
|
+
process.stdout.write('Contact the repository owner for collaborator access.\n');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { join, relative } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { copyKitFiles } from '../lib/copy.js';
|
|
6
|
+
import { writeManifest } from '../lib/manifest.js';
|
|
7
|
+
import { computeChecksum } from '../lib/checksum.js';
|
|
8
|
+
import { resolveTargetDir, resolveManifestPath } from '../lib/paths.js';
|
|
9
|
+
import { MANIFEST_FILENAME } from '../lib/constants.js';
|
|
10
|
+
import { resolveTokenOrLogin } from '../lib/auth.js';
|
|
11
|
+
import { writeToken, readStoredToken } from '../lib/config.js';
|
|
12
|
+
import { downloadAndExtractKit, cleanupTempDir } from '../lib/download.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Run the init command.
|
|
16
|
+
*
|
|
17
|
+
* @param {{
|
|
18
|
+
* sourceDir?: string,
|
|
19
|
+
* targetDir?: string,
|
|
20
|
+
* manifestPath?: string,
|
|
21
|
+
* scope?: 'project'|'global',
|
|
22
|
+
* dryRun?: boolean
|
|
23
|
+
* }} params
|
|
24
|
+
* @returns {Promise<{ files: Array, totalSize: number }>}
|
|
25
|
+
*/
|
|
26
|
+
export async function runInit(params = {}) {
|
|
27
|
+
const {
|
|
28
|
+
sourceDir = resolveSourceDir(),
|
|
29
|
+
targetDir = resolveTargetDir({ global: false }),
|
|
30
|
+
manifestPath = resolveManifestPath({ global: false }),
|
|
31
|
+
scope = 'project',
|
|
32
|
+
dryRun = false
|
|
33
|
+
} = params;
|
|
34
|
+
|
|
35
|
+
// Guard: abort if already installed
|
|
36
|
+
if (existsSync(manifestPath)) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`Already installed. Use 'mk update' to update kit files. (Manifest found at: ${manifestPath})`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Copy files (dry-run or real)
|
|
43
|
+
const fileList = copyKitFiles(sourceDir, targetDir, { dryRun });
|
|
44
|
+
|
|
45
|
+
if (dryRun) {
|
|
46
|
+
return { files: fileList, totalSize: fileList.reduce((s, f) => s + f.size, 0), dryRun: true };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Compute checksums and build manifest files map
|
|
50
|
+
// fileURLToPath handles Windows drive-letter prefix correctly (avoids leading /C:/ from .pathname)
|
|
51
|
+
const pkg = JSON.parse(readFileSync(
|
|
52
|
+
fileURLToPath(new URL('../../package.json', import.meta.url)),
|
|
53
|
+
'utf8'
|
|
54
|
+
));
|
|
55
|
+
|
|
56
|
+
const files = {};
|
|
57
|
+
for (const entry of fileList) {
|
|
58
|
+
if (existsSync(entry.absolutePath)) {
|
|
59
|
+
const checksum = computeChecksum(entry.absolutePath);
|
|
60
|
+
files[entry.relativePath] = { checksum, size: entry.size };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Write manifest
|
|
65
|
+
writeManifest(manifestPath, files, pkg.version, scope);
|
|
66
|
+
|
|
67
|
+
const totalSize = fileList.reduce((s, f) => s + f.size, 0);
|
|
68
|
+
return { files: fileList, totalSize, fileCount: fileList.length };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* CLI action handler for 'mk init'.
|
|
73
|
+
* Downloads kit from GitHub and installs .claude/ to target directory.
|
|
74
|
+
*
|
|
75
|
+
* @param {{ global: boolean, dryRun: boolean }} options
|
|
76
|
+
* @param {object} [deps] - Injected dependencies (for testing)
|
|
77
|
+
*/
|
|
78
|
+
export async function initAction(options = {}, deps = {}) {
|
|
79
|
+
const {
|
|
80
|
+
resolveTokenOrLogin: resolveAndLogin = resolveTokenOrLogin,
|
|
81
|
+
downloadAndExtractKit: download = downloadAndExtractKit,
|
|
82
|
+
cleanupTempDir: cleanup = cleanupTempDir,
|
|
83
|
+
writeToken: storeToken = writeToken,
|
|
84
|
+
readStoredToken: readToken = readStoredToken
|
|
85
|
+
} = deps;
|
|
86
|
+
|
|
87
|
+
const targetDir = resolveTargetDir(options);
|
|
88
|
+
const manifestPath = resolveManifestPath(options);
|
|
89
|
+
const scope = options.global ? 'global' : 'project';
|
|
90
|
+
|
|
91
|
+
// Pre-flight: warn if target exists with files
|
|
92
|
+
if (existsSync(targetDir)) {
|
|
93
|
+
process.stdout.write(
|
|
94
|
+
chalk.yellow(`Warning: ${targetDir} already exists. Kit files will be added to existing structure.\n`)
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (options.dryRun) {
|
|
99
|
+
process.stdout.write(chalk.cyan('Dry run — no files will be written.\n\n'));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let tempDir = null;
|
|
103
|
+
try {
|
|
104
|
+
// Authenticate and download kit from GitHub
|
|
105
|
+
process.stdout.write('Authenticating with GitHub...\n');
|
|
106
|
+
const token = await resolveAndLogin({
|
|
107
|
+
readStored: readToken,
|
|
108
|
+
writeStored: storeToken,
|
|
109
|
+
display: ({ userCode, verificationUri, expiresIn }) => {
|
|
110
|
+
process.stdout.write('\n');
|
|
111
|
+
process.stdout.write(chalk.bold('To authenticate, visit: ') + chalk.cyan(verificationUri) + '\n');
|
|
112
|
+
process.stdout.write(chalk.bold('And enter the code: ') + chalk.yellow(userCode) + '\n');
|
|
113
|
+
process.stdout.write(`(Code expires in ${Math.round(expiresIn / 60)} minutes)\n\n`);
|
|
114
|
+
process.stdout.write('Waiting for authorization...\n');
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
process.stdout.write('Downloading kit from GitHub...\n');
|
|
119
|
+
tempDir = await download(token);
|
|
120
|
+
const sourceDir = join(tempDir, '.claude');
|
|
121
|
+
|
|
122
|
+
const result = await runInit({ sourceDir, targetDir, manifestPath, scope, dryRun: options.dryRun });
|
|
123
|
+
|
|
124
|
+
if (options.dryRun) {
|
|
125
|
+
process.stdout.write(`Would install ${result.files.length} files:\n`);
|
|
126
|
+
for (const f of result.files) {
|
|
127
|
+
process.stdout.write(` ${f.relativePath}\n`);
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
const sizeKB = Math.round(result.totalSize / 1024);
|
|
131
|
+
process.stdout.write(
|
|
132
|
+
chalk.green(`Installed ${result.fileCount} files (${sizeKB} KB) to ${targetDir}\n`)
|
|
133
|
+
);
|
|
134
|
+
process.stdout.write(`Manifest written to: ${manifestPath}\n`);
|
|
135
|
+
}
|
|
136
|
+
} catch (err) {
|
|
137
|
+
process.stderr.write(chalk.red(`Error: ${err.message}\n`));
|
|
138
|
+
process.exit(1);
|
|
139
|
+
} finally {
|
|
140
|
+
if (tempDir) {
|
|
141
|
+
cleanup(tempDir);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|