@nlaprell/shipit 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/commands/create_intent_from_issue.md +28 -0
- package/.cursor/commands/create_pr.md +28 -0
- package/.cursor/commands/dashboard.md +39 -0
- package/.cursor/commands/deploy.md +152 -0
- package/.cursor/commands/drift_check.md +36 -0
- package/.cursor/commands/fix.md +39 -0
- package/.cursor/commands/generate_release_plan.md +31 -0
- package/.cursor/commands/generate_roadmap.md +38 -0
- package/.cursor/commands/help.md +37 -0
- package/.cursor/commands/init_project.md +26 -0
- package/.cursor/commands/kill.md +72 -0
- package/.cursor/commands/new_intent.md +68 -0
- package/.cursor/commands/pr.md +77 -0
- package/.cursor/commands/revert-plan.md +58 -0
- package/.cursor/commands/risk.md +64 -0
- package/.cursor/commands/rollback.md +43 -0
- package/.cursor/commands/scope_project.md +53 -0
- package/.cursor/commands/ship.md +345 -0
- package/.cursor/commands/status.md +71 -0
- package/.cursor/commands/suggest.md +44 -0
- package/.cursor/commands/test_shipit.md +197 -0
- package/.cursor/commands/verify.md +50 -0
- package/.cursor/rules/architect.mdc +84 -0
- package/.cursor/rules/assumption-extractor.mdc +95 -0
- package/.cursor/rules/docs.mdc +66 -0
- package/.cursor/rules/implementer.mdc +112 -0
- package/.cursor/rules/pm.mdc +136 -0
- package/.cursor/rules/qa.mdc +97 -0
- package/.cursor/rules/security.mdc +90 -0
- package/.cursor/rules/steward.mdc +99 -0
- package/.cursor/rules/test-runner.mdc +196 -0
- package/AGENTS.md +121 -0
- package/README.md +264 -0
- package/_system/architecture/CANON.md +159 -0
- package/_system/architecture/invariants.yml +87 -0
- package/_system/architecture/project-schema.json +98 -0
- package/_system/architecture/workflow-state-layout.md +68 -0
- package/_system/artifacts/SYSTEM_STATE.md +43 -0
- package/_system/artifacts/confidence-calibration.json +16 -0
- package/_system/artifacts/dependencies.md +46 -0
- package/_system/artifacts/framework-files-manifest.json +179 -0
- package/_system/artifacts/usage.json +1 -0
- package/_system/behaviors/DO_RELEASE.md +371 -0
- package/_system/behaviors/DO_RELEASE_AI.md +329 -0
- package/_system/behaviors/PREPARE_RELEASE.md +373 -0
- package/_system/behaviors/PREPARE_RELEASE_AI.md +234 -0
- package/_system/behaviors/WORK_ROOT_PLATFORM_ISSUES.md +140 -0
- package/_system/behaviors/WORK_TEST_PLAN_ISSUES.md +380 -0
- package/_system/do-not-repeat/abandoned-designs.md +18 -0
- package/_system/do-not-repeat/bad-patterns.md +19 -0
- package/_system/do-not-repeat/failed-experiments.md +18 -0
- package/_system/do-not-repeat/rejected-libraries.md +19 -0
- package/_system/drift/baselines.md +49 -0
- package/_system/drift/metrics.md +33 -0
- package/_system/golden-data/.gitkeep +0 -0
- package/_system/golden-data/README.md +47 -0
- package/_system/reports/mutation/mutation.html +492 -0
- package/_system/security/audit-allowlist.json +4 -0
- package/bin/create-shipit-app +29 -0
- package/bin/shipit +183 -0
- package/cli/src/commands/check.js +82 -0
- package/cli/src/commands/create.js +195 -0
- package/cli/src/commands/init.js +267 -0
- package/cli/src/commands/upgrade.js +196 -0
- package/cli/src/utils/config.js +27 -0
- package/cli/src/utils/file-copy.js +144 -0
- package/cli/src/utils/gitignore-merge.js +44 -0
- package/cli/src/utils/manifest.js +105 -0
- package/cli/src/utils/package-json-merge.js +163 -0
- package/cli/src/utils/project-json-merge.js +57 -0
- package/cli/src/utils/prompts.js +30 -0
- package/cli/src/utils/stack-detection.js +56 -0
- package/cli/src/utils/stack-files.js +364 -0
- package/cli/src/utils/upgrade-backup.js +159 -0
- package/cli/src/utils/version.js +64 -0
- package/dashboard-app/README.md +73 -0
- package/dashboard-app/eslint.config.js +23 -0
- package/dashboard-app/index.html +13 -0
- package/dashboard-app/package.json +30 -0
- package/dashboard-app/pnpm-lock.yaml +2721 -0
- package/dashboard-app/public/dashboard.json +66 -0
- package/dashboard-app/public/vite.svg +1 -0
- package/dashboard-app/src/App.css +141 -0
- package/dashboard-app/src/App.tsx +155 -0
- package/dashboard-app/src/assets/react.svg +1 -0
- package/dashboard-app/src/index.css +68 -0
- package/dashboard-app/src/main.tsx +10 -0
- package/dashboard-app/tsconfig.app.json +28 -0
- package/dashboard-app/tsconfig.json +4 -0
- package/dashboard-app/tsconfig.node.json +26 -0
- package/dashboard-app/vite.config.ts +7 -0
- package/package.json +116 -0
- package/scripts/README.md +70 -0
- package/scripts/audit-check.sh +125 -0
- package/scripts/calibration-report.sh +198 -0
- package/scripts/check-readiness.sh +155 -0
- package/scripts/collect-metrics.sh +116 -0
- package/scripts/command-manifest.yml +131 -0
- package/scripts/create-test-plan-issue.sh +110 -0
- package/scripts/dashboard-start.sh +16 -0
- package/scripts/deploy.sh +170 -0
- package/scripts/drift-check.sh +93 -0
- package/scripts/execute-rollback.sh +177 -0
- package/scripts/export-dashboard-json.js +208 -0
- package/scripts/fix-intents.sh +239 -0
- package/scripts/generate-dashboard.sh +136 -0
- package/scripts/generate-docs.sh +279 -0
- package/scripts/generate-project-context.sh +142 -0
- package/scripts/generate-release-plan.sh +443 -0
- package/scripts/generate-roadmap.sh +189 -0
- package/scripts/generate-system-state.sh +95 -0
- package/scripts/gh/create-intent-from-issue.sh +82 -0
- package/scripts/gh/create-issue-from-intent.sh +59 -0
- package/scripts/gh/create-pr.sh +41 -0
- package/scripts/gh/link-issue.sh +44 -0
- package/scripts/gh/on-ship-update-issue.sh +42 -0
- package/scripts/headless/README.md +8 -0
- package/scripts/headless/call-llm.js +109 -0
- package/scripts/headless/run-phase.sh +99 -0
- package/scripts/help.sh +271 -0
- package/scripts/init-project.sh +976 -0
- package/scripts/kill-intent.sh +125 -0
- package/scripts/lib/common.sh +29 -0
- package/scripts/lib/intent.sh +61 -0
- package/scripts/lib/progress.sh +57 -0
- package/scripts/lib/suggest-next.sh +131 -0
- package/scripts/lib/validate-intents.sh +240 -0
- package/scripts/lib/verify-outputs.sh +55 -0
- package/scripts/lib/workflow_state.sh +201 -0
- package/scripts/new-intent.sh +271 -0
- package/scripts/publish-npm.sh +28 -0
- package/scripts/scope-project.sh +380 -0
- package/scripts/setup-worktrees.sh +125 -0
- package/scripts/status.sh +278 -0
- package/scripts/suggest.sh +173 -0
- package/scripts/test-headless.sh +47 -0
- package/scripts/test-shipit.sh +52 -0
- package/scripts/test-workflow-state.sh +49 -0
- package/scripts/usage-report.sh +47 -0
- package/scripts/usage.sh +58 -0
- package/scripts/validate-cursor.sh +151 -0
- package/scripts/validate-project.sh +71 -0
- package/scripts/validate-vscode.sh +146 -0
- package/scripts/verify.sh +153 -0
- package/scripts/workflow-orchestrator.sh +97 -0
- package/scripts/workflow-templates/01_analysis.md.tpl +25 -0
- package/scripts/workflow-templates/02_plan.md.tpl +30 -0
- package/scripts/workflow-templates/03_implementation.md.tpl +25 -0
- package/scripts/workflow-templates/04_verification.md.tpl +29 -0
- package/scripts/workflow-templates/05_release_notes.md.tpl +16 -0
- package/scripts/workflow-templates/05_verification_legacy.md.tpl +6 -0
- package/scripts/workflow-templates/active.md.tpl +18 -0
- package/scripts/workflow-templates/phases.yml +39 -0
- package/stryker.conf.json +8 -0
- package/work/intent/templates/api-endpoint.md +124 -0
- package/work/intent/templates/bugfix.md +116 -0
- package/work/intent/templates/frontend-feature.md +115 -0
- package/work/intent/templates/generic.md +122 -0
- package/work/intent/templates/infra-change.md +121 -0
- package/work/intent/templates/refactor.md +116 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Export dashboard JSON for the React dashboard.
|
|
4
|
+
* Reads: work/intent/, work/workflow-state/, _system/artifacts/, _system/drift/
|
|
5
|
+
* Layout: Supports both flat and per-intent workflow-state per workflow-state-layout.md
|
|
6
|
+
* Output: dashboard-app/public/dashboard.json
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
readFileSync,
|
|
11
|
+
readdirSync,
|
|
12
|
+
existsSync,
|
|
13
|
+
writeFileSync,
|
|
14
|
+
mkdirSync,
|
|
15
|
+
} from "node:fs";
|
|
16
|
+
import { join, dirname } from "node:path";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
18
|
+
import { execSync } from "node:child_process";
|
|
19
|
+
|
|
20
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
const REPO_ROOT = join(__dirname, "..");
|
|
22
|
+
|
|
23
|
+
function readText(path) {
|
|
24
|
+
try {
|
|
25
|
+
return readFileSync(path, "utf-8");
|
|
26
|
+
} catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function readJson(path) {
|
|
32
|
+
const text = readText(path);
|
|
33
|
+
if (!text) return null;
|
|
34
|
+
try {
|
|
35
|
+
return JSON.parse(text);
|
|
36
|
+
} catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function findIntentFiles(dir, files = []) {
|
|
42
|
+
if (!existsSync(dir)) return files;
|
|
43
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
44
|
+
for (const e of entries) {
|
|
45
|
+
const p = join(dir, e.name);
|
|
46
|
+
if (e.isDirectory()) {
|
|
47
|
+
if (e.name !== "templates") findIntentFiles(p, files);
|
|
48
|
+
} else if (e.name.endsWith(".md") && e.name !== "_TEMPLATE.md") {
|
|
49
|
+
files.push(p);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return files;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function parseIntentStatus(content) {
|
|
56
|
+
const m = content.match(/##\s*Status\s*\n\s*(\w+)/i);
|
|
57
|
+
return m ? m[1].toLowerCase() : "unknown";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getIntentId(path) {
|
|
61
|
+
const base = path.split(/[/\\]/).pop() || "";
|
|
62
|
+
return base.replace(/\.md$/, "");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function collectIntents() {
|
|
66
|
+
const intentDir = join(REPO_ROOT, "work", "intent");
|
|
67
|
+
const files = findIntentFiles(intentDir);
|
|
68
|
+
const intents = [];
|
|
69
|
+
for (const f of files) {
|
|
70
|
+
const content = readText(f);
|
|
71
|
+
if (!content) continue;
|
|
72
|
+
const status = parseIntentStatus(content);
|
|
73
|
+
const id = getIntentId(f);
|
|
74
|
+
intents.push({ id, status });
|
|
75
|
+
}
|
|
76
|
+
const byStatus = { planned: 0, active: 0, shipped: 0, killed: 0 };
|
|
77
|
+
for (const i of intents) {
|
|
78
|
+
if (i.status in byStatus) byStatus[i.status]++;
|
|
79
|
+
}
|
|
80
|
+
return { intents, counts: byStatus };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function getWorkflowState() {
|
|
84
|
+
const flatActive = join(REPO_ROOT, "work", "workflow-state", "active.md");
|
|
85
|
+
const content = readText(flatActive);
|
|
86
|
+
let currentPhase = "none";
|
|
87
|
+
let activeIntent = "none";
|
|
88
|
+
const activeIntents = [];
|
|
89
|
+
let waitingForApproval = false;
|
|
90
|
+
|
|
91
|
+
const workflowDir = join(REPO_ROOT, "work", "workflow-state");
|
|
92
|
+
if (!existsSync(workflowDir)) {
|
|
93
|
+
return { currentPhase, activeIntent, activeIntents, waitingForApproval };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (content) {
|
|
97
|
+
const phaseM = content.match(/Current Phase:\s*(\S+)/i);
|
|
98
|
+
const idM = content.match(/Intent ID:\s*(\S+)/i);
|
|
99
|
+
if (phaseM) currentPhase = phaseM[1];
|
|
100
|
+
if (idM && idM[1] !== "none") {
|
|
101
|
+
activeIntent = idM[1];
|
|
102
|
+
activeIntents.push(idM[1]);
|
|
103
|
+
}
|
|
104
|
+
// Multi-intent: parse "## Active intents" block (lines like "F-001 | Phase | active")
|
|
105
|
+
const activeSection = content.match(/##\s+Active intents\s*\n([\s\S]*?)(?=\n##|$)/i);
|
|
106
|
+
if (activeSection && activeSection[1]) {
|
|
107
|
+
const lines = activeSection[1].split("\n");
|
|
108
|
+
for (const line of lines) {
|
|
109
|
+
const m = line.match(/^(F-\d+|B-\d+|T-\d+)\s*\|/);
|
|
110
|
+
if (m && !activeIntents.includes(m[1])) activeIntents.push(m[1]);
|
|
111
|
+
}
|
|
112
|
+
if (activeIntents.length > 0 && activeIntent === "none")
|
|
113
|
+
activeIntent = activeIntents[0];
|
|
114
|
+
if (activeIntents.length > 1) currentPhase = "multiple";
|
|
115
|
+
}
|
|
116
|
+
waitingForApproval = /waiting|approval|gate/i.test(content);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return { currentPhase, activeIntent, activeIntents, waitingForApproval };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function getCalibration() {
|
|
123
|
+
try {
|
|
124
|
+
const out = execSync("pnpm calibration-report --json", {
|
|
125
|
+
cwd: REPO_ROOT,
|
|
126
|
+
encoding: "utf-8",
|
|
127
|
+
});
|
|
128
|
+
return JSON.parse(out);
|
|
129
|
+
} catch {
|
|
130
|
+
return { decisions_count: 0, message: "No calibration data" };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function getDocLinks() {
|
|
135
|
+
const base = REPO_ROOT;
|
|
136
|
+
const links = [];
|
|
137
|
+
const candidates = [
|
|
138
|
+
["_system/artifacts/SYSTEM_STATE.md", "System State"],
|
|
139
|
+
["work/release/plan.md", "Release Plan"],
|
|
140
|
+
["work/roadmap/now.md", "Roadmap (Now)"],
|
|
141
|
+
["work/roadmap/next.md", "Roadmap (Next)"],
|
|
142
|
+
["work/roadmap/later.md", "Roadmap (Later)"],
|
|
143
|
+
["_system/drift/metrics.md", "Drift Metrics"],
|
|
144
|
+
["_system/artifacts/dependencies.md", "Dependencies"],
|
|
145
|
+
];
|
|
146
|
+
for (const [rel, label] of candidates) {
|
|
147
|
+
if (existsSync(join(base, rel))) {
|
|
148
|
+
links.push({ path: rel, label });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return links;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function getDriftSummary() {
|
|
155
|
+
const p = join(REPO_ROOT, "_system", "drift", "metrics.md");
|
|
156
|
+
const content = readText(p);
|
|
157
|
+
if (!content) return null;
|
|
158
|
+
return { raw: content.slice(0, 2000), hasMetrics: true };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function getUsageSummary() {
|
|
162
|
+
const p = join(REPO_ROOT, "_system", "artifacts", "usage.json");
|
|
163
|
+
const data = readJson(p);
|
|
164
|
+
if (!data) return null;
|
|
165
|
+
const entries = Array.isArray(data) ? data : data.entries || [];
|
|
166
|
+
return { entryCount: entries.length, hasUsage: entries.length > 0 };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function getProjectName() {
|
|
170
|
+
const pj = readJson(join(REPO_ROOT, "project.json"));
|
|
171
|
+
if (pj?.name) return pj.name;
|
|
172
|
+
const pkg = readJson(join(REPO_ROOT, "package.json"));
|
|
173
|
+
return pkg?.name || "project";
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function exportDashboard() {
|
|
177
|
+
const intents = collectIntents();
|
|
178
|
+
const workflow = getWorkflowState();
|
|
179
|
+
const calibration = getCalibration();
|
|
180
|
+
const docLinks = getDocLinks();
|
|
181
|
+
const drift = getDriftSummary();
|
|
182
|
+
const usage = getUsageSummary();
|
|
183
|
+
|
|
184
|
+
const payload = {
|
|
185
|
+
generated: new Date().toISOString(),
|
|
186
|
+
projectName: getProjectName(),
|
|
187
|
+
intents,
|
|
188
|
+
workflow,
|
|
189
|
+
calibration,
|
|
190
|
+
docLinks,
|
|
191
|
+
drift,
|
|
192
|
+
usage,
|
|
193
|
+
testSummary: null,
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const outDir = join(REPO_ROOT, "dashboard-app", "public");
|
|
197
|
+
const outPath = join(outDir, "dashboard.json");
|
|
198
|
+
mkdirSync(outDir, { recursive: true });
|
|
199
|
+
writeFileSync(outPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
200
|
+
console.log(`Wrote ${outPath}`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
exportDashboard();
|
|
205
|
+
} catch (err) {
|
|
206
|
+
console.error(err);
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Fix Intent Issues Script
|
|
4
|
+
# Detects and auto-fixes common intent issues
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
error_exit() {
|
|
9
|
+
echo "ERROR: $1" >&2
|
|
10
|
+
exit "${2:-1}"
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
warning() {
|
|
14
|
+
echo "WARNING: $1" >&2
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
# Colors
|
|
18
|
+
RED='\033[0;31m'
|
|
19
|
+
YELLOW='\033[1;33m'
|
|
20
|
+
GREEN='\033[0;32m'
|
|
21
|
+
BLUE='\033[0;34m'
|
|
22
|
+
NC='\033[0m'
|
|
23
|
+
|
|
24
|
+
# Source validation library
|
|
25
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
26
|
+
source "$SCRIPT_DIR/lib/validate-intents.sh" || error_exit "Failed to load validation library" 1
|
|
27
|
+
|
|
28
|
+
INTENT_DIR="${INTENT_DIR:-work/intent}"
|
|
29
|
+
|
|
30
|
+
# Fix whitespace issues in dependencies
|
|
31
|
+
fix_whitespace() {
|
|
32
|
+
local intent_file="$1"
|
|
33
|
+
local temp_file=$(mktemp)
|
|
34
|
+
|
|
35
|
+
if ! awk '
|
|
36
|
+
BEGIN { in_deps=0 }
|
|
37
|
+
/^## Dependencies/ {
|
|
38
|
+
in_deps=1
|
|
39
|
+
print
|
|
40
|
+
next
|
|
41
|
+
}
|
|
42
|
+
in_deps && /^## / {
|
|
43
|
+
in_deps=0
|
|
44
|
+
print
|
|
45
|
+
next
|
|
46
|
+
}
|
|
47
|
+
in_deps && /^[[:space:]]+- / {
|
|
48
|
+
# Remove leading whitespace
|
|
49
|
+
sub(/^[[:space:]]+/, "")
|
|
50
|
+
print
|
|
51
|
+
next
|
|
52
|
+
}
|
|
53
|
+
{ print }
|
|
54
|
+
' "$intent_file" > "$temp_file"; then
|
|
55
|
+
rm -f "$temp_file"
|
|
56
|
+
return 1
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
mv "$temp_file" "$intent_file"
|
|
60
|
+
echo "โ Fixed whitespace in $(basename "$intent_file")"
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# Fix dependency ordering by moving dependency to same/earlier release
|
|
64
|
+
fix_dependency_ordering() {
|
|
65
|
+
local intent_id="$1"
|
|
66
|
+
local dep_id="$2"
|
|
67
|
+
local intent_release="$3"
|
|
68
|
+
|
|
69
|
+
local dep_file=$(find "$INTENT_DIR" -type f -name "${dep_id}.md" -print -quit 2>/dev/null)
|
|
70
|
+
if [ -z "$dep_file" ] || [ ! -f "$dep_file" ]; then
|
|
71
|
+
warning "Cannot fix: $dep_id file not found"
|
|
72
|
+
return 1
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
# Update dependency's release target to match or be earlier than dependent's release
|
|
76
|
+
local temp_file=$(mktemp)
|
|
77
|
+
if ! awk -v target_release="$intent_release" '
|
|
78
|
+
BEGIN { in_release=0; release_set=0 }
|
|
79
|
+
/^## Release Target/ {
|
|
80
|
+
in_release=1
|
|
81
|
+
# Check if inline format
|
|
82
|
+
if (match($0, /R[0-9]+/)) {
|
|
83
|
+
current_release = substr($0, RSTART, RLENGTH)
|
|
84
|
+
current_num = substr(current_release, 2)
|
|
85
|
+
target_num = substr(target_release, 2)
|
|
86
|
+
if (current_num > target_num) {
|
|
87
|
+
# Replace with target release
|
|
88
|
+
sub(/R[0-9]+/, target_release)
|
|
89
|
+
release_set=1
|
|
90
|
+
}
|
|
91
|
+
print
|
|
92
|
+
next
|
|
93
|
+
}
|
|
94
|
+
print
|
|
95
|
+
next
|
|
96
|
+
}
|
|
97
|
+
in_release && /^## / {
|
|
98
|
+
if (!release_set) {
|
|
99
|
+
# Release target not set yet, add it
|
|
100
|
+
print target_release
|
|
101
|
+
release_set=1
|
|
102
|
+
}
|
|
103
|
+
in_release=0
|
|
104
|
+
print
|
|
105
|
+
next
|
|
106
|
+
}
|
|
107
|
+
in_release && /R[0-9]+/ {
|
|
108
|
+
if (!/\|/) { # Not a template line
|
|
109
|
+
if (match($0, /R[0-9]+/)) {
|
|
110
|
+
current_release = substr($0, RSTART, RLENGTH)
|
|
111
|
+
current_num = substr(current_release, 2)
|
|
112
|
+
target_num = substr(target_release, 2)
|
|
113
|
+
if (current_num > target_num) {
|
|
114
|
+
# Replace with target release
|
|
115
|
+
sub(/R[0-9]+/, target_release)
|
|
116
|
+
release_set=1
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
print
|
|
121
|
+
next
|
|
122
|
+
}
|
|
123
|
+
{ print }
|
|
124
|
+
END {
|
|
125
|
+
if (in_release && !release_set) {
|
|
126
|
+
print target_release
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
' "$dep_file" > "$temp_file"; then
|
|
130
|
+
rm -f "$temp_file"
|
|
131
|
+
return 1
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
mv "$temp_file" "$dep_file"
|
|
135
|
+
echo "โ Moved $dep_id to $intent_release (to satisfy $intent_id dependency)"
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
# Main fix function
|
|
139
|
+
main() {
|
|
140
|
+
echo -e "${BLUE}Scanning for intent issues...${NC}"
|
|
141
|
+
echo ""
|
|
142
|
+
|
|
143
|
+
# Collect all issues
|
|
144
|
+
local all_issues=()
|
|
145
|
+
local intent_files=()
|
|
146
|
+
|
|
147
|
+
# Find all intent files recursively
|
|
148
|
+
intent_files=()
|
|
149
|
+
while IFS= read -r file; do
|
|
150
|
+
intent_files+=("$file")
|
|
151
|
+
done < <(find "$INTENT_DIR" -type f -name "*.md" ! -name "_TEMPLATE.md" 2>/dev/null)
|
|
152
|
+
|
|
153
|
+
if [ ${#intent_files[@]} -eq 0 ]; then
|
|
154
|
+
echo -e "${GREEN}โ No intent files found${NC}"
|
|
155
|
+
return 0
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
# Validate all intents and collect issues
|
|
159
|
+
for intent_file in "${intent_files[@]}"; do
|
|
160
|
+
while IFS= read -r issue; do
|
|
161
|
+
[ -n "$issue" ] && all_issues+=("$issue")
|
|
162
|
+
done < <(validate_intent "$intent_file" || true)
|
|
163
|
+
done
|
|
164
|
+
|
|
165
|
+
if [ ${#all_issues[@]} -eq 0 ]; then
|
|
166
|
+
echo -e "${GREEN}โ No issues found${NC}"
|
|
167
|
+
return 0
|
|
168
|
+
fi
|
|
169
|
+
|
|
170
|
+
# Display issues
|
|
171
|
+
echo -e "${YELLOW}Found ${#all_issues[@]} issue(s):${NC}"
|
|
172
|
+
echo ""
|
|
173
|
+
|
|
174
|
+
local i=1
|
|
175
|
+
for issue in "${all_issues[@]}"; do
|
|
176
|
+
local issue_type=$(echo "$issue" | cut -d'|' -f1)
|
|
177
|
+
local intent_id=$(echo "$issue" | cut -d'|' -f2)
|
|
178
|
+
local message=$(echo "$issue" | cut -d'|' -f3)
|
|
179
|
+
local fix_suggestion=$(echo "$issue" | cut -d'|' -f4)
|
|
180
|
+
|
|
181
|
+
echo -e "${YELLOW}[$i]${NC} $intent_id: $message"
|
|
182
|
+
echo " Fix: $fix_suggestion"
|
|
183
|
+
echo ""
|
|
184
|
+
i=$((i + 1))
|
|
185
|
+
done
|
|
186
|
+
|
|
187
|
+
# Ask for confirmation
|
|
188
|
+
echo -e "${BLUE}Apply fixes? (y/n):${NC} "
|
|
189
|
+
read -r response
|
|
190
|
+
|
|
191
|
+
if [ "$response" != "y" ] && [ "$response" != "Y" ]; then
|
|
192
|
+
echo "Cancelled."
|
|
193
|
+
return 0
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
echo ""
|
|
197
|
+
echo -e "${BLUE}Applying fixes...${NC}"
|
|
198
|
+
echo ""
|
|
199
|
+
|
|
200
|
+
# Apply fixes
|
|
201
|
+
local fixed_count=0
|
|
202
|
+
for issue in "${all_issues[@]}"; do
|
|
203
|
+
local issue_type=$(echo "$issue" | cut -d'|' -f1)
|
|
204
|
+
local intent_id=$(echo "$issue" | cut -d'|' -f2)
|
|
205
|
+
local intent_file=$(find "$INTENT_DIR" -type f -name "${intent_id}.md" -print -quit 2>/dev/null)
|
|
206
|
+
if [ -z "$intent_file" ] || [ ! -f "$intent_file" ]; then
|
|
207
|
+
warning "Intent file not found: $intent_id"
|
|
208
|
+
continue
|
|
209
|
+
fi
|
|
210
|
+
|
|
211
|
+
case "$issue_type" in
|
|
212
|
+
whitespace)
|
|
213
|
+
fix_whitespace "$intent_file"
|
|
214
|
+
fixed_count=$((fixed_count + 1))
|
|
215
|
+
;;
|
|
216
|
+
dependency_ordering)
|
|
217
|
+
# Extract dependency ID from message
|
|
218
|
+
local dep_id=$(echo "$issue" | grep -o '[A-Z]-[0-9]*' | head -2 | tail -1)
|
|
219
|
+
local intent_release=$(echo "$issue" | grep -o 'R[0-9]*' | head -1)
|
|
220
|
+
if [ -n "$dep_id" ] && [ -n "$intent_release" ]; then
|
|
221
|
+
fix_dependency_ordering "$intent_id" "$dep_id" "$intent_release"
|
|
222
|
+
fixed_count=$((fixed_count + 1))
|
|
223
|
+
fi
|
|
224
|
+
;;
|
|
225
|
+
missing_dependency|circular)
|
|
226
|
+
echo -e "${YELLOW}โ Skipping $issue_type issue (requires manual intervention)${NC}"
|
|
227
|
+
;;
|
|
228
|
+
esac
|
|
229
|
+
done
|
|
230
|
+
|
|
231
|
+
echo ""
|
|
232
|
+
if [ $fixed_count -gt 0 ]; then
|
|
233
|
+
echo -e "${GREEN}โ Fixed $fixed_count issue(s)${NC}"
|
|
234
|
+
else
|
|
235
|
+
echo -e "${YELLOW}No automatic fixes applied${NC}"
|
|
236
|
+
fi
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
main "$@"
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Generate Project Dashboard Script
|
|
4
|
+
# Creates a project status dashboard
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
error_exit() {
|
|
9
|
+
echo "ERROR: $1" >&2
|
|
10
|
+
exit "${2:-1}"
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
# Colors
|
|
14
|
+
GREEN='\033[0;32m'
|
|
15
|
+
YELLOW='\033[1;33m'
|
|
16
|
+
BLUE='\033[0;34m'
|
|
17
|
+
NC='\033[0m'
|
|
18
|
+
|
|
19
|
+
DASHBOARD_FILE="DASHBOARD.md"
|
|
20
|
+
|
|
21
|
+
echo -e "${BLUE}Generating project dashboard...${NC}"
|
|
22
|
+
|
|
23
|
+
# Check prerequisites
|
|
24
|
+
if [ ! -f "project.json" ]; then
|
|
25
|
+
error_exit "project.json not found. Run /init-project first." 1
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
PROJECT_NAME=$(jq -r '.name' project.json 2>/dev/null || echo "project")
|
|
29
|
+
|
|
30
|
+
# Collect metrics
|
|
31
|
+
intent_files=()
|
|
32
|
+
while IFS= read -r file; do
|
|
33
|
+
intent_files+=("$file")
|
|
34
|
+
done < <(find intent -type f -name "*.md" ! -name "_TEMPLATE.md" 2>/dev/null)
|
|
35
|
+
|
|
36
|
+
INTENT_TOTAL=${#intent_files[@]}
|
|
37
|
+
if [ "$INTENT_TOTAL" -gt 0 ]; then
|
|
38
|
+
INTENT_PLANNED=$(grep -l "Status.*planned" "${intent_files[@]}" 2>/dev/null | wc -l | tr -d ' ')
|
|
39
|
+
INTENT_ACTIVE=$(grep -l "Status.*active" "${intent_files[@]}" 2>/dev/null | wc -l | tr -d ' ')
|
|
40
|
+
INTENT_SHIPPED=$(grep -l "Status.*shipped" "${intent_files[@]}" 2>/dev/null | wc -l | tr -d ' ')
|
|
41
|
+
INTENT_BLOCKED=$(grep -l "Status.*blocked" "${intent_files[@]}" 2>/dev/null | wc -l | tr -d ' ')
|
|
42
|
+
else
|
|
43
|
+
INTENT_PLANNED=0
|
|
44
|
+
INTENT_ACTIVE=0
|
|
45
|
+
INTENT_SHIPPED=0
|
|
46
|
+
INTENT_BLOCKED=0
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# Calculate progress
|
|
50
|
+
if [ "$INTENT_TOTAL" -gt 0 ]; then
|
|
51
|
+
PROGRESS=$((INTENT_SHIPPED * 100 / INTENT_TOTAL))
|
|
52
|
+
else
|
|
53
|
+
PROGRESS=0
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# Get active intent details
|
|
57
|
+
ACTIVE_INTENT=$(grep -h "Intent ID:" work/workflow-state/active.md 2>/dev/null | grep -o "F-[0-9]*\|B-[0-9]*\|T-[0-9]*" | head -1 || echo "none")
|
|
58
|
+
ACTIVE_PHASE=$(grep -h "Current Phase:" work/workflow-state/active.md 2>/dev/null | sed 's/.*Current Phase: *//' || echo "unknown")
|
|
59
|
+
|
|
60
|
+
# Test coverage (if available)
|
|
61
|
+
if command -v pnpm >/dev/null 2>&1 && [ -f "package.json" ]; then
|
|
62
|
+
COVERAGE=$(pnpm test:coverage 2>/dev/null | grep -o "[0-9]*%" | head -1 || echo "N/A")
|
|
63
|
+
else
|
|
64
|
+
COVERAGE="N/A"
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# Generate dashboard
|
|
68
|
+
cat > "$DASHBOARD_FILE" << EOF || error_exit "Failed to generate dashboard"
|
|
69
|
+
# Project Dashboard: $PROJECT_NAME
|
|
70
|
+
|
|
71
|
+
**Last Updated:** $(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
72
|
+
|
|
73
|
+
## ๐ Status Overview
|
|
74
|
+
|
|
75
|
+
| Metric | Value |
|
|
76
|
+
|--------|-------|
|
|
77
|
+
| **Total Intents** | $INTENT_TOTAL |
|
|
78
|
+
| **Planned** | $INTENT_PLANNED |
|
|
79
|
+
| **Active** | $INTENT_ACTIVE |
|
|
80
|
+
| **Shipped** | $INTENT_SHIPPED |
|
|
81
|
+
| **Blocked** | $INTENT_BLOCKED |
|
|
82
|
+
| **Progress** | $PROGRESS% |
|
|
83
|
+
|
|
84
|
+
## ๐ฏ Current Work
|
|
85
|
+
|
|
86
|
+
- **Active Intent:** $ACTIVE_INTENT
|
|
87
|
+
- **Current Phase:** $ACTIVE_PHASE
|
|
88
|
+
|
|
89
|
+
## ๐ Progress Visualization
|
|
90
|
+
|
|
91
|
+
\`\`\`
|
|
92
|
+
Shipped: [$(printf '#%.0s' $(seq 1 $((PROGRESS / 2))))$(printf ' %.0s' $(seq 1 $((50 - PROGRESS / 2))))] $PROGRESS%
|
|
93
|
+
\`\`\`
|
|
94
|
+
|
|
95
|
+
## ๐ Intent Status Breakdown
|
|
96
|
+
|
|
97
|
+
- โ
Shipped: $INTENT_SHIPPED
|
|
98
|
+
- ๐ Active: $INTENT_ACTIVE
|
|
99
|
+
- ๐ Planned: $INTENT_PLANNED
|
|
100
|
+
- โ Blocked: $INTENT_BLOCKED
|
|
101
|
+
|
|
102
|
+
## ๐งช Quality Metrics
|
|
103
|
+
|
|
104
|
+
- **Test Coverage:** $COVERAGE
|
|
105
|
+
- **Lint Status:** [Run: pnpm lint]
|
|
106
|
+
- **Type Check:** [Run: pnpm typecheck]
|
|
107
|
+
|
|
108
|
+
## ๐ Recent Intents
|
|
109
|
+
|
|
110
|
+
$(if [ "$INTENT_TOTAL" -gt 0 ]; then printf "%s\n" "${intent_files[@]}" | head -5 | xargs -I{} basename "{}" .md | sed 's/^/- /'; else echo "No intents yet"; fi)
|
|
111
|
+
|
|
112
|
+
## ๐ Quick Links
|
|
113
|
+
|
|
114
|
+
- [Project Context](./PROJECT_CONTEXT.md)
|
|
115
|
+
- [Roadmap](./work/roadmap/now.md)
|
|
116
|
+
- [Dependencies](./_system/artifacts/dependencies.md)
|
|
117
|
+
- [Deployment History](./deployment-history.md)
|
|
118
|
+
|
|
119
|
+
## ๐ Next Actions
|
|
120
|
+
|
|
121
|
+
1. Review active intent: $ACTIVE_INTENT
|
|
122
|
+
2. Check workflow state: work/workflow-state/
|
|
123
|
+
3. Update roadmap: work/roadmap/
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
*This dashboard is auto-generated. Run \`pnpm generate-dashboard\` to update.*
|
|
128
|
+
EOF
|
|
129
|
+
|
|
130
|
+
echo -e "${GREEN}โ Generated $DASHBOARD_FILE${NC}"
|
|
131
|
+
echo ""
|
|
132
|
+
echo -e "${YELLOW}Dashboard Metrics:${NC}"
|
|
133
|
+
echo " Total Intents: $INTENT_TOTAL"
|
|
134
|
+
echo " Progress: $PROGRESS%"
|
|
135
|
+
echo " Active: $ACTIVE_INTENT"
|
|
136
|
+
echo ""
|