ledgit-cli 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +457 -141
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -24666,21 +24666,29 @@ When you run \`create-entry\` on main branch, a new git branch is automatically
|
|
|
24666
24666
|
|
|
24667
24667
|
## Bookkeeping New Entries
|
|
24668
24668
|
|
|
24669
|
-
**Important:** Always ask the user for clarification if anything is unclear about the transaction before creating an entry.
|
|
24670
|
-
|
|
24671
24669
|
To create a journal entry from an inbox document:
|
|
24672
24670
|
|
|
24673
|
-
1. **Read the document** -
|
|
24674
|
-
2. **Update documents.yaml** -
|
|
24671
|
+
1. **Read the document** - You can read PDFs directly, or use \`npx ledgit-cli parse-pdf\` to extract text
|
|
24672
|
+
2. **Update documents.yaml** - Populate missing metadata in \`documents.yaml\`:
|
|
24675
24673
|
- \`documentDate\`: Invoice/receipt date (YYYY-MM-DD)
|
|
24676
24674
|
- \`description\`: Vendor or document name
|
|
24677
24675
|
- \`totalAmount\`: With \`amount\` (string) and \`currency\` (e.g., EUR, SEK)
|
|
24678
24676
|
3. **Look for similar entries** - Search \`journal-entries/\` for entries from the same vendor or similar transaction types to learn which accounts and tax codes were used previously
|
|
24679
24677
|
4. **Check accounts** - Review \`accounts.yaml\` for appropriate expense/revenue accounts
|
|
24680
|
-
5. **Determine tax code** - Based on vendor location and transaction type (see Tax Codes below)
|
|
24681
|
-
6. **
|
|
24682
|
-
7. **
|
|
24683
|
-
|
|
24678
|
+
5. **Determine tax code** - Based on vendor location and transaction type (see Tax Codes below)
|
|
24679
|
+
6. **Create entry** - Run \`npx ledgit-cli create-entry\` with the appropriate parameters
|
|
24680
|
+
7. **Review and sync** - The entry is created in \`journal-entries/\`. Review with \`git diff\`, commit, and run \`npx ledgit-cli sync-journal\` to post to {{PROVIDER}}
|
|
24681
|
+
|
|
24682
|
+
### GitHub Actions / CI Mode
|
|
24683
|
+
|
|
24684
|
+
When running in GitHub Actions (automated bookkeeping):
|
|
24685
|
+
- **Always proceed** with creating entries using your best judgment
|
|
24686
|
+
- **Always end** by creating or updating a PR
|
|
24687
|
+
- If anything is unclear, include your questions in the PR description
|
|
24688
|
+
- The user will respond via PR comments, which triggers another workflow run
|
|
24689
|
+
- Group related entries (same vendor, same week) in one PR when sensible
|
|
24690
|
+
|
|
24691
|
+
**Never stop and wait for confirmation in CI mode** - the user can't respond until you create a PR.
|
|
24684
24692
|
|
|
24685
24693
|
To discard unwanted inbox items:
|
|
24686
24694
|
\`\`\`bash
|
|
@@ -24788,12 +24796,12 @@ function renderAgentsTemplate(options) {
|
|
|
24788
24796
|
}
|
|
24789
24797
|
// ../bookkeeping/dist/utils/paths.js
|
|
24790
24798
|
import * as path2 from "node:path";
|
|
24791
|
-
var
|
|
24799
|
+
var LEDGIT_DIR = ".ledgit";
|
|
24792
24800
|
function getTokensDir(cwd) {
|
|
24793
|
-
return path2.join(cwd,
|
|
24801
|
+
return path2.join(cwd, LEDGIT_DIR, "tokens");
|
|
24794
24802
|
}
|
|
24795
24803
|
function getCacheDir(cwd) {
|
|
24796
|
-
return path2.join(cwd,
|
|
24804
|
+
return path2.join(cwd, LEDGIT_DIR, "cache");
|
|
24797
24805
|
}
|
|
24798
24806
|
// ../bookkeeping/dist/services/journal.service.js
|
|
24799
24807
|
class JournalService {
|
|
@@ -26931,7 +26939,7 @@ async function loginFortnox(options) {
|
|
|
26931
26939
|
onStatus("Exchanging authorization code for token...");
|
|
26932
26940
|
const tokenResponse = await exchangeCodeForToken(config2, callbackResult.code);
|
|
26933
26941
|
const storedToken = saveFortnoxToken(cwd, tokenResponse);
|
|
26934
|
-
onStatus("Token saved to .
|
|
26942
|
+
onStatus("Token saved to .ledgit/tokens/fortnox.json");
|
|
26935
26943
|
return storedToken;
|
|
26936
26944
|
} finally {
|
|
26937
26945
|
stop();
|
|
@@ -27792,6 +27800,22 @@ async function syncFortnoxJournal(cwd, options) {
|
|
|
27792
27800
|
import ora2 from "ora";
|
|
27793
27801
|
import * as fs12 from "node:fs/promises";
|
|
27794
27802
|
import * as path8 from "node:path";
|
|
27803
|
+
var MANIFEST_PATH = ".ledgit/synced-inbox.json";
|
|
27804
|
+
async function loadManifest(cwd) {
|
|
27805
|
+
const manifestPath = path8.join(cwd, MANIFEST_PATH);
|
|
27806
|
+
try {
|
|
27807
|
+
const content = await fs12.readFile(manifestPath, "utf-8");
|
|
27808
|
+
const ids = JSON.parse(content);
|
|
27809
|
+
return new Set(ids);
|
|
27810
|
+
} catch {
|
|
27811
|
+
return new Set;
|
|
27812
|
+
}
|
|
27813
|
+
}
|
|
27814
|
+
async function saveManifest(cwd, syncedIds) {
|
|
27815
|
+
const manifestPath = path8.join(cwd, MANIFEST_PATH);
|
|
27816
|
+
await fs12.mkdir(path8.dirname(manifestPath), { recursive: true });
|
|
27817
|
+
await fs12.writeFile(manifestPath, JSON.stringify([...syncedIds], null, 2));
|
|
27818
|
+
}
|
|
27795
27819
|
function slugify4(text, maxLength = 30) {
|
|
27796
27820
|
return text.toLowerCase().slice(0, maxLength).replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
27797
27821
|
}
|
|
@@ -27815,6 +27839,7 @@ async function syncInboxCommand() {
|
|
|
27815
27839
|
async function syncBokioInbox(cwd) {
|
|
27816
27840
|
const config3 = loadConfig(cwd);
|
|
27817
27841
|
const client2 = createBokioClient(config3);
|
|
27842
|
+
const syncedIds = await loadManifest(cwd);
|
|
27818
27843
|
const validateSpinner = ora2("Connecting to Bokio...").start();
|
|
27819
27844
|
try {
|
|
27820
27845
|
const companyInfo = await client2.getCompanyInformation();
|
|
@@ -27867,8 +27892,13 @@ async function syncBokioInbox(cwd) {
|
|
|
27867
27892
|
for (const upload of unprocessedUploads) {
|
|
27868
27893
|
const slug = upload.description ? slugify4(upload.description) : upload.id.slice(0, 8);
|
|
27869
27894
|
let dirName = `${today}-${slug}`;
|
|
27895
|
+
if (syncedIds.has(upload.id)) {
|
|
27896
|
+
existingCount++;
|
|
27897
|
+
continue;
|
|
27898
|
+
}
|
|
27870
27899
|
const alreadyDownloaded = await isAlreadyDownloaded2(cwd, upload.id);
|
|
27871
27900
|
if (alreadyDownloaded) {
|
|
27901
|
+
syncedIds.add(upload.id);
|
|
27872
27902
|
existingCount++;
|
|
27873
27903
|
continue;
|
|
27874
27904
|
}
|
|
@@ -27896,6 +27926,7 @@ async function syncBokioInbox(cwd) {
|
|
|
27896
27926
|
];
|
|
27897
27927
|
const documentsPath = path8.join(uploadDir, "documents.yaml");
|
|
27898
27928
|
await fs12.writeFile(documentsPath, toYaml2(documents));
|
|
27929
|
+
syncedIds.add(upload.id);
|
|
27899
27930
|
newCount++;
|
|
27900
27931
|
} catch (error) {
|
|
27901
27932
|
console.error(`
|
|
@@ -27906,6 +27937,7 @@ async function syncBokioInbox(cwd) {
|
|
|
27906
27937
|
}
|
|
27907
27938
|
}
|
|
27908
27939
|
}
|
|
27940
|
+
await saveManifest(cwd, syncedIds);
|
|
27909
27941
|
console.log(`
|
|
27910
27942
|
|
|
27911
27943
|
Sync complete!
|
|
@@ -28925,6 +28957,8 @@ async function updateEnvFile(cwd) {
|
|
|
28925
28957
|
// src/commands/update.ts
|
|
28926
28958
|
import * as fs17 from "node:fs/promises";
|
|
28927
28959
|
import * as path13 from "node:path";
|
|
28960
|
+
import { exec as exec6 } from "node:child_process";
|
|
28961
|
+
import { promisify as promisify6 } from "node:util";
|
|
28928
28962
|
import ora6 from "ora";
|
|
28929
28963
|
|
|
28930
28964
|
// ../../node_modules/ansi-regex/index.js
|
|
@@ -34005,83 +34039,6 @@ var dist_default8 = createPrompt4((config3, done) => {
|
|
|
34005
34039
|
`).trimEnd();
|
|
34006
34040
|
return `${lines}${cursorHide4}`;
|
|
34007
34041
|
});
|
|
34008
|
-
// src/commands/update.ts
|
|
34009
|
-
async function updateCommand(options = {}) {
|
|
34010
|
-
const cwd = process.cwd();
|
|
34011
|
-
const agentsPath = path13.join(cwd, "AGENTS.md");
|
|
34012
|
-
let companyName;
|
|
34013
|
-
let provider;
|
|
34014
|
-
try {
|
|
34015
|
-
const content = await fs17.readFile(agentsPath, "utf-8");
|
|
34016
|
-
const match = content.match(/repository for (.+?) \(Swedish company\).+?integrates with (Fortnox|Bokio)/s);
|
|
34017
|
-
if (!match || !match[1] || !match[2]) {
|
|
34018
|
-
console.error("Could not parse company info from AGENTS.md");
|
|
34019
|
-
process.exit(1);
|
|
34020
|
-
}
|
|
34021
|
-
companyName = match[1];
|
|
34022
|
-
provider = match[2];
|
|
34023
|
-
console.log(` Company: ${companyName}`);
|
|
34024
|
-
console.log(` Provider: ${provider}`);
|
|
34025
|
-
} catch {
|
|
34026
|
-
console.error("AGENTS.md not found. Run this command in a ledgit repository.");
|
|
34027
|
-
process.exit(1);
|
|
34028
|
-
}
|
|
34029
|
-
const newContent = renderAgentsTemplate({
|
|
34030
|
-
companyName,
|
|
34031
|
-
provider
|
|
34032
|
-
});
|
|
34033
|
-
if (!options.force) {
|
|
34034
|
-
const currentContent = await fs17.readFile(agentsPath, "utf-8");
|
|
34035
|
-
const hasCustomChanges = currentContent !== newContent;
|
|
34036
|
-
if (hasCustomChanges) {
|
|
34037
|
-
const proceed = await dist_default2({
|
|
34038
|
-
message: "AGENTS.md has been modified. Overwrite with latest template?",
|
|
34039
|
-
default: true
|
|
34040
|
-
});
|
|
34041
|
-
if (!proceed) {
|
|
34042
|
-
console.log(" Update cancelled.");
|
|
34043
|
-
return;
|
|
34044
|
-
}
|
|
34045
|
-
}
|
|
34046
|
-
}
|
|
34047
|
-
const updateSpinner = ora6("Updating AGENTS.md...").start();
|
|
34048
|
-
try {
|
|
34049
|
-
await fs17.writeFile(agentsPath, newContent, "utf-8");
|
|
34050
|
-
const claudePath = path13.join(cwd, "CLAUDE.md");
|
|
34051
|
-
try {
|
|
34052
|
-
const stat2 = await fs17.lstat(claudePath);
|
|
34053
|
-
if (!stat2.isSymbolicLink()) {
|
|
34054
|
-
await fs17.unlink(claudePath);
|
|
34055
|
-
await fs17.symlink("AGENTS.md", claudePath);
|
|
34056
|
-
}
|
|
34057
|
-
} catch {
|
|
34058
|
-
await fs17.symlink("AGENTS.md", claudePath);
|
|
34059
|
-
}
|
|
34060
|
-
updateSpinner.succeed("Updated AGENTS.md");
|
|
34061
|
-
} catch (error) {
|
|
34062
|
-
updateSpinner.fail("Failed to update AGENTS.md");
|
|
34063
|
-
console.error(error instanceof Error ? error.message : "Unknown error");
|
|
34064
|
-
process.exit(1);
|
|
34065
|
-
}
|
|
34066
|
-
const commitMessage = "Update AGENTS.md to latest version";
|
|
34067
|
-
const committed = await commitAll(cwd, commitMessage);
|
|
34068
|
-
if (committed) {
|
|
34069
|
-
console.log(`
|
|
34070
|
-
Committed: "${commitMessage}"
|
|
34071
|
-
`);
|
|
34072
|
-
} else {
|
|
34073
|
-
console.log(`
|
|
34074
|
-
No changes to commit.
|
|
34075
|
-
`);
|
|
34076
|
-
}
|
|
34077
|
-
}
|
|
34078
|
-
|
|
34079
|
-
// src/commands/setup-github.ts
|
|
34080
|
-
import * as fs18 from "node:fs/promises";
|
|
34081
|
-
import * as path14 from "node:path";
|
|
34082
|
-
import { exec as exec6 } from "node:child_process";
|
|
34083
|
-
import { promisify as promisify6 } from "node:util";
|
|
34084
|
-
|
|
34085
34042
|
// src/templates/workflows.ts
|
|
34086
34043
|
var SYNC_INBOX_WORKFLOW = `name: Sync Inbox
|
|
34087
34044
|
on:
|
|
@@ -34096,7 +34053,6 @@ jobs:
|
|
|
34096
34053
|
contents: write
|
|
34097
34054
|
pull-requests: write
|
|
34098
34055
|
issues: write
|
|
34099
|
-
id-token: write
|
|
34100
34056
|
steps:
|
|
34101
34057
|
- uses: actions/checkout@v5
|
|
34102
34058
|
|
|
@@ -34113,6 +34069,14 @@ jobs:
|
|
|
34113
34069
|
BOKIO_TOKEN: \${{ secrets.BOKIO_TOKEN }}
|
|
34114
34070
|
BOKIO_COMPANY_ID: \${{ secrets.BOKIO_COMPANY_ID }}
|
|
34115
34071
|
|
|
34072
|
+
- name: Commit sync manifest
|
|
34073
|
+
run: |
|
|
34074
|
+
if [ -f .ledgit/synced-inbox.json ]; then
|
|
34075
|
+
git add .ledgit/synced-inbox.json
|
|
34076
|
+
git diff --cached --quiet || git commit -m "chore: update synced inbox manifest"
|
|
34077
|
+
git push
|
|
34078
|
+
fi
|
|
34079
|
+
|
|
34116
34080
|
- name: Check for new items
|
|
34117
34081
|
id: check
|
|
34118
34082
|
run: |
|
|
@@ -34122,32 +34086,89 @@ jobs:
|
|
|
34122
34086
|
echo "has_new=false" >> $GITHUB_OUTPUT
|
|
34123
34087
|
fi
|
|
34124
34088
|
|
|
34089
|
+
- name: Install Claude Code
|
|
34090
|
+
if: steps.check.outputs.has_new == 'true'
|
|
34091
|
+
run: |
|
|
34092
|
+
curl -fsSL https://claude.ai/install.sh | bash
|
|
34093
|
+
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
|
34094
|
+
|
|
34125
34095
|
- name: Bookkeep with Claude
|
|
34126
34096
|
if: steps.check.outputs.has_new == 'true'
|
|
34127
|
-
|
|
34097
|
+
env:
|
|
34098
|
+
CLAUDE_CODE_OAUTH_TOKEN: \${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
34099
|
+
GH_TOKEN: \${{ github.token }}
|
|
34100
|
+
run: |
|
|
34101
|
+
claude -p "New documents synced to inbox/ (unstaged).
|
|
34102
|
+
|
|
34103
|
+
YOU handle all git operations.
|
|
34104
|
+
|
|
34105
|
+
FIRST: Check for existing open bookkeeping PRs with gh pr list.
|
|
34106
|
+
- If there's an active PR, checkout that branch and add to it
|
|
34107
|
+
- Otherwise, create a new branch: book/{date}-{description}
|
|
34108
|
+
- Group related entries (same vendor, same week) in one PR when sensible
|
|
34109
|
+
|
|
34110
|
+
THEN:
|
|
34111
|
+
1. Check each new item in inbox/
|
|
34112
|
+
2. Find similar entries in journal-entries/ for reference
|
|
34113
|
+
3. Create entries with npx ledgit-cli create-entry
|
|
34114
|
+
4. Stage and commit all changes (inbox files + entries)
|
|
34115
|
+
5. Push and create/update PR
|
|
34116
|
+
|
|
34117
|
+
PR FORMAT:
|
|
34118
|
+
## Summary
|
|
34119
|
+
Brief description (e.g., 'Book 2 invoices from Anthropic and AWS')
|
|
34120
|
+
|
|
34121
|
+
## Entries
|
|
34122
|
+
| Entry | Vendor | Amount | Date |
|
|
34123
|
+
|-------|--------|--------|------|
|
|
34124
|
+
| V-XXX | Vendor | Amount SEK | YYYY-MM-DD |
|
|
34125
|
+
|
|
34126
|
+
### V-XXX: Description
|
|
34127
|
+
- **Account**: XXXX (Name) - why this account
|
|
34128
|
+
- **Tax**: Tax treatment - why (e.g., 'Reverse charge - US vendor')
|
|
34129
|
+
- **Exchange rate**: X.XX SEK/USD from Riksbank (date) - if foreign currency
|
|
34130
|
+
- **Reference**: Similar to V-YYY - if found similar entry
|
|
34131
|
+
|
|
34132
|
+
## Questions
|
|
34133
|
+
(Only if something needs clarification - otherwise omit this section)" \\
|
|
34134
|
+
--allowedTools "Read,Write,Edit,Bash(npx ledgit-cli:*),Bash(git:*),Bash(gh pr:*),Bash(ls:*),Bash(find:*),Bash(grep:*),Bash(cat:*)" \\
|
|
34135
|
+
--verbose \\
|
|
34136
|
+
--output-format stream-json \\
|
|
34137
|
+
2>&1 | tee /tmp/claude-output.json
|
|
34138
|
+
|
|
34139
|
+
- name: Write job summary
|
|
34140
|
+
if: always() && steps.check.outputs.has_new == 'true'
|
|
34141
|
+
run: |
|
|
34142
|
+
echo "## \uD83E\uDD16 Claude Bookkeeping Report" >> $GITHUB_STEP_SUMMARY
|
|
34143
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
34144
|
+
|
|
34145
|
+
# Show model from init
|
|
34146
|
+
jq -r 'select(.type == "system" and .subtype == "init") | "**Model:** \\(.model // "claude")"' /tmp/claude-output.json >> $GITHUB_STEP_SUMMARY 2>/dev/null || true
|
|
34147
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
34148
|
+
|
|
34149
|
+
# Count tool uses
|
|
34150
|
+
TOOL_COUNT=$(jq -s '[.[] | select(.type == "tool_use")] | length' /tmp/claude-output.json 2>/dev/null || echo "0")
|
|
34151
|
+
echo "**Tool calls:** $TOOL_COUNT" >> $GITHUB_STEP_SUMMARY
|
|
34152
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
34153
|
+
|
|
34154
|
+
# List unique tools used
|
|
34155
|
+
echo "### Tools Used" >> $GITHUB_STEP_SUMMARY
|
|
34156
|
+
jq -r 'select(.type == "tool_use") | .name' /tmp/claude-output.json 2>/dev/null | sort | uniq -c | while read count name; do
|
|
34157
|
+
echo "- $name ($count calls)" >> $GITHUB_STEP_SUMMARY
|
|
34158
|
+
done
|
|
34159
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
34160
|
+
|
|
34161
|
+
# Final result
|
|
34162
|
+
echo "### Result" >> $GITHUB_STEP_SUMMARY
|
|
34163
|
+
jq -r 'select(.type == "result") | "**Status:** \\(.subtype // "completed")\\n**Duration:** \\((.duration_ms // 0) / 1000 | floor)s\\n**Turns:** \\(.num_turns // "N/A")\\n**Cost:** $\\(.total_cost_usd // 0 | tostring | .[0:6])"' /tmp/claude-output.json >> $GITHUB_STEP_SUMMARY 2>/dev/null || true
|
|
34164
|
+
|
|
34165
|
+
- name: Upload Claude execution log
|
|
34166
|
+
if: always() && steps.check.outputs.has_new == 'true'
|
|
34167
|
+
uses: actions/upload-artifact@v4
|
|
34128
34168
|
with:
|
|
34129
|
-
|
|
34130
|
-
|
|
34131
|
-
|
|
34132
|
-
|
|
34133
|
-
YOU handle all git operations. The inbox files are NOT committed yet.
|
|
34134
|
-
|
|
34135
|
-
FIRST: Check for existing open bookkeeping PRs with \\\`gh pr list\\\`.
|
|
34136
|
-
- If there's an active PR, checkout that branch and add to it
|
|
34137
|
-
- Otherwise, create a new branch: book/{date}-{description}
|
|
34138
|
-
- Group related entries (same vendor, same week) in one PR when sensible
|
|
34139
|
-
|
|
34140
|
-
THEN:
|
|
34141
|
-
1. Check each new item in inbox/ (you can read PDFs directly)
|
|
34142
|
-
2. Find similar entries in journal-entries/ for reference
|
|
34143
|
-
3. Create entries with \\\`npx ledgit-cli create-entry\\\`
|
|
34144
|
-
4. Stage and commit all changes (inbox files + entries)
|
|
34145
|
-
5. Push and create/update PR
|
|
34146
|
-
|
|
34147
|
-
If anything is unclear, commit the inbox files anyway and ask questions in the PR description.
|
|
34148
|
-
claude_args: |
|
|
34149
|
-
--allowedTools "Read,Write,Edit,Bash(npx ledgit-cli:*),Bash(git:*),Bash(gh pr:*),Bash(ls:*),Bash(find:*),Bash(grep:*),Bash(cat:*)"
|
|
34150
|
-
track_progress: true
|
|
34169
|
+
name: claude-execution-log
|
|
34170
|
+
path: /tmp/claude-output.json
|
|
34171
|
+
retention-days: 7
|
|
34151
34172
|
`;
|
|
34152
34173
|
var CLAUDE_BOOKKEEPING_WORKFLOW = `name: Claude Bookkeeping
|
|
34153
34174
|
on:
|
|
@@ -34164,25 +34185,118 @@ jobs:
|
|
|
34164
34185
|
contents: write
|
|
34165
34186
|
pull-requests: write
|
|
34166
34187
|
issues: write
|
|
34167
|
-
id-token: write
|
|
34168
34188
|
steps:
|
|
34189
|
+
- name: Get PR branch
|
|
34190
|
+
id: pr
|
|
34191
|
+
env:
|
|
34192
|
+
GH_TOKEN: \${{ github.token }}
|
|
34193
|
+
run: |
|
|
34194
|
+
PR_NUMBER=\${{ github.event.issue.number }}
|
|
34195
|
+
BRANCH=$(gh pr view $PR_NUMBER --repo \${{ github.repository }} --json headRefName --jq '.headRefName')
|
|
34196
|
+
echo "branch=$BRANCH" >> $GITHUB_OUTPUT
|
|
34197
|
+
|
|
34169
34198
|
- uses: actions/checkout@v5
|
|
34170
34199
|
with:
|
|
34171
|
-
ref: \${{
|
|
34200
|
+
ref: \${{ steps.pr.outputs.branch }}
|
|
34172
34201
|
|
|
34173
34202
|
- uses: oven-sh/setup-bun@v2
|
|
34174
34203
|
|
|
34175
|
-
-
|
|
34204
|
+
- name: Configure git
|
|
34205
|
+
run: |
|
|
34206
|
+
git config user.name "ledgit[bot]"
|
|
34207
|
+
git config user.email "bot@ledgit.se"
|
|
34208
|
+
|
|
34209
|
+
- name: Get PR context
|
|
34210
|
+
id: context
|
|
34211
|
+
env:
|
|
34212
|
+
GH_TOKEN: \${{ github.token }}
|
|
34213
|
+
run: |
|
|
34214
|
+
PR_NUMBER=\${{ github.event.issue.number }}
|
|
34215
|
+
|
|
34216
|
+
# Fetch PR details and format as context
|
|
34217
|
+
{
|
|
34218
|
+
echo "## PR Details"
|
|
34219
|
+
gh pr view $PR_NUMBER --json title,body --jq '"### \\(.title)\\n\\n\\(.body)"'
|
|
34220
|
+
echo ""
|
|
34221
|
+
echo "## Comments"
|
|
34222
|
+
gh pr view $PR_NUMBER --json comments --jq '.comments[] | "[\\(.author.login) at \\(.createdAt)]: \\(.body)"'
|
|
34223
|
+
echo ""
|
|
34224
|
+
echo "## Files changed"
|
|
34225
|
+
gh pr view $PR_NUMBER --json files --jq '.files[].path'
|
|
34226
|
+
echo ""
|
|
34227
|
+
echo "---"
|
|
34228
|
+
echo "## Latest comment from \${{ github.event.comment.user.login }}:"
|
|
34229
|
+
echo "\${{ github.event.comment.body }}"
|
|
34230
|
+
} > /tmp/pr_context.md
|
|
34231
|
+
|
|
34232
|
+
- name: Install Claude Code
|
|
34233
|
+
run: |
|
|
34234
|
+
curl -fsSL https://claude.ai/install.sh | bash
|
|
34235
|
+
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
|
34236
|
+
|
|
34237
|
+
- name: Save PR context as artifact
|
|
34238
|
+
uses: actions/upload-artifact@v4
|
|
34176
34239
|
with:
|
|
34177
|
-
|
|
34178
|
-
|
|
34179
|
-
|
|
34240
|
+
name: pr-context-\${{ github.event.issue.number }}
|
|
34241
|
+
path: /tmp/pr_context.md
|
|
34242
|
+
retention-days: 7
|
|
34243
|
+
|
|
34244
|
+
- name: Respond with Claude
|
|
34245
|
+
env:
|
|
34246
|
+
CLAUDE_CODE_OAUTH_TOKEN: \${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
34247
|
+
GH_TOKEN: \${{ github.token }}
|
|
34248
|
+
run: |
|
|
34249
|
+
PR_CONTEXT=$(cat /tmp/pr_context.md)
|
|
34250
|
+
|
|
34251
|
+
claude -p "This is an ongoing bookkeeping conversation on a GitHub PR.
|
|
34252
|
+
|
|
34253
|
+
$PR_CONTEXT
|
|
34254
|
+
|
|
34255
|
+
---
|
|
34256
|
+
|
|
34257
|
+
Respond to the user's latest comment. If they answered your questions, continue creating the journal entries.
|
|
34258
|
+
|
|
34259
|
+
Read AGENTS.md for bookkeeping instructions.
|
|
34180
34260
|
|
|
34181
|
-
|
|
34261
|
+
When done, post your response as a PR comment using: gh pr comment \${{ github.event.issue.number }} --body 'your response'" \\
|
|
34262
|
+
--allowedTools "Read,Write,Edit,Bash(npx ledgit-cli:*),Bash(git:*),Bash(gh pr:*),Bash(ls:*),Bash(find:*),Bash(grep:*),Bash(cat:*)" \\
|
|
34263
|
+
--verbose \\
|
|
34264
|
+
--output-format stream-json \\
|
|
34265
|
+
2>&1 | tee /tmp/claude-output.json
|
|
34182
34266
|
|
|
34183
|
-
|
|
34184
|
-
|
|
34185
|
-
|
|
34267
|
+
- name: Write job summary
|
|
34268
|
+
if: always()
|
|
34269
|
+
run: |
|
|
34270
|
+
echo "## \uD83E\uDD16 Claude Response Report" >> $GITHUB_STEP_SUMMARY
|
|
34271
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
34272
|
+
|
|
34273
|
+
# Show model from init
|
|
34274
|
+
jq -r 'select(.type == "system" and .subtype == "init") | "**Model:** \\(.model // "claude")"' /tmp/claude-output.json >> $GITHUB_STEP_SUMMARY 2>/dev/null || true
|
|
34275
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
34276
|
+
|
|
34277
|
+
# Count tool uses
|
|
34278
|
+
TOOL_COUNT=$(jq -s '[.[] | select(.type == "tool_use")] | length' /tmp/claude-output.json 2>/dev/null || echo "0")
|
|
34279
|
+
echo "**Tool calls:** $TOOL_COUNT" >> $GITHUB_STEP_SUMMARY
|
|
34280
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
34281
|
+
|
|
34282
|
+
# List unique tools used
|
|
34283
|
+
echo "### Tools Used" >> $GITHUB_STEP_SUMMARY
|
|
34284
|
+
jq -r 'select(.type == "tool_use") | .name' /tmp/claude-output.json 2>/dev/null | sort | uniq -c | while read count name; do
|
|
34285
|
+
echo "- $name ($count calls)" >> $GITHUB_STEP_SUMMARY
|
|
34286
|
+
done
|
|
34287
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
34288
|
+
|
|
34289
|
+
# Final result
|
|
34290
|
+
echo "### Result" >> $GITHUB_STEP_SUMMARY
|
|
34291
|
+
jq -r 'select(.type == "result") | "**Status:** \\(.subtype // "completed")\\n**Duration:** \\((.duration_ms // 0) / 1000 | floor)s\\n**Turns:** \\(.num_turns // "N/A")\\n**Cost:** $\\(.total_cost_usd // 0 | tostring | .[0:6])"' /tmp/claude-output.json >> $GITHUB_STEP_SUMMARY 2>/dev/null || true
|
|
34292
|
+
|
|
34293
|
+
- name: Upload Claude execution log
|
|
34294
|
+
if: always()
|
|
34295
|
+
uses: actions/upload-artifact@v4
|
|
34296
|
+
with:
|
|
34297
|
+
name: claude-execution-log-pr-\${{ github.event.issue.number }}
|
|
34298
|
+
path: /tmp/claude-output.json
|
|
34299
|
+
retention-days: 7
|
|
34186
34300
|
`;
|
|
34187
34301
|
var SYNC_JOURNAL_WORKFLOW = `name: Sync Journal
|
|
34188
34302
|
on:
|
|
@@ -34212,11 +34326,211 @@ jobs:
|
|
|
34212
34326
|
BOKIO_COMPANY_ID: \${{ secrets.BOKIO_COMPANY_ID }}
|
|
34213
34327
|
`;
|
|
34214
34328
|
|
|
34215
|
-
// src/commands/
|
|
34329
|
+
// src/commands/update.ts
|
|
34216
34330
|
var execAsync2 = promisify6(exec6);
|
|
34331
|
+
var AGENTS_COMMIT_MSG = "Update AGENTS.md to latest version";
|
|
34332
|
+
var WORKFLOWS_COMMIT_MSG = "Update GitHub Actions workflows to latest version";
|
|
34333
|
+
async function hasCustomChanges(cwd, filePath, expectedMessage) {
|
|
34334
|
+
try {
|
|
34335
|
+
const result = await execAsync2(`git log -1 --format="%s" -- "${filePath}"`, { cwd });
|
|
34336
|
+
const lastMessage = result.stdout.trim();
|
|
34337
|
+
if (lastMessage === "" || lastMessage === expectedMessage) {
|
|
34338
|
+
return false;
|
|
34339
|
+
}
|
|
34340
|
+
return true;
|
|
34341
|
+
} catch {
|
|
34342
|
+
return false;
|
|
34343
|
+
}
|
|
34344
|
+
}
|
|
34345
|
+
async function getDiff(cwd, path14) {
|
|
34346
|
+
try {
|
|
34347
|
+
const result = await execAsync2(`git diff "${path14}"`, { cwd });
|
|
34348
|
+
return result.stdout;
|
|
34349
|
+
} catch {
|
|
34350
|
+
return "";
|
|
34351
|
+
}
|
|
34352
|
+
}
|
|
34353
|
+
async function revertChanges(cwd, path14) {
|
|
34354
|
+
try {
|
|
34355
|
+
await execAsync2(`git checkout "${path14}"`, { cwd });
|
|
34356
|
+
} catch {
|
|
34357
|
+
}
|
|
34358
|
+
}
|
|
34359
|
+
async function commitFiles(cwd, files, message) {
|
|
34360
|
+
try {
|
|
34361
|
+
await execAsync2(`git add ${files.map((f2) => `"${f2}"`).join(" ")}`, { cwd });
|
|
34362
|
+
try {
|
|
34363
|
+
await execAsync2("git diff --cached --quiet", { cwd });
|
|
34364
|
+
return false;
|
|
34365
|
+
} catch {
|
|
34366
|
+
}
|
|
34367
|
+
await execAsync2(`git commit -m "${message}"`, { cwd });
|
|
34368
|
+
return true;
|
|
34369
|
+
} catch {
|
|
34370
|
+
return false;
|
|
34371
|
+
}
|
|
34372
|
+
}
|
|
34373
|
+
async function updateCommand(options = {}) {
|
|
34374
|
+
const cwd = process.cwd();
|
|
34375
|
+
const agentsPath = path13.join(cwd, "AGENTS.md");
|
|
34376
|
+
let companyName;
|
|
34377
|
+
let provider;
|
|
34378
|
+
try {
|
|
34379
|
+
const content = await fs17.readFile(agentsPath, "utf-8");
|
|
34380
|
+
const match = content.match(/repository for (.+?) \(Swedish company\).+?integrates with (Fortnox|Bokio)/s);
|
|
34381
|
+
if (!match || !match[1] || !match[2]) {
|
|
34382
|
+
console.error("Could not parse company info from AGENTS.md");
|
|
34383
|
+
process.exit(1);
|
|
34384
|
+
}
|
|
34385
|
+
companyName = match[1];
|
|
34386
|
+
provider = match[2];
|
|
34387
|
+
console.log(` Company: ${companyName}`);
|
|
34388
|
+
console.log(` Provider: ${provider}`);
|
|
34389
|
+
} catch {
|
|
34390
|
+
console.error("AGENTS.md not found. Run this command in a ledgit repository.");
|
|
34391
|
+
process.exit(1);
|
|
34392
|
+
}
|
|
34393
|
+
const agentsHasCustom = await hasCustomChanges(cwd, "AGENTS.md", AGENTS_COMMIT_MSG);
|
|
34394
|
+
const currentAgentsContent = await fs17.readFile(agentsPath, "utf-8");
|
|
34395
|
+
const newContent = renderAgentsTemplate({
|
|
34396
|
+
companyName,
|
|
34397
|
+
provider
|
|
34398
|
+
});
|
|
34399
|
+
const agentsNeedsUpdate = currentAgentsContent !== newContent;
|
|
34400
|
+
if (agentsNeedsUpdate) {
|
|
34401
|
+
const updateSpinner = ora6("Updating AGENTS.md...").start();
|
|
34402
|
+
try {
|
|
34403
|
+
await fs17.writeFile(agentsPath, newContent, "utf-8");
|
|
34404
|
+
const claudePath = path13.join(cwd, "CLAUDE.md");
|
|
34405
|
+
try {
|
|
34406
|
+
const stat2 = await fs17.lstat(claudePath);
|
|
34407
|
+
if (!stat2.isSymbolicLink()) {
|
|
34408
|
+
await fs17.unlink(claudePath);
|
|
34409
|
+
await fs17.symlink("AGENTS.md", claudePath);
|
|
34410
|
+
}
|
|
34411
|
+
} catch {
|
|
34412
|
+
await fs17.symlink("AGENTS.md", claudePath);
|
|
34413
|
+
}
|
|
34414
|
+
updateSpinner.succeed("Updated AGENTS.md");
|
|
34415
|
+
} catch (error) {
|
|
34416
|
+
updateSpinner.fail("Failed to update AGENTS.md");
|
|
34417
|
+
console.error(error instanceof Error ? error.message : "Unknown error");
|
|
34418
|
+
process.exit(1);
|
|
34419
|
+
}
|
|
34420
|
+
} else {
|
|
34421
|
+
console.log(" AGENTS.md is already up to date");
|
|
34422
|
+
}
|
|
34423
|
+
if (agentsNeedsUpdate) {
|
|
34424
|
+
if (agentsHasCustom && !options.force) {
|
|
34425
|
+
const diff = await getDiff(cwd, "AGENTS.md");
|
|
34426
|
+
if (diff) {
|
|
34427
|
+
console.log(`
|
|
34428
|
+
AGENTS.md has custom changes. Diff:
|
|
34429
|
+
`);
|
|
34430
|
+
console.log(diff);
|
|
34431
|
+
const proceed = await dist_default2({
|
|
34432
|
+
message: "Overwrite custom changes to AGENTS.md?",
|
|
34433
|
+
default: false
|
|
34434
|
+
});
|
|
34435
|
+
if (!proceed) {
|
|
34436
|
+
await revertChanges(cwd, "AGENTS.md");
|
|
34437
|
+
console.log(" Reverted AGENTS.md");
|
|
34438
|
+
} else {
|
|
34439
|
+
const committed = await commitFiles(cwd, ["AGENTS.md", "CLAUDE.md"], AGENTS_COMMIT_MSG);
|
|
34440
|
+
if (committed) {
|
|
34441
|
+
console.log(` Committed: "${AGENTS_COMMIT_MSG}"`);
|
|
34442
|
+
}
|
|
34443
|
+
}
|
|
34444
|
+
}
|
|
34445
|
+
} else {
|
|
34446
|
+
const committed = await commitFiles(cwd, ["AGENTS.md", "CLAUDE.md"], AGENTS_COMMIT_MSG);
|
|
34447
|
+
if (committed) {
|
|
34448
|
+
console.log(` Committed: "${AGENTS_COMMIT_MSG}"`);
|
|
34449
|
+
}
|
|
34450
|
+
}
|
|
34451
|
+
}
|
|
34452
|
+
const workflowsDir = path13.join(cwd, ".github", "workflows");
|
|
34453
|
+
const workflowFiles = [
|
|
34454
|
+
{ name: "sync-inbox.yml", template: SYNC_INBOX_WORKFLOW },
|
|
34455
|
+
{ name: "claude-bookkeeping.yml", template: CLAUDE_BOOKKEEPING_WORKFLOW },
|
|
34456
|
+
{ name: "sync-journal.yml", template: SYNC_JOURNAL_WORKFLOW }
|
|
34457
|
+
];
|
|
34458
|
+
const workflowsToUpdate = [];
|
|
34459
|
+
for (const wf of workflowFiles) {
|
|
34460
|
+
const wfPath = path13.join(workflowsDir, wf.name);
|
|
34461
|
+
try {
|
|
34462
|
+
const currentContent = await fs17.readFile(wfPath, "utf-8");
|
|
34463
|
+
if (currentContent !== wf.template) {
|
|
34464
|
+
workflowsToUpdate.push(wf);
|
|
34465
|
+
}
|
|
34466
|
+
} catch {
|
|
34467
|
+
}
|
|
34468
|
+
}
|
|
34469
|
+
if (workflowsToUpdate.length === 0) {
|
|
34470
|
+
console.log(" Workflows are already up to date");
|
|
34471
|
+
console.log("");
|
|
34472
|
+
return;
|
|
34473
|
+
}
|
|
34474
|
+
let workflowsHaveCustom = false;
|
|
34475
|
+
for (const wf of workflowsToUpdate) {
|
|
34476
|
+
const wfPath = `.github/workflows/${wf.name}`;
|
|
34477
|
+
if (await hasCustomChanges(cwd, wfPath, WORKFLOWS_COMMIT_MSG)) {
|
|
34478
|
+
workflowsHaveCustom = true;
|
|
34479
|
+
break;
|
|
34480
|
+
}
|
|
34481
|
+
}
|
|
34482
|
+
const workflowSpinner = ora6("Updating workflows...").start();
|
|
34483
|
+
try {
|
|
34484
|
+
for (const wf of workflowsToUpdate) {
|
|
34485
|
+
const wfPath = path13.join(workflowsDir, wf.name);
|
|
34486
|
+
await fs17.writeFile(wfPath, wf.template, "utf-8");
|
|
34487
|
+
}
|
|
34488
|
+
workflowSpinner.succeed(`Updated ${workflowsToUpdate.length} workflow(s)`);
|
|
34489
|
+
} catch (error) {
|
|
34490
|
+
workflowSpinner.fail("Failed to update workflows");
|
|
34491
|
+
console.error(error instanceof Error ? error.message : "Unknown error");
|
|
34492
|
+
return;
|
|
34493
|
+
}
|
|
34494
|
+
const workflowPaths = workflowsToUpdate.map((wf) => `.github/workflows/${wf.name}`);
|
|
34495
|
+
if (workflowsHaveCustom && !options.force) {
|
|
34496
|
+
const diff = await getDiff(cwd, ".github/workflows/");
|
|
34497
|
+
if (diff) {
|
|
34498
|
+
console.log(`
|
|
34499
|
+
Workflows have custom changes. Diff:
|
|
34500
|
+
`);
|
|
34501
|
+
console.log(diff);
|
|
34502
|
+
const proceed = await dist_default2({
|
|
34503
|
+
message: "Overwrite custom changes to workflows?",
|
|
34504
|
+
default: false
|
|
34505
|
+
});
|
|
34506
|
+
if (!proceed) {
|
|
34507
|
+
await revertChanges(cwd, ".github/workflows/");
|
|
34508
|
+
console.log(" Reverted workflows");
|
|
34509
|
+
} else {
|
|
34510
|
+
const committed = await commitFiles(cwd, workflowPaths, WORKFLOWS_COMMIT_MSG);
|
|
34511
|
+
if (committed) {
|
|
34512
|
+
console.log(` Committed: "${WORKFLOWS_COMMIT_MSG}"`);
|
|
34513
|
+
}
|
|
34514
|
+
}
|
|
34515
|
+
}
|
|
34516
|
+
} else {
|
|
34517
|
+
const committed = await commitFiles(cwd, workflowPaths, WORKFLOWS_COMMIT_MSG);
|
|
34518
|
+
if (committed) {
|
|
34519
|
+
console.log(` Committed: "${WORKFLOWS_COMMIT_MSG}"`);
|
|
34520
|
+
}
|
|
34521
|
+
}
|
|
34522
|
+
console.log("");
|
|
34523
|
+
}
|
|
34524
|
+
|
|
34525
|
+
// src/commands/setup-github.ts
|
|
34526
|
+
import * as fs18 from "node:fs/promises";
|
|
34527
|
+
import * as path14 from "node:path";
|
|
34528
|
+
import { exec as exec7 } from "node:child_process";
|
|
34529
|
+
import { promisify as promisify7 } from "node:util";
|
|
34530
|
+
var execAsync3 = promisify7(exec7);
|
|
34217
34531
|
async function hasGitRemote() {
|
|
34218
34532
|
try {
|
|
34219
|
-
const { stdout } = await
|
|
34533
|
+
const { stdout } = await execAsync3("git remote -v");
|
|
34220
34534
|
return stdout.trim().length > 0;
|
|
34221
34535
|
} catch {
|
|
34222
34536
|
return false;
|
|
@@ -34226,25 +34540,25 @@ async function getRepoName(cwd) {
|
|
|
34226
34540
|
return path14.basename(cwd);
|
|
34227
34541
|
}
|
|
34228
34542
|
async function getGitHubUsername() {
|
|
34229
|
-
const { stdout } = await
|
|
34543
|
+
const { stdout } = await execAsync3("gh api user -q .login");
|
|
34230
34544
|
return stdout.trim();
|
|
34231
34545
|
}
|
|
34232
34546
|
async function createGitHubRepo(repoName) {
|
|
34233
34547
|
const username = await getGitHubUsername();
|
|
34234
34548
|
const fullRepoName = `${username}/${repoName}`;
|
|
34235
|
-
await
|
|
34549
|
+
await execAsync3(`gh repo create "${fullRepoName}" --private`);
|
|
34236
34550
|
const sshUrl = `git@github.com:${fullRepoName}.git`;
|
|
34237
34551
|
try {
|
|
34238
|
-
await
|
|
34552
|
+
await execAsync3("git remote remove origin");
|
|
34239
34553
|
} catch {
|
|
34240
34554
|
}
|
|
34241
|
-
await
|
|
34242
|
-
await
|
|
34555
|
+
await execAsync3(`git remote add origin "${sshUrl}"`);
|
|
34556
|
+
await execAsync3(`gh repo view "${fullRepoName}" --json url`);
|
|
34243
34557
|
return `https://github.com/${fullRepoName}`;
|
|
34244
34558
|
}
|
|
34245
34559
|
async function checkGhInstalled() {
|
|
34246
34560
|
try {
|
|
34247
|
-
await
|
|
34561
|
+
await execAsync3("which gh");
|
|
34248
34562
|
return true;
|
|
34249
34563
|
} catch {
|
|
34250
34564
|
return false;
|
|
@@ -34252,7 +34566,7 @@ async function checkGhInstalled() {
|
|
|
34252
34566
|
}
|
|
34253
34567
|
async function checkGhWorkflowScope() {
|
|
34254
34568
|
try {
|
|
34255
|
-
const { stdout, stderr } = await
|
|
34569
|
+
const { stdout, stderr } = await execAsync3("gh auth status 2>&1");
|
|
34256
34570
|
const output = stdout + stderr;
|
|
34257
34571
|
return output.includes("workflow");
|
|
34258
34572
|
} catch {
|
|
@@ -34261,14 +34575,14 @@ async function checkGhWorkflowScope() {
|
|
|
34261
34575
|
}
|
|
34262
34576
|
async function checkGhAuthenticated() {
|
|
34263
34577
|
try {
|
|
34264
|
-
await
|
|
34578
|
+
await execAsync3("gh auth status");
|
|
34265
34579
|
return true;
|
|
34266
34580
|
} catch {
|
|
34267
34581
|
return false;
|
|
34268
34582
|
}
|
|
34269
34583
|
}
|
|
34270
34584
|
async function setGhSecret(name, value) {
|
|
34271
|
-
await
|
|
34585
|
+
await execAsync3(`echo "${value}" | gh secret set ${name}`);
|
|
34272
34586
|
}
|
|
34273
34587
|
async function setupGithubCommand() {
|
|
34274
34588
|
const cwd = process.cwd();
|
|
@@ -34419,22 +34733,24 @@ Make sure this repository has a GitHub remote configured.`);
|
|
|
34419
34733
|
Creating workflow files...`);
|
|
34420
34734
|
const workflowsDir = path14.join(cwd, ".github", "workflows");
|
|
34421
34735
|
await fs18.mkdir(workflowsDir, { recursive: true });
|
|
34422
|
-
|
|
34736
|
+
const claudeAuthLine = authMethod === "api_key" ? "anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}" : "claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}";
|
|
34737
|
+
const replaceAuth = (template) => template.replace(/\{\{CLAUDE_AUTH\}\}/g, claudeAuthLine);
|
|
34738
|
+
await fs18.writeFile(path14.join(workflowsDir, "sync-inbox.yml"), replaceAuth(SYNC_INBOX_WORKFLOW));
|
|
34423
34739
|
console.log("✓ Created .github/workflows/sync-inbox.yml");
|
|
34424
|
-
await fs18.writeFile(path14.join(workflowsDir, "claude-bookkeeping.yml"), CLAUDE_BOOKKEEPING_WORKFLOW);
|
|
34740
|
+
await fs18.writeFile(path14.join(workflowsDir, "claude-bookkeeping.yml"), replaceAuth(CLAUDE_BOOKKEEPING_WORKFLOW));
|
|
34425
34741
|
console.log("✓ Created .github/workflows/claude-bookkeeping.yml");
|
|
34426
34742
|
await fs18.writeFile(path14.join(workflowsDir, "sync-journal.yml"), SYNC_JOURNAL_WORKFLOW);
|
|
34427
34743
|
console.log("✓ Created .github/workflows/sync-journal.yml");
|
|
34428
34744
|
try {
|
|
34429
|
-
await
|
|
34430
|
-
const { stdout: status } = await
|
|
34745
|
+
await execAsync3("git add .github/workflows");
|
|
34746
|
+
const { stdout: status } = await execAsync3("git status --porcelain .github/workflows");
|
|
34431
34747
|
if (status.trim()) {
|
|
34432
34748
|
const shouldCommit = await dist_default2({
|
|
34433
34749
|
message: "Commit workflow files?",
|
|
34434
34750
|
default: true
|
|
34435
34751
|
});
|
|
34436
34752
|
if (shouldCommit) {
|
|
34437
|
-
await
|
|
34753
|
+
await execAsync3('git commit -m "Add GitHub Actions for automated bookkeeping"');
|
|
34438
34754
|
console.log(`
|
|
34439
34755
|
✓ Committed workflow files`);
|
|
34440
34756
|
}
|
|
@@ -34455,9 +34771,9 @@ Warning: Could not commit files:`);
|
|
|
34455
34771
|
try {
|
|
34456
34772
|
console.log(`
|
|
34457
34773
|
Pushing to GitHub...`);
|
|
34458
|
-
const { stdout: currentBranch } = await
|
|
34774
|
+
const { stdout: currentBranch } = await execAsync3("git rev-parse --abbrev-ref HEAD");
|
|
34459
34775
|
const branch = currentBranch.trim();
|
|
34460
|
-
await
|
|
34776
|
+
await execAsync3(`git push -u origin ${branch}`);
|
|
34461
34777
|
console.log("✓ Pushed to GitHub");
|
|
34462
34778
|
} catch (error) {
|
|
34463
34779
|
console.error(`
|