@benzotti/jedi 0.1.39 → 0.1.40
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/action/workflow-template.yml +18 -192
- package/dist/index.js +510 -13
- package/package.json +1 -1
|
@@ -60,14 +60,13 @@ jobs:
|
|
|
60
60
|
- name: Resolve PR branch
|
|
61
61
|
id: pr
|
|
62
62
|
run: |
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
echo "branch=$BRANCH" >> "$GITHUB_OUTPUT"
|
|
68
|
-
fi
|
|
63
|
+
bunx @benzotti/jedi@latest action resolve-branch \
|
|
64
|
+
--repo "$REPO" \
|
|
65
|
+
${PR_HEAD_REF:+--pr-head-ref "$PR_HEAD_REF"} \
|
|
66
|
+
${PR_NUMBER:+--pr-number "$PR_NUMBER"}
|
|
69
67
|
env:
|
|
70
68
|
GH_TOKEN: ${{ github.token }}
|
|
69
|
+
REPO: ${{ github.repository }}
|
|
71
70
|
PR_HEAD_REF: ${{ github.event.pull_request.head.ref || '' }}
|
|
72
71
|
PR_NUMBER: ${{ github.event.issue.number || github.event.pull_request.number || '' }}
|
|
73
72
|
|
|
@@ -100,81 +99,14 @@ jobs:
|
|
|
100
99
|
# Bootstrap Jedi framework if not present (first run or cache miss)
|
|
101
100
|
- name: Bootstrap Jedi
|
|
102
101
|
run: |
|
|
103
|
-
|
|
104
|
-
bunx @benzotti/jedi@latest init --ci
|
|
105
|
-
fi
|
|
106
|
-
mkdir -p .jdi/persistence
|
|
107
|
-
|
|
108
|
-
# If cache was restored from fallback (not exact branch match),
|
|
109
|
-
# clear plans and state — they're branch-local, only learnings carry forward
|
|
110
|
-
if [ "${{ steps.cache.outputs.cache-hit }}" != "true" ]; then
|
|
111
|
-
echo "Cache miss or fallback — clearing plan state"
|
|
112
|
-
rm -rf .jdi/plans/*
|
|
113
|
-
mkdir -p .jdi/config
|
|
114
|
-
printf 'active_plan: null\ncurrent_wave: null\nmode: null\n' > .jdi/config/state.yaml
|
|
115
|
-
fi
|
|
116
|
-
|
|
117
|
-
# Exclude Jedi working directories from git so they don't get committed
|
|
118
|
-
# or conflict with branch checkouts — they live in the cache only
|
|
119
|
-
mkdir -p .git/info
|
|
120
|
-
for pattern in '.jdi/' '.claude/'; do
|
|
121
|
-
grep -qxF "$pattern" .git/info/exclude 2>/dev/null || echo "$pattern" >> .git/info/exclude
|
|
122
|
-
done
|
|
102
|
+
bunx @benzotti/jedi@latest action bootstrap --cache-hit "${{ steps.cache.outputs.cache-hit }}"
|
|
123
103
|
|
|
124
104
|
# Fetch shared learnings from an external repo if configured.
|
|
125
105
|
# This merges cross-project learnings into the local framework before agents run.
|
|
126
106
|
- name: Fetch shared learnings
|
|
127
107
|
if: vars.JEDI_LEARNINGS_REPO != ''
|
|
128
108
|
run: |
|
|
129
|
-
|
|
130
|
-
REMOTE_SUBDIR="jedi/learnings"
|
|
131
|
-
mkdir -p "$LEARNINGS_DIR"
|
|
132
|
-
|
|
133
|
-
# Clone only the learnings directory (sparse checkout)
|
|
134
|
-
TMPDIR=$(mktemp -d)
|
|
135
|
-
git clone --depth 1 --filter=blob:none --sparse \
|
|
136
|
-
"https://x-access-token:${LEARNINGS_TOKEN}@github.com/${LEARNINGS_REPO}.git" \
|
|
137
|
-
"$TMPDIR" 2>/dev/null || {
|
|
138
|
-
echo "Could not clone learnings repo — continuing without shared learnings"
|
|
139
|
-
exit 0
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
cd "$TMPDIR"
|
|
143
|
-
git sparse-checkout set "$REMOTE_SUBDIR" 2>/dev/null || true
|
|
144
|
-
cd - > /dev/null
|
|
145
|
-
|
|
146
|
-
# Merge learnings from external repo into local framework
|
|
147
|
-
if [ -d "$TMPDIR/$REMOTE_SUBDIR" ]; then
|
|
148
|
-
for f in "$TMPDIR/$REMOTE_SUBDIR"/*.md; do
|
|
149
|
-
[ -f "$f" ] || continue
|
|
150
|
-
BASENAME=$(basename "$f")
|
|
151
|
-
LOCAL_FILE="$LEARNINGS_DIR/$BASENAME"
|
|
152
|
-
|
|
153
|
-
if [ ! -f "$LOCAL_FILE" ]; then
|
|
154
|
-
cp "$f" "$LOCAL_FILE"
|
|
155
|
-
echo "Loaded shared learning: $BASENAME"
|
|
156
|
-
else
|
|
157
|
-
# Append lines from remote that aren't already in the local file
|
|
158
|
-
MERGED=0
|
|
159
|
-
while IFS= read -r line; do
|
|
160
|
-
[ -z "$line" ] && continue
|
|
161
|
-
if ! grep -qFx "$line" "$LOCAL_FILE" 2>/dev/null; then
|
|
162
|
-
echo "$line" >> "$LOCAL_FILE"
|
|
163
|
-
MERGED=$((MERGED + 1))
|
|
164
|
-
fi
|
|
165
|
-
done < "$f"
|
|
166
|
-
if [ "$MERGED" -gt 0 ]; then
|
|
167
|
-
echo "Merged $MERGED new lines into $BASENAME"
|
|
168
|
-
else
|
|
169
|
-
echo "No new learnings to merge for $BASENAME"
|
|
170
|
-
fi
|
|
171
|
-
fi
|
|
172
|
-
done
|
|
173
|
-
else
|
|
174
|
-
echo "No shared learnings found — continuing"
|
|
175
|
-
fi
|
|
176
|
-
|
|
177
|
-
rm -rf "$TMPDIR"
|
|
109
|
+
bunx @benzotti/jedi@latest action fetch-learnings --learnings-repo "$LEARNINGS_REPO"
|
|
178
110
|
env:
|
|
179
111
|
LEARNINGS_REPO: ${{ vars.JEDI_LEARNINGS_REPO }}
|
|
180
112
|
LEARNINGS_TOKEN: ${{ secrets.JEDI_LEARNINGS_TOKEN || github.token }}
|
|
@@ -198,7 +130,7 @@ jobs:
|
|
|
198
130
|
# Set JEDI_ALLOWED_USERS to a comma-separated list for explicit allow-listing.
|
|
199
131
|
- name: Run Jedi
|
|
200
132
|
run: |
|
|
201
|
-
bunx @benzotti/jedi@latest action "$COMMENT_BODY" \
|
|
133
|
+
bunx @benzotti/jedi@latest action run "$COMMENT_BODY" \
|
|
202
134
|
--repo "$REPO" \
|
|
203
135
|
${COMMENT_ID:+--comment-id "$COMMENT_ID"} \
|
|
204
136
|
${PR_NUMBER:+--pr-number "$PR_NUMBER"} \
|
|
@@ -241,32 +173,10 @@ jobs:
|
|
|
241
173
|
- name: Check if Jedi was involved
|
|
242
174
|
id: check
|
|
243
175
|
run: |
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
echo "skip=true" >> "$GITHUB_OUTPUT"
|
|
249
|
-
exit 0
|
|
250
|
-
fi
|
|
251
|
-
|
|
252
|
-
# Check if Jedi commented on this PR (comments from github-actions bot mentioning Jedi)
|
|
253
|
-
JEDI_ACTIVITY=$(gh api "repos/$REPO/issues/$PR_NUMBER/comments" --paginate \
|
|
254
|
-
--jq '[.[] | select(.user.login == "github-actions[bot]" and (.body | test("jedi|Jedi|🤖")))] | length' 2>/dev/null || echo "0")
|
|
255
|
-
|
|
256
|
-
# Also check if jedi[bot] made any commits on the PR
|
|
257
|
-
JEDI_COMMITS=$(gh api "repos/$REPO/pulls/$PR_NUMBER/commits" --paginate \
|
|
258
|
-
--jq '[.[] | select(.commit.author.name == "jedi[bot]")] | length' 2>/dev/null || echo "0")
|
|
259
|
-
|
|
260
|
-
if [ "$JEDI_ACTIVITY" -gt 0 ] || [ "$JEDI_COMMITS" -gt 0 ]; then
|
|
261
|
-
BRANCH=$(gh api "repos/$REPO/pulls/$PR_NUMBER" --jq '.head.ref')
|
|
262
|
-
echo "Jedi was active on PR #$PR_NUMBER (comments: $JEDI_ACTIVITY, commits: $JEDI_COMMITS) — promoting learnings"
|
|
263
|
-
echo "skip=false" >> "$GITHUB_OUTPUT"
|
|
264
|
-
echo "branch=$BRANCH" >> "$GITHUB_OUTPUT"
|
|
265
|
-
echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT"
|
|
266
|
-
else
|
|
267
|
-
echo "No Jedi activity on PR #$PR_NUMBER — skipping"
|
|
268
|
-
echo "skip=true" >> "$GITHUB_OUTPUT"
|
|
269
|
-
fi
|
|
176
|
+
bunx @benzotti/jedi@latest action promote-learnings \
|
|
177
|
+
--check-only \
|
|
178
|
+
--repo "$REPO" \
|
|
179
|
+
--sha "${{ github.sha }}"
|
|
270
180
|
env:
|
|
271
181
|
GH_TOKEN: ${{ github.token }}
|
|
272
182
|
REPO: ${{ github.repository }}
|
|
@@ -315,98 +225,14 @@ jobs:
|
|
|
315
225
|
&& steps.restore.outputs.cache-hit != ''
|
|
316
226
|
&& vars.JEDI_COMMIT_LEARNINGS == 'true'
|
|
317
227
|
run: |
|
|
318
|
-
LEARNINGS_SRC=".jdi/framework/learnings"
|
|
319
|
-
REMOTE_SUBDIR="jedi/learnings"
|
|
320
|
-
|
|
321
|
-
# Only proceed if there are learnings files with content
|
|
322
|
-
if [ ! -d "$LEARNINGS_SRC" ]; then
|
|
323
|
-
echo "No learnings directory found — skipping commit"
|
|
324
|
-
exit 0
|
|
325
|
-
fi
|
|
326
|
-
|
|
327
|
-
# Check if any learnings files have meaningful content (not just headers)
|
|
328
|
-
HAS_CONTENT=false
|
|
329
|
-
for f in "$LEARNINGS_SRC"/*.md; do
|
|
330
|
-
[ -f "$f" ] || continue
|
|
331
|
-
if grep -qvE '^\s*(#|<!--|$)' "$f" 2>/dev/null; then
|
|
332
|
-
HAS_CONTENT=true
|
|
333
|
-
break
|
|
334
|
-
fi
|
|
335
|
-
done
|
|
336
|
-
|
|
337
|
-
if [ "$HAS_CONTENT" != "true" ]; then
|
|
338
|
-
echo "No learnings content to commit — skipping"
|
|
339
|
-
exit 0
|
|
340
|
-
fi
|
|
341
|
-
|
|
342
228
|
git config user.name "jedi[bot]"
|
|
343
229
|
git config user.email "jedi[bot]@users.noreply.github.com"
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
"https://x-access-token:${LEARNINGS_TOKEN}@github.com/${LEARNINGS_REPO}.git" \
|
|
351
|
-
"$TMPDIR" 2>/dev/null || {
|
|
352
|
-
echo "::warning::Could not clone learnings repo ${LEARNINGS_REPO} — skipping commit"
|
|
353
|
-
exit 0
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
mkdir -p "$TMPDIR/$REMOTE_SUBDIR"
|
|
357
|
-
|
|
358
|
-
# Merge learnings: append new lines from local that don't exist in remote
|
|
359
|
-
for f in "$LEARNINGS_SRC"/*.md; do
|
|
360
|
-
[ -f "$f" ] || continue
|
|
361
|
-
BASENAME=$(basename "$f")
|
|
362
|
-
REMOTE_FILE="$TMPDIR/$REMOTE_SUBDIR/$BASENAME"
|
|
363
|
-
|
|
364
|
-
if [ -f "$REMOTE_FILE" ]; then
|
|
365
|
-
# Append lines from local that aren't already in the remote file
|
|
366
|
-
while IFS= read -r line; do
|
|
367
|
-
[ -z "$line" ] && continue
|
|
368
|
-
if ! grep -qFx "$line" "$REMOTE_FILE" 2>/dev/null; then
|
|
369
|
-
echo "$line" >> "$REMOTE_FILE"
|
|
370
|
-
fi
|
|
371
|
-
done < "$f"
|
|
372
|
-
echo "Merged learning: $BASENAME"
|
|
373
|
-
else
|
|
374
|
-
cp "$f" "$REMOTE_FILE"
|
|
375
|
-
echo "Added new learning: $BASENAME"
|
|
376
|
-
fi
|
|
377
|
-
done
|
|
378
|
-
|
|
379
|
-
cd "$TMPDIR"
|
|
380
|
-
git config user.name "jedi[bot]"
|
|
381
|
-
git config user.email "jedi[bot]@users.noreply.github.com"
|
|
382
|
-
git add "$REMOTE_SUBDIR"/*.md
|
|
383
|
-
|
|
384
|
-
if git diff --cached --quiet; then
|
|
385
|
-
echo "Learnings unchanged in external repo — nothing to commit"
|
|
386
|
-
else
|
|
387
|
-
git commit -m "chore(jedi): update learnings from ${{ github.repository }}
|
|
388
|
-
|
|
389
|
-
Source: PR #${{ steps.check.outputs.pr_number }} on ${{ github.repository }}
|
|
390
|
-
Learnings accumulated from PR reviews and feedback."
|
|
391
|
-
git push
|
|
392
|
-
echo "Learnings committed to ${LEARNINGS_REPO}/$REMOTE_SUBDIR"
|
|
393
|
-
fi
|
|
394
|
-
|
|
395
|
-
cd - > /dev/null
|
|
396
|
-
rm -rf "$TMPDIR"
|
|
397
|
-
else
|
|
398
|
-
# ── Same repo: commit directly to main ──
|
|
399
|
-
git add "$LEARNINGS_SRC"/*.md
|
|
400
|
-
if git diff --cached --quiet; then
|
|
401
|
-
echo "Learnings unchanged — nothing to commit"
|
|
402
|
-
else
|
|
403
|
-
git commit -m "chore(jedi): update team learnings
|
|
404
|
-
|
|
405
|
-
Auto-committed by Jedi after PR #${{ steps.check.outputs.pr_number }} merged.
|
|
406
|
-
These learnings are accumulated from PR reviews and feedback."
|
|
407
|
-
git push
|
|
408
|
-
fi
|
|
409
|
-
fi
|
|
230
|
+
bunx @benzotti/jedi@latest action promote-learnings \
|
|
231
|
+
--repo "$REPO" \
|
|
232
|
+
--sha "${{ github.sha }}" \
|
|
233
|
+
--pr-number "${{ steps.check.outputs.pr_number }}" \
|
|
234
|
+
--branch "${{ steps.check.outputs.branch }}" \
|
|
235
|
+
${LEARNINGS_REPO:+--learnings-repo "$LEARNINGS_REPO"}
|
|
410
236
|
env:
|
|
411
237
|
LEARNINGS_REPO: ${{ vars.JEDI_LEARNINGS_REPO || '' }}
|
|
412
238
|
LEARNINGS_TOKEN: ${{ secrets.JEDI_LEARNINGS_TOKEN || github.token }}
|
package/dist/index.js
CHANGED
|
@@ -11292,7 +11292,7 @@ var planApproveCommand = defineCommand({
|
|
|
11292
11292
|
}
|
|
11293
11293
|
});
|
|
11294
11294
|
|
|
11295
|
-
// src/commands/action.ts
|
|
11295
|
+
// src/commands/action/run.ts
|
|
11296
11296
|
import { resolve as resolve11 } from "path";
|
|
11297
11297
|
|
|
11298
11298
|
// src/utils/clickup.ts
|
|
@@ -11568,7 +11568,7 @@ function formatErrorComment(command, summary) {
|
|
|
11568
11568
|
`);
|
|
11569
11569
|
}
|
|
11570
11570
|
|
|
11571
|
-
// src/commands/action.ts
|
|
11571
|
+
// src/commands/action/run.ts
|
|
11572
11572
|
function parseComment(comment, isFollowUp) {
|
|
11573
11573
|
const hasDryRun = /--dry-run/i.test(comment);
|
|
11574
11574
|
const cleanComment = comment.replace(/--dry-run/gi, "").trim();
|
|
@@ -11625,9 +11625,9 @@ function parseComment(comment, isFollowUp) {
|
|
|
11625
11625
|
}
|
|
11626
11626
|
return { ...base, command: "plan", description };
|
|
11627
11627
|
}
|
|
11628
|
-
var
|
|
11628
|
+
var runCommand2 = defineCommand({
|
|
11629
11629
|
meta: {
|
|
11630
|
-
name: "
|
|
11630
|
+
name: "run",
|
|
11631
11631
|
description: "GitHub Action entry point \u2014 parse 'Hey Jedi' comment and run workflow"
|
|
11632
11632
|
},
|
|
11633
11633
|
args: {
|
|
@@ -12129,9 +12129,506 @@ Use the ClickUp ticket above as the primary requirements source.` : ``,
|
|
|
12129
12129
|
}
|
|
12130
12130
|
});
|
|
12131
12131
|
|
|
12132
|
+
// src/commands/action/resolve-branch.ts
|
|
12133
|
+
import { appendFileSync } from "fs";
|
|
12134
|
+
function writeGitHubOutput(key, value) {
|
|
12135
|
+
const outputFile = process.env.GITHUB_OUTPUT;
|
|
12136
|
+
if (outputFile) {
|
|
12137
|
+
appendFileSync(outputFile, `${key}=${value}
|
|
12138
|
+
`);
|
|
12139
|
+
}
|
|
12140
|
+
console.log(`${key}=${value}`);
|
|
12141
|
+
}
|
|
12142
|
+
function resolveBranch(opts) {
|
|
12143
|
+
if (opts.prHeadRef) {
|
|
12144
|
+
return opts.prHeadRef;
|
|
12145
|
+
}
|
|
12146
|
+
if (opts.prNumber && opts.repo) {
|
|
12147
|
+
const result = Bun.spawnSync(["gh", "api", `repos/${opts.repo}/pulls/${opts.prNumber}`, "--jq", ".head.ref"], { stdout: "pipe", stderr: "pipe" });
|
|
12148
|
+
if (result.exitCode === 0) {
|
|
12149
|
+
const branch = result.stdout.toString().trim();
|
|
12150
|
+
if (branch)
|
|
12151
|
+
return branch;
|
|
12152
|
+
} else {
|
|
12153
|
+
consola.warn(`Failed to resolve branch via API: ${result.stderr.toString().trim()}`);
|
|
12154
|
+
}
|
|
12155
|
+
}
|
|
12156
|
+
return null;
|
|
12157
|
+
}
|
|
12158
|
+
var resolveBranchCommand = defineCommand({
|
|
12159
|
+
meta: {
|
|
12160
|
+
name: "resolve-branch",
|
|
12161
|
+
description: "Resolve the PR head branch for checkout"
|
|
12162
|
+
},
|
|
12163
|
+
args: {
|
|
12164
|
+
"pr-head-ref": {
|
|
12165
|
+
type: "string",
|
|
12166
|
+
description: "The PR head ref (if available from event context)"
|
|
12167
|
+
},
|
|
12168
|
+
"pr-number": {
|
|
12169
|
+
type: "string",
|
|
12170
|
+
description: "The PR number to look up"
|
|
12171
|
+
},
|
|
12172
|
+
repo: {
|
|
12173
|
+
type: "string",
|
|
12174
|
+
description: "Repository in owner/repo format",
|
|
12175
|
+
required: true
|
|
12176
|
+
}
|
|
12177
|
+
},
|
|
12178
|
+
run({ args }) {
|
|
12179
|
+
const branch = resolveBranch({
|
|
12180
|
+
prHeadRef: args["pr-head-ref"],
|
|
12181
|
+
prNumber: args["pr-number"],
|
|
12182
|
+
repo: args.repo
|
|
12183
|
+
});
|
|
12184
|
+
if (branch) {
|
|
12185
|
+
writeGitHubOutput("branch", branch);
|
|
12186
|
+
} else {
|
|
12187
|
+
consola.info("No branch resolved \u2014 no PR context available");
|
|
12188
|
+
}
|
|
12189
|
+
}
|
|
12190
|
+
});
|
|
12191
|
+
|
|
12192
|
+
// src/commands/action/bootstrap.ts
|
|
12193
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync4, readFileSync, writeFileSync, rmSync, readdirSync } from "fs";
|
|
12194
|
+
import { join as join13 } from "path";
|
|
12195
|
+
function ensureFramework(cwd) {
|
|
12196
|
+
const frameworkDir = join13(cwd, ".jdi/framework");
|
|
12197
|
+
if (!existsSync14(frameworkDir)) {
|
|
12198
|
+
consola.info("Framework not found \u2014 initializing...");
|
|
12199
|
+
const result = Bun.spawnSync(["bunx", "@benzotti/jedi@latest", "init", "--ci"], {
|
|
12200
|
+
cwd,
|
|
12201
|
+
stdout: "inherit",
|
|
12202
|
+
stderr: "inherit"
|
|
12203
|
+
});
|
|
12204
|
+
if (result.exitCode !== 0) {
|
|
12205
|
+
consola.error("Failed to initialize framework");
|
|
12206
|
+
process.exit(1);
|
|
12207
|
+
}
|
|
12208
|
+
}
|
|
12209
|
+
mkdirSync4(join13(cwd, ".jdi/persistence"), { recursive: true });
|
|
12210
|
+
}
|
|
12211
|
+
function clearStaleState(cwd) {
|
|
12212
|
+
const plansDir = join13(cwd, ".jdi/plans");
|
|
12213
|
+
if (existsSync14(plansDir)) {
|
|
12214
|
+
for (const entry of readdirSync(plansDir)) {
|
|
12215
|
+
rmSync(join13(plansDir, entry), { recursive: true, force: true });
|
|
12216
|
+
}
|
|
12217
|
+
} else {
|
|
12218
|
+
mkdirSync4(plansDir, { recursive: true });
|
|
12219
|
+
}
|
|
12220
|
+
const configDir = join13(cwd, ".jdi/config");
|
|
12221
|
+
mkdirSync4(configDir, { recursive: true });
|
|
12222
|
+
writeFileSync(join13(configDir, "state.yaml"), `active_plan: null
|
|
12223
|
+
current_wave: null
|
|
12224
|
+
mode: null
|
|
12225
|
+
`);
|
|
12226
|
+
consola.info("Cache miss or fallback \u2014 cleared plan state");
|
|
12227
|
+
}
|
|
12228
|
+
function setupGitExclude(cwd) {
|
|
12229
|
+
const excludeDir = join13(cwd, ".git/info");
|
|
12230
|
+
mkdirSync4(excludeDir, { recursive: true });
|
|
12231
|
+
const excludePath = join13(excludeDir, "exclude");
|
|
12232
|
+
let content = "";
|
|
12233
|
+
if (existsSync14(excludePath)) {
|
|
12234
|
+
content = readFileSync(excludePath, "utf-8");
|
|
12235
|
+
}
|
|
12236
|
+
const patterns = [".jdi/", ".claude/"];
|
|
12237
|
+
for (const pattern of patterns) {
|
|
12238
|
+
const lines = content.split(`
|
|
12239
|
+
`);
|
|
12240
|
+
if (!lines.some((line) => line === pattern)) {
|
|
12241
|
+
content = content.endsWith(`
|
|
12242
|
+
`) || content === "" ? content : content + `
|
|
12243
|
+
`;
|
|
12244
|
+
content += pattern + `
|
|
12245
|
+
`;
|
|
12246
|
+
}
|
|
12247
|
+
}
|
|
12248
|
+
writeFileSync(excludePath, content);
|
|
12249
|
+
}
|
|
12250
|
+
var bootstrapCommand = defineCommand({
|
|
12251
|
+
meta: {
|
|
12252
|
+
name: "bootstrap",
|
|
12253
|
+
description: "Bootstrap Jedi framework, clear stale state, and configure git excludes"
|
|
12254
|
+
},
|
|
12255
|
+
args: {
|
|
12256
|
+
"cache-hit": {
|
|
12257
|
+
type: "string",
|
|
12258
|
+
description: "Cache hit status from actions/cache ('true' if exact match)"
|
|
12259
|
+
}
|
|
12260
|
+
},
|
|
12261
|
+
run({ args }) {
|
|
12262
|
+
const cwd = process.cwd();
|
|
12263
|
+
ensureFramework(cwd);
|
|
12264
|
+
if (args["cache-hit"] !== "true") {
|
|
12265
|
+
clearStaleState(cwd);
|
|
12266
|
+
}
|
|
12267
|
+
setupGitExclude(cwd);
|
|
12268
|
+
consola.success("Bootstrap complete");
|
|
12269
|
+
}
|
|
12270
|
+
});
|
|
12271
|
+
|
|
12272
|
+
// src/commands/action/fetch-learnings.ts
|
|
12273
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync5, readdirSync as readdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, copyFileSync, rmSync as rmSync2 } from "fs";
|
|
12274
|
+
import { join as join14 } from "path";
|
|
12275
|
+
import { mkdtempSync } from "fs";
|
|
12276
|
+
import { tmpdir } from "os";
|
|
12277
|
+
function cloneLearningsRepo(repo, token, tmpDir) {
|
|
12278
|
+
const cloneUrl = `https://x-access-token:${token}@github.com/${repo}.git`;
|
|
12279
|
+
const cloneResult = Bun.spawnSync(["git", "clone", "--depth", "1", "--filter=blob:none", "--sparse", cloneUrl, tmpDir], { stdout: "pipe", stderr: "pipe" });
|
|
12280
|
+
if (cloneResult.exitCode !== 0) {
|
|
12281
|
+
consola.warn("Could not clone learnings repo \u2014 continuing without shared learnings");
|
|
12282
|
+
return false;
|
|
12283
|
+
}
|
|
12284
|
+
Bun.spawnSync(["git", "sparse-checkout", "set", "jedi/learnings"], {
|
|
12285
|
+
cwd: tmpDir,
|
|
12286
|
+
stdout: "pipe",
|
|
12287
|
+
stderr: "pipe"
|
|
12288
|
+
});
|
|
12289
|
+
return existsSync15(join14(tmpDir, "jedi/learnings"));
|
|
12290
|
+
}
|
|
12291
|
+
function mergeLearnings(sourceDir, targetDir) {
|
|
12292
|
+
const result = { copied: 0, merged: 0 };
|
|
12293
|
+
if (!existsSync15(sourceDir)) {
|
|
12294
|
+
return result;
|
|
12295
|
+
}
|
|
12296
|
+
mkdirSync5(targetDir, { recursive: true });
|
|
12297
|
+
const files = readdirSync2(sourceDir).filter((f3) => f3.endsWith(".md"));
|
|
12298
|
+
for (const file of files) {
|
|
12299
|
+
const sourcePath = join14(sourceDir, file);
|
|
12300
|
+
const targetPath = join14(targetDir, file);
|
|
12301
|
+
if (!existsSync15(targetPath)) {
|
|
12302
|
+
copyFileSync(sourcePath, targetPath);
|
|
12303
|
+
result.copied++;
|
|
12304
|
+
consola.info(`Loaded shared learning: ${file}`);
|
|
12305
|
+
} else {
|
|
12306
|
+
const sourceContent = readFileSync2(sourcePath, "utf-8");
|
|
12307
|
+
const targetContent = readFileSync2(targetPath, "utf-8");
|
|
12308
|
+
const targetLines = new Set(targetContent.split(`
|
|
12309
|
+
`));
|
|
12310
|
+
const newLines = [];
|
|
12311
|
+
for (const line of sourceContent.split(`
|
|
12312
|
+
`)) {
|
|
12313
|
+
if (line.trim() === "")
|
|
12314
|
+
continue;
|
|
12315
|
+
if (!targetLines.has(line)) {
|
|
12316
|
+
newLines.push(line);
|
|
12317
|
+
}
|
|
12318
|
+
}
|
|
12319
|
+
if (newLines.length > 0) {
|
|
12320
|
+
const appendContent = (targetContent.endsWith(`
|
|
12321
|
+
`) ? "" : `
|
|
12322
|
+
`) + newLines.join(`
|
|
12323
|
+
`) + `
|
|
12324
|
+
`;
|
|
12325
|
+
writeFileSync2(targetPath, targetContent + appendContent);
|
|
12326
|
+
result.merged++;
|
|
12327
|
+
consola.info(`Merged ${newLines.length} new lines into ${file}`);
|
|
12328
|
+
} else {
|
|
12329
|
+
consola.info(`No new learnings to merge for ${file}`);
|
|
12330
|
+
}
|
|
12331
|
+
}
|
|
12332
|
+
}
|
|
12333
|
+
return result;
|
|
12334
|
+
}
|
|
12335
|
+
var fetchLearningsCommand = defineCommand({
|
|
12336
|
+
meta: {
|
|
12337
|
+
name: "fetch-learnings",
|
|
12338
|
+
description: "Fetch and merge shared learnings from an external repository"
|
|
12339
|
+
},
|
|
12340
|
+
args: {
|
|
12341
|
+
"learnings-repo": {
|
|
12342
|
+
type: "string",
|
|
12343
|
+
description: "External learnings repository (e.g. org/jedi-learnings)"
|
|
12344
|
+
},
|
|
12345
|
+
"learnings-token": {
|
|
12346
|
+
type: "string",
|
|
12347
|
+
description: "Token for accessing the learnings repo"
|
|
12348
|
+
}
|
|
12349
|
+
},
|
|
12350
|
+
run({ args }) {
|
|
12351
|
+
const learningsRepo = args["learnings-repo"];
|
|
12352
|
+
if (!learningsRepo) {
|
|
12353
|
+
consola.info("No learnings repo configured \u2014 skipping");
|
|
12354
|
+
return;
|
|
12355
|
+
}
|
|
12356
|
+
const token = args["learnings-token"] || process.env.LEARNINGS_TOKEN || process.env.GH_TOKEN || "";
|
|
12357
|
+
if (!token) {
|
|
12358
|
+
consola.warn("No token available for learnings repo \u2014 skipping");
|
|
12359
|
+
return;
|
|
12360
|
+
}
|
|
12361
|
+
const cwd = process.cwd();
|
|
12362
|
+
const learningsDir = join14(cwd, ".jdi/framework/learnings");
|
|
12363
|
+
mkdirSync5(learningsDir, { recursive: true });
|
|
12364
|
+
const tmpDir = mkdtempSync(join14(tmpdir(), "jedi-learnings-"));
|
|
12365
|
+
try {
|
|
12366
|
+
const cloned = cloneLearningsRepo(learningsRepo, token, tmpDir);
|
|
12367
|
+
if (!cloned) {
|
|
12368
|
+
return;
|
|
12369
|
+
}
|
|
12370
|
+
const sourceDir = join14(tmpDir, "jedi/learnings");
|
|
12371
|
+
const result = mergeLearnings(sourceDir, learningsDir);
|
|
12372
|
+
consola.success(`Learnings fetch complete (copied: ${result.copied}, merged: ${result.merged})`);
|
|
12373
|
+
} finally {
|
|
12374
|
+
rmSync2(tmpDir, { recursive: true, force: true });
|
|
12375
|
+
}
|
|
12376
|
+
}
|
|
12377
|
+
});
|
|
12378
|
+
|
|
12379
|
+
// src/commands/action/promote-learnings.ts
|
|
12380
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync6, readdirSync as readdirSync3, readFileSync as readFileSync3, rmSync as rmSync3, appendFileSync as appendFileSync2 } from "fs";
|
|
12381
|
+
import { join as join15 } from "path";
|
|
12382
|
+
import { mkdtempSync as mkdtempSync2 } from "fs";
|
|
12383
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
12384
|
+
function writeGitHubOutput2(key, value) {
|
|
12385
|
+
const outputFile = process.env.GITHUB_OUTPUT;
|
|
12386
|
+
if (outputFile) {
|
|
12387
|
+
appendFileSync2(outputFile, `${key}=${value}
|
|
12388
|
+
`);
|
|
12389
|
+
}
|
|
12390
|
+
console.log(`${key}=${value}`);
|
|
12391
|
+
}
|
|
12392
|
+
function checkJediInvolvement(repo, sha) {
|
|
12393
|
+
const prResult = Bun.spawnSync(["gh", "api", `repos/${repo}/commits/${sha}/pulls`, "--jq", ".[0].number // empty"], { stdout: "pipe", stderr: "pipe" });
|
|
12394
|
+
const prNumberStr = prResult.stdout.toString().trim();
|
|
12395
|
+
if (!prNumberStr) {
|
|
12396
|
+
consola.info("No associated PR found \u2014 skipping");
|
|
12397
|
+
return { skip: true };
|
|
12398
|
+
}
|
|
12399
|
+
const prNumber = parseInt(prNumberStr, 10);
|
|
12400
|
+
const commentsResult = Bun.spawnSync([
|
|
12401
|
+
"gh",
|
|
12402
|
+
"api",
|
|
12403
|
+
`repos/${repo}/issues/${prNumber}/comments`,
|
|
12404
|
+
"--paginate",
|
|
12405
|
+
"--jq",
|
|
12406
|
+
`[.[] | select(.user.login == "github-actions[bot]" and (.body | test("jedi|Jedi")))] | length`
|
|
12407
|
+
], { stdout: "pipe", stderr: "pipe" });
|
|
12408
|
+
const jediActivity = parseInt(commentsResult.stdout.toString().trim() || "0", 10);
|
|
12409
|
+
const commitsResult = Bun.spawnSync([
|
|
12410
|
+
"gh",
|
|
12411
|
+
"api",
|
|
12412
|
+
`repos/${repo}/pulls/${prNumber}/commits`,
|
|
12413
|
+
"--paginate",
|
|
12414
|
+
"--jq",
|
|
12415
|
+
`[.[] | select(.commit.author.name == "jedi[bot]")] | length`
|
|
12416
|
+
], { stdout: "pipe", stderr: "pipe" });
|
|
12417
|
+
const jediCommits = parseInt(commitsResult.stdout.toString().trim() || "0", 10);
|
|
12418
|
+
if (jediActivity > 0 || jediCommits > 0) {
|
|
12419
|
+
const branchResult = Bun.spawnSync(["gh", "api", `repos/${repo}/pulls/${prNumber}`, "--jq", ".head.ref"], { stdout: "pipe", stderr: "pipe" });
|
|
12420
|
+
const branch = branchResult.stdout.toString().trim();
|
|
12421
|
+
consola.info(`Jedi was active on PR #${prNumber} (comments: ${jediActivity}, commits: ${jediCommits}) \u2014 promoting learnings`);
|
|
12422
|
+
return { skip: false, branch, prNumber };
|
|
12423
|
+
}
|
|
12424
|
+
consola.info(`No Jedi activity on PR #${prNumber} \u2014 skipping`);
|
|
12425
|
+
return { skip: true };
|
|
12426
|
+
}
|
|
12427
|
+
function hasLearningsContent(learningsDir) {
|
|
12428
|
+
if (!existsSync16(learningsDir)) {
|
|
12429
|
+
return false;
|
|
12430
|
+
}
|
|
12431
|
+
const files = readdirSync3(learningsDir).filter((f3) => f3.endsWith(".md"));
|
|
12432
|
+
for (const file of files) {
|
|
12433
|
+
const content = readFileSync3(join15(learningsDir, file), "utf-8");
|
|
12434
|
+
const lines = content.split(`
|
|
12435
|
+
`);
|
|
12436
|
+
for (const line of lines) {
|
|
12437
|
+
const trimmed = line.trim();
|
|
12438
|
+
if (trimmed === "" || trimmed.startsWith("#") || trimmed.startsWith("<!--")) {
|
|
12439
|
+
continue;
|
|
12440
|
+
}
|
|
12441
|
+
return true;
|
|
12442
|
+
}
|
|
12443
|
+
}
|
|
12444
|
+
return false;
|
|
12445
|
+
}
|
|
12446
|
+
function commitLearningsToSameRepo(learningsDir, prNumber) {
|
|
12447
|
+
const addResult = Bun.spawnSync(["git", "add", `${learningsDir}/*.md`], {
|
|
12448
|
+
stdout: "pipe",
|
|
12449
|
+
stderr: "pipe"
|
|
12450
|
+
});
|
|
12451
|
+
if (addResult.exitCode !== 0) {
|
|
12452
|
+
Bun.spawnSync(["bash", "-c", `git add "${learningsDir}"/*.md`], {
|
|
12453
|
+
stdout: "pipe",
|
|
12454
|
+
stderr: "pipe"
|
|
12455
|
+
});
|
|
12456
|
+
}
|
|
12457
|
+
const diffResult = Bun.spawnSync(["git", "diff", "--cached", "--quiet"], {
|
|
12458
|
+
stdout: "pipe",
|
|
12459
|
+
stderr: "pipe"
|
|
12460
|
+
});
|
|
12461
|
+
if (diffResult.exitCode === 0) {
|
|
12462
|
+
consola.info("Learnings unchanged \u2014 nothing to commit");
|
|
12463
|
+
return false;
|
|
12464
|
+
}
|
|
12465
|
+
const message = prNumber ? `chore(jedi): update team learnings
|
|
12466
|
+
|
|
12467
|
+
Auto-committed by Jedi after PR #${prNumber} merged.
|
|
12468
|
+
These learnings are accumulated from PR reviews and feedback.` : `chore(jedi): update team learnings`;
|
|
12469
|
+
const commitResult = Bun.spawnSync(["git", "commit", "-m", message], {
|
|
12470
|
+
stdout: "pipe",
|
|
12471
|
+
stderr: "pipe"
|
|
12472
|
+
});
|
|
12473
|
+
if (commitResult.exitCode !== 0) {
|
|
12474
|
+
consola.error("Failed to commit learnings:", commitResult.stderr.toString());
|
|
12475
|
+
return false;
|
|
12476
|
+
}
|
|
12477
|
+
const pushResult = Bun.spawnSync(["git", "push"], {
|
|
12478
|
+
stdout: "pipe",
|
|
12479
|
+
stderr: "pipe"
|
|
12480
|
+
});
|
|
12481
|
+
if (pushResult.exitCode !== 0) {
|
|
12482
|
+
consola.error("Failed to push learnings:", pushResult.stderr.toString());
|
|
12483
|
+
return false;
|
|
12484
|
+
}
|
|
12485
|
+
consola.success("Learnings committed and pushed");
|
|
12486
|
+
return true;
|
|
12487
|
+
}
|
|
12488
|
+
function commitLearningsToExternalRepo(learningsDir, externalRepo, token, prNumber, sourceRepo) {
|
|
12489
|
+
const tmpDir = mkdtempSync2(join15(tmpdir2(), "jedi-promote-"));
|
|
12490
|
+
try {
|
|
12491
|
+
const cloneUrl = `https://x-access-token:${token}@github.com/${externalRepo}.git`;
|
|
12492
|
+
const cloneResult = Bun.spawnSync(["git", "clone", "--depth", "1", cloneUrl, tmpDir], { stdout: "pipe", stderr: "pipe" });
|
|
12493
|
+
if (cloneResult.exitCode !== 0) {
|
|
12494
|
+
consola.warn(`Could not clone learnings repo ${externalRepo} \u2014 skipping commit`);
|
|
12495
|
+
return false;
|
|
12496
|
+
}
|
|
12497
|
+
const remoteSubdir = join15(tmpDir, "jedi/learnings");
|
|
12498
|
+
mkdirSync6(remoteSubdir, { recursive: true });
|
|
12499
|
+
mergeLearnings(learningsDir, remoteSubdir);
|
|
12500
|
+
Bun.spawnSync(["git", "config", "user.name", "jedi[bot]"], { cwd: tmpDir });
|
|
12501
|
+
Bun.spawnSync(["git", "config", "user.email", "jedi[bot]@users.noreply.github.com"], { cwd: tmpDir });
|
|
12502
|
+
Bun.spawnSync(["bash", "-c", `git add "jedi/learnings"/*.md`], {
|
|
12503
|
+
cwd: tmpDir,
|
|
12504
|
+
stdout: "pipe",
|
|
12505
|
+
stderr: "pipe"
|
|
12506
|
+
});
|
|
12507
|
+
const diffResult = Bun.spawnSync(["git", "diff", "--cached", "--quiet"], {
|
|
12508
|
+
cwd: tmpDir,
|
|
12509
|
+
stdout: "pipe",
|
|
12510
|
+
stderr: "pipe"
|
|
12511
|
+
});
|
|
12512
|
+
if (diffResult.exitCode === 0) {
|
|
12513
|
+
consola.info("Learnings unchanged in external repo \u2014 nothing to commit");
|
|
12514
|
+
return false;
|
|
12515
|
+
}
|
|
12516
|
+
const source = sourceRepo || "unknown";
|
|
12517
|
+
const prRef = prNumber ? `PR #${prNumber}` : "merge";
|
|
12518
|
+
const message = `chore(jedi): update learnings from ${source}
|
|
12519
|
+
|
|
12520
|
+
Source: ${prRef} on ${source}
|
|
12521
|
+
Learnings accumulated from PR reviews and feedback.`;
|
|
12522
|
+
const commitResult = Bun.spawnSync(["git", "commit", "-m", message], {
|
|
12523
|
+
cwd: tmpDir,
|
|
12524
|
+
stdout: "pipe",
|
|
12525
|
+
stderr: "pipe"
|
|
12526
|
+
});
|
|
12527
|
+
if (commitResult.exitCode !== 0) {
|
|
12528
|
+
consola.error("Failed to commit to external repo:", commitResult.stderr.toString());
|
|
12529
|
+
return false;
|
|
12530
|
+
}
|
|
12531
|
+
const pushResult = Bun.spawnSync(["git", "push"], {
|
|
12532
|
+
cwd: tmpDir,
|
|
12533
|
+
stdout: "pipe",
|
|
12534
|
+
stderr: "pipe"
|
|
12535
|
+
});
|
|
12536
|
+
if (pushResult.exitCode !== 0) {
|
|
12537
|
+
consola.error("Failed to push to external repo:", pushResult.stderr.toString());
|
|
12538
|
+
return false;
|
|
12539
|
+
}
|
|
12540
|
+
consola.success(`Learnings committed to ${externalRepo}/jedi/learnings`);
|
|
12541
|
+
return true;
|
|
12542
|
+
} finally {
|
|
12543
|
+
rmSync3(tmpDir, { recursive: true, force: true });
|
|
12544
|
+
}
|
|
12545
|
+
}
|
|
12546
|
+
var promoteLearningsCommand = defineCommand({
|
|
12547
|
+
meta: {
|
|
12548
|
+
name: "promote-learnings",
|
|
12549
|
+
description: "Check Jedi involvement and promote learnings after PR merge"
|
|
12550
|
+
},
|
|
12551
|
+
args: {
|
|
12552
|
+
repo: {
|
|
12553
|
+
type: "string",
|
|
12554
|
+
description: "Repository in owner/repo format",
|
|
12555
|
+
required: true
|
|
12556
|
+
},
|
|
12557
|
+
sha: {
|
|
12558
|
+
type: "string",
|
|
12559
|
+
description: "The merge commit SHA",
|
|
12560
|
+
required: true
|
|
12561
|
+
},
|
|
12562
|
+
"check-only": {
|
|
12563
|
+
type: "boolean",
|
|
12564
|
+
description: "Only check involvement, write outputs, and exit"
|
|
12565
|
+
},
|
|
12566
|
+
"pr-number": {
|
|
12567
|
+
type: "string",
|
|
12568
|
+
description: "PR number (if already known)"
|
|
12569
|
+
},
|
|
12570
|
+
branch: {
|
|
12571
|
+
type: "string",
|
|
12572
|
+
description: "Branch name (if already known)"
|
|
12573
|
+
},
|
|
12574
|
+
"learnings-repo": {
|
|
12575
|
+
type: "string",
|
|
12576
|
+
description: "External learnings repository"
|
|
12577
|
+
},
|
|
12578
|
+
"learnings-token": {
|
|
12579
|
+
type: "string",
|
|
12580
|
+
description: "Token for the learnings repo"
|
|
12581
|
+
}
|
|
12582
|
+
},
|
|
12583
|
+
run({ args }) {
|
|
12584
|
+
const repo = args.repo;
|
|
12585
|
+
const sha = args.sha;
|
|
12586
|
+
if (args["check-only"]) {
|
|
12587
|
+
const involvement = checkJediInvolvement(repo, sha);
|
|
12588
|
+
writeGitHubOutput2("skip", String(involvement.skip));
|
|
12589
|
+
if (involvement.branch) {
|
|
12590
|
+
writeGitHubOutput2("branch", involvement.branch);
|
|
12591
|
+
}
|
|
12592
|
+
if (involvement.prNumber) {
|
|
12593
|
+
writeGitHubOutput2("pr_number", String(involvement.prNumber));
|
|
12594
|
+
}
|
|
12595
|
+
return;
|
|
12596
|
+
}
|
|
12597
|
+
const cwd = process.cwd();
|
|
12598
|
+
const learningsDir = join15(cwd, ".jdi/framework/learnings");
|
|
12599
|
+
if (!hasLearningsContent(learningsDir)) {
|
|
12600
|
+
consola.info("No learnings content to commit \u2014 skipping");
|
|
12601
|
+
return;
|
|
12602
|
+
}
|
|
12603
|
+
const learningsRepo = args["learnings-repo"];
|
|
12604
|
+
const token = args["learnings-token"] || process.env.LEARNINGS_TOKEN || process.env.GH_TOKEN || "";
|
|
12605
|
+
const prNumber = args["pr-number"] ? parseInt(args["pr-number"], 10) : undefined;
|
|
12606
|
+
if (learningsRepo) {
|
|
12607
|
+
commitLearningsToExternalRepo(learningsDir, learningsRepo, token, prNumber, repo);
|
|
12608
|
+
} else {
|
|
12609
|
+
commitLearningsToSameRepo(learningsDir, prNumber);
|
|
12610
|
+
}
|
|
12611
|
+
}
|
|
12612
|
+
});
|
|
12613
|
+
|
|
12614
|
+
// src/commands/action/index.ts
|
|
12615
|
+
var actionCommand = defineCommand({
|
|
12616
|
+
meta: {
|
|
12617
|
+
name: "action",
|
|
12618
|
+
description: "GitHub Action commands \u2014 run workflows, bootstrap, manage learnings"
|
|
12619
|
+
},
|
|
12620
|
+
subCommands: {
|
|
12621
|
+
run: runCommand2,
|
|
12622
|
+
"resolve-branch": resolveBranchCommand,
|
|
12623
|
+
bootstrap: bootstrapCommand,
|
|
12624
|
+
"fetch-learnings": fetchLearningsCommand,
|
|
12625
|
+
"promote-learnings": promoteLearningsCommand
|
|
12626
|
+
}
|
|
12627
|
+
});
|
|
12628
|
+
|
|
12132
12629
|
// src/commands/setup-action.ts
|
|
12133
|
-
import { join as
|
|
12134
|
-
import { existsSync as
|
|
12630
|
+
import { join as join16, dirname as dirname3 } from "path";
|
|
12631
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync7 } from "fs";
|
|
12135
12632
|
var setupActionCommand = defineCommand({
|
|
12136
12633
|
meta: {
|
|
12137
12634
|
name: "setup-action",
|
|
@@ -12140,19 +12637,19 @@ var setupActionCommand = defineCommand({
|
|
|
12140
12637
|
args: {},
|
|
12141
12638
|
async run() {
|
|
12142
12639
|
const cwd = process.cwd();
|
|
12143
|
-
const workflowDest =
|
|
12144
|
-
if (
|
|
12640
|
+
const workflowDest = join16(cwd, ".github", "workflows", "jedi.yml");
|
|
12641
|
+
if (existsSync17(workflowDest)) {
|
|
12145
12642
|
consola.warn(`Workflow already exists at ${workflowDest}`);
|
|
12146
12643
|
consola.info("Skipping workflow copy. Delete it manually to regenerate.");
|
|
12147
12644
|
} else {
|
|
12148
|
-
const templatePath =
|
|
12149
|
-
if (!
|
|
12645
|
+
const templatePath = join16(import.meta.dir, "../action/workflow-template.yml");
|
|
12646
|
+
if (!existsSync17(templatePath)) {
|
|
12150
12647
|
consola.error("Workflow template not found. Ensure @benzotti/jedi is properly installed.");
|
|
12151
12648
|
process.exit(1);
|
|
12152
12649
|
}
|
|
12153
12650
|
const dir = dirname3(workflowDest);
|
|
12154
|
-
if (!
|
|
12155
|
-
|
|
12651
|
+
if (!existsSync17(dir))
|
|
12652
|
+
mkdirSync7(dir, { recursive: true });
|
|
12156
12653
|
const template = await Bun.file(templatePath).text();
|
|
12157
12654
|
await Bun.write(workflowDest, template);
|
|
12158
12655
|
consola.success(`Created ${workflowDest}`);
|
|
@@ -12294,7 +12791,7 @@ var stateCommand = defineCommand({
|
|
|
12294
12791
|
// package.json
|
|
12295
12792
|
var package_default = {
|
|
12296
12793
|
name: "@benzotti/jedi",
|
|
12297
|
-
version: "0.1.
|
|
12794
|
+
version: "0.1.40",
|
|
12298
12795
|
description: "JDI - Context-efficient AI development framework for Claude Code",
|
|
12299
12796
|
type: "module",
|
|
12300
12797
|
bin: {
|