@jenga-ai/agent 1.3.0 → 2.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/README.md +97 -92
- package/agents/developer.md +9 -8
- package/agents/scrum-master.md +57 -23
- package/agents/tester.md +51 -5
- package/hooks/on_session_end.sh +13 -1
- package/lib/generate-agent-context.js +18 -1
- package/lib/generate-copilot-instructions.js +18 -1
- package/lib/generate-skill-allow-list.js +191 -0
- package/lib/skill-allow-list.json +43 -0
- package/package.json +18 -4
- package/scripts/apply-j-prefix.sh +230 -0
- package/scripts/consume-context-digest.sh +103 -0
- package/scripts/postinstall.js +25 -0
- package/scripts/sweep-stale-context-digests.sh +132 -0
- package/scripts/validate-board.sh +5 -0
- package/scripts/write-context-digest.sh +230 -0
- package/skills/brainstorm/SKILL.md +1 -1
- package/skills/btw/SKILL.md +1 -1
- package/skills/clearify/SKILL.md +1 -1
- package/skills/close-story/SKILL.md +78 -6
- package/skills/close-story/scripts/check-privatized.sh +345 -0
- package/skills/commit/SKILL.md +1 -1
- package/skills/continue/SKILL.md +1 -1
- package/skills/deep-dive/SKILL.md +1 -1
- package/skills/dev-done/SKILL.md +1 -1
- package/skills/distribute/SKILL.md +1 -1
- package/skills/do/SKILL.md +100 -10
- package/skills/doc/README.md +155 -0
- package/skills/doc/SKILL.md +43 -13
- package/skills/doc/authoring-notes.md +72 -0
- package/skills/doc/scripts/resolve_last_update.py +149 -0
- package/skills/doc-sync/SKILL.md +1 -1
- package/skills/dooo/SKILL.md +1 -1
- package/skills/error/SKILL.md +1 -1
- package/skills/evaluate/SKILL.md +1 -1
- package/skills/examplify/SKILL.md +1 -1
- package/skills/help/SKILL.md +1 -1
- package/skills/idea/SKILL.md +1 -1
- package/skills/improve/SKILL.md +1 -1
- package/skills/init/SKILL.md +1 -1
- package/skills/init/assets/scope-thresholds_template.json +3 -3
- package/skills/j-init/SKILL.md +168 -0
- package/skills/j-init/assets/.gitignore_template +15 -0
- package/skills/j-init/assets/PROJECT_SUMMARY_template.md +13 -0
- package/skills/j-init/assets/directory_structure.txt +14 -0
- package/skills/j-init/assets/scope-thresholds_template.json +7 -0
- package/skills/j-init/assets/strategy_stub_template.md +38 -0
- package/skills/j-init/assets/test-config_template.json +4 -0
- package/skills/j-init/assets/workflow_template.json +30 -0
- package/skills/j-init/scripts/apply-project-visibility.sh +176 -0
- package/skills/j-init/scripts/detect-existing-codebase.sh +166 -0
- package/skills/j-init/scripts/init.sh +116 -0
- package/skills/jbp/SKILL.md +1 -1
- package/skills/jenga/SKILL.md +1 -1
- package/skills/jenga/scripts/render-confirmation.sh +55 -18
- package/skills/jenga-permission-level/SKILL.md +1 -1
- package/skills/lgtm/SKILL.md +1 -1
- package/skills/pi-plan/SKILL.md +1 -1
- package/skills/proceed/SKILL.md +1 -1
- package/skills/publish/SKILL.md +1 -1
- package/skills/publish/adapters/npm-ci.md +26 -4
- package/skills/publish/scripts/npm_ci_pipeline.sh +21 -1
- package/skills/reconcile/SKILL.md +1 -1
- package/skills/reconcile-origin/SKILL.md +1 -1
- package/skills/redo/SKILL.md +1 -1
- package/skills/skillify/SKILL.md +1 -1
- package/skills/spinoff/SKILL.md +1 -1
- package/skills/status/SKILL.md +1 -1
- package/skills/todo/SKILL.md +40 -3
- package/skills/todo/scripts/add_trivial_task.sh +216 -0
- package/skills/todo/scripts/update_story_tasks.py +87 -0
- package/skills/uncharted/SKILL.md +1 -1
- package/skills/wtf/SKILL.md +1 -1
- package/templates/SCRUM_BOARD_SCHEMA.md +33 -2
- package/templates/agent-context.md.tpl +32 -9
- package/templates/copilot-instructions.md.tpl +25 -8
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# `/doc` Authoring and Usage Guide
|
|
2
|
+
|
|
3
|
+
`/doc` generates or regenerates a complete Markdown document for a resolved target path. The skill owns the entire target file: it resolves the documentation objective first, gathers evidence, reads any existing target for still-valid maintainer intent, then writes a full replacement document.
|
|
4
|
+
|
|
5
|
+
## Basic Usage
|
|
6
|
+
|
|
7
|
+
### Default target
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
/doc
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
When no target is provided, `/doc` resolves the default target from `skills/doc/assets/path-objectives.yaml`. Today that default is `README.md`.
|
|
14
|
+
|
|
15
|
+
Expected flow:
|
|
16
|
+
1. Resolve `README.md`
|
|
17
|
+
2. Match the `project overview` objective
|
|
18
|
+
3. Gather project evidence
|
|
19
|
+
4. Regenerate the full `README.md`
|
|
20
|
+
|
|
21
|
+
### Custom target
|
|
22
|
+
|
|
23
|
+
Canonical custom-target form:
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
/doc docs/API.md
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Accepted convenience form:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
/doc update: docs/API.md
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Both forms resolve `docs/API.md`, then apply the matching rule from `skills/doc/assets/path-objectives.yaml`.
|
|
36
|
+
|
|
37
|
+
## Target Resolution and Objective Rules
|
|
38
|
+
|
|
39
|
+
`skills/doc/assets/path-objectives.yaml` is the source of truth for:
|
|
40
|
+
- the default target
|
|
41
|
+
- known target paths
|
|
42
|
+
- each path's documentation objective
|
|
43
|
+
- required and optional sections for known targets
|
|
44
|
+
|
|
45
|
+
Known targets bypass ambiguity. Unknown targets must stop for clarification.
|
|
46
|
+
|
|
47
|
+
## Evidence Sources and Precedence
|
|
48
|
+
|
|
49
|
+
`/doc` builds a synthesis context before writing. When sources disagree, use this precedence order:
|
|
50
|
+
1. explicit user instruction
|
|
51
|
+
2. scrum-board context
|
|
52
|
+
3. codebase evidence
|
|
53
|
+
4. git history
|
|
54
|
+
|
|
55
|
+
The synthesis context contract currently includes:
|
|
56
|
+
- `target_path`
|
|
57
|
+
- `objective`
|
|
58
|
+
- `project_name`
|
|
59
|
+
- `project_description`
|
|
60
|
+
- `features`
|
|
61
|
+
- `getting_started`
|
|
62
|
+
- `board_items`
|
|
63
|
+
- `conflicts_resolved`
|
|
64
|
+
- `sources_used`
|
|
65
|
+
- `existing_intent`
|
|
66
|
+
|
|
67
|
+
Use stronger sources to break ties. Do not let weaker evidence overwrite explicit user direction.
|
|
68
|
+
|
|
69
|
+
## `last_update` Provenance
|
|
70
|
+
|
|
71
|
+
`last_update` is the documentation provenance field described by Epic E24's provenance work. It is intended to capture the most recent completed board item that materially updated the target document.
|
|
72
|
+
|
|
73
|
+
### Expected source of truth
|
|
74
|
+
|
|
75
|
+
The provenance lookup relies on scrum-board `docs: [...]` annotations that point at repo-relative documentation targets such as:
|
|
76
|
+
- `README.md`
|
|
77
|
+
- `docs/API.md`
|
|
78
|
+
|
|
79
|
+
Board authors should add `docs` annotations to stories or tasks whenever implementation work changes a specific document or should be reflected in that document later.
|
|
80
|
+
|
|
81
|
+
### How provenance is expected to resolve
|
|
82
|
+
|
|
83
|
+
1. Look for completed board items whose `docs` list includes the target path exactly.
|
|
84
|
+
2. Prefer the most recent qualifying item.
|
|
85
|
+
3. Use that item as the basis for the document's `last_update` value.
|
|
86
|
+
|
|
87
|
+
### Fallback behavior
|
|
88
|
+
|
|
89
|
+
If provenance cannot be resolved, `/doc` should follow the fallback defined by E24_S05:
|
|
90
|
+
- omit `last_update`, or
|
|
91
|
+
- mark it as `unknown`
|
|
92
|
+
|
|
93
|
+
Typical failure modes:
|
|
94
|
+
- the relevant board item never declared `docs: [...]`
|
|
95
|
+
- the target path in the board item does not exactly match the generated path
|
|
96
|
+
- the board item exists but is not yet in a completed/passed state
|
|
97
|
+
|
|
98
|
+
## Ambiguity Gate
|
|
99
|
+
|
|
100
|
+
If the resolved target path is not present in `skills/doc/assets/path-objectives.yaml`, `/doc` must not guess.
|
|
101
|
+
|
|
102
|
+
It should stop and ask exactly:
|
|
103
|
+
|
|
104
|
+
```text
|
|
105
|
+
What should <target_path> document? Please describe the objective.
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Example
|
|
109
|
+
|
|
110
|
+
```text
|
|
111
|
+
/doc update: docs/UNKNOWN.md
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Expected behavior:
|
|
115
|
+
- do not generate a file yet
|
|
116
|
+
- do not invent a target objective
|
|
117
|
+
- ask the user what `docs/UNKNOWN.md` is meant to document
|
|
118
|
+
|
|
119
|
+
Once the user clarifies the objective, surface the resolved contract and continue.
|
|
120
|
+
|
|
121
|
+
## Target-Specific Notes
|
|
122
|
+
|
|
123
|
+
### `README.md`
|
|
124
|
+
Use `/doc` with no arguments when you want to regenerate the project overview. The generated file should center on:
|
|
125
|
+
- project description
|
|
126
|
+
- getting started steps
|
|
127
|
+
- examples only when they are strongly supported by evidence
|
|
128
|
+
|
|
129
|
+
### `docs/API.md`
|
|
130
|
+
Use `/doc docs/API.md` (or `/doc update: docs/API.md`) when you want an API reference document. The generated file should remain grounded in known interfaces, endpoints, parameters, return values, and errors.
|
|
131
|
+
|
|
132
|
+
## Tips for Board Authors
|
|
133
|
+
|
|
134
|
+
Add `docs` annotations when board work affects documentation scope or provenance. Good examples:
|
|
135
|
+
|
|
136
|
+
```yaml
|
|
137
|
+
docs:
|
|
138
|
+
- README.md
|
|
139
|
+
- docs/API.md
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Use annotations when:
|
|
143
|
+
- a task introduces or changes user-visible behavior that belongs in README
|
|
144
|
+
- a story adds or changes an endpoint, command, interface, or workflow doc
|
|
145
|
+
- you want `/doc` provenance to trace the work back to the board reliably
|
|
146
|
+
|
|
147
|
+
Avoid annotations when the work has no documentation impact.
|
|
148
|
+
|
|
149
|
+
## Maintainer Checklist
|
|
150
|
+
|
|
151
|
+
Before relying on `/doc`, confirm that:
|
|
152
|
+
1. the target path exists in `skills/doc/assets/path-objectives.yaml`, or you are prepared to answer the ambiguity prompt
|
|
153
|
+
2. relevant board items include accurate `docs: [...]` annotations
|
|
154
|
+
3. higher-priority evidence sources are up to date
|
|
155
|
+
4. any existing target file content that should survive regeneration is genuinely still valid
|
package/skills/doc/SKILL.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: doc
|
|
2
|
+
name: j:doc
|
|
3
3
|
description: Generate or update a documentation file by resolving a target path to a clear documentation objective before writing.
|
|
4
4
|
metadata:
|
|
5
5
|
prefered_agent: developer
|
|
@@ -12,6 +12,7 @@ keywords:
|
|
|
12
12
|
examples:
|
|
13
13
|
- "/doc"
|
|
14
14
|
- "/doc docs/API.md"
|
|
15
|
+
- "/doc update: docs/API.md"
|
|
15
16
|
- "generate documentation for the CLI"
|
|
16
17
|
- "update the contributing guide"
|
|
17
18
|
---
|
|
@@ -25,6 +26,7 @@ examples:
|
|
|
25
26
|
```
|
|
26
27
|
|
|
27
28
|
- If `target-path` is omitted, default to `README.md`.
|
|
29
|
+
- If the remainder starts with `update:`, strip that prefix, then trim again before resolving the target path.
|
|
28
30
|
- If `target-path` is provided, use it exactly as written after `/doc`.
|
|
29
31
|
- Do not guess additional arguments or rewrite the requested path.
|
|
30
32
|
|
|
@@ -32,6 +34,8 @@ examples:
|
|
|
32
34
|
|
|
33
35
|
Load `skills/doc/assets/path-objectives.yaml` before resolving the documentation objective. Treat it as the source of truth for the `default_target`, known target paths, and their structural requirements.
|
|
34
36
|
|
|
37
|
+
For extended usage guidance, provenance notes, and board-author tips, see `skills/doc/README.md`.
|
|
38
|
+
|
|
35
39
|
## Synthesis Context Contract
|
|
36
40
|
|
|
37
41
|
After target resolution, all generation must operate on a synthesis context object. E24_S03 is responsible for producing the final implementation, but this story defines the field contract that downstream generation must consume.
|
|
@@ -47,6 +51,7 @@ board_items: []
|
|
|
47
51
|
conflicts_resolved: []
|
|
48
52
|
sources_used: []
|
|
49
53
|
existing_intent: null
|
|
54
|
+
last_update: null
|
|
50
55
|
```
|
|
51
56
|
|
|
52
57
|
Required fields from E24_S03:
|
|
@@ -60,8 +65,9 @@ Required fields from E24_S03:
|
|
|
60
65
|
- `conflicts_resolved`
|
|
61
66
|
- `sources_used`
|
|
62
67
|
- `existing_intent`
|
|
68
|
+
- `last_update`
|
|
63
69
|
|
|
64
|
-
If the shared collector from E24_S03 is not yet merged, construct a temporary context with the same field names so later steps remain compatible.
|
|
70
|
+
If the shared collector from E24_S03 is not yet merged, construct a temporary context with the same field names so later steps remain compatible. `last_update` is resolved during the provenance step below.
|
|
65
71
|
|
|
66
72
|
## Instructions
|
|
67
73
|
|
|
@@ -70,12 +76,14 @@ If the shared collector from E24_S03 is not yet merged, construct a temporary co
|
|
|
70
76
|
1. Read `default_target` from `skills/doc/assets/path-objectives.yaml`. If it is missing, fall back to `README.md`.
|
|
71
77
|
2. Remove the `/doc` command token from the invocation.
|
|
72
78
|
3. Trim the remaining text.
|
|
73
|
-
4. If
|
|
74
|
-
5.
|
|
79
|
+
4. If the trimmed remainder starts with the exact prefix `update:`, remove that prefix and trim the remainder again.
|
|
80
|
+
5. If nothing remains, set `target_path` to `default_target`.
|
|
81
|
+
6. Otherwise, set `target_path` to the trimmed remainder.
|
|
75
82
|
|
|
76
83
|
Examples:
|
|
77
84
|
- `/doc` → `target_path = README.md`
|
|
78
85
|
- `/doc docs/API.md` → `target_path = docs/API.md`
|
|
86
|
+
- `/doc update: docs/API.md` → `target_path = docs/API.md`
|
|
79
87
|
- `/doc docs/CLI.md` → `target_path = docs/CLI.md`
|
|
80
88
|
|
|
81
89
|
### 2. Resolve the objective from the rule table
|
|
@@ -135,15 +143,37 @@ If `target_path` is not present in `skills/doc/assets/path-objectives.yaml`:
|
|
|
135
143
|
|
|
136
144
|
If the target file does not exist, keep `existing_intent = null`.
|
|
137
145
|
|
|
138
|
-
### 7.
|
|
146
|
+
### 7. Resolve `last_update` provenance before writing
|
|
147
|
+
|
|
148
|
+
1. Run `python3 skills/doc/scripts/resolve_last_update.py <target_path>` from the repository root.
|
|
149
|
+
2. The resolver must scan `project/board/epics/`, `project/board/stories/`, and `project/board/tasks/`.
|
|
150
|
+
3. Treat a board item as provenance only when all of the following are true:
|
|
151
|
+
- `status` is exactly `Done` or `Passed`
|
|
152
|
+
- `docs` is a YAML list that contains `target_path` as an exact repo-relative string match
|
|
153
|
+
- `date_completed` is present and parses as `YYYY-MM-DD`
|
|
154
|
+
4. If multiple board items match, select the most recent `date_completed` and store it in `synthesis_context.last_update`.
|
|
155
|
+
5. If no matching provenance is found, set `synthesis_context.last_update = "unknown"`. This is the required fallback because it keeps the frontmatter shape stable while making the missing provenance explicit.
|
|
156
|
+
6. Ignore board items in any other status, items missing `docs`, and items whose `docs` entry uses a non-matching path form.
|
|
157
|
+
|
|
158
|
+
### 8. Generate a complete replacement file
|
|
139
159
|
|
|
140
160
|
1. Build a **full file string** from the synthesis context and the resolved target objective.
|
|
141
|
-
2.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
161
|
+
2. Start the file with YAML frontmatter for provenance, even when provenance could not be resolved:
|
|
162
|
+
|
|
163
|
+
```yaml
|
|
164
|
+
---
|
|
165
|
+
last_update: <YYYY-MM-DD or unknown>
|
|
166
|
+
---
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
3. Use the resolved `synthesis_context.last_update` value in that frontmatter. Emit `unknown` verbatim when no completed board item provides provenance.
|
|
170
|
+
4. This fallback is mandatory: do not omit the `last_update` key when provenance is missing.
|
|
171
|
+
5. Treat the generated output as the entire authoritative file.
|
|
172
|
+
6. Do **not** patch a single section, append new text to the end, or leave untouched legacy sections in place.
|
|
173
|
+
7. Keep the output valid Markdown.
|
|
174
|
+
8. When writing, replace the old file contents in one operation.
|
|
145
175
|
|
|
146
|
-
###
|
|
176
|
+
### 9. Generate `README.md` for the project-overview objective
|
|
147
177
|
|
|
148
178
|
When `target_path = README.md`, generate the full document around the resolved project-overview contract.
|
|
149
179
|
|
|
@@ -175,7 +205,7 @@ When `target_path = README.md`, generate the full document around the resolved p
|
|
|
175
205
|
- Preserve useful setup warnings from `existing_intent` when they are still valid.
|
|
176
206
|
- Output valid Markdown lists or numbered steps.
|
|
177
207
|
|
|
178
|
-
###
|
|
208
|
+
### 10. Conditionally include a README Examples section
|
|
179
209
|
|
|
180
210
|
Only add `## Examples` to `README.md` when the synthesis context supports a grounded project-type inference.
|
|
181
211
|
|
|
@@ -212,7 +242,7 @@ Choose the strongest evidenced type in this priority order when multiple types a
|
|
|
212
242
|
- Do **not** include placeholder examples, pseudo-commands, or guessed endpoints.
|
|
213
243
|
- If you cannot produce two grounded examples, omit the section instead of improvising.
|
|
214
244
|
|
|
215
|
-
###
|
|
245
|
+
### 11. Generate non-README targets from the rule table
|
|
216
246
|
|
|
217
247
|
For every known non-README target, the path-to-objective rule table determines the file structure. Generate a full document that satisfies the matched rule.
|
|
218
248
|
|
|
@@ -309,6 +339,6 @@ Rules:
|
|
|
309
339
|
- Summarize each entry from commit subjects and, when needed, nearby commit context.
|
|
310
340
|
- Keep newest entries first.
|
|
311
341
|
|
|
312
|
-
###
|
|
342
|
+
### 12. Continue using the resolved objective
|
|
313
343
|
|
|
314
344
|
After the target path and objective are resolved, use them as the contract for all subsequent `/doc` work. Known paths must bypass the ambiguity gate, and all later decisions about evidence gathering, scope, regeneration, and structure must honor the surfaced `target_path` and `objective` instead of inferring a different goal.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# `/doc` authoring notes
|
|
2
|
+
|
|
3
|
+
## `last_update` provenance
|
|
4
|
+
|
|
5
|
+
`/doc` writes a `last_update` frontmatter field at the top of generated documentation files. That value is derived from completed scrum-board items that explicitly declare they affected the target document.
|
|
6
|
+
|
|
7
|
+
The resolver is `skills/doc/scripts/resolve_last_update.py`. It scans:
|
|
8
|
+
|
|
9
|
+
- `project/board/epics/`
|
|
10
|
+
- `project/board/stories/`
|
|
11
|
+
- `project/board/tasks/`
|
|
12
|
+
|
|
13
|
+
A board item counts as provenance only when all of the following are true:
|
|
14
|
+
|
|
15
|
+
1. `status` is `Done` or `Passed`
|
|
16
|
+
2. `docs` is a YAML list
|
|
17
|
+
3. The `docs` list contains the target file path as an exact repo-relative match (for example `README.md` or `docs/API.md`)
|
|
18
|
+
4. `date_completed` exists and parses as `YYYY-MM-DD`
|
|
19
|
+
|
|
20
|
+
If multiple board items match, `/doc` uses the most recent `date_completed` as `last_update`.
|
|
21
|
+
|
|
22
|
+
## Required board annotations
|
|
23
|
+
|
|
24
|
+
For provenance to work, the scrum-board item that changed a documentation file must carry a matching `docs: [...]` annotation in its YAML frontmatter.
|
|
25
|
+
|
|
26
|
+
Examples:
|
|
27
|
+
|
|
28
|
+
```yaml
|
|
29
|
+
docs:
|
|
30
|
+
- README.md
|
|
31
|
+
- docs/API.md
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Use repo-relative paths only. The resolver does not normalize absolute paths or `./`-prefixed variants.
|
|
35
|
+
|
|
36
|
+
## Fallback behavior
|
|
37
|
+
|
|
38
|
+
When `/doc` cannot determine provenance, it still emits valid YAML frontmatter and sets:
|
|
39
|
+
|
|
40
|
+
```yaml
|
|
41
|
+
last_update: unknown
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
This fallback is intentional. It keeps the frontmatter shape stable while making the missing provenance explicit to both humans and downstream tooling.
|
|
45
|
+
|
|
46
|
+
## Failure modes and assumptions
|
|
47
|
+
|
|
48
|
+
### Board item completed without `docs` annotations
|
|
49
|
+
|
|
50
|
+
If a task, story, or epic reached `Done`/`Passed` but omitted the target file from `docs`, `/doc` cannot attribute that item to the document. The resolver will ignore it and may fall back to `unknown`.
|
|
51
|
+
|
|
52
|
+
### Target path mismatch
|
|
53
|
+
|
|
54
|
+
Matching is exact and repo-relative. These examples do **not** match `README.md`:
|
|
55
|
+
|
|
56
|
+
- `/Users/sam/.../README.md`
|
|
57
|
+
- `./README.md`
|
|
58
|
+
- `docs/../README.md`
|
|
59
|
+
|
|
60
|
+
Authors should record the canonical repo-relative path that `/doc` was invoked with.
|
|
61
|
+
|
|
62
|
+
### Item not in a completed status
|
|
63
|
+
|
|
64
|
+
Items in statuses such as `Pending`, `In Progress`, `Blocked`, or `Failed` do not count as provenance even if they include the right `docs` annotation. Only completed work is eligible.
|
|
65
|
+
|
|
66
|
+
### Missing or invalid `date_completed`
|
|
67
|
+
|
|
68
|
+
A matching board item without a valid `date_completed` cannot contribute a `last_update` value. The resolver skips it and logs a warning to stderr.
|
|
69
|
+
|
|
70
|
+
### Malformed legacy board files
|
|
71
|
+
|
|
72
|
+
Some older board artifacts may not follow the current YAML-frontmatter schema. The resolver skips unparseable files instead of failing the whole `/doc` run. This is a degradation path, not successful provenance.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Resolve /doc last_update provenance from scrum board annotations."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import json
|
|
8
|
+
import sys
|
|
9
|
+
from datetime import date
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
COMPLETED_STATUSES = {"Done", "Passed"}
|
|
14
|
+
BOARD_DIRS = (
|
|
15
|
+
("epic", Path("project/board/epics")),
|
|
16
|
+
("story", Path("project/board/stories")),
|
|
17
|
+
("task", Path("project/board/tasks")),
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def parse_args() -> argparse.Namespace:
|
|
22
|
+
parser = argparse.ArgumentParser(
|
|
23
|
+
description="Resolve the latest completed board date for a documentation target.",
|
|
24
|
+
)
|
|
25
|
+
parser.add_argument("target_path", help="Repo-relative documentation target path, e.g. README.md")
|
|
26
|
+
parser.add_argument(
|
|
27
|
+
"--root",
|
|
28
|
+
default=Path(__file__).resolve().parents[3],
|
|
29
|
+
type=Path,
|
|
30
|
+
help="Repository root containing project/board/",
|
|
31
|
+
)
|
|
32
|
+
return parser.parse_args()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def parse_scalar(value: str) -> Any:
|
|
36
|
+
text = value.strip()
|
|
37
|
+
if text in {"", "null", "~"}:
|
|
38
|
+
return ""
|
|
39
|
+
if text.startswith("[") and text.endswith("]"):
|
|
40
|
+
inner = text[1:-1].strip()
|
|
41
|
+
if not inner:
|
|
42
|
+
return []
|
|
43
|
+
return [item.strip().strip("\"'") for item in inner.split(",") if item.strip()]
|
|
44
|
+
return text.strip("\"'")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def parse_frontmatter(path: Path) -> dict[str, Any]:
|
|
48
|
+
text = path.read_text(encoding="utf-8")
|
|
49
|
+
if not text.startswith("---\n"):
|
|
50
|
+
raise ValueError("missing opening frontmatter delimiter")
|
|
51
|
+
parts = text.split("\n---\n", 1)
|
|
52
|
+
if len(parts) != 2:
|
|
53
|
+
raise ValueError("missing closing frontmatter delimiter")
|
|
54
|
+
|
|
55
|
+
lines = parts[0].splitlines()[1:]
|
|
56
|
+
data: dict[str, Any] = {}
|
|
57
|
+
current_key: str | None = None
|
|
58
|
+
|
|
59
|
+
for line in lines:
|
|
60
|
+
if not line.strip():
|
|
61
|
+
continue
|
|
62
|
+
if line.startswith(" - "):
|
|
63
|
+
if current_key is None:
|
|
64
|
+
raise ValueError(f"orphaned list item: {line.strip()}")
|
|
65
|
+
existing = data.get(current_key, "")
|
|
66
|
+
if existing == "":
|
|
67
|
+
existing = []
|
|
68
|
+
data[current_key] = existing
|
|
69
|
+
if not isinstance(existing, list):
|
|
70
|
+
raise ValueError(f"frontmatter key {current_key!r} is not a list")
|
|
71
|
+
existing.append(line[4:].strip().strip("\"'"))
|
|
72
|
+
continue
|
|
73
|
+
if ":" not in line:
|
|
74
|
+
raise ValueError(f"invalid frontmatter line: {line}")
|
|
75
|
+
key, raw_value = line.split(":", 1)
|
|
76
|
+
key = key.strip()
|
|
77
|
+
value = parse_scalar(raw_value)
|
|
78
|
+
data[key] = value
|
|
79
|
+
current_key = key if value == "" else None
|
|
80
|
+
|
|
81
|
+
return data
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def iter_matches(root: Path, target_path: str):
|
|
85
|
+
for item_type, relative_dir in BOARD_DIRS:
|
|
86
|
+
board_dir = root / relative_dir
|
|
87
|
+
if not board_dir.exists():
|
|
88
|
+
continue
|
|
89
|
+
for path in sorted(board_dir.glob("*.md")):
|
|
90
|
+
try:
|
|
91
|
+
frontmatter = parse_frontmatter(path)
|
|
92
|
+
except Exception as exc: # pragma: no cover - defensive degradation
|
|
93
|
+
print(f"warning: skipping {path}: {exc}", file=sys.stderr)
|
|
94
|
+
continue
|
|
95
|
+
|
|
96
|
+
if frontmatter.get("status") not in COMPLETED_STATUSES:
|
|
97
|
+
continue
|
|
98
|
+
|
|
99
|
+
docs = frontmatter.get("docs")
|
|
100
|
+
if not isinstance(docs, list) or target_path not in docs:
|
|
101
|
+
continue
|
|
102
|
+
|
|
103
|
+
completed_raw = str(frontmatter.get("date_completed", "")).strip()
|
|
104
|
+
if not completed_raw:
|
|
105
|
+
print(
|
|
106
|
+
f"warning: skipping {path}: matching docs annotation without date_completed",
|
|
107
|
+
file=sys.stderr,
|
|
108
|
+
)
|
|
109
|
+
continue
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
completed_on = date.fromisoformat(completed_raw)
|
|
113
|
+
except ValueError:
|
|
114
|
+
print(
|
|
115
|
+
f"warning: skipping {path}: invalid date_completed {completed_raw!r}",
|
|
116
|
+
file=sys.stderr,
|
|
117
|
+
)
|
|
118
|
+
continue
|
|
119
|
+
|
|
120
|
+
yield {
|
|
121
|
+
"id": frontmatter.get("id") or path.stem,
|
|
122
|
+
"type": item_type,
|
|
123
|
+
"status": frontmatter.get("status"),
|
|
124
|
+
"date_completed": completed_on.isoformat(),
|
|
125
|
+
"path": path.relative_to(root).as_posix(),
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def main() -> int:
|
|
130
|
+
args = parse_args()
|
|
131
|
+
root = args.root.resolve()
|
|
132
|
+
matches = sorted(
|
|
133
|
+
iter_matches(root, args.target_path),
|
|
134
|
+
key=lambda item: (item["date_completed"], item["id"]),
|
|
135
|
+
)
|
|
136
|
+
last_update = matches[-1]["date_completed"] if matches else "unknown"
|
|
137
|
+
payload = {
|
|
138
|
+
"target_path": args.target_path,
|
|
139
|
+
"last_update": last_update,
|
|
140
|
+
"provenance_found": bool(matches),
|
|
141
|
+
"fallback": "unknown",
|
|
142
|
+
"matched_items": matches,
|
|
143
|
+
}
|
|
144
|
+
print(json.dumps(payload, ensure_ascii=False))
|
|
145
|
+
return 0
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
if __name__ == "__main__":
|
|
149
|
+
raise SystemExit(main())
|
package/skills/doc-sync/SKILL.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: doc-sync
|
|
2
|
+
name: j:doc-sync
|
|
3
3
|
description: Compare the current state of a project with its documentation and update documentation to reflect changes. Accepts `update:`, `source:`, `exclude:`, and `minify:` arguments to control scope. Use when documentation may be out of date with implementation, or when the user asks to sync, refresh, update, or shrink docs.
|
|
4
4
|
keywords:
|
|
5
5
|
- doc-sync
|
package/skills/dooo/SKILL.md
CHANGED
package/skills/error/SKILL.md
CHANGED
package/skills/evaluate/SKILL.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: examplify
|
|
2
|
+
name: j:examplify
|
|
3
3
|
description: Explains concepts, features, use cases, and patterns based on provided context — a description, scenario, code snippet, or file. Use when the user wants to understand what something is, how it works, when to use it, or wants a concrete example.
|
|
4
4
|
keywords:
|
|
5
5
|
- examplify
|
package/skills/help/SKILL.md
CHANGED
package/skills/idea/SKILL.md
CHANGED
package/skills/improve/SKILL.md
CHANGED
package/skills/init/SKILL.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: init
|
|
2
|
+
name: j:init
|
|
3
3
|
description: Initialize a new project with the standard directory structure, PROJECT_SUMMARY.md, workflow.json, git repo, and gitignore. Follows a defined ordered onboarding sequence. Use when setting up a new or empty project.
|
|
4
4
|
keywords:
|
|
5
5
|
- init
|