@joshski/dust 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.
- package/dist/agent-events.d.ts +13 -0
- package/dist/artifacts/index.d.ts +13 -2
- package/dist/artifacts/parsed-artifact.d.ts +32 -0
- package/dist/artifacts.js +159 -0
- package/dist/bucket/repository.d.ts +2 -0
- package/dist/cli/commands/focus.d.ts +1 -1
- package/dist/cli/process-runner.d.ts +35 -0
- package/dist/dust.js +622 -450
- package/dist/lint/validators/content-validator.d.ts +5 -4
- package/dist/lint/validators/filename-validator.d.ts +2 -1
- package/dist/lint/validators/idea-validator.d.ts +4 -3
- package/dist/lint/validators/link-validator.d.ts +4 -3
- package/dist/lint/validators/principle-hierarchy.d.ts +3 -2
- package/dist/loop/events.d.ts +18 -1
- package/dist/loop/iteration.d.ts +4 -1
- package/dist/patch/fact.d.ts +15 -0
- package/dist/patch/idea.d.ts +23 -0
- package/dist/patch/index.d.ts +54 -0
- package/dist/patch/principle.d.ts +17 -0
- package/dist/patch/task.d.ts +25 -0
- package/dist/patch.js +1632 -0
- package/dist/validation/validation-pipeline.d.ts +42 -0
- package/dist/validation.js +427 -397
- package/package.json +5 -1
package/dist/agent-events.d.ts
CHANGED
|
@@ -29,6 +29,19 @@ export type AgentSessionEvent = {
|
|
|
29
29
|
} | {
|
|
30
30
|
type: 'command-event';
|
|
31
31
|
commandEvent: CommandEvent;
|
|
32
|
+
} | {
|
|
33
|
+
type: 'preflight-started';
|
|
34
|
+
step: string;
|
|
35
|
+
title?: string;
|
|
36
|
+
} | {
|
|
37
|
+
type: 'preflight-completed';
|
|
38
|
+
step: string;
|
|
39
|
+
output?: string;
|
|
40
|
+
} | {
|
|
41
|
+
type: 'preflight-failed';
|
|
42
|
+
step: string;
|
|
43
|
+
output: string;
|
|
44
|
+
title?: string;
|
|
32
45
|
};
|
|
33
46
|
export interface EventMessage {
|
|
34
47
|
sequence: number;
|
|
@@ -4,8 +4,9 @@ import { type Idea, type IdeaOpenQuestion, type IdeaOption, type ParsedIdeaConte
|
|
|
4
4
|
import { extractTitle } from '../markdown/markdown-utilities';
|
|
5
5
|
import { type Principle } from './principles';
|
|
6
6
|
import { type Task } from './tasks';
|
|
7
|
+
import { type ParsedArtifact, type ParsedMarkdownLink, type ParsedSection, parseArtifact } from './parsed-artifact';
|
|
7
8
|
import { type AllWorkflowTasks, CAPTURE_IDEA_PREFIX, type CreateIdeaTransitionTaskResult, type DecomposeIdeaOptions, findAllWorkflowTasks, type IdeaInProgress, type OpenQuestionResponse, type ParsedCaptureIdeaTask, parseResolvedQuestions, type WorkflowTaskMatch, type WorkflowTaskType } from './workflow-tasks';
|
|
8
|
-
export type { AllWorkflowTasks, CreateIdeaTransitionTaskResult, DecomposeIdeaOptions, Fact, Idea, IdeaOpenQuestion, IdeaOption, OpenQuestionResponse, ParsedCaptureIdeaTask, ParsedIdeaContent, Principle, Task, WorkflowTaskMatch, WorkflowTaskType, };
|
|
9
|
+
export type { AllWorkflowTasks, CreateIdeaTransitionTaskResult, DecomposeIdeaOptions, Fact, Idea, IdeaOpenQuestion, IdeaOption, OpenQuestionResponse, ParsedArtifact, ParsedCaptureIdeaTask, ParsedIdeaContent, ParsedMarkdownLink, ParsedSection, Principle, Task, WorkflowTaskMatch, WorkflowTaskType, };
|
|
9
10
|
export interface TaskGraphNode {
|
|
10
11
|
task: Task;
|
|
11
12
|
workflowType: WorkflowTaskType | null;
|
|
@@ -17,9 +18,19 @@ export interface TaskGraph {
|
|
|
17
18
|
to: string;
|
|
18
19
|
}>;
|
|
19
20
|
}
|
|
20
|
-
export { CAPTURE_IDEA_PREFIX, extractTitle, findAllWorkflowTasks, ideaContentToMarkdown, parseIdeaContent, parseOpenQuestions, parseResolvedQuestions, };
|
|
21
|
+
export { CAPTURE_IDEA_PREFIX, extractTitle, findAllWorkflowTasks, ideaContentToMarkdown, parseArtifact, parseIdeaContent, parseOpenQuestions, parseResolvedQuestions, };
|
|
21
22
|
export type { IdeaInProgress };
|
|
22
23
|
export type ArtifactType = 'ideas' | 'tasks' | 'principles' | 'facts';
|
|
24
|
+
export declare const DUST_PATH_PREFIX = ".dust/";
|
|
25
|
+
/**
|
|
26
|
+
* Parses an artifact path into its type and slug components.
|
|
27
|
+
* This is the inverse of ArtifactsRepository.artifactPath().
|
|
28
|
+
* Works with relative paths (e.g., '.dust/tasks/my-task.md').
|
|
29
|
+
*/
|
|
30
|
+
export declare function parseArtifactPath(path: string): {
|
|
31
|
+
type: ArtifactType;
|
|
32
|
+
slug: string;
|
|
33
|
+
} | null;
|
|
23
34
|
export interface ReadOnlyArtifactsRepository {
|
|
24
35
|
artifactPath(type: ArtifactType, slug: string): string;
|
|
25
36
|
parseIdea(options: {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parsed artifact types with positional metadata for validation.
|
|
3
|
+
*
|
|
4
|
+
* These types enrich artifact parsing with line numbers to support
|
|
5
|
+
* single-pass validation and better error reporting.
|
|
6
|
+
*/
|
|
7
|
+
export interface ParsedMarkdownLink {
|
|
8
|
+
text: string;
|
|
9
|
+
target: string;
|
|
10
|
+
line: number;
|
|
11
|
+
}
|
|
12
|
+
export interface ParsedSection {
|
|
13
|
+
heading: string;
|
|
14
|
+
level: number;
|
|
15
|
+
startLine: number;
|
|
16
|
+
endLine: number;
|
|
17
|
+
links: ParsedMarkdownLink[];
|
|
18
|
+
}
|
|
19
|
+
export interface ParsedArtifact {
|
|
20
|
+
filePath: string;
|
|
21
|
+
rawContent: string;
|
|
22
|
+
title: string | null;
|
|
23
|
+
titleLine: number | null;
|
|
24
|
+
openingSentence: string | null;
|
|
25
|
+
openingSentenceLine: number | null;
|
|
26
|
+
sections: ParsedSection[];
|
|
27
|
+
allLinks: ParsedMarkdownLink[];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Parses markdown content into a ParsedArtifact with positional metadata.
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseArtifact(filePath: string, content: string): ParsedArtifact;
|
package/dist/artifacts.js
CHANGED
|
@@ -379,6 +379,143 @@ async function parseTask(fileSystem, dustPath, slug) {
|
|
|
379
379
|
};
|
|
380
380
|
}
|
|
381
381
|
|
|
382
|
+
// lib/artifacts/parsed-artifact.ts
|
|
383
|
+
var MARKDOWN_LINK_PATTERN2 = /\[([^\]]+)\]\(([^)]+)\)/g;
|
|
384
|
+
function parseArtifact(filePath, content) {
|
|
385
|
+
const lines = content.split(`
|
|
386
|
+
`);
|
|
387
|
+
let title = null;
|
|
388
|
+
let titleLine = null;
|
|
389
|
+
let openingSentence = null;
|
|
390
|
+
let openingSentenceLine = null;
|
|
391
|
+
const sections = [];
|
|
392
|
+
const allLinks = [];
|
|
393
|
+
let currentSection = null;
|
|
394
|
+
let inCodeFence = false;
|
|
395
|
+
let openingSentenceResolved = false;
|
|
396
|
+
for (let i = 0;i < lines.length; i++) {
|
|
397
|
+
const line = lines[i];
|
|
398
|
+
const lineNumber = i + 1;
|
|
399
|
+
if (line.startsWith("```")) {
|
|
400
|
+
inCodeFence = !inCodeFence;
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
if (inCodeFence) {
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
const h1Match = line.match(/^#\s+(.+)$/);
|
|
407
|
+
if (h1Match) {
|
|
408
|
+
if (title === null) {
|
|
409
|
+
title = h1Match[1].trim();
|
|
410
|
+
titleLine = lineNumber;
|
|
411
|
+
} else {
|
|
412
|
+
if (currentSection !== null) {
|
|
413
|
+
currentSection.endLine = findLastNonEmptyLine(lines, currentSection.startLine, lineNumber - 2);
|
|
414
|
+
sections.push(currentSection);
|
|
415
|
+
currentSection = null;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
const headingMatch = line.match(/^(#{2,6})\s+(.+)$/);
|
|
421
|
+
if (headingMatch) {
|
|
422
|
+
openingSentenceResolved = true;
|
|
423
|
+
if (currentSection !== null) {
|
|
424
|
+
currentSection.endLine = findLastNonEmptyLine(lines, currentSection.startLine, lineNumber - 2);
|
|
425
|
+
sections.push(currentSection);
|
|
426
|
+
}
|
|
427
|
+
currentSection = {
|
|
428
|
+
heading: headingMatch[2].trim(),
|
|
429
|
+
level: headingMatch[1].length,
|
|
430
|
+
startLine: lineNumber,
|
|
431
|
+
endLine: -1,
|
|
432
|
+
links: []
|
|
433
|
+
};
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
if (shouldCheckForOpeningSentence(title, openingSentenceResolved, line)) {
|
|
437
|
+
const result = tryExtractOpeningSentence(lines, i);
|
|
438
|
+
openingSentence = result.sentence;
|
|
439
|
+
openingSentenceLine = result.sentence !== null ? lineNumber : null;
|
|
440
|
+
openingSentenceResolved = true;
|
|
441
|
+
}
|
|
442
|
+
const linkMatches = line.matchAll(MARKDOWN_LINK_PATTERN2);
|
|
443
|
+
for (const match of linkMatches) {
|
|
444
|
+
const link = {
|
|
445
|
+
text: match[1],
|
|
446
|
+
target: match[2],
|
|
447
|
+
line: lineNumber
|
|
448
|
+
};
|
|
449
|
+
allLinks.push(link);
|
|
450
|
+
if (currentSection !== null) {
|
|
451
|
+
currentSection.links.push(link);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
if (currentSection !== null) {
|
|
456
|
+
currentSection.endLine = findLastNonEmptyLine(lines, currentSection.startLine, lines.length - 1);
|
|
457
|
+
sections.push(currentSection);
|
|
458
|
+
}
|
|
459
|
+
return {
|
|
460
|
+
filePath,
|
|
461
|
+
rawContent: content,
|
|
462
|
+
title,
|
|
463
|
+
titleLine,
|
|
464
|
+
openingSentence,
|
|
465
|
+
openingSentenceLine,
|
|
466
|
+
sections,
|
|
467
|
+
allLinks
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
function shouldCheckForOpeningSentence(title, resolved, line) {
|
|
471
|
+
if (title === null || resolved) {
|
|
472
|
+
return false;
|
|
473
|
+
}
|
|
474
|
+
const trimmed = line.trim();
|
|
475
|
+
return trimmed !== "" && !isStructuralElement2(trimmed);
|
|
476
|
+
}
|
|
477
|
+
function tryExtractOpeningSentence(lines, startIndex) {
|
|
478
|
+
const paragraph = collectParagraph2(lines, startIndex);
|
|
479
|
+
return { sentence: extractFirstSentence2(paragraph) };
|
|
480
|
+
}
|
|
481
|
+
function findLastNonEmptyLine(lines, contentStartIndex, fromIndex) {
|
|
482
|
+
for (let i = fromIndex;i >= contentStartIndex; i--) {
|
|
483
|
+
if (lines[i].trim() !== "") {
|
|
484
|
+
return i + 1;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return contentStartIndex;
|
|
488
|
+
}
|
|
489
|
+
var LIST_ITEM_PREFIXES2 = ["-", "*", "+"];
|
|
490
|
+
var STRUCTURAL_PREFIXES2 = ["#", "```", ">"];
|
|
491
|
+
function isStructuralElement2(line) {
|
|
492
|
+
if (STRUCTURAL_PREFIXES2.some((prefix) => line.startsWith(prefix))) {
|
|
493
|
+
return true;
|
|
494
|
+
}
|
|
495
|
+
if (LIST_ITEM_PREFIXES2.some((prefix) => line.startsWith(prefix))) {
|
|
496
|
+
return true;
|
|
497
|
+
}
|
|
498
|
+
return /^\d+\./.test(line);
|
|
499
|
+
}
|
|
500
|
+
function isBlockBreak2(line) {
|
|
501
|
+
return STRUCTURAL_PREFIXES2.some((prefix) => line.startsWith(prefix));
|
|
502
|
+
}
|
|
503
|
+
function collectParagraph2(lines, startIndex) {
|
|
504
|
+
const parts = [];
|
|
505
|
+
for (let i = startIndex;i < lines.length; i++) {
|
|
506
|
+
const line = lines[i].trim();
|
|
507
|
+
if (line === "" || isBlockBreak2(line)) {
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
parts.push(line);
|
|
511
|
+
}
|
|
512
|
+
return parts.join(" ");
|
|
513
|
+
}
|
|
514
|
+
function extractFirstSentence2(paragraph) {
|
|
515
|
+
const match = paragraph.match(/^(.+?[.?!])(?:\s|$)/);
|
|
516
|
+
return match ? match[1] : null;
|
|
517
|
+
}
|
|
518
|
+
|
|
382
519
|
// lib/artifacts/workflow-tasks.ts
|
|
383
520
|
var CAPTURE_IDEA_PREFIX = "Add Idea: ";
|
|
384
521
|
var EXPEDITE_IDEA_PREFIX = "Expedite Idea: ";
|
|
@@ -756,6 +893,25 @@ async function parseCaptureIdeaTask(fileSystem, dustPath, taskSlug) {
|
|
|
756
893
|
}
|
|
757
894
|
|
|
758
895
|
// lib/artifacts/index.ts
|
|
896
|
+
var ARTIFACT_TYPES = ["ideas", "tasks", "principles", "facts"];
|
|
897
|
+
var DUST_PATH_PREFIX = ".dust/";
|
|
898
|
+
function parseArtifactPath(path) {
|
|
899
|
+
if (!path.startsWith(DUST_PATH_PREFIX) || !path.endsWith(".md")) {
|
|
900
|
+
return null;
|
|
901
|
+
}
|
|
902
|
+
const withoutPrefix = path.slice(DUST_PATH_PREFIX.length);
|
|
903
|
+
const slashIndex = withoutPrefix.indexOf("/");
|
|
904
|
+
if (slashIndex === -1) {
|
|
905
|
+
return null;
|
|
906
|
+
}
|
|
907
|
+
const type = withoutPrefix.slice(0, slashIndex);
|
|
908
|
+
if (!ARTIFACT_TYPES.includes(type)) {
|
|
909
|
+
return null;
|
|
910
|
+
}
|
|
911
|
+
const filename = withoutPrefix.slice(slashIndex + 1);
|
|
912
|
+
const slug = filename.slice(0, -3);
|
|
913
|
+
return { type, slug };
|
|
914
|
+
}
|
|
759
915
|
function buildReadOperations(fileSystem, dustPath) {
|
|
760
916
|
return {
|
|
761
917
|
artifactPath(type, slug) {
|
|
@@ -861,10 +1017,13 @@ export {
|
|
|
861
1017
|
parseResolvedQuestions,
|
|
862
1018
|
parseOpenQuestions,
|
|
863
1019
|
parseIdeaContent,
|
|
1020
|
+
parseArtifactPath,
|
|
1021
|
+
parseArtifact,
|
|
864
1022
|
ideaContentToMarkdown,
|
|
865
1023
|
findAllWorkflowTasks,
|
|
866
1024
|
extractTitle,
|
|
867
1025
|
buildReadOnlyArtifactsRepository,
|
|
868
1026
|
buildArtifactsRepository,
|
|
1027
|
+
DUST_PATH_PREFIX,
|
|
869
1028
|
CAPTURE_IDEA_PREFIX
|
|
870
1029
|
};
|
|
@@ -64,6 +64,8 @@ export interface RepositoryDependencies {
|
|
|
64
64
|
forwardToolExecution?: (request: ToolExecutionRequest) => Promise<ToolExecutionResult>;
|
|
65
65
|
/** Mark a tool family as revealed (for progressive disclosure) */
|
|
66
66
|
revealFamily?: (familyName: string) => void;
|
|
67
|
+
/** Shell runner for pre-flight commands (install, check) */
|
|
68
|
+
shellRunner?: import('../cli/process-runner').ShellRunner;
|
|
67
69
|
}
|
|
68
70
|
/**
|
|
69
71
|
* Start (or restart) the per-repository loop and keep lifecycle state accurate.
|
|
@@ -7,5 +7,5 @@
|
|
|
7
7
|
* Usage: dust focus "add login box"
|
|
8
8
|
*/
|
|
9
9
|
import type { CommandDependencies, CommandResult } from '../types';
|
|
10
|
-
export declare function buildImplementationInstructions(bin: string, hooksInstalled: boolean, taskTitle?: string, taskPath?: string, installCommand?: string): string;
|
|
10
|
+
export declare function buildImplementationInstructions(bin: string, hooksInstalled: boolean, taskTitle?: string, taskPath?: string, installCommand?: string, skipPreflightSteps?: boolean): string;
|
|
11
11
|
export declare function focus(dependencies: CommandDependencies): Promise<CommandResult>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared process runner utilities for buffered command execution.
|
|
3
|
+
*
|
|
4
|
+
* Provides a unified interface for running shell commands and git commands
|
|
5
|
+
* with buffered output capture.
|
|
6
|
+
*/
|
|
7
|
+
import { type ChildProcess } from 'node:child_process';
|
|
8
|
+
export interface ProcessResult {
|
|
9
|
+
exitCode: number;
|
|
10
|
+
output: string;
|
|
11
|
+
timedOut?: boolean;
|
|
12
|
+
}
|
|
13
|
+
type SpawnFn = (command: string, commandArguments: string[], options: {
|
|
14
|
+
cwd: string;
|
|
15
|
+
shell?: boolean;
|
|
16
|
+
}) => ChildProcess;
|
|
17
|
+
/**
|
|
18
|
+
* Creates a buffered process runner for executing shell commands.
|
|
19
|
+
* Commands are run with shell: true for command string interpretation.
|
|
20
|
+
*/
|
|
21
|
+
export interface ShellRunner {
|
|
22
|
+
run: (command: string, cwd: string, timeoutMs?: number) => Promise<ProcessResult>;
|
|
23
|
+
}
|
|
24
|
+
export declare function createShellRunner(spawnFn: SpawnFn): ShellRunner;
|
|
25
|
+
export declare const defaultShellRunner: ShellRunner;
|
|
26
|
+
/**
|
|
27
|
+
* Creates a buffered process runner for executing git commands.
|
|
28
|
+
* Git is run directly without shell interpretation.
|
|
29
|
+
*/
|
|
30
|
+
export interface GitRunner {
|
|
31
|
+
run: (gitArguments: string[], cwd: string) => Promise<ProcessResult>;
|
|
32
|
+
}
|
|
33
|
+
export declare function createGitRunner(spawnFn: SpawnFn): GitRunner;
|
|
34
|
+
export declare const defaultGitRunner: GitRunner;
|
|
35
|
+
export {};
|