pm-skill 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.
package/.env.example ADDED
@@ -0,0 +1,17 @@
1
+ # === Linear ===
2
+ # Settings > API > Personal API Keys 에서 발급
3
+ LINEAR_API_KEY=lin_api_xxxxxxxx
4
+
5
+ # setup 커맨드로 확인 가능
6
+ LINEAR_DEFAULT_TEAM_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
7
+ LINEAR_DEFAULT_PROJECT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
8
+
9
+ # === Notion ===
10
+ # https://www.notion.so/my-integrations 에서 발급
11
+ NOTION_API_KEY=secret_xxxxxxxx
12
+
13
+ # Notion에서 문서를 생성할 상위 페이지 ID
14
+ NOTION_ROOT_PAGE_ID=xxxxxxxx
15
+
16
+ # (선택) 버그 추적 DB ID — 설정 시 report-bug가 DB 엔트리로 생성
17
+ NOTION_BUG_DB_ID=xxxxxxxx
package/AGENTS.md ADDED
@@ -0,0 +1,87 @@
1
+ # PM Skill — Agent Instructions
2
+
3
+ This file provides instructions for AI coding assistants (Codex, Claude Code, etc.) to use the pm-skill CLI.
4
+
5
+ ## What is pm-skill?
6
+
7
+ A structured project management CLI that integrates Linear (issue tracking) and Notion (documentation). It enforces a config-driven workflow where only pre-defined labels, templates, and severity levels are allowed.
8
+
9
+ ## How to Run Commands
10
+
11
+ ```bash
12
+ # If installed globally via npm:
13
+ pm-skill <command> [args] [flags]
14
+
15
+ # If running from source:
16
+ npx tsx src/workflows.ts <command> [args] [flags]
17
+ ```
18
+
19
+ ## Available Commands
20
+
21
+ ### setup
22
+ Verify Linear/Notion connection and show team/label configuration.
23
+ ```bash
24
+ pm-skill setup
25
+ ```
26
+
27
+ ### start-feature
28
+ Create a Linear issue + Notion PRD page with bidirectional links.
29
+ ```bash
30
+ pm-skill start-feature "<title>"
31
+ ```
32
+
33
+ ### report-bug
34
+ File a bug report with severity-based priority mapping.
35
+ ```bash
36
+ pm-skill report-bug "<title>" --severity <urgent|high|medium|low>
37
+ ```
38
+ Default severity: medium.
39
+
40
+ ### add-task
41
+ Add a sub-issue to a parent issue.
42
+ ```bash
43
+ pm-skill add-task <parent-issue-id> "<title>"
44
+ ```
45
+
46
+ ### relate
47
+ Set a relationship between two issues.
48
+ ```bash
49
+ pm-skill relate <issue1> <issue2> --type <related|similar>
50
+ ```
51
+ Default type: related.
52
+
53
+ ### block
54
+ Set a blocking dependency (issue1 blocks issue2).
55
+ ```bash
56
+ pm-skill block <blocker-issue> <blocked-issue>
57
+ ```
58
+
59
+ ### attach-doc
60
+ Attach a document URL to an issue with type validation.
61
+ ```bash
62
+ pm-skill attach-doc <issue> --url "<url>" --title "<title>" --type <source-of-truth|issue-tracking|domain-knowledge>
63
+ ```
64
+
65
+ ### get
66
+ Show issue details including children, relations, and attachments.
67
+ ```bash
68
+ pm-skill get <issue-id>
69
+ ```
70
+
71
+ ## Configuration
72
+
73
+ - **`.env`** — API keys and IDs. Looked up in: CWD → `~/.pm-skill/` → package root.
74
+ - **`config.yml`** — Labels, templates, priorities, severity mappings. Same lookup order.
75
+
76
+ ## Workflow Patterns
77
+
78
+ ### Feature Development
79
+ 1. `start-feature "Feature Name"` — creates Linear issue + Notion PRD
80
+ 2. `add-task ENG-XX "Sub-task 1"` — break down into sub-tasks
81
+ 3. `relate ENG-XX ENG-YY` — link related issues
82
+ 4. `attach-doc ENG-XX --url "..." --title "..." --type source-of-truth`
83
+
84
+ ### Bug Fix
85
+ 1. `report-bug "Bug Description" --severity high`
86
+ 2. `add-task ENG-XX "Root cause analysis"`
87
+ 3. `add-task ENG-XX "Fix and test"`
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 lsm
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,144 @@
1
+ # pm-skill
2
+
3
+ Structured project management CLI that integrates **Linear** and **Notion**. Designed to work with AI coding assistants — Claude Code, Codex, and any tool that can run shell commands.
4
+
5
+ > "Design freedom, usage discipline" — only labels, templates, and severity levels defined in `config.yml` are allowed.
6
+
7
+ ## Features
8
+
9
+ - **start-feature** — Create Linear issue + Notion PRD page, auto-linked
10
+ - **report-bug** — File bug with severity-based priority mapping
11
+ - **add-task** — Add sub-issues to a parent
12
+ - **relate / block** — Set issue relationships and dependencies
13
+ - **attach-doc** — Attach documents with type validation
14
+ - **get** — View issue details with children, relations, and attachments
15
+ - **setup** — Verify connections and discover team/label IDs
16
+
17
+ ## Installation
18
+
19
+ ### Option A: npm (recommended)
20
+
21
+ ```bash
22
+ npm install -g pm-skill
23
+ pm-skill help
24
+ ```
25
+
26
+ ### Option B: npx (no install)
27
+
28
+ ```bash
29
+ npx pm-skill help
30
+ ```
31
+
32
+ ### Option C: Clone as Claude Code skill
33
+
34
+ ```bash
35
+ git clone https://github.com/Leonamin/pm-skill.git ~/.claude/skills/pm-skill
36
+ cd ~/.claude/skills/pm-skill && npm install
37
+ ```
38
+
39
+ ### Option D: Clone for Codex
40
+
41
+ ```bash
42
+ git clone https://github.com/Leonamin/pm-skill.git ~/pm-skill
43
+ cd ~/pm-skill && npm install
44
+ # Codex will read AGENTS.md for instructions
45
+ ```
46
+
47
+ ## Setup
48
+
49
+ ### 1. Create `.env`
50
+
51
+ Place `.env` in any of these locations (checked in order):
52
+ 1. Current working directory
53
+ 2. `~/.pm-skill/`
54
+ 3. Package root
55
+
56
+ ```bash
57
+ # Copy the template
58
+ cp .env.example ~/.pm-skill/.env
59
+ # Edit with your API keys
60
+ ```
61
+
62
+ ```env
63
+ LINEAR_API_KEY=lin_api_xxxxxxxx
64
+ LINEAR_DEFAULT_TEAM_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
65
+ NOTION_API_KEY=secret_xxxxxxxx
66
+ NOTION_ROOT_PAGE_ID=xxxxxxxx
67
+ ```
68
+
69
+ ### 2. Run setup
70
+
71
+ ```bash
72
+ pm-skill setup
73
+ ```
74
+
75
+ This shows your Linear teams, workflow states, and labels — and verifies that `config.yml` labels match your Linear workspace.
76
+
77
+ ### 3. Customize `config.yml`
78
+
79
+ Copy `config.yml` to `~/.pm-skill/config.yml` and edit labels, templates, priorities, and severity mappings to match your project.
80
+
81
+ **Rule: every label and template must have a `description` field.**
82
+
83
+ ## Usage
84
+
85
+ ```bash
86
+ # Start a feature
87
+ pm-skill start-feature "Booking cancellation"
88
+
89
+ # Report a bug
90
+ pm-skill report-bug "Payment amount error" --severity high
91
+
92
+ # Add sub-tasks
93
+ pm-skill add-task ENG-10 "Write unit tests"
94
+ pm-skill add-task ENG-10 "Frontend UI"
95
+
96
+ # Link issues
97
+ pm-skill relate ENG-10 ENG-8 --type related
98
+ pm-skill block ENG-10 ENG-15
99
+
100
+ # Attach documents
101
+ pm-skill attach-doc ENG-10 --url "https://notion.so/..." --title "Design Doc" --type source-of-truth
102
+
103
+ # View issue details
104
+ pm-skill get ENG-10
105
+ ```
106
+
107
+ ## Using with AI Assistants
108
+
109
+ ### Claude Code
110
+
111
+ If installed as a skill in `~/.claude/skills/pm-skill/`, Claude Code auto-discovers it via `SKILL.md`. You can invoke commands through natural language:
112
+
113
+ > "Create a feature issue for booking cancellation"
114
+
115
+ ### Codex
116
+
117
+ Clone the repo and Codex reads `AGENTS.md` for command instructions. Run commands via:
118
+
119
+ ```bash
120
+ npx tsx src/workflows.ts start-feature "Booking cancellation"
121
+ ```
122
+
123
+ ### Any AI Assistant
124
+
125
+ Any assistant that can execute shell commands can use pm-skill:
126
+
127
+ ```bash
128
+ pm-skill start-feature "My Feature"
129
+ ```
130
+
131
+ ## Config Structure
132
+
133
+ | Section | Description |
134
+ |---------|-------------|
135
+ | `labels` | Available labels (description required) |
136
+ | `templates` | Command-to-label/priority/Notion-template mappings |
137
+ | `priorities` | Priority key (p0-p3) to Linear priority number mapping |
138
+ | `severity_mapping` | Severity name to priority key mapping |
139
+ | `doc_types` | Document types for attach-doc validation |
140
+ | `epics` | Epic definitions (project-specific) |
141
+
142
+ ## License
143
+
144
+ MIT
package/SKILL.md ADDED
@@ -0,0 +1,134 @@
1
+ # PM Skill — 정형화된 프로젝트 관리 도구
2
+
3
+ Linear와 Notion을 정형화된 틀 안에서 조작하는 프로젝트 관리 스킬.
4
+ "설계의 자유, 사용의 부자유" — config.yml에 정의된 라벨/템플릿/severity만 사용 가능.
5
+
6
+ ## 설정
7
+
8
+ ### 1. API 키 발급
9
+
10
+ **Linear**: Settings > API > Personal API Keys
11
+ **Notion**: https://www.notion.so/my-integrations > New integration
12
+
13
+ ### 2. .env 생성
14
+
15
+ ```bash
16
+ cp .env.example .env
17
+ # LINEAR_API_KEY, NOTION_API_KEY 입력
18
+ ```
19
+
20
+ ### 3. Setup 실행
21
+
22
+ ```bash
23
+ npx tsx src/workflows.ts setup
24
+ ```
25
+
26
+ 팀/상태/라벨 ID를 확인하고 `.env`에 `LINEAR_DEFAULT_TEAM_ID` 등을 설정.
27
+
28
+ ### 4. Config 커스터마이징
29
+
30
+ `config.yml`에서 라벨, 템플릿, severity 매핑을 프로젝트에 맞게 수정.
31
+ **규칙: 모든 라벨/템플릿에 `description` 필수.** 없으면 로딩 시 에러.
32
+
33
+ ```yaml
34
+ labels:
35
+ - id: my-label
36
+ name: My Label
37
+ description: "이 라벨의 용도 설명" # ← 필수!
38
+ ```
39
+
40
+ ## 커맨드 레퍼런스
41
+
42
+ ### setup
43
+ Linear/Notion 연결 상태 확인 + .env 안내.
44
+ ```bash
45
+ npx tsx src/workflows.ts setup
46
+ ```
47
+
48
+ ### start-feature
49
+ 기능 개발 시작. Linear 이슈 + Notion PRD + 상호 URL 연결.
50
+ ```bash
51
+ npx tsx src/workflows.ts start-feature "예약 취소 기능"
52
+ ```
53
+
54
+ ### report-bug
55
+ 버그 리포트. severity로 우선순위 자동 매핑.
56
+ ```bash
57
+ npx tsx src/workflows.ts report-bug "결제 금액 오류" --severity high
58
+ # severity: urgent, high, medium(기본), low
59
+ ```
60
+
61
+ ### add-task
62
+ 이슈에 하위 태스크 추가.
63
+ ```bash
64
+ npx tsx src/workflows.ts add-task ENG-10 "단위 테스트 작성"
65
+ ```
66
+
67
+ ### relate
68
+ 이슈 간 관계 설정. (related, similar)
69
+ ```bash
70
+ npx tsx src/workflows.ts relate ENG-10 ENG-11 --type related
71
+ ```
72
+
73
+ ### block
74
+ 선행 관계 설정. (ENG-10 완료 후 ENG-11 진행)
75
+ ```bash
76
+ npx tsx src/workflows.ts block ENG-10 ENG-11
77
+ ```
78
+
79
+ ### attach-doc
80
+ 이슈에 문서 URL 첨부. doc_type은 config 검증.
81
+ ```bash
82
+ npx tsx src/workflows.ts attach-doc ENG-10 \
83
+ --url "https://notion.so/..." \
84
+ --title "결제 설계서" \
85
+ --type source-of-truth
86
+ # type: source-of-truth, issue-tracking, domain-knowledge
87
+ ```
88
+
89
+ ### get
90
+ 이슈 상세 조회. 하위이슈, 관계, 첨부문서 포함.
91
+ ```bash
92
+ npx tsx src/workflows.ts get ENG-10
93
+ ```
94
+
95
+ ## 워크플로우 예시
96
+
97
+ ### 기능 개발
98
+ ```bash
99
+ # 1. 기능 시작
100
+ npx tsx src/workflows.ts start-feature "예약 취소 기능"
101
+ # → Linear ENG-10 + Notion PRD 생성
102
+
103
+ # 2. 하위 태스크 추가
104
+ npx tsx src/workflows.ts add-task ENG-10 "API 엔드포인트 구현"
105
+ npx tsx src/workflows.ts add-task ENG-10 "프론트엔드 UI"
106
+ npx tsx src/workflows.ts add-task ENG-10 "테스트 작성"
107
+
108
+ # 3. 관련 이슈 연결
109
+ npx tsx src/workflows.ts relate ENG-10 ENG-8 --type related
110
+
111
+ # 4. 선행 관계 설정
112
+ npx tsx src/workflows.ts block ENG-10 ENG-15
113
+ ```
114
+
115
+ ### 버그 수정
116
+ ```bash
117
+ # 1. 버그 리포트
118
+ npx tsx src/workflows.ts report-bug "결제 금액 오류" --severity high
119
+
120
+ # 2. 하위 태스크
121
+ npx tsx src/workflows.ts add-task ENG-20 "원인 분석"
122
+ npx tsx src/workflows.ts add-task ENG-20 "수정 및 테스트"
123
+ ```
124
+
125
+ ## Config 구조
126
+
127
+ | 섹션 | 설명 |
128
+ |------|------|
129
+ | `labels` | 사용 가능한 라벨 목록 (description 필수) |
130
+ | `templates` | 커맨드별 기본 라벨/우선순위/Notion 템플릿 매핑 |
131
+ | `priorities` | Plank 우선순위 → Linear 우선순위 매핑 (p0-p3) |
132
+ | `severity_mapping` | severity 이름 → priority 키 매핑 |
133
+ | `doc_types` | 문서 유형 (attach-doc의 --type 값) |
134
+ | `epics` | 에픽 목록 (프로젝트별 정의) |
package/config.yml ADDED
@@ -0,0 +1,82 @@
1
+ labels:
2
+ - id: dev
3
+ name: Development
4
+ description: "코드 개발 작업 (새 기능, 기존 기능 변경)"
5
+ color: "#0088ff"
6
+ - id: design
7
+ name: Design
8
+ description: "UI/UX 디자인 관련 작업"
9
+ color: "#ff00ff"
10
+ - id: plan
11
+ name: Planning
12
+ description: "기획, 설계, 의사결정 문서 작업"
13
+ color: "#00cc88"
14
+ - id: bug
15
+ name: Bug
16
+ description: "버그 수정"
17
+ color: "#ff0000"
18
+ - id: refactor
19
+ name: Refactor
20
+ description: "기존 코드 리팩토링 (기능 변경 없음)"
21
+ color: "#888888"
22
+ - id: admin
23
+ name: Admin
24
+ description: "인프라, 배포, 환경 설정 등 관리 작업"
25
+ color: "#ffaa00"
26
+ - id: guest
27
+ name: Guest
28
+ description: "외부 의존성, 서드파티 연동 관련"
29
+ color: "#aa88ff"
30
+
31
+ templates:
32
+ - id: feature
33
+ name: Feature PRD
34
+ description: "기능 개발용 PRD 템플릿"
35
+ notion_template: feature-prd
36
+ linear_labels: [dev]
37
+ linear_priority: p2
38
+ - id: bugfix
39
+ name: Bug Report
40
+ description: "버그 리포트 템플릿"
41
+ notion_template: bug-report
42
+ linear_labels: [bug]
43
+ - id: improvement
44
+ name: Improvement
45
+ description: "기존 기능 개선 템플릿"
46
+ notion_template: improvement
47
+ linear_labels: [dev]
48
+ linear_priority: p2
49
+ - id: refactor
50
+ name: Refactor
51
+ description: "리팩토링 템플릿"
52
+ notion_template: refactor
53
+ linear_labels: [refactor]
54
+ linear_priority: p3
55
+ - id: design
56
+ name: Design Doc
57
+ description: "설계 문서 템플릿"
58
+ notion_template: design-doc
59
+ linear_labels: [design, plan]
60
+ linear_priority: p2
61
+
62
+ priorities:
63
+ p0: { linear: 1, name: "Urgent" }
64
+ p1: { linear: 2, name: "High" }
65
+ p2: { linear: 3, name: "Medium" }
66
+ p3: { linear: 4, name: "Low" }
67
+
68
+ severity_mapping:
69
+ urgent: p0
70
+ high: p1
71
+ medium: p2
72
+ low: p3
73
+
74
+ doc_types:
75
+ - id: source-of-truth
76
+ description: "진실의 원천 문서 — 프로젝트의 공식 기준이 되는 문서"
77
+ - id: issue-tracking
78
+ description: "장기 이슈 추적 문서 — 해결까지 시간이 걸리는 문제 기록"
79
+ - id: domain-knowledge
80
+ description: "도메인 지식 문서 — 프로젝트 도메인 관련 학습/참고 자료"
81
+
82
+ epics: []
@@ -0,0 +1,40 @@
1
+ export interface PmLabel {
2
+ id: string;
3
+ name: string;
4
+ description: string;
5
+ color?: string;
6
+ }
7
+ export interface PmTemplate {
8
+ id: string;
9
+ name: string;
10
+ description: string;
11
+ notion_template: string;
12
+ linear_labels: string[];
13
+ linear_priority?: string;
14
+ }
15
+ export interface PmPriority {
16
+ linear: number;
17
+ name: string;
18
+ }
19
+ export interface PmDocType {
20
+ id: string;
21
+ description: string;
22
+ }
23
+ export interface PmEpic {
24
+ id: string;
25
+ name: string;
26
+ }
27
+ export interface PmConfig {
28
+ labels: PmLabel[];
29
+ templates: PmTemplate[];
30
+ priorities: Record<string, PmPriority>;
31
+ severity_mapping: Record<string, string>;
32
+ doc_types: PmDocType[];
33
+ epics: PmEpic[];
34
+ }
35
+ export declare function loadConfig(configPath?: string): PmConfig;
36
+ export declare function validateLabel(config: PmConfig, labelId: string): PmLabel;
37
+ export declare function validateDocType(config: PmConfig, typeId: string): PmDocType;
38
+ export declare function getTemplate(config: PmConfig, templateId: string): PmTemplate;
39
+ export declare function resolvePriority(config: PmConfig, priorityKey: string): number;
40
+ export declare function resolveSeverity(config: PmConfig, severity: string): number;
package/dist/config.js ADDED
@@ -0,0 +1,144 @@
1
+ import { readFileSync } from "fs";
2
+ import yaml from "js-yaml";
3
+ import { resolveFile } from "./env.js";
4
+ // ── Validation ──
5
+ class ConfigValidationError extends Error {
6
+ constructor(message) {
7
+ super(`[Config validation failed] ${message}`);
8
+ this.name = "ConfigValidationError";
9
+ }
10
+ }
11
+ function validateLabels(labels) {
12
+ if (!Array.isArray(labels) || labels.length === 0) {
13
+ throw new ConfigValidationError("labels array is empty or missing.");
14
+ }
15
+ for (const label of labels) {
16
+ if (!label.id || !label.name) {
17
+ throw new ConfigValidationError(`Label missing id or name: ${JSON.stringify(label)}`);
18
+ }
19
+ if (!label.description) {
20
+ throw new ConfigValidationError(`Label '${label.id}' is missing description. All labels require a description.`);
21
+ }
22
+ }
23
+ }
24
+ function validateTemplates(templates, labelIds) {
25
+ if (!Array.isArray(templates) || templates.length === 0) {
26
+ throw new ConfigValidationError("templates array is empty or missing.");
27
+ }
28
+ for (const tmpl of templates) {
29
+ if (!tmpl.id || !tmpl.name || !tmpl.description || !tmpl.notion_template) {
30
+ throw new ConfigValidationError(`Template '${tmpl.id ?? "(unknown)"}' is missing required fields (id, name, description, notion_template).`);
31
+ }
32
+ if (Array.isArray(tmpl.linear_labels)) {
33
+ for (const lid of tmpl.linear_labels) {
34
+ if (!labelIds.has(lid)) {
35
+ throw new ConfigValidationError(`Template '${tmpl.id}' references unregistered label '${lid}' in linear_labels.`);
36
+ }
37
+ }
38
+ }
39
+ }
40
+ }
41
+ function validatePriorities(priorities) {
42
+ if (!priorities || typeof priorities !== "object") {
43
+ throw new ConfigValidationError("priorities is missing.");
44
+ }
45
+ for (const key of ["p0", "p1", "p2", "p3"]) {
46
+ const p = priorities[key];
47
+ if (!p ||
48
+ typeof p.linear !== "number" ||
49
+ typeof p.name !== "string") {
50
+ throw new ConfigValidationError(`priorities.${key} requires linear (number) and name (string).`);
51
+ }
52
+ }
53
+ }
54
+ function validateSeverityMapping(mapping, priorityKeys) {
55
+ if (!mapping || typeof mapping !== "object") {
56
+ throw new ConfigValidationError("severity_mapping is missing.");
57
+ }
58
+ for (const [severity, pKey] of Object.entries(mapping)) {
59
+ if (!priorityKeys.has(pKey)) {
60
+ throw new ConfigValidationError(`severity_mapping '${severity}: ${pKey}' — '${pKey}' is not defined in priorities.`);
61
+ }
62
+ }
63
+ }
64
+ function validateDocTypes(docTypes) {
65
+ if (!Array.isArray(docTypes) || docTypes.length === 0) {
66
+ throw new ConfigValidationError("doc_types array is empty or missing.");
67
+ }
68
+ for (const dt of docTypes) {
69
+ if (!dt.id || !dt.description) {
70
+ throw new ConfigValidationError(`doc_type '${dt.id ?? "(unknown)"}' is missing id or description.`);
71
+ }
72
+ }
73
+ }
74
+ // ── Loader ──
75
+ export function loadConfig(configPath) {
76
+ const path = configPath ?? resolveFile("config.yml");
77
+ if (!path) {
78
+ throw new ConfigValidationError("config.yml not found. Looked in: CWD, ~/.pm-skill/, package root.\n" +
79
+ "Copy the bundled config.yml to one of these locations and customize it.");
80
+ }
81
+ let raw;
82
+ try {
83
+ const content = readFileSync(path, "utf-8");
84
+ raw = yaml.load(content);
85
+ }
86
+ catch (e) {
87
+ throw new ConfigValidationError(`Cannot read config.yml: ${path}\n${e.message}`);
88
+ }
89
+ validateLabels(raw.labels);
90
+ const labelIds = new Set(raw.labels.map((l) => l.id));
91
+ validateTemplates(raw.templates, labelIds);
92
+ validatePriorities(raw.priorities);
93
+ const priorityKeys = new Set(Object.keys(raw.priorities));
94
+ validateSeverityMapping(raw.severity_mapping, priorityKeys);
95
+ validateDocTypes(raw.doc_types);
96
+ return {
97
+ labels: raw.labels,
98
+ templates: raw.templates,
99
+ priorities: raw.priorities,
100
+ severity_mapping: raw.severity_mapping,
101
+ doc_types: raw.doc_types,
102
+ epics: Array.isArray(raw.epics) ? raw.epics : [],
103
+ };
104
+ }
105
+ // ── Query helpers ──
106
+ export function validateLabel(config, labelId) {
107
+ const label = config.labels.find((l) => l.id === labelId);
108
+ if (!label) {
109
+ const available = config.labels.map((l) => l.id).join(", ");
110
+ throw new Error(`Label '${labelId}' is not defined in config.\nAvailable: ${available}`);
111
+ }
112
+ return label;
113
+ }
114
+ export function validateDocType(config, typeId) {
115
+ const dt = config.doc_types.find((d) => d.id === typeId);
116
+ if (!dt) {
117
+ const available = config.doc_types.map((d) => d.id).join(", ");
118
+ throw new Error(`Doc type '${typeId}' is not defined in config.\nAvailable: ${available}`);
119
+ }
120
+ return dt;
121
+ }
122
+ export function getTemplate(config, templateId) {
123
+ const tmpl = config.templates.find((t) => t.id === templateId);
124
+ if (!tmpl) {
125
+ const available = config.templates.map((t) => t.id).join(", ");
126
+ throw new Error(`Template '${templateId}' is not defined in config.\nAvailable: ${available}`);
127
+ }
128
+ return tmpl;
129
+ }
130
+ export function resolvePriority(config, priorityKey) {
131
+ const p = config.priorities[priorityKey];
132
+ if (!p) {
133
+ throw new Error(`Priority '${priorityKey}' is not defined in config.`);
134
+ }
135
+ return p.linear;
136
+ }
137
+ export function resolveSeverity(config, severity) {
138
+ const pKey = config.severity_mapping[severity];
139
+ if (!pKey) {
140
+ const available = Object.keys(config.severity_mapping).join(", ");
141
+ throw new Error(`Severity '${severity}' is not defined in config severity_mapping.\nAvailable: ${available}`);
142
+ }
143
+ return resolvePriority(config, pKey);
144
+ }
package/dist/env.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ export declare const GLOBAL_DIR: string;
2
+ /**
3
+ * Resolve a file by checking multiple directories in order:
4
+ * 1. CWD (project-local override)
5
+ * 2. ~/.pm-skill/ (user-global config)
6
+ * 3. Package root (bundled defaults)
7
+ */
8
+ export declare function resolveFile(filename: string): string | null;
9
+ export interface ValidatedEnv {
10
+ LINEAR_API_KEY: string;
11
+ LINEAR_DEFAULT_TEAM_ID: string;
12
+ LINEAR_DEFAULT_PROJECT_ID?: string;
13
+ NOTION_API_KEY?: string;
14
+ NOTION_ROOT_PAGE_ID?: string;
15
+ NOTION_BUG_DB_ID?: string;
16
+ }
17
+ /**
18
+ * Hierarchical .env loading:
19
+ * 1. CWD/.env (project-specific, highest priority)
20
+ * 2. ~/.pm-skill/.env (global defaults, fills in missing keys)
21
+ *
22
+ * Both files are loaded. CWD values take precedence over global.
23
+ */
24
+ export declare function loadEnvFiles(): void;
25
+ export declare function validateEnv(command: string): ValidatedEnv;
26
+ /**
27
+ * Write key=value pairs to a .env file.
28
+ * If the file exists, updates existing keys and appends new ones.
29
+ * If not, creates it fresh.
30
+ */
31
+ export declare function writeEnvFile(targetDir: string, entries: Record<string, string>): string;