@raquezha/norpiv 0.0.2
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 +154 -0
- package/bin/norpiv-install.cjs +166 -0
- package/cleanup/SKILL.md +53 -0
- package/frame/SKILL.md +38 -0
- package/grill-with-docs/SKILL.md +43 -0
- package/implement/SKILL.md +132 -0
- package/implement/scripts/enforce-branch.sh +122 -0
- package/package.json +49 -0
- package/plan/SKILL.md +34 -0
- package/scripts/reposcry-bootstrap.sh +84 -0
- package/scripts/reposcry-refresh.sh +19 -0
- package/scripts/reposcry-task-context.sh +32 -0
- package/scripts/triage_helper.sh +401 -0
- package/scripts/validate_active_task.sh +328 -0
- package/sync/SKILL.md +131 -0
- package/sync/design-brief.md +39 -0
- package/sync/jira_smart_sync.sh +156 -0
- package/triage/SKILL.md +102 -0
- package/update-docs/SKILL.md +32 -0
- package/update-docs/references/doc-destination-map.md +31 -0
- package/verify/SKILL.md +32 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# enforce-branch.sh
|
|
4
|
+
# Verifies the active branch before implementation.
|
|
5
|
+
# Prevents working on main/master and ensures alignment with task metadata.
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
json_read() {
|
|
10
|
+
local file="$1"
|
|
11
|
+
local expr="$2"
|
|
12
|
+
python3 - "$file" "$expr" <<'PY'
|
|
13
|
+
import json
|
|
14
|
+
import re
|
|
15
|
+
import sys
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
path = Path(sys.argv[1])
|
|
19
|
+
expr = sys.argv[2]
|
|
20
|
+
try:
|
|
21
|
+
data = json.loads(path.read_text())
|
|
22
|
+
except Exception:
|
|
23
|
+
print("")
|
|
24
|
+
raise SystemExit
|
|
25
|
+
|
|
26
|
+
for part in [p.strip() for p in expr.split("//")]:
|
|
27
|
+
if part == "empty":
|
|
28
|
+
continue
|
|
29
|
+
match = re.fullmatch(r"\.([A-Za-z_][A-Za-z0-9_]*)", part)
|
|
30
|
+
if not match or not isinstance(data, dict):
|
|
31
|
+
continue
|
|
32
|
+
value = data.get(match.group(1))
|
|
33
|
+
if value not in (None, ""):
|
|
34
|
+
print(value)
|
|
35
|
+
raise SystemExit
|
|
36
|
+
print("")
|
|
37
|
+
PY
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
json_pretty() {
|
|
41
|
+
local file="$1"
|
|
42
|
+
python3 -m json.tool "$file"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# Find repo root to ensure paths are absolute and reliable
|
|
46
|
+
REPO_ROOT=$(git rev-parse --show-toplevel)
|
|
47
|
+
ACTIVE_TASK_FILE="$REPO_ROOT/.workflow/active_task.json"
|
|
48
|
+
|
|
49
|
+
if [[ ! -f "$ACTIVE_TASK_FILE" ]]; then
|
|
50
|
+
echo "ERROR: No active task found at $ACTIVE_TASK_FILE."
|
|
51
|
+
echo "Please run /triage [source]:[id] first."
|
|
52
|
+
exit 1
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# Read canonical active task fields
|
|
56
|
+
TASK_SOURCE=$(json_read "$ACTIVE_TASK_FILE" '.source // empty')
|
|
57
|
+
TASK_ID=$(json_read "$ACTIVE_TASK_FILE" '.sourceId // .id // empty')
|
|
58
|
+
TASK_PATH=$(json_read "$ACTIVE_TASK_FILE" '.taskPath // .path // empty')
|
|
59
|
+
|
|
60
|
+
# Prefer explicit taskPath when available (most reliable)
|
|
61
|
+
if [[ -n "$TASK_PATH" ]]; then
|
|
62
|
+
echo "INFO: Using taskPath from active_task.json: $TASK_PATH"
|
|
63
|
+
if [[ "$TASK_PATH" = /* ]]; then
|
|
64
|
+
WORK_MD="$TASK_PATH/WORK.md"
|
|
65
|
+
else
|
|
66
|
+
WORK_MD="$REPO_ROOT/$TASK_PATH/WORK.md"
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# If no taskPath, but we have a source+id, construct the folder name as '<source>-<id>'
|
|
70
|
+
elif [[ -n "$TASK_ID" ]]; then
|
|
71
|
+
echo "INFO: taskPath missing; deriving task folder from source+id"
|
|
72
|
+
if [[ -n "$TASK_SOURCE" ]]; then
|
|
73
|
+
TASK_FOLDER="${TASK_SOURCE}-${TASK_ID}"
|
|
74
|
+
else
|
|
75
|
+
TASK_FOLDER="$TASK_ID"
|
|
76
|
+
fi
|
|
77
|
+
WORK_MD="$REPO_ROOT/.workflow/tasks/$TASK_FOLDER/WORK.md"
|
|
78
|
+
|
|
79
|
+
else
|
|
80
|
+
echo "ERROR: No 'sourceId' or 'taskPath' found in $ACTIVE_TASK_FILE."
|
|
81
|
+
echo "Please run /triage [source]:[id] to initialize a task. Here are the file contents for debugging:"
|
|
82
|
+
json_pretty "$ACTIVE_TASK_FILE" || true
|
|
83
|
+
exit 1
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# Diagnostic output for easier debugging when things go wrong
|
|
87
|
+
echo "DEBUG: Computed WORK.md path: $WORK_MD"
|
|
88
|
+
|
|
89
|
+
if [[ ! -f "$WORK_MD" ]]; then
|
|
90
|
+
echo "ERROR: WORK.md not found at $WORK_MD."
|
|
91
|
+
echo "Checked active_task.json values:"
|
|
92
|
+
json_pretty "$ACTIVE_TASK_FILE" || true
|
|
93
|
+
echo "Please ensure the task was initialized with /triage and that WORK.md exists."
|
|
94
|
+
exit 1
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
CURRENT_BRANCH=$(git branch --show-current)
|
|
98
|
+
echo "Current branch: $CURRENT_BRANCH"
|
|
99
|
+
|
|
100
|
+
# Check if we are on a protected branch
|
|
101
|
+
if [[ "$CURRENT_BRANCH" == "main" || "$CURRENT_BRANCH" == "master" ]]; then
|
|
102
|
+
echo "WARNING: Currently on protected branch '$CURRENT_BRANCH'."
|
|
103
|
+
|
|
104
|
+
# Generate a safe task branch name
|
|
105
|
+
# Prefer using the active_task folder name if available, else sanitize TASK_ID
|
|
106
|
+
if [[ -n "${TASK_FOLDER:-}" ]]; then
|
|
107
|
+
CLEAN_ID=$(echo "$TASK_FOLDER" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//;s/-$//')
|
|
108
|
+
else
|
|
109
|
+
CLEAN_ID=$(echo "$TASK_ID" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//;s/-$//')
|
|
110
|
+
fi
|
|
111
|
+
NEW_BRANCH="feat/${CLEAN_ID}"
|
|
112
|
+
|
|
113
|
+
echo "ACTION: Creating and switching to task branch: $NEW_BRANCH"
|
|
114
|
+
git checkout -b "$NEW_BRANCH"
|
|
115
|
+
echo "SUCCESS: Branch switched to $NEW_BRANCH."
|
|
116
|
+
echo "AGENT_INSTRUCTION: Update [META] in WORK.md to record 'Branch: $NEW_BRANCH'."
|
|
117
|
+
exit 0
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
echo "Branch is not protected. Safe to proceed."
|
|
121
|
+
echo "AGENT_INSTRUCTION: Ensure $CURRENT_BRANCH is recorded in [META] of WORK.md."
|
|
122
|
+
exit 0
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@raquezha/norpiv",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "Bundled RPIV workflow skills for Pi, Claude, and Codex-style coding agents",
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"bin": {
|
|
7
|
+
"norpiv-install": "bin/norpiv-install.cjs"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"cleanup",
|
|
12
|
+
"frame",
|
|
13
|
+
"grill-with-docs",
|
|
14
|
+
"implement",
|
|
15
|
+
"plan",
|
|
16
|
+
"scripts",
|
|
17
|
+
"sync",
|
|
18
|
+
"triage",
|
|
19
|
+
"update-docs",
|
|
20
|
+
"verify",
|
|
21
|
+
"README.md"
|
|
22
|
+
],
|
|
23
|
+
"keywords": [
|
|
24
|
+
"agent-skills",
|
|
25
|
+
"pi-agent",
|
|
26
|
+
"claude-skills",
|
|
27
|
+
"codex",
|
|
28
|
+
"workflow",
|
|
29
|
+
"rpiv",
|
|
30
|
+
"pi-package"
|
|
31
|
+
],
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"pi": {
|
|
37
|
+
"skills": [
|
|
38
|
+
"triage",
|
|
39
|
+
"frame",
|
|
40
|
+
"grill-with-docs",
|
|
41
|
+
"plan",
|
|
42
|
+
"implement",
|
|
43
|
+
"verify",
|
|
44
|
+
"sync",
|
|
45
|
+
"update-docs",
|
|
46
|
+
"cleanup"
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
}
|
package/plan/SKILL.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plan
|
|
3
|
+
description: Create or revise vertical implementation slices in the active WORK.md. Use after /grill-with-docs to produce a concise, reviewable plan for implementation.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: plan
|
|
7
|
+
|
|
8
|
+
Map the "how" into tracer-bullet vertical slices.
|
|
9
|
+
|
|
10
|
+
## Guardrails
|
|
11
|
+
- READ: `.workflow/active_task.json` then active `WORK.md` `[BRIEF]` and `[GRILL]`.
|
|
12
|
+
- WRITE: `WORK.md` -> `[PLAN]` and append to `[LOG]` only.
|
|
13
|
+
- NEVER: implement code during planning.
|
|
14
|
+
- NEVER: create standalone `PLAN.md`.
|
|
15
|
+
- NEVER: ask whether to plan if the user invoked `/plan`; produce the plan.
|
|
16
|
+
|
|
17
|
+
## Workflow
|
|
18
|
+
1. Read the brief and grill decisions.
|
|
19
|
+
2. **Branch Check**: Verify the current git branch. Planning on `main` is safe and encouraged. If you are on an unrelated feature branch, warn the human that the plan is being made on a stale or mismatched context.
|
|
20
|
+
3. Draft thin vertical slices that are independently verifiable.
|
|
21
|
+
4. Mark each slice:
|
|
22
|
+
- **AFK**: agent can implement with clear checks.
|
|
23
|
+
- **HITL**: human judgment, product decision, external access, or manual review required.
|
|
24
|
+
5. Include dependencies and verification command(s) per slice.
|
|
25
|
+
6. Write the plan into `[PLAN]` with checkboxes.
|
|
26
|
+
7. **Log Activity**: Append a timestamped entry to `[LOG]` summarizing the plan or revision (Format: `YYYY-MM-DD hh:mm AM/PM`).
|
|
27
|
+
8. Recommend `/sync` if the task has a tracker, then `/implement`.
|
|
28
|
+
|
|
29
|
+
## Output contract
|
|
30
|
+
End with:
|
|
31
|
+
- **Slices**: count and names
|
|
32
|
+
- **AFK/HITL split**
|
|
33
|
+
- **Verification commands**
|
|
34
|
+
- **Next step**: `/sync` or `/implement`
|
|
@@ -0,0 +1,84 @@
|
|
|
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
|
+
ensure_reposcry_gitignore
|
|
46
|
+
refuse_tracked_reposcry_cache
|
|
47
|
+
|
|
48
|
+
if ! command -v reposcry >/dev/null 2>&1; then
|
|
49
|
+
echo "RepoScry not installed; skipping bootstrap."
|
|
50
|
+
exit 0
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
HAD_REPOSCRYIGNORE=0
|
|
54
|
+
[[ -f .reposcryignore ]] && HAD_REPOSCRYIGNORE=1
|
|
55
|
+
|
|
56
|
+
FORCE_REINDEX=0
|
|
57
|
+
if [[ "${1:-}" == "--force" || "${1:-}" == "--reindex" ]]; then
|
|
58
|
+
FORCE_REINDEX=1
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
if [[ ! -d .reposcry ]]; then
|
|
62
|
+
echo "Initializing RepoScry..."
|
|
63
|
+
reposcry init >/dev/null 2>&1 || reposcry init || {
|
|
64
|
+
echo "RepoScry init failed; continuing without RepoScry." >&2
|
|
65
|
+
exit 0
|
|
66
|
+
}
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
INDEXED_FILES=$(reposcry_indexed_files || true)
|
|
70
|
+
INDEXED_FILES=${INDEXED_FILES:-0}
|
|
71
|
+
|
|
72
|
+
if [[ $FORCE_REINDEX -eq 1 || "$INDEXED_FILES" == "0" ]]; then
|
|
73
|
+
echo "Indexing repository with RepoScry (--no-semantic)..."
|
|
74
|
+
reposcry index --no-semantic || {
|
|
75
|
+
echo "RepoScry index failed; continuing without RepoScry." >&2
|
|
76
|
+
exit 0
|
|
77
|
+
}
|
|
78
|
+
else
|
|
79
|
+
echo "RepoScry index already present ($INDEXED_FILES files indexed)."
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
if [[ $HAD_REPOSCRYIGNORE -eq 0 && -f .reposcryignore ]]; then
|
|
83
|
+
echo "RepoScry created .reposcryignore. Review and commit it if you want stable RepoScry indexing rules."
|
|
84
|
+
fi
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
|
@@ -0,0 +1,32 @@
|
|
|
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
|
+
BUDGET=${REPOSCRY_CONTEXT_BUDGET:-20000}
|
|
24
|
+
OUTPUT_PATH=".reposcry/AI_CONTEXT.md"
|
|
25
|
+
|
|
26
|
+
echo "Generating RepoScry task context at $OUTPUT_PATH..."
|
|
27
|
+
if reposcry context "$TASK_TEXT" --strict --budget "$BUDGET" --format markdown > "$OUTPUT_PATH"; then
|
|
28
|
+
echo "Wrote $OUTPUT_PATH"
|
|
29
|
+
else
|
|
30
|
+
echo "RepoScry context generation failed; leaving RPIV flow unchanged." >&2
|
|
31
|
+
rm -f "$OUTPUT_PATH"
|
|
32
|
+
fi
|