@programinglive/dev-workflow-mcp-server 1.13.1 → 1.13.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/git-helpers.js CHANGED
@@ -1,27 +1,37 @@
1
1
  import { exec } from "./exec.js";
2
2
 
3
3
  export async function hasStagedChanges() {
4
- try {
5
- const { stdout } = await exec("git status --porcelain");
6
- return stdout
7
- .split("\n")
8
- .filter(Boolean)
9
- .some((line) => {
10
- const indexStatus = line[0];
11
- return indexStatus && indexStatus !== " " && indexStatus !== "?";
12
- });
13
- } catch (error) {
14
- return false;
15
- }
4
+ const { stdout } = await exec("git status --porcelain");
5
+ return stdout
6
+ .split("\n")
7
+ .filter(Boolean)
8
+ .some((line) => {
9
+ const indexStatus = line[0];
10
+ return indexStatus && indexStatus !== " " && indexStatus !== "?";
11
+ });
16
12
  }
17
13
 
14
+ /**
15
+ * Apakah ada perubahan yang belum di-commit?
16
+ *
17
+ * MENGAPA TIDAK `catch { return false }`
18
+ * --------------------------------------
19
+ * Dulu fungsi ini menelan setiap error git dan menjawab `false`. Pemanggil
20
+ * membacanya sebagai "tidak ada perubahan", lalu `commit_and_push` menjawab
21
+ * "No changes detected. Assuming commit and push already completed" - sukses
22
+ * palsu, padahal tidak ada commit sama sekali.
23
+ *
24
+ * Yang terjadi 30 Oktober 2026: server MCP dijalankan dengan cwd direktori sesi
25
+ * OpenCode (`Documents\Default Project`) yang bukan repo git. `git status`
26
+ * gagal, error ditelan, delapan berkas tidak pernah ter-commit, dan tool tetap
27
+ * melaporkan berhasil.
28
+ *
29
+ * "Tidak tahu" bukan "tidak ada perubahan". Sekarang error diteruskan supaya
30
+ * pemanggil bisa melaporkan masalah sebenarnya.
31
+ */
18
32
  export async function hasWorkingChanges() {
19
- try {
20
- const { stdout } = await exec("git status --porcelain");
21
- return stdout.split("\n").some((line) => line && line.trim().length > 0);
22
- } catch (error) {
23
- return false;
24
- }
33
+ const { stdout } = await exec("git status --porcelain");
34
+ return stdout.split("\n").some((line) => line && line.trim().length > 0);
25
35
  }
26
36
 
27
37
  export function workingTreeSummary(statusOutput) {
@@ -113,51 +123,31 @@ function parseNameStatusLine(line) {
113
123
  }
114
124
 
115
125
  export async function getStagedChanges() {
116
- try {
117
- const { stdout } = await exec("git diff --cached --name-status");
118
- return stdout
119
- .split("\n")
120
- .map((line) => parseNameStatusLine(line))
121
- .filter(Boolean);
122
- } catch (error) {
123
- return [];
124
- }
126
+ const { stdout } = await exec("git diff --cached --name-status");
127
+ return stdout
128
+ .split("\n")
129
+ .map((line) => parseNameStatusLine(line))
130
+ .filter(Boolean);
125
131
  }
126
132
 
127
133
  export async function hasTestChanges() {
128
- try {
129
- const { stdout } = await exec("git status --porcelain");
130
- return containsTestFilesInStatus(stdout);
131
- } catch (error) {
132
- return false;
133
- }
134
+ const { stdout } = await exec("git status --porcelain");
135
+ return containsTestFilesInStatus(stdout);
134
136
  }
135
137
 
136
138
  export async function getLastCommitMessage() {
137
- try {
138
- const { stdout } = await exec("git log -1 --pretty=%B");
139
- return stdout.trim();
140
- } catch (error) {
141
- return "";
142
- }
139
+ const { stdout } = await exec("git log -1 --pretty=%B");
140
+ return stdout.trim();
143
141
  }
144
142
 
145
143
  export async function getCurrentBranch() {
146
- try {
147
- const { stdout } = await exec("git rev-parse --abbrev-ref HEAD");
148
- return stdout.trim();
149
- } catch (error) {
150
- return "";
151
- }
144
+ const { stdout } = await exec("git rev-parse --abbrev-ref HEAD");
145
+ return stdout.trim();
152
146
  }
153
147
 
154
148
  export async function getStatusOutput() {
155
- try {
156
- const { stdout } = await exec("git status --porcelain");
157
- return stdout;
158
- } catch (error) {
159
- return "";
160
- }
149
+ const { stdout } = await exec("git status --porcelain");
150
+ return stdout;
161
151
  }
162
152
 
163
153
  export async function getPrimaryBranch() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@programinglive/dev-workflow-mcp-server",
3
- "version": "1.13.1",
3
+ "version": "1.13.2",
4
4
  "description": "MCP server to enforce development workflow discipline",
5
5
  "homepage": "https://devworkflow.programinglive.com/",
6
6
  "mcpName": "io.github.programinglive/dev-workflow-mcp-server",
@@ -15,11 +15,26 @@ export async function handleCommitAndPush(args, context) {
15
15
  typeof args.commitMessage === "string" ? args.commitMessage.trim() : "";
16
16
 
17
17
  const requestedBranch = typeof args.branch === "string" ? args.branch.trim() : "";
18
- const primaryBranch = await git.getPrimaryBranch();
19
- const currentBranch = await git.getCurrentBranch();
20
- const branchForPush = requestedBranch || primaryBranch || currentBranch || "";
21
18
 
22
- const hasWorkingChanges = await git.hasWorkingChanges();
19
+ // Preflight: kalau git tidak bisa dijalankan, jangan menebak. Dulu rantai ini
20
+ // gagal, error ditelan helper, lalu tool menjawab "No changes detected" dan
21
+ // menandai commit selesai padahal tidak ada commit sama sekali.
22
+ let primaryBranch;
23
+ let currentBranch;
24
+ let hasWorkingChanges;
25
+ try {
26
+ primaryBranch = await git.getPrimaryBranch();
27
+ currentBranch = await git.getCurrentBranch();
28
+ hasWorkingChanges = await git.hasWorkingChanges();
29
+ } catch (error) {
30
+ return textResponse(
31
+ `❌ Tidak bisa memeriksa repository Git, jadi tidak ada yang di-commit.\n\n` +
32
+ `Penyebab: ${error.message}\n\n` +
33
+ `Set WORKFLOW_PROJECT_DIR ke direktori proyek, atau jalankan server MCP dari dalam direktori proyek.`
34
+ );
35
+ }
36
+
37
+ const branchForPush = requestedBranch || primaryBranch || currentBranch || "";
23
38
 
24
39
  if (!hasWorkingChanges) {
25
40
  const lastCommitMessage = await git.getLastCommitMessage();
@@ -50,7 +50,18 @@ export async function handlePerformRelease(args, context) {
50
50
  return textResponse("⚠️ Please start a task first using 'start_task'!");
51
51
  }
52
52
 
53
- if (await resetToCommitIfWorkingChanges(workflowState, git)) {
53
+ let hasNewChanges;
54
+ try {
55
+ hasNewChanges = await resetToCommitIfWorkingChanges(workflowState, git);
56
+ } catch (error) {
57
+ return textResponse(
58
+ `❌ Tidak bisa memeriksa repository Git, jadi status rilis tidak dapat diverifikasi.\n\n` +
59
+ `Penyebab: ${error.message}\n\n` +
60
+ `Set WORKFLOW_PROJECT_DIR ke direktori proyek, atau jalankan server MCP dari dalam direktori proyek.`
61
+ );
62
+ }
63
+
64
+ if (hasNewChanges) {
54
65
  return textResponse(
55
66
  "⚠️ Detected new changes after the last commit. Please run 'commit_and_push' again before recording a release."
56
67
  );
@@ -63,7 +74,16 @@ export async function handlePerformRelease(args, context) {
63
74
  }
64
75
 
65
76
  if (!workflowState.state.commitAndPushCompleted) {
66
- const hasWorkingChanges = await git.hasWorkingChanges();
77
+ let hasWorkingChanges;
78
+ try {
79
+ hasWorkingChanges = await git.hasWorkingChanges();
80
+ } catch (error) {
81
+ return textResponse(
82
+ `❌ Tidak bisa memeriksa repository Git, jadi status rilis tidak dapat diverifikasi.\n\n` +
83
+ `Penyebab: ${error.message}\n\n` +
84
+ `Set WORKFLOW_PROJECT_DIR ke direktori proyek, atau jalankan server MCP dari dalam direktori proyek.`
85
+ );
86
+ }
67
87
 
68
88
  if (hasWorkingChanges) {
69
89
  return textResponse(