ledgit-cli 0.2.0 → 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 +462 -157
- 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,20 +27937,13 @@ 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!
|
|
27912
27944
|
- ${newCount} new inbox items downloaded
|
|
27913
27945
|
- ${existingCount} already existed
|
|
27914
27946
|
`);
|
|
27915
|
-
if (newCount > 0) {
|
|
27916
|
-
const commitMessage = `Sync inbox: ${newCount} items downloaded`;
|
|
27917
|
-
const committed = await commitAll(cwd, commitMessage);
|
|
27918
|
-
if (committed) {
|
|
27919
|
-
console.log(` Committed: "${commitMessage}"
|
|
27920
|
-
`);
|
|
27921
|
-
}
|
|
27922
|
-
}
|
|
27923
27947
|
}
|
|
27924
27948
|
function getExtensionFromContentType(contentType) {
|
|
27925
27949
|
const map = {
|
|
@@ -28006,14 +28030,6 @@ async function syncFortnoxInbox2(cwd) {
|
|
|
28006
28030
|
- ${result.newCount} new inbox items downloaded
|
|
28007
28031
|
- ${result.existingCount} already existed
|
|
28008
28032
|
`);
|
|
28009
|
-
if (result.newCount > 0) {
|
|
28010
|
-
const commitMessage = `Sync inbox: ${result.newCount} items downloaded from Fortnox`;
|
|
28011
|
-
const committed = await commitAll(cwd, commitMessage);
|
|
28012
|
-
if (committed) {
|
|
28013
|
-
console.log(` Committed: "${commitMessage}"
|
|
28014
|
-
`);
|
|
28015
|
-
}
|
|
28016
|
-
}
|
|
28017
28033
|
}
|
|
28018
28034
|
|
|
28019
28035
|
// src/commands/company-info.ts
|
|
@@ -28941,6 +28957,8 @@ async function updateEnvFile(cwd) {
|
|
|
28941
28957
|
// src/commands/update.ts
|
|
28942
28958
|
import * as fs17 from "node:fs/promises";
|
|
28943
28959
|
import * as path13 from "node:path";
|
|
28960
|
+
import { exec as exec6 } from "node:child_process";
|
|
28961
|
+
import { promisify as promisify6 } from "node:util";
|
|
28944
28962
|
import ora6 from "ora";
|
|
28945
28963
|
|
|
28946
28964
|
// ../../node_modules/ansi-regex/index.js
|
|
@@ -34021,83 +34039,6 @@ var dist_default8 = createPrompt4((config3, done) => {
|
|
|
34021
34039
|
`).trimEnd();
|
|
34022
34040
|
return `${lines}${cursorHide4}`;
|
|
34023
34041
|
});
|
|
34024
|
-
// src/commands/update.ts
|
|
34025
|
-
async function updateCommand(options = {}) {
|
|
34026
|
-
const cwd = process.cwd();
|
|
34027
|
-
const agentsPath = path13.join(cwd, "AGENTS.md");
|
|
34028
|
-
let companyName;
|
|
34029
|
-
let provider;
|
|
34030
|
-
try {
|
|
34031
|
-
const content = await fs17.readFile(agentsPath, "utf-8");
|
|
34032
|
-
const match = content.match(/repository for (.+?) \(Swedish company\).+?integrates with (Fortnox|Bokio)/s);
|
|
34033
|
-
if (!match || !match[1] || !match[2]) {
|
|
34034
|
-
console.error("Could not parse company info from AGENTS.md");
|
|
34035
|
-
process.exit(1);
|
|
34036
|
-
}
|
|
34037
|
-
companyName = match[1];
|
|
34038
|
-
provider = match[2];
|
|
34039
|
-
console.log(` Company: ${companyName}`);
|
|
34040
|
-
console.log(` Provider: ${provider}`);
|
|
34041
|
-
} catch {
|
|
34042
|
-
console.error("AGENTS.md not found. Run this command in a ledgit repository.");
|
|
34043
|
-
process.exit(1);
|
|
34044
|
-
}
|
|
34045
|
-
const newContent = renderAgentsTemplate({
|
|
34046
|
-
companyName,
|
|
34047
|
-
provider
|
|
34048
|
-
});
|
|
34049
|
-
if (!options.force) {
|
|
34050
|
-
const currentContent = await fs17.readFile(agentsPath, "utf-8");
|
|
34051
|
-
const hasCustomChanges = currentContent !== newContent;
|
|
34052
|
-
if (hasCustomChanges) {
|
|
34053
|
-
const proceed = await dist_default2({
|
|
34054
|
-
message: "AGENTS.md has been modified. Overwrite with latest template?",
|
|
34055
|
-
default: true
|
|
34056
|
-
});
|
|
34057
|
-
if (!proceed) {
|
|
34058
|
-
console.log(" Update cancelled.");
|
|
34059
|
-
return;
|
|
34060
|
-
}
|
|
34061
|
-
}
|
|
34062
|
-
}
|
|
34063
|
-
const updateSpinner = ora6("Updating AGENTS.md...").start();
|
|
34064
|
-
try {
|
|
34065
|
-
await fs17.writeFile(agentsPath, newContent, "utf-8");
|
|
34066
|
-
const claudePath = path13.join(cwd, "CLAUDE.md");
|
|
34067
|
-
try {
|
|
34068
|
-
const stat2 = await fs17.lstat(claudePath);
|
|
34069
|
-
if (!stat2.isSymbolicLink()) {
|
|
34070
|
-
await fs17.unlink(claudePath);
|
|
34071
|
-
await fs17.symlink("AGENTS.md", claudePath);
|
|
34072
|
-
}
|
|
34073
|
-
} catch {
|
|
34074
|
-
await fs17.symlink("AGENTS.md", claudePath);
|
|
34075
|
-
}
|
|
34076
|
-
updateSpinner.succeed("Updated AGENTS.md");
|
|
34077
|
-
} catch (error) {
|
|
34078
|
-
updateSpinner.fail("Failed to update AGENTS.md");
|
|
34079
|
-
console.error(error instanceof Error ? error.message : "Unknown error");
|
|
34080
|
-
process.exit(1);
|
|
34081
|
-
}
|
|
34082
|
-
const commitMessage = "Update AGENTS.md to latest version";
|
|
34083
|
-
const committed = await commitAll(cwd, commitMessage);
|
|
34084
|
-
if (committed) {
|
|
34085
|
-
console.log(`
|
|
34086
|
-
Committed: "${commitMessage}"
|
|
34087
|
-
`);
|
|
34088
|
-
} else {
|
|
34089
|
-
console.log(`
|
|
34090
|
-
No changes to commit.
|
|
34091
|
-
`);
|
|
34092
|
-
}
|
|
34093
|
-
}
|
|
34094
|
-
|
|
34095
|
-
// src/commands/setup-github.ts
|
|
34096
|
-
import * as fs18 from "node:fs/promises";
|
|
34097
|
-
import * as path14 from "node:path";
|
|
34098
|
-
import { exec as exec6 } from "node:child_process";
|
|
34099
|
-
import { promisify as promisify6 } from "node:util";
|
|
34100
|
-
|
|
34101
34042
|
// src/templates/workflows.ts
|
|
34102
34043
|
var SYNC_INBOX_WORKFLOW = `name: Sync Inbox
|
|
34103
34044
|
on:
|
|
@@ -34112,18 +34053,30 @@ jobs:
|
|
|
34112
34053
|
contents: write
|
|
34113
34054
|
pull-requests: write
|
|
34114
34055
|
issues: write
|
|
34115
|
-
id-token: write
|
|
34116
34056
|
steps:
|
|
34117
34057
|
- uses: actions/checkout@v5
|
|
34118
34058
|
|
|
34119
34059
|
- uses: oven-sh/setup-bun@v2
|
|
34120
34060
|
|
|
34061
|
+
- name: Configure git
|
|
34062
|
+
run: |
|
|
34063
|
+
git config user.name "ledgit[bot]"
|
|
34064
|
+
git config user.email "bot@ledgit.se"
|
|
34065
|
+
|
|
34121
34066
|
- name: Sync inbox
|
|
34122
34067
|
run: npx ledgit-cli sync-inbox
|
|
34123
34068
|
env:
|
|
34124
34069
|
BOKIO_TOKEN: \${{ secrets.BOKIO_TOKEN }}
|
|
34125
34070
|
BOKIO_COMPANY_ID: \${{ secrets.BOKIO_COMPANY_ID }}
|
|
34126
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
|
+
|
|
34127
34080
|
- name: Check for new items
|
|
34128
34081
|
id: check
|
|
34129
34082
|
run: |
|
|
@@ -34133,32 +34086,89 @@ jobs:
|
|
|
34133
34086
|
echo "has_new=false" >> $GITHUB_OUTPUT
|
|
34134
34087
|
fi
|
|
34135
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
|
+
|
|
34136
34095
|
- name: Bookkeep with Claude
|
|
34137
34096
|
if: steps.check.outputs.has_new == 'true'
|
|
34138
|
-
|
|
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
|
|
34139
34168
|
with:
|
|
34140
|
-
|
|
34141
|
-
|
|
34142
|
-
|
|
34143
|
-
|
|
34144
|
-
FIRST: Check for existing open bookkeeping PRs with \\\`gh pr list\\\`.
|
|
34145
|
-
If there's an active PR you should continue on, check out that branch and add to it.
|
|
34146
|
-
Group related entries (e.g., same vendor, same week) in one PR when it makes sense.
|
|
34147
|
-
|
|
34148
|
-
THEN:
|
|
34149
|
-
1. Check each new item in inbox/ (you can read PDFs directly)
|
|
34150
|
-
2. Find similar entries in journal-entries/ for reference
|
|
34151
|
-
3. Create entries with \\\`npx ledgit-cli create-entry\\\`
|
|
34152
|
-
|
|
34153
|
-
ALWAYS create or update a PR:
|
|
34154
|
-
- If you created entries successfully, PR contains the new entries
|
|
34155
|
-
- If anything is unclear, commit the inbox files and ask questions in the PR description
|
|
34156
|
-
- The user will answer your questions via PR comments
|
|
34157
|
-
|
|
34158
|
-
For new PRs, create branch: book/{date}-{description}
|
|
34159
|
-
claude_args: |
|
|
34160
|
-
--allowedTools "Read,Write,Edit,Bash(npx ledgit-cli:*),Bash(git:*),Bash(gh pr:*),Bash(ls:*),Bash(find:*),Bash(grep:*),Bash(cat:*)"
|
|
34161
|
-
track_progress: true
|
|
34169
|
+
name: claude-execution-log
|
|
34170
|
+
path: /tmp/claude-output.json
|
|
34171
|
+
retention-days: 7
|
|
34162
34172
|
`;
|
|
34163
34173
|
var CLAUDE_BOOKKEEPING_WORKFLOW = `name: Claude Bookkeeping
|
|
34164
34174
|
on:
|
|
@@ -34175,25 +34185,118 @@ jobs:
|
|
|
34175
34185
|
contents: write
|
|
34176
34186
|
pull-requests: write
|
|
34177
34187
|
issues: write
|
|
34178
|
-
id-token: write
|
|
34179
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
|
+
|
|
34180
34198
|
- uses: actions/checkout@v5
|
|
34181
34199
|
with:
|
|
34182
|
-
ref: \${{
|
|
34200
|
+
ref: \${{ steps.pr.outputs.branch }}
|
|
34183
34201
|
|
|
34184
34202
|
- uses: oven-sh/setup-bun@v2
|
|
34185
34203
|
|
|
34186
|
-
-
|
|
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
|
|
34187
34239
|
with:
|
|
34188
|
-
|
|
34189
|
-
|
|
34190
|
-
|
|
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.
|
|
34260
|
+
|
|
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
|
|
34191
34266
|
|
|
34192
|
-
|
|
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
|
|
34193
34288
|
|
|
34194
|
-
|
|
34195
|
-
|
|
34196
|
-
|
|
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
|
|
34197
34300
|
`;
|
|
34198
34301
|
var SYNC_JOURNAL_WORKFLOW = `name: Sync Journal
|
|
34199
34302
|
on:
|
|
@@ -34223,11 +34326,211 @@ jobs:
|
|
|
34223
34326
|
BOKIO_COMPANY_ID: \${{ secrets.BOKIO_COMPANY_ID }}
|
|
34224
34327
|
`;
|
|
34225
34328
|
|
|
34226
|
-
// src/commands/
|
|
34329
|
+
// src/commands/update.ts
|
|
34227
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);
|
|
34228
34531
|
async function hasGitRemote() {
|
|
34229
34532
|
try {
|
|
34230
|
-
const { stdout } = await
|
|
34533
|
+
const { stdout } = await execAsync3("git remote -v");
|
|
34231
34534
|
return stdout.trim().length > 0;
|
|
34232
34535
|
} catch {
|
|
34233
34536
|
return false;
|
|
@@ -34237,25 +34540,25 @@ async function getRepoName(cwd) {
|
|
|
34237
34540
|
return path14.basename(cwd);
|
|
34238
34541
|
}
|
|
34239
34542
|
async function getGitHubUsername() {
|
|
34240
|
-
const { stdout } = await
|
|
34543
|
+
const { stdout } = await execAsync3("gh api user -q .login");
|
|
34241
34544
|
return stdout.trim();
|
|
34242
34545
|
}
|
|
34243
34546
|
async function createGitHubRepo(repoName) {
|
|
34244
34547
|
const username = await getGitHubUsername();
|
|
34245
34548
|
const fullRepoName = `${username}/${repoName}`;
|
|
34246
|
-
await
|
|
34549
|
+
await execAsync3(`gh repo create "${fullRepoName}" --private`);
|
|
34247
34550
|
const sshUrl = `git@github.com:${fullRepoName}.git`;
|
|
34248
34551
|
try {
|
|
34249
|
-
await
|
|
34552
|
+
await execAsync3("git remote remove origin");
|
|
34250
34553
|
} catch {
|
|
34251
34554
|
}
|
|
34252
|
-
await
|
|
34253
|
-
await
|
|
34555
|
+
await execAsync3(`git remote add origin "${sshUrl}"`);
|
|
34556
|
+
await execAsync3(`gh repo view "${fullRepoName}" --json url`);
|
|
34254
34557
|
return `https://github.com/${fullRepoName}`;
|
|
34255
34558
|
}
|
|
34256
34559
|
async function checkGhInstalled() {
|
|
34257
34560
|
try {
|
|
34258
|
-
await
|
|
34561
|
+
await execAsync3("which gh");
|
|
34259
34562
|
return true;
|
|
34260
34563
|
} catch {
|
|
34261
34564
|
return false;
|
|
@@ -34263,7 +34566,7 @@ async function checkGhInstalled() {
|
|
|
34263
34566
|
}
|
|
34264
34567
|
async function checkGhWorkflowScope() {
|
|
34265
34568
|
try {
|
|
34266
|
-
const { stdout, stderr } = await
|
|
34569
|
+
const { stdout, stderr } = await execAsync3("gh auth status 2>&1");
|
|
34267
34570
|
const output = stdout + stderr;
|
|
34268
34571
|
return output.includes("workflow");
|
|
34269
34572
|
} catch {
|
|
@@ -34272,14 +34575,14 @@ async function checkGhWorkflowScope() {
|
|
|
34272
34575
|
}
|
|
34273
34576
|
async function checkGhAuthenticated() {
|
|
34274
34577
|
try {
|
|
34275
|
-
await
|
|
34578
|
+
await execAsync3("gh auth status");
|
|
34276
34579
|
return true;
|
|
34277
34580
|
} catch {
|
|
34278
34581
|
return false;
|
|
34279
34582
|
}
|
|
34280
34583
|
}
|
|
34281
34584
|
async function setGhSecret(name, value) {
|
|
34282
|
-
await
|
|
34585
|
+
await execAsync3(`echo "${value}" | gh secret set ${name}`);
|
|
34283
34586
|
}
|
|
34284
34587
|
async function setupGithubCommand() {
|
|
34285
34588
|
const cwd = process.cwd();
|
|
@@ -34430,22 +34733,24 @@ Make sure this repository has a GitHub remote configured.`);
|
|
|
34430
34733
|
Creating workflow files...`);
|
|
34431
34734
|
const workflowsDir = path14.join(cwd, ".github", "workflows");
|
|
34432
34735
|
await fs18.mkdir(workflowsDir, { recursive: true });
|
|
34433
|
-
|
|
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));
|
|
34434
34739
|
console.log("✓ Created .github/workflows/sync-inbox.yml");
|
|
34435
|
-
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));
|
|
34436
34741
|
console.log("✓ Created .github/workflows/claude-bookkeeping.yml");
|
|
34437
34742
|
await fs18.writeFile(path14.join(workflowsDir, "sync-journal.yml"), SYNC_JOURNAL_WORKFLOW);
|
|
34438
34743
|
console.log("✓ Created .github/workflows/sync-journal.yml");
|
|
34439
34744
|
try {
|
|
34440
|
-
await
|
|
34441
|
-
const { stdout: status } = await
|
|
34745
|
+
await execAsync3("git add .github/workflows");
|
|
34746
|
+
const { stdout: status } = await execAsync3("git status --porcelain .github/workflows");
|
|
34442
34747
|
if (status.trim()) {
|
|
34443
34748
|
const shouldCommit = await dist_default2({
|
|
34444
34749
|
message: "Commit workflow files?",
|
|
34445
34750
|
default: true
|
|
34446
34751
|
});
|
|
34447
34752
|
if (shouldCommit) {
|
|
34448
|
-
await
|
|
34753
|
+
await execAsync3('git commit -m "Add GitHub Actions for automated bookkeeping"');
|
|
34449
34754
|
console.log(`
|
|
34450
34755
|
✓ Committed workflow files`);
|
|
34451
34756
|
}
|
|
@@ -34466,9 +34771,9 @@ Warning: Could not commit files:`);
|
|
|
34466
34771
|
try {
|
|
34467
34772
|
console.log(`
|
|
34468
34773
|
Pushing to GitHub...`);
|
|
34469
|
-
const { stdout: currentBranch } = await
|
|
34774
|
+
const { stdout: currentBranch } = await execAsync3("git rev-parse --abbrev-ref HEAD");
|
|
34470
34775
|
const branch = currentBranch.trim();
|
|
34471
|
-
await
|
|
34776
|
+
await execAsync3(`git push -u origin ${branch}`);
|
|
34472
34777
|
console.log("✓ Pushed to GitHub");
|
|
34473
34778
|
} catch (error) {
|
|
34474
34779
|
console.error(`
|