@nklisch/pi-agile-workflow 0.15.3
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/.claude-plugin/plugin.json +10 -0
- package/.codex-plugin/plugin.json +28 -0
- package/CHANGELOG.md +591 -0
- package/README.md +107 -0
- package/docs/ARCHITECTURE.md +716 -0
- package/docs/MIGRATION.md +419 -0
- package/docs/PRINCIPLES.md +464 -0
- package/docs/ROADMAP.md +95 -0
- package/docs/SPEC.md +855 -0
- package/docs/VISION.md +106 -0
- package/extensions/agile-workflow.test.ts +419 -0
- package/extensions/agile-workflow.ts +467 -0
- package/hooks/hooks.json +52 -0
- package/hooks/scripts/prompt-context.py +628 -0
- package/hooks/scripts/substrate-maintainer.py +359 -0
- package/hooks/scripts/test_prompt_context.py +757 -0
- package/package.json +30 -0
- package/scripts/install-work-view.sh +165 -0
- package/scripts/tests/agent-metadata.test.sh +75 -0
- package/scripts/tests/bump-version.test.sh +552 -0
- package/scripts/tests/channel-parity.test.sh +117 -0
- package/scripts/tests/convert-content-integrity.test.sh +246 -0
- package/scripts/tests/convert-install-routing.test.sh +159 -0
- package/scripts/tests/install-work-view.test.sh +411 -0
- package/scripts/tests/pi-package-metadata.test.sh +119 -0
- package/scripts/tests/work-board-shim.test.sh +239 -0
- package/scripts/tests/work-view-dist-version.test.sh +105 -0
- package/scripts/work-board.sh +64 -0
- package/scripts/work-view.sh +423 -0
- package/skills/autopilot/SKILL.md +330 -0
- package/skills/autopilot/agents/openai.yaml +6 -0
- package/skills/board/SKILL.md +62 -0
- package/skills/board/agents/openai.yaml +6 -0
- package/skills/bold-refactor/SKILL.md +244 -0
- package/skills/bold-refactor/agents/openai.yaml +6 -0
- package/skills/bug-scan/SKILL.md +378 -0
- package/skills/bug-scan/agents/openai.yaml +6 -0
- package/skills/bug-scan/references/async-promises.md +119 -0
- package/skills/bug-scan/references/concurrency-races.md +148 -0
- package/skills/bug-scan/references/data-layer.md +167 -0
- package/skills/bug-scan/references/error-handling.md +197 -0
- package/skills/bug-scan/references/gate-item-template.md +94 -0
- package/skills/bug-scan/references/language-footguns.md +219 -0
- package/skills/bug-scan/references/parked-item-template.md +103 -0
- package/skills/bug-scan/references/report-template.md +115 -0
- package/skills/bug-scan/references/resource-leaks.md +199 -0
- package/skills/bug-scan/references/state-closures.md +148 -0
- package/skills/bug-scan/references/time-numbers.md +158 -0
- package/skills/convert/SKILL.md +1463 -0
- package/skills/convert/agents/openai.yaml +6 -0
- package/skills/convert/references/legacy-overlap-migration.md +179 -0
- package/skills/deep-code-scan/SKILL.md +378 -0
- package/skills/deep-code-scan/agents/openai.yaml +6 -0
- package/skills/deep-code-scan/references/artifact-ledger.md +148 -0
- package/skills/deep-code-scan/references/consolidation.md +91 -0
- package/skills/deep-code-scan/references/decomposition.md +127 -0
- package/skills/deep-code-scan/references/item-templates.md +263 -0
- package/skills/deep-code-scan/references/lane-catalog.md +129 -0
- package/skills/deep-code-scan/references/review-gauntlet.md +108 -0
- package/skills/deep-code-scan/references/scanner-brief.md +152 -0
- package/skills/e2e-test-design/SKILL.md +517 -0
- package/skills/e2e-test-design/references/anti-tautology.md +261 -0
- package/skills/e2e-test-design/references/service-mocks.md +234 -0
- package/skills/e2e-test-design/references/test-taxonomy.md +211 -0
- package/skills/epic-design/SKILL.md +476 -0
- package/skills/epicize/SKILL.md +210 -0
- package/skills/epicize/agents/openai.yaml +6 -0
- package/skills/feature-design/SKILL.md +472 -0
- package/skills/fix/SKILL.md +173 -0
- package/skills/gate-cruft/SKILL.md +258 -0
- package/skills/gate-docs/SKILL.md +278 -0
- package/skills/gate-patterns/SKILL.md +389 -0
- package/skills/gate-refactor/SKILL.md +408 -0
- package/skills/gate-security/SKILL.md +275 -0
- package/skills/gate-tests/SKILL.md +367 -0
- package/skills/groom/SKILL.md +162 -0
- package/skills/groom/agents/openai.yaml +6 -0
- package/skills/ideate/SKILL.md +185 -0
- package/skills/ideate/agents/openai.yaml +6 -0
- package/skills/implement/SKILL.md +288 -0
- package/skills/implement-orchestrator/SKILL.md +604 -0
- package/skills/park/SKILL.md +111 -0
- package/skills/perf-design/SKILL.md +427 -0
- package/skills/perf-scout/SKILL.md +404 -0
- package/skills/perf-scout/agents/openai.yaml +6 -0
- package/skills/perf-scout/references/algorithmic-and-data-structures.md +126 -0
- package/skills/perf-scout/references/approximation-and-precomputation.md +136 -0
- package/skills/perf-scout/references/caching-and-memoization.md +123 -0
- package/skills/perf-scout/references/compiler-runtime-and-language.md +136 -0
- package/skills/perf-scout/references/database-and-storage-internals.md +144 -0
- package/skills/perf-scout/references/distributed-systems.md +150 -0
- package/skills/perf-scout/references/game-engine-and-realtime.md +136 -0
- package/skills/perf-scout/references/gpu-and-accelerators.md +130 -0
- package/skills/perf-scout/references/idea-ranking.md +75 -0
- package/skills/perf-scout/references/io-and-batching.md +135 -0
- package/skills/perf-scout/references/memory-and-data-locality.md +137 -0
- package/skills/perf-scout/references/parallelism-and-vectorization.md +126 -0
- package/skills/perf-scout/references/parked-item-template.md +117 -0
- package/skills/perf-scout/references/peer-review-pass.md +150 -0
- package/skills/perf-scout/references/report-template.md +148 -0
- package/skills/principles/SKILL.md +678 -0
- package/skills/principles/references/models.md +191 -0
- package/skills/principles/references/subagents.md +158 -0
- package/skills/prose-author/SKILL.md +185 -0
- package/skills/refactor-conventions-creator/SKILL.md +359 -0
- package/skills/refactor-conventions-creator/agents/openai.yaml +6 -0
- package/skills/refactor-conventions-creator/references/common-structures.md +91 -0
- package/skills/refactor-conventions-creator/references/common-styles.md +90 -0
- package/skills/refactor-design/SKILL.md +384 -0
- package/skills/release-deploy/SKILL.md +667 -0
- package/skills/release-deploy/agents/openai.yaml +6 -0
- package/skills/research/SKILL.md +166 -0
- package/skills/research/agents/openai.yaml +6 -0
- package/skills/review/SKILL.md +267 -0
- package/skills/review/references/deep-review.md +98 -0
- package/skills/review/references/review-lenses.md +68 -0
- package/skills/review/references/substrate-side-effects.md +161 -0
- package/skills/review/references/target-resolution.md +52 -0
- package/skills/scope/SKILL.md +486 -0
- package/work-view/Cargo.lock +486 -0
- package/work-view/Cargo.toml +9 -0
- package/work-view/crates/cli/.work-view-version +1 -0
- package/work-view/crates/cli/Cargo.toml +18 -0
- package/work-view/crates/cli/src/actionable.rs +500 -0
- package/work-view/crates/cli/src/args.rs +792 -0
- package/work-view/crates/cli/src/board/assets/board.css +855 -0
- package/work-view/crates/cli/src/board/assets/board.js +259 -0
- package/work-view/crates/cli/src/board/assets/card.js +139 -0
- package/work-view/crates/cli/src/board/assets/components.css +290 -0
- package/work-view/crates/cli/src/board/assets/dependency.js +1193 -0
- package/work-view/crates/cli/src/board/assets/detail.js +256 -0
- package/work-view/crates/cli/src/board/assets/filters.js +389 -0
- package/work-view/crates/cli/src/board/assets/index.html +77 -0
- package/work-view/crates/cli/src/board/assets/kanban.js +231 -0
- package/work-view/crates/cli/src/board/assets/markdown.js +260 -0
- package/work-view/crates/cli/src/board/assets/motion.css +131 -0
- package/work-view/crates/cli/src/board/assets/state.js +245 -0
- package/work-view/crates/cli/src/board/assets/table.js +304 -0
- package/work-view/crates/cli/src/board/assets/tokens.css +602 -0
- package/work-view/crates/cli/src/board/assets/views.js +41 -0
- package/work-view/crates/cli/src/board/assets.rs +162 -0
- package/work-view/crates/cli/src/board/feed.rs +204 -0
- package/work-view/crates/cli/src/board/mod.rs +246 -0
- package/work-view/crates/cli/src/board/open.rs +144 -0
- package/work-view/crates/cli/src/board/server.rs +407 -0
- package/work-view/crates/cli/src/main.rs +195 -0
- package/work-view/crates/cli/src/render.rs +354 -0
- package/work-view/crates/cli/src/scope.rs +157 -0
- package/work-view/crates/cli/src/stale.rs +581 -0
- package/work-view/crates/cli/tests/board-js/dependency-table.test.mjs +64 -0
- package/work-view/crates/cli/tests/board-js/expanded-browsing.test.mjs +79 -0
- package/work-view/crates/cli/tests/board-js/harness-smoke.test.mjs +11 -0
- package/work-view/crates/cli/tests/board-js/harness.mjs +437 -0
- package/work-view/crates/cli/tests/board-js/kanban-detail.test.mjs +75 -0
- package/work-view/crates/cli/tests/board-js/markdown-filter.test.mjs +60 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/CONVENTIONS.md +3 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/active/epics/epic-alpha.md +16 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/active/features/feat-a.md +16 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/active/features/feat-b.md +16 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/active/stories/story-alpha-1.md +16 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/active/stories/story-research-1.md +19 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/archive/feat-done.md +16 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/archive/feat-shipped.md +18 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/backlog/idea-backlog.md +7 -0
- package/work-view/crates/cli/tests/fixtures/golden/.work/releases/v1.0/v1.0.md +16 -0
- package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/CONVENTIONS.md +6 -0
- package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/active/features/feat-dep-done.md +17 -0
- package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/active/features/feat-design-ready.md +17 -0
- package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/active/features/feat-impl-ready.md +17 -0
- package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/active/stories/story-review-ready.md +17 -0
- package/work-view/crates/cli/tests/fixtures/with-malformed/.work/CONVENTIONS.md +4 -0
- package/work-view/crates/cli/tests/fixtures/with-malformed/.work/active/epics/malformed-no-closing-fence.md +8 -0
- package/work-view/crates/cli/tests/fixtures/with-malformed/.work/active/features/good-item.md +16 -0
- package/work-view/crates/cli/tests/integration.rs +2833 -0
- package/work-view/crates/core/Cargo.toml +13 -0
- package/work-view/crates/core/src/error.rs +61 -0
- package/work-view/crates/core/src/filter.rs +804 -0
- package/work-view/crates/core/src/graph.rs +324 -0
- package/work-view/crates/core/src/index.rs +590 -0
- package/work-view/crates/core/src/lib.rs +54 -0
- package/work-view/crates/core/src/model.rs +202 -0
- package/work-view/crates/core/src/parse.rs +494 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/CONVENTIONS.md +15 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/active/epics/epic-core.md +16 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/active/features/feat-parser.md +16 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/active/features/feat-query.md +16 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/active/stories/malformed-no-id.md +15 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/active/stories/story-parse-unit.md +16 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/active/stories/story-security-gate.md +16 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/archive/feat-old.md +16 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/archive/feat-parser.md +17 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/backlog/idea-future-board.md +7 -0
- package/work-view/crates/core/tests/fixtures/golden/.work/releases/v1.0/v1.0.md +16 -0
- package/work-view/crates/core/tests/fixtures/precedence/.work/CONVENTIONS.md +10 -0
- package/work-view/crates/core/tests/fixtures/precedence/.work/archive/dup-item.md +18 -0
- package/work-view/crates/core/tests/fixtures/precedence/.work/releases/v2.0/dup-item.md +18 -0
- package/work-view/crates/core/tests/integration.rs +541 -0
- package/work-view/dist/.gitattributes +1 -0
- package/work-view/dist/README.md +42 -0
- package/work-view/dist/aarch64-apple-darwin/work-view +0 -0
- package/work-view/dist/aarch64-unknown-linux-musl/work-view +0 -0
- package/work-view/dist/x86_64-apple-darwin/work-view +0 -0
- package/work-view/dist/x86_64-unknown-linux-musl/work-view +0 -0
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# work-view — query items in the agile-workflow substrate.
|
|
3
|
+
#
|
|
4
|
+
# FROZEN DEGRADED FALLBACK. The canonical work-view is the Rust binary
|
|
5
|
+
# (crates/, installed to .work/bin/work-view). This script is only the
|
|
6
|
+
# install fallback for platforms without a prebuilt binary. It deliberately
|
|
7
|
+
# LACKS newer features: no `--scope` tier filtering (it always queries all
|
|
8
|
+
# tiers) and no `work-view board`. bash<->Rust byte-parity is no longer
|
|
9
|
+
# enforced. Full retirement is tracked as a parked epic; until then this stays
|
|
10
|
+
# as a best-effort degraded fallback.
|
|
11
|
+
#
|
|
12
|
+
# Pure bash + a single awk pass. Frontmatter for every item is parsed exactly
|
|
13
|
+
# once, in one awk process for the whole tree, instead of spawning an awk per
|
|
14
|
+
# field per file. Optional yq enhancement detected at runtime but not required.
|
|
15
|
+
#
|
|
16
|
+
# Exit codes:
|
|
17
|
+
# 0 success
|
|
18
|
+
# 1 usage error (bad flag, conflicting flags)
|
|
19
|
+
# 2 no substrate found (no .work/CONVENTIONS.md in CWD or ancestor)
|
|
20
|
+
# 3 internal error (corrupted item file)
|
|
21
|
+
|
|
22
|
+
set -euo pipefail
|
|
23
|
+
|
|
24
|
+
# Kept in lockstep with plugin.json by scripts/bump-version.sh. Do not hand-edit.
|
|
25
|
+
WORK_VIEW_VERSION="0.15.3"
|
|
26
|
+
|
|
27
|
+
# ============================================================================
|
|
28
|
+
# Version prelude (POSIX / bash 3.2 safe — runs BEFORE the Bash-4 guard)
|
|
29
|
+
# ============================================================================
|
|
30
|
+
#
|
|
31
|
+
# `--version` must answer correctly even on a host whose only bash is the
|
|
32
|
+
# macOS system 3.2 with no modern bash to re-exec into. If it fell through to
|
|
33
|
+
# the Bash-4 guard below it would print "requires bash 4" and exit 1, and a
|
|
34
|
+
# self-heal staleness probe would read that failure as a broken/stale tool
|
|
35
|
+
# instead of a version. So short-circuit here using only constructs that work
|
|
36
|
+
# in bash 3.2 (printf, simple parameter expansion, case). Output is
|
|
37
|
+
# byte-identical to the Rust binary: `work-view <semver>\n`, exit 0.
|
|
38
|
+
case "${1:-}" in
|
|
39
|
+
--version|-V) printf 'work-view %s\n' "$WORK_VIEW_VERSION"; exit 0 ;;
|
|
40
|
+
esac
|
|
41
|
+
|
|
42
|
+
# ============================================================================
|
|
43
|
+
# Bash 4+ required (associative arrays). macOS ships bash 3.2 at /bin/bash,
|
|
44
|
+
# so re-exec under a modern bash if one is available.
|
|
45
|
+
# ============================================================================
|
|
46
|
+
|
|
47
|
+
if [[ "${BASH_VERSINFO[0]:-0}" -lt 4 ]]; then
|
|
48
|
+
for candidate in \
|
|
49
|
+
/opt/homebrew/bin/bash \
|
|
50
|
+
/usr/local/bin/bash \
|
|
51
|
+
/home/linuxbrew/.linuxbrew/bin/bash \
|
|
52
|
+
/usr/bin/bash \
|
|
53
|
+
"$(command -v bash 2>/dev/null || true)"
|
|
54
|
+
do
|
|
55
|
+
[[ -n "$candidate" && -x "$candidate" ]] || continue
|
|
56
|
+
ver="$("$candidate" -c 'echo "${BASH_VERSINFO[0]}"' 2>/dev/null || echo 0)"
|
|
57
|
+
if [[ "${ver:-0}" -ge 4 ]]; then
|
|
58
|
+
exec "$candidate" "$0" "$@"
|
|
59
|
+
fi
|
|
60
|
+
done
|
|
61
|
+
{
|
|
62
|
+
echo "work-view: requires bash 4 or newer (current: ${BASH_VERSION:-unknown})"
|
|
63
|
+
case "$(uname -s 2>/dev/null)" in
|
|
64
|
+
Darwin) echo " macOS ships bash 3.2. Install a modern bash: brew install bash" ;;
|
|
65
|
+
*) echo " Install bash 4+ via your package manager." ;;
|
|
66
|
+
esac
|
|
67
|
+
} >&2
|
|
68
|
+
exit 1
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
# ============================================================================
|
|
72
|
+
# Usage
|
|
73
|
+
# ============================================================================
|
|
74
|
+
|
|
75
|
+
usage() {
|
|
76
|
+
cat <<'USAGE'
|
|
77
|
+
work-view — query items in the agile-workflow substrate
|
|
78
|
+
|
|
79
|
+
Usage: work-view [FILTERS...] [OUTPUT]
|
|
80
|
+
|
|
81
|
+
Filters (compose with AND semantics):
|
|
82
|
+
--stage <stage> Items at the given stage
|
|
83
|
+
--tag <tag> Items with the given tag (repeatable, AND)
|
|
84
|
+
--kind <kind> Items of the given kind (epic|feature|story|release)
|
|
85
|
+
--parent <id> Direct children of the given item
|
|
86
|
+
--release <version> Items with release_binding: <version>
|
|
87
|
+
--gate <name> Items with gate_origin: <name>
|
|
88
|
+
--ready Active items at drafting/implementing/review with all depends_on done
|
|
89
|
+
--blocked Active items at drafting/implementing/review with unmet dependencies
|
|
90
|
+
--blocking <id> Items that depend on <id>
|
|
91
|
+
|
|
92
|
+
Output (default tabular):
|
|
93
|
+
--paths One file path per line
|
|
94
|
+
--cat Full item bodies (separated by ---)
|
|
95
|
+
--count Match count only
|
|
96
|
+
|
|
97
|
+
Other:
|
|
98
|
+
--version Print the work-view version and exit
|
|
99
|
+
--help Show this help and exit
|
|
100
|
+
USAGE
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# ============================================================================
|
|
104
|
+
# Substrate root detection
|
|
105
|
+
# ============================================================================
|
|
106
|
+
|
|
107
|
+
find_substrate_root() {
|
|
108
|
+
local dir
|
|
109
|
+
dir="$(pwd)"
|
|
110
|
+
while [[ "$dir" != "/" && -n "$dir" ]]; do
|
|
111
|
+
if [[ -f "$dir/.work/CONVENTIONS.md" ]]; then
|
|
112
|
+
printf '%s\n' "$dir"
|
|
113
|
+
return 0
|
|
114
|
+
fi
|
|
115
|
+
dir="$(dirname "$dir")"
|
|
116
|
+
done
|
|
117
|
+
return 1
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
# ============================================================================
|
|
121
|
+
# Frontmatter parser (single awk pass for the whole tree)
|
|
122
|
+
# ============================================================================
|
|
123
|
+
#
|
|
124
|
+
# Reads every item file once and emits one Unit-Separator-delimited record per
|
|
125
|
+
# file:
|
|
126
|
+
# path \037 id \037 kind \037 stage \037 parent \037 release_binding \037
|
|
127
|
+
# gate_origin \037 tags \037 depends_on
|
|
128
|
+
#
|
|
129
|
+
# Scalars are whitespace- and one-layer-quote-stripped exactly like the old
|
|
130
|
+
# fm_field. tags/depends_on are normalized from flow style ([a, b, c]) to
|
|
131
|
+
# space-joined tokens exactly like the old fm_array (only flow arrays are
|
|
132
|
+
# supported, matching prior behavior). The Unit Separator (0x1F) never appears
|
|
133
|
+
# in YAML frontmatter, so empty fields survive the round trip.
|
|
134
|
+
|
|
135
|
+
read -r -d '' AWK_INDEX <<'AWK' || true
|
|
136
|
+
function strip(v) {
|
|
137
|
+
sub(/^[[:space:]]+/, "", v)
|
|
138
|
+
sub(/[[:space:]]+$/, "", v)
|
|
139
|
+
sub(/^"/, "", v); sub(/"$/, "", v)
|
|
140
|
+
sub(/^'/, "", v); sub(/'$/, "", v)
|
|
141
|
+
return v
|
|
142
|
+
}
|
|
143
|
+
# Normalize a flow-style scalar ("[a, b]") to space-joined tokens ("a b").
|
|
144
|
+
function narr(v) {
|
|
145
|
+
if (v == "" || v == "[]") return ""
|
|
146
|
+
sub(/^\[/, "", v); sub(/\]$/, "", v)
|
|
147
|
+
gsub(/ /, "", v)
|
|
148
|
+
gsub(/,/, " ", v)
|
|
149
|
+
return v
|
|
150
|
+
}
|
|
151
|
+
function field(line, key, p) {
|
|
152
|
+
p = "^" key ":"
|
|
153
|
+
if (match(line, p)) return strip(substr(line, RLENGTH + 1))
|
|
154
|
+
return "\001NOMATCH\001"
|
|
155
|
+
}
|
|
156
|
+
function emit() {
|
|
157
|
+
if (cf == "") return
|
|
158
|
+
printf "%s\037%s\037%s\037%s\037%s\037%s\037%s\037%s\037%s\n", \
|
|
159
|
+
cf, id, kind, stage, parent, rel, gate, narr(tags), narr(deps)
|
|
160
|
+
}
|
|
161
|
+
function reset(fname) {
|
|
162
|
+
cf = fname; in_fm = 0; fm_done = 0
|
|
163
|
+
id = ""; kind = ""; stage = ""; parent = ""
|
|
164
|
+
rel = ""; gate = ""; tags = ""; deps = ""
|
|
165
|
+
delete have
|
|
166
|
+
}
|
|
167
|
+
BEGIN { v = "" }
|
|
168
|
+
FNR == 1 { emit(); reset(FILENAME) }
|
|
169
|
+
{
|
|
170
|
+
if (fm_done) next
|
|
171
|
+
if ($0 ~ /^---[[:space:]]*$/) {
|
|
172
|
+
if (in_fm == 0) { in_fm = 1 } else { fm_done = 1 }
|
|
173
|
+
next
|
|
174
|
+
}
|
|
175
|
+
if (in_fm != 1) next
|
|
176
|
+
if (!("id" in have)) { v = field($0, "id"); if (v != "\001NOMATCH\001") { id = v; have["id"] = 1; next } }
|
|
177
|
+
if (!("kind" in have)) { v = field($0, "kind"); if (v != "\001NOMATCH\001") { kind = v; have["kind"] = 1; next } }
|
|
178
|
+
if (!("stage" in have)) { v = field($0, "stage"); if (v != "\001NOMATCH\001") { stage = v; have["stage"] = 1; next } }
|
|
179
|
+
if (!("parent" in have)) { v = field($0, "parent"); if (v != "\001NOMATCH\001") { parent = v; have["parent"] = 1; next } }
|
|
180
|
+
if (!("rel" in have)) { v = field($0, "release_binding"); if (v != "\001NOMATCH\001") { rel = v; have["rel"] = 1; next } }
|
|
181
|
+
if (!("gate" in have)) { v = field($0, "gate_origin"); if (v != "\001NOMATCH\001") { gate = v; have["gate"] = 1; next } }
|
|
182
|
+
if (!("tags" in have)) { v = field($0, "tags"); if (v != "\001NOMATCH\001") { tags = v; have["tags"] = 1; next } }
|
|
183
|
+
if (!("deps" in have)) { v = field($0, "depends_on"); if (v != "\001NOMATCH\001") { deps = v; have["deps"] = 1; next } }
|
|
184
|
+
}
|
|
185
|
+
END { emit() }
|
|
186
|
+
AWK
|
|
187
|
+
|
|
188
|
+
# ============================================================================
|
|
189
|
+
# Item index
|
|
190
|
+
# ============================================================================
|
|
191
|
+
#
|
|
192
|
+
# Globals populated by build_index (all keyed by file path unless noted):
|
|
193
|
+
# ALL_FILES — array of item file paths, in find traversal order
|
|
194
|
+
# IDX_ID/KIND/STAGE/PARENT/REL/GATE — scalar fields
|
|
195
|
+
# IDX_TAGS/IDX_DEPS — space-joined tokens
|
|
196
|
+
# FILE_BY_ID/STAGE_BY_ID — keyed by item id (for the dependency graph)
|
|
197
|
+
|
|
198
|
+
declare -a ALL_FILES=()
|
|
199
|
+
declare -A IDX_ID IDX_KIND IDX_STAGE IDX_PARENT IDX_REL IDX_GATE IDX_TAGS IDX_DEPS
|
|
200
|
+
declare -A STAGE_BY_ID FILE_BY_ID
|
|
201
|
+
|
|
202
|
+
build_index() {
|
|
203
|
+
local root="$1"
|
|
204
|
+
local path id kind stage parent rel gate tags deps
|
|
205
|
+
ALL_FILES=()
|
|
206
|
+
while IFS=$'\037' read -r path id kind stage parent rel gate tags deps; do
|
|
207
|
+
[[ -n "$path" ]] || continue
|
|
208
|
+
ALL_FILES+=("$path")
|
|
209
|
+
IDX_ID["$path"]="$id"
|
|
210
|
+
IDX_KIND["$path"]="$kind"
|
|
211
|
+
IDX_STAGE["$path"]="$stage"
|
|
212
|
+
IDX_PARENT["$path"]="$parent"
|
|
213
|
+
IDX_REL["$path"]="$rel"
|
|
214
|
+
IDX_GATE["$path"]="$gate"
|
|
215
|
+
IDX_TAGS["$path"]="$tags"
|
|
216
|
+
IDX_DEPS["$path"]="$deps"
|
|
217
|
+
if [[ -n "$id" ]]; then
|
|
218
|
+
FILE_BY_ID["$id"]="$path"
|
|
219
|
+
STAGE_BY_ID["$id"]="$stage"
|
|
220
|
+
fi
|
|
221
|
+
done < <(find "$root/.work/active" "$root/.work/backlog" "$root/.work/releases" "$root/.work/archive" \
|
|
222
|
+
-type f -name '*.md' -print0 2>/dev/null \
|
|
223
|
+
| LC_ALL=C sort -z \
|
|
224
|
+
| xargs -0 awk "$AWK_INDEX" 2>/dev/null || true)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
# True if item id is at stage:done OR lives in releases/ or archive/ (terminal).
|
|
228
|
+
is_done() {
|
|
229
|
+
local id="$1"
|
|
230
|
+
local stage="${STAGE_BY_ID[$id]:-}"
|
|
231
|
+
if [[ "$stage" == "done" || "$stage" == "released" ]]; then
|
|
232
|
+
return 0
|
|
233
|
+
fi
|
|
234
|
+
local file="${FILE_BY_ID[$id]:-}"
|
|
235
|
+
case "$file" in
|
|
236
|
+
*/.work/releases/*|*/.work/archive/*) return 0 ;;
|
|
237
|
+
esac
|
|
238
|
+
return 1
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
# True if item file's depends_on are all done.
|
|
242
|
+
deps_satisfied() {
|
|
243
|
+
local file="$1"
|
|
244
|
+
local dep
|
|
245
|
+
for dep in ${IDX_DEPS[$file]:-}; do
|
|
246
|
+
if ! is_done "$dep"; then
|
|
247
|
+
return 1
|
|
248
|
+
fi
|
|
249
|
+
done
|
|
250
|
+
return 0
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
# ============================================================================
|
|
254
|
+
# Argument parsing
|
|
255
|
+
# ============================================================================
|
|
256
|
+
|
|
257
|
+
want_stage=""
|
|
258
|
+
want_kind=""
|
|
259
|
+
want_parent=""
|
|
260
|
+
want_release=""
|
|
261
|
+
want_gate=""
|
|
262
|
+
want_blocking=""
|
|
263
|
+
want_ready=0
|
|
264
|
+
want_blocked=0
|
|
265
|
+
declare -a want_tags=()
|
|
266
|
+
output_mode="table"
|
|
267
|
+
|
|
268
|
+
while [[ $# -gt 0 ]]; do
|
|
269
|
+
case "$1" in
|
|
270
|
+
--help|-h) usage; exit 0 ;;
|
|
271
|
+
# Short-circuits before substrate detection (like --help) so --version
|
|
272
|
+
# works outside a substrate. -V is the short form; lowercase -v is reserved
|
|
273
|
+
# for a possible future --verbose. The POSIX prelude at the top already
|
|
274
|
+
# handles --version as the first arg (for bash 3.2); this arm covers it in
|
|
275
|
+
# any position once running under bash 4+.
|
|
276
|
+
--version|-V) printf 'work-view %s\n' "$WORK_VIEW_VERSION"; exit 0 ;;
|
|
277
|
+
--stage) want_stage="$2"; shift 2 ;;
|
|
278
|
+
--kind) want_kind="$2"; shift 2 ;;
|
|
279
|
+
--parent) want_parent="$2"; shift 2 ;;
|
|
280
|
+
--release) want_release="$2"; shift 2 ;;
|
|
281
|
+
--gate) want_gate="$2"; shift 2 ;;
|
|
282
|
+
--blocking) want_blocking="$2"; shift 2 ;;
|
|
283
|
+
--tag) want_tags+=("$2"); shift 2 ;;
|
|
284
|
+
--ready) want_ready=1; shift ;;
|
|
285
|
+
--blocked) want_blocked=1; shift ;;
|
|
286
|
+
--paths) output_mode="paths"; shift ;;
|
|
287
|
+
--cat) output_mode="cat"; shift ;;
|
|
288
|
+
--count) output_mode="count"; shift ;;
|
|
289
|
+
--) shift; break ;;
|
|
290
|
+
-*) echo "work-view: unknown flag: $1" >&2; exit 1 ;;
|
|
291
|
+
*) echo "work-view: unexpected argument: $1" >&2; exit 1 ;;
|
|
292
|
+
esac
|
|
293
|
+
done
|
|
294
|
+
|
|
295
|
+
# --ready and --blocked are mutually exclusive
|
|
296
|
+
if [[ $want_ready -eq 1 && $want_blocked -eq 1 ]]; then
|
|
297
|
+
echo "work-view: --ready and --blocked are mutually exclusive" >&2
|
|
298
|
+
exit 1
|
|
299
|
+
fi
|
|
300
|
+
|
|
301
|
+
# ============================================================================
|
|
302
|
+
# Main
|
|
303
|
+
# ============================================================================
|
|
304
|
+
|
|
305
|
+
ROOT="$(find_substrate_root || true)"
|
|
306
|
+
if [[ -z "$ROOT" ]]; then
|
|
307
|
+
echo "work-view: no substrate found (no .work/CONVENTIONS.md in CWD or ancestor)" >&2
|
|
308
|
+
exit 2
|
|
309
|
+
fi
|
|
310
|
+
|
|
311
|
+
build_index "$ROOT"
|
|
312
|
+
|
|
313
|
+
declare -a matches=()
|
|
314
|
+
|
|
315
|
+
for f in "${ALL_FILES[@]}"; do
|
|
316
|
+
# --kind
|
|
317
|
+
if [[ -n "$want_kind" ]]; then
|
|
318
|
+
[[ "${IDX_KIND[$f]}" == "$want_kind" ]] || continue
|
|
319
|
+
fi
|
|
320
|
+
|
|
321
|
+
# --stage
|
|
322
|
+
if [[ -n "$want_stage" ]]; then
|
|
323
|
+
[[ "${IDX_STAGE[$f]}" == "$want_stage" ]] || continue
|
|
324
|
+
fi
|
|
325
|
+
|
|
326
|
+
# --parent
|
|
327
|
+
if [[ -n "$want_parent" ]]; then
|
|
328
|
+
[[ "${IDX_PARENT[$f]}" == "$want_parent" ]] || continue
|
|
329
|
+
fi
|
|
330
|
+
|
|
331
|
+
# --release
|
|
332
|
+
if [[ -n "$want_release" ]]; then
|
|
333
|
+
[[ "${IDX_REL[$f]}" == "$want_release" ]] || continue
|
|
334
|
+
fi
|
|
335
|
+
|
|
336
|
+
# --gate
|
|
337
|
+
if [[ -n "$want_gate" ]]; then
|
|
338
|
+
[[ "${IDX_GATE[$f]}" == "$want_gate" ]] || continue
|
|
339
|
+
fi
|
|
340
|
+
|
|
341
|
+
# --tag (AND semantics, repeatable)
|
|
342
|
+
if (( ${#want_tags[@]} > 0 )); then
|
|
343
|
+
file_tags="${IDX_TAGS[$f]}"
|
|
344
|
+
skip=0
|
|
345
|
+
for t in "${want_tags[@]}"; do
|
|
346
|
+
if [[ " $file_tags " != *" $t "* ]]; then
|
|
347
|
+
skip=1
|
|
348
|
+
break
|
|
349
|
+
fi
|
|
350
|
+
done
|
|
351
|
+
[[ $skip -eq 0 ]] || continue
|
|
352
|
+
fi
|
|
353
|
+
|
|
354
|
+
# --blocking <id>
|
|
355
|
+
if [[ -n "$want_blocking" ]]; then
|
|
356
|
+
if [[ " ${IDX_DEPS[$f]} " != *" $want_blocking "* ]]; then
|
|
357
|
+
continue
|
|
358
|
+
fi
|
|
359
|
+
fi
|
|
360
|
+
|
|
361
|
+
# --ready / --blocked: active-tier items at a movable stage
|
|
362
|
+
if [[ $want_ready -eq 1 || $want_blocked -eq 1 ]]; then
|
|
363
|
+
# only active-tier items are actionable candidates
|
|
364
|
+
case "$f" in */.work/active/*) ;; *) continue ;; esac
|
|
365
|
+
# stage must be one of: drafting, implementing, review
|
|
366
|
+
case "${IDX_STAGE[$f]}" in drafting|implementing|review) ;; *) continue ;; esac
|
|
367
|
+
if [[ $want_ready -eq 1 ]]; then
|
|
368
|
+
deps_satisfied "$f" || continue
|
|
369
|
+
else
|
|
370
|
+
if deps_satisfied "$f"; then
|
|
371
|
+
continue
|
|
372
|
+
fi
|
|
373
|
+
fi
|
|
374
|
+
fi
|
|
375
|
+
|
|
376
|
+
matches+=("$f")
|
|
377
|
+
done
|
|
378
|
+
|
|
379
|
+
# ============================================================================
|
|
380
|
+
# Output
|
|
381
|
+
# ============================================================================
|
|
382
|
+
|
|
383
|
+
case "$output_mode" in
|
|
384
|
+
count)
|
|
385
|
+
echo "${#matches[@]}"
|
|
386
|
+
;;
|
|
387
|
+
paths)
|
|
388
|
+
for f in "${matches[@]}"; do
|
|
389
|
+
printf '%s\n' "$f"
|
|
390
|
+
done
|
|
391
|
+
;;
|
|
392
|
+
cat)
|
|
393
|
+
first=1
|
|
394
|
+
for f in "${matches[@]}"; do
|
|
395
|
+
if [[ $first -eq 0 ]]; then
|
|
396
|
+
echo ""
|
|
397
|
+
echo "---"
|
|
398
|
+
echo ""
|
|
399
|
+
fi
|
|
400
|
+
cat "$f"
|
|
401
|
+
first=0
|
|
402
|
+
done
|
|
403
|
+
;;
|
|
404
|
+
table|*)
|
|
405
|
+
if (( ${#matches[@]} == 0 )); then
|
|
406
|
+
exit 0
|
|
407
|
+
fi
|
|
408
|
+
# Header
|
|
409
|
+
printf '%-40s %-8s %-14s %-30s %s\n' "ID" "KIND" "STAGE" "TAGS" "PARENT"
|
|
410
|
+
printf '%-40s %-8s %-14s %-30s %s\n' "----------------------------------------" "--------" "--------------" "------------------------------" "----------------"
|
|
411
|
+
for f in "${matches[@]}"; do
|
|
412
|
+
id="${IDX_ID[$f]}"
|
|
413
|
+
kind="${IDX_KIND[$f]}"
|
|
414
|
+
stage="${IDX_STAGE[$f]}"
|
|
415
|
+
tags_csv="${IDX_TAGS[$f]// /,}"
|
|
416
|
+
parent="${IDX_PARENT[$f]}"
|
|
417
|
+
[[ "$parent" == "null" ]] && parent="-"
|
|
418
|
+
printf '%-40s %-8s %-14s %-30s %s\n' "$id" "$kind" "$stage" "$tags_csv" "$parent"
|
|
419
|
+
done
|
|
420
|
+
;;
|
|
421
|
+
esac
|
|
422
|
+
|
|
423
|
+
exit 0
|