@xfey/tutti 0.1.97 → 0.1.98

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.
Files changed (30) hide show
  1. package/dist/control-plane/index.js +15 -3
  2. package/dist/control-plane/worklist-terminal-feedback.js +4 -4
  3. package/dist/server-shell/http/routes/agent-context-references.js +8 -14
  4. package/dist/server-shell/http/routes/project-api/openapi-viewer-routes.d.ts +0 -2
  5. package/dist/server-shell/http/routes/project-api/reference-files.js +6 -17
  6. package/dist/workspace-ops/constants.d.ts +0 -1
  7. package/dist/workspace-ops/constants.js +0 -1
  8. package/dist/workspace-ops/index.d.ts +3 -2
  9. package/dist/workspace-ops/index.js +3 -2
  10. package/dist/workspace-ops/project-doc-templates.js +1 -1
  11. package/dist/workspace-ops/reference-files.js +3 -2
  12. package/dist/workspace-ops/reference-metadata.d.ts +2 -2
  13. package/dist/workspace-ops/reference-metadata.js +8 -22
  14. package/dist/workspace-ops/reference-summaries.js +11 -17
  15. package/dist/workspace-ops/reference-tree.d.ts +11 -0
  16. package/dist/workspace-ops/reference-tree.js +34 -0
  17. package/dist/workspace-ops/viewer-git.d.ts +1 -0
  18. package/dist/workspace-ops/viewer-git.js +11 -0
  19. package/node_modules/@tutti/shared/dist/schemas/api/viewer-reference.d.ts +0 -5
  20. package/node_modules/@tutti/shared/dist/schemas/api/viewer-reference.js +0 -2
  21. package/package.json +1 -1
  22. package/prompts/procedures/context-sync.md +3 -3
  23. package/prompts/procedures/reference-file-summary.md +1 -1
  24. package/prompts/procedures/task-compile.md +2 -2
  25. package/prompts/prompt-flow-map.md +1 -1
  26. package/prompts/runs/task-execute.md +2 -2
  27. package/prompts/skills/read-references/SKILL.md +2 -2
  28. package/web/assets/{homepage-motion-scene-lpr-1FjJ.js → homepage-motion-scene-_nsq44Ph.js} +1 -1
  29. package/web/assets/{index-CQoq2PtU.js → index-BtTlbKEP.js} +2 -2
  30. package/web/index.html +1 -1
@@ -1,7 +1,7 @@
1
- import { readActiveClarificationProjection, readActiveTaskCompileContext, markScratchpadRefreshSucceeded, readPendingTaskCompileSubmission, readTaskProjectionByActivityRef, readScratchpadSourceState, readWorklistProjection, } from "../collaboration-state/index.js";
1
+ import { readActiveClarificationProjection, readActiveTaskCompileContext, markScratchpadRefreshSucceeded, readPendingTaskCompileSubmission, readTaskDetailProjection, readTaskProjectionByActivityRef, readScratchpadSourceState, readWorklistProjection, } from "../collaboration-state/index.js";
2
2
  import { ProcedureEngine } from "../procedure-engine/index.js";
3
3
  import { RunEngine } from "../run-engine/index.js";
4
- import { WorkspaceOpsError, readReferenceSummaryTargets } from "../workspace-ops/index.js";
4
+ import { WorkspaceOpsError, isReferenceFilePath, readReferenceSummaryTargets, } from "../workspace-ops/index.js";
5
5
  import { publishProcedureTransition as publishProcedureTransitionEvent, publishRunResultRecorded as publishRunResultRecordedEvent, publishRunTransition as publishRunTransitionEvent, publishTaskRunStarted as publishTaskRunStartedEvent, } from "./event-publishers.js";
6
6
  import { startContextSyncAnalysisProcedure } from "./context-sync.js";
7
7
  import { refreshProjectBriefProjection, } from "./project-brief-refresh.js";
@@ -488,10 +488,22 @@ export class Phase5ControlPlane {
488
488
  this.pendingRunInputs.delete(activityRef);
489
489
  if (state.activity.status === "finished") {
490
490
  if (state.activity.task_id !== undefined) {
491
+ const taskId = state.activity.task_id;
492
+ const detail = readTaskDetailProjection(this.store.db, taskId);
493
+ const result = detail?.detail.result;
494
+ if (result?.result_kind === "finished" &&
495
+ result.outcome === "completed" &&
496
+ result.promotion?.status === "promoted") {
497
+ for (const path of result.changed_paths ?? []) {
498
+ if (isReferenceFilePath(path)) {
499
+ this.pendingReferenceSummaryPaths.add(path);
500
+ }
501
+ }
502
+ }
491
503
  emitTaskCompletedResultMessage({
492
504
  store: this.store,
493
505
  events: this.events,
494
- taskId: state.activity.task_id,
506
+ taskId,
495
507
  activityRef: state.activity.activity_ref,
496
508
  ...(this.projectContext === undefined
497
509
  ? {}
@@ -1,4 +1,4 @@
1
- import { readViewerFile, referenceFileSourceForPath, referenceMediaTypeForPath, referenceTuttiGroupForPath, } from "../workspace-ops/index.js";
1
+ import { isReferenceFilePath, readViewerFile, referenceFolderGroupForPath, referenceMediaTypeForPath, } from "../workspace-ops/index.js";
2
2
  import { createMainChatAgentMessage, hasMainChatTaskResultMessage, hasMainChatWorklistFeedbackMessage, readCompletedWorklistFeedbackTaskIds, readTaskDetailProjection, readWorklistProjection, } from "../collaboration-state/index.js";
3
3
  import { mainChatInvalidates } from "./invalidations.js";
4
4
  const BODY_ITEM_LIMIT = 12;
@@ -62,7 +62,7 @@ function generatedFileProjection(file) {
62
62
  name: file.name,
63
63
  size_bytes: file.size_bytes,
64
64
  };
65
- const group = referenceTuttiGroupForPath(file.path);
65
+ const group = referenceFolderGroupForPath(file.path);
66
66
  if (group !== undefined) {
67
67
  projection.group = group;
68
68
  }
@@ -73,7 +73,7 @@ function generatedFileProjection(file) {
73
73
  return projection;
74
74
  }
75
75
  function readGeneratedFile(input) {
76
- if (referenceFileSourceForPath(input.path) !== "tutti_output") {
76
+ if (!isReferenceFilePath(input.path)) {
77
77
  return null;
78
78
  }
79
79
  try {
@@ -100,7 +100,7 @@ function generatedFilesForCompletedEntries(input, entries) {
100
100
  continue;
101
101
  }
102
102
  for (const path of entry.detail?.detail.result.changed_paths ?? []) {
103
- if (referenceFileSourceForPath(path) === "tutti_output") {
103
+ if (isReferenceFilePath(path)) {
104
104
  paths.add(path);
105
105
  }
106
106
  }
@@ -1,4 +1,4 @@
1
- import { REFERENCE_DIRECTORY_PATH, REFERENCE_INDEX_PATH, WorkspaceOpsError, readReferenceSummaryIndex, readViewerFile, readViewerTree, referenceFileCategoryForPath, referenceMediaTypeForPath, referenceSummaryProjectionForEntry, } from "../../../workspace-ops/index.js";
1
+ import { REFERENCE_INDEX_PATH, WorkspaceOpsError, readReferenceFileEntries, readReferenceSummaryIndex, readViewerFile, referenceFileCategoryForPath, referenceMediaTypeForPath, referenceSummaryProjectionForEntry, } from "../../../workspace-ops/index.js";
2
2
  import { AgentContextBadRequestError } from "./agent-context-errors.js";
3
3
  function requireWorkspaceRoot(options) {
4
4
  if (options.workspaceOps === undefined) {
@@ -46,14 +46,11 @@ function unavailableReasonForContent(content) {
46
46
  }
47
47
  function referenceEntries(options) {
48
48
  const workspaceRoot = requireWorkspaceRoot(options);
49
- let tree;
50
49
  try {
51
- tree = readViewerTree({
52
- workspaceRoot,
53
- path: REFERENCE_DIRECTORY_PATH,
54
- depth: 2,
55
- ...(options.now === undefined ? {} : { now: options.now }),
56
- });
50
+ return readReferenceFileEntries({ workspaceRoot })
51
+ .filter((entry) => entry.path !== REFERENCE_INDEX_PATH)
52
+ .filter((entry) => referenceFileCategoryForPath(entry.path) !== undefined)
53
+ .sort((left, right) => left.path.localeCompare(right.path, "en", { sensitivity: "base" }));
57
54
  }
58
55
  catch (error) {
59
56
  if (error instanceof WorkspaceOpsError && error.code === "not_found") {
@@ -61,11 +58,6 @@ function referenceEntries(options) {
61
58
  }
62
59
  throw error;
63
60
  }
64
- return tree.items
65
- .filter((entry) => entry.kind === "file")
66
- .filter((entry) => entry.path !== REFERENCE_INDEX_PATH)
67
- .filter((entry) => referenceFileCategoryForPath(entry.path) !== undefined)
68
- .sort((left, right) => left.path.localeCompare(right.path, "en", { sensitivity: "base" }));
69
61
  }
70
62
  function canReadReferenceText(options, path) {
71
63
  try {
@@ -76,7 +68,9 @@ function canReadReferenceText(options, path) {
76
68
  ...(options.now === undefined ? {} : { now: options.now }),
77
69
  });
78
70
  const reason = unavailableReasonForContent(file.content);
79
- return reason === undefined ? { can_read_text: true } : { can_read_text: false, blocked_reason: reason };
71
+ return reason === undefined
72
+ ? { can_read_text: true }
73
+ : { can_read_text: false, blocked_reason: reason };
80
74
  }
81
75
  catch {
82
76
  return { can_read_text: false, blocked_reason: "restricted_path" };
@@ -283,14 +283,12 @@ export declare const HOST_PROJECT_VIEWER_OPENAPI_ROUTES: ({
283
283
  index_path: import("@sinclair/typebox").TLiteral<"docs/reference/README.md">;
284
284
  files_path: import("@sinclair/typebox").TLiteral<"docs/reference/files">;
285
285
  images_path: import("@sinclair/typebox").TLiteral<"docs/reference/images">;
286
- tutti_path: import("@sinclair/typebox").TLiteral<"docs/reference/tutti">;
287
286
  index_status: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"available">, import("@sinclair/typebox").TLiteral<"missing">, import("@sinclair/typebox").TLiteral<"unreadable">]>;
288
287
  index_unreadable_reason: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"restricted_path">, import("@sinclair/typebox").TLiteral<"secret_like">, import("@sinclair/typebox").TLiteral<"binary">, import("@sinclair/typebox").TLiteral<"unsupported_encoding">, import("@sinclair/typebox").TLiteral<"too_large">]>>;
289
288
  }>;
290
289
  items: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
291
290
  path: import("@sinclair/typebox").TString;
292
291
  name: import("@sinclair/typebox").TString;
293
- source: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"human_upload">, import("@sinclair/typebox").TLiteral<"tutti_output">]>;
294
292
  category: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"file">, import("@sinclair/typebox").TLiteral<"image">]>;
295
293
  size_bytes: import("@sinclair/typebox").TInteger;
296
294
  media_type: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
@@ -1,4 +1,4 @@
1
- import { REFERENCE_DIRECTORY_PATH, REFERENCE_FILES_DIRECTORY_PATH, REFERENCE_IMAGES_DIRECTORY_PATH, REFERENCE_INDEX_PATH, REFERENCE_TUTTI_DIRECTORY_PATH, VIEWER_HARD_READ_LIMIT_BYTES, WorkspaceOpsError, readViewerFile, readViewerSnapshot, readViewerTree, readReferenceSummaryIndex, referenceFileCategoryForPath, referenceFileSourceForPath, referenceMediaTypeForPath, referenceSummaryProjectionForEntry, } from "../../../../workspace-ops/index.js";
1
+ import { REFERENCE_DIRECTORY_PATH, REFERENCE_FILES_DIRECTORY_PATH, REFERENCE_IMAGES_DIRECTORY_PATH, REFERENCE_INDEX_PATH, VIEWER_HARD_READ_LIMIT_BYTES, WorkspaceOpsError, readReferenceFileEntries, readReferenceSummaryIndex, readViewerFile, readViewerSnapshot, referenceFileCategoryForPath, referenceMediaTypeForPath, referenceSummaryProjectionForEntry, } from "../../../../workspace-ops/index.js";
2
2
  import { paginateViewerItems, viewerPageLimit } from "./viewer-projections.js";
3
3
  function unreadableReasonForReferenceContent(content) {
4
4
  if (content.kind === "text") {
@@ -34,7 +34,6 @@ export function toReferenceFileProjection(options) {
34
34
  path: options.entry.path,
35
35
  name: options.entry.name,
36
36
  category,
37
- source: referenceFileSourceForPath(options.entry.path) ?? "human_upload",
38
37
  size_bytes: options.entry.size_bytes ?? 0,
39
38
  text_read: referenceTextReadProjection(options.workspaceRoot, options.entry, options.now),
40
39
  summary: referenceSummaryProjectionForEntry({
@@ -65,7 +64,6 @@ function referenceDirectoryProjection(options) {
65
64
  index_path: REFERENCE_INDEX_PATH,
66
65
  files_path: REFERENCE_FILES_DIRECTORY_PATH,
67
66
  images_path: REFERENCE_IMAGES_DIRECTORY_PATH,
68
- tutti_path: REFERENCE_TUTTI_DIRECTORY_PATH,
69
67
  index_status: "missing",
70
68
  };
71
69
  }
@@ -82,7 +80,6 @@ function referenceDirectoryProjection(options) {
82
80
  index_path: REFERENCE_INDEX_PATH,
83
81
  files_path: REFERENCE_FILES_DIRECTORY_PATH,
84
82
  images_path: REFERENCE_IMAGES_DIRECTORY_PATH,
85
- tutti_path: REFERENCE_TUTTI_DIRECTORY_PATH,
86
83
  index_status: "available",
87
84
  };
88
85
  }
@@ -92,7 +89,6 @@ function referenceDirectoryProjection(options) {
92
89
  index_path: REFERENCE_INDEX_PATH,
93
90
  files_path: REFERENCE_FILES_DIRECTORY_PATH,
94
91
  images_path: REFERENCE_IMAGES_DIRECTORY_PATH,
95
- tutti_path: REFERENCE_TUTTI_DIRECTORY_PATH,
96
92
  index_status: "unreadable",
97
93
  index_unreadable_reason: file.content.reason,
98
94
  };
@@ -105,7 +101,6 @@ function referenceDirectoryProjection(options) {
105
101
  index_path: REFERENCE_INDEX_PATH,
106
102
  files_path: REFERENCE_FILES_DIRECTORY_PATH,
107
103
  images_path: REFERENCE_IMAGES_DIRECTORY_PATH,
108
- tutti_path: REFERENCE_TUTTI_DIRECTORY_PATH,
109
104
  index_status: "missing",
110
105
  };
111
106
  }
@@ -119,14 +114,9 @@ export function readReferenceFilesResponse(options, query) {
119
114
  workspaceRoot,
120
115
  ...(now === undefined ? {} : { now }),
121
116
  });
122
- let tree;
117
+ let entries;
123
118
  try {
124
- tree = readViewerTree({
125
- workspaceRoot,
126
- path: REFERENCE_DIRECTORY_PATH,
127
- depth: 3,
128
- ...(now === undefined ? {} : { now }),
129
- });
119
+ entries = readReferenceFileEntries({ workspaceRoot });
130
120
  }
131
121
  catch (error) {
132
122
  if (error instanceof WorkspaceOpsError && error.code === "not_found") {
@@ -148,11 +138,10 @@ export function readReferenceFilesResponse(options, query) {
148
138
  }
149
139
  const includeIndex = query.include_index ?? false;
150
140
  const summaryIndex = readReferenceSummaryIndex({ workspaceRoot });
151
- const referenceItems = tree.items
152
- .filter((entry) => entry.kind === "file")
141
+ const referenceItems = entries
153
142
  .filter((entry) => includeIndex || entry.path !== REFERENCE_INDEX_PATH)
154
143
  .filter((entry) => entry.path === REFERENCE_INDEX_PATH ||
155
- referenceFileSourceForPath(entry.path) !== undefined)
144
+ referenceFileCategoryForPath(entry.path) !== undefined)
156
145
  .sort((left, right) => {
157
146
  const leftCategory = referenceFileCategoryForPath(left.path) ?? "file";
158
147
  const rightCategory = referenceFileCategoryForPath(right.path) ?? "file";
@@ -202,7 +191,7 @@ export function referenceFileMessageRef(file) {
202
191
  const ref = {
203
192
  path: file.path,
204
193
  name: file.name,
205
- source: file.source,
194
+ source: "human_upload",
206
195
  category: file.category,
207
196
  size_bytes: file.size_bytes,
208
197
  text_read: file.text_read,
@@ -5,7 +5,6 @@ export declare const VIEWER_HARD_READ_LIMIT_BYTES: number;
5
5
  export declare const REFERENCE_DIRECTORY_PATH: "docs/reference";
6
6
  export declare const REFERENCE_FILES_DIRECTORY_PATH: "docs/reference/files";
7
7
  export declare const REFERENCE_IMAGES_DIRECTORY_PATH: "docs/reference/images";
8
- export declare const REFERENCE_TUTTI_DIRECTORY_PATH: "docs/reference/tutti";
9
8
  export declare const REFERENCE_INDEX_PATH: "docs/reference/README.md";
10
9
  export declare const MAX_REFERENCE_UPLOAD_BYTES: number;
11
10
  export declare const VIEWER_ASSET_READ_LIMIT_BYTES: number;
@@ -5,7 +5,6 @@ export const VIEWER_HARD_READ_LIMIT_BYTES = 5 * 1024 * 1024;
5
5
  export const REFERENCE_DIRECTORY_PATH = "docs/reference";
6
6
  export const REFERENCE_FILES_DIRECTORY_PATH = "docs/reference/files";
7
7
  export const REFERENCE_IMAGES_DIRECTORY_PATH = "docs/reference/images";
8
- export const REFERENCE_TUTTI_DIRECTORY_PATH = "docs/reference/tutti";
9
8
  export const REFERENCE_INDEX_PATH = "docs/reference/README.md";
10
9
  export const MAX_REFERENCE_UPLOAD_BYTES = 10 * 1024 * 1024;
11
10
  export const VIEWER_ASSET_READ_LIMIT_BYTES = MAX_REFERENCE_UPLOAD_BYTES;
@@ -1,10 +1,11 @@
1
- export { DEFAULT_VIEWER_MAX_BYTES, MAX_REFERENCE_UPLOAD_BYTES, MAX_VIEWER_MAX_BYTES, PROJECT_DOC_PATHS, REFERENCE_IMAGE_ASSET_CACHE_VERSION, REFERENCE_IMAGE_PREVIEW_MAX_EDGE_PX, REFERENCE_IMAGE_THUMBNAIL_MAX_EDGE_PX, REFERENCE_DIRECTORY_PATH, REFERENCE_FILES_DIRECTORY_PATH, REFERENCE_IMAGES_DIRECTORY_PATH, REFERENCE_INDEX_PATH, REFERENCE_TUTTI_DIRECTORY_PATH, VIEWER_ASSET_READ_LIMIT_BYTES, VIEWER_HARD_READ_LIMIT_BYTES, WORKSPACE_OPS_MAINLINE_BRANCH, } from "./constants.js";
1
+ export { DEFAULT_VIEWER_MAX_BYTES, MAX_REFERENCE_UPLOAD_BYTES, MAX_VIEWER_MAX_BYTES, PROJECT_DOC_PATHS, REFERENCE_IMAGE_ASSET_CACHE_VERSION, REFERENCE_IMAGE_PREVIEW_MAX_EDGE_PX, REFERENCE_IMAGE_THUMBNAIL_MAX_EDGE_PX, REFERENCE_DIRECTORY_PATH, REFERENCE_FILES_DIRECTORY_PATH, REFERENCE_IMAGES_DIRECTORY_PATH, REFERENCE_INDEX_PATH, VIEWER_ASSET_READ_LIMIT_BYTES, VIEWER_HARD_READ_LIMIT_BYTES, WORKSPACE_OPS_MAINLINE_BRANCH, } from "./constants.js";
2
2
  export { WorkspaceOpsError } from "./errors.js";
3
3
  export { commitOwnedPaths, requireOwnedPathsWriteReady } from "./system-commits.js";
4
4
  export { collectProjectSourceDiff, createAcceptedCheckpoint, inspectProjectWorkspace, recoverAcceptedCheckpoint, } from "./project-workspace.js";
5
5
  export { initializeProjectDocs, readProjectDocsInitializationStatus, syncProjectDocs, } from "./project-docs.js";
6
6
  export { uploadReferenceFile } from "./reference-files.js";
7
- export { referenceFileCategoryForPath, referenceFileSourceForPath, referenceMediaTypeForPath, referenceTuttiGroupForPath, } from "./reference-metadata.js";
7
+ export { isReferenceFilePath, referenceFileCategoryForPath, referenceFolderGroupForPath, referenceMediaTypeForPath, } from "./reference-metadata.js";
8
+ export { readReferenceFileEntries } from "./reference-tree.js";
8
9
  export { readReferenceSummaryEntries, readReferenceSummaryIndex, readReferenceSummaryTargets, referenceSummaryProjectionForEntry, updateReferenceSummaries, } from "./reference-summaries.js";
9
10
  export { readViewerAsset, readViewerFile, readViewerSnapshot, readViewerTree } from "./viewer.js";
10
11
  export type { WorkspaceOpsErrorCode } from "./errors.js";
@@ -1,10 +1,11 @@
1
- export { DEFAULT_VIEWER_MAX_BYTES, MAX_REFERENCE_UPLOAD_BYTES, MAX_VIEWER_MAX_BYTES, PROJECT_DOC_PATHS, REFERENCE_IMAGE_ASSET_CACHE_VERSION, REFERENCE_IMAGE_PREVIEW_MAX_EDGE_PX, REFERENCE_IMAGE_THUMBNAIL_MAX_EDGE_PX, REFERENCE_DIRECTORY_PATH, REFERENCE_FILES_DIRECTORY_PATH, REFERENCE_IMAGES_DIRECTORY_PATH, REFERENCE_INDEX_PATH, REFERENCE_TUTTI_DIRECTORY_PATH, VIEWER_ASSET_READ_LIMIT_BYTES, VIEWER_HARD_READ_LIMIT_BYTES, WORKSPACE_OPS_MAINLINE_BRANCH, } from "./constants.js";
1
+ export { DEFAULT_VIEWER_MAX_BYTES, MAX_REFERENCE_UPLOAD_BYTES, MAX_VIEWER_MAX_BYTES, PROJECT_DOC_PATHS, REFERENCE_IMAGE_ASSET_CACHE_VERSION, REFERENCE_IMAGE_PREVIEW_MAX_EDGE_PX, REFERENCE_IMAGE_THUMBNAIL_MAX_EDGE_PX, REFERENCE_DIRECTORY_PATH, REFERENCE_FILES_DIRECTORY_PATH, REFERENCE_IMAGES_DIRECTORY_PATH, REFERENCE_INDEX_PATH, VIEWER_ASSET_READ_LIMIT_BYTES, VIEWER_HARD_READ_LIMIT_BYTES, WORKSPACE_OPS_MAINLINE_BRANCH, } from "./constants.js";
2
2
  export { WorkspaceOpsError } from "./errors.js";
3
3
  export { commitOwnedPaths, requireOwnedPathsWriteReady } from "./system-commits.js";
4
4
  export { collectProjectSourceDiff, createAcceptedCheckpoint, inspectProjectWorkspace, recoverAcceptedCheckpoint, } from "./project-workspace.js";
5
5
  export { initializeProjectDocs, readProjectDocsInitializationStatus, syncProjectDocs, } from "./project-docs.js";
6
6
  export { uploadReferenceFile } from "./reference-files.js";
7
- export { referenceFileCategoryForPath, referenceFileSourceForPath, referenceMediaTypeForPath, referenceTuttiGroupForPath, } from "./reference-metadata.js";
7
+ export { isReferenceFilePath, referenceFileCategoryForPath, referenceFolderGroupForPath, referenceMediaTypeForPath, } from "./reference-metadata.js";
8
+ export { readReferenceFileEntries } from "./reference-tree.js";
8
9
  export { readReferenceSummaryEntries, readReferenceSummaryIndex, readReferenceSummaryTargets, referenceSummaryProjectionForEntry, updateReferenceSummaries, } from "./reference-summaries.js";
9
10
  export { readViewerAsset, readViewerFile, readViewerSnapshot, readViewerTree } from "./viewer.js";
10
11
  //# sourceMappingURL=index.js.map
@@ -129,7 +129,7 @@ ${asBulletList([
129
129
  ...repoHints.directories.map((directory) => `Top-level directory: \`${directory}/\``),
130
130
  ...repoHints.root_files.map((file) => `Root file: \`${file}\``),
131
131
  ], "Repository structure is pending context sync.")}
132
- - Human-uploaded reference materials, when present, live under \`docs/reference/\`.
132
+ - Shared reference materials maintained by project members and Tutti, when present, live under \`docs/reference/\`.
133
133
 
134
134
  ## Naming Conventions
135
135
 
@@ -110,7 +110,7 @@ function appendReferenceIndexEntry(options) {
110
110
  ? [
111
111
  "# Reference Files",
112
112
  "",
113
- "Human-uploaded project reference files.",
113
+ "Shared project reference files maintained by project members and Tutti.",
114
114
  "",
115
115
  "## Files",
116
116
  "",
@@ -185,7 +185,8 @@ export function uploadReferenceFile(options) {
185
185
  paths: [REFERENCE_INDEX_PATH, repoPath],
186
186
  message: `Add reference file: ${safeName}`,
187
187
  });
188
- const resolvedCommitOid = commitOid ?? readViewerFile({ workspaceRoot: options.workspaceRoot, path: repoPath }).snapshot.head.oid;
188
+ const resolvedCommitOid = commitOid ??
189
+ readViewerFile({ workspaceRoot: options.workspaceRoot, path: repoPath }).snapshot.head.oid;
189
190
  const file = readViewerFile({
190
191
  workspaceRoot: options.workspaceRoot,
191
192
  path: repoPath,
@@ -9,7 +9,7 @@ export declare function classifyReferenceUpload(options: {
9
9
  directoryPath: string;
10
10
  };
11
11
  export declare function referenceFileCategoryForPath(path: string): ReferenceFileCategory | undefined;
12
- export declare function referenceFileSourceForPath(path: string): "human_upload" | "tutti_output" | undefined;
13
- export declare function referenceTuttiGroupForPath(path: string): string | undefined;
12
+ export declare function isReferenceFilePath(path: string): boolean;
13
+ export declare function referenceFolderGroupForPath(path: string): string | undefined;
14
14
  export declare function referenceMediaTypeForPath(path: string): string | undefined;
15
15
  //# sourceMappingURL=reference-metadata.d.ts.map
@@ -1,5 +1,5 @@
1
- import { extname, posix } from "node:path";
2
- import { REFERENCE_DIRECTORY_PATH, REFERENCE_FILES_DIRECTORY_PATH, REFERENCE_IMAGES_DIRECTORY_PATH, REFERENCE_INDEX_PATH, REFERENCE_TUTTI_DIRECTORY_PATH, } from "./constants.js";
1
+ import { extname } from "node:path";
2
+ import { REFERENCE_DIRECTORY_PATH, REFERENCE_FILES_DIRECTORY_PATH, REFERENCE_IMAGES_DIRECTORY_PATH, REFERENCE_INDEX_PATH, } from "./constants.js";
3
3
  const REFERENCE_IMAGE_EXTENSIONS = new Set([".png", ".jpg", ".jpeg", ".webp", ".gif"]);
4
4
  const REFERENCE_IMAGE_MEDIA_TYPES = new Set(["image/png", "image/jpeg", "image/webp", "image/gif"]);
5
5
  function normalizeMediaType(mediaType) {
@@ -54,33 +54,19 @@ export function referenceFileCategoryForPath(path) {
54
54
  if (path.startsWith(`${REFERENCE_IMAGES_DIRECTORY_PATH}/`)) {
55
55
  return "image";
56
56
  }
57
- if (path.startsWith(`${REFERENCE_FILES_DIRECTORY_PATH}/`)) {
58
- return "file";
59
- }
60
- if (path.startsWith(`${REFERENCE_TUTTI_DIRECTORY_PATH}/`)) {
61
- return "file";
62
- }
63
- if (posix.dirname(path) === REFERENCE_DIRECTORY_PATH && path !== REFERENCE_INDEX_PATH) {
57
+ if (isReferenceFilePath(path)) {
64
58
  return "file";
65
59
  }
66
60
  return undefined;
67
61
  }
68
- export function referenceFileSourceForPath(path) {
69
- if (path.startsWith(`${REFERENCE_TUTTI_DIRECTORY_PATH}/`)) {
70
- return "tutti_output";
71
- }
72
- if (path.startsWith(`${REFERENCE_FILES_DIRECTORY_PATH}/`) ||
73
- path.startsWith(`${REFERENCE_IMAGES_DIRECTORY_PATH}/`) ||
74
- (posix.dirname(path) === REFERENCE_DIRECTORY_PATH && path !== REFERENCE_INDEX_PATH)) {
75
- return "human_upload";
76
- }
77
- return undefined;
62
+ export function isReferenceFilePath(path) {
63
+ return path !== REFERENCE_INDEX_PATH && path.startsWith(`${REFERENCE_DIRECTORY_PATH}/`);
78
64
  }
79
- export function referenceTuttiGroupForPath(path) {
80
- if (!path.startsWith(`${REFERENCE_TUTTI_DIRECTORY_PATH}/`)) {
65
+ export function referenceFolderGroupForPath(path) {
66
+ if (!isReferenceFilePath(path)) {
81
67
  return undefined;
82
68
  }
83
- const rest = path.slice(REFERENCE_TUTTI_DIRECTORY_PATH.length + 1);
69
+ const rest = path.slice(REFERENCE_DIRECTORY_PATH.length + 1);
84
70
  const [first, second] = rest.split("/");
85
71
  if (first === undefined || first.length === 0 || second === undefined) {
86
72
  return undefined;
@@ -1,10 +1,10 @@
1
1
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { redactText } from "@tutti/shared/utils";
4
- import { REFERENCE_DIRECTORY_PATH, REFERENCE_INDEX_PATH, } from "./constants.js";
4
+ import { REFERENCE_INDEX_PATH } from "./constants.js";
5
5
  import { referenceFileCategoryForPath, referenceMediaTypeForPath } from "./reference-metadata.js";
6
+ import { readReferenceFileEntries } from "./reference-tree.js";
6
7
  import { commitOwnedPaths, requireOwnedPathsWriteReady } from "./system-commits.js";
7
- import { readViewerTree } from "./viewer.js";
8
8
  const SUMMARY_SECTION_HEADING = "## Summaries";
9
9
  const SUMMARY_BLOCK_START = "<!-- tutti-reference-summaries:v1";
10
10
  const SUMMARY_BLOCK_END = "-->";
@@ -115,7 +115,7 @@ function readReferenceIndexContent(workspaceRoot) {
115
115
  return [
116
116
  "# Reference Files",
117
117
  "",
118
- "Human-uploaded project reference files.",
118
+ "Shared project reference files maintained by project members and Tutti.",
119
119
  "",
120
120
  "## Files",
121
121
  "",
@@ -138,8 +138,7 @@ export function referenceSummaryProjectionForEntry(options) {
138
138
  ...(record.source_blob_oid === undefined ? {} : { source_blob_oid: record.source_blob_oid }),
139
139
  ...(record.read_paths === undefined ? {} : { read_paths: record.read_paths }),
140
140
  };
141
- if (options.entry.blob_oid !== undefined &&
142
- record.source_blob_oid !== options.entry.blob_oid) {
141
+ if (options.entry.blob_oid !== undefined && record.source_blob_oid !== options.entry.blob_oid) {
143
142
  return {
144
143
  status: "stale",
145
144
  ...base,
@@ -165,14 +164,8 @@ export function referenceSummaryProjectionForEntry(options) {
165
164
  ...(record.summary === undefined ? {} : { text: record.summary }),
166
165
  };
167
166
  }
168
- function readReferenceFileEntries(options) {
169
- const tree = readViewerTree({
170
- workspaceRoot: options.workspaceRoot,
171
- path: REFERENCE_DIRECTORY_PATH,
172
- depth: 2,
173
- ...(options.now === undefined ? {} : { now: options.now }),
174
- });
175
- return tree.items
167
+ function readSummaryFileEntries(options) {
168
+ return readReferenceFileEntries({ workspaceRoot: options.workspaceRoot })
176
169
  .filter((entry) => entry.kind === "file")
177
170
  .filter((entry) => referenceFileCategoryForPath(entry.path) !== undefined)
178
171
  .filter((entry) => entry.blob_oid !== undefined && entry.size_bytes !== undefined)
@@ -181,7 +174,7 @@ function readReferenceFileEntries(options) {
181
174
  export function readReferenceSummaryEntries(options) {
182
175
  const summaries = readReferenceSummaryIndex({ workspaceRoot: options.workspaceRoot });
183
176
  const requested = options.paths === undefined ? undefined : new Set(options.paths);
184
- return readReferenceFileEntries(options)
177
+ return readSummaryFileEntries(options)
185
178
  .filter((entry) => requested === undefined || requested.has(entry.path))
186
179
  .map((entry) => {
187
180
  const category = referenceFileCategoryForPath(entry.path) ?? "file";
@@ -239,7 +232,9 @@ function renderSummaryRecord(record) {
239
232
  return rendered;
240
233
  }
241
234
  function renderSummariesSection(records) {
242
- const items = records.map(renderSummaryRecord).sort((left, right) => left.path.localeCompare(right.path, "en", { sensitivity: "base" }));
235
+ const items = records
236
+ .map(renderSummaryRecord)
237
+ .sort((left, right) => left.path.localeCompare(right.path, "en", { sensitivity: "base" }));
243
238
  const json = JSON.stringify({ items }, null, 2);
244
239
  const lines = [
245
240
  SUMMARY_SECTION_HEADING,
@@ -277,9 +272,8 @@ export function updateReferenceSummaries(options) {
277
272
  }
278
273
  existing.set(summary.path, rendered);
279
274
  }
280
- const currentPaths = new Set(readReferenceFileEntries({
275
+ const currentPaths = new Set(readSummaryFileEntries({
281
276
  workspaceRoot: options.workspaceRoot,
282
- ...(options.now === undefined ? {} : { now: options.now }),
283
277
  }).map((entry) => entry.path));
284
278
  const records = [...existing.values()].filter((record) => currentPaths.has(record.path));
285
279
  const nextContent = `${removeSummarySection(existingContent)}\n\n${renderSummariesSection(records)}\n`;
@@ -0,0 +1,11 @@
1
+ import type { ViewerTreeEntry } from "./types.js";
2
+ /**
3
+ * Enumerate every committed regular file in the shared Reference directory.
4
+ * The generic Repo Viewer intentionally keeps its bounded depth contract; this
5
+ * Reference-specific reader is the single recursive source for the API,
6
+ * summaries, and agent context.
7
+ */
8
+ export declare function readReferenceFileEntries(options: {
9
+ workspaceRoot: string;
10
+ }): ViewerTreeEntry[];
11
+ //# sourceMappingURL=reference-tree.d.ts.map
@@ -0,0 +1,34 @@
1
+ import { posix } from "node:path";
2
+ import { REFERENCE_DIRECTORY_PATH } from "./constants.js";
3
+ import { WorkspaceOpsError } from "./errors.js";
4
+ import { entryKind, lsPath, lsTreeRecursive } from "./viewer-git.js";
5
+ import { isPathAllowedForProjection } from "./viewer-paths.js";
6
+ import { createViewerTreeEntry } from "./viewer-tree.js";
7
+ /**
8
+ * Enumerate every committed regular file in the shared Reference directory.
9
+ * The generic Repo Viewer intentionally keeps its bounded depth contract; this
10
+ * Reference-specific reader is the single recursive source for the API,
11
+ * summaries, and agent context.
12
+ */
13
+ export function readReferenceFileEntries(options) {
14
+ const root = lsPath(options.workspaceRoot, REFERENCE_DIRECTORY_PATH);
15
+ if (root === null) {
16
+ throw new WorkspaceOpsError("not_found", "Reference directory was not found");
17
+ }
18
+ if (entryKind(root) !== "directory") {
19
+ throw new WorkspaceOpsError("validation_failed", "Reference path is not a directory");
20
+ }
21
+ return lsTreeRecursive(options.workspaceRoot, REFERENCE_DIRECTORY_PATH)
22
+ .filter((entry) => entryKind(entry) === "file")
23
+ .filter((entry) => isPathAllowedForProjection(entry.path))
24
+ .map((entry) => {
25
+ const relativePath = entry.path.slice(REFERENCE_DIRECTORY_PATH.length + 1);
26
+ return createViewerTreeEntry(entry, {
27
+ path: entry.path,
28
+ parentPath: posix.dirname(entry.path),
29
+ depth: relativePath.split("/").length,
30
+ });
31
+ })
32
+ .sort((left, right) => left.path.localeCompare(right.path, "en", { sensitivity: "base" }));
33
+ }
34
+ //# sourceMappingURL=reference-tree.js.map
@@ -9,6 +9,7 @@ export type RawTreeEntry = {
9
9
  export declare function readViewerSnapshot(options: WorkspaceViewerOptions): ViewerRepoSnapshot;
10
10
  export declare function lsPath(workspaceRoot: string, normalizedPath: string): RawTreeEntry | null;
11
11
  export declare function lsDirectoryChildren(workspaceRoot: string, normalizedPath: string): RawTreeEntry[];
12
+ export declare function lsTreeRecursive(workspaceRoot: string, normalizedPath: string): RawTreeEntry[];
12
13
  export declare function entryKind(entry: RawTreeEntry): ViewerEntryKind;
13
14
  export declare function requireFileEntry(options: {
14
15
  workspaceRoot: string;
@@ -76,6 +76,17 @@ export function lsDirectoryChildren(workspaceRoot, normalizedPath) {
76
76
  : `${WORKSPACE_OPS_MAINLINE_BRANCH}:${normalizedPath}`;
77
77
  return lsTree(workspaceRoot, ["ls-tree", "-z", "-l", treeish]);
78
78
  }
79
+ export function lsTreeRecursive(workspaceRoot, normalizedPath) {
80
+ return lsTree(workspaceRoot, [
81
+ "ls-tree",
82
+ "-r",
83
+ "-z",
84
+ "-l",
85
+ WORKSPACE_OPS_MAINLINE_BRANCH,
86
+ "--",
87
+ normalizedPath,
88
+ ]);
89
+ }
79
90
  export function entryKind(entry) {
80
91
  if (entry.mode === "040000" || entry.type === "tree") {
81
92
  return "directory";
@@ -250,7 +250,6 @@ export declare const ReferenceDirectoryProjectionSchema: import("@sinclair/typeb
250
250
  index_path: import("@sinclair/typebox").TLiteral<"docs/reference/README.md">;
251
251
  files_path: import("@sinclair/typebox").TLiteral<"docs/reference/files">;
252
252
  images_path: import("@sinclair/typebox").TLiteral<"docs/reference/images">;
253
- tutti_path: import("@sinclair/typebox").TLiteral<"docs/reference/tutti">;
254
253
  index_status: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"available">, import("@sinclair/typebox").TLiteral<"missing">, import("@sinclair/typebox").TLiteral<"unreadable">]>;
255
254
  index_unreadable_reason: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"restricted_path">, import("@sinclair/typebox").TLiteral<"secret_like">, import("@sinclair/typebox").TLiteral<"binary">, import("@sinclair/typebox").TLiteral<"unsupported_encoding">, import("@sinclair/typebox").TLiteral<"too_large">]>>;
256
255
  }>;
@@ -266,7 +265,6 @@ export declare const ReferenceSummaryProjectionSchema: import("@sinclair/typebox
266
265
  export declare const ReferenceFileProjectionSchema: import("@sinclair/typebox").TObject<{
267
266
  path: import("@sinclair/typebox").TString;
268
267
  name: import("@sinclair/typebox").TString;
269
- source: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"human_upload">, import("@sinclair/typebox").TLiteral<"tutti_output">]>;
270
268
  category: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"file">, import("@sinclair/typebox").TLiteral<"image">]>;
271
269
  size_bytes: import("@sinclair/typebox").TInteger;
272
270
  media_type: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
@@ -308,14 +306,12 @@ export declare const GetReferenceFilesResponseSchema: import("@sinclair/typebox"
308
306
  index_path: import("@sinclair/typebox").TLiteral<"docs/reference/README.md">;
309
307
  files_path: import("@sinclair/typebox").TLiteral<"docs/reference/files">;
310
308
  images_path: import("@sinclair/typebox").TLiteral<"docs/reference/images">;
311
- tutti_path: import("@sinclair/typebox").TLiteral<"docs/reference/tutti">;
312
309
  index_status: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"available">, import("@sinclair/typebox").TLiteral<"missing">, import("@sinclair/typebox").TLiteral<"unreadable">]>;
313
310
  index_unreadable_reason: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"restricted_path">, import("@sinclair/typebox").TLiteral<"secret_like">, import("@sinclair/typebox").TLiteral<"binary">, import("@sinclair/typebox").TLiteral<"unsupported_encoding">, import("@sinclair/typebox").TLiteral<"too_large">]>>;
314
311
  }>;
315
312
  items: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
316
313
  path: import("@sinclair/typebox").TString;
317
314
  name: import("@sinclair/typebox").TString;
318
- source: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"human_upload">, import("@sinclair/typebox").TLiteral<"tutti_output">]>;
319
315
  category: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"file">, import("@sinclair/typebox").TLiteral<"image">]>;
320
316
  size_bytes: import("@sinclair/typebox").TInteger;
321
317
  media_type: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
@@ -359,7 +355,6 @@ export declare const UploadReferenceFileResultSchema: import("@sinclair/typebox"
359
355
  file: import("@sinclair/typebox").TObject<{
360
356
  path: import("@sinclair/typebox").TString;
361
357
  name: import("@sinclair/typebox").TString;
362
- source: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"human_upload">, import("@sinclair/typebox").TLiteral<"tutti_output">]>;
363
358
  category: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"file">, import("@sinclair/typebox").TLiteral<"image">]>;
364
359
  size_bytes: import("@sinclair/typebox").TInteger;
365
360
  media_type: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
@@ -164,7 +164,6 @@ export const ReferenceDirectoryProjectionSchema = Type.Object({
164
164
  index_path: Type.Literal("docs/reference/README.md"),
165
165
  files_path: Type.Literal("docs/reference/files"),
166
166
  images_path: Type.Literal("docs/reference/images"),
167
- tutti_path: Type.Literal("docs/reference/tutti"),
168
167
  index_status: Type.Union([
169
168
  Type.Literal("available"),
170
169
  Type.Literal("missing"),
@@ -196,7 +195,6 @@ export const ReferenceSummaryProjectionSchema = Type.Object({
196
195
  export const ReferenceFileProjectionSchema = Type.Object({
197
196
  path: ViewerPathSchema,
198
197
  name: NonEmptyStringSchema,
199
- source: Type.Union([Type.Literal("human_upload"), Type.Literal("tutti_output")]),
200
198
  category: ReferenceFileCategorySchema,
201
199
  size_bytes: Type.Integer({ minimum: 0 }),
202
200
  media_type: Type.Optional(NonEmptyStringSchema),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xfey/tutti",
3
- "version": "0.1.97",
3
+ "version": "0.1.98",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -16,9 +16,9 @@ Use repository inspection and attached skills only when useful:
16
16
 
17
17
  - Use `read-project-docs` for existing project documents and availability.
18
18
  - Use `read-worklist` when repo state or completed formal tasks can explain the current implementation stage. Do not turn task results into a changelog.
19
- - Use `read-references` only when a human-uploaded reference file's actual content matters.
19
+ - Use `read-references` only when a file in the shared project Reference space actually matters.
20
20
 
21
- Reference upload events, file names, media types, and paths do not prove file contents. Read the relevant file before relying on it.
21
+ Reference creation events, file names, media types, paths, and summaries do not prove file contents. Read the relevant file before relying on it.
22
22
 
23
23
  # Documentation Scope
24
24
 
@@ -62,5 +62,5 @@ Return only JSON matching this schema:
62
62
  If no concrete documentation update is needed, return an empty `updates` array:
63
63
 
64
64
  ```json
65
- {"summary":"Checked current docs against repository facts; no updates needed.","updates":[]}
65
+ { "summary": "Checked current docs against repository facts; no updates needed.", "updates": [] }
66
66
  ```
@@ -6,7 +6,7 @@ model_path: codex.app_server.read_only
6
6
 
7
7
  # Role
8
8
 
9
- You summarize one human-uploaded Tutti reference file.
9
+ You summarize one file from Tutti's shared project Reference space.
10
10
 
11
11
  # Runtime
12
12
 
@@ -36,9 +36,9 @@ You compile Tutti's Scratchpad into an append-only Worklist proposal for post-ba
36
36
  Tutti can expose direct user-viewable outputs through two repo-backed surfaces when a task result is meant for project members to inspect:
37
37
 
38
38
  - a single active visual artifact, declared by `tutti.artifact.json`, for previewable pages, apps, dashboards, or similar interactive outputs;
39
- - generated files under `docs/reference/tutti/**`, for reviewable files such as reports, notes, specs, exports, or similar outputs.
39
+ - generated files in the shared `docs/reference/**` space, directly at the root or under a meaningful semantic subfolder, for reviewable files such as reports, notes, specs, exports, or similar outputs.
40
40
 
41
- Treat these as delivery surfaces, not as task subjects by themselves. Human-uploaded references still live under `docs/reference/files/` or `docs/reference/images/`, and stable project facts still belong in the relevant project docs or README-style files.
41
+ Treat these as delivery surfaces, not as task subjects by themselves. Browser uploads continue to enter the Host-managed `docs/reference/files/` or `docs/reference/images/` partitions, `docs/reference/README.md` remains Host-owned, and stable project facts still belong in the relevant project docs or README-style files.
42
42
 
43
43
  When the Scratchpad clearly asks for one of these direct deliverables, include a concise task `scope` item naming the expected surface. If a completion card should call out the result or generated file name, the scope may also mention `user_note_candidate`. Do not add artifact or generated-reference outputs solely to create feedback for ordinary implementation tasks.
44
44