@gethmy/mcp 2.9.1 → 2.9.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/cli.js +24 -39
- package/dist/index.js +24 -39
- package/dist/lib/api-client.js +19 -36
- package/package.json +1 -1
- package/src/api-client.ts +42 -34
- package/src/prompt-builder.ts +0 -67
- package/src/server.ts +15 -4
package/dist/cli.js
CHANGED
|
@@ -20,7 +20,6 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
20
20
|
// src/prompt-builder.ts
|
|
21
21
|
var exports_prompt_builder = {};
|
|
22
22
|
__export(exports_prompt_builder, {
|
|
23
|
-
proposePromptVariant: () => proposePromptVariant,
|
|
24
23
|
inferCategoryFromLabels: () => inferCategoryFromLabels,
|
|
25
24
|
getRoleFraming: () => getRoleFraming,
|
|
26
25
|
getAvailableVariants: () => getAvailableVariants,
|
|
@@ -337,26 +336,7 @@ function getAvailableCategories() {
|
|
|
337
336
|
function getAvailableVariants() {
|
|
338
337
|
return ["analysis", "draft", "execute"];
|
|
339
338
|
}
|
|
340
|
-
|
|
341
|
-
if (!contentHash)
|
|
342
|
-
return null;
|
|
343
|
-
const cohort = await fetchCohort(contentHash);
|
|
344
|
-
if (!cohort || cohort.length < VARIANT_MIN_COHORT)
|
|
345
|
-
return null;
|
|
346
|
-
const completed = cohort.filter((r) => r.status === "completed" && (r.progressPercent ?? 0) >= 100 && !r.hadBlockers).length;
|
|
347
|
-
const completionRate = completed / cohort.length;
|
|
348
|
-
if (completionRate >= VARIANT_COMPLETION_THRESHOLD)
|
|
349
|
-
return null;
|
|
350
|
-
const blockerRate = cohort.filter((r) => r.hadBlockers).length / cohort.length;
|
|
351
|
-
const framingHint = blockerRate >= 0.4 ? "Cohort hits frequent blockers — try a more diagnostic framing (require root-cause + repro before any fix)." : "Cohort frequently stalls without finishing — try a more action-forcing framing (smaller subtasks, explicit DoD checklist).";
|
|
352
|
-
return {
|
|
353
|
-
contentHash,
|
|
354
|
-
cohortSize: cohort.length,
|
|
355
|
-
completionRate,
|
|
356
|
-
framingHint
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
var PROMPT_TEMPLATE_VERSION = 1, LABEL_CATEGORY_MAP, DEFAULT_ROLE_FRAMINGS, VARIANT_INSTRUCTIONS, VARIANT_MIN_COHORT = 10, VARIANT_COMPLETION_THRESHOLD = 0.4;
|
|
339
|
+
var PROMPT_TEMPLATE_VERSION = 1, LABEL_CATEGORY_MAP, DEFAULT_ROLE_FRAMINGS, VARIANT_INSTRUCTIONS;
|
|
360
340
|
var init_prompt_builder = __esm(() => {
|
|
361
341
|
LABEL_CATEGORY_MAP = {
|
|
362
342
|
bug: "bug",
|
|
@@ -1860,16 +1840,6 @@ class HarmonyApiClient {
|
|
|
1860
1840
|
async recordPromptHistory(data) {
|
|
1861
1841
|
return this.request("POST", "/prompt-history", data);
|
|
1862
1842
|
}
|
|
1863
|
-
async recordPromptHistoryFeedback(sessionId, outcome) {
|
|
1864
|
-
return this.request("POST", "/prompt-history/feedback", {
|
|
1865
|
-
sessionId,
|
|
1866
|
-
outcome
|
|
1867
|
-
});
|
|
1868
|
-
}
|
|
1869
|
-
async getPromptHistoryCohort(contentHash) {
|
|
1870
|
-
const params = new URLSearchParams({ content_hash: contentHash });
|
|
1871
|
-
return this.request("GET", `/prompt-history/cohort?${params.toString()}`);
|
|
1872
|
-
}
|
|
1873
1843
|
async generateCardPrompt(options) {
|
|
1874
1844
|
const { generatePrompt: generatePrompt2 } = await loadPromptModules();
|
|
1875
1845
|
const cardResult = await this.getCard(options.cardId);
|
|
@@ -1974,6 +1944,23 @@ ${section}`;
|
|
|
1974
1944
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1975
1945
|
console.debug(`[generateCardPrompt] comments fetch failed: ${msg}`);
|
|
1976
1946
|
}
|
|
1947
|
+
if (cardData.plan_id) {
|
|
1948
|
+
try {
|
|
1949
|
+
const { plan } = await this.getPlan(cardData.plan_id);
|
|
1950
|
+
const planContent = plan?.content;
|
|
1951
|
+
if (planContent?.trim()) {
|
|
1952
|
+
result.prompt = `${result.prompt}
|
|
1953
|
+
|
|
1954
|
+
## Approved Plan
|
|
1955
|
+
This card has an approved implementation plan. Follow it unless you find a concrete reason it is wrong — if you must diverge, say so in a comment and explain why.
|
|
1956
|
+
|
|
1957
|
+
${planContent.trim()}`;
|
|
1958
|
+
}
|
|
1959
|
+
} catch (err) {
|
|
1960
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1961
|
+
console.debug(`[generateCardPrompt] plan fetch failed: ${msg}`);
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1977
1964
|
try {
|
|
1978
1965
|
await this.recordPromptHistory({
|
|
1979
1966
|
cardId: cardData.id,
|
|
@@ -1983,11 +1970,7 @@ ${section}`;
|
|
|
1983
1970
|
assemblyId: result.assemblyId ?? null,
|
|
1984
1971
|
tokenEstimate: result.tokenEstimate,
|
|
1985
1972
|
contextSummary: result.contextSummary
|
|
1986
|
-
}
|
|
1987
|
-
sessionId: options.sessionId ?? null,
|
|
1988
|
-
contentHash: result.contentHash,
|
|
1989
|
-
templateVersion: result.version,
|
|
1990
|
-
confidence: 0.5
|
|
1973
|
+
}
|
|
1991
1974
|
});
|
|
1992
1975
|
} catch (err) {
|
|
1993
1976
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -3200,7 +3183,7 @@ var TOOLS = {
|
|
|
3200
3183
|
title: { type: "string", description: "Card title" },
|
|
3201
3184
|
columnId: {
|
|
3202
3185
|
type: "string",
|
|
3203
|
-
description: "Target column ID (optional, defaults to first column)"
|
|
3186
|
+
description: "Target column ID (optional, defaults to the project's default column, or the first column if none is set)"
|
|
3204
3187
|
},
|
|
3205
3188
|
projectId: {
|
|
3206
3189
|
type: "string",
|
|
@@ -5720,10 +5703,12 @@ async function handleToolCall(name, args, deps) {
|
|
|
5720
5703
|
source: args.source || "agent",
|
|
5721
5704
|
tasks: args.tasks
|
|
5722
5705
|
});
|
|
5723
|
-
const
|
|
5706
|
+
const planId = result.plan.id;
|
|
5707
|
+
const { workspaceSlug, projectSlug } = result;
|
|
5708
|
+
const planUrl = workspaceSlug && projectSlug ? `https://app.gethmy.com/${workspaceSlug}/${projectSlug}/plans/${planId}` : `https://app.gethmy.com/plans/${planId}`;
|
|
5724
5709
|
return {
|
|
5725
5710
|
success: true,
|
|
5726
|
-
planId
|
|
5711
|
+
planId,
|
|
5727
5712
|
planUrl,
|
|
5728
5713
|
plan: result.plan,
|
|
5729
5714
|
tasks: result.tasks
|
package/dist/index.js
CHANGED
|
@@ -20,7 +20,6 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
20
20
|
// src/prompt-builder.ts
|
|
21
21
|
var exports_prompt_builder = {};
|
|
22
22
|
__export(exports_prompt_builder, {
|
|
23
|
-
proposePromptVariant: () => proposePromptVariant,
|
|
24
23
|
inferCategoryFromLabels: () => inferCategoryFromLabels,
|
|
25
24
|
getRoleFraming: () => getRoleFraming,
|
|
26
25
|
getAvailableVariants: () => getAvailableVariants,
|
|
@@ -337,26 +336,7 @@ function getAvailableCategories() {
|
|
|
337
336
|
function getAvailableVariants() {
|
|
338
337
|
return ["analysis", "draft", "execute"];
|
|
339
338
|
}
|
|
340
|
-
|
|
341
|
-
if (!contentHash)
|
|
342
|
-
return null;
|
|
343
|
-
const cohort = await fetchCohort(contentHash);
|
|
344
|
-
if (!cohort || cohort.length < VARIANT_MIN_COHORT)
|
|
345
|
-
return null;
|
|
346
|
-
const completed = cohort.filter((r) => r.status === "completed" && (r.progressPercent ?? 0) >= 100 && !r.hadBlockers).length;
|
|
347
|
-
const completionRate = completed / cohort.length;
|
|
348
|
-
if (completionRate >= VARIANT_COMPLETION_THRESHOLD)
|
|
349
|
-
return null;
|
|
350
|
-
const blockerRate = cohort.filter((r) => r.hadBlockers).length / cohort.length;
|
|
351
|
-
const framingHint = blockerRate >= 0.4 ? "Cohort hits frequent blockers — try a more diagnostic framing (require root-cause + repro before any fix)." : "Cohort frequently stalls without finishing — try a more action-forcing framing (smaller subtasks, explicit DoD checklist).";
|
|
352
|
-
return {
|
|
353
|
-
contentHash,
|
|
354
|
-
cohortSize: cohort.length,
|
|
355
|
-
completionRate,
|
|
356
|
-
framingHint
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
var PROMPT_TEMPLATE_VERSION = 1, LABEL_CATEGORY_MAP, DEFAULT_ROLE_FRAMINGS, VARIANT_INSTRUCTIONS, VARIANT_MIN_COHORT = 10, VARIANT_COMPLETION_THRESHOLD = 0.4;
|
|
339
|
+
var PROMPT_TEMPLATE_VERSION = 1, LABEL_CATEGORY_MAP, DEFAULT_ROLE_FRAMINGS, VARIANT_INSTRUCTIONS;
|
|
360
340
|
var init_prompt_builder = __esm(() => {
|
|
361
341
|
LABEL_CATEGORY_MAP = {
|
|
362
342
|
bug: "bug",
|
|
@@ -1856,16 +1836,6 @@ class HarmonyApiClient {
|
|
|
1856
1836
|
async recordPromptHistory(data) {
|
|
1857
1837
|
return this.request("POST", "/prompt-history", data);
|
|
1858
1838
|
}
|
|
1859
|
-
async recordPromptHistoryFeedback(sessionId, outcome) {
|
|
1860
|
-
return this.request("POST", "/prompt-history/feedback", {
|
|
1861
|
-
sessionId,
|
|
1862
|
-
outcome
|
|
1863
|
-
});
|
|
1864
|
-
}
|
|
1865
|
-
async getPromptHistoryCohort(contentHash) {
|
|
1866
|
-
const params = new URLSearchParams({ content_hash: contentHash });
|
|
1867
|
-
return this.request("GET", `/prompt-history/cohort?${params.toString()}`);
|
|
1868
|
-
}
|
|
1869
1839
|
async generateCardPrompt(options) {
|
|
1870
1840
|
const { generatePrompt: generatePrompt2 } = await loadPromptModules();
|
|
1871
1841
|
const cardResult = await this.getCard(options.cardId);
|
|
@@ -1970,6 +1940,23 @@ ${section}`;
|
|
|
1970
1940
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1971
1941
|
console.debug(`[generateCardPrompt] comments fetch failed: ${msg}`);
|
|
1972
1942
|
}
|
|
1943
|
+
if (cardData.plan_id) {
|
|
1944
|
+
try {
|
|
1945
|
+
const { plan } = await this.getPlan(cardData.plan_id);
|
|
1946
|
+
const planContent = plan?.content;
|
|
1947
|
+
if (planContent?.trim()) {
|
|
1948
|
+
result.prompt = `${result.prompt}
|
|
1949
|
+
|
|
1950
|
+
## Approved Plan
|
|
1951
|
+
This card has an approved implementation plan. Follow it unless you find a concrete reason it is wrong — if you must diverge, say so in a comment and explain why.
|
|
1952
|
+
|
|
1953
|
+
${planContent.trim()}`;
|
|
1954
|
+
}
|
|
1955
|
+
} catch (err) {
|
|
1956
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1957
|
+
console.debug(`[generateCardPrompt] plan fetch failed: ${msg}`);
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1973
1960
|
try {
|
|
1974
1961
|
await this.recordPromptHistory({
|
|
1975
1962
|
cardId: cardData.id,
|
|
@@ -1979,11 +1966,7 @@ ${section}`;
|
|
|
1979
1966
|
assemblyId: result.assemblyId ?? null,
|
|
1980
1967
|
tokenEstimate: result.tokenEstimate,
|
|
1981
1968
|
contextSummary: result.contextSummary
|
|
1982
|
-
}
|
|
1983
|
-
sessionId: options.sessionId ?? null,
|
|
1984
|
-
contentHash: result.contentHash,
|
|
1985
|
-
templateVersion: result.version,
|
|
1986
|
-
confidence: 0.5
|
|
1969
|
+
}
|
|
1987
1970
|
});
|
|
1988
1971
|
} catch (err) {
|
|
1989
1972
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -3196,7 +3179,7 @@ var TOOLS = {
|
|
|
3196
3179
|
title: { type: "string", description: "Card title" },
|
|
3197
3180
|
columnId: {
|
|
3198
3181
|
type: "string",
|
|
3199
|
-
description: "Target column ID (optional, defaults to first column)"
|
|
3182
|
+
description: "Target column ID (optional, defaults to the project's default column, or the first column if none is set)"
|
|
3200
3183
|
},
|
|
3201
3184
|
projectId: {
|
|
3202
3185
|
type: "string",
|
|
@@ -5716,10 +5699,12 @@ async function handleToolCall(name, args, deps) {
|
|
|
5716
5699
|
source: args.source || "agent",
|
|
5717
5700
|
tasks: args.tasks
|
|
5718
5701
|
});
|
|
5719
|
-
const
|
|
5702
|
+
const planId = result.plan.id;
|
|
5703
|
+
const { workspaceSlug, projectSlug } = result;
|
|
5704
|
+
const planUrl = workspaceSlug && projectSlug ? `https://app.gethmy.com/${workspaceSlug}/${projectSlug}/plans/${planId}` : `https://app.gethmy.com/plans/${planId}`;
|
|
5720
5705
|
return {
|
|
5721
5706
|
success: true,
|
|
5722
|
-
planId
|
|
5707
|
+
planId,
|
|
5723
5708
|
planUrl,
|
|
5724
5709
|
plan: result.plan,
|
|
5725
5710
|
tasks: result.tasks
|
package/dist/lib/api-client.js
CHANGED
|
@@ -17,7 +17,6 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
|
17
17
|
// src/prompt-builder.ts
|
|
18
18
|
var exports_prompt_builder = {};
|
|
19
19
|
__export(exports_prompt_builder, {
|
|
20
|
-
proposePromptVariant: () => proposePromptVariant,
|
|
21
20
|
inferCategoryFromLabels: () => inferCategoryFromLabels,
|
|
22
21
|
getRoleFraming: () => getRoleFraming,
|
|
23
22
|
getAvailableVariants: () => getAvailableVariants,
|
|
@@ -334,26 +333,7 @@ function getAvailableCategories() {
|
|
|
334
333
|
function getAvailableVariants() {
|
|
335
334
|
return ["analysis", "draft", "execute"];
|
|
336
335
|
}
|
|
337
|
-
|
|
338
|
-
if (!contentHash)
|
|
339
|
-
return null;
|
|
340
|
-
const cohort = await fetchCohort(contentHash);
|
|
341
|
-
if (!cohort || cohort.length < VARIANT_MIN_COHORT)
|
|
342
|
-
return null;
|
|
343
|
-
const completed = cohort.filter((r) => r.status === "completed" && (r.progressPercent ?? 0) >= 100 && !r.hadBlockers).length;
|
|
344
|
-
const completionRate = completed / cohort.length;
|
|
345
|
-
if (completionRate >= VARIANT_COMPLETION_THRESHOLD)
|
|
346
|
-
return null;
|
|
347
|
-
const blockerRate = cohort.filter((r) => r.hadBlockers).length / cohort.length;
|
|
348
|
-
const framingHint = blockerRate >= 0.4 ? "Cohort hits frequent blockers — try a more diagnostic framing (require root-cause + repro before any fix)." : "Cohort frequently stalls without finishing — try a more action-forcing framing (smaller subtasks, explicit DoD checklist).";
|
|
349
|
-
return {
|
|
350
|
-
contentHash,
|
|
351
|
-
cohortSize: cohort.length,
|
|
352
|
-
completionRate,
|
|
353
|
-
framingHint
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
var PROMPT_TEMPLATE_VERSION = 1, LABEL_CATEGORY_MAP, DEFAULT_ROLE_FRAMINGS, VARIANT_INSTRUCTIONS, VARIANT_MIN_COHORT = 10, VARIANT_COMPLETION_THRESHOLD = 0.4;
|
|
336
|
+
var PROMPT_TEMPLATE_VERSION = 1, LABEL_CATEGORY_MAP, DEFAULT_ROLE_FRAMINGS, VARIANT_INSTRUCTIONS;
|
|
357
337
|
var init_prompt_builder = __esm(() => {
|
|
358
338
|
LABEL_CATEGORY_MAP = {
|
|
359
339
|
bug: "bug",
|
|
@@ -1463,16 +1443,6 @@ class HarmonyApiClient {
|
|
|
1463
1443
|
async recordPromptHistory(data) {
|
|
1464
1444
|
return this.request("POST", "/prompt-history", data);
|
|
1465
1445
|
}
|
|
1466
|
-
async recordPromptHistoryFeedback(sessionId, outcome) {
|
|
1467
|
-
return this.request("POST", "/prompt-history/feedback", {
|
|
1468
|
-
sessionId,
|
|
1469
|
-
outcome
|
|
1470
|
-
});
|
|
1471
|
-
}
|
|
1472
|
-
async getPromptHistoryCohort(contentHash) {
|
|
1473
|
-
const params = new URLSearchParams({ content_hash: contentHash });
|
|
1474
|
-
return this.request("GET", `/prompt-history/cohort?${params.toString()}`);
|
|
1475
|
-
}
|
|
1476
1446
|
async generateCardPrompt(options) {
|
|
1477
1447
|
const { generatePrompt: generatePrompt2 } = await loadPromptModules();
|
|
1478
1448
|
const cardResult = await this.getCard(options.cardId);
|
|
@@ -1577,6 +1547,23 @@ ${section}`;
|
|
|
1577
1547
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1578
1548
|
console.debug(`[generateCardPrompt] comments fetch failed: ${msg}`);
|
|
1579
1549
|
}
|
|
1550
|
+
if (cardData.plan_id) {
|
|
1551
|
+
try {
|
|
1552
|
+
const { plan } = await this.getPlan(cardData.plan_id);
|
|
1553
|
+
const planContent = plan?.content;
|
|
1554
|
+
if (planContent?.trim()) {
|
|
1555
|
+
result.prompt = `${result.prompt}
|
|
1556
|
+
|
|
1557
|
+
## Approved Plan
|
|
1558
|
+
This card has an approved implementation plan. Follow it unless you find a concrete reason it is wrong — if you must diverge, say so in a comment and explain why.
|
|
1559
|
+
|
|
1560
|
+
${planContent.trim()}`;
|
|
1561
|
+
}
|
|
1562
|
+
} catch (err) {
|
|
1563
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1564
|
+
console.debug(`[generateCardPrompt] plan fetch failed: ${msg}`);
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1580
1567
|
try {
|
|
1581
1568
|
await this.recordPromptHistory({
|
|
1582
1569
|
cardId: cardData.id,
|
|
@@ -1586,11 +1573,7 @@ ${section}`;
|
|
|
1586
1573
|
assemblyId: result.assemblyId ?? null,
|
|
1587
1574
|
tokenEstimate: result.tokenEstimate,
|
|
1588
1575
|
contextSummary: result.contextSummary
|
|
1589
|
-
}
|
|
1590
|
-
sessionId: options.sessionId ?? null,
|
|
1591
|
-
contentHash: result.contentHash,
|
|
1592
|
-
templateVersion: result.version,
|
|
1593
|
-
confidence: 0.5
|
|
1576
|
+
}
|
|
1594
1577
|
});
|
|
1595
1578
|
} catch (err) {
|
|
1596
1579
|
const msg = err instanceof Error ? err.message : String(err);
|
package/package.json
CHANGED
package/src/api-client.ts
CHANGED
|
@@ -486,10 +486,12 @@ export class HarmonyApiClient {
|
|
|
486
486
|
description?: string;
|
|
487
487
|
priority?: string;
|
|
488
488
|
assigneeId?: string | null;
|
|
489
|
+
assignedAgentId?: string | null;
|
|
489
490
|
dueDate?: string | null;
|
|
490
491
|
done?: boolean;
|
|
491
492
|
archivedAt?: string | null;
|
|
492
493
|
planId?: string | null;
|
|
494
|
+
needsPlanRefresh?: boolean;
|
|
493
495
|
},
|
|
494
496
|
): Promise<{ card: unknown }> {
|
|
495
497
|
return this.request("PATCH", `/cards/${cardId}`, updates);
|
|
@@ -734,6 +736,10 @@ export class HarmonyApiClient {
|
|
|
734
736
|
| "budget"
|
|
735
737
|
| "timeout"
|
|
736
738
|
| "stale"
|
|
739
|
+
| "rate_limit"
|
|
740
|
+
| "out_of_credits"
|
|
741
|
+
| "usage_limit"
|
|
742
|
+
| "auth"
|
|
737
743
|
| "other";
|
|
738
744
|
failureSummary?: string;
|
|
739
745
|
recoveryBranch?: string;
|
|
@@ -762,6 +768,10 @@ export class HarmonyApiClient {
|
|
|
762
768
|
| "budget"
|
|
763
769
|
| "timeout"
|
|
764
770
|
| "stale"
|
|
771
|
+
| "rate_limit"
|
|
772
|
+
| "out_of_credits"
|
|
773
|
+
| "usage_limit"
|
|
774
|
+
| "auth"
|
|
765
775
|
| "other";
|
|
766
776
|
failureSummary?: string;
|
|
767
777
|
recoveryBranch?: string;
|
|
@@ -1215,7 +1225,12 @@ export class HarmonyApiClient {
|
|
|
1215
1225
|
status?: "pending" | "in_progress" | "completed";
|
|
1216
1226
|
}>;
|
|
1217
1227
|
},
|
|
1218
|
-
): Promise<{
|
|
1228
|
+
): Promise<{
|
|
1229
|
+
plan: unknown;
|
|
1230
|
+
tasks?: unknown[];
|
|
1231
|
+
projectSlug?: string | null;
|
|
1232
|
+
workspaceSlug?: string | null;
|
|
1233
|
+
}> {
|
|
1219
1234
|
return this.request("POST", "/plans", { projectId, ...data });
|
|
1220
1235
|
}
|
|
1221
1236
|
|
|
@@ -1322,37 +1337,12 @@ export class HarmonyApiClient {
|
|
|
1322
1337
|
generatedPrompt: string;
|
|
1323
1338
|
variant: "analysis" | "draft" | "execute";
|
|
1324
1339
|
contextIncluded?: Record<string, unknown>;
|
|
1325
|
-
sessionId?: string | null;
|
|
1326
|
-
contentHash?: string;
|
|
1327
|
-
templateVersion?: number;
|
|
1328
|
-
confidence?: number;
|
|
1329
1340
|
templateId?: string | null;
|
|
1330
1341
|
isPinned?: boolean;
|
|
1331
1342
|
}): Promise<{ entry: unknown }> {
|
|
1332
1343
|
return this.request("POST", "/prompt-history", data);
|
|
1333
1344
|
}
|
|
1334
1345
|
|
|
1335
|
-
async recordPromptHistoryFeedback(
|
|
1336
|
-
sessionId: string,
|
|
1337
|
-
outcome: "success" | "blocker" | "neutral",
|
|
1338
|
-
): Promise<{ adjusted: number }> {
|
|
1339
|
-
return this.request("POST", "/prompt-history/feedback", {
|
|
1340
|
-
sessionId,
|
|
1341
|
-
outcome,
|
|
1342
|
-
});
|
|
1343
|
-
}
|
|
1344
|
-
|
|
1345
|
-
async getPromptHistoryCohort(contentHash: string): Promise<{
|
|
1346
|
-
cohort: Array<{
|
|
1347
|
-
status: string | null;
|
|
1348
|
-
progressPercent: number | null;
|
|
1349
|
-
hadBlockers: boolean;
|
|
1350
|
-
}>;
|
|
1351
|
-
}> {
|
|
1352
|
-
const params = new URLSearchParams({ content_hash: contentHash });
|
|
1353
|
-
return this.request("GET", `/prompt-history/cohort?${params.toString()}`);
|
|
1354
|
-
}
|
|
1355
|
-
|
|
1356
1346
|
// ============ PROMPT GENERATION ============
|
|
1357
1347
|
|
|
1358
1348
|
/**
|
|
@@ -1373,8 +1363,6 @@ export class HarmonyApiClient {
|
|
|
1373
1363
|
includeLinks: boolean;
|
|
1374
1364
|
includeDescription: boolean;
|
|
1375
1365
|
}>;
|
|
1376
|
-
/** Optional active session ID to associate with the prompt snapshot. */
|
|
1377
|
-
sessionId?: string | null;
|
|
1378
1366
|
}): Promise<{
|
|
1379
1367
|
prompt: string;
|
|
1380
1368
|
variant: string;
|
|
@@ -1546,8 +1534,30 @@ export class HarmonyApiClient {
|
|
|
1546
1534
|
console.debug(`[generateCardPrompt] comments fetch failed: ${msg}`);
|
|
1547
1535
|
}
|
|
1548
1536
|
|
|
1549
|
-
//
|
|
1550
|
-
//
|
|
1537
|
+
// Surface a linked plan's full markdown (e.g. a plan the agent daemon
|
|
1538
|
+
// produced and a human approved in gated mode, or any plan attached via
|
|
1539
|
+
// cards.plan_id). This is the central injection point so EVERY caller —
|
|
1540
|
+
// daemon implement run, Claude Code, in-app builder — sees the approved
|
|
1541
|
+
// approach, not just a reference. Best-effort: never fail prompt generation
|
|
1542
|
+
// on a plan fetch error.
|
|
1543
|
+
if (cardData.plan_id) {
|
|
1544
|
+
try {
|
|
1545
|
+
const { plan } = await this.getPlan(cardData.plan_id);
|
|
1546
|
+
const planContent = (plan as { content?: string | null } | null)
|
|
1547
|
+
?.content;
|
|
1548
|
+
if (planContent?.trim()) {
|
|
1549
|
+
result.prompt = `${result.prompt}\n\n## Approved Plan\nThis card has an approved implementation plan. Follow it unless you find a concrete reason it is wrong — if you must diverge, say so in a comment and explain why.\n\n${planContent.trim()}`;
|
|
1550
|
+
}
|
|
1551
|
+
} catch (err) {
|
|
1552
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1553
|
+
console.debug(`[generateCardPrompt] plan fetch failed: ${msg}`);
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
// Persist a snapshot of the generated prompt. Best-effort — never fail
|
|
1558
|
+
// prompt generation just because logging didn't land. (The AGP-era
|
|
1559
|
+
// session_id / content_hash / template_version / confidence columns were
|
|
1560
|
+
// dropped in migration 20260508000000 — card #329.)
|
|
1551
1561
|
try {
|
|
1552
1562
|
await this.recordPromptHistory({
|
|
1553
1563
|
cardId: cardData.id,
|
|
@@ -1558,10 +1568,6 @@ export class HarmonyApiClient {
|
|
|
1558
1568
|
tokenEstimate: result.tokenEstimate,
|
|
1559
1569
|
contextSummary: result.contextSummary,
|
|
1560
1570
|
},
|
|
1561
|
-
sessionId: options.sessionId ?? null,
|
|
1562
|
-
contentHash: result.contentHash,
|
|
1563
|
-
templateVersion: result.version,
|
|
1564
|
-
confidence: 0.5,
|
|
1565
1571
|
});
|
|
1566
1572
|
} catch (err) {
|
|
1567
1573
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -1610,6 +1616,8 @@ interface CardPromptData {
|
|
|
1610
1616
|
}>;
|
|
1611
1617
|
column_id?: string;
|
|
1612
1618
|
project_id?: string;
|
|
1619
|
+
/** Linked plan (e.g. an agent-approved plan). Surfaced in the prompt. */
|
|
1620
|
+
plan_id?: string | null;
|
|
1613
1621
|
}
|
|
1614
1622
|
|
|
1615
1623
|
interface MemoryItem {
|
package/src/prompt-builder.ts
CHANGED
|
@@ -773,70 +773,3 @@ export function getAvailableCategories(): LabelCategory[] {
|
|
|
773
773
|
export function getAvailableVariants(): PromptVariant[] {
|
|
774
774
|
return ["analysis", "draft", "execute"];
|
|
775
775
|
}
|
|
776
|
-
|
|
777
|
-
// ─── Variant proposal (logged-only — no auto-commit) ──────────────────
|
|
778
|
-
|
|
779
|
-
/** Cohort row shape consumed by {@link proposePromptVariant}. */
|
|
780
|
-
export interface PromptCohortRow {
|
|
781
|
-
/** Final agent session status — only "completed" is treated as success. */
|
|
782
|
-
status: string | null;
|
|
783
|
-
/** Final progress percent recorded on the linked session, when present. */
|
|
784
|
-
progressPercent: number | null;
|
|
785
|
-
/** Whether the linked session ended with non-empty blockers. */
|
|
786
|
-
hadBlockers: boolean;
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
export interface PromptVariantSuggestion {
|
|
790
|
-
contentHash: string;
|
|
791
|
-
cohortSize: number;
|
|
792
|
-
completionRate: number;
|
|
793
|
-
framingHint: string;
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
const VARIANT_MIN_COHORT = 10;
|
|
797
|
-
const VARIANT_COMPLETION_THRESHOLD = 0.4;
|
|
798
|
-
|
|
799
|
-
/**
|
|
800
|
-
* Propose an alternative framing for prompts with a given content hash, based
|
|
801
|
-
* on observed session outcomes. Returns null when the cohort is too small or
|
|
802
|
-
* the completion rate is acceptable.
|
|
803
|
-
*
|
|
804
|
-
* Per the AGP-P2 locked decision, this is logged-only — callers may surface
|
|
805
|
-
* the suggestion to humans, but no auto-commit of new templates is allowed.
|
|
806
|
-
*
|
|
807
|
-
* @param fetchCohort — async loader that returns one row per session that
|
|
808
|
-
* consumed a prompt with this hash. Keeps this module decoupled from the
|
|
809
|
-
* API client so it stays pure-testable.
|
|
810
|
-
*/
|
|
811
|
-
export async function proposePromptVariant(
|
|
812
|
-
contentHash: string,
|
|
813
|
-
fetchCohort: (hash: string) => Promise<PromptCohortRow[]>,
|
|
814
|
-
): Promise<PromptVariantSuggestion | null> {
|
|
815
|
-
if (!contentHash) return null;
|
|
816
|
-
const cohort = await fetchCohort(contentHash);
|
|
817
|
-
if (!cohort || cohort.length < VARIANT_MIN_COHORT) return null;
|
|
818
|
-
|
|
819
|
-
const completed = cohort.filter(
|
|
820
|
-
(r) =>
|
|
821
|
-
r.status === "completed" &&
|
|
822
|
-
(r.progressPercent ?? 0) >= 100 &&
|
|
823
|
-
!r.hadBlockers,
|
|
824
|
-
).length;
|
|
825
|
-
const completionRate = completed / cohort.length;
|
|
826
|
-
|
|
827
|
-
if (completionRate >= VARIANT_COMPLETION_THRESHOLD) return null;
|
|
828
|
-
|
|
829
|
-
const blockerRate =
|
|
830
|
-
cohort.filter((r) => r.hadBlockers).length / cohort.length;
|
|
831
|
-
const framingHint =
|
|
832
|
-
blockerRate >= 0.4
|
|
833
|
-
? "Cohort hits frequent blockers — try a more diagnostic framing (require root-cause + repro before any fix)."
|
|
834
|
-
: "Cohort frequently stalls without finishing — try a more action-forcing framing (smaller subtasks, explicit DoD checklist).";
|
|
835
|
-
|
|
836
|
-
return {
|
|
837
|
-
contentHash,
|
|
838
|
-
cohortSize: cohort.length,
|
|
839
|
-
completionRate,
|
|
840
|
-
framingHint,
|
|
841
|
-
};
|
|
842
|
-
}
|
package/src/server.ts
CHANGED
|
@@ -295,7 +295,8 @@ export const TOOLS = {
|
|
|
295
295
|
title: { type: "string", description: "Card title" },
|
|
296
296
|
columnId: {
|
|
297
297
|
type: "string",
|
|
298
|
-
description:
|
|
298
|
+
description:
|
|
299
|
+
"Target column ID (optional, defaults to the project's default column, or the first column if none is set)",
|
|
299
300
|
},
|
|
300
301
|
projectId: {
|
|
301
302
|
type: "string",
|
|
@@ -3644,12 +3645,22 @@ async function handleToolCall(
|
|
|
3644
3645
|
| undefined,
|
|
3645
3646
|
});
|
|
3646
3647
|
|
|
3647
|
-
// Build URL for viewing the plan
|
|
3648
|
-
|
|
3648
|
+
// Build URL for viewing the plan. The real route is project-scoped
|
|
3649
|
+
// (/{workspaceSlug}/{projectSlug}/plans/{planId}); fall back to the flat
|
|
3650
|
+
// path only if the API didn't return slugs (older deployments).
|
|
3651
|
+
const planId = (result.plan as { id: string }).id;
|
|
3652
|
+
const { workspaceSlug, projectSlug } = result as {
|
|
3653
|
+
workspaceSlug?: string | null;
|
|
3654
|
+
projectSlug?: string | null;
|
|
3655
|
+
};
|
|
3656
|
+
const planUrl =
|
|
3657
|
+
workspaceSlug && projectSlug
|
|
3658
|
+
? `https://app.gethmy.com/${workspaceSlug}/${projectSlug}/plans/${planId}`
|
|
3659
|
+
: `https://app.gethmy.com/plans/${planId}`;
|
|
3649
3660
|
|
|
3650
3661
|
return {
|
|
3651
3662
|
success: true,
|
|
3652
|
-
planId
|
|
3663
|
+
planId,
|
|
3653
3664
|
planUrl,
|
|
3654
3665
|
plan: result.plan,
|
|
3655
3666
|
tasks: result.tasks,
|