@jahanxu/trellis 0.5.1 → 0.5.6

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 (51) 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 +99 -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/commands/trellis/update-spec.md +3 -19
  16. package/dist/templates/claude/hooks/enforce-output-dir.py +115 -0
  17. package/dist/templates/claude/hooks/session-start.py +87 -166
  18. package/dist/templates/claude/settings.json +10 -0
  19. package/dist/templates/iflow/commands/trellis/update-spec.md +3 -19
  20. package/dist/templates/iflow/hooks/session-start.py +0 -171
  21. package/dist/templates/markdown/index.d.ts +0 -9
  22. package/dist/templates/markdown/index.d.ts.map +1 -1
  23. package/dist/templates/markdown/index.js +0 -10
  24. package/dist/templates/markdown/index.js.map +1 -1
  25. package/dist/templates/trellis/index.d.ts +9 -1
  26. package/dist/templates/trellis/index.d.ts.map +1 -1
  27. package/dist/templates/trellis/index.js +17 -2
  28. package/dist/templates/trellis/index.js.map +1 -1
  29. package/dist/templates/trellis/scripts/common/__init__.py +11 -0
  30. package/dist/templates/trellis/scripts/common/paths.py +1 -49
  31. package/dist/templates/trellis/scripts/common/roles.py +252 -0
  32. package/dist/templates/trellis/spec/roles/designer/index.md +49 -0
  33. package/dist/templates/trellis/spec/roles/frontend-impl/index.md +52 -0
  34. package/dist/templates/trellis/spec/roles/pm/index.md +44 -0
  35. package/dist/utils/template-hash.d.ts.map +1 -1
  36. package/dist/utils/template-hash.js +2 -0
  37. package/dist/utils/template-hash.js.map +1 -1
  38. package/package.json +2 -2
  39. package/dist/templates/claude/commands/trellis/pick-task.md +0 -145
  40. package/dist/templates/iflow/commands/trellis/handoff.md +0 -148
  41. package/dist/templates/iflow/commands/trellis/pick-task.md +0 -145
  42. package/dist/templates/markdown/spec/roles/designer/index.md.txt +0 -57
  43. package/dist/templates/markdown/spec/roles/designer/mock-data-standards.md.txt +0 -63
  44. package/dist/templates/markdown/spec/roles/designer/prototype-guidelines.md.txt +0 -49
  45. package/dist/templates/markdown/spec/roles/frontend-impl/api-integration.md.txt +0 -63
  46. package/dist/templates/markdown/spec/roles/frontend-impl/index.md.txt +0 -57
  47. package/dist/templates/markdown/spec/roles/frontend-impl/prototype-to-production.md.txt +0 -57
  48. package/dist/templates/markdown/spec/roles/pm/index.md.txt +0 -45
  49. package/dist/templates/markdown/spec/roles/pm/prd-template.md.txt +0 -64
  50. package/dist/templates/markdown/spec/roles/pm/requirement-checklist.md.txt +0 -43
  51. package/dist/templates/trellis/scripts/pool.py +0 -321
@@ -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,321 +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
- from datetime import datetime, timezone
31
- from pathlib import Path
32
-
33
- from common.paths import (
34
- get_repo_root,
35
- get_developer,
36
- get_pool_dir,
37
- get_pool_file,
38
- )
39
-
40
-
41
- # =============================================================================
42
- # Colors
43
- # =============================================================================
44
-
45
- class Colors:
46
- RED = "\033[0;31m"
47
- GREEN = "\033[0;32m"
48
- YELLOW = "\033[1;33m"
49
- BLUE = "\033[0;34m"
50
- CYAN = "\033[0;36m"
51
- NC = "\033[0m"
52
-
53
-
54
- def colored(text: str, color: str) -> str:
55
- """Apply color to text."""
56
- return f"{color}{text}{Colors.NC}"
57
-
58
-
59
- # =============================================================================
60
- # Constants
61
- # =============================================================================
62
-
63
- DEFAULT_POOLS = ["requirements", "prototypes", "implementations"]
64
-
65
- EMPTY_POOL = {"available": []}
66
-
67
-
68
- # =============================================================================
69
- # Pool I/O (atomic writes)
70
- # =============================================================================
71
-
72
- def _read_pool(pool_path: Path) -> dict:
73
- """Read pool JSON file. Auto-create if missing, backup if corrupted."""
74
- if not pool_path.is_file():
75
- return {"available": []}
76
-
77
- try:
78
- data = json.loads(pool_path.read_text(encoding="utf-8"))
79
- if not isinstance(data, dict) or "available" not in data:
80
- raise ValueError("Invalid pool schema")
81
- return data
82
- except (json.JSONDecodeError, ValueError):
83
- # Backup corrupted file
84
- bak_path = pool_path.with_suffix(".json.bak")
85
- try:
86
- pool_path.rename(bak_path)
87
- print(colored(f" Warning: Corrupted pool backed up to {bak_path.name}", Colors.YELLOW))
88
- except OSError:
89
- pass
90
- return {"available": []}
91
-
92
-
93
- def _write_pool(pool_path: Path, data: dict) -> bool:
94
- """Write pool JSON atomically (write .tmp then os.replace)."""
95
- tmp_path = pool_path.with_suffix(".json.tmp")
96
- try:
97
- pool_path.parent.mkdir(parents=True, exist_ok=True)
98
- tmp_path.write_text(
99
- json.dumps(data, indent=2, ensure_ascii=False) + "\n",
100
- encoding="utf-8",
101
- )
102
- os.replace(str(tmp_path), str(pool_path))
103
- return True
104
- except (OSError, IOError) as e:
105
- print(colored(f" Error writing pool: {e}", Colors.RED))
106
- # Clean up tmp if it exists
107
- try:
108
- tmp_path.unlink(missing_ok=True)
109
- except OSError:
110
- pass
111
- return False
112
-
113
-
114
- # =============================================================================
115
- # Subcommands
116
- # =============================================================================
117
-
118
- def cmd_init(args: argparse.Namespace) -> int:
119
- """Create pool JSON files."""
120
- repo_root = get_repo_root()
121
- pool_dir = get_pool_dir(repo_root)
122
- pool_dir.mkdir(parents=True, exist_ok=True)
123
-
124
- pool_names = args.pools if args.pools else DEFAULT_POOLS
125
-
126
- for name in pool_names:
127
- pool_path = pool_dir / f"{name}.json"
128
- if pool_path.is_file():
129
- print(colored(f" Pool '{name}' already exists, skipping", Colors.YELLOW))
130
- continue
131
- if _write_pool(pool_path, EMPTY_POOL):
132
- print(colored(f" Created pool: {name}", Colors.GREEN))
133
- else:
134
- print(colored(f" Failed to create pool: {name}", Colors.RED))
135
- return 1
136
-
137
- print(colored("Pool initialization complete.", Colors.GREEN))
138
- return 0
139
-
140
-
141
- def cmd_add(args: argparse.Namespace) -> int:
142
- """Add a deliverable to a pool."""
143
- repo_root = get_repo_root()
144
- pool_path = get_pool_file(args.pool, repo_root)
145
-
146
- data = _read_pool(pool_path)
147
-
148
- # Check for duplicate ID
149
- for item in data["available"]:
150
- if item["id"] == args.id:
151
- print(colored(f" Warning: ID '{args.id}' already exists in pool '{args.pool}', skipping", Colors.YELLOW))
152
- return 0
153
-
154
- developer = get_developer(repo_root) or "unknown"
155
-
156
- entry = {
157
- "id": args.id,
158
- "title": args.title,
159
- "path": args.path,
160
- "completed_by": developer,
161
- "completed_at": datetime.now(timezone.utc).isoformat(),
162
- "handoff_doc": args.handoff if args.handoff else f"{args.path}/HANDOFF.md",
163
- "status": "available",
164
- }
165
-
166
- data["available"].append(entry)
167
-
168
- if _write_pool(pool_path, data):
169
- print(colored(f" Added '{args.id}' to pool '{args.pool}'", Colors.GREEN))
170
- return 0
171
- return 1
172
-
173
-
174
- def cmd_list(args: argparse.Namespace) -> int:
175
- """List available items in pools."""
176
- repo_root = get_repo_root()
177
- pool_dir = get_pool_dir(repo_root)
178
-
179
- if not pool_dir.is_dir():
180
- print(colored("No pool directory found. Run 'pool.py init' first.", Colors.YELLOW))
181
- return 0
182
-
183
- pools_to_list = [args.pool] if args.pool else [
184
- p.stem for p in sorted(pool_dir.glob("*.json"))
185
- ]
186
-
187
- if not pools_to_list:
188
- print(colored("No pools found.", Colors.YELLOW))
189
- return 0
190
-
191
- for pool_name in pools_to_list:
192
- pool_path = get_pool_file(pool_name, repo_root)
193
- data = _read_pool(pool_path)
194
-
195
- available = [i for i in data["available"] if i.get("status") == "available"]
196
- consumed = [i for i in data["available"] if i.get("status") == "consumed"]
197
-
198
- print(colored(f"\n=== {pool_name} ===", Colors.CYAN))
199
- print(f" Available: {len(available)} | Consumed: {len(consumed)}")
200
-
201
- if available:
202
- print(colored(" Available items:", Colors.GREEN))
203
- for item in available:
204
- print(f" - {item['id']}: {item['title']} (by {item.get('completed_by', '?')})")
205
- if consumed:
206
- print(colored(" Consumed items:", Colors.YELLOW))
207
- for item in consumed:
208
- consumed_by = item.get("consumed_by", "?")
209
- print(f" - {item['id']}: {item['title']} (consumed by {consumed_by})")
210
-
211
- print()
212
- return 0
213
-
214
-
215
- def cmd_status(args: argparse.Namespace) -> int:
216
- """Get status of a specific item."""
217
- repo_root = get_repo_root()
218
- pool_path = get_pool_file(args.pool, repo_root)
219
- data = _read_pool(pool_path)
220
-
221
- for item in data["available"]:
222
- if item["id"] == args.id:
223
- print(json.dumps(item, indent=2, ensure_ascii=False))
224
- return 0
225
-
226
- print(colored(f" Item '{args.id}' not found in pool '{args.pool}'", Colors.RED))
227
- return 1
228
-
229
-
230
- def cmd_consume(args: argparse.Namespace) -> int:
231
- """Mark an item as consumed."""
232
- repo_root = get_repo_root()
233
- pool_path = get_pool_file(args.pool, repo_root)
234
- data = _read_pool(pool_path)
235
-
236
- for item in data["available"]:
237
- if item["id"] == args.id:
238
- if item.get("status") == "consumed":
239
- consumed_by = item.get("consumed_by", "?")
240
- print(colored(f" Item '{args.id}' already consumed by {consumed_by}", Colors.YELLOW))
241
- return 1
242
-
243
- item["status"] = "consumed"
244
- item["consumed_by"] = args.consumed_by
245
- item["consumed_at"] = datetime.now(timezone.utc).isoformat()
246
-
247
- if _write_pool(pool_path, data):
248
- print(colored(f" Marked '{args.id}' as consumed by {args.consumed_by}", Colors.GREEN))
249
- return 0
250
- return 1
251
-
252
- print(colored(f" Item '{args.id}' not found in pool '{args.pool}'", Colors.RED))
253
- return 1
254
-
255
-
256
- # =============================================================================
257
- # CLI Parser
258
- # =============================================================================
259
-
260
- def build_parser() -> argparse.ArgumentParser:
261
- """Build argument parser."""
262
- parser = argparse.ArgumentParser(
263
- description="Pool management for three-role collaboration pipeline",
264
- )
265
- subparsers = parser.add_subparsers(dest="command", help="Available commands")
266
-
267
- # init
268
- p_init = subparsers.add_parser("init", help="Create pool JSON files")
269
- p_init.add_argument("pools", nargs="*", help="Pool names (default: requirements, prototypes, implementations)")
270
-
271
- # add
272
- p_add = subparsers.add_parser("add", help="Add deliverable to pool")
273
- p_add.add_argument("pool", help="Pool name (e.g., requirements)")
274
- p_add.add_argument("id", help="Deliverable ID (e.g., user-login)")
275
- p_add.add_argument("title", help="Deliverable title")
276
- p_add.add_argument("path", help="Path to deliverable directory")
277
- p_add.add_argument("--handoff", help="Path to HANDOFF.md (default: <path>/HANDOFF.md)")
278
-
279
- # list
280
- p_list = subparsers.add_parser("list", help="List available items")
281
- p_list.add_argument("pool", nargs="?", help="Pool name (omit for all)")
282
-
283
- # status
284
- p_status = subparsers.add_parser("status", help="Get item status")
285
- p_status.add_argument("pool", help="Pool name")
286
- p_status.add_argument("id", help="Item ID")
287
-
288
- # consume
289
- p_consume = subparsers.add_parser("consume", help="Mark item as consumed")
290
- p_consume.add_argument("pool", help="Pool name")
291
- p_consume.add_argument("id", help="Item ID")
292
- p_consume.add_argument("consumed_by", help="Consumer identity (e.g., designer-bob)")
293
-
294
- return parser
295
-
296
-
297
- # =============================================================================
298
- # Main
299
- # =============================================================================
300
-
301
- def main() -> int:
302
- parser = build_parser()
303
- args = parser.parse_args()
304
-
305
- if not args.command:
306
- parser.print_help()
307
- return 1
308
-
309
- commands = {
310
- "init": cmd_init,
311
- "add": cmd_add,
312
- "list": cmd_list,
313
- "status": cmd_status,
314
- "consume": cmd_consume,
315
- }
316
-
317
- return commands[args.command](args)
318
-
319
-
320
- if __name__ == "__main__":
321
- sys.exit(main())