maskweaver 0.9.9 → 0.10.1
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/config-manager/npm-dist-tags.d.ts +7 -0
- package/dist/cli/config-manager/npm-dist-tags.js +15 -0
- package/dist/cli/config-manager/plugin-name-with-version.d.ts +1 -0
- package/dist/cli/config-manager/plugin-name-with-version.js +21 -0
- package/dist/cli/config-manager/version-compatibility.d.ts +9 -0
- package/dist/cli/config-manager/version-compatibility.js +81 -0
- package/dist/cli/install.js +26 -11
- package/dist/plugin/index.js +80 -41
- package/dist/plugin/tools/weave.js +124 -8
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/weave/stages/intake-ambiguity.d.ts +5 -0
- package/dist/weave/stages/intake-ambiguity.js +200 -0
- package/dist/weave/stages/intake-persistence.d.ts +9 -0
- package/dist/weave/stages/intake-persistence.js +66 -0
- package/dist/weave/stages/intake-questions.d.ts +6 -0
- package/dist/weave/stages/intake-questions.js +195 -0
- package/dist/weave/stages/intake-types.d.ts +116 -0
- package/dist/weave/stages/intake-types.js +1 -0
- package/dist/weave/stages/intake.d.ts +8 -55
- package/dist/weave/stages/intake.js +112 -128
- package/dist/weave/stages/plan.js +11 -1
- package/package.json +1 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const NPM_FETCH_TIMEOUT_MS = 5000;
|
|
2
|
+
export async function fetchNpmDistTags(packageName) {
|
|
3
|
+
try {
|
|
4
|
+
const res = await fetch(`https://registry.npmjs.org/-/package/${encodeURIComponent(packageName)}/dist-tags`, {
|
|
5
|
+
signal: AbortSignal.timeout(NPM_FETCH_TIMEOUT_MS),
|
|
6
|
+
});
|
|
7
|
+
if (!res.ok)
|
|
8
|
+
return null;
|
|
9
|
+
const data = (await res.json());
|
|
10
|
+
return data;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getPluginNameWithVersion(currentVersion: string, packageName: string): Promise<string>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { fetchNpmDistTags } from "./npm-dist-tags.js";
|
|
2
|
+
const PRIORITIZED_TAGS = ["latest", "beta", "next"];
|
|
3
|
+
function getFallbackEntry(version, packageName) {
|
|
4
|
+
const prereleaseMatch = version.match(/-([a-zA-Z][a-zA-Z0-9-]*)(?:\.|$)/);
|
|
5
|
+
if (prereleaseMatch) {
|
|
6
|
+
return `${packageName}@${prereleaseMatch[1]}`;
|
|
7
|
+
}
|
|
8
|
+
return packageName;
|
|
9
|
+
}
|
|
10
|
+
export async function getPluginNameWithVersion(currentVersion, packageName) {
|
|
11
|
+
const distTags = await fetchNpmDistTags(packageName);
|
|
12
|
+
if (distTags) {
|
|
13
|
+
const allTags = new Set([...PRIORITIZED_TAGS, ...Object.keys(distTags)]);
|
|
14
|
+
for (const tag of allTags) {
|
|
15
|
+
if (distTags[tag] === currentVersion) {
|
|
16
|
+
return `${packageName}@${tag}`;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return getFallbackEntry(currentVersion, packageName);
|
|
21
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface VersionCompatibility {
|
|
2
|
+
canUpgrade: boolean;
|
|
3
|
+
reason?: string;
|
|
4
|
+
isDowngrade: boolean;
|
|
5
|
+
isMajorBump: boolean;
|
|
6
|
+
requiresMigration: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function checkVersionCompatibility(currentVersion: string | null, newVersion: string): VersionCompatibility;
|
|
9
|
+
export declare function extractVersionFromPluginEntry(entry: string): string | null;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
function parseVersion(version) {
|
|
2
|
+
const clean = version.replace(/^v/, "").split("-")[0];
|
|
3
|
+
return clean.split(".").map(Number);
|
|
4
|
+
}
|
|
5
|
+
function compareVersions(a, b) {
|
|
6
|
+
const partsA = parseVersion(a);
|
|
7
|
+
const partsB = parseVersion(b);
|
|
8
|
+
const maxLen = Math.max(partsA.length, partsB.length);
|
|
9
|
+
for (let i = 0; i < maxLen; i++) {
|
|
10
|
+
const numA = partsA[i] ?? 0;
|
|
11
|
+
const numB = partsB[i] ?? 0;
|
|
12
|
+
if (numA !== numB) {
|
|
13
|
+
return numA - numB;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
18
|
+
export function checkVersionCompatibility(currentVersion, newVersion) {
|
|
19
|
+
if (!currentVersion) {
|
|
20
|
+
return {
|
|
21
|
+
canUpgrade: true,
|
|
22
|
+
isDowngrade: false,
|
|
23
|
+
isMajorBump: false,
|
|
24
|
+
requiresMigration: false,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const cleanCurrent = currentVersion.replace(/^v/, "");
|
|
28
|
+
const cleanNew = newVersion.replace(/^v/, "");
|
|
29
|
+
try {
|
|
30
|
+
const comparison = compareVersions(cleanNew, cleanCurrent);
|
|
31
|
+
if (comparison < 0) {
|
|
32
|
+
return {
|
|
33
|
+
canUpgrade: false,
|
|
34
|
+
reason: `Downgrade from ${currentVersion} to ${newVersion} is not allowed`,
|
|
35
|
+
isDowngrade: true,
|
|
36
|
+
isMajorBump: false,
|
|
37
|
+
requiresMigration: false,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
if (comparison === 0) {
|
|
41
|
+
return {
|
|
42
|
+
canUpgrade: true,
|
|
43
|
+
reason: `Version ${newVersion} is already installed`,
|
|
44
|
+
isDowngrade: false,
|
|
45
|
+
isMajorBump: false,
|
|
46
|
+
requiresMigration: false,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const currentMajor = cleanCurrent.split(".")[0];
|
|
50
|
+
const newMajor = cleanNew.split(".")[0];
|
|
51
|
+
const isMajorBump = currentMajor !== newMajor;
|
|
52
|
+
if (isMajorBump) {
|
|
53
|
+
return {
|
|
54
|
+
canUpgrade: true,
|
|
55
|
+
reason: `Major version upgrade from ${currentVersion} to ${newVersion} - configuration migration may be required`,
|
|
56
|
+
isDowngrade: false,
|
|
57
|
+
isMajorBump: true,
|
|
58
|
+
requiresMigration: true,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
canUpgrade: true,
|
|
63
|
+
isDowngrade: false,
|
|
64
|
+
isMajorBump: false,
|
|
65
|
+
requiresMigration: false,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return {
|
|
70
|
+
canUpgrade: true,
|
|
71
|
+
reason: `Unable to compare versions ${currentVersion} and ${newVersion} - proceeding with caution`,
|
|
72
|
+
isDowngrade: false,
|
|
73
|
+
isMajorBump: false,
|
|
74
|
+
requiresMigration: false,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export function extractVersionFromPluginEntry(entry) {
|
|
79
|
+
const match = entry.match(/@(.+)$/);
|
|
80
|
+
return match ? match[1] : null;
|
|
81
|
+
}
|
package/dist/cli/install.js
CHANGED
|
@@ -7,8 +7,10 @@ import { VERSION } from "../version.js";
|
|
|
7
7
|
import { runDoctor, printDoctorReport } from "./doctor.js";
|
|
8
8
|
import { writeDefaultRuntimeConfig, writeDefaultPluginConfig } from "../shared/generate-agents.js";
|
|
9
9
|
const PLUGIN_NAME = "maskweaver";
|
|
10
|
+
const PLUGIN_LATEST = "maskweaver@latest";
|
|
10
11
|
const LEGACY_PLUGIN_NAMES = ["maskweaver/plugin", "@maskweaver/plugin"];
|
|
11
|
-
const ALL_PLUGIN_NAMES = [PLUGIN_NAME, ...LEGACY_PLUGIN_NAMES];
|
|
12
|
+
const ALL_PLUGIN_NAMES = [PLUGIN_NAME, PLUGIN_LATEST, ...LEGACY_PLUGIN_NAMES];
|
|
13
|
+
const ALL_BARE_OR_VERSIONED = [PLUGIN_NAME, PLUGIN_LATEST];
|
|
12
14
|
const CONFIG_NAME = "opencode.json";
|
|
13
15
|
const colors = {
|
|
14
16
|
reset: "\x1b[0m",
|
|
@@ -83,19 +85,31 @@ function getInstalledPluginEntries(config) {
|
|
|
83
85
|
return config.plugin.filter((plugin) => ALL_PLUGIN_NAMES.includes(plugin));
|
|
84
86
|
}
|
|
85
87
|
function isPluginInstalled(config) {
|
|
86
|
-
|
|
88
|
+
if (!config.plugin || !Array.isArray(config.plugin))
|
|
89
|
+
return false;
|
|
90
|
+
return config.plugin.some((p) => ALL_BARE_OR_VERSIONED.some(name => p === name || p.startsWith(`${name}@`)));
|
|
87
91
|
}
|
|
88
92
|
function normalizePluginEntries(config) {
|
|
89
93
|
if (!config.plugin || !Array.isArray(config.plugin)) {
|
|
90
94
|
return { migratedFrom: [], changed: false };
|
|
91
95
|
}
|
|
92
96
|
const migratedFrom = config.plugin.filter((plugin) => LEGACY_PLUGIN_NAMES.includes(plugin));
|
|
93
|
-
|
|
97
|
+
const hasBare = config.plugin.some((p) => p === PLUGIN_NAME);
|
|
98
|
+
const hasVersioned = config.plugin.some((p) => p.startsWith(`${PLUGIN_NAME}@`));
|
|
99
|
+
if (migratedFrom.length === 0 && !hasBare && !hasVersioned) {
|
|
100
|
+
if (config.plugin.some((p) => p === PLUGIN_LATEST)) {
|
|
101
|
+
return { migratedFrom: [], changed: false };
|
|
102
|
+
}
|
|
94
103
|
return { migratedFrom: [], changed: false };
|
|
95
104
|
}
|
|
96
|
-
const preserved = config.plugin.filter((plugin) => !ALL_PLUGIN_NAMES.includes(plugin)
|
|
97
|
-
|
|
98
|
-
|
|
105
|
+
const preserved = config.plugin.filter((plugin) => !ALL_PLUGIN_NAMES.includes(plugin) &&
|
|
106
|
+
!plugin.startsWith(`${PLUGIN_NAME}@`));
|
|
107
|
+
const existingVersioned = config.plugin.find((p) => p.startsWith(`${PLUGIN_NAME}@`));
|
|
108
|
+
config.plugin = [...preserved, existingVersioned || PLUGIN_LATEST];
|
|
109
|
+
if (migratedFrom.length > 0 || hasBare) {
|
|
110
|
+
return { migratedFrom: [...migratedFrom, hasBare ? PLUGIN_NAME : ""].filter(Boolean), changed: true };
|
|
111
|
+
}
|
|
112
|
+
return { migratedFrom: [], changed: false };
|
|
99
113
|
}
|
|
100
114
|
async function installPlugin(options) {
|
|
101
115
|
const isLocal = options.local || false;
|
|
@@ -120,13 +134,14 @@ async function installPlugin(options) {
|
|
|
120
134
|
log("");
|
|
121
135
|
return;
|
|
122
136
|
}
|
|
123
|
-
|
|
137
|
+
const alreadyInstalled = config.plugin.some((p) => p === PLUGIN_NAME || p.startsWith(`${PLUGIN_NAME}@`));
|
|
138
|
+
if (alreadyInstalled) {
|
|
124
139
|
warning(`플러그인이 이미 설치되어 있습니다.`);
|
|
125
|
-
info(`현재 ${scope} 설정에 등록되어 있습니다: ${
|
|
140
|
+
info(`현재 ${scope} 설정에 등록되어 있습니다: ${PLUGIN_LATEST}`);
|
|
126
141
|
log("");
|
|
127
142
|
return;
|
|
128
143
|
}
|
|
129
|
-
config.plugin.push(
|
|
144
|
+
config.plugin.push(PLUGIN_LATEST);
|
|
130
145
|
writeConfig(configPath, config);
|
|
131
146
|
let createdRuntimeConfig = false;
|
|
132
147
|
let createdPluginConfig = false;
|
|
@@ -150,7 +165,7 @@ async function installPlugin(options) {
|
|
|
150
165
|
}
|
|
151
166
|
success(`플러그인이 성공적으로 설치되었습니다!`);
|
|
152
167
|
log("");
|
|
153
|
-
log(`📦 설치된 플러그인: ${
|
|
168
|
+
log(`📦 설치된 플러그인: ${PLUGIN_LATEST}`, "bright");
|
|
154
169
|
log(`📍 위치: ${configPath}`, "dim");
|
|
155
170
|
log("");
|
|
156
171
|
log(`다음 단계:`, "bright");
|
|
@@ -190,7 +205,7 @@ async function uninstallPlugin(options) {
|
|
|
190
205
|
log("");
|
|
191
206
|
return;
|
|
192
207
|
}
|
|
193
|
-
config.plugin = config.plugin.filter((p) => !ALL_PLUGIN_NAMES.includes(p));
|
|
208
|
+
config.plugin = config.plugin.filter((p) => !ALL_PLUGIN_NAMES.includes(p) && !p.startsWith(`${PLUGIN_NAME}@`));
|
|
194
209
|
writeConfig(configPath, config);
|
|
195
210
|
success(`플러그인이 성공적으로 제거되었습니다.`);
|
|
196
211
|
log("");
|
package/dist/plugin/index.js
CHANGED
|
@@ -5,6 +5,8 @@ import * as os from 'node:os';
|
|
|
5
5
|
import { spawnSync } from 'node:child_process';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { VERSION } from '../version.js';
|
|
8
|
+
import { fetchNpmDistTags } from '../cli/config-manager/npm-dist-tags.js';
|
|
9
|
+
import { checkVersionCompatibility } from '../cli/config-manager/version-compatibility.js';
|
|
8
10
|
import { loadPluginConfig, isMaskEnabled, isToolEnabled, getDefaultMask, isAutoActivateEnabled, isVerboseLoggingEnabled, isCompletionSoundEnabled, validateConfig, } from './config/index.js';
|
|
9
11
|
import { createMemorySearchTool } from './tools/memorySearch.js';
|
|
10
12
|
import { createMemoryWriteTool } from './tools/memoryWrite.js';
|
|
@@ -595,10 +597,10 @@ function playCompletionSound(config) {
|
|
|
595
597
|
}
|
|
596
598
|
let state = null;
|
|
597
599
|
const OPENCODE_PACKAGES_DIR = path.join(os.homedir(), '.cache', 'opencode', 'packages');
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
600
|
+
function getCachedPackageVersion() {
|
|
601
|
+
if (!fs.existsSync(OPENCODE_PACKAGES_DIR)) {
|
|
602
|
+
return { version: null, pkgDir: null };
|
|
603
|
+
}
|
|
602
604
|
const entries = fs.readdirSync(OPENCODE_PACKAGES_DIR);
|
|
603
605
|
for (const entry of entries) {
|
|
604
606
|
if (!entry.startsWith('maskweaver@'))
|
|
@@ -615,51 +617,88 @@ function getCacheVersion() {
|
|
|
615
617
|
continue;
|
|
616
618
|
}
|
|
617
619
|
}
|
|
618
|
-
return null;
|
|
620
|
+
return { version: null, pkgDir: null };
|
|
619
621
|
}
|
|
620
|
-
function
|
|
621
|
-
|
|
622
|
-
const result = spawnSync('npm', ['view', 'maskweaver', 'version'], {
|
|
623
|
-
encoding: 'utf-8',
|
|
624
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
625
|
-
timeout: 10000,
|
|
626
|
-
windowsHide: true,
|
|
627
|
-
});
|
|
628
|
-
if (result.status === 0 && result.stdout) {
|
|
629
|
-
return result.stdout.trim();
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
catch { }
|
|
633
|
-
return null;
|
|
622
|
+
async function fetchLatestFromRegistry() {
|
|
623
|
+
return fetchNpmDistTags('maskweaver');
|
|
634
624
|
}
|
|
635
|
-
function
|
|
636
|
-
const cached =
|
|
637
|
-
|
|
638
|
-
|
|
625
|
+
async function checkForUpdates() {
|
|
626
|
+
const cached = getCachedPackageVersion();
|
|
627
|
+
const installedVersion = cached.version;
|
|
628
|
+
const latestTags = await fetchLatestFromRegistry();
|
|
629
|
+
const latestVersion = latestTags?.latest ?? null;
|
|
630
|
+
if (!installedVersion) {
|
|
631
|
+
return {
|
|
632
|
+
updateAvailable: false,
|
|
633
|
+
installedVersion: null,
|
|
634
|
+
latestVersion,
|
|
635
|
+
isDowngrade: false,
|
|
636
|
+
isMajorBump: false,
|
|
637
|
+
requiresMigration: false,
|
|
638
|
+
message: null,
|
|
639
|
+
};
|
|
639
640
|
}
|
|
640
|
-
if (
|
|
641
|
-
return {
|
|
641
|
+
if (installedVersion === VERSION) {
|
|
642
|
+
return {
|
|
643
|
+
updateAvailable: false,
|
|
644
|
+
installedVersion,
|
|
645
|
+
latestVersion,
|
|
646
|
+
isDowngrade: false,
|
|
647
|
+
isMajorBump: false,
|
|
648
|
+
requiresMigration: false,
|
|
649
|
+
message: null,
|
|
650
|
+
};
|
|
642
651
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
+
if (!latestVersion || latestVersion === installedVersion) {
|
|
653
|
+
return {
|
|
654
|
+
updateAvailable: false,
|
|
655
|
+
installedVersion,
|
|
656
|
+
latestVersion,
|
|
657
|
+
isDowngrade: false,
|
|
658
|
+
isMajorBump: false,
|
|
659
|
+
requiresMigration: false,
|
|
660
|
+
message: `Plugin cache v${installedVersion} — current is v${VERSION}.`,
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
const compatibility = checkVersionCompatibility(installedVersion, latestVersion);
|
|
664
|
+
const currentIsLatest = installedVersion === latestVersion;
|
|
665
|
+
if (currentIsLatest) {
|
|
666
|
+
return {
|
|
667
|
+
updateAvailable: false,
|
|
668
|
+
installedVersion,
|
|
669
|
+
latestVersion,
|
|
670
|
+
isDowngrade: false,
|
|
671
|
+
isMajorBump: false,
|
|
672
|
+
requiresMigration: false,
|
|
673
|
+
message: `Plugin cache v${installedVersion} — current is v${VERSION}.`,
|
|
674
|
+
};
|
|
652
675
|
}
|
|
653
|
-
return {
|
|
676
|
+
return {
|
|
677
|
+
updateAvailable: true,
|
|
678
|
+
installedVersion,
|
|
679
|
+
latestVersion,
|
|
680
|
+
isDowngrade: compatibility.isDowngrade,
|
|
681
|
+
isMajorBump: compatibility.isMajorBump,
|
|
682
|
+
requiresMigration: compatibility.requiresMigration,
|
|
683
|
+
message: `Update available: v${installedVersion} → v${latestVersion}${compatibility.requiresMigration ? ' (major upgrade — migration may be required)' : ''}.`,
|
|
684
|
+
};
|
|
654
685
|
}
|
|
655
686
|
export const MaskweaverPlugin = async ({ client, directory, project, worktree, $, serverUrl }) => {
|
|
656
|
-
const
|
|
657
|
-
if (
|
|
658
|
-
|
|
659
|
-
|
|
687
|
+
const updateCheck = await checkForUpdates();
|
|
688
|
+
if (updateCheck.updateAvailable) {
|
|
689
|
+
if (updateCheck.isDowngrade) {
|
|
690
|
+
pluginLog(client, 'warn', `Downgrade detected (v${updateCheck.installedVersion} → v${updateCheck.latestVersion}) — not allowed. Please use opencode --upgrade.`);
|
|
691
|
+
}
|
|
692
|
+
else {
|
|
693
|
+
pluginLog(client, 'warn', `Update available: maskweaver v${updateCheck.installedVersion} → v${updateCheck.latestVersion}`);
|
|
694
|
+
if (updateCheck.requiresMigration) {
|
|
695
|
+
pluginLog(client, 'warn', `Major version upgrade — configuration migration may be required.`);
|
|
696
|
+
}
|
|
697
|
+
pluginLog(client, 'info', `Run \`opencode --upgrade\` or \`npm update -g maskweaver\` to update.`);
|
|
698
|
+
}
|
|
660
699
|
}
|
|
661
|
-
else if (
|
|
662
|
-
pluginLog(client, 'info',
|
|
700
|
+
else if (updateCheck.message) {
|
|
701
|
+
pluginLog(client, 'info', updateCheck.message);
|
|
663
702
|
}
|
|
664
703
|
const pluginConfig = loadPluginConfig(directory, { client, verbose: false });
|
|
665
704
|
const configErrors = validateConfig(pluginConfig);
|
|
@@ -17,7 +17,7 @@ import { preparePhaseExecution, formatExecutionPlan, runAIVerification, generate
|
|
|
17
17
|
import { archiveChange } from '../../weave/stages/archive.js';
|
|
18
18
|
import { generateStatusReport, handleUserResponse } from '../../weave/stages/handoff.js';
|
|
19
19
|
import { analyzeCodebase, runGraphifyAnalysis, readMapResult } from '../../weave/stages/map.js';
|
|
20
|
-
import { interview as interviewStage } from '../../weave/stages/intake.js';
|
|
20
|
+
import { interview as interviewStage, listInterviewStates } from '../../weave/stages/intake.js';
|
|
21
21
|
import { executeBuildLoop, generateBuildState, generateBuildId, loadBuildState } from '../../weave/stages/build.js';
|
|
22
22
|
import { WeaveOrchestrator } from '../../weave/orchestrator.js';
|
|
23
23
|
import { getPhaseManager } from '../../weave/phase-manager.js';
|
|
@@ -1778,8 +1778,22 @@ async function handleMap(args, basePath) {
|
|
|
1778
1778
|
async function handleInterview(args, basePath) {
|
|
1779
1779
|
const docsPath = args.docsPath || 'docs';
|
|
1780
1780
|
const lines = [];
|
|
1781
|
+
const existingStates = listInterviewStates(basePath);
|
|
1782
|
+
const canResume = existingStates.some(s => s.status === 'in_progress');
|
|
1781
1783
|
lines.push('## 💬 Interview');
|
|
1782
1784
|
lines.push('');
|
|
1785
|
+
if (canResume && !args.resumeId) {
|
|
1786
|
+
lines.push('### 📋 Existing Interviews');
|
|
1787
|
+
lines.push('');
|
|
1788
|
+
for (const state of existingStates) {
|
|
1789
|
+
const statusIcon = state.status === 'in_progress' ? '🔄' : '✅';
|
|
1790
|
+
lines.push(`- ${statusIcon} \`${state.id}\` — ${state.rounds} rounds, **${state.status}**`);
|
|
1791
|
+
}
|
|
1792
|
+
lines.push('');
|
|
1793
|
+
lines.push('To resume an interview, use: `weave command=interview resumeId="<id>"`');
|
|
1794
|
+
lines.push('To start fresh: `weave command=interview`');
|
|
1795
|
+
lines.push('');
|
|
1796
|
+
}
|
|
1783
1797
|
const mapResult = await readMapResult(basePath);
|
|
1784
1798
|
if (mapResult) {
|
|
1785
1799
|
lines.push(`📍 Map available: \`${mapResult.mapPath}\``);
|
|
@@ -1797,14 +1811,56 @@ async function handleInterview(args, basePath) {
|
|
|
1797
1811
|
docsPath,
|
|
1798
1812
|
basePath,
|
|
1799
1813
|
mapResult,
|
|
1814
|
+
resumeId: args.resumeId,
|
|
1800
1815
|
});
|
|
1801
|
-
lines.push(
|
|
1816
|
+
lines.push('### 📄 Documents Analyzed');
|
|
1802
1817
|
lines.push(`- ${interviewResult.intake.documents.length} document(s) analyzed`);
|
|
1803
1818
|
lines.push(`- ${interviewResult.intake.features.length} feature(s) identified`);
|
|
1804
1819
|
lines.push('');
|
|
1820
|
+
if (interviewResult.intake.features.length > 0) {
|
|
1821
|
+
lines.push('**Features:**');
|
|
1822
|
+
for (const feature of interviewResult.intake.features) {
|
|
1823
|
+
lines.push(` - ${feature}`);
|
|
1824
|
+
}
|
|
1825
|
+
lines.push('');
|
|
1826
|
+
}
|
|
1827
|
+
if (interviewResult.ambiguityScore) {
|
|
1828
|
+
const score = interviewResult.ambiguityScore;
|
|
1829
|
+
const milestoneIcon = score.milestone === 'ready' ? '✅'
|
|
1830
|
+
: score.milestone === 'refined' ? '🟡'
|
|
1831
|
+
: score.milestone === 'progress' ? '🟠'
|
|
1832
|
+
: '🔴';
|
|
1833
|
+
lines.push('### 📊 Ambiguity Score');
|
|
1834
|
+
lines.push('');
|
|
1835
|
+
lines.push(`**${milestoneIcon} ${(score.overallScore * 100).toFixed(1)}% Ambiguity [${score.milestone.toUpperCase()}]**`);
|
|
1836
|
+
lines.push(`_${score.milestoneDescription}_`);
|
|
1837
|
+
lines.push('');
|
|
1838
|
+
lines.push('| Component | Clarity | Weight |');
|
|
1839
|
+
lines.push('|-----------|---------|--------|');
|
|
1840
|
+
const components = [
|
|
1841
|
+
score.breakdown.goalClarity,
|
|
1842
|
+
score.breakdown.constraintClarity,
|
|
1843
|
+
score.breakdown.successCriteriaClarity,
|
|
1844
|
+
];
|
|
1845
|
+
if (score.breakdown.contextClarity) {
|
|
1846
|
+
components.push(score.breakdown.contextClarity);
|
|
1847
|
+
}
|
|
1848
|
+
for (const comp of components) {
|
|
1849
|
+
const bar = '█'.repeat(Math.round(comp.clarityScore * 10)) + '░'.repeat(10 - Math.round(comp.clarityScore * 10));
|
|
1850
|
+
lines.push(`| ${comp.name} | ${bar} ${(comp.clarityScore * 100).toFixed(0)}% | ${(comp.weight * 100).toFixed(0)}% |`);
|
|
1851
|
+
}
|
|
1852
|
+
lines.push('');
|
|
1853
|
+
lines.push(`**Weakest area:** ${score.weakestArea}`);
|
|
1854
|
+
if (score.nextMilestone) {
|
|
1855
|
+
lines.push(`**Next milestone:** ${score.nextMilestone.label} (<= ${(score.nextMilestone.threshold * 100).toFixed(0)}%) — ${score.nextMilestone.description}`);
|
|
1856
|
+
}
|
|
1857
|
+
lines.push('');
|
|
1858
|
+
lines.push(`**Ready for Seed:** ${score.isReadyForSeed ? '✅ Yes' : '❌ No (ambiguity > 20%)'}`);
|
|
1859
|
+
lines.push(`**Ready for Gherkin:** ${score.readinessForGherkin ? '✅ Yes' : '❌ No (ambiguity > 30%)'}`);
|
|
1860
|
+
lines.push('');
|
|
1861
|
+
}
|
|
1805
1862
|
if (interviewResult.intake.structuralChanges && interviewResult.intake.structuralChanges.length > 0) {
|
|
1806
1863
|
lines.push('### ⚠️ Structural Changes Detected');
|
|
1807
|
-
lines.push('The following structural changes were identified from codebase analysis:');
|
|
1808
1864
|
lines.push('');
|
|
1809
1865
|
for (const sc of interviewResult.intake.structuralChanges) {
|
|
1810
1866
|
const icon = sc.breaking ? '🔴' : '🟡';
|
|
@@ -1814,13 +1870,73 @@ async function handleInterview(args, basePath) {
|
|
|
1814
1870
|
lines.push(` - Status: ${sc.agreed ? '✅ Agreed' : '⏳ Pending approval'}`);
|
|
1815
1871
|
}
|
|
1816
1872
|
lines.push('');
|
|
1817
|
-
|
|
1873
|
+
}
|
|
1874
|
+
if (interviewResult.generatedScenarios && interviewResult.generatedScenarios.length > 0) {
|
|
1875
|
+
lines.push('### 🥒 Generated Gherkin Scenarios');
|
|
1818
1876
|
lines.push('');
|
|
1877
|
+
const byFeature = new Map();
|
|
1878
|
+
for (const scenario of interviewResult.generatedScenarios) {
|
|
1879
|
+
const existing = byFeature.get(scenario.feature) || [];
|
|
1880
|
+
existing.push(scenario);
|
|
1881
|
+
byFeature.set(scenario.feature, existing);
|
|
1882
|
+
}
|
|
1883
|
+
for (const [feature, scenarios] of byFeature) {
|
|
1884
|
+
lines.push(`**Feature: ${feature}**`);
|
|
1885
|
+
lines.push('');
|
|
1886
|
+
for (const scenario of scenarios) {
|
|
1887
|
+
lines.push(` \`\`\`gherkin`);
|
|
1888
|
+
lines.push(` Scenario: ${scenario.scenario}`);
|
|
1889
|
+
for (const g of scenario.given)
|
|
1890
|
+
lines.push(` Given ${g}`);
|
|
1891
|
+
for (const w of scenario.when)
|
|
1892
|
+
lines.push(` When ${w}`);
|
|
1893
|
+
for (const t of scenario.then)
|
|
1894
|
+
lines.push(` Then ${t}`);
|
|
1895
|
+
lines.push(` \`\`\``);
|
|
1896
|
+
lines.push('');
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
if (interviewResult.intake.questions.length > 0) {
|
|
1901
|
+
lines.push('### ❓ Questions');
|
|
1902
|
+
lines.push('');
|
|
1903
|
+
lines.push('Answer the following questions to reduce ambiguity and generate better Gherkin scenarios:');
|
|
1904
|
+
lines.push('');
|
|
1905
|
+
for (const q of interviewResult.intake.questions) {
|
|
1906
|
+
const typeLabel = q.questionType === 'gherkin-given' ? '[GIVEN]'
|
|
1907
|
+
: q.questionType === 'gherkin-when' ? '[WHEN]'
|
|
1908
|
+
: q.questionType === 'gherkin-then' ? '[THEN]'
|
|
1909
|
+
: q.questionType === 'edge-case' ? '[EDGE]'
|
|
1910
|
+
: q.questionType === 'constraint' ? '[CONSTRAINT]'
|
|
1911
|
+
: '';
|
|
1912
|
+
lines.push(`**Q${q.id.replace('Q', '')}:** ${typeLabel ? typeLabel + ' ' : ''}${q.question}`);
|
|
1913
|
+
if (q.targetFeature) {
|
|
1914
|
+
lines.push(` → Target: _${q.targetFeature}_`);
|
|
1915
|
+
}
|
|
1916
|
+
if (q.options && q.options.length > 0) {
|
|
1917
|
+
lines.push(` Options: ${q.options.join(' | ')}`);
|
|
1918
|
+
}
|
|
1919
|
+
if (q.required) {
|
|
1920
|
+
lines.push(` ⚠️ _Required_`);
|
|
1921
|
+
}
|
|
1922
|
+
lines.push('');
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
if (interviewResult.isMultiRound) {
|
|
1926
|
+
lines.push('---');
|
|
1927
|
+
lines.push('');
|
|
1928
|
+
lines.push(`**Interview Round ${(interviewResult.interviewState?.currentRound || 1)} complete.**`);
|
|
1929
|
+
lines.push('More questions exist to refine requirements. Answer the remaining questions above and run `weave command=interview` again.');
|
|
1930
|
+
lines.push(`Interview ID: \`${interviewResult.interviewState?.interviewId}\``);
|
|
1931
|
+
lines.push('');
|
|
1932
|
+
}
|
|
1933
|
+
else if (interviewResult.satisfied) {
|
|
1934
|
+
lines.push('---');
|
|
1935
|
+
lines.push('');
|
|
1936
|
+
lines.push('✅ **Requirements are clear. Ready to generate specification and plan.**');
|
|
1937
|
+
lines.push('');
|
|
1938
|
+
lines.push('Next: `weave command=design docsPath="docs/"` to create the plan with full Gherkin acceptance criteria.');
|
|
1819
1939
|
}
|
|
1820
|
-
lines.push('### Questions');
|
|
1821
|
-
lines.push('Review the features and technical requirements above.');
|
|
1822
|
-
lines.push('');
|
|
1823
|
-
lines.push('Next: `weave command=design docsPath="docs/"` to create the plan.');
|
|
1824
1940
|
}
|
|
1825
1941
|
catch (e) {
|
|
1826
1942
|
return `Error during interview: ${e.message || e}`;
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.10.1";
|
|
2
2
|
export declare function getVersionString(): string;
|
package/dist/version.js
CHANGED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { GherkinScenario } from '../types.js';
|
|
2
|
+
import type { AmbiguityScore, IntakeResult } from './intake-types.js';
|
|
3
|
+
export declare const AMBIGUITY_THRESHOLD = 0.2;
|
|
4
|
+
export declare const GHERKIN_READINESS_THRESHOLD = 0.3;
|
|
5
|
+
export declare function scoreAmbiguity(features: string[], techReqs: IntakeResult['technicalRequirements'], answers: Record<string, string>, gherkinScenarios?: GherkinScenario[], isBrownfield?: boolean, codebaseMapAvailable?: boolean): AmbiguityScore;
|