@jahanxu/trellis 0.5.0 → 0.5.5

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 (49) hide show
  1. package/dist/cli/index.js +1 -0
  2. package/dist/cli/index.js.map +1 -1
  3. package/dist/commands/init.d.ts +1 -0
  4. package/dist/commands/init.d.ts.map +1 -1
  5. package/dist/commands/init.js +98 -5
  6. package/dist/commands/init.js.map +1 -1
  7. package/dist/configurators/workflow.d.ts.map +1 -1
  8. package/dist/configurators/workflow.js +8 -58
  9. package/dist/configurators/workflow.js.map +1 -1
  10. package/dist/constants/paths.d.ts +0 -17
  11. package/dist/constants/paths.d.ts.map +1 -1
  12. package/dist/constants/paths.js +0 -19
  13. package/dist/constants/paths.js.map +1 -1
  14. package/dist/templates/claude/commands/trellis/handoff.md +56 -122
  15. package/dist/templates/claude/hooks/enforce-output-dir.py +115 -0
  16. package/dist/templates/claude/hooks/session-start.py +87 -166
  17. package/dist/templates/claude/settings.json +10 -0
  18. package/dist/templates/iflow/hooks/session-start.py +0 -171
  19. package/dist/templates/markdown/index.d.ts +0 -9
  20. package/dist/templates/markdown/index.d.ts.map +1 -1
  21. package/dist/templates/markdown/index.js +0 -10
  22. package/dist/templates/markdown/index.js.map +1 -1
  23. package/dist/templates/trellis/index.d.ts +9 -1
  24. package/dist/templates/trellis/index.d.ts.map +1 -1
  25. package/dist/templates/trellis/index.js +17 -2
  26. package/dist/templates/trellis/index.js.map +1 -1
  27. package/dist/templates/trellis/scripts/common/__init__.py +11 -0
  28. package/dist/templates/trellis/scripts/common/paths.py +1 -49
  29. package/dist/templates/trellis/scripts/common/roles.py +252 -0
  30. package/dist/templates/trellis/spec/roles/designer/index.md +49 -0
  31. package/dist/templates/trellis/spec/roles/frontend-impl/index.md +52 -0
  32. package/dist/templates/trellis/spec/roles/pm/index.md +44 -0
  33. package/dist/utils/template-hash.d.ts.map +1 -1
  34. package/dist/utils/template-hash.js +2 -0
  35. package/dist/utils/template-hash.js.map +1 -1
  36. package/package.json +2 -2
  37. package/dist/templates/claude/commands/trellis/pick-task.md +0 -145
  38. package/dist/templates/iflow/commands/trellis/handoff.md +0 -148
  39. package/dist/templates/iflow/commands/trellis/pick-task.md +0 -145
  40. package/dist/templates/markdown/spec/roles/designer/index.md.txt +0 -57
  41. package/dist/templates/markdown/spec/roles/designer/mock-data-standards.md.txt +0 -63
  42. package/dist/templates/markdown/spec/roles/designer/prototype-guidelines.md.txt +0 -49
  43. package/dist/templates/markdown/spec/roles/frontend-impl/api-integration.md.txt +0 -63
  44. package/dist/templates/markdown/spec/roles/frontend-impl/index.md.txt +0 -57
  45. package/dist/templates/markdown/spec/roles/frontend-impl/prototype-to-production.md.txt +0 -57
  46. package/dist/templates/markdown/spec/roles/pm/index.md.txt +0 -45
  47. package/dist/templates/markdown/spec/roles/pm/prd-template.md.txt +0 -64
  48. package/dist/templates/markdown/spec/roles/pm/requirement-checklist.md.txt +0 -43
  49. package/dist/templates/trellis/scripts/pool.py +0 -322
@@ -1,64 +0,0 @@
1
- # PRD Template
2
-
3
- > Standard structure for Product Requirements Documents.
4
-
5
- ---
6
-
7
- ## Template
8
-
9
- ```markdown
10
- # Feature: {Feature Name}
11
-
12
- ## Background
13
- Why this feature is needed. Business context and user pain points.
14
-
15
- ## Goals
16
- - Primary goal
17
- - Secondary goals
18
-
19
- ## User Stories
20
- As a [user type], I want to [action] so that [benefit].
21
-
22
- ## Requirements
23
-
24
- ### Functional Requirements
25
- 1. ...
26
- 2. ...
27
-
28
- ### Non-Functional Requirements
29
- - Performance: ...
30
- - Security: ...
31
- - Accessibility: ...
32
-
33
- ## UI/UX Requirements
34
- - Layout description
35
- - Key interactions
36
- - Error states
37
-
38
- ## API Requirements (if applicable)
39
- - Endpoints needed
40
- - Data format expectations
41
-
42
- ## Acceptance Criteria
43
- - [ ] Criterion 1
44
- - [ ] Criterion 2
45
-
46
- ## Out of Scope
47
- - Items explicitly excluded from this feature
48
-
49
- ## Open Questions
50
- - Questions that need resolution
51
- ```
52
-
53
- ---
54
-
55
- ## Guidelines
56
-
57
- 1. **Be specific**: Avoid vague language like "should be fast" - specify measurable criteria
58
- 2. **Include edge cases**: Document what happens with empty data, errors, permission issues
59
- 3. **Reference designs**: Link to Figma/mockups when available
60
- 4. **Scope clearly**: Explicitly state what is NOT included
61
-
62
- ---
63
-
64
- **Language**: All documentation should be written in **English**.
@@ -1,43 +0,0 @@
1
- # Requirement Checklist
2
-
3
- > Quality checklist before handing off requirements to the Designer.
4
-
5
- ---
6
-
7
- ## Pre-Handoff Checklist
8
-
9
- ### Completeness
10
- - [ ] All user stories are written with clear acceptance criteria
11
- - [ ] Edge cases are documented (empty states, error states, loading states)
12
- - [ ] UI/UX requirements include layout descriptions and key interactions
13
- - [ ] API requirements are specified (if applicable)
14
- - [ ] Non-functional requirements are defined (performance, security, accessibility)
15
-
16
- ### Clarity
17
- - [ ] No ambiguous language (e.g., "fast", "nice", "user-friendly" without metrics)
18
- - [ ] Technical constraints are explicitly stated
19
- - [ ] Out-of-scope items are listed
20
- - [ ] Open questions are resolved or explicitly deferred
21
-
22
- ### Consistency
23
- - [ ] Feature aligns with existing product patterns
24
- - [ ] Naming conventions match existing features
25
- - [ ] No contradictions between requirements sections
26
-
27
- ### Downstream Readiness
28
- - [ ] Designer can start prototyping without needing clarification meetings
29
- - [ ] All referenced resources (Figma links, API docs) are accessible
30
- - [ ] Priority and scope are clear
31
-
32
- ---
33
-
34
- ## Common Issues to Avoid
35
-
36
- 1. **Missing error states**: Every user action should have a defined failure path
37
- 2. **Implicit assumptions**: State all assumptions explicitly
38
- 3. **Feature creep**: Keep scope tight - add "future considerations" for nice-to-haves
39
- 4. **Missing data requirements**: Specify what data the UI needs and where it comes from
40
-
41
- ---
42
-
43
- **Language**: All documentation should be written in **English**.
@@ -1,322 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- Pool Management Script for Three-Role Collaboration Pipeline.
5
-
6
- Usage:
7
- python3 pool.py init [pool-name...] # Create pool JSON files
8
- python3 pool.py add <pool> <id> <title> <path> [--handoff <path>] # Add deliverable
9
- python3 pool.py list [pool] # List available items
10
- python3 pool.py status <pool> <id> # Get item status
11
- python3 pool.py consume <pool> <id> <consumed-by> # Mark as consumed
12
- """
13
-
14
- from __future__ import annotations
15
-
16
- import sys
17
-
18
- # IMPORTANT: Force stdout to use UTF-8 on Windows
19
- # This fixes UnicodeEncodeError when outputting non-ASCII characters
20
- if sys.platform == "win32":
21
- import io as _io
22
- if hasattr(sys.stdout, "reconfigure"):
23
- sys.stdout.reconfigure(encoding="utf-8", errors="replace") # type: ignore[union-attr]
24
- elif hasattr(sys.stdout, "detach"):
25
- sys.stdout = _io.TextIOWrapper(sys.stdout.detach(), encoding="utf-8", errors="replace") # type: ignore[union-attr]
26
-
27
- import argparse
28
- import json
29
- import os
30
- import sys
31
- from datetime import datetime, timezone
32
- from pathlib import Path
33
-
34
- from common.paths import (
35
- get_repo_root,
36
- get_developer,
37
- get_pool_dir,
38
- get_pool_file,
39
- )
40
-
41
-
42
- # =============================================================================
43
- # Colors
44
- # =============================================================================
45
-
46
- class Colors:
47
- RED = "\033[0;31m"
48
- GREEN = "\033[0;32m"
49
- YELLOW = "\033[1;33m"
50
- BLUE = "\033[0;34m"
51
- CYAN = "\033[0;36m"
52
- NC = "\033[0m"
53
-
54
-
55
- def colored(text: str, color: str) -> str:
56
- """Apply color to text."""
57
- return f"{color}{text}{Colors.NC}"
58
-
59
-
60
- # =============================================================================
61
- # Constants
62
- # =============================================================================
63
-
64
- DEFAULT_POOLS = ["requirements", "prototypes", "implementations"]
65
-
66
- EMPTY_POOL = {"available": []}
67
-
68
-
69
- # =============================================================================
70
- # Pool I/O (atomic writes)
71
- # =============================================================================
72
-
73
- def _read_pool(pool_path: Path) -> dict:
74
- """Read pool JSON file. Auto-create if missing, backup if corrupted."""
75
- if not pool_path.is_file():
76
- return {"available": []}
77
-
78
- try:
79
- data = json.loads(pool_path.read_text(encoding="utf-8"))
80
- if not isinstance(data, dict) or "available" not in data:
81
- raise ValueError("Invalid pool schema")
82
- return data
83
- except (json.JSONDecodeError, ValueError):
84
- # Backup corrupted file
85
- bak_path = pool_path.with_suffix(".json.bak")
86
- try:
87
- pool_path.rename(bak_path)
88
- print(colored(f" Warning: Corrupted pool backed up to {bak_path.name}", Colors.YELLOW))
89
- except OSError:
90
- pass
91
- return {"available": []}
92
-
93
-
94
- def _write_pool(pool_path: Path, data: dict) -> bool:
95
- """Write pool JSON atomically (write .tmp then os.replace)."""
96
- tmp_path = pool_path.with_suffix(".json.tmp")
97
- try:
98
- pool_path.parent.mkdir(parents=True, exist_ok=True)
99
- tmp_path.write_text(
100
- json.dumps(data, indent=2, ensure_ascii=False) + "\n",
101
- encoding="utf-8",
102
- )
103
- os.replace(str(tmp_path), str(pool_path))
104
- return True
105
- except (OSError, IOError) as e:
106
- print(colored(f" Error writing pool: {e}", Colors.RED))
107
- # Clean up tmp if it exists
108
- try:
109
- tmp_path.unlink(missing_ok=True)
110
- except OSError:
111
- pass
112
- return False
113
-
114
-
115
- # =============================================================================
116
- # Subcommands
117
- # =============================================================================
118
-
119
- def cmd_init(args: argparse.Namespace) -> int:
120
- """Create pool JSON files."""
121
- repo_root = get_repo_root()
122
- pool_dir = get_pool_dir(repo_root)
123
- pool_dir.mkdir(parents=True, exist_ok=True)
124
-
125
- pool_names = args.pools if args.pools else DEFAULT_POOLS
126
-
127
- for name in pool_names:
128
- pool_path = pool_dir / f"{name}.json"
129
- if pool_path.is_file():
130
- print(colored(f" Pool '{name}' already exists, skipping", Colors.YELLOW))
131
- continue
132
- if _write_pool(pool_path, EMPTY_POOL):
133
- print(colored(f" Created pool: {name}", Colors.GREEN))
134
- else:
135
- print(colored(f" Failed to create pool: {name}", Colors.RED))
136
- return 1
137
-
138
- print(colored("Pool initialization complete.", Colors.GREEN))
139
- return 0
140
-
141
-
142
- def cmd_add(args: argparse.Namespace) -> int:
143
- """Add a deliverable to a pool."""
144
- repo_root = get_repo_root()
145
- pool_path = get_pool_file(args.pool, repo_root)
146
-
147
- data = _read_pool(pool_path)
148
-
149
- # Check for duplicate ID
150
- for item in data["available"]:
151
- if item["id"] == args.id:
152
- print(colored(f" Warning: ID '{args.id}' already exists in pool '{args.pool}', skipping", Colors.YELLOW))
153
- return 0
154
-
155
- developer = get_developer(repo_root) or "unknown"
156
-
157
- entry = {
158
- "id": args.id,
159
- "title": args.title,
160
- "path": args.path,
161
- "completed_by": developer,
162
- "completed_at": datetime.now(timezone.utc).isoformat(),
163
- "handoff_doc": args.handoff if args.handoff else f"{args.path}/HANDOFF.md",
164
- "status": "available",
165
- }
166
-
167
- data["available"].append(entry)
168
-
169
- if _write_pool(pool_path, data):
170
- print(colored(f" Added '{args.id}' to pool '{args.pool}'", Colors.GREEN))
171
- return 0
172
- return 1
173
-
174
-
175
- def cmd_list(args: argparse.Namespace) -> int:
176
- """List available items in pools."""
177
- repo_root = get_repo_root()
178
- pool_dir = get_pool_dir(repo_root)
179
-
180
- if not pool_dir.is_dir():
181
- print(colored("No pool directory found. Run 'pool.py init' first.", Colors.YELLOW))
182
- return 0
183
-
184
- pools_to_list = [args.pool] if args.pool else [
185
- p.stem for p in sorted(pool_dir.glob("*.json"))
186
- ]
187
-
188
- if not pools_to_list:
189
- print(colored("No pools found.", Colors.YELLOW))
190
- return 0
191
-
192
- for pool_name in pools_to_list:
193
- pool_path = get_pool_file(pool_name, repo_root)
194
- data = _read_pool(pool_path)
195
-
196
- available = [i for i in data["available"] if i.get("status") == "available"]
197
- consumed = [i for i in data["available"] if i.get("status") == "consumed"]
198
-
199
- print(colored(f"\n=== {pool_name} ===", Colors.CYAN))
200
- print(f" Available: {len(available)} | Consumed: {len(consumed)}")
201
-
202
- if available:
203
- print(colored(" Available items:", Colors.GREEN))
204
- for item in available:
205
- print(f" - {item['id']}: {item['title']} (by {item.get('completed_by', '?')})")
206
- if consumed:
207
- print(colored(" Consumed items:", Colors.YELLOW))
208
- for item in consumed:
209
- consumed_by = item.get("consumed_by", "?")
210
- print(f" - {item['id']}: {item['title']} (consumed by {consumed_by})")
211
-
212
- print()
213
- return 0
214
-
215
-
216
- def cmd_status(args: argparse.Namespace) -> int:
217
- """Get status of a specific item."""
218
- repo_root = get_repo_root()
219
- pool_path = get_pool_file(args.pool, repo_root)
220
- data = _read_pool(pool_path)
221
-
222
- for item in data["available"]:
223
- if item["id"] == args.id:
224
- print(json.dumps(item, indent=2, ensure_ascii=False))
225
- return 0
226
-
227
- print(colored(f" Item '{args.id}' not found in pool '{args.pool}'", Colors.RED))
228
- return 1
229
-
230
-
231
- def cmd_consume(args: argparse.Namespace) -> int:
232
- """Mark an item as consumed."""
233
- repo_root = get_repo_root()
234
- pool_path = get_pool_file(args.pool, repo_root)
235
- data = _read_pool(pool_path)
236
-
237
- for item in data["available"]:
238
- if item["id"] == args.id:
239
- if item.get("status") == "consumed":
240
- consumed_by = item.get("consumed_by", "?")
241
- print(colored(f" Item '{args.id}' already consumed by {consumed_by}", Colors.YELLOW))
242
- return 1
243
-
244
- item["status"] = "consumed"
245
- item["consumed_by"] = args.consumed_by
246
- item["consumed_at"] = datetime.now(timezone.utc).isoformat()
247
-
248
- if _write_pool(pool_path, data):
249
- print(colored(f" Marked '{args.id}' as consumed by {args.consumed_by}", Colors.GREEN))
250
- return 0
251
- return 1
252
-
253
- print(colored(f" Item '{args.id}' not found in pool '{args.pool}'", Colors.RED))
254
- return 1
255
-
256
-
257
- # =============================================================================
258
- # CLI Parser
259
- # =============================================================================
260
-
261
- def build_parser() -> argparse.ArgumentParser:
262
- """Build argument parser."""
263
- parser = argparse.ArgumentParser(
264
- description="Pool management for three-role collaboration pipeline",
265
- )
266
- subparsers = parser.add_subparsers(dest="command", help="Available commands")
267
-
268
- # init
269
- p_init = subparsers.add_parser("init", help="Create pool JSON files")
270
- p_init.add_argument("pools", nargs="*", help="Pool names (default: requirements, prototypes, implementations)")
271
-
272
- # add
273
- p_add = subparsers.add_parser("add", help="Add deliverable to pool")
274
- p_add.add_argument("pool", help="Pool name (e.g., requirements)")
275
- p_add.add_argument("id", help="Deliverable ID (e.g., user-login)")
276
- p_add.add_argument("title", help="Deliverable title")
277
- p_add.add_argument("path", help="Path to deliverable directory")
278
- p_add.add_argument("--handoff", help="Path to HANDOFF.md (default: <path>/HANDOFF.md)")
279
-
280
- # list
281
- p_list = subparsers.add_parser("list", help="List available items")
282
- p_list.add_argument("pool", nargs="?", help="Pool name (omit for all)")
283
-
284
- # status
285
- p_status = subparsers.add_parser("status", help="Get item status")
286
- p_status.add_argument("pool", help="Pool name")
287
- p_status.add_argument("id", help="Item ID")
288
-
289
- # consume
290
- p_consume = subparsers.add_parser("consume", help="Mark item as consumed")
291
- p_consume.add_argument("pool", help="Pool name")
292
- p_consume.add_argument("id", help="Item ID")
293
- p_consume.add_argument("consumed_by", help="Consumer identity (e.g., designer-bob)")
294
-
295
- return parser
296
-
297
-
298
- # =============================================================================
299
- # Main
300
- # =============================================================================
301
-
302
- def main() -> int:
303
- parser = build_parser()
304
- args = parser.parse_args()
305
-
306
- if not args.command:
307
- parser.print_help()
308
- return 1
309
-
310
- commands = {
311
- "init": cmd_init,
312
- "add": cmd_add,
313
- "list": cmd_list,
314
- "status": cmd_status,
315
- "consume": cmd_consume,
316
- }
317
-
318
- return commands[args.command](args)
319
-
320
-
321
- if __name__ == "__main__":
322
- sys.exit(main())