@raquezha/norpiv 0.0.7 → 0.0.8
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 +29 -34
- package/frame/SKILL.md +2 -3
- package/grill-with-docs/SKILL.md +8 -14
- package/implement/SKILL.md +16 -16
- package/implement/scripts/enforce-branch.sh +22 -12
- package/package.json +3 -1
- package/refine/SKILL.md +54 -0
- package/scripts/graphify-grill.py +42 -0
- package/scripts/graphify-grill.sh +24 -0
- package/scripts/test_graphify_grill.py +21 -0
- package/scripts/triage_helper.sh +132 -35
- package/scripts/validate_active_task.sh +63 -37
- package/sync/SKILL.md +12 -3
- package/triage/SKILL.md +9 -13
- package/verify/SKILL.md +2 -3
- package/scripts/reposcry-bootstrap.sh +0 -108
- package/scripts/reposcry-refresh.sh +0 -19
- package/scripts/reposcry-task-context.sh +0 -40
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ The RPIV engine splits task execution into separate, focused phases:
|
|
|
12
12
|
|
|
13
13
|
| Phase | Command | Purpose | Input / Output Files |
|
|
14
14
|
| :--- | :--- | :--- | :--- |
|
|
15
|
+
| **0. Refine (optional)** | `/refine [source]:[id]` | Repair tracker readiness before RPIV when outcome, acceptance criteria, ownership, dependencies, or decomposition are still unclear. | Proposes tracker edits/child work with ownership/dependency recommendations; no `.workflow/` state |
|
|
15
16
|
| **1. Ingest** | `/triage [source]:[id]` | Initial task verification and workspace setup. | Creates `.workflow/tasks/[source-id]/WORK.md` & `metadata.json` |
|
|
16
17
|
| **2. Scoping** | `/frame` | Author a clear, structured task brief. | Populates `WORK.md` ➔ `[BRIEF]` section |
|
|
17
18
|
| **3. Interrogate**| `/grill-with-docs` | Stress-test brief against rules and docs. | Records decisions in `WORK.md` ➔ `[GRILL]` |
|
|
@@ -46,6 +47,10 @@ Rules:
|
|
|
46
47
|
- **Measure Twice, Cut Once**: Never implement code during scoping or planning. The agent will wait for an explicit `EXECUTE` statement before modifying files.
|
|
47
48
|
- **One Source of Truth**: All task state belongs in `.workflow/tasks/[source-id]/WORK.md`. Avoid creating separate `PROBLEM.md` or `PLAN.md` files.
|
|
48
49
|
- **Safe Branching**: Triage and planning happen on the main branch. Create the feature branch (`feat/*` or `fix/*`) only when starting `/implement`.
|
|
50
|
+
- **Real In-Progress Starts at Implement**: `/refine`, `/triage`, `/frame`, and `/plan` are preparation, not execution. Move the remote Jira/GitHub/GitLab work item to an in-progress state only when `/implement` actually begins.
|
|
51
|
+
- **Refine Preserves Intent**: `/refine` should preserve product-authored text, append acceptance criteria instead of rewriting it, and only assign or link child work when ownership/dependency signals are clear.
|
|
52
|
+
- **Canonical Pointer**: `.workflow/active.json` is the active RPIV pointer. Legacy `active_task.json` is compatibility-only during migration.
|
|
53
|
+
- **Normalized Intake**: `/triage` writes a small local projection into `WORK.md`; tracker snapshots live in `metadata.json`, not raw CLI dumps.
|
|
49
54
|
|
|
50
55
|
## 📦 Install as a skill bundle
|
|
51
56
|
|
|
@@ -55,7 +60,7 @@ Best for trying or handing off RPIV skills without installing the full `nothing`
|
|
|
55
60
|
|
|
56
61
|
```bash
|
|
57
62
|
npx -y skills add raquezha/nothing --full-depth -g -a pi \
|
|
58
|
-
-s triage frame grill-with-docs plan implement verify sync post-merge-prune update-docs distill \
|
|
63
|
+
-s refine triage frame grill-with-docs plan implement verify sync post-merge-prune update-docs distill \
|
|
59
64
|
-y
|
|
60
65
|
```
|
|
61
66
|
|
|
@@ -88,7 +93,7 @@ Targets:
|
|
|
88
93
|
|
|
89
94
|
| Hat | Purpose |
|
|
90
95
|
| :--- | :--- |
|
|
91
|
-
| `pi --rpiv` | Full RPIV workflow. Loads triage, frame, grill-with-docs, plan, implement, verify, sync, post-merge-prune, and update-docs. |
|
|
96
|
+
| `pi --rpiv` | Full RPIV workflow. Loads refine, triage, frame, grill-with-docs, plan, implement, verify, sync, post-merge-prune, and update-docs. |
|
|
92
97
|
| `pi --notes` | Conversation distiller. Saves useful thinking to Obsidian without RPIV ceremony. |
|
|
93
98
|
|
|
94
99
|
## 📝 Pre-RPIV note capture
|
|
@@ -127,34 +132,39 @@ Research is a separate local workflow bundle in `packages/workflows/noresearch`
|
|
|
127
132
|
|
|
128
133
|
If installed via `npx skills add` or `norpiv-install`, invoke the skills directly in your agent instead.
|
|
129
134
|
|
|
130
|
-
2. **
|
|
135
|
+
2. **Optional Tracker Refinement**:
|
|
136
|
+
```text
|
|
137
|
+
/refine github:45
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
3. **Triage an Issue**:
|
|
131
141
|
```text
|
|
132
142
|
/triage github:45
|
|
133
143
|
```
|
|
134
144
|
|
|
135
|
-
|
|
145
|
+
4. **Frame the Work**:
|
|
136
146
|
```text
|
|
137
147
|
/frame
|
|
138
148
|
```
|
|
139
149
|
|
|
140
|
-
|
|
150
|
+
5. **Verify Constraints**:
|
|
141
151
|
```text
|
|
142
152
|
/grill-with-docs
|
|
143
153
|
```
|
|
144
154
|
|
|
145
|
-
|
|
155
|
+
6. **Write the Plan Slices**:
|
|
146
156
|
```text
|
|
147
157
|
/plan
|
|
148
158
|
```
|
|
149
159
|
|
|
150
|
-
|
|
160
|
+
7. **Authorize Execution**:
|
|
151
161
|
Provide the agent explicit permission to implement:
|
|
152
162
|
```text
|
|
153
163
|
EXECUTE
|
|
154
164
|
/implement
|
|
155
165
|
```
|
|
156
166
|
|
|
157
|
-
|
|
167
|
+
8. **Verify & Close**:
|
|
158
168
|
```text
|
|
159
169
|
/verify
|
|
160
170
|
/sync
|
|
@@ -167,39 +177,24 @@ The bundle includes helper scripts used by the workflow skills:
|
|
|
167
177
|
|
|
168
178
|
- `scripts/triage_helper.sh`
|
|
169
179
|
- `scripts/validate_active_task.sh`
|
|
170
|
-
- `scripts/
|
|
171
|
-
- `scripts/
|
|
172
|
-
- `scripts/reposcry-refresh.sh`
|
|
180
|
+
- `scripts/graphify-grill.sh`
|
|
181
|
+
- `scripts/graphify-grill.py`
|
|
173
182
|
|
|
174
183
|
When skills are loaded directly from this package, relative references like `../scripts/...` resolve against the package root. When skills are installed with `norpiv-install`, the same layout is recreated under the target runtime.
|
|
175
184
|
|
|
176
|
-
## 🔎 Optional
|
|
185
|
+
## 🔎 Optional Graphify grilling
|
|
177
186
|
|
|
178
|
-
|
|
187
|
+
Graphify is an optional grill-only evidence layer for RPIV. norpiv does **not** require it.
|
|
179
188
|
|
|
180
|
-
If `
|
|
189
|
+
If bootstrap provisions `~/.graphify/venv`, `/grill-with-docs` may run:
|
|
181
190
|
|
|
182
191
|
```bash
|
|
183
|
-
./scripts/
|
|
184
|
-
./scripts/reposcry-task-context.sh "fix dependency graph rebuild"
|
|
185
|
-
# edit code
|
|
186
|
-
./scripts/reposcry-refresh.sh main
|
|
187
|
-
reposcry validate main HEAD
|
|
192
|
+
./scripts/graphify-grill.sh
|
|
188
193
|
```
|
|
189
194
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
- `/triage`: optionally seed `.reposcry/` with `scripts/reposcry-bootstrap.sh`
|
|
193
|
-
- `/frame`: optionally generate `.reposcry/AI_CONTEXT.md` with `scripts/reposcry-task-context.sh`
|
|
194
|
-
- `/grill-with-docs`: optionally use `reposcry query_graph`, `get_architecture_overview`, and `get_impact_radius`
|
|
195
|
-
- `/implement`: optionally run `scripts/reposcry-refresh.sh` after edit batches
|
|
196
|
-
- `/verify`: optionally add `reposcry validate main HEAD` and affected-flow output as extra evidence
|
|
197
|
-
|
|
198
|
-
If RepoScry is absent, the helpers no-op and RPIV continues with normal repo reading, grep, and tests.
|
|
199
|
-
|
|
200
|
-
RepoScry guardrails:
|
|
195
|
+
Guardrails:
|
|
201
196
|
|
|
202
|
-
-
|
|
203
|
-
-
|
|
204
|
-
-
|
|
205
|
-
-
|
|
197
|
+
- input is a temporary `git archive HEAD` extraction, not the live repository root
|
|
198
|
+
- extraction is structural only by default
|
|
199
|
+
- Graphify warnings never block grilling; normal source reading remains the fallback
|
|
200
|
+
- `INFERRED` or `AMBIGUOUS` edges are leads that still require source verification
|
package/frame/SKILL.md
CHANGED
|
@@ -10,15 +10,14 @@ description: Define the task brief inside the active WORK.md. Use after /triage
|
|
|
10
10
|
Turn raw task context into the stable "what/why" brief.
|
|
11
11
|
|
|
12
12
|
## Guardrails
|
|
13
|
-
- READ: `.workflow/active.json`
|
|
14
|
-
- WRITE: `WORK.md` -> `[BRIEF]` section and append to `[LOG]` only
|
|
13
|
+
- READ: `.workflow/active.json` first, then legacy compatibility `.workflow/active_task.json` only if needed, and active `WORK.md`.
|
|
14
|
+
- WRITE: `WORK.md` -> `[BRIEF]` section and append to `[LOG]` only.
|
|
15
15
|
- NEVER: create `PROBLEM.md`, `PRD.md`, or extra planning files.
|
|
16
16
|
- NEVER: overwrite `[PLAN]` or `[GRILL]`.
|
|
17
17
|
- NEVER: ask whether to frame if the user invoked `/frame`; do it.
|
|
18
18
|
|
|
19
19
|
## Workflow
|
|
20
20
|
1. Read the active task and remote metadata.
|
|
21
|
-
2. If RepoScry is available, run the bundled `../scripts/reposcry-task-context.sh "<task summary>"` helper to generate `.reposcry/AI_CONTEXT.md`, then use that file as supplemental repo context. The helper path must preserve RepoScry guardrails: `.reposcry/` ignored, cache never tracked, `.reposcryignore` treated as reviewable indexing policy. Continue normally when unavailable.
|
|
22
21
|
3. Determine brief type:
|
|
23
22
|
- **Problem** for bugs, regressions, crashes, broken behavior.
|
|
24
23
|
- **Proposal** for features, enhancements, refactors, new behavior.
|
package/grill-with-docs/SKILL.md
CHANGED
|
@@ -10,31 +10,25 @@ description: Stress-test the active WORK.md brief against docs, code, and domain
|
|
|
10
10
|
Challenge the brief before planning. This replaces passive ubiquitous-language collection with active clarification.
|
|
11
11
|
|
|
12
12
|
## Guardrails
|
|
13
|
-
- READ: `.workflow/active.json`
|
|
13
|
+
- READ: `.workflow/active.json` first, then legacy compatibility `.workflow/active_task.json` only if needed, plus active `WORK.md` `[BRIEF]`, `CONTEXT.md`, and relevant `docs/agents/*`.
|
|
14
14
|
- WRITE: `WORK.md` -> append to `[GRILL]` and `[LOG]` only; durable docs only when a stable rule is confirmed.
|
|
15
15
|
- NEVER: edit `[BRIEF]` silently; propose brief changes if contradictions are found.
|
|
16
16
|
- NEVER: plan or implement during grilling.
|
|
17
17
|
- NEVER: ask questions the codebase can answer; inspect first.
|
|
18
18
|
|
|
19
19
|
## Workflow
|
|
20
|
-
1. **Context Loading**: Read the active brief, `CONTEXT.md`, relevant `docs/agents
|
|
20
|
+
1. **Context Loading**: Read the active brief, `CONTEXT.md`, and relevant `docs/agents/*`.
|
|
21
21
|
2. **Investigation & Trace**:
|
|
22
22
|
- Locate the files/lines mentioned in the brief.
|
|
23
23
|
- Trace the data flow related to the problem/proposal.
|
|
24
24
|
- Search for "Impact Surface": Who else uses or depends on these components?
|
|
25
|
-
3. **Optional
|
|
26
|
-
- `reposcry --repo . get_architecture_overview --format json`
|
|
27
|
-
- `reposcry --repo . query_graph "callers_of <symbol>"`
|
|
28
|
-
- `reposcry --repo . query_graph "tests_for <symbol>"`
|
|
29
|
-
- `reposcry --repo . get_impact_radius <symbol> --depth 4`
|
|
30
|
-
Proceed normally when RepoScry is absent.
|
|
25
|
+
3. **Optional Graphify pass**: run `../scripts/graphify-grill.sh` once for bounded structural evidence. It reads only a temporary `git archive HEAD` extraction and warns instead of failing when unavailable. Treat `INFERRED` or `AMBIGUOUS` edges as leads: verify them in source before recording conclusions.
|
|
31
26
|
4. **Cross-check**: Compare findings against docs, ADRs, and repo patterns.
|
|
32
|
-
5. **
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
8. **Context Curation**: If a durable term/rule emerges, propose or apply a concise `docs/agents/*` update.
|
|
27
|
+
5. **Challenge**: record confirmed constraints, challenged assumptions, chosen decisions, open blockers / HITL questions, and risks handed to `/plan` or `/implement`.
|
|
28
|
+
6. **Interview**: ask one question at a time only for execution-blocking ambiguity the repository cannot answer.
|
|
29
|
+
7. **Log Evidence**: Append resolved decisions, technical findings, edge cases, and constraints to `[GRILL]`.
|
|
30
|
+
8. **Log Activity**: Append a timestamped summary of the grilling session to `[LOG]` (Format: `YYYY-MM-DD hh:mm AM/PM`).
|
|
31
|
+
9. **Context Curation**: If a durable term/rule emerges, propose or apply a concise `docs/agents/*` update.
|
|
38
32
|
|
|
39
33
|
## Output contract
|
|
40
34
|
End with:
|
package/implement/SKILL.md
CHANGED
|
@@ -10,34 +10,34 @@ description: Implement the next approved vertical slice from the active WORK.md
|
|
|
10
10
|
Execute one functional vertical slice and hand it to the human for review.
|
|
11
11
|
|
|
12
12
|
## Guardrails
|
|
13
|
-
- READ: `.workflow/active.json`
|
|
14
|
-
- WRITE: code changes and `WORK.md` -> append to `[LOG]` only
|
|
13
|
+
- READ: `.workflow/active.json` first, then compatibility `.workflow/active_task.json` only if needed, then active `WORK.md` `[PLAN]`, `[BRIEF]`, and relevant `[LOG]` evidence.
|
|
14
|
+
- WRITE: code changes and `WORK.md` -> append to `[LOG]` only.
|
|
15
15
|
- NEVER: edit `[BRIEF]` or `[GRILL]`.
|
|
16
16
|
- NEVER: implement without explicit user instruction.
|
|
17
|
-
- NEVER: commit a Jira-tracked task with a Jira-less subject; if the active task source is `jira`, the commit subject MUST include the Jira key from `.workflow/active_task.json` (e.g. `fix(PROJ-123): ...`).
|
|
18
|
-
- NEVER: hide the Jira key only in the commit body/footer when the task is Jira-tracked; the subject itself must carry the key.
|
|
19
17
|
- NEVER: add `Signed-off-by`; only the human can certify DCO.
|
|
20
18
|
- NEVER: freestyle PR/MR descriptions; use the Draft PR/MR body contract below.
|
|
21
19
|
|
|
22
20
|
## Workflow
|
|
23
21
|
1. Identify the first approved unchecked slice in `[PLAN]`.
|
|
24
|
-
2.
|
|
25
|
-
3. **Mandatory Branch Check**: You MUST run the branch enforcement script before modifying any code.
|
|
22
|
+
2. **Mandatory Branch Check**: You MUST run the branch enforcement script before modifying any code.
|
|
26
23
|
- Use the absolute path if possible: `<skill_location>/scripts/enforce-branch.sh`.
|
|
27
24
|
- This script prevents accidental implementation on `main`/`master`.
|
|
28
25
|
- If the script switches branches, you must update the `[META]` section of `WORK.md` to reflect the new branch name.
|
|
29
26
|
- If the script fails, STOP and ask the human for help. Do not proceed with code changes.
|
|
30
|
-
|
|
27
|
+
3. Select the executable child item, not an umbrella parent; if it is unclear, STOP and ask.
|
|
28
|
+
4. Move that remote item to **In Progress** immediately before code changes.
|
|
29
|
+
- Jira: transition only when an `In Progress`-style state exists.
|
|
30
|
+
- GitHub / GitLab: use an existing repository-specific status mapping only; do not invent a label, comment, or MR change. If none exists, leave the item unchanged and record why in `[LOG]`.
|
|
31
|
+
- If a configured transition fails, STOP and ask the human; do not begin code changes.
|
|
32
|
+
- Do not mark work in progress during `/refine`, `/triage`, `/frame`, or `/plan`.
|
|
31
33
|
5. Implement test-first where practical; otherwise document why not in `[LOG]`.
|
|
32
|
-
6.
|
|
33
|
-
7.
|
|
34
|
-
|
|
35
|
-
-
|
|
36
|
-
|
|
37
|
-
9.
|
|
38
|
-
|
|
39
|
-
10. Use a temporary body file (`--body-file` or API equivalent) for PR/MR descriptions to avoid shell quoting and markdown escaping bugs.
|
|
40
|
-
11. Append summary, commit hash, and PR/MR link to `[LOG]` (Format: `YYYY-MM-DD hh:mm AM/PM`).
|
|
34
|
+
6. Run the slice verification command and available quality gates.
|
|
35
|
+
7. Commit using the repository's normal conventions and hooks/CI rules.
|
|
36
|
+
- If the repo or tracker expects a Jira key, preserve it where that repository normally requires it.
|
|
37
|
+
- If AI attribution trailers are required, obtain them from the repository's documented policy rather than hard-coding RPIV-specific formatting.
|
|
38
|
+
8. Push and open a Draft PR/MR with `gh` or `glab` when a remote exists.
|
|
39
|
+
9. Use a temporary body file (`--body-file` or API equivalent) for PR/MR descriptions to avoid shell quoting and markdown escaping bugs.
|
|
40
|
+
10. Append summary, commit hash, and PR/MR link to `[LOG]` (Format: `YYYY-MM-DD hh:mm AM/PM`).
|
|
41
41
|
|
|
42
42
|
## Draft PR/MR body contract
|
|
43
43
|
|
|
@@ -44,22 +44,32 @@ json_pretty() {
|
|
|
44
44
|
|
|
45
45
|
# Find repo root to ensure paths are absolute and reliable
|
|
46
46
|
REPO_ROOT=$(git rev-parse --show-toplevel)
|
|
47
|
+
ACTIVE_WORKFLOW_FILE="$REPO_ROOT/.workflow/active.json"
|
|
48
|
+
# Deprecated compatibility fallback during active.json migration. Remove once all readers stop depending on active_task.json.
|
|
47
49
|
ACTIVE_TASK_FILE="$REPO_ROOT/.workflow/active_task.json"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
POINTER_FILE=""
|
|
51
|
+
POINTER_LABEL=""
|
|
52
|
+
|
|
53
|
+
if [[ -f "$ACTIVE_WORKFLOW_FILE" ]]; then
|
|
54
|
+
POINTER_FILE="$ACTIVE_WORKFLOW_FILE"
|
|
55
|
+
POINTER_LABEL="active.json"
|
|
56
|
+
elif [[ -f "$ACTIVE_TASK_FILE" ]]; then
|
|
57
|
+
POINTER_FILE="$ACTIVE_TASK_FILE"
|
|
58
|
+
POINTER_LABEL="active_task.json"
|
|
59
|
+
else
|
|
60
|
+
echo "ERROR: No active task found at $ACTIVE_WORKFLOW_FILE or $ACTIVE_TASK_FILE."
|
|
51
61
|
echo "Please run /triage [source]:[id] first."
|
|
52
62
|
exit 1
|
|
53
63
|
fi
|
|
54
64
|
|
|
55
65
|
# Read canonical active task fields
|
|
56
|
-
TASK_SOURCE=$(json_read "$
|
|
57
|
-
TASK_ID=$(json_read "$
|
|
58
|
-
TASK_PATH=$(json_read "$
|
|
66
|
+
TASK_SOURCE=$(json_read "$POINTER_FILE" '.source // empty')
|
|
67
|
+
TASK_ID=$(json_read "$POINTER_FILE" '.sourceId // .id // empty')
|
|
68
|
+
TASK_PATH=$(json_read "$POINTER_FILE" '.taskPath // .path // empty')
|
|
59
69
|
|
|
60
70
|
# Prefer explicit taskPath when available (most reliable)
|
|
61
71
|
if [[ -n "$TASK_PATH" ]]; then
|
|
62
|
-
echo "INFO: Using taskPath from
|
|
72
|
+
echo "INFO: Using taskPath from $POINTER_LABEL: $TASK_PATH"
|
|
63
73
|
if [[ "$TASK_PATH" = /* ]]; then
|
|
64
74
|
WORK_MD="$TASK_PATH/WORK.md"
|
|
65
75
|
else
|
|
@@ -77,9 +87,9 @@ elif [[ -n "$TASK_ID" ]]; then
|
|
|
77
87
|
WORK_MD="$REPO_ROOT/.workflow/tasks/$TASK_FOLDER/WORK.md"
|
|
78
88
|
|
|
79
89
|
else
|
|
80
|
-
echo "ERROR: No 'sourceId' or 'taskPath' found in $
|
|
81
|
-
echo "Please run /triage [source]:[id] to initialize a task. Here are the file contents for debugging:"
|
|
82
|
-
json_pretty "$
|
|
90
|
+
echo "ERROR: No 'sourceId' or 'taskPath' found in $POINTER_LABEL."
|
|
91
|
+
echo "Please run /triage [source]:[id] to initialize a task. Here are the file contents for debugging:"
|
|
92
|
+
json_pretty "$POINTER_FILE" || true
|
|
83
93
|
exit 1
|
|
84
94
|
fi
|
|
85
95
|
|
|
@@ -88,8 +98,8 @@ echo "DEBUG: Computed WORK.md path: $WORK_MD"
|
|
|
88
98
|
|
|
89
99
|
if [[ ! -f "$WORK_MD" ]]; then
|
|
90
100
|
echo "ERROR: WORK.md not found at $WORK_MD."
|
|
91
|
-
echo "Checked
|
|
92
|
-
json_pretty "$
|
|
101
|
+
echo "Checked $POINTER_LABEL values:"
|
|
102
|
+
json_pretty "$POINTER_FILE" || true
|
|
93
103
|
echo "Please ensure the task was initialized with /triage and that WORK.md exists."
|
|
94
104
|
exit 1
|
|
95
105
|
fi
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raquezha/norpiv",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "Bundled RPIV workflow skills for Pi, Claude, and Codex-style coding agents",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"bin": {
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"distill",
|
|
13
13
|
"frame",
|
|
14
14
|
"grill-with-docs",
|
|
15
|
+
"refine",
|
|
15
16
|
"implement",
|
|
16
17
|
"plan",
|
|
17
18
|
"scripts",
|
|
@@ -36,6 +37,7 @@
|
|
|
36
37
|
},
|
|
37
38
|
"pi": {
|
|
38
39
|
"skills": [
|
|
40
|
+
"refine",
|
|
39
41
|
"triage",
|
|
40
42
|
"frame",
|
|
41
43
|
"grill-with-docs",
|
package/refine/SKILL.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: refine
|
|
3
|
+
workflow: pre-rpiv
|
|
4
|
+
workflowPhase: refine
|
|
5
|
+
description: "Repair tracker work-item readiness before RPIV intake. Use for jira:, github:, or gitlab: items that still need clarified outcome, acceptance criteria, or child work before /triage."
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Skill: refine
|
|
9
|
+
|
|
10
|
+
Make a tracker item ready for engineering handoff without inventing product decisions.
|
|
11
|
+
|
|
12
|
+
## Guardrails
|
|
13
|
+
- READ: tracker item, linked/child work, relevant comments, and targeted repo context only when needed for ownership or feasibility.
|
|
14
|
+
- WRITE: proposals in chat first; tracker comments/field edits/child creation only after explicit human approval.
|
|
15
|
+
- NEVER: implement code, create branches, or start RPIV task state.
|
|
16
|
+
- NEVER: invent missing product intent when a human decision is required.
|
|
17
|
+
- NEVER: duplicate existing child work or spam repeated agent comments.
|
|
18
|
+
- NEVER: move work across trackers.
|
|
19
|
+
|
|
20
|
+
## Workflow
|
|
21
|
+
1. Parse namespaced source: `jira:KEY`, `github:ID`, or `gitlab:ID`.
|
|
22
|
+
2. Read the item, current structure, and recent comments.
|
|
23
|
+
3. Classify readiness gaps: missing outcome, acceptance criteria, ownership, dependencies, blockers, or decomposition.
|
|
24
|
+
4. For every proposed child item, state intended owner, confidence, and fallback `unassigned`. Auto-assign only when the signal is strong from the tracker, comments, or existing team ownership patterns.
|
|
25
|
+
5. Decide dependency handling explicitly for each child pair or blocker:
|
|
26
|
+
- no dependency
|
|
27
|
+
- description note only
|
|
28
|
+
- real tracker link
|
|
29
|
+
Prefer actual Jira `Blocks` links when child A must precede child B.
|
|
30
|
+
6. Return one outcome:
|
|
31
|
+
- `ready-single`: one executable owner, no decomposition needed
|
|
32
|
+
- `ready-decomposed`: split is needed and children, links, and owners are clear
|
|
33
|
+
- `needs-decision`: missing product decision or not truly ready-decomposed because ownership/dependencies are still muddy
|
|
34
|
+
7. Propose the smallest useful tracker edits and child items before mutating anything.
|
|
35
|
+
8. Before any approved mutation, walk this checklist in order:
|
|
36
|
+
- preserve product-authored text
|
|
37
|
+
- append acceptance criteria instead of rewriting existing acceptance criteria
|
|
38
|
+
- create child?
|
|
39
|
+
- assign child?
|
|
40
|
+
- link child?
|
|
41
|
+
- then mutate parent text/fields
|
|
42
|
+
9. On explicit approval, apply the approved tracker edits only.
|
|
43
|
+
10. Re-run idempotently: reuse existing child items and any prior agent-owned refinement marker/comment.
|
|
44
|
+
11. Recommend `/triage` only once the item is execution-ready.
|
|
45
|
+
|
|
46
|
+
## Output contract
|
|
47
|
+
End with:
|
|
48
|
+
- **Outcome**: ready-single / ready-decomposed / needs-decision
|
|
49
|
+
- **Missing information**
|
|
50
|
+
- **Proposed tracker edits**
|
|
51
|
+
- **Proposed child work**
|
|
52
|
+
- **Proposed ownership**
|
|
53
|
+
- **Proposed dependency links**
|
|
54
|
+
- **Next step**: approve edits / answer question / `/triage`
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Emit bounded structural Graphify evidence from a disposable archive."""
|
|
2
|
+
import json
|
|
3
|
+
import sys
|
|
4
|
+
from contextlib import redirect_stdout
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
EDGE_LIMIT = 100
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def normalize_edge(edge):
|
|
11
|
+
return {
|
|
12
|
+
"confidence": edge.get("confidence", "EXTRACTED"),
|
|
13
|
+
"source": edge.get("source", edge.get("from", "")),
|
|
14
|
+
"target": edge.get("target", edge.get("to", "")),
|
|
15
|
+
"type": edge.get("type", edge.get("relation", "")),
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def render_evidence(graph, limit=EDGE_LIMIT):
|
|
20
|
+
edges = graph.get("edges", [])[:limit]
|
|
21
|
+
return {
|
|
22
|
+
"edges": [normalize_edge(edge) for edge in edges],
|
|
23
|
+
"truncated": len(graph.get("edges", [])) > len(edges),
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def main(root_arg):
|
|
28
|
+
root = Path(root_arg)
|
|
29
|
+
out = root / "graphify-out"
|
|
30
|
+
from graphify.extract import collect_files, extract
|
|
31
|
+
|
|
32
|
+
with redirect_stdout(sys.stderr):
|
|
33
|
+
graph = extract(collect_files(root), cache_root=out, root=root, parallel=False)
|
|
34
|
+
print(json.dumps(render_evidence(graph)))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if __name__ == "__main__":
|
|
38
|
+
try:
|
|
39
|
+
main(sys.argv[1])
|
|
40
|
+
except Exception as error:
|
|
41
|
+
print(f"Graphify extraction failed: {error}", file=sys.stderr)
|
|
42
|
+
raise
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Optional structural Graphify pass over committed HEAD only.
|
|
3
|
+
set -u
|
|
4
|
+
|
|
5
|
+
repo_root=$(git rev-parse --show-toplevel 2>/dev/null) || { echo "WARN: Graphify skipped: not a git repository." >&2; exit 0; }
|
|
6
|
+
python=${GRAPHIFY_PYTHON:-"$HOME/.graphify/venv/bin/python"}
|
|
7
|
+
if [[ ! -x "$python" ]]; then
|
|
8
|
+
python="$repo_root/.graphify/venv/bin/python"
|
|
9
|
+
fi
|
|
10
|
+
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
11
|
+
temp=$(mktemp -d "${TMPDIR:-/tmp}/norpiv-graphify.XXXXXX") || { echo "WARN: Graphify skipped: cannot create temporary directory." >&2; exit 0; }
|
|
12
|
+
trap 'rm -rf "$temp"' EXIT
|
|
13
|
+
|
|
14
|
+
if [[ ! -x "$python" ]]; then
|
|
15
|
+
echo "WARN: Graphify skipped: machine-wide or repo-local Python is unavailable." >&2
|
|
16
|
+
exit 0
|
|
17
|
+
fi
|
|
18
|
+
if ! git -C "$repo_root" archive HEAD | tar -x -C "$temp"; then
|
|
19
|
+
echo "WARN: Graphify skipped: cannot archive HEAD." >&2
|
|
20
|
+
exit 0
|
|
21
|
+
fi
|
|
22
|
+
if ! "$python" "$script_dir/graphify-grill.py" "$temp"; then
|
|
23
|
+
echo "WARN: Graphify failed; continue with normal source reading." >&2
|
|
24
|
+
fi
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import importlib.util
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
module_path = Path(__file__).with_name("graphify-grill.py")
|
|
5
|
+
spec = importlib.util.spec_from_file_location("graphify_grill", module_path)
|
|
6
|
+
module = importlib.util.module_from_spec(spec)
|
|
7
|
+
spec.loader.exec_module(module)
|
|
8
|
+
|
|
9
|
+
edge = module.normalize_edge({"from": "a", "to": "b", "relation": "calls"})
|
|
10
|
+
assert edge == {
|
|
11
|
+
"confidence": "EXTRACTED",
|
|
12
|
+
"source": "a",
|
|
13
|
+
"target": "b",
|
|
14
|
+
"type": "calls",
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
rendered = module.render_evidence({"edges": [{"source": str(i)} for i in range(105)]}, limit=100)
|
|
18
|
+
assert len(rendered["edges"]) == 100
|
|
19
|
+
assert rendered["truncated"] is True
|
|
20
|
+
|
|
21
|
+
print("graphify-grill.py self-check passed")
|
package/scripts/triage_helper.sh
CHANGED
|
@@ -120,21 +120,12 @@ PY
|
|
|
120
120
|
|
|
121
121
|
write_active_pointer() {
|
|
122
122
|
mkdir -p ".workflow"
|
|
123
|
-
python3 - ".workflow/
|
|
123
|
+
python3 - ".workflow/active.json" "$TASK_FOLDER" "$SOURCE" "$ID" "$TASK_DIR" "$WORK_MD" "$BRANCH_NAME" "$ISO_NOW" <<'PY'
|
|
124
124
|
import json
|
|
125
125
|
import sys
|
|
126
126
|
from pathlib import Path
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
legacy_data = {
|
|
130
|
-
"active_task": active_task,
|
|
131
|
-
"source": source,
|
|
132
|
-
"id": raw_id,
|
|
133
|
-
"sourceId": raw_id,
|
|
134
|
-
"taskPath": task_path,
|
|
135
|
-
"path": task_path,
|
|
136
|
-
"branch": branch,
|
|
137
|
-
}
|
|
128
|
+
active_workflow_path, active_task, source, raw_id, task_path, state_file, branch, now = sys.argv[1:]
|
|
138
129
|
workflow_data = {
|
|
139
130
|
"workflow": "rpiv",
|
|
140
131
|
"id": active_task,
|
|
@@ -147,9 +138,7 @@ workflow_data = {
|
|
|
147
138
|
"branch": branch,
|
|
148
139
|
"startedAt": now,
|
|
149
140
|
"updatedAt": now,
|
|
150
|
-
"compatPointer": ".workflow/active_task.json",
|
|
151
141
|
}
|
|
152
|
-
Path(active_task_path).write_text(json.dumps(legacy_data, indent=2) + "\n")
|
|
153
142
|
Path(active_workflow_path).write_text(json.dumps(workflow_data, indent=2) + "\n")
|
|
154
143
|
PY
|
|
155
144
|
}
|
|
@@ -202,24 +191,43 @@ PY
|
|
|
202
191
|
}
|
|
203
192
|
|
|
204
193
|
update_work_meta() {
|
|
205
|
-
local status
|
|
194
|
+
local status
|
|
206
195
|
status=$(json_get "$METADATA_JSON" status)
|
|
207
|
-
phase=$(json_get "$METADATA_JSON" phase)
|
|
208
196
|
status=${status:-active}
|
|
209
|
-
phase=${phase:-triaged}
|
|
210
197
|
|
|
211
|
-
python3 - "$WORK_MD" "$BRANCH_NAME" "$status" "$
|
|
198
|
+
python3 - "$WORK_MD" "$BRANCH_NAME" "$status" "$SOURCE" "$ID" <<'PY'
|
|
212
199
|
import re
|
|
213
200
|
import sys
|
|
214
201
|
from pathlib import Path
|
|
215
202
|
|
|
216
203
|
path = Path(sys.argv[1])
|
|
217
|
-
branch, status,
|
|
204
|
+
branch, status, source, raw_id = sys.argv[2:]
|
|
218
205
|
text = path.read_text() if path.exists() else ""
|
|
219
206
|
lines = text.splitlines()
|
|
220
207
|
header_re = re.compile(r"^(## )?\[[A-Z0-9_-]+\]\s*$")
|
|
221
208
|
meta_re = re.compile(r"^(## )?\[META\]\s*$")
|
|
222
209
|
|
|
210
|
+
|
|
211
|
+
def section_body(section):
|
|
212
|
+
header = re.compile(rf"^(## )?\[{re.escape(section)}\]\s*$")
|
|
213
|
+
start = next((i for i, line in enumerate(lines) if header.match(line)), None)
|
|
214
|
+
if start is None:
|
|
215
|
+
return ""
|
|
216
|
+
end = len(lines)
|
|
217
|
+
for i in range(start + 1, len(lines)):
|
|
218
|
+
if header_re.match(lines[i]):
|
|
219
|
+
end = i
|
|
220
|
+
break
|
|
221
|
+
return "\n".join(lines[start + 1:end]).strip()
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def meaningful(section):
|
|
225
|
+
body = section_body(section)
|
|
226
|
+
return any(line.strip() and line.strip() not in {"-", "- [ ]"} for line in body.splitlines())
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
phase = "planned" if meaningful("PLAN") else "grilled" if meaningful("GRILL") else "framed" if meaningful("BRIEF") else "triaged"
|
|
230
|
+
|
|
223
231
|
new_block = [
|
|
224
232
|
"## [META]",
|
|
225
233
|
f"- Branch: `{branch}`",
|
|
@@ -247,6 +255,100 @@ path.write_text("\n".join(updated).rstrip() + "\n")
|
|
|
247
255
|
PY
|
|
248
256
|
}
|
|
249
257
|
|
|
258
|
+
write_work_snapshot() {
|
|
259
|
+
python3 - "$WORK_MD" "$METADATA_JSON" "$SOURCE" "$ID" <<'PY'
|
|
260
|
+
import json
|
|
261
|
+
import re
|
|
262
|
+
import sys
|
|
263
|
+
from pathlib import Path
|
|
264
|
+
|
|
265
|
+
work_md, metadata_json, source, raw_id = sys.argv[1:]
|
|
266
|
+
|
|
267
|
+
try:
|
|
268
|
+
metadata = json.loads(Path(metadata_json).read_text())
|
|
269
|
+
if not isinstance(metadata, dict):
|
|
270
|
+
metadata = {}
|
|
271
|
+
except Exception:
|
|
272
|
+
metadata = {}
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def pick(*values, fallback=""):
|
|
276
|
+
for value in values:
|
|
277
|
+
if isinstance(value, str) and value.strip():
|
|
278
|
+
return value.strip()
|
|
279
|
+
return fallback
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def squash(value):
|
|
283
|
+
if isinstance(value, dict):
|
|
284
|
+
value = json.dumps(value, ensure_ascii=False)
|
|
285
|
+
if not isinstance(value, str):
|
|
286
|
+
return ""
|
|
287
|
+
text = re.sub(r"```.*?```", " ", value, flags=re.S)
|
|
288
|
+
text = re.sub(r"^(## )?\[[A-Z0-9_-]+\]\s*$", " ", text, flags=re.M)
|
|
289
|
+
text = re.sub(r"\s+", " ", text).strip()
|
|
290
|
+
return text[:280].rstrip() + ("..." if len(text) > 280 else "")
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
fields = metadata.get("fields") if isinstance(metadata.get("fields"), dict) else {}
|
|
294
|
+
title = {
|
|
295
|
+
"github": pick(metadata.get("title"), fallback=f"GitHub #{raw_id}"),
|
|
296
|
+
"gitlab": pick(metadata.get("title"), fallback=f"GitLab #{raw_id}"),
|
|
297
|
+
"jira": pick(fields.get("summary"), metadata.get("key"), fallback=f"Jira {raw_id}"),
|
|
298
|
+
}.get(source, pick(metadata.get("title"), fallback=f"Local Task {raw_id}"))
|
|
299
|
+
url = {
|
|
300
|
+
"github": pick(metadata.get("url"), fallback=f"https://github.com/issues/{raw_id}"),
|
|
301
|
+
"gitlab": pick(metadata.get("web_url"), metadata.get("url")),
|
|
302
|
+
"jira": pick(metadata.get("self")),
|
|
303
|
+
}.get(source, "")
|
|
304
|
+
summary = {
|
|
305
|
+
"github": squash(metadata.get("body")),
|
|
306
|
+
"gitlab": squash(metadata.get("description")),
|
|
307
|
+
"jira": squash(fields.get("description")),
|
|
308
|
+
}.get(source, squash(metadata.get("body"))) or title
|
|
309
|
+
tracker_updated = {
|
|
310
|
+
"github": pick(metadata.get("updatedAt"), metadata.get("createdAt")),
|
|
311
|
+
"gitlab": pick(metadata.get("updated_at"), metadata.get("created_at")),
|
|
312
|
+
"jira": pick(fields.get("updated"), metadata.get("updated")),
|
|
313
|
+
}.get(source, "")
|
|
314
|
+
|
|
315
|
+
lines = [
|
|
316
|
+
f"# WORK: {title}",
|
|
317
|
+
"",
|
|
318
|
+
"## [INTAKE]",
|
|
319
|
+
"### Outcome",
|
|
320
|
+
summary,
|
|
321
|
+
"",
|
|
322
|
+
"### Acceptance Criteria",
|
|
323
|
+
"- [ ] Confirm concrete acceptance criteria from tracker or refine them during `/frame`.",
|
|
324
|
+
"",
|
|
325
|
+
"### Scope / Non-goals",
|
|
326
|
+
"- Keep local execution state concise; do not copy raw tracker or CLI rendering into `WORK.md`.",
|
|
327
|
+
"",
|
|
328
|
+
"### Dependencies / Blockers",
|
|
329
|
+
"- None noted from intake snapshot.",
|
|
330
|
+
"",
|
|
331
|
+
"### Tracker Context",
|
|
332
|
+
f"- Task: `{source}:{raw_id}`",
|
|
333
|
+
f"- URL: {url}" if url else "- URL: None noted",
|
|
334
|
+
f"- Tracker updated: `{tracker_updated}`" if tracker_updated else "- Tracker updated: None noted",
|
|
335
|
+
"",
|
|
336
|
+
"## [BRIEF]",
|
|
337
|
+
"- ",
|
|
338
|
+
"",
|
|
339
|
+
"## [GRILL]",
|
|
340
|
+
"- ",
|
|
341
|
+
"",
|
|
342
|
+
"## [PLAN]",
|
|
343
|
+
"- [ ] ",
|
|
344
|
+
"",
|
|
345
|
+
"## [LOG]",
|
|
346
|
+
]
|
|
347
|
+
|
|
348
|
+
Path(work_md).write_text("\n".join(lines).rstrip() + "\n")
|
|
349
|
+
PY
|
|
350
|
+
}
|
|
351
|
+
|
|
250
352
|
backfill_metadata() {
|
|
251
353
|
json_upsert "$METADATA_JSON" \
|
|
252
354
|
"id=$ID" \
|
|
@@ -254,21 +356,18 @@ backfill_metadata() {
|
|
|
254
356
|
"branch=$BRANCH_NAME" \
|
|
255
357
|
"taskFolder=$TASK_FOLDER" \
|
|
256
358
|
"?status=active" \
|
|
257
|
-
"?phase=triaged" \
|
|
258
359
|
"createdAt=$ISO_NOW" \
|
|
259
360
|
"updatedAt=$ISO_NOW"
|
|
260
361
|
}
|
|
261
362
|
|
|
262
363
|
set_metadata_status_phase() {
|
|
263
364
|
local status="$1"
|
|
264
|
-
local phase="$2"
|
|
265
365
|
json_upsert "$METADATA_JSON" \
|
|
266
366
|
"id=$ID" \
|
|
267
367
|
"source=$SOURCE" \
|
|
268
368
|
"branch=$BRANCH_NAME" \
|
|
269
369
|
"taskFolder=$TASK_FOLDER" \
|
|
270
370
|
"status=$status" \
|
|
271
|
-
"phase=$phase" \
|
|
272
371
|
"createdAt=$ISO_NOW" \
|
|
273
372
|
"updatedAt=$ISO_NOW"
|
|
274
373
|
}
|
|
@@ -281,24 +380,25 @@ create_task() {
|
|
|
281
380
|
github)
|
|
282
381
|
command -v gh >/dev/null 2>&1 || { echo "ERROR: gh CLI is required for github tasks."; exit 1; }
|
|
283
382
|
echo "Fetching GitHub Issue #$ID..."
|
|
284
|
-
gh issue view "$ID" --json title,body,author,labels,
|
|
285
|
-
echo "# WORK: GitHub #$ID" > "$WORK_MD"
|
|
286
|
-
gh issue view "$ID" >> "$WORK_MD"
|
|
383
|
+
gh issue view "$ID" --json title,body,author,labels,url,number,createdAt,updatedAt > "$METADATA_JSON"
|
|
287
384
|
;;
|
|
288
385
|
gitlab)
|
|
289
386
|
command -v glab >/dev/null 2>&1 || { echo "ERROR: glab CLI is required for gitlab tasks."; exit 1; }
|
|
290
387
|
echo "Fetching GitLab Issue #$ID..."
|
|
291
|
-
glab issue view "$ID" > "$
|
|
292
|
-
|
|
388
|
+
if ! glab issue view "$ID" --output json > "$METADATA_JSON" 2>/dev/null; then
|
|
389
|
+
echo '{}' > "$METADATA_JSON"
|
|
390
|
+
json_upsert "$METADATA_JSON" "title=GitLab Issue $ID"
|
|
391
|
+
fi
|
|
293
392
|
;;
|
|
294
393
|
jira)
|
|
295
394
|
echo "Fetching Jira Ticket $ID..."
|
|
296
395
|
if command -v jira >/dev/null 2>&1; then
|
|
297
|
-
jira issue view "$ID" > "$WORK_MD"
|
|
298
396
|
jira issue view "$ID" --raw > "$METADATA_JSON"
|
|
299
397
|
elif command -v acli >/dev/null 2>&1; then
|
|
300
|
-
acli jira workitem view "$ID" > "$
|
|
301
|
-
|
|
398
|
+
if ! acli jira workitem view "$ID" --json > "$METADATA_JSON" 2>/dev/null; then
|
|
399
|
+
echo '{}' > "$METADATA_JSON"
|
|
400
|
+
json_upsert "$METADATA_JSON" "title=Jira $ID"
|
|
401
|
+
fi
|
|
302
402
|
else
|
|
303
403
|
echo "ERROR: jira or acli CLI is required for jira tasks."
|
|
304
404
|
exit 1
|
|
@@ -306,8 +406,8 @@ create_task() {
|
|
|
306
406
|
;;
|
|
307
407
|
local)
|
|
308
408
|
echo "Initializing local task workspace: $ID..."
|
|
309
|
-
echo "# WORK: Local Task $ID" > "$WORK_MD"
|
|
310
409
|
echo '{}' > "$METADATA_JSON"
|
|
410
|
+
json_upsert "$METADATA_JSON" "title=Local Task $ID"
|
|
311
411
|
;;
|
|
312
412
|
*)
|
|
313
413
|
echo "Unknown source: $SOURCE"
|
|
@@ -315,11 +415,8 @@ create_task() {
|
|
|
315
415
|
;;
|
|
316
416
|
esac
|
|
317
417
|
|
|
318
|
-
set_metadata_status_phase "active"
|
|
319
|
-
|
|
320
|
-
ensure_section "GRILL" "- "
|
|
321
|
-
ensure_section "PLAN" "- [ ] "
|
|
322
|
-
ensure_section "LOG" ""
|
|
418
|
+
set_metadata_status_phase "active"
|
|
419
|
+
write_work_snapshot
|
|
323
420
|
update_work_meta
|
|
324
421
|
append_log "Task initialized via /triage"
|
|
325
422
|
write_active_pointer
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
|
|
3
3
|
# validate_active_task.sh
|
|
4
|
-
# Lightweight, print-only validation of .workflow/
|
|
4
|
+
# Lightweight, print-only validation of .workflow/active.json and compatibility .workflow/active_task.json.
|
|
5
5
|
# This script never mutates task state; it only reports inconsistencies and suggested fixes.
|
|
6
6
|
|
|
7
7
|
set -euo pipefail
|
|
@@ -9,6 +9,7 @@ set -euo pipefail
|
|
|
9
9
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
10
|
TRIAGE_HELPER="$SCRIPT_DIR/triage_helper.sh"
|
|
11
11
|
REPO_ROOT=$(git rev-parse --show-toplevel)
|
|
12
|
+
ACTIVE_JSON="$REPO_ROOT/.workflow/active.json"
|
|
12
13
|
ATF="$REPO_ROOT/.workflow/active_task.json"
|
|
13
14
|
CURRENT_BRANCH=$(git branch --show-current 2>/dev/null || true)
|
|
14
15
|
WORK_MD=""
|
|
@@ -152,29 +153,52 @@ print_json_field_report() {
|
|
|
152
153
|
done
|
|
153
154
|
}
|
|
154
155
|
|
|
155
|
-
|
|
156
|
-
|
|
156
|
+
POINTER_LABEL=""
|
|
157
|
+
TASK_PATH=""
|
|
158
|
+
TASK_SOURCE=""
|
|
159
|
+
TASK_ID=""
|
|
160
|
+
ACTIVE_TASK=""
|
|
161
|
+
POINTER_BRANCH=""
|
|
162
|
+
|
|
163
|
+
if [[ -f "$ACTIVE_JSON" ]]; then
|
|
164
|
+
if ! python3 -m json.tool "$ACTIVE_JSON" >/dev/null 2>&1; then
|
|
165
|
+
error "active.json is not valid JSON: $ACTIVE_JSON"
|
|
166
|
+
exit 1
|
|
167
|
+
fi
|
|
168
|
+
POINTER_LABEL="active.json"
|
|
169
|
+
echo "Active workflow file: $ACTIVE_JSON"
|
|
170
|
+
json_pretty "$ACTIVE_JSON" || true
|
|
171
|
+
|
|
172
|
+
TASK_PATH=$(json_read "$ACTIVE_JSON" '.taskPath // .path')
|
|
173
|
+
TASK_SOURCE=$(json_read "$ACTIVE_JSON" '.source')
|
|
174
|
+
TASK_ID=$(json_read "$ACTIVE_JSON" '.sourceId // .id')
|
|
175
|
+
ACTIVE_TASK=$(json_read "$ACTIVE_JSON" '.taskId // .id')
|
|
176
|
+
POINTER_BRANCH=$(json_read "$ACTIVE_JSON" '.branch')
|
|
177
|
+
|
|
178
|
+
print_json_field_report "active.json" "$ACTIVE_JSON" workflow id taskId source sourceId taskPath path branch stateFile
|
|
179
|
+
elif [[ -f "$ATF" ]]; then
|
|
180
|
+
if ! python3 -m json.tool "$ATF" >/dev/null 2>&1; then
|
|
181
|
+
error "active_task.json is not valid JSON: $ATF"
|
|
182
|
+
exit 1
|
|
183
|
+
fi
|
|
184
|
+
POINTER_LABEL="active_task.json"
|
|
185
|
+
echo "Legacy active task file: $ATF"
|
|
186
|
+
json_pretty "$ATF" || true
|
|
187
|
+
|
|
188
|
+
TASK_PATH=$(json_read "$ATF" '.taskPath // .path')
|
|
189
|
+
TASK_SOURCE=$(json_read "$ATF" '.source')
|
|
190
|
+
TASK_ID=$(json_read "$ATF" '.sourceId // .id')
|
|
191
|
+
ACTIVE_TASK=$(json_read "$ATF" '.active_task')
|
|
192
|
+
POINTER_BRANCH=$(json_read "$ATF" '.branch')
|
|
193
|
+
|
|
194
|
+
print_json_field_report "active_task.json" "$ATF" active_task source id sourceId taskPath path branch
|
|
195
|
+
else
|
|
196
|
+
error "No active workflow pointer found at $ACTIVE_JSON or $ATF"
|
|
157
197
|
echo "Suggested fixes (dry):"
|
|
158
198
|
echo " - Run: $TRIAGE_HELPER <source> <id>"
|
|
159
199
|
exit 1
|
|
160
200
|
fi
|
|
161
201
|
|
|
162
|
-
if ! python3 -m json.tool "$ATF" >/dev/null 2>&1; then
|
|
163
|
-
error "active_task.json is not valid JSON: $ATF"
|
|
164
|
-
exit 1
|
|
165
|
-
fi
|
|
166
|
-
|
|
167
|
-
echo "Active task file: $ATF"
|
|
168
|
-
json_pretty "$ATF" || true
|
|
169
|
-
|
|
170
|
-
TASK_PATH=$(json_read "$ATF" '.taskPath // .path')
|
|
171
|
-
TASK_SOURCE=$(json_read "$ATF" '.source')
|
|
172
|
-
TASK_ID=$(json_read "$ATF" '.sourceId // .id')
|
|
173
|
-
ACTIVE_TASK=$(json_read "$ATF" '.active_task')
|
|
174
|
-
POINTER_BRANCH=$(json_read "$ATF" '.branch')
|
|
175
|
-
|
|
176
|
-
print_json_field_report "active_task.json" "$ATF" active_task source id sourceId taskPath path branch
|
|
177
|
-
|
|
178
202
|
echo
|
|
179
203
|
if [[ -n "$TASK_PATH" ]]; then
|
|
180
204
|
if [[ "$TASK_PATH" = /* ]]; then
|
|
@@ -184,7 +208,7 @@ if [[ -n "$TASK_PATH" ]]; then
|
|
|
184
208
|
fi
|
|
185
209
|
echo "Using taskPath -> checking: $WORK_MD"
|
|
186
210
|
elif [[ -n "$TASK_ID" ]]; then
|
|
187
|
-
echo "No taskPath in
|
|
211
|
+
echo "No taskPath in $POINTER_LABEL; attempting to derive from source+id"
|
|
188
212
|
if [[ -n "$TASK_SOURCE" ]]; then
|
|
189
213
|
TASK_FOLDER="$TASK_SOURCE-$TASK_ID"
|
|
190
214
|
else
|
|
@@ -193,7 +217,7 @@ elif [[ -n "$TASK_ID" ]]; then
|
|
|
193
217
|
WORK_MD="$REPO_ROOT/.workflow/tasks/$TASK_FOLDER/WORK.md"
|
|
194
218
|
echo "Derived path: $WORK_MD"
|
|
195
219
|
else
|
|
196
|
-
error "Neither taskPath/path nor sourceId/id present in
|
|
220
|
+
error "Neither taskPath/path nor sourceId/id present in $POINTER_LABEL"
|
|
197
221
|
fi
|
|
198
222
|
|
|
199
223
|
if [[ -n "$WORK_MD" && -f "$WORK_MD" ]]; then
|
|
@@ -209,7 +233,7 @@ if [[ -n "$ACTIVE_TASK" && -n "$TASK_DIR" ]]; then
|
|
|
209
233
|
if [[ "$ACTIVE_TASK" == "$ACTUAL_TASK_FOLDER" ]]; then
|
|
210
234
|
ok "active_task matches task folder ($ACTIVE_TASK)"
|
|
211
235
|
else
|
|
212
|
-
warn "
|
|
236
|
+
warn "pointer task id ($ACTIVE_TASK) differs from task folder ($ACTUAL_TASK_FOLDER)"
|
|
213
237
|
fi
|
|
214
238
|
fi
|
|
215
239
|
|
|
@@ -225,8 +249,8 @@ META_ID=""
|
|
|
225
249
|
META_SOURCE=""
|
|
226
250
|
META_TASK_FOLDER=""
|
|
227
251
|
META_STATUS=""
|
|
228
|
-
META_PHASE=""
|
|
229
252
|
META_BRANCH=""
|
|
253
|
+
DERIVED_PHASE=""
|
|
230
254
|
|
|
231
255
|
if [[ -n "$META" ]]; then
|
|
232
256
|
if [[ -f "$META" ]]; then
|
|
@@ -234,20 +258,19 @@ if [[ -n "$META" ]]; then
|
|
|
234
258
|
echo
|
|
235
259
|
echo "Metadata file: $META"
|
|
236
260
|
json_pretty "$META" || true
|
|
237
|
-
print_json_field_report "metadata.json" "$META" id source taskFolder status
|
|
261
|
+
print_json_field_report "metadata.json" "$META" id source taskFolder status branch createdAt updatedAt
|
|
238
262
|
|
|
239
263
|
META_ID=$(json_read "$META" '.id')
|
|
240
264
|
META_SOURCE=$(json_read "$META" '.source')
|
|
241
265
|
META_TASK_FOLDER=$(json_read "$META" '.taskFolder')
|
|
242
266
|
META_STATUS=$(json_read "$META" '.status')
|
|
243
|
-
META_PHASE=$(json_read "$META" '.phase')
|
|
244
267
|
META_BRANCH=$(json_read "$META" '.branch')
|
|
245
268
|
|
|
246
269
|
if [[ -n "$META_ID" && -n "$TASK_ID" && "$META_ID" != "$TASK_ID" ]]; then
|
|
247
|
-
warn "metadata.id ($META_ID) differs from
|
|
270
|
+
warn "metadata.id ($META_ID) differs from pointer id/sourceId ($TASK_ID)"
|
|
248
271
|
fi
|
|
249
272
|
if [[ -n "$META_SOURCE" && -n "$TASK_SOURCE" && "$META_SOURCE" != "$TASK_SOURCE" ]]; then
|
|
250
|
-
warn "metadata.source ($META_SOURCE) differs from
|
|
273
|
+
warn "metadata.source ($META_SOURCE) differs from pointer source ($TASK_SOURCE)"
|
|
251
274
|
fi
|
|
252
275
|
if [[ -n "$META_TASK_FOLDER" && -n "$TASK_DIR" && "$META_TASK_FOLDER" != "$(basename "$TASK_DIR")" ]]; then
|
|
253
276
|
warn "metadata.taskFolder ($META_TASK_FOLDER) differs from task folder ($(basename "$TASK_DIR"))"
|
|
@@ -275,16 +298,19 @@ if [[ -n "$WORK_MD" && -f "$WORK_MD" ]]; then
|
|
|
275
298
|
esac
|
|
276
299
|
done
|
|
277
300
|
|
|
278
|
-
if [[ "$
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
301
|
+
if [[ "$(section_body_nonempty "PLAN" "$WORK_MD")" == "yes" ]]; then
|
|
302
|
+
DERIVED_PHASE="planned"
|
|
303
|
+
elif [[ "$(section_body_nonempty "GRILL" "$WORK_MD")" == "yes" ]]; then
|
|
304
|
+
DERIVED_PHASE="grilled"
|
|
305
|
+
elif [[ "$(section_body_nonempty "BRIEF" "$WORK_MD")" == "yes" ]]; then
|
|
306
|
+
DERIVED_PHASE="framed"
|
|
307
|
+
else
|
|
308
|
+
DERIVED_PHASE="triaged"
|
|
284
309
|
fi
|
|
310
|
+
ok "derived phase from WORK.md: $DERIVED_PHASE"
|
|
285
311
|
|
|
286
|
-
if [[ "$
|
|
287
|
-
warn "phase=
|
|
312
|
+
if [[ "$DERIVED_PHASE" == "planned" && "$(has_plan_checkbox "$WORK_MD")" != "yes" ]]; then
|
|
313
|
+
warn "derived phase=planned but [PLAN] has no checkboxes"
|
|
288
314
|
fi
|
|
289
315
|
fi
|
|
290
316
|
|
|
@@ -296,12 +322,12 @@ echo
|
|
|
296
322
|
echo "Suggested fixes (dry):"
|
|
297
323
|
if [[ -n "$WORK_MD" && ! -f "$WORK_MD" ]]; then
|
|
298
324
|
echo " - Re-run triage: $TRIAGE_HELPER <source> <id>"
|
|
299
|
-
echo " - Or update
|
|
325
|
+
echo " - Or update the active workflow pointer to include a correct taskPath or source+id"
|
|
300
326
|
fi
|
|
301
327
|
if [[ -n "$META" && ! -f "$META" ]]; then
|
|
302
328
|
echo " - Resume/backfill metadata: $TRIAGE_HELPER ${TASK_SOURCE:-local} ${TASK_ID:-task} resume"
|
|
303
329
|
elif [[ -n "$META" && -f "$META" ]]; then
|
|
304
|
-
if [[ -z "$META_STATUS" || -z "$
|
|
330
|
+
if [[ -z "$META_STATUS" || -z "$(json_read "$META" '.createdAt')" || -z "$(json_read "$META" '.updatedAt')" ]]; then
|
|
305
331
|
echo " - Backfill missing metadata fields: $TRIAGE_HELPER ${TASK_SOURCE:-local} ${TASK_ID:-task} resume"
|
|
306
332
|
fi
|
|
307
333
|
fi
|
package/sync/SKILL.md
CHANGED
|
@@ -10,11 +10,13 @@ description: Synchronizes local RPIV task state (WORK.md) to external trackers (
|
|
|
10
10
|
Maintains consistency between local `.workflow` state and the remote source of truth using a single Pi-owned living status comment per task.
|
|
11
11
|
|
|
12
12
|
## Guardrails
|
|
13
|
-
- **Pre-flight**: Always read `.workflow/active.json`
|
|
13
|
+
- **Pre-flight**: Always read `.workflow/active.json` first, then compatibility `.workflow/active_task.json` only if needed, and the active `WORK.md` before executing.
|
|
14
14
|
- **Privacy**: NEVER sync secrets, environment variables, or private notes not intended for stakeholders.
|
|
15
15
|
- **Integrity**: Do not modify `[BRIEF]` or `[GRILL]` sections.
|
|
16
16
|
- **Idempotency**: If the remote Pi status already reflects the current local state, do not post or update.
|
|
17
17
|
- **Human safety**: NEVER edit human-authored comments. Only update comments/notes containing the Pi sync marker.
|
|
18
|
+
- **Target ownership**: Sync the executable child issue/MR/PR that the work completed, not the umbrella parent, unless the user explicitly asks for parent status. If the active GitHub issue has sub-issues, verify the PR/body/current request points to the right child before posting.
|
|
19
|
+
- **Shell safety**: Never pass markdown bodies inline through shell strings. Write bodies to files and use `--body-file` or JSON `--input` API calls so backticks and `$()` cannot execute.
|
|
18
20
|
|
|
19
21
|
## Living status marker
|
|
20
22
|
Every sync body MUST include this marker at the end:
|
|
@@ -46,7 +48,12 @@ Do **not** use latest-comment ownership as the primary decision. Latest-comment-
|
|
|
46
48
|
## Workflow
|
|
47
49
|
|
|
48
50
|
### 1. Discovery & State Loading
|
|
49
|
-
- Identify the platform and ID from `.workflow/active.json
|
|
51
|
+
- Identify the platform and ID from `.workflow/active.json`, falling back to compatibility `.workflow/active_task.json` only when required.
|
|
52
|
+
- For GitHub, check hierarchy before syncing umbrella issues:
|
|
53
|
+
```bash
|
|
54
|
+
gh issue view <id> --json parent,subIssues --jq '{parent:.parent, subIssues:.subIssues}'
|
|
55
|
+
```
|
|
56
|
+
If the issue has sub-issues and the work maps to one child, switch the sync target to that child or ask once. Do not sync a child deliverable to the parent just because the active task points at the parent.
|
|
50
57
|
- Extract **Slices** from `[PLAN]`, **Status** from `[LOG]`, and **Artifacts** such as PR/MR links, commit hashes, and verification output.
|
|
51
58
|
|
|
52
59
|
### 2. Payload Preparation
|
|
@@ -81,8 +88,9 @@ gh api repos/:owner/:repo/issues/<id>/comments --paginate \
|
|
|
81
88
|
|
|
82
89
|
Update:
|
|
83
90
|
```bash
|
|
91
|
+
jq -n --rawfile body body.md '{body: $body}' > body.json
|
|
84
92
|
gh api -X PATCH repos/:owner/:repo/issues/comments/<comment_id> \
|
|
85
|
-
|
|
93
|
+
--input body.json
|
|
86
94
|
```
|
|
87
95
|
|
|
88
96
|
Create:
|
|
@@ -94,6 +102,7 @@ Rules:
|
|
|
94
102
|
- update only a comment containing the marker
|
|
95
103
|
- no-op when normalized body is already current
|
|
96
104
|
- create only when no marker comment exists
|
|
105
|
+
- keep markdown in files; do not use inline `-f body="$(cat body.md)"` or shell-expanded PR/comment bodies
|
|
97
106
|
|
|
98
107
|
#### GitLab Issues / MRs
|
|
99
108
|
Use notes API for issues or merge requests.
|
package/triage/SKILL.md
CHANGED
|
@@ -10,10 +10,10 @@ description: "Ingest or resume a tracked/local task in the RPIV workspace. Use w
|
|
|
10
10
|
Start RPIV by creating, resuming, or explicitly reopening a task workspace.
|
|
11
11
|
|
|
12
12
|
## Guardrails
|
|
13
|
-
- READ: user argument, `.workflow/active.json`
|
|
14
|
-
- WRITE: `.workflow/tasks/[source-id]/WORK.md`, `.workflow/tasks/[source-id]/metadata.json`, `.workflow/active.json
|
|
13
|
+
- READ: user argument, `.workflow/active.json` first and compatibility `.workflow/active_task.json` only if present/needed, target `metadata.json`, and target `WORK.md` if resuming.
|
|
14
|
+
- WRITE: `.workflow/tasks/[source-id]/WORK.md`, `.workflow/tasks/[source-id]/metadata.json`, `.workflow/active.json`.
|
|
15
15
|
- On **create**, initialize required guarded sections only if absent: `[BRIEF]`, `[GRILL]`, `[PLAN]`, `[LOG]`, `[META]`.
|
|
16
|
-
- On **resume**, only update `.workflow/active.json`,
|
|
16
|
+
- On **resume**, only update `.workflow/active.json`, metadata timestamps/state as needed, and `[META]`, then append one concise `[LOG]` entry.
|
|
17
17
|
- NEVER: duplicate guarded sections.
|
|
18
18
|
- NEVER: overwrite existing `[BRIEF]`, `[GRILL]`, or `[PLAN]` during triage.
|
|
19
19
|
- NEVER: create `PROBLEM.md`, `PRD.md`, `PLAN.md`, or `EVIDENCE.md`.
|
|
@@ -60,17 +60,14 @@ Start RPIV by creating, resuming, or explicitly reopening a task workspace.
|
|
|
60
60
|
- Preferred absolute path: `<skill_dir>/../scripts/triage_helper.sh`.
|
|
61
61
|
3. Read the resulting active pointer, metadata, and `WORK.md`.
|
|
62
62
|
4. Determine action based on helper output and task state: `created`, `resumed`, `reopened`, `refused`, `fresh/reset`.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
- The helper must ensure `.reposcry/` is ignored, must stop if `.reposcry/` tracked/staged, and continue normally if RepoScry is unavailable.
|
|
67
|
-
6. Verify branch: ensure current branch matches metadata `branch` (or is `main`/`master` for planning).
|
|
68
|
-
7. If task was newly created, infer classification:
|
|
63
|
+
6. New task creation should store structured tracker facts in `metadata.json` and write a concise normalized intake snapshot into `WORK.md`; never paste raw tracker CLI rendering.
|
|
64
|
+
7. Verify branch: ensure current branch matches metadata `branch` (or is `main`/`master` for planning).
|
|
65
|
+
8. If task was newly created, infer classification:
|
|
69
66
|
- `github:`, `gitlab:`, `jira:` labels/type often indicate Problem (bug) or Proposal (feature).
|
|
70
67
|
- Local tasks default to Proposal unless specified.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
9. If resuming, check `[BRIEF]` and `[PLAN]` completion status.
|
|
69
|
+
10. **Final Guidance**: record current branch in `[META]`. For `jira:` tasks, preserve the Jira key in `[META]`. Planning on `main`/`master` allowed; implementation will use `/implement` branch enforcement.
|
|
70
|
+
11. End by recommending next valid command:
|
|
74
71
|
- newly created -> `/frame`
|
|
75
72
|
- existing empty `[BRIEF]` -> `/frame`
|
|
76
73
|
- framed but not grilled -> `/grill-with-docs`
|
|
@@ -85,6 +82,5 @@ End with:
|
|
|
85
82
|
- **Status**: active / blocked / done / archived / unknown
|
|
86
83
|
- **Phase**: triaged / framed / grilled / planned / implementing / verifying / synced / closed / unknown
|
|
87
84
|
- **Branch**: current branch
|
|
88
|
-
- **Repo Pulse**: Warm / Cold / Missing / Not applicable
|
|
89
85
|
- **Classification**: Problem / Proposal
|
|
90
86
|
- **Next step**: `/frame`, `/grill-with-docs`, `/plan`, `/implement`, or explicit reopen/fresh choice
|
package/verify/SKILL.md
CHANGED
|
@@ -10,7 +10,7 @@ description: Verify the active slice or task against WORK.md, quality gates, and
|
|
|
10
10
|
The final gate for a slice or task. Verify truth before reporting progress.
|
|
11
11
|
|
|
12
12
|
## Guardrails
|
|
13
|
-
- READ: `.workflow/active.json`
|
|
13
|
+
- READ: `.workflow/active.json` first, then compatibility `.workflow/active_task.json` only if needed, plus active `WORK.md` `[BRIEF]`, `[PLAN]`, and `[LOG]`.
|
|
14
14
|
- WRITE: `WORK.md` -> `[PLAN]` checkboxes and append to `[LOG]` only.
|
|
15
15
|
- NEVER: add `Signed-off-by`; tell the human to sign if needed.
|
|
16
16
|
- NEVER: transition tracker state if verification fails.
|
|
@@ -19,9 +19,8 @@ The final gate for a slice or task. Verify truth before reporting progress.
|
|
|
19
19
|
## Workflow
|
|
20
20
|
1. Compare code changes against `[BRIEF]` and the current `[PLAN]` slice.
|
|
21
21
|
2. Run stated verification commands and available quality gates.
|
|
22
|
-
3. If RepoScry is available, optionally add graph-aware evidence with commands such as `reposcry validate main HEAD` and `reposcry --repo . get_affected_flows main HEAD`. Treat RepoScry as supplemental evidence, not a hard requirement. Verify `.reposcry/` is not staged or tracked before reporting review readiness.
|
|
23
22
|
4. Check for AI artifacts: placeholder comments, fake APIs, dead code, inconsistent naming.
|
|
24
|
-
5. Confirm
|
|
23
|
+
5. Confirm commits and PR/MR metadata satisfy the repository's own hooks, CI, and release policies. Do not invent or enforce extra RPIV-specific formatting here.
|
|
25
24
|
6. If passing, mark the slice checkbox complete in `[PLAN]` and append verification evidence to `[LOG]` (Format: `YYYY-MM-DD hh:mm AM/PM`).
|
|
26
25
|
7. Recommend `/sync` for tracker update, or `/post-merge-prune` if the task is fully merged and user approves.
|
|
27
26
|
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
|
|
3
|
-
set -euo pipefail
|
|
4
|
-
|
|
5
|
-
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
6
|
-
cd "$REPO_ROOT"
|
|
7
|
-
|
|
8
|
-
ensure_reposcry_gitignore() {
|
|
9
|
-
local gitignore=".gitignore"
|
|
10
|
-
touch "$gitignore"
|
|
11
|
-
|
|
12
|
-
if ! grep -qxF ".reposcry/" "$gitignore"; then
|
|
13
|
-
{
|
|
14
|
-
printf '\n# RepoScry local cache\n'
|
|
15
|
-
printf '.reposcry/\n'
|
|
16
|
-
} >> "$gitignore"
|
|
17
|
-
echo "Added .reposcry/ to .gitignore"
|
|
18
|
-
fi
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
refuse_tracked_reposcry_cache() {
|
|
22
|
-
local tracked staged
|
|
23
|
-
tracked=$(git ls-files .reposcry 2>/dev/null || true)
|
|
24
|
-
staged=$(git diff --cached --name-only -- .reposcry 2>/dev/null || true)
|
|
25
|
-
|
|
26
|
-
if [[ -n "$tracked" || -n "$staged" ]]; then
|
|
27
|
-
echo "ERROR: .reposcry/ cache is tracked or staged. It is generated local state and must not be committed." >&2
|
|
28
|
-
if [[ -n "$tracked" ]]; then
|
|
29
|
-
echo "Tracked files:" >&2
|
|
30
|
-
printf '%s\n' "$tracked" >&2
|
|
31
|
-
fi
|
|
32
|
-
if [[ -n "$staged" ]]; then
|
|
33
|
-
echo "Staged files:" >&2
|
|
34
|
-
printf '%s\n' "$staged" >&2
|
|
35
|
-
fi
|
|
36
|
-
echo "Fix: git rm --cached -r .reposcry && keep .reposcry/ in .gitignore" >&2
|
|
37
|
-
exit 1
|
|
38
|
-
fi
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
reposcry_indexed_files() {
|
|
42
|
-
reposcry stats 2>/dev/null | awk '/Files indexed:/ {print $3; exit}'
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
# Quick health check for Pulse info
|
|
46
|
-
check_pulse() {
|
|
47
|
-
if ! command -v reposcry >/dev/null 2>&1; then
|
|
48
|
-
echo "PULSE: Missing (Not installed)"
|
|
49
|
-
return
|
|
50
|
-
fi
|
|
51
|
-
if [[ ! -d .reposcry ]]; then
|
|
52
|
-
echo "PULSE: Cold (No cache)"
|
|
53
|
-
return
|
|
54
|
-
fi
|
|
55
|
-
local files
|
|
56
|
-
files=$(reposcry_indexed_files || echo "0")
|
|
57
|
-
if [[ "$files" == "0" ]]; then
|
|
58
|
-
echo "PULSE: Cold (Zero files)"
|
|
59
|
-
else
|
|
60
|
-
echo "PULSE: Warm ($files files)"
|
|
61
|
-
fi
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if [[ "${1:-}" == "--pulse" ]]; then
|
|
65
|
-
check_pulse
|
|
66
|
-
exit 0
|
|
67
|
-
fi
|
|
68
|
-
|
|
69
|
-
ensure_reposcry_gitignore
|
|
70
|
-
refuse_tracked_reposcry_cache
|
|
71
|
-
|
|
72
|
-
if ! command -v reposcry >/dev/null 2>&1; then
|
|
73
|
-
echo "RepoScry not installed; skipping bootstrap."
|
|
74
|
-
exit 0
|
|
75
|
-
fi
|
|
76
|
-
|
|
77
|
-
HAD_REPOSCRYIGNORE=0
|
|
78
|
-
[[ -f .reposcryignore ]] && HAD_REPOSCRYIGNORE=1
|
|
79
|
-
|
|
80
|
-
FORCE_REINDEX=0
|
|
81
|
-
if [[ "${1:-}" == "--force" || "${1:-}" == "--reindex" ]]; then
|
|
82
|
-
FORCE_REINDEX=1
|
|
83
|
-
fi
|
|
84
|
-
|
|
85
|
-
if [[ ! -d .reposcry ]]; then
|
|
86
|
-
echo "Initializing RepoScry..."
|
|
87
|
-
reposcry init >/dev/null 2>&1 || reposcry init || {
|
|
88
|
-
echo "RepoScry init failed; continuing without RepoScry." >&2
|
|
89
|
-
exit 0
|
|
90
|
-
}
|
|
91
|
-
fi
|
|
92
|
-
|
|
93
|
-
INDEXED_FILES=$(reposcry_indexed_files || true)
|
|
94
|
-
INDEXED_FILES=${INDEXED_FILES:-0}
|
|
95
|
-
|
|
96
|
-
if [[ $FORCE_REINDEX -eq 1 || "$INDEXED_FILES" == "0" ]]; then
|
|
97
|
-
echo "Indexing repository with RepoScry (--no-semantic)..."
|
|
98
|
-
reposcry index --no-semantic || {
|
|
99
|
-
echo "RepoScry index failed; continuing without RepoScry." >&2
|
|
100
|
-
exit 0
|
|
101
|
-
}
|
|
102
|
-
else
|
|
103
|
-
echo "RepoScry index already present ($INDEXED_FILES files indexed)."
|
|
104
|
-
fi
|
|
105
|
-
|
|
106
|
-
if [[ $HAD_REPOSCRYIGNORE -eq 0 && -f .reposcryignore ]]; then
|
|
107
|
-
echo "RepoScry created .reposcryignore. Review and commit it if you want stable RepoScry indexing rules."
|
|
108
|
-
fi
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
|
|
3
|
-
set -euo pipefail
|
|
4
|
-
|
|
5
|
-
BASE_REF=${1:-${REPOSCRY_BASE_REF:-main}}
|
|
6
|
-
|
|
7
|
-
if ! command -v reposcry-update >/dev/null 2>&1; then
|
|
8
|
-
echo "reposcry-update not installed; skipping refresh."
|
|
9
|
-
exit 0
|
|
10
|
-
fi
|
|
11
|
-
|
|
12
|
-
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
13
|
-
cd "$REPO_ROOT"
|
|
14
|
-
|
|
15
|
-
echo "Refreshing RepoScry for changed files against base '$BASE_REF'..."
|
|
16
|
-
if ! reposcry-update --changed --base "$BASE_REF"; then
|
|
17
|
-
echo "RepoScry refresh failed; continuing without refreshed graph state." >&2
|
|
18
|
-
exit 0
|
|
19
|
-
fi
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
|
|
3
|
-
set -euo pipefail
|
|
4
|
-
|
|
5
|
-
TASK_TEXT=${*:-}
|
|
6
|
-
if [[ -z "$TASK_TEXT" ]]; then
|
|
7
|
-
echo "Usage: $0 <task summary>"
|
|
8
|
-
exit 1
|
|
9
|
-
fi
|
|
10
|
-
|
|
11
|
-
if ! command -v reposcry >/dev/null 2>&1; then
|
|
12
|
-
echo "RepoScry not installed; skipping task context generation."
|
|
13
|
-
exit 0
|
|
14
|
-
fi
|
|
15
|
-
|
|
16
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
17
|
-
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
18
|
-
cd "$REPO_ROOT"
|
|
19
|
-
|
|
20
|
-
"$SCRIPT_DIR/reposcry-bootstrap.sh" || true
|
|
21
|
-
|
|
22
|
-
mkdir -p .reposcry
|
|
23
|
-
|
|
24
|
-
# Detect low-token modes (Caveman, etc.)
|
|
25
|
-
DEFAULT_BUDGET=20000
|
|
26
|
-
if [[ "${CAVEMAN_MODE:-off}" != "off" ]] || [[ "${LOW_TOKEN_MODE:-0}" == "1" ]]; then
|
|
27
|
-
DEFAULT_BUDGET=5000
|
|
28
|
-
echo "Low-token mode detected: reducing context budget to $DEFAULT_BUDGET"
|
|
29
|
-
fi
|
|
30
|
-
|
|
31
|
-
BUDGET=${REPOSCRY_CONTEXT_BUDGET:-$DEFAULT_BUDGET}
|
|
32
|
-
OUTPUT_PATH=".reposcry/AI_CONTEXT.md"
|
|
33
|
-
|
|
34
|
-
echo "Generating RepoScry task context at $OUTPUT_PATH..."
|
|
35
|
-
if reposcry context "$TASK_TEXT" --strict --budget "$BUDGET" --format markdown > "$OUTPUT_PATH"; then
|
|
36
|
-
echo "Wrote $OUTPUT_PATH"
|
|
37
|
-
else
|
|
38
|
-
echo "RepoScry context generation failed; leaving RPIV flow unchanged." >&2
|
|
39
|
-
rm -f "$OUTPUT_PATH"
|
|
40
|
-
fi
|