@gajae-code/coding-agent 0.11.8 → 0.11.10
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/CHANGELOG.md +35 -1
- package/dist/types/cli.d.ts +1 -0
- package/dist/types/config/model-profiles.d.ts +2 -3
- package/dist/types/config/model-registry.d.ts +2 -0
- package/dist/types/gjc-runtime/deep-interview-ambiguity.d.ts +12 -5
- package/dist/types/gjc-runtime/deep-interview-draft.d.ts +8 -0
- package/dist/types/gjc-runtime/deep-interview-payload.d.ts +41 -0
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +32 -17
- package/dist/types/gjc-runtime/deep-interview-repair.d.ts +17 -0
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +135 -4
- package/dist/types/gjc-runtime/state-writer.d.ts +45 -0
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +5 -3
- package/dist/types/modes/components/welcome.d.ts +2 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +16 -8
- package/dist/types/runtime-mcp/redaction.d.ts +2 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +3 -1
- package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
- package/dist/types/sdk/bus/index.d.ts +8 -1
- package/dist/types/sdk/bus/operator-runtime.d.ts +1 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +14 -7
- package/dist/types/sdk/bus/telegram-daemon.d.ts +74 -5
- package/dist/types/sdk/session.d.ts +7 -0
- package/dist/types/session/agent-session.d.ts +2 -0
- package/dist/types/session/internal/managed-session-scope.d.ts +13 -3
- package/dist/types/session/internal/managed-session-storage.d.ts +8 -3
- package/dist/types/session/session-manager.d.ts +5 -1
- package/dist/types/session/session-storage.d.ts +13 -0
- package/dist/types/skill-state/active-state.d.ts +4 -0
- package/dist/types/task/provider-retry-status.d.ts +13 -0
- package/dist/types/task/types.d.ts +3 -0
- package/dist/types/tools/index.d.ts +3 -1
- package/package.json +8 -8
- package/scripts/generate-sdk-operation-inventory.ts +1 -0
- package/src/cli/mcp-cli.ts +4 -21
- package/src/cli/notify-cli.ts +1 -0
- package/src/cli.ts +32 -0
- package/src/commands/deep-interview.ts +9 -4
- package/src/commit/agentic/index.ts +3 -3
- package/src/commit/map-reduce/index.ts +2 -2
- package/src/config/model-profiles.ts +9 -10
- package/src/config/model-registry.ts +4 -2
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +78 -163
- package/src/eval/js/tool-bridge.ts +1 -1
- package/src/gjc-runtime/deep-interview-ambiguity.ts +68 -47
- package/src/gjc-runtime/deep-interview-draft.ts +843 -0
- package/src/gjc-runtime/deep-interview-payload.ts +262 -0
- package/src/gjc-runtime/deep-interview-recorder.ts +310 -227
- package/src/gjc-runtime/deep-interview-repair.ts +1674 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +264 -103
- package/src/gjc-runtime/deep-interview-state.ts +916 -4
- package/src/gjc-runtime/ralplan-runtime.ts +3 -1
- package/src/gjc-runtime/state-writer.ts +166 -2
- package/src/gjc-runtime/workflow-manifest.generated.json +386 -1
- package/src/gjc-runtime/workflow-manifest.ts +191 -5
- package/src/hooks/skill-state.ts +23 -1
- package/src/internal-urls/docs-index.generated.ts +6 -5
- package/src/modes/components/welcome.ts +10 -2
- package/src/modes/controllers/selector-controller.ts +38 -8
- package/src/modes/interactive-mode.ts +29 -9
- package/src/prompts/agent-fragments/restricted-bash.md +1 -1
- package/src/prompts/agents/architect.md +8 -1
- package/src/prompts/agents/critic.md +8 -1
- package/src/prompts/agents/planner.md +8 -1
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/runtime-mcp/json-rpc.ts +7 -4
- package/src/runtime-mcp/redaction.ts +30 -0
- package/src/sdk/broker/identity.ts +22 -12
- package/src/sdk/broker/session-index.ts +11 -1
- package/src/sdk/bus/chat-daemon-control.ts +5 -3
- package/src/sdk/bus/daemon-paths.ts +2 -0
- package/src/sdk/bus/index.ts +60 -5
- package/src/sdk/bus/notification-service.ts +19 -5
- package/src/sdk/bus/operator-runtime.ts +31 -5
- package/src/sdk/bus/telegram-cli.ts +6 -13
- package/src/sdk/bus/telegram-daemon-cli.ts +8 -3
- package/src/sdk/bus/telegram-daemon-contract.ts +15 -7
- package/src/sdk/bus/telegram-daemon-control.ts +14 -5
- package/src/sdk/bus/telegram-daemon.ts +685 -120
- package/src/sdk/protocol/operation-inventory.generated.json +11 -0
- package/src/sdk/session.ts +12 -0
- package/src/session/agent-session.ts +140 -10
- package/src/session/internal/managed-session-scope.ts +339 -228
- package/src/session/internal/managed-session-storage.ts +17 -4
- package/src/session/session-manager.ts +96 -33
- package/src/session/session-storage.ts +23 -0
- package/src/skill-state/active-state.ts +10 -0
- package/src/skill-state/workflow-mutation-guard.ts +41 -7
- package/src/task/executor.ts +154 -44
- package/src/task/provider-retry-status.ts +88 -0
- package/src/task/render.ts +45 -26
- package/src/task/types.ts +3 -0
- package/src/tools/ask.ts +1 -0
- package/src/tools/bash-allowed-prefixes.ts +45 -1
- package/src/tools/index.ts +3 -1
- package/src/tools/subagent-render.ts +74 -16
- package/src/tools/subagent.ts +2 -0
- package/vendor/insane-search/MANIFEST.json +4 -1
- package/vendor/insane-search/engine/bias_check.py +5 -0
- package/vendor/insane-search/engine/fetch_chain.py +67 -1
- package/vendor/insane-search/engine/transport.py +13 -2
- package/vendor/insane-search/engine/validators.py +4 -1
|
@@ -3,9 +3,11 @@ import * as fs from "node:fs/promises";
|
|
|
3
3
|
import * as os from "node:os";
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import { YAML } from "bun";
|
|
6
|
-
import { syncSkillActiveState } from "../skill-state/active-state";
|
|
7
|
-
import { deriveDeepInterviewHud } from "../skill-state/workflow-hud";
|
|
8
6
|
import { WORKFLOW_STATE_VERSION } from "../skill-state/workflow-state-contract";
|
|
7
|
+
import { scoreToUnits } from "./deep-interview-ambiguity";
|
|
8
|
+
import { runDeepInterviewDraftCommand } from "./deep-interview-draft";
|
|
9
|
+
import { runDeepInterviewPostCommitEffects } from "./deep-interview-recorder";
|
|
10
|
+
import { isDeepInterviewRepairVerb, runDeepInterviewRepairCommand } from "./deep-interview-repair";
|
|
9
11
|
import {
|
|
10
12
|
assertDeepInterviewInputWithinLimit,
|
|
11
13
|
assertDeepInterviewIntentReview,
|
|
@@ -17,12 +19,20 @@ import {
|
|
|
17
19
|
MAX_INITIAL_CONTEXT_LENGTH,
|
|
18
20
|
normalizeDeepInterviewEnvelope,
|
|
19
21
|
reviewDeepInterviewIntent,
|
|
22
|
+
validateDeepInterviewV1Envelope,
|
|
20
23
|
} from "./deep-interview-state";
|
|
21
24
|
import { runNativeRalplanCommand } from "./ralplan-runtime";
|
|
22
25
|
import { modeStatePath, sessionSpecsDir } from "./session-layout";
|
|
23
|
-
import { resolveGjcSessionForWrite
|
|
26
|
+
import { resolveGjcSessionForWrite } from "./session-resolution";
|
|
24
27
|
import { runNativeStateCommand } from "./state-runtime";
|
|
25
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
appendJsonl,
|
|
30
|
+
readExistingStateForMutation,
|
|
31
|
+
verifyWorkflowEnvelopeReceiptValue,
|
|
32
|
+
withWorkflowStateLock,
|
|
33
|
+
writeArtifact,
|
|
34
|
+
writeWorkflowEnvelopeAtomic,
|
|
35
|
+
} from "./state-writer";
|
|
26
36
|
import { assertSafePathComponent, CommandError, flagValue, hasFlag } from "./workflow-cli-common";
|
|
27
37
|
|
|
28
38
|
export * from "./deep-interview-recorder";
|
|
@@ -354,7 +364,7 @@ async function readSettingsAmbiguityThreshold(
|
|
|
354
364
|
} catch (error) {
|
|
355
365
|
const err = error as NodeJS.ErrnoException;
|
|
356
366
|
if (err.code === "ENOENT") return undefined;
|
|
357
|
-
|
|
367
|
+
throw new Error(`failed to read deep-interview threshold config ${settingsPath}: ${err.message}`);
|
|
358
368
|
}
|
|
359
369
|
let parsed: unknown;
|
|
360
370
|
try {
|
|
@@ -364,7 +374,7 @@ async function readSettingsAmbiguityThreshold(
|
|
|
364
374
|
}
|
|
365
375
|
const candidate = (parsed as { gjc?: { deepInterview?: { ambiguityThreshold?: unknown } } })?.gjc?.deepInterview
|
|
366
376
|
?.ambiguityThreshold;
|
|
367
|
-
if (typeof candidate !== "number" || !
|
|
377
|
+
if (typeof candidate !== "number" || !isIntegralScore(candidate) || candidate <= 0) {
|
|
368
378
|
return undefined;
|
|
369
379
|
}
|
|
370
380
|
return { threshold: candidate, source: settingsPath };
|
|
@@ -380,16 +390,23 @@ function modernSettingsPath(): string {
|
|
|
380
390
|
|
|
381
391
|
async function readModernSettingsAmbiguityThreshold(): Promise<{ threshold: number; source: string } | undefined> {
|
|
382
392
|
const modernConfigPath = modernSettingsPath();
|
|
393
|
+
let raw: string;
|
|
394
|
+
try {
|
|
395
|
+
raw = await fs.readFile(modernConfigPath, "utf-8");
|
|
396
|
+
} catch (error) {
|
|
397
|
+
const err = error as NodeJS.ErrnoException;
|
|
398
|
+
if (err.code === "ENOENT") return undefined;
|
|
399
|
+
throw new Error(`failed to read deep-interview threshold config ${modernConfigPath}: ${err.message}`);
|
|
400
|
+
}
|
|
383
401
|
let parsed: unknown;
|
|
384
402
|
try {
|
|
385
|
-
parsed = YAML.parse(
|
|
403
|
+
parsed = YAML.parse(raw);
|
|
386
404
|
} catch {
|
|
387
405
|
return undefined;
|
|
388
406
|
}
|
|
389
407
|
const candidate = (parsed as { gjc?: { deepInterview?: { ambiguityThreshold?: unknown } } })?.gjc?.deepInterview
|
|
390
408
|
?.ambiguityThreshold;
|
|
391
|
-
if (typeof candidate !== "number" || !
|
|
392
|
-
return undefined;
|
|
409
|
+
if (typeof candidate !== "number" || !isIntegralScore(candidate) || candidate <= 0) return undefined;
|
|
393
410
|
return { threshold: candidate, source: modernConfigPath };
|
|
394
411
|
}
|
|
395
412
|
|
|
@@ -504,6 +521,15 @@ async function resolveSpecWriteArgs(args: readonly string[], cwd: string): Promi
|
|
|
504
521
|
};
|
|
505
522
|
}
|
|
506
523
|
|
|
524
|
+
function isIntegralScore(value: number): boolean {
|
|
525
|
+
try {
|
|
526
|
+
scoreToUnits(value);
|
|
527
|
+
return true;
|
|
528
|
+
} catch {
|
|
529
|
+
return false;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
507
533
|
async function resolveDeepInterviewArgs(args: readonly string[], cwd: string): Promise<ResolvedDeepInterviewArgs> {
|
|
508
534
|
const session = resolveGjcSessionForWrite(cwd, {
|
|
509
535
|
flagValue: flagValue(args, "--session-id"),
|
|
@@ -524,10 +550,10 @@ async function resolveDeepInterviewArgs(args: readonly string[], cwd: string): P
|
|
|
524
550
|
const thresholdOverride = flagValue(args, "--threshold");
|
|
525
551
|
if (thresholdOverride !== undefined) {
|
|
526
552
|
const parsed = Number(thresholdOverride);
|
|
527
|
-
if (!
|
|
553
|
+
if (!isIntegralScore(parsed) || parsed <= 0) {
|
|
528
554
|
throw new DeepInterviewCommandError(
|
|
529
555
|
2,
|
|
530
|
-
`invalid --threshold: ${thresholdOverride}. Expected
|
|
556
|
+
`invalid --threshold: ${thresholdOverride}. Expected integral 1e-4 units in (0, 1].`,
|
|
531
557
|
);
|
|
532
558
|
}
|
|
533
559
|
threshold = parsed;
|
|
@@ -545,7 +571,16 @@ async function resolveDeepInterviewArgs(args: readonly string[], cwd: string): P
|
|
|
545
571
|
|
|
546
572
|
const ideaParts: string[] = [];
|
|
547
573
|
let skipNext = false;
|
|
574
|
+
let positionalOnly = false;
|
|
548
575
|
for (const arg of args) {
|
|
576
|
+
if (positionalOnly) {
|
|
577
|
+
ideaParts.push(arg);
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
if (arg === "--") {
|
|
581
|
+
positionalOnly = true;
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
549
584
|
if (skipNext) {
|
|
550
585
|
skipNext = false;
|
|
551
586
|
continue;
|
|
@@ -719,13 +754,13 @@ export async function persistDeepInterviewSpec(
|
|
|
719
754
|
forced: resolved.force,
|
|
720
755
|
},
|
|
721
756
|
});
|
|
722
|
-
await
|
|
723
|
-
await syncDeepInterviewHud({
|
|
757
|
+
await runDeepInterviewPostCommitEffects({
|
|
724
758
|
cwd,
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
759
|
+
statePath,
|
|
760
|
+
sessionId: resolved.sessionId ?? "",
|
|
761
|
+
envelope: normalizeDeepInterviewEnvelope(payload),
|
|
762
|
+
revision: 0,
|
|
763
|
+
writer: "deep-interview-runtime",
|
|
729
764
|
});
|
|
730
765
|
|
|
731
766
|
return {
|
|
@@ -738,91 +773,169 @@ export async function persistDeepInterviewSpec(
|
|
|
738
773
|
};
|
|
739
774
|
}
|
|
740
775
|
|
|
741
|
-
|
|
776
|
+
function isEmptyPendingTopology(value: unknown): boolean {
|
|
777
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
778
|
+
const topology = value as Record<string, unknown>;
|
|
779
|
+
return (
|
|
780
|
+
topology.status === "pending" &&
|
|
781
|
+
Array.isArray(topology.components) &&
|
|
782
|
+
topology.components.length === 0 &&
|
|
783
|
+
Array.isArray(topology.deferred_components) &&
|
|
784
|
+
topology.deferred_components.length === 0
|
|
785
|
+
);
|
|
786
|
+
}
|
|
787
|
+
function isUnresolvedNativeSetup(value: unknown): boolean {
|
|
788
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
789
|
+
const state = value as Record<string, unknown>;
|
|
790
|
+
return (
|
|
791
|
+
state.setup !== null &&
|
|
792
|
+
typeof state.setup === "object" &&
|
|
793
|
+
!Array.isArray(state.setup) &&
|
|
794
|
+
(state.setup as Record<string, unknown>).status === "unresolved"
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
async function seedDeepInterviewState(
|
|
798
|
+
cwd: string,
|
|
799
|
+
resolved: ResolvedDeepInterviewArgs,
|
|
800
|
+
): Promise<{ statePath: string; warnings: unknown[] }> {
|
|
742
801
|
const statePath = deepInterviewStatePath(cwd, resolved.sessionId);
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
802
|
+
return withWorkflowStateLock(
|
|
803
|
+
`${statePath}.kickoff`,
|
|
804
|
+
async () => {
|
|
805
|
+
const existing = await readExistingStateForMutation(statePath);
|
|
806
|
+
if (existing.kind === "valid") {
|
|
807
|
+
try {
|
|
808
|
+
if (verifyWorkflowEnvelopeReceiptValue(existing.value, statePath) !== "native-valid")
|
|
809
|
+
throw new Error("invalid receipt");
|
|
810
|
+
validateDeepInterviewV1Envelope(existing.value);
|
|
811
|
+
} catch {
|
|
812
|
+
throw new DeepInterviewCommandError(3, "DI_STATE_CORRUPT");
|
|
813
|
+
}
|
|
814
|
+
const phase =
|
|
815
|
+
typeof existing.value.current_phase === "string" ? existing.value.current_phase : "interviewing";
|
|
816
|
+
if (phase === "complete" || phase === "handoff" || existing.value.active === false)
|
|
817
|
+
throw new DeepInterviewCommandError(3, "DI_PHASE_NOT_REPAIRABLE");
|
|
818
|
+
const priorState =
|
|
819
|
+
existing.value.state && typeof existing.value.state === "object" && !Array.isArray(existing.value.state)
|
|
820
|
+
? (existing.value.state as Record<string, unknown>)
|
|
821
|
+
: {};
|
|
822
|
+
const exactEmptyNativeSeed =
|
|
823
|
+
isUnresolvedNativeSetup(priorState) &&
|
|
824
|
+
priorState.type === (resolved.trace ? "brownfield" : "greenfield") &&
|
|
825
|
+
existing.value.skill === "deep-interview" &&
|
|
826
|
+
existing.value.active === true &&
|
|
827
|
+
phase === "interviewing" &&
|
|
828
|
+
existing.value.resolution === resolved.resolution &&
|
|
829
|
+
existing.value.threshold === resolved.threshold &&
|
|
830
|
+
existing.value.threshold_source === resolved.thresholdSource &&
|
|
831
|
+
priorState.initial_idea === resolved.idea &&
|
|
832
|
+
priorState.intent_contract_required === true &&
|
|
833
|
+
Array.isArray(priorState.rounds) &&
|
|
834
|
+
priorState.rounds.length === 0 &&
|
|
835
|
+
Array.isArray(priorState.established_facts) &&
|
|
836
|
+
priorState.established_facts.length === 0 &&
|
|
837
|
+
Array.isArray(priorState.ontology_snapshots) &&
|
|
838
|
+
priorState.ontology_snapshots.length === 0 &&
|
|
839
|
+
Array.isArray(priorState.auto_researched_rounds) &&
|
|
840
|
+
priorState.auto_researched_rounds.length === 0 &&
|
|
841
|
+
Array.isArray(priorState.auto_answered_rounds) &&
|
|
842
|
+
priorState.auto_answered_rounds.length === 0 &&
|
|
843
|
+
isEmptyPendingTopology(priorState.topology) &&
|
|
844
|
+
priorState.architect_failures === 0 &&
|
|
845
|
+
priorState.current_ambiguity === 1 &&
|
|
846
|
+
JSON.stringify(existing.value.trace) === JSON.stringify(resolved.trace) &&
|
|
847
|
+
JSON.stringify(existing.value.language) === JSON.stringify(resolved.language);
|
|
848
|
+
if (exactEmptyNativeSeed) return { statePath, warnings: [] };
|
|
849
|
+
throw new DeepInterviewCommandError(2, "DI_INTERVIEW_ALREADY_ACTIVE");
|
|
850
|
+
}
|
|
851
|
+
if (existing.kind === "corrupt") throw new DeepInterviewCommandError(3, "DI_STATE_CORRUPT");
|
|
852
|
+
const now = new Date().toISOString();
|
|
853
|
+
const type = resolved.trace ? "brownfield" : "greenfield";
|
|
854
|
+
const payload: Record<string, unknown> = {
|
|
855
|
+
active: true,
|
|
856
|
+
current_phase: "interviewing",
|
|
857
|
+
schema_version: 1,
|
|
858
|
+
skill: "deep-interview",
|
|
859
|
+
version: WORKFLOW_STATE_VERSION,
|
|
860
|
+
resolution: resolved.resolution,
|
|
861
|
+
threshold: resolved.threshold,
|
|
862
|
+
threshold_units: scoreToUnits(resolved.threshold),
|
|
863
|
+
threshold_source: resolved.thresholdSource,
|
|
864
|
+
state: {
|
|
865
|
+
setup: { status: "unresolved" },
|
|
866
|
+
type,
|
|
867
|
+
initial_idea: resolved.idea,
|
|
868
|
+
intent_contract_required: true,
|
|
869
|
+
rounds: [],
|
|
870
|
+
established_facts: [],
|
|
871
|
+
topology: { status: "pending", components: [], deferred_components: [] },
|
|
872
|
+
ontology_snapshots: [],
|
|
873
|
+
auto_researched_rounds: [],
|
|
874
|
+
auto_answered_rounds: [],
|
|
875
|
+
architect_failures: 0,
|
|
876
|
+
current_ambiguity: 1.0,
|
|
877
|
+
threshold: resolved.threshold,
|
|
878
|
+
threshold_units: scoreToUnits(resolved.threshold),
|
|
879
|
+
threshold_source: resolved.thresholdSource,
|
|
880
|
+
},
|
|
881
|
+
updated_at: now,
|
|
882
|
+
};
|
|
883
|
+
validateDeepInterviewV1Envelope(payload);
|
|
884
|
+
if (resolved.trace) {
|
|
885
|
+
payload.trace = resolved.trace;
|
|
886
|
+
(payload.state as Record<string, unknown>).trace = resolved.trace;
|
|
887
|
+
(payload.state as Record<string, unknown>).trace_summary = resolved.trace;
|
|
888
|
+
(payload.state as Record<string, unknown>).codebase_context = {
|
|
889
|
+
source: "trace",
|
|
890
|
+
summary: resolved.trace.findings,
|
|
891
|
+
relevant_paths: resolved.trace.relevant_paths,
|
|
892
|
+
project_hints: resolved.trace.project_hints,
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
if (resolved.language) {
|
|
896
|
+
payload.language = resolved.language;
|
|
897
|
+
(payload.state as Record<string, unknown>).language = resolved.language;
|
|
898
|
+
}
|
|
899
|
+
if (resolved.sessionId) payload.session_id = resolved.sessionId;
|
|
900
|
+
await writeWorkflowEnvelopeAtomic(statePath, payload, {
|
|
901
|
+
cwd,
|
|
902
|
+
receipt: {
|
|
903
|
+
cwd,
|
|
904
|
+
skill: "deep-interview",
|
|
905
|
+
owner: "gjc-runtime",
|
|
906
|
+
command: "gjc deep-interview seed",
|
|
907
|
+
sessionId: resolved.sessionId,
|
|
908
|
+
nowIso: now,
|
|
909
|
+
},
|
|
910
|
+
audit: {
|
|
911
|
+
category: "state",
|
|
912
|
+
verb: "write",
|
|
913
|
+
owner: "gjc-runtime",
|
|
914
|
+
skill: "deep-interview",
|
|
915
|
+
sessionId: resolved.sessionId,
|
|
916
|
+
},
|
|
917
|
+
});
|
|
918
|
+
const stamped = await readExistingStateForMutation(statePath);
|
|
919
|
+
if (stamped.kind !== "valid") throw new DeepInterviewCommandError(3, "DI_STATE_CORRUPT");
|
|
920
|
+
try {
|
|
921
|
+
if (verifyWorkflowEnvelopeReceiptValue(stamped.value, statePath) !== "native-valid")
|
|
922
|
+
throw new Error("invalid receipt");
|
|
923
|
+
validateDeepInterviewV1Envelope(stamped.value);
|
|
924
|
+
} catch {
|
|
925
|
+
throw new DeepInterviewCommandError(3, "DI_STATE_CORRUPT");
|
|
926
|
+
}
|
|
927
|
+
const warnings = await runDeepInterviewPostCommitEffects({
|
|
928
|
+
cwd,
|
|
929
|
+
statePath,
|
|
930
|
+
sessionId: resolved.sessionId ?? "",
|
|
931
|
+
envelope: normalizeDeepInterviewEnvelope(stamped.value),
|
|
932
|
+
revision: typeof stamped.value.state_revision === "number" ? stamped.value.state_revision : 0,
|
|
933
|
+
writer: "deep-interview-runtime",
|
|
934
|
+
});
|
|
935
|
+
return { statePath, warnings };
|
|
796
936
|
},
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
await syncDeepInterviewHud({ cwd, sessionId: resolved.sessionId, payload, phase: "interviewing" });
|
|
800
|
-
return statePath;
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
async function syncDeepInterviewHud(options: {
|
|
804
|
-
cwd: string;
|
|
805
|
-
sessionId?: string;
|
|
806
|
-
payload: Record<string, unknown>;
|
|
807
|
-
phase?: string;
|
|
808
|
-
specStatus?: string;
|
|
809
|
-
}): Promise<void> {
|
|
810
|
-
try {
|
|
811
|
-
const phase =
|
|
812
|
-
options.phase ??
|
|
813
|
-
(typeof options.payload.current_phase === "string" ? options.payload.current_phase : "interviewing");
|
|
814
|
-
await syncSkillActiveState({
|
|
815
|
-
cwd: options.cwd,
|
|
816
|
-
skill: "deep-interview",
|
|
817
|
-
active: phase !== "complete",
|
|
818
|
-
phase,
|
|
819
|
-
sessionId: options.sessionId,
|
|
820
|
-
source: "gjc-deep-interview-native",
|
|
821
|
-
hud: deriveDeepInterviewHud(options.payload, { phase, specStatus: options.specStatus }),
|
|
822
|
-
});
|
|
823
|
-
} catch {
|
|
824
|
-
// HUD sync is best-effort and must not change command semantics.
|
|
825
|
-
}
|
|
937
|
+
{ cwd },
|
|
938
|
+
);
|
|
826
939
|
}
|
|
827
940
|
|
|
828
941
|
async function handleSpecWrite(args: readonly string[], cwd: string): Promise<DeepInterviewCommandResult> {
|
|
@@ -888,6 +1001,28 @@ async function handleSpecWrite(args: readonly string[], cwd: string): Promise<De
|
|
|
888
1001
|
.join("\n");
|
|
889
1002
|
return { status: 0, stdout };
|
|
890
1003
|
}
|
|
1004
|
+
function isDeepInterviewRepairInvocation(args: readonly string[]): boolean {
|
|
1005
|
+
const separator = args.indexOf("--");
|
|
1006
|
+
return (separator === -1 || separator > 0) && isDeepInterviewRepairVerb(args[0] ?? "");
|
|
1007
|
+
}
|
|
1008
|
+
function isDeepInterviewDraftInvocation(args: readonly string[]): boolean {
|
|
1009
|
+
const separator = args.indexOf("--");
|
|
1010
|
+
return (separator === -1 || separator > 0) && args[0] === "draft";
|
|
1011
|
+
}
|
|
1012
|
+
function draftCommandArgs(args: readonly string[]): string[] {
|
|
1013
|
+
const normalized: string[] = [];
|
|
1014
|
+
for (let index = 1; index < args.length; index++) {
|
|
1015
|
+
const argument = args[index];
|
|
1016
|
+
if (argument === "--for") {
|
|
1017
|
+
normalized.push("--kind", args[++index] ?? "");
|
|
1018
|
+
continue;
|
|
1019
|
+
}
|
|
1020
|
+
normalized.push(argument);
|
|
1021
|
+
if (argument === "--json" && !args[index + 1]?.startsWith("--")) continue;
|
|
1022
|
+
if (argument === "--json") normalized.push("true");
|
|
1023
|
+
}
|
|
1024
|
+
return normalized;
|
|
1025
|
+
}
|
|
891
1026
|
|
|
892
1027
|
export async function runNativeDeepInterviewCommand(
|
|
893
1028
|
args: string[],
|
|
@@ -895,14 +1030,39 @@ export async function runNativeDeepInterviewCommand(
|
|
|
895
1030
|
): Promise<DeepInterviewCommandResult> {
|
|
896
1031
|
try {
|
|
897
1032
|
if (isDeepInterviewSpecWriteInvocation(args)) return await handleSpecWrite(args, cwd);
|
|
1033
|
+
if (isDeepInterviewRepairInvocation(args)) return await runDeepInterviewRepairCommand(args, cwd);
|
|
1034
|
+
if (isDeepInterviewDraftInvocation(args)) return await runDeepInterviewDraftCommand(draftCommandArgs(args), cwd);
|
|
898
1035
|
const resolved = await resolveDeepInterviewArgs(args, cwd);
|
|
899
1036
|
if (!resolved.idea) {
|
|
1037
|
+
const existing = await readExistingStateForMutation(deepInterviewStatePath(cwd, resolved.sessionId));
|
|
1038
|
+
if (existing.kind === "valid") {
|
|
1039
|
+
const phase =
|
|
1040
|
+
typeof existing.value.current_phase === "string" ? existing.value.current_phase : "interviewing";
|
|
1041
|
+
if (phase === "complete" || existing.value.active === false)
|
|
1042
|
+
throw new DeepInterviewCommandError(
|
|
1043
|
+
2,
|
|
1044
|
+
"deep-interview session is terminal; start a new session to kick off another interview.",
|
|
1045
|
+
);
|
|
1046
|
+
const state = existing.value.state as Record<string, unknown> | undefined;
|
|
1047
|
+
return {
|
|
1048
|
+
status: 0,
|
|
1049
|
+
stdout: `${JSON.stringify({
|
|
1050
|
+
skill: "deep-interview",
|
|
1051
|
+
resumed: true,
|
|
1052
|
+
session_id: resolved.sessionId ?? null,
|
|
1053
|
+
state_path: deepInterviewStatePath(cwd, resolved.sessionId),
|
|
1054
|
+
state_revision: existing.value.state_revision ?? 0,
|
|
1055
|
+
idea: typeof state?.initial_idea === "string" ? state.initial_idea : null,
|
|
1056
|
+
handoff: "/skill:deep-interview",
|
|
1057
|
+
})}\n`,
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
900
1060
|
throw new DeepInterviewCommandError(
|
|
901
1061
|
2,
|
|
902
1062
|
'gjc deep-interview requires an idea, e.g. `gjc deep-interview "<idea>"`.',
|
|
903
1063
|
);
|
|
904
1064
|
}
|
|
905
|
-
const
|
|
1065
|
+
const seeded = await seedDeepInterviewState(cwd, resolved);
|
|
906
1066
|
|
|
907
1067
|
const summary = {
|
|
908
1068
|
skill: "deep-interview",
|
|
@@ -912,13 +1072,14 @@ export async function runNativeDeepInterviewCommand(
|
|
|
912
1072
|
idea: resolved.idea,
|
|
913
1073
|
language: resolved.language,
|
|
914
1074
|
trace: resolved.trace,
|
|
915
|
-
state_path: statePath,
|
|
1075
|
+
state_path: seeded.statePath,
|
|
1076
|
+
warnings: seeded.warnings,
|
|
916
1077
|
handoff: "/skill:deep-interview",
|
|
917
1078
|
};
|
|
918
1079
|
const stdout = resolved.json
|
|
919
1080
|
? `${JSON.stringify(summary)}\n`
|
|
920
1081
|
: [
|
|
921
|
-
`deep-interview seed state_path=${statePath}`,
|
|
1082
|
+
`deep-interview seed state_path=${seeded.statePath}`,
|
|
922
1083
|
`resolution=${resolved.resolution} threshold=${resolved.threshold} threshold_source=${resolved.thresholdSource}`,
|
|
923
1084
|
resolved.trace ? `trace=enabled bounded_paths=${resolved.trace.relevant_paths.length}` : undefined,
|
|
924
1085
|
"handoff=/skill:deep-interview",
|