@flydocs/cli 0.6.0-alpha.20 → 0.6.0-alpha.22
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/dist/cli.js +28 -2
- package/package.json +1 -1
- package/template/.claude/CLAUDE.md +55 -59
- package/template/.claude/hooks/auto-approve.py +82 -2
- package/template/.claude/hooks/post-transition-check.py +190 -3
- package/template/.claude/hooks/prompt-submit.py +53 -12
- package/template/.claude/hooks/stop-gate.py +63 -10
- package/template/.claude/skills/flydocs-workflow/scripts/flydocs_api.py +33 -2
- package/template/.claude/skills/flydocs-workflow/scripts/issues.py +256 -7
- package/template/.claude/skills/flydocs-workflow/scripts/test_enforcement.py +225 -0
- package/template/.claude/skills/flydocs-workflow/session.md +37 -17
- package/template/.flydocs/config.json +1 -1
- package/template/.flydocs/templates/bug.md +30 -0
- package/template/.flydocs/templates/chore.md +22 -0
- package/template/.flydocs/templates/feature.md +27 -0
- package/template/.flydocs/templates/idea.md +22 -0
- package/template/.flydocs/version +1 -1
- package/template/manifest.json +1 -1
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Tests for workflow enforcement logic.
|
|
3
|
+
|
|
4
|
+
Run: python3 test_enforcement.py
|
|
5
|
+
Tests the enforcement functions in issues.py, auto-approve.py, stop-gate.py,
|
|
6
|
+
and post-transition-check.py without requiring API access.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
12
|
+
import tempfile
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from unittest.mock import patch
|
|
15
|
+
|
|
16
|
+
# Add parent dirs to path for imports
|
|
17
|
+
SCRIPT_DIR = Path(__file__).parent.resolve()
|
|
18
|
+
HOOKS_DIR = SCRIPT_DIR.parent.parent.parent / "hooks"
|
|
19
|
+
sys.path.insert(0, str(SCRIPT_DIR))
|
|
20
|
+
sys.path.insert(0, str(HOOKS_DIR))
|
|
21
|
+
|
|
22
|
+
passed = 0
|
|
23
|
+
failed = 0
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test(name: str):
|
|
27
|
+
"""Decorator to register and run a test."""
|
|
28
|
+
def decorator(fn):
|
|
29
|
+
global passed, failed
|
|
30
|
+
try:
|
|
31
|
+
fn()
|
|
32
|
+
print(f" PASS: {name}")
|
|
33
|
+
passed += 1
|
|
34
|
+
except AssertionError as e:
|
|
35
|
+
print(f" FAIL: {name} — {e}")
|
|
36
|
+
failed += 1
|
|
37
|
+
except Exception as e:
|
|
38
|
+
print(f" ERROR: {name} — {type(e).__name__}: {e}")
|
|
39
|
+
failed += 1
|
|
40
|
+
return fn
|
|
41
|
+
return decorator
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# ---------------------------------------------------------------------------
|
|
45
|
+
# issues.py enforcement tests
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
print("\n## issues.py enforcement")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@test("create rejects empty description")
|
|
52
|
+
def _():
|
|
53
|
+
"""issues.py create should fail with empty --description."""
|
|
54
|
+
import subprocess
|
|
55
|
+
result = subprocess.run(
|
|
56
|
+
[sys.executable, str(SCRIPT_DIR / "issues.py"), "create",
|
|
57
|
+
"--title", "Test", "--type", "feature", "--description", ""],
|
|
58
|
+
capture_output=True, text=True, timeout=10,
|
|
59
|
+
)
|
|
60
|
+
assert result.returncode != 0, f"Expected failure, got rc={result.returncode}"
|
|
61
|
+
assert "Description is required" in result.stderr, f"Expected error message, got: {result.stderr[:200]}"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@test("create rejects missing description")
|
|
65
|
+
def _():
|
|
66
|
+
"""issues.py create should fail without --description flag."""
|
|
67
|
+
import subprocess
|
|
68
|
+
result = subprocess.run(
|
|
69
|
+
[sys.executable, str(SCRIPT_DIR / "issues.py"), "create",
|
|
70
|
+
"--title", "Test", "--type", "feature"],
|
|
71
|
+
capture_output=True, text=True, timeout=10,
|
|
72
|
+
)
|
|
73
|
+
assert result.returncode != 0, f"Expected failure, got rc={result.returncode}"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@test("create allows --triage without description")
|
|
77
|
+
def _():
|
|
78
|
+
"""issues.py create with --triage should not fail on empty description."""
|
|
79
|
+
# This would succeed only if we have API access, so just check the
|
|
80
|
+
# enforcement bypass by testing the error message doesn't say "Description is required"
|
|
81
|
+
import subprocess
|
|
82
|
+
result = subprocess.run(
|
|
83
|
+
[sys.executable, str(SCRIPT_DIR / "issues.py"), "create",
|
|
84
|
+
"--title", "Quick test", "--type", "bug", "--triage"],
|
|
85
|
+
capture_output=True, text=True, timeout=10,
|
|
86
|
+
)
|
|
87
|
+
# It may fail for other reasons (no API), but NOT for missing description
|
|
88
|
+
assert "Description is required" not in result.stderr, \
|
|
89
|
+
f"--triage should bypass description check, got: {result.stderr[:200]}"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@test("transition rejects empty comment")
|
|
93
|
+
def _():
|
|
94
|
+
"""issues.py transition should reject whitespace-only comment."""
|
|
95
|
+
import subprocess
|
|
96
|
+
result = subprocess.run(
|
|
97
|
+
[sys.executable, str(SCRIPT_DIR / "issues.py"), "transition",
|
|
98
|
+
"FLY-999", "REVIEW", " "],
|
|
99
|
+
capture_output=True, text=True, timeout=10,
|
|
100
|
+
)
|
|
101
|
+
assert result.returncode != 0, f"Expected failure, got rc={result.returncode}"
|
|
102
|
+
assert "comment cannot be empty" in result.stderr, f"Got: {result.stderr[:200]}"
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# ---------------------------------------------------------------------------
|
|
106
|
+
# auto-approve.py enforcement tests
|
|
107
|
+
# ---------------------------------------------------------------------------
|
|
108
|
+
|
|
109
|
+
print("\n## auto-approve.py enforcement")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@test("should_approve matches workflow scripts")
|
|
113
|
+
def _():
|
|
114
|
+
# Import the function directly
|
|
115
|
+
sys.path.insert(0, str(HOOKS_DIR))
|
|
116
|
+
# We need to import carefully since it's a hook script
|
|
117
|
+
import importlib.util
|
|
118
|
+
spec = importlib.util.spec_from_file_location("auto_approve", HOOKS_DIR / "auto-approve.py")
|
|
119
|
+
mod = importlib.util.module_from_spec(spec)
|
|
120
|
+
spec.loader.exec_module(mod)
|
|
121
|
+
|
|
122
|
+
assert mod.should_approve("python3 .claude/skills/flydocs-workflow/scripts/issues.py create --title test")
|
|
123
|
+
assert mod.should_approve("python3 .claude/skills/flydocs-workflow/scripts/workspace.py validate")
|
|
124
|
+
assert not mod.should_approve("python3 malicious.py")
|
|
125
|
+
assert not mod.should_approve("rm -rf /")
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@test("validate_create_args warns on missing description")
|
|
129
|
+
def _():
|
|
130
|
+
import importlib.util
|
|
131
|
+
spec = importlib.util.spec_from_file_location("auto_approve", HOOKS_DIR / "auto-approve.py")
|
|
132
|
+
mod = importlib.util.module_from_spec(spec)
|
|
133
|
+
spec.loader.exec_module(mod)
|
|
134
|
+
|
|
135
|
+
warnings = mod.validate_create_args(
|
|
136
|
+
'python3 .claude/skills/flydocs-workflow/scripts/issues.py create --title "test" --type feature'
|
|
137
|
+
)
|
|
138
|
+
assert len(warnings) > 0, "Should warn about missing --description"
|
|
139
|
+
assert any("description" in w.lower() for w in warnings)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@test("validate_create_args no warning when description present")
|
|
143
|
+
def _():
|
|
144
|
+
import importlib.util
|
|
145
|
+
spec = importlib.util.spec_from_file_location("auto_approve", HOOKS_DIR / "auto-approve.py")
|
|
146
|
+
mod = importlib.util.module_from_spec(spec)
|
|
147
|
+
spec.loader.exec_module(mod)
|
|
148
|
+
|
|
149
|
+
warnings = mod.validate_create_args(
|
|
150
|
+
'python3 .claude/skills/flydocs-workflow/scripts/issues.py create --title "test" --type feature --description "Full description here with enough content"'
|
|
151
|
+
)
|
|
152
|
+
desc_warnings = [w for w in warnings if "Missing --description" in w]
|
|
153
|
+
assert len(desc_warnings) == 0, f"Should not warn when description present, got: {warnings}"
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@test("get_transition_comment_hint returns template for known status")
|
|
157
|
+
def _():
|
|
158
|
+
import importlib.util
|
|
159
|
+
spec = importlib.util.spec_from_file_location("auto_approve", HOOKS_DIR / "auto-approve.py")
|
|
160
|
+
mod = importlib.util.module_from_spec(spec)
|
|
161
|
+
spec.loader.exec_module(mod)
|
|
162
|
+
|
|
163
|
+
hint = mod.get_transition_comment_hint("issues.py transition FLY-123 IMPLEMENTING")
|
|
164
|
+
assert hint is not None, "Should return hint for IMPLEMENTING"
|
|
165
|
+
assert "Starting implementation" in hint
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@test("get_transition_comment_hint returns None for non-transition")
|
|
169
|
+
def _():
|
|
170
|
+
import importlib.util
|
|
171
|
+
spec = importlib.util.spec_from_file_location("auto_approve", HOOKS_DIR / "auto-approve.py")
|
|
172
|
+
mod = importlib.util.module_from_spec(spec)
|
|
173
|
+
spec.loader.exec_module(mod)
|
|
174
|
+
|
|
175
|
+
hint = mod.get_transition_comment_hint("issues.py list --status BACKLOG")
|
|
176
|
+
assert hint is None, "Should return None for non-transition commands"
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
# ---------------------------------------------------------------------------
|
|
180
|
+
# Transition validation tests
|
|
181
|
+
# ---------------------------------------------------------------------------
|
|
182
|
+
|
|
183
|
+
print("\n## Transition validation")
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@test("VALID_TRANSITIONS allows IMPLEMENTING -> REVIEW")
|
|
187
|
+
def _():
|
|
188
|
+
# Test the transition map directly from issues.py
|
|
189
|
+
import importlib.util
|
|
190
|
+
spec = importlib.util.spec_from_file_location("issues", SCRIPT_DIR / "issues.py")
|
|
191
|
+
mod = importlib.util.module_from_spec(spec)
|
|
192
|
+
spec.loader.exec_module(mod)
|
|
193
|
+
|
|
194
|
+
assert "REVIEW" in mod.VALID_TRANSITIONS["IMPLEMENTING"]
|
|
195
|
+
assert "BLOCKED" in mod.VALID_TRANSITIONS["IMPLEMENTING"]
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@test("VALID_TRANSITIONS blocks BACKLOG -> REVIEW")
|
|
199
|
+
def _():
|
|
200
|
+
import importlib.util
|
|
201
|
+
spec = importlib.util.spec_from_file_location("issues", SCRIPT_DIR / "issues.py")
|
|
202
|
+
mod = importlib.util.module_from_spec(spec)
|
|
203
|
+
spec.loader.exec_module(mod)
|
|
204
|
+
|
|
205
|
+
assert "REVIEW" not in mod.VALID_TRANSITIONS["BACKLOG"]
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@test("VALID_TRANSITIONS blocks READY -> REVIEW")
|
|
209
|
+
def _():
|
|
210
|
+
import importlib.util
|
|
211
|
+
spec = importlib.util.spec_from_file_location("issues", SCRIPT_DIR / "issues.py")
|
|
212
|
+
mod = importlib.util.module_from_spec(spec)
|
|
213
|
+
spec.loader.exec_module(mod)
|
|
214
|
+
|
|
215
|
+
assert "REVIEW" not in mod.VALID_TRANSITIONS["READY"]
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
# ---------------------------------------------------------------------------
|
|
219
|
+
# Summary
|
|
220
|
+
# ---------------------------------------------------------------------------
|
|
221
|
+
|
|
222
|
+
print(f"\n{'='*50}")
|
|
223
|
+
print(f"Results: {passed} passed, {failed} failed, {passed + failed} total")
|
|
224
|
+
if failed > 0:
|
|
225
|
+
sys.exit(1)
|
|
@@ -4,7 +4,17 @@
|
|
|
4
4
|
|
|
5
5
|
When a conversation begins or the user returns after a gap:
|
|
6
6
|
|
|
7
|
-
1. **
|
|
7
|
+
1. **Verify config completeness** — Read `.flydocs/config.json` and check:
|
|
8
|
+
- `workspace.activeProjects` is non-empty (cloud tier) — warn if missing
|
|
9
|
+
- `workspace.defaultMilestoneId` is set — warn if null
|
|
10
|
+
- `issueLabels.category` has non-null values — warn if unconfigured
|
|
11
|
+
These are informational warnings, not blocking. Example:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
Config check: activeProjects OK | milestone OK | labels: 2 of 4 configured (idea, chore missing)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
2. **Query graph for issue context** (if an issue ID is known from the prompt or last session) —
|
|
8
18
|
|
|
9
19
|
```
|
|
10
20
|
python3 .claude/skills/flydocs-workflow/scripts/graph_query.py --node <issue-id> --depth 2
|
|
@@ -13,28 +23,28 @@ When a conversation begins or the user returns after a gap:
|
|
|
13
23
|
This surfaces related decisions, blocking relationships, and recent session activity.
|
|
14
24
|
Skip silently if the script is not installed or the graph has not been built.
|
|
15
25
|
|
|
16
|
-
|
|
26
|
+
3. **Check workspace context** — If `flydocs/context/service.json` exists, note the
|
|
17
27
|
repo's topology and dependencies. For multi-repo workspaces (topology type 3 or 4),
|
|
18
28
|
briefly mention which sibling repos exist and any active cross-repo dependencies.
|
|
19
29
|
This helps the user orient when switching between repos.
|
|
20
30
|
|
|
21
|
-
|
|
31
|
+
4. **Fetch all product issues in one call** — Run `issues.py list --active --limit 100`.
|
|
22
32
|
This is auto-scoped by the product cascade: `activeProjects` → `product.labelIds` → team-wide.
|
|
23
33
|
Issues outside the product scope are never shown.
|
|
24
34
|
|
|
25
|
-
|
|
35
|
+
5. **Identify the active project** — Read `workspace.activeProjects` from config.
|
|
26
36
|
Separate issues into two buckets:
|
|
27
37
|
- **Active project issues** — issues whose `projectId` matches an active project
|
|
28
38
|
- **Other product issues** — issues in the product scope but in a different project
|
|
29
39
|
|
|
30
|
-
|
|
40
|
+
6. **Group active project issues by milestone** — Use the `milestone` and `milestoneSortOrder`
|
|
31
41
|
fields returned by the script. Sort milestones by `milestoneSortOrder` (lowest first = earliest).
|
|
32
42
|
Within each milestone, group by status.
|
|
33
43
|
|
|
34
|
-
|
|
44
|
+
7. **Identify the current milestone** — The first milestone (by sort order) that still has
|
|
35
45
|
open issues. This is where work should focus.
|
|
36
46
|
|
|
37
|
-
|
|
47
|
+
8. **Present the dashboard:**
|
|
38
48
|
|
|
39
49
|
```
|
|
40
50
|
Welcome back! [Product Name] status:
|
|
@@ -57,21 +67,21 @@ When a conversation begins or the user returns after a gap:
|
|
|
57
67
|
Suggested starting point: [ISSUE-ID] — [reason: highest priority in current milestone / unblocks others / due soon]
|
|
58
68
|
```
|
|
59
69
|
|
|
60
|
-
|
|
70
|
+
9. **Suggest where to start** — Use this priority cascade:
|
|
61
71
|
- Blocked issues that need unblocking (always surface first)
|
|
62
72
|
- In-progress issues (continue existing work)
|
|
63
73
|
- Due date approaching (within 7 days)
|
|
64
74
|
- Highest priority in the current milestone
|
|
65
75
|
- Issues that unblock downstream milestones
|
|
66
76
|
|
|
67
|
-
|
|
68
|
-
|
|
77
|
+
10. **Surface other product issues briefly** — If there are issues in the product scope
|
|
78
|
+
but outside the active project, mention the count with a one-line summary:
|
|
69
79
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
80
|
+
```
|
|
81
|
+
Also in [Product Name]: [N] issues across other projects (use --all to see)
|
|
82
|
+
```
|
|
73
83
|
|
|
74
|
-
|
|
84
|
+
11. **Check for stale issues** — Flag issues exceeding staleness thresholds (see below).
|
|
75
85
|
|
|
76
86
|
**Important:** Do NOT make separate API calls per status or per milestone. One call returns
|
|
77
87
|
all issues with their status and milestone fields — group them in your response.
|
|
@@ -109,7 +119,17 @@ When the user indicates they're done for the session:
|
|
|
109
119
|
Populate from the session data gathered in step 1. Use the Write tool or a script — the file
|
|
110
120
|
must exist on disk so the prompt hook can read it on the next session start.
|
|
111
121
|
|
|
112
|
-
7. **
|
|
122
|
+
7. **Audit session issues** — Run `issues.py audit --limit 20` to check all recently
|
|
123
|
+
touched issues for compliance. Report findings as part of the wrap summary:
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
Session audit: 5 issues checked, 1 finding
|
|
127
|
+
- FLY-484: missing_description
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
This is informational — don't block the wrap, just surface gaps.
|
|
131
|
+
|
|
132
|
+
8. **Record session in context graph** — Call `graph_session.py` with summary and issues worked on:
|
|
113
133
|
```
|
|
114
134
|
python3 .claude/skills/flydocs-workflow/scripts/graph_session.py \
|
|
115
135
|
--summary "Brief summary of session outcomes" \
|
|
@@ -117,8 +137,8 @@ When the user indicates they're done for the session:
|
|
|
117
137
|
[--decision NNN]
|
|
118
138
|
```
|
|
119
139
|
This creates a session node for cross-session continuity. Skip silently if the script is not installed.
|
|
120
|
-
|
|
121
|
-
|
|
140
|
+
9. **Verify** — Confirm the project update was posted (update ID returned).
|
|
141
|
+
10. **Ask about uncommitted changes** — If git shows uncommitted work, offer to commit.
|
|
122
142
|
|
|
123
143
|
Do not just summarize in chat. Actually post the update. Do not skip if the user seems in a hurry.
|
|
124
144
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<!-- AGENT: Use this template when creating bug issues. Fill all [bracketed] sections. -->
|
|
2
|
+
|
|
3
|
+
## What
|
|
4
|
+
|
|
5
|
+
[One sentence describing the bug]
|
|
6
|
+
|
|
7
|
+
## Expected Behavior
|
|
8
|
+
|
|
9
|
+
[What should happen]
|
|
10
|
+
|
|
11
|
+
## Actual Behavior
|
|
12
|
+
|
|
13
|
+
[What actually happens]
|
|
14
|
+
|
|
15
|
+
## Steps to Reproduce
|
|
16
|
+
|
|
17
|
+
1. [Step 1]
|
|
18
|
+
2. [Step 2]
|
|
19
|
+
3. [Step 3]
|
|
20
|
+
|
|
21
|
+
## Acceptance Criteria
|
|
22
|
+
|
|
23
|
+
- [ ] Bug is fixed and original behavior restored
|
|
24
|
+
- [ ] [Additional verification criteria]
|
|
25
|
+
|
|
26
|
+
## Context
|
|
27
|
+
|
|
28
|
+
- **Frequency:** [Always / Sometimes / Rare]
|
|
29
|
+
- **Impact:** [Blocking / Degraded / Cosmetic]
|
|
30
|
+
- **Environment:** [Where it occurs — browser, OS, tier]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!-- AGENT: Use this template when creating chore issues. Fill all [bracketed] sections. -->
|
|
2
|
+
|
|
3
|
+
## What
|
|
4
|
+
|
|
5
|
+
[One sentence describing the task]
|
|
6
|
+
|
|
7
|
+
## Why
|
|
8
|
+
|
|
9
|
+
[Why this maintenance/cleanup is needed now]
|
|
10
|
+
|
|
11
|
+
## Acceptance Criteria
|
|
12
|
+
|
|
13
|
+
- [ ] [Specific, testable criterion 1]
|
|
14
|
+
- [ ] [Specific, testable criterion 2]
|
|
15
|
+
|
|
16
|
+
## Scope
|
|
17
|
+
|
|
18
|
+
[What's included and what's explicitly excluded]
|
|
19
|
+
|
|
20
|
+
## Technical Notes
|
|
21
|
+
|
|
22
|
+
[Approach, risks, or migration steps — remove if not applicable]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<!-- AGENT: Use this template when creating feature issues. Fill all [bracketed] sections. -->
|
|
2
|
+
|
|
3
|
+
## What
|
|
4
|
+
|
|
5
|
+
[One sentence describing the feature]
|
|
6
|
+
|
|
7
|
+
## Why
|
|
8
|
+
|
|
9
|
+
[Problem this solves or opportunity it creates]
|
|
10
|
+
|
|
11
|
+
## User Story
|
|
12
|
+
|
|
13
|
+
As a [role], I want to [action] so that [benefit].
|
|
14
|
+
|
|
15
|
+
## Acceptance Criteria
|
|
16
|
+
|
|
17
|
+
- [ ] [Specific, testable criterion 1]
|
|
18
|
+
- [ ] [Specific, testable criterion 2]
|
|
19
|
+
- [ ] [Specific, testable criterion 3]
|
|
20
|
+
|
|
21
|
+
## Technical Notes
|
|
22
|
+
|
|
23
|
+
[Implementation approach, constraints, or dependencies — remove if not applicable]
|
|
24
|
+
|
|
25
|
+
## Out of Scope
|
|
26
|
+
|
|
27
|
+
[Explicitly list what this does NOT include — remove if not applicable]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!-- AGENT: Use this template when creating idea issues. Fill all [bracketed] sections. -->
|
|
2
|
+
|
|
3
|
+
## What
|
|
4
|
+
|
|
5
|
+
[One sentence describing the idea]
|
|
6
|
+
|
|
7
|
+
## Why
|
|
8
|
+
|
|
9
|
+
[Problem or opportunity this addresses]
|
|
10
|
+
|
|
11
|
+
## Rough Shape
|
|
12
|
+
|
|
13
|
+
[Initial thinking on approach — does not need to be detailed]
|
|
14
|
+
|
|
15
|
+
## Open Questions
|
|
16
|
+
|
|
17
|
+
- [Question 1]
|
|
18
|
+
- [Question 2]
|
|
19
|
+
|
|
20
|
+
## Next Steps
|
|
21
|
+
|
|
22
|
+
- [ ] [First step to explore or validate this idea]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.6.0-alpha.
|
|
1
|
+
0.6.0-alpha.22
|
package/template/manifest.json
CHANGED