@longtable/cli 0.1.4 → 0.1.6
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/README.md +11 -0
- package/dist/cli.js +44 -22
- package/dist/persona-router.d.ts +9 -0
- package/dist/persona-router.js +69 -4
- package/dist/project-session.js +64 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,6 +53,7 @@ longtable start
|
|
|
53
53
|
- 프로젝트 디렉토리 생성
|
|
54
54
|
- `.longtable/` 메모리 파일 생성
|
|
55
55
|
- 프로젝트용 `AGENTS.md` 생성
|
|
56
|
+
- `START-HERE.md` 생성
|
|
56
57
|
|
|
57
58
|
을 수행합니다.
|
|
58
59
|
|
|
@@ -71,6 +72,16 @@ codex
|
|
|
71
72
|
|
|
72
73
|
프로젝트 디렉토리 안에서는 보통 그냥 `codex`를 열고 자연어로 연구를 시작하면 됩니다.
|
|
73
74
|
|
|
75
|
+
Codex 안에서 더 명시적으로 부르고 싶다면, 아래처럼 짧은 문법도 사용할 수 있습니다.
|
|
76
|
+
|
|
77
|
+
```text
|
|
78
|
+
lt explore: 연구 질문을 어디서부터 좁혀야 할지 모르겠어.
|
|
79
|
+
lt review: 이 주장 어디가 약한지 봐줘.
|
|
80
|
+
lt panel: 이 방향에 대한 의견 충돌도 같이 보여줘.
|
|
81
|
+
lt editor: BJET 편집자 관점에서 봐줘.
|
|
82
|
+
lt methods: 방법론적으로 어디가 취약한지 말해줘.
|
|
83
|
+
```
|
|
84
|
+
|
|
74
85
|
CLI를 계속 쓰고 싶다면 아래 명령은 보조 경로입니다.
|
|
75
86
|
|
|
76
87
|
## Advanced commands
|
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import { resolve } from "node:path";
|
|
|
7
7
|
import { buildProviderChoices, buildQuickSetupFlow, createPersistedSetupOutput, installRuntimeConfigFromStoredSetup, loadSetupOutput, renderInstallSummary, renderSetupSummary, resolveDefaultRuntimeConfigPath, resolveDefaultSetupPath, saveSetupAndRuntimeConfig, serializeSetupOutput } from "@diverga/setup";
|
|
8
8
|
import { buildCodexThinWrappedPrompt, runCodexThinWrapper } from "@diverga/provider-codex";
|
|
9
9
|
import { installCodexPromptAliases, listInstalledCodexPromptAliases, removeCodexPromptAliases, resolveCodexPromptsDir } from "./prompt-aliases.js";
|
|
10
|
-
import { buildPersonaGuidance } from "./persona-router.js";
|
|
10
|
+
import { buildPersonaGuidance, parseInvocationDirective } from "./persona-router.js";
|
|
11
11
|
import { PERSONA_DEFINITIONS } from "./personas.js";
|
|
12
12
|
import { createOrUpdateProjectWorkspace, loadProjectContextFromDirectory, renderProjectWorkspaceSummary } from "./project-session.js";
|
|
13
13
|
const VALID_MODES = new Set([
|
|
@@ -27,6 +27,24 @@ const VALID_STAGES = new Set([
|
|
|
27
27
|
"writing",
|
|
28
28
|
"submission"
|
|
29
29
|
]);
|
|
30
|
+
const ANSI = {
|
|
31
|
+
reset: "\u001B[0m",
|
|
32
|
+
bold: "\u001B[1m",
|
|
33
|
+
dim: "\u001B[2m",
|
|
34
|
+
cyan: "\u001B[36m",
|
|
35
|
+
green: "\u001B[32m"
|
|
36
|
+
};
|
|
37
|
+
function style(text, prefix) {
|
|
38
|
+
return `${prefix}${text}${ANSI.reset}`;
|
|
39
|
+
}
|
|
40
|
+
function renderSectionCard(title, body) {
|
|
41
|
+
return [
|
|
42
|
+
"┌──────────────────────────────────────────────┐",
|
|
43
|
+
`│ ${title.padEnd(44, " ")}│`,
|
|
44
|
+
"└──────────────────────────────────────────────┘",
|
|
45
|
+
...body
|
|
46
|
+
].join("\n");
|
|
47
|
+
}
|
|
30
48
|
function usage() {
|
|
31
49
|
return [
|
|
32
50
|
"Usage:",
|
|
@@ -122,15 +140,10 @@ function renderSetupHeader(flow) {
|
|
|
122
140
|
const subtitle = flow === "interview"
|
|
123
141
|
? "We will ask about your research persona, challenge preferences, and authorship defaults."
|
|
124
142
|
: "We will capture the minimum profile needed to start using Long Table.";
|
|
125
|
-
return [
|
|
126
|
-
"┌──────────────────────────────────────────────┐",
|
|
127
|
-
`│ ${title.padEnd(44, " ")}│`,
|
|
128
|
-
"└──────────────────────────────────────────────┘",
|
|
129
|
-
subtitle
|
|
130
|
-
].join("\n");
|
|
143
|
+
return renderSectionCard(title, [subtitle]);
|
|
131
144
|
}
|
|
132
145
|
function renderQuestionHeader(index, total, section, prompt) {
|
|
133
|
-
return [``, `[${index}/${total}] ${section}`, prompt].join("\n");
|
|
146
|
+
return [``, style(`[${index}/${total}] ${section}`, `${ANSI.bold}${ANSI.cyan}`), prompt].join("\n");
|
|
134
147
|
}
|
|
135
148
|
function questionSection(questionId) {
|
|
136
149
|
if (questionId === "field" || questionId === "careerStage" || questionId === "experienceLevel") {
|
|
@@ -154,9 +167,9 @@ function clearLine() {
|
|
|
154
167
|
return "\u001B[2K\r";
|
|
155
168
|
}
|
|
156
169
|
function renderArrowMenu(prompt, choices, selectedIndex) {
|
|
157
|
-
const lines = [prompt, "Use ↑/↓ and Enter."];
|
|
170
|
+
const lines = [style(prompt, ANSI.bold), style("Use ↑/↓ and Enter.", ANSI.dim)];
|
|
158
171
|
for (let index = 0; index < choices.length; index += 1) {
|
|
159
|
-
const prefix = index === selectedIndex ? ">" : " ";
|
|
172
|
+
const prefix = index === selectedIndex ? style(">", `${ANSI.bold}${ANSI.green}`) : " ";
|
|
160
173
|
lines.push(`${prefix} ${choices[index].label} - ${choices[index].description}`);
|
|
161
174
|
}
|
|
162
175
|
return lines.join("\n");
|
|
@@ -450,10 +463,10 @@ async function collectProjectInterview(setup, args) {
|
|
|
450
463
|
try {
|
|
451
464
|
if (needsInteractivePrompts) {
|
|
452
465
|
console.log("");
|
|
453
|
-
console.log("
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
466
|
+
console.log(renderSectionCard("Long Table Project Start", [
|
|
467
|
+
"We will create a project workspace and a session memory seed for today's work.",
|
|
468
|
+
"At the end, Long Table will tell you exactly which directory to open in Codex."
|
|
469
|
+
]));
|
|
457
470
|
console.log("");
|
|
458
471
|
}
|
|
459
472
|
const projectName = (typeof args.name === "string" && args.name.trim()) ||
|
|
@@ -790,7 +803,9 @@ async function runAsk(args) {
|
|
|
790
803
|
if (!prompt) {
|
|
791
804
|
throw new Error("A prompt is required.");
|
|
792
805
|
}
|
|
793
|
-
const
|
|
806
|
+
const directive = parseInvocationDirective(prompt);
|
|
807
|
+
const effectivePrompt = directive.cleanedPrompt;
|
|
808
|
+
const inferred = directive.mode ?? inferModeFromPrompt(effectivePrompt);
|
|
794
809
|
if (inferred === "status") {
|
|
795
810
|
await runCodexSubcommand("status", args);
|
|
796
811
|
return;
|
|
@@ -798,9 +813,12 @@ async function runAsk(args) {
|
|
|
798
813
|
const mode = inferred === "panel" ? "review" : inferred;
|
|
799
814
|
const delegatedArgs = {
|
|
800
815
|
...args,
|
|
801
|
-
prompt
|
|
816
|
+
prompt: effectivePrompt
|
|
802
817
|
};
|
|
803
|
-
if (
|
|
818
|
+
if (directive.roles.length > 0 && typeof delegatedArgs.role !== "string") {
|
|
819
|
+
delegatedArgs.role = directive.roles.join(",");
|
|
820
|
+
}
|
|
821
|
+
if ((inferred === "panel" || directive.panel) && delegatedArgs.panel !== true) {
|
|
804
822
|
delegatedArgs.panel = true;
|
|
805
823
|
delegatedArgs["show-conflicts"] = true;
|
|
806
824
|
}
|
|
@@ -855,11 +873,15 @@ async function runStart(args) {
|
|
|
855
873
|
}
|
|
856
874
|
console.log(renderProjectWorkspaceSummary(context));
|
|
857
875
|
console.log("");
|
|
858
|
-
console.log("Next
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
876
|
+
console.log(renderSectionCard("Next Step", [
|
|
877
|
+
`1. cd "${context.project.projectPath}"`,
|
|
878
|
+
"2. run `codex` in that directory",
|
|
879
|
+
"3. begin with your current goal in natural language",
|
|
880
|
+
"",
|
|
881
|
+
`Suggested first message: ${context.session.currentBlocker ? `"I want to work on ${context.session.currentGoal}. My current blocker is ${context.session.currentBlocker}."` : `"I want to work on ${context.session.currentGoal}."`}`,
|
|
882
|
+
"",
|
|
883
|
+
`Optional CLI path: longtable ask --cwd "${context.project.projectPath}" --prompt "${context.session.currentGoal.replaceAll("\"", "\\\"")}"`
|
|
884
|
+
]));
|
|
863
885
|
}
|
|
864
886
|
async function runCodexSubcommand(subcommand, args) {
|
|
865
887
|
const customDir = typeof args.dir === "string" ? args.dir : undefined;
|
package/dist/persona-router.d.ts
CHANGED
|
@@ -8,8 +8,17 @@ export interface PersonaRoutingResult {
|
|
|
8
8
|
consultedRoles: CanonicalPersona[];
|
|
9
9
|
ambiguousSignal: string | null;
|
|
10
10
|
}
|
|
11
|
+
export interface LongTableInvocationDirective {
|
|
12
|
+
explicit: boolean;
|
|
13
|
+
cleanedPrompt: string;
|
|
14
|
+
mode?: InteractionMode | "panel" | "status";
|
|
15
|
+
roles: CanonicalPersona[];
|
|
16
|
+
panel: boolean;
|
|
17
|
+
showConflicts: boolean;
|
|
18
|
+
}
|
|
11
19
|
export declare function detectOutputLanguage(input: string): OutputLanguage;
|
|
12
20
|
export declare function parseRoleFlag(value?: string): CanonicalPersona[];
|
|
21
|
+
export declare function parseInvocationDirective(prompt: string): LongTableInvocationDirective;
|
|
13
22
|
export declare function routePersonas(prompt: string, explicitRoleFlag?: string): PersonaRoutingResult;
|
|
14
23
|
export declare function renderDisclosure(roles: CanonicalPersona[], language: OutputLanguage): string | null;
|
|
15
24
|
export declare function buildPersonaGuidance(options: {
|
package/dist/persona-router.js
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
import { getPersonaDefinition, parsePersonaKey, PERSONA_DEFINITIONS } from "./personas.js";
|
|
2
2
|
const AUTO_CALL_LIMIT = 3;
|
|
3
|
+
const INVOCATION_PREFIX = /^(?:lt|longtable|long table|롱테이블)\s+/i;
|
|
4
|
+
const DIRECTIVE_MAP = [
|
|
5
|
+
{ key: "explore", mode: "explore" },
|
|
6
|
+
{ key: "review", mode: "review" },
|
|
7
|
+
{ key: "critique", mode: "critique" },
|
|
8
|
+
{ key: "draft", mode: "draft" },
|
|
9
|
+
{ key: "commit", mode: "commit" },
|
|
10
|
+
{ key: "panel", mode: "panel", panel: true, showConflicts: true },
|
|
11
|
+
{ key: "status", mode: "status" },
|
|
12
|
+
{ key: "editor", mode: "review", roles: ["editor"] },
|
|
13
|
+
{ key: "reviewer", mode: "review", roles: ["reviewer"] },
|
|
14
|
+
{ key: "methods", mode: "review", roles: ["methods_critic"] },
|
|
15
|
+
{ key: "method", mode: "review", roles: ["methods_critic"] },
|
|
16
|
+
{ key: "theory", mode: "review", roles: ["theory_critic"] },
|
|
17
|
+
{ key: "measurement", mode: "review", roles: ["measurement_auditor"] },
|
|
18
|
+
{ key: "ethics", mode: "review", roles: ["ethics_reviewer"] },
|
|
19
|
+
{ key: "voice", mode: "review", roles: ["voice_keeper"] },
|
|
20
|
+
{ key: "venue", mode: "review", roles: ["venue_strategist"] }
|
|
21
|
+
];
|
|
3
22
|
function unique(items) {
|
|
4
23
|
return [...new Set(items)];
|
|
5
24
|
}
|
|
@@ -15,6 +34,49 @@ export function parseRoleFlag(value) {
|
|
|
15
34
|
.map((part) => parsePersonaKey(part))
|
|
16
35
|
.filter((part) => part !== null));
|
|
17
36
|
}
|
|
37
|
+
export function parseInvocationDirective(prompt) {
|
|
38
|
+
const trimmed = prompt.trim();
|
|
39
|
+
if (!INVOCATION_PREFIX.test(trimmed)) {
|
|
40
|
+
return {
|
|
41
|
+
explicit: false,
|
|
42
|
+
cleanedPrompt: prompt,
|
|
43
|
+
roles: [],
|
|
44
|
+
panel: false,
|
|
45
|
+
showConflicts: false
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const withoutPrefix = trimmed.replace(INVOCATION_PREFIX, "");
|
|
49
|
+
const colonIndex = withoutPrefix.indexOf(":");
|
|
50
|
+
if (colonIndex === -1) {
|
|
51
|
+
return {
|
|
52
|
+
explicit: false,
|
|
53
|
+
cleanedPrompt: prompt,
|
|
54
|
+
roles: [],
|
|
55
|
+
panel: false,
|
|
56
|
+
showConflicts: false
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const directiveKey = withoutPrefix.slice(0, colonIndex).trim().toLowerCase();
|
|
60
|
+
const body = withoutPrefix.slice(colonIndex + 1).trim();
|
|
61
|
+
const directive = DIRECTIVE_MAP.find((entry) => entry.key === directiveKey);
|
|
62
|
+
if (!directive) {
|
|
63
|
+
return {
|
|
64
|
+
explicit: false,
|
|
65
|
+
cleanedPrompt: body || prompt,
|
|
66
|
+
roles: [],
|
|
67
|
+
panel: false,
|
|
68
|
+
showConflicts: false
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
explicit: true,
|
|
73
|
+
cleanedPrompt: body || prompt,
|
|
74
|
+
mode: directive.mode,
|
|
75
|
+
roles: directive.roles ?? [],
|
|
76
|
+
panel: directive.panel === true,
|
|
77
|
+
showConflicts: directive.showConflicts === true
|
|
78
|
+
};
|
|
79
|
+
}
|
|
18
80
|
export function routePersonas(prompt, explicitRoleFlag) {
|
|
19
81
|
const normalizedPrompt = prompt.toLowerCase();
|
|
20
82
|
const explicitRoles = parseRoleFlag(explicitRoleFlag);
|
|
@@ -60,7 +122,10 @@ export function renderDisclosure(roles, language) {
|
|
|
60
122
|
: `Long Table consulted: ${labels.join(", ")}`;
|
|
61
123
|
}
|
|
62
124
|
export function buildPersonaGuidance(options) {
|
|
63
|
-
const
|
|
125
|
+
const directive = parseInvocationDirective(options.prompt);
|
|
126
|
+
const effectivePrompt = directive.cleanedPrompt;
|
|
127
|
+
const mergedRoleFlag = [options.roleFlag, directive.roles.join(",")].filter(Boolean).join(",");
|
|
128
|
+
const routing = routePersonas(effectivePrompt, mergedRoleFlag || undefined);
|
|
64
129
|
const disclosure = renderDisclosure(routing.consultedRoles, routing.outputLanguage);
|
|
65
130
|
const lines = [];
|
|
66
131
|
lines.push(routing.outputLanguage === "ko"
|
|
@@ -74,12 +139,12 @@ export function buildPersonaGuidance(options) {
|
|
|
74
139
|
? "Ambiguity note: 편집자 관점인지 리뷰어 관점인지 애매합니다. 먼저 둘 중 무엇을 우선할지 짧게 확인하세요."
|
|
75
140
|
: "Ambiguity note: it is unclear whether the user wants an editor view or reviewer view. Ask briefly before closing.");
|
|
76
141
|
}
|
|
77
|
-
if (options.panel) {
|
|
142
|
+
if (options.panel || directive.panel) {
|
|
78
143
|
lines.push(routing.outputLanguage === "ko"
|
|
79
144
|
? "Return format: 1) Long Table synthesis 2) panel opinions by role 3) decision prompt to the researcher."
|
|
80
145
|
: "Return format: 1) Long Table synthesis 2) panel opinions by role 3) decision prompt to the researcher.");
|
|
81
146
|
}
|
|
82
|
-
if (options.showConflicts) {
|
|
147
|
+
if (options.showConflicts || directive.showConflicts) {
|
|
83
148
|
lines.push(routing.outputLanguage === "ko"
|
|
84
149
|
? "If roles disagree, show the conflict explicitly instead of forcing one answer."
|
|
85
150
|
: "If roles disagree, show the conflict explicitly instead of forcing one answer.");
|
|
@@ -92,7 +157,7 @@ export function buildPersonaGuidance(options) {
|
|
|
92
157
|
lines.push(routing.outputLanguage === "ko"
|
|
93
158
|
? "Do not show internal file-search logs, tool traces, or process commentary in the researcher-facing answer."
|
|
94
159
|
: "Do not show internal file-search logs, tool traces, or process commentary in the researcher-facing answer.");
|
|
95
|
-
lines.push(
|
|
160
|
+
lines.push(effectivePrompt.trim());
|
|
96
161
|
return {
|
|
97
162
|
guidedPrompt: lines.join("\n\n"),
|
|
98
163
|
routing
|
package/dist/project-session.js
CHANGED
|
@@ -42,6 +42,46 @@ function buildWorkspaceGuide(project, session) {
|
|
|
42
42
|
];
|
|
43
43
|
return lines.join("\n");
|
|
44
44
|
}
|
|
45
|
+
function buildStartHereGuide(project, session) {
|
|
46
|
+
const lines = [
|
|
47
|
+
"# Start Here",
|
|
48
|
+
"",
|
|
49
|
+
`Project: ${project.projectName}`,
|
|
50
|
+
"",
|
|
51
|
+
"This workspace was created by Long Table for a single research project.",
|
|
52
|
+
"",
|
|
53
|
+
"## What to do now",
|
|
54
|
+
"1. Open Codex in this directory.",
|
|
55
|
+
"2. Start with your current goal, not a shell command.",
|
|
56
|
+
"3. Let Long Table keep disagreement visible when it matters.",
|
|
57
|
+
"",
|
|
58
|
+
"## Suggested first message",
|
|
59
|
+
session.currentBlocker
|
|
60
|
+
? `"I want to work on ${session.currentGoal}. My current blocker is: ${session.currentBlocker}."`
|
|
61
|
+
: `"I want to work on ${session.currentGoal}."`,
|
|
62
|
+
"",
|
|
63
|
+
"## Explicit Long Table invocation inside Codex",
|
|
64
|
+
"- `lt explore: <question>`",
|
|
65
|
+
"- `lt review: <claim or plan>`",
|
|
66
|
+
"- `lt panel: <claim or plan>`",
|
|
67
|
+
"- `lt editor: <draft or positioning question>`",
|
|
68
|
+
"- `lt reviewer: <claim or section>`",
|
|
69
|
+
"- `lt methods: <design, measure, or analysis plan>`",
|
|
70
|
+
"- `lt commit: <decision that needs commitment>`",
|
|
71
|
+
"",
|
|
72
|
+
"## What Long Table already knows in this directory",
|
|
73
|
+
`- Current goal: ${session.currentGoal}`,
|
|
74
|
+
...(session.currentBlocker ? [`- Current blocker: ${session.currentBlocker}`] : []),
|
|
75
|
+
`- Requested perspectives: ${session.requestedPerspectives.length > 0 ? session.requestedPerspectives.join(", ") : "auto"}`,
|
|
76
|
+
`- Disagreement visibility: ${session.disagreementPreference}`,
|
|
77
|
+
"",
|
|
78
|
+
"## Files",
|
|
79
|
+
"- `AGENTS.md` tells Codex how to behave in this workspace.",
|
|
80
|
+
"- `.longtable/current-session.json` stores the current session goal and blocker.",
|
|
81
|
+
"- `.longtable/project.json` stores the project-level record."
|
|
82
|
+
];
|
|
83
|
+
return lines.join("\n");
|
|
84
|
+
}
|
|
45
85
|
function buildProjectAgentsMd(project, session) {
|
|
46
86
|
return [
|
|
47
87
|
"# AGENTS.md",
|
|
@@ -57,15 +97,23 @@ function buildProjectAgentsMd(project, session) {
|
|
|
57
97
|
"",
|
|
58
98
|
"## Research-facing behavior",
|
|
59
99
|
"- Treat researcher interaction as the primary task.",
|
|
100
|
+
"- If the user message starts with `lt `, `longtable `, `long table `, or `롱테이블 ` followed by a directive and `:`, treat it as an explicit Long Table invocation.",
|
|
101
|
+
"- Supported explicit directives are: explore, review, critique, draft, commit, panel, status, editor, reviewer, methods, theory, measurement, ethics, voice, venue.",
|
|
102
|
+
"- For explicit Long Table invocations, do not begin by scanning the workspace. Use the current session files first and answer as Long Table immediately.",
|
|
103
|
+
"- For general research requests in this workspace, prefer Long Table behavior before generic coding behavior.",
|
|
60
104
|
"- Begin exploratory work with clarifying or tension questions before recommending a direction.",
|
|
61
105
|
"- If you foreground role perspectives, disclose them with `Long Table consulted: ...`.",
|
|
62
106
|
"- Keep one accountable synthesis, but do not hide meaningful disagreement.",
|
|
107
|
+
...(session.disagreementPreference === "always_visible"
|
|
108
|
+
? ["- In this workspace, panel disagreement should be visible by default rather than hidden behind a single synthesis."]
|
|
109
|
+
: []),
|
|
63
110
|
"- Do not expose internal tool logs, file-search traces, or process commentary in the researcher-facing answer.",
|
|
64
111
|
"",
|
|
65
112
|
"## Session memory",
|
|
66
113
|
"- Read `.longtable/current-session.json` before giving substantial guidance.",
|
|
67
114
|
"- Use `.longtable/project.json` as the project-level context.",
|
|
68
115
|
"- Prefer the current goal and blocker over generic assumptions.",
|
|
116
|
+
"- Do not recursively search unrelated files unless the researcher explicitly asks for file analysis or code changes.",
|
|
69
117
|
"",
|
|
70
118
|
"## Scope",
|
|
71
119
|
"- These instructions apply to this directory and its children."
|
|
@@ -161,6 +209,7 @@ export async function createOrUpdateProjectWorkspace(options) {
|
|
|
161
209
|
await writeFile(join(sessionsDir, `${sessionId}.json`), JSON.stringify(session, null, 2), "utf8");
|
|
162
210
|
await writeFile(join(metaDir, "state.json"), buildStateSeed(project, session, options.setup), "utf8");
|
|
163
211
|
await writeFile(join(projectPath, "LONGTABLE.md"), buildWorkspaceGuide(project, session), "utf8");
|
|
212
|
+
await writeFile(join(projectPath, "START-HERE.md"), buildStartHereGuide(project, session), "utf8");
|
|
164
213
|
await writeFile(join(projectPath, "AGENTS.md"), buildProjectAgentsMd(project, session), "utf8");
|
|
165
214
|
return {
|
|
166
215
|
project,
|
|
@@ -194,14 +243,20 @@ export async function loadProjectContextFromDirectory(startPath) {
|
|
|
194
243
|
}
|
|
195
244
|
export function renderProjectWorkspaceSummary(context) {
|
|
196
245
|
return [
|
|
197
|
-
"
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
`
|
|
201
|
-
|
|
202
|
-
`
|
|
203
|
-
`
|
|
204
|
-
`
|
|
205
|
-
`
|
|
246
|
+
"┌──────────────────────────────────────────────┐",
|
|
247
|
+
"│ Long Table Project Workspace │",
|
|
248
|
+
"└──────────────────────────────────────────────┘",
|
|
249
|
+
`Project: ${context.project.projectName}`,
|
|
250
|
+
`Path: ${context.project.projectPath}`,
|
|
251
|
+
`Goal: ${context.session.currentGoal}`,
|
|
252
|
+
...(context.session.currentBlocker ? [`Blocker: ${context.session.currentBlocker}`] : []),
|
|
253
|
+
`Perspectives: ${context.session.requestedPerspectives.length > 0 ? context.session.requestedPerspectives.join(", ") : "auto"}`,
|
|
254
|
+
`Disagreement: ${context.session.disagreementPreference}`,
|
|
255
|
+
"",
|
|
256
|
+
"Created files:",
|
|
257
|
+
`- ${context.projectFilePath}`,
|
|
258
|
+
`- ${context.sessionFilePath}`,
|
|
259
|
+
`- ${join(context.project.projectPath, "START-HERE.md")}`,
|
|
260
|
+
`- ${join(context.project.projectPath, "AGENTS.md")}`
|
|
206
261
|
].join("\n");
|
|
207
262
|
}
|